Compare commits

...
99 Commits
Author SHA1 Message Date
Irmen de Jong 7657edcb7d latest symboldumps 2024-12-22 09:24:45 +01:00
Irmen de Jong 580e786952 change math.crc32 to the same algorithm as pkzip/zlib uses (ISO-HDLC). Add math.crc32_end_result(). Fix a parse error in profiler.py script. 2024-12-22 09:19:54 +01:00
Irmen de Jong c0ae35b3a3 tweaks, bump version 11.0 2024-12-22 06:34:17 +01:00
Irmen de Jong c3dc74788a added diskio.get_loadaddress()
added compression.decode_tscrunch_inplace()
2024-12-22 03:17:16 +01:00
Irmen de Jong 379d241a0d various library modules now also use regular asm symbol prefixing rules: buffers, compression, cx16logo, test_stack. 2024-12-21 06:34:55 +01:00
Irmen de Jong 1f49e8fe75 in diskio.f_readline make sure AY result isn't clobbered 2024-12-21 06:25:56 +01:00
Irmen de Jong d70cfbb661 added sorting module and sortingbench example 2024-12-21 06:18:35 +01:00
Irmen de Jong 5482ac0302 simplify grammar of @tags, also improving their error message 2024-12-21 01:44:58 +01:00
Irmen de Jong 131d5ceb4f avoid re-reading all source files when sourcelines are requested in the asm 2024-12-21 00:06:18 +01:00
Irmen de Jong 512ddd1694 cleanups 2024-12-20 22:59:20 +01:00
Irmen de Jong 14a213bff9 compression module: added decode_zx0 and decode_tscrunch
two very fast decompressors while still having pretty good compression ratio
2024-12-20 20:44:57 +01:00
Irmen de Jong d586846bc5 use simpler set_screen_mode() 2024-12-19 22:17:09 +01:00
Irmen de Jong ef4efcb112 cleanup 2024-12-19 21:06:51 +01:00
Irmen de Jong b01555d75e cx16.set_screen_mode() no longer returns anything.
tweak when codegen slightly.
allow trailing comma in array literals.

set_screen_mode failure status is really uncommon and still returned by the real kernal routine screen_mode().
2024-12-19 20:56:07 +01:00
Irmen de Jong 3804fba0f1 moved jdk version config back to main gradle build file, version consistency 2024-12-19 13:39:27 +01:00
Irmen de Jong f93b7e3303 changed IR JUMPI instruction to support more indirect jump cases 2024-12-19 04:29:16 +01:00
Irmen de Jong 73baaeff1f avoid compiler crash when using char literal in str initialization
fix compiler crash when using str var in an expression without &
2024-12-18 15:08:45 +01:00
Irmen de Jong 7c79cdbd2f fix symbol prefixing on goto with expression
added coroutines example
2024-12-17 16:16:38 +01:00
Irmen de Jong 8ea032ed66 fix compiler crash on certain split array values 2024-12-17 12:31:47 +01:00
Irmen de Jong e7a0cb636c add $< and $> operators to get the lsb and msb addresses of a @split array respectively.
document the new split array things.
2024-12-16 17:45:54 +01:00
Irmen de Jong 02f3f5d0f5 @split is back to force splitting of word arrays 2024-12-16 14:51:32 +01:00
Irmen de Jong 1e9bbd662b add palette.set_rgb_nosplit() and set_rbg_be_nosplit()
fix stream-wav missing rts which corrupted playback
fix showbmx example palette and image centering
2024-12-16 02:00:51 +01:00
Irmen de Jong 8644a4ae91 more split array fixes 2024-12-15 22:54:06 +01:00
Irmen de Jong 1e85f7812f removed anyall library module altogether. The routines weren't very optimized and didn't work on split word arrays. 2024-12-15 17:45:31 +01:00
Irmen de Jong 80d88b3c61 fix many split array issues 2024-12-15 17:08:07 +01:00
Irmen de Jong d2827a7431 fix ast printer for arrays containing label addresses 2024-12-15 13:53:24 +01:00
Irmen de Jong 28c721fa7d add a split-array version for word containment check 2024-12-15 13:45:47 +01:00
Irmen de Jong 8f799567cf make word arrays split by default (w.i.p.) 2024-12-15 08:12:34 +01:00
Irmen de Jong 9e8cc8b54d goto can now accept any expression as address (instead of just a constant), and ofcourse a label name still. 2024-12-15 05:22:37 +01:00
Irmen de Jong cc59069876 allow goto to take any expression, not only an integer or an identifier (part 1) 2024-12-14 01:01:32 +01:00
Irmen de Jong 697d54e10a fix asmgen for call $3000 2024-12-13 22:33:26 +01:00
Irmen de Jong 1679ca79b4 can now use boolean params mapped to Rx register 2024-12-13 20:47:23 +01:00
Irmen de Jong 124ec77b58 update zsmkit to version 2.4, including the new on_deck routines 2024-12-13 20:15:36 +01:00
Irmen de Jong 3675d7961b boolean variables can now also be memory-mapped (including boolean arrays) 2024-12-11 18:25:27 +01:00
Irmen de Jong f8aaa2d13c explicit integer type check for @R0-R15 parameters
avoids weird type inconsistency for boolean parameters that would get aliased as unsigned byte instead invisibly
2024-12-10 23:19:41 +01:00
Irmen de Jong b7afda781a Optimize 6502 bitwise operations on word values where only the msb or lsb is touched 2024-12-10 21:42:42 +01:00
Irmen de Jong 535ec13072 improved codegen for testing for single bits: x & mask == mask 2024-12-09 04:05:00 +01:00
Irmen de Jong 26d0a174db optimize codegen for while loops with empty body 2024-12-09 03:21:20 +01:00
Irmen de Jong b2e821755c optimized palette module
removed palette.set_monochrome(), added start color index to several color set functions
removed mcf example
update gradle wrapper
2024-12-08 15:30:42 +01:00
Irmen de Jong 2e303041c1 fix crash when using undefined variable in for loop 2024-12-06 21:50:22 +01:00
Irmen de Jong 96bed8f57f tweaks 2024-12-06 00:37:16 +01:00
Irmen de Jong 86d4a4309f cleanups 2024-12-05 21:56:00 +01:00
Irmen de Jong 1a1ab0dac6 changed the data type system to composite types 2024-12-05 21:48:51 +01:00
Irmen de Jong ba8c3d14f7 diskio docs, remove super harmless warning message 2024-12-05 20:51:44 +01:00
Irmen de Jong 617ea15c3a fix failing optimization of 'not' in if statements 2024-12-04 19:03:24 +01:00
Irmen de Jong ef192a5778 easier notation for builtin function signatures by using varargs 2024-12-04 01:57:02 +01:00
Irmen de Jong 565973c520 diskio read & write routines now always reset the io channels back to the defaults before returning
This means you don't have to call CLRCHN yourself anymore inbetween if you want to do screen output or keyboard input while a file is open
2024-12-03 23:46:07 +01:00
Irmen de Jong 25b1043572 c64 diskio: Always call CLRCHN before CHKIN/CHKOUT calls
this seems to work around a Vice emulator issue when using host filesystem disk emulation.
Fixes #156
2024-12-03 19:15:44 +01:00
Irmen de Jong 1ebfff7c7b add -plaintext and -ignorefootguns options 2024-12-03 19:12:30 +01:00
Irmen de Jong 8341f9c066 diskio.status(): remove unreliable device not present error detection 2024-12-02 23:33:33 +01:00
Irmen de Jong 28cac291de diskio.f_open_w() now also resets io channels back to defaults, like f_open() already did 2024-12-02 22:25:32 +01:00
Irmen de Jong 8fa14a10e2 Optimize diskio.f_read for size=1, also improve ST check 2024-12-02 21:25:38 +01:00
Irmen de Jong 55dbd095ed fix IR codegen missing a CMPI after if not condition
fix IR codegen for containmentcheck
2024-12-02 03:06:06 +01:00
Irmen de Jong 31ad8bdd8d remove bankof(), documented msw() and lsw() 2024-12-01 21:24:26 +01:00
Irmen de Jong 181f3e9eb1 remove the unary/prefix operators ^ and << again 2024-12-01 20:50:33 +01:00
Irmen de Jong 50c3d809dc fix type casting issues and unary ^ operator
signed numbers are no longer implicitly converted to unsigned
proper range check on bankof()
2024-12-01 17:43:53 +01:00
Irmen de Jong 58f696d00a document the @R0 - @R15 register support for normal subroutine parameters 2024-11-30 20:46:31 +01:00
Irmen de Jong f603c543d3 restructure documentation to get rid of redundant syntax chapter 2024-11-30 20:26:06 +01:00
Irmen de Jong 6aaa0f928e IR: fix invalid asm name matching that resulted in not removing subs with a name matching an IR asm instruction 2024-11-30 00:10:57 +01:00
Irmen de Jong feb8aa435e monogfx, gfx_lores, gfx_hires now all uses 8kb stack from buffers module; no more broken flood fills. fill() has an extra byte parameter now where you need to pass in the ram bank to use for the stack. (not on virtual target) 2024-11-29 21:28:34 +01:00
Irmen de Jong 310e8f15cd update to latest zsmkit lib v2.2 2024-11-29 18:37:06 +01:00
Irmen de Jong da03941582 fix build 2024-11-29 01:01:59 +01:00
Irmen de Jong dcbb36a3bd update gradle wrapper version 2024-11-29 00:54:21 +01:00
Irmen de Jong 53558f5c1d add zmskit example for zsmkit v2 2024-11-29 00:04:57 +01:00
Irmen de Jong 189399d5f8 update to kotlin 2.1.0 2024-11-28 03:49:07 +01:00
Irmen de Jong 5406a992f5 improved buffers library, added to docs 2024-11-28 03:30:32 +01:00
Irmen de Jong bc9683cc54 add compression.decode_rle_vram() to decompress RLE data directly to X16's VRAM.
Document the compression library.
2024-11-26 02:06:35 +01:00
Irmen de Jong 2eed75f602 call convention for @Rx parameters, also use cpu registers if possible, like normal parameters 2024-11-25 22:22:24 +01:00
Irmen de Jong d58f9f56c4 tests for register args for normal subs
some warnings demoted into infos
2024-11-24 19:21:45 +01:00
Irmen de Jong 2e35f3c3a3 code check cleanups 2024-11-24 16:14:22 +01:00
Irmen de Jong 5c6bd9c091 register params support for normal subroutines 2024-11-24 15:56:54 +01:00
Irmen de Jong 857d2eefca added floats.interpolate(), math.interpolate(), and LERP example 2024-11-24 10:00:21 +01:00
Irmen de Jong 90f1e7fd6a ast printing fixes, added alias to syntax files 2024-11-24 07:28:33 +01:00
Irmen de Jong 18e37accf9 improve detection of register re-use in parameters 2024-11-24 05:27:43 +01:00
Irmen de Jong cc53d698bf added msw() and lsw() builtin functions (experimental) 2024-11-24 03:53:37 +01:00
Irmen de Jong cb86206698 added unary ^ and << operators (experimental) (gets bank and address of a long integer) 2024-11-24 03:07:18 +01:00
Irmen de Jong d77b1944fb rename bnk() to bankof() 2024-11-24 00:53:09 +01:00
Irmen de Jong a58cb43c4a fixed weird error messages when attempting to create variable with type long 2024-11-23 21:35:57 +01:00
Irmen de Jong 88574c87c4 convert vtui and zsmkit to new extsub address expression capability 2024-11-23 21:21:52 +01:00
Irmen de Jong 3a7a7091c0 update some docs 2024-11-23 21:01:18 +01:00
Irmen de Jong 906b137a7c renamed 'string' module to 'strings' for consistency 2024-11-23 15:51:38 +01:00
Irmen de Jong 42e2c5f605 fix some deprecated code in tests
silence redundant error about unused txt block
2024-11-23 15:48:18 +01:00
Irmen de Jong cc13a51493 fix import order problem related to %option merge 2024-11-23 12:15:15 +01:00
Irmen de Jong f569ce6141 setting a byte >=128 or word >=32768 now results in an out-of-range error, instead of an invalid casted value 2024-11-22 21:24:04 +01:00
Irmen de Jong 4958463e75 moved floats.MIN/MAX to sys.MIN_FLOAT/MAX_FLOAT
added txt.print_f as alias to floats.print
2024-11-22 00:46:23 +01:00
Irmen de Jong 2360625927 added min/max values for the various integer types as sys.MAX_XXX and sys.MIN_XXX
renamed sys.sizeof_xxx into sys.SIZEOF_XXX to be consistent with the uppercasing of the other constants
2024-11-21 23:25:02 +01:00
Irmen de Jong 8badc40883 added several float limits contants such as floats.EPSILON, E, MIN, MAX
fix VM float min max limits
2024-11-21 23:25:02 +01:00
Irmen de Jong 844c97930f fix Antlr grammar build and convert final build.gradle to build.gradle.kts (kotlin DSL) 2024-11-20 23:23:26 +01:00
Irmen de Jong 5c09dc10ae convert build.gradle to build.gradle.kts (kotlin DSL) 2024-11-20 23:23:26 +01:00
Irmen de Jong 9fd9e9ab5f change block sort order so that blocks with address are now sorted last 2024-11-20 23:23:26 +01:00
Irmen de Jong 35c477b5a6 Make extsub address a (constant) expression instead of a numeric literal
this makes it easier to define API jump tables
2024-11-20 23:23:26 +01:00
Irmen de Jong ae0cadb383 added bnk() builtin function 2024-11-20 23:23:21 +01:00
Irmen de Jong 984230e8fa removed txt.VERA_TEXTMATRIX_BANK/VERA_TEXTMATRIX_ADDR it's now just txt.VERA_TEXTMATRIX (long const) 2024-11-20 23:22:56 +01:00
Irmen de Jong a874aec6a1 implementing const long 2024-11-20 23:22:56 +01:00
Irmen de Jong ea1daa97d3 remove the 'addmissingrts' compiler option 2024-11-20 23:22:56 +01:00
Irmen de Jong fb0d9b46b0 remove 'romsub' as a recognised alternative for 'extsub' 2024-11-20 23:22:56 +01:00
Irmen de Jong 9da70bdf05 simplify ReturnConvention a little 2024-11-20 23:22:56 +01:00
Irmen de Jong d640cfbe13 removed BuiltinFunctionCallStatement redundant ast node type 2024-11-20 23:22:56 +01:00
Irmen de Jong 51a05ec4b7 removed BuiltinFunctionCall redundant ast node type 2024-11-20 23:22:56 +01:00
324 changed files with 12975 additions and 13251 deletions

No files matched your search

+13 -3
View File
@@ -1,9 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JsCompilerArguments">
<option name="moduleKind" value="plain" />
</component>
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="11" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="2.0.21" />
<component name="KotlinCommonCompilerArguments">
<option name="apiVersion" value="2.1" />
<option name="languageVersion" value="2.1" />
</component>
</project>
<component name="KotlinCompilerSettings">
<option name="additionalArguments" value="-Xwhen-guards" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="2.1.0" />
</component>
</project>
+10 -10
View File
@@ -1,23 +1,23 @@
<component name="libraryTable">
<library name="KotlinJavaRuntime" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21" />
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.0" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.0.21/kotlin-stdlib-jdk8-2.0.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.21/kotlin-stdlib-2.0.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.0/kotlin-stdlib-jdk8-2.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.0.21/kotlin-stdlib-jdk7-2.0.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.0/kotlin-stdlib-jdk7-2.1.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.0.21/kotlin-stdlib-jdk8-2.0.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.21/kotlin-stdlib-2.0.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.0/kotlin-stdlib-jdk8-2.1.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.0.21/kotlin-stdlib-jdk7-2.0.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.0/kotlin-stdlib-jdk7-2.1.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.0.21/kotlin-stdlib-jdk8-2.0.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.21/kotlin-stdlib-2.0.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.0/kotlin-stdlib-jdk8-2.1.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.0.21/kotlin-stdlib-jdk7-2.0.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.0/kotlin-stdlib-jdk7-2.1.0-sources.jar!/" />
</SOURCES>
</library>
</component>
+2 -2
View File
@@ -4,13 +4,13 @@
<option name="perGrammarGenerationSettings">
<list>
<PerGrammarGenerationSettings>
<option name="fileName" value="$PROJECT_DIR$/parser/antlr/Prog8ANTLR.g4" />
<option name="fileName" value="$PROJECT_DIR$/parser/src/main/antlr/Prog8ANTLR.g4" />
<option name="autoGen" value="true" />
<option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" />
<option name="libDir" value="" />
<option name="encoding" value="" />
<option name="pkg" value="" />
<option name="language" value="" />
<option name="language" value="Java" />
<option name="generateListener" value="false" />
</PerGrammarGenerationSettings>
</list>
+3 -2
View File
@@ -84,11 +84,12 @@ What does Prog8 provide?
*Multiple supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
- "c64": Commodore-64 (6502 like CPU)
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
- "pet32": Commodore PET (experimental)
- "pet32": Commodore PET (limited support)
- "atari": Atari 8 bit such as 800XL (experimental)
- "neo": Neo6502 (experimental)
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)
+1 -27
View File
@@ -1,13 +1,12 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
id("application")
kotlin("jvm")
}
val serverMainClassName = "prog8lsp.MainKt"
val applicationName = "prog8-beanshell"
val javaVersion: String by project
application {
mainClass.set(serverMainClassName)
@@ -45,11 +44,6 @@ sourceSets.main {
resources.srcDir("resources")
}
sourceSets.test {
java.srcDir("src")
resources.srcDir("resources")
}
tasks.startScripts {
applicationName = "prog8-beanshell"
}
@@ -62,13 +56,6 @@ tasks.register<Exec>("fixFilePermissions") {
commandLine("chmod", "+x", "${tasks.installDist.get().destinationDir}/bin/prog8-beanshell")
}
tasks.withType<Test>() {
testLogging {
events("failed")
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
}
}
tasks.installDist {
finalizedBy("fixFilePermissions")
}
@@ -76,16 +63,3 @@ tasks.installDist {
tasks.build {
finalizedBy("installDist")
}
java {
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_11
}
kotlin {
compilerOptions.jvmTarget = JvmTarget.JVM_11
// jvmToolchain {
// languageVersion = JavaLanguageVersion.of(javaVersion.toInt())
// }
}
+6 -6
View File
@@ -64,14 +64,14 @@ rotate3d {
matrix_math {
; vertices
word[] @split xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ]
word[] @split ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ]
word[] @split zcoor = [ -40, 40, -40, 40, -40, 40, -40, 40 ]
word[] xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ]
word[] ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ]
word[] zcoor = [ -40, 40, -40, 40, -40, 40, -40, 40 ]
; storage for rotated coordinates
word[len(xcoor)] @split rotatedx
word[len(ycoor)] @split rotatedy
word[len(zcoor)] @split rotatedz
word[len(xcoor)] rotatedx
word[len(ycoor)] rotatedy
word[len(zcoor)] rotatedz
sub rotate_vertices(ubyte ax, ubyte ay, ubyte az) {
; rotate around origin (0,0,0)
+3 -3
View File
@@ -25,8 +25,8 @@ adpcm {
; IMA ADPCM decoder. Supports mono and stereo streams.
ubyte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8]
uword[] @split t_step = [
byte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8]
uword[] t_step = [
7, 8, 9, 10, 11, 12, 13, 14,
16, 17, 19, 21, 23, 25, 28, 31,
34, 37, 41, 45, 50, 55, 60, 66,
@@ -79,7 +79,7 @@ adpcm {
; elif predicted < -32767:
; predicted = - 32767
index += t_index[nibble]
index += t_index[nibble] as ubyte
if_neg
index = 0
else if index >= len(t_step)-1
+2 -2
View File
@@ -4,8 +4,8 @@
circles {
const ubyte MAX_NUM_CIRCLES = 80
const ubyte GROWTH_RATE = 4
uword[MAX_NUM_CIRCLES] @split circle_x
uword[MAX_NUM_CIRCLES] @split circle_y
uword[MAX_NUM_CIRCLES] circle_x
uword[MAX_NUM_CIRCLES] circle_y
ubyte[MAX_NUM_CIRCLES] circle_radius
ubyte color
uword total_num_circles
+43 -44
View File
@@ -1,7 +1,6 @@
%import textio
%import conv
%import string
%import string
%import strings
textelite {
@@ -172,7 +171,7 @@ textelite {
sub next_input(str buffer) -> ubyte {
input_index++
return string.copy(inputs[input_index], buffer)
return strings.copy(inputs[input_index], buffer)
}
}
@@ -571,7 +570,7 @@ elite_galaxy {
ubyte distance = elite_planet.distance(px, py)
if distance <= max_distance {
elite_planet.name = make_current_planet_name()
elite_planet.name[0] = string.upperchar(elite_planet.name[0])
elite_planet.name[0] = strings.upperchar(elite_planet.name[0])
uword tx = elite_planet.x
uword ty = elite_planet.y
if local {
@@ -743,42 +742,42 @@ elite_galaxy {
}
elite_planet {
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"]
str[] words82 = ["very", "mildly", "most", "reasonably", ""]
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"]
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
str[] words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"]
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
str[] words91 = ["planet", "world", "place", "little planet", "dump"]
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"]
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
str[] @nosplit words81 = ["fabled", "notable", "well known", "famous", "noted"]
str[] @nosplit words82 = ["very", "mildly", "most", "reasonably", ""]
str[] @nosplit words83 = ["ancient", "\x95", "great", "vast", "pink"]
str[] @nosplit words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
str[] @nosplit words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
str[] @nosplit words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
str[] @nosplit words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
str[] @nosplit words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
str[] @nosplit words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
str[] @nosplit words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
str[] @nosplit words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
str[] @nosplit words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
str[] @nosplit words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
str[] @nosplit words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
str[] @nosplit words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"]
str[] @nosplit words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
str[] @nosplit words91 = ["planet", "world", "place", "little planet", "dump"]
str[] @nosplit words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
str[] @nosplit words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
str[] @nosplit words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
str[] @nosplit words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
str[] @nosplit words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
str[] @nosplit words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
str[] @nosplit words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
str[] @nosplit words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
str[] @nosplit words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
str[] @nosplit words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
str[] @nosplit words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
str[] @nosplit words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
str[] @nosplit words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
str[] @nosplit words9F = ["shrew", "beast", "bison", "snake", "wolf"]
str[] @nosplit wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
str[] @nosplit wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
str[] @nosplit wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
str[] @nosplit wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
str[] @nosplit wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
uword[] @shared wordlists = [
words81, words82, words83, words84, words85, words86, words87, words88,
@@ -840,7 +839,7 @@ elite_planet {
}
}
randname[nx] = 0
randname[0] = string.upperchar(randname[0])
randname[0] = strings.upperchar(randname[0])
return randname
}
@@ -912,12 +911,12 @@ elite_planet {
source_ptr = source_stack[stack_ptr]
} else {
if c == $b0 {
@(result_ptr) = string.upperchar(name[0])
@(result_ptr) = strings.upperchar(name[0])
result_ptr++
concat_string(&name + 1)
}
else if c == $b1 {
@(result_ptr) = string.upperchar(name[0])
@(result_ptr) = strings.upperchar(name[0])
result_ptr++
ubyte ni
for ni in 1 to len(name) {
@@ -981,7 +980,7 @@ elite_util {
if pc == 0
return true
; to lowercase for case insensitive compare:
if string.lowerchar(pc)!=string.lowerchar(sc)
if strings.lowerchar(pc)!=strings.lowerchar(sc)
return false
prefixptr++
stringptr++
-10
View File
@@ -1,10 +0,0 @@
plugins {
id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" apply false
}
allprojects {
repositories {
mavenLocal()
mavenCentral()
}
}
+26
View File
@@ -0,0 +1,26 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm") version "2.1.0"
}
allprojects {
apply(plugin="kotlin")
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
compilerOptions {
freeCompilerArgs = listOf("-Xwhen-guards")
jvmTarget = JvmTarget.JVM_11
}
}
java {
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_11
}
}
-42
View File
@@ -1,42 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
// should have no dependencies to other modules
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
}
// note: there are no unit tests in this module!
+24
View File
@@ -0,0 +1,24 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
// should have no dependencies to other modules
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
}
sourceSets {
main {
java {
srcDir("${project.projectDir}/src")
}
resources {
srcDir("${project.projectDir}/res")
}
}
}
// note: there are no unit tests in this module!
+13 -10
View File
@@ -6,6 +6,9 @@ import prog8.code.ast.PtProgram
import prog8.code.core.*
const val internedStringsModuleName = "prog8_interned_strings"
/**
* Tree structure containing all symbol definitions in the program
* (blocks, subroutines, variables (all types), memoryslabs, and labels).
@@ -208,25 +211,25 @@ class StStaticVariable(name: String,
require(initializationArrayValue.isEmpty() ||initializationArrayValue.size==length)
}
if(initializationNumericValue!=null) {
require(dt in NumericDatatypes || dt==DataType.BOOL)
require(dt.isNumericOrBool)
}
if(initializationArrayValue!=null) {
require(dt in ArrayDatatypes)
require(length==initializationArrayValue.size)
require(dt.isArray)
require(length == initializationArrayValue.size)
}
if(initializationStringValue!=null) {
require(dt == DataType.STR)
require(length == initializationStringValue.first.length+1)
require(dt.isString)
require(length == initializationStringValue.first.length + 1)
}
if(align > 0) {
require(dt == DataType.STR || dt in ArrayDatatypes)
require(dt.isString || dt.isArray)
require(zpwish != ZeropageWish.REQUIRE_ZEROPAGE && zpwish != ZeropageWish.PREFER_ZEROPAGE)
}
}
}
class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) :
class StConstant(name: String, val dt: BaseDataType, val value: Double, astNode: PtNode) :
StNode(name, StNodeType.CONSTANT, astNode)
@@ -238,8 +241,8 @@ class StMemVar(name: String,
StNode(name, StNodeType.MEMVAR, astNode) {
init{
require(dt!=DataType.BOOL && dt!=DataType.ARRAY_BOOL)
if(dt in ArrayDatatypes || dt == DataType.STR)
require(!dt.isString)
if(dt.isStringly && !dt.isWord)
requireNotNull(length)
}
}
@@ -266,7 +269,7 @@ class StExtSub(name: String,
class StSubroutineParameter(val name: String, val type: DataType)
class StSubroutineParameter(val name: String, val type: DataType, val register: RegisterOrPair?)
class StExtSubParameter(val register: RegisterOrStatusflag, val type: DataType)
class StArrayElement(val number: Double?, val addressOfSymbol: String?, val boolean: Boolean?) {
init {
+9 -7
View File
@@ -10,7 +10,8 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
val st = SymbolTable(program)
BuiltinFunctions.forEach {
st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, it.value.returnType ?: DataType.UNDEFINED, Position.DUMMY)))
val dt = DataType.forDt(it.value.returnType ?: BaseDataType.UNDEFINED)
st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, dt, Position.DUMMY)))
}
val scopestack = ArrayDeque<StNode>()
@@ -22,10 +23,10 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
if(options.compTarget.name != VMTarget.NAME) {
listOf(
PtMemMapped("P8ZP_SCRATCH_B1", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_B1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), options.compTarget.machine.zeropage.SCRATCH_B1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_REG", DataType.forDt(BaseDataType.UBYTE), options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UWORD), options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD), options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
).forEach {
it.parent = program
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
@@ -46,7 +47,8 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
StNode(node.name, StNodeType.BLOCK, node)
}
is PtConstant -> {
StConstant(node.name, node.type, node.value, node)
require(node.type.isNumericOrBool)
StConstant(node.name, node.type.base, node.value, node)
}
is PtLabel -> {
StNode(node.name, StNodeType.LABEL, node)
@@ -55,7 +57,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
StMemVar(node.name, node.type, node.address, node.arraySize?.toInt(), node)
}
is PtSub -> {
val params = node.parameters.map {StSubroutineParameter(it.name, it.type) }
val params = node.parameters.map {StSubroutineParameter(it.name, it.type, it.register) }
StSub(node.name, params, node.returntype, node)
}
is PtVariable -> {
+2 -2
View File
@@ -3,7 +3,7 @@ package prog8.code.ast
import prog8.code.core.IMemSizer
import prog8.code.core.IStringEncoding
import prog8.code.core.Position
import prog8.code.core.SourceCode
import prog8.code.source.SourceCode
import java.nio.file.Path
// New simplified AST for the code generator.
@@ -99,7 +99,7 @@ class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Positi
class PtLabel(name: String, position: Position) : PtNamedNode(name, position) {
companion object {
// all automatically generated labels everywhere need to have the same label name prefix:
const val GeneratedLabelPrefix = "p8_label_gen_"
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
}
}
+41 -35
View File
@@ -1,7 +1,7 @@
package prog8.code.ast
import prog8.code.core.*
import java.util.Objects
import java.util.*
import kotlin.math.abs
import kotlin.math.truncate
@@ -9,7 +9,7 @@ import kotlin.math.truncate
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
init {
if(type==DataType.UNDEFINED) {
if(type.isUndefined) {
@Suppress("LeakingThis")
when(this) {
is PtBuiltinFunctionCall -> { /* void function call */ }
@@ -66,19 +66,17 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
}
}
infix fun isSameAs(target: PtAssignTarget): Boolean {
return when {
target.memory != null && this is PtMemoryByte-> {
target.memory!!.address isSameAs this.address
}
target.identifier != null && this is PtIdentifier -> {
this.name == target.identifier!!.name
}
target.array != null && this is PtArrayIndexer -> {
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
}
else -> false
infix fun isSameAs(target: PtAssignTarget): Boolean = when {
target.memory != null && this is PtMemoryByte -> {
target.memory!!.address isSameAs this.address
}
target.identifier != null && this is PtIdentifier -> {
this.name == target.identifier!!.name
}
target.array != null && this is PtArrayIndexer -> {
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
}
else -> false
}
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() ?: (this as? PtBool)?.asInt()
@@ -92,7 +90,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
is PtBinaryExpression -> false
is PtBuiltinFunctionCall -> {
when (name) {
in arrayOf("msb", "lsb", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple() }
in arrayOf("msb", "lsb", "msw", "lsw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple() }
else -> false
}
}
@@ -139,7 +137,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
*/
}
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
class PtAddressOf(position: Position, val isMsbForSplitArray: Boolean=false) : PtExpression(DataType.forDt(BaseDataType.UWORD), position) {
val identifier: PtIdentifier
get() = children[0] as PtIdentifier
val arrayIndexExpr: PtExpression?
@@ -156,10 +154,10 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
val index: PtExpression
get() = children[1] as PtExpression
val splitWords: Boolean
get() = variable.type in SplitWordArrayTypes
get() = variable.type.isSplitWordArray
init {
require(elementType in NumericDatatypesWithBoolean)
require(elementType.isNumericOrBool)
}
}
@@ -185,7 +183,7 @@ class PtBuiltinFunctionCall(val name: String,
position: Position) : PtExpression(type, position) {
init {
if(!void)
require(type!=DataType.UNDEFINED)
require(!type.isUndefined)
}
val args: List<PtExpression>
@@ -201,9 +199,9 @@ class PtBinaryExpression(val operator: String, type: DataType, position: Positio
init {
if(operator in ComparisonOperators + LogicalOperators)
require(type==DataType.BOOL)
require(type.isBool)
else
require(type!=DataType.BOOL) { "no bool allowed for this operator $operator"}
require(!type.isBool) { "no bool allowed for this operator $operator"}
}
}
@@ -218,7 +216,7 @@ class PtIfExpression(type: DataType, position: Position): PtExpression(type, pos
}
class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, position) {
class PtContainmentCheck(position: Position): PtExpression(DataType.forDt(BaseDataType.BOOL), position) {
val needle: PtExpression
get() = children[0] as PtExpression
val haystackHeapVar: PtIdentifier?
@@ -227,8 +225,8 @@ class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, positi
get() = children[1] as? PtArray
companion object {
val MAX_SIZE_FOR_INLINE_CHECKS_BYTE = 5
val MAX_SIZE_FOR_INLINE_CHECKS_WORD = 4
const val MAX_SIZE_FOR_INLINE_CHECKS_BYTE = 5
const val MAX_SIZE_FOR_INLINE_CHECKS_WORD = 4
}
}
@@ -241,7 +239,7 @@ class PtFunctionCall(val name: String,
get() = children.map { it as PtExpression }
init {
if(void) require(type==DataType.UNDEFINED) {
if(void) require(type.isUndefined) {
"void fcall should have undefined datatype"
}
// note: non-void calls can have UNDEFINED type: is if they return more than 1 value
@@ -258,13 +256,13 @@ class PtIdentifier(val name: String, type: DataType, position: Position) : PtExp
}
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
class PtMemoryByte(position: Position) : PtExpression(DataType.forDt(BaseDataType.UBYTE), position) {
val address: PtExpression
get() = children.single() as PtExpression
}
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOOL, position) {
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.forDt(BaseDataType.BOOL), position) {
override fun hashCode(): Int = Objects.hash(type, value)
override fun equals(other: Any?): Boolean {
@@ -279,21 +277,29 @@ class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOO
}
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
class PtNumber(type: BaseDataType, val number: Double, position: Position) : PtExpression(DataType.forDt(type), position) {
companion object {
fun fromBoolean(bool: Boolean, position: Position): PtNumber =
PtNumber(DataType.UBYTE, if(bool) 1.0 else 0.0, position)
PtNumber(BaseDataType.UBYTE, if(bool) 1.0 else 0.0, position)
}
init {
if(type==DataType.BOOL)
if(type==BaseDataType.BOOL)
throw IllegalArgumentException("use PtBool instead")
if(type!=DataType.FLOAT) {
if(type!=BaseDataType.FLOAT) {
val trunc = truncate(number)
if (trunc != number)
throw IllegalArgumentException("refused truncating of float to avoid loss of precision @$position")
}
when(type) {
BaseDataType.UBYTE -> require(number in 0.0..255.0)
BaseDataType.BYTE -> require(number in -128.0..127.0)
BaseDataType.UWORD -> require(number in 0.0..65535.0)
BaseDataType.WORD -> require(number in -32728.0..32767.0)
BaseDataType.LONG -> require(number in -2147483647.0..2147483647.0)
else -> require(type.isNumeric) { "numeric literal type should be numeric: $type" }
}
}
override fun hashCode(): Int = Objects.hash(type, number)
@@ -301,7 +307,7 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
override fun equals(other: Any?): Boolean {
return if(other==null || other !is PtNumber)
false
else if(type!=DataType.BOOL && other.type!=DataType.BOOL)
else if(!type.isBool && !other.type.isBool)
number==other.number
else
type==other.type && number==other.number
@@ -318,7 +324,7 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
get() = children.single() as PtExpression
init {
require(operator in setOf("+", "-", "~", "not")) { "invalid prefix operator: $operator" }
require(operator in PrefixOperators) { "invalid prefix operator: $operator" }
}
}
@@ -360,7 +366,7 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position)
}
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.STR, position) {
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.forDt(BaseDataType.STR), position) {
override fun hashCode(): Int = Objects.hash(value, encoding)
override fun equals(other: Any?): Boolean {
if(other==null || other !is PtString)
@@ -370,7 +376,7 @@ class PtString(val value: String, val encoding: Encoding, position: Position) :
}
class PtTypeCast(type: DataType, position: Position) : PtExpression(type, position) {
class PtTypeCast(type: BaseDataType, position: Position) : PtExpression(DataType.forDt(type), position) {
val value: PtExpression
get() = children.single() as PtExpression
}
+37 -32
View File
@@ -1,13 +1,15 @@
package prog8.code.ast
import prog8.code.core.*
import prog8.code.core.DataType
import prog8.code.core.escape
import prog8.code.core.toHex
/**
* Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root),
* passing it as a String to the specified receiver function.
*/
fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Unit) {
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
fun type(dt: DataType) = "!${dt}!"
fun txt(node: PtNode): String {
return when(node) {
is PtAlign -> "%align ${node.align}"
@@ -17,20 +19,23 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
is PtBreakpoint -> "%breakpoint"
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
is PtAddressOf -> {
if(node.isFromArrayElement)
if(node.isMsbForSplitArray)
"&>"
else if(node.isFromArrayElement)
"& array-element"
else
"&"
}
is PtArray -> {
val valuelist = node.children.map {
val valuelist = node.children.joinToString(", ") {
when (it) {
is PtBool -> it.toString()
is PtNumber -> it.number.toString()
is PtIdentifier -> it.name
else -> "?"
is PtAddressOf -> "& ${it.identifier.name}"
else -> "invalid array element $it"
}
}.joinToString(", ")
}
"array len=${node.children.size} ${type(node.type)} [ $valuelist ]"
}
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
@@ -48,14 +53,14 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
is PtIrRegister -> "IRREG#${node.register} ${type(node.type)}"
is PtMemoryByte -> "@()"
is PtNumber -> {
val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex()
val numstr = if(node.type.isFloat) node.number.toString() else node.number.toHex()
"$numstr ${type(node.type)}"
}
is PtBool -> node.value.toString()
is PtPrefix -> node.operator
is PtRange -> "<range>"
is PtString -> "\"${node.value.escape()}\""
is PtTypeCast -> "as ${node.type.name.lowercase()}"
is PtTypeCast -> "as ${node.type}"
is PtForLoop -> "for"
is PtIfElse -> "ifelse"
is PtIncludeBinary -> "%incbin '${node.file}', ${node.offset}, ${node.length}"
@@ -66,12 +71,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
"%asm {{ ...${node.assembly.length} characters... }}"
}
is PtJump -> {
if(node.identifier!=null)
"goto ${node.identifier.name}"
else if(node.address!=null)
"goto ${node.address.toHex()}"
else
"???"
"goto ${txt(node.target)}"
}
is PtAsmSub -> {
val params = node.parameters.joinToString(", ") {
@@ -103,32 +103,34 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
"\nblock '${node.name}' $addr"
}
is PtConstant -> {
val value = when(node.type) {
DataType.BOOL -> if(node.value==0.0) "false" else "true"
in IntegerDatatypes -> node.value.toInt().toString()
val value = when {
node.type.isBool -> if(node.value==0.0) "false" else "true"
node.type.isInteger -> node.value.toInt().toString()
else -> node.value.toString()
}
"const ${node.type.name.lowercase()} ${node.name} = $value"
"const ${node.type} ${node.name} = $value"
}
is PtLabel -> "${node.name}:"
is PtMemMapped -> {
if(node.type in ArrayDatatypes) {
if(node.type.isArray) {
val arraysize = if(node.arraySize==null) "" else node.arraySize.toString()
val eltType = ArrayToElementTypes.getValue(node.type)
"&${eltType.name.lowercase()}[$arraysize] ${node.name} = ${node.address.toHex()}"
"&${node.type.elementType()}[$arraysize] ${node.name} = ${node.address.toHex()}"
} else {
"&${node.type.name.lowercase()} ${node.name} = ${node.address.toHex()}"
"&${node.type} ${node.name} = ${node.address.toHex()}"
}
}
is PtSub -> {
val params = node.parameters.joinToString(", ") { "${it.type} ${it.name}" }
val params = node.parameters.joinToString(", ") {
val reg = if(it.register!=null) "@${it.register}" else ""
"${it.type} ${it.name} $reg"
}
var str = "sub ${node.name}($params) "
if(node.returntype!=null)
str += "-> ${node.returntype.name.lowercase()}"
str += "-> ${node.returntype}"
str
}
is PtVariable -> {
val split = if(node.type in SplitWordArrayTypes) "@split" else ""
val split = if(node.type.isSplitWordArray) "" else "@nosplit"
val align = when(node.align) {
0u -> ""
2u -> "@alignword"
@@ -137,15 +139,15 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
else -> throw IllegalArgumentException("invalid alignment size")
}
val str = if(node.arraySize!=null) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[${node.arraySize}] $split $align ${node.name}"
val eltType = node.type.elementType()
"${eltType}[${node.arraySize}] $split $align ${node.name}"
}
else if(node.type in ArrayDatatypes) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[] $split $align ${node.name}"
else if(node.type.isArray) {
val eltType = node.type.elementType()
"${eltType}[] $split $align ${node.name}"
}
else
"${node.type.name.lowercase()} ${node.name}"
"${node.type} ${node.name}"
if(node.value!=null)
str + " = " + txt(node.value)
else
@@ -156,7 +158,10 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
is PtProgram -> "PROGRAM ${node.name}"
is PtRepeatLoop -> "repeat"
is PtReturn -> "return"
is PtSubroutineParameter -> "${node.type.name.lowercase()} ${node.name}"
is PtSubroutineParameter -> {
val reg = if(node.register!=null) "@${node.register}" else ""
"${node.type} ${node.name} $reg"
}
is PtWhen -> "when"
is PtWhenChoice -> {
if(node.isElse)
+7 -10
View File
@@ -30,16 +30,16 @@ class PtSub(
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
init {
// params and return value should not be str
if(parameters.any{ it.type !in NumericDatatypes && it.type!=DataType.BOOL })
if(parameters.any{ !it.type.isNumericOrBool })
throw AssemblyError("non-numeric/non-bool parameter")
if(returntype!=null && returntype !in NumericDatatypes && returntype!=DataType.BOOL)
if(returntype!=null && !returntype.isNumericOrBool)
throw AssemblyError("non-numeric/non-bool returntype $returntype")
parameters.forEach { it.parent=this }
}
}
class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position)
class PtSubroutineParameter(name: String, val type: DataType, val register: RegisterOrPair?, position: Position): PtNamedNode(name, position)
sealed interface IPtAssignment {
@@ -116,12 +116,9 @@ class PtIfElse(position: Position) : PtNode(position) {
}
class PtJump(val identifier: PtIdentifier?, // note: even ad-hoc labels are wrapped as an Identifier to simplify code. Just use dummy type and position.
val address: UInt?,
position: Position) : PtNode(position) {
init {
identifier?.let {it.parent = this }
}
class PtJump(position: Position) : PtNode(position) {
val target: PtExpression
get() = children.single() as PtExpression
}
@@ -172,7 +169,7 @@ class PtConstant(name: String, override val type: DataType, val value: Double, p
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
init {
require(type!=DataType.BOOL && type!=DataType.ARRAY_BOOL)
require(!type.isString)
}
}
@@ -1,7 +1,7 @@
package prog8.code.core
class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean)
class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean)
class ReturnConvention(val dt: BaseDataType?, val reg: RegisterOrPair?)
class ParamConvention(val dt: BaseDataType, val reg: RegisterOrPair?, val variable: Boolean)
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
override fun toString(): String {
val paramConvs = params.mapIndexed { index, it ->
@@ -13,35 +13,43 @@ class CallConvention(val params: List<ParamConvention>, val returns: ReturnConve
}
val returnConv =
when {
returns.reg!=null -> returns.reg.toString()
returns.floatFac1 -> "floatFAC1"
returns.reg == RegisterOrPair.FAC1 -> "floatFAC1"
returns.reg != null -> returns.reg.toString()
else -> "<no returnvalue>"
}
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
}
}
class FParam(val name: String, val possibleDatatypes: Array<DataType>)
class FParam(val name: String, vararg val possibleDatatypes: BaseDataType)
private val IterableDatatypes = arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
private val IntegerDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
private val NumericDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
private val ByteDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
private val ArrayDatatypes = arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
class FSignature(val pure: Boolean, // does it have side effects?
val parameters: List<FParam>,
val returnType: DataType?) {
val returnType: BaseDataType?,
vararg val parameters: FParam) {
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
fun callConvention(actualParamTypes: List<BaseDataType>): CallConvention {
val returns: ReturnConvention = when (returnType) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false)
DataType.FLOAT -> ReturnConvention(returnType, null, true)
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false)
null -> ReturnConvention(null, null, false)
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A)
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY)
BaseDataType.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1)
in IterableDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY)
null -> ReturnConvention(null, null)
else -> {
// return type depends on arg type
when (val paramType = actualParamTypes.first()) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ReturnConvention(paramType, null, true)
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
else -> ReturnConvention(paramType, null, false)
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A)
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY)
BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
else -> ReturnConvention(paramType, null)
}
}
}
@@ -52,16 +60,24 @@ class FSignature(val pure: Boolean, // does it have side effects?
// One parameter goes via register/registerpair.
// this avoids repeated code for every caller to store the value in the subroutine's argument variable.
// (that store is still done, but only coded once at the start at the subroutine itself rather than at every call site).
// TODO can we start using the X register as well for a third byte or word+byte combo
val paramConv = when(val paramType = actualParamTypes[0]) {
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) // TODO is this correct? shouldn't it be FAC1?
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
BaseDataType.UBYTE, BaseDataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
BaseDataType.UWORD, BaseDataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
BaseDataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) // NOTE: for builtin functions, floating point arguments are passed by reference (so you get a pointer in AY)
in IterableDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
else -> ParamConvention(paramType, null, false)
}
CallConvention(listOf(paramConv), returns)
}
actualParamTypes.size==2 && (actualParamTypes[0] in ByteDatatypes && actualParamTypes[1].isWord) -> {
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
}
actualParamTypes.size==2 && (actualParamTypes[0].isWord && actualParamTypes[1].isByte) -> {
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
}
actualParamTypes.size==3 && actualParamTypes.all { it.isByte } -> {
TODO("opportunity to pass 3 byte arguments in A,Y and X registers but not implemented yet")
}
else -> {
// multiple parameters go via variables
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
@@ -72,66 +88,65 @@ class FSignature(val pure: Boolean, // does it have side effects?
}
val BuiltinFunctions: Map<String, FSignature> = mapOf(
// this set of function have no return value and operate in-place:
"setlsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
"setmsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null),
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"ror2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
// cmp returns a status in the carry flag, but not a proper return value
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null),
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE),
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD),
"prog8_ifelse_bittest_set" to FSignature(true, listOf(FParam("variable", ByteDatatypes), FParam("bitnumber", arrayOf(DataType.UBYTE))), DataType.BOOL),
"prog8_ifelse_bittest_notset" to FSignature(true, listOf(FParam("variable", ByteDatatypes), FParam("bitnumber", arrayOf(DataType.UBYTE))), DataType.BOOL),
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE),
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD),
"abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
// normal functions follow:
"sizeof" to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE),
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE),
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null),
"sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"sqrt__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
"sqrt__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
"divmod" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divisor", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("quotient", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"divmod__ubyte" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE)), FParam("divisor", arrayOf(DataType.UBYTE)), FParam("quotient", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
"divmod__uword" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UWORD)), FParam("divisor", arrayOf(DataType.UWORD)), FParam("quotient", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
"clamp" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), null),
"clamp__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), DataType.BYTE),
"clamp__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE)), FParam("minimum", arrayOf(DataType.UBYTE)), FParam("maximum", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"clamp__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD)), FParam("minimum", arrayOf(DataType.WORD)), FParam("maximum", arrayOf(DataType.WORD))), DataType.WORD),
"clamp__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD)), FParam("minimum", arrayOf(DataType.UWORD)), FParam("maximum", arrayOf(DataType.UWORD))), DataType.UWORD),
"min" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
"min__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
"min__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"min__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
"min__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
"max" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
"max__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
"max__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"max__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
"max__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
"peekf" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.FLOAT),
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
"pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
"pokef" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.FLOAT))), null),
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"rsave" to FSignature(false, emptyList(), null),
"rrestore" to FSignature(false, emptyList(), null),
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
"callfar2" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("argA", arrayOf(DataType.UBYTE)), FParam("argX", arrayOf(DataType.UBYTE)), FParam("argY", arrayOf(DataType.UBYTE)), FParam("argC", arrayOf(DataType.BOOL))), DataType.UWORD),
"call" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
"setlsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"setmsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"rol" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"ror" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"rol2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"ror2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"cmp" to FSignature(false, null, FParam("value1", *IntegerDatatypes), FParam("value2", *NumericDatatypes)), // cmp returns a status in the carry flag, but not a proper return value
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
"prog8_ifelse_bittest_set" to FSignature(true, BaseDataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", BaseDataType.UBYTE)),
"prog8_ifelse_bittest_notset" to FSignature(true, BaseDataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", BaseDataType.UBYTE)),
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"abs__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE)),
"abs__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD)),
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *BaseDataType.entries.toTypedArray())),
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
"sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)),
"sqrt__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"divmod" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("divisor", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("quotient", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("remainder", BaseDataType.UBYTE, BaseDataType.UWORD)),
"divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)),
"divmod__uword" to FSignature(false, null, FParam("dividend", BaseDataType.UWORD), FParam("divisor", BaseDataType.UWORD), FParam("quotient", BaseDataType.UWORD), FParam("remainder", BaseDataType.UWORD)),
"lsb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"lsw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"msb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"msw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"mkword" to FSignature(true, BaseDataType.UWORD, FParam("msb", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
"clamp" to FSignature(true, null, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
"clamp__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
"clamp__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE), FParam("minimum", BaseDataType.UBYTE), FParam("maximum", BaseDataType.UBYTE)),
"clamp__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD), FParam("minimum", BaseDataType.WORD), FParam("maximum", BaseDataType.WORD)),
"clamp__uword" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD), FParam("minimum", BaseDataType.UWORD), FParam("maximum", BaseDataType.UWORD)),
"min" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"min__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"min__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
"min__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
"min__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
"max" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"max__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"max__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
"peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
"pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
"pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
"rsave" to FSignature(false, null),
"rrestore" to FSignature(false, null),
"memory" to FSignature(true, BaseDataType.UWORD, FParam("name", BaseDataType.STR), FParam("size", BaseDataType.UWORD), FParam("alignment", BaseDataType.UWORD)),
"callfar" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("arg", BaseDataType.UWORD)),
"callfar2" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("argA", BaseDataType.UBYTE), FParam("argX", BaseDataType.UBYTE), FParam("argY", BaseDataType.UBYTE), FParam("argC", BaseDataType.BOOL)),
"call" to FSignature(false, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
)
val InplaceModifyingBuiltinFunctions = setOf(
@@ -27,9 +27,9 @@ class CompilationOptions(val output: OutputType,
var varsGolden: Boolean = false,
var slabsHighBank: Int? = null,
var slabsGolden: Boolean = false,
var splitWordArrays: Boolean = false,
var addMissingRts: Boolean = false, // deprecated, will likely go way in future version
var dontSplitWordArrays: Boolean = false,
var breakpointCpuInstruction: String? = null,
var ignoreFootguns: Boolean = false,
var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap()
) {
+4 -2
View File
@@ -11,6 +11,7 @@ fun Number.toHex(): String {
// 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff"
// larger -> "$12345678"
// negative values are prefixed with '-'.
val integer = this.toInt()
if(integer<0)
@@ -19,7 +20,7 @@ fun Number.toHex(): String {
in 0 until 16 -> integer.toString()
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
else -> throw IllegalArgumentException("number too large for 16 bits $this")
else -> "$"+integer.toString(16).padStart(8,'0')
}
}
@@ -27,11 +28,12 @@ fun UInt.toHex(): String {
// 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff"
// larger -> "$12345678"
return when (this) {
in 0u until 16u -> this.toString()
in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0')
in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0')
else -> throw IllegalArgumentException("number too large for 16 bits $this")
else -> "$"+this.toString(16).padStart(8,'0')
}
}
+246 -79
View File
@@ -1,63 +1,255 @@
package prog8.code.core
enum class DataType {
import java.util.Objects
enum class BaseDataType {
UBYTE, // pass by value 8 bits unsigned
BYTE, // pass by value 8 bits signed
UWORD, // pass by value 16 bits unsigned
WORD, // pass by value 16 bits signed
LONG, // pass by value 32 bits signed
FLOAT, // pass by value machine dependent
BOOL, // pass by value bit 0 of a 8 bit byte
BOOL, // pass by value bit 0 of an 8-bit byte
STR, // pass by reference
ARRAY_UB, // pass by reference
ARRAY_B, // pass by reference
ARRAY_UW, // pass by reference
ARRAY_UW_SPLIT, // pass by reference, lo/hi byte split
ARRAY_W, // pass by reference
ARRAY_W_SPLIT, // pass by reference, lo/hi byte split
ARRAY_F, // pass by reference
ARRAY_BOOL, // pass by reference
ARRAY, // pass by reference, subtype is the element type
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
UNDEFINED;
/**
* is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
*/
infix fun isAssignableTo(targetType: DataType) =
when(this) {
BOOL -> targetType == BOOL
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT)
BYTE -> targetType.oneOf(BYTE, WORD, LONG, FLOAT)
UWORD -> targetType.oneOf(UWORD, LONG, FLOAT)
WORD -> targetType.oneOf(WORD, LONG, FLOAT)
LONG -> targetType.oneOf(LONG, FLOAT)
FLOAT -> targetType.oneOf(FLOAT)
STR -> targetType.oneOf(STR, UWORD)
in ArrayDatatypes -> targetType == this
else -> false
}
fun oneOf(vararg types: DataType) = this in types
infix fun largerThan(other: DataType) =
fun largerSizeThan(other: BaseDataType) =
when {
this == other -> false
this in ByteDatatypesWithBoolean -> false
this in WordDatatypes -> other in ByteDatatypesWithBoolean
this == LONG -> other in ByteDatatypesWithBoolean+WordDatatypes
this.isByteOrBool -> false
this.isWord -> other.isByteOrBool
this == LONG -> other.isByteOrBool || other.isWord
this == STR && other == UWORD || this == UWORD && other == STR -> false
this.isArray -> other != FLOAT
this == STR -> other != FLOAT
else -> true
}
infix fun equalsSize(other: DataType) =
fun equalsSize(other: BaseDataType) =
when {
this == other -> true
this in ByteDatatypesWithBoolean -> other in ByteDatatypesWithBoolean
this in WordDatatypes -> other in WordDatatypes
this== STR && other== UWORD || this== UWORD && other== STR -> true
this.isByteOrBool -> other.isByteOrBool
this.isWord -> other.isWord
this == STR && other== UWORD || this== UWORD && other== STR -> true
this == STR && other.isArray -> true
this.isArray && other == STR -> true
else -> false
}
}
val BaseDataType.isByte get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
val BaseDataType.isByteOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL)
val BaseDataType.isWord get() = this in arrayOf(BaseDataType.UWORD, BaseDataType.WORD)
val BaseDataType.isInteger get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.BOOL)
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
val BaseDataType.isPassByRef get() = this.isIterable
val BaseDataType.isPassByValue get() = !this.isIterable
sealed class SubType(val dt: BaseDataType) {
companion object {
private val types by lazy {
// lazy because of static initialization order
mapOf(
BaseDataType.UBYTE to SubUnsignedByte,
BaseDataType.BYTE to SubSignedByte,
BaseDataType.UWORD to SubUnsignedWord,
BaseDataType.WORD to SubSignedWord,
BaseDataType.FLOAT to SubFloat,
BaseDataType.BOOL to SubBool
)}
fun forDt(dt: BaseDataType) = types.getValue(dt)
}
}
data object SubUnsignedByte: SubType(BaseDataType.UBYTE)
data object SubSignedByte: SubType(BaseDataType.BYTE)
data object SubUnsignedWord: SubType(BaseDataType.UWORD)
data object SubSignedWord: SubType(BaseDataType.WORD)
data object SubBool: SubType(BaseDataType.BOOL)
data object SubFloat: SubType(BaseDataType.FLOAT)
class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
init {
if(base.isArray) {
require(sub != null)
if(base.isSplitWordArray)
require(sub.dt == BaseDataType.UWORD || sub.dt == BaseDataType.WORD)
}
else if(base==BaseDataType.STR)
require(sub?.dt==BaseDataType.UBYTE) { "STR subtype should be ubyte" }
else
require(sub == null)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is DataType) return false
return base == other.base && sub == other.sub
}
override fun hashCode(): Int = Objects.hash(base, sub)
companion object {
private val simpletypes = mapOf(
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
BaseDataType.WORD to DataType(BaseDataType.WORD, null),
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
BaseDataType.STR to DataType(BaseDataType.STR, SubUnsignedByte),
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
)
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
return if(splitwordarray && elementDt.isWord)
DataType(BaseDataType.ARRAY_SPLITW, SubType.forDt(actualElementDt))
else
DataType(BaseDataType.ARRAY, SubType.forDt(actualElementDt))
}
}
fun elementToArray(splitwords: Boolean = true): DataType {
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
else arrayFor(base, false)
}
fun elementType(): DataType =
if(base.isArray || base==BaseDataType.STR)
forDt(sub!!.dt)
else
throw IllegalArgumentException("not an array")
override fun toString(): String = when(base) {
BaseDataType.ARRAY -> {
when(sub) {
SubBool -> "bool[]"
SubFloat -> "float[]"
SubSignedByte -> "byte[]"
SubSignedWord -> "word[]"
SubUnsignedByte -> "ubyte[]"
SubUnsignedWord -> "uword[]"
null -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.ARRAY_SPLITW -> {
when(sub) {
SubSignedWord -> "word[] (split)"
SubUnsignedWord -> "uword[] (split)"
else -> throw IllegalArgumentException("invalid sub type")
}
}
else -> base.name.lowercase()
}
fun sourceString(): String = when (base) {
BaseDataType.BOOL -> "bool"
BaseDataType.UBYTE -> "ubyte"
BaseDataType.BYTE -> "byte"
BaseDataType.UWORD -> "uword"
BaseDataType.WORD -> "word"
BaseDataType.LONG -> "long"
BaseDataType.FLOAT -> "float"
BaseDataType.STR -> "str"
BaseDataType.ARRAY -> {
when(sub) {
SubUnsignedByte -> "ubyte["
SubUnsignedWord -> "@nosplit uword["
SubBool -> "bool["
SubSignedByte -> "byte["
SubSignedWord -> "@nosplit word["
SubFloat -> "float["
null -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.ARRAY_SPLITW -> {
when(sub) {
SubUnsignedWord -> "uword["
SubSignedWord -> "word["
else -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.UNDEFINED -> throw IllegalArgumentException("wrong dt")
}
// is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
infix fun isAssignableTo(targetType: DataType) =
when(base) {
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
BaseDataType.UNDEFINED -> false
}
fun largerSizeThan(other: DataType): Boolean {
if(isArray) throw IllegalArgumentException("cannot compare size of array types")
return base.largerSizeThan(other.base)
}
fun equalsSize(other: DataType): Boolean {
if(isArray) throw IllegalArgumentException("cannot compare size of array types")
return base.equalsSize(other.base)
}
val isUndefined = base == BaseDataType.UNDEFINED
val isByte = base.isByte
val isUnsignedByte = base == BaseDataType.UBYTE
val isSignedByte = base == BaseDataType.BYTE
val isByteOrBool = base.isByteOrBool
val isWord = base.isWord
val isUnsignedWord = base == BaseDataType.UWORD
val isSignedWord = base == BaseDataType.WORD
val isInteger = base.isInteger
val isIntegerOrBool = base.isIntegerOrBool
val isNumeric = base.isNumeric
val isNumericOrBool = base.isNumericOrBool
val isSigned = base.isSigned
val isUnsigned = !base.isSigned
val isArray = base.isArray
val isBoolArray = base.isArray && sub?.dt == BaseDataType.BOOL
val isByteArray = base.isArray && (sub?.dt == BaseDataType.UBYTE || sub?.dt == BaseDataType.BYTE)
val isUnsignedByteArray = base.isArray && sub?.dt == BaseDataType.UBYTE
val isSignedByteArray = base.isArray && sub?.dt == BaseDataType.BYTE
val isWordArray = base.isArray && (sub?.dt == BaseDataType.UWORD || sub?.dt == BaseDataType.WORD)
val isUnsignedWordArray = base.isArray && sub?.dt == BaseDataType.UWORD
val isSignedWordArray = base.isArray && sub?.dt == BaseDataType.WORD
val isFloatArray = base.isArray && sub?.dt == BaseDataType.FLOAT
val isString = base == BaseDataType.STR
val isBool = base == BaseDataType.BOOL
val isFloat = base == BaseDataType.FLOAT
val isLong = base == BaseDataType.LONG
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub?.dt == BaseDataType.UBYTE || sub?.dt == BaseDataType.BYTE))
val isSplitWordArray = base.isSplitWordArray
val isSplitUnsignedWordArray = base.isSplitWordArray && sub?.dt == BaseDataType.UWORD
val isSplitSignedWordArray = base.isSplitWordArray && sub?.dt == BaseDataType.WORD
val isIterable = base.isIterable
val isPassByRef = base.isPassByRef
val isPassByValue = base.isPassByValue
}
enum class CpuRegister {
A,
X,
@@ -95,6 +287,17 @@ enum class RegisterOrPair {
else -> throw IllegalArgumentException("no cpu hardware register for $this")
}
fun asScopedNameVirtualReg(type: DataType?): List<String> {
require(this in Cx16VirtualRegisters)
val suffix = when(type?.base) {
BaseDataType.UBYTE, BaseDataType.BOOL -> "L"
BaseDataType.BYTE -> "sL"
BaseDataType.WORD -> "s"
BaseDataType.UWORD, null -> ""
else -> throw kotlin.IllegalArgumentException("invalid register param type")
}
return listOf("cx16", name.lowercase()+suffix)
}
} // only used in parameter and return value specs in asm subroutines
enum class Statusflag {
@@ -123,48 +326,6 @@ enum class BranchCondition {
VC
}
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
val ByteDatatypesWithBoolean = ByteDatatypes + DataType.BOOL
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG)
val IntegerDatatypesWithBoolean = IntegerDatatypes + DataType.BOOL
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT)
val NumericDatatypesWithBoolean = NumericDatatypes + DataType.BOOL
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.LONG, DataType.FLOAT)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL)
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT)
val IterableDatatypes = arrayOf(
DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
DataType.ARRAY_F, DataType.ARRAY_BOOL
)
val PassByValueDatatypes = NumericDatatypesWithBoolean
val PassByReferenceDatatypes = IterableDatatypes
val ArrayToElementTypes = mapOf(
DataType.STR to DataType.UBYTE,
DataType.ARRAY_B to DataType.BYTE,
DataType.ARRAY_UB to DataType.UBYTE,
DataType.ARRAY_W to DataType.WORD,
DataType.ARRAY_UW to DataType.UWORD,
DataType.ARRAY_W_SPLIT to DataType.WORD,
DataType.ARRAY_UW_SPLIT to DataType.UWORD,
DataType.ARRAY_F to DataType.FLOAT,
DataType.ARRAY_BOOL to DataType.BOOL
)
val ElementToArrayTypes = mapOf(
DataType.BYTE to DataType.ARRAY_B,
DataType.UBYTE to DataType.ARRAY_UB,
DataType.WORD to DataType.ARRAY_W,
DataType.UWORD to DataType.ARRAY_UW,
DataType.FLOAT to DataType.ARRAY_F,
DataType.BOOL to DataType.ARRAY_BOOL,
DataType.STR to DataType.ARRAY_UW // array of str is just an array of pointers
)
val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
@@ -172,7 +333,7 @@ val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
)
val CpuRegisters = setOf(
val CpuRegisters = arrayOf(
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY
)
@@ -203,3 +364,9 @@ enum class ZeropageWish {
DONTCARE,
NOT_IN_ZEROPAGE
}
enum class SplitWish {
DONTCARE,
SPLIT,
NOSPLIT
}
+2 -2
View File
@@ -1,6 +1,6 @@
package prog8.code.core
interface IMemSizer {
fun memorySize(dt: DataType): Int
fun memorySize(arrayDt: DataType, numElements: Int): Int
fun memorySize(dt: DataType, numElements: Int?): Int
fun memorySize(dt: SubType): Int
}
+17 -18
View File
@@ -38,7 +38,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
for (reserved in options.zpReserved)
reserve(reserved)
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
}
}
@@ -70,9 +70,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
return Err(MemAllocationError("zero page usage has been disabled"))
val size: Int =
when (datatype) {
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
DataType.STR, in ArrayDatatypes -> {
when {
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
datatype.isString || datatype.isArray -> {
val memsize = options.compTarget.memorySize(datatype, numElements!!)
if(position!=null)
errors.warn("allocating a large value in zeropage; str/array $memsize bytes", position)
@@ -80,9 +80,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
errors.warn("$name: allocating a large value in zeropage; str/array $memsize bytes", Position.DUMMY)
memsize
}
DataType.FLOAT -> {
datatype.isFloat -> {
if (options.floats) {
val memsize = options.compTarget.memorySize(DataType.FLOAT)
val memsize = options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
if(position!=null)
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
else
@@ -94,7 +94,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
}
synchronized(this) {
if(free.size > 0) {
if(free.isNotEmpty()) {
if(size==1) {
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
if(oneSeparateByteFree(candidate))
@@ -118,10 +118,10 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
require(size>=0)
free.removeAll(address until address+size.toUInt())
if(name.isNotEmpty()) {
allocatedVariables[name] = when(datatype) {
in NumericDatatypes, DataType.BOOL -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
DataType.STR -> VarAllocation(address, datatype, size)
in ArrayDatatypes -> VarAllocation(address, datatype, size)
allocatedVariables[name] = when {
datatype.isNumericOrBool -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
datatype.isString -> VarAllocation(address, datatype, size)
datatype.isArray -> VarAllocation(address, datatype, size)
else -> throw AssemblyError("invalid dt")
}
}
@@ -150,14 +150,13 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
val size: Int =
when (datatype) {
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
DataType.STR, in ArrayDatatypes -> {
options.compTarget.memorySize(datatype, numElements!!)
}
DataType.FLOAT -> {
when {
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
datatype.isString -> numElements!!
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
datatype.isFloat -> {
if (options.floats) {
options.compTarget.memorySize(DataType.FLOAT)
options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
} else return Err(MemAllocationError("floating point option not enabled"))
}
else -> throw MemAllocationError("weird dt")
+5 -5
View File
@@ -1,10 +1,10 @@
package prog8.code.core
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are no longer associative because of Shortcircuit/McCarthy evaluation
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val LogicalOperators = setOf("and", "or", "xor", "not", "in")
val BitwiseOperators = setOf("&", "|", "^", "~")
val PrefixOperators = setOf("+", "-", "~", "not")
val AssociativeOperators = arrayOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are not associative because of Shortcircuit/McCarthy evaluation
val ComparisonOperators = arrayOf("==", "!=", "<", ">", "<=", ">=")
val LogicalOperators = arrayOf("and", "or", "xor", "not", "in")
val BitwiseOperators = arrayOf("&", "|", "^", "~")
val PrefixOperators = arrayOf("+", "-", "~", "not")
fun invertedComparisonOperator(operator: String) =
when (operator) {
+3 -3
View File
@@ -1,6 +1,6 @@
package prog8.code.core
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
import prog8.code.source.SourceCode
import java.nio.file.InvalidPathException
import kotlin.io.path.Path
import kotlin.io.path.absolute
@@ -10,12 +10,12 @@ data class Position(val file: String, val line: Int, val startCol: Int, val endC
fun toClickableStr(): String {
if(this===DUMMY)
return ""
if(file.startsWith(LIBRARYFILEPREFIX))
if(SourceCode.isLibraryResource(file))
return "$file:$line:$startCol:"
return try {
val path = Path(file).absolute().normalize().toString()
"file://$path:$line:$startCol:"
} catch(x: InvalidPathException) {
} catch(_: InvalidPathException) {
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
"file://$file:$line:$startCol:"
}
+12 -12
View File
@@ -11,7 +11,7 @@ fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, st:
return
while (errors.noErrors() &&
(optimizeBitTest(program, options)
+ optimizeAssignTargets(program, st, errors)) > 0
+ optimizeAssignTargets(program, st)) > 0
) {
// keep rolling
}
@@ -27,7 +27,7 @@ private fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Boolean) {
}
private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable, errors: IErrorReporter): Int {
private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable): Int {
var changes = 0
walkAst(program) { node: PtNode, depth: Int ->
if(node is PtAssignment) {
@@ -60,7 +60,7 @@ private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable, errors: I
if(node.children.dropLast(1).all { (it as PtAssignTarget).void }) {
// all targets are now void, the whole assignment can be discarded and replaced by just a (void) call to the subroutine
val index = node.parent.children.indexOf(node)
val voidCall = PtFunctionCall(functionName, true, DataType.UNDEFINED, value.position)
val voidCall = PtFunctionCall(functionName, true, DataType.forDt(BaseDataType.UNDEFINED), value.position)
value.children.forEach { voidCall.add(it) }
node.parent.children[index] = voidCall
voidCall.parent = node.parent
@@ -81,12 +81,12 @@ private fun optimizeBitTest(program: PtProgram, options: CompilationOptions): In
fun makeBittestCall(condition: PtBinaryExpression, and: PtBinaryExpression, variable: PtIdentifier, bitmask: Int): PtBuiltinFunctionCall {
require(bitmask==128 || bitmask==64)
val setOrNot = if(condition.operator=="!=") "set" else "notset"
val bittestCall = PtBuiltinFunctionCall("prog8_ifelse_bittest_$setOrNot", false, true, DataType.BOOL, condition.position)
val bittestCall = PtBuiltinFunctionCall("prog8_ifelse_bittest_$setOrNot", false, true, DataType.forDt(BaseDataType.BOOL), condition.position)
bittestCall.add(variable)
if(bitmask==128)
bittestCall.add(PtNumber(DataType.UBYTE, 7.0, and.right.position))
bittestCall.add(PtNumber(BaseDataType.UBYTE, 7.0, and.right.position))
else
bittestCall.add(PtNumber(DataType.UBYTE, 6.0, and.right.position))
bittestCall.add(PtNumber(BaseDataType.UBYTE, 6.0, and.right.position))
return bittestCall
}
@@ -94,17 +94,17 @@ private fun optimizeBitTest(program: PtProgram, options: CompilationOptions): In
if(condition!=null && (condition.operator=="==" || condition.operator=="!=")) {
if (condition.right.asConstInteger() == 0) {
val and = condition.left as? PtBinaryExpression
if (and != null && and.operator == "&" && and.type == DataType.UBYTE) {
if (and != null && and.operator == "&" && and.type.isUnsignedByte) {
val bitmask = and.right.asConstInteger()
if(bitmask==128 || bitmask==64) {
val variable = and.left as? PtIdentifier
if (variable != null && variable.type in ByteDatatypes) {
if (variable != null && variable.type.isByte) {
return Triple(and, variable, bitmask)
}
val typecast = and.left as? PtTypeCast
if (typecast != null && typecast.type == DataType.UBYTE) {
if (typecast != null && typecast.type.isUnsignedByte) {
val castedVariable = typecast.value as? PtIdentifier
if(castedVariable!=null && castedVariable.type in ByteDatatypes)
if(castedVariable!=null && castedVariable.type.isByte)
return Triple(and, castedVariable, bitmask)
}
}
@@ -165,12 +165,12 @@ internal fun isSame(identifier: PtIdentifier, type: DataType, returnedRegister:
cx16.r?sL BYTE
cx16.r?sH BYTE
*/
if(identifier.type in ByteDatatypes && type in ByteDatatypes) {
if(identifier.type.isByte && type.isByte) {
if(identifier.name.startsWith("cx16.$regname") && identifierRegName.startsWith(regname)) {
return identifierRegName.substring(2) in arrayOf("", "L", "sL") // note: not the -H (msb) variants!
}
}
else if(identifier.type in WordDatatypes && type in WordDatatypes) {
else if(identifier.type.isWord && type.isWord) {
if(identifier.name.startsWith("cx16.$regname") && identifierRegName.startsWith(regname)) {
return identifierRegName.substring(2) in arrayOf("", "s")
}
@@ -0,0 +1,60 @@
package prog8.code.source
import prog8.code.core.Position
import java.nio.file.Path
import kotlin.io.path.Path
// Resource caching "filesystem".
// Note that it leaves the decision to load a resource or an actual disk file to the caller.
object ImportFileSystem {
fun getFile(path: Path): SourceCode {
val cached = cache[path.toString()]
if (cached != null) return cached
val file = SourceCode.File(path)
cache[path.toString()] = file
return file
}
fun getResource(name: String): SourceCode {
val cached = cache[name]
if (cached != null) return cached
val resource = SourceCode.Resource(name)
cache[name] = resource
return resource
}
fun retrieveSourceLine(position: Position): String {
if(SourceCode.isLibraryResource(position.file)) {
val cached = cache[SourceCode.withoutPrefix(position.file)]
if(cached != null)
return getLine(cached, position.line)
}
val cached = cache[position.file]
if(cached != null)
return getLine(cached, position.line)
val path = Path(position.file).toAbsolutePath().normalize()
val cached2 = cache[path.toString()]
if(cached2 != null)
return getLine(cached2, position.line)
throw NoSuchElementException("cannot get source line $position")
}
private fun getLine(code: SourceCode, lineIndex: Int): String {
var spans = lineSpanCache[code]
if(spans==null) {
val lineSpans = Regex("^", RegexOption.MULTILINE).findAll(code.text).map { it.range.first }
val ends = lineSpans.drop(1) + code.text.length
spans = lineSpans.zip(ends).map { (start, end) -> LineSpan(start, end) }.toList().toTypedArray()
lineSpanCache[code] = spans
}
val span = spans[lineIndex - 1]
return code.text.substring(span.start, span.end).trim()
}
private class LineSpan(val start: Int, val end: Int)
private val cache = mutableMapOf<String, SourceCode>()
private val lineSpanCache = mutableMapOf<SourceCode, Array<LineSpan>>()
}
@@ -1,16 +1,11 @@
package prog8.code.core
package prog8.code.source
import java.io.File
import java.io.IOException
import java.nio.file.Path
import java.text.Normalizer
import kotlin.io.path.Path
import kotlin.io.path.readText
const val internedStringsModuleName = "prog8_interned_strings"
/**
* Encapsulates - and ties together - actual source code (=text) and its [origin].
*/
@@ -55,14 +50,21 @@ sealed class SourceCode {
/**
* filename prefix to designate library files that will be retreived from internal resources rather than disk
*/
const val LIBRARYFILEPREFIX = "library:"
const val STRINGSOURCEPREFIX = "string:"
private const val LIBRARYFILEPREFIX = "library:"
private const val STRINGSOURCEPREFIX = "string:"
val curdir: Path = Path(".").toAbsolutePath()
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
fun isRegularFilesystemPath(pathString: String) =
!(pathString.startsWith(LIBRARYFILEPREFIX) || pathString.startsWith(STRINGSOURCEPREFIX))
fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
fun withoutPrefix(path: String): String {
return if(isLibraryResource(path))
path.removePrefix(LIBRARYFILEPREFIX)
else if(isStringResource(path))
path.removePrefix(STRINGSOURCEPREFIX)
else
path
}
}
/**
@@ -80,12 +82,13 @@ sealed class SourceCode {
/**
* Get [SourceCode] from the file represented by the specified Path.
* This immediately reads the file fully into memory.
* You can only get an instance of this via the ImportFileSystem object.
*
* [origin] will be the given path in absolute and normalized form.
* @throws NoSuchFileException if the file does not exist
* @throws FileSystemException if the file cannot be read
*/
class File(path: Path): SourceCode() {
internal class File(path: Path): SourceCode() {
override val text: String
override val origin: String
override val name: String
@@ -109,8 +112,9 @@ sealed class SourceCode {
/**
* [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8"
* You can only get an instance of this via the ImportFileSystem object.
*/
class Resource(pathString: String): SourceCode() {
internal class Resource(pathString: String): SourceCode() {
private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/")
override val isFromResources = true
@@ -124,7 +128,7 @@ sealed class SourceCode {
if (rscURL == null) {
val rscRoot = object {}.javaClass.getResource("/")
throw NoSuchFileException(
File(normalized),
java.io.File(normalized),
reason = "looked in resources rooted at $rscRoot"
)
}
@@ -144,34 +148,4 @@ sealed class SourceCode {
override val origin: String = name
override val text: String = "<generated code node, no text representation>"
}
}
object SourceLineCache {
private val cache = mutableMapOf<String, List<String>>()
private fun getCachedFile(file: String): List<String> {
val existing = cache[file]
if(existing!=null)
return existing
if (SourceCode.isRegularFilesystemPath(file)) {
val source = SourceCode.File(Path(file))
cache[file] = source.text.split('\n', '\r').map { it.trim() }
return cache.getValue(file)
} else if(file.startsWith(SourceCode.LIBRARYFILEPREFIX)) {
val source = SourceCode.Resource(file.drop(SourceCode.LIBRARYFILEPREFIX.length))
cache[file] = source.text.split('\n', '\r').map { it.trim()}
return cache.getValue(file)
}
return emptyList()
}
fun retrieveLine(position: Position): String? {
if (position.line>0) {
val lines = getCachedFile(position.file)
if(lines.isNotEmpty())
return lines[position.line-1]
}
return null
}
}
+18 -12
View File
@@ -13,19 +13,25 @@ class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
const val NAME = "atari"
}
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype")
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray) {
require(numElements!=null)
return when(dt.sub?.dt) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
BaseDataType.FLOAT-> numElements * machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid sub type")
}
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> machine.FLOAT_MEM_SIZE * (numElements ?: 1)
else -> 2 * (numElements ?: 1)
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
override fun memorySize(dt: SubType): Int {
return memorySize(DataType.forDt(dt.dt), null)
}
}
+18 -11
View File
@@ -13,18 +13,25 @@ class Neo6502Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
const val NAME = "neo"
}
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype")
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray) {
require(numElements!=null)
return when(dt.sub?.dt) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
BaseDataType.FLOAT-> numElements * machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid sub type")
}
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> machine.FLOAT_MEM_SIZE * (numElements ?: 1)
else -> 2 * (numElements ?: 1)
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt== DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
override fun memorySize(dt: SubType): Int {
return memorySize(DataType.forDt(dt.dt), null)
}
}
+18 -11
View File
@@ -12,18 +12,25 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
const val NAME = "virtual"
}
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype")
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray) {
require(numElements!=null)
return when(dt.sub?.dt) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
BaseDataType.FLOAT-> numElements * machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid sub type")
}
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> machine.FLOAT_MEM_SIZE * (numElements ?: 1)
else -> 2 * (numElements ?: 1)
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
override fun memorySize(dt: SubType): Int {
return memorySize(DataType.forDt(dt.dt), null)
}
}
@@ -28,7 +28,7 @@ class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
ZeropageType.FULL -> {
// TODO all atari usable zero page locations, except the ones used by the system's IRQ routine
free.addAll(0x00u..0xffu)
// TODO atari free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
// TODO atari free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x80u..0xffu) // TODO
@@ -6,7 +6,7 @@ import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
// reference: "Mapping the C128" zero page chapter.
// reference: "Mapping the C128" zeropage chapter.
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
@@ -33,18 +33,18 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
ZeropageType.FULL -> {
// $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such
free.addAll(0x0au..0xffu)
free.removeAll(listOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u)) // these are updated/used by IRQ
free.removeAll(arrayOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u)) // these are updated/used by IRQ
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x0au..0x8fu) // BASIC variables
free.addAll(listOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
free.addAll(arrayOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u))
}
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE -> {
free.addAll(listOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au))
free.addAll(arrayOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au))
free.addAll(0x1bu..0x23u)
free.addAll(listOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu,
free.addAll(arrayOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu,
0x55u, 0x56u, 0x57u, 0x58u,
0x74u, 0x75u, 0x78u, 0x80u, 0x83u, 0x87u, 0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu,
0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
@@ -53,7 +53,7 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
// if(options.zeropage==ZeropageType.BASICSAFE) {
// can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0
free.addAll(listOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu,
free.addAll(arrayOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu,
0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u,
0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u,
0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u))
@@ -55,7 +55,7 @@ class C64MachineDefinition: IMachineDefinition {
val process: Process
try {
process=processb.start()
} catch(x: IOException) {
} catch(_: IOException) {
continue // try the next emulator executable
}
process.waitFor()
@@ -21,11 +21,11 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
if (options.zeropage == ZeropageType.FULL) {
free.addAll(0x02u..0xffu)
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
} else {
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
free.addAll(listOf(
free.addAll(arrayOf(
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
@@ -43,7 +43,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
if (options.zeropage == ZeropageType.FLOATSAFE) {
// remove the zeropage locations used for floating point operations from the free list
free.removeAll(listOf(
free.removeAll(arrayOf(
0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
@@ -56,7 +56,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
if(options.zeropage != ZeropageType.DONTUSE) {
// add the free Zp addresses
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
free.addAll(arrayOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6,
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
} else {
@@ -81,16 +81,16 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
override fun allocateCx16VirtualRegisters() {
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
// 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.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
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
free.remove((4+reg*2).toUInt())
free.remove((5+reg*2).toUInt())
}
@@ -1,21 +1,30 @@
package prog8.code.target.cbm
import prog8.code.core.*
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
import prog8.code.core.IMemSizer
import prog8.code.core.SubType
internal object CbmMemorySizer: IMemSizer {
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype")
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray) {
require(numElements!=null)
return when(dt.sub?.dt) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements * 2
BaseDataType.FLOAT-> numElements * Mflpt5.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid sub type")
}
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> Mflpt5.FLOAT_MEM_SIZE * (numElements ?: 1)
else -> 2 * (numElements ?: 1)
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
override fun memorySize(dt: SubType): Int {
return memorySize(DataType.forDt(dt.dt), null)
}
}
@@ -54,15 +54,15 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
override fun allocateCx16VirtualRegisters() {
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
for(reg in 0..15) {
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
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
}
}
}
@@ -7,7 +7,7 @@ import java.io.CharConversionException
object PetsciiEncoding {
// decoding: from Petscii/Screencodes (0-255) to unicode
// decoding: from Petscii/Screencodes (0-255) to Unicode
// character tables used from https://github.com/irmen/cbmcodecs2
private val decodingPetsciiLowercase = charArrayOf(
@@ -1089,7 +1089,7 @@ object PetsciiEncoding {
Ok(text.map {
try {
encodeChar(it, lowercase)
} catch (x: CharConversionException) {
} catch (_: CharConversionException) {
encodeChar(it, !lowercase)
}
})
@@ -1135,7 +1135,7 @@ object PetsciiEncoding {
Ok(text.map {
try {
encodeChar(it, lowercase)
} catch (x: CharConversionException) {
} catch (_: CharConversionException) {
encodeChar(it, !lowercase)
}
})
@@ -1157,16 +1157,16 @@ object PetsciiEncoding {
}
}
fun petscii2scr(petscii_code: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> {
fun petscii2scr(petsciicode: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> {
val code: UInt = when {
petscii_code <= 0x1fu -> petscii_code + 128u
petscii_code <= 0x3fu -> petscii_code.toUInt()
petscii_code <= 0x5fu -> petscii_code - 64u
petscii_code <= 0x7fu -> petscii_code - 32u
petscii_code <= 0x9fu -> petscii_code + 64u
petscii_code <= 0xbfu -> petscii_code - 64u
petscii_code <= 0xfeu -> petscii_code - 128u
petscii_code == 255.toUByte() -> 95u
petsciicode <= 0x1fu -> petsciicode + 128u
petsciicode <= 0x3fu -> petsciicode.toUInt()
petsciicode <= 0x5fu -> petsciicode - 64u
petsciicode <= 0x7fu -> petsciicode - 32u
petsciicode <= 0x9fu -> petsciicode + 64u
petsciicode <= 0xbfu -> petsciicode - 64u
petsciicode <= 0xfeu -> petsciicode - 128u
petsciicode == 255.toUByte() -> 95u
else -> return Err(CharConversionException("petscii code out of range"))
}
if(inverseVideo) {
@@ -34,15 +34,15 @@ class Neo6502Zeropage(options: CompilationOptions) : Zeropage(options) {
override fun allocateCx16VirtualRegisters() {
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
for(reg in 0..15) {
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
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
}
}
}
@@ -10,8 +10,8 @@ class VirtualMachineDefinition: IMachineDefinition {
override val cpu = CpuType.VIRTUAL
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE.toDouble()
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE.toDouble()
override val FLOAT_MEM_SIZE = 8 // 64-bits double
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
-63
View File
@@ -1,63 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.1'
testImplementation 'io.kotest:kotest-framework-datatest:5.9.1'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
test {
java {
srcDir "${project.projectDir}/test"
}
}
}
test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}
+46
View File
@@ -0,0 +1,46 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
implementation(project(":codeCore"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
sourceSets {
main {
java {
setSrcDirs(listOf("$projectDir/src"))
}
resources {
setSrcDirs(listOf("$projectDir/res"))
}
}
test {
java {
setSrcDirs(listOf("$projectDir/test"))
}
}
}
tasks.test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn("cleanTest")
// Show test results.
testLogging {
events("skipped", "failed")
}
}
+306 -139
View File
@@ -7,6 +7,8 @@ import prog8.code.SymbolTable
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.source.ImportFileSystem
import prog8.code.source.SourceCode
import prog8.code.target.Cx16Target
import prog8.codegen.cpu6502.assignment.*
import kotlin.io.path.Path
@@ -36,7 +38,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.GeneratedLabelPrefix)) node.name = "p8l_${node.name}" // don't prefix autogenerated labels
is PtLabel -> if(!node.name.startsWith(PtLabel.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}"
}
@@ -64,7 +66,7 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
}
is PtIdentifier -> {
var lookupName = node.name
if(node.type in SplitWordArrayTypes && (lookupName.endsWith("_lsb") || lookupName.endsWith("_msb"))) {
if(node.type.isSplitWordArray && (lookupName.endsWith("_lsb") || lookupName.endsWith("_msb"))) {
lookupName = lookupName.dropLast(4)
}
val stNode = st.lookup(lookupName) ?: throw AssemblyError("unknown identifier $node")
@@ -73,13 +75,6 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
nodesToPrefix += node.parent to index
}
}
is PtJump -> {
val stNode = st.lookup(node.identifier!!.name) ?: throw AssemblyError("name not found ${node.identifier}")
if(stNode.astNode.definingBlock()?.options?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
}
is PtBlock -> prefixNamedNode(node)
is PtConstant -> prefixNamedNode(node)
is PtLabel -> prefixNamedNode(node)
@@ -104,7 +99,6 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
when(val node = parent.children[index]) {
is PtIdentifier -> parent.children[index] = node.prefix(parent, st)
is PtFunctionCall -> throw AssemblyError("PtFunctionCall should be processed in their own list, last")
is PtJump -> parent.children[index] = node.prefix(parent, st)
is PtVariable -> parent.children[index] = node.prefix(parent, st)
else -> throw AssemblyError("weird node to prefix $node")
}
@@ -126,14 +120,14 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
private fun prefixScopedName(name: String, type: Char): String {
if('.' !in name) {
if(name.startsWith(PtLabel.GeneratedLabelPrefix))
if(name.startsWith(PtLabel.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.GeneratedLabelPrefix)) lastPart else "p8${type}_$lastPart"
val lastPrefixed = if(lastPart.startsWith(PtLabel.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)
@@ -174,13 +168,6 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
else this
}
private fun PtJump.prefix(parent: PtNode, st: SymbolTable): PtJump {
val prefixedIdent = identifier!!.prefix(this, st)
val jump = PtJump(prefixedIdent, address, position)
jump.parent = parent
return jump
}
private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall {
val newName = prefixScopedName(name, 's')
val call = PtFunctionCall(newName, void, type, position)
@@ -228,8 +215,8 @@ class AsmGen6502Internal (
private var generatedLabelSequenceNumber: Int
) {
internal val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
internal val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320,640)
internal val optimizedByteMultiplications = arrayOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
internal val optimizedWordMultiplications = arrayOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320,640)
internal val loopEndLabels = ArrayDeque<String>()
private val zeropage = options.compTarget.machine.zeropage
private val allocator = VariableAllocator(symbolTable, options, errors)
@@ -241,7 +228,8 @@ class AsmGen6502Internal (
private val anyExprGen = AnyExprAsmGen(this)
private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, allocator, assignmentAsmGen, errors)
private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, assignmentAsmGen, errors)
private val ifExpressionAsmgen = IfExpressionAsmGen(this, assignmentAsmGen, errors)
fun compileToAssembly(): IAssemblyProgram? {
@@ -349,11 +337,10 @@ class AsmGen6502Internal (
lastSourceLineNumber = node.position.line
val srcComment = "\t; source: ${node.position.file}:${node.position.line}"
val line = SourceLineCache.retrieveLine(node.position)
if(line==null)
out(srcComment, false)
else
if(node.position.line>0) {
val line = ImportFileSystem.retrieveSourceLine(node.position)
out("$srcComment $line", false)
}
}
internal fun out(str: String, splitlines: Boolean = true) {
@@ -413,17 +400,17 @@ class AsmGen6502Internal (
internal val tempVarsCounters = mutableMapOf(
DataType.BOOL to 0,
DataType.BYTE to 0,
DataType.UBYTE to 0,
DataType.WORD to 0,
DataType.UWORD to 0,
DataType.FLOAT to 0
BaseDataType.BOOL to 0,
BaseDataType.BYTE to 0,
BaseDataType.UBYTE to 0,
BaseDataType.WORD to 0,
BaseDataType.UWORD to 0,
BaseDataType.FLOAT to 0
)
internal fun buildTempVarName(dt: DataType, counter: Int): String = "prog8_tmpvar_${dt.toString().lowercase()}_$counter"
internal fun buildTempVarName(dt: BaseDataType, counter: Int): String = "prog8_tmpvar_${dt.toString().lowercase()}_$counter"
internal fun getTempVarName(dt: DataType): String {
internal fun getTempVarName(dt: BaseDataType): String {
tempVarsCounters[dt] = tempVarsCounters.getValue(dt)+1
return buildTempVarName(dt, tempVarsCounters.getValue(dt))
}
@@ -600,8 +587,9 @@ class AsmGen6502Internal (
}
is PtAugmentedAssign -> assignmentAsmGen.translate(stmt)
is PtJump -> {
val (asmLabel, indirect) = getJumpTarget(stmt)
jmp(asmLabel, indirect)
val target = getJumpTarget(stmt)
require(!target.needsExpressionEvaluation)
jmp(target.asmLabel, target.indirect)
}
is PtLabel -> translate(stmt)
is PtConditionalBranch -> translate(stmt)
@@ -627,7 +615,7 @@ class AsmGen6502Internal (
val indexValue = if(expr.splitWords)
indexnum
else
indexnum * options.compTarget.memorySize(expr.type)
indexnum * options.compTarget.memorySize(expr.type, null)
out(" ld$reg #$indexValue")
return
}
@@ -637,11 +625,11 @@ class AsmGen6502Internal (
return
}
when (expr.type) {
in ByteDatatypesWithBoolean -> {
when {
expr.type.isByteOrBool -> {
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false)
}
in WordDatatypes -> {
expr.type.isWord -> {
assignExpressionToRegister(expr.index, RegisterOrPair.A, false)
out(" asl a")
when (register) {
@@ -650,8 +638,8 @@ class AsmGen6502Internal (
CpuRegister.Y -> out(" tay")
}
}
DataType.FLOAT -> {
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
expr.type.isFloat -> {
require(options.compTarget.machine.FLOAT_MEM_SIZE == 5) {"invalid float size ${expr.position}"}
assignExpressionToRegister(expr.index, RegisterOrPair.A, false)
out("""
sta P8ZP_SCRATCH_REG
@@ -670,7 +658,7 @@ class AsmGen6502Internal (
}
}
internal fun translateBuiltinFunctionCallExpression(bfc: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): DataType? =
internal fun translateBuiltinFunctionCallExpression(bfc: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): BaseDataType? =
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultRegister)
internal fun translateFunctionCall(functionCallExpr: PtFunctionCall) =
@@ -692,7 +680,7 @@ class AsmGen6502Internal (
when(reg) {
RegisterOrPair.A,
RegisterOrPair.X,
RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister(), target.datatype in SignedDatatypes, true)
RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister(), target.datatype.isSigned, true)
RegisterOrPair.AX,
RegisterOrPair.AY,
RegisterOrPair.XY,
@@ -704,8 +692,8 @@ class AsmGen6502Internal (
}
internal fun assignExpressionTo(value: PtExpression, target: AsmAssignTarget) {
when (target.datatype) {
in ByteDatatypesWithBoolean -> {
when {
target.datatype.isByteOrBool -> {
if (value.asConstInteger()==0) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
@@ -725,7 +713,7 @@ class AsmGen6502Internal (
}
}
TargetStorageKind.REGISTER -> {
val zero = PtNumber(DataType.UBYTE, 0.0, value.position)
val zero = PtNumber(BaseDataType.UBYTE, 0.0, value.position)
zero.parent = value
assignExpressionToRegister(zero, target.register!!, false)
return
@@ -737,7 +725,7 @@ class AsmGen6502Internal (
assignExpressionToRegister(value, RegisterOrPair.A)
assignRegister(RegisterOrPair.A, target)
}
in WordDatatypes, in PassByReferenceDatatypes -> {
target.datatype.isWord || target.datatype.isPassByRef -> {
assignExpressionToRegister(value, RegisterOrPair.AY)
translateNormalAssignment(
AsmAssignment(
@@ -746,7 +734,7 @@ class AsmGen6502Internal (
), value.definingISub()
)
}
DataType.FLOAT -> {
target.datatype.isFloat -> {
assignExpressionToRegister(value, RegisterOrPair.FAC1)
assignRegister(RegisterOrPair.FAC1, target)
}
@@ -798,25 +786,25 @@ class AsmGen6502Internal (
val symbol = symbolTable.lookup((stmt.count as PtIdentifier).name)
val vardecl = symbol!!.astNode as IPtVariable
val name = asmVariableName(stmt.count as PtIdentifier)
when(vardecl.type) {
DataType.UBYTE, DataType.BYTE -> {
when {
vardecl.type.isByte -> {
assignVariableToRegister(name, RegisterOrPair.Y, stmt.definingISub(), stmt.count.position)
repeatCountInY(stmt, endLabel)
}
DataType.UWORD, DataType.WORD -> {
vardecl.type.isWord -> {
assignVariableToRegister(name, RegisterOrPair.AY, stmt.definingISub(), stmt.count.position)
repeatWordCountInAY(endLabel, stmt)
}
else -> throw AssemblyError("invalid loop variable datatype $vardecl")
else -> throw AssemblyError("invalid loop variable datatype ${vardecl.type}")
}
}
else -> {
when (stmt.count.type) {
in ByteDatatypes -> {
when {
stmt.count.type.isByte -> {
assignExpressionToRegister(stmt.count, RegisterOrPair.Y)
repeatCountInY(stmt, endLabel)
}
in WordDatatypes -> {
stmt.count.type.isWord -> {
assignExpressionToRegister(stmt.count, RegisterOrPair.AY)
repeatWordCountInAY(endLabel, stmt)
}
@@ -831,7 +819,7 @@ class AsmGen6502Internal (
private fun repeatWordCount(iterations: Int, stmt: PtRepeatLoop) {
require(iterations in 257..65536) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat")
val counterVar = createRepeatCounterVar(DataType.UWORD, isTargetCpu(CpuType.CPU65c02), stmt)
val counterVar = createRepeatCounterVar(BaseDataType.UWORD, isTargetCpu(CpuType.CPU65c02), stmt)
val loopcount = if(iterations==65536) 0 else if(iterations and 0x00ff == 0) iterations else iterations + 0x0100 // so that the loop can simply use a double-dec
out("""
ldy #>$loopcount
@@ -851,7 +839,7 @@ $repeatLabel""")
// note: A/Y must have been loaded with the number of iterations!
// the iny + double dec is microoptimization of the 16 bit loop
val repeatLabel = makeLabel("repeat")
val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt)
val counterVar = createRepeatCounterVar(BaseDataType.UWORD, false, stmt)
out("""
cmp #0
beq +
@@ -874,13 +862,13 @@ $repeatLabel""")
require(count in 2..256) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat")
if(isTargetCpu(CpuType.CPU65c02)) {
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, true, stmt)
out(" lda #${count and 255} | sta $counterVar")
out(repeatLabel)
translate(stmt.statements)
out(" dec $counterVar | bne $repeatLabel")
} else {
val counterVar = createRepeatCounterVar(DataType.UBYTE, false, stmt)
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, false, stmt)
out(" lda #${count and 255} | sta $counterVar")
out(repeatLabel)
translate(stmt.statements)
@@ -892,13 +880,13 @@ $repeatLabel""")
val repeatLabel = makeLabel("repeat")
out(" cpy #0")
if(isTargetCpu(CpuType.CPU65c02)) {
val counterVar = createRepeatCounterVar(DataType.UBYTE, true, stmt)
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, true, stmt)
out(" beq $endLabel | sty $counterVar")
out(repeatLabel)
translate(stmt.statements)
out(" dec $counterVar | bne $repeatLabel")
} else {
val counterVar = createRepeatCounterVar(DataType.UBYTE, false, stmt)
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, false, stmt)
out(" beq $endLabel | sty $counterVar")
out(repeatLabel)
translate(stmt.statements)
@@ -907,7 +895,7 @@ $repeatLabel""")
out(endLabel)
}
private fun createRepeatCounterVar(dt: DataType, preferZeropage: Boolean, stmt: PtRepeatLoop): String {
private fun createRepeatCounterVar(dt: BaseDataType, preferZeropage: Boolean, stmt: PtRepeatLoop): String {
val scope = stmt.definingISub()!!
val asmInfo = subroutineExtra(scope)
var parent = stmt.parent
@@ -929,8 +917,8 @@ $repeatLabel""")
val counterVar = makeLabel("counter")
when(dt) {
DataType.UBYTE, DataType.UWORD -> {
val result = zeropage.allocate(counterVar, dt, null, stmt.position, errors)
BaseDataType.UBYTE, BaseDataType.UWORD -> {
val result = zeropage.allocate(counterVar, DataType.forDt(dt), null, stmt.position, errors)
result.fold(
success = { (address, _, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally
@@ -942,33 +930,38 @@ $repeatLabel""")
}
private fun translate(stmt: PtWhen) {
val endLabel = makeLabel("choice_end")
val endLabel = makeLabel("when_end")
val choiceBlocks = mutableListOf<Pair<String, PtNodeGroup>>()
val conditionDt = stmt.value.type
if(conditionDt in ByteDatatypes)
if(conditionDt.isByte)
assignExpressionToRegister(stmt.value, RegisterOrPair.A)
else
assignExpressionToRegister(stmt.value, RegisterOrPair.AY)
for(choiceNode in stmt.choices.children) {
val choice = choiceNode as PtWhenChoice
val choiceLabel = makeLabel("choice")
var choiceLabel = makeLabel("choice")
if(choice.isElse) {
translate(choice.statements)
} else {
choiceBlocks.add(choiceLabel to choice.statements)
if(choice.statements.children.isEmpty()) {
// no statements for this choice value, jump to the end immediately
choiceLabel = endLabel
} else {
choiceBlocks.add(choiceLabel to choice.statements)
}
for (cv in choice.values.children) {
val value = (cv as PtNumber).number.toInt()
if(conditionDt in ByteDatatypes) {
if(conditionDt.isByte) {
out(" cmp #${value.toHex()} | beq $choiceLabel")
} else {
out("""
cmp #<${value.toHex()}
bne +
cpy #>${value.toHex()}
beq $choiceLabel
cmp #<${value.toHex()}
bne +
cpy #>${value.toHex()}
beq $choiceLabel
+
""")
""")
}
}
}
@@ -977,7 +970,7 @@ $repeatLabel""")
for(choiceBlock in choiceBlocks.withIndex()) {
out(choiceBlock.value.first)
translate(choiceBlock.value.second)
if(choiceBlock.index<choiceBlocks.size-1)
if (choiceBlock.index < choiceBlocks.size - 1)
jmp(endLabel)
}
out(endLabel)
@@ -995,16 +988,17 @@ $repeatLabel""")
if(jump!=null) {
// branch with only a jump (goto)
val instruction = branchInstruction(stmt.condition, false)
val (asmLabel, indirect) = getJumpTarget(jump)
if(indirect) {
val target = getJumpTarget(jump)
require(!target.needsExpressionEvaluation)
if(target.indirect) {
val complementedInstruction = branchInstruction(stmt.condition, true)
out("""
$complementedInstruction +
jmp ($asmLabel)
jmp (${target.asmLabel})
+""")
}
else {
out(" $instruction $asmLabel")
out(" $instruction ${target.asmLabel}")
}
translate(stmt.falseScope)
} else {
@@ -1030,20 +1024,29 @@ $repeatLabel""")
}
}
internal fun getJumpTarget(jump: PtJump): Pair<String, Boolean> {
val ident = jump.identifier
val addr = jump.address
return when {
ident!=null -> {
// can be a label, or a pointer variable
val symbol = symbolTable.lookup(ident.name)
if(symbol?.type in arrayOf(StNodeType.STATICVAR, StNodeType.MEMVAR, StNodeType.CONSTANT))
Pair(asmSymbolName(ident), true) // indirect jump if the jump symbol is a variable
else
Pair(asmSymbolName(ident), false)
class JumpTarget(val asmLabel: String, val indirect: Boolean, val needsExpressionEvaluation: Boolean)
internal fun getJumpTarget(jump: PtJump, evaluateAddressExpression: Boolean = true): JumpTarget {
val ident = jump.target as? PtIdentifier
if(ident!=null) {
// 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
else
JumpTarget(asmSymbolName(ident), false, false)
}
val addr = jump.target.asConstInteger()
if(addr!=null)
return JumpTarget(addr.toHex(), false, false)
else {
if(evaluateAddressExpression) {
// 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)
} else {
return JumpTarget("PROG8_JUMP_TARGET_IS_UNEVALUATED_ADDRESS_EXPRESSION", true, true)
}
addr!=null -> Pair(addr.toHex(), false)
else -> Pair("????", false)
}
}
@@ -1051,17 +1054,15 @@ $repeatLabel""")
ret.value?.let { returnvalue ->
val sub = ret.definingSub()!!
val returnReg = sub.returnRegister()!!
when (sub.returntype) {
in NumericDatatypes, DataType.BOOL -> {
assignExpressionToRegister(returnvalue, returnReg.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.registerOrPair!!, false)
}
if (sub.returntype?.isNumericOrBool==true) {
assignExpressionToRegister(returnvalue, returnReg.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.registerOrPair!!, false)
}
}
out(" rts")
@@ -1098,11 +1099,11 @@ $repeatLabel""")
}
}
internal fun signExtendAYlsb(valueDt: DataType) {
internal fun signExtendAYlsb(valueDt: BaseDataType) {
// sign extend signed byte in A to full word in AY
when(valueDt) {
DataType.UBYTE -> out(" ldy #0")
DataType.BYTE -> out("""
BaseDataType.UBYTE -> out(" ldy #0")
BaseDataType.BYTE -> out("""
ldy #0
cmp #$80
bcc +
@@ -1113,16 +1114,16 @@ $repeatLabel""")
}
}
internal fun signExtendVariableLsb(asmvar: String, valueDt: DataType) {
internal fun signExtendVariableLsb(asmvar: String, valueDt: BaseDataType) {
// sign extend signed byte in a var to a full word in that variable
when(valueDt) {
DataType.UBYTE -> {
BaseDataType.UBYTE -> {
if(isTargetCpu(CpuType.CPU65c02))
out(" stz $asmvar+1")
else
out(" lda #0 | sta $asmvar+1")
}
DataType.BYTE -> {
BaseDataType.BYTE -> {
out("""
lda $asmvar
ora #$7f
@@ -1155,24 +1156,24 @@ $repeatLabel""")
if (operator != "+") return null
val leftDt = left.type
val rightDt = right.type
if(leftDt == DataType.UWORD && rightDt == DataType.UBYTE)
if(leftDt.isUnsignedWord && rightDt.isUnsignedByte)
return Pair(left, right)
if(leftDt == DataType.UBYTE && rightDt == DataType.UWORD)
if(leftDt.isUnsignedByte && rightDt.isUnsignedWord)
return Pair(right, left)
if(leftDt == DataType.UWORD && rightDt == DataType.UWORD) {
if(leftDt.isUnsignedWord && rightDt.isUnsignedWord) {
// could be that the index was a constant numeric byte but converted to word, check that
val constIdx = right as? PtNumber
if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) {
val num = PtNumber(DataType.UBYTE, constIdx.number, constIdx.position)
val num = PtNumber(BaseDataType.UBYTE, constIdx.number, constIdx.position)
num.parent = right.parent
return Pair(left, num)
}
// could be that the index was typecasted into uword, check that
val rightTc = right as? PtTypeCast
if(rightTc!=null && rightTc.value.type == DataType.UBYTE)
if(rightTc!=null && rightTc.value.type.isUnsignedByte)
return Pair(left, rightTc.value)
val leftTc = left as? PtTypeCast
if(leftTc!=null && leftTc.value.type == DataType.UBYTE)
if(leftTc!=null && leftTc.value.type.isUnsignedByte)
return Pair(right, leftTc.value)
}
return null
@@ -1182,8 +1183,7 @@ $repeatLabel""")
// optimize pointer,indexregister if possible
fun evalBytevalueWillClobberA(expr: PtExpression): Boolean {
val dt = expr.type
if(dt != DataType.UBYTE && dt != DataType.BYTE)
if(!expr.type.isByte)
return true
return when(expr) {
is PtIdentifier -> false
@@ -1222,14 +1222,14 @@ $repeatLabel""")
if(saveA)
out(" pha")
if(ptrAndIndex.second.isSimple()) {
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
if(saveA)
out(" pla")
out(" sta (P8ZP_SCRATCH_W2),y")
} else {
pushCpuStack(DataType.UBYTE, ptrAndIndex.second)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
restoreRegisterStack(CpuRegister.Y, true)
if(saveA)
out(" pla")
@@ -1243,12 +1243,12 @@ $repeatLabel""")
} else {
// copy the pointer var to zp first
if(ptrAndIndex.second.isSimple()) {
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" lda (P8ZP_SCRATCH_W2),y")
} else {
pushCpuStack(DataType.UBYTE, ptrAndIndex.second)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
restoreRegisterStack(CpuRegister.Y, false)
out(" lda (P8ZP_SCRATCH_W2),y")
}
@@ -1273,22 +1273,22 @@ $repeatLabel""")
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
if(left.isSimple()) {
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
assignExpressionToVariable(right, rightVarName, DataType.forDt(BaseDataType.UBYTE))
assignExpressionToRegister(left, RegisterOrPair.A)
} else {
pushCpuStack(DataType.UBYTE, left)
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
pushCpuStack(BaseDataType.UBYTE, left)
assignExpressionToVariable(right, rightVarName, DataType.forDt(BaseDataType.UBYTE))
out(" pla")
}
}
internal fun assignWordOperandsToAYAndVar(left: PtExpression, right: PtExpression, rightVarname: String) {
if(left.isSimple()) {
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
assignExpressionToVariable(right, rightVarname, DataType.forDt(BaseDataType.UWORD))
assignExpressionToRegister(left, RegisterOrPair.AY)
} else {
pushCpuStack(DataType.UWORD, left)
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
pushCpuStack(BaseDataType.UWORD, left)
assignExpressionToVariable(right, rightVarname, DataType.forDt(BaseDataType.UWORD))
restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false)
}
@@ -1297,7 +1297,7 @@ $repeatLabel""")
internal fun translateDirectMemReadExpressionToRegA(expr: PtMemoryByte) {
fun assignViaExprEval() {
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD))
if (isTargetCpu(CpuType.CPU65c02)) {
out(" lda (P8ZP_SCRATCH_W2)")
} else {
@@ -1324,12 +1324,12 @@ $repeatLabel""")
}
}
internal fun pushCpuStack(dt: DataType, value: PtExpression) {
val signed = value.type.oneOf(DataType.BYTE, DataType.WORD)
if(dt in ByteDatatypesWithBoolean) {
internal fun pushCpuStack(dt: BaseDataType, value: PtExpression) {
val signed = value.type.isSigned
if(dt.isByteOrBool) {
assignExpressionToRegister(value, RegisterOrPair.A, signed)
out(" pha")
} else if(dt in WordDatatypes) {
} else if(dt.isWord) {
assignExpressionToRegister(value, RegisterOrPair.AY, signed)
if (isTargetCpu(CpuType.CPU65c02))
out(" pha | phy")
@@ -1370,10 +1370,10 @@ $repeatLabel""")
internal fun makeLabel(postfix: String): String {
generatedLabelSequenceNumber++
return "${PtLabel.GeneratedLabelPrefix}${generatedLabelSequenceNumber}_$postfix"
return "${PtLabel.GENERATED_LABEL_PREFIX}${generatedLabelSequenceNumber}_$postfix"
}
fun assignConstFloatToPointerAY(number: PtNumber) {
internal fun assignConstFloatToPointerAY(number: PtNumber) {
val floatConst = allocator.getFloatAsmConst(number.number)
out("""
pha
@@ -1386,7 +1386,174 @@ $repeatLabel""")
}
internal fun assignIfExpression(target: AsmAssignTarget, value: PtIfExpression) {
ifElseAsmgen.assignIfExpression(target, value)
ifExpressionAsmgen.assignIfExpression(target, value)
}
internal fun cmpAwithByteValue(value: PtExpression, useSbc: Boolean) {
val compare = if(useSbc) "sec | sbc" else "cmp"
fun cmpViaScratch() {
if(assignmentAsmGen.directIntoY(value)) {
assignExpressionToRegister(value, RegisterOrPair.Y, false)
out(" sty P8ZP_SCRATCH_REG")
} else {
out(" pha")
assignExpressionToVariable(value, "P8ZP_SCRATCH_REG", value.type)
out(" pla")
}
out(" $compare P8ZP_SCRATCH_REG")
}
when(value) {
is PtArrayIndexer -> {
val constIndex = value.index.asConstInteger()
if(constIndex!=null) {
val offset = program.memsizer.memorySize(value.type, constIndex)
if(offset<256) {
return out(" ldy #$offset | $compare ${asmVariableName(value.variable)},y")
}
}
cmpViaScratch()
}
is PtMemoryByte -> {
val constAddr = value.address.asConstInteger()
if(constAddr!=null) {
out(" $compare ${constAddr.toHex()}")
} else {
cmpViaScratch()
}
}
is PtIdentifier -> {
out(" $compare ${asmVariableName(value)}")
}
is PtNumber -> {
if(value.number!=0.0)
out(" $compare #${value.number.toInt()}")
}
else -> {
cmpViaScratch()
}
}
}
internal fun immediateAndInplace(name: String, value: Int) {
if(isTargetCpu(CpuType.CPU65c02)) {
out(" lda #${value xor 255} | trb $name") // reset bit
} else {
out(" lda $name | and #$value | sta $name")
}
}
internal fun immediateOrInplace(name: String, value: Int) {
if(isTargetCpu(CpuType.CPU65c02)) {
out(" lda #$value | tsb $name") // set bit
} else {
out(" lda $name | ora #$value | sta $name")
}
}
internal fun assignConditionValueToRegisterAndTest(condition: PtExpression) {
assignExpressionToRegister(condition, RegisterOrPair.A, false)
when(condition) {
is PtNumber,
is PtBool,
is PtIdentifier,
is PtIrRegister,
is PtArrayIndexer,
is PtPrefix,
is PtIfExpression,
is PtBinaryExpression -> { /* no cmp necessary the lda has been done just prior */ }
is PtTypeCast -> {
if(!condition.value.type.isByte && !condition.value.type.isWord)
out(" cmp #0")
}
else -> out(" cmp #0")
}
}
internal fun translateFloatsEqualsConditionIntoA(left: PtExpression, right: PtExpression) {
fun equalf(leftName: String, rightName: String) {
out("""
lda #<$leftName
ldy #>$leftName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<$rightName
ldy #>$rightName
jsr floats.vars_equal_f""")
}
fun equalf(expr: PtExpression, rightName: String) {
assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
out("""
lda #<$rightName
ldy #>$rightName
jsr floats.var_fac1_equal_f""")
}
if(left is PtIdentifier) {
when (right) {
is PtIdentifier -> equalf(asmVariableName(left), asmVariableName(right))
is PtNumber -> equalf(asmVariableName(left), allocator.getFloatAsmConst(right.number))
else -> {
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
equalf(asmVariableName(left), subroutineFloatEvalResultVar1)
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
}
}
} else {
when (right) {
is PtIdentifier -> equalf(left, asmVariableName(right))
is PtNumber -> equalf(left, allocator.getFloatAsmConst(right.number))
else -> {
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
equalf(left, subroutineFloatEvalResultVar1)
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
}
}
}
}
internal fun translateFloatsLessConditionIntoA(left: PtExpression, right: PtExpression, lessOrEquals: Boolean) {
fun lessf(leftName: String, rightName: String) {
out("""
lda #<$rightName
ldy #>$rightName
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda #<$leftName
ldy #>$leftName""")
if(lessOrEquals)
out("jsr floats.vars_lesseq_f")
else
out("jsr floats.vars_less_f")
}
fun lessf(expr: PtExpression, rightName: String) {
assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
out(" lda #<$rightName | ldy #>$rightName")
if(lessOrEquals)
out(" jsr floats.var_fac1_lesseq_f")
else
out(" jsr floats.var_fac1_less_f")
}
if(left is PtIdentifier) {
when (right) {
is PtIdentifier -> lessf(asmVariableName(left), asmVariableName(right))
is PtNumber -> lessf(asmVariableName(left), allocator.getFloatAsmConst(right.number))
else -> {
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
lessf(asmVariableName(left), subroutineFloatEvalResultVar1)
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
}
}
} else {
when (right) {
is PtIdentifier -> lessf(left, asmVariableName(right))
is PtNumber -> lessf(left, allocator.getFloatAsmConst(right.number))
else -> {
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT))
lessf(left, subroutineFloatEvalResultVar1)
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
}
}
}
}
}
@@ -1400,5 +1567,5 @@ internal class SubroutineExtraAsmInfo {
var usedFloatEvalResultVar1 = false
var usedFloatEvalResultVar2 = false
val extraVars = mutableListOf<Triple<DataType, String, UInt?>>()
val extraVars = mutableListOf<Triple<BaseDataType, String, UInt?>>()
}
@@ -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.GeneratedLabelPrefix
val autoLabelPrefix = PtLabel.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:"))) {
@@ -10,7 +10,7 @@ import java.nio.file.Path
internal class AssemblyProgram(
override val name: String,
private val outputDir: Path,
outputDir: Path,
private val compTarget: ICompilationTarget) : IAssemblyProgram {
private val assemblyFile = outputDir.resolve("$name.asm")
@@ -19,14 +19,14 @@ internal class AssemblyProgram(
private val binFile = outputDir.resolve("$name.bin")
private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name))
private val listFile = outputDir.resolve("$name.list")
private val targetWithoutBreakpointsForEmulator = setOf(AtariTarget.NAME, Neo6502Target.NAME)
private val targetWithoutBreakpointsForEmulator = arrayOf(AtariTarget.NAME, Neo6502Target.NAME)
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
val assemblerCommand: List<String>
when (compTarget.name) {
in setOf("c64", "c128", "cx16", "pet32") -> {
in arrayOf("c64", "c128", "cx16", "pet32") -> {
// CBM machines .prg generation.
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
@@ -148,7 +148,7 @@ internal class AssemblyProgram(
}
private fun removeGeneratedLabelsFromMonlist() {
val pattern = Regex("""al (\w+) \S+${PtLabel.GeneratedLabelPrefix}.+?""")
val pattern = Regex("""al (\w+) \S+${PtLabel.GENERATED_LABEL_PREFIX}.+?""")
val lines = viceMonListFile.toFile().readLines()
viceMonListFile.toFile().outputStream().bufferedWriter().use {
for (line in lines) {
@@ -9,7 +9,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private val asmgen: AsmGen6502Internal,
private val assignAsmGen: AssignmentAsmGen) {
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): DataType? {
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): BaseDataType? {
return translateFunctioncall(fcall, discardResult = false, resultRegister = resultRegister)
}
@@ -17,13 +17,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
translateFunctioncall(fcall, discardResult = true, resultRegister = null)
}
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?): DataType? {
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?): BaseDataType? {
if (discardResult && fcall.hasNoSideEffects)
return null // can just ignore the whole function call altogether
val sscope = fcall.definingISub()
when (fcall.name) {
"lsw" -> throw AssemblyError("lsw() should have been removed or replaced by a const value")
"msw" -> throw AssemblyError("msw() should have been removed or replaced by a const value")
"msb" -> funcMsb(fcall, resultRegister)
"lsb" -> funcLsb(fcall, resultRegister)
"mkword" -> funcMkword(fcall, resultRegister)
@@ -53,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.UBYTE, fcall.definingISub(), fcall.position, memory=memread)
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.position, memory=memread)
asmgen.assignExpressionTo(fcall.args[1], memtarget)
asmgen.out(" pla")
}
@@ -67,25 +69,25 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse statement")
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse statement")
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, resultRegister)
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD, resultRegister)
"prog8_lib_square_byte" -> funcSquare(fcall, BaseDataType.UBYTE, resultRegister)
"prog8_lib_square_word" -> funcSquare(fcall, BaseDataType.UWORD, resultRegister)
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
}
return BuiltinFunctions.getValue(fcall.name).returnType
}
private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: DataType, resultRegister: RegisterOrPair?) {
private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: BaseDataType, resultRegister: RegisterOrPair?) {
// square of word value is faster with dedicated routine, square of byte just use the regular multiplication routine.
when (resultType) {
DataType.UBYTE -> {
BaseDataType.UBYTE -> {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A)
asmgen.out(" tay | jsr prog8_math.multiply_bytes")
if(resultRegister!=null) {
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister, false, fcall.position, null, asmgen), CpuRegister.A, false, false)
}
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_math.square")
if(resultRegister!=null) {
@@ -107,8 +109,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.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[3].position, var3name)
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)
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false, false)
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false, false)
}
@@ -116,11 +118,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcDivmodW(fcall: PtBuiltinFunctionCall) {
asmgen.assignWordOperandsToAYAndVar(fcall.args[1], fcall.args[0], "P8ZP_SCRATCH_W1")
// math.divmod_uw_asm: -- divide two unsigned words (16 bit each) into 16 bit results
// input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor
// output: P8ZP_SCRATCH_W2 in ZP: 16 bit remainder, A/Y: 16 bit division result
// input: P8ZP_SCRATCH_W1 in ZP: 16-bit number, A/Y: 16 bit divisor
// output: P8ZP_SCRATCH_W2 in ZP: 16-bit remainder, A/Y: 16 bit division result
asmgen.out(" jsr prog8_math.divmod_uw_asm")
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.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 +198,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
return
}
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD) // jump address
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.forDt(BaseDataType.UWORD)) // jump address
asmgen.out("""
; push a return address so the jmp becomes indirect jsr
lda #>((+)-1)
@@ -303,8 +305,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcCmp(fcall: PtBuiltinFunctionCall) {
val arg1 = fcall.args[0]
val arg2 = fcall.args[1]
if(arg1.type in ByteDatatypes) {
if(arg2.type in ByteDatatypes) {
if(arg1.type.isByte) {
if(arg2.type.isByte) {
when (arg2) {
is PtIdentifier -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
@@ -336,7 +338,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
throw AssemblyError("args for cmp() should have same dt")
} else {
// arg1 is a word
if(arg2.type in WordDatatypes) {
if(arg2.type.isWord) {
when (arg2) {
is PtIdentifier -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
@@ -374,11 +376,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.UWORD, fcall.position)
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.forDt(BaseDataType.UWORD), fcall.position)
val addressOf = PtAddressOf(fcall.position)
addressOf.add(slabname)
addressOf.parent = fcall
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.forDt(BaseDataType.UWORD), expression = addressOf)
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
asmgen.translateNormalAssignment(assign, fcall.definingISub())
@@ -386,16 +388,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
when(fcall.args[0].type) {
DataType.UBYTE -> {
when(fcall.args[0].type.base) {
BaseDataType.UBYTE -> {
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false, false)
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false, false)
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
}
@@ -405,8 +407,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcRor2(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single()
when (what.type) {
DataType.UBYTE -> {
when (what.type.base) {
BaseDataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
@@ -429,7 +431,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
@@ -452,8 +454,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcRor(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single()
when (what.type) {
DataType.UBYTE -> {
when (what.type.base) {
BaseDataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
@@ -496,7 +498,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
@@ -521,8 +523,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcRol2(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single()
when (what.type) {
DataType.UBYTE -> {
when (what.type.base) {
BaseDataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
@@ -545,7 +547,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
@@ -568,8 +570,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcRol(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single()
when (what.type) {
DataType.UBYTE -> {
when (what.type.base) {
BaseDataType.UBYTE -> {
when (what) {
is PtArrayIndexer -> {
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
@@ -612,7 +614,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
when (what) {
is PtArrayIndexer -> {
if(!what.index.isSimple()) asmgen.out(" php") // save Carry
@@ -640,27 +642,33 @@ 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.UBYTE, fcall.definingSub(), fcall.position, variableAsmName = varname)
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, variableAsmName = varname)
}
is PtNumber -> {
val num = (fcall.args[0] as PtNumber).number + if(msb) 1 else 0
val mem = PtMemoryByte(fcall.position)
mem.add(PtNumber(DataType.UBYTE, num, fcall.position))
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, memory = mem)
mem.add(PtNumber(BaseDataType.UBYTE, num, fcall.position))
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, memory = mem)
}
is PtAddressOf -> {
val mem = PtMemoryByte(fcall.position)
if((fcall.args[0] as PtAddressOf).isFromArrayElement)
TODO("address-of arrayelement")
if(msb) {
val address = PtBinaryExpression("+", DataType.UWORD, fcall.args[0].position)
address.add(fcall.args[0])
address.add(PtNumber(address.type, 1.0, fcall.args[0].position))
mem.add(address)
val addrof = fcall.args[0] as PtAddressOf
if(addrof.identifier.type.isSplitWordArray) {
TODO("address of split word array")
} else {
mem.add(fcall.args[0])
val mem = PtMemoryByte(fcall.position)
if(addrof.isFromArrayElement)
TODO("address-of arrayelement")
if(msb) {
val address = PtBinaryExpression("+", DataType.forDt(BaseDataType.UWORD), addrof.position)
address.add(addrof)
address.add(PtNumber(address.type.base, 1.0, addrof.position))
mem.add(address)
} else {
mem.add(addrof)
}
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, memory = mem)
}
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, memory = mem)
}
is PtArrayIndexer -> {
val indexer = fcall.args[0] as PtArrayIndexer
@@ -668,7 +676,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val msbAdd: Int
if(indexer.splitWords) {
val arrayVariable = indexer.variable
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.ARRAY_UB, arrayVariable.position)
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.arrayFor(BaseDataType.UBYTE, false), arrayVariable.position)
indexer.children[0].parent = indexer
elementSize = 1
msbAdd = 0
@@ -680,7 +688,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
// double the index because of word array (if not split), add one if msb (if not split)
val constIndexNum = (indexer.index as? PtNumber)?.number
if(constIndexNum!=null) {
indexer.children[1] = PtNumber(indexer.index.type, constIndexNum*elementSize + msbAdd, indexer.position)
indexer.children[1] = PtNumber(indexer.index.type.base, constIndexNum*elementSize + msbAdd, indexer.position)
indexer.children[1].parent = indexer
} else {
val multipliedIndex: PtExpression
@@ -689,12 +697,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else {
multipliedIndex = PtBinaryExpression("<<", indexer.index.type, indexer.position)
multipliedIndex.add(indexer.index)
multipliedIndex.add(PtNumber(DataType.UBYTE, 1.0, indexer.position))
multipliedIndex.add(PtNumber(BaseDataType.UBYTE, 1.0, indexer.position))
}
if(msbAdd>0) {
val msbIndex = PtBinaryExpression("+", indexer.index.type, indexer.position)
msbIndex.add(multipliedIndex)
msbIndex.add(PtNumber(DataType.UBYTE, msbAdd.toDouble(), indexer.position))
msbIndex.add(PtNumber(BaseDataType.UBYTE, msbAdd.toDouble(), indexer.position))
indexer.children[1] = msbIndex
msbIndex.parent = indexer
} else {
@@ -702,7 +710,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
multipliedIndex.parent=indexer
}
}
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, array = indexer)
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, array = indexer)
}
else -> throw AssemblyError("setlsb/setmsb on weird target ${fcall.args[0]}")
}
@@ -717,12 +725,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
when (val dt = fcall.args.single().type) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
DataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_into_A")
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
when (val dt = fcall.args.single().type.base) {
BaseDataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
BaseDataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
BaseDataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
BaseDataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_into_A")
BaseDataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
else -> throw AssemblyError("weird type $dt")
}
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true, true)
@@ -730,24 +738,24 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
val dt = fcall.args.single().type
val dt = fcall.args.single().type.base
when (dt) {
DataType.BYTE -> {
BaseDataType.BYTE -> {
asmgen.out(" jsr prog8_lib.abs_b_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A,false, true)
}
DataType.WORD -> {
BaseDataType.WORD -> {
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
}
DataType.UBYTE -> {
BaseDataType.UBYTE -> {
asmgen.assignRegister(RegisterOrPair.A, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.A, false, fcall.position, scope, asmgen))
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
asmgen.assignRegister(RegisterOrPair.AY, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.AY, false, fcall.position, scope, asmgen))
}
else -> throw AssemblyError("weird type")
@@ -773,8 +781,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.assignConstFloatToPointerAY(number)
}
else -> {
val tempvar = asmgen.getTempVarName(DataType.FLOAT)
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.forDt(BaseDataType.FLOAT))
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out("""
pha
@@ -849,7 +857,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" jsr floats.MOVFM")
if(resultRegister!=null) {
assignAsmGen.assignFAC1float(
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position, null, null, null, resultRegister, null))
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.FLOAT), fcall.definingISub(), fcall.position, null, null, null, resultRegister, null))
}
}
@@ -917,9 +925,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
when(fcall.type) {
in ByteDatatypes -> {
val signed = fcall.type.isSigned
when {
fcall.type.isByte -> {
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W1+1", fcall.args[2].type) // maximum
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, signed) // value
@@ -927,7 +935,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
assignAsmGen.assignRegisterByte(targetReg, CpuRegister.A, signed, true)
}
in WordDatatypes -> {
fcall.type.isWord -> {
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W2", fcall.args[2].type) // maximum
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY, signed) // value
@@ -940,9 +948,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcMin(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
when (fcall.type) {
in ByteDatatypes -> {
val signed = fcall.type.isSigned
when {
fcall.type.isByte -> {
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_B1", fcall.type) // right
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // left
asmgen.out(" cmp P8ZP_SCRATCH_B1")
@@ -953,7 +961,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.A, targetReg)
}
in WordDatatypes -> {
fcall.type.isWord -> {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
if(signed) {
@@ -998,8 +1006,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
private fun funcMax(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
if(fcall.type in ByteDatatypes) {
val signed = fcall.type.isSigned
if(fcall.type.isByte) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // right
asmgen.out(" cmp P8ZP_SCRATCH_B1")
@@ -1009,7 +1017,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
+""")
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.A, targetReg)
} else if(fcall.type in WordDatatypes) {
} else if(fcall.type.isWord) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
if(signed) {
@@ -1100,7 +1108,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcMsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val arg = fcall.args.single()
if (arg.type !in WordDatatypes)
if (!arg.type.isWord)
throw AssemblyError("msb required word argument")
if (arg is PtNumber)
throw AssemblyError("msb(const) should have been const-folded away")
@@ -1123,7 +1131,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("invalid reg")
}
} else {
if(arg is PtArrayIndexer && resultRegister in setOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
// just read the msb byte out of the word array
if(arg.splitWords) {
val arrayVar = asmgen.asmVariableName(arg.variable)+"_msb"
@@ -1199,7 +1207,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcLsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val arg = fcall.args.single()
if (arg.type !in WordDatatypes)
if (!arg.type.isWord)
throw AssemblyError("lsb required word argument")
if (arg is PtNumber)
throw AssemblyError("lsb(const) should have been const-folded away")
@@ -1223,7 +1231,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("invalid reg")
}
} else {
if(arg is PtArrayIndexer && resultRegister in setOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
// just read the lsb byte out of the word array
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable)
when(resultRegister) {
@@ -1282,7 +1290,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) {
val signature = BuiltinFunctions.getValue(call.name)
val callConv = signature.callConvention(call.args.map { it.type})
val callConv = signature.callConvention(call.args.map {
require(it.type.isNumericOrBool)
it.type.base
})
fun getSourceForFloat(value: PtExpression): AsmAssignSource {
return when (value) {
@@ -1300,11 +1311,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.FLOAT, value.position)
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.forDt(BaseDataType.FLOAT), value.position)
val addr = PtAddressOf(value.position)
addr.add(variable)
addr.parent = call
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.forDt(BaseDataType.FLOAT))
AsmAssignSource.fromAstSource(addr, program, asmgen)
}
}
@@ -1317,9 +1328,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
when {
conv.variable -> {
val varname = "prog8_lib.func_${call.name}._arg_${paramName}"
val src = when (conv.dt) {
DataType.FLOAT -> getSourceForFloat(value)
in PassByReferenceDatatypes -> {
val src = when {
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
conv.dt.isPassByRef -> {
// put the address of the argument in AY
val addr = PtAddressOf(value.position)
addr.add(value)
@@ -1330,14 +1341,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
AsmAssignSource.fromAstSource(value, program, asmgen)
}
}
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, value.position, variableAsmName = varname)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(conv.dt), null, value.position, variableAsmName = varname)
val assign = AsmAssignment(src, tgt, program.memsizer, value.position)
asmgen.translateNormalAssignment(assign, scope)
}
conv.reg != null -> {
val src = when (conv.dt) {
DataType.FLOAT -> getSourceForFloat(value)
in PassByReferenceDatatypes -> {
val src = when {
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
conv.dt.isPassByRef -> {
// put the address of the argument in AY
val addr = PtAddressOf(value.position)
addr.add(value)
@@ -3,7 +3,9 @@ package prog8.codegen.cpu6502
import prog8.code.ast.IPtSubroutine
import prog8.code.ast.PtAsmSub
import prog8.code.ast.PtSub
import prog8.code.core.*
import prog8.code.core.DataType
import prog8.code.core.RegisterOrPair
import prog8.code.core.RegisterOrStatusflag
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> {
@@ -16,10 +18,10 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
return if(returntype==null)
emptyList()
else {
val register = when (returntype!!) {
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
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)
}
listOf(Pair(register, returntype!!))
@@ -30,11 +32,11 @@ internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, D
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
return when(returntype) {
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
null -> null
return when {
returntype?.isByteOrBool==true -> RegisterOrStatusflag(RegisterOrPair.A, null)
returntype?.isWord==true -> RegisterOrStatusflag(RegisterOrPair.AY, null)
returntype?.isFloat==true -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
returntype==null -> null
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
}
}
@@ -46,10 +46,10 @@ internal class ForLoopsAsmGen(
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) {
DataType.ARRAY_B, DataType.ARRAY_UB -> {
when {
iterableDt.isByteArray -> {
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
if (stepsize==-1 && range.to.asConstInteger()==0) {
// simple loop downto 0 step -1
asmgen.out(loopLabel)
@@ -75,7 +75,7 @@ internal class ForLoopsAsmGen(
// loop over byte range via loopvar
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
// pre-check for end already reached
if(iterableDt==DataType.ARRAY_B) {
if(iterableDt.isSignedByteArray) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0) {
asmgen.out("""
@@ -121,7 +121,7 @@ $modifiedLabel cmp #0 ; modified
// loop over byte range via loopvar
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
// pre-check for end already reached
if(iterableDt==DataType.ARRAY_B) {
if(iterableDt.isSignedByteArray) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0)
asmgen.out("""
@@ -171,7 +171,7 @@ $modifiedLabel cmp #0 ; modified
asmgen.out(endLabel)
}
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> {
val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvarWord(stmt, range)
if(stepsize==-1 && range.to.asConstInteger()==0) {
@@ -249,7 +249,7 @@ $modifiedLabel2 cmp #0 ; modified
$loopLabel""")
asmgen.translate(stmt.statements)
if (iterableDt == DataType.ARRAY_UW) {
if (iterableDt.isUnsignedWordArray) {
asmgen.out("""
lda $varname
clc
@@ -324,7 +324,7 @@ $endLabel""")
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
// pre-check for end already reached.
// 'to' is in AY, do NOT clobber this!
if(iterableDt==DataType.ARRAY_W) {
if(iterableDt.isSignedWordArray) {
if(stepsize<0)
asmgen.out("""
sta P8ZP_SCRATCH_W2 ; to
@@ -381,8 +381,8 @@ $endLabel""")
is StMemVar -> symbol.length!!
else -> 0
}
when(iterableDt) {
DataType.STR -> {
when {
iterableDt.isString -> {
asmgen.out("""
lda #<$iterableName
ldy #>$iterableName
@@ -399,7 +399,7 @@ $loopLabel lda ${65535.toHex()} ; modified
bne $loopLabel
$endLabel""")
}
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_BOOL -> {
iterableDt.isByteArray || iterableDt.isBoolArray -> {
val indexVar = asmgen.makeLabel("for_index")
asmgen.out("""
ldy #0
@@ -424,7 +424,7 @@ $loopLabel sty $indexVar
}
if(numElements>=16) {
// allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
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") }
@@ -434,7 +434,45 @@ $loopLabel sty $indexVar
}
asmgen.out(endLabel)
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
iterableDt.isSplitWordArray -> {
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
ldy #0
$loopLabel sty $indexVar
lda ${iterableName}_lsb,y
sta $loopvarName
lda ${iterableName}_msb,y
sta $loopvarName+1""")
asmgen.translate(stmt.statements)
if(numElements<=255) {
asmgen.out("""
ldy $indexVar
iny
cpy #$numElements
beq $endLabel
bne $loopLabel""")
} else {
// length is 256
asmgen.out("""
ldy $indexVar
iny
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")
}
asmgen.out(endLabel)
}
iterableDt.isWordArray -> {
val length = numElements * 2
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
@@ -465,7 +503,7 @@ $loopLabel sty $indexVar
}
if(length>=16) {
// allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
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") }
@@ -475,45 +513,7 @@ $loopLabel sty $indexVar
}
asmgen.out(endLabel)
}
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
ldy #0
$loopLabel sty $indexVar
lda ${iterableName}_lsb,y
sta $loopvarName
lda ${iterableName}_msb,y
sta $loopvarName+1""")
asmgen.translate(stmt.statements)
if(numElements<=255) {
asmgen.out("""
ldy $indexVar
iny
cpy #$numElements
beq $endLabel
bne $loopLabel""")
} else {
// length is 256
asmgen.out("""
ldy $indexVar
iny
bne $loopLabel
beq $endLabel""")
}
if(numElements>=16) {
// allocate index var on ZP if possible
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)
}
DataType.ARRAY_F -> {
iterableDt.isFloatArray -> {
throw AssemblyError("for loop with floating point variables is not supported")
}
else -> throw AssemblyError("can't iterate over $iterableDt")
@@ -524,12 +524,12 @@ $loopLabel sty $indexVar
private fun translateForOverConstRange(stmt: PtForLoop, iterableDt: DataType, range: IntProgression) {
if (range.isEmpty() || range.step==0)
throw AssemblyError("empty range or step 0")
if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) {
if(iterableDt.isByteArray) {
if(range.last==range.first) return translateForSimpleByteRangeAsc(stmt, range)
if(range.step==1 && range.last>range.first) return translateForSimpleByteRangeAsc(stmt, range)
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range, iterableDt==DataType.ARRAY_UB)
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range, iterableDt.isUnsignedByteArray)
}
else if(iterableDt==DataType.ARRAY_W || iterableDt==DataType.ARRAY_UW) {
else if(iterableDt.isWordArray) {
if(range.last==range.first) return translateForSimpleWordRangeAsc(stmt, range)
if(range.step==1 && range.last>range.first) return translateForSimpleWordRangeAsc(stmt, range)
if(range.step==-1 && range.last<range.first) return translateForSimpleWordRangeDesc(stmt, range)
@@ -539,8 +539,8 @@ $loopLabel sty $indexVar
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.add(endLabel)
when(iterableDt) {
DataType.ARRAY_B, DataType.ARRAY_UB -> {
when {
iterableDt.isByteArray -> {
// loop over byte range via loopvar, step >= 2 or <= -2
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
@@ -605,7 +605,7 @@ $loopLabel""")
}
asmgen.out(endLabel)
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> {
// loop over word range via loopvar, step >= 2 or <= -2
val varname = asmgen.asmVariableName(stmt.variable)
when (range.step) {
@@ -15,10 +15,12 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// just ignore any result values from the function call.
}
// TODO tweak subroutine call convention to also make use of X register to pass a third byte?
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypesWithBoolean)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypesWithBoolean && sub.parameters[1].type in ByteDatatypesWithBoolean)
internal fun optimizeIntArgsViaCpuRegisters(params: List<PtSubroutineParameter>) =
when(params.size) {
1 -> params[0].type.isIntegerOrBool
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool
else -> false
}
internal fun translateFunctionCall(call: PtFunctionCall) {
// Output only the code to set up the parameters and perform the actual call
@@ -122,23 +124,23 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
}
}
else if(sub is PtSub) {
if(optimizeIntArgsViaRegisters(sub)) {
if(sub.parameters.size==1) {
val register = if (sub.parameters[0].type in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register)
} else {
// 2 byte params, second in Y, first in A
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], RegisterOrPair.A)
if(asmgen.needAsaveForExpr(call.args[1]))
asmgen.out(" pha")
argumentViaRegister(sub, IndexedValue(1, sub.parameters[1]), call.args[1], RegisterOrPair.Y)
if(asmgen.needAsaveForExpr(call.args[1]))
asmgen.out(" pla")
}
if(optimizeIntArgsViaCpuRegisters(sub.parameters)) {
// Note that if the args fit into cpu registers, we don't concern ourselves here
// if they should be put into regular subroutine parameter variables, or the R0-R15 register variables.
// That is now up to the subroutine itself.
useCpuRegistersForArgs(call.args, sub)
} else {
// arguments via variables
for(arg in sub.parameters.withIndex().zip(call.args))
argumentViaVariable(sub, arg.first.value, arg.second)
val (normalParams, registerParams) = sub.parameters.withIndex().partition { it.value.register == null }
if (normalParams.isNotEmpty()) {
for (arg in normalParams.zip(call.args))
argumentViaVariable(sub, arg.first.value, arg.second)
}
if (registerParams.isNotEmpty()) {
// the R0-R15 'registers' are not really registers. They're just special variables.
for (arg in registerParams.zip(call.args))
argumentViaVariable(sub, arg.first.value, arg.second)
}
}
asmgen.out(" jsr $subAsmName")
}
@@ -147,11 +149,35 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
}
private fun useCpuRegistersForArgs(args: List<PtExpression>, sub: PtSub) {
val params = sub.parameters
when(params.size) {
1 -> {
val register = if (params[0].type.isByteOrBool) RegisterOrPair.A else RegisterOrPair.AY
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], register)
}
2 -> {
if(params[0].type.isByteOrBool && params[1].type.isByteOrBool) {
// 2 byte params, second in Y, first in A
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
if(asmgen.needAsaveForExpr(args[1]))
asmgen.out(" pha")
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
if(asmgen.needAsaveForExpr(args[1]))
asmgen.out(" pla")
} else {
throw AssemblyError("cannot use registers for word+byte")
}
}
else -> throw AssemblyError("cannot use cpu registers for >2 arguments")
}
}
private fun usesOtherRegistersWhileEvaluating(arg: PtExpression): Boolean {
return when(arg) {
is PtBuiltinFunctionCall -> {
if (arg.name == "lsb" || arg.name == "msb")
if (arg.name in arrayOf("lsb", "msb", "lsw", "msw"))
return usesOtherRegistersWhileEvaluating(arg.args[0])
if (arg.name == "mkword")
return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1])
@@ -209,8 +235,15 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
if(!isArgumentTypeCompatible(value.type, parameter.type))
throw AssemblyError("argument type incompatible")
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
asmgen.assignExpressionToVariable(value, varName, parameter.type)
val reg = parameter.register
if(reg!=null) {
require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
val varName = "cx16.${reg.name.lowercase()}"
asmgen.assignExpressionToVariable(value, varName, parameter.type)
} else {
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
asmgen.assignExpressionToVariable(value, varName, parameter.type)
}
}
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag {
@@ -226,7 +259,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
val register = paramRegister.registerOrPair
val requiredDt = parameter.value.type
if(requiredDt!=value.type) {
if(value.type largerThan requiredDt)
if(value.type.largerSizeThan(requiredDt))
throw AssemblyError("can only convert byte values to word param types")
}
if (statusflag!=null) {
@@ -265,21 +298,20 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
else {
// via register or register pair
register!!
if(requiredDt largerThan value.type) {
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.UBYTE)
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UBYTE))
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type.base)
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
} else {
val scope = value.definingISub()
val target: AsmAssignTarget =
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
if(parameter.value.type.isByte && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
else {
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD
AsmAssignTarget.fromRegisters(register, signed, value.position, scope, asmgen)
AsmAssignTarget.fromRegisters(register, parameter.value.type.isSigned, value.position, scope, asmgen)
}
val src = if(value.type in PassByReferenceDatatypes) {
val src = if(value.type.isPassByRef) {
if(value is PtIdentifier) {
val addr = PtAddressOf(Position.DUMMY)
addr.add(value)
@@ -300,18 +332,18 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
if(argType isAssignableTo paramType)
return true
if(argType==DataType.BOOL && paramType==DataType.BOOL)
if(argType.isBool && paramType.isBool)
return true
if(argType in ByteDatatypes && paramType in ByteDatatypes)
if(argType.isByte && paramType.isByte)
return true
if(argType in WordDatatypes && paramType in WordDatatypes)
if(argType.isWord && paramType.isWord)
return true
// we have a special rule for some types.
// strings are assignable to UWORD, for example, and vice versa
if(argType==DataType.STR && paramType==DataType.UWORD)
if(argType.isString && paramType.isUnsignedWord)
return true
if(argType==DataType.UWORD && paramType == DataType.STR)
if(argType.isUnsignedWord && paramType.isString)
return true
return false
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,370 @@
package prog8.codegen.cpu6502
import prog8.code.ast.PtBinaryExpression
import prog8.code.ast.PtBuiltinFunctionCall
import prog8.code.ast.PtExpression
import prog8.code.ast.PtIdentifier
import prog8.code.ast.PtIfExpression
import prog8.code.ast.PtNumber
import prog8.code.ast.PtPrefix
import prog8.code.core.AssemblyError
import prog8.code.core.BaseDataType
import prog8.code.core.CpuRegister
import prog8.code.core.DataType
import prog8.code.core.IErrorReporter
import prog8.code.core.LogicalOperators
import prog8.code.core.RegisterOrPair
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
import prog8.codegen.cpu6502.assignment.TargetStorageKind
internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) {
internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) {
require(target.datatype==expr.type)
val falseLabel = asmgen.makeLabel("ifexpr_false")
val endLabel = asmgen.makeLabel("ifexpr_end")
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
when {
expr.type.isByteOrBool -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
}
expr.type.isWord -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
}
expr.type.isFloat -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
asmgen.out(endLabel)
asmgen.assignRegister(RegisterOrPair.FAC1, target)
}
else -> throw AssemblyError("weird dt")
}
}
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")
}
}
else if(condition is PtPrefix && condition.operator=="not") {
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
} else {
// 'simple' condition, check if it is a byte bittest
val bittest = condition as? PtBuiltinFunctionCall
if(bittest!=null && bittest.name.startsWith("prog8_ifelse_bittest_")) {
val variable = bittest.args[0] as PtIdentifier
val bitnumber = (bittest.args[1] as PtNumber).number.toInt()
val testForBitSet = bittest.name.endsWith("_set")
when (bitnumber) {
7 -> {
// test via bit + N flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) asmgen.out(" bpl $falseLabel")
else asmgen.out(" bmi $falseLabel")
return
}
6 -> {
// test via bit + V flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) asmgen.out(" bvc $falseLabel")
else asmgen.out(" bvs $falseLabel")
return
}
else -> throw AssemblyError("prog8_ifelse_bittest can only work on bits 7 and 6")
}
}
// 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")
}
}
private fun translateIfExpressionByteConditionBranch(condition: PtBinaryExpression, falseLabel: String) {
val signed = condition.left.type.isSigned
val constValue = condition.right.asConstInteger()
if(constValue==0) {
return translateIfCompareWithZeroByteBranch(condition, signed, falseLabel)
}
when(condition.operator) {
"==" -> {
// if X==value
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
asmgen.cmpAwithByteValue(condition.right, false)
asmgen.out(" bne $falseLabel")
}
"!=" -> {
// if X!=value
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
asmgen.cmpAwithByteValue(condition.right, false)
asmgen.out(" beq $falseLabel")
}
in LogicalOperators -> {
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), condition.definingISub(), condition.position, register=RegisterOrPair.A)
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
asmgen.out(" beq $falseLabel")
} else {
errors.warn("SLOW FALLBACK FOR 'IFEXPR' CODEGEN - ask for support", condition.position) // should not occur ;-)
asmgen.assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
}
else -> {
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
// TODO: special cases for <, <=, >, >= above.
asmgen.assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
}
}
private fun translateIfExpressionWordConditionBranch(condition: PtBinaryExpression, falseLabel: String) {
// TODO can we reuse this whole thing from IfElse ?
val constValue = condition.right.asConstInteger()
if(constValue!=null) {
if (constValue == 0) {
when (condition.operator) {
"==" -> return translateWordExprIsZero(condition.left, falseLabel)
"!=" -> return translateWordExprIsNotZero(condition.left, falseLabel)
}
}
if (constValue != 0) {
when (condition.operator) {
"==" -> return translateWordExprEqualsNumber(condition.left, constValue, falseLabel)
"!=" -> return translateWordExprNotEqualsNumber(condition.left, constValue, falseLabel)
}
}
}
val variable = condition.right as? PtIdentifier
if(variable!=null) {
when (condition.operator) {
"==" -> return translateWordExprEqualsVariable(condition.left, variable, falseLabel)
"!=" -> return translateWordExprNotEqualsVariable(condition.left, variable, falseLabel)
}
}
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
asmgen.assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
private fun translateIfExpressionFloatConditionBranch(condition: PtBinaryExpression, elseLabel: String) {
val constValue = (condition.right as? PtNumber)?.number
if(constValue==0.0) {
if (condition.operator == "==") {
// if FL==0.0
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | cmp #0 | bne $elseLabel")
return
} else if(condition.operator=="!=") {
// if FL!=0.0
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | cmp #0 | beq $elseLabel")
return
}
}
when(condition.operator) {
"==" -> {
asmgen.translateFloatsEqualsConditionIntoA(condition.left, condition.right)
asmgen.out(" beq $elseLabel")
}
"!=" -> {
asmgen.translateFloatsEqualsConditionIntoA(condition.left, condition.right)
asmgen.out(" bne $elseLabel")
}
"<" -> {
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, false)
asmgen.out(" beq $elseLabel")
}
"<=" -> {
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, true)
asmgen.out(" beq $elseLabel")
}
">" -> {
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, true)
asmgen.out(" bne $elseLabel")
}
">=" -> {
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, false)
asmgen.out(" bne $elseLabel")
}
else -> throw AssemblyError("expected comparison operator")
}
}
private fun translateWordExprNotEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
// if w!=variable
// TODO reuse code from ifElse?
val varRight = asmgen.asmVariableName(variable)
if(expr is PtIdentifier) {
val varLeft = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varLeft
cmp $varRight
bne +
lda $varLeft+1
cmp $varRight+1
beq $falseLabel
+""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.out("""
cmp $varRight
bne +
cpy $varRight+1
beq $falseLabel
+""")
}
}
private fun translateWordExprEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
// if w==variable
// TODO reuse code from ifElse?
val varRight = asmgen.asmVariableName(variable)
if(expr is PtIdentifier) {
val varLeft = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varLeft
cmp $varRight
bne $falseLabel
lda $varLeft+1
cmp $varRight+1
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.out("""
cmp $varRight
bne $falseLabel
cpy $varRight+1
bne $falseLabel""")
}
}
private fun translateWordExprNotEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
// if w!=number
// TODO reuse code from ifElse?
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varname
cmp #<$number
bne +
lda $varname+1
cmp #>$number
beq $falseLabel
+""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.out("""
cmp #<$number
bne +
cpy #>$number
beq $falseLabel
+""")
}
}
private fun translateWordExprEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
// if w==number
// TODO reuse code from ifElse?
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varname
cmp #<$number
bne $falseLabel
lda $varname+1
cmp #>$number
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.out( """
cmp #<$number
bne $falseLabel
cpy #>$number
bne $falseLabel""")
}
}
private fun translateWordExprIsNotZero(expr: PtExpression, falseLabel: String) {
// if w!=0
// TODO reuse code from ifElse?
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varname
ora $varname+1
beq $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
}
}
private fun translateWordExprIsZero(expr: PtExpression, falseLabel: String) {
// if w==0
// TODO reuse code from ifElse?
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varname
ora $varname+1
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
}
}
private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) {
// optimized code for byte comparisons with 0
asmgen.assignConditionValueToRegisterAndTest(condition.left)
when (condition.operator) {
"==" -> asmgen.out(" bne $falseLabel")
"!=" -> asmgen.out(" beq $falseLabel")
">" -> {
if(signed) asmgen.out(" bmi $falseLabel | beq $falseLabel")
else asmgen.out(" beq $falseLabel")
}
">=" -> {
if(signed) asmgen.out(" bmi $falseLabel")
else { /* always true for unsigned */ }
}
"<" -> {
if(signed) asmgen.out(" bpl $falseLabel")
else asmgen.jmp(falseLabel)
}
"<=" -> {
if(signed) {
// inverted '>'
asmgen.out("""
beq +
bpl $falseLabel
+""")
} else asmgen.out(" bne $falseLabel")
}
else -> throw AssemblyError("expected comparison operator")
}
}
}
@@ -198,12 +198,12 @@ internal class ProgramAndVarsGen(
for(num in 1..count) {
val name = asmgen.buildTempVarName(dt, num)
when (dt) {
DataType.BOOL -> asmgen.out("$name .byte ?")
DataType.BYTE -> asmgen.out("$name .char ?")
DataType.UBYTE -> asmgen.out("$name .byte ?")
DataType.WORD -> asmgen.out("$name .sint ?")
DataType.UWORD -> asmgen.out("$name .word ?")
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
BaseDataType.BOOL -> asmgen.out("$name .byte ?")
BaseDataType.BYTE -> asmgen.out("$name .char ?")
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
BaseDataType.WORD -> asmgen.out("$name .sint ?")
BaseDataType.UWORD -> asmgen.out("$name .word ?")
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
else -> throw AssemblyError("weird dt for extravar $dt")
}
}
@@ -421,22 +421,35 @@ internal class ProgramAndVarsGen(
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
entrypointInitialization()
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
asmgen.out("; simple int arg(s) passed via register(s)")
if(sub.parameters.size==1) {
val dt = sub.parameters[0].type
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
if(dt in ByteDatatypesWithBoolean)
asmgen.assignRegister(RegisterOrPair.A, target)
val params = sub.parameters
if(functioncallAsmGen.optimizeIntArgsViaCpuRegisters(params)) {
asmgen.out("; simple int arg(s) passed via cpu register(s)")
fun varname(param: PtSubroutineParameter): String =
if(param.register==null)
param.name
else
asmgen.assignRegister(RegisterOrPair.AY, target)
} else {
require(sub.parameters.size==2)
// 2 simple byte args, first in A, second in Y
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name)
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, sub.parameters[1].position, variableAsmName = sub.parameters[1].name)
asmgen.assignRegister(RegisterOrPair.A, target1)
asmgen.assignRegister(RegisterOrPair.Y, target2)
param.register!!.asScopedNameVirtualReg(param.type).joinToString(".")
when(params.size) {
1 -> {
val dt = params[0].type
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, params[0].position, variableAsmName = varname(params[0]))
if(dt.isByteOrBool)
asmgen.assignRegister(RegisterOrPair.A, target) // single byte in A
else
asmgen.assignRegister(RegisterOrPair.AY, target) // word in AY
}
2 -> {
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, params[0].type, sub, params[0].position, variableAsmName = varname(params[0]))
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, params[1].type, sub, params[1].position, variableAsmName = varname(params[1]))
if(params[0].type.isByteOrBool && params[1].type.isByteOrBool) {
// 2 byte args, first in A, second in Y
asmgen.assignRegister(RegisterOrPair.A, target1)
asmgen.assignRegister(RegisterOrPair.Y, target2)
} else throw AssemblyError("cannot use registers for word+byte args")
}
else -> throw AssemblyError("cannot use registers for >2 args")
}
}
@@ -450,9 +463,9 @@ internal class ProgramAndVarsGen(
if(addr!=null)
asmgen.out("$name = $addr")
else when(dt) {
DataType.UBYTE -> asmgen.out("$name .byte ?")
DataType.UWORD -> asmgen.out("$name .word ?")
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
BaseDataType.UWORD -> asmgen.out("$name .word ?")
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
else -> throw AssemblyError("weird dt for extravar $dt")
}
}
@@ -544,7 +557,7 @@ internal class ProgramAndVarsGen(
private fun getZpStringVarsWithInitvalue(): Collection<ZpStringWithInitial> {
val result = mutableListOf<ZpStringWithInitial>()
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
val vars = allocator.zeropageVars.filter { it.value.dt.isString }
for (variable in vars) {
val scopedName = variable.key
val svar = symboltable.lookup(scopedName) as? StStaticVariable
@@ -556,7 +569,7 @@ internal class ProgramAndVarsGen(
private fun getZpArrayVarsWithInitvalue(): Collection<ZpArrayWithInitial> {
val result = mutableListOf<ZpArrayWithInitial>()
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
val vars = allocator.zeropageVars.filter { it.value.dt.isArray }
for (variable in vars) {
val scopedName = variable.key
val svar = symboltable.lookup(scopedName) as? StStaticVariable
@@ -572,7 +585,7 @@ internal class ProgramAndVarsGen(
if (scopedName.startsWith("cx16.r"))
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped
val variable = symboltable.flat.getValue(scopedName) as StStaticVariable
if(variable.dt in SplitWordArrayTypes) {
if(variable.dt.isSplitWordArray) {
val lsbAddr = zpvar.address
val msbAddr = zpvar.address + (zpvar.size/2).toUInt()
asmgen.out("${scopedName.substringAfterLast('.')}_lsb \t= $lsbAddr \t; zp ${zpvar.dt} (lsbs)")
@@ -590,10 +603,10 @@ internal class ProgramAndVarsGen(
asmgen.out("; non-zeropage variables")
asmgen.out(" .section BSS")
val (notAligned, aligned) = varsNoInit.partition { it.align==0 }
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt }).forEach {
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
uninitializedVariable2asm(it)
}
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt }).forEach {
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base }).forEach {
uninitializedVariable2asm(it)
}
asmgen.out(" .send BSS")
@@ -601,7 +614,7 @@ internal class ProgramAndVarsGen(
if(varsWithInit.isNotEmpty()) {
asmgen.out("; non-zeropage variables with init value")
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt == DataType.STR }
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt.isString }
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0 }
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0 }
notAlignedStrings.forEach {
@@ -631,19 +644,20 @@ internal class ProgramAndVarsGen(
}
private fun uninitializedVariable2asm(variable: StStaticVariable) {
when (variable.dt) {
DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?")
DataType.BYTE -> asmgen.out("${variable.name}\t.char ?")
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
in SplitWordArrayTypes -> {
val dt = variable.dt
when {
dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ?")
dt.isSignedByte -> asmgen.out("${variable.name}\t.char ?")
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ?")
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint ?")
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
dt.isSplitWordArray -> {
alignVar(variable.align)
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
}
in ArrayDatatypes -> {
dt.isArray -> {
alignVar(variable.align)
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
asmgen.out("${variable.name}\t.fill $numbytes")
@@ -662,18 +676,19 @@ internal class ProgramAndVarsGen(
private fun staticVariable2asm(variable: StStaticVariable) {
val initialValue: Number =
if(variable.initializationNumericValue!=null) {
if(variable.dt== DataType.FLOAT)
if(variable.dt.isFloat)
variable.initializationNumericValue!!
else
variable.initializationNumericValue!!.toInt()
} else 0
when (variable.dt) {
DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
DataType.BYTE -> asmgen.out("${variable.name}\t.char $initialValue")
DataType.UWORD -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
DataType.WORD -> asmgen.out("${variable.name}\t.sint $initialValue")
DataType.FLOAT -> {
val dt=variable.dt
when {
dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
dt.isSignedByte -> asmgen.out("${variable.name}\t.char $initialValue")
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint $initialValue")
dt.isFloat -> {
if(initialValue==0) {
asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
} else {
@@ -681,10 +696,10 @@ internal class ProgramAndVarsGen(
asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue")
}
}
DataType.STR -> {
dt.isString -> {
throw AssemblyError("all string vars should have been interned into prog")
}
in ArrayDatatypes -> {
dt.isArray -> {
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length)
}
else -> {
@@ -695,8 +710,8 @@ internal class ProgramAndVarsGen(
private fun arrayVariable2asm(varname: String, dt: DataType, align: Int, value: StArray?, orNumberOfZeros: Int?) {
alignVar(align)
when(dt) {
DataType.ARRAY_UB, DataType.ARRAY_BOOL -> {
when {
dt.isUnsignedByteArray || dt.isBoolArray -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$varname\t.byte ${data.joinToString()}")
@@ -706,7 +721,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" .byte " + chunk.joinToString())
}
}
DataType.ARRAY_B -> {
dt.isSignedByteArray -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$varname\t.char ${data.joinToString()}")
@@ -716,7 +731,20 @@ internal class ProgramAndVarsGen(
asmgen.out(" .char " + chunk.joinToString())
}
}
DataType.ARRAY_UW -> {
dt.isSplitWordArray -> {
if(dt.elementType().isUnsignedWord) {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
} else {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
}
dt.isUnsignedWordArray -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$varname\t.word ${data.joinToString()}")
@@ -726,7 +754,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" .word " + chunk.joinToString())
}
}
DataType.ARRAY_W -> {
dt.isSignedWordArray -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$varname\t.sint ${data.joinToString()}")
@@ -736,19 +764,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" .sint " + chunk.joinToString())
}
}
DataType.ARRAY_UW_SPLIT -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_W_SPLIT -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_F -> {
dt.isFloatArray -> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
val floatFills = array.map {
compTarget.machine.getFloatAsmBytes(it.number!!)
@@ -774,7 +790,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" ${it.name} = ${it.address.toHex()}")
}
consts.sortedBy { it.name }.forEach {
if(it.dt==DataType.FLOAT)
if(it.dt==BaseDataType.FLOAT)
asmgen.out(" ${it.name} = ${it.value}")
else
asmgen.out(" ${it.name} = ${it.value.toHex()}")
@@ -805,8 +821,8 @@ internal class ProgramAndVarsGen(
private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
return when (dt) {
DataType.ARRAY_BOOL ->
return when {
dt.isBoolArray ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
if(it.boolean!=null)
@@ -816,18 +832,23 @@ internal class ProgramAndVarsGen(
if(number==0.0) "0" else "1"
}
}
DataType.ARRAY_UB ->
dt.isUnsignedByteArray ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
val number = it.number!!.toInt()
"$"+number.toString(16).padStart(2, '0')
}
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
if(it.number!=null) {
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
}
else if(it.addressOfSymbol!=null) {
asmgen.asmSymbolName(it.addressOfSymbol!!)
val addrOfSymbol = it.addressOfSymbol!!
val symbol = symboltable.lookup(addrOfSymbol)!!
if(symbol is StStaticVariable && symbol.dt.isSplitWordArray)
asmgen.asmSymbolName(addrOfSymbol+"_lsb") // the _lsb split array comes first in memory
else
asmgen.asmSymbolName(addrOfSymbol)
}
else
throw AssemblyError("weird array elt")
@@ -838,14 +859,14 @@ internal class ProgramAndVarsGen(
private fun makeArrayFillDataSigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
return when (dt) {
return when {
// byte array can never contain pointer-to types, so treat values as all integers
DataType.ARRAY_UB ->
dt.isUnsignedByteArray ->
array.map {
val number = it.number!!.toInt()
"$"+number.toString(16).padStart(2, '0')
}
DataType.ARRAY_B ->
dt.isSignedByteArray ->
array.map {
val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(2, '0')
@@ -854,11 +875,11 @@ internal class ProgramAndVarsGen(
else
"-$$hexnum"
}
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
val number = it.number!!.toInt()
"$" + number.toString(16).padStart(4, '0')
}
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> array.map {
dt.isArray && dt.elementType().isSignedWord -> array.map {
val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
if(number>=0)
@@ -91,7 +91,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
if(errors.noErrors()) {
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedName }
for (variable in sortedList) {
if(variable.dt in IntegerDatatypesWithBoolean) {
if(variable.dt.isIntegerOrBool) {
if(zeropage.free.isEmpty()) {
break
} else {
@@ -124,6 +124,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
}
}
collect(st)
return vars.sortedBy { it.dt }
return vars.sortedBy { it.dt.base }
}
}
@@ -17,28 +17,28 @@ internal class AnyExprAsmGen(
private val asmgen: AsmGen6502Internal
) {
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.type) {
in ByteDatatypesWithBoolean -> {
if(expr.left.type in ByteDatatypesWithBoolean && expr.right.type in ByteDatatypesWithBoolean)
when {
expr.type.isByteOrBool -> {
if(expr.left.type.isByteOrBool && expr.right.type.isByteOrBool)
return assignByteBinExpr(expr, assign)
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
if (expr.left.type.isWord && expr.right.type.isWord) {
require(expr.operator in ComparisonOperators)
throw AssemblyError("words operands comparison -> byte, should have been handled elsewhere")
}
if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
if (expr.left.type.isFloat && expr.right.type.isFloat) {
require(expr.operator in ComparisonOperators)
return assignFloatBinExpr(expr, assign)
}
throw AssemblyError("weird expr operand types: ${expr.left.type} and ${expr.right.type}")
}
in WordDatatypes -> {
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
expr.type.isWord -> {
require(expr.left.type.isWord && expr.right.type.isWord) {
"both operands must be words"
}
throw AssemblyError("expression should have been handled otherwise: word ${expr.operator} at ${expr.position}")
}
DataType.FLOAT -> {
require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
expr.type.isFloat -> {
require(expr.left.type.isFloat && expr.right.type.isFloat) {
"both operands must be floats"
}
return assignFloatBinExpr(expr, assign)
@@ -52,7 +52,7 @@ internal class AnyExprAsmGen(
"+" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
@@ -60,7 +60,7 @@ internal class AnyExprAsmGen(
"-" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
@@ -75,7 +75,7 @@ internal class AnyExprAsmGen(
"&" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
@@ -83,7 +83,7 @@ internal class AnyExprAsmGen(
"|" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
@@ -91,7 +91,7 @@ internal class AnyExprAsmGen(
"^", "xor" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
@@ -197,7 +197,7 @@ internal class AnyExprAsmGen(
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.FLOAT)
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.forDt(BaseDataType.FLOAT))
if(!expr.right.isSimple()) asmgen.popFAC1()
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
}
@@ -44,7 +44,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
}
init {
if(register!=null && datatype !in NumericDatatypesWithBoolean)
if(register!=null && !datatype.isNumericOrBool)
throw AssemblyError("must be numeric type")
}
@@ -77,12 +77,20 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
when(registers) {
RegisterOrPair.A,
RegisterOrPair.X,
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, pos, register = registers)
RegisterOrPair.Y -> {
val dt = DataType.forDt(if(signed) BaseDataType.BYTE else BaseDataType.UBYTE)
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
}
RegisterOrPair.AX,
RegisterOrPair.AY,
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
RegisterOrPair.XY -> {
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD)
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
}
RegisterOrPair.FAC1,
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
RegisterOrPair.FAC2 -> {
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.FLOAT), scope, pos, register = registers)
}
RegisterOrPair.R0,
RegisterOrPair.R1,
RegisterOrPair.R2,
@@ -98,7 +106,10 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
RegisterOrPair.R12,
RegisterOrPair.R13,
RegisterOrPair.R14,
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers)
RegisterOrPair.R15 -> {
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD)
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
}
}
}
@@ -151,7 +162,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.BOOL, boolean = bv)
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.forDt(BaseDataType.BOOL), boolean = bv)
return when(value) {
// checked above: is PtNumber -> throw AssemblyError("should have been constant value")
@@ -163,7 +174,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
val varName=asmgen.asmVariableName(value)
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
if(value.type == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) {
if(value.type.isUnsignedWord && varName.lowercase().startsWith("cx16.r")) {
val regStr = varName.lowercase().substring(5)
val reg = RegisterOrPair.valueOf(regStr.uppercase())
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, value.type, register = reg)
@@ -172,7 +183,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
}
}
is PtMemoryByte -> {
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.forDt(BaseDataType.UBYTE), memory = value)
}
is PtArrayIndexer -> {
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
@@ -202,9 +213,9 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, boolean, expression)
if(target.datatype!=datatype) {
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {
if(target.datatype.isByte && datatype.isByte) {
return withAdjustedDt(target.datatype)
} else if(target.datatype in WordDatatypes && datatype in WordDatatypes) {
} else if(target.datatype.isWord && datatype.isWord) {
return withAdjustedDt(target.datatype)
}
}
@@ -220,8 +231,9 @@ internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
val position: Position) {
init {
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" }
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
require(!source.datatype.isUndefined) { "must not be placeholder/undefined datatype at $position" }
if(!source.datatype.isArray && !target.datatype.isArray)
require(memsizer.memorySize(source.datatype, null) <= memsizer.memorySize(target.datatype, null)) {
"source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}"
}
}
File diff suppressed because it is too large. Load diff
@@ -58,20 +58,20 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
// the asm-gen code can deal with situations where you want to assign a byte into a word.
// it will create the most optimized code to do this (so it type-extends for us).
// But we can't deal with writing a word into a byte - explicit typeconversion should be done
if(program.memsizer.memorySize(value.datatype) > program.memsizer.memorySize(target.datatype))
if(program.memsizer.memorySize(value.datatype, null) > program.memsizer.memorySize(target.datatype, null))
throw AssemblyError("missing type cast: value type > target type ${target.position}")
fun regName(v: AsmAssignSource) = "cx16.${v.register!!.name.lowercase()}"
when (target.kind) {
TargetStorageKind.VARIABLE -> {
when (target.datatype) {
in ByteDatatypesWithBoolean -> {
when {
target.datatype.isByteOrBool -> {
when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteVariableWithLiteralval(target.asmVarname, target.datatype, operator, value.boolean!!.asInt())
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteVariableWithLiteralval(target.asmVarname, target.datatype, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplacemodificationByteVariableWithVariable(target.asmVarname, target.datatype, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationByteVariableWithVariable(target.asmVarname, target.datatype, operator, regName(value))
SourceStorageKind.VARIABLE -> inplacemodificationByteVariableWithVariable(target.asmVarname, target.datatype.isSigned, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationByteVariableWithVariable(target.asmVarname, target.datatype.isSigned, operator, regName(value))
SourceStorageKind.MEMORY -> inplacemodificationByteVariableWithValue(target.asmVarname, target.datatype, operator, value.memory!!)
SourceStorageKind.ARRAY -> inplacemodificationByteVariableWithValue(target.asmVarname, target.datatype, operator, value.array!!)
SourceStorageKind.EXPRESSION -> {
@@ -84,7 +84,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
}
in WordDatatypes -> {
target.datatype.isWord -> {
val block = target.origAstTarget?.definingBlock()
when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationWordWithLiteralval(target.asmVarname, target.datatype, operator, value.boolean!!.asInt(), block)
@@ -104,7 +104,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
}
DataType.FLOAT -> {
target.datatype.isFloat -> {
when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationFloatWithLiteralval(target.asmVarname, operator, value.boolean!!.asInt().toDouble())
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(target.asmVarname, operator, value.number!!.number)
@@ -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 -> inplacemodificationByteVariableWithLiteralval(addr.toHex(), DataType.UBYTE, operator, value.boolean!!.asInt())
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteVariableWithLiteralval(addr.toHex(), DataType.UBYTE, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplacemodificationByteVariableWithVariable(addr.toHex(), DataType.UBYTE, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationByteVariableWithVariable(addr.toHex(), DataType.UBYTE, operator, regName(value))
SourceStorageKind.MEMORY -> inplacemodificationByteVariableWithValue(addr.toHex(), DataType.UBYTE, operator, value.memory!!)
SourceStorageKind.ARRAY -> inplacemodificationByteVariableWithValue(addr.toHex(), DataType.UBYTE, operator, value.array!!)
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteVariableWithLiteralval(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.boolean!!.asInt())
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteVariableWithLiteralval(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplacemodificationByteVariableWithVariable(addr.toHex(), false, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationByteVariableWithVariable(addr.toHex(), false, operator, regName(value))
SourceStorageKind.MEMORY -> inplacemodificationByteVariableWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.memory!!)
SourceStorageKind.ARRAY -> inplacemodificationByteVariableWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.array!!)
SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
inplacemodificationByteVariableWithValue(addr.toHex(), DataType.UBYTE, operator, value.expression)
inplacemodificationByteVariableWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.expression)
} else {
inplacemodificationByteVariableWithValue(addr.toHex(), DataType.UBYTE, operator, value.expression!!)
inplacemodificationByteVariableWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.expression!!)
}
}
}
@@ -192,21 +192,21 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
SourceStorageKind.MEMORY -> {
asmgen.out(" sta P8ZP_SCRATCH_B1")
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.memory!!)
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), operator, value.memory!!)
asmgen.out(" ldx P8ZP_SCRATCH_B1")
}
SourceStorageKind.ARRAY -> {
asmgen.out(" sta P8ZP_SCRATCH_B1")
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.array!!)
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), operator, value.array!!)
asmgen.out(" ldx P8ZP_SCRATCH_B1")
}
SourceStorageKind.EXPRESSION -> {
val tempVar = asmgen.getTempVarName(DataType.UBYTE)
val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE)
asmgen.out(" sta $tempVar")
if(value.expression is PtTypeCast)
inplacemodificationByteVariableWithValue(tempVar, DataType.UBYTE, operator, value.expression)
inplacemodificationByteVariableWithValue(tempVar, DataType.forDt(BaseDataType.UBYTE), operator, value.expression)
else
inplacemodificationByteVariableWithValue(tempVar, DataType.UBYTE, operator, value.expression!!)
inplacemodificationByteVariableWithValue(tempVar, DataType.forDt(BaseDataType.UBYTE), operator, value.expression!!)
asmgen.out(" ldx $tempVar")
}
}
@@ -235,14 +235,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
return
}
// normal array
val targetVarName = "${target.asmVarname} + ${index*program.memsizer.memorySize(target.datatype)}"
when (target.datatype) {
in ByteDatatypesWithBoolean -> {
val targetVarName = "${target.asmVarname} + ${index*program.memsizer.memorySize(target.datatype, null)}"
when {
target.datatype.isByteOrBool -> {
when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteVariableWithLiteralval(targetVarName, target.datatype, operator, value.boolean!!.asInt())
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteVariableWithLiteralval(targetVarName, target.datatype, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplacemodificationByteVariableWithVariable(targetVarName, target.datatype, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationByteVariableWithVariable(targetVarName, target.datatype, operator, regName(value))
SourceStorageKind.VARIABLE -> inplacemodificationByteVariableWithVariable(targetVarName, target.datatype.isSigned, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationByteVariableWithVariable(targetVarName, target.datatype.isSigned, operator, regName(value))
SourceStorageKind.MEMORY -> inplacemodificationByteVariableWithValue(targetVarName, target.datatype, operator, value.memory!!)
SourceStorageKind.ARRAY -> inplacemodificationByteVariableWithValue(targetVarName, target.datatype, operator, value.array!!)
SourceStorageKind.EXPRESSION -> {
@@ -256,7 +256,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
in WordDatatypes -> {
target.datatype.isWord -> {
val block = target.origAstTarget?.definingBlock()
when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationWordWithLiteralval(targetVarName, target.datatype, operator, value.boolean!!.asInt(), block)
@@ -276,7 +276,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
DataType.FLOAT -> {
target.datatype.isFloat -> {
when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationFloatWithLiteralval(targetVarName, operator, value.boolean!!.asInt().toDouble())
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(targetVarName, operator, value.number!!.number)
@@ -305,8 +305,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
&& tryIndexedIncDec(target.array, operator))
return
when (target.datatype) {
in ByteDatatypesWithBoolean -> {
when {
target.datatype.isByteOrBool -> {
if(value.kind==SourceStorageKind.EXPRESSION
&& value.expression is PtTypeCast
&& tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
@@ -316,21 +316,21 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda ${target.array.variable.name},y")
when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> {
inplacemodificationRegisterAwithVariable(operator, "#${value.boolean!!.asInt()}", target.datatype in SignedDatatypes)
inplacemodificationRegisterAwithVariable(operator, "#${value.boolean!!.asInt()}", target.datatype.isSigned)
asmgen.restoreRegisterStack(CpuRegister.Y, true)
}
SourceStorageKind.LITERALNUMBER -> {
inplacemodificationRegisterAwithVariable(operator, "#${value.number!!.number.toInt()}", target.datatype in SignedDatatypes)
inplacemodificationRegisterAwithVariable(operator, "#${value.number!!.number.toInt()}", target.datatype.isSigned)
asmgen.restoreRegisterStack(CpuRegister.Y, true)
}
SourceStorageKind.VARIABLE -> {
inplacemodificationRegisterAwithVariable(operator, value.asmVarname, target.datatype in SignedDatatypes)
inplacemodificationRegisterAwithVariable(operator, value.asmVarname, target.datatype.isSigned)
asmgen.restoreRegisterStack(CpuRegister.Y, true)
}
SourceStorageKind.REGISTER -> {
inplacemodificationRegisterAwithVariable(operator, regName(value), target.datatype in SignedDatatypes)
inplacemodificationRegisterAwithVariable(operator, regName(value), target.datatype.isSigned)
asmgen.restoreRegisterStack(CpuRegister.Y, true)
}
@@ -349,7 +349,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
SourceStorageKind.EXPRESSION -> {
val tempVar = asmgen.getTempVarName(DataType.UBYTE)
val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE)
asmgen.out(" sta $tempVar")
if(value.expression is PtTypeCast)
inplacemodificationByteVariableWithValue(tempVar, target.datatype, operator, value.expression)
@@ -362,7 +362,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" sta ${target.array.variable.name},y")
}
in WordDatatypes -> {
target.datatype.isWord -> {
if(value.kind==SourceStorageKind.EXPRESSION
&& value.expression is PtTypeCast
&& tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
@@ -399,7 +399,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
if(!inplacemodificationRegisterAXwithVariable(
operator,
value.asmVarname,
value.datatype
value.datatype.base
)) {
asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1")
inplacemodificationWordWithVariable("P8ZP_SCRATCH_W1", target.datatype, operator, value.asmVarname, value.datatype, block)
@@ -411,7 +411,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
if(!inplacemodificationRegisterAXwithVariable(
operator,
regName(value),
value.datatype
value.datatype.base
)) {
asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1")
inplacemodificationWordWithVariable("P8ZP_SCRATCH_W1", target.datatype, operator, regName(value), value.datatype, block)
@@ -432,7 +432,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
SourceStorageKind.EXPRESSION -> {
val tempVar = asmgen.getTempVarName(DataType.UWORD)
val tempVar = asmgen.getTempVarName(BaseDataType.UWORD)
asmgen.out(" sta $tempVar | stx $tempVar+1")
if(value.expression is PtTypeCast)
inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression, block)
@@ -448,9 +448,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" sta ${target.array.variable.name},y | txa | sta ${target.array.variable.name}+1,y")
}
DataType.FLOAT -> {
target.datatype.isFloat -> {
// copy array value into tempvar
val tempvar = asmgen.getTempVarName(DataType.FLOAT)
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.A)
asmgen.out("""
ldy #>${target.asmVarname}
@@ -508,8 +508,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
private fun tryIndexedIncDec(array: PtArrayIndexer, operator: String): Boolean {
val arrayvar = asmgen.asmVariableName(array.variable)
when (array.type) {
in ByteDatatypes -> {
when {
array.type.isByte -> {
asmgen.loadScaledArrayIndexIntoRegister(array, CpuRegister.X)
if (operator == "+")
asmgen.out(" inc $arrayvar,x")
@@ -517,7 +517,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" dec $arrayvar,x")
return true
}
in WordDatatypes -> {
array.type.isWord -> {
asmgen.loadScaledArrayIndexIntoRegister(array, CpuRegister.X)
if(array.splitWords) {
if(operator=="+") {
@@ -551,7 +551,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
return true
}
}
DataType.FLOAT -> {
array.type.isFloat -> {
asmgen.loadScaledArrayIndexIntoRegister(array, CpuRegister.A)
asmgen.out("""
ldy #>$arrayvar
@@ -568,7 +568,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
private fun tryOptimizedMemoryInplace(address: PtBinaryExpression, operator: String, value: AsmAssignSource): Boolean {
if(value.datatype !in ByteDatatypes || operator !in "|&^+-")
if(!value.datatype.isByte || operator !in "|&^+-")
return false
fun addrIntoZpPointer(): String {
@@ -583,7 +583,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
fun assignValueToA() {
val assignValue = AsmAssignment(value,
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE,
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.UBYTE),
address.definingISub(), Position.DUMMY, register = RegisterOrPair.A),
program.memsizer, Position.DUMMY)
assignmentAsmGen.translateNormalAssignment(assignValue, address.definingISub()) // calculate value into A
@@ -591,7 +591,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
val rightTc = address.right as? PtTypeCast
val constOffset = (address.right as? PtNumber)?.number?.toInt()
if(address.operator=="+" && (address.right.type in ByteDatatypes || (rightTc!=null && rightTc.value.type in ByteDatatypes) || (constOffset!=null && constOffset<256)) ) {
if(address.operator=="+" && (address.right.type.isByte || (rightTc!=null && rightTc.value.type.isByte) || (constOffset!=null && constOffset<256)) ) {
if(constOffset!=null) {
val zpPointerVarName = addrIntoZpPointer()
if(value.number==null) assignValueToA()
@@ -677,10 +677,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
inplacemodificationSomeWordWithLiteralval("${arrayVar}_lsb+$index", "${arrayVar}_msb+$index", dt, operator, value, null)
}
private fun inplacemodificationRegisterAXwithVariable(operator: String, variable: String, varDt: DataType): Boolean {
private fun inplacemodificationRegisterAXwithVariable(operator: String, variable: String, varDt: BaseDataType): Boolean {
when(operator) {
"+" -> {
return if(varDt in WordDatatypes) {
return if(varDt.isWord) {
asmgen.out("""
clc
adc $variable
@@ -704,7 +704,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
}
"-" -> {
return if(varDt in WordDatatypes) {
return if(varDt.isWord) {
asmgen.out("""
sec
sbc $variable
@@ -882,7 +882,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
private fun tryInplaceModifyWithRemovedRedundantCast(value: PtTypeCast, target: AsmAssignTarget, operator: String): Boolean {
if (target.datatype == value.type) {
val childDt = value.value.type
if (value.type!=DataType.FLOAT && (value.type.equalsSize(childDt) || value.type.largerThan(childDt))) {
if (!value.type.isFloat && (value.type.equalsSize(childDt) || value.type.largerSizeThan(childDt))) {
// this typecast is redundant here; the rest of the code knows how to deal with the uncasted value.
// (works for integer types, not for float.)
val src = AsmAssignSource.fromAstSource(value.value, program, asmgen)
@@ -894,7 +894,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
private fun inplacemodificationBytePointerWithValue(pointervar: PtIdentifier, operator: String, value: PtExpression) {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE))
inplacemodificationBytePointerWithVariable(pointervar, operator, "P8ZP_SCRATCH_B1")
}
@@ -1058,6 +1058,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
}
private fun inplacemodificationByteVariableWithValue(name: String, dt: DataType, operator: String, value: PtExpression) {
require(dt.isByteOrBool)
if(!value.isSimple()) {
// attempt short-circuit (McCarthy) evaluation
when (operator) {
@@ -1065,7 +1066,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
val shortcutLabel = asmgen.makeLabel("shortcut")
asmgen.out(" lda $name | beq $shortcutLabel")
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt.isSigned)
asmgen.out("""
and $name
sta $name
@@ -1076,7 +1077,7 @@ $shortcutLabel:""")
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
val shortcutLabel = asmgen.makeLabel("shortcut")
asmgen.out(" lda $name | bne $shortcutLabel")
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt.isSigned)
asmgen.out("""
ora $name
sta $name
@@ -1089,25 +1090,25 @@ $shortcutLabel:""")
if(asmgen.isTargetCpu(CpuType.CPU65c02)) {
if(operator=="&" && value is PtPrefix && value.operator=="~") {
// M &= ~A --> use TRB 65c02 instruction for that
asmgen.assignExpressionToRegister(value.value, RegisterOrPair.A, dt in SignedDatatypes)
asmgen.assignExpressionToRegister(value.value, RegisterOrPair.A, dt.isSigned)
asmgen.out(" trb $name")
return
}
else if(operator=="|") {
// M |= A --> use TSB 65c02 instruction for that
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt.isSigned)
asmgen.out(" tsb $name")
return
}
}
// normal evaluation
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt in SignedDatatypes)
inplacemodificationRegisterAwithVariableWithSwappedOperands(operator, name, dt in SignedDatatypes)
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, dt.isSigned)
inplacemodificationRegisterAwithVariableWithSwappedOperands(operator, name, dt.isSigned)
asmgen.out(" sta $name")
}
private fun inplacemodificationByteVariableWithVariable(name: String, dt: DataType, operator: String, otherName: String) {
private fun inplacemodificationByteVariableWithVariable(name: String, signed: Boolean, operator: String, otherName: String) {
// note: no logical and/or shortcut here, not worth it due to simple right operand
if(asmgen.isTargetCpu(CpuType.CPU65c02)) {
@@ -1119,7 +1120,7 @@ $shortcutLabel:""")
}
asmgen.out(" lda $name")
inplacemodificationRegisterAwithVariable(operator, otherName, dt in SignedDatatypes)
inplacemodificationRegisterAwithVariable(operator, otherName, signed)
asmgen.out(" sta $name")
}
@@ -1467,6 +1468,7 @@ $shortcutLabel:""")
private fun inplacemodificationByteVariableWithLiteralval(name: String, dt: DataType, operator: String, value: Int) {
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
// note: no logical and/or shortcut here, not worth it due to simple right operand
require(dt.isByteOrBool)
when (operator) {
"+" -> {
if(value==1) asmgen.out(" inc $name")
@@ -1484,13 +1486,13 @@ $shortcutLabel:""")
}
"/" -> {
// replacing division by shifting is done in an optimizer step.
if (dt == DataType.UBYTE)
if (dt.isUnsignedByte)
asmgen.out(" lda $name | ldy #$value | jsr prog8_math.divmod_ub_asm | sty $name")
else
asmgen.out(" lda $name | ldy #$value | jsr prog8_math.divmod_b_asm | sty $name")
}
"%" -> {
if(dt==DataType.BYTE)
if(dt.isSignedByte)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out("""
lda $name
@@ -1509,7 +1511,7 @@ $shortcutLabel:""")
}
">>" -> {
if(value>0) {
if (dt == DataType.UBYTE) {
if (dt.isUnsignedByte) {
if(value>=8) {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name")
@@ -1536,8 +1538,8 @@ $shortcutLabel:""")
}
}
}
"&", "and" -> immediateAndInplace(name, value)
"|", "or" -> immediateOrInplace(name, value)
"&", "and" -> asmgen.immediateAndInplace(name, value)
"|", "or" -> asmgen.immediateOrInplace(name, value)
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
"==" -> {
asmgen.out("""
@@ -1560,7 +1562,7 @@ $shortcutLabel:""")
+ sta $name""")
}
"<" -> {
if(dt==DataType.UBYTE) {
if(dt.isUnsignedByte) {
asmgen.out("""
lda #0
ldy $name
@@ -1585,7 +1587,7 @@ $shortcutLabel:""")
}
}
"<=" -> {
if(dt==DataType.UBYTE) {
if(dt.isUnsignedByte) {
asmgen.out("""
lda #0
ldy #$value
@@ -1608,7 +1610,7 @@ $shortcutLabel:""")
}
}
">" -> {
if(dt==DataType.UBYTE) {
if(dt.isUnsignedByte) {
asmgen.out("""
lda #0
ldy $name
@@ -1632,7 +1634,7 @@ $shortcutLabel:""")
}
}
">=" -> {
if(dt==DataType.UBYTE) {
if(dt.isUnsignedByte) {
asmgen.out("""
lda #0
ldy $name
@@ -1658,23 +1660,8 @@ $shortcutLabel:""")
}
}
private fun immediateAndInplace(name: String, value: Int) {
if(asmgen.isTargetCpu(CpuType.CPU65c02)) {
asmgen.out(" lda #${value xor 255} | trb $name") // reset bit
} else {
asmgen.out(" lda $name | and #$value | sta $name")
}
}
private fun immediateOrInplace(name: String, value: Int) {
if(asmgen.isTargetCpu(CpuType.CPU65c02)) {
asmgen.out(" lda #$value | tsb $name") // set bit
} else {
asmgen.out(" lda $name | ora #$value | sta $name")
}
}
private fun inplacemodificationWordWithMemread(name: String, dt: DataType, operator: String, memread: PtMemoryByte) {
require(dt.isInteger)
when (operator) {
"+" -> {
asmgen.translateDirectMemReadExpressionToRegA(memread)
@@ -1707,7 +1694,7 @@ $shortcutLabel:""")
"&" -> {
asmgen.translateDirectMemReadExpressionToRegA(memread)
asmgen.out(" and $name | sta $name")
if(dt in WordDatatypes) {
if(dt.isWord) {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name+1")
else
@@ -1732,6 +1719,7 @@ $shortcutLabel:""")
}
private fun inplacemodificationSomeWordWithLiteralval(lsb: String, msb: String, dt: DataType, operator: String, value: Int, block: PtBlock?) {
require(dt.isWord)
when (operator) {
"+" -> {
when {
@@ -1836,7 +1824,7 @@ $shortcutLabel:""")
if(value==0) {
throw AssemblyError("division by zero")
} else {
if(dt==DataType.WORD) {
if(dt.isSignedWord) {
asmgen.out("""
lda $lsb
ldy $msb
@@ -1867,7 +1855,7 @@ $shortcutLabel:""")
"%" -> {
if(value==0)
throw AssemblyError("division by zero")
if(dt==DataType.WORD)
if(dt.isSignedWord)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out("""
lda $lsb
@@ -1910,7 +1898,7 @@ $shortcutLabel:""")
}
">>" -> {
if (value > 0) {
if(dt==DataType.UWORD) {
if(dt.isUnsignedWord) {
when {
value>=16 -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
@@ -1994,7 +1982,7 @@ $shortcutLabel:""")
asmgen.out(" lda $msb | and #>$value | sta $msb")
}
value < 0x0100 -> {
immediateAndInplace(lsb, value)
asmgen.immediateAndInplace(lsb, value)
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $msb")
else
@@ -2007,7 +1995,7 @@ $shortcutLabel:""")
when {
value == 0 -> {}
value and 255 == 0 -> asmgen.out(" lda $msb | ora #>$value | sta $msb")
value < 0x0100 -> immediateOrInplace(lsb, value)
value < 0x0100 -> asmgen.immediateOrInplace(lsb, value)
else -> asmgen.out(" lda $lsb | ora #<$value | sta $lsb | lda $msb | ora #>$value | sta $msb")
}
}
@@ -2050,7 +2038,7 @@ $shortcutLabel:""")
sta $msb""")
}
"<" -> {
if(dt==DataType.UWORD) {
if(dt.isUnsignedWord) {
asmgen.out("""
lda $msb
cmp #>$value
@@ -2091,7 +2079,7 @@ $shortcutLabel:""")
}
}
"<=" -> {
if(dt==DataType.UWORD) {
if(dt.isUnsignedWord) {
asmgen.out("""
lda $msb
cmp #>$value
@@ -2134,7 +2122,7 @@ $shortcutLabel:""")
}
">" -> {
// word > value --> value < word
if(dt==DataType.UWORD) {
if(dt.isUnsignedWord) {
asmgen.out("""
lda #>$value
cmp $msb
@@ -2176,7 +2164,7 @@ $shortcutLabel:""")
}
">=" -> {
// word >= value --> value <= word
if(dt==DataType.UWORD) {
if(dt.isUnsignedWord) {
asmgen.out("""
lda #>$value
cmp $msb
@@ -2222,12 +2210,14 @@ $shortcutLabel:""")
}
private fun inplacemodificationWordWithVariable(name: String, dt: DataType, operator: String, otherName: String, valueDt: DataType, block: PtBlock?) {
when (valueDt) {
in ByteDatatypes -> {
require(dt.isWord)
require(valueDt.isInteger)
when {
valueDt.isByte -> {
// the other variable is a BYTE type so optimize for that
when (operator) {
"+" -> {
if(valueDt==DataType.UBYTE)
if(valueDt.isUnsignedByte)
asmgen.out("""
lda $name
clc
@@ -2250,7 +2240,7 @@ $shortcutLabel:""")
sta $name+1""")
}
"-" -> {
if(valueDt==DataType.UBYTE)
if(valueDt.isUnsignedByte)
asmgen.out("""
lda $name
sec
@@ -2276,7 +2266,7 @@ $shortcutLabel:""")
"*" -> {
if(block?.options?.veraFxMuls==true) {
// cx16 verafx hardware muls
if(valueDt==DataType.UBYTE) {
if(valueDt.isUnsignedByte) {
asmgen.out(" lda $otherName | sta cx16.r1")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz cx16.r1+1")
@@ -2284,7 +2274,7 @@ $shortcutLabel:""")
asmgen.out(" lda #0 | sta cx16.r1+1")
} else {
asmgen.out(" lda $otherName")
asmgen.signExtendAYlsb(valueDt)
asmgen.signExtendAYlsb(valueDt.base)
asmgen.out(" sta cx16.r1 | sty cx16.r1+1")
}
asmgen.out("""
@@ -2296,7 +2286,7 @@ $shortcutLabel:""")
sta $name
sty $name+1""")
} else {
if(valueDt==DataType.UBYTE) {
if(valueDt.isUnsignedByte) {
asmgen.out(" lda $otherName | sta prog8_math.multiply_words.multiplier")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz prog8_math.multiply_words.multiplier+1")
@@ -2304,7 +2294,7 @@ $shortcutLabel:""")
asmgen.out(" lda #0 | sta prog8_math.multiply_words.multiplier+1")
} else {
asmgen.out(" lda $otherName")
asmgen.signExtendAYlsb(valueDt)
asmgen.signExtendAYlsb(valueDt.base)
asmgen.out(" sta prog8_math.multiply_words.multiplier | sty prog8_math.multiply_words.multiplier+1")
}
asmgen.out("""
@@ -2316,7 +2306,7 @@ $shortcutLabel:""")
}
}
"/" -> {
if(dt==DataType.UWORD) {
if(dt.isUnsignedWord) {
asmgen.out("""
lda $name
ldy $name+1
@@ -2343,7 +2333,7 @@ $shortcutLabel:""")
}
}
"%" -> {
if(valueDt!=DataType.UBYTE || dt!=DataType.UWORD)
if(!valueDt.isUnsignedByte || !dt.isUnsignedWord)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out("""
lda $name
@@ -2370,7 +2360,7 @@ $shortcutLabel:""")
+""")
}
">>" -> {
if(dt==DataType.UWORD) {
if(dt.isUnsignedWord) {
asmgen.out("""
ldy $otherName
beq +
@@ -2394,12 +2384,10 @@ $shortcutLabel:""")
}
"&" -> {
asmgen.out(" lda $otherName | and $name | sta $name")
if(dt in WordDatatypes) {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
"|" -> asmgen.out(" lda $otherName | ora $name | sta $name")
"^" -> asmgen.out(" lda $otherName | eor $name | sta $name")
@@ -2436,7 +2424,7 @@ $shortcutLabel:""")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
in WordDatatypes -> {
valueDt.isWord -> {
// the value is a proper 16-bit word, so use both bytes of it.
when (operator) {
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
@@ -2469,7 +2457,7 @@ $shortcutLabel:""")
sty $name+1""")
}
"/" -> {
if(dt==DataType.WORD) {
if(dt.isSignedWord) {
asmgen.out("""
lda $name
ldy $name+1
@@ -2495,7 +2483,7 @@ $shortcutLabel:""")
}
}
"%" -> {
if(dt==DataType.WORD)
if(dt.isSignedWord)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out("""
lda $name
@@ -2548,7 +2536,7 @@ $shortcutLabel:""")
sta $name+1""")
}
"<" -> {
val compareRoutine = if(dt==DataType.UWORD) "reg_less_uw" else "reg_less_w"
val compareRoutine = if(dt.isUnsignedWord) "reg_less_uw" else "reg_less_w"
asmgen.out("""
lda $otherName
ldy $otherName+1
@@ -2563,7 +2551,7 @@ $shortcutLabel:""")
}
">" -> {
// a > b --> b < a
val compareRoutine = if(dt==DataType.UWORD) "reg_less_uw" else "reg_less_w"
val compareRoutine = if(dt.isUnsignedWord) "reg_less_uw" else "reg_less_w"
asmgen.out("""
lda $name
ldy $name+1
@@ -2577,7 +2565,7 @@ $shortcutLabel:""")
sta $name+1""")
}
"<=" -> {
if(dt==DataType.UWORD) {
if(dt.isUnsignedWord) {
asmgen.out("""
lda $otherName
ldy $otherName+1
@@ -2606,7 +2594,7 @@ $shortcutLabel:""")
}
">=" -> {
// a>=b --> b<=a
if(dt==DataType.UWORD) {
if(dt.isUnsignedWord) {
asmgen.out("""
lda $name
ldy $name+1
@@ -2645,6 +2633,7 @@ $shortcutLabel:""")
}
private fun inplacemodificationWordWithValue(name: String, dt: DataType, operator: String, value: PtExpression, block: PtBlock?) {
require(dt.isWord)
fun multiplyVarByWordInAY() {
if(block?.options?.veraFxMuls==true)
// cx16 verafx hardware muls
@@ -2679,7 +2668,7 @@ $shortcutLabel:""")
lda $name+1
sta P8ZP_SCRATCH_W1+1
txa""")
if (dt == DataType.WORD)
if (dt.isSignedWord)
asmgen.out(" jsr prog8_math.divmod_w_asm")
else
asmgen.out(" jsr prog8_math.divmod_uw_asm")
@@ -2687,7 +2676,7 @@ $shortcutLabel:""")
}
fun remainderVarByWordInAY() {
if(dt==DataType.WORD)
if(dt.isSignedWord)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out("""
tax
@@ -2703,14 +2692,14 @@ $shortcutLabel:""")
sty $name+1
""")
}
when (val valueDt = value.type) {
in ByteDatatypes -> {
val valueDt = value.type
when {
valueDt.isByte -> {
// the other variable is a BYTE type so optimize for that
when (operator) {
"+" -> {
// name += byteexpression
if(valueDt==DataType.UBYTE) {
if(valueDt.isUnsignedByte) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, false)
asmgen.out("""
clc
@@ -2737,7 +2726,7 @@ $shortcutLabel:""")
"-" -> {
// name -= byteexpression
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt)
if(valueDt==DataType.UBYTE)
if(valueDt.isUnsignedByte)
asmgen.out("""
lda $name
sec
@@ -2764,21 +2753,21 @@ $shortcutLabel:""")
// value is (u) byte value, sign extend that and proceed with regular 16 bit operation
// TODO use an optimized word * byte multiplication routine?
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.signExtendAYlsb(valueDt)
asmgen.signExtendAYlsb(valueDt.base)
multiplyVarByWordInAY()
}
"/" -> {
// value is (u) byte value, sign extend that and proceed with regular 16 bit operation
// TODO use an optimized word / byte divmod routine?
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.signExtendAYlsb(valueDt)
asmgen.signExtendAYlsb(valueDt.base)
divideVarByWordInAY()
}
"%" -> {
// value is (u) byte value, sign extend that and proceed with regular 16 bit operation
// TODO use an optimized word / byte divmod routine?
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.signExtendAYlsb(valueDt)
asmgen.signExtendAYlsb(valueDt.base)
remainderVarByWordInAY()
}
"<<" -> {
@@ -2793,7 +2782,7 @@ $shortcutLabel:""")
}
">>" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.Y)
if(dt==DataType.UWORD)
if(dt.isUnsignedWord)
asmgen.out("""
beq +
- lsr $name+1
@@ -2815,12 +2804,10 @@ $shortcutLabel:""")
"&" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name")
if(dt in WordDatatypes) {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
"|" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
@@ -2863,7 +2850,7 @@ $shortcutLabel:""")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
in WordDatatypes -> {
valueDt.isWord -> {
// the value is a proper 16-bit word, so use both bytes of it.
when (operator) {
"+" -> {
@@ -2889,11 +2876,7 @@ $shortcutLabel:""")
}
"<<", ">>" -> {
if(value is PtNumber && value.number<=255) {
when (dt) {
in WordDatatypes -> TODO("shift a word var by ${value.number}")
in ByteDatatypes -> TODO("shift a byte var by ${value.number}")
else -> throw AssemblyError("weird dt for shift")
}
TODO("shift a word var by ${value.number}")
} else {
throw AssemblyError("shift by a word value not supported, max is a byte")
}
+18 -9
View File
@@ -4,16 +4,25 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = when(dt) {
in ByteDatatypesWithBoolean -> 1
DataType.FLOAT -> 5
else -> 2
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray) {
require(numElements != null)
return when(dt.sub?.dt) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
BaseDataType.FLOAT -> numElements*5
else -> throw IllegalArgumentException("invalid sub type")
}
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> 5 * (numElements ?: 1)
else -> 2 * (numElements ?: 1)
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) {
DataType.ARRAY_UW -> numElements*2
DataType.ARRAY_W -> numElements*2
DataType.ARRAY_F -> numElements*5
else -> numElements
override fun memorySize(dt: SubType): Int {
return memorySize(DataType.forDt(dt.dt), null)
}
}
+20 -19
View File
@@ -8,6 +8,7 @@ import io.kotest.matchers.shouldBe
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 java.nio.file.Files
@@ -50,16 +51,16 @@ class TestCodegen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable(
"pi",
DataType.UBYTE,
DataType.forDt(BaseDataType.UBYTE),
ZeropageWish.DONTCARE,
0u,
PtNumber(DataType.UBYTE, 0.0, Position.DUMMY),
PtNumber(BaseDataType.UBYTE, 0.0, Position.DUMMY),
null,
Position.DUMMY
))
sub.add(PtVariable(
"particleX",
DataType.ARRAY_UB,
DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE,
0u,
null,
@@ -68,7 +69,7 @@ class TestCodegen: FunSpec({
))
sub.add(PtVariable(
"particleDX",
DataType.ARRAY_UB,
DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE,
0u,
null,
@@ -77,51 +78,51 @@ class TestCodegen: FunSpec({
))
sub.add(PtVariable(
"xx",
DataType.WORD,
DataType.forDt(BaseDataType.WORD),
ZeropageWish.DONTCARE,
0u,
PtNumber(DataType.WORD, 1.0, Position.DUMMY),
PtNumber(BaseDataType.WORD, 1.0, Position.DUMMY),
null,
Position.DUMMY
))
val assign = PtAugmentedAssign("+=", Position.DUMMY)
val target = PtAssignTarget(false, Position.DUMMY).also {
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
val targetIdx = PtArrayIndexer(DataType.forDt(BaseDataType.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.UBYTE, Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX", DataType.ARRAY_UB, Position.DUMMY))
value.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
val value = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
assign.add(target)
assign.add(value)
sub.add(assign)
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
}
prefixAssign.add(prefixTarget)
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
prefixAssign.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.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.WORD, Position.DUMMY))
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
}
numberAssign.add(numberAssignTarget)
numberAssign.add(PtNumber(DataType.WORD, 42.0, Position.DUMMY))
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
sub.add(numberAssign)
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
}
cxregAssign.add(cxregAssignTarget)
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY))
sub.add(cxregAssign)
block.add(sub)
@@ -129,7 +130,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.UWORD, 100u, null, Position.DUMMY))
cx16block.add(PtMemMapped("r0", DataType.forDt(BaseDataType.UWORD), 100u, null, Position.DUMMY))
program.add(cx16block)
val options = getTestOptions()
-46
View File
@@ -1,46 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
implementation project(':intermediate')
implementation project(':codeGenIntermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
}
// note: there are no unit tests in this module!
+27
View File
@@ -0,0 +1,27 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
implementation(project(":codeCore"))
implementation(project(":intermediate"))
implementation(project(":codeGenIntermediate"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
}
sourceSets {
main {
java {
srcDir(file("${project.projectDir}/src"))
}
resources {
srcDir(file("${project.projectDir}/res"))
}
}
}
// note: there are no unit tests in this module!
-66
View File
@@ -1,66 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
implementation project(':intermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.1'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testImplementation 'io.kotest:kotest-framework-datatest:5.9.1'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
test {
java {
srcDir "${project.projectDir}/test"
}
}
}
test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}
+48
View File
@@ -0,0 +1,48 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
implementation(project(":codeCore"))
implementation(project(":intermediate"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
sourceSets {
main {
java {
srcDir("${project.projectDir}/src")
}
resources {
srcDir("${project.projectDir}/res")
}
}
test {
java {
srcDir("${project.projectDir}/test")
}
}
}
tasks.test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn("cleanTest")
// Show test results.
testLogging {
events("skipped", "failed")
}
}
@@ -83,7 +83,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val symbol = target.identifier?.name
val array = target.array
val value = augAssign.value
val signed = target.type in SignedDatatypes
val signed = target.type.isSigned
val chunks = when (augAssign.operator) {
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
@@ -182,7 +182,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
private fun inplacePrefixArray(operator: String, array: PtArrayIndexer): IRCodeChunks {
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
val result = mutableListOf<IRCodeChunkBase>()
val vmDt = irType(array.type)
val constIndex = array.index.asConstInteger()
@@ -336,7 +336,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
addToResult(result, tr, valueRegister, -1)
if(extendByteToWord) {
valueRegister = codeGen.registers.nextFree()
val opcode = if(assignment.value.type in SignedDatatypes) Opcode.EXTS else Opcode.EXT
val opcode = if(assignment.value.type.isSigned) Opcode.EXTS else Opcode.EXT
addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg), null)
}
}
@@ -357,7 +357,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
else if(targetArray!=null) {
val variable = targetArray.variable.name
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type)
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type, null)
val fixedIndex = targetArray.index.asConstInteger()
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name)
@@ -465,10 +465,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
&& (ptrWithOffset.right.type in ByteDatatypes || offsetTypecast?.value?.type in ByteDatatypes)) {
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// STOREIX only works with byte index.
val tr = if(offsetTypecast?.value?.type in ByteDatatypes)
expressionEval.translateExpression(offsetTypecast!!.value)
val tr = if(offsetTypecast?.value?.type?.isByte==true)
expressionEval.translateExpression(offsetTypecast.value)
else
expressionEval.translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
@@ -498,9 +498,9 @@ 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.UBYTE, array.position)
val mult: PtExpression = PtBinaryExpression("*", DataType.forDt(BaseDataType.UBYTE), array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
mult.children += PtNumber(BaseDataType.UBYTE, itemsize.toDouble(), array.position)
val tr = expressionEval.translateExpression(mult)
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)
@@ -511,7 +511,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constIndex!=null && constValue!=null) {
if(array.splitWords) {
val valueRegLsb = codeGen.registers.nextFree()
@@ -551,7 +551,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constIndex!=null && constValue!=null) {
if(array.splitWords) {
val valueRegLsb = codeGen.registers.nextFree()
@@ -611,7 +611,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constIndex!=null && constValue!=null) {
if(array.splitWords) {
val valueRegLsb = codeGen.registers.nextFree()
@@ -651,7 +651,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constIndex!=null && constValue!=null) {
if(array.splitWords) {
val valueRegLsb = codeGen.registers.nextFree()
@@ -716,7 +716,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
val constFactorRight = operand as? PtNumber
if(vmDt==IRDataType.FLOAT) {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
val factor = constFactorRight.number
result += codeGen.divideByConstFloatInplace(constAddress, symbol, factor)
} else {
@@ -737,7 +737,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
addInstr(result, ins, null)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
val factor = constFactorRight.number.toInt()
result += codeGen.divideByConstInplace(vmDt, constAddress, symbol, factor, signed)
} else {
@@ -763,7 +763,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun operatorMultiplyInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) {
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
val result = mutableListOf<IRCodeChunkBase>()
if(array.splitWords)
return operatorMultiplyInplaceSplitArray(array, operand)
@@ -801,7 +801,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
, null)
}
} else {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
val factor = constFactorRight.number.toInt()
result += codeGen.multiplyByConstInplace(vmDt, constAddress, symbol, factor)
} else {
@@ -819,7 +819,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun operatorMinusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) {
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
val result = mutableListOf<IRCodeChunkBase>()
if(array.splitWords)
return operatorMinusInplaceSplitArray(array, operand)
@@ -915,7 +915,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
if(array.splitWords)
return operatorPlusInplaceSplitArray(array, operand)
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
val elementDt = irType(array.type)
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
@@ -1055,7 +1055,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger()
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constIndex!=null && constValue!=null) {
if(array.splitWords) {
val valueRegLsb = codeGen.registers.nextFree()
@@ -1369,10 +1369,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
private fun createInplaceArrayComparison(array: PtArrayIndexer, value: PtExpression, comparisonOperator: String): IRCodeChunks? {
if(array.type==DataType.FLOAT)
if(array.type.isFloat)
return null // TODO("optimized in-place compare on float arrays"))
val eltSize = codeGen.program.memsizer.memorySize(array.type)
val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
val result = mutableListOf<IRCodeChunkBase>()
if(array.splitWords)
TODO("inplace compare for split word array")
@@ -1,7 +1,8 @@
package prog8.codegen.intermediate
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.core.AssemblyError
import prog8.code.core.BaseDataType
import prog8.intermediate.*
@@ -19,6 +20,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"callfar" -> funcCallfar(call)
"callfar2" -> funcCallfar2(call)
"call" -> funcCall(call)
"lsw" -> throw AssemblyError("lsw() should have been removed or replaced by a const value")
"msw" -> throw AssemblyError("msw() should have been removed or replaced by a const value")
"msb" -> funcMsb(call)
"lsb" -> funcLsb(call)
"memory" -> funcMemory(call)
@@ -167,13 +170,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcAbs(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val sourceDt = call.args.single().type
val result = mutableListOf<IRCodeChunkBase>()
if(sourceDt==DataType.UWORD)
if(sourceDt.isUnsignedWord)
return ExpressionCodeResult.EMPTY
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1)
when (sourceDt) {
DataType.BYTE -> {
when (sourceDt.base) {
BaseDataType.BYTE -> {
val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
@@ -184,7 +187,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
result += IRCodeChunk(notNegativeLabel, null)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
DataType.WORD -> {
BaseDataType.WORD -> {
val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
@@ -195,7 +198,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
result += IRCodeChunk(notNegativeLabel, null)
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
val resultFpReg = codeGen.registers.nextFreeFloat()
addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null)
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
@@ -227,8 +230,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
val dt = call.args[0].type
when(dt) {
DataType.UBYTE -> {
when(dt.base) {
BaseDataType.UBYTE -> {
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
@@ -236,7 +239,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
@@ -244,7 +247,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
addToResult(result, tr, -1, tr.resultFpReg)
val resultFpReg = codeGen.registers.nextFreeFloat()
result += IRCodeChunk(null, null).also {
@@ -294,11 +297,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
)
return ExpressionCodeResult(result, type, -1, valueTr.resultFpReg)
} else {
val syscall = when(call.type) {
DataType.UBYTE -> IMSyscall.CLAMP_UBYTE
DataType.BYTE -> IMSyscall.CLAMP_BYTE
DataType.UWORD -> IMSyscall.CLAMP_UWORD
DataType.WORD -> IMSyscall.CLAMP_WORD
val syscall = when(call.type.base) {
BaseDataType.UBYTE -> IMSyscall.CLAMP_UBYTE
BaseDataType.BYTE -> IMSyscall.CLAMP_BYTE
BaseDataType.UWORD -> IMSyscall.CLAMP_UWORD
BaseDataType.WORD -> IMSyscall.CLAMP_WORD
else -> throw AssemblyError("invalid dt")
}
result += codeGen.makeSyscall(syscall, listOf(
@@ -318,7 +321,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1)
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
val comparisonOpcode = if(call.type.isSigned) Opcode.BGTSR else Opcode.BGTR
val after = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(comparisonOpcode, type, reg1 = rightTr.resultReg, reg2 = leftTr.resultReg, labelSymbol = after)
@@ -337,7 +340,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1)
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
val comparisonOpcode = if(call.type.isSigned) Opcode.BGTSR else Opcode.BGTR
val after = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(comparisonOpcode, type, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg, labelSymbol = after)
@@ -544,8 +547,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
if(arr!=null && index!=null) {
if(arr.splitWords) TODO("IR rol/ror on split words array")
val variable = arr.variable.name
val itemsize = codeGen.program.memsizer.memorySize(arr.type)
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = index*itemsize), null)
val offset = codeGen.program.memsizer.memorySize(arr.type, index)
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = offset), null)
return ExpressionCodeResult(result, vmDt, -1, -1)
}
@@ -630,7 +633,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
}
else {
val eltSize = codeGen.program.memsizer.memorySize(target.type)
val eltSize = codeGen.program.memsizer.memorySize(target.type, null)
val constIndex = target.index.asConstInteger()
if(isConstZeroValue) {
if(constIndex!=null) {
@@ -53,7 +53,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
is PtIdentifier -> {
val code = IRCodeChunk(null, null)
if (expr.type in PassByValueDatatypes) {
if (expr.type.isPassByValue) {
val vmDt = irType(expr.type)
if(vmDt==IRDataType.FLOAT) {
val resultFpRegister = codeGen.registers.nextFreeFloat()
@@ -67,9 +67,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
} else {
// for strings and arrays etc., load the *address* of the value instead
val vmDt = if(expr.type==DataType.UNDEFINED) IRDataType.WORD else irType(expr.type)
// for arrays this could mean a split word array, in which case we take the address of the _lsb array which comes first
val vmDt = if(expr.type.isUndefined) IRDataType.WORD else irType(expr.type)
val resultRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = expr.name)
val labelsymbol = if(expr.type.isSplitWordArray) expr.name+"_lsb" else expr.name
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = labelsymbol)
ExpressionCodeResult(code, vmDt, resultRegister, -1)
}
}
@@ -86,7 +88,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
is PtRange,
is PtArray,
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
else -> throw AssemblyError("weird expression")
}
}
@@ -132,13 +133,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val result = mutableListOf<IRCodeChunkBase>()
val resultRegister = codeGen.registers.nextFree()
if(expr.isFromArrayElement) {
require(expr.identifier.type !in SplitWordArrayTypes)
require(!expr.identifier.type.isSplitWordArray)
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null)
val indexTr2 = translateExpression(expr.arrayIndexExpr!!)
addToResult(result, indexTr2, indexTr2.resultReg, -1)
val indexWordReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexWordReg, reg2=indexTr2.resultReg), null)
if(expr.identifier.type == DataType.UWORD) {
if(expr.identifier.type.isUnsignedWord) {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
@@ -155,7 +156,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
} else {
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null)
if(expr.isMsbForSplitArray) {
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol+"_msb"), null)
} else if(expr.identifier.type.isSplitWordArray) {
// the _lsb split array comes first in memory
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol+"_lsb"), null)
} else
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = symbol), null)
}
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
}
@@ -185,10 +192,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
&& (ptrWithOffset.right.type in ByteDatatypes || offsetTypecast?.value?.type in ByteDatatypes)) {
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// LOADIX only works with byte index.
val tr = if(offsetTypecast?.value?.type in ByteDatatypes)
translateExpression(offsetTypecast!!.value)
val tr = if(offsetTypecast?.value?.type?.isByte==true)
translateExpression(offsetTypecast.value)
else
translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
@@ -212,10 +219,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(it is PtBool) it.asInt()
else (it as PtNumber).number.toInt()
}
when(elementDt) {
in IntegerDatatypesWithBoolean -> {
if (elementDt in ByteDatatypesWithBoolean) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_BYTE)
if (elementDt in WordDatatypes) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_WORD)
when {
elementDt.isIntegerOrBool -> {
if (elementDt.isByteOrBool) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_BYTE)
if (elementDt.isWord) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_WORD)
val gottemLabel = codeGen.createLabelName()
val endLabel = codeGen.createLabelName()
val elementTr = translateExpression(check.needle)
@@ -230,17 +237,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, elementTr.resultReg, immediate = 1), gottemLabel)
result += IRCodeChunk(endLabel, null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.FLOAT -> throw AssemblyError("containmentchecks for floats should always be done on an array variable with subroutine")
elementDt.isFloat -> throw AssemblyError("containmentchecks for floats should always be done on an array variable with subroutine")
else -> throw AssemblyError("weird dt $elementDt")
}
}
val haystackVar = check.haystackHeapVar!!
when(haystackVar.type) {
DataType.STR -> {
when {
haystackVar.type.isString -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, elementTr.resultReg, -1)
@@ -250,7 +256,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.ARRAY_UB, DataType.ARRAY_B -> {
haystackVar.type.isByteArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, elementTr.resultReg, -1)
@@ -263,7 +269,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
haystackVar.type.isWordArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, elementTr.resultReg, -1)
@@ -276,7 +282,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.ARRAY_F -> {
haystackVar.type.isFloatArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, -1, elementTr.resultFpReg)
@@ -295,7 +301,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
private fun translate(arrayIx: PtArrayIndexer): ExpressionCodeResult {
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type)
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type, null)
val vmDt = irType(arrayIx.type)
val result = mutableListOf<IRCodeChunkBase>()
val arrayVarSymbol = arrayIx.variable.name
@@ -386,18 +392,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addToResult(result, tr, tr.resultReg, tr.resultFpReg)
var actualResultReg2 = -1
var actualResultFpReg2 = -1
when(cast.type) {
DataType.BOOL -> {
when (cast.value.type) {
in ByteDatatypes -> {
val valueDt = cast.value.type
when(cast.type.base) {
BaseDataType.BOOL -> {
when {
valueDt.isByte -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.SNZ, IRDataType.BYTE, reg1=actualResultReg2, reg2=tr.resultReg), null)
}
in WordDatatypes -> {
valueDt.isWord -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.SNZ, IRDataType.WORD, reg1=actualResultReg2, reg2=tr.resultReg), null)
}
DataType.FLOAT -> {
valueDt.isFloat -> {
actualResultReg2 = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SGN, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg)
@@ -407,87 +414,87 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else -> throw AssemblyError("weird cast value type")
}
}
DataType.UBYTE -> {
when(cast.value.type) {
DataType.BOOL, DataType.BYTE, DataType.UWORD, DataType.WORD -> {
BaseDataType.UBYTE -> {
when(valueDt.base) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD -> {
actualResultReg2 = tr.resultReg // just keep the LSB as it is
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.FTOUB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast value type")
}
}
DataType.BYTE -> {
when(cast.value.type) {
DataType.BOOL, DataType.UBYTE, DataType.UWORD, DataType.WORD -> {
BaseDataType.BYTE -> {
when(valueDt.base) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.WORD -> {
actualResultReg2 = tr.resultReg // just keep the LSB as it is
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.FTOSB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast value type")
}
}
DataType.UWORD -> {
when(cast.value.type) {
DataType.BYTE -> {
BaseDataType.UWORD -> {
when(valueDt.base) {
BaseDataType.BYTE -> {
// byte -> uword: sign extend
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null)
}
DataType.BOOL, DataType.UBYTE -> {
BaseDataType.BOOL, BaseDataType.UBYTE -> {
// ubyte -> uword: sign extend
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null)
}
DataType.WORD -> {
BaseDataType.WORD -> {
actualResultReg2 = tr.resultReg
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast value type")
}
}
DataType.WORD -> {
when(cast.value.type) {
DataType.BYTE -> {
BaseDataType.WORD -> {
when(valueDt.base) {
BaseDataType.BYTE -> {
// byte -> word: sign extend
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null)
}
DataType.BOOL, DataType.UBYTE -> {
BaseDataType.BOOL, BaseDataType.UBYTE -> {
// byte -> word: sign extend
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null)
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
actualResultReg2 = tr.resultReg
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.FTOSW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
}
else -> throw AssemblyError("weird cast value type")
}
}
DataType.FLOAT -> {
BaseDataType.FLOAT -> {
actualResultFpReg2 = codeGen.registers.nextFreeFloat()
when(cast.value.type) {
DataType.BOOL, DataType.UBYTE -> {
when(valueDt.base) {
BaseDataType.BOOL, BaseDataType.UBYTE -> {
addInstr(result, IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
DataType.BYTE -> {
BaseDataType.BYTE -> {
addInstr(result, IRInstruction(Opcode.FFROMSB, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
addInstr(result, IRInstruction(Opcode.FFROMUW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
DataType.WORD -> {
BaseDataType.WORD -> {
addInstr(result, IRInstruction(Opcode.FFROMSW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
}
else -> throw AssemblyError("weird cast value type")
@@ -501,7 +508,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
val vmDt = irType(binExpr.left.type)
val signed = binExpr.left.type in SignedDatatypes
val signed = binExpr.left.type.isSigned
return when(binExpr.operator) {
"+" -> operatorPlus(binExpr, vmDt)
"-" -> operatorMinus(binExpr, vmDt)
@@ -563,12 +570,23 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
val paramDt = irType(parameter.type)
val tr = translateExpression(arg)
if(paramDt==IRDataType.FLOAT)
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(IRDataType.FLOAT, tr.resultFpReg, null)))
else
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, null)))
result += tr.chunks
if(parameter.register==null) {
val tr = translateExpression(arg)
result += tr.chunks
if(paramDt==IRDataType.FLOAT)
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(IRDataType.FLOAT, tr.resultFpReg, null)))
else
argRegisters.add(FunctionCallArgs.ArgumentSpec(parameter.name, null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, null)))
} else {
require(parameter.register in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
val regname = parameter.register!!.asScopedNameVirtualReg(parameter.type).joinToString(".")
val assign = PtAssignment(fcall.position)
val target = PtAssignTarget(true, fcall.position)
target.add(PtIdentifier(regname, parameter.type, fcall.position))
assign.add(target)
assign.add(arg)
result += codeGen.translateNode(assign)
}
}
// return value (always singular for normal Subs)
val returnRegSpec = if(fcall.void) null else {
@@ -583,7 +601,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
fcallArgs = FunctionCallArgs(argRegisters, if(returnRegSpec==null) emptyList() else listOf(returnRegSpec))), null)
return if(fcall.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else if(fcall.type==DataType.FLOAT)
else if(fcall.type.isFloat)
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, returnRegSpec.registerNum)
else
ExpressionCodeResult(result, returnRegSpec!!.dt, returnRegSpec.registerNum, -1)
@@ -696,7 +714,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return if(fcall.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else if(fcall.type==DataType.FLOAT)
else if(fcall.type.isFloat)
ExpressionCodeResult(result, returnRegSpec!!.dt, -1, finalReturnRegister)
else
ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1)
@@ -759,7 +777,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
): ExpressionCodeResult {
// return multiple values
val returnRegisters = callTarget.returns.map {
val regnum = if(it.type==DataType.FLOAT) codeGen.registers.nextFreeFloat() else codeGen.registers.nextFree()
val regnum = if(it.type.isFloat) codeGen.registers.nextFreeFloat() else codeGen.registers.nextFree()
FunctionCallArgs.RegSpec(irType(it.type), regnum, it.register)
}
// create the call
@@ -808,7 +826,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1=cmpResultReg, reg2 = resultRegister, reg3 = zeroRegister), null)
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultReg, -1)
} else {
if(binExpr.left.type==DataType.STR || binExpr.right.type==DataType.STR) {
if(binExpr.left.type.isString || binExpr.right.type.isString) {
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else {
val leftTr = translateExpression(binExpr.left)
@@ -851,7 +869,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1=cmpResultRegister, reg2 = resultRegister, reg3 = zeroRegister), null)
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultRegister, -1)
} else {
if(binExpr.left.type==DataType.STR || binExpr.right.type==DataType.STR) {
if(binExpr.left.type.isString || binExpr.right.type.isString) {
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else {
val leftTr = translateExpression(binExpr.left)
@@ -892,7 +910,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
result += IRCodeChunk(label, null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else {
if(binExpr.left.type==DataType.STR || binExpr.right.type==DataType.STR) {
if(binExpr.left.type.isString || binExpr.right.type.isString) {
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else {
return if(binExpr.right.asConstValue()==0.0) {
@@ -1064,7 +1082,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val result = mutableListOf<IRCodeChunkBase>()
val constFactorRight = binExpr.right as? PtNumber
if(vmDt==IRDataType.FLOAT) {
if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
if(constFactorRight!=null && !constFactorRight.type.isFloat) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, -1, tr.resultFpReg)
val factor = constFactorRight.number
@@ -1083,7 +1101,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
}
} else {
return if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
return if(constFactorRight!=null && !constFactorRight.type.isFloat) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1)
val factor = constFactorRight.number.toInt()
@@ -1141,13 +1159,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
}
} else {
return if(constFactorLeft!=null && constFactorLeft.type!=DataType.FLOAT) {
return if(constFactorLeft!=null && !constFactorLeft.type.isFloat) {
val tr = translateExpression(binExpr.right)
addToResult(result, tr, tr.resultReg, -1)
val factor = constFactorLeft.number.toInt()
result += codeGen.multiplyByConst(vmDt, tr.resultReg, factor)
ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else if(constFactorRight!=null && constFactorRight.type!=DataType.FLOAT) {
} else if(constFactorRight!=null && !constFactorRight.type.isFloat) {
val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1)
val factor = constFactorRight.number.toInt()
@@ -147,12 +147,11 @@ class IRCodeGen(
}
is IRInlineAsmChunk -> IRInlineAsmChunk(sub.label, first.assembly, first.isIR, first.next)
is IRInlineBinaryChunk -> IRInlineBinaryChunk(sub.label, first.data, first.next)
else -> throw AssemblyError("invalid chunk")
}
sub.chunks.removeAt(0)
sub.chunks.add(0, replacement)
} else if(first.label != sub.label) {
val next = if(first is IRCodeChunk) first else null
val next = first as? IRCodeChunk
sub.chunks.add(0, IRCodeChunk(sub.label, next))
}
}
@@ -278,7 +277,8 @@ class IRCodeGen(
val goto = branch.trueScope.children.firstOrNull() as? PtJump
if (goto is PtJump) {
// special case the form: if_cc goto <place> (with optional else)
val address = goto.address?.toInt()
val address = goto.target.asConstInteger()
val label = (goto.target as? PtIdentifier)?.name
if(address!=null) {
val branchIns = when(branch.condition) {
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, address = address)
@@ -291,8 +291,7 @@ class IRCodeGen(
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, address = address)
}
addInstr(result, branchIns, null)
} else {
val label = goto.identifier!!.name
} else if(label!=null && !isIndirectJump(goto)) {
val branchIns = when(branch.condition) {
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label)
@@ -304,6 +303,8 @@ class IRCodeGen(
BranchCondition.VS -> IRInstruction(Opcode.BSTVS, labelSymbol = label)
}
addInstr(result, branchIns, null)
} else {
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
if(branch.falseScope.children.isNotEmpty())
result += translateNode(branch.falseScope)
@@ -363,9 +364,6 @@ class IRCodeGen(
is IRInlineBinaryChunk -> {
IRInlineBinaryChunk(label, first.data, first.next)
}
else -> {
throw AssemblyError("invalid chunk")
}
}
return listOf(labeledFirstChunk) + chunks.drop(1)
}
@@ -436,8 +434,8 @@ class IRCodeGen(
val tmpReg = registers.nextFree()
val loopLabel = createLabelName()
val endLabel = createLabelName()
when (iterable.type) {
DataType.STR -> {
when {
iterable.type.isString -> {
// iterate over a zero-terminated string
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also {
@@ -452,10 +450,10 @@ class IRCodeGen(
result += jumpChunk
result += IRCodeChunk(endLabel, null)
}
in SplitWordArrayTypes -> {
iterable.type.isSplitWordArray -> {
// iterate over lsb/msb split word array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
if(elementDt !in WordDatatypes)
val elementDt = iterable.type.elementType()
if(!elementDt.isWord)
throw AssemblyError("weird dt")
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also {
@@ -479,13 +477,14 @@ class IRCodeGen(
}
else -> {
// iterate over regular array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
val elementSize = program.memsizer.memorySize(elementDt)
val element = iterable.type.sub!!
val elementDt = element.dt
val elementSize = program.memsizer.memorySize(element)
val lengthBytes = iterableLength!! * elementSize
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also {
it += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
it += IRInstruction(Opcode.LOADX, irType(DataType.forDt(elementDt)), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
it += IRInstruction(Opcode.STOREM, irType(DataType.forDt(elementDt)), reg1=tmpReg, labelSymbol = loopvarSymbol)
}
result += translateNode(forLoop.statements)
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
@@ -552,7 +551,7 @@ class IRCodeGen(
addToResult(result, fromTr, fromTr.resultReg, -1)
val labelAfterFor = createLabelName()
val precheckInstruction = if(loopvarDt in SignedDatatypes) {
val precheckInstruction = if(loopvarDt.isSigned) {
if(step>0)
IRInstruction(Opcode.BGTSR, loopvarDtIr, fromTr.resultReg, toTr.resultReg, labelSymbol=labelAfterFor)
else
@@ -620,7 +619,7 @@ class IRCodeGen(
val chunk2 = addConstMem(loopvarDtIr, null, loopvarSymbol, iterable.step)
if(loopvarDtIr==IRDataType.BYTE && iterable.step==-1 && iterable.last==0) {
// downto 0 optimization (byte)
if(loopvarDt==DataType.BYTE || iterable.first<=127) {
if(loopvarDt.isSignedByte || iterable.first<=127) {
chunk2 += IRInstruction(Opcode.BSTPOS, labelSymbol = loopLabel)
} else {
chunk2 += IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = indexReg, labelSymbol = loopvarSymbol)
@@ -981,7 +980,7 @@ class IRCodeGen(
private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump): MutableList<IRCodeChunkBase> {
val condition = ifElse.condition as? PtBinaryExpression
if(condition==null || condition.left.type!=DataType.FLOAT) {
if(condition==null || !condition.left.type.isFloat) {
return if(isIndirectJump(goto))
ifWithOnlyIndirectJump_IntegerCond(ifElse, goto)
else
@@ -1028,7 +1027,12 @@ class IRCodeGen(
it += IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel)
}
}
it += IRInstruction(Opcode.JUMPI, labelSymbol = goto.identifier!!.name)
// evaluate jump address expression into a register and jump indirectly to it
val tr = expressionEval.translateExpression(goto.target)
for(i in tr.chunks.flatMap { it.instructions }) {
it += i
}
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
} else {
// normal jump, directly to target with branch opcode
when(condition.operator) {
@@ -1048,10 +1052,12 @@ class IRCodeGen(
">=" -> Opcode.BGES
else -> throw AssemblyError("weird operator")
}
it += if (goto.address != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.address?.toInt())
it += if (goto.target.asConstInteger() != null)
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.target.asConstInteger())
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = (goto.target as PtIdentifier).name)
else
IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = goto.identifier!!.name)
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
}
}
@@ -1062,11 +1068,15 @@ class IRCodeGen(
}
private fun branchInstr(goto: PtJump, branchOpcode: Opcode): IRInstruction {
return if (goto.address != null)
IRInstruction(branchOpcode, address = goto.address?.toInt())
return if (goto.target.asConstInteger() != null)
IRInstruction(branchOpcode, address = goto.target.asConstInteger())
else {
require(!isIndirectJump(goto)) { "indirect jumps cannot be expressed using a branch opcode"}
IRInstruction(branchOpcode, labelSymbol = goto.identifier!!.name)
val identifier = goto.target as? PtIdentifier
if(identifier!=null && !isIndirectJump(goto))
IRInstruction(branchOpcode, labelSymbol = identifier.name)
else
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
}
@@ -1074,7 +1084,6 @@ class IRCodeGen(
// indirect jump to target so the if has to jump past it instead
val result = mutableListOf<IRCodeChunkBase>()
val afterIfLabel = createLabelName()
val gotoSymbol = goto.identifier!!.name
fun ifNonZeroIntThenJump_BinExpr(condition: PtBinaryExpression) {
if(condition.operator in LogicalOperators) {
@@ -1086,7 +1095,7 @@ class IRCodeGen(
val leftTr = expressionEval.translateExpression(condition.left)
val irDt = leftTr.dt
val signed = condition.left.type in SignedDatatypes
val signed = condition.left.type.isSigned
addToResult(result, leftTr, leftTr.resultReg, -1)
val number = (condition.right as? PtNumber)?.number?.toInt()
if(number!=null) {
@@ -1174,7 +1183,7 @@ class IRCodeGen(
when(val cond = ifElse.condition) {
is PtTypeCast -> {
require(cond.type==DataType.BOOL && cond.value.type in NumericDatatypes)
require(cond.type.isBool && cond.value.type.isNumeric)
val tr = expressionEval.translateExpression(cond)
result += tr.chunks
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = afterIfLabel), null)
@@ -1196,7 +1205,10 @@ class IRCodeGen(
else -> throw AssemblyError("weird if condition ${ifElse.condition}")
}
addInstr(result, IRInstruction(Opcode.JUMPI, labelSymbol = gotoSymbol), null)
// indirect jump to some computed address
val tr = expressionEval.translateExpression(goto.target)
result += tr.chunks
addInstr(result, IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg), null)
result += IRCodeChunk(afterIfLabel, null)
return result
}
@@ -1216,7 +1228,7 @@ class IRCodeGen(
val leftTr = expressionEval.translateExpression(condition.left)
val irDt = leftTr.dt
val signed = condition.left.type in SignedDatatypes
val signed = condition.left.type.isSigned
addToResult(result, leftTr, leftTr.resultReg, -1)
val number = (condition.right as? PtNumber)?.number?.toInt()
if(number!=null) {
@@ -1238,10 +1250,12 @@ class IRCodeGen(
">=" -> if(signed) Opcode.BGES else Opcode.BGE
else -> throw AssemblyError("invalid comparison operator")
}
if (goto.address != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, address = goto.address?.toInt()), null)
if (goto.target.asConstInteger() != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, address = goto.target.asConstInteger()), null)
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = (goto.target as PtIdentifier).name), null)
else
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = goto.identifier!!.name), null)
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
}
} else {
@@ -1295,17 +1309,19 @@ class IRCodeGen(
it += branchInstr(goto, opcode)
}
} else {
if (goto.address != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, address = goto.address?.toInt()), null)
if (goto.target.asConstInteger() != null)
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, address = goto.target.asConstInteger()), null)
else if(goto.target is PtIdentifier && !isIndirectJump(goto))
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = (goto.target as PtIdentifier).name), null)
else
addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this!
}
}
}
when(val cond = ifElse.condition) {
is PtTypeCast -> {
require(cond.type==DataType.BOOL && cond.value.type in NumericDatatypes)
require(cond.type.isBool && cond.value.type.isNumeric)
val tr = expressionEval.translateExpression(cond)
result += tr.chunks
addInstr(result, branchInstr(goto, Opcode.BSTNE), null)
@@ -1332,7 +1348,7 @@ class IRCodeGen(
throw AssemblyError("not prefix in ifelse should have been replaced by swapped if-else blocks")
val condition = ifElse.condition as? PtBinaryExpression
if(condition==null || condition.left.type != DataType.FLOAT) {
if(condition==null || !condition.left.type.isFloat) {
return ifWithElse_IntegerCond(ifElse)
}
@@ -1425,7 +1441,7 @@ class IRCodeGen(
if(condition.operator in LogicalOperators)
return translateSimple(condition, Opcode.BSTEQ, false)
val signed = condition.left.type in SignedDatatypes
val signed = condition.left.type.isSigned
val elseBranchFirstReg: Int
val elseBranchSecondReg: Int
val number = (condition.right as? PtNumber)?.number?.toInt()
@@ -1560,12 +1576,13 @@ class IRCodeGen(
}
when(val cond=ifElse.condition) {
// TODO investigate; maybe do all conditions require a CMPI at the end? Some here still have false, but do they work correctly in all cases?
is PtBool -> {
// normally this will be optimized away, but not with -noopt
translateSimple(cond, Opcode.BSTEQ, false)
}
is PtTypeCast -> {
require(cond.type==DataType.BOOL && cond.value.type in NumericDatatypes)
require(cond.type.isBool && cond.value.type.isNumeric)
translateSimple(cond, Opcode.BSTEQ, false)
}
is PtIdentifier, is PtArrayIndexer, is PtContainmentCheck -> {
@@ -1576,7 +1593,7 @@ class IRCodeGen(
}
is PtPrefix -> {
require(cond.operator=="not")
translateSimple(cond.value, Opcode.BSTNE, false)
translateSimple(cond.value, Opcode.BSTNE, true)
}
is PtBinaryExpression -> {
translateBinExpr(cond)
@@ -1592,7 +1609,7 @@ class IRCodeGen(
1 -> return translateGroup(repeat.children)
256 -> {
// 256 iterations can still be done with just a byte counter if you set it to zero as starting value.
repeat.children[0] = PtNumber(DataType.UBYTE, 0.0, repeat.count.position)
repeat.children[0] = PtNumber(BaseDataType.UBYTE, 0.0, repeat.count.position)
}
}
@@ -1630,26 +1647,35 @@ class IRCodeGen(
private fun translate(jump: PtJump): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val chunk = IRCodeChunk(null, null)
chunk += if(jump.address!=null) {
IRInstruction(Opcode.JUMP, address = jump.address!!.toInt())
if(jump.target.asConstInteger()!=null) {
chunk += IRInstruction(Opcode.JUMP, address = jump.target.asConstInteger())
result += chunk
return result
} else {
if (jump.identifier != null) {
if(isIndirectJump(jump)) {
IRInstruction(Opcode.JUMPI, labelSymbol = jump.identifier!!.name)
} else {
IRInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.name)
}
} else
throw AssemblyError("weird jump")
val identifier = jump.target as? PtIdentifier
if (identifier != null && !isIndirectJump(jump)) {
// jump to label
chunk += IRInstruction(Opcode.JUMP, labelSymbol = identifier.name)
result += chunk
return result
}
// evaluate jump address expression into a register and jump indirectly to it
val tr = expressionEval.translateExpression(jump.target)
result += tr.chunks
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.JUMPI, reg1=tr.resultReg)
}
return result
}
result += chunk
return result
}
private fun isIndirectJump(jump: PtJump): Boolean {
if(jump.identifier==null)
if(jump.target.asConstInteger()!=null)
return false
val symbol = symbolTable.lookup(jump.identifier!!.name)
val identifier = jump.target as? PtIdentifier
if(identifier==null)
return true
val symbol = symbolTable.lookup(identifier.name)
return symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR
}
@@ -1665,7 +1691,7 @@ class IRCodeGen(
if(value==null) {
addInstr(result, IRInstruction(Opcode.RETURN), null)
} else {
if(value.type==DataType.FLOAT) {
if(value.type.isFloat) {
if(value is PtNumber) {
addInstr(result, IRInstruction(Opcode.RETURNI, IRDataType.FLOAT, immediateFp = value.number), null)
} else {
@@ -1750,19 +1776,30 @@ class IRCodeGen(
return irBlock
}
private fun translate(parameters: List<PtSubroutineParameter>) =
parameters.map {
val flattenedName = it.definingISub()!!.name + "." + it.name
if(symbolTable.lookup(flattenedName)==null)
TODO("fix missing lookup for: $flattenedName parameter")
val orig = symbolTable.lookup(flattenedName) as StStaticVariable
IRSubroutine.IRParam(flattenedName, orig.dt)
private fun translate(parameters: List<PtSubroutineParameter>): List<IRSubroutine.IRParam> {
val result = mutableListOf<IRSubroutine.IRParam>()
parameters.forEach {
if(it.register==null) {
val flattenedName = it.definingISub()!!.name + "." + it.name
if (symbolTable.lookup(flattenedName) == null)
TODO("fix missing lookup for: $flattenedName parameter")
val orig = symbolTable.lookup(flattenedName) as StStaticVariable
result += IRSubroutine.IRParam(flattenedName, orig.dt)
} else {
val reg = it.register
require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
val regname = it.register!!.asScopedNameVirtualReg(it.type).joinToString(".")
val targetVar = symbolTable.lookup(regname) as StMemVar
result += IRSubroutine.IRParam(regname, targetVar.dt)
}
}
return result
}
private var labelSequenceNumber = 0
internal fun createLabelName(): String {
labelSequenceNumber++
return "${PtLabel.GeneratedLabelPrefix}$labelSequenceNumber"
return "${PtLabel.GENERATED_LABEL_PREFIX}$labelSequenceNumber"
}
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult
@@ -84,7 +84,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
/*
Empty Code chunk with label ->
If next chunk has no label -> move label to next chunk, remove original
If next chunk has label -> label name should be the same, remove original. Otherwise merge both labels into 1.
If next chunk has label -> label name should be the same, remove original, otherwise merge both labels into 1.
If is last chunk -> keep chunk in place because of the label.
Empty Code chunk without label ->
should not have been generated! ERROR.
@@ -195,10 +195,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
chunks += if(label!=null)
IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
else
chunks += candidate
candidate
}
}
is IRInlineBinaryChunk -> {
@@ -206,10 +206,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineBinaryChunk(label, candidate.data, candidate.next)
chunks += if(label!=null)
IRInlineBinaryChunk(label, candidate.data, candidate.next)
else
chunks += candidate
candidate
}
}
}
+18 -9
View File
@@ -2,16 +2,25 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = when(dt) {
in ByteDatatypesWithBoolean -> 1
DataType.FLOAT -> 5
else -> 2
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray || dt.isSplitWordArray) {
require(numElements!=null)
return when(dt.sub?.dt) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
BaseDataType.FLOAT -> numElements*5
else -> throw IllegalArgumentException("invalid sub type")
}
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> 5 * (numElements ?: 1)
else -> 2 * (numElements ?: 1)
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) {
DataType.ARRAY_UW -> numElements*2
DataType.ARRAY_W -> numElements*2
DataType.ARRAY_F -> numElements*5
else -> numElements
override fun memorySize(dt: SubType): Int {
return memorySize(DataType.forDt(dt.dt), null)
}
}
+95 -90
View File
@@ -4,6 +4,7 @@ import io.kotest.matchers.shouldBe
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.source.SourceCode
import prog8.code.target.VMTarget
import prog8.codegen.vm.VmAssemblyProgram
import prog8.codegen.vm.VmCodeGen
@@ -47,16 +48,16 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable(
"pi",
DataType.UBYTE,
DataType.forDt(BaseDataType.UBYTE),
ZeropageWish.DONTCARE,
0u,
PtNumber(DataType.UBYTE, 0.0, Position.DUMMY),
PtNumber(BaseDataType.UBYTE, 0.0, Position.DUMMY),
null,
Position.DUMMY
))
sub.add(PtVariable(
"particleX",
DataType.ARRAY_UB,
DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE,
0u,
null,
@@ -65,7 +66,7 @@ class TestVmCodeGen: FunSpec({
))
sub.add(PtVariable(
"particleDX",
DataType.ARRAY_UB,
DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE,
0u,
null,
@@ -74,51 +75,55 @@ class TestVmCodeGen: FunSpec({
))
sub.add(PtVariable(
"xx",
DataType.WORD,
DataType.forDt(BaseDataType.WORD),
ZeropageWish.DONTCARE,
0u,
PtNumber(DataType.WORD, 1.0, Position.DUMMY),
PtNumber(BaseDataType.WORD, 1.0, Position.DUMMY),
null,
Position.DUMMY
))
val assign = PtAugmentedAssign("+=", Position.DUMMY)
val target = PtAssignTarget(false, Position.DUMMY).also {
val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY))
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
val targetIdx = PtArrayIndexer(DataType.forDt(BaseDataType.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.UBYTE, Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX", DataType.ARRAY_UB, Position.DUMMY))
value.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY))
val value = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX",
DataType.arrayFor(BaseDataType.UBYTE),
Position.DUMMY))
value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
assign.add(target)
assign.add(value)
sub.add(assign)
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
}
prefixAssign.add(prefixTarget)
prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
prefixAssign.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.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.WORD, Position.DUMMY))
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
}
numberAssign.add(numberAssignTarget)
numberAssign.add(PtNumber(DataType.WORD, 42.0, Position.DUMMY))
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
sub.add(numberAssign)
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY))
}
cxregAssign.add(cxregAssignTarget)
cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY))
sub.add(cxregAssign)
block.add(sub)
@@ -126,7 +131,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.UWORD, 100u, null, Position.DUMMY))
cx16block.add(PtMemMapped("r0", DataType.forDt(BaseDataType.UWORD), 100u, null, Position.DUMMY))
program.add(cx16block)
val options = getTestOptions()
@@ -158,7 +163,7 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable(
"f1",
DataType.FLOAT,
DataType.forDt(BaseDataType.FLOAT),
ZeropageWish.DONTCARE,
0u,
null,
@@ -166,33 +171,33 @@ class TestVmCodeGen: FunSpec({
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.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.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.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.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp3.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.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.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp4.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp4.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
@@ -229,7 +234,7 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable(
"f1",
DataType.FLOAT,
DataType.forDt(BaseDataType.FLOAT),
ZeropageWish.DONTCARE,
0u,
null,
@@ -237,33 +242,33 @@ class TestVmCodeGen: FunSpec({
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.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.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.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.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp3.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.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.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp4.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp4.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
@@ -296,7 +301,7 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable(
"f1",
DataType.FLOAT,
DataType.forDt(BaseDataType.FLOAT),
ZeropageWish.DONTCARE,
0u,
null,
@@ -304,19 +309,19 @@ class TestVmCodeGen: FunSpec({
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
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.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
if2.add(PtNodeGroup())
sub.add(if2)
block.add(sub)
@@ -351,7 +356,7 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable(
"sb1",
DataType.BYTE,
DataType.forDt(BaseDataType.BYTE),
ZeropageWish.DONTCARE,
0u,
null,
@@ -359,33 +364,33 @@ class TestVmCodeGen: FunSpec({
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.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.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.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.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp3.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.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.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp4.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
cmp4.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
@@ -422,7 +427,7 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable(
"sb1",
DataType.BYTE,
DataType.forDt(BaseDataType.BYTE),
ZeropageWish.DONTCARE,
0u,
null,
@@ -430,33 +435,33 @@ class TestVmCodeGen: FunSpec({
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.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.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.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.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp3.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.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.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp4.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY))
cmp4.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
@@ -489,7 +494,7 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable(
"ub1",
DataType.UBYTE,
DataType.forDt(BaseDataType.BYTE),
ZeropageWish.DONTCARE,
0u,
null,
@@ -497,19 +502,19 @@ class TestVmCodeGen: FunSpec({
Position.DUMMY
))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp1.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
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.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY)
cmp2.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also {it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
if2.add(PtNodeGroup())
sub.add(if2)
block.add(sub)
@@ -537,7 +542,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(), null, Position.DUMMY)
val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
val call = PtFunctionCall("main.routine", true, DataType.forDt(BaseDataType.UNDEFINED), Position.DUMMY)
sub.add(call)
block.add(sub)
program.add(block)
-44
View File
@@ -1,44 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
implementation project(':compilerAst')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
}
// note: there are no unit tests in this module!
+26
View File
@@ -0,0 +1,26 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
implementation(project(":codeCore"))
implementation(project(":compilerAst"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
// implementation "org.jetbrains.kotlin:kotlin-reflect"
}
sourceSets {
main {
java {
srcDir("${project.projectDir}/src")
}
resources {
srcDir("${project.projectDir}/res")
}
}
}
// note: there are no unit tests in this module!
@@ -5,10 +5,10 @@ import prog8.ast.base.ExpressionError
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.FunctionCallExpression
import prog8.ast.expressions.NumericLiteral
import prog8.code.core.DataType
import prog8.code.core.IntegerDatatypes
import prog8.code.core.IntegerDatatypesWithBoolean
import prog8.code.core.BaseDataType
import prog8.code.core.Position
import prog8.code.core.isInteger
import prog8.code.core.isIntegerOrBool
import kotlin.math.*
@@ -44,10 +44,10 @@ class ConstExprEvaluator {
}
private fun shiftedright(left: NumericLiteral, amount: NumericLiteral): NumericLiteral {
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
if(!left.type.isInteger || !amount.type.isInteger)
throw ExpressionError("cannot compute $left >> $amount", left.position)
val result =
if(left.type== DataType.UBYTE || left.type== DataType.UWORD)
if(left.type == BaseDataType.UBYTE || left.type == BaseDataType.UWORD)
left.number.toInt().ushr(amount.number.toInt())
else
left.number.toInt().shr(amount.number.toInt())
@@ -55,7 +55,7 @@ class ConstExprEvaluator {
}
private fun shiftedleft(left: NumericLiteral, amount: NumericLiteral): NumericLiteral {
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
if(!left.type.isInteger || !amount.type.isInteger)
throw ExpressionError("cannot compute $left << $amount", left.position)
val result = left.number.toInt().shl(amount.number.toInt())
// when(left.type) {
@@ -70,33 +70,34 @@ class ConstExprEvaluator {
}
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
if(right.type in IntegerDatatypesWithBoolean) {
return NumericLiteral(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
if(right.type.isIntegerOrBool) {
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type==DataType.UWORD) {
if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
} else if(left.type==BaseDataType.UWORD) {
if(right.type.isInteger) {
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
}
} else if(left.type==DataType.LONG) {
if(right.type in IntegerDatatypes) {
} else if(left.type==BaseDataType.LONG) {
if(right.type.isInteger) {
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
}
}
throw ExpressionError("cannot calculate $left ^ $right", left.position)
}
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
if(right.type in IntegerDatatypesWithBoolean) {
return NumericLiteral(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
if(right.type.isIntegerOrBool) {
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type==DataType.UWORD) {
if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
} else if(left.type==BaseDataType.UWORD) {
if(right.type.isInteger) {
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
}
} else if(left.type==DataType.LONG) {
if(right.type in IntegerDatatypes) {
} else if(left.type==BaseDataType.LONG) {
if(right.type.isInteger) {
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
}
}
@@ -104,16 +105,16 @@ class ConstExprEvaluator {
}
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type==DataType.UBYTE || left.type==DataType.BOOL) {
if(right.type in IntegerDatatypesWithBoolean) {
return NumericLiteral(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
if(right.type.isIntegerOrBool) {
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type==DataType.UWORD) {
if(right.type in IntegerDatatypes) {
return NumericLiteral(DataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
} else if(left.type==BaseDataType.UWORD) {
if(right.type.isInteger) {
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
}
} else if(left.type== DataType.LONG) {
if(right.type in IntegerDatatypes) {
} else if(left.type==BaseDataType.LONG) {
if(right.type.isInteger) {
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
}
}
@@ -131,15 +132,15 @@ class ConstExprEvaluator {
private fun plus(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot add $left and $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() + right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() + right.number, left.position)
return when {
left.type.isInteger -> when {
right.type.isInteger -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() + right.number.toInt(), left.position)
right.type == BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, left.number.toInt() + right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteral(DataType.FLOAT, left.number + right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number + right.number, left.position)
left.type == BaseDataType.FLOAT -> when {
right.type.isInteger -> NumericLiteral(BaseDataType.FLOAT, left.number + right.number.toInt(), left.position)
right.type == BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, left.number + right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@@ -148,15 +149,15 @@ class ConstExprEvaluator {
private fun minus(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot subtract $left and $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() - right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() - right.number, left.position)
return when {
left.type.isInteger -> when {
right.type.isInteger -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() - right.number.toInt(), left.position)
right.type == BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, left.number.toInt() - right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteral(DataType.FLOAT, left.number - right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number - right.number, left.position)
left.type == BaseDataType.FLOAT -> when {
right.type.isInteger -> NumericLiteral(BaseDataType.FLOAT, left.number - right.number.toInt(), left.position)
right.type == BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, left.number - right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@@ -165,15 +166,15 @@ class ConstExprEvaluator {
private fun multiply(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot multiply ${left.type} and ${right.type}"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() * right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() * right.number, left.position)
return when {
left.type.isInteger -> when {
right.type.isInteger -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() * right.number.toInt(), left.position)
right.type == BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, left.number.toInt() * right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteral(DataType.FLOAT, left.number * right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number * right.number, left.position)
left.type == BaseDataType.FLOAT -> when {
right.type.isInteger -> NumericLiteral(BaseDataType.FLOAT, left.number * right.number.toInt(), left.position)
right.type == BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, left.number * right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@@ -185,27 +186,27 @@ class ConstExprEvaluator {
private fun divide(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot divide $left by $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> {
return when {
left.type.isInteger -> when {
right.type.isInteger -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
val result: Int = left.number.toInt() / right.number.toInt()
NumericLiteral.optimalInteger(left.type, right.type, result, left.position)
}
DataType.FLOAT -> {
right.type == BaseDataType.FLOAT -> {
if(right.number==0.0) divideByZeroError(right.position)
NumericLiteral(DataType.FLOAT, left.number.toInt() / right.number, left.position)
NumericLiteral(BaseDataType.FLOAT, left.number.toInt() / right.number, left.position)
}
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> {
left.type == BaseDataType.FLOAT -> when {
right.type.isInteger -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteral(DataType.FLOAT, left.number / right.number.toInt(), left.position)
NumericLiteral(BaseDataType.FLOAT, left.number / right.number.toInt(), left.position)
}
DataType.FLOAT -> {
right.type == BaseDataType.FLOAT -> {
if(right.number ==0.0) divideByZeroError(right.position)
NumericLiteral(DataType.FLOAT, left.number / right.number, left.position)
NumericLiteral(BaseDataType.FLOAT, left.number / right.number, left.position)
}
else -> throw ExpressionError(error, left.position)
}
@@ -215,26 +216,26 @@ class ConstExprEvaluator {
private fun remainder(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute remainder of $left by $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> {
return when {
left.type.isInteger -> when {
right.type.isInteger -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteral.optimalNumeric(left.type, right.type, left.number.toInt().toDouble() % right.number.toInt().toDouble(), left.position)
}
DataType.FLOAT -> {
right.type == BaseDataType.FLOAT -> {
if(right.number ==0.0) divideByZeroError(right.position)
NumericLiteral(DataType.FLOAT, left.number.toInt() % right.number, left.position)
NumericLiteral(BaseDataType.FLOAT, left.number.toInt() % right.number, left.position)
}
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> {
left.type == BaseDataType.FLOAT -> when {
right.type.isInteger -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteral(DataType.FLOAT, left.number % right.number.toInt(), left.position)
NumericLiteral(BaseDataType.FLOAT, left.number % right.number.toInt(), left.position)
}
DataType.FLOAT -> {
right.type == BaseDataType.FLOAT -> {
if(right.number ==0.0) divideByZeroError(right.position)
NumericLiteral(DataType.FLOAT, left.number % right.number, left.position)
NumericLiteral(BaseDataType.FLOAT, left.number % right.number, left.position)
}
else -> throw ExpressionError(error, left.position)
}
@@ -252,7 +253,7 @@ class ConstExprEvaluator {
return when(call.target.nameInSource[0]) {
"math" -> evalMath(call, constArgs)
"floats" -> evalFloats(call, constArgs)
"string" -> evalString(call, constArgs)
"strings" -> evalString(call, constArgs)
else -> null
}
}
@@ -290,50 +291,50 @@ class ConstExprEvaluator {
return if(result==null)
null
else
NumericLiteral(DataType.FLOAT, result, func.position)
NumericLiteral(BaseDataType.FLOAT, result, func.position)
}
private fun evalMath(func: FunctionCallExpression, args: List<NumericLiteral>): NumericLiteral? {
return when(func.target.nameInSource[1]) {
"sin8u" -> {
val value = truncate(128.0 + 127.5 * sin(args.single().number / 256.0 * 2 * PI))
NumericLiteral(DataType.UBYTE, value, func.position)
NumericLiteral(BaseDataType.UBYTE, value, func.position)
}
"cos8u" -> {
val value = truncate(128.0 + 127.5 * cos(args.single().number / 256.0 * 2 * PI))
NumericLiteral(DataType.UBYTE, value, func.position)
NumericLiteral(BaseDataType.UBYTE, value, func.position)
}
"sin8" -> {
val value = truncate(127.0 * sin(args.single().number / 256.0 * 2 * PI))
NumericLiteral(DataType.BYTE, value, func.position)
NumericLiteral(BaseDataType.BYTE, value, func.position)
}
"cos8" -> {
val value = truncate(127.0 * cos(args.single().number / 256.0 * 2 * PI))
NumericLiteral(DataType.BYTE, value, func.position)
NumericLiteral(BaseDataType.BYTE, value, func.position)
}
"sinr8u" -> {
val value = truncate(128.0 + 127.5 * sin(args.single().number / 180.0 * 2 * PI))
NumericLiteral(DataType.UBYTE, value, func.position)
NumericLiteral(BaseDataType.UBYTE, value, func.position)
}
"cosr8u" -> {
val value = truncate(128.0 + 127.5 * cos(args.single().number / 180.0 * 2 * PI))
NumericLiteral(DataType.UBYTE, value, func.position)
NumericLiteral(BaseDataType.UBYTE, value, func.position)
}
"sinr8" -> {
val value = truncate(127.0 * sin(args.single().number / 180.0 * 2 * PI))
NumericLiteral(DataType.BYTE, value, func.position)
NumericLiteral(BaseDataType.BYTE, value, func.position)
}
"cosr8" -> {
val value = truncate(127.0 * cos(args.single().number / 180.0 * 2 * PI))
NumericLiteral(DataType.BYTE, value, func.position)
NumericLiteral(BaseDataType.BYTE, value, func.position)
}
"log2" -> {
val value = truncate(log2(args.single().number))
NumericLiteral(DataType.UBYTE, value, func.position)
NumericLiteral(BaseDataType.UBYTE, value, func.position)
}
"log2w" -> {
val value = truncate(log2(args.single().number))
NumericLiteral(DataType.UWORD, value, func.position)
NumericLiteral(BaseDataType.UWORD, value, func.position)
}
"atan2" -> {
val x1f = args[0].number
@@ -343,19 +344,19 @@ class ConstExprEvaluator {
var radians = atan2(y2f-y1f, x2f-x1f)
if(radians<0)
radians+=2*PI
NumericLiteral(DataType.UWORD, floor(radians/2.0/PI*256.0), func.position)
NumericLiteral(BaseDataType.UWORD, floor(radians/2.0/PI*256.0), func.position)
}
"diff" -> {
val n1 = args[0].number
val n2 = args[1].number
val value = if(n1>n2) n1-n2 else n2-n1
NumericLiteral(DataType.UBYTE, value, func.position)
NumericLiteral(BaseDataType.UBYTE, value, func.position)
}
"diffw" -> {
val n1 = args[0].number
val n2 = args[1].number
val value = if(n1>n2) n1-n2 else n2-n1
NumericLiteral(DataType.UWORD, value, func.position)
NumericLiteral(BaseDataType.UWORD, value, func.position)
}
else -> null
}
@@ -9,7 +9,7 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.AssociativeOperators
import prog8.code.core.DataType
import prog8.code.core.BaseDataType
import prog8.code.core.IErrorReporter
import kotlin.math.floor
@@ -35,17 +35,18 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
override fun after(numLiteral: NumericLiteral, parent: Node): Iterable<IAstModification> {
if(numLiteral.type==DataType.LONG) {
if(numLiteral.type==BaseDataType.LONG) {
// see if LONG values may be reduced to something smaller
val smaller = NumericLiteral.optimalInteger(numLiteral.number.toInt(), numLiteral.position)
if(smaller.type!=DataType.LONG)
if(smaller.type!=BaseDataType.LONG) {
return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent))
}
}
if(parent is Assignment) {
val iDt = parent.target.inferType(program)
if(iDt.isKnown && !iDt.isBool && !iDt.istype(numLiteral.type)) {
val casted = numLiteral.cast(iDt.getOr(DataType.UNDEFINED), true)
if(iDt.isKnown && !iDt.isBool && !(iDt issimpletype numLiteral.type)) {
val casted = numLiteral.cast(iDt.getOrUndef().base, true)
if(casted.isValid) {
return listOf(IAstModification.ReplaceNode(numLiteral, casted.valueOrZero(), parent))
}
@@ -88,7 +89,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val leftconst = expr.left.constValue(program)
val rightconst = expr.right.constValue(program)
if(expr.left.inferType(program) istype DataType.STR) {
if(expr.left.inferType(program).isStringLy) {
if(expr.operator=="+" && expr.left is StringLiteral && expr.right is StringLiteral) {
// concatenate two strings.
val leftString = expr.left as StringLiteral
@@ -163,7 +164,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
}
}
if(expr.inferType(program) istype DataType.FLOAT) {
if(expr.inferType(program) issimpletype BaseDataType.FLOAT) {
val subExpr: BinaryExpression? = when {
leftconst != null -> expr.right as? BinaryExpression
rightconst != null -> expr.left as? BinaryExpression
@@ -234,7 +235,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val newExpr = BinaryExpression(leftBinExpr.left, "*", constants, expr.position)
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
} else if (leftBinExpr.operator=="/") {
if(expr.inferType(program).istype(DataType.FLOAT)) {
if(expr.inferType(program) issimpletype BaseDataType.FLOAT) {
// (X / C2) * rightConst --> X * (rightConst/C2) only valid for floating point
val constants = BinaryExpression(rightconst, "/", c2, c2.position)
val newExpr = BinaryExpression(leftBinExpr.left, "*", constants, expr.position)
@@ -312,7 +313,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
} else {
val arrayDt = array.guessDatatype(program)
if (arrayDt.isKnown) {
val newArray = array.cast(arrayDt.getOr(DataType.UNDEFINED))
val newArray = array.cast(arrayDt.getOrUndef())
if (newArray != null && newArray != array)
return listOf(IAstModification.ReplaceNode(array, newArray, parent))
}
@@ -359,16 +360,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
}
}
override fun after(bfc: BuiltinFunctionCall, parent: Node): Iterable<IAstModification> {
val constvalue = bfc.constValue(program)
return if(constvalue!=null)
listOf(IAstModification.ReplaceNode(bfc, constvalue, parent))
else
noModifications
}
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: DataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? {
fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: BaseDataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? {
val fromCast = rangeFrom.cast(targetDt, true)
val toCast = rangeTo.cast(targetDt, true)
if(!fromCast.isValid || !toCast.isValid)
@@ -397,35 +390,37 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: return noModifications
val stepLiteral = iterableRange.step as? NumericLiteral
when(loopvar.datatype) {
DataType.UBYTE -> {
if(rangeFrom.type!= DataType.UBYTE) {
require(loopvar.datatype.sub == null)
val loopvarSimpleDt = loopvar.datatype.base
when(loopvarSimpleDt) {
BaseDataType.UBYTE -> {
if(rangeFrom.type != BaseDataType.UBYTE) {
// attempt to translate the iterable into ubyte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
val newIter = adjustRangeDt(rangeFrom, loopvarSimpleDt, rangeTo, stepLiteral, iterableRange)
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
DataType.BYTE -> {
if(rangeFrom.type!= DataType.BYTE) {
BaseDataType.BYTE -> {
if(rangeFrom.type != BaseDataType.BYTE) {
// attempt to translate the iterable into byte values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
val newIter = adjustRangeDt(rangeFrom, loopvarSimpleDt, rangeTo, stepLiteral, iterableRange)
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
DataType.UWORD -> {
if(rangeFrom.type!= DataType.UWORD) {
BaseDataType.UWORD -> {
if(rangeFrom.type != BaseDataType.UWORD) {
// attempt to translate the iterable into uword values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
val newIter = adjustRangeDt(rangeFrom, loopvarSimpleDt, rangeTo, stepLiteral, iterableRange)
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
}
DataType.WORD -> {
if(rangeFrom.type!= DataType.WORD) {
BaseDataType.WORD -> {
if(rangeFrom.type != BaseDataType.WORD) {
// attempt to translate the iterable into word values
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
val newIter = adjustRangeDt(rangeFrom, loopvarSimpleDt, rangeTo, stepLiteral, iterableRange)
if(newIter!=null)
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
}
@@ -440,13 +435,14 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val numval = decl.value as? NumericLiteral
if(decl.type== VarDeclType.CONST && numval!=null) {
val valueDt = numval.inferType(program)
if(valueDt istype DataType.LONG) {
if(valueDt issimpletype BaseDataType.LONG) {
return noModifications // this is handled in the numericalvalue case
}
if(valueDt isnot decl.datatype) {
val cast = numval.cast(decl.datatype, true)
if (cast.isValid)
if(!(valueDt istype decl.datatype)) {
val cast = numval.cast(decl.datatype.base, true)
if (cast.isValid) {
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
}
}
}
return noModifications
@@ -467,6 +463,18 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
return listOf(IAstModification.ReplaceNode(typecast, constValue, parent))
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
val address = subroutine.asmAddress?.address
if(address!=null) {
val constAddress = address.constValue(program)
if(constAddress!=null) {
subroutine.asmAddress!!.address = constAddress
constAddress.parent = subroutine
}
}
return noModifications
}
private class ShuffleOperands(val expr: BinaryExpression,
val exprOperator: String?,
val subExpr: BinaryExpression,
@@ -575,7 +583,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
return IAstModification.ReplaceNode(expr, change, expr.parent)
}
}
else if(expr.operator=="*" && subExpr.operator=="/" && subExpr.inferType(program).istype(DataType.FLOAT)) {
else if(expr.operator=="*" && subExpr.operator=="/" && subExpr.inferType(program) issimpletype BaseDataType.FLOAT) {
// division optimizations only valid for floats
if(leftIsConst) {
val change = if(subleftIsConst) {
@@ -31,16 +31,10 @@ class VarConstantValueTypeAdjuster(
try {
val declConstValue = decl.value?.constValue(program)
if(declConstValue!=null && (decl.type== VarDeclType.VAR || decl.type==VarDeclType.CONST)
&& declConstValue.type != decl.datatype) {
// avoid silent float roundings
if(decl.datatype in IntegerDatatypes && declConstValue.type == DataType.FLOAT) {
&& declConstValue.type != decl.datatype.base) {
if(decl.datatype.isInteger && declConstValue.type == BaseDataType.FLOAT) {
// avoid silent float roundings
errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position)
} else if(decl.datatype!=DataType.BOOL) {
// cast the numeric literal to the appropriate datatype of the variable if it's not boolean
declConstValue.linkParents(decl)
val cast = declConstValue.cast(decl.datatype, true)
if (cast.isValid)
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
}
}
} catch (x: UndefinedSymbolError) {
@@ -49,7 +43,7 @@ class VarConstantValueTypeAdjuster(
// replace variables by constants, if possible
if(options.optimize) {
if (decl.sharedWithAsm || decl.type != VarDeclType.VAR || decl.origin != VarDeclOrigin.USERCODE || decl.datatype !in NumericDatatypes)
if (decl.sharedWithAsm || decl.type != VarDeclType.VAR || decl.origin != VarDeclOrigin.USERCODE || !decl.datatype.isNumeric)
return noModifications
if (decl.value != null && decl.value!!.constValue(program) == null)
return noModifications
@@ -76,7 +70,7 @@ class VarConstantValueTypeAdjuster(
if (declValue != null) {
// variable is never written to, so it can be replaced with a constant, IF the value is a constant
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.splitArray, decl.alignment, decl.dirty, decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.name, decl.names, declValue, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.position)
decl.value = null
return listOf(
IAstModification.ReplaceNode(decl, const, parent)
@@ -94,9 +88,9 @@ class VarConstantValueTypeAdjuster(
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
)
}
// variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
// variable only has a single write, and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.alignment, decl.dirty, decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.position)
return listOf(
IAstModification.ReplaceNode(decl, const, parent),
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
@@ -155,9 +149,9 @@ class VarConstantValueTypeAdjuster(
val t1 = functionCallExpr.args[0].inferType(program)
if(t1.isKnown) {
val replaceFunc = if(t1.isBytes) {
if(t1.istype(DataType.BYTE)) "clamp__byte" else "clamp__ubyte"
if(t1 issimpletype BaseDataType.BYTE) "clamp__byte" else "clamp__ubyte"
} else if(t1.isInteger) {
if(t1.istype(DataType.WORD)) "clamp__word" else "clamp__uword"
if(t1 issimpletype BaseDataType.WORD) "clamp__word" else "clamp__uword"
} else {
errors.err("clamp builtin not supported for floats, use floats.clamp", functionCallExpr.position)
return noModifications
@@ -174,12 +168,12 @@ class VarConstantValueTypeAdjuster(
val funcName = func[0]
val replaceFunc: String
if(t1.isBytes && t2.isBytes) {
replaceFunc = if(t1.istype(DataType.BYTE) || t2.istype(DataType.BYTE))
replaceFunc = if(t1 issimpletype BaseDataType.BYTE || t2 issimpletype BaseDataType.BYTE)
"${funcName}__byte"
else
"${funcName}__ubyte"
} else if(t1.isInteger && t2.isInteger) {
replaceFunc = if(t1.istype(DataType.WORD) || t2.istype(DataType.WORD))
replaceFunc = if(t1 issimpletype BaseDataType.WORD || t2 issimpletype BaseDataType.WORD)
"${funcName}__word"
else
"${funcName}__uword"
@@ -199,11 +193,11 @@ class VarConstantValueTypeAdjuster(
val t1 = functionCallExpr.args[0].inferType(program)
if(t1.isKnown) {
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
val replaceFunc = when(dt) {
DataType.BYTE -> "abs__byte"
DataType.WORD -> "abs__word"
DataType.FLOAT -> "abs__float"
DataType.UBYTE, DataType.UWORD -> {
val replaceFunc = when {
dt.isSignedByte -> "abs__byte"
dt.isSignedWord -> "abs__word"
dt.isFloat -> "abs__float"
dt.isUnsignedByte || dt.isUnsignedWord -> {
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
}
else -> {
@@ -220,10 +214,10 @@ class VarConstantValueTypeAdjuster(
val t1 = functionCallExpr.args[0].inferType(program)
if(t1.isKnown) {
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
val replaceFunc = when(dt) {
DataType.UBYTE -> "sqrt__ubyte"
DataType.UWORD -> "sqrt__uword"
DataType.FLOAT -> "sqrt__float"
val replaceFunc = when {
dt.isUnsignedByte -> "sqrt__ubyte"
dt.isUnsignedWord -> "sqrt__uword"
dt.isFloat -> "sqrt__float"
else -> {
errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position)
return noModifications
@@ -249,9 +243,9 @@ class VarConstantValueTypeAdjuster(
val t1 = argTypes.single()
if(t1.isKnown) {
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
val replaceFunc = when(dt) {
DataType.UBYTE -> "divmod__ubyte"
DataType.UWORD -> "divmod__uword"
val replaceFunc = when {
dt.isUnsignedByte -> "divmod__ubyte"
dt.isUnsignedWord -> "divmod__uword"
else -> {
errors.err("expected all ubyte or all uword arguments", functionCallStatement.args[0].position)
return noModifications
@@ -300,7 +294,7 @@ internal class ConstantIdentifierReplacer(
try {
val cval = identifier.constValue(program) ?: return noModifications
val arrayIdx = identifier.parent as? ArrayIndexedExpression
if(arrayIdx!=null && cval.type in NumericDatatypes) {
if(arrayIdx!=null && cval.type.isNumeric) {
// special case when the identifier is used as a pointer var
// var = constpointer[x] --> var = @(constvalue+x) [directmemoryread]
// constpointer[x] = var -> @(constvalue+x) [directmemorywrite] = var
@@ -314,8 +308,8 @@ internal class ConstantIdentifierReplacer(
listOf(IAstModification.ReplaceNode(arrayIdx, memread, arrayIdx.parent))
}
}
when (cval.type) {
in NumericDatatypesWithBoolean -> {
when {
cval.type.isNumericOrBool -> {
if(parent is AddressOf)
return noModifications // cannot replace the identifier INSIDE the addr-of here, let's do it later.
return listOf(
@@ -326,7 +320,7 @@ internal class ConstantIdentifierReplacer(
)
)
}
in PassByReferenceDatatypes -> throw InternalCompilerException("pass-by-reference type should not be considered a constant")
cval.type.isPassByRef -> throw InternalCompilerException("pass-by-reference type should not be considered a constant")
else -> return noModifications
}
} catch (x: UndefinedSymbolError) {
@@ -367,16 +361,16 @@ internal class ConstantIdentifierReplacer(
}
}
when(decl.datatype) {
DataType.FLOAT -> {
when {
decl.datatype.isFloat -> {
// vardecl: for scalar float vars, promote constant integer initialization values to floats
val litval = decl.value as? NumericLiteral
if (litval!=null && litval.type in IntegerDatatypesWithBoolean) {
val newValue = NumericLiteral(DataType.FLOAT, litval.number, litval.position)
if (litval!=null && litval.type.isIntegerOrBool) {
val newValue = NumericLiteral(BaseDataType.FLOAT, litval.number, litval.position)
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
}
}
in ArrayDatatypes -> {
decl.datatype.isArray -> {
val replacedArrayInitializer = createConstArrayInitializerValue(decl)
if(replacedArrayInitializer!=null)
return listOf(IAstModification.ReplaceNode(decl.value!!, replacedArrayInitializer, decl))
@@ -396,9 +390,9 @@ internal class ConstantIdentifierReplacer(
if(range!=null) {
val targetDatatype = assignment.target.inferType(program)
if(targetDatatype.isArray) {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, targetDatatype.getOr(DataType.UNDEFINED),
ZeropageWish.DONTCARE, null, "dummy", emptyList(),
assignment.value, false, false, 0u, false, Position.DUMMY)
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, targetDatatype.getOrUndef(),
ZeropageWish.DONTCARE, SplitWish.NOSPLIT, null, "dummy", emptyList(),
assignment.value, false, 0u, false, Position.DUMMY)
val replaceValue = createConstArrayInitializerValue(decl)
if(replaceValue!=null) {
return listOf(IAstModification.ReplaceNode(assignment.value, replaceValue, assignment))
@@ -430,29 +424,31 @@ internal class ConstantIdentifierReplacer(
else if(constRange.first<constRange.last && constRange.step<=0)
errors.err("ascending range with negative step", decl.value?.position!!)
}
when(decl.datatype) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT -> {
val dt = decl.datatype
when {
dt.isUnsignedByteArray || dt.isSignedByteArray || dt.isUnsignedWordArray || dt.isSignedWordArray -> {
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 eltType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
return if(eltType in ByteDatatypes) {
val rangeType = rangeExpr.inferType(program).getOr(DataType.forDt(BaseDataType.UBYTE))
return if(rangeType.isByte) {
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
constRange.map { NumericLiteral(rangeType.base, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
} else {
require(rangeType.sub!=null)
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
constRange.map { NumericLiteral(rangeType.sub!!.dt, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
}
}
}
DataType.ARRAY_F -> {
dt.isFloatArray -> {
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) {
return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F),
constRange.map { NumericLiteral(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
return ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.FLOAT, false)),
constRange.map { NumericLiteral(BaseDataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
}
}
@@ -30,11 +30,11 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val subTypecast = typecast.expression as? TypecastExpression
if (subTypecast != null) {
// remove the sub-typecast if its datatype is larger than the outer typecast
if(subTypecast.type largerThan typecast.type) {
if(subTypecast.type.largerSizeThan(typecast.type)) {
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
}
} else {
if (typecast.expression.inferType(program) istype typecast.type) {
if (typecast.expression.inferType(program) issimpletype typecast.type) {
// remove duplicate cast
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
}
@@ -67,19 +67,19 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val booleanCondition = ifElse.condition as? BinaryExpression
if(booleanCondition!=null && booleanCondition.operator=="&") {
val rightNum = booleanCondition.right as? NumericLiteral
if (rightNum!=null && rightNum.type==DataType.UWORD) {
if (rightNum!=null && rightNum.type==BaseDataType.UWORD) {
if ((rightNum.number.toInt() and 0x00ff) == 0) {
// if WORD & $xx00 -> if msb(WORD) & $xx
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
val bytevalue = NumericLiteral(DataType.UBYTE, (rightNum.number.toInt() shr 8).toDouble(), booleanCondition.right.position)
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
val bytevalue = NumericLiteral(BaseDataType.UBYTE, (rightNum.number.toInt() shr 8).toDouble(), booleanCondition.right.position)
return listOf(
IAstModification.ReplaceNode(booleanCondition.left, msb, booleanCondition),
IAstModification.ReplaceNode(booleanCondition.right, bytevalue, booleanCondition))
}
else if ((rightNum.number.toInt() and 0xff00) == 0) {
// if WORD & $00ff -> if lsb(WORD) & $ff
val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
val bytevalue = NumericLiteral(DataType.UBYTE, rightNum.number, booleanCondition.right.position)
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), booleanCondition.left.position), mutableListOf(booleanCondition.left), booleanCondition.left.position)
val bytevalue = NumericLiteral(BaseDataType.UBYTE, rightNum.number, booleanCondition.right.position)
return listOf(
IAstModification.ReplaceNode(booleanCondition.left, lsb, booleanCondition),
IAstModification.ReplaceNode(booleanCondition.right, bytevalue, booleanCondition))
@@ -130,12 +130,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
))
}
val leftDt = leftIDt.getOr(DataType.UNDEFINED)
val rightDt = rightIDt.getOr(DataType.UNDEFINED)
val leftDt = leftIDt.getOrUndef()
val rightDt = rightIDt.getOrUndef()
if (expr.operator == "+" || expr.operator == "-"
&& leftVal == null && rightVal == null
&& leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
&& leftDt.isNumeric && rightDt.isNumeric) {
val leftBinExpr = expr.left as? BinaryExpression
val rightBinExpr = expr.right as? BinaryExpression
if (leftBinExpr?.operator == "*") {
@@ -145,7 +145,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val x = expr.right
val y = determineY(x, leftBinExpr)
if (y != null) {
val yPlus1 = BinaryExpression(y, "+", NumericLiteral(leftDt, 1.0, y.position), y.position)
val yPlus1 = BinaryExpression(y, "+", NumericLiteral(leftDt.base, 1.0, y.position), y.position)
val replacement = BinaryExpression(x, "*", yPlus1, x.position)
return listOf(IAstModification.ReplaceNode(expr, replacement, parent))
}
@@ -155,7 +155,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val x = expr.right
val y = determineY(x, leftBinExpr)
if (y != null) {
val yMinus1 = BinaryExpression(y, "-", NumericLiteral(leftDt, 1.0, y.position), y.position)
val yMinus1 = BinaryExpression(y, "-", NumericLiteral(leftDt.base, 1.0, y.position), y.position)
val replacement = BinaryExpression(x, "*", yMinus1, x.position)
return listOf(IAstModification.ReplaceNode(expr, replacement, parent))
}
@@ -176,7 +176,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
// X <= Y-1 ---> X<Y , X >= Y+1 ---> X>Y
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
if(leftDt.isInteger && rightDt.isInteger) {
val rightExpr = expr.right as? BinaryExpression
if(rightExpr!=null && rightExpr.right.constValue(program)?.number==1.0) {
if (expr.operator == "<=" && rightExpr.operator == "-") {
@@ -189,24 +189,24 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
}
if(leftDt!=DataType.FLOAT && expr.operator == ">=" && rightVal?.number == 1.0) {
if(!leftDt.isFloat && expr.operator == ">=" && rightVal?.number == 1.0) {
// for integers: x >= 1 --> x > 0
expr.operator = ">"
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral.optimalInteger(0, expr.right.position), expr))
}
// for signed integers: X <= -1 => X<0 , X > -1 => X>=0
if(leftDt in SignedDatatypes && leftDt!=DataType.FLOAT && rightVal?.number==-1.0) {
if(leftDt.isSigned && !leftDt.isFloat && rightVal?.number==-1.0) {
if(expr.operator=="<=") {
expr.operator = "<"
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral(rightDt, 0.0, expr.right.position), expr))
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral(rightDt.base, 0.0, expr.right.position), expr))
} else if(expr.operator==">") {
expr.operator = ">="
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral(rightDt, 0.0, expr.right.position), expr))
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral(rightDt.base, 0.0, expr.right.position), expr))
}
}
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
if (leftDt.isUnsignedByte || leftDt.isUnsignedWord) {
if(expr.operator == ">=" && rightVal?.number == 0.0) {
// unsigned >= 0 --> true
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(true, expr.position), parent))
@@ -217,13 +217,13 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
}
if(leftDt!=DataType.FLOAT && expr.operator == "<" && rightVal?.number == 1.0) {
if(!leftDt.isFloat && expr.operator == "<" && rightVal?.number == 1.0) {
// for integers: x < 1 --> x <= 0
expr.operator = "<="
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral.optimalInteger(0, expr.right.position), expr))
}
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
if (leftDt.isUnsignedByte || leftDt.isUnsignedWord) {
if(expr.operator == "<" && rightVal?.number == 0.0) {
// unsigned < 0 --> false
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(false, expr.position), parent))
@@ -236,7 +236,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
// optimize boolean constant comparisons
if(expr.operator=="==") {
if(rightDt==DataType.BOOL && leftDt==DataType.BOOL) {
if(rightDt.isBool && leftDt.isBool) {
val rightConstBool = rightVal?.asBooleanValue
if(rightConstBool==true) {
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
@@ -244,19 +244,19 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
if (rightVal?.number == 1.0) {
if (rightDt != leftDt) {
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}
else if (rightVal?.number == 0.0) {
if (rightDt != leftDt) {
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}
}
if (expr.operator=="!=") {
if(rightDt==DataType.BOOL && leftDt==DataType.BOOL) {
if(rightDt.isBool && leftDt.isBool) {
val rightConstBool = rightVal?.asBooleanValue
if(rightConstBool==false) {
listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
@@ -264,13 +264,13 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
if (rightVal?.number == 1.0) {
if(rightDt!=leftDt) {
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}
else if (rightVal?.number == 0.0) {
if(rightDt!=leftDt) {
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
}
}
@@ -322,10 +322,10 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
when {
leftVal?.number==0.0 -> expr.right
rightVal?.number==0.0 -> expr.left
rightIDt.isBytes && rightVal?.number==255.0 -> NumericLiteral(DataType.UBYTE, 255.0, rightVal.position)
rightIDt.isWords && rightVal?.number==65535.0 -> NumericLiteral(DataType.UWORD, 65535.0, rightVal.position)
leftIDt.isBytes && leftVal?.number==255.0 -> NumericLiteral(DataType.UBYTE, 255.0, leftVal.position)
leftIDt.isWords && leftVal?.number==65535.0 -> NumericLiteral(DataType.UWORD, 65535.0, leftVal.position)
rightIDt.isBytes && rightVal?.number==255.0 -> NumericLiteral(BaseDataType.UBYTE, 255.0, rightVal.position)
rightIDt.isWords && rightVal?.number==65535.0 -> NumericLiteral(BaseDataType.UWORD, 65535.0, rightVal.position)
leftIDt.isBytes && leftVal?.number==255.0 -> NumericLiteral(BaseDataType.UBYTE, 255.0, leftVal.position)
leftIDt.isWords && leftVal?.number==65535.0 -> NumericLiteral(BaseDataType.UWORD, 65535.0, leftVal.position)
else -> null
}
}
@@ -361,7 +361,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
else -> null
}
if(rightVal!=null && leftDt==DataType.BOOL) {
if(rightVal!=null && leftDt.isBool) {
// boolean compare against a number -> just keep the boolean, no compare
if(expr.operator=="==" || expr.operator=="!=") {
val test = if (expr.operator == "==") rightVal.asBooleanValue else !rightVal.asBooleanValue
@@ -378,30 +378,44 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
if (rightVal!=null && (expr.operator == "==" || expr.operator == "!=")) {
val bitwise = expr.left as? BinaryExpression
if(bitwise!=null && bitwise.operator=="&" && bitwise.inferType(program).isWords) {
val andNum = (bitwise.right as? NumericLiteral)?.number?.toInt()
if (andNum!=null) {
if ((andNum and 0x00ff) == 0) {
// (WORD & $xx00)==y -> (msb(WORD) & $xx)==y
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
val bytevalue = NumericLiteral(DataType.UBYTE, (andNum shr 8).toDouble(), bitwise.right.position)
val rightvalByte = NumericLiteral(DataType.UBYTE, (rightVal.number.toInt() shr 8).toDouble(), rightVal.position)
return listOf(
IAstModification.ReplaceNode(bitwise.left, msb, bitwise),
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
)
if (bitwise!=null && bitwise.operator=="&") {
if(rightVal.number in powersOfTwoFloat && bitwise.right.constValue(program) == rightVal) {
if(expr.operator=="==") {
// x & const == const -> x & const != 0 (if const has only a single bit set!)
val notZero = BinaryExpression(bitwise, "!=", NumericLiteral(rightDt.base, 0.0, rightVal.position), expr.position)
return listOf(IAstModification.ReplaceNode(expr, notZero, parent))
} else {
// x & const != const -> x & const == 0 (if const has only a single bit set!)
val equalsZero = BinaryExpression(bitwise, "==", NumericLiteral(rightDt.base, 0.0, rightVal.position), expr.position)
return listOf(IAstModification.ReplaceNode(expr, equalsZero, parent))
}
else if((andNum and 0xff00) == 0) {
// (WORD & $00xx)==y -> (lsb(WORD) & $xx)==y
val lsb = BuiltinFunctionCall(IdentifierReference(listOf("lsb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
val bytevalue = NumericLiteral(DataType.UBYTE, andNum.toDouble(), bitwise.right.position)
val rightvalByte = NumericLiteral(DataType.UBYTE, (rightVal.number.toInt() and 255).toDouble(), rightVal.position)
return listOf(
IAstModification.ReplaceNode(bitwise.left, lsb, bitwise),
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
)
}
if (bitwise.inferType(program).isWords) {
val andNum = (bitwise.right as? NumericLiteral)?.number?.toInt()
if (andNum!=null) {
if ((andNum and 0x00ff) == 0) {
// (WORD & $xx00)==y -> (msb(WORD) & $xx)==y
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
val bytevalue = NumericLiteral(BaseDataType.UBYTE, (andNum shr 8).toDouble(), bitwise.right.position)
val rightvalByte = NumericLiteral(BaseDataType.UBYTE, (rightVal.number.toInt() shr 8).toDouble(), rightVal.position)
return listOf(
IAstModification.ReplaceNode(bitwise.left, msb, bitwise),
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
)
} else if((andNum and 0xff00) == 0) {
// (WORD & $00xx)==y -> (lsb(WORD) & $xx)==y
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), bitwise.left.position), mutableListOf(bitwise.left), bitwise.left.position)
val bytevalue = NumericLiteral(BaseDataType.UBYTE, andNum.toDouble(), bitwise.right.position)
val rightvalByte = NumericLiteral(BaseDataType.UBYTE, (rightVal.number.toInt() and 255).toDouble(), rightVal.position)
return listOf(
IAstModification.ReplaceNode(bitwise.left, lsb, bitwise),
IAstModification.ReplaceNode(bitwise.right, bytevalue, bitwise),
IAstModification.ReplaceNode(expr.right, rightvalByte, expr)
)
}
}
}
}
@@ -480,13 +494,13 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val arg = functionCallExpr.args[0]
if(arg is TypecastExpression) {
val valueDt = arg.expression.inferType(program)
if (valueDt istype DataType.UBYTE) {
if (valueDt issimpletype BaseDataType.UBYTE) {
// useless lsb() of ubyte value
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg.expression, parent))
}
else if (valueDt istype DataType.BYTE) {
else if (valueDt issimpletype BaseDataType.BYTE) {
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
val cast = TypecastExpression(arg.expression, DataType.UBYTE, true, arg.position)
val cast = TypecastExpression(arg.expression, BaseDataType.UBYTE, true, arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
} else {
@@ -497,13 +511,13 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return listOf(IAstModification.ReplaceNode(functionCallExpr, highReg, parent))
}
val argDt = arg.inferType(program)
if (argDt istype DataType.UBYTE) {
if (argDt issimpletype BaseDataType.UBYTE) {
// useless lsb() of byte value
return listOf(IAstModification.ReplaceNode(functionCallExpr, arg, parent))
}
else if (argDt istype DataType.BYTE) {
else if (argDt issimpletype BaseDataType.BYTE) {
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
val cast = TypecastExpression(arg, DataType.UBYTE, true, arg.position)
val cast = TypecastExpression(arg, BaseDataType.UBYTE, true, arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
}
@@ -514,11 +528,11 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val arg = functionCallExpr.args[0]
if(arg is TypecastExpression) {
val valueDt = arg.expression.inferType(program)
if (valueDt istype DataType.BYTE || valueDt istype DataType.UBYTE) {
if (valueDt issimpletype BaseDataType.BYTE || valueDt issimpletype BaseDataType.UBYTE) {
// useless msb() of byte value that was typecasted to word, replace with 0
return listOf(IAstModification.ReplaceNode(
functionCallExpr,
NumericLiteral(valueDt.getOr(DataType.UBYTE), 0.0, arg.expression.position),
NumericLiteral(valueDt.getOr(DataType.forDt(BaseDataType.UBYTE)).base, 0.0, arg.expression.position),
parent))
}
} else {
@@ -529,11 +543,11 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return listOf(IAstModification.ReplaceNode(functionCallExpr, highReg, parent))
}
val argDt = arg.inferType(program)
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
if (argDt issimpletype BaseDataType.BYTE || argDt issimpletype BaseDataType.UBYTE) {
// useless msb() of byte value, replace with 0
return listOf(IAstModification.ReplaceNode(
functionCallExpr,
NumericLiteral(argDt.getOr(DataType.UBYTE), 0.0, arg.position),
NumericLiteral(argDt.getOr(DataType.forDt(BaseDataType.UBYTE)).base, 0.0, arg.position),
parent))
}
}
@@ -542,11 +556,11 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
if(functionCallExpr.target.nameInSource == listOf("mkword")) {
if(functionCallExpr.args[0].constValue(program)?.number==0.0) {
// just cast the lsb to uword
val cast = TypecastExpression(functionCallExpr.args[1], DataType.UWORD, true, functionCallExpr.position)
val cast = TypecastExpression(functionCallExpr.args[1], BaseDataType.UWORD, true, functionCallExpr.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
}
}
else if(functionCallExpr.target.nameInSource == listOf("string", "contains")) {
else if(functionCallExpr.target.nameInSource == listOf("strings", "contains")) {
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl(program)
if(target?.value is StringLiteral) {
errors.info("for actual strings, use a regular containment check instead: 'char in string'", functionCallExpr.position)
@@ -651,7 +665,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val idt = expr.inferType(program)
if(!idt.isKnown)
throw FatalAstException("unknown dt")
return NumericLiteral(idt.getOr(DataType.UNDEFINED), 0.0, expr.position)
return NumericLiteral(idt.getOrUndef().base, 0.0, expr.position)
} else if (cv in powersOfTwoFloat) {
expr.operator = "&"
expr.right = NumericLiteral.optimalInteger(cv!!.toInt()-1, expr.position)
@@ -675,7 +689,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val leftIDt = expr.left.inferType(program)
if (!leftIDt.isKnown)
return null
val leftDt = leftIDt.getOr(DataType.UNDEFINED)
val leftDt = leftIDt.getOrUndef()
when (cv) {
0.0 -> return null // fall through to regular float division to properly deal with division by zero
-1.0 -> {
@@ -691,36 +705,36 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
}
256.0 -> {
when(leftDt) {
DataType.UBYTE -> return NumericLiteral(DataType.UBYTE, 0.0, expr.position)
DataType.BYTE -> return null // is either 0 or -1 we cannot tell here
DataType.UWORD, DataType.WORD -> {
when(leftDt.base) {
BaseDataType.UBYTE -> return NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)
BaseDataType.BYTE -> return null // is either 0 or -1 we cannot tell here
BaseDataType.UWORD, BaseDataType.WORD -> {
// just use: msb(value) as type
val msb = BuiltinFunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return if(leftDt==DataType.WORD)
TypecastExpression(msb, DataType.BYTE, true, expr.position)
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return if(leftDt.isSignedWord)
TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
else
TypecastExpression(msb, DataType.UWORD, true, expr.position)
TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
}
else -> return null
}
}
in powersOfTwoFloat -> {
val numshifts = powersOfTwoFloat.indexOf(cv)
if (leftDt in IntegerDatatypes) {
if (leftDt.isInteger) {
// division by a power of two => shift right (signed and unsigned)
return BinaryExpression(expr.left, ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
}
}
}
if (leftDt == DataType.UBYTE) {
if (leftDt.isUnsignedByte) {
if (abs(rightConst.number) >= 256.0) {
return NumericLiteral(DataType.UBYTE, 0.0, expr.position)
return NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)
}
} else if (leftDt == DataType.UWORD) {
} else if (leftDt.isUnsignedWord) {
if (abs(rightConst.number) >= 65536.0) {
return NumericLiteral(DataType.UBYTE, 0.0, expr.position)
return NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)
}
}
}
@@ -793,22 +807,22 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val targetIDt = expr.left.inferType(program)
if(!targetIDt.isKnown)
throw FatalAstException("unknown dt")
when (val targetDt = targetIDt.getOr(DataType.UNDEFINED)) {
DataType.UBYTE, DataType.BYTE -> {
when (val targetDt = targetIDt.getOrUndef().base) {
BaseDataType.UBYTE, BaseDataType.BYTE -> {
if (amount >= 8) {
errors.warn("shift always results in 0", expr.position)
errors.info("shift always results in 0", expr.position)
return NumericLiteral(targetDt, 0.0, expr.position)
}
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
if (amount >= 16) {
errors.warn("shift always results in 0", expr.position)
errors.info("shift always results in 0", expr.position)
return NumericLiteral(targetDt, 0.0, expr.position)
}
else if(amount==8) {
// shift left by 8 bits is just a byte operation: mkword(lsb(X), 0)
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(DataType.UBYTE, 0.0, expr.position)), expr.position)
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)), expr.position)
}
else if (amount > 8) {
// same as above but with residual shifts.
@@ -817,23 +831,23 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
}
}
DataType.WORD -> {
BaseDataType.WORD -> {
if (amount >= 16) {
errors.warn("shift always results in 0", expr.position)
errors.info("shift always results in 0", expr.position)
return NumericLiteral(targetDt, 0.0, expr.position)
}
else if(amount==8) {
// shift left by 8 bits is just a byte operation: mkword(lsb(X), 0)
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(DataType.UBYTE, 0.0, expr.position)), expr.position)
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)), expr.position)
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
}
else if (amount > 8) {
// same as above but with residual shifts.
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
}
}
else -> {
@@ -853,20 +867,20 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val idt = expr.left.inferType(program)
if(!idt.isKnown)
throw FatalAstException("unknown dt")
when (idt.getOr(DataType.UNDEFINED)) {
DataType.UBYTE -> {
when (idt.getOrUndef().base) {
BaseDataType.UBYTE -> {
if (amount >= 8) {
errors.warn("shift always results in 0", expr.position)
errors.info("shift always results in 0", expr.position)
return NumericLiteral.optimalInteger(0, expr.position)
}
}
DataType.BYTE -> {
BaseDataType.BYTE -> {
if (amount > 8) {
expr.right = NumericLiteral.optimalInteger(8, expr.right.position)
return null
}
}
DataType.UWORD -> {
BaseDataType.UWORD -> {
if (amount >= 16) {
errors.err("useless to shift by more than 15 bits", expr.position)
return null
@@ -874,15 +888,15 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
else if(amount==8) {
// shift right by 8 bits is just a byte operation: msb(X) as uword
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(msb, DataType.UWORD, true, expr.position)
return TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
}
else if (amount > 8) {
// same as above but with residual shifts.
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), BaseDataType.UWORD, true, expr.position)
}
}
DataType.WORD -> {
BaseDataType.WORD -> {
if (amount >= 16) {
errors.err("useless to shift by more than 15 bits", expr.position)
return null
@@ -890,12 +904,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
else if(amount == 8) {
// shift right by 8 bits is just a byte operation: msb(X) as byte (will get converted to word later)
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(msb, DataType.BYTE, true, expr.position)
return TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
}
else if(amount > 8) {
// same as above but with residual shifts. Take care to do signed shift.
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
val signed = TypecastExpression(msb, DataType.BYTE, true, expr.position)
val signed = TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
return BinaryExpression(signed, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
}
}
+1 -33
View File
@@ -48,11 +48,6 @@ class Inliner(private val program: Program, private val options: CompilationOpti
true
} else if (stmt.value!! is IFunctionCall && (stmt.value as IFunctionCall).args.size <= 1 && (stmt.value as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
when (stmt.value) {
is BuiltinFunctionCall -> {
makeFullyScoped(stmt.value as BuiltinFunctionCall)
true
}
is FunctionCallExpression -> {
makeFullyScoped(stmt.value as FunctionCallExpression)
true
@@ -99,14 +94,6 @@ class Inliner(private val program: Program, private val options: CompilationOpti
false
}
is BuiltinFunctionCallStatement -> {
val inline =
stmt.args.size <= 1 && stmt.args.all { it is NumericLiteral || it is IdentifierReference }
if (inline)
makeFullyScoped(stmt)
inline
}
is FunctionCallStatement -> {
val inline =
stmt.args.size <= 1 && stmt.args.all { it is NumericLiteral || it is IdentifierReference }
@@ -115,7 +102,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
inline
}
is Jump -> true
is Jump -> stmt.target is NumericLiteral || stmt.target is IdentifierReference
else -> false
}
}
@@ -138,14 +125,6 @@ class Inliner(private val program: Program, private val options: CompilationOpti
}
}
private fun makeFullyScoped(call: BuiltinFunctionCallStatement) {
val scopedArgs = makeScopedArgs(call.args)
if(scopedArgs.any()) {
val scopedCall = BuiltinFunctionCallStatement(call.target.copy(), scopedArgs.toMutableList(), call.position)
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
}
}
private fun makeFullyScoped(call: FunctionCallStatement) {
makeFullyScoped(call.target)
call.target.targetSubroutine(program)?.let { sub ->
@@ -158,17 +137,6 @@ class Inliner(private val program: Program, private val options: CompilationOpti
}
}
private fun makeFullyScoped(call: BuiltinFunctionCall) {
call.target.targetSubroutine(program)?.let { sub ->
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
val scopedArgs = makeScopedArgs(call.args)
if(scopedArgs.any()) {
val scopedCall = BuiltinFunctionCall(scopedName, scopedArgs.toMutableList(), call.position)
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
}
}
}
private fun makeFullyScoped(call: FunctionCallExpression) {
makeFullyScoped(call.target)
call.target.targetSubroutine(program)?.let { sub ->
@@ -6,8 +6,8 @@ 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.DataType
import prog8.code.core.IErrorReporter
@@ -22,7 +22,7 @@ class StatementOptimizer(private val program: Program,
val functionName = functionCallStatement.target.nameInSource[0]
if (functionName in functions.purefunctionNames) {
if("ignore_unused" !in parent.definingBlock.options())
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
errors.info("statement has no effect (function return value is discarded)", functionCallStatement.position)
return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
}
}
@@ -44,7 +44,7 @@ class StatementOptimizer(private val program: Program,
val firstCharEncoded = options.compTarget.encodeString(string.value, string.encoding)[0]
val chrout = FunctionCallStatement(
IdentifierReference(listOf("txt", "chrout"), pos),
mutableListOf(NumericLiteral(DataType.UBYTE, firstCharEncoded.toDouble(), pos)),
mutableListOf(NumericLiteral(BaseDataType.UBYTE, firstCharEncoded.toDouble(), pos)),
functionCallStatement.void, pos
)
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
@@ -52,12 +52,12 @@ class StatementOptimizer(private val program: Program,
val firstTwoCharsEncoded = options.compTarget.encodeString(string.value.take(2), string.encoding)
val chrout1 = FunctionCallStatement(
IdentifierReference(listOf("txt", "chrout"), pos),
mutableListOf(NumericLiteral(DataType.UBYTE, firstTwoCharsEncoded[0].toDouble(), pos)),
mutableListOf(NumericLiteral(BaseDataType.UBYTE, firstTwoCharsEncoded[0].toDouble(), pos)),
functionCallStatement.void, pos
)
val chrout2 = FunctionCallStatement(
IdentifierReference(listOf("txt", "chrout"), pos),
mutableListOf(NumericLiteral(DataType.UBYTE, firstTwoCharsEncoded[1].toDouble(), pos)),
mutableListOf(NumericLiteral(BaseDataType.UBYTE, firstTwoCharsEncoded[1].toDouble(), pos)),
functionCallStatement.void, pos
)
return listOf(
@@ -157,13 +157,13 @@ class StatementOptimizer(private val program: Program,
}
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program)
if(iterable!=null) {
if(iterable.datatype==DataType.STR) {
if(iterable.datatype.isString) {
val sv = iterable.value as StringLiteral
val size = sv.value.length
if(size==1) {
// loop over string of length 1 -> just assign the single character
val character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
val byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position)
val byte = NumericLiteral(BaseDataType.UBYTE, character.toDouble(), iterable.position)
val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements)
@@ -366,7 +366,7 @@ class StatementOptimizer(private val program: Program,
if (fcall != null && (fcall.target.nameInSource == listOf("lsb"))) {
if (fcall.args.single() isSameAs assignment.target) {
// optimize word=lsb(word) ==> word &= $00ff
val and255 = BinaryExpression(fcall.args[0], "&", NumericLiteral(DataType.UWORD, 255.0, fcall.position), fcall.position)
val and255 = BinaryExpression(fcall.args[0], "&", NumericLiteral(BaseDataType.UWORD, 255.0, fcall.position), fcall.position)
val newAssign = Assignment(assignment.target, and255, AssignmentOrigin.OPTIMIZER, fcall.position)
return listOf(IAstModification.ReplaceNode(assignment, newAssign, parent))
}
@@ -8,10 +8,9 @@ import prog8.ast.expressions.TypecastExpression
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.DataType
import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
import prog8.code.core.internedStringsModuleName
import prog8.code.internedStringsModuleName
import prog8.compiler.CallGraph
@@ -94,18 +93,18 @@ 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.statements.any { it is Subroutine && it.hasBeenInlined })
if (block.name != internedStringsModuleName && "ignore_unused" !in block.options()) {
if (!block.statements.any { it is Subroutine && it.hasBeenInlined })
errors.info("removing unused block '${block.name}'", block.position)
}
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
}
if(callgraph.unused(block)) {
if(block.statements.any{ it !is VarDecl || it.type== VarDeclType.VAR} && "ignore_unused" !in block.options()) {
if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
if (callgraph.unused(block)) {
if (block.statements.any { it !is VarDecl || it.type == VarDeclType.VAR } && "ignore_unused" !in block.options()) {
if (!block.statements.any { it is Subroutine && it.hasBeenInlined })
errors.info("removing unused block '${block.name}'", block.position)
}
if(!block.statements.any { it is Subroutine && it.hasBeenInlined }) {
if (!block.statements.any { it is Subroutine && it.hasBeenInlined }) {
program.removeInternedStringsFromRemovedBlock(block)
}
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
@@ -291,7 +290,7 @@ class UnusedCodeRemover(private val program: Program,
val cvalue1 = assign1.value.constValue(program)
if(cvalue1!=null && cvalue1.number==0.0 && assign2.target.isSameAs(assign1.target, program) && assign2.isAugmentable) {
val value2 = assign2.value
val zero = defaultZero(value2.inferType(program).getOr(DataType.UNDEFINED), value2.position)
val zero = defaultZero(value2.inferType(program), value2.position)
when(value2) {
is BinaryExpression -> substituteZeroInBinexpr(value2, zero, assign1, assign2)
is PrefixExpression -> substituteZeroInPrefixexpr(value2, zero, assign1, assign2)
-119
View File
@@ -1,119 +0,0 @@
plugins {
id 'java'
id 'application'
id 'org.jetbrains.kotlin.jvm'
// id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'io.github.goooler.shadow' version '8.1.7'
id 'com.peterabeles.gversion' version '1.10.2'
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
implementation project(':codeOptimizers')
implementation project(':compilerAst')
implementation project(':codeGenCpu6502')
implementation project(':codeGenIntermediate')
implementation project(':codeGenExperimental')
implementation project(':virtualmachine')
// implementation project(':beanshell')
implementation "org.antlr:antlr4-runtime:4.13.2"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.6'
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
testImplementation project(':codeCore')
testImplementation project(':intermediate')
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.1'
testImplementation 'io.kotest:kotest-framework-datatest:5.9.1'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
configurations.all {
exclude group: 'com.ibm.icu', module: 'icu4j'
exclude group: "org.antlr", module: "antlr4"
}
configurations {
// strange antlr plugin issue, see https://github.com/gradle/gradle/issues/820
// this avoids linking in the complete antlr binary jar
compile {
extendsFrom = extendsFrom.findAll { it != configurations.antlr }
}
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
test {
java {
srcDir "${project.projectDir}/test"
}
}
}
startScripts.enabled = true
application {
mainClass = 'prog8.CompilerMainKt'
applicationName = 'prog8c'
}
shadowJar {
archiveBaseName = 'prog8c'
archiveVersion = version
// minimize()
}
test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}
gversion {
srcDir = "src/" // path is relative to the sub-project by default
classPackage = "prog8.buildversion"
className = "Version"
language = "kotlin"
}
build.finalizedBy installDist, installShadowDist
compileKotlin.dependsOn createVersionFile // , failDirtyNotSnapshot
compileJava.dependsOn createVersionFile
+97
View File
@@ -0,0 +1,97 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("application")
kotlin("jvm")
// id("com.github.johnrengelman.shadow") version "8.1.1"
id("io.github.goooler.shadow") version "8.1.8"
id("com.peterabeles.gversion") version "1.10.3"
}
dependencies {
implementation(project(":codeCore"))
implementation(project(":codeOptimizers"))
implementation(project(":compilerAst"))
implementation(project(":codeGenCpu6502"))
implementation(project(":codeGenIntermediate"))
implementation(project(":codeGenExperimental"))
implementation(project(":virtualmachine"))
// implementation(project(":beanshell"))
implementation("org.antlr:antlr4-runtime:4.13.2")
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.6")
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
testImplementation(project(":codeCore"))
testImplementation(project(":intermediate"))
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
configurations.all {
exclude(group = "com.ibm.icu", module = "icu4j")
exclude(group = "org.antlr", module = "antlr4")
}
sourceSets {
main {
java {
srcDir("${project.projectDir}/src")
}
resources {
srcDir("${project.projectDir}/res")
}
}
test {
java {
srcDir("${project.projectDir}/test")
}
}
}
tasks.startScripts {
enabled = true
}
application {
mainClass.set("prog8.CompilerMainKt")
applicationName = "prog8c"
}
tasks.shadowJar {
archiveBaseName.set("prog8c")
archiveVersion.set(version.toString())
// minimize()
}
tasks.test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Show test results.
testLogging {
events("skipped", "failed")
}
}
gversion {
srcDir = "src/" // path is relative to the sub-project by default
classPackage = "prog8.buildversion"
className = "Version"
language = "kotlin"
}
tasks.build {
finalizedBy(tasks.installDist, tasks.installShadowDist)
}
tasks.compileKotlin {
dependsOn(tasks.createVersionFile) // , failDirtyNotSnapshot
}
tasks.compileJava {
dependsOn(tasks.createVersionFile)
}
-82
View File
@@ -1,82 +0,0 @@
; any() and all() checks on arrays/memory buffers.
; These were builtin functions in older versions of the language.
%option no_symbol_prefixing, ignore_unused
anyall {
sub any(uword arrayptr, uword num_elements) -> bool {
; -- returns true if any byte in the array is not zero.
cx16.r1 = arrayptr
if msb(num_elements)==0 {
for cx16.r0L in 0 to lsb(num_elements)-1 {
if cx16.r1[cx16.r0L]!=0
return true
}
return false
}
repeat num_elements {
if @(cx16.r1)!=0
return true
cx16.r1++
}
return false
}
sub all(uword arrayptr, uword num_elements) -> bool {
; -- returns true if all bytes in the array are not zero.
cx16.r1 = arrayptr
if msb(num_elements)==0 {
for cx16.r0L in 0 to lsb(num_elements)-1 {
if cx16.r1[cx16.r0L]==0
return false
}
return true
}
repeat num_elements {
if @(cx16.r1)==0
return false
cx16.r1++
}
return true
}
sub anyw(uword arrayptr, uword num_elements) -> bool {
; -- returns true if any word in the array is not zero.
; doesn't work on @split arrays.
cx16.r1 = arrayptr
if msb(num_elements)==0 {
repeat lsb(num_elements) {
if peekw(cx16.r1)!=0
return true
cx16.r1+=2
}
return false
}
repeat num_elements {
if peekw(cx16.r1)!=0
return true
cx16.r1+=2
}
return false
}
sub allw(uword arrayptr, uword num_elements) -> bool {
; -- returns true if all words in the array are not zero.
; doesn't work on @split arrays.
cx16.r1 = arrayptr
if msb(num_elements)==0 {
repeat lsb(num_elements) {
if peekw(cx16.r1)==0
return false
cx16.r1+=2
}
return true
}
repeat num_elements {
if peekw(cx16.r1)==0
return false
cx16.r1+=2
}
return true
}
}
+16 -6
View File
@@ -23,12 +23,22 @@ sys {
const ubyte target = 8 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
const ubyte sizeof_bool = 1
const ubyte sizeof_byte = 1
const ubyte sizeof_ubyte = 1
const ubyte sizeof_word = 2
const ubyte sizeof_uword = 2
const ubyte sizeof_float = 0 ; undefined, no float support
const ubyte SIZEOF_BOOL = 1
const ubyte SIZEOF_BYTE = 1
const ubyte SIZEOF_UBYTE = 1
const ubyte SIZEOF_WORD = 2
const ubyte SIZEOF_UWORD = 2
const ubyte SIZEOF_FLOAT = 0 ; undefined, no float support
const byte MIN_BYTE = -128
const byte MAX_BYTE = 127
const ubyte MIN_UBYTE = 0
const ubyte MAX_UBYTE = 255
const word MIN_WORD = -32768
const word MAX_WORD = 32767
const uword MIN_UWORD = 0
const uword MAX_UWORD = 65535
; MIN_FLOAT and MAX_FLOAT are defined in the floats module if imported
asmsub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt.
+120 -64
View File
@@ -1,136 +1,195 @@
; **experimental** buffer data structures, API subject to change!!
%option no_symbol_prefixing, ignore_unused
%option ignore_unused
smallringbuffer {
; -- A ringbuffer (FIFO queue) that occupies a single page in memory, containing 255 bytes maximum.
; You can store and retrieve words too.
; It's optimized for speed and depends on the byte-wrap-around feature when doing incs and decs.
; -- A ringbuffer (FIFO queue) that occupies 256 bytes in memory.
; You can store and retrieve bytes and words. No guards against over/underflow.
; It's optimized for speed and depends on the byte-wrap-around when doing incs and decs.
ubyte fill
ubyte head
ubyte tail
ubyte fill = 0
ubyte head = 0
ubyte tail = 255
ubyte[256] buffer
sub init() {
; -- (re)initialize the ringbuffer, you must call this before using the other routines
; -- (re)initialize the ringbuffer
head = fill = 0
tail = 255
}
sub put(ubyte value) -> bool {
; -- store a byte in the buffer, returns success
if fill==255
return false
sub size() -> ubyte {
return fill
}
sub free() -> ubyte {
return 255-fill
}
sub isfull() -> bool {
return fill>=254
}
sub isempty() -> bool {
return fill<=1
}
sub put(ubyte value) {
; -- store a byte in the buffer
buffer[head] = value
head++
fill++
return true
}
sub putw(uword value) -> bool {
; -- store a word in the buffer, returns success
if fill>=254
return false
sub putw(uword value) {
; -- store a word in the buffer
fill += 2
buffer[head] = lsb(value)
head++
buffer[head] = msb(value)
head++
return true
}
sub get() -> ubyte {
; -- retrieves a byte from the buffer. Also sets Carry flag: set=success, clear=buffer was empty
if fill==0 {
sys.clear_carry()
return 0
}
; -- retrieves a byte from the buffer
fill--
tail++
sys.set_carry()
return buffer[tail]
}
sub getw() -> uword {
; -- retrieves a word from the buffer. Also sets Carry flag: set=success, clear=buffer was empty
if fill<2 {
sys.clear_carry()
return 0
}
; -- retrieves a word from the buffer
fill -= 2
tail++
cx16.r0L = buffer[tail]
tail++
cx16.r0H = buffer[tail]
sys.set_carry()
return cx16.r0
}
}
; note: for a "small stack" (256 bytes size) just use the CPU stack via sys.push[w] / sys.pop[w].
stack {
; -- A stack (LIFO) that uses a block of 8 KB of memory. Growing downward from the top of the buffer.
; You can store and retrieve bytes and words. There are no guards against stack over/underflow.
uword sp = 8191
uword buffer_ptr = memory("buffers_stack", 8192, 0)
sub init() {
sp = 8191
}
sub size() -> uword {
return 8191-sp
}
sub free() -> uword {
return sp
}
sub isfull() -> bool {
return sp==0
}
sub isempty() -> bool {
return sp==8191
}
sub push(ubyte value) {
; -- put a byte on the stack
buffer_ptr[sp] = value
sp--
}
sub pushw(uword value) {
; -- put a word on the stack (lsb first then msb)
buffer_ptr[sp] = lsb(value)
sp--
buffer_ptr[sp] = msb(value)
sp--
}
sub pop() -> ubyte {
; -- pops a byte off the stack
sp++
return buffer_ptr[sp]
}
sub popw() -> uword {
; -- pops a word off the stack.
sp++
cx16.r0H = buffer_ptr[sp]
sp++
cx16.r0L = buffer_ptr[sp]
return cx16.r0
}
}
ringbuffer {
; -- A ringbuffer (FIFO queue) that occupies a single page in memory, containing 8 KB maximum.
; You can store and retrieve words too.
; -- A ringbuffer (FIFO queue) that uses a block of 8 KB of memory.
; You can store and retrieve bytes and words too. No guards against buffer under/overflow.
uword fill
uword head
uword tail
uword buffer_ptr = memory("ringbuffer", 8192, 0)
uword fill = 0
uword head = 0
uword tail = 8191
uword buffer_ptr = memory("buffers_ringbuffer", 8192, 0)
sub init() {
; -- (re)initialize the ringbuffer, you must call this before using the other routines
head = fill = 0
tail = 8191
}
sub put(ubyte value) -> bool {
; -- store a byte in the buffer, returns success
if fill==8192
return false
sub size() -> uword {
return fill
}
sub free() -> uword {
return 8191-fill
}
sub isempty() -> bool {
return fill==0
}
sub isfull() -> bool {
return fill>=8191
}
sub put(ubyte value) {
; -- store a byte in the buffer
buffer_ptr[head] = value
inc_head()
fill++
return true
}
sub putw(uword value) -> bool {
; -- store a word in the buffer, returns success
if fill>=8191
return false
sub putw(uword value) {
; -- store a word in the buffer
fill += 2
buffer_ptr[head] = lsb(value)
inc_head()
buffer_ptr[head] = msb(value)
inc_head()
return true
}
sub get() -> ubyte {
; -- retrieves a byte from the buffer. Also sets Carry flag: set=success, clear=buffer was empty
if fill==0 {
sys.clear_carry()
return 0
}
; -- retrieves a byte from the buffer
fill--
inc_tail()
cx16.r0L = buffer_ptr[tail]
sys.set_carry()
return cx16.r0L
return buffer_ptr[tail]
}
sub getw() -> uword {
; -- retrieves a word from the buffer. Also sets Carry flag: set=success, clear=buffer was empty
if fill<2 {
sys.clear_carry()
return 0
}
; -- retrieves a word from the buffer
fill -= 2
inc_tail()
cx16.r0L = buffer_ptr[tail]
inc_tail()
cx16.r0H = buffer_ptr[tail]
sys.set_carry()
return cx16.r0
}
@@ -147,6 +206,3 @@ ringbuffer {
}
}
; TODO ringbuffer (FIFO queue) should use banked ram on the X16, but still work on virtual
; TODO stack (LIFO queue) using more than 1 page of ram (maybe even banked ram on the x16)
+17 -7
View File
@@ -188,7 +188,7 @@ c64 {
&ubyte SP7X = $d00e
&ubyte SP7Y = $d00f
&ubyte[16] SPXY = $d000 ; the 8 sprite X and Y registers as an array.
&uword[8] SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
&uword[8] @nosplit SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
&ubyte MSIGX = $d010
&ubyte SCROLY = $d011
@@ -320,6 +320,7 @@ c128 {
; TODO c128 a bunch of kernal routines are missing here that are specific to the c128
extsub $FF6E = JSRFAR()
extsub $FF68 = SETBNK(ubyte databank @A, ubyte filenamebank @X)
; ---- C128 specific system utility routines: ----
@@ -418,12 +419,21 @@ sys {
const ubyte target = 128 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
const ubyte sizeof_bool = 1
const ubyte sizeof_byte = 1
const ubyte sizeof_ubyte = 1
const ubyte sizeof_word = 2
const ubyte sizeof_uword = 2
const ubyte sizeof_float = 5
const ubyte SIZEOF_BOOL = 1
const ubyte SIZEOF_BYTE = 1
const ubyte SIZEOF_UBYTE = 1
const ubyte SIZEOF_WORD = 2
const ubyte SIZEOF_UWORD = 2
const ubyte SIZEOF_FLOAT = 5
const byte MIN_BYTE = -128
const byte MAX_BYTE = 127
const ubyte MIN_UBYTE = 0
const ubyte MAX_UBYTE = 255
const word MIN_WORD = -32768
const word MAX_WORD = 32767
const uword MIN_UWORD = 0
const uword MAX_UWORD = 65535
; MIN_FLOAT and MAX_FLOAT are defined in the floats module if imported
sub disable_runstop_and_charsetswitch() {
+17 -7
View File
@@ -189,7 +189,7 @@ c64 {
&ubyte SP7X = $d00e
&ubyte SP7Y = $d00f
&ubyte[16] SPXY = $d000 ; the 8 sprite X and Y registers as an array.
&uword[8] SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
&uword[8] @nosplit SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
&ubyte MSIGX = $d010
&ubyte SCROLY = $d011
@@ -434,12 +434,22 @@ sys {
const ubyte target = 64 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
const ubyte sizeof_bool = 1
const ubyte sizeof_byte = 1
const ubyte sizeof_ubyte = 1
const ubyte sizeof_word = 2
const ubyte sizeof_uword = 2
const ubyte sizeof_float = 5
const ubyte SIZEOF_BOOL = 1
const ubyte SIZEOF_BYTE = 1
const ubyte SIZEOF_UBYTE = 1
const ubyte SIZEOF_WORD = 2
const ubyte SIZEOF_UWORD = 2
const ubyte SIZEOF_FLOAT = 5
const byte MIN_BYTE = -128
const byte MAX_BYTE = 127
const ubyte MIN_UBYTE = 0
const ubyte MAX_UBYTE = 255
const word MIN_WORD = -32768
const word MAX_WORD = 32767
const uword MIN_UWORD = 0
const uword MAX_UWORD = 65535
; MIN_FLOAT and MAX_FLOAT are defined in the floats module if imported
sub disable_runstop_and_charsetswitch() {
p8_sys_startup.disable_runstop_and_charsetswitch()
+3 -404
View File
@@ -1,409 +1,8 @@
; **experimental** data compression/decompression routines, API subject to change!!
%import shared_compression
compression {
%option no_symbol_prefixing, ignore_unused
sub encode_rle_outfunc(uword data, uword size, uword output_function, bool is_last_block) {
; -- Compress the given data block using ByteRun1 aka PackBits RLE encoding.
; output_function = address of a routine that gets a byte arg in A,
; this is the next RLE byte to write to the compressed output buffer or file.
; is_last_block = usually true, but you can set it to false if you want to concatenate multiple
; compressed blocks (for instance if the source data is >64Kb)
; Worst case result storage size needed = (size + (size+126) / 127) + 1
; This routine is not optimized for speed but for readability and ease of use.
uword idx = 0
uword literals_start_idx = 0
ubyte literals_length = 0
asmsub call_output_function(ubyte arg @A) {
%asm {{
jmp (p8v_output_function)
}}
}
sub next_same_span() {
; returns length in cx16.r1L, and the byte value in cx16.r1H
cx16.r1H = data[idx]
cx16.r1L = 0
while data[idx]==cx16.r1H and cx16.r1L<128 and idx<size {
idx++
cx16.r1L++
}
}
sub output_literals() {
call_output_function(literals_length-1)
uword dataptr = data + literals_start_idx
ubyte i
for i in 0 to literals_length-1 {
call_output_function(@(dataptr))
dataptr++
}
literals_length = 0
}
while idx<size {
next_same_span() ; count in r1L, value in r1H
if cx16.r1L>1 {
; a replicate run
if literals_length>0
output_literals()
call_output_function((cx16.r1L^255)+2) ; 257-cx16.r1L
call_output_function(cx16.r1H)
}
else {
; add more to the literals run
if literals_length==128
output_literals()
if literals_length==0
literals_start_idx = idx-1
literals_length++
}
}
if literals_length>0
output_literals()
if is_last_block
call_output_function(128)
}
sub encode_rle(uword data, uword size, uword target, bool is_last_block) -> uword {
; -- Compress the given data block using ByteRun1 aka PackBits RLE encoding.
; Returns the size of the compressed RLE data. Worst case result storage size needed = (size + (size+126) / 127) + 1.
; is_last_block = usually true, but you can set it to false if you want to concatenate multiple
; compressed blocks (for instance if the source data is >64Kb)
; This routine is not optimized for speed but for readability and ease of use.
uword idx = 0
uword literals_start_idx = 0
ubyte literals_length = 0
uword orig_target = target
sub next_same_span() {
; returns length in cx16.r1L, and the byte value in cx16.r1H
cx16.r1H = data[idx]
cx16.r1L = 0
while data[idx]==cx16.r1H and cx16.r1L<128 and idx<size {
idx++
cx16.r1L++
}
}
sub output_literals() {
@(target) = literals_length-1
target++
uword dataptr = data + literals_start_idx
ubyte i
for i in 0 to literals_length-1 {
@(target) = @(dataptr)
target++
dataptr++
}
literals_length = 0
}
while idx<size {
next_same_span() ; count in r1L, value in r1H
if cx16.r1L>1 {
; a replicate run
if literals_length>0
output_literals()
@(target) = (cx16.r1L^255)+2 ; 257-cx16.r1L
target++
@(target) = cx16.r1H
target++
}
else {
; add more to the literals run
if literals_length==128
output_literals()
if literals_length==0
literals_start_idx = idx-1
literals_length++
}
}
if literals_length>0
output_literals()
if is_last_block {
@(target) = 128
target ++
}
return target-orig_target
}
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.
; instead of a source buffer, you provide a callback function that must return the next byte to compress in A.
; Stops decompressing when the maxsize has been reached.
%asm {{
sta _cb_mod1+1
sty _cb_mod1+2
sta _cb_mod2+1
sty _cb_mod2+2
sta _cb_mod3+1
sty _cb_mod3+2
lda cx16.r0L
ldy cx16.r0H
sta P8ZP_SCRATCH_W2 ; target ptr
sta _orig_target
sty P8ZP_SCRATCH_W2+1
sty _orig_target+1
lda cx16.r0L
clc
adc cx16.r1L
sta cx16.r1L
lda cx16.r0H
adc cx16.r1H
sta cx16.r1H ; decompression limit
_loop
; while target (W2) < limit (r1)
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
cmp cx16.r1L
tya
sbc cx16.r1H
bcs _end
_cb_mod1 jsr $ffff ; modified
bpl _literals
cmp #128
beq _end
; replicate next byte -n+1 times
eor #255
clc
adc #2
sta P8ZP_SCRATCH_REG
_cb_mod2 jsr $ffff ; modified
ldx P8ZP_SCRATCH_REG
ldy #0
- sta (P8ZP_SCRATCH_W2),y
iny
dex
bne -
; add A to target
lda P8ZP_SCRATCH_REG
clc
adc P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W2
lda #0
adc P8ZP_SCRATCH_W2+1
sta P8ZP_SCRATCH_W2+1
jmp _loop
_literals
; copy the next n+1 bytes
pha
sta P8ZP_SCRATCH_B1
ldy #0
sty P8ZP_SCRATCH_REG
_cb_mod3 jsr $ffff ; modified
ldy P8ZP_SCRATCH_REG
sta (P8ZP_SCRATCH_W2),y
inc P8ZP_SCRATCH_REG
dec P8ZP_SCRATCH_B1
bpl _cb_mod3
; add N+1 to target
pla
sec
adc P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W2
lda #0
adc P8ZP_SCRATCH_W2+1
sta P8ZP_SCRATCH_W2+1
jmp _loop
_orig_target .word 0
_end
; return w2-orig_target, the size of the decompressed data
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
sec
sbc _orig_target
tax
tya
sbc _orig_target+1
tay
txa
rts
}}
}
asmsub decode_rle(uword compressed @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.
; Stops decompressing when the maxsize has been reached.
%asm {{
sta P8ZP_SCRATCH_W1 ; compressed data ptr
sty P8ZP_SCRATCH_W1+1
lda cx16.r0L
ldy cx16.r0H
sta P8ZP_SCRATCH_W2 ; target ptr
sta _orig_target
sty P8ZP_SCRATCH_W2+1
sty _orig_target+1
lda cx16.r0L
clc
adc cx16.r1L
sta cx16.r1L
lda cx16.r0H
adc cx16.r1H
sta cx16.r1H ; decompression limit
_loop ; while target (W2) < limit (r1)
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
cmp cx16.r1L
tya
sbc cx16.r1H
bcs _end
ldy #0
lda (P8ZP_SCRATCH_W1),y
bpl _literals
cmp #128
beq _end
; replicate next byte -n+1 times
eor #255
clc
adc #2
pha
tax
iny
lda (P8ZP_SCRATCH_W1),y
ldy #0
- sta (P8ZP_SCRATCH_W2),y
iny
dex
bne -
; add A to target
pla
clc
adc P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W2
lda #0
adc P8ZP_SCRATCH_W2+1
sta P8ZP_SCRATCH_W2+1
; increase source by 2
clc
lda P8ZP_SCRATCH_W1
adc #2
sta P8ZP_SCRATCH_W1
lda #0
adc P8ZP_SCRATCH_W1+1
sta P8ZP_SCRATCH_W1+1
jmp _loop
_literals
; copy the next n+1 bytes
pha
tax
inc P8ZP_SCRATCH_W1
bne +
inc P8ZP_SCRATCH_W1+1
+ ldy #0
- lda (P8ZP_SCRATCH_W1),y
sta (P8ZP_SCRATCH_W2),y
iny
dex
bpl -
; add N+1 to source
pla
tax
sec
adc P8ZP_SCRATCH_W1
sta P8ZP_SCRATCH_W1
lda #0
adc P8ZP_SCRATCH_W1+1
sta P8ZP_SCRATCH_W1+1
; add N+1 to target as well
txa
sec
adc P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_W2
lda #0
adc P8ZP_SCRATCH_W2+1
sta P8ZP_SCRATCH_W2+1
jmp _loop
_orig_target .word 0
_end
; return w2-orig_target, the size of the decompressed data
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
sec
sbc _orig_target
tax
tya
sbc _orig_target+1
tay
txa
rts
}}
}
/***
; prog8 source code for the asm routine above:
sub decode_rle_prog8(uword @zp compressed, uword @zp target, uword maxsize) -> uword {
cx16.r0 = target ; original target
cx16.r1 = target+maxsize ; decompression limit
while target<cx16.r1 {
cx16.r2L = @(compressed)
if_neg {
if cx16.r2L==128
break
; replicate the next byte -n+1 times
compressed++
cx16.r3L = @(compressed)
repeat 2+(cx16.r2L^255) {
@(target) = cx16.r3L
target++
}
compressed++
} else {
; copy the next n+1 bytes
compressed++
repeat cx16.r2L+1 {
@(target) = @(compressed)
compressed++
target++
}
}
}
return target-cx16.r0
}
sub decode_rle_callback_prog8(uword producer_callback, uword @zp target, uword maxsize) -> uword {
cx16.r0 = target ; original target
cx16.r1 = target+maxsize ; decompression limit
while target<cx16.r1 {
cx16.r2L = lsb(call(producer_callback))
if_neg {
if cx16.r2L==128
break
; replicate the next byte -n+1 times
cx16.r3L = lsb(call(producer_callback))
repeat 2+(cx16.r2L^255) {
@(target) = cx16.r3L
target++
}
} else {
; copy the next n+1 bytes
repeat cx16.r2L+1 {
@(target) = lsb(call(producer_callback))
target++
}
}
}
return target-cx16.r0
}
***/
%option ignore_unused
}
+244
View File
@@ -0,0 +1,244 @@
; **experimental** buffer data structures, API subject to change!!
%option ignore_unused
smallringbuffer {
; -- A ringbuffer (FIFO queue) that occupies 256 bytes in memory.
; You can store and retrieve bytes and words. No guards against over/underflow.
; It's optimized for speed and depends on the byte-wrap-around when doing incs and decs.
ubyte fill = 0
ubyte head = 0
ubyte tail = 255
ubyte[256] buffer
sub init() {
; -- (re)initialize the ringbuffer
head = fill = 0
tail = 255
}
sub size() -> ubyte {
return fill
}
sub free() -> ubyte {
return 255-fill
}
sub isfull() -> bool {
return fill>=254
}
sub isempty() -> bool {
return fill<=1
}
sub put(ubyte value) {
; -- store a byte in the buffer
buffer[head] = value
head++
fill++
}
sub putw(uword value) {
; -- store a word in the buffer
fill += 2
buffer[head] = lsb(value)
head++
buffer[head] = msb(value)
head++
}
sub get() -> ubyte {
; -- retrieves a byte from the buffer
fill--
tail++
return buffer[tail]
}
sub getw() -> uword {
; -- retrieves a word from the buffer
fill -= 2
tail++
cx16.r0L = buffer[tail]
tail++
cx16.r0H = buffer[tail]
return cx16.r0
}
}
; note: for a "small stack" (256 bytes size) just use the CPU stack via sys.push[w] / sys.pop[w].
stack {
; -- A stack (LIFO) that uses a block of 8 KB of memory. Growing downward from the top of the buffer.
; You can store and retrieve bytes and words. There are no guards against stack over/underflow.
uword sp
ubyte bank
sub init(ubyte rambank) {
; -- initialize the stack, must be called before use. Supply the HiRAM bank to use as buffer space.
sp = $bfff
bank = rambank
}
sub size() -> uword {
return $bfff-sp
}
sub free() -> uword {
return sp-$a000
}
sub isfull() -> bool {
return sp<=$a001
}
sub isempty() -> bool {
return sp==$bfff
}
sub push(ubyte value) {
; -- put a byte on the stack
sys.push(cx16.getrambank())
cx16.rambank(bank)
@(sp) = value
sp--
cx16.rambank(sys.pop())
}
sub pushw(uword value) {
; -- put a word on the stack (lsb first then msb)
sys.push(cx16.getrambank())
cx16.rambank(bank)
@(sp) = lsb(value)
sp--
@(sp) = msb(value)
sp--
cx16.rambank(sys.pop())
}
sub pop() -> ubyte {
; -- pops a byte off the stack
sys.push(cx16.getrambank())
cx16.rambank(bank)
sp++
cx16.r0L = @(sp)
cx16.rambank(sys.pop())
return cx16.r0L
}
sub popw() -> uword {
; -- pops a word off the stack.
sys.push(cx16.getrambank())
cx16.rambank(bank)
sp++
cx16.r0H = @(sp)
sp++
cx16.r0L = @(sp)
cx16.rambank(sys.pop())
return cx16.r0
}
}
ringbuffer {
; -- A ringbuffer (FIFO queue) that uses a block of 8 KB of memory.
; You can store and retrieve bytes and words too. No guards against buffer under/overflow.
uword fill, head, tail
ubyte bank = 255 ; set via init()
sub init(ubyte rambank) {
; -- initialize the ringbuffer, must be called before use. Supply the HiRAM bank to use as buffer space.
head = $a000
tail = $bfff
fill = 0
bank = rambank
}
sub size() -> uword {
return fill
}
sub free() -> uword {
return $1fff-fill
}
sub isempty() -> bool {
return fill<=1
}
sub isfull() -> bool {
return fill>=8191
}
sub put(ubyte value) {
; -- store a byte in the buffer
sys.push(cx16.getrambank())
cx16.rambank(bank)
@(head) = value
fill++
inc_head()
cx16.rambank(sys.pop())
}
sub putw(uword value) {
; -- store a word in the buffer
sys.push(cx16.getrambank())
cx16.rambank(bank)
pokew(head, value)
fill += 2
inc_head()
inc_head()
cx16.rambank(sys.pop())
}
sub get() -> ubyte {
; -- retrieves a byte from the buffer
sys.push(cx16.getrambank())
cx16.rambank(bank)
fill--
inc_tail()
cx16.r0L= @(tail)
cx16.rambank(sys.pop())
return cx16.r0L
}
sub getw() -> uword {
; -- retrieves a word from the buffer
sys.push(cx16.getrambank())
cx16.rambank(bank)
fill -= 2
inc_tail()
cx16.r0L = @(tail)
inc_tail()
cx16.r0H = @(tail)
cx16.rambank(sys.pop())
return cx16.r0
}
sub inc_head() {
head++
if msb(head)==$c0
head=$a000
}
sub inc_tail() {
tail++
if msb(tail)==$c0
tail=$a000
}
}
+68
View File
@@ -0,0 +1,68 @@
; **experimental** data compression/decompression routines, API subject to change!!
%import shared_compression
compression {
%option ignore_unused
asmsub decode_rle_vram(uword compressed @R0, ubyte vbank @X, uword vaddr @AY) {
; -- Decodes "ByteRun1" (aka PackBits) RLE compressed data directly into Vera VRAM.
; Control byte value 128 ends the decoding. This routine is for the Commander X16 only.
%asm {{
stz cx16.VERA_CTRL
sta cx16.VERA_ADDR_L
sty cx16.VERA_ADDR_M
txa
ora #%00010000 ; autoincr by 1
sta cx16.VERA_ADDR_H
_loop
lda (cx16.r0)
bpl _copy_literals
cmp #128
bne +
rts ; DONE!
; replicate the next byte -n+1 times
+
inc cx16.r0L
bne +
inc cx16.r0H
+ eor #255
clc
adc #2
tay
lda (cx16.r0)
- sta cx16.VERA_DATA0
dey
bne -
inc cx16.r0L
bne _loop
inc cx16.r0H
bra _loop
_copy_literals
; copy the next n+1 bytes
inc cx16.r0L
bne +
inc cx16.r0H
+ pha
tax
inx
ldy #0
- lda (cx16.r0),y
sta cx16.VERA_DATA0
iny
dex
bne -
; increase pointer by n+1 bytes
pla
sec
adc cx16.r0L
sta cx16.r0L
bcc _loop
inc cx16.r0H
bra _loop
}}
}
}
+109 -56
View File
@@ -13,7 +13,7 @@
%import textio
%import conv
%import string
%import strings
%import syslib
diskio {
@@ -163,7 +163,7 @@ io_error:
if lf_start_list(pattern_ptr) {
while lf_next_entry() {
if list_filetype!="dir" {
filenames_buffer += string.copy(list_filename, filenames_buffer) + 1
filenames_buffer += strings.copy(list_filename, filenames_buffer) + 1
files_found++
if filenames_buffer - buffer_start > filenames_buf_size-20 {
@(filenames_buffer)=0
@@ -265,7 +265,7 @@ io_error:
if not list_skip_disk_name {
if list_pattern==0
return true
if string.pattern_match(list_filename, list_pattern)
if strings.pattern_match(list_filename, list_pattern)
return true
}
list_skip_disk_name = false
@@ -297,7 +297,7 @@ close_end:
; if you're going to read from it yourself instead of using f_read()!
f_close()
cbm.SETNAM(string.length(filenameptr), filenameptr)
cbm.SETNAM(strings.length(filenameptr), filenameptr)
cbm.SETLFS(READ_IO_CHANNEL, drivenumber, READ_IO_CHANNEL) ; note: has to be Channel,x,Channel because otherwise f_seek doesn't work
void cbm.OPEN() ; open 12,8,12,"filename"
if_cc {
@@ -327,8 +327,20 @@ close_end:
return 0
reset_read_channel()
list_blocks = 0 ; we reuse this variable for the total number of bytes read
if num_bytes==1 {
; optimize for reading just a single byte
@(bufferpointer) = cbm.CHRIN()
cx16.r0L = cbm.READST()
cbm.CLRCHN() ; reset default i/o channels
if cx16.r0L!=0 {
f_close()
if cx16.r0L & $40 == 0
return 0
}
return 1
}
list_blocks = 0 ; we reuse this variable for the total number of bytes read
uword readsize
while num_bytes!=0 {
readsize = 255
@@ -347,6 +359,7 @@ close_end:
break
}
}
cbm.CLRCHN() ; reset default i/o channels
return list_blocks ; number of bytes read
byte_read_loop: ; fallback if MACPTR isn't supported on the device
@@ -357,12 +370,6 @@ byte_read_loop: ; fallback if MACPTR isn't supported on the device
sta m_in_buffer+2
}}
while num_bytes!=0 {
if cbm.READST()!=0 {
f_close()
if cbm.READST() & $40 !=0 ; eof?
return list_blocks ; number of bytes read
return 0 ; error.
}
%asm {{
jsr cbm.CHRIN
m_in_buffer sta $ffff
@@ -371,9 +378,18 @@ m_in_buffer sta $ffff
inc m_in_buffer+2
+
}}
cx16.r0L = cbm.READST()
if_nz {
f_close()
cbm.CLRCHN() ; reset default i/o channels
if cx16.r0L & $40 !=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
}
@@ -392,12 +408,13 @@ m_in_buffer sta $ffff
total_read += cx16.r0
bufferpointer += cx16.r0
}
cbm.CLRCHN() ; reset default i/o channels
return total_read
}
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y, ubyte @A {
; Routine to read text lines from a text file. Lines must be less than 255 characters.
; Reads characters from the input file UNTIL a newline or return character (or EOF).
; Reads characters from the input file UNTIL a newline or return character, or 0 byte (likely EOF).
; The line read will be 0-terminated in the buffer (and not contain the end of line character).
; The length of the line is returned in Y. Note that an empty line is okay and is length 0!
; The I/O error status byte is returned in A.
@@ -418,6 +435,11 @@ _line_end dey ; get rid of the trailing end-of-line char
lda #0
sta (P8ZP_SCRATCH_W1),y
_end jsr cbm.READST
pha
phy
jsr cbm.CLRCHN
ply
pla
rts
}}
}
@@ -442,6 +464,8 @@ _end jsr cbm.READST
; (for example, if it already exists). This is different than f_open()!
; To be 100% sure if this call was successful, you have to use status()
; and check the drive's status message!
; NOTE: the default output isn't yet set to this file, you must use reset_write_channel() to do this,
; if you're going to write to it yourself instead of using f_write()!
return internal_f_open_w(filename, false)
}
@@ -450,28 +474,6 @@ _end jsr cbm.READST
return internal_f_open_w(filename, true)
}
sub internal_f_open_w(str filename, bool open_for_seeks) -> bool {
f_close_w()
list_filename = filename
str modifier = ",s,?"
modifier[3] = 'w'
if open_for_seeks
modifier[3] = 'm'
cx16.r0L = string.append(list_filename, modifier) ; secondary 13 requires a mode suffix to signal we're writing/modifying
cbm.SETNAM(cx16.r0L, list_filename)
cbm.SETLFS(WRITE_IO_CHANNEL, drivenumber, WRITE_IO_CHANNEL)
void cbm.OPEN() ; open 13,8,13,"filename"
if_cc {
if cbm.READST()==0 {
write_iteration_in_progress = true
return true
}
}
cbm.CLOSE(WRITE_IO_CHANNEL)
f_close_w()
return false
}
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
; -- write the given number of bytes to the currently open file
; you can call this multiple times to append more data
@@ -486,9 +488,9 @@ _end jsr cbm.READST
if msb(bufferpointer) == $c0
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
if cbm.READST()!=0
return false
goto return_status
} until num_bytes==0
return cbm.READST()==0
goto return_status
no_mciout:
; the device doesn't support MCIOUT, use a normal per-byte write loop
@@ -496,7 +498,10 @@ no_mciout:
cbm.CHROUT(@(bufferpointer))
bufferpointer++
}
return cbm.READST()==0
return_status:
cx16.r0L = cbm.READST()
cbm.CLRCHN() ; reset default i/o channels
return cx16.r0L==0
}
return true
}
@@ -511,17 +516,42 @@ no_mciout:
}
}
sub internal_f_open_w(str filename, bool open_for_seeks) -> bool {
f_close_w()
list_filename = filename
str modifier = ",s,?"
modifier[3] = 'w'
if open_for_seeks
modifier[3] = 'm'
cx16.r0L = strings.append(list_filename, modifier) ; secondary 13 requires a mode suffix to signal we're writing/modifying
cbm.SETNAM(cx16.r0L, list_filename)
cbm.SETLFS(WRITE_IO_CHANNEL, drivenumber, WRITE_IO_CHANNEL)
void cbm.OPEN() ; open 13,8,13,"filename"
if_cc {
if cbm.READST()==0 {
write_iteration_in_progress = true
cbm.CLRCHN() ; reset default i/o channels
return true
}
}
cbm.CLOSE(WRITE_IO_CHANNEL)
f_close_w()
return false
}
; ---- other functions ----
sub status() -> uword {
; -- retrieve the disk drive's current status message
str device_not_present_error = "device not present #xx"
if cbm.READST()==128 {
device_not_present_error[len(device_not_present_error)-2] = 0
void string.copy(conv.str_ub(drivenumber), &device_not_present_error+len(device_not_present_error)-2)
return device_not_present_error
}
; TODO this doesn't seem to work reliably, sometimes READST returns 128 when the drive is just fine
; str device_not_present_error = "device not present #xx"
; if cbm.READST()==128 {
; device_not_present_error[len(device_not_present_error)-2] = 0
; void strings.copy(conv.str_ub(drivenumber), &device_not_present_error+len(device_not_present_error)-2)
; return device_not_present_error
; }
uword messageptr = &list_filename
cbm.SETNAM(0, list_filename)
@@ -596,7 +626,7 @@ io_error:
}
sub internal_save_routine(uword filenameptr, uword startaddress, uword savesize, bool headerless) -> bool {
cbm.SETNAM(string.length(filenameptr), filenameptr)
cbm.SETNAM(strings.length(filenameptr), filenameptr)
cbm.SETLFS(1, drivenumber, 0)
uword @shared end_address = startaddress + savesize
cx16.r0L = 0
@@ -638,20 +668,24 @@ io_error:
; NOTE: data is read into the current Ram bank if you're reading into banked ram.
; if you require loading into another ram bank, you have to set that
; yourself using cx16.rambank(bank) before calling load().
; NOTE: if the file is loaded in a hiram bank, and fills the bank exactly to the end ($bfff),
; the return address will still be one higher. Which means, because the Kernal
; load routine is bank-aware, it will return $a000 and will have switched to the next hiram bank!
; So you'll have to reset the ram bank with cx16.rambank() to switch back to the bank that the data was put in.
sub load(uword filenameptr, uword address_override) -> uword {
return internal_load_routine(filenameptr, address_override, false)
}
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
; No program header is assumed in the file. Everything is loaded.
; See comments on load() for more details.
; See comments on load() for more details. Including the banking behavior on the X16.
sub load_raw(uword filenameptr, uword startaddress) -> uword {
return internal_load_routine(filenameptr, startaddress, true)
}
sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword {
cbm.SETNAM(string.length(filenameptr), filenameptr)
cbm.SETNAM(strings.length(filenameptr), filenameptr)
ubyte secondary = 1
cx16.r1 = 0
if address_override!=0
@@ -677,7 +711,7 @@ io_error:
; -- delete a file on the drive
list_filename[0] = 's'
list_filename[1] = ':'
ubyte flen = string.copy(filenameptr, &list_filename+2)
ubyte flen = strings.copy(filenameptr, &list_filename+2)
cbm.SETNAM(flen+2, list_filename)
cbm.SETLFS(1, drivenumber, 15)
void cbm.OPEN()
@@ -689,9 +723,9 @@ io_error:
; -- rename a file on the drive
list_filename[0] = 'r'
list_filename[1] = ':'
ubyte flen_new = string.copy(newfileptr, &list_filename+2)
ubyte flen_new = strings.copy(newfileptr, &list_filename+2)
list_filename[flen_new+2] = '='
ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new)
ubyte flen_old = strings.copy(oldfileptr, &list_filename+3+flen_new)
cbm.SETNAM(3+flen_new+flen_old, list_filename)
cbm.SETLFS(1, drivenumber, 15)
void cbm.OPEN()
@@ -701,13 +735,31 @@ io_error:
sub send_command(uword commandptr) {
; -- send a dos command to the drive (don't read any response)
cbm.SETNAM(string.length(commandptr), commandptr)
cbm.SETNAM(strings.length(commandptr), commandptr)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
cbm.CLRCHN()
cbm.CLOSE(15)
}
sub get_loadaddress(str filename) -> uword {
; get the load adress from a PRG file (usually $0801 but it can be different)
cbm.SETNAM(strings.length(filename), filename)
cbm.SETLFS(READ_IO_CHANNEL, drivenumber, READ_IO_CHANNEL)
void cbm.OPEN() ; open 12,8,12,"filename"
cx16.r0 = 0
if_cc {
void cbm.CHKIN(READ_IO_CHANNEL)
cx16.r0L = cbm.CHRIN()
cx16.r0H = cbm.CHRIN()
if cbm.READST()!=0
cx16.r0 = 0
}
cbm.CLOSE(READ_IO_CHANNEL)
return cx16.r0
}
; CommanderX16 extensions over the basic C64/C128 diskio routines:
@@ -776,7 +828,7 @@ internal_vload:
list_filename[0] = 'c'
list_filename[1] = 'd'
list_filename[2] = ':'
void string.copy(path, &list_filename+3)
void strings.copy(path, &list_filename+3)
send_command(list_filename)
}
@@ -785,19 +837,19 @@ internal_vload:
list_filename[0] = 'm'
list_filename[1] = 'd'
list_filename[2] = ':'
void string.copy(name, &list_filename+3)
void strings.copy(name, &list_filename+3)
send_command(list_filename)
}
sub rmdir(str name) {
; -- remove a subdirectory.
void string.find(name, '*')
void strings.find(name, '*')
if_cs
return ; refuse to act on a wildcard *
list_filename[0] = 'r'
list_filename[1] = 'd'
list_filename[2] = ':'
void string.copy(name, &list_filename+3)
void strings.copy(name, &list_filename+3)
send_command(list_filename)
}
@@ -860,7 +912,7 @@ io_error:
sub prepend(str dir) {
if dir[0]=='/' and dir[1]==0
return
cx16.r9L = string.length(dir)
cx16.r9L = strings.length(dir)
cx16.r12 -= cx16.r9L
sys.memcopy(dir, cx16.r12, cx16.r9L)
cx16.r12--
@@ -874,12 +926,13 @@ io_error:
list_filename[1] = '-'
list_filename[2] = 'h'
list_filename[3] = ':'
void string.copy(name, &list_filename+4)
void strings.copy(name, &list_filename+4)
send_command(list_filename)
}
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!
if f_open(filename) {
f_close()
return true
+13 -58
View File
@@ -12,6 +12,8 @@
; This is compatible with the CX16's screen mode 128. (void cx16.set_screen_mode(128))
;
%import buffers
gfx_hires {
%option ignore_unused
@@ -574,7 +576,7 @@ gfx_hires {
}}
}
sub fill(uword x, uword y, ubyte new_color) {
sub fill(uword x, uword y, ubyte new_color, ubyte stack_rambank) {
; reuse a few virtual registers in ZP for variables
&ubyte fillm = &cx16.r7L
&ubyte seedm = &cx16.r8L
@@ -586,75 +588,28 @@ gfx_hires {
; Non-recursive scanline flood fill.
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
const ubyte MAXDEPTH = 100
word @zp xx = x as word
word @zp yy = y as word
word[MAXDEPTH] @split @shared stack_xl
word[MAXDEPTH] @split @shared stack_xr
word[MAXDEPTH] @split @shared stack_y
byte[MAXDEPTH] @shared stack_dy
cx16.r12L = 0 ; stack pointer
word x1
word x2
byte dy
cx16.r10L = new_color
stack.init(stack_rambank)
sub push_stack(word sxl, word sxr, word sy, byte sdy) {
if cx16.r12L==MAXDEPTH
return
cx16.r0s = sy+sdy
if cx16.r0s>=0 and cx16.r0s<=HEIGHT-1 {
;; stack_xl[cx16.r12L] = sxl
;; stack_xr[cx16.r12L] = sxr
;; stack_y[cx16.r12L] = sy
;; stack_dy[cx16.r12L] = sdy
;; cx16.r12L++
%asm {{
ldy cx16.r12L
lda p8v_sxl
sta p8v_stack_xl_lsb,y
lda p8v_sxl+1
sta p8v_stack_xl_msb,y
lda p8v_sxr
sta p8v_stack_xr_lsb,y
lda p8v_sxr+1
sta p8v_stack_xr_msb,y
lda p8v_sy
sta p8v_stack_y_lsb,y
lda p8v_sy+1
sta p8v_stack_y_msb,y
ldy cx16.r12L
lda p8v_sdy
sta p8v_stack_dy,y
inc cx16.r12L
}}
stack.pushw(sxl as uword)
stack.pushw(sxr as uword)
stack.pushw(sy as uword)
stack.push(sdy as ubyte)
}
}
sub pop_stack() {
;; cx16.r12L--
;; x1 = stack_xl[cx16.r12L]
;; x2 = stack_xr[cx16.r12L]
;; y = stack_y[cx16.r12L]
;; dy = stack_dy[cx16.r12L]
%asm {{
dec cx16.r12L
ldy cx16.r12L
lda p8v_stack_xl_lsb,y
sta p8v_x1
lda p8v_stack_xl_msb,y
sta p8v_x1+1
lda p8v_stack_xr_lsb,y
sta p8v_x2
lda p8v_stack_xr_msb,y
sta p8v_x2+1
lda p8v_stack_y_lsb,y
sta p8v_yy
lda p8v_stack_y_msb,y
sta p8v_yy+1
ldy cx16.r12L
lda p8v_stack_dy,y
sta p8v_dy
}}
dy = stack.pop() as byte
yy = stack.popw() as word
x2 = stack.popw() as word
x1 = stack.popw() as word
yy+=dy
}
cx16.r11L = pget(xx as uword, yy as uword) ; old_color
@@ -666,7 +621,7 @@ gfx_hires {
push_stack(xx, xx, yy, 1)
push_stack(xx, xx, yy + 1, -1)
word left = 0
while cx16.r12L!=0 {
while not stack.isempty() {
pop_stack()
xx = x1
if fill_scanline_left_2bpp() goto skip
+12 -58
View File
@@ -5,6 +5,7 @@
%import syslib
%import verafx
%import buffers
gfx_lores {
%option ignore_unused
@@ -627,7 +628,7 @@ gfx_lores {
}}
}
sub fill(uword x, ubyte y, ubyte new_color) {
sub fill(uword x, ubyte y, ubyte new_color, ubyte stack_rambank) {
; reuse a few virtual registers in ZP for variables
&ubyte fillm = &cx16.r7L
&ubyte seedm = &cx16.r8L
@@ -639,75 +640,28 @@ gfx_lores {
; Non-recursive scanline flood fill.
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
const ubyte MAXDEPTH = 100
word @zp xx = x as word
word @zp yy = y as word
word[MAXDEPTH] @split @shared stack_xl
word[MAXDEPTH] @split @shared stack_xr
word[MAXDEPTH] @split @shared stack_y
byte[MAXDEPTH] @shared stack_dy
cx16.r12L = 0 ; stack pointer
word x1
word x2
byte dy
cx16.r10L = new_color
stack.init(stack_rambank)
sub push_stack(word sxl, word sxr, word sy, byte sdy) {
if cx16.r12L==MAXDEPTH
return
cx16.r0s = sy+sdy
if cx16.r0s>=0 and cx16.r0s<=HEIGHT-1 {
;; stack_xl[cx16.r12L] = sxl
;; stack_xr[cx16.r12L] = sxr
;; stack_y[cx16.r12L] = sy
;; stack_dy[cx16.r12L] = sdy
;; cx16.r12L++
%asm {{
ldy cx16.r12L
lda p8v_sxl
sta p8v_stack_xl_lsb,y
lda p8v_sxl+1
sta p8v_stack_xl_msb,y
lda p8v_sxr
sta p8v_stack_xr_lsb,y
lda p8v_sxr+1
sta p8v_stack_xr_msb,y
lda p8v_sy
sta p8v_stack_y_lsb,y
lda p8v_sy+1
sta p8v_stack_y_msb,y
ldy cx16.r12L
lda p8v_sdy
sta p8v_stack_dy,y
inc cx16.r12L
}}
stack.pushw(sxl as uword)
stack.pushw(sxr as uword)
stack.pushw(sy as uword)
stack.push(sdy as ubyte)
}
}
sub pop_stack() {
;; cx16.r12L--
;; x1 = stack_xl[cx16.r12L]
;; x2 = stack_xr[cx16.r12L]
;; y = stack_y[cx16.r12L]
;; dy = stack_dy[cx16.r12L]
%asm {{
dec cx16.r12L
ldy cx16.r12L
lda p8v_stack_xl_lsb,y
sta p8v_x1
lda p8v_stack_xl_msb,y
sta p8v_x1+1
lda p8v_stack_xr_lsb,y
sta p8v_x2
lda p8v_stack_xr_msb,y
sta p8v_x2+1
lda p8v_stack_y_lsb,y
sta p8v_yy
lda p8v_stack_y_msb,y
sta p8v_yy+1
ldy cx16.r12L
lda p8v_stack_dy,y
sta p8v_dy
}}
dy = stack.pop() as byte
yy = stack.popw() as word
x2 = stack.popw() as word
x1 = stack.popw() as word
yy+=dy
}
cx16.r11L = pget(xx as uword, lsb(yy)) ; old_color
@@ -718,7 +672,7 @@ gfx_lores {
push_stack(xx, xx, yy, 1)
push_stack(xx, xx, yy + 1, -1)
word left = 0
while cx16.r12L!=0 {
while not stack.isempty() {
pop_stack()
xx = x1
if fill_scanline_left_8bpp() goto skip
+2 -2
View File
@@ -26,14 +26,14 @@ graphics {
sub enable_bitmap_mode() {
; enable bitmap screen, erase it and set colors to black/white.
void cx16.screen_mode($80, false)
cx16.set_screen_mode($80)
cx16.GRAPH_init(0)
clear_screen(1, 0)
}
sub disable_bitmap_mode() {
; enables text mode, erase the text screen, color white
void cx16.screen_mode(0, false)
cx16.set_screen_mode(0)
cbm.CHROUT(147)
}
+15 -60
View File
@@ -7,6 +7,8 @@
; NOTE: For sake of speed, NO BOUNDS CHECKING is performed in most routines!
; You'll have to make sure yourself that you're not writing outside of bitmap boundaries!
%import buffers
monogfx {
%option ignore_unused
@@ -733,78 +735,31 @@ invert:
}}
}
sub fill(uword x, uword y, bool draw) {
sub fill(uword x, uword y, bool draw, ubyte stack_rambank) {
; Non-recursive scanline flood fill.
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
const ubyte MAXDEPTH = 100
word @zp xx = x as word
word @zp yy = y as word
word[MAXDEPTH] @split @shared stack_xl
word[MAXDEPTH] @split @shared stack_xr
word[MAXDEPTH] @split @shared stack_y
byte[MAXDEPTH] @shared stack_dy
cx16.r12L = 0 ; stack pointer
word x1
word x2
byte dy
stack.init(stack_rambank)
cx16.r10L = draw as ubyte
sub push_stack(word sxl, word sxr, word sy, byte sdy) {
if cx16.r12L==MAXDEPTH
return
cx16.r0s = sy+sdy
if cx16.r0s>=0 and cx16.r0s<=height-1 {
;; stack_xl[cx16.r12L] = sxl
;; stack_xr[cx16.r12L] = sxr
;; stack_y[cx16.r12L] = sy
;; stack_dy[cx16.r12L] = sdy
;; cx16.r12L++
%asm {{
ldy cx16.r12L
lda p8v_sxl
sta p8v_stack_xl_lsb,y
lda p8v_sxl+1
sta p8v_stack_xl_msb,y
lda p8v_sxr
sta p8v_stack_xr_lsb,y
lda p8v_sxr+1
sta p8v_stack_xr_msb,y
lda p8v_sy
sta p8v_stack_y_lsb,y
lda p8v_sy+1
sta p8v_stack_y_msb,y
ldy cx16.r12L
lda p8v_sdy
sta p8v_stack_dy,y
inc cx16.r12L
}}
stack.pushw(sxl as uword)
stack.pushw(sxr as uword)
stack.pushw(sy as uword)
stack.push(sdy as ubyte)
}
}
sub pop_stack() {
;; cx16.r12L--
;; x1 = stack_xl[cx16.r12L]
;; x2 = stack_xr[cx16.r12L]
;; y = stack_y[cx16.r12L]
;; dy = stack_dy[cx16.r12L]
%asm {{
dec cx16.r12L
ldy cx16.r12L
lda p8v_stack_xl_lsb,y
sta p8v_x1
lda p8v_stack_xl_msb,y
sta p8v_x1+1
lda p8v_stack_xr_lsb,y
sta p8v_x2
lda p8v_stack_xr_msb,y
sta p8v_x2+1
lda p8v_stack_y_lsb,y
sta p8v_yy
lda p8v_stack_y_msb,y
sta p8v_yy+1
ldy cx16.r12L
lda p8v_stack_dy,y
sta p8v_dy
}}
dy = stack.pop() as byte
yy = stack.popw() as word
x2 = stack.popw() as word
x1 = stack.popw() as word
yy+=dy
}
cx16.r11L = pget(xx as uword, yy as uword) as ubyte ; old_color
@@ -815,7 +770,7 @@ invert:
push_stack(xx, xx, yy, 1)
push_stack(xx, xx, yy + 1, -1)
word left = 0
while cx16.r12L!=0 {
while not stack.isempty() {
pop_stack()
xx = x1
if fill_scanline_left() goto skip
@@ -841,7 +796,7 @@ skip:
}
sub fill_scanline_left() -> bool {
; TODO maybe this could use vera auto decrement, but that would require some clever masking calculations
; TODO maybe this could use vera auto decrement, but that requires some clever masking calculations
cx16.r9s = xx
while xx >= 0 {
if pgetset()
@@ -852,7 +807,7 @@ skip:
}
sub fill_scanline_right() {
; TODO maybe this could use vera auto increment, but that would require some clever masking calculations
; TODO maybe this could use vera auto increment, but that requires some clever masking calculations
cx16.r9s = xx
while xx <= width-1 {
if pgetset()
+90 -71
View File
@@ -1,59 +1,113 @@
; Manipulate the Commander X16's display color palette.
; Should you want to restore the full default palette, you can call cbm.CINT()
; The first 16 colors can be restored to their default with set_default16()
; NOTE: assume R0, R1 and R2 are clobbered when using routines in this library!
palette {
%option ignore_unused
uword vera_palette_ptr
sub set_color(ubyte index, uword color) {
vera_palette_ptr = $fa00+(index as uword * 2)
cx16.vpoke(1, vera_palette_ptr, lsb(color))
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(color))
cx16.vaddr(1, $fa00+(index as uword * 2), 0, 1)
cx16.VERA_DATA0 = lsb(color)
cx16.VERA_DATA0 = msb(color)
cx16.VERA_ADDR_H &= 1
}
sub get_color(ubyte index) -> uword {
vera_palette_ptr = $fa00+(index as uword * 2)
return mkword(cx16.vpeek(1, vera_palette_ptr+1), cx16.vpeek(1, vera_palette_ptr))
cx16.vaddr(1, $fa00+(index as uword * 2), 0, 1)
cx16.r0L = cx16.VERA_DATA0
cx16.r0H = cx16.VERA_DATA0
cx16.VERA_ADDR_H &= 1
return cx16.r0
}
sub set_rgb_be(uword palette_ptr, uword num_colors) {
; 1 word per color entry, $0rgb in big endian format
vera_palette_ptr = $fa00
sub set_rgb_be(uword palette_ptr, uword num_colors, ubyte startindex) {
; 1 word per color entry, $0rgb in big endian format (split arrays)
cx16.vaddr(1, $fa00+(startindex as uword * 2), 0, 1)
repeat num_colors {
cx16.vpoke(1, vera_palette_ptr+1, @(palette_ptr))
cx16.VERA_DATA0 = @(palette_ptr+num_colors)
cx16.VERA_DATA0 = @(palette_ptr)
palette_ptr++
cx16.vpoke(1, vera_palette_ptr, @(palette_ptr))
palette_ptr++
vera_palette_ptr+=2
}
cx16.VERA_ADDR_H &= 1
}
sub set_rgb(uword palette_words_ptr, uword num_colors) {
; 1 word per color entry (in little endian format as layed out in video memory, so $gb;$0r)
vera_palette_ptr = $fa00
repeat num_colors*2 {
cx16.vpoke(1, vera_palette_ptr, @(palette_words_ptr))
sub set_rgb_be_nosplit(uword palette_ptr, uword num_colors, ubyte startindex) {
; 1 word per color entry, $0rgb in big endian format (linear arrays)
cx16.vaddr(1, $fa00+(startindex as uword * 2), 0, 1)
repeat num_colors {
cx16.VERA_DATA0 = @(palette_ptr+1)
cx16.VERA_DATA0 = @(palette_ptr)
palette_ptr += 2
}
cx16.VERA_ADDR_H &= 1
}
sub set_rgb(uword palette_words_ptr, uword num_colors, ubyte startindex) {
; 1 word per color entry (in little endian format as layed out in video memory, so $gb;$0r) (split arrays)
cx16.vaddr(1, $fa00+(startindex as uword * 2), 0, 1)
repeat num_colors {
cx16.VERA_DATA0 = @(palette_words_ptr)
cx16.VERA_DATA0 = @(palette_words_ptr+num_colors)
palette_words_ptr++
vera_palette_ptr++
}
cx16.VERA_ADDR_H &= 1
}
sub set_rgb8(uword palette_bytes_ptr, uword num_colors) {
sub set_rgb_nosplit(uword palette_words_ptr, uword num_colors, ubyte startindex) {
; 1 word per color entry (in little endian format as layed out in video memory, so $gb;$0r) (linear arrays)
cx16.vaddr(1, $fa00+(startindex as uword * 2), 0, 1)
repeat num_colors {
cx16.VERA_DATA0 = @(palette_words_ptr)
palette_words_ptr++
cx16.VERA_DATA0 = @(palette_words_ptr)
palette_words_ptr++
}
cx16.VERA_ADDR_H &= 1
}
sub set_rgb8(uword palette_bytes_ptr, uword num_colors, ubyte startindex) {
; 3 bytes per color entry, adjust color depth from 8 to 4 bits per channel.
vera_palette_ptr = $fa00
cx16.vaddr(1, $fa00+(startindex as uword * 2), 0, 1)
ubyte red
ubyte greenblue
repeat num_colors {
cx16.r1 = color8to4(palette_bytes_ptr)
palette_bytes_ptr+=3
cx16.vpoke(1, vera_palette_ptr, cx16.r1H) ; $GB
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, cx16.r1L) ; $0R
vera_palette_ptr++
cx16.VERA_DATA0 = cx16.r1H ; $GB
cx16.VERA_DATA0 = cx16.r1L ; $0R
}
cx16.VERA_ADDR_H &= 1
}
sub set_all_black() {
cx16.vaddr(1, $fa00, 0, 1)
repeat 256 {
cx16.VERA_DATA0 = 0
cx16.VERA_DATA0 = 0
}
cx16.VERA_ADDR_H &= 1
}
sub set_all_white() {
cx16.vaddr(1, $fa00, 0, 1)
repeat 256 {
cx16.VERA_DATA0 = $ff
cx16.VERA_DATA0 = $0f
}
cx16.VERA_ADDR_H &= 1
}
sub set_grayscale(ubyte startindex) {
; set 16 consecutive colors to a grayscale gradient from black to white
cx16.vaddr(1, $fa00+(startindex as uword * 2), 0, 1)
cx16.r0L=0
repeat 16 {
cx16.VERA_DATA0 = cx16.r0L
cx16.VERA_DATA0 = cx16.r0L
cx16.r0L += $11
}
cx16.VERA_ADDR_H &= 1
}
sub color8to4(uword colorpointer) -> uword {
@@ -70,42 +124,6 @@ palette {
return msb(channelvalue * $000f + 135)
}
sub set_monochrome(uword screencolorRGB, uword drawcolorRGB) {
vera_palette_ptr = $fa00
cx16.vpoke(1, vera_palette_ptr, lsb(screencolorRGB)) ; G,B
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(screencolorRGB)) ; R
vera_palette_ptr++
repeat 255 {
cx16.vpoke(1, vera_palette_ptr, lsb(drawcolorRGB)) ; G,B
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(drawcolorRGB)) ; R
vera_palette_ptr++
}
}
sub set_all_black() {
set_monochrome($000, $000)
}
sub set_all_white() {
set_monochrome($fff, $fff)
}
sub set_grayscale() {
; set first 16 colors to a grayscale gradient from black to white
vera_palette_ptr = $fa00
cx16.r2L=0
repeat 16 {
cx16.vpoke(1, vera_palette_ptr, cx16.r2L)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, cx16.r2L)
vera_palette_ptr++
cx16.r2L += $11
}
}
sub fade_step_multi(ubyte startindex, ubyte endindex, uword target_rgb) -> bool {
; Perform one color fade step for multiple consecutive palette entries.
; startindex = palette index of first color to fade
@@ -127,8 +145,9 @@ palette {
sub fade_step_colors(ubyte startindex, ubyte endindex, uword target_colors) -> bool {
; Perform one color fade step for multiple consecutive palette entries, to different target colors.
; startindex = palette index of first color to fade
; endindex = palette index of last color to fade
; target_colors = address of uword $RGB array of colors to fade towards
; endindex = palette index of last color to fade, inclusive
; target_colors = address of uword $RGB array of colors to fade towards,
; in *linear* storage format (@nosplit) which is usually how palette blobs are loaded from disk.
; Returns true if one or more colors were changed, false if no fade steps were done anymore.
; So you usually keep calling this until it returns false.
bool changed = false
@@ -192,7 +211,7 @@ palette {
sub set_c64pepto() {
; set first 16 colors to the "Pepto" PAL commodore-64 palette http://www.pepto.de/projects/colorvic/
uword[] colors = [
uword[] @nosplit colors = [
$000, ; 0 = black
$FFF, ; 1 = white
$833, ; 2 = red
@@ -210,12 +229,12 @@ palette {
$76e, ; 14 = light blue
$bbb ; 15 = light grey
]
set_rgb(colors, len(colors))
set_rgb_nosplit(colors, len(colors), 0)
}
sub set_c64ntsc() {
; set first 16 colors to a NTSC commodore-64 palette
uword[] colors = [
uword[] @nosplit colors = [
$000, ; 0 = black
$FFF, ; 1 = white
$934, ; 2 = red
@@ -233,12 +252,12 @@ palette {
$36f, ; 14 = light blue
$ccc ; 15 = light grey
]
set_rgb(colors, len(colors))
set_rgb_nosplit(colors, len(colors), 0)
}
sub set_default16() {
; set first 16 colors to the defaults on the X16
uword[] colors = [
uword[] @nosplit colors = [
$000, ; 0 = black
$fff, ; 1 = white
$800, ; 2 = red
@@ -256,6 +275,6 @@ palette {
$08f, ; 14 = light blue
$bbb ; 15 = light grey
]
set_rgb(colors, len(colors))
set_rgb_nosplit(colors, len(colors), 0)
}
}
+1 -1
View File
@@ -174,7 +174,7 @@ psg {
}
ubyte[16] envelope_states
uword[16] @split envelope_volumes ; scaled by 256
uword[16] envelope_volumes ; scaled by 256
ubyte[16] envelope_attacks
ubyte[16] envelope_sustains
ubyte[16] envelope_releases
+21 -1
View File
@@ -83,7 +83,27 @@ sprites {
}
sub pos_batch(ubyte first_spritenum, ubyte num_sprites, uword xpositions_ptr, uword ypositions_ptr) {
; -- note: the x and y positions word arrays must be regular arrays, they cannot be @split arrays!
; -- note: the x and y positions word arrays must both be split word arrays in this version of the routine! (the default)
sprite_reg = VERA_SPRITEREGS + 2 + first_spritenum*$0008
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
repeat num_sprites {
cx16.VERA_DATA0 = @(xpositions_ptr)
cx16.VERA_DATA1 = @(xpositions_ptr+num_sprites)
xpositions_ptr ++
}
sprite_reg += 2
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
repeat num_sprites {
cx16.VERA_DATA0 = @(ypositions_ptr)
cx16.VERA_DATA1 = @(ypositions_ptr+num_sprites)
ypositions_ptr ++
}
}
sub pos_batch_nosplit(ubyte first_spritenum, ubyte num_sprites, uword xpositions_ptr, uword ypositions_ptr) {
; -- note: the x and y positions word arrays must both be regular linear arrays in this version of the routine!
sprite_reg = VERA_SPRITEREGS + 2 + first_spritenum*$0008
cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
+20 -14
View File
@@ -397,7 +397,7 @@ cx16 {
extsub $ff4a = CLOSE_ALL(ubyte device @A) clobbers(A,X,Y)
extsub $ff59 = LKUPLA(ubyte la @A) clobbers(A,X,Y)
extsub $ff5c = LKUPSA(ubyte sa @Y) clobbers(A,X,Y)
extsub $ff5f = screen_mode(ubyte mode @A, bool getCurrent @Pc) -> ubyte @A, ubyte @X, ubyte @Y, bool @Pc ; also see SCREEN or get_screen_mode()
extsub $ff5f = screen_mode(ubyte mode @A, bool getCurrent @Pc) -> ubyte @A, ubyte @X, ubyte @Y, bool @Pc ; also see SCREEN or get/set_screen_mode()
extsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y)
extsub $ff6e = JSRFAR() ; following word = address to call, byte after that=rom/ram bank it is in
extsub $ff74 = fetch(ubyte zp_startaddr @A, ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A
@@ -422,7 +422,7 @@ extsub $ff41 = GRAPH_put_next_char(ubyte character @A) clobbers(A,X,Y) ; al
; framebuffer
extsub $fef6 = FB_init() clobbers(A,X,Y)
extsub $fef9 = FB_get_info() clobbers(X,Y) -> byte @A, uword @R0, uword @R1 ; width=r0, height=r1
extsub $fefc = FB_set_palette(uword pointer @R0, ubyte index @A, ubyte colorcount @X) clobbers(A,X,Y)
extsub $fefc = FB_set_palette(uword pointer @R0, ubyte index @A, ubyte colorcount @X) clobbers(A,X,Y) ; note: palette array must be @nosplit
extsub $feff = FB_cursor_position(uword x @R0, uword y @R1) clobbers(A,X,Y)
extsub $ff02 = FB_cursor_next_line(uword x @R0) clobbers(A,X,Y)
extsub $ff05 = FB_get_pixel() clobbers(X,Y) -> ubyte @A
@@ -565,8 +565,8 @@ const ubyte EXTAPI16_stack_enter_kernal_stack = $03
const ubyte EXTAPI16_stack_leave_kernal_stack = $04
asmsub set_screen_mode(ubyte mode @A) clobbers(A,X,Y) -> bool @Pc {
; -- convenience wrapper for screen_mode() to just set a new mode (and return success)
asmsub set_screen_mode(ubyte mode @A) clobbers(A,X,Y) {
; -- convenience wrapper for screen_mode() to just set a new mode and ignore any return values
%asm {{
clc
jmp screen_mode
@@ -1172,7 +1172,6 @@ _default_aflow_handler
asmsub set_vsync_irq_handler(uword address @AY) clobbers(A) {
; Sets the VSYNC irq handler to use with enable_irq_handlers(). Also enables VSYNC irqs.
; NOTE: unless a proper irq handler is already running, you should enclose this call in set_irqd() / clear_irqd() to avoid system crashes.
%asm {{
php
sei
@@ -1188,7 +1187,6 @@ asmsub set_vsync_irq_handler(uword address @AY) clobbers(A) {
asmsub set_line_irq_handler(uword rasterline @R0, uword address @AY) clobbers(A,Y) {
; Sets the LINE irq handler to use with enable_irq_handlers(), for the given rasterline. Also enables LINE irqs.
; You can use sys.set_rasterline() later to adjust the rasterline on which to trigger.
; NOTE: unless a proper irq handler is already running, you should enclose this call in set_irqd() / clear_irqd() to avoid system crashes.
%asm {{
php
sei
@@ -1206,7 +1204,6 @@ asmsub set_line_irq_handler(uword rasterline @R0, uword address @AY) clobbers(A,
asmsub set_sprcol_irq_handler(uword address @AY) clobbers(A) {
; Sets the SPRCOL irq handler to use with enable_irq_handlers(). Also enables SPRCOL irqs.
; NOTE: unless a proper irq handler is already running, you should enclose this call in set_irqd() / clear_irqd() to avoid system crashes.
%asm {{
php
sei
@@ -1221,7 +1218,6 @@ asmsub set_sprcol_irq_handler(uword address @AY) clobbers(A) {
asmsub set_aflow_irq_handler(uword address @AY) clobbers(A) {
; Sets the AFLOW irq handler to use with enable_irq_handlers(). Also enables AFLOW irqs.
; NOTE: unless a proper irq handler is already running, you should enclose this call in set_irqd() / clear_irqd() to avoid system crashes.
; NOTE: the handler itself must fill the audio fifo buffer to at least 25% full again (1 KB) or the aflow irq will keep triggering!
%asm {{
php
@@ -1374,12 +1370,22 @@ sys {
const ubyte target = 16 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
const ubyte sizeof_bool = 1
const ubyte sizeof_byte = 1
const ubyte sizeof_ubyte = 1
const ubyte sizeof_word = 2
const ubyte sizeof_uword = 2
const ubyte sizeof_float = 5
const ubyte SIZEOF_BOOL = 1
const ubyte SIZEOF_BYTE = 1
const ubyte SIZEOF_UBYTE = 1
const ubyte SIZEOF_WORD = 2
const ubyte SIZEOF_UWORD = 2
const ubyte SIZEOF_FLOAT = 5
const byte MIN_BYTE = -128
const byte MAX_BYTE = 127
const ubyte MIN_UBYTE = 0
const ubyte MAX_UBYTE = 255
const word MIN_WORD = -32768
const word MAX_WORD = 32767
const uword MIN_UWORD = 0
const uword MAX_UWORD = 65535
; MIN_FLOAT and MAX_FLOAT are defined in the floats module if imported
asmsub set_irq(uword handler @AY) clobbers(A) {
; Sets the handler for the VSYNC interrupt, and enable that interrupt.
Loaded 100 of 324 files, more files were not shown because too many files have changed in this diff. Show more