mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 20:02:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84026b105f | ||
|
|
a4d0589f10 | ||
|
|
e375f6afce | ||
|
|
5a7bc04816 | ||
|
|
bd1894580e | ||
|
|
9e694c0337 | ||
|
|
c82586db28 | ||
|
|
dd2d466350 | ||
|
|
830da8de0a | ||
|
|
4e5ee333c8 | ||
|
|
9df899eb63 | ||
|
|
ca7491a702 | ||
|
|
1a07129865 | ||
|
|
4fbd67ff99 | ||
|
|
5bc6c50f42 | ||
|
|
063de3801d | ||
|
|
ae65266a4a | ||
|
|
8ed2401e0b | ||
|
|
d2e8ee8269 | ||
|
|
1f996e3b8b | ||
|
|
7108b74105 | ||
|
|
801fe1b604 | ||
|
|
fb44c87597 | ||
|
|
6b9cdbd482 | ||
|
|
0ab98033b5 | ||
|
|
14a2b96609 | ||
|
|
f829b689db | ||
|
|
dfda8b7ed5 | ||
|
|
4388466451 | ||
|
|
5c2f509a52 | ||
|
|
59582f5210 | ||
|
|
e2a8bdbdfb | ||
|
|
0916b943da | ||
|
|
9c7ebc883c | ||
|
|
0ee42b9aa0 | ||
|
|
37b3868ca3 | ||
|
|
a6835ce3f0 | ||
|
|
69c96ad99b | ||
|
|
b72877d59d | ||
|
|
05eb15d4f7 | ||
|
|
f1fec37c79 | ||
|
|
73f6880ff8 | ||
|
|
8a53742f31 | ||
|
|
9be40e85ff | ||
|
|
61079c1eb7 | ||
|
|
1075ee8fc3 | ||
|
|
a28b265197 | ||
|
|
20e534c468 | ||
|
|
da7aa5dc49 | ||
|
|
8f2a43ca0a | ||
|
|
d0909d7810 | ||
|
|
1641999d20 | ||
|
|
e16452037c | ||
|
|
344d79684a | ||
|
|
573a1d9b7b | ||
|
|
25ab57580c | ||
|
|
a332e0e3d1 | ||
|
|
376f1cb139 | ||
|
|
90f80558d7 | ||
|
|
e281994898 | ||
|
|
29fac122e1 | ||
|
|
1dc412eb90 | ||
|
|
3770a4fe0c | ||
|
|
79cda544c8 | ||
|
|
f04b97d890 | ||
|
|
3e9b4ccc45 | ||
|
|
2c3d838dd8 | ||
|
|
7668a3c660 | ||
|
|
5dd45b714a | ||
|
|
8b08895d0f | ||
|
|
8f8d99e3ed | ||
|
|
23474360ec | ||
|
|
81c255c450 | ||
|
|
ef23d52ed7 | ||
|
|
220ab773aa | ||
|
|
e3e5bff7bb | ||
|
|
7b9a841b2a | ||
|
|
40423911ef | ||
|
|
582a70b046 | ||
|
|
5b63590ebf | ||
|
|
2b6510dc19 | ||
|
|
9d49589d73 | ||
|
|
125b66c929 | ||
|
|
5255f1c052 | ||
|
|
a6ba05d60c | ||
|
|
41e963b04b | ||
|
|
6ff75bef29 | ||
|
|
72c16d0d32 | ||
|
|
94653e5c8c | ||
|
|
3e2b2a698d | ||
|
|
ae04f5aee8 | ||
|
|
5c56267662 | ||
|
|
e55ce5504e | ||
|
|
fb1e89d9ef | ||
|
|
bc550a4549 | ||
|
|
ebdea9cf76 | ||
|
|
09ec508f82 | ||
|
|
d06e9ea7f6 | ||
|
|
a36bdc54fd |
No files matched your search
+1452
-7
File diff suppressed because it is too large.
Load diff
Generated
+10
-10
@@ -1,23 +1,23 @@
|
||||
<component name="libraryTable">
|
||||
<library name="KotlinJavaRuntime" type="repository">
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.10" />
|
||||
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.20" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.10/kotlin-stdlib-jdk7-2.1.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.10/kotlin-stdlib-jdk7-2.1.10-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.10/kotlin-stdlib-jdk7-2.1.10-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
+5
-5
@@ -1,11 +1,11 @@
|
||||
<component name="libraryTable">
|
||||
<library name="eclipse.lsp4j" type="repository">
|
||||
<properties maven-id="org.eclipse.lsp4j:org.eclipse.lsp4j:0.23.1" />
|
||||
<properties maven-id="org.eclipse.lsp4j:org.eclipse.lsp4j:0.24.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.23.1/org.eclipse.lsp4j-0.23.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.23.1/org.eclipse.lsp4j.jsonrpc-0.23.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.11.0/gson-2.11.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.27.0/error_prone_annotations-2.27.0.jar!/" />
|
||||
<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!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
Generated
+1
-1
@@ -22,7 +22,7 @@
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<type id="Python" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="openjdk-11" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+1
@@ -17,6 +17,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.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" />
|
||||
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
|
||||
@@ -63,10 +63,12 @@ What does Prog8 provide?
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- floating point math is supported on certain targets
|
||||
- access to most Kernal ROM routines as external subroutine definitions you can call normally
|
||||
- tight control over Zeropage usage
|
||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- programs can be configured to execute in ROM
|
||||
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
|
||||
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||
- high-level program optimizations
|
||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- conditional branches that map 1:1 to cpu status flags
|
||||
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
|
||||
- ``in`` expression for concise and efficient multi-value/containment check
|
||||
@@ -93,8 +95,7 @@ What does Prog8 provide?
|
||||
- "c64": Commodore-64 (6502 like CPU)
|
||||
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
|
||||
- "pet32": Commodore PET (limited support)
|
||||
- "atari": Atari 8 bit such as 800XL (experimental)
|
||||
- "neo": Neo6502 (experimental)
|
||||
- via external configurable targets: Atari 800 XL, Neo6502, NES, C64OS, ...
|
||||
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ circles {
|
||||
|
||||
sub draw(bool use_kernal, uword max_time) -> uword {
|
||||
if use_kernal
|
||||
void cx16.set_screen_mode(128)
|
||||
cx16.set_screen_mode(128)
|
||||
else
|
||||
gfx_lores.graphics_mode()
|
||||
|
||||
@@ -33,7 +33,7 @@ circles {
|
||||
}
|
||||
|
||||
if use_kernal
|
||||
void cx16.set_screen_mode(3)
|
||||
cx16.set_screen_mode(3)
|
||||
else {
|
||||
gfx_lores.text_mode()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
%import sprites
|
||||
%import coroutines
|
||||
%import math
|
||||
|
||||
|
||||
animsprites {
|
||||
uword num_iterations
|
||||
ubyte[64] sx
|
||||
ubyte[64] sy
|
||||
ubyte[64] sc
|
||||
ubyte[64] dx
|
||||
ubyte[64] dy
|
||||
uword maximum_duration
|
||||
|
||||
sub benchmark(uword max_duration) -> uword {
|
||||
maximum_duration = max_duration
|
||||
math.rndseed(1122,9876)
|
||||
cx16.set_screen_mode(3)
|
||||
cx16.mouse_config2(1)
|
||||
sprites.set_mousepointer_hand()
|
||||
repeat 64
|
||||
void coroutines.add(animsprite, 0)
|
||||
cx16.mouse_config2(0)
|
||||
|
||||
cbm.SETTIM(0,0,0)
|
||||
coroutines.run(supervisor)
|
||||
|
||||
sprites.reset(0, 64)
|
||||
return num_iterations
|
||||
}
|
||||
|
||||
sub supervisor() -> bool {
|
||||
if cbm.RDTIM16() >= maximum_duration {
|
||||
coroutines.killall()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sub animsprite() {
|
||||
num_iterations++
|
||||
; set up the sprite
|
||||
ubyte sprnum = coroutines.current()
|
||||
cx16.r6L, cx16.r7 = sprites.get_data_ptr(0)
|
||||
sprites.init(sprnum, cx16.r6L, cx16.r7, sprites.SIZE_16, sprites.SIZE_16, sprites.COLORS_256, 0)
|
||||
sx[sprnum] = math.rnd()
|
||||
sy[sprnum] = math.rnd()
|
||||
sc[sprnum] = math.rnd()
|
||||
dx[sprnum] = if math.rnd()&1 == 1 1 else 255
|
||||
dy[sprnum] = if math.rnd()&1 == 1 1 else 255
|
||||
|
||||
; move the sprite around
|
||||
while sc[sprnum]!=0 {
|
||||
animate(sprnum)
|
||||
void coroutines.yield()
|
||||
sprnum = coroutines.current()
|
||||
}
|
||||
|
||||
sub animate(ubyte spr) {
|
||||
defer sc[spr]--
|
||||
sprites.pos(spr, sx[spr], sy[spr])
|
||||
sx[spr] += dx[spr]
|
||||
sy[spr] += dy[spr]
|
||||
}
|
||||
|
||||
; end the task but replace it with a fresh animated sprite task
|
||||
void coroutines.add(animsprite, 0)
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
%import b_queens
|
||||
%import b_textelite
|
||||
%import b_maze
|
||||
%import b_sprites
|
||||
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
@@ -29,7 +30,7 @@ main {
|
||||
sub start() {
|
||||
ubyte benchmark_number
|
||||
|
||||
void cx16.set_screen_mode(3)
|
||||
cx16.set_screen_mode(3)
|
||||
txt.color2(1, 6)
|
||||
txt.clear_screen()
|
||||
|
||||
@@ -74,10 +75,14 @@ main {
|
||||
benchmark_score[benchmark_number] = textelite.bench(120)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("sprites-coroutines-defer")
|
||||
benchmark_score[benchmark_number] = animsprites.benchmark(300)
|
||||
benchmark_number++
|
||||
|
||||
benchmark_names[benchmark_number] = 0
|
||||
benchmark_score[benchmark_number] = 0
|
||||
|
||||
void cx16.set_screen_mode(3)
|
||||
cx16.set_screen_mode(3)
|
||||
txt.uppercase()
|
||||
txt.color2(1, 6)
|
||||
uword final_score
|
||||
@@ -99,7 +104,7 @@ main {
|
||||
|
||||
sub announce_benchmark(str name) {
|
||||
benchmark_names[benchmark_number] = name
|
||||
void cx16.set_screen_mode(3)
|
||||
cx16.set_screen_mode(3)
|
||||
txt.uppercase()
|
||||
txt.color2(1, 6)
|
||||
txt.clear_screen()
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
|
||||
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "2.1.10"
|
||||
kotlin("jvm") version "2.1.20"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package prog8.code
|
||||
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
|
||||
// the automatically generated module where all string literals are interned to:
|
||||
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
|
||||
|
||||
// all automatically generated labels everywhere need to have the same label name prefix:
|
||||
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
|
||||
|
||||
|
||||
/**
|
||||
* Returns the absolute path of the given path,
|
||||
* where links are replaced by the actual directories,
|
||||
* and containing no redundant path elements.
|
||||
* If the path doesn't refer to an existing directory or file on the file system,
|
||||
* it is returned unchanged.
|
||||
*/
|
||||
fun Path.sanitize(): Path {
|
||||
return try {
|
||||
this.toRealPath().normalize()
|
||||
} catch (_: java.nio.file.NoSuchFileException) {
|
||||
this.absolute().normalize()
|
||||
//throw NoSuchFileException(this.toFile(), null, nx.reason).also { it.initCause(nx) }
|
||||
} catch (iox: IOException) {
|
||||
throw FileSystemException(this.toFile()).also { it.initCause(iox) }
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ class CompilationOptions(val output: OutputType,
|
||||
val zpAllowed: List<UIntRange>,
|
||||
val floats: Boolean,
|
||||
val noSysInit: Boolean,
|
||||
val romable: Boolean,
|
||||
val compTarget: ICompilationTarget,
|
||||
// these are set later, based on command line arguments or options in the source code:
|
||||
var loadAddress: UInt,
|
||||
@@ -31,6 +32,7 @@ class CompilationOptions(val output: OutputType,
|
||||
var breakpointCpuInstruction: String? = null,
|
||||
var ignoreFootguns: Boolean = false,
|
||||
var outputDir: Path = Path(""),
|
||||
var quiet: Boolean = false,
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
) {
|
||||
init {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package prog8.code.core
|
||||
|
||||
import java.util.Objects
|
||||
import java.util.*
|
||||
|
||||
enum class BaseDataType {
|
||||
UBYTE, // pass by value 8 bits unsigned
|
||||
@@ -80,6 +80,17 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
override fun hashCode(): Int = Objects.hash(base, sub)
|
||||
|
||||
companion object {
|
||||
|
||||
val UBYTE = DataType(BaseDataType.UBYTE, null)
|
||||
val BYTE = DataType(BaseDataType.BYTE, null)
|
||||
val UWORD = DataType(BaseDataType.UWORD, null)
|
||||
val WORD = DataType(BaseDataType.WORD, null)
|
||||
val LONG = DataType(BaseDataType.LONG, null)
|
||||
val FLOAT = DataType(BaseDataType.FLOAT, null)
|
||||
val BOOL = DataType(BaseDataType.BOOL, null)
|
||||
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE)
|
||||
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null)
|
||||
|
||||
private val simpletypes = mapOf(
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
||||
|
||||
@@ -28,6 +28,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
val libraryPath: Path?
|
||||
val customLauncher: List<String>
|
||||
val additionalAssemblerOptions: String?
|
||||
val defaultOutputType: OutputType
|
||||
|
||||
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
|
||||
fun getFloatAsmBytes(num: Number): String
|
||||
@@ -35,7 +36,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
|
||||
fun convertFloatToBytes(num: Double): List<UByte>
|
||||
fun convertBytesToFloat(bytes: List<UByte>): Double
|
||||
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
|
||||
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean)
|
||||
fun isIOAddress(address: UInt): Boolean
|
||||
|
||||
override fun encodeString(str: String, encoding: Encoding): List<UByte>
|
||||
|
||||
@@ -13,4 +13,6 @@ interface IErrorReporter {
|
||||
}
|
||||
|
||||
fun noErrorForLine(position: Position): Boolean
|
||||
|
||||
fun print_single_error(errormessage: String)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ interface IMemSizer {
|
||||
|
||||
fun memorySize(dt: BaseDataType): Int {
|
||||
if(dt.isPassByRef)
|
||||
return memorySize(DataType.forDt(BaseDataType.UWORD), null) // a pointer size
|
||||
return memorySize(DataType.UWORD, null) // a pointer size
|
||||
try {
|
||||
return memorySize(DataType.forDt(dt), null)
|
||||
} catch (x: NoSuchElementException) {
|
||||
|
||||
@@ -82,7 +82,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
|
||||
}
|
||||
datatype.isFloat -> {
|
||||
if (options.floats) {
|
||||
val memsize = options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
|
||||
val memsize = options.compTarget.memorySize(DataType.FLOAT, null)
|
||||
if(position!=null)
|
||||
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
|
||||
else
|
||||
@@ -154,7 +154,7 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
|
||||
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
|
||||
datatype.isFloat -> {
|
||||
if (options.floats) {
|
||||
options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
|
||||
options.compTarget.memorySize(DataType.FLOAT, null)
|
||||
} else return Err(MemAllocationError("floating point option not enabled"))
|
||||
}
|
||||
else -> throw MemAllocationError("weird dt")
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package prog8.code.core
|
||||
|
||||
import prog8.code.sanitize
|
||||
import prog8.code.source.SourceCode
|
||||
import java.nio.file.InvalidPathException
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||
@@ -13,7 +13,7 @@ data class Position(val file: String, val line: Int, val startCol: Int, val endC
|
||||
if(SourceCode.isLibraryResource(file))
|
||||
return "$file:$line:$startCol:"
|
||||
return try {
|
||||
val path = Path(file).absolute().normalize().toString()
|
||||
val path = Path(file).sanitize().toString()
|
||||
"file://$path:$line:$startCol:"
|
||||
} catch(_: InvalidPathException) {
|
||||
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package prog8.code.source
|
||||
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.sanitize
|
||||
import java.nio.file.Path
|
||||
import java.util.TreeMap
|
||||
import java.util.*
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
|
||||
// Resource caching "filesystem".
|
||||
@@ -22,7 +22,7 @@ object ImportFileSystem {
|
||||
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
|
||||
|
||||
fun getFile(path: Path, isLibrary: Boolean=false): SourceCode {
|
||||
val normalized = path.absolute().normalize()
|
||||
val normalized = path.sanitize()
|
||||
val cached = cache[normalized.toString()]
|
||||
if (cached != null)
|
||||
return cached
|
||||
@@ -48,7 +48,7 @@ object ImportFileSystem {
|
||||
val cached = cache[position.file]
|
||||
if(cached != null)
|
||||
return getLine(cached, position.line)
|
||||
val path = Path(position.file).absolute().normalize()
|
||||
val path = Path(position.file).sanitize()
|
||||
val cached2 = cache[path.toString()]
|
||||
if(cached2 != null)
|
||||
return getLine(cached2, position.line)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8.code.source
|
||||
|
||||
import prog8.code.sanitize
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
import java.text.Normalizer
|
||||
@@ -59,7 +60,7 @@ sealed class SourceCode {
|
||||
private const val LIBRARYFILEPREFIX = "library:"
|
||||
private const val STRINGSOURCEPREFIX = "string:"
|
||||
val curdir: Path = Path(".").absolute()
|
||||
fun relative(path: Path): Path = curdir.relativize(path.absolute())
|
||||
fun relative(path: Path): Path = curdir.relativize(path.sanitize())
|
||||
fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
|
||||
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
|
||||
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
|
||||
|
||||
@@ -12,6 +12,7 @@ class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "c128"
|
||||
@@ -48,17 +49,21 @@ class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The c128 target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
println("\nStarting C-128 emulator x128...")
|
||||
if(!quiet)
|
||||
println("\nStarting C-128 emulator x128...")
|
||||
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "c64"
|
||||
@@ -52,18 +53,22 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The c64 target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
for(emulator in listOf("x64sc", "x64")) {
|
||||
println("\nStarting C-64 emulator $emulator...")
|
||||
if(!quiet)
|
||||
println("\nStarting C-64 emulator $emulator...")
|
||||
|
||||
val viceMonlist = viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
process=processb.start()
|
||||
|
||||
@@ -24,6 +24,7 @@ class ConfigFileTarget(
|
||||
override val BSSHIGHRAM_END: UInt,
|
||||
override val BSSGOLDENRAM_START: UInt,
|
||||
override val BSSGOLDENRAM_END: UInt,
|
||||
override val defaultOutputType: OutputType,
|
||||
override val libraryPath: Path,
|
||||
override val customLauncher: List<String>,
|
||||
override val additionalAssemblerOptions: String?,
|
||||
@@ -108,7 +109,10 @@ class ConfigFileTarget(
|
||||
(customLauncherStr+"\n").lines().map { it.trimEnd() }
|
||||
else emptyList()
|
||||
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
|
||||
val assemblerOptions = if(assemblerOptionsStr.isBlank()) null else assemblerOptionsStr
|
||||
val assemblerOptions = assemblerOptionsStr.ifBlank { null }
|
||||
|
||||
val outputTypeString = props.getProperty("output_type", "PRG")
|
||||
val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
|
||||
|
||||
return ConfigFileTarget(
|
||||
configfile.nameWithoutExtension,
|
||||
@@ -121,6 +125,7 @@ class ConfigFileTarget(
|
||||
props.getInteger("bss_highram_end"),
|
||||
props.getInteger("bss_goldenram_start"),
|
||||
props.getInteger("bss_goldenram_end"),
|
||||
defaultOutputType,
|
||||
libraryPath,
|
||||
customLauncher,
|
||||
assemblerOptions,
|
||||
@@ -148,7 +153,7 @@ class ConfigFileTarget(
|
||||
override fun getFloatAsmBytes(num: Number) = TODO("floats")
|
||||
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats")
|
||||
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats")
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.")
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "cx16"
|
||||
@@ -48,7 +49,7 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
val emulator: String
|
||||
val extraArgs: List<String>
|
||||
|
||||
@@ -67,9 +68,13 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
|
||||
}
|
||||
}
|
||||
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
if(!quiet)
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
|
||||
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
|
||||
val process: Process = processb.start()
|
||||
process.waitFor()
|
||||
|
||||
@@ -18,8 +18,8 @@ internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
||||
else return 2 // treat it as the size to store a string pointer
|
||||
return 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 {
|
||||
|
||||
@@ -12,6 +12,7 @@ class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "pet32"
|
||||
@@ -47,17 +48,21 @@ class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
|
||||
return m5.toDouble()
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(selectedEmulator!=1) {
|
||||
System.err.println("The pet target only supports the main emulator (Vice).")
|
||||
return
|
||||
}
|
||||
|
||||
println("\nStarting PET emulator...")
|
||||
if(!quiet)
|
||||
println("\nStarting PET emulator...")
|
||||
|
||||
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
|
||||
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val processb = ProcessBuilder(cmdline)
|
||||
if(!quiet)
|
||||
processb.inheritIO()
|
||||
val process=processb.start()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
override val libraryPath = null
|
||||
override val customLauncher: List<String> = emptyList()
|
||||
override val additionalAssemblerOptions = null
|
||||
override val defaultOutputType = OutputType.PRG
|
||||
|
||||
companion object {
|
||||
const val NAME = "virtual"
|
||||
@@ -21,8 +22,8 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
|
||||
override val cpu = CpuType.VIRTUAL
|
||||
|
||||
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE.toDouble()
|
||||
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE
|
||||
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE
|
||||
override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE
|
||||
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
|
||||
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
|
||||
@@ -39,7 +40,7 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
// little endian binary representation
|
||||
val bits = num.toDouble().toBits().toULong()
|
||||
val hexStr = bits.toString(16).padStart(16, '0')
|
||||
val parts = hexStr.chunked(2).map { "\$" + it }
|
||||
val parts = hexStr.chunked(2).map { "$$it" }
|
||||
return parts.joinToString(", ")
|
||||
}
|
||||
|
||||
@@ -63,19 +64,21 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
|
||||
}
|
||||
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
|
||||
println("\nStarting Virtual Machine...")
|
||||
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
|
||||
if(!quiet)
|
||||
println("\nStarting Virtual Machine...")
|
||||
|
||||
// to not have external module dependencies in our own module, we launch the virtual machine via reflection
|
||||
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
|
||||
val filename = programNameWithPath.name
|
||||
if(programNameWithPath.isReadable()) {
|
||||
vm.runProgram(programNameWithPath.readText())
|
||||
vm.runProgram(programNameWithPath.readText(), quiet)
|
||||
} else {
|
||||
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
|
||||
if(withExt.isReadable())
|
||||
vm.runProgram(withExt.readText())
|
||||
vm.runProgram(withExt.readText(), quiet)
|
||||
else
|
||||
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
|
||||
throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,8 +101,8 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
}
|
||||
}
|
||||
else if (dt.isString) {
|
||||
if(numElements!=null) return numElements // treat it as the size of the given string with the length
|
||||
else return 2 // treat it as the size to store a string pointer
|
||||
return 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 {
|
||||
@@ -115,7 +118,7 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
|
||||
|
||||
interface IVirtualMachineRunner {
|
||||
fun runProgram(irSource: String)
|
||||
fun runProgram(irSource: String, quiet: Boolean)
|
||||
}
|
||||
|
||||
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
|
||||
|
||||
@@ -26,7 +26,7 @@ object AtasciiEncoding {
|
||||
'▖',
|
||||
|
||||
// $10
|
||||
'♣',
|
||||
'♣',
|
||||
'┌',
|
||||
'─',
|
||||
'┼',
|
||||
@@ -62,7 +62,7 @@ object AtasciiEncoding {
|
||||
'/',
|
||||
|
||||
// $30
|
||||
'0',
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
'3',
|
||||
@@ -80,7 +80,7 @@ object AtasciiEncoding {
|
||||
'?',
|
||||
|
||||
// $40
|
||||
'@',
|
||||
'@',
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
|
||||
@@ -85,12 +85,12 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
free.remove((4+reg*2).toUInt())
|
||||
free.remove((5+reg*2).toUInt())
|
||||
}
|
||||
|
||||
@@ -57,12 +57,12 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
|
||||
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
|
||||
for(reg in 0..15) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package prog8.code.target.zp
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Zeropage
|
||||
import prog8.code.core.ZeropageType
|
||||
|
||||
class ConfigurableZeropage(
|
||||
override val SCRATCH_B1: UInt, // temp storage for a single byte
|
||||
@@ -46,12 +49,12 @@ class ConfigurableZeropage(
|
||||
for(reg in 0..15) {
|
||||
val address = virtualRegistersStart + (2*reg).toUInt()
|
||||
if(address<=0xffu) {
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH
|
||||
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.UWORD, 2) // cx16.r0 .. cx16.r15
|
||||
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.WORD, 2) // cx16.r0s .. cx16.r15s
|
||||
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
|
||||
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
|
||||
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
|
||||
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
implementation(project(":simpleAst"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import com.github.michaelbull.result.fold
|
||||
import prog8.code.StNode
|
||||
import prog8.code.StNodeType
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.*
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.source.ImportFileSystem
|
||||
@@ -12,7 +9,6 @@ import prog8.code.source.SourceCode
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.codegen.cpu6502.assignment.*
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
import kotlin.io.path.writeLines
|
||||
|
||||
|
||||
@@ -39,7 +35,7 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
when(node) {
|
||||
is PtAsmSub, is PtSub -> node.name = "p8s_${node.name}"
|
||||
is PtBlock -> node.name = "p8b_${node.name}"
|
||||
is PtLabel -> if(!node.name.startsWith(PtLabel.GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // don't prefix autogenerated labels
|
||||
is PtLabel -> if(!node.name.startsWith(GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // don't prefix autogenerated labels
|
||||
is PtConstant -> node.name = "p8c_${node.name}"
|
||||
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> node.name = "p8v_${node.name}"
|
||||
}
|
||||
@@ -90,9 +86,35 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
node.children.forEach { prefixSymbols(it) }
|
||||
}
|
||||
|
||||
fun maybePrefixFunctionCallsAndIdentifierReferences(node: PtNode) {
|
||||
if(node is PtFunctionCall) {
|
||||
// function calls to subroutines defined in a block that does NOT have NoSymbolPrefixing, still have to be prefixed at the call site
|
||||
val stNode = st.lookup(node.name)!!
|
||||
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
|
||||
val index = node.parent.children.indexOf(node)
|
||||
functionCallsToPrefix += node.parent to index
|
||||
}
|
||||
}
|
||||
else if (node is PtIdentifier) {
|
||||
// identifier references to things defined in a block that does NOT have NoSymbolPrefixing, still have to be prefixed at the referencing point
|
||||
var lookupName = node.name
|
||||
if(node.type.isSplitWordArray && (lookupName.endsWith("_lsb") || lookupName.endsWith("_msb"))) {
|
||||
lookupName = lookupName.dropLast(4)
|
||||
}
|
||||
val stNode = st.lookup(lookupName) ?: throw AssemblyError("unknown identifier $node")
|
||||
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
|
||||
val index = node.parent.children.indexOf(node)
|
||||
nodesToPrefix += node.parent to index
|
||||
}
|
||||
}
|
||||
node.children.forEach { maybePrefixFunctionCallsAndIdentifierReferences(it) }
|
||||
}
|
||||
|
||||
program.allBlocks().forEach { block ->
|
||||
if (!block.options.noSymbolPrefixing) {
|
||||
prefixSymbols(block)
|
||||
} else {
|
||||
maybePrefixFunctionCallsAndIdentifierReferences(block)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +131,9 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
functionCallsToPrefix.reversed().forEach { (parent, index) ->
|
||||
val node = parent.children[index]
|
||||
if(node is PtFunctionCall) {
|
||||
parent.children[index] = node.prefix(parent)
|
||||
val prefixedName = PtIdentifier(node.name, DataType.UNDEFINED, Position.DUMMY).prefix(parent, st)
|
||||
val prefixedNode = node.withNewName(prefixedName.name)
|
||||
parent.children[index] = prefixedNode
|
||||
} else {
|
||||
throw AssemblyError("expected PtFunctionCall")
|
||||
}
|
||||
@@ -121,14 +145,14 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
|
||||
private fun prefixScopedName(name: String, type: Char): String {
|
||||
if('.' !in name) {
|
||||
if(name.startsWith(PtLabel.GENERATED_LABEL_PREFIX))
|
||||
if(name.startsWith(GENERATED_LABEL_PREFIX))
|
||||
return name
|
||||
return "p8${type}_$name"
|
||||
}
|
||||
val parts = name.split('.')
|
||||
val firstPrefixed = "p8b_${parts[0]}"
|
||||
val lastPart = parts.last()
|
||||
val lastPrefixed = if(lastPart.startsWith(PtLabel.GENERATED_LABEL_PREFIX)) lastPart else "p8${type}_$lastPart"
|
||||
val lastPrefixed = if(lastPart.startsWith(GENERATED_LABEL_PREFIX)) lastPart else "p8${type}_$lastPart"
|
||||
// the parts in between are assumed to be subroutine scopes.
|
||||
val inbetweenPrefixed = parts.drop(1).dropLast(1).map{ "p8s_$it" }
|
||||
val prefixed = listOf(firstPrefixed) + inbetweenPrefixed + listOf(lastPrefixed)
|
||||
@@ -169,9 +193,13 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
|
||||
else this
|
||||
}
|
||||
|
||||
private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall {
|
||||
val newName = prefixScopedName(name, 's')
|
||||
val call = PtFunctionCall(newName, void, type, position)
|
||||
//private fun PtFunctionCall.prefix(targetType: Char): PtFunctionCall {
|
||||
// val newName = prefixScopedName(name, targetType)
|
||||
// return this.withNewName(newName)
|
||||
//}
|
||||
|
||||
private fun PtFunctionCall.withNewName(name: String): PtFunctionCall {
|
||||
val call = PtFunctionCall(name, void, type, position)
|
||||
call.children.addAll(children)
|
||||
call.children.forEach { it.parent = call }
|
||||
call.parent = parent
|
||||
@@ -237,7 +265,9 @@ class AsmGen6502Internal (
|
||||
assembly.clear()
|
||||
loopEndLabels.clear()
|
||||
|
||||
println("Generating assembly code... ")
|
||||
if(!options.quiet)
|
||||
println("Generating assembly code... ")
|
||||
|
||||
programGen.generate()
|
||||
|
||||
if(errors.noErrors()) {
|
||||
@@ -408,6 +438,7 @@ class AsmGen6502Internal (
|
||||
BaseDataType.UBYTE to 0,
|
||||
BaseDataType.WORD to 0,
|
||||
BaseDataType.UWORD to 0,
|
||||
BaseDataType.LONG to 0,
|
||||
BaseDataType.FLOAT to 0
|
||||
)
|
||||
|
||||
@@ -592,7 +623,7 @@ class AsmGen6502Internal (
|
||||
is PtJump -> {
|
||||
val target = getJumpTarget(stmt)
|
||||
require(!target.needsExpressionEvaluation)
|
||||
jmp(target.asmLabel, target.indirect)
|
||||
jmp(target.asmLabel, target.indirect, target.indexedX)
|
||||
}
|
||||
is PtLabel -> translate(stmt)
|
||||
is PtConditionalBranch -> translate(stmt)
|
||||
@@ -687,8 +718,8 @@ class AsmGen6502Internal (
|
||||
RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister(), target.datatype.isSigned, true)
|
||||
RegisterOrPair.AX,
|
||||
RegisterOrPair.AY,
|
||||
RegisterOrPair.XY,
|
||||
in Cx16VirtualRegisters -> assignmentAsmGen.assignRegisterpairWord(target, reg)
|
||||
RegisterOrPair.XY -> assignmentAsmGen.assignRegisterpairWord(target, reg)
|
||||
in Cx16VirtualRegisters -> assignmentAsmGen.assignVirtualRegister(target, reg)
|
||||
RegisterOrPair.FAC1 -> assignmentAsmGen.assignFAC1float(target)
|
||||
RegisterOrPair.FAC2 -> assignmentAsmGen.assignFAC2float(target)
|
||||
else -> throw AssemblyError("invalid register")
|
||||
@@ -727,7 +758,7 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
assignRegister(RegisterOrPair.A, target)
|
||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, target.datatype.isSigned, false)
|
||||
}
|
||||
target.datatype.isWord || target.datatype.isPassByRef -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||
@@ -1035,7 +1066,7 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
class JumpTarget(val asmLabel: String, val indirect: Boolean, val needsExpressionEvaluation: Boolean)
|
||||
class JumpTarget(val asmLabel: String, val indirect: Boolean, val indexedX: Boolean, val needsExpressionEvaluation: Boolean)
|
||||
|
||||
internal fun getJumpTarget(jump: PtJump, evaluateAddressExpression: Boolean = true): JumpTarget {
|
||||
val ident = jump.target as? PtIdentifier
|
||||
@@ -1043,45 +1074,60 @@ $repeatLabel""")
|
||||
// can be a label, or a pointer variable
|
||||
val symbol = symbolTable.lookup(ident.name)
|
||||
return if(symbol?.type in arrayOf(StNodeType.STATICVAR, StNodeType.MEMVAR, StNodeType.CONSTANT))
|
||||
JumpTarget(asmSymbolName(ident), true, false) // indirect jump if the jump symbol is a variable
|
||||
JumpTarget(asmSymbolName(ident), true, false,false) // indirect jump if the jump symbol is a variable
|
||||
else
|
||||
JumpTarget(asmSymbolName(ident), false, false)
|
||||
JumpTarget(asmSymbolName(ident), false, false,false)
|
||||
}
|
||||
val addr = jump.target.asConstInteger()
|
||||
if(addr!=null)
|
||||
return JumpTarget(addr.toHex(), false, false)
|
||||
return JumpTarget(addr.toHex(), false, false,false)
|
||||
else {
|
||||
if(evaluateAddressExpression) {
|
||||
val arrayIdx = jump.target as? PtArrayIndexer
|
||||
if(arrayIdx!=null && !arrayIdx.splitWords) {
|
||||
// if the jump target is an address in a non-split array (like a jump table of only pointers),
|
||||
// more optimal assembly can be generated using JMP address,X
|
||||
assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
|
||||
out(" asl a | tax")
|
||||
return JumpTarget(asmSymbolName(arrayIdx.variable), true, true, false)
|
||||
}
|
||||
// we can do the address evaluation right now and just use a temporary pointer variable
|
||||
assignExpressionToVariable(jump.target, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UWORD))
|
||||
return JumpTarget("P8ZP_SCRATCH_W1", true, false)
|
||||
assignExpressionToVariable(jump.target, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
return JumpTarget("P8ZP_SCRATCH_W1", true, false,false)
|
||||
} else {
|
||||
return JumpTarget("PROG8_JUMP_TARGET_IS_UNEVALUATED_ADDRESS_EXPRESSION", true, true)
|
||||
return JumpTarget("PROG8_JUMP_TARGET_IS_UNEVALUATED_ADDRESS_EXPRESSION", true, false,true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(ret: PtReturn) {
|
||||
val returnvalue = ret.children.singleOrNull()
|
||||
val sub = ret.definingSub()!!
|
||||
val returnRegs = sub.returnsWhatWhere()
|
||||
|
||||
if(returnvalue!=null) {
|
||||
val sub = ret.definingSub()!!
|
||||
val returnReg = sub.returnsWhatWhere().single()
|
||||
if (sub.returns.single().isNumericOrBool==true) {
|
||||
assignExpressionToRegister(returnvalue as PtExpression, returnReg.first.registerOrPair!!)
|
||||
if (sub.returns.single().isNumericOrBool) {
|
||||
assignExpressionToRegister(returnvalue as PtExpression, returnRegs.single().first.registerOrPair!!)
|
||||
}
|
||||
else {
|
||||
// all else take its address and assign that also to AY register pair
|
||||
val addrofValue = PtAddressOf(returnvalue.position)
|
||||
addrofValue.add(returnvalue as PtIdentifier)
|
||||
addrofValue.parent = ret.parent
|
||||
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnReg.first.registerOrPair!!, false)
|
||||
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnRegs.single().first.registerOrPair!!, false)
|
||||
}
|
||||
}
|
||||
else if(ret.children.size>1) {
|
||||
// multi-value returns are passed throug cx16.R15 down to R0 (allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
val registersReverseOrder = Cx16VirtualRegisters.reversed()
|
||||
ret.children.zip(registersReverseOrder).forEach { (value, register) ->
|
||||
assignExpressionToRegister(value as PtExpression, register)
|
||||
// note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
|
||||
// (this allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
// to avoid register clobbering, assign the first return value last in row.
|
||||
val assigns = ret.children.zip(returnRegs).map { it.first to it.second }
|
||||
assigns.drop(1).forEach {
|
||||
val tgt = AsmAssignTarget(TargetStorageKind.REGISTER, this, it.second.second, null, it.first.position, register = it.second.first.registerOrPair!!)
|
||||
assignExpressionTo(it.first as PtExpression, tgt)
|
||||
}
|
||||
assigns.first().also {
|
||||
assignExpressionToRegister(it.first as PtExpression, it.second.first.registerOrPair!!)
|
||||
}
|
||||
}
|
||||
out(" rts")
|
||||
@@ -1102,8 +1148,8 @@ $repeatLabel""")
|
||||
val sourcePath = Path(incbin.definingBlock()!!.source.origin)
|
||||
val includedPath = sourcePath.resolveSibling(incbin.file)
|
||||
val pathForAssembler = options.outputDir // #54: 64tass needs the path *relative to the .asm file*
|
||||
.absolute()
|
||||
.relativize(includedPath.absolute())
|
||||
.sanitize()
|
||||
.relativize(includedPath.sanitize())
|
||||
.normalize() // avoid assembler warnings (-Wportable; only some, not all)
|
||||
.toString().replace('\\', '/')
|
||||
out(" .binary \"$pathForAssembler\" $offset $length")
|
||||
@@ -1169,10 +1215,14 @@ $repeatLabel""")
|
||||
|
||||
internal fun isZpVar(variable: PtIdentifier): Boolean = allocator.isZpVar(variable.name)
|
||||
|
||||
internal fun jmp(asmLabel: String, indirect: Boolean=false) {
|
||||
internal fun jmp(asmLabel: String, indirect: Boolean=false, indexedX: Boolean=false) {
|
||||
if(indirect) {
|
||||
out(" jmp ($asmLabel)")
|
||||
if(indexedX)
|
||||
out(" jmp ($asmLabel,x)")
|
||||
else
|
||||
out(" jmp ($asmLabel)")
|
||||
} else {
|
||||
require(!indexedX) { "indexedX only allowed for indirect jumps" }
|
||||
if (isTargetCpu(CpuType.CPU65C02))
|
||||
out(" bra $asmLabel") // note: 64tass will convert this automatically to a jmp if the relative distance is too large
|
||||
else
|
||||
@@ -1259,13 +1309,13 @@ $repeatLabel""")
|
||||
val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second)
|
||||
if(saveA) out(" pha")
|
||||
if(ptrAndIndex.second.isSimple()) {
|
||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||
if(saveA) out(" pla")
|
||||
out(" sta (P8ZP_SCRATCH_W2),y")
|
||||
} else {
|
||||
pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second)
|
||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, true)
|
||||
if(saveA) out(" pla")
|
||||
out(" sta (P8ZP_SCRATCH_W2),y")
|
||||
@@ -1302,12 +1352,12 @@ $repeatLabel""")
|
||||
} else {
|
||||
// copy the pointer var to zp first
|
||||
if(ptrAndIndex.second.isSimple()) {
|
||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
|
||||
out(" lda (P8ZP_SCRATCH_W2),y")
|
||||
} else {
|
||||
pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second)
|
||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
out(" lda (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
@@ -1342,13 +1392,14 @@ $repeatLabel""")
|
||||
if(pointervar!=null && isZpVar(pointervar)) {
|
||||
val varname = asmSymbolName(pointervar)
|
||||
out(" ldy #${256-constOffset} ; negative offset $constOffset")
|
||||
out(" dec $varname+1 | sta ($varname),y | inc $varname+1") // temporarily make MSB 1 less
|
||||
out(" dec $varname+1 | sta ($varname),y | inc $varname+1") // temporarily make MSB 1 less to be able to use the negative Y offset
|
||||
return true
|
||||
} else {
|
||||
// copy the pointer var to zp first
|
||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
||||
out(" pha")
|
||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
out(" ldy #${256-constOffset} ; negative offset $constOffset")
|
||||
out(" dec P8ZP_SCRATCH_W2+1 | sta (P8ZP_SCRATCH_W2),y | inc P8ZP_SCRATCH_W2+1") // temporarily make MSB 1 less
|
||||
out(" dec P8ZP_SCRATCH_W2+1 | pla | sta (P8ZP_SCRATCH_W2),y") // temporarily make MSB 1 less to be able to use the negative Y offset
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1372,13 +1423,13 @@ $repeatLabel""")
|
||||
if(pointervar!=null && isZpVar(pointervar)) {
|
||||
val varname = asmSymbolName(pointervar)
|
||||
out(" ldy #${256-constOffset} ; negative offset $constOffset")
|
||||
out(" dec $varname+1 | lda ($varname),y | inc $varname+1") // temporarily make MSB 1 less
|
||||
out(" dec $varname+1 | lda ($varname),y | inc $varname+1") // temporarily make MSB 1 less to be able to use the negative Y offset
|
||||
return true
|
||||
} else {
|
||||
// copy the pointer var to zp first
|
||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
||||
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
out(" ldy #${256-constOffset} ; negative offset $constOffset")
|
||||
out(" dec P8ZP_SCRATCH_W2+1 | lda (P8ZP_SCRATCH_W2),y | inc P8ZP_SCRATCH_W2+1") // temporarily make MSB 1 less
|
||||
out(" dec P8ZP_SCRATCH_W2+1 | lda (P8ZP_SCRATCH_W2),y") // temporarily make MSB 1 less to be able to use the negative Y offset
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1398,22 +1449,22 @@ $repeatLabel""")
|
||||
|
||||
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, rightVarName, DataType.forDt(BaseDataType.UBYTE))
|
||||
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(BaseDataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, rightVarName, DataType.forDt(BaseDataType.UBYTE))
|
||||
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
|
||||
out(" pla")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignWordOperandsToAYAndVar(left: PtExpression, right: PtExpression, rightVarname: String) {
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, rightVarname, DataType.forDt(BaseDataType.UWORD))
|
||||
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(BaseDataType.UWORD, left)
|
||||
assignExpressionToVariable(right, rightVarname, DataType.forDt(BaseDataType.UWORD))
|
||||
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
@@ -1422,7 +1473,7 @@ $repeatLabel""")
|
||||
internal fun translateDirectMemReadExpressionToRegA(expr: PtMemoryByte) {
|
||||
|
||||
fun assignViaExprEval() {
|
||||
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
||||
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
if (isTargetCpu(CpuType.CPU65C02)) {
|
||||
out(" lda (P8ZP_SCRATCH_W2)")
|
||||
} else {
|
||||
@@ -1495,7 +1546,7 @@ $repeatLabel""")
|
||||
|
||||
internal fun makeLabel(postfix: String): String {
|
||||
generatedLabelSequenceNumber++
|
||||
return "${PtLabel.GENERATED_LABEL_PREFIX}${generatedLabelSequenceNumber}_$postfix"
|
||||
return "$GENERATED_LABEL_PREFIX${generatedLabelSequenceNumber}_$postfix"
|
||||
}
|
||||
|
||||
internal fun assignConstFloatToPointerAY(number: PtNumber) {
|
||||
@@ -1618,7 +1669,7 @@ $repeatLabel""")
|
||||
is PtIdentifier -> equalf(asmVariableName(left), asmVariableName(right))
|
||||
is PtNumber -> equalf(asmVariableName(left), allocator.getFloatAsmConst(right.number))
|
||||
else -> {
|
||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
|
||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
|
||||
equalf(asmVariableName(left), subroutineFloatEvalResultVar1)
|
||||
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
|
||||
}
|
||||
@@ -1628,7 +1679,7 @@ $repeatLabel""")
|
||||
is PtIdentifier -> equalf(left, asmVariableName(right))
|
||||
is PtNumber -> equalf(left, allocator.getFloatAsmConst(right.number))
|
||||
else -> {
|
||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
|
||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
|
||||
equalf(left, subroutineFloatEvalResultVar1)
|
||||
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
|
||||
}
|
||||
@@ -1663,7 +1714,7 @@ $repeatLabel""")
|
||||
is PtIdentifier -> lessf(asmVariableName(left), asmVariableName(right))
|
||||
is PtNumber -> lessf(asmVariableName(left), allocator.getFloatAsmConst(right.number))
|
||||
else -> {
|
||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
|
||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
|
||||
lessf(asmVariableName(left), subroutineFloatEvalResultVar1)
|
||||
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
|
||||
}
|
||||
@@ -1673,7 +1724,7 @@ $repeatLabel""")
|
||||
is PtIdentifier -> lessf(left, asmVariableName(right))
|
||||
is PtNumber -> lessf(left, allocator.getFloatAsmConst(right.number))
|
||||
else -> {
|
||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
|
||||
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
|
||||
lessf(left, subroutineFloatEvalResultVar1)
|
||||
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
|
||||
}
|
||||
@@ -1704,6 +1755,16 @@ $repeatLabel""")
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun romableError(problem: String, pos: Position, assemblerShouldFail: Boolean = true) {
|
||||
if(options.romable) {
|
||||
// until the code generation can provide an alternative, we have to report about code generated that is incompatible with ROMable code mode...
|
||||
errors.warn("problem for ROMable code: $problem", pos)
|
||||
if(assemblerShouldFail) {
|
||||
out(" .error \"ROMable code selected but incompatible code was generated: $problem $pos\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.GENERATED_LABEL_PREFIX
|
||||
import prog8.code.StConstant
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtLabel
|
||||
import prog8.code.core.ICompilationTarget
|
||||
|
||||
|
||||
@@ -362,7 +362,7 @@ or *_afterif labels.
|
||||
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
|
||||
*/
|
||||
|
||||
val autoLabelPrefix = PtLabel.GENERATED_LABEL_PREFIX
|
||||
val autoLabelPrefix = GENERATED_LABEL_PREFIX
|
||||
if(first=="beq +" && second=="lda #1" && third=="+") {
|
||||
if((fourth.startsWith("beq $autoLabelPrefix") || fourth.startsWith("bne $autoLabelPrefix")) &&
|
||||
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
|
||||
@@ -459,7 +459,7 @@ private fun optimizeStoreLoadSame(
|
||||
return mods
|
||||
}
|
||||
|
||||
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""")
|
||||
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_.$]*)""")
|
||||
|
||||
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
|
||||
// try to get the constant value address, could return null if it's a symbol instead
|
||||
@@ -687,18 +687,20 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
|
||||
// phy + ldy + pla -> tya + ldy
|
||||
// phx + ldx + pla -> txa + ldx
|
||||
// pha + lda + pla -> nop
|
||||
if(first=="phy" && second.startsWith("ldy ") && third=="pla") {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
mods.add(Modification(lines[1].index, false, " tya"))
|
||||
}
|
||||
else if(first=="phx" && second.startsWith("ldx ") && third=="pla") {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
mods.add(Modification(lines[1].index, false, " txa"))
|
||||
}
|
||||
else if(first=="pha" && second.startsWith("lda ") && third=="pla") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
when (first) {
|
||||
"phy" if second.startsWith("ldy ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
mods.add(Modification(lines[1].index, false, " tya"))
|
||||
}
|
||||
"phx" if second.startsWith("ldx ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
mods.add(Modification(lines[1].index, false, " txa"))
|
||||
}
|
||||
"pha" if second.startsWith("lda ") && third=="pla" -> {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.PtLabel
|
||||
import prog8.code.core.*
|
||||
import prog8.code.GENERATED_LABEL_PREFIX
|
||||
import prog8.code.IAssemblyProgram
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.OutputType
|
||||
import prog8.code.target.C128Target
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.PETTarget
|
||||
@@ -45,7 +49,8 @@ internal class AssemblyProgram(
|
||||
|
||||
command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
println("\nCreating prg for target ${compTarget.name}.")
|
||||
if(!options.quiet)
|
||||
println("\nCreating prg for target ${compTarget.name}.")
|
||||
}
|
||||
OutputType.XEX -> {
|
||||
// Atari800XL .xex generation.
|
||||
@@ -66,7 +71,8 @@ internal class AssemblyProgram(
|
||||
|
||||
command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
println("\nCreating xex for target ${compTarget.name}.")
|
||||
if(!options.quiet)
|
||||
println("\nCreating xex for target ${compTarget.name}.")
|
||||
}
|
||||
OutputType.RAW -> {
|
||||
// Neo6502/headerless raw program generation.
|
||||
@@ -86,7 +92,8 @@ internal class AssemblyProgram(
|
||||
|
||||
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
|
||||
assemblerCommand = command
|
||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||
if(!options.quiet)
|
||||
println("\nCreating raw binary for target ${compTarget.name}.")
|
||||
}
|
||||
OutputType.LIBRARY -> {
|
||||
// CBM machines library (.bin) generation (with or without 2 byte load address header depending on the compilation target machine)
|
||||
@@ -106,10 +113,12 @@ internal class AssemblyProgram(
|
||||
command.add("--list=$listFile")
|
||||
|
||||
if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) {
|
||||
println("\nCreating binary library file with header for target ${compTarget.name}.")
|
||||
if(!options.quiet)
|
||||
println("\nCreating binary library file with header for target ${compTarget.name}.")
|
||||
command.add("--cbm-prg")
|
||||
} else {
|
||||
println("\nCreating binary library file without header for target ${compTarget.name}.")
|
||||
if(!options.quiet)
|
||||
println("\nCreating binary library file without header for target ${compTarget.name}.")
|
||||
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
|
||||
}
|
||||
|
||||
@@ -121,8 +130,11 @@ internal class AssemblyProgram(
|
||||
if(options.compTarget.additionalAssemblerOptions!=null)
|
||||
assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
|
||||
|
||||
val proc = ProcessBuilder(assemblerCommand).inheritIO().start()
|
||||
val result = proc.waitFor()
|
||||
val proc = ProcessBuilder(assemblerCommand)
|
||||
if(!options.quiet)
|
||||
proc.inheritIO()
|
||||
val process = proc.start()
|
||||
val result = process.waitFor()
|
||||
if (result == 0) {
|
||||
removeGeneratedLabelsFromMonlist()
|
||||
generateBreakpointList()
|
||||
@@ -131,7 +143,7 @@ internal class AssemblyProgram(
|
||||
}
|
||||
|
||||
private fun removeGeneratedLabelsFromMonlist() {
|
||||
val pattern = Regex("""al (\w+) \S+${PtLabel.GENERATED_LABEL_PREFIX}.+?""")
|
||||
val pattern = Regex("""al (\w+) \S+$GENERATED_LABEL_PREFIX.+?""")
|
||||
val lines = viceMonListFile.toFile().readLines()
|
||||
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
||||
for (line in lines) {
|
||||
@@ -148,7 +160,7 @@ internal class AssemblyProgram(
|
||||
for (line in viceMonListFile.toFile().readLines()) {
|
||||
val match = pattern.matchEntire(line)
|
||||
if (match != null)
|
||||
breakpoints.add("break \$" + match.groupValues[1])
|
||||
breakpoints.add("break $" + match.groupValues[1])
|
||||
}
|
||||
val num = breakpoints.size
|
||||
breakpoints.add(0, "; breakpoint list now follows")
|
||||
|
||||
@@ -55,7 +55,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
memread.parent = fcall
|
||||
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.position, memory=memread)
|
||||
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.position, memory=memread)
|
||||
asmgen.assignExpressionTo(fcall.args[1], memtarget)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
@@ -107,8 +107,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr prog8_math.divmod_ub_asm")
|
||||
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
||||
val var3name = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[2].position, var2name)
|
||||
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[3].position, var3name)
|
||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
|
||||
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[3].position, var3name)
|
||||
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false, false)
|
||||
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false, false)
|
||||
}
|
||||
@@ -120,7 +120,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
// output: P8ZP_SCRATCH_W2 in ZP: 16-bit remainder, A/Y: 16 bit division result
|
||||
asmgen.out(" jsr prog8_math.divmod_uw_asm")
|
||||
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
|
||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[2].position, var2name)
|
||||
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
|
||||
val remainderVar = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
|
||||
assignAsmGen.assignRegisterpairWord(divisionTarget, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
@@ -196,7 +196,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
return
|
||||
}
|
||||
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.forDt(BaseDataType.UWORD)) // jump address
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD) // jump address
|
||||
asmgen.out("""
|
||||
; push a return address so the jmp becomes indirect jsr
|
||||
lda #>((+)-1)
|
||||
@@ -227,6 +227,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
.word ${constAddress.toHex()}
|
||||
.byte $constBank""")
|
||||
} else {
|
||||
if(asmgen.options.romable)
|
||||
TODO("no code for non-const callfar (jsrfar) yet that's usable in ROM ${fcall.position}")
|
||||
// self-modifying code: set jsrfar arguments
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
|
||||
asmgen.out(" sta (++)+0")
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
||||
@@ -283,6 +286,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
.word ${constAddress.toHex()}
|
||||
.byte $constBank""")
|
||||
} else {
|
||||
if(asmgen.options.romable)
|
||||
TODO("no code for non-const callfar2 (jsrfar) yet that's usable in ROM ${fcall.position}")
|
||||
// self-modifying code: set jsrfar arguments
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
|
||||
asmgen.out(" sta (++)+0")
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
|
||||
@@ -346,7 +352,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
cmp ${asmgen.asmVariableName(arg2)}
|
||||
+""")
|
||||
}
|
||||
is PtBool -> TODO("word compare against bool")
|
||||
is PtBool -> TODO("word compare against bool ${arg2.position}")
|
||||
is PtNumber -> {
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
@@ -374,11 +380,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||
val name = (fcall.args[0] as PtString).value
|
||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
||||
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.forDt(BaseDataType.UWORD), fcall.position)
|
||||
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
|
||||
val addressOf = PtAddressOf(fcall.position)
|
||||
addressOf.add(slabname)
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.forDt(BaseDataType.UWORD), expression = addressOf)
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
|
||||
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||
val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position)
|
||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||
@@ -411,12 +417,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
is PtArrayIndexer -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #\$80 |+ | sta ${varname},x")
|
||||
asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #$80 |+ | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
|
||||
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #$80 |+ | sta ${number.toHex()}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
||||
@@ -424,7 +430,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
||||
asmgen.out(" lda $variable | lsr a | bcc + | ora #$80 |+ | sta $variable")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
@@ -435,13 +441,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #\$80 | sta ${varname}_msb,x |+ ")
|
||||
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #$80 | sta ${varname}_msb,x |+ ")
|
||||
else
|
||||
asmgen.out(" lsr ${varname}+1,x | ror ${varname},x | bcc + | lda ${varname}+1,x | ora #\$80 | sta ${varname}+1,x |+ ")
|
||||
asmgen.out(" lsr ${varname}+1,x | ror ${varname},x | bcc + | lda ${varname}+1,x | ora #$80 | sta ${varname}+1,x |+ ")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ")
|
||||
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #$80 | sta $variable+1 |+ ")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
@@ -463,29 +469,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" ror ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" ror ${number.toHex()}")
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.out(" php")
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
plp
|
||||
+ ror ${'$'}ffff,x ; modified""")
|
||||
} else {
|
||||
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
if(!what.address.isSimple()) asmgen.out(" plp")
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ ror ${'$'}ffff ; modified""")
|
||||
when {
|
||||
what.address is PtNumber -> {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" ror ${number.toHex()}")
|
||||
}
|
||||
what.address is PtIdentifier -> {
|
||||
asmgen.out(" php") // save Carry
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
|
||||
asmgen.out(" plp | ror a")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
else -> {
|
||||
TODO("ror ptr-expression ${what.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -527,12 +523,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
is PtArrayIndexer -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
asmgen.out(" lda ${varname},x | cmp #\$80 | rol a | sta ${varname},x")
|
||||
asmgen.out(" lda ${varname},x | cmp #$80 | rol a | sta ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
|
||||
asmgen.out(" lda ${number.toHex()} | cmp #$80 | rol a | sta ${number.toHex()}")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
||||
@@ -540,7 +536,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
||||
asmgen.out(" lda $variable | cmp #$80 | rol a | sta $variable")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
@@ -551,7 +547,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
|
||||
val varname = asmgen.asmVariableName(what.variable)
|
||||
if(what.splitWords)
|
||||
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb |+")
|
||||
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb,x |+")
|
||||
else
|
||||
asmgen.out(" asl ${varname},x | rol ${varname}+1,x | bcc + | inc ${varname},x |+ ")
|
||||
}
|
||||
@@ -579,29 +575,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" rol ${varname},x")
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
if (what.address is PtNumber) {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" rol ${number.toHex()}")
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.out(" php")
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, false)
|
||||
asmgen.out("""
|
||||
plp
|
||||
+ rol ${'$'}ffff,x ; modified""")
|
||||
} else {
|
||||
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
|
||||
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
|
||||
if(!what.address.isSimple()) asmgen.out(" plp")
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ rol ${'$'}ffff ; modified""")
|
||||
when {
|
||||
what.address is PtNumber -> {
|
||||
val number = (what.address as PtNumber).number
|
||||
asmgen.out(" rol ${number.toHex()}")
|
||||
}
|
||||
what.address is PtIdentifier -> {
|
||||
asmgen.out(" php") // save Carry
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
|
||||
asmgen.out(" plp | rol a")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
else -> {
|
||||
TODO("rol ptr-expression ${what.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -640,7 +626,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
when(fcall.args[0]) {
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(fcall.args[0] as PtIdentifier) + if(msb) "+1" else ""
|
||||
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, variableAsmName = varname)
|
||||
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, variableAsmName = varname)
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
val indexer = fcall.args[0] as PtArrayIndexer
|
||||
@@ -682,7 +668,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
multipliedIndex.parent=indexer
|
||||
}
|
||||
}
|
||||
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, array = indexer)
|
||||
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, array = indexer)
|
||||
}
|
||||
else -> throw AssemblyError("setlsb/setmsb on weird target ${fcall.args[0]}")
|
||||
}
|
||||
@@ -754,7 +740,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
else -> {
|
||||
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.forDt(BaseDataType.FLOAT))
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
pha
|
||||
@@ -829,7 +815,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr floats.MOVFM")
|
||||
if(resultRegister!=null) {
|
||||
assignAsmGen.assignFAC1float(
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.FLOAT), fcall.definingISub(), fcall.position, null, null, null, resultRegister, null))
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position, null, null, null, resultRegister, null))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1303,11 +1289,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
|
||||
|
||||
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
|
||||
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.forDt(BaseDataType.FLOAT), value.position)
|
||||
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
|
||||
val addr = PtAddressOf(value.position)
|
||||
addr.add(variable)
|
||||
addr.parent = call
|
||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.forDt(BaseDataType.FLOAT))
|
||||
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.IPtSubroutine
|
||||
import prog8.code.ast.PtAsmSub
|
||||
import prog8.code.ast.PtSub
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.RegisterOrPair
|
||||
import prog8.code.core.RegisterOrStatusflag
|
||||
|
||||
|
||||
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> {
|
||||
when(this) {
|
||||
is PtAsmSub -> {
|
||||
return returns
|
||||
}
|
||||
is PtSub -> {
|
||||
// for non-asm subroutines, determine the return registers based on the type of the return values
|
||||
|
||||
when(returns.size) {
|
||||
0 -> return emptyList()
|
||||
1 -> {
|
||||
val returntype = returns.single()
|
||||
val register = when {
|
||||
returntype.isByteOrBool -> RegisterOrStatusflag(RegisterOrPair.A, null)
|
||||
returntype.isWord -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
returntype.isFloat -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
|
||||
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
|
||||
}
|
||||
return listOf(Pair(register, returntype))
|
||||
}
|
||||
else -> {
|
||||
// TODO for multi-value results, put the first one in register(s) and only the rest in the virtual registers?
|
||||
throw AssemblyError("multi-value returns from a normal subroutine are not put into registers, this routine shouldn't have been called in this scenario")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,224 +33,351 @@ internal class ForLoopsAsmGen(
|
||||
}
|
||||
|
||||
private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val stepsize=range.step.asConstInteger()!!
|
||||
|
||||
if(stepsize < -1) {
|
||||
if(range.step.asConstInteger()!! < -1) {
|
||||
val limit = range.to.asConstInteger()
|
||||
if(limit==0)
|
||||
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
|
||||
}
|
||||
|
||||
when {
|
||||
iterableDt.isByteArray -> {
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
|
||||
if (stepsize==-1 && range.to.asConstInteger()==0) {
|
||||
// simple loop downto 0 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
iterableDt.isByteArray -> forOverNonconstByteRange(stmt, iterableDt, range)
|
||||
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> forOverNonconstWordRange(stmt, iterableDt, range)
|
||||
else -> throw AssemblyError("range expression can only be byte or word")
|
||||
}
|
||||
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
}
|
||||
|
||||
private fun forOverNonconstByteRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
|
||||
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
|
||||
when (stepsize) {
|
||||
-1 if range.to.asConstInteger()==0 -> {
|
||||
// simple loop downto 0 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
lda $varname
|
||||
cmp #255
|
||||
bne $loopLabel""")
|
||||
}
|
||||
-1 if range.to.asConstInteger()==1 -> {
|
||||
// simple loop downto 1 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
bne $loopLabel""")
|
||||
}
|
||||
1, -1 -> forOverBytesRangeStepOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
else -> forOverBytesRangeStepGreaterOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun forOverBytesRangeStepOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||
// bytes range, step 1 or -1
|
||||
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val incdec = if(stepsize==1) "inc" else "dec"
|
||||
|
||||
if(asmgen.options.romable) {
|
||||
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
|
||||
// so we need to store the loop end value in a newly allocated temporary variable
|
||||
val toValueVar = asmgen.getTempVarName(iterableDt.elementType().base)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
asmgen.out(" sta $toValueVar")
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
lda $varname
|
||||
cmp #255
|
||||
bne $loopLabel""")
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $endLabel""")
|
||||
}
|
||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
||||
// simple loop downto 1 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
else {
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
bne $loopLabel""")
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel""")
|
||||
}
|
||||
else if (stepsize==1 || stepsize==-1) {
|
||||
// bytes array, step 1 or -1
|
||||
|
||||
val incdec = if(stepsize==1) "inc" else "dec"
|
||||
// loop over byte range via loopvar
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bpl $endLabel""")
|
||||
}
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
}
|
||||
else
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
} else {
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
beq $endLabel
|
||||
$incdec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
|
||||
} else {
|
||||
|
||||
// bytes, step >= 2 or <= -2
|
||||
|
||||
// loop over byte range via loopvar
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bpl $endLabel""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
else
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
if(stepsize>0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #$stepsize
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bmi $loopLabel
|
||||
beq $loopLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bpl $loopLabel""")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else {
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
}
|
||||
}
|
||||
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> {
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvarWord(stmt, range)
|
||||
if(stepsize==-1 && range.to.asConstInteger()==0) {
|
||||
// simple loop downto 0 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(forloop.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp $toValueVar
|
||||
beq $endLabel
|
||||
$incdec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
|
||||
} else {
|
||||
|
||||
// use self-modifying code to store the loop end comparison value
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
bne ++
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $endLabel""")
|
||||
}
|
||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
||||
// simple loop downto 1 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #1
|
||||
bne +
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
}
|
||||
else if (stepsize == 1 || stepsize == -1) {
|
||||
// words, step 1 or -1
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
else {
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
}
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(forloop.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
beq $endLabel
|
||||
$incdec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun forOverBytesRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||
// bytes range, step >= 2 or <= -2
|
||||
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $endLabel""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0)
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
else {
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
}
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
}
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(forloop.statements)
|
||||
|
||||
asmgen.romableError("self-modifying code (forloop over bytes range)", forloop.position) // TODO fix romable; there is self-modifying code below
|
||||
if(stepsize>0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #$stepsize
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bmi $loopLabel
|
||||
beq $loopLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bpl $loopLabel""")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
|
||||
private fun forOverNonconstWordRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvarWord(stmt, range)
|
||||
if(stepsize==-1 && range.to.asConstInteger()==0) {
|
||||
// simple loop downto 0 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
bne ++
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
||||
// simple loop downto 1 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #1
|
||||
bne +
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else if (stepsize == 1 || stepsize == -1)
|
||||
forOverWordsRangeStepOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
else if (stepsize > 0)
|
||||
forOverWordsRangeStepGreaterOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
else
|
||||
forOverWordsRangeStepGreaterOneDescending(range, varname, iterableDt, loopLabel, endLabel, stmt)
|
||||
}
|
||||
|
||||
private fun forOverWordsRangeStepOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
|
||||
// words range, step 1 or -1
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
|
||||
if(asmgen.options.romable) {
|
||||
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
|
||||
// so we need to store the loop end value in a newly allocated temporary variable
|
||||
val toValueVar = asmgen.getTempVarName(iterableDt.elementType().base)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out(" sta $toValueVar")
|
||||
asmgen.out(" sty $toValueVar+1")
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(forloop.statements)
|
||||
asmgen.out("""
|
||||
lda $varname+1
|
||||
cmp $toValueVar+1
|
||||
bne +
|
||||
lda $varname
|
||||
cmp $toValueVar
|
||||
beq $endLabel""")
|
||||
if(stepsize==1) {
|
||||
asmgen.out("""
|
||||
+ inc $varname
|
||||
bne $loopLabel
|
||||
inc $varname+1""")
|
||||
asmgen.jmp(loopLabel)
|
||||
} else {
|
||||
asmgen.out("""
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
|
||||
} else {
|
||||
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
asmgen.translate(forloop.statements)
|
||||
asmgen.out("""
|
||||
lda $varname+1
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bne +
|
||||
lda $varname
|
||||
$modifiedLabel2 cmp #0 ; modified
|
||||
beq $endLabel""")
|
||||
if(stepsize==1) {
|
||||
asmgen.out("""
|
||||
+ inc $varname
|
||||
bne $loopLabel
|
||||
inc $varname+1""")
|
||||
asmgen.jmp(loopLabel)
|
||||
} else {
|
||||
asmgen.out("""
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else if (stepsize > 0) {
|
||||
// (u)words, step >= 2
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
if(stepsize==1) {
|
||||
asmgen.out("""
|
||||
+ inc $varname
|
||||
bne $loopLabel
|
||||
inc $varname+1""")
|
||||
asmgen.jmp(loopLabel)
|
||||
} else {
|
||||
asmgen.out("""
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun forOverWordsRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
|
||||
// (u)words, step >= 2
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.translate(stmt.statements)
|
||||
|
||||
if (iterableDt.isUnsignedWordArray) {
|
||||
asmgen.out("""
|
||||
asmgen.romableError("self-modifying code (forloop over word range)", stmt.position) // TODO fix romable; there is self-modifying code below
|
||||
if (iterableDt.isUnsignedWordArray) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #<$stepsize
|
||||
@@ -266,8 +393,8 @@ $modifiedLabel2 lda #0 ; modified
|
||||
bcc $endLabel
|
||||
bcs $loopLabel
|
||||
$endLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #<$stepsize
|
||||
@@ -283,20 +410,22 @@ $modifiedLabel lda #0 ; modified
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
else {
|
||||
}
|
||||
}
|
||||
|
||||
// (u)words, step <= -2
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
private fun forOverWordsRangeStepGreaterOneDescending(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
|
||||
// (u)words, step <= -2
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
|
||||
asmgen.out("""
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #<${stepsize.absoluteValue}
|
||||
@@ -313,12 +442,7 @@ $modifiedLabel sbc #0 ; modified
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("range expression can only be byte or word")
|
||||
}
|
||||
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
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) {
|
||||
@@ -330,23 +454,23 @@ $endLabel""")
|
||||
sta P8ZP_SCRATCH_W2 ; to
|
||||
sty P8ZP_SCRATCH_W2+1 ; to
|
||||
lda $fromVar
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
lda $fromVar+1
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
cmp $fromVar
|
||||
tya
|
||||
sbc $fromVar+1
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel
|
||||
cmp $fromVar
|
||||
tya
|
||||
sbc $fromVar+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi $endLabel
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
} else {
|
||||
if(stepsize<0)
|
||||
@@ -362,11 +486,11 @@ $endLabel""")
|
||||
+""")
|
||||
else
|
||||
asmgen.out("""
|
||||
cpy $fromVar+1
|
||||
bcc $endLabel
|
||||
bne +
|
||||
cmp $fromVar
|
||||
bcc $endLabel
|
||||
cpy $fromVar+1
|
||||
bcc $endLabel
|
||||
bne +
|
||||
cmp $fromVar
|
||||
bcc $endLabel
|
||||
+""")
|
||||
}
|
||||
}
|
||||
@@ -383,24 +507,39 @@ $endLabel""")
|
||||
}
|
||||
when {
|
||||
iterableDt.isString -> {
|
||||
asmgen.out("""
|
||||
lda #<$iterableName
|
||||
ldy #>$iterableName
|
||||
sta $loopLabel+1
|
||||
sty $loopLabel+2
|
||||
$loopLabel lda ${65535.toHex()} ; modified
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
inc $loopLabel+1
|
||||
bne $loopLabel
|
||||
inc $loopLabel+2
|
||||
bne $loopLabel
|
||||
if(asmgen.options.romable) {
|
||||
val indexVar = asmgen.getTempVarName(BaseDataType.UBYTE)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sty $indexVar
|
||||
$loopLabel lda $iterableName,y
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
inc $indexVar
|
||||
ldy $indexVar
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
} else {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sty $indexVar
|
||||
$loopLabel lda $iterableName,y
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
inc $indexVar
|
||||
ldy $indexVar
|
||||
bne $loopLabel
|
||||
$indexVar .byte 0
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
iterableDt.isByteArray || iterableDt.isBoolArray -> {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(iterableDt.elementType().base) else asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
$loopLabel sty $indexVar
|
||||
@@ -422,20 +561,22 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
if(!asmgen.options.romable) {
|
||||
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(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
iterableDt.isSplitWordArray -> {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(BaseDataType.UBYTE) else asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
@@ -460,21 +601,23 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(numElements>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
if(!asmgen.options.romable) {
|
||||
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(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
iterableDt.isWordArray -> {
|
||||
val length = numElements * 2
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val indexVar = if(asmgen.options.romable) asmgen.getTempVarName(BaseDataType.UBYTE) else asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
@@ -501,15 +644,17 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(length>=16) {
|
||||
// allocate index var on ZP if possible
|
||||
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
if(!asmgen.options.romable) {
|
||||
if(length>=16) {
|
||||
// allocate index var on ZP if possible, otherwise inline
|
||||
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
|
||||
result.fold(
|
||||
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
|
||||
failure = { asmgen.out("$indexVar .byte 0") }
|
||||
)
|
||||
} else {
|
||||
asmgen.out("$indexVar .byte 0")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.StNodeType
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.AsmAssignSource
|
||||
@@ -32,8 +33,14 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
|
||||
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
|
||||
|
||||
val symbol = asmgen.symbolTable.lookup(call.name)
|
||||
val sub = symbol?.astNode as IPtSubroutine
|
||||
val symbol = asmgen.symbolTable.lookup(call.name)!!
|
||||
if(symbol.type == StNodeType.LABEL) {
|
||||
require(call.void)
|
||||
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedName)}")
|
||||
return
|
||||
}
|
||||
|
||||
val sub = symbol.astNode as IPtSubroutine
|
||||
val subAsmName = asmgen.asmSymbolName(call.name)
|
||||
|
||||
if(sub is PtAsmSub) {
|
||||
@@ -51,6 +58,10 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
if(bank==null) {
|
||||
val varbank = if(sub.address?.varbank==null) null else asmgen.asmVariableName(sub.address!!.varbank!!)
|
||||
if(varbank!=null) {
|
||||
if(asmgen.options.romable)
|
||||
TODO("no codegen yet for non-const bank in subroutine call that's usable in ROM ${call.position}")
|
||||
|
||||
// self-modifying code: set jsrfar bank argument
|
||||
when(asmgen.options.compTarget.name) {
|
||||
"cx16" -> {
|
||||
// JSRFAR can jump to a banked RAM address as well!
|
||||
@@ -315,7 +326,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
register!!
|
||||
if(requiredDt.largerSizeThan(value.type)) {
|
||||
// we need to sign extend the source, do this via temporary word variable
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
|
||||
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type.base)
|
||||
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
|
||||
} else {
|
||||
|
||||
@@ -243,7 +243,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
in LogicalOperators -> {
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), stmt.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, stmt.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
||||
if (jumpAfterIf != null)
|
||||
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
|
||||
|
||||
@@ -43,21 +43,26 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
}
|
||||
|
||||
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
|
||||
if (condition is PtBinaryExpression) {
|
||||
val rightDt = condition.right.type
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
|
||||
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
|
||||
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
when (condition) {
|
||||
is PtBinaryExpression -> {
|
||||
val rightDt = condition.right.type
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
|
||||
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
|
||||
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
|
||||
is PtPrefix if condition.operator=="not" -> {
|
||||
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
|
||||
}
|
||||
|
||||
else -> {
|
||||
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
||||
asmgen.assignConditionValueToRegisterAndTest(condition)
|
||||
asmgen.out(" beq $falseLabel")
|
||||
}
|
||||
}
|
||||
else if(condition is PtPrefix && condition.operator=="not") {
|
||||
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
|
||||
} else {
|
||||
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
|
||||
asmgen.assignConditionValueToRegisterAndTest(condition)
|
||||
asmgen.out(" beq $falseLabel")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +87,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
asmgen.out(" beq $falseLabel")
|
||||
}
|
||||
in LogicalOperators -> {
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), condition.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, condition.definingISub(), condition.position, register=RegisterOrPair.A)
|
||||
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
|
||||
asmgen.out(" beq $falseLabel")
|
||||
} else {
|
||||
|
||||
@@ -232,6 +232,7 @@ internal class ProgramAndVarsGen(
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -299,6 +300,7 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out(" .dsection slabs_BSS")
|
||||
asmgen.out("prog8_program_end\t; end of program label for progend()")
|
||||
asmgen.out(" * = ${relocatedBssStart.toHex()}")
|
||||
asmgen.out(" .dsection BSS_NOCLEAR")
|
||||
asmgen.out("prog8_bss_section_start")
|
||||
asmgen.out(" .dsection BSS")
|
||||
if(relocateBssSlabs)
|
||||
@@ -306,6 +308,7 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
} else {
|
||||
asmgen.out(" .dsection BSS_NOCLEAR")
|
||||
asmgen.out("prog8_bss_section_start")
|
||||
asmgen.out(" .dsection BSS")
|
||||
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
|
||||
@@ -318,6 +321,10 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
|
||||
}
|
||||
}
|
||||
|
||||
if(relocatedBssEnd >= options.memtopAddress)
|
||||
options.memtopAddress = relocatedBssEnd+1u
|
||||
|
||||
asmgen.out(" ; memtop check")
|
||||
asmgen.out(" .cerror * >= ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${(options.memtopAddress-1u).toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"")
|
||||
}
|
||||
@@ -652,6 +659,7 @@ internal class ProgramAndVarsGen(
|
||||
it.initializationStringValue!!.second,
|
||||
it.initializationStringValue!!.first
|
||||
)
|
||||
asmgen.romableError("string (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||
}
|
||||
alignedStrings.sortedBy { it.align }.forEach {
|
||||
outputStringvar(
|
||||
@@ -660,13 +668,22 @@ internal class ProgramAndVarsGen(
|
||||
it.initializationStringValue!!.second,
|
||||
it.initializationStringValue!!.first
|
||||
)
|
||||
asmgen.romableError("string (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||
}
|
||||
|
||||
notAlignedOther.sortedBy { it.type }.forEach {
|
||||
staticVariable2asm(it)
|
||||
if(it.dt.isArray)
|
||||
asmgen.romableError("array (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||
else
|
||||
asmgen.romableError("inlined variable (${it.dt} ${it.name})", Position.DUMMY) // TODO print warning with position of the var
|
||||
}
|
||||
alignedOther.sortedBy { it.align }.sortedBy { it.type }.forEach {
|
||||
staticVariable2asm(it)
|
||||
if(it.dt.isArray)
|
||||
asmgen.romableError("array (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
|
||||
else
|
||||
asmgen.romableError("inlined variable (${it.dt} ${it.name})", Position.DUMMY) // TODO print warning with position of the var
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@ package prog8.codegen.cpu6502
|
||||
|
||||
import com.github.michaelbull.result.fold
|
||||
import com.github.michaelbull.result.onSuccess
|
||||
import prog8.code.StNode
|
||||
import prog8.code.StNodeType
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.*
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
@@ -23,7 +20,13 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
zeropageVars = zeropage.allocatedVariables
|
||||
}
|
||||
|
||||
internal fun isZpVar(scopedName: String) = scopedName in zeropageVars
|
||||
internal fun isZpVar(scopedName: String): Boolean {
|
||||
if(scopedName in zeropageVars)
|
||||
return true
|
||||
|
||||
val v = symboltable.lookup(scopedName)
|
||||
return if(v is StMemVar) v.address <= 255u else false
|
||||
}
|
||||
|
||||
internal fun getFloatAsmConst(number: Double): String {
|
||||
val asmName = globalFloatConsts[number]
|
||||
|
||||
@@ -2,7 +2,10 @@ package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.code.ast.PtBinaryExpression
|
||||
import prog8.code.ast.PtExpression
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.ComparisonOperators
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.RegisterOrPair
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
@@ -52,7 +55,7 @@ internal class AnyExprAsmGen(
|
||||
"+" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
@@ -60,7 +63,7 @@ internal class AnyExprAsmGen(
|
||||
"-" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
@@ -75,7 +78,7 @@ internal class AnyExprAsmGen(
|
||||
"&" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
@@ -83,7 +86,7 @@ internal class AnyExprAsmGen(
|
||||
"|" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
@@ -91,7 +94,7 @@ internal class AnyExprAsmGen(
|
||||
"^", "xor" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
|
||||
asmgen.assignRegister(RegisterOrPair.A, assign.target)
|
||||
return true
|
||||
@@ -190,14 +193,14 @@ internal class AnyExprAsmGen(
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
|
||||
if (!right.isSimple()) asmgen.popFAC1()
|
||||
}
|
||||
else -> TODO("don't know how to evaluate float expression for selected compilation target")
|
||||
else -> TODO("don't know how to evaluate float expression for selected compilation target ${left.position}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
|
||||
if(!expr.right.isSimple()) asmgen.pushFAC1()
|
||||
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.forDt(BaseDataType.FLOAT))
|
||||
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.FLOAT)
|
||||
if(!expr.right.isSimple()) asmgen.popFAC1()
|
||||
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package prog8.codegen.cpu6502.assignment
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
import prog8.codegen.cpu6502.returnsWhatWhere
|
||||
|
||||
|
||||
internal enum class TargetStorageKind {
|
||||
@@ -53,7 +52,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
fun fromAstAssignmentMulti(targets: List<PtAssignTarget>, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): List<AsmAssignTarget> {
|
||||
return targets.map {
|
||||
if(it.void)
|
||||
AsmAssignTarget(TargetStorageKind.VOID, asmgen, DataType.forDt(BaseDataType.UNDEFINED), null, it.position)
|
||||
AsmAssignTarget(TargetStorageKind.VOID, asmgen, DataType.UNDEFINED, null, it.position)
|
||||
else
|
||||
fromAstAssignment(it, definingSub, asmgen)
|
||||
}
|
||||
@@ -88,18 +87,18 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
RegisterOrPair.A,
|
||||
RegisterOrPair.X,
|
||||
RegisterOrPair.Y -> {
|
||||
val dt = DataType.forDt(if(signed) BaseDataType.BYTE else BaseDataType.UBYTE)
|
||||
val dt = if(signed) DataType.BYTE else DataType.UBYTE
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||
}
|
||||
RegisterOrPair.AX,
|
||||
RegisterOrPair.AY,
|
||||
RegisterOrPair.XY -> {
|
||||
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD)
|
||||
val dt = if(signed) DataType.WORD else DataType.UWORD
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||
}
|
||||
RegisterOrPair.FAC1,
|
||||
RegisterOrPair.FAC2 -> {
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.FLOAT), scope, pos, register = registers)
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
|
||||
}
|
||||
RegisterOrPair.R0,
|
||||
RegisterOrPair.R1,
|
||||
@@ -117,7 +116,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
RegisterOrPair.R13,
|
||||
RegisterOrPair.R14,
|
||||
RegisterOrPair.R15 -> {
|
||||
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD)
|
||||
val dt = if(signed) DataType.WORD else DataType.UWORD
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||
}
|
||||
}
|
||||
@@ -171,7 +170,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
||||
val bv = value as? PtBool
|
||||
if(bv!=null)
|
||||
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.forDt(BaseDataType.BOOL), boolean = bv)
|
||||
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.BOOL, boolean = bv)
|
||||
|
||||
return when(value) {
|
||||
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
|
||||
@@ -192,7 +191,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
}
|
||||
}
|
||||
is PtMemoryByte -> {
|
||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.forDt(BaseDataType.UBYTE), memory = value)
|
||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
|
||||
@@ -205,7 +204,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
val sub = symbol.astNode as IPtSubroutine
|
||||
val returnType =
|
||||
if(sub is PtSub && sub.returns.size>1)
|
||||
DataType.forDt(BaseDataType.UNDEFINED) // TODO list of types instead?
|
||||
DataType.UNDEFINED // TODO list of types instead?
|
||||
else
|
||||
sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
|
||||
?: throw AssemblyError("can't translate zero return values in assignment")
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.StExtSub
|
||||
import prog8.code.StExtSubParameter
|
||||
import prog8.code.StStaticVariable
|
||||
import prog8.code.StSub
|
||||
import prog8.code.*
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
import prog8.codegen.cpu6502.VariableAllocator
|
||||
import prog8.codegen.cpu6502.returnsWhatWhere
|
||||
|
||||
|
||||
internal class AssignmentAsmGen(
|
||||
@@ -44,7 +39,7 @@ internal class AssignmentAsmGen(
|
||||
if(extsub!=null) {
|
||||
require(extsub.returns.size>=2)
|
||||
if(extsub.returns.any { it.type.isFloat })
|
||||
TODO("deal with (multiple?) FP return registers")
|
||||
TODO("deal with (multiple?) FP return registers ${assignment.position}")
|
||||
|
||||
asmgen.translate(values)
|
||||
|
||||
@@ -154,7 +149,7 @@ internal class AssignmentAsmGen(
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
else TODO("array target for multi-value assignment") // Not done yet due to result register clobbering complexity
|
||||
else TODO("array target for multi-value assignment ${target.position}") // Not done yet due to result register clobbering complexity
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,7 +237,7 @@ internal class AssignmentAsmGen(
|
||||
targetDt.isSignedWord -> assignVariableWord(assign.target, variable, assign.source.datatype)
|
||||
targetDt.isUnsignedWord -> {
|
||||
if(assign.source.datatype.isPassByRef)
|
||||
assignAddressOf(assign.target, variable, false, null, null)
|
||||
assignAddressOf(assign.target, variable, false, assign.source.datatype, assign.source.array ?: PtNumber(BaseDataType.UBYTE, 0.0, assign.position))
|
||||
else
|
||||
assignVariableWord(assign.target, variable, assign.source.datatype)
|
||||
}
|
||||
@@ -383,7 +378,7 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
|
||||
// fallback assignmen through temporary pointer var
|
||||
assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
||||
assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false)
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
}
|
||||
@@ -434,7 +429,7 @@ internal class AssignmentAsmGen(
|
||||
// }
|
||||
}
|
||||
if(saveA) asmgen.out(" pha")
|
||||
assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
||||
assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
if(saveA) asmgen.out(" pla")
|
||||
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2", false)
|
||||
}
|
||||
@@ -610,11 +605,13 @@ internal class AssignmentAsmGen(
|
||||
val sub = symbol!!.astNode as IPtSubroutine
|
||||
asmgen.translateFunctionCall(value)
|
||||
if(sub is PtSub && sub.returns.size>1) {
|
||||
// multi-value returns are passed throug cx16.R15 down to R0 (allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
val registersReverseOrder = Cx16VirtualRegisters.reversed()
|
||||
assign.targets.zip(registersReverseOrder).forEach { (target, register) ->
|
||||
if(target.kind!=TargetStorageKind.VOID)
|
||||
assignVirtualRegister(target, register)
|
||||
// note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
|
||||
// (this allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
val returnRegs = sub.returnsWhatWhere()
|
||||
assign.targets.zip(returnRegs).forEach { target ->
|
||||
if(target.first.kind != TargetStorageKind.VOID) {
|
||||
asmgen.assignRegister(target.second.first.registerOrPair!!, target.first)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val target = assign.target
|
||||
@@ -700,13 +697,16 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignVirtualRegister(target: AsmAssignTarget, register: RegisterOrPair) {
|
||||
internal fun assignVirtualRegister(target: AsmAssignTarget, register: RegisterOrPair) {
|
||||
// Note: while the virtual register R0-R15 can hold a word value,
|
||||
// the actual datatype that gets assigned is determined by the assignment target.
|
||||
// This can be a single byte!
|
||||
when {
|
||||
target.datatype.isByteOrBool -> {
|
||||
if(register in Cx16VirtualRegisters) {
|
||||
asmgen.out(" lda cx16.${register.toString().lowercase()}L")
|
||||
} else {
|
||||
TODO("LDA byte from $register")
|
||||
TODO("LDA byte from $register ${target.position}")
|
||||
}
|
||||
assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
@@ -850,7 +850,7 @@ internal class AssignmentAsmGen(
|
||||
RegisterOrPair.A -> "a"
|
||||
RegisterOrPair.X -> "x"
|
||||
RegisterOrPair.Y -> "y"
|
||||
else -> TODO("comparison to word register")
|
||||
else -> TODO("comparison to word register ${expr.position}")
|
||||
}
|
||||
val assignTrue = PtInlineAssembly("\tld${reg} #1", false, assign.target.position)
|
||||
val assignFalse = PtInlineAssembly("\tld${reg} #0", false, assign.target.position)
|
||||
@@ -981,7 +981,7 @@ internal class AssignmentAsmGen(
|
||||
expr.type.isUnsignedWord -> {
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out(" jsr prog8_math.divmod_uw_asm")
|
||||
assignVariableWord(target, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
||||
assignVariableWord(target, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
return true
|
||||
}
|
||||
else -> return false
|
||||
@@ -1497,7 +1497,7 @@ internal class AssignmentAsmGen(
|
||||
if(right.type.isWord && castedValue.type.isByte && castedValue is PtIdentifier) {
|
||||
if(right.type.isSigned) {
|
||||
// we need to sign extend, do this via temporary word variable
|
||||
asmgen.assignExpressionToVariable(right, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.WORD))
|
||||
asmgen.assignExpressionToVariable(right, "P8ZP_SCRATCH_W1", DataType.WORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY, dt.isSigned)
|
||||
if(expr.operator=="+") {
|
||||
asmgen.out("""
|
||||
@@ -1634,7 +1634,7 @@ internal class AssignmentAsmGen(
|
||||
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
||||
} else {
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
when (expr.operator) {
|
||||
@@ -1687,7 +1687,7 @@ internal class AssignmentAsmGen(
|
||||
asmgen.out(" sty P8ZP_SCRATCH_B1")
|
||||
} else {
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
when (operator) {
|
||||
@@ -1841,8 +1841,7 @@ internal class AssignmentAsmGen(
|
||||
"==" -> {
|
||||
val dt = expr.left.type
|
||||
when {
|
||||
dt.isBool -> TODO("compare bool to 0")
|
||||
dt.isByte -> {
|
||||
dt.isBool || dt.isByte -> {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt.isSigned)
|
||||
asmgen.out("""
|
||||
cmp #0
|
||||
@@ -1877,8 +1876,7 @@ internal class AssignmentAsmGen(
|
||||
"!=" -> {
|
||||
val dt = expr.left.type
|
||||
when {
|
||||
dt.isBool -> TODO("compare bool to 0")
|
||||
dt.isByte -> {
|
||||
dt.isBool || dt.isByte -> {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt.isSigned)
|
||||
asmgen.out(" beq + | lda #1")
|
||||
asmgen.out("+")
|
||||
@@ -1962,27 +1960,27 @@ $endLabel""")
|
||||
val (dt, numElements) = when(symbol) {
|
||||
is StStaticVariable -> symbol.dt to symbol.length!!
|
||||
is StMemVar -> symbol.dt to symbol.length!!
|
||||
else -> DataType.forDt(BaseDataType.UNDEFINED) to 0
|
||||
else -> DataType.UNDEFINED to 0
|
||||
}
|
||||
when {
|
||||
dt.isString -> {
|
||||
assignExpressionToRegister(containment.needle, RegisterOrPair.A, elementDt.isSigned)
|
||||
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position,"P8ZP_SCRATCH_W1"), symbolName, false, null, null)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position,"P8ZP_SCRATCH_W1"), symbolName, false, null, null)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out(" ldy #${numElements-1}")
|
||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||
}
|
||||
dt.isFloatArray -> {
|
||||
assignExpressionToRegister(containment.needle, RegisterOrPair.FAC1, true)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, false, null, null)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, false, null, null)
|
||||
asmgen.out(" ldy #$numElements")
|
||||
asmgen.out(" jsr floats.containment_floatarray")
|
||||
}
|
||||
dt.isByteArray -> {
|
||||
assignExpressionToRegister(containment.needle, RegisterOrPair.A, elementDt.isSigned)
|
||||
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, false, null, null)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, false, null, null)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out(" ldy #$numElements")
|
||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||
@@ -1990,11 +1988,11 @@ $endLabel""")
|
||||
dt.isWordArray -> {
|
||||
assignExpressionToVariable(containment.needle, "P8ZP_SCRATCH_W1", elementDt)
|
||||
if(dt.isSplitWordArray) {
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName+"_lsb", false, null, null)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName+"_lsb", false, null, null)
|
||||
asmgen.out(" ldy #$numElements")
|
||||
asmgen.out(" jsr prog8_lib.containment_splitwordarray")
|
||||
} else {
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName, false, null, null)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName, false, null, null)
|
||||
asmgen.out(" ldy #$numElements")
|
||||
asmgen.out(" jsr prog8_lib.containment_linearwordarray")
|
||||
}
|
||||
@@ -2058,7 +2056,7 @@ $endLabel""")
|
||||
if(targetDt.isWord) {
|
||||
|
||||
fun assignViaExprEval(addressExpression: PtExpression) {
|
||||
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
||||
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false)
|
||||
asmgen.out(" ldy #0")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
@@ -2285,10 +2283,10 @@ $endLabel""")
|
||||
}
|
||||
|
||||
private fun assignCastViaLsbFunc(value: PtExpression, target: AsmAssignTarget) {
|
||||
val lsb = PtBuiltinFunctionCall("lsb", false, true, DataType.forDt(BaseDataType.UBYTE), value.position)
|
||||
val lsb = PtBuiltinFunctionCall("lsb", false, true, DataType.UBYTE, value.position)
|
||||
lsb.parent = value.parent
|
||||
lsb.add(value)
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.forDt(BaseDataType.UBYTE), expression = lsb)
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
|
||||
val assign = AsmAssignment(src, listOf(target), program.memsizer, value.position)
|
||||
translateNormalAssignment(assign, value.definingISub())
|
||||
}
|
||||
@@ -2609,9 +2607,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==true) {
|
||||
if (arrayDt.isUnsignedWord) {
|
||||
require(!msb)
|
||||
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
||||
if(constIndex>0)
|
||||
@@ -2624,21 +2623,21 @@ $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 #<($sourceName + $offset) | ldy #>($sourceName + $offset)")
|
||||
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)")
|
||||
} else {
|
||||
asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||
asmgen.out(" lda #<$arrayName | ldy #>$arrayName")
|
||||
}
|
||||
}
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return
|
||||
} else {
|
||||
if (arrayDt?.isUnsignedWord==true) {
|
||||
if (arrayDt.isUnsignedWord) {
|
||||
require(!msb)
|
||||
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_REG", DataType.forDt(BaseDataType.UBYTE))
|
||||
assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_REG", DataType.UBYTE)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
asmgen.out("""
|
||||
@@ -2651,9 +2650,9 @@ $endLabel""")
|
||||
else {
|
||||
assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.A, false)
|
||||
asmgen.out("""
|
||||
ldy #>$sourceName
|
||||
ldy #>$arrayName
|
||||
clc
|
||||
adc #<$sourceName
|
||||
adc #<$arrayName
|
||||
bcc +
|
||||
iny
|
||||
+""")
|
||||
@@ -3222,7 +3221,7 @@ $endLabel""")
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
cmp #${'$'}80
|
||||
cmp #$80
|
||||
bcc +
|
||||
dey
|
||||
+""")
|
||||
@@ -3234,7 +3233,7 @@ $endLabel""")
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
ldx #0
|
||||
cmp #${'$'}80
|
||||
cmp #$80
|
||||
bcc +
|
||||
dex
|
||||
+""")
|
||||
@@ -3247,7 +3246,7 @@ $endLabel""")
|
||||
asmgen.out("""
|
||||
tax
|
||||
ldy #0
|
||||
cpx #${'$'}80
|
||||
cpx #$80
|
||||
bcc +
|
||||
dey
|
||||
+""")
|
||||
@@ -3273,7 +3272,7 @@ $endLabel""")
|
||||
asmgen.out("""
|
||||
txa
|
||||
ldy #0
|
||||
cmp #${'$'}80
|
||||
cmp #$80
|
||||
bcc +
|
||||
dey
|
||||
+""")
|
||||
@@ -3286,7 +3285,7 @@ $endLabel""")
|
||||
asmgen.out("""
|
||||
txa
|
||||
ldx #0
|
||||
cmp #${'$'}80
|
||||
cmp #$80
|
||||
bcc +
|
||||
dex
|
||||
+""")
|
||||
@@ -3298,7 +3297,7 @@ $endLabel""")
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
cpx #${'$'}80
|
||||
cpx #$80
|
||||
bcc +
|
||||
dey
|
||||
+""")
|
||||
@@ -3324,7 +3323,7 @@ $endLabel""")
|
||||
asmgen.out("""
|
||||
tya
|
||||
ldy #0
|
||||
cmp #${'$'}80
|
||||
cmp #$80
|
||||
bcc +
|
||||
dey
|
||||
+""")
|
||||
@@ -3337,7 +3336,7 @@ $endLabel""")
|
||||
asmgen.out("""
|
||||
tya
|
||||
ldx #0
|
||||
cmp #${'$'}80
|
||||
cmp #$80
|
||||
bcc +
|
||||
dex
|
||||
+""")
|
||||
@@ -3351,7 +3350,7 @@ $endLabel""")
|
||||
tya
|
||||
tax
|
||||
ldy #0
|
||||
cpx #${'$'}80
|
||||
cpx #$80
|
||||
bcc +
|
||||
dey
|
||||
+""")
|
||||
@@ -4089,7 +4088,7 @@ $endLabel""")
|
||||
asmgen.storeAIntoPointerVar(memory.address as PtIdentifier)
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
|
||||
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
asmgen.out("""
|
||||
lda (P8ZP_SCRATCH_W2)
|
||||
@@ -4151,8 +4150,8 @@ $endLabel""")
|
||||
val target = assign.target
|
||||
val datatype = if(ignoreDatatype) {
|
||||
when {
|
||||
target.datatype.isByte -> DataType.forDt(BaseDataType.BYTE)
|
||||
target.datatype.isWord -> DataType.forDt(BaseDataType.WORD)
|
||||
target.datatype.isByte -> DataType.BYTE
|
||||
target.datatype.isWord -> DataType.WORD
|
||||
else -> target.datatype
|
||||
}
|
||||
} else target.datatype
|
||||
|
||||
+36
-18
@@ -131,18 +131,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
is PtNumber -> {
|
||||
val addr = (memory.address as PtNumber).number.toInt()
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.boolean!!.asInt())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.UBYTE, operator, value.boolean!!.asInt())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.UBYTE, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationByteWithVariable(addr.toHex(), false, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationByteWithVariable(addr.toHex(), false, operator, regName(value))
|
||||
SourceStorageKind.MEMORY -> inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.memory!!)
|
||||
SourceStorageKind.ARRAY -> inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.array!!)
|
||||
SourceStorageKind.MEMORY -> inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.memory!!)
|
||||
SourceStorageKind.ARRAY -> inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.array!!)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
|
||||
inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.expression)
|
||||
inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.expression)
|
||||
} else {
|
||||
inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.expression!!)
|
||||
inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.expression!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,21 +196,21 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
SourceStorageKind.MEMORY -> {
|
||||
asmgen.out(" sta P8ZP_SCRATCH_B1")
|
||||
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), operator, value.memory!!)
|
||||
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.memory!!)
|
||||
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
||||
}
|
||||
SourceStorageKind.ARRAY -> {
|
||||
asmgen.out(" sta P8ZP_SCRATCH_B1")
|
||||
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), operator, value.array!!)
|
||||
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.array!!)
|
||||
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE)
|
||||
asmgen.out(" sta $tempVar")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationByteWithValue(tempVar, DataType.forDt(BaseDataType.UBYTE), operator, value.expression)
|
||||
inplacemodificationByteWithValue(tempVar, DataType.UBYTE, operator, value.expression)
|
||||
else
|
||||
inplacemodificationByteWithValue(tempVar, DataType.forDt(BaseDataType.UBYTE), operator, value.expression!!)
|
||||
inplacemodificationByteWithValue(tempVar, DataType.UBYTE, operator, value.expression!!)
|
||||
asmgen.out(" ldx $tempVar")
|
||||
}
|
||||
}
|
||||
@@ -592,7 +592,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
fun assignValueToA() {
|
||||
val assignValue = AsmAssignment(value,
|
||||
listOf(
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.UBYTE),
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE,
|
||||
address.definingISub(), Position.DUMMY, register = RegisterOrPair.A)
|
||||
),
|
||||
program.memsizer, Position.DUMMY)
|
||||
@@ -898,7 +898,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun inplacemodificationBytePointerWithValue(pointervar: PtIdentifier, operator: String, value: PtExpression) {
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
inplacemodificationBytePointerWithVariable(pointervar, operator, "P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@@ -962,9 +962,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
if(value==1) {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
if(asmgen.options.romable) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" inc a")
|
||||
else
|
||||
asmgen.out(" clc | adc #1")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
}
|
||||
} else {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" clc | adc #$value")
|
||||
@@ -973,9 +982,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
"-" -> {
|
||||
if(value==1) {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
if(asmgen.options.romable) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" dec a")
|
||||
else
|
||||
asmgen.out(" sec | sbc #1")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
} else {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" sec | sbc #$value")
|
||||
|
||||
@@ -68,6 +68,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
override fun print_single_error(errormessage: String) { /* prints nothing in tests */ }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
|
||||
@@ -5,12 +5,15 @@ import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
|
||||
import io.kotest.matchers.shouldBe
|
||||
import prog8.code.StMemVar
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.source.SourceCode
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.codegen.cpu6502.AsmGen6502
|
||||
import prog8.codegen.cpu6502.VariableAllocator
|
||||
import java.nio.file.Files
|
||||
import kotlin.io.path.Path
|
||||
|
||||
@@ -26,6 +29,7 @@ class TestCodegen: FunSpec({
|
||||
zpAllowed = CompilationOptions.AllZeropageAllowed,
|
||||
floats = true,
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
@@ -51,7 +55,7 @@ class TestCodegen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"pi",
|
||||
DataType.forDt(BaseDataType.UBYTE),
|
||||
DataType.UBYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
null,
|
||||
@@ -78,7 +82,7 @@ class TestCodegen: FunSpec({
|
||||
))
|
||||
sub.add(PtVariable(
|
||||
"xx",
|
||||
DataType.forDt(BaseDataType.WORD),
|
||||
DataType.WORD,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
null,
|
||||
@@ -88,13 +92,13 @@ class TestCodegen: FunSpec({
|
||||
|
||||
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val target = PtAssignTarget(false, Position.DUMMY).also {
|
||||
val targetIdx = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY).also { idx ->
|
||||
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
|
||||
idx.add(PtIdentifier("main.start.particleX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
||||
idx.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
||||
}
|
||||
it.add(targetIdx)
|
||||
}
|
||||
val value = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)
|
||||
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
|
||||
value.add(PtIdentifier("main.start.particleDX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
|
||||
value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
|
||||
assign.add(target)
|
||||
@@ -103,15 +107,15 @@ class TestCodegen: FunSpec({
|
||||
|
||||
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
||||
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
prefixAssign.add(prefixTarget)
|
||||
prefixAssign.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
sub.add(prefixAssign)
|
||||
|
||||
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
|
||||
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
numberAssign.add(numberAssignTarget)
|
||||
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
||||
@@ -119,10 +123,10 @@ class TestCodegen: FunSpec({
|
||||
|
||||
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
cxregAssign.add(cxregAssignTarget)
|
||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY))
|
||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
|
||||
sub.add(cxregAssign)
|
||||
|
||||
block.add(sub)
|
||||
@@ -130,7 +134,7 @@ class TestCodegen: FunSpec({
|
||||
|
||||
// define the "cx16.r0" virtual register
|
||||
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
cx16block.add(PtMemMapped("r0", DataType.forDt(BaseDataType.UWORD), 100u, null, Position.DUMMY))
|
||||
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||
program.add(cx16block)
|
||||
|
||||
val options = getTestOptions()
|
||||
@@ -159,5 +163,15 @@ class TestCodegen: FunSpec({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test("memory mapped zp var is correctly considered to be zp var") {
|
||||
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
|
||||
val st = SymbolTable(program)
|
||||
st.add(StMemVar("zpmemvar", DataType.WORD, 0x20u, null, null))
|
||||
st.add(StMemVar("normalmemvar", DataType.WORD, 0x9000u, null, null))
|
||||
val allocator = VariableAllocator(st, getTestOptions(), ErrorReporterForTests())
|
||||
allocator.isZpVar("zpmemvar") shouldBe true
|
||||
allocator.isZpVar("normalmemvar") shouldBe false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,6 +6,7 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
implementation(project(":simpleAst"))
|
||||
implementation(project(":intermediate"))
|
||||
implementation(project(":codeGenIntermediate"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
|
||||
@@ -13,5 +13,6 @@
|
||||
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -3,8 +3,8 @@ package prog8.codegen.experimental
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.IAssemblyProgram
|
||||
import prog8.code.core.ICodeGeneratorBackend
|
||||
import prog8.code.IAssemblyProgram
|
||||
import prog8.code.ICodeGeneratorBackend
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.codegen.intermediate.IRCodeGen
|
||||
import prog8.intermediate.IRFileWriter
|
||||
|
||||
@@ -7,6 +7,7 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
implementation(project(":simpleAst"))
|
||||
implementation(project(":intermediate"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
|
||||
@@ -41,15 +41,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
} else {
|
||||
val normalsub = codeGen.symbolTable.lookup(values.name) as? StSub
|
||||
if (normalsub != null) {
|
||||
// multi-value returns are passed throug cx16.R15 down to R0 (allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
val registersReverseOrder = Cx16VirtualRegisters.reversed()
|
||||
normalsub.returns.zip(assignmentTargets).zip(registersReverseOrder).forEach {
|
||||
// note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
|
||||
// (this allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
val returnregs = (normalsub.astNode!! as IPtSubroutine).returnsWhatWhere()
|
||||
normalsub.returns.zip(assignmentTargets).zip(returnregs).forEach {
|
||||
val target = it.first.second as PtAssignTarget
|
||||
if(!target.void) {
|
||||
val assignSingle = PtAssignment(assignment.position, assignment.isVarInitializer)
|
||||
assignSingle.add(target)
|
||||
assignSingle.add(PtIdentifier("cx16.${it.second.toString().lowercase()}", it.first.first, assignment.position))
|
||||
result += translateRegularAssign(assignSingle)
|
||||
val reg = it.second.first
|
||||
val regnum = codeGen.registers.next(irType(it.second.second))
|
||||
val p = StExtSubParameter(reg, it.second.second)
|
||||
result += assignCpuRegister(p, regnum, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +75,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
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, IRDataType.WORD, reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}")
|
||||
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!!}")
|
||||
}
|
||||
@@ -360,10 +363,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val instruction = if(zero) {
|
||||
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
|
||||
} else {
|
||||
if (targetDt == IRDataType.FLOAT)
|
||||
if (targetDt == IRDataType.FLOAT) {
|
||||
require(valueFpRegister>=0)
|
||||
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
||||
else
|
||||
}
|
||||
else {
|
||||
require(valueRegister>=0)
|
||||
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
|
||||
}
|
||||
}
|
||||
result += IRCodeChunk(null, null).also { it += instruction }
|
||||
return result
|
||||
@@ -513,7 +520,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
return Pair(result, tr.resultReg)
|
||||
}
|
||||
val mult: PtExpression = PtBinaryExpression("*", DataType.forDt(BaseDataType.UBYTE), array.position)
|
||||
val mult: PtExpression = PtBinaryExpression("*", DataType.UBYTE, array.position)
|
||||
mult.children += array.index
|
||||
mult.children += PtNumber(BaseDataType.UBYTE, itemsize.toDouble(), array.position)
|
||||
val tr = expressionEval.translateExpression(mult)
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.StNode
|
||||
import prog8.code.StExtSub
|
||||
import prog8.code.StNode
|
||||
import prog8.code.StNodeType
|
||||
import prog8.code.StSub
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.Cx16VirtualRegisters
|
||||
import prog8.code.core.Statusflag
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
@@ -29,7 +33,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
fun translateExpression(expr: PtExpression): ExpressionCodeResult {
|
||||
return when (expr) {
|
||||
is PtIrRegister -> {
|
||||
ExpressionCodeResult(emptyList(), irType(expr.type), expr.register, -1)
|
||||
if(expr.type.isFloat)
|
||||
ExpressionCodeResult(emptyList(), IRDataType.FLOAT, -1, expr.register)
|
||||
else
|
||||
ExpressionCodeResult(emptyList(), irType(expr.type), expr.register, -1)
|
||||
}
|
||||
is PtBool -> {
|
||||
val code = IRCodeChunk(null, null)
|
||||
@@ -579,7 +586,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
internal fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
|
||||
val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)
|
||||
val callTarget = codeGen.symbolTable.lookup(fcall.name)!!
|
||||
|
||||
if(callTarget.scopedName in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
|
||||
// special case, these should be inlined, or even use specialized instructions. Instead of doing a normal subroutine call.
|
||||
@@ -635,7 +642,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
// return value(s)
|
||||
val returnRegSpecs = if(fcall.void) emptyList() else {
|
||||
// TODO: for current implemenation of the call convention in case of multiple return values,
|
||||
// a list of Ir virtual registers to hold the results is NOT correct (they're loaded into AY + R15..R0 instead!)
|
||||
// So we use an empty list to avoid confusion here. This may change in a future version.
|
||||
val returnRegSpecs = if(fcall.void || callTarget.returns.size>1) emptyList() else {
|
||||
callTarget.returns.map {
|
||||
val returnIrType = irType(it)
|
||||
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.next(returnIrType), null)
|
||||
@@ -653,7 +663,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else
|
||||
ExpressionCodeResult(result, returnRegSpec.dt, returnRegSpec.registerNum, -1)
|
||||
} else {
|
||||
// multi-value returns are passed throug cx16.R15 down to R0 (allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
// note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
|
||||
// so the actual result of the expression here is 'void' (doesn't use IR virtual registers at all)
|
||||
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
@@ -671,25 +681,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else
|
||||
argRegisters.add(FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, parameter.register)))
|
||||
result += tr.chunks
|
||||
when(parameter.register.registerOrPair) {
|
||||
RegisterOrPair.A -> addInstr(result, IRInstruction(Opcode.STOREHA, IRDataType.BYTE, reg1=tr.resultReg), null)
|
||||
RegisterOrPair.X -> addInstr(result, IRInstruction(Opcode.STOREHX, IRDataType.BYTE, reg1=tr.resultReg), null)
|
||||
RegisterOrPair.Y -> addInstr(result, IRInstruction(Opcode.STOREHY, IRDataType.BYTE, reg1=tr.resultReg), null)
|
||||
RegisterOrPair.AX -> addInstr(result, IRInstruction(Opcode.STOREHAX, IRDataType.WORD, reg1=tr.resultReg), null)
|
||||
RegisterOrPair.AY -> addInstr(result, IRInstruction(Opcode.STOREHAY, IRDataType.WORD, reg1=tr.resultReg), null)
|
||||
RegisterOrPair.XY -> addInstr(result, IRInstruction(Opcode.STOREHXY, IRDataType.WORD, reg1=tr.resultReg), null)
|
||||
RegisterOrPair.FAC1 -> addInstr(result, IRInstruction(Opcode.STOREHFACZERO, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null)
|
||||
RegisterOrPair.FAC2 -> addInstr(result, IRInstruction(Opcode.STOREHFACONE, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null)
|
||||
in Cx16VirtualRegisters -> {
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, paramDt, reg1=tr.resultReg, labelSymbol = "cx16.${parameter.register.registerOrPair.toString().lowercase()}"), null)
|
||||
}
|
||||
null -> when(parameter.register.statusflag) {
|
||||
// TODO: do the statusflag argument as last
|
||||
Statusflag.Pc -> addInstr(result, IRInstruction(Opcode.LSR, paramDt, reg1=tr.resultReg), null)
|
||||
else -> throw AssemblyError("weird statusflag as param")
|
||||
}
|
||||
else -> throw AssemblyError("unsupported register arg")
|
||||
}
|
||||
result += codeGen.setCpuRegister(parameter.register, paramDt, tr.resultReg, tr.resultFpReg)
|
||||
}
|
||||
|
||||
if(callTarget.returns.size>1)
|
||||
@@ -770,7 +762,18 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else
|
||||
ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1)
|
||||
}
|
||||
else -> throw AssemblyError("invalid node type")
|
||||
else -> {
|
||||
if(callTarget.type == StNodeType.LABEL) {
|
||||
require(fcall.void)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val args = FunctionCallArgs(emptyList(), emptyList())
|
||||
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = args), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
}
|
||||
else {
|
||||
throw AssemblyError("invalid node type")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class IRCodeGen(
|
||||
verifyNameScoping(program, symbolTable)
|
||||
changeGlobalVarInits(symbolTable)
|
||||
|
||||
val irSymbolTable = IRSymbolTable.fromAstSymboltable(symbolTable)
|
||||
val irSymbolTable = convertStToIRSt(symbolTable)
|
||||
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
|
||||
|
||||
// collect global variables initializers
|
||||
@@ -1759,12 +1759,35 @@ class IRCodeGen(
|
||||
private fun translate(ret: PtReturn): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(ret.children.size>1) {
|
||||
// multi-value returns are passed throug cx16.R15 down to R0 (allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
val registersReverseOrder = Cx16VirtualRegisters.reversed()
|
||||
for ((value, register) in ret.children.zip(registersReverseOrder)) {
|
||||
// note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
|
||||
// (this allows unencumbered use of many Rx registers if you don't return that many values)
|
||||
// a floating point value is passed via FAC (just one fp value is possible)
|
||||
|
||||
val returnRegs = ret.definingISub()!!.returnsWhatWhere()
|
||||
val values = ret.children.zip(returnRegs)
|
||||
// first all but the first return values
|
||||
for ((value, register) in values.drop(1)) {
|
||||
val tr = expressionEval.translateExpression(value as PtExpression)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, tr.dt, reg1=tr.resultReg, labelSymbol = "cx16.${register.toString().lowercase()}"), null)
|
||||
if(register.second.isFloat) {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
result += setCpuRegister(register.first, IRDataType.FLOAT, -1, tr.resultFpReg)
|
||||
}
|
||||
else {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
// finally do the first of the return values (this avoids clobbering of its value in AY)
|
||||
values.first().also { (value, register) ->
|
||||
val tr = expressionEval.translateExpression(value as PtExpression)
|
||||
if(register.second.isFloat) {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
result += setCpuRegister(register.first, IRDataType.FLOAT, -1, tr.resultFpReg)
|
||||
}
|
||||
else {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
|
||||
}
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.RETURN), null)
|
||||
return result
|
||||
@@ -1891,7 +1914,7 @@ class IRCodeGen(
|
||||
private var labelSequenceNumber = 0
|
||||
internal fun createLabelName(): String {
|
||||
labelSequenceNumber++
|
||||
return "${PtLabel.GENERATED_LABEL_PREFIX}$labelSequenceNumber"
|
||||
return "${GENERATED_LABEL_PREFIX}$labelSequenceNumber"
|
||||
}
|
||||
|
||||
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult
|
||||
@@ -1913,4 +1936,28 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
|
||||
|
||||
fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
|
||||
val chunk = IRCodeChunk(null, null)
|
||||
when(registerOrFlag.registerOrPair) {
|
||||
RegisterOrPair.A -> chunk += IRInstruction(Opcode.STOREHA, IRDataType.BYTE, reg1=resultReg)
|
||||
RegisterOrPair.X -> chunk += IRInstruction(Opcode.STOREHX, IRDataType.BYTE, reg1=resultReg)
|
||||
RegisterOrPair.Y -> chunk += IRInstruction(Opcode.STOREHY, IRDataType.BYTE, reg1=resultReg)
|
||||
RegisterOrPair.AX -> chunk += IRInstruction(Opcode.STOREHAX, IRDataType.WORD, reg1=resultReg)
|
||||
RegisterOrPair.AY -> chunk += IRInstruction(Opcode.STOREHAY, IRDataType.WORD, reg1=resultReg)
|
||||
RegisterOrPair.XY -> chunk += IRInstruction(Opcode.STOREHXY, IRDataType.WORD, reg1=resultReg)
|
||||
RegisterOrPair.FAC1 -> chunk += IRInstruction(Opcode.STOREHFACZERO, IRDataType.FLOAT, fpReg1 = resultFpReg)
|
||||
RegisterOrPair.FAC2 -> chunk += IRInstruction(Opcode.STOREHFACONE, IRDataType.FLOAT, fpReg1 = resultFpReg)
|
||||
in Cx16VirtualRegisters -> {
|
||||
chunk += IRInstruction(Opcode.STOREM, paramDt, reg1=resultReg, labelSymbol = "cx16.${registerOrFlag.registerOrPair.toString().lowercase()}")
|
||||
}
|
||||
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 register arg")
|
||||
}
|
||||
return chunk
|
||||
}
|
||||
}
|
||||
@@ -384,15 +384,19 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
}
|
||||
Opcode.AND -> {
|
||||
if (ins.immediate == 0) {
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
|
||||
changed = true
|
||||
} else if (ins.immediate == 255 && ins.type == IRDataType.BYTE) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
} else if (ins.immediate == 65535 && ins.type == IRDataType.WORD) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
when (ins.immediate) {
|
||||
0 -> {
|
||||
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
|
||||
changed = true
|
||||
}
|
||||
255 if ins.type == IRDataType.BYTE -> {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
65535 if ins.type == IRDataType.WORD -> {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
Opcode.OR -> {
|
||||
|
||||
@@ -173,7 +173,7 @@ class IRUnusedCodeRemover(
|
||||
if(chunk!=null)
|
||||
new+=chunk
|
||||
else
|
||||
allLabeledChunks[label]?.let { new += it }
|
||||
allLabeledChunks[label]?.let { c -> new += c }
|
||||
}
|
||||
else
|
||||
new += instr.branchTarget!!
|
||||
@@ -226,7 +226,7 @@ class IRUnusedCodeRemover(
|
||||
chunk.instructions.forEach {
|
||||
if(it.labelSymbol!=null) {
|
||||
val chunkName = it.labelSymbol!!.substringBeforeLast('.')
|
||||
allLabeledChunks[chunkName]?.let { linkedChunks+=it }
|
||||
allLabeledChunks[chunkName]?.let { c -> linkedChunks += c }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.*
|
||||
import prog8.code.core.DataType
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||
val st = IRSymbolTable()
|
||||
if (sourceSt != null) {
|
||||
sourceSt.flat.forEach {
|
||||
when(it.value.type) {
|
||||
StNodeType.STATICVAR -> st.add(convert(it.value as StStaticVariable))
|
||||
StNodeType.MEMVAR -> st.add(convert(it.value as StMemVar))
|
||||
StNodeType.CONSTANT -> st.add(convert(it.value as StConstant))
|
||||
StNodeType.MEMORYSLAB -> st.add(convert(it.value as StMemorySlab))
|
||||
else -> { }
|
||||
}
|
||||
}
|
||||
|
||||
st.validate()
|
||||
|
||||
st.allVariables().forEach { variable ->
|
||||
variable.onetimeInitializationArrayValue?.let {
|
||||
it.forEach { arrayElt ->
|
||||
val addrOfSymbol = arrayElt.addressOfSymbol
|
||||
if (addrOfSymbol != null) {
|
||||
require(addrOfSymbol.contains('.')) {
|
||||
"pointer var in array should be properly scoped: $addrOfSymbol in ${variable.name}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
|
||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
|
||||
fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
|
||||
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
|
||||
else
|
||||
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
||||
|
||||
if('.' in variable.name) {
|
||||
return IRStStaticVariable(variable.name,
|
||||
variable.dt,
|
||||
variable.initializationNumericValue,
|
||||
variable.initializationStringValue,
|
||||
variable.initializationArrayValue?.map { convertArrayElt(it) },
|
||||
variable.length,
|
||||
variable.zpwish,
|
||||
variable.align)
|
||||
} else {
|
||||
fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? {
|
||||
if(array==null)
|
||||
return null
|
||||
val newArray = mutableListOf<IRStArrayElement>()
|
||||
array.forEach {
|
||||
if(it.addressOfSymbol!=null) {
|
||||
val target = variable.lookup(it.addressOfSymbol!!) ?: throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
|
||||
newArray.add(IRStArrayElement(null, null, target.scopedName))
|
||||
} else {
|
||||
newArray.add(convertArrayElt(it))
|
||||
}
|
||||
}
|
||||
return newArray
|
||||
}
|
||||
val scopedName = variable.scopedName
|
||||
return IRStStaticVariable(scopedName,
|
||||
variable.dt,
|
||||
variable.initializationNumericValue,
|
||||
variable.initializationStringValue,
|
||||
fixupAddressOfInArray(variable.initializationArrayValue),
|
||||
variable.length,
|
||||
variable.zpwish,
|
||||
variable.align
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun convert(variable: StMemVar): IRStMemVar {
|
||||
if('.' in variable.name) {
|
||||
return IRStMemVar(
|
||||
variable.name,
|
||||
variable.dt,
|
||||
variable.address,
|
||||
variable.length
|
||||
)
|
||||
} else {
|
||||
val scopedName = try {
|
||||
variable.scopedName
|
||||
} catch (_: UninitializedPropertyAccessException) {
|
||||
variable.name
|
||||
}
|
||||
return IRStMemVar(scopedName, variable.dt, variable.address, variable.length)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun convert(constant: StConstant): IRStConstant {
|
||||
val dt = DataType.forDt(constant.dt)
|
||||
val scopedName = if('.' in constant.name) {
|
||||
constant.name
|
||||
} else {
|
||||
try {
|
||||
constant.scopedName
|
||||
} catch (_: UninitializedPropertyAccessException) {
|
||||
constant.name
|
||||
}
|
||||
}
|
||||
return IRStConstant(scopedName, dt, constant.value)
|
||||
}
|
||||
|
||||
|
||||
private fun convert(variable: StMemorySlab): IRStMemorySlab {
|
||||
return if('.' in variable.name)
|
||||
IRStMemorySlab(variable.name, variable.size, variable.align)
|
||||
else
|
||||
IRStMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
@@ -3,8 +3,8 @@ package prog8.codegen.vm
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.IAssemblyProgram
|
||||
import prog8.code.core.ICodeGeneratorBackend
|
||||
import prog8.code.IAssemblyProgram
|
||||
import prog8.code.ICodeGeneratorBackend
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.codegen.intermediate.IRCodeGen
|
||||
import prog8.intermediate.IRFileWriter
|
||||
|
||||
@@ -66,6 +66,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
override fun print_single_error(errormessage: String) { /* prints nothing in tests */ }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
|
||||
@@ -21,6 +21,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
CompilationOptions.AllZeropageAllowed,
|
||||
floats = false,
|
||||
noSysInit = true,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
|
||||
@@ -23,6 +23,7 @@ class TestVmCodeGen: FunSpec({
|
||||
zpAllowed = CompilationOptions.AllZeropageAllowed,
|
||||
floats = true,
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
@@ -48,7 +49,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"pi",
|
||||
DataType.forDt(BaseDataType.UBYTE),
|
||||
DataType.UBYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
null,
|
||||
@@ -75,7 +76,7 @@ class TestVmCodeGen: FunSpec({
|
||||
))
|
||||
sub.add(PtVariable(
|
||||
"xx",
|
||||
DataType.forDt(BaseDataType.WORD),
|
||||
DataType.WORD,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
null,
|
||||
@@ -85,7 +86,7 @@ class TestVmCodeGen: FunSpec({
|
||||
|
||||
val assign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val target = PtAssignTarget(false, Position.DUMMY).also {
|
||||
val targetIdx = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY).also { idx ->
|
||||
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
|
||||
idx.add(PtIdentifier("main.start.particleX",
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
Position.DUMMY))
|
||||
@@ -93,7 +94,7 @@ class TestVmCodeGen: FunSpec({
|
||||
}
|
||||
it.add(targetIdx)
|
||||
}
|
||||
val value = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)
|
||||
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
|
||||
value.add(PtIdentifier("main.start.particleDX",
|
||||
DataType.arrayFor(BaseDataType.UBYTE),
|
||||
Position.DUMMY))
|
||||
@@ -104,15 +105,15 @@ class TestVmCodeGen: FunSpec({
|
||||
|
||||
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
|
||||
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
prefixAssign.add(prefixTarget)
|
||||
prefixAssign.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
sub.add(prefixAssign)
|
||||
|
||||
val numberAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
numberAssign.add(numberAssignTarget)
|
||||
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
|
||||
@@ -120,10 +121,10 @@ class TestVmCodeGen: FunSpec({
|
||||
|
||||
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
|
||||
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
|
||||
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
|
||||
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
|
||||
}
|
||||
cxregAssign.add(cxregAssignTarget)
|
||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY))
|
||||
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
|
||||
sub.add(cxregAssign)
|
||||
|
||||
block.add(sub)
|
||||
@@ -131,7 +132,7 @@ class TestVmCodeGen: FunSpec({
|
||||
|
||||
// define the "cx16.r0" virtual register
|
||||
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
|
||||
cx16block.add(PtMemMapped("r0", DataType.forDt(BaseDataType.UWORD), 100u, null, Position.DUMMY))
|
||||
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
|
||||
program.add(cx16block)
|
||||
|
||||
val options = getTestOptions()
|
||||
@@ -163,7 +164,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"f1",
|
||||
DataType.forDt(BaseDataType.FLOAT),
|
||||
DataType.FLOAT,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
null,
|
||||
@@ -171,32 +172,32 @@ class TestVmCodeGen: FunSpec({
|
||||
Position.DUMMY
|
||||
))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp1.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp2.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp3.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp4.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
@@ -234,7 +235,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"f1",
|
||||
DataType.forDt(BaseDataType.FLOAT),
|
||||
DataType.FLOAT,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
null,
|
||||
@@ -242,32 +243,32 @@ class TestVmCodeGen: FunSpec({
|
||||
Position.DUMMY
|
||||
))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp3.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp4.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
@@ -301,7 +302,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"f1",
|
||||
DataType.forDt(BaseDataType.FLOAT),
|
||||
DataType.FLOAT,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
null,
|
||||
@@ -309,16 +310,16 @@ class TestVmCodeGen: FunSpec({
|
||||
Position.DUMMY
|
||||
))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||
if1.add(PtNodeGroup())
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
|
||||
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
|
||||
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||
@@ -356,7 +357,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"sb1",
|
||||
DataType.forDt(BaseDataType.BYTE),
|
||||
DataType.BYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
null,
|
||||
@@ -364,32 +365,32 @@ class TestVmCodeGen: FunSpec({
|
||||
Position.DUMMY
|
||||
))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp1.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp2.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp3.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp4.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
@@ -427,7 +428,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"sb1",
|
||||
DataType.forDt(BaseDataType.BYTE),
|
||||
DataType.BYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
null,
|
||||
@@ -435,32 +436,32 @@ class TestVmCodeGen: FunSpec({
|
||||
Position.DUMMY
|
||||
))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp1.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp2.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if2)
|
||||
val if3 = PtIfElse(Position.DUMMY)
|
||||
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
|
||||
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp3.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||
if3.add(cmp3)
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
sub.add(if3)
|
||||
val if4 = PtIfElse(Position.DUMMY)
|
||||
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
|
||||
val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
|
||||
cmp4.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
|
||||
if4.add(cmp4)
|
||||
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
|
||||
@@ -494,7 +495,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
sub.add(PtVariable(
|
||||
"ub1",
|
||||
DataType.forDt(BaseDataType.BYTE),
|
||||
DataType.BYTE,
|
||||
ZeropageWish.DONTCARE,
|
||||
0u,
|
||||
null,
|
||||
@@ -502,16 +503,16 @@ class TestVmCodeGen: FunSpec({
|
||||
Position.DUMMY
|
||||
))
|
||||
val if1 = PtIfElse(Position.DUMMY)
|
||||
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
|
||||
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
|
||||
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||
cmp1.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
|
||||
if1.add(cmp1)
|
||||
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||
if1.add(PtNodeGroup())
|
||||
sub.add(if1)
|
||||
val if2 = PtIfElse(Position.DUMMY)
|
||||
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
|
||||
val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
|
||||
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
|
||||
cmp2.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
|
||||
if2.add(cmp2)
|
||||
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also {it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
|
||||
@@ -542,7 +543,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val extsub = PtAsmSub("routine", PtAsmSub.Address(null, null, 0x5000u), setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
|
||||
block.add(extsub)
|
||||
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
|
||||
val call = PtFunctionCall("main.routine", true, DataType.forDt(BaseDataType.UNDEFINED), Position.DUMMY)
|
||||
val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
|
||||
sub.add(call)
|
||||
block.add(sub)
|
||||
program.add(block)
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.UndefinedSymbolError
|
||||
import prog8.ast.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
@@ -457,7 +452,7 @@ internal class ConstantIdentifierReplacer(
|
||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
||||
if(constRange!=null) {
|
||||
val rangeType = rangeExpr.inferType(program).getOr(DataType.forDt(BaseDataType.UBYTE))
|
||||
val rangeType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
|
||||
return if(rangeType.isByte) {
|
||||
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
|
||||
constRange.map { NumericLiteral(rangeType.base, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
||||
|
||||
@@ -531,7 +531,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
// useless msb() of byte value that was typecasted to word, replace with 0
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
functionCallExpr,
|
||||
NumericLiteral(valueDt.getOr(DataType.forDt(BaseDataType.UBYTE)).base, 0.0, arg.expression.position),
|
||||
NumericLiteral(valueDt.getOr(DataType.UBYTE).base, 0.0, arg.expression.position),
|
||||
parent))
|
||||
}
|
||||
} else {
|
||||
@@ -546,7 +546,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
// useless msb() of byte value, replace with 0
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
functionCallExpr,
|
||||
NumericLiteral(argDt.getOr(DataType.forDt(BaseDataType.UBYTE)).base, 0.0, arg.position),
|
||||
NumericLiteral(argDt.getOr(DataType.UBYTE).base, 0.0, arg.position),
|
||||
parent))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,11 @@ fun Program.constantFold(errors: IErrorReporter, options: CompilationOptions) {
|
||||
|
||||
val optimizer = ConstantFoldingOptimizer(this, errors)
|
||||
optimizer.visit(this)
|
||||
while (errors.noErrors() && optimizer.applyModifications() > 0) {
|
||||
var tries=0
|
||||
while (errors.noErrors() && optimizer.applyModifications() > 0 && tries++ < 100000) {
|
||||
optimizer.visit(this)
|
||||
}
|
||||
require(tries<100000) { "endless loop in constantfold" }
|
||||
|
||||
if (errors.noErrors()) {
|
||||
replacer.visit(this)
|
||||
|
||||
@@ -13,7 +13,7 @@ import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.target.VMTarget
|
||||
|
||||
|
||||
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.values.size==0
|
||||
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.values.isEmpty()
|
||||
|
||||
|
||||
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
|
||||
@@ -45,7 +45,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
else if (stmt.values[0] is IdentifierReference) {
|
||||
makeFullyScoped(stmt.values[0] as IdentifierReference)
|
||||
true
|
||||
} else if (stmt.values[0]!! is IFunctionCall && (stmt.values[0] as IFunctionCall).args.size <= 1 && (stmt.values[0] as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
|
||||
} else if (stmt.values[0] is IFunctionCall && (stmt.values[0] as IFunctionCall).args.size <= 1 && (stmt.values[0] as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
|
||||
if (stmt.values[0] is FunctionCallExpression) {
|
||||
makeFullyScoped(stmt.values[0] as FunctionCallExpression)
|
||||
true
|
||||
|
||||
@@ -5,11 +5,7 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.AssociativeOperators
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
class StatementOptimizer(private val program: Program,
|
||||
@@ -315,17 +311,6 @@ class StatementOptimizer(private val program: Program,
|
||||
val bexpr=assignment.value as? BinaryExpression
|
||||
if(bexpr!=null) {
|
||||
val rightCv = bexpr.right.constValue(program)?.number
|
||||
if(bexpr.operator=="-" && rightCv==null && targetIDt.isInteger) {
|
||||
if(bexpr.right.isSimple && bexpr.right isSameAs assignment.target) {
|
||||
// X = value - X --> X = -X ; X += value (to avoid need of stack-evaluation, for integers)
|
||||
val negation = PrefixExpression("-", bexpr.right.copy(), bexpr.position)
|
||||
val addValue = Assignment(assignment.target.copy(), BinaryExpression(bexpr.right, "+", bexpr.left, bexpr.position), AssignmentOrigin.OPTIMIZER, assignment.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(bexpr, negation, assignment),
|
||||
IAstModification.InsertAfter(assignment, addValue, parent as IStatementContainer)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (rightCv != null && assignment.target isSameAs bexpr.left) {
|
||||
// assignments of the form: X = X <operator> <expr>
|
||||
|
||||
@@ -10,7 +10,7 @@ import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.internedStringsModuleName
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.compiler.CallGraph
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ class UnusedCodeRemover(private val program: Program,
|
||||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||
if("force_output" !in block.options()) {
|
||||
if (block.containsNoCodeNorVars) {
|
||||
if (block.name != internedStringsModuleName && "ignore_unused" !in block.options()) {
|
||||
if (block.name != INTERNED_STRINGS_MODULENAME && "ignore_unused" !in block.options()) {
|
||||
if (!block.statements.any { it is Subroutine && it.hasBeenInlined })
|
||||
errors.info("removing unused block '${block.name}'", block.position)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
implementation(project(":simpleAst"))
|
||||
implementation(project(":codeOptimizers"))
|
||||
implementation(project(":compilerAst"))
|
||||
implementation(project(":codeGenCpu6502"))
|
||||
|
||||
@@ -17,13 +17,14 @@
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="simpleAst" />
|
||||
<orderEntry type="module" module-name="compilerAst" />
|
||||
<orderEntry type="module" module-name="codeOptimizers" />
|
||||
<orderEntry type="module" module-name="codeGenCpu6502" />
|
||||
<orderEntry type="module" module-name="codeGenExperimental" />
|
||||
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||
<orderEntry type="module" module-name="virtualmachine" />
|
||||
<orderEntry type="module" module-name="intermediate" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="library" name="antlr.antlr4" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,4 +1,7 @@
|
||||
%import shared_cbm_diskio
|
||||
|
||||
; No alterations here; everything is taken from the shared module.
|
||||
diskio {
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
; No alterations here; everything is taken from the shared module.
|
||||
}
|
||||
@@ -459,10 +459,12 @@ sys {
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta save_SCRATCH_ZPWORD2+1
|
||||
rts
|
||||
save_SCRATCH_ZPB1 .byte 0
|
||||
save_SCRATCH_ZPREG .byte 0
|
||||
save_SCRATCH_ZPWORD1 .word 0
|
||||
save_SCRATCH_ZPWORD2 .word 0
|
||||
.section BSS
|
||||
save_SCRATCH_ZPB1 .byte ?
|
||||
save_SCRATCH_ZPREG .byte ?
|
||||
save_SCRATCH_ZPWORD1 .word ?
|
||||
save_SCRATCH_ZPWORD2 .word ?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -488,8 +490,8 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
lda #<_irq_handler
|
||||
sta cbm.CINV
|
||||
lda #>_irq_handler
|
||||
@@ -499,8 +501,8 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
cld
|
||||
_modified
|
||||
jsr $ffff ; modified
|
||||
|
||||
jsr _run_custom
|
||||
pha
|
||||
jsr sys.restore_prog8_internals
|
||||
pla
|
||||
@@ -515,6 +517,12 @@ _modified
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -525,8 +533,8 @@ asmsub restore_irq() clobbers(A) {
|
||||
sta cbm.CINV
|
||||
lda #>cbm.IRQDFRT
|
||||
sta cbm.CINV+1
|
||||
lda #0
|
||||
sta c64.IREQMASK ; disable raster irq
|
||||
lda #1
|
||||
sta c64.IREQMASK ; enable raster irq
|
||||
lda #%10000001
|
||||
sta c64.CIA1ICR ; restore CIA1 irq
|
||||
cli
|
||||
@@ -537,8 +545,8 @@ asmsub restore_irq() clobbers(A) {
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr _setup_raster_irq
|
||||
@@ -552,8 +560,8 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
_raster_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
cld
|
||||
_modified
|
||||
jsr $ffff ; modified
|
||||
|
||||
jsr _run_custom
|
||||
pha
|
||||
jsr sys.restore_prog8_internals
|
||||
lda #$ff
|
||||
@@ -567,6 +575,12 @@ _modified
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
.send BSS
|
||||
|
||||
|
||||
_setup_raster_irq
|
||||
pha
|
||||
@@ -881,7 +895,7 @@ _no_msb_size
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcode
|
||||
lda #0
|
||||
rol a
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcodeCarry
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcarry
|
||||
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
||||
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
@@ -1064,9 +1078,11 @@ cx16 {
|
||||
bpl -
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
_cx16_vreg_storage
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word ?,?,?,?,?,?,?,?
|
||||
.word ?,?,?,?,?,?,?,?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -1136,16 +1152,21 @@ asmsub cleanup_at_exit() {
|
||||
sta $ff00 ; default bank 15
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
jsr enable_runstop_and_charsetswitch
|
||||
_exitcodeCarry = *+1
|
||||
lda #0
|
||||
lda _exitcarry
|
||||
lsr a
|
||||
_exitcode = *+1
|
||||
lda #0 ; exit code possibly modified in exit()
|
||||
_exitcodeX = *+1
|
||||
ldx #0
|
||||
_exitcodeY = *+1
|
||||
ldy #0
|
||||
lda _exitcode
|
||||
ldx _exitcodeX
|
||||
ldy _exitcodeY
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
_exitcarry .byte ?
|
||||
_exitcode .byte ?
|
||||
_exitcodeX .byte ?
|
||||
_exitcodeY .byte ?
|
||||
.send BSS
|
||||
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -302,15 +302,16 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
|
||||
asl a
|
||||
tay
|
||||
lda _screenrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
txa
|
||||
clc
|
||||
adc _screenrows,y
|
||||
sta _mod+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ pla
|
||||
_mod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
|
||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||
@@ -326,14 +327,15 @@ asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
clc
|
||||
adc setchr._screenrows,y
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -346,15 +348,16 @@ asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A, Y) {
|
||||
asl a
|
||||
tay
|
||||
lda _colorrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
txa
|
||||
clc
|
||||
adc _colorrows,y
|
||||
sta _mod+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ pla
|
||||
_mod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
|
||||
_colorrows .word $d800 + range(0, 1000, 40)
|
||||
@@ -370,14 +373,15 @@ asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
asl a
|
||||
tay
|
||||
lda setclr._colorrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
clc
|
||||
adc setclr._colorrows,y
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -385,25 +389,28 @@ _mod lda $ffff ; modified
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
%asm {{
|
||||
_charptr = P8ZP_SCRATCH_W1
|
||||
_colorptr = P8ZP_SCRATCH_W2
|
||||
lda row
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _charmod+2
|
||||
sta _charptr+1
|
||||
adc #$d4
|
||||
sta _colormod+2
|
||||
sta _colorptr+1
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc col
|
||||
sta _charmod+1
|
||||
sta _colormod+1
|
||||
sta _charptr
|
||||
sta _colorptr
|
||||
bcc +
|
||||
inc _charmod+2
|
||||
inc _colormod+2
|
||||
inc _charptr+1
|
||||
inc _colorptr+1
|
||||
+ lda character
|
||||
_charmod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (_charptr),y
|
||||
lda charcolor
|
||||
_colormod sta $ffff ; modified
|
||||
sta (_colorptr),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
%import shared_cbm_diskio
|
||||
|
||||
; No alterations here; everything is taken from the shared module.
|
||||
diskio {
|
||||
%option no_symbol_prefixing, ignore_unused
|
||||
|
||||
; No alterations here; everything is taken from the shared module.
|
||||
}
|
||||
@@ -4,8 +4,9 @@ FL_ONE_const .byte 129 ; 1.0
|
||||
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
|
||||
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
|
||||
|
||||
|
||||
floats_temp_var .byte 0,0,0,0,0 ; temporary storage for a float
|
||||
.section BSS
|
||||
floats_temp_var .byte ?,?,?,?,? ; temporary storage for a float
|
||||
.send BSS
|
||||
|
||||
ub2float .proc
|
||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||
@@ -178,10 +179,10 @@ dec_var_f .proc
|
||||
jmp MOVMF
|
||||
.pend
|
||||
|
||||
|
||||
fmath_float1 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
fmath_float2 .byte 0,0,0,0,0 ; storage for a mflpt5 value
|
||||
|
||||
.section BSS
|
||||
fmath_float1 .byte ?,?,?,?,? ; storage for a mflpt5 value
|
||||
fmath_float2 .byte ?,?,?,?,? ; storage for a mflpt5 value
|
||||
.send BSS
|
||||
|
||||
var_fac1_less_f .proc
|
||||
; -- is the float in FAC1 < the variable AY? Result in A. Clobbers X.
|
||||
|
||||
@@ -193,6 +193,7 @@ graphics {
|
||||
|
||||
if length!=0 {
|
||||
%asm {{
|
||||
_pixeladdr = P8ZP_SCRATCH_W1
|
||||
lda p8v_length
|
||||
and #7
|
||||
sta p8v_separate_pixels
|
||||
@@ -203,37 +204,35 @@ graphics {
|
||||
lsr p8v_length+1
|
||||
ror p8v_length
|
||||
lda p8v_pixaddr
|
||||
sta _modified+1
|
||||
sta _pixeladdr
|
||||
lda p8v_pixaddr+1
|
||||
sta _modified+2
|
||||
sta _pixeladdr+1
|
||||
lda p8v_length
|
||||
ora p8v_length+1
|
||||
beq _zero
|
||||
ldy p8v_length
|
||||
sty P8ZP_SCRATCH_B1 ; length
|
||||
ldx #$ff
|
||||
_modified stx $ffff ; modified
|
||||
lda _modified+1
|
||||
_more txa
|
||||
ldy #0
|
||||
sta (_pixeladdr),y
|
||||
lda _pixeladdr
|
||||
clc
|
||||
adc #8
|
||||
sta _modified+1
|
||||
sta _pixeladdr
|
||||
bcc +
|
||||
inc _modified+2
|
||||
+ dey
|
||||
bne _modified
|
||||
inc _pixeladdr+1
|
||||
+ dec P8ZP_SCRATCH_B1 ; length
|
||||
bne _more
|
||||
_zero
|
||||
ldy p8v_separate_pixels
|
||||
beq hline_zero2
|
||||
lda _modified+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda _modified+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
beq _end
|
||||
lda hline_filled_right,y
|
||||
ldy #0
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
jmp hline_zero2
|
||||
ora (_pixeladdr),y
|
||||
sta (_pixeladdr),y
|
||||
_end rts
|
||||
hline_filled_right .byte 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110
|
||||
hline_zero2
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -334,10 +333,10 @@ hline_zero2
|
||||
; @(pixaddr) |= ormask[lsb(px) & 7]
|
||||
; }
|
||||
|
||||
inline asmsub plot(uword plotx @XY, ubyte ploty @A) clobbers (A, X, Y) {
|
||||
inline asmsub plot(uword plotx @AX, ubyte ploty @Y) clobbers (A, X, Y) {
|
||||
%asm {{
|
||||
stx p8b_graphics.p8v_internal_plotx
|
||||
sty p8b_graphics.p8v_internal_plotx+1
|
||||
sta p8b_graphics.p8v_internal_plotx
|
||||
stx p8b_graphics.p8v_internal_plotx+1
|
||||
jsr p8b_graphics.p8s_internal_plot
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -349,10 +349,10 @@ inline asmsub getbanks() -> ubyte @A {
|
||||
; retrieve arguments
|
||||
ldy #$01
|
||||
lda (P8ZP_SCRATCH_W1),y ; grab low byte of target address
|
||||
sta _jmpfar+1
|
||||
sta _jmpfar_vec
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y ; now the high byte
|
||||
sta _jmpfar+2
|
||||
sta _jmpfar_vec+1
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y ; then the target bank
|
||||
sta P8ZP_SCRATCH_B1
|
||||
@@ -377,7 +377,7 @@ inline asmsub getbanks() -> ubyte @A {
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
plp
|
||||
jsr _jmpfar ; do the actual call
|
||||
jsr _jsrfar ; do the actual call
|
||||
; restore bank without clobbering status flags and A register
|
||||
sta P8ZP_SCRATCH_W1
|
||||
php
|
||||
@@ -390,8 +390,13 @@ inline asmsub getbanks() -> ubyte @A {
|
||||
lda P8ZP_SCRATCH_W1
|
||||
plp
|
||||
rts
|
||||
_jsrfar jmp (_jmpfar_vec)
|
||||
|
||||
_jmpfar jmp $0000 ; modified
|
||||
.section BSS
|
||||
_jmpfar_vec .word ?
|
||||
.send BSS
|
||||
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -474,10 +479,12 @@ sys {
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta save_SCRATCH_ZPWORD2+1
|
||||
rts
|
||||
save_SCRATCH_ZPB1 .byte 0
|
||||
save_SCRATCH_ZPREG .byte 0
|
||||
save_SCRATCH_ZPWORD1 .word 0
|
||||
save_SCRATCH_ZPWORD2 .word 0
|
||||
.section BSS
|
||||
save_SCRATCH_ZPB1 .byte ?
|
||||
save_SCRATCH_ZPREG .byte ?
|
||||
save_SCRATCH_ZPWORD1 .word ?
|
||||
save_SCRATCH_ZPWORD2 .word ?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -503,8 +510,8 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
lda #<_irq_handler
|
||||
sta cbm.CINV
|
||||
lda #>_irq_handler
|
||||
@@ -514,8 +521,8 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
cld
|
||||
_modified
|
||||
jsr $ffff ; modified
|
||||
|
||||
jsr _run_custom
|
||||
pha
|
||||
jsr sys.restore_prog8_internals
|
||||
pla
|
||||
@@ -530,6 +537,13 @@ _modified
|
||||
tax
|
||||
pla
|
||||
rti
|
||||
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -552,8 +566,8 @@ asmsub restore_irq() clobbers(A) {
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr _setup_raster_irq
|
||||
@@ -567,8 +581,8 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
_raster_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
cld
|
||||
_modified
|
||||
jsr $ffff ; modified
|
||||
|
||||
jsr _run_custom
|
||||
pha
|
||||
jsr sys.restore_prog8_internals
|
||||
lda #$ff
|
||||
@@ -583,6 +597,12 @@ _modified
|
||||
pla
|
||||
rti
|
||||
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
.send BSS
|
||||
|
||||
_setup_raster_irq
|
||||
pha
|
||||
lda #%01111111
|
||||
@@ -897,7 +917,7 @@ _no_msb_size
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcode
|
||||
lda #0
|
||||
rol a
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcodeCarry
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcarry
|
||||
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
||||
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
@@ -1080,9 +1100,11 @@ cx16 {
|
||||
bpl -
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
_cx16_vreg_storage
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word ?,?,?,?,?,?,?,?
|
||||
.word ?,?,?,?,?,?,?,?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -1168,16 +1190,21 @@ asmsub cleanup_at_exit() {
|
||||
jsr cbm.MEMTOP ; adjust MEMTOP down again
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
jsr enable_runstop_and_charsetswitch
|
||||
_exitcodeCarry = *+1
|
||||
lda #0
|
||||
lda _exitcarry
|
||||
lsr a
|
||||
_exitcode = *+1
|
||||
lda #0 ; exit code possibly modified in exit()
|
||||
_exitcodeX = *+1
|
||||
ldx #0
|
||||
_exitcodeY = *+1
|
||||
ldy #0
|
||||
lda _exitcode
|
||||
ldx _exitcodeX
|
||||
ldy _exitcodeY
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
_exitcarry .byte ?
|
||||
_exitcode .byte ?
|
||||
_exitcodeX .byte ?
|
||||
_exitcodeY .byte ?
|
||||
.send BSS
|
||||
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -305,15 +305,16 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
|
||||
asl a
|
||||
tay
|
||||
lda _screenrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
txa
|
||||
clc
|
||||
adc _screenrows,y
|
||||
sta _mod+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ pla
|
||||
_mod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
|
||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||
@@ -329,14 +330,15 @@ asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
clc
|
||||
adc setchr._screenrows,y
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -349,15 +351,16 @@ asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A, Y) {
|
||||
asl a
|
||||
tay
|
||||
lda _colorrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
txa
|
||||
clc
|
||||
adc _colorrows,y
|
||||
sta _mod+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ pla
|
||||
_mod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
|
||||
_colorrows .word $d800 + range(0, 1000, 40)
|
||||
@@ -373,14 +376,15 @@ asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
asl a
|
||||
tay
|
||||
lda setclr._colorrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
clc
|
||||
adc setclr._colorrows,y
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -388,25 +392,28 @@ _mod lda $ffff ; modified
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
%asm {{
|
||||
_charptr = P8ZP_SCRATCH_W1
|
||||
_colorptr = P8ZP_SCRATCH_W2
|
||||
lda row
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _charmod+2
|
||||
sta _charptr+1
|
||||
adc #$d4
|
||||
sta _colormod+2
|
||||
sta _colorptr+1
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc col
|
||||
sta _charmod+1
|
||||
sta _colormod+1
|
||||
sta _charptr
|
||||
sta _colorptr
|
||||
bcc +
|
||||
inc _charmod+2
|
||||
inc _colormod+2
|
||||
inc _charptr+1
|
||||
inc _colorptr+1
|
||||
+ lda character
|
||||
_charmod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (_charptr),y
|
||||
lda charcolor
|
||||
_colormod sta $ffff ; modified
|
||||
sta (_colorptr),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ conv {
|
||||
|
||||
; ----- number conversions to decimal strings ----
|
||||
|
||||
str @shared string_out = "????????????????" ; result buffer for the string conversion routines
|
||||
ubyte[16] @shared string_out ; result buffer for the string conversion routines (note: uses uninitialized ARRAY instead of STR, to force it to be allocated in BSS area so it's ROM-compatible)
|
||||
|
||||
asmsub str_ub0(ubyte value @A) clobbers(X) -> str @AY {
|
||||
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
|
||||
@@ -402,7 +402,9 @@ _digit
|
||||
+ iny
|
||||
bne _parse
|
||||
; never reached
|
||||
_negative .byte 0
|
||||
.section BSS
|
||||
_negative .byte ?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -699,12 +701,14 @@ ShiftedBcdTab
|
||||
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
|
||||
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
|
||||
|
||||
decTenThousands .byte 0
|
||||
decThousands .byte 0
|
||||
decHundreds .byte 0
|
||||
decTens .byte 0
|
||||
decOnes .byte 0
|
||||
.byte 0 ; zero-terminate the decimal output string
|
||||
.section BSS
|
||||
decTenThousands .byte ?
|
||||
decThousands .byte ?
|
||||
decHundreds .byte ?
|
||||
decTens .byte ?
|
||||
decOnes .byte ?
|
||||
.byte ? ; zero-terminate the decimal output string, set to 0 by bss init mechanisms
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -756,7 +760,9 @@ asmsub internal_uword2hex (uword value @AY) clobbers(A,Y) {
|
||||
sta output+2
|
||||
sty output+3
|
||||
rts
|
||||
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
|
||||
.section BSS
|
||||
output .fill 5 ; 0-terminated output buffer (to make printing easier)
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
; - 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!!!
|
||||
|
||||
coroutines {
|
||||
%option ignore_unused
|
||||
|
||||
@@ -4,12 +4,16 @@
|
||||
; NOTE: If you experience weird behavior with these routines and you are using them
|
||||
; in the X16 emulator using HostFs, please try again with an SD-card image instead first.
|
||||
; It is possible that there are still small differences between HostFS and actual CBM DOS in the emulator.
|
||||
;
|
||||
|
||||
; About the secondary addresses:
|
||||
; for writes (new files or overwrites), you can use 1 (without the mode string) or 2-14 (with the mode string)
|
||||
; for reads (existing files) you can use 0 or 2-14 (mode string is optional)
|
||||
; for modify mode (new files* or existing files), you must use 2-14, and the mode string ,s,m is required
|
||||
|
||||
; About the Dos/Drive error status message:
|
||||
; The routines don't usually read/clear the dos/drive error status and message. ("blinking red led")
|
||||
; In case of an error, you usually have to read/clear that yourself (with status() for example).
|
||||
|
||||
|
||||
%import textio
|
||||
%import conv
|
||||
@@ -21,6 +25,7 @@ diskio {
|
||||
|
||||
const ubyte READ_IO_CHANNEL=12
|
||||
const ubyte WRITE_IO_CHANNEL=13
|
||||
const ubyte STATUS_EOF=$40
|
||||
|
||||
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
||||
|
||||
@@ -94,7 +99,7 @@ io_error:
|
||||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
|
||||
if status!=0 and status & $40 == 0 { ; bit 6=end of file
|
||||
if status!=0 and status & STATUS_EOF == 0 { ; bit 6=end of file
|
||||
txt.print("\ni/o error, status: ")
|
||||
txt.print_ub(status)
|
||||
txt.nl()
|
||||
@@ -346,7 +351,7 @@ close_end:
|
||||
if cbm.READST()==0 {
|
||||
iteration_in_progress = true
|
||||
void cbm.CHRIN() ; read first byte to test for file not found
|
||||
if cbm.READST()==0 {
|
||||
if cbm.READST() & ~STATUS_EOF == 0 {
|
||||
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
|
||||
void cbm.OPEN() ; re-open the file
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
@@ -375,7 +380,7 @@ close_end:
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
if cx16.r0L!=0 {
|
||||
f_close()
|
||||
if cx16.r0L & $40 == 0
|
||||
if cx16.r0L & STATUS_EOF == 0
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
@@ -395,7 +400,7 @@ close_end:
|
||||
if msb(bufferpointer) == $c0
|
||||
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
|
||||
num_bytes -= readsize
|
||||
if cbm.READST() & $40 !=0 {
|
||||
if cbm.READST() & STATUS_EOF !=0 {
|
||||
f_close() ; end of file, close it
|
||||
break
|
||||
}
|
||||
@@ -406,39 +411,39 @@ close_end:
|
||||
byte_read_loop: ; fallback if MACPTR isn't supported on the device
|
||||
%asm {{
|
||||
lda bufferpointer
|
||||
sta m_in_buffer+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda bufferpointer+1
|
||||
sta m_in_buffer+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
}}
|
||||
while num_bytes!=0 {
|
||||
%asm {{
|
||||
jsr cbm.CHRIN
|
||||
m_in_buffer sta $ffff
|
||||
inc m_in_buffer+1
|
||||
sta (P8ZP_SCRATCH_W1)
|
||||
inc P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
inc m_in_buffer+2
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+
|
||||
}}
|
||||
list_blocks++
|
||||
cx16.r0L = cbm.READST()
|
||||
if_nz {
|
||||
f_close()
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
if cx16.r0L & $40 !=0 ; eof?
|
||||
if cx16.r0L & STATUS_EOF !=0 ; eof?
|
||||
return list_blocks ; number of bytes read
|
||||
return 0 ; error.
|
||||
}
|
||||
list_blocks++
|
||||
num_bytes--
|
||||
}
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
return list_blocks ; number of bytes read
|
||||
}
|
||||
|
||||
; optimized for Commander X16 to use MACPTR block read kernal call
|
||||
sub f_read_all(uword bufferpointer) -> uword {
|
||||
; -- read the full contents of the file, returns number of bytes read.
|
||||
; -- read the full rest of the file, returns number of bytes read.
|
||||
; It is assumed the file size is less than 64 K.
|
||||
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.MACPTR() yourself with the vera data register as address.
|
||||
; Usually you will just be using load() / load_raw() or vload() / vload_raw() to read entire files!
|
||||
if not iteration_in_progress
|
||||
return 0
|
||||
|
||||
@@ -446,6 +451,8 @@ m_in_buffer sta $ffff
|
||||
uword total_read = 0
|
||||
while cbm.READST()==0 {
|
||||
cx16.r0 = f_read(bufferpointer, 256)
|
||||
if cx16.r0==0
|
||||
break
|
||||
total_read += cx16.r0
|
||||
bufferpointer += cx16.r0
|
||||
}
|
||||
@@ -724,6 +731,12 @@ io_error:
|
||||
}
|
||||
|
||||
|
||||
; Load a prog8 compiled library binary blob at the given location into memory.
|
||||
sub loadlib(uword libnameptr, uword libaddress) -> uword {
|
||||
return internal_load_routine(libnameptr, libaddress, true)
|
||||
}
|
||||
|
||||
|
||||
sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword {
|
||||
cbm.SETNAM(strings.length(filenameptr), filenameptr)
|
||||
ubyte secondary = 1
|
||||
@@ -941,7 +954,7 @@ internal_vload:
|
||||
io_error:
|
||||
cbm.CLRCHN()
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
if status!=0 and status & $40 == 0
|
||||
if status!=0 and status & STATUS_EOF == 0
|
||||
return 0
|
||||
if @(cx16.r12)==0 {
|
||||
cx16.r12--
|
||||
@@ -973,6 +986,7 @@ io_error:
|
||||
sub exists(str filename) -> bool {
|
||||
; -- returns true if the given file exists on the disk, otherwise false
|
||||
; DON'T use this if you already have a file open with f_open!
|
||||
; NOTE: doesn't clear the dos error status and message, you'll have to read/clear that yourself (with status() for example)
|
||||
if f_open(filename) {
|
||||
f_close()
|
||||
return true
|
||||
|
||||
@@ -154,9 +154,9 @@ gfx_hires {
|
||||
inc cx16.VERA_ADDR_L
|
||||
bne ++
|
||||
inc cx16.VERA_ADDR_M
|
||||
+ bne +
|
||||
+ bne +
|
||||
inc cx16.VERA_ADDR_H
|
||||
+ inx ; next pixel
|
||||
+ inx ; next pixel
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,6 +257,7 @@ palette {
|
||||
|
||||
sub set_default16() {
|
||||
; set first 16 colors to the defaults on the X16
|
||||
; (doesn't use the rom table so this works on roms older than 49 as well)
|
||||
uword[] @nosplit colors = [
|
||||
$000, ; 0 = black
|
||||
$fff, ; 1 = white
|
||||
@@ -277,4 +278,12 @@ palette {
|
||||
]
|
||||
set_rgb_nosplit(colors, len(colors), 0)
|
||||
}
|
||||
|
||||
; set the full 256 colors in the palette back to its default values on the X16
|
||||
; NOTE: this routine requires rom version 49+
|
||||
alias set_default = cx16.set_default_palette
|
||||
|
||||
; get the bank and address of the word-array containing the 256 default palette colors
|
||||
; NOTE: this routine requires rom version 49+
|
||||
alias get_default = cx16.get_default_palette
|
||||
}
|
||||
@@ -15,14 +15,22 @@ psg {
|
||||
const ubyte NOISE = %11000000
|
||||
const ubyte LEFT = %01000000
|
||||
const ubyte RIGHT = %10000000
|
||||
const ubyte DISABLED = %11111111
|
||||
|
||||
sub init() {
|
||||
; -- initializes the psg module (all 16 voices set to disabled)
|
||||
for cx16.r1L in 0 to 15
|
||||
voice_enabled[cx16.r1L] = false
|
||||
}
|
||||
|
||||
sub voice(ubyte voice_num, ubyte channel, ubyte vol, ubyte waveform, ubyte pulsewidth) {
|
||||
; -- Enables a 'voice' on the PSG.
|
||||
; voice_num = 0-15, the voice number.
|
||||
; channel = either LEFT or RIGHT or (LEFT|RIGHT). Specifies the stereo channel(s) to use.
|
||||
; channel = either LEFT or RIGHT or (LEFT|RIGHT). Specifies the stereo channel(s) to use. DISABLED=disable the voice.
|
||||
; vol = 0-63, the starting volume for the voice
|
||||
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
|
||||
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
|
||||
voice_enabled[voice_num] = false
|
||||
envelope_states[voice_num] = 255
|
||||
sys.irqsafe_set_irqd()
|
||||
cx16.r0 = $f9c2 + voice_num * 4
|
||||
@@ -30,11 +38,19 @@ psg {
|
||||
cx16.VERA_ADDR_L = lsb(cx16.r0)
|
||||
cx16.VERA_ADDR_M = msb(cx16.r0)
|
||||
cx16.VERA_ADDR_H = 1
|
||||
cx16.VERA_DATA0 = channel | vol
|
||||
cx16.VERA_ADDR_L++
|
||||
cx16.VERA_DATA0 = waveform | pulsewidth
|
||||
envelope_volumes[voice_num] = mkword(vol, 0)
|
||||
envelope_maxvolumes[voice_num] = vol
|
||||
if channel!=DISABLED {
|
||||
cx16.VERA_DATA0 = channel | vol
|
||||
cx16.VERA_ADDR_L++
|
||||
cx16.VERA_DATA0 = waveform | pulsewidth
|
||||
envelope_volumes[voice_num] = mkword(vol, 0)
|
||||
envelope_maxvolumes[voice_num] = vol
|
||||
voice_enabled[voice_num] = true
|
||||
} else {
|
||||
cx16.VERA_DATA0 = 0
|
||||
envelope_volumes[voice_num] = 0
|
||||
envelope_maxvolumes[voice_num] = 0
|
||||
voice_enabled[voice_num] = false
|
||||
}
|
||||
sys.irqsafe_clear_irqd()
|
||||
}
|
||||
|
||||
@@ -99,10 +115,12 @@ psg {
|
||||
}
|
||||
|
||||
sub silent() {
|
||||
; -- Shut down all PSG voices.
|
||||
; -- Silence all active PSG voices.
|
||||
for cx16.r1L in 0 to 15 {
|
||||
envelope_states[cx16.r1L] = 255
|
||||
volume(cx16.r1L, 0)
|
||||
if voice_enabled[cx16.r1L] {
|
||||
envelope_states[cx16.r1L] = 255
|
||||
volume(cx16.r1L, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +140,8 @@ psg {
|
||||
sys.pushw(cx16.r9)
|
||||
; calculate new volumes
|
||||
for cx16.r1L in 0 to 15 {
|
||||
if not voice_enabled[cx16.r1L]
|
||||
continue
|
||||
when envelope_states[cx16.r1L] {
|
||||
0 -> {
|
||||
; attack
|
||||
@@ -165,7 +185,10 @@ psg {
|
||||
cx16.VERA_ADDR_M = $f9
|
||||
cx16.VERA_ADDR_H = 1 | %00110000
|
||||
for cx16.r1L in 0 to 15 {
|
||||
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
|
||||
if voice_enabled[cx16.r1L]
|
||||
cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L])
|
||||
else
|
||||
cx16.VERA_DATA0 = cx16.VERA_DATA1
|
||||
}
|
||||
cx16.restore_vera_context()
|
||||
cx16.r9 = sys.popw()
|
||||
@@ -175,6 +198,7 @@ psg {
|
||||
return true ; run the system IRQ handler afterwards
|
||||
}
|
||||
|
||||
bool[16] voice_enabled
|
||||
ubyte[16] envelope_states
|
||||
uword[16] envelope_volumes ; scaled by 256
|
||||
ubyte[16] envelope_attacks
|
||||
|
||||
@@ -332,7 +332,7 @@ cx16 {
|
||||
&ubyte VERA_FX_X_POS_S = VERA_BASE + $0009
|
||||
&ubyte VERA_FX_Y_POS_S = VERA_BASE + $000a
|
||||
&ubyte VERA_FX_POLY_FILL_L = VERA_BASE + $000b
|
||||
&ubyte VERA_FX_POLY_FILL_H = VERA_BASE + $000cF
|
||||
&ubyte VERA_FX_POLY_FILL_H = VERA_BASE + $000c
|
||||
&uword VERA_FX_POLY_FILL = VERA_BASE + $000b
|
||||
&ubyte VERA_FX_CACHE_L = VERA_BASE + $0009
|
||||
&ubyte VERA_FX_CACHE_M = VERA_BASE + $000a
|
||||
@@ -558,6 +558,7 @@ const ubyte EXTAPI_mouse_set_position = $0C
|
||||
const ubyte EXTAPI_scnsiz = $0D
|
||||
const ubyte EXTAPI_kbd_leds = $0E
|
||||
const ubyte EXTAPI_memory_decompress_from_func = $0F
|
||||
const ubyte EXTAPI_default_palette = $10
|
||||
|
||||
; extapi16 call numbers
|
||||
const ubyte EXTAPI16_test = $00
|
||||
@@ -668,6 +669,34 @@ asmsub scnsiz(ubyte width @X, ubyte heigth @Y) clobbers(A,X,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub memory_decompress_from_func(uword datafunction @R4, uword output @R1) clobbers(A,X,Y) -> uword @R1 {
|
||||
; requires rom v49+
|
||||
%asm {{
|
||||
lda #EXTAPI_memory_decompress_from_func
|
||||
jmp cx16.extapi
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_default_palette() -> ubyte @A, uword @XY {
|
||||
; returns memory address of default palette; bank in A and address in XY
|
||||
; requires rom v49+
|
||||
%asm {{
|
||||
sec
|
||||
lda #EXTAPI_default_palette
|
||||
jmp cx16.extapi
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_default_palette() {
|
||||
; sets default palette in to the Vera
|
||||
; requires rom v49+
|
||||
%asm {{
|
||||
clc
|
||||
lda #EXTAPI_default_palette
|
||||
jmp cx16.extapi
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_charset() -> ubyte @A {
|
||||
;Get charset mode. result: 0=unknown, 1=ISO, 2=PETSCII upper case/gfx, 3=PETSCII lowercase.
|
||||
%asm {{
|
||||
@@ -728,6 +757,42 @@ inline asmsub getrambank() -> ubyte @A {
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_rombank(ubyte newbank @A) clobbers(Y) {
|
||||
; push the current rombank on the stack and makes the given rom bank active
|
||||
; combined with pop_rombank() makes for easy temporary rom bank switch
|
||||
%asm {{
|
||||
ldy $01
|
||||
phy
|
||||
sta $01
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop_rombank() {
|
||||
; sets the current rom bank back to what was stored previously on the stack
|
||||
%asm {{
|
||||
pla
|
||||
sta $01
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub push_rambank(ubyte newbank @A) clobbers(Y) {
|
||||
; push the current hiram bank on the stack and makes the given hiram bank active
|
||||
; combined with pop_rombank() makes for easy temporary hiram bank switch
|
||||
%asm {{
|
||||
ldy $00
|
||||
phy
|
||||
sta $00
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pop_rambank() {
|
||||
; sets the current hiram bank back to what was stored previously on the stack
|
||||
%asm {{
|
||||
pla
|
||||
sta $00
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub numbanks() clobbers(X) -> uword @AY {
|
||||
; -- Returns the number of available RAM banks according to the kernal (each bank is 8 Kb).
|
||||
; Note that the number of such banks can be 256 so a word is returned.
|
||||
@@ -989,9 +1054,11 @@ asmsub save_virtual_registers() clobbers(A,Y) {
|
||||
bpl -
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
_cx16_vreg_storage
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word ?,?,?,?,?,?,?,?
|
||||
.word ?,?,?,?,?,?,?,?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -1030,7 +1097,9 @@ asmsub save_vera_context() clobbers(A) {
|
||||
lda cx16.VERA_ADDR_H
|
||||
sta _vera_storage+6
|
||||
rts
|
||||
_vera_storage: .byte 0,0,0,0,0,0,0,0
|
||||
.section BSS
|
||||
_vera_storage: .byte ?,?,?,?,?,?,?,?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -1125,9 +1194,30 @@ asmsub enable_irq_handlers(bool disable_all_irq_sources @Pc) clobbers(A,Y) {
|
||||
ldy #>_irq_dispatcher
|
||||
sta cbm.CINV
|
||||
sty cbm.CINV+1
|
||||
|
||||
lda #<_default_1_handler
|
||||
ldy #>_default_1_handler
|
||||
sta _vsync_vec
|
||||
sty _vsync_vec+1
|
||||
lda #<_default_0_handler
|
||||
ldy #>_default_0_handler
|
||||
sta _line_vec
|
||||
sty _line_vec+1
|
||||
sta _aflow_vec
|
||||
sty _aflow_vec+1
|
||||
sta _sprcol_vec
|
||||
sty _sprcol_vec+1
|
||||
|
||||
plp
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
_vsync_vec .word ?
|
||||
_line_vec .word ?
|
||||
_aflow_vec .word ?
|
||||
_sprcol_vec .word ?
|
||||
.send BSS
|
||||
|
||||
_irq_dispatcher
|
||||
; order of handling: LINE, SPRCOL, AFLOW, VSYNC.
|
||||
jsr sys.save_prog8_internals
|
||||
@@ -1137,31 +1227,27 @@ _irq_dispatcher
|
||||
|
||||
bit #2
|
||||
beq +
|
||||
_mod_line_jump
|
||||
jsr _default_line_handler ; modified
|
||||
jsr _line_handler
|
||||
ldy #2
|
||||
sty cx16.VERA_ISR
|
||||
bra _dispatch_end
|
||||
+
|
||||
bit #4
|
||||
beq +
|
||||
_mod_sprcol_jump
|
||||
jsr _default_sprcol_handler ; modified
|
||||
jsr _sprcol_handler
|
||||
ldy #4
|
||||
sty cx16.VERA_ISR
|
||||
bra _dispatch_end
|
||||
+
|
||||
bit #8
|
||||
beq +
|
||||
_mod_aflow_jump
|
||||
jsr _default_aflow_handler ; modified
|
||||
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 +
|
||||
_mod_vsync_jump
|
||||
jsr _default_vsync_handler ; modified
|
||||
jsr _vsync_handler
|
||||
cmp #0
|
||||
bne _dispatch_end
|
||||
ldy #1
|
||||
@@ -1181,18 +1267,21 @@ _return_irq
|
||||
pla
|
||||
rti
|
||||
|
||||
_default_vsync_handler
|
||||
_default_0_handler
|
||||
lda #0
|
||||
rts
|
||||
_default_1_handler
|
||||
lda #1
|
||||
rts
|
||||
_default_line_handler
|
||||
lda #0
|
||||
rts
|
||||
_default_sprcol_handler
|
||||
lda #0
|
||||
rts
|
||||
_default_aflow_handler
|
||||
lda #0
|
||||
rts
|
||||
|
||||
_vsync_handler
|
||||
jmp (_vsync_vec)
|
||||
_line_handler
|
||||
jmp (_line_vec)
|
||||
_sprcol_handler
|
||||
jmp (_sprcol_vec)
|
||||
_aflow_handler
|
||||
jmp (_aflow_vec)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -1201,8 +1290,8 @@ asmsub set_vsync_irq_handler(uword address @AY) clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta enable_irq_handlers._mod_vsync_jump+1
|
||||
sty enable_irq_handlers._mod_vsync_jump+2
|
||||
sta enable_irq_handlers._vsync_vec
|
||||
sty enable_irq_handlers._vsync_vec+1
|
||||
lda #1
|
||||
tsb cx16.VERA_IEN
|
||||
plp
|
||||
@@ -1216,8 +1305,8 @@ asmsub set_line_irq_handler(uword rasterline @R0, uword address @AY) clobbers(A,
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta enable_irq_handlers._mod_line_jump+1
|
||||
sty enable_irq_handlers._mod_line_jump+2
|
||||
sta enable_irq_handlers._line_vec
|
||||
sty enable_irq_handlers._line_vec+1
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr sys.set_rasterline
|
||||
@@ -1233,8 +1322,8 @@ asmsub set_sprcol_irq_handler(uword address @AY) clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta enable_irq_handlers._mod_sprcol_jump+1
|
||||
sty enable_irq_handlers._mod_sprcol_jump+2
|
||||
sta enable_irq_handlers._sprcol_vec
|
||||
sty enable_irq_handlers._sprcol_vec+1
|
||||
lda #4
|
||||
tsb cx16.VERA_IEN
|
||||
plp
|
||||
@@ -1248,8 +1337,8 @@ asmsub set_aflow_irq_handler(uword address @AY) clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta enable_irq_handlers._mod_aflow_jump+1
|
||||
sty enable_irq_handlers._mod_aflow_jump+2
|
||||
sta enable_irq_handlers._aflow_vec
|
||||
sty enable_irq_handlers._aflow_vec+1
|
||||
lda #8
|
||||
tsb cx16.VERA_IEN
|
||||
plp
|
||||
@@ -1356,12 +1445,12 @@ _continue iny
|
||||
|
||||
sub reset_system() {
|
||||
; -- Soft-reset the system back to initial power-on Basic prompt.
|
||||
sys.reset_system()
|
||||
goto sys.reset_system
|
||||
}
|
||||
|
||||
sub poweroff_system() {
|
||||
; -- use the SMC to shutdown the computer
|
||||
void cx16.i2c_write_byte($42, $01, $00)
|
||||
goto sys.poweroff_system
|
||||
}
|
||||
|
||||
sub set_led_state(bool on) {
|
||||
@@ -1417,8 +1506,8 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
; Sets the handler for the VSYNC interrupt, and enable that interrupt.
|
||||
%asm {{
|
||||
sei
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
lda #<_irq_handler
|
||||
sta cbm.CINV
|
||||
lda #>_irq_handler
|
||||
@@ -1431,8 +1520,8 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
cld
|
||||
_modified
|
||||
jsr $ffff ; modified
|
||||
|
||||
jsr _run_custom
|
||||
pha
|
||||
jsr sys.restore_prog8_internals
|
||||
pla
|
||||
@@ -1444,6 +1533,12 @@ _modified
|
||||
plx
|
||||
pla
|
||||
rti
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -1460,7 +1555,9 @@ asmsub restore_irq() clobbers(A) {
|
||||
sta cx16.VERA_IEN
|
||||
cli
|
||||
rts
|
||||
_orig_irqvec .word 0
|
||||
.section BSS_NOCLEAR
|
||||
_orig_irqvec .word ?
|
||||
.send BSS_NOCLEAR
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -1469,8 +1566,8 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
; Sets the handler for the LINE interrupt, and enable (only) that interrupt.
|
||||
%asm {{
|
||||
sei
|
||||
sta _modified+1
|
||||
sty _modified+2
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
lda cx16.VERA_IEN
|
||||
@@ -1490,7 +1587,7 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
_raster_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
cld
|
||||
_modified jsr $ffff ; modified
|
||||
jsr _run_custom
|
||||
jsr sys.restore_prog8_internals
|
||||
; end irq processing - don't use kernal's irq handling
|
||||
lda #2
|
||||
@@ -1499,6 +1596,13 @@ _modified jsr $ffff ; modified
|
||||
plx
|
||||
pla
|
||||
rti
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
.send BSS
|
||||
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -1527,16 +1631,21 @@ asmsub set_rasterline(uword line @AY) {
|
||||
; (note: this is an asmsub on purpose! don't change into a normal sub)
|
||||
%asm {{
|
||||
sei
|
||||
ldx #$42
|
||||
- ldx #$42
|
||||
ldy #2
|
||||
lda #0
|
||||
jmp cx16.i2c_write_byte
|
||||
jsr cx16.i2c_write_byte
|
||||
bra - ; to work around an issue where the routine does not in fact immediately reset the system
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
sub poweroff_system() {
|
||||
; use the SMC to shutdown the computer
|
||||
void cx16.i2c_write_byte($42, $01, $00)
|
||||
; in a loop for the event where the shutdown command does not in fact immediately shutdown the system
|
||||
repeat {
|
||||
void cx16.i2c_write_byte($42, $01, $00)
|
||||
}
|
||||
}
|
||||
|
||||
asmsub wait(uword jiffies @AY) clobbers(X) {
|
||||
@@ -1784,10 +1893,12 @@ _no_msb_size
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta save_SCRATCH_ZPWORD2+1
|
||||
rts
|
||||
save_SCRATCH_ZPB1 .byte 0
|
||||
save_SCRATCH_ZPREG .byte 0
|
||||
save_SCRATCH_ZPWORD1 .word 0
|
||||
save_SCRATCH_ZPWORD2 .word 0
|
||||
.section BSS
|
||||
save_SCRATCH_ZPB1 .byte ?
|
||||
save_SCRATCH_ZPREG .byte ?
|
||||
save_SCRATCH_ZPWORD1 .word ?
|
||||
save_SCRATCH_ZPWORD2 .word ?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -1838,7 +1949,7 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcode
|
||||
lda #0
|
||||
rol a
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcodeCarry
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcarry
|
||||
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
||||
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
@@ -1976,16 +2087,21 @@ asmsub cleanup_at_exit() {
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
lda #9
|
||||
jsr cbm.CHROUT ; enable charset switch
|
||||
_exitcodeCarry = *+1
|
||||
lda #0
|
||||
lda _exitcarry
|
||||
lsr a
|
||||
_exitcode = *+1
|
||||
lda #0 ; exit code possibly modified in exit()
|
||||
_exitcodeX = *+1
|
||||
ldx #0
|
||||
_exitcodeY = *+1
|
||||
ldy #0
|
||||
lda _exitcode
|
||||
ldx _exitcodeX
|
||||
ldy _exitcodeY
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
_exitcarry .byte ?
|
||||
_exitcode .byte ?
|
||||
_exitcodeX .byte ?
|
||||
_exitcodeY .byte ?
|
||||
.send BSS
|
||||
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,19 +96,21 @@ asmsub get_cursor() -> ubyte @X, ubyte @Y {
|
||||
asmsub fill_screen (ubyte character @ A, ubyte color @ Y) clobbers(A, X) {
|
||||
; ---- fill the character screen with the given fill character and character color.
|
||||
%asm {{
|
||||
sty _ly+1
|
||||
_color = P8ZP_SCRATCH_B1
|
||||
_numrows = P8ZP_SCRATCH_REG
|
||||
sty _color
|
||||
pha
|
||||
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
lsr a
|
||||
lsr a
|
||||
sta _lx+1
|
||||
sta _numrows
|
||||
lda #%00010000
|
||||
jsr set_vera_textmatrix_addresses
|
||||
pla
|
||||
_lx ldx #0 ; modified
|
||||
_more ldx _numrows
|
||||
phy
|
||||
_ly ldy #1 ; modified
|
||||
ldy _color
|
||||
- sta cx16.VERA_DATA0
|
||||
sty cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
@@ -124,7 +126,7 @@ _ly ldy #1 ; modified
|
||||
beq +
|
||||
stz cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M ; next line
|
||||
bra _lx
|
||||
bra _more
|
||||
+ rts
|
||||
|
||||
set_vera_textmatrix_addresses:
|
||||
@@ -148,11 +150,11 @@ asmsub clear_screenchars (ubyte character @ A) clobbers(X, Y) {
|
||||
txa
|
||||
lsr a
|
||||
lsr a
|
||||
sta _lx+1
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda #%00100000
|
||||
jsr fill_screen.set_vera_textmatrix_addresses
|
||||
pla
|
||||
_lx ldx #0 ; modified
|
||||
_more ldx P8ZP_SCRATCH_REG
|
||||
- sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
@@ -163,7 +165,7 @@ _lx ldx #0 ; modified
|
||||
beq +
|
||||
stz cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M ; next line
|
||||
bra _lx
|
||||
bra _more
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
@@ -172,18 +174,20 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(X, Y) {
|
||||
; ---- clear the character screen colors with the given color (leaves characters).
|
||||
; (assumes color matrix is at the default address)
|
||||
%asm {{
|
||||
sta _la+1
|
||||
_color = P8ZP_SCRATCH_B1
|
||||
_numrows = P8ZP_SCRATCH_REG
|
||||
sta _color
|
||||
jsr cbm.SCREEN ; get dimensions in X/Y
|
||||
txa
|
||||
lsr a
|
||||
lsr a
|
||||
sta _lx+1
|
||||
sta _numrows
|
||||
stz cx16.VERA_CTRL
|
||||
lda #%00100000
|
||||
jsr fill_screen.set_vera_textmatrix_addresses
|
||||
inc cx16.VERA_ADDR_L ; start at (1,0) - the color attribute byte
|
||||
_lx ldx #0 ; modified
|
||||
_la lda #0 ; modified
|
||||
_more ldx _numrows
|
||||
lda _color
|
||||
- sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
@@ -195,7 +199,7 @@ _la lda #0 ; modified
|
||||
lda #1
|
||||
sta cx16.VERA_ADDR_L
|
||||
inc cx16.VERA_ADDR_M ; next line
|
||||
bra _lx
|
||||
bra _more
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
@@ -272,7 +276,7 @@ asmsub scroll_left() clobbers(A, X, Y) {
|
||||
%asm {{
|
||||
jsr cbm.SCREEN
|
||||
dex
|
||||
stx _lx+1
|
||||
stx P8ZP_SCRATCH_REG ; columns
|
||||
dey
|
||||
sty P8ZP_SCRATCH_B1 ; number of rows to scroll
|
||||
|
||||
@@ -294,7 +298,7 @@ _nextline
|
||||
stz cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
|
||||
_lx ldx #0 ; modified
|
||||
ldx P8ZP_SCRATCH_REG ; columns
|
||||
- lda cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA1 ; copy char
|
||||
lda cx16.VERA_DATA0
|
||||
@@ -314,16 +318,19 @@ asmsub scroll_right() clobbers(A,X,Y) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
%asm {{
|
||||
_columns = P8ZP_SCRATCH_REG
|
||||
_rcolv1 = P8ZP_SCRATCH_W1
|
||||
_rcolv2 = P8ZP_SCRATCH_W1+1
|
||||
jsr cbm.SCREEN
|
||||
dex
|
||||
stx _lx+1
|
||||
stx _columns
|
||||
txa
|
||||
asl a
|
||||
dea
|
||||
sta _rcol+1
|
||||
sta _rcolv1
|
||||
ina
|
||||
ina
|
||||
sta _rcol2+1
|
||||
sta _rcolv2
|
||||
dey
|
||||
sty P8ZP_SCRATCH_B1 ; number of rows to scroll
|
||||
|
||||
@@ -331,7 +338,7 @@ _nextline
|
||||
stz cx16.VERA_CTRL ; data port 0: source column
|
||||
lda #%00011000 | VERA_TEXTMATRIX>>16 ; auto decrement 1
|
||||
sta cx16.VERA_ADDR_H
|
||||
_rcol lda #79*2-1 ; modified
|
||||
lda _rcolv1
|
||||
sta cx16.VERA_ADDR_L ; begin in rightmost column minus one
|
||||
lda P8ZP_SCRATCH_B1
|
||||
clc
|
||||
@@ -342,11 +349,11 @@ _rcol lda #79*2-1 ; modified
|
||||
sta cx16.VERA_CTRL ; data port 1: destination column
|
||||
lda #%00011000 | VERA_TEXTMATRIX>>16 ; auto decrement 1
|
||||
sta cx16.VERA_ADDR_H
|
||||
_rcol2 lda #79*2+1 ; modified
|
||||
lda _rcolv2
|
||||
sta cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
|
||||
_lx ldx #0 ; modified
|
||||
ldx _columns
|
||||
- lda cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA1 ; copy char
|
||||
lda cx16.VERA_DATA0
|
||||
@@ -367,7 +374,7 @@ asmsub scroll_up() clobbers(A, X, Y) {
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
jsr cbm.SCREEN
|
||||
stx _nextline+1
|
||||
stx P8ZP_SCRATCH_REG ; columns
|
||||
dey
|
||||
sty P8ZP_SCRATCH_B1
|
||||
stz cx16.VERA_CTRL ; data port 0 is source
|
||||
@@ -386,7 +393,7 @@ asmsub scroll_up() clobbers(A, X, Y) {
|
||||
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||
|
||||
_nextline
|
||||
ldx #80 ; modified
|
||||
ldx P8ZP_SCRATCH_REG ; columns
|
||||
- lda cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA1 ; copy char
|
||||
lda cx16.VERA_DATA0
|
||||
@@ -415,7 +422,7 @@ asmsub scroll_down() clobbers(A, X, Y) {
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
%asm {{
|
||||
jsr cbm.SCREEN
|
||||
stx _nextline+1
|
||||
stx P8ZP_SCRATCH_REG ; columns
|
||||
dey
|
||||
sty P8ZP_SCRATCH_B1
|
||||
stz cx16.VERA_CTRL ; data port 0 is source
|
||||
@@ -440,7 +447,7 @@ asmsub scroll_down() clobbers(A, X, Y) {
|
||||
sta cx16.VERA_ADDR_H ; enable auto increment by 1, bank 0.
|
||||
|
||||
_nextline
|
||||
ldx #80 ; modified
|
||||
ldx P8ZP_SCRATCH_REG ; columns
|
||||
- lda cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA1 ; copy char
|
||||
lda cx16.VERA_DATA0
|
||||
|
||||
@@ -180,8 +180,9 @@ _inner_loop2
|
||||
ldy result+1
|
||||
rts
|
||||
|
||||
result .byte 0,0,0,0 ; routine could be faster if this were in Zeropage...
|
||||
|
||||
.section BSS
|
||||
result .byte ?,?,?,? ; routine could be faster if this were in Zeropage...
|
||||
.send BSS
|
||||
.pend
|
||||
|
||||
|
||||
@@ -214,7 +215,9 @@ divmod_b_asm .proc
|
||||
adc #0 ; negate result
|
||||
tay
|
||||
+ rts
|
||||
_remainder .byte 0
|
||||
.section BSS
|
||||
_remainder .byte ?
|
||||
.send BSS
|
||||
.pend
|
||||
|
||||
|
||||
@@ -287,7 +290,6 @@ divmod_uw_asm .proc
|
||||
; output: P8ZP_SCRATCH_W2 in ZP: 16 bit remainder, A/Y: 16 bit division result
|
||||
; division by zero will result in quotient = 65535 and remainder = divident
|
||||
|
||||
|
||||
dividend = P8ZP_SCRATCH_W1
|
||||
remainder = P8ZP_SCRATCH_W2
|
||||
result = dividend ;save memory by reusing divident to store the result
|
||||
@@ -321,13 +323,14 @@ result = dividend ;save memory by reusing divident to store the result
|
||||
lda result
|
||||
ldy result+1
|
||||
rts
|
||||
_divisor .word 0
|
||||
.section BSS
|
||||
_divisor .word ?
|
||||
.send BSS
|
||||
.pend
|
||||
|
||||
|
||||
randword .proc
|
||||
; -- 16 bit pseudo random number generator into AY
|
||||
; default seed = $00c2 $1137
|
||||
; default seed = $00c2 $1137. NOTE: uses self-modifying code so won't work in ROM (use randword_rom instead)
|
||||
; routine from https://codebase64.org/doku.php?id=base:x_abc_random_number_generator_8_16_bit
|
||||
inc x1
|
||||
clc
|
||||
@@ -349,7 +352,34 @@ b1=*+1
|
||||
rts
|
||||
.pend
|
||||
|
||||
randbyte = randword ; -- 8 bit pseudo random number generator into A (by just reusing randword)
|
||||
randword_rom .proc
|
||||
; -- 16 bit pseudo random number generator into AY. Can run from ROM.
|
||||
; NOTE: you have to set the initial seed using randseed_rom! (a good default seed = $00c2 $1137)
|
||||
; routine from https://codebase64.org/doku.php?id=base:x_abc_random_number_generator_8_16_bit
|
||||
inc _x1
|
||||
clc
|
||||
lda _x1
|
||||
eor _c1
|
||||
eor _a1
|
||||
sta _a1
|
||||
adc _b1
|
||||
sta _b1
|
||||
lsr a
|
||||
eor _a1
|
||||
adc _c1
|
||||
sta _c1
|
||||
ldy _b1
|
||||
rts
|
||||
.section BSS
|
||||
_x1 .byte ?
|
||||
_c1 .byte ?
|
||||
_a1 .byte ?
|
||||
_b1 .byte ?
|
||||
.send BSS
|
||||
.pend
|
||||
|
||||
randbyte = randword ; -- 8 bit pseudo random number generator into A (by just reusing randword) NOTE: can not run from ROM
|
||||
randbyte_rom = randword_rom ; -- 8 bit pseudo random number generator into A (by just reusing randword). Can run from ROM
|
||||
|
||||
|
||||
; ----------- optimized multiplications (in-place A (byte) and ?? (word)) : ---------
|
||||
|
||||
@@ -83,14 +83,27 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub rnd_rom() clobbers(Y) -> ubyte @A {
|
||||
%asm {{
|
||||
jmp prog8_math.randbyte_rom
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub rndw() -> uword @AY {
|
||||
%asm {{
|
||||
jmp prog8_math.randword
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub rndw_rom() -> uword @AY {
|
||||
%asm {{
|
||||
jmp prog8_math.randword_rom
|
||||
}}
|
||||
}
|
||||
|
||||
sub randrange(ubyte n) -> ubyte {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias).
|
||||
; NOTE: does not work for code in ROM
|
||||
cx16.r0H = 255 / n * n
|
||||
do {
|
||||
cx16.r0L = math.rnd()
|
||||
@@ -98,8 +111,19 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
|
||||
return cx16.r0L % n
|
||||
}
|
||||
|
||||
sub randrange_rom(ubyte n) -> ubyte {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias).
|
||||
; NOTE: works for code in ROM, make sure to initialize seed using rndseed_rom
|
||||
cx16.r0H = 255 / n * n
|
||||
do {
|
||||
cx16.r0L = math.rnd_rom()
|
||||
} until cx16.r0L < cx16.r0H
|
||||
return cx16.r0L % n
|
||||
}
|
||||
|
||||
sub randrangew(uword n) -> uword {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
|
||||
; NOTE: does not work for code in ROM
|
||||
cx16.r1 = 65535 / n * n
|
||||
do {
|
||||
cx16.r0 = math.rndw()
|
||||
@@ -107,8 +131,18 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
|
||||
return cx16.r0 % n
|
||||
}
|
||||
|
||||
sub randrangew_rom(uword n) -> uword {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
|
||||
; NOTE: works for code in ROM, make sure to initialize seed using rndseed_rom
|
||||
cx16.r1 = 65535 / n * n
|
||||
do {
|
||||
cx16.r0 = math.rndw_rom()
|
||||
} until cx16.r0 < cx16.r1
|
||||
return cx16.r0 % n
|
||||
}
|
||||
|
||||
asmsub rndseed(uword seed1 @AY, uword seed2 @R0) clobbers(A,Y) {
|
||||
; -- set new pseudo RNG's seed values. Defaults are: $00c2, $1137
|
||||
; -- set new pseudo RNG's seed values. Defaults are: $00c2, $1137. NOTE: does not work for code in ROM, use rndseed_rom instead
|
||||
%asm {{
|
||||
sta prog8_math.randword.x1
|
||||
sty prog8_math.randword.c1
|
||||
@@ -120,6 +154,19 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub rndseed_rom(uword seed1 @AY, uword seed2 @R0) clobbers(A,Y) {
|
||||
; -- set new pseudo RNG's seed values (for the ROM-version of the RNG). Good defaults are: $00c2, $1137.
|
||||
%asm {{
|
||||
sta prog8_math.randword_rom._x1
|
||||
sty prog8_math.randword_rom._c1
|
||||
lda cx16.r0L
|
||||
sta prog8_math.randword_rom._a1
|
||||
lda cx16.r0H
|
||||
sta prog8_math.randword_rom._b1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub log2(ubyte value @A) -> ubyte @Y {
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
|
||||
@@ -374,10 +374,12 @@ _no_msb_size
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta save_SCRATCH_ZPWORD2+1
|
||||
rts
|
||||
save_SCRATCH_ZPB1 .byte 0
|
||||
save_SCRATCH_ZPREG .byte 0
|
||||
save_SCRATCH_ZPWORD1 .word 0
|
||||
save_SCRATCH_ZPWORD2 .word 0
|
||||
.section BSS
|
||||
save_SCRATCH_ZPB1 .byte ?
|
||||
save_SCRATCH_ZPREG .byte ?
|
||||
save_SCRATCH_ZPWORD1 .word ?
|
||||
save_SCRATCH_ZPWORD2 .word ?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -428,7 +430,7 @@ save_SCRATCH_ZPWORD2 .word 0
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcode
|
||||
lda #0
|
||||
rol a
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcodeCarry
|
||||
sta p8_sys_startup.cleanup_at_exit._exitcarry
|
||||
stx p8_sys_startup.cleanup_at_exit._exitcodeX
|
||||
sty p8_sys_startup.cleanup_at_exit._exitcodeY
|
||||
ldx prog8_lib.orig_stackpointer
|
||||
@@ -611,9 +613,11 @@ cx16 {
|
||||
bpl -
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
_cx16_vreg_storage
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word 0,0,0,0,0,0,0,0
|
||||
.word ?,?,?,?,?,?,?,?
|
||||
.word ?,?,?,?,?,?,?,?
|
||||
.send BSS
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
@@ -666,16 +670,21 @@ asmsub init_system_phase2() {
|
||||
asmsub cleanup_at_exit() {
|
||||
; executed when the main subroutine does rts
|
||||
%asm {{
|
||||
_exitcodeCarry = *+1
|
||||
lda #0
|
||||
lda _exitcarry
|
||||
lsr a
|
||||
_exitcode = *+1
|
||||
lda #0 ; exit code possibly modified in exit()
|
||||
_exitcodeX = *+1
|
||||
ldx #0
|
||||
_exitcodeY = *+1
|
||||
ldy #0
|
||||
lda _exitcode
|
||||
ldx _exitcodeX
|
||||
ldy _exitcodeY
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
_exitcarry .byte ?
|
||||
_exitcode .byte ?
|
||||
_exitcodeX .byte ?
|
||||
_exitcodeY .byte ?
|
||||
.send BSS
|
||||
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -159,15 +159,16 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
|
||||
asl a
|
||||
tay
|
||||
lda _screenrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
txa
|
||||
clc
|
||||
adc _screenrows,y
|
||||
sta _mod+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ pla
|
||||
_mod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
|
||||
_screenrows .word cbm.Screen + range(0, 1000, 40)
|
||||
@@ -183,14 +184,15 @@ asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _mod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
pla
|
||||
clc
|
||||
adc setchr._screenrows,y
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -200,22 +202,23 @@ sub setclr (ubyte col, ubyte row, ubyte color) {
|
||||
}
|
||||
|
||||
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored) {
|
||||
; ---- set char at the given position on the screen. charcolor is ignored on PET
|
||||
%asm {{
|
||||
lda row
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _charmod+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc col
|
||||
sta _charmod+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc _charmod+2
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ lda character
|
||||
_charmod sta $ffff ; modified
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -236,8 +236,10 @@ _l3 ldy P8ZP_SCRATCH_B1 ;where the largest value shall be put
|
||||
dec P8ZP_SCRATCH_B1
|
||||
bne _sort_loop ;start working with the shorter sequence
|
||||
rts
|
||||
_work1 .byte 0
|
||||
_work3 .word 0
|
||||
.section BSS
|
||||
_work1 .byte ?
|
||||
_work3 .word ?
|
||||
.send BSS
|
||||
.pend
|
||||
|
||||
|
||||
@@ -299,8 +301,10 @@ _l3 ldy P8ZP_SCRATCH_B1 ;where the largest value shall be put
|
||||
dec P8ZP_SCRATCH_B1
|
||||
bne _sort_loop ;start working with the shorter sequence
|
||||
rts
|
||||
_work1 .byte 0
|
||||
_work3 .word 0
|
||||
.section BSS
|
||||
_work1 .byte ?
|
||||
_work3 .word ?
|
||||
.send BSS
|
||||
.pend
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
; Internal library routines - always included by the compiler
|
||||
; Generic machine independent 6502 code.
|
||||
|
||||
|
||||
orig_stackpointer .byte 0 ; stores the Stack pointer register at program start
|
||||
|
||||
.section BSS_NOCLEAR
|
||||
orig_stackpointer .byte ? ; stores the Stack pointer register at program start
|
||||
.send BSS_NOCLEAR
|
||||
|
||||
program_startup_clear_bss .proc
|
||||
; this is always ran first thing from the start routine to clear out the BSS area
|
||||
@@ -147,9 +147,9 @@ memset .proc
|
||||
; -- fill memory from (P8ZP_SCRATCH_W1), length XY, with value in A.
|
||||
; clobbers X, Y
|
||||
stx P8ZP_SCRATCH_B1
|
||||
sty _save_reg
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
ldx _save_reg
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
beq _lastpage
|
||||
|
||||
_fullpage sta (P8ZP_SCRATCH_W1),y
|
||||
@@ -166,17 +166,14 @@ _lastpage ldy P8ZP_SCRATCH_B1
|
||||
bne -
|
||||
|
||||
+ rts
|
||||
_save_reg .byte 0
|
||||
.pend
|
||||
|
||||
|
||||
memsetw .proc
|
||||
; -- fill memory from (P8ZP_SCRATCH_W1) number of words in P8ZP_SCRATCH_W2, with word value in AY.
|
||||
; clobbers A, X, Y
|
||||
sta _mod1+1 ; self-modify
|
||||
sty _mod1b+1 ; self-modify
|
||||
sta _mod2+1 ; self-modify
|
||||
sty _mod2b+1 ; self-modify
|
||||
sta _val
|
||||
sty _val+1
|
||||
ldx P8ZP_SCRATCH_W1
|
||||
stx P8ZP_SCRATCH_B1
|
||||
ldx P8ZP_SCRATCH_W1+1
|
||||
@@ -188,11 +185,11 @@ memsetw .proc
|
||||
beq _lastpage
|
||||
|
||||
_fullpage
|
||||
_mod1 lda #0 ; self-modified
|
||||
lda _val
|
||||
sta (P8ZP_SCRATCH_W1),y ; first page
|
||||
sta (P8ZP_SCRATCH_B1),y ; second page
|
||||
iny
|
||||
_mod1b lda #0 ; self-modified
|
||||
lda _val+1
|
||||
sta (P8ZP_SCRATCH_W1),y ; first page
|
||||
sta (P8ZP_SCRATCH_B1),y ; second page
|
||||
iny
|
||||
@@ -209,12 +206,12 @@ _lastpage ldx P8ZP_SCRATCH_W2
|
||||
|
||||
ldy #0
|
||||
-
|
||||
_mod2 lda #0 ; self-modified
|
||||
sta (P8ZP_SCRATCH_W1), y
|
||||
lda _val
|
||||
sta (P8ZP_SCRATCH_W1), y
|
||||
inc P8ZP_SCRATCH_W1
|
||||
bne _mod2b
|
||||
bne +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
_mod2b lda #0 ; self-modified
|
||||
+ lda _val+1
|
||||
sta (P8ZP_SCRATCH_W1), y
|
||||
inc P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
@@ -222,6 +219,9 @@ _mod2b lda #0 ; self-modified
|
||||
+ dex
|
||||
bne -
|
||||
_done rts
|
||||
.section BSS
|
||||
_val .word ?
|
||||
.send BSS
|
||||
.pend
|
||||
|
||||
|
||||
@@ -275,8 +275,10 @@ strcmp_expression .proc
|
||||
lda _arg_s1
|
||||
ldy _arg_s1+1
|
||||
jmp strcmp_mem
|
||||
_arg_s1 .word 0
|
||||
_arg_s2 .word 0
|
||||
.section BSS
|
||||
_arg_s1 .word ?
|
||||
_arg_s2 .word ?
|
||||
.send BSS
|
||||
.pend
|
||||
|
||||
strcmp_mem .proc
|
||||
@@ -302,6 +304,34 @@ _c1_zero lda (P8ZP_SCRATCH_W2),y
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
strncmp_mem .proc
|
||||
; -- compares strings in s1 (AY) and s2 (P8ZP_SCRATCH_W2).
|
||||
; Compares up to maximum length specified in X.
|
||||
; Returns -1,0,1 in A, depending on the ordering. Clobbers X & Y.
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
_loop lda (P8ZP_SCRATCH_W1),y
|
||||
beq _c1_zero
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
beq _equal
|
||||
bmi _less
|
||||
lda #1
|
||||
rts
|
||||
_less lda #-1
|
||||
rts
|
||||
_equal dex
|
||||
bne +
|
||||
lda #0
|
||||
rts
|
||||
+ iny
|
||||
bne _loop
|
||||
_c1_zero lda (P8ZP_SCRATCH_W2),y
|
||||
beq +
|
||||
lda #-1
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
|
||||
strlen .proc
|
||||
; -- returns the number of bytes in the string in AY, in Y. Clobbers A.
|
||||
@@ -396,80 +426,6 @@ _found lda #1
|
||||
.pend
|
||||
|
||||
|
||||
arraycopy_split_to_normal_words .proc
|
||||
; P8ZP_SCRATCH_W1 = start of lsb array
|
||||
; P8ZP_SCRATCH_W2 = start of msb array
|
||||
; AY = start of normal word target array
|
||||
; X = number of elements to copy
|
||||
sta _modlsb+1
|
||||
sty _modlsb+2
|
||||
clc
|
||||
adc #1
|
||||
bne +
|
||||
iny
|
||||
+ sta _modmsb+1
|
||||
sty _modmsb+2
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
_modlsb sta $ffff ; modified lsb store
|
||||
lda _modlsb+1
|
||||
clc
|
||||
adc #2
|
||||
sta _modlsb+1
|
||||
bcc +
|
||||
inc _modlsb+2
|
||||
+ lda (P8ZP_SCRATCH_W2),y
|
||||
_modmsb sta $ffff ; modified msb store
|
||||
lda _modmsb+1
|
||||
clc
|
||||
adc #2
|
||||
sta _modmsb+1
|
||||
bcc +
|
||||
inc _modmsb+2
|
||||
+ iny
|
||||
dex
|
||||
bne -
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
arraycopy_normal_to_split_words .proc
|
||||
; P8ZP_SCRATCH_W1 = start of target lsb array
|
||||
; P8ZP_SCRATCH_W2 = start of target msb array
|
||||
; AY = start of normal word source array
|
||||
; X = number of elements to copy
|
||||
sta _modsrclsb+1
|
||||
sty _modsrclsb+2
|
||||
clc
|
||||
adc #1
|
||||
bne +
|
||||
iny
|
||||
+ sta _modsrcmsb+1
|
||||
sty _modsrcmsb+2
|
||||
ldy #0
|
||||
_modsrclsb lda $ffff ; modified lsb read
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
lda _modsrclsb+1
|
||||
clc
|
||||
adc #2
|
||||
sta _modsrclsb+1
|
||||
bcc +
|
||||
inc _modsrclsb+2
|
||||
+
|
||||
_modsrcmsb lda $ffff ; modnfied msb read
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
lda _modsrcmsb+1
|
||||
clc
|
||||
adc #2
|
||||
sta _modsrcmsb+1
|
||||
bcc +
|
||||
inc _modsrcmsb+2
|
||||
+ iny
|
||||
dex
|
||||
bne _modsrclsb
|
||||
rts
|
||||
.pend
|
||||
|
||||
memcopy_small .proc
|
||||
; copy up to a single page (256 bytes) of memory.
|
||||
; note: only works for NON-OVERLAPPING memory regions!
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
; shared CBM (C64/C128) disk drive I/O routines.
|
||||
|
||||
; About the Dos/Drive error status message:
|
||||
; The routines don't usually read/clear the dos/drive error status and message. ("blinking red led")
|
||||
; In case of an error, you usually have to read/clear that yourself (with status() for example).
|
||||
|
||||
|
||||
%import textio
|
||||
%import conv
|
||||
%import strings
|
||||
@@ -10,6 +15,7 @@ diskio {
|
||||
|
||||
const ubyte READ_IO_CHANNEL=12
|
||||
const ubyte WRITE_IO_CHANNEL=13
|
||||
const ubyte STATUS_EOF=$40
|
||||
|
||||
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
|
||||
|
||||
@@ -72,7 +78,7 @@ io_error:
|
||||
cbm.CLRCHN() ; restore default i/o devices
|
||||
cbm.CLOSE(READ_IO_CHANNEL)
|
||||
|
||||
if status!=0 and status & $40 == 0 { ; bit 6=end of file
|
||||
if status!=0 and status & STATUS_EOF == 0 {
|
||||
txt.print("\ni/o error, status: ")
|
||||
txt.print_ub(status)
|
||||
txt.nl()
|
||||
@@ -322,7 +328,7 @@ close_end:
|
||||
if cbm.READST()==0 {
|
||||
iteration_in_progress = true
|
||||
void cbm.CHRIN() ; read first byte to test for file not found
|
||||
if cbm.READST()==0 {
|
||||
if cbm.READST() & ~STATUS_EOF == 0 {
|
||||
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
|
||||
void cbm.OPEN() ; re-open the file
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
@@ -349,7 +355,7 @@ close_end:
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
if cx16.r0L!=0 {
|
||||
f_close()
|
||||
if cx16.r0L & $40 == 0
|
||||
if cx16.r0L & STATUS_EOF == 0
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
@@ -358,28 +364,29 @@ close_end:
|
||||
list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
||||
%asm {{
|
||||
lda bufferpointer
|
||||
sta m_in_buffer+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda bufferpointer+1
|
||||
sta m_in_buffer+2
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
}}
|
||||
while num_bytes!=0 {
|
||||
%asm {{
|
||||
jsr cbm.CHRIN
|
||||
m_in_buffer sta $ffff
|
||||
inc m_in_buffer+1
|
||||
ldy #0
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
inc P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
inc m_in_buffer+2
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+
|
||||
}}
|
||||
list_blocks++
|
||||
cx16.r0L = cbm.READST()
|
||||
if_nz {
|
||||
f_close()
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
if cx16.r0L & $40 !=0 ; eof?
|
||||
if cx16.r0L & STATUS_EOF !=0 ; eof?
|
||||
return list_blocks ; number of bytes read
|
||||
return 0 ; error.
|
||||
}
|
||||
list_blocks++
|
||||
num_bytes--
|
||||
}
|
||||
cbm.CLRCHN() ; reset default i/o channels
|
||||
@@ -387,8 +394,9 @@ m_in_buffer sta $ffff
|
||||
}
|
||||
|
||||
sub f_read_all(uword bufferpointer) -> uword {
|
||||
; -- read the full contents of the file, returns number of bytes read.
|
||||
; -- read the full rest of the file, returns number of bytes read.
|
||||
; It is assumed the file size is less than 64 K.
|
||||
; Usually you will just be using load() / load_raw() to read entire files!
|
||||
if not iteration_in_progress
|
||||
return 0
|
||||
|
||||
@@ -396,6 +404,8 @@ m_in_buffer sta $ffff
|
||||
uword total_read = 0
|
||||
while cbm.READST()==0 {
|
||||
cx16.r0 = f_read(bufferpointer, 256)
|
||||
if cx16.r0==0
|
||||
break
|
||||
total_read += cx16.r0
|
||||
bufferpointer += cx16.r0
|
||||
}
|
||||
@@ -643,6 +653,12 @@ io_error:
|
||||
return load(filenameptr, start_address)
|
||||
}
|
||||
|
||||
; Load a prog8 compiled library binary blob at the given location into memory.
|
||||
sub loadlib(uword libnameptr, uword libaddress) -> uword {
|
||||
return load(libnameptr, libaddress)
|
||||
}
|
||||
|
||||
|
||||
sub delete(uword filenameptr) {
|
||||
; -- delete a file on the drive
|
||||
list_filename[0] = 's'
|
||||
@@ -672,6 +688,7 @@ io_error:
|
||||
sub exists(str filename) -> bool {
|
||||
; -- returns true if the given file exists on the disk, otherwise false
|
||||
; DON'T use this if you already have a file open with f_open!
|
||||
; NOTE: doesn't clear the dos error status and message, you'll have to read/clear that yourself (with status() for example)
|
||||
if f_open(filename) {
|
||||
f_close()
|
||||
return true
|
||||
|
||||
@@ -139,7 +139,8 @@ compression {
|
||||
asmsub decode_rle_srcfunc(uword source_function @AY, uword target @R0, uword maxsize @R1) clobbers(X) -> uword @AY {
|
||||
; -- Decodes "ByteRun1" (aka PackBits) RLE compressed data. Control byte value 128 ends the decoding.
|
||||
; Also stops decompressing when the maxsize has been reached. Returns the size of the decompressed data.
|
||||
; Instead of a source buffer, you provide a callback function that must return the next byte to compress in A.
|
||||
; Instead of a source buffer, you provide a callback function that must return the next byte to decompress in A.
|
||||
; Note: the callback routine MUST NOT MODIFY the prog8 scratch variables such as P8ZP_SCRATCH_W1 etc!
|
||||
%asm {{
|
||||
sta _cb_mod1+1
|
||||
sty _cb_mod1+2
|
||||
@@ -172,6 +173,7 @@ _loop
|
||||
|
||||
; check control byte
|
||||
_cb_mod1 jsr $ffff ; modified
|
||||
cmp #0
|
||||
bpl _literals
|
||||
cmp #128
|
||||
beq _end
|
||||
@@ -218,7 +220,10 @@ _cb_mod3 jsr $ffff ; modified
|
||||
inc P8ZP_SCRATCH_W2+1
|
||||
bcs _loop
|
||||
|
||||
_orig_target .word 0
|
||||
.section BSS
|
||||
_orig_target .word ?
|
||||
.send BSS
|
||||
|
||||
_end
|
||||
; return w2-orig_target, the size of the decompressed data
|
||||
lda P8ZP_SCRATCH_W2
|
||||
@@ -330,7 +335,10 @@ _literals
|
||||
inc P8ZP_SCRATCH_W2+1
|
||||
bcs _loop
|
||||
|
||||
_orig_target .word 0
|
||||
.section BSS
|
||||
_orig_target .word ?
|
||||
.send BSS
|
||||
|
||||
_end
|
||||
; return w2-orig_target, the size of the decompressed data
|
||||
lda P8ZP_SCRATCH_W2
|
||||
|
||||
@@ -8,10 +8,11 @@ sorting {
|
||||
|
||||
; NOTE: all word arrays are assumed to be @nosplit!!
|
||||
; NOTE: sorting is done in ascending order!!!
|
||||
; Note: could be made slightly faster by using modifying dcode for the CPY after _loop but that compromises romability
|
||||
|
||||
asmsub gnomesort_ub(uword bytearray @AY, ubyte num_elements @X) {
|
||||
%asm {{
|
||||
stx _loop+1 ; modifying
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
@@ -22,7 +23,7 @@ sorting {
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #1 ; pos
|
||||
_loop
|
||||
cpy #0 ; modified
|
||||
cpy P8ZP_SCRATCH_REG
|
||||
beq _done
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
|
||||
@@ -10,6 +10,7 @@ strings {
|
||||
; This value is determined during runtime and counts upto the first terminating 0 byte in the string,
|
||||
; regardless of the size of the string during compilation time. Don’t confuse this with len and sizeof!
|
||||
|
||||
; uses P8ZP_SCRATCH_W1 to store the string address, do not change this, other routines here may depend on it (as optimization implementation detail)
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
@@ -47,7 +48,6 @@ _loop dey
|
||||
bne _loop
|
||||
+ rts
|
||||
}}
|
||||
; asmgen.out(" jsr prog8_lib.func_leftstr")
|
||||
}
|
||||
|
||||
asmsub right(uword source @AY, ubyte length @X, uword target @R1) clobbers(A,Y) {
|
||||
@@ -56,16 +56,16 @@ _loop dey
|
||||
; Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
|
||||
; Modifies in-place, doesn’t return a value (so can’t be used in an expression).
|
||||
%asm {{
|
||||
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
|
||||
stx P8ZP_SCRATCH_B1
|
||||
sta cx16.r0
|
||||
sty cx16.r0+1
|
||||
jsr length
|
||||
tya
|
||||
sec
|
||||
sbc P8ZP_SCRATCH_B1
|
||||
clc
|
||||
adc cx16.r0
|
||||
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
|
||||
stx P8ZP_SCRATCH_B1
|
||||
sta cx16.r0
|
||||
sty cx16.r0+1
|
||||
jsr length
|
||||
tya
|
||||
sec
|
||||
sbc P8ZP_SCRATCH_B1
|
||||
clc
|
||||
adc cx16.r0
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda cx16.r0+1
|
||||
adc #0
|
||||
@@ -96,7 +96,7 @@ _loop dey
|
||||
; Also, you have to make sure yourself that start and length are within bounds of the strings.
|
||||
; Modifies in-place, doesn’t return a value (so can’t be used in an expression).
|
||||
%asm {{
|
||||
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
|
||||
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
|
||||
; substr(source, target, start, length)
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda cx16.r0
|
||||
@@ -156,14 +156,11 @@ _found tya
|
||||
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_B1
|
||||
sta _str
|
||||
sty _str+1
|
||||
; note: we make use of the fact that length() stores the string address AY in P8ZP_SCRATCH_W1 for us! we need that later
|
||||
jsr length
|
||||
cpy #0
|
||||
beq _notfound
|
||||
dey
|
||||
lda _str
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda _str+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
beq _found
|
||||
@@ -176,9 +173,6 @@ _notfound lda #255
|
||||
_found tya
|
||||
sec
|
||||
rts
|
||||
|
||||
_str .word 0
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -242,6 +236,19 @@ _str .word 0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub ncompare(uword string1 @R0, uword string2 @AY, ubyte length @X) clobbers(X, Y) -> byte @A {
|
||||
; Compares two strings for sorting.
|
||||
; Returns -1 (255), 0 or 1, meaning: string1 sorts before, equal or after string2.
|
||||
; Only compares the strings from index 0 up to the length argument.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jmp prog8_lib.strncmp_mem
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub lower(uword st @AY) -> ubyte @Y {
|
||||
; Lowercases the petscii string in-place. Returns length of the string.
|
||||
; (for efficiency, non-letter characters > 128 will also not be left intact,
|
||||
@@ -309,16 +316,23 @@ _done rts
|
||||
}
|
||||
|
||||
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> bool @A {
|
||||
; This routine matches a string against a pattern and returns with the carry bit set if they match, or clear if they don't.
|
||||
; The two characters ? and * have a special meaning when they appear in the pattern. All other characters match themselves.
|
||||
; ? matches any one character. For example, F?? matches FOO but not FU, and ?? matches all two-character strings.
|
||||
; * matches any string, including the empty string.
|
||||
; For example, F* matches all strings starting with F. *O*O* matches all strings with at least two Os. Finally, ?* matches all non-empty strings.
|
||||
; Both the pattern and the string must be NUL-terminated (that it, followed with a 00 byte) and at most 255 characters long (excluding the NUL).
|
||||
; Code taken from http://6502.org/source/strings/patmatch.htm
|
||||
;
|
||||
; Input: cx16.r0: A NUL-terminated, <255-length pattern
|
||||
; AY: A NUL-terminated, <255-length string
|
||||
;
|
||||
; Output: A = 1 if the string matches the pattern, A = 0 if not.
|
||||
;
|
||||
; Notes: Clobbers A, X, Y. Each * in the pattern uses 4 bytes of stack.
|
||||
; Does not work in ROM due to self-modifying code.
|
||||
|
||||
%asm {{
|
||||
; pattern matching of a string.
|
||||
; Input: cx16.r0: A NUL-terminated, <255-length pattern
|
||||
; AY: A NUL-terminated, <255-length string
|
||||
;
|
||||
; Output: A = 1 if the string matches the pattern, A = 0 if not.
|
||||
;
|
||||
; Notes: Clobbers A, X, Y. Each * in the pattern uses 4 bytes of stack.
|
||||
;
|
||||
; see http://6502.org/source/strings/patmatch.htm
|
||||
|
||||
strptr = P8ZP_SCRATCH_W1
|
||||
|
||||
|
||||
@@ -18,40 +18,8 @@ diskio {
|
||||
}}
|
||||
}
|
||||
|
||||
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
|
||||
; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
|
||||
; Files in the buffer are separated by a 0 byte. You can provide an optional pattern to match against.
|
||||
; After the last filename one additional 0 byte is placed to indicate the end of the list.
|
||||
; Returns number of files (it skips 'dir' entries i.e. subdirectories).
|
||||
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
|
||||
; Note that no list of pointers of some form is returned, the names are just squashed together.
|
||||
; If you really need a list of pointers to the names, that is pretty straightforward to construct by iterating over the names
|
||||
; and registering when the next one starts after the 0-byte separator.
|
||||
txt.print("@TODO: list_filenames\n")
|
||||
sys.clear_carry()
|
||||
return 0
|
||||
}
|
||||
|
||||
; ----- iterative file lister functions (uses the read io channel) -----
|
||||
|
||||
sub lf_start_list(uword pattern_ptr) -> bool {
|
||||
; -- start an iterative file listing with optional pattern matching.
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
txt.print("@TODO: lf_start_list\n")
|
||||
return false
|
||||
}
|
||||
|
||||
sub lf_next_entry() -> bool {
|
||||
; -- retrieve the next entry from an iterative file listing session.
|
||||
; results will be found in list_blocks, list_filename, and list_filetype.
|
||||
; if it returns false though, there are no more entries (or an error occurred).
|
||||
txt.print("@TODO: lf_next_entry\n")
|
||||
return false
|
||||
}
|
||||
|
||||
sub lf_end_list() {
|
||||
txt.print("@TODO: lf_end_list\n")
|
||||
}
|
||||
; the VM version of diskio has no facility to iterate over filenames.
|
||||
; (list_filenames, lf_start_list, lf_next_entry, lf_end_list)
|
||||
|
||||
|
||||
; ----- iterative file loader functions (uses the input io channel) -----
|
||||
@@ -90,8 +58,9 @@ diskio {
|
||||
}
|
||||
|
||||
sub f_read_all(uword bufferpointer) -> uword {
|
||||
; -- read the full contents of the file, returns number of bytes read.
|
||||
; -- read the full rest of the file, returns number of bytes read.
|
||||
; It is assumed the file size is less than 64 K.
|
||||
; Usually you will just be using load() / load_raw() to read entire files!
|
||||
uword actual
|
||||
repeat {
|
||||
%ir {{
|
||||
|
||||
Loaded 100 of 239 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user