Compare commits

...
165 Commits
Author SHA1 Message Date
Irmen de Jong e403c4cf99 version 9.3 2023-08-12 17:58:10 +02:00
Irmen de Jong e3fbe37f9f fixed optimized code for >= and <= 2023-08-12 13:45:08 +02:00
Irmen de Jong dc870cd5ea fixed optimized code for > and < 2023-08-12 13:15:32 +02:00
Irmen de Jong 584be44743 fix compiler error on float comparison expressions 2023-08-12 00:09:38 +02:00
Irmen de Jong 5fffd35ec1 IR: fix augmented assignment operators 2023-08-11 18:24:37 +02:00
Irmen de Jong b92e22e4a6 IR: fix for loop over range with step 2023-08-11 03:05:47 +02:00
Irmen de Jong 3e6d16a7a8 add error message for invalid step size in range expression 2023-08-11 02:35:52 +02:00
Irmen de Jong ecbcc277b8 improve -varshigh documentation 2023-08-10 00:17:50 +02:00
Irmen de Jong dff1d9e4dd cleanup range expression doc 2023-08-09 22:58:04 +02:00
Irmen de Jong 7c0bde7310 parser: allow curly brace on next line for asmsub too
downgrade antlr4 one version again to what is used in IntelliJ's antlr plugin, to avoid potential version conflicts
2023-08-09 20:01:12 +02:00
Irmen de Jong a82d21ac05 fixed gfx2.plot in mode 1+5 with certain combinations of color and stipple 2023-08-08 00:01:43 +02:00
Irmen de Jong 0bf8378fcb fixed gfx2.horizontal_line problem with monochrome stippling mode (regression since version 9.0)
todo
2023-08-07 22:56:07 +02:00
Irmen de Jong 017ef8a837 optimization of > and <= in expressions 2023-08-07 21:23:31 +02:00
Irmen de Jong 0d63cdcb96 optimization of < and >= in expressions 2023-08-07 04:54:35 +02:00
Irmen de Jong 68a6f99c9f optimization of < in expressions 2023-08-07 02:32:07 +02:00
Irmen de Jong 60781bcfc4 optimization of == and != in expressions 2023-08-07 01:25:41 +02:00
Irmen de Jong 77fa2e2722 optimization in + or - assignment to word array 2023-08-05 23:28:40 +02:00
Irmen de Jong c36afd872e optimization in assignment to memory 2023-08-04 23:54:11 +02:00
Irmen de Jong 7e58a4c130 optimization in assignment to array 2023-08-04 23:06:55 +02:00
Irmen de Jong 19a4bf1088 clean up AugmentableAssignmentAsmGen a bit 2023-08-04 21:48:02 +02:00
Irmen de Jong 9678bbae4b dedup 2023-08-02 23:19:52 +02:00
Irmen de Jong a4d093afa1 added -sourcelines cli option to include src lines in generated assembly (which is now off by default) 2023-08-02 23:05:24 +02:00
Irmen de Jong ba788bcf0f put the original p8 source lines into the generated assembly as comments (not only the line numbers). 2023-08-02 02:18:13 +02:00
Irmen de Jong f2c62bee7e docs 2023-08-01 22:49:55 +02:00
Irmen de Jong 548721e306 docs 2023-07-31 22:17:43 +02:00
Irmen de Jong 1ae950a638 Merge branch 'remove_evalstack'
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt
2023-07-31 21:57:43 +02:00
Irmen de Jong c9385e93fe fix postincr/decr on indexed pointervariables 2023-07-31 20:13:49 +02:00
Irmen de Jong 9bb16e293c vm: fix postincr/decr on indexed pointervariables 2023-07-31 19:37:30 +02:00
Irmen de Jong c223702ea0 code cleanups 2023-07-30 18:42:45 +02:00
Irmen de Jong 9167ba499d Merge branch 'remove_evalstack' 2023-07-30 17:49:35 +02:00
Irmen de Jong 2d7e95e1b6 release 9.2.1 2023-07-30 17:39:18 +02:00
Irmen de Jong 0cba736446 Merge branch 'master' into remove_evalstack 2023-07-30 14:53:40 +02:00
Irmen de Jong 0816a57032 never add rts to inline asmsubs and always inline them regardless of optimization setting
otherwise they can't specify a sequence of assembly instructions that should be inserted in-place, such as those that manipulate the cpu stack.
for instance cx16.irqsafe_set_irqd() / cx16.irqsafe_clear_irqd()
2023-07-30 14:52:37 +02:00
Irmen de Jong a0ab0bd3e2 Merge branch 'master' into remove_evalstack
# Conflicts:
#	examples/test.p8
2023-07-29 18:57:06 +02:00
Irmen de Jong b89ad4b328 don't optimize empty where choice away! It would call the else clause incorrectly. 2023-07-29 18:25:52 +02:00
Irmen de Jong 6cda76a116 comments 2023-07-29 17:32:27 +02:00
Irmen de Jong c112b327ab tiny optimization 2023-07-29 17:04:41 +02:00
Irmen de Jong 46c12a8899 fix byte in array assignment,
remove no longer needed array assignment ast transformation
2023-07-28 22:40:06 +02:00
Irmen de Jong c5219dfb3f fix assignment of register into byte array 2023-07-28 22:16:01 +02:00
Irmen de Jong 4a8ee6815a merge 2023-07-28 03:34:58 +02:00
Irmen de Jong e1b6bb154a Merge branch 'master' into remove_evalstack
# Conflicts:
#	compiler/res/prog8lib/cx16/gfx2.p8
#	docs/source/todo.rst
#	examples/test.p8
2023-07-28 02:09:45 +02:00
Irmen de Jong b19c282269 release 9.2 2023-07-28 01:40:14 +02:00
Irmen de Jong e520921746 todo 2023-07-26 23:16:43 +02:00
Irmen de Jong 970642244b optimized gfx2.text() for hires 4c mode 2023-07-26 04:17:44 +02:00
Irmen de Jong 3b90be2d9e gfx2.text() per-pixel positioning implemented for screen modes 1 and 5 2023-07-25 00:43:45 +02:00
Irmen de Jong 2f756f1e3a fix and optimize inplace invert and negate 2023-07-24 23:28:32 +02:00
Irmen de Jong 78e84182f0 todo 2023-07-24 22:36:17 +02:00
Irmen de Jong 65a7a8caf8 fix and optimize gfx2.position2(), added cx16.vaddr_clone() 2023-07-24 00:04:47 +02:00
Irmen de Jong 4c6a2f5df9 emphasize index value size on pointer var indexing 2023-07-23 00:11:18 +02:00
Irmen de Jong fea297e409 cleanup some compilation warnings 2023-07-22 23:44:26 +02:00
Irmen de Jong 7cf6aba625 Merge branch 'master' into remove_evalstack
# Conflicts:
#	examples/test.p8
2023-07-22 23:37:20 +02:00
Irmen de Jong 3bbc00cc8c more caution notices about symbols in inlined asm 2023-07-22 23:22:06 +02:00
Irmen de Jong 70ed2b4203 fix compilation of large bitshifts 2023-07-22 23:08:22 +02:00
Irmen de Jong 0adce9b9c6 removed complexity restriction on array indexing expressions 2023-07-22 22:11:30 +02:00
Irmen de Jong 0e781d18fa cx16: added cx16.vaddr_autoincr() and cx16.vaddr_autodecr() 2023-07-21 23:04:21 +02:00
Irmen de Jong 4575a8fffe cx16: added cx16.vaddr_autoincr() and cx16.vaddr_autodecr() 2023-07-21 22:40:07 +02:00
Irmen de Jong 10d0ff252b ignore buildversion changes 2023-07-21 00:14:06 +02:00
Irmen de Jong c7d54570cc IR: sXX, CONCAT instructions now use 3 register format 2023-07-21 00:07:56 +02:00
Irmen de Jong 7136b33f2e cx16: change reset_system() to use Reset SMC sequence instead of hard reboot 2023-07-20 01:59:20 +02:00
Irmen de Jong 70a78e74f6 get rid of binexpr splitter 2023-07-20 01:36:43 +02:00
Irmen de Jong d5707b7bf3 rebuilding floating point stack evaluation (using cpu stack) 2023-07-20 00:45:04 +02:00
Irmen de Jong 9f247901d4 Merge branch 'master' into remove_evalstack
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt
#	codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt
#	compiler/src/prog8/buildversion/BuildVersion.kt
#	examples/test.p8
2023-07-16 23:45:04 +02:00
Irmen de Jong 5659742d97 fixed assigning byte to word not clearing msb sometimes 2023-07-16 23:16:32 +02:00
Irmen de Jong 450eaf7c4a fixed lsb() to uword problem 2023-07-16 20:05:59 +02:00
Irmen de Jong 47485e4b49 added more missing codegen for bit shifts 2023-07-16 17:42:10 +02:00
Irmen de Jong 64254e758d cleaned up cx16 keyboardhandler example and some compiler warnings for redundant else 2023-07-16 13:23:53 +02:00
Irmen de Jong c1aa5d4e47 IR: optimized when statement translation 2023-07-16 12:10:46 +02:00
Irmen de Jong ab8173637a remove redundant asm optimizer 2023-07-16 11:15:28 +02:00
Irmen de Jong 3841cef497 implemented missing bitshift codegen (non-stack) 2023-07-15 22:26:56 +02:00
Irmen de Jong b717f1c7eb little refactor of huge if statement 2023-07-15 22:26:56 +02:00
Irmen de Jong da57f76de3 fix augassign 2023-07-15 22:26:56 +02:00
Irmen de Jong 4784f1c65a remove eval stack from documentation 2023-07-15 22:26:56 +02:00
Irmen de Jong 41af63b333 remove even more X register save/restore code 2023-07-15 22:26:54 +02:00
Irmen de Jong e2bb0de24d clean up X register save/store in compiler code, remove temp vars for register saving 2023-07-15 22:25:58 +02:00
Irmen de Jong b791fae9ce remove stack based ExpressionAsmGen 2023-07-15 22:24:22 +02:00
Irmen de Jong 6033a9e20c remove optfloatx option 2023-07-15 22:24:22 +02:00
Irmen de Jong 9e8c8973d8 remove eval stack references from asm code 2023-07-15 22:24:22 +02:00
Irmen de Jong 3933bf5c1a remove eval stack references from p8 code 2023-07-15 22:24:22 +02:00
Irmen de Jong 708e296774 remove eval stack assignment source and targets 2023-07-15 22:24:22 +02:00
Irmen de Jong 84925ab69c remove eval stack options 2023-07-15 22:24:19 +02:00
Irmen de Jong b3cb9b7fe2 added optimizer to remove needless pha/pla pairs 2023-07-15 22:19:48 +02:00
Irmen de Jong 9cb61fa34d tweaks 2023-07-15 20:46:14 +02:00
Irmen de Jong 7c219d235c fixed possible type mismatch error in when statements 2023-07-14 23:35:58 +02:00
Irmen de Jong 6938c79f88 IR: added CMPI instruction 2023-07-14 23:17:29 +02:00
Irmen de Jong b8284a147d allow boolean when conditions, optimize into a regular if 2023-07-11 21:33:29 +02:00
Irmen de Jong 15ee90e99c no error about missing target when -vm is used.
also version 9.1
2023-07-11 18:13:49 +02:00
Irmen de Jong 795f80b4ec fix forloop 6502 codegen in case of descending word values 2023-07-11 00:33:12 +02:00
Irmen de Jong 6b6427492d fix forloop 6502 codegen in case of descending word values 2023-07-10 23:10:16 +02:00
Irmen de Jong 6055b8c3dc IR: fix forloop codegen for steps != 1 2023-07-10 21:36:44 +02:00
Irmen de Jong a98cb50d55 Revert "ir: SCC now sets all bits to 1 (or 0)"
This reverts commit 7245aece4f.
2023-07-09 23:16:13 +02:00
Irmen de Jong e98bbc1c52 todo 2023-07-09 22:29:54 +02:00
Irmen de Jong 7245aece4f ir: SCC now sets all bits to 1 (or 0) 2023-07-08 23:16:01 +02:00
Irmen de Jong 60cbb02822 vm: actually fix EXT(S) in vm too 2023-07-08 23:05:03 +02:00
Irmen de Jong 4e863ecdac vm: fixed abs() and word-to-string conversion 2023-07-08 22:57:16 +02:00
Irmen de Jong 5037033fcf ir: EXT and EXTS opcodes now have 2 registers to avoid type clash 2023-07-08 22:42:11 +02:00
Irmen de Jong e6b158bc97 @(..) argument must be of type UWORD 2023-07-08 22:34:47 +02:00
Irmen de Jong 4cc0dfa10b comment 2023-07-08 11:42:29 +02:00
Irmen de Jong 4ced8889d3 cx16: fix signature return values of cx16.screen_mode(), add get_screen_mode() and set_screen_mode() convenience routines 2023-07-08 11:37:29 +02:00
Irmen de Jong d26967a87d ir doc 2023-07-07 22:35:05 +02:00
Irmen de Jong fc8955941b slight optimization for certain word multiplications 2023-07-07 21:30:37 +02:00
Irmen de Jong 071a80360f ir: fix some problem with comparison against zero 2023-07-07 21:17:28 +02:00
Irmen de Jong d2154f5f2e remove empty when choices, fixes ir compilation error on those 2023-07-07 20:34:24 +02:00
Irmen de Jong 334d382bfa ir: JUMPI instruction added to support indirect jumps 2023-07-07 19:10:39 +02:00
Irmen de Jong 90c4b00f74 ir: fix any() all() reverse() sort() on memory mapped arrays and on byte arrays 2023-07-07 17:25:32 +02:00
Irmen de Jong 71261525e8 fix containment check on memory mapped arrays 2023-07-07 17:07:34 +02:00
Irmen de Jong 3126959576 ir: several fixes 2023-07-07 16:53:32 +02:00
Irmen de Jong 02e51d8282 ir: fix initial chunk linking 2023-07-07 00:30:56 +02:00
Irmen de Jong ffb2027a19 repeat loop count now always rounded to integer 2023-07-06 23:58:02 +02:00
Irmen de Jong 70c9ab9074 upgrade libraries 2023-07-06 23:33:58 +02:00
Irmen de Jong 6d1fdf1ba6 upgrade to Kotlin 1.9.0 2023-07-06 23:03:47 +02:00
Irmen de Jong 1f7180d9a8 math.multiply_words returns lower 16 bits of the result also in AY (to avoid repeating some load instructions) 2023-07-06 22:54:13 +02:00
Irmen de Jong b4e94ae4dd optimizer: avoid symbol name clash when inlining subroutine 2023-07-05 23:15:04 +02:00
Irmen de Jong 07c606bfc9 optimizer: don't replace for loop with repeat loop (the loop variable might be used elsewhere!) 2023-07-05 21:16:17 +02:00
Irmen de Jong e705a8bd89 discord info 2023-07-04 23:50:16 +02:00
Irmen de Jong b3bdfb7f1f more info about building the compiler 2023-07-04 22:41:38 +02:00
Irmen de Jong 5af1aeb092 added block comment /* ...... */ 2023-07-04 00:46:29 +02:00
Irmen de Jong be64fa674a doc 2023-07-03 22:44:50 +02:00
Irmen de Jong 204f5591a9 todos 2023-07-03 21:57:32 +02:00
Irmen de Jong ee3e3a3a40 optimize text rendering in gfx2 2023-07-03 21:45:09 +02:00
Irmen de Jong f9200a2b75 fix IR loader for romsub calls (calls to an address) 2023-07-02 23:41:15 +02:00
Irmen de Jong f570b70827 fix type error with returning an array from a subroutine returning uword 2023-07-02 22:09:19 +02:00
Irmen de Jong 0db141eeac todo 2023-07-02 21:19:33 +02:00
Irmen de Jong acb2ee53bb Merge branch 'prefixing' 2023-07-02 21:15:30 +02:00
Irmen de Jong c544b7f5ba fixing up p8_ prefixing 2023-07-02 21:15:05 +02:00
Irmen de Jong c0024e97e5 fix doc version 2023-07-02 21:01:11 +02:00
Irmen de Jong bdf8aa9168 get rid of newexpr compiler option 2023-07-02 15:26:04 +02:00
Irmen de Jong de5ce0f515 tiny optimization and doc 2023-07-02 11:17:18 +02:00
Irmen de Jong bb95484c8a uniform symbol prefixing with p8_ 2023-07-02 06:15:09 +02:00
Irmen de Jong cad18b8a3a uniform symbol prefixing with p8_ 2023-07-02 06:15:02 +02:00
Irmen de Jong 0f6a98751a tiny optimization 2023-07-02 06:13:22 +02:00
Irmen de Jong aac5a4c27f optimize word repeat loop codegen 2023-07-02 04:51:22 +02:00
Irmen de Jong d3f6415387 vm: fix repeat 256 2023-07-02 02:38:35 +02:00
Irmen de Jong 04da44eb98 fix certain inefficient codegen when assigning a type casted value 2023-06-29 22:56:26 +02:00
Irmen de Jong 7649be97b1 add git hash to compiler header output 2023-06-29 21:01:02 +02:00
Irmen de Jong c9ef777e0f fix rest of possible temp variable conflicts 2023-06-28 23:24:48 +02:00
Irmen de Jong c0cb2438d5 1-letter symbols now also prefixed with 'p8p_'
to avoid assembly errors caused by confusing variable 'a' with register 'a' etc.
2023-06-28 23:17:59 +02:00
Irmen de Jong 30c531b39e attempting to fix array expression inplace assign 2023-06-28 00:38:08 +02:00
Irmen de Jong bf703a8a66 unittest 2023-06-27 23:43:35 +02:00
Irmen de Jong e7b631b087 allow comment lines inside array initializer value 2023-06-27 23:30:37 +02:00
Irmen de Jong a9f5dc036c fix cpu stack corruption in array assignment codegen 2023-06-27 18:49:49 +02:00
Irmen de Jong 0a83b51e00 allow more curly brace styles 2023-06-27 01:59:22 +02:00
Irmen de Jong eab63ecc6c allow curly brace on next line also after subroutine and when 2023-06-27 01:29:25 +02:00
Irmen de Jong b0794cf35e added hiram bank number to -varshigh 2023-06-27 00:27:34 +02:00
Irmen de Jong 5b9e71a27d docs 2023-06-25 21:35:30 +02:00
Irmen de Jong eae41de27d improve errors generated for undefined symbols 2023-06-25 15:19:51 +02:00
Irmen de Jong e9163aa3a7 added cx16.save_virtual_registers() and cx16.restore_virtual_registers() 2023-06-24 21:04:47 +02:00
Irmen de Jong 8c617515ba don't prefix 3-letter symbols too aggressively (could cause some compilation errors) 2023-06-23 23:36:59 +02:00
Irmen de Jong 04e4e71f2e uword == str is now possible (sugar for string.compare) 2023-06-22 00:20:30 +02:00
Irmen de Jong a587482edf optimize dangling else 2023-06-18 13:46:02 +02:00
Irmen de Jong 0aac9350d5 rename math.atan() to math.atan2() 2023-06-18 13:05:36 +02:00
Irmen de Jong f56c12ee4e cx16 spotlight example 2023-06-18 12:49:22 +02:00
Irmen de Jong 4bb9ae61f2 library source links 2023-06-18 02:31:45 +02:00
Irmen de Jong ff7f3484e4 atan 2023-06-17 23:01:47 +02:00
Irmen de Jong 5da3abe6b4 fix silent typecast on return statements that could lose data (word->byte) 2023-06-17 14:44:36 +02:00
Irmen de Jong c0b398e0ce add various math.atan() routines 2023-06-17 00:43:33 +02:00
Irmen de Jong 3de10adac2 bump required 64tass version 2023-06-16 23:24:31 +02:00
Irmen de Jong 1b573d6552 add note about lacking fp parse routine 2023-06-16 00:12:52 +02:00
Irmen de Jong 2a96f93919 vm: fix compiler error when dealing with label 2023-06-14 22:14:47 +02:00
Irmen de Jong c6b2639ca4 fix compiler crash due to missing 6502 codegen
(assigning a direct memory read byte to a cx16 virtual register)
2023-06-14 21:10:01 +02:00
Irmen de Jong b9abf37a7e fix invalid code when subroutines are defined in a repeat loop 2023-06-13 00:46:32 +02:00
Irmen de Jong 373cbb4144 gradle build error explained 2023-06-11 17:51:18 +02:00
Irmen de Jong a521982576 fix subroutine inline problem with strings 2023-06-09 21:45:05 +02:00
Irmen de Jong a77fde577c update GitHub action steps 2023-06-09 19:51:04 +02:00
Irmen de Jong ea6926e57d fix float expression crash: fl = abs/sqrt (fl)+0.5 2023-06-09 19:28:34 +02:00
Irmen de Jong ba25b7fee6 fix diskio.diskname(). cx16: add diskio.curdir() 2023-06-07 22:38:51 +02:00
184 changed files with 6808 additions and 7499 deletions

No files matched your search

+2 -2
View File
@@ -10,7 +10,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: build and install recent 64tass
run: |
@@ -21,7 +21,7 @@ jobs:
sudo make install
- name: Set up JDK 11
uses: actions/setup-java@v2
uses: actions/setup-java@v3
with:
java-version: 11
distribution: adopt
+3
View File
@@ -15,6 +15,7 @@ out/
parser/**/*.interp
parser/**/*.tokens
parser/**/*.java
compiler/src/prog8/buildversion/*
*.py[cod]
*.egg
*.egg-info
@@ -29,6 +30,8 @@ parsetab.py
compiler/lib/
.gradle
**/BuildVersion.kt
/prog8compiler.jar
sd*.img
*.d64
+1 -1
View File
@@ -4,6 +4,6 @@
<option name="jvmTarget" value="11" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.21-release-380" />
<option name="version" value="1.9.0-release-358" />
</component>
</project>
+2 -2
View File
@@ -1,8 +1,8 @@
<component name="libraryTable">
<library name="michael.bull.kotlin.result.jvm" type="repository">
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.17" />
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.17/kotlin-result-jvm-1.1.17.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.18/kotlin-result-jvm-1.1.18.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.8.10/kotlin-stdlib-jdk8-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.10/kotlin-stdlib-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
+15
View File
@@ -14,6 +14,21 @@ Documentation
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
https://prog8.readthedocs.io/
How to get it/build it
----------------------
- Download the latest [official release](https://github.com/irmen/prog8/releases) from github.
- Or, if you want/need a bleeding edge development version, you can:
- download a build artifact zipfile from a recent [github action build](https://github.com/irmen/prog8/actions).
- you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html).
Community
---------
Most of the development on Prog8 and the use of it is currently centered around
the [Commander X16](https://www.commanderx16.com/) retro computer. Their [discord server](https://discord.gg/nS2PqEC) contains a small channel
dedicated to Prog8. Other than that, use the issue tracker on github.
Software license
----------------
GNU GPL 3.0 (see file LICENSE), with exception for generated code:
+1 -1
View File
@@ -26,7 +26,7 @@ compileTestKotlin {
dependencies {
// should have no dependencies to other modules
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.17"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
}
sourceSets {
+14 -6
View File
@@ -80,6 +80,16 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
}
override fun lookup(scopedName: String) = flat[scopedName]
fun getLength(name: String): Int? {
val node = flat[name]
return when(node) {
is StMemVar -> node.length
is StMemorySlab -> node.size.toInt()
is StStaticVariable -> node.length
else -> null
}
}
}
@@ -203,8 +213,7 @@ class StStaticVariable(name: String,
class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) :
StNode(name, StNodeType.CONSTANT, astNode) {
}
StNode(name, StNodeType.CONSTANT, astNode)
class StMemVar(name: String,
@@ -226,12 +235,11 @@ class StMemorySlab(
val align: UInt,
astNode: PtNode
):
StNode(name, StNodeType.MEMORYSLAB, astNode) {
}
StNode(name, StNodeType.MEMORYSLAB, astNode)
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode) :
StNode(name, StNodeType.SUBROUTINE, astNode) {
}
StNode(name, StNodeType.SUBROUTINE, astNode)
class StRomSub(name: String,
+3 -2
View File
@@ -26,8 +26,6 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
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("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 128u, Position.DUMMY),
PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 128u, Position.DUMMY)
).forEach {
it.parent = program
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
@@ -94,6 +92,9 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
initialString = null
numElements = node.arraySize?.toInt()
}
// if(node.type in SplitWordArrayTypes) {
// ... split array also add _lsb and _msb to symboltable?
// }
StStaticVariable(node.name, node.type, initialNumeric, initialString, initialArray, numElements, node.zeropage, node)
}
is PtBuiltinFunctionCall -> {
+14 -10
View File
@@ -37,16 +37,17 @@ class PtNodeGroup : PtNode(Position.DUMMY)
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
// Note that as an exception, the 'name' is not read-only
// but a var. This is to allow for cheap node renames.
val scopedName: String by lazy {
var namedParent: PtNode = this.parent
if(namedParent is PtProgram)
name
else {
while (namedParent !is PtNamedNode)
namedParent = namedParent.parent
namedParent.scopedName + "." + name
val scopedName: String
get() {
var namedParent: PtNode = this.parent
return if(namedParent is PtProgram)
name
else {
while (namedParent !is PtNamedNode)
namedParent = namedParent.parent
namedParent.scopedName + "." + name
}
}
}
}
@@ -63,7 +64,9 @@ class PtProgram(
children.asSequence().filterIsInstance<PtBlock>()
fun entrypoint(): PtSub? =
allBlocks().firstOrNull { it.name == "main" }?.children?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start") } as PtSub?
allBlocks().firstOrNull { it.name == "main" || it.name=="p8_main" }
?.children
?.firstOrNull { it is PtSub && (it.name == "start" || it.name=="main.start" || it.name=="p8_start" || it.name=="p8_main.p8_start") } as PtSub?
}
@@ -71,6 +74,7 @@ class PtBlock(name: String,
val address: UInt?,
val library: Boolean,
val forceOutput: Boolean,
val noSymbolPrefixing: Boolean,
val alignment: BlockAlignment,
val source: SourceCode, // taken from the module the block is defined in.
position: Position
@@ -118,6 +118,9 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
val splitWords: Boolean
get() = variable.type in SplitWordArrayTypes
val usesPointerVariable: Boolean
get() = variable.type==DataType.UWORD
init {
require(elementType in NumericDatatypes)
}
@@ -126,7 +126,6 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
else
"->"
}
else -> throw InternalCompilerException("unrecognised ast node $node")
}
}
@@ -85,7 +85,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"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.values())), DataType.UBYTE),
"sizeof" to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE),
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
"sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
@@ -124,9 +124,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null),
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
"rsave" to FSignature(false, emptyList(), null),
"rsavex" to FSignature(false, emptyList(), null),
"rrestore" to FSignature(false, emptyList(), null),
"rrestorex" 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),
)
@@ -13,16 +13,13 @@ class CompilationOptions(val output: OutputType,
val compTarget: ICompilationTarget,
// these are set later, based on command line arguments or options in the source code:
var loadAddress: UInt,
var slowCodegenWarnings: Boolean = false,
var optimize: Boolean = false,
var optimizeFloatExpressions: Boolean = false,
var asmQuiet: Boolean = false,
var asmListfile: Boolean = false,
var includeSourcelines: Boolean = false,
var experimentalCodegen: Boolean = false,
var varsHigh: Boolean = false,
var useNewExprCode: Boolean = false,
var varsHighBank: Int? = null,
var splitWordArrays: Boolean = false,
var evalStackBaseAddress: UInt? = null,
var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap()
) {
@@ -3,6 +3,7 @@ package prog8.code.core
interface IErrorReporter {
fun err(msg: String, position: Position)
fun warn(msg: String, position: Position)
fun undefined(symbol: List<String>, position: Position)
fun noErrors(): Boolean
fun report()
fun finalizeNumErrors(numErrors: Int, numWarnings: Int) {
@@ -13,8 +13,6 @@ interface IMachineDefinition {
val FLOAT_MAX_NEGATIVE: Double
val FLOAT_MAX_POSITIVE: Double
val FLOAT_MEM_SIZE: Int
var ESTACK_LO: UInt
var ESTACK_HI: UInt
val PROGRAM_LOAD_ADDRESS : UInt
val BSSHIGHRAM_START: UInt
val BSSHIGHRAM_END: UInt
@@ -29,11 +27,4 @@ interface IMachineDefinition {
fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
fun isIOAddress(address: UInt): Boolean
fun overrideEvalStack(evalStackBaseAddress: UInt) {
require(evalStackBaseAddress and 255u == 0u)
ESTACK_LO = evalStackBaseAddress
ESTACK_HI = evalStackBaseAddress + 256u
require(ESTACK_LO !in golden.region && ESTACK_HI !in golden.region) { "user-set ESTACK can't be in GOLDEN ram" }
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
package prog8.code.core
import prog8.code.core.SourceCode.Companion.libraryFilePrefix
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
import java.nio.file.InvalidPathException
import kotlin.io.path.Path
import kotlin.io.path.absolute
@@ -10,7 +10,7 @@ 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(file.startsWith(LIBRARYFILEPREFIX))
return "$file:$line:$startCol:"
return try {
val path = Path(file).absolute().normalize().toString()
+37 -5
View File
@@ -54,12 +54,14 @@ 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:"
const val LIBRARYFILEPREFIX = "library:"
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))
!(pathString.startsWith(LIBRARYFILEPREFIX) || pathString.startsWith(STRINGSOURCEPREFIX))
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
}
/**
@@ -69,7 +71,7 @@ sealed class SourceCode {
class Text(override val text: String): SourceCode() {
override val isFromResources = false
override val isFromFilesystem = false
override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}"
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
override val name = "<unnamed-text>"
}
@@ -110,7 +112,7 @@ sealed class SourceCode {
override val isFromResources = true
override val isFromFilesystem = false
override val origin = "$libraryFilePrefix$normalized"
override val origin = "$LIBRARYFILEPREFIX$normalized"
override val text: String
override val name: String
@@ -139,3 +141,33 @@ sealed class SourceCode {
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
}
}
@@ -13,10 +13,6 @@ class AtariMachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = 6
override val PROGRAM_LOAD_ADDRESS = 0x2000u
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
override var ESTACK_LO = 0x1b00u // $1b00-$1b7f inclusive // TODO
override var ESTACK_HI = 0x1b80u // $1b80-$1bff inclusive // TODO
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
@@ -15,10 +15,6 @@ class C128MachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
override var ESTACK_LO = 0x1b00u // $1b00-$1b7f inclusive
override var ESTACK_HI = 0x1b80u // $1b80-$1bff inclusive
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
@@ -16,12 +16,8 @@ class C64MachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0801u
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
override var ESTACK_LO = 0xcf00u // $cf00-$cf7f inclusive
override var ESTACK_HI = 0xcf80u // $cf80-$cfff inclusive
override val BSSHIGHRAM_START = 0xc000u
override val BSSHIGHRAM_END = ESTACK_LO
override val BSSHIGHRAM_END = 0xd000u
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
@@ -62,7 +58,7 @@ class C64MachineDefinition: IMachineDefinition {
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = C64Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, 0xc000u until ESTACK_LO)
golden = GoldenRam(compilerOptions, 0xc000u until 0xd000u)
}
}
@@ -15,10 +15,6 @@ class CX16MachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val PROGRAM_LOAD_ADDRESS = 0x0801u
// the 2*128 byte evaluation stack (1 page, on which bytes, words, and even floats are stored during calculations)
override var ESTACK_LO = 0x0700u // $0700-$077f inclusive
override var ESTACK_HI = 0x0780u // $0780-$07ff inclusive
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
override val BSSHIGHRAM_END = 0xc000u // rom starts here.
@@ -64,7 +60,7 @@ class CX16MachineDefinition: IMachineDefinition {
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = CX16Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, 0x0400u until ESTACK_LO)
golden = GoldenRam(compilerOptions, 0x0400u until 0x0800u)
}
}
@@ -40,7 +40,6 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
}
val distinctFree = free.distinct()
@@ -15,8 +15,6 @@ class VirtualMachineDefinition: IMachineDefinition {
override val FLOAT_MEM_SIZE = 4 // 32-bits floating point
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
override var ESTACK_LO = 0u // not actually used
override var ESTACK_HI = 0u // not actually used
override val BSSHIGHRAM_START = 0u // not actually used
override val BSSHIGHRAM_END = 0u // not actually used
override lateinit var zeropage: Zeropage // not actually used
+1 -1
View File
@@ -28,7 +28,7 @@ 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:1.1.17"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
}
+238 -172
View File
@@ -3,6 +3,7 @@ package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold
import prog8.code.StNodeType
import prog8.code.SymbolTable
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.*
@@ -14,18 +15,178 @@ import kotlin.io.path.writeLines
internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1"
internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
class AsmGen6502: ICodeGeneratorBackend {
class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
override fun generate(
program: PtProgram,
symbolTable: SymbolTable,
options: CompilationOptions,
errors: IErrorReporter
): IAssemblyProgram? {
val asmgen = AsmGen6502Internal(program, symbolTable, options, errors)
val st = if(prefixSymbols) prefixSymbols(program, options, symbolTable) else symbolTable
val asmgen = AsmGen6502Internal(program, st, options, errors)
return asmgen.compileToAssembly()
}
private fun prefixSymbols(program: PtProgram, options: CompilationOptions, st: SymbolTable): SymbolTable {
val nodesToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index
val functionCallsToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index
fun prefixNamedNode(node: PtNamedNode) {
node.name = "p8_${node.name}"
}
fun prefixSymbols(node: PtNode) {
when(node) {
is PtAsmSub -> {
prefixNamedNode(node)
node.parameters.forEach { (_, param) -> prefixNamedNode(param) }
}
is PtSub -> {
prefixNamedNode(node)
node.parameters.forEach { prefixNamedNode(it) }
}
is PtFunctionCall -> {
val stNode = st.lookup(node.name)!!
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
functionCallsToPrefix += node.parent to index
}
}
is PtIdentifier -> {
var lookupName = node.name
if(node.type in SplitWordArrayTypes && (lookupName.endsWith("_lsb") || lookupName.endsWith("_msb"))) {
lookupName = lookupName.dropLast(4)
}
val stNode = st.lookup(lookupName)!!
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
}
is PtJump -> {
if(node.identifier!=null) {
val stNode = st.lookup(node.identifier!!.name)!!
if(stNode.astNode.definingBlock()?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
}
else if(node.generatedLabel!=null) {
val stNode = st.lookup(node.generatedLabel!!)!!
if(stNode.astNode.definingBlock()?.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)
is PtMemMapped -> prefixNamedNode(node)
is PtSubroutineParameter -> prefixNamedNode(node)
is PtVariable -> {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
else -> { }
}
node.children.forEach { prefixSymbols(it) }
}
program.allBlocks().forEach { block ->
if (!block.noSymbolPrefixing) {
prefixSymbols(block)
}
}
nodesToPrefix.forEach { (parent, index) ->
val node = parent.children[index]
when(node) {
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(st)
else -> throw AssemblyError("weird node to prefix $node")
}
}
// reversed so inner calls (such as arguments to a function call) get processed before the actual function call itself
functionCallsToPrefix.reversed().forEach { (parent, index) ->
val node = parent.children[index]
if(node is PtFunctionCall) {
parent.children[index] = node.prefix(parent)
} else {
throw AssemblyError("expected PtFunctionCall")
}
}
return SymbolTableMaker(program, options).make()
}
}
private fun PtVariable.prefix(st: SymbolTable): PtVariable {
name = name.split('.').map {"p8_$it" }.joinToString(".")
if(value==null)
return this
val arrayValue = value as? PtArray
return if(arrayValue!=null && arrayValue.children.any { it !is PtNumber} ) {
val newValue = PtArray(arrayValue.type, arrayValue.position)
arrayValue.children.forEach { elt ->
when(elt) {
is PtIdentifier -> newValue.add(elt.prefix(arrayValue, st))
is PtNumber -> newValue.add(elt)
is PtAddressOf -> {
if(elt.definingBlock()?.noSymbolPrefixing==true)
newValue.add(elt)
else {
val newAddr = PtAddressOf(elt.position)
newAddr.children.add(elt.identifier.prefix(newAddr, st))
newAddr.parent = arrayValue
newValue.add(newAddr)
}
}
else -> throw AssemblyError("weird array value element $elt")
}
}
PtVariable(name, type, zeropage, newValue, arraySize, position)
}
else this
}
private fun PtJump.prefix(parent: PtNode, st: SymbolTable): PtJump {
val jump = if(identifier!=null) {
val prefixedIdent = identifier!!.prefix(this, st)
PtJump(prefixedIdent, address, generatedLabel, position)
} else {
val prefixedLabel = generatedLabel!!.split('.').map {"p8_$it" }.joinToString(".")
PtJump(null, address, prefixedLabel, position)
}
jump.parent = parent
return jump
}
private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall {
val newName = name.split('.').map {"p8_$it" }.joinToString(".")
val call = PtFunctionCall(newName, void, type, position)
call.children.addAll(children)
call.children.forEach { it.parent = call }
call.parent = parent
return call
}
private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
val target = st.lookup(name)
if(target?.astNode?.definingBlock()?.noSymbolPrefixing==true)
return this
val newName = name.split('.').map { "p8_$it" }.joinToString(".")
val node = PtIdentifier(newName, type, position)
node.parent = parent
return node
}
class AsmGen6502Internal (
val program: PtProgram,
internal val symbolTable: SymbolTable,
@@ -40,12 +201,12 @@ class AsmGen6502Internal (
private val allocator = VariableAllocator(symbolTable, options, errors)
private val assemblyLines = mutableListOf<String>()
private val breakpointLabels = mutableListOf<String>()
private val forloopsAsmGen = ForLoopsAsmGen(program, this, zeropage)
private val forloopsAsmGen = ForLoopsAsmGen(this, zeropage)
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
private val assignmentAsmGen = AssignmentAsmGen(program, symbolTable, this, allocator)
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator)
private val anyExprGen = AnyExprAsmGen(this)
private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
fun compileToAssembly(): IAssemblyProgram? {
@@ -77,8 +238,19 @@ class AsmGen6502Internal (
internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu
private var lastSourceLineNumber: Int = -1
internal fun outputSourceLine(node: PtNode) {
out(" ;\tsrc line: ${node.position.file}:${node.position.line}")
if(!options.includeSourcelines || node.position===Position.DUMMY || node.position.line==lastSourceLineNumber)
return
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
out("$srcComment $line", false)
}
internal fun out(str: String, splitlines: Boolean = true) {
@@ -231,32 +403,6 @@ class AsmGen6502Internal (
return name2.replace("prog8_lib.P8ZP_SCRATCH_", "P8ZP_SCRATCH_") // take care of the 'hooks' to the temp vars -> reference zp symbols directly
}
internal fun saveRegisterLocal(register: CpuRegister, scope: IPtSubroutine) {
if (isTargetCpu(CpuType.CPU65c02)) {
// just use the cpu's stack for all registers, shorter code
when (register) {
CpuRegister.A -> out(" pha")
CpuRegister.X -> out(" phx")
CpuRegister.Y -> out(" phy")
}
} else {
when (register) {
CpuRegister.A -> {
// just use the stack, only for A
out(" pha")
}
CpuRegister.X -> {
out(" stx prog8_regsaveX")
subroutineExtra(scope).usedRegsaveX = true
}
CpuRegister.Y -> {
out(" sty prog8_regsaveY")
subroutineExtra(scope).usedRegsaveY = true
}
}
}
}
internal fun saveRegisterStack(register: CpuRegister, keepA: Boolean) {
when (register) {
CpuRegister.A -> out(" pha")
@@ -281,24 +427,6 @@ class AsmGen6502Internal (
}
}
internal fun restoreRegisterLocal(register: CpuRegister) {
if (isTargetCpu(CpuType.CPU65c02)) {
when (register) {
// this just used the stack, for all registers. Shorter code.
CpuRegister.A -> out(" pla")
CpuRegister.X -> out(" plx")
CpuRegister.Y -> out(" ply")
}
} else {
when (register) {
CpuRegister.A -> out(" pla") // this just used the stack but only for A
CpuRegister.X -> out(" ldx prog8_regsaveX")
CpuRegister.Y -> out(" ldy prog8_regsaveY")
}
}
}
internal fun restoreRegisterStack(register: CpuRegister, keepA: Boolean) {
when (register) {
CpuRegister.A -> {
@@ -418,22 +546,12 @@ class AsmGen6502Internal (
}
}
@Deprecated("avoid calling this as it generates slow evalstack based code")
internal fun translateExpression(expression: PtExpression) =
expressionsAsmGen.translateExpression(expression)
internal fun translateBuiltinFunctionCallExpression(bfc: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? =
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister)
internal fun translateBuiltinFunctionCallExpression(bfc: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): DataType? =
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultRegister)
internal fun translateFunctionCall(functionCallExpr: PtFunctionCall) =
functioncallAsmGen.translateFunctionCall(functionCallExpr)
internal fun saveXbeforeCall(functionCall: PtFunctionCall) =
functioncallAsmGen.saveXbeforeCall(functionCall)
internal fun restoreXafterCall(functionCall: PtFunctionCall) =
functioncallAsmGen.restoreXafterCall(functionCall)
internal fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) =
assignmentAsmGen.translateNormalAssignment(assign, scope)
@@ -462,7 +580,6 @@ class AsmGen6502Internal (
}
internal fun assignExpressionTo(value: PtExpression, target: AsmAssignTarget) {
// don't use translateExpression() to avoid evalstack
when (target.datatype) {
in ByteDatatypes -> {
assignExpressionToRegister(value, RegisterOrPair.A)
@@ -513,7 +630,6 @@ class AsmGen6502Internal (
private fun translate(stmt: PtIfElse) {
val condition = stmt.condition as? PtBinaryExpression
if(condition!=null) {
require(!options.useNewExprCode)
requireComparisonExpression(condition) // IfStatement: condition must be of form 'x <comparison> <value>'
if (stmt.elseScope.children.isEmpty()) {
val jump = stmt.ifScope.children.singleOrNull()
@@ -641,65 +757,48 @@ class AsmGen6502Internal (
private fun repeatWordCount(count: Int, stmt: PtRepeatLoop) {
require(count in 257..65535) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat")
if(isTargetCpu(CpuType.CPU65c02)) {
val counterVar = createRepeatCounterVar(DataType.UWORD, true, stmt)
out("""
lda #<$count
ldy #>$count
sta $counterVar
sty $counterVar+1
val counterVar = createRepeatCounterVar(DataType.UWORD, isTargetCpu(CpuType.CPU65c02), stmt)
// the iny + double dec is microoptimization of the 16 bit loop
out("""
ldy #>$count
lda #<$count
beq +
iny
+ sta $counterVar
sty $counterVar+1
$repeatLabel""")
translate(stmt.statements)
out("""
lda $counterVar
bne +
dec $counterVar+1
+ dec $counterVar
lda $counterVar
ora $counterVar+1
bne $repeatLabel""")
} else {
val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt)
out("""
lda #<$count
ldy #>$count
sta $counterVar
sty $counterVar+1
$repeatLabel""")
translate(stmt.statements)
out("""
lda $counterVar
bne +
dec $counterVar+1
+ dec $counterVar
lda $counterVar
ora $counterVar+1
bne $repeatLabel""")
}
translate(stmt.statements)
out("""
dec $counterVar
bne $repeatLabel
dec $counterVar+1
bne $repeatLabel""")
}
private fun repeatWordCountInAY(endLabel: String, stmt: PtRepeatLoop) {
// note: A/Y must have been loaded with the number of iterations!
// no need to explicitly test for 0 iterations as this is done in the countdown logic below
// the iny + double dec is microoptimization of the 16 bit loop
val repeatLabel = makeLabel("repeat")
val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt)
out("""
sta $counterVar
sty $counterVar+1
$repeatLabel lda $counterVar
bne +
lda $counterVar+1
beq $endLabel
lda $counterVar
bne +
dec $counterVar+1
+ dec $counterVar
""")
cmp #0
beq +
iny
+ sta $counterVar
sty $counterVar+1
ora $counterVar+1
beq $endLabel
$repeatLabel""")
translate(stmt.statements)
jmp(repeatLabel)
out("""
dec $counterVar
bne $repeatLabel
dec $counterVar+1
bne $repeatLabel""")
out(endLabel)
}
private fun repeatByteCount(count: Int, stmt: PtRepeatLoop) {
require(count in 2..256) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat")
@@ -762,7 +861,7 @@ $repeatLabel lda $counterVar
DataType.UBYTE, DataType.UWORD -> {
val result = zeropage.allocate(counterVar, dt, null, stmt.position, errors)
result.fold(
success = { (address, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
success = { (address, _, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally
)
return counterVar
@@ -902,7 +1001,10 @@ $repeatLabel lda $counterVar
}
private fun translate(asm: PtInlineAssembly) {
assemblyLines.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
if(asm.isIR)
throw AssemblyError("%asm containing IR code cannot be translated to 6502 assembly")
else
assemblyLines.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
}
private fun translate(incbin: PtIncludeBinary) {
@@ -941,20 +1043,6 @@ $repeatLabel lda $counterVar
}
}
internal fun signExtendStackLsb(valueDt: DataType) {
// sign extend signed byte on stack to signed word on stack
when(valueDt) {
DataType.UBYTE -> {
if(isTargetCpu(CpuType.CPU65c02))
out(" stz P8ESTACK_HI+1,x")
else
out(" lda #0 | sta P8ESTACK_HI+1,x")
}
DataType.BYTE -> out(" jsr prog8_lib.sign_extend_stack_byte")
else -> throw AssemblyError("need byte type")
}
}
internal fun signExtendVariableLsb(asmvar: String, valueDt: DataType) {
// sign extend signed byte in a var to a full word in that variable
when(valueDt) {
@@ -990,18 +1078,10 @@ $repeatLabel lda $counterVar
}
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair<PtExpression, PtExpression>? {
val left: PtExpression
val right: PtExpression
val operator: String
if (pointerOffsetExpr is PtBinaryExpression) {
require(!options.useNewExprCode)
operator = pointerOffsetExpr.operator
left = pointerOffsetExpr.left
right = pointerOffsetExpr.right
}
else return null
if (pointerOffsetExpr !is PtBinaryExpression) return null
val operator = pointerOffsetExpr.operator
val left = pointerOffsetExpr.left
val right = pointerOffsetExpr.right
if (operator != "+") return null
val leftDt = left.type
val rightDt = right.type
@@ -1113,10 +1193,7 @@ $repeatLabel lda $counterVar
}
internal fun findSubroutineParameter(name: String, asmgen: AsmGen6502Internal): PtSubroutineParameter? {
val stScope = asmgen.symbolTable.lookup(name)
require(stScope!=null) {
"invalid name lookup $name"
}
val stScope = asmgen.symbolTable.lookup(name) ?: return null
val node = stScope.astNode
if(node is PtSubroutineParameter)
return node
@@ -1594,7 +1671,7 @@ $repeatLabel lda $counterVar
}
else if (left is PtMemoryByte) {
return if(rightConstVal.number.toInt()!=0) {
translateDirectMemReadExpressionToRegAorStack(left, false)
translateDirectMemReadExpressionToRegA(left)
code("#${rightConstVal.number.toInt()}")
}
else
@@ -1741,7 +1818,7 @@ $repeatLabel lda $counterVar
out(" beq $jumpIfFalseLabel")
}
else if (left is PtMemoryByte) {
translateDirectMemReadExpressionToRegAorStack(left, false)
translateDirectMemReadExpressionToRegA(left)
return if(rightConstVal.number.toInt()!=0)
code("#${rightConstVal.number.toInt()}")
else
@@ -1902,7 +1979,7 @@ $repeatLabel lda $counterVar
out(" bne $jumpIfFalseLabel")
}
else if (left is PtMemoryByte) {
translateDirectMemReadExpressionToRegAorStack(left, false)
translateDirectMemReadExpressionToRegA(left)
return if(rightConstVal.number.toInt()!=0)
code("#${rightConstVal.number.toInt()}")
else
@@ -2066,7 +2143,7 @@ $repeatLabel lda $counterVar
}
else if (left is PtMemoryByte) {
if(rightConstVal.number.toInt()!=0) {
translateDirectMemReadExpressionToRegAorStack(left, false)
translateDirectMemReadExpressionToRegA(left)
code("#${rightConstVal.number.toInt()}")
}
return
@@ -2229,7 +2306,7 @@ $repeatLabel lda $counterVar
out(" bne $jumpIfFalseLabel")
}
else if (left is PtMemoryByte) {
translateDirectMemReadExpressionToRegAorStack(left, false)
translateDirectMemReadExpressionToRegA(left)
return if(rightConstVal.number.toInt()!=0)
code("#${rightConstVal.number.toInt()}")
else
@@ -2275,7 +2352,7 @@ $repeatLabel lda $counterVar
out(" beq $jumpIfFalseLabel")
}
else if (left is PtMemoryByte) {
translateDirectMemReadExpressionToRegAorStack(left, false)
translateDirectMemReadExpressionToRegA(left)
return if(rightConstVal.number.toInt()!=0)
code("#${rightConstVal.number.toInt()}")
else
@@ -2731,22 +2808,14 @@ $repeatLabel lda $counterVar
+""")
}
internal fun translateDirectMemReadExpressionToRegAorStack(expr: PtMemoryByte, pushResultOnEstack: Boolean) {
internal fun translateDirectMemReadExpressionToRegA(expr: PtMemoryByte) {
fun assignViaExprEval() {
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
if (isTargetCpu(CpuType.CPU65c02)) {
if (pushResultOnEstack) {
out(" lda (P8ZP_SCRATCH_W2) | dex | sta P8ESTACK_LO+1,x")
} else {
out(" lda (P8ZP_SCRATCH_W2)")
}
out(" lda (P8ZP_SCRATCH_W2)")
} else {
if (pushResultOnEstack) {
out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y | dex | sta P8ESTACK_LO+1,x")
} else {
out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
}
out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
}
}
@@ -2754,22 +2823,14 @@ $repeatLabel lda $counterVar
is PtNumber -> {
val address = (expr.address as PtNumber).number.toInt()
out(" lda ${address.toHex()}")
if(pushResultOnEstack)
out(" sta P8ESTACK_LO,x | dex")
}
is PtIdentifier -> {
// the identifier is a pointer variable, so read the value from the address in it
loadByteFromPointerIntoA(expr.address as PtIdentifier)
if(pushResultOnEstack)
out(" sta P8ESTACK_LO,x | dex")
}
is PtBinaryExpression -> {
require(!options.useNewExprCode)
val addrExpr = expr.address as PtBinaryExpression
if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
if(pushResultOnEstack)
out(" sta P8ESTACK_LO,x | dex")
} else {
if(!tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
assignViaExprEval()
}
}
@@ -2965,6 +3026,14 @@ $repeatLabel lda $counterVar
}
}
internal fun pushFAC1() {
out(" jsr floats.pushFAC1")
}
internal fun popFAC1() {
out(" jsr floats.popFAC1")
}
internal fun needAsaveForExpr(arg: PtExpression): Boolean =
arg !is PtNumber && arg !is PtIdentifier && (arg !is PtMemoryByte || !arg.isSimple())
@@ -2997,9 +3066,6 @@ $repeatLabel lda $counterVar
* it's more consistent to only define these attributes on a Subroutine node.
*/
internal class SubroutineExtraAsmInfo {
var usedRegsaveA = false
var usedRegsaveX = false
var usedRegsaveY = false
var usedFloatEvalResultVar1 = false
var usedFloatEvalResultVar2 = false
@@ -15,21 +15,7 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
var linesByFour = getLinesBy(lines, 4)
var mods = optimizeUselessStackByteWrites(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
mods = optimizeIncDec(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
mods = optimizeCmpSequence(linesByFour)
var mods = optimizeIncDec(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
@@ -43,7 +29,14 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
numberOfOptimizations++
}
mods= optimizeJsrRtsAndOtherCombinations(linesByFour)
mods = optimizeJsrRtsAndOtherCombinations(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
mods = optimizeUselessPushPopStack(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
@@ -90,44 +83,6 @@ private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
// all lines (that aren't empty or comments) in sliding windows of certain size
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
// when statement (on bytes) generates a sequence of:
// lda $ce01,x
// cmp #$20
// beq check_prog8_s72choice_32
// lda $ce01,x
// cmp #$21
// beq check_prog8_s73choice_33
// the repeated lda can be removed
val mods = mutableListOf<Modification>()
for(lines in linesByFour) {
if(lines[0].value.trim()=="lda P8ESTACK_LO+1,x" &&
lines[1].value.trim().startsWith("cmp ") &&
lines[2].value.trim().startsWith("beq ") &&
lines[3].value.trim()=="lda P8ESTACK_LO+1,x") {
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
}
}
return mods
}
private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
// this is a lot harder for word values because the instruction sequence varies.
val mods = mutableListOf<Modification>()
for(lines in linesByFour) {
if(lines[0].value.trim()=="sta P8ESTACK_LO,x" &&
lines[1].value.trim()=="dex" &&
lines[2].value.trim()=="inx" &&
lines[3].value.trim()=="lda P8ESTACK_LO,x") {
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[3].index, true, null))
}
}
return mods
}
private fun optimizeSameAssignments(
linesByFourteen: List<List<IndexedValue<String>>>,
machine: IMachineDefinition,
@@ -517,3 +472,40 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedVal
}
return mods
}
private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
val mods = mutableListOf<Modification>()
fun optimize(register: Char, lines: List<IndexedValue<String>>) {
if(lines[0].value.trimStart().startsWith("ph$register")) {
if(lines[2].value.trimStart().startsWith("pl$register")) {
val second = lines[1].value.trimStart().take(6).lowercase()
if(register!in second
&& !second.startsWith("jsr")
&& !second.startsWith("pl")
&& !second.startsWith("ph")) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[2].index, true, null))
}
}
else if (lines[3].value.trimStart().startsWith("pl$register")) {
val second = lines[1].value.trimStart().take(6).lowercase()
val third = lines[2].value.trimStart().take(6).lowercase()
if(register !in second && register !in third
&& !second.startsWith("jsr") && !third.startsWith("jsr")
&& !second.startsWith("pl") && !third.startsWith("pl")
&& !second.startsWith("ph") && !third.startsWith("ph")) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[3].index, true, null))
}
}
}
}
for (lines in linesByFour) {
optimize('a', lines)
optimize('x', lines)
optimize('y', lines)
}
return mods
}
@@ -1,14 +1,8 @@
package prog8.codegen.cpu6502
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.mapError
import prog8.code.core.*
import prog8.code.target.C64Target
import java.io.File
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
internal class AssemblyProgram(
@@ -130,18 +124,3 @@ internal class AssemblyProgram(
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
}
}
internal fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
return if (filename.startsWith(SourceCode.libraryFilePrefix)) {
return com.github.michaelbull.result.runCatching {
SourceCode.Resource("/prog8lib/${filename.substring(SourceCode.libraryFilePrefix.length)}").text
}.mapError { NoSuchFileException(File(filename)) }
} else {
val sib = Path(source.origin).resolveSibling(filename)
if (sib.isRegularFile())
Ok(SourceCode.File(sib).text)
else
Ok(SourceCode.File(Path(filename)).text)
}
}
@@ -10,34 +10,31 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private val asmgen: AsmGen6502Internal,
private val assignAsmGen: AssignmentAsmGen) {
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? {
return translateFunctioncall(fcall, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): DataType? {
return translateFunctioncall(fcall, discardResult = false, resultRegister = resultRegister)
}
internal fun translateFunctioncallStatement(fcall: PtBuiltinFunctionCall) {
translateFunctioncall(fcall, discardResult = true, resultToStack = false, resultRegister = null)
translateFunctioncall(fcall, discardResult = true, resultRegister = null)
}
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?): DataType? {
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?): DataType? {
if (discardResult && fcall.hasNoSideEffects)
return null // can just ignore the whole function call altogether
if(discardResult && resultToStack)
throw AssemblyError("cannot both discard the result AND put it onto stack")
val sscope = fcall.definingISub()
when (fcall.name) {
"msb" -> funcMsb(fcall, resultToStack, resultRegister)
"lsb" -> funcLsb(fcall, resultToStack, resultRegister)
"mkword" -> funcMkword(fcall, resultToStack, resultRegister)
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(fcall, resultToStack, resultRegister)
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultToStack, resultRegister)
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultToStack, resultRegister)
"abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultToStack, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultToStack, resultRegister, sscope)
"msb" -> funcMsb(fcall, resultRegister)
"lsb" -> funcLsb(fcall, resultRegister)
"mkword" -> funcMkword(fcall, resultRegister)
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(fcall, resultRegister)
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultRegister)
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultRegister)
"abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
"any", "all" -> funcAnyAll(fcall, resultRegister, sscope)
"sgn" -> funcSgn(fcall, resultRegister, sscope)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
"divmod__ubyte" -> funcDivmod(fcall)
"divmod__uword" -> funcDivmodW(fcall)
"rol" -> funcRol(fcall)
@@ -46,8 +43,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"ror2" -> funcRor2(fcall)
"sort" -> funcSort(fcall)
"reverse" -> funcReverse(fcall)
"memory" -> funcMemory(fcall, discardResult, resultToStack, resultRegister)
"peekw" -> funcPeekW(fcall, resultToStack, resultRegister)
"memory" -> funcMemory(fcall, discardResult, resultRegister)
"peekw" -> funcPeekW(fcall, resultRegister)
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
"pokew" -> funcPokeW(fcall)
"pokemon" -> { /* meme function */ }
@@ -71,12 +68,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.popCpuStack(DataType.UWORD, target, fcall.definingISub())
}
"rsave" -> funcRsave()
"rsavex" -> funcRsaveX()
"rrestore" -> funcRrestore()
"rrestorex" -> funcRrestoreX()
"cmp" -> funcCmp(fcall)
"callfar" -> funcCallFar(fcall)
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultToStack)
"prog8_lib_stringcompare" -> funcStringCompare(fcall)
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
}
@@ -115,11 +110,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
sty $remainderVar+1""")
}
private fun funcStringCompare(fcall: PtBuiltinFunctionCall, resultToStack: Boolean) {
private fun funcStringCompare(fcall: PtBuiltinFunctionCall) {
asmgen.assignWordOperandsToAYAndVar(fcall.args[0], fcall.args[1], "P8ZP_SCRATCH_W2")
asmgen.out(" jsr prog8_lib.strcmp_mem")
if(resultToStack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
}
private fun funcRsave() {
@@ -142,13 +135,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
lda P8ZP_SCRATCH_REG""")
}
private fun funcRsaveX() {
if (asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" phx")
else
asmgen.out(" txa | pha")
}
private fun funcRrestore() {
if (asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out("""
@@ -166,13 +152,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
plp""")
}
private fun funcRrestoreX() {
if (asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" plx")
else
asmgen.out(" sta P8ZP_SCRATCH_B1 | pla | tax | lda P8ZP_SCRATCH_B1")
}
private fun funcCallFar(fcall: PtBuiltinFunctionCall) {
if(asmgen.options.compTarget.name != "cx16")
throw AssemblyError("callfar only works on cx16 target at this time")
@@ -253,7 +232,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
}
}
private fun funcMemory(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
private fun funcMemory(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?) {
if(discardResult)
throw AssemblyError("should not discard result of memory allocation at $fcall")
val name = (fcall.args[0] as PtString).value
@@ -263,41 +242,25 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
addressOf.add(slabname)
addressOf.parent = fcall
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
val target =
if(resultToStack)
AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.UWORD, null, fcall.position)
else
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
val assign = AsmAssignment(src, target, program.memsizer, fcall.position)
asmgen.translateNormalAssignment(assign, fcall.definingISub())
}
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
when(fcall.args[0].type) {
DataType.UBYTE -> {
if(resultToStack)
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_stack")
else {
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)
}
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)
}
DataType.UWORD -> {
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
else {
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
}
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false)
}
DataType.FLOAT -> {
if(resultToStack)
throw AssemblyError("no support for sqrt float onto stack")
else {
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
}
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
}
else -> throw AssemblyError("weird dt")
}
@@ -466,16 +429,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
if(ptrAndIndex!=null) {
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
asmgen.saveRegisterStack(CpuRegister.A, true)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.restoreRegisterStack(CpuRegister.X, false)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ ror ${'$'}ffff,x ; modified""")
asmgen.restoreRegisterLocal(CpuRegister.X)
} else {
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
asmgen.out("""
@@ -571,16 +531,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address)
if(ptrAndIndex!=null) {
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A)
asmgen.saveRegisterStack(CpuRegister.A, true)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
asmgen.restoreRegisterLocal(CpuRegister.X)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.restoreRegisterStack(CpuRegister.X, false)
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ rol ${'$'}ffff,x ; modified""")
asmgen.restoreRegisterLocal(CpuRegister.X)
} else {
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
asmgen.out("""
@@ -633,85 +590,56 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.assignExpressionToVariable(indexer.index, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE)
}
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
val dt = fcall.args.single().type
if(resultToStack) {
when (dt) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_stack")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_stack")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_stack")
DataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_stack")
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_stack")
else -> throw AssemblyError("weird type $dt")
}
} else {
when (dt) {
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")
else -> throw AssemblyError("weird type $dt")
}
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true)
when (dt) {
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")
else -> throw AssemblyError("weird type $dt")
}
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true)
}
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
private fun funcAnyAll(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
outputAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().type
if(resultToStack) {
when (dt) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_stack")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_stack")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_stack")
in SplitWordArrayTypes -> TODO("split word any/all")
else -> throw AssemblyError("weird type $dt")
}
} else {
when (dt) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
in SplitWordArrayTypes -> TODO("split word any/all")
else -> throw AssemblyError("weird type $dt")
}
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes)
when (dt) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_w_into_A | ldy #0")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${fcall.name}_f_into_A | ldy #0")
in SplitWordArrayTypes -> TODO("split word any/all")
else -> throw AssemblyError("weird type $dt")
}
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes)
}
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope)
val dt = fcall.args.single().type
if(resultToStack) {
when (dt) {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack")
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack")
else -> throw AssemblyError("no support for abs onto stack for this dt")
when (dt) {
DataType.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)
}
} else {
when (dt) {
DataType.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)
}
DataType.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 -> {
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
}
DataType.UBYTE -> {
asmgen.assignRegister(RegisterOrPair.A, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.A, false, fcall.position, scope, asmgen))
}
DataType.UWORD -> {
asmgen.assignRegister(RegisterOrPair.AY, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.AY, false, fcall.position, scope, asmgen))
}
else -> throw AssemblyError("weird type")
DataType.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 -> {
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
}
DataType.UBYTE -> {
asmgen.assignRegister(RegisterOrPair.A, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.A, false, fcall.position, scope, asmgen))
}
DataType.UWORD -> {
asmgen.assignRegister(RegisterOrPair.AY, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.AY, false, fcall.position, scope, asmgen))
}
else -> throw AssemblyError("weird type")
}
}
@@ -727,7 +655,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val varname = asmgen.asmVariableName(addrExpr)
if(asmgen.isZpVar(addrExpr)) {
// pointervar is already in the zero page, no need to copy
asmgen.saveRegisterLocal(CpuRegister.X, fcall.definingISub()!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
asmgen.out("""
@@ -743,29 +670,24 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
iny
sta ($varname),y""")
}
asmgen.restoreRegisterLocal(CpuRegister.X)
return
}
}
is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
val pointer = result?.first as? PtIdentifier
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
// can do ZP,Y indexing
val varname = asmgen.asmVariableName(pointer)
val scope = fcall.definingISub()!!
asmgen.saveRegisterLocal(CpuRegister.X, scope)
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
asmgen.saveRegisterLocal(CpuRegister.Y, scope)
asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
asmgen.restoreRegisterLocal(CpuRegister.Y)
asmgen.restoreRegisterStack(CpuRegister.Y, true)
asmgen.out("""
sta ($varname),y
txa
iny
sta ($varname),y""")
asmgen.restoreRegisterLocal(CpuRegister.X)
return
}
}
@@ -777,7 +699,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" jsr prog8_lib.func_pokew")
}
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
private fun funcPeekW(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
fun fallback() {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peekw")
@@ -810,7 +732,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else fallback()
}
is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
val pointer = result?.first as? PtIdentifier
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
@@ -829,23 +750,19 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> fallback()
}
if(resultToStack){
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
} else {
when(resultRegister ?: RegisterOrPair.AY) {
RegisterOrPair.AY -> {}
RegisterOrPair.AX -> asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG")
RegisterOrPair.XY -> asmgen.out(" tax")
in Cx16VirtualRegisters -> asmgen.out(
" sta cx16.${
resultRegister.toString().lowercase()
} | sty cx16.${resultRegister.toString().lowercase()}+1")
else -> throw AssemblyError("invalid reg")
}
when(resultRegister ?: RegisterOrPair.AY) {
RegisterOrPair.AY -> {}
RegisterOrPair.AX -> asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG")
RegisterOrPair.XY -> asmgen.out(" tax")
in Cx16VirtualRegisters -> asmgen.out(
" sta cx16.${
resultRegister.toString().lowercase()
} | sty cx16.${resultRegister.toString().lowercase()}+1")
else -> throw AssemblyError("invalid reg")
}
}
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
when(fcall.type) {
in ByteDatatypes -> {
@@ -853,30 +770,22 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W1+1", fcall.args[2].type) // maximum
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, signed) // value
asmgen.out(" jsr prog8_lib.func_clamp_${fcall.type.toString().lowercase()}")
if(resultToStack) {
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
assignAsmGen.assignRegisterByte(targetReg, CpuRegister.A, signed)
}
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
assignAsmGen.assignRegisterByte(targetReg, CpuRegister.A, signed)
}
in WordDatatypes -> {
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
asmgen.out(" jsr prog8_lib.func_clamp_${fcall.type.toString().lowercase()}")
if(resultToStack) {
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
} else {
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
assignAsmGen.assignRegisterpairWord(targetReg, RegisterOrPair.AY)
}
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
assignAsmGen.assignRegisterpairWord(targetReg, RegisterOrPair.AY)
}
else -> throw AssemblyError("invalid dt")
}
}
private fun funcMin(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
private fun funcMin(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
if(fcall.type in ByteDatatypes) {
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_B1", fcall.type) // right
@@ -886,12 +795,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out("""
lda P8ZP_SCRATCH_B1
+""")
if(resultToStack) {
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.A, targetReg)
}
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.A, targetReg)
} else if(fcall.type in WordDatatypes) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
@@ -927,18 +832,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
ldy P8ZP_SCRATCH_W1+1
+""")
}
if(resultToStack) {
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
} else {
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
}
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
} else {
throw AssemblyError("min float not supported")
}
}
private fun funcMax(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
private fun funcMax(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
if(fcall.type in ByteDatatypes) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
@@ -948,12 +849,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out("""
lda P8ZP_SCRATCH_B1
+""")
if(resultToStack) {
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.A, targetReg)
}
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.A, targetReg)
} else if(fcall.type in WordDatatypes) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
@@ -989,72 +886,61 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
ldy P8ZP_SCRATCH_W2+1
+""")
}
if(resultToStack) {
asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
} else {
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
}
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
} else {
throw AssemblyError("max float not supported")
}
}
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
if(resultToStack) {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
asmgen.out(" sta P8ESTACK_LO,x | pla | sta P8ESTACK_HI,x | dex")
} else {
val reg = resultRegister ?: RegisterOrPair.AY
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
if(!needAsave) {
val mr0 = fcall.args[0] as? PtMemoryByte
val mr1 = fcall.args[1] as? PtMemoryByte
if (mr0 != null)
needAsave = mr0.address !is PtNumber
if (mr1 != null)
needAsave = needAsave or (mr1.address !is PtNumber)
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val reg = resultRegister ?: RegisterOrPair.AY
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
if(!needAsave) {
val mr0 = fcall.args[0] as? PtMemoryByte
val mr1 = fcall.args[1] as? PtMemoryByte
if (mr0 != null)
needAsave = mr0.address !is PtNumber
if (mr1 != null)
needAsave = needAsave or (mr1.address !is PtNumber)
}
when(reg) {
RegisterOrPair.AX -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if(needAsave)
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
if(needAsave)
asmgen.out(" pla")
}
when(reg) {
RegisterOrPair.AX -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if(needAsave)
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
if(needAsave)
asmgen.out(" pla")
}
RegisterOrPair.AY -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if(needAsave)
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
if(needAsave)
asmgen.out(" pla")
}
RegisterOrPair.XY -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if(needAsave)
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
if(needAsave)
asmgen.out(" pla")
asmgen.out(" tax")
}
in Cx16VirtualRegisters -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
asmgen.out(" sta cx16.${reg.toString().lowercase()}")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
asmgen.out(" sta cx16.${reg.toString().lowercase()}+1")
}
else -> throw AssemblyError("invalid mkword target reg")
RegisterOrPair.AY -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if(needAsave)
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
if(needAsave)
asmgen.out(" pla")
}
RegisterOrPair.XY -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if(needAsave)
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
if(needAsave)
asmgen.out(" pla")
asmgen.out(" tax")
}
in Cx16VirtualRegisters -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
asmgen.out(" sta cx16.${reg.toString().lowercase()}")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
asmgen.out(" sta cx16.${reg.toString().lowercase()}+1")
}
else -> throw AssemblyError("invalid mkword target reg")
}
}
private fun funcMsb(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
private fun funcMsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val arg = fcall.args.single()
if (arg.type !in WordDatatypes)
throw AssemblyError("msb required word argument")
@@ -1062,53 +948,44 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
throw AssemblyError("msb(const) should have been const-folded away")
if (arg is PtIdentifier) {
val sourceName = asmgen.asmVariableName(arg)
if(resultToStack) {
asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex")
} else {
when(resultRegister) {
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName+1")
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+1")
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+1")
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+1 | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda $sourceName+1 | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
in Cx16VirtualRegisters -> {
val regname = resultRegister.name.lowercase()
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | stz cx16.$regname+1")
else
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
}
else -> throw AssemblyError("invalid reg")
when(resultRegister) {
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName+1")
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+1")
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+1")
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+1 | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda $sourceName+1 | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
in Cx16VirtualRegisters -> {
val regname = resultRegister.name.lowercase()
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | stz cx16.$regname+1")
else
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
}
else -> throw AssemblyError("invalid reg")
}
} else {
if(resultToStack) {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
} else {
when(resultRegister) {
null, RegisterOrPair.A -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" tya")
}
RegisterOrPair.X -> {
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX)
asmgen.out(" pla")
}
RegisterOrPair.Y -> {
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" pla")
}
else -> throw AssemblyError("invalid reg")
when(resultRegister) {
null, RegisterOrPair.A -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" tya")
}
RegisterOrPair.X -> {
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX)
asmgen.out(" pla")
}
RegisterOrPair.Y -> {
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" pla")
}
else -> throw AssemblyError("invalid reg")
}
}
}
private fun funcLsb(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
private fun funcLsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val arg = fcall.args.single()
if (arg.type !in WordDatatypes)
throw AssemblyError("lsb required word argument")
@@ -1117,54 +994,60 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if (arg is PtIdentifier) {
val sourceName = asmgen.asmVariableName(arg)
if(resultToStack) {
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex")
} else {
when(resultRegister) {
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName")
RegisterOrPair.X -> asmgen.out(" ldx $sourceName")
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName")
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
in Cx16VirtualRegisters -> {
val regname = resultRegister.name.lowercase()
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
else
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
}
else -> throw AssemblyError("invalid reg")
when(resultRegister) {
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName")
RegisterOrPair.X -> asmgen.out(" ldx $sourceName")
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName")
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
in Cx16VirtualRegisters -> {
val regname = resultRegister.name.lowercase()
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
else
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
}
else -> throw AssemblyError("invalid reg")
}
} else {
if(resultToStack) {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
// NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction.
// this is required because the compiler assumes the status bits are set according to what A is (lsb)
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
when(resultRegister) {
null, RegisterOrPair.A -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
// NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction.
// this is required because the compiler assumes the status bits are set according to what A is (lsb)
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
}
RegisterOrPair.X -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY)
// NOTE: we rely on the fact that the above assignment to XY, assigns the Lsb to X as the last instruction.
// this is required because the compiler assumes the status bits are set according to what X is (lsb)
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
}
RegisterOrPair.Y -> {
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" tay | pla | cpy #0")
}
else -> throw AssemblyError("invalid reg")
when(resultRegister) {
null, RegisterOrPair.A -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
// NOTE: we rely on the fact that the above assignment to AY, assigns the Lsb to A as the last instruction.
// this is required because the compiler assumes the status bits are set according to what A is (lsb)
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
}
RegisterOrPair.X -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY)
// NOTE: we rely on the fact that the above assignment to XY, assigns the Lsb to X as the last instruction.
// this is required because the compiler assumes the status bits are set according to what X is (lsb)
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
}
RegisterOrPair.Y -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" tay | cpy #0")
}
RegisterOrPair.AY -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" ldy #0 | cmp #0")
}
RegisterOrPair.AX -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX)
asmgen.out(" ldx #0 | cmp #0")
}
RegisterOrPair.XY -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY)
asmgen.out(" ldy #0 | cpx #0")
}
in Cx16VirtualRegisters -> {
asmgen.assignExpressionToRegister(fcall.args.single(), resultRegister)
val zero = PtNumber(DataType.UBYTE, 0.0, Position.DUMMY)
zero.parent=fcall
assignAsmGen.assignExpressionToVariable(zero, "cx16.${resultRegister.toString().lowercase()}H", DataType.UBYTE)
asmgen.out(" lda cx16.r0L")
}
else -> throw AssemblyError("invalid reg")
}
}
}
@@ -1,935 +0,0 @@
package prog8.codegen.cpu6502
import prog8.code.ast.*
import prog8.code.core.*
import kotlin.math.absoluteValue
internal class ExpressionsAsmGen(private val program: PtProgram,
private val asmgen: AsmGen6502Internal,
private val allocator: VariableAllocator) {
@Deprecated("avoid calling this as it generates slow evalstack based code")
internal fun translateExpression(expression: PtExpression) {
if (this.asmgen.options.slowCodegenWarnings) {
asmgen.errors.warn("slow stack evaluation used for expression", expression.position)
}
translateExpressionInternal(expression)
}
// the rest of the methods are all PRIVATE
private fun translateExpressionInternal(expression: PtExpression) {
when(expression) {
is PtPrefix -> translateExpression(expression)
is PtBinaryExpression -> translateExpression(expression)
is PtArrayIndexer -> translateExpression(expression)
is PtTypeCast -> translateExpression(expression)
is PtAddressOf -> translateExpression(expression)
is PtMemoryByte -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true)
is PtNumber -> translateExpression(expression)
is PtIdentifier -> translateExpression(expression)
is PtFunctionCall -> translateFunctionCallResultOntoStack(expression)
is PtBuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
is PtContainmentCheck -> translateContainmentCheck(expression)
is PtArray, is PtString -> throw AssemblyError("string/array literal value assignment should have been replaced by a variable")
is PtRange -> throw AssemblyError("range expression should have been changed into array values")
is PtMachineRegister -> throw AssemblyError("machine register ast node should not occur in 6502 codegen it is for IR code")
else -> TODO("missing expression asmgen for $expression")
}
}
private fun translateContainmentCheck(check: PtContainmentCheck) {
asmgen.assignExpressionToRegister(check, RegisterOrPair.A)
asmgen.out(" sta P8ESTACK_LO,x | dex")
}
private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
// only for use in nested expression evaluation
val symbol = asmgen.symbolTable.lookup(call.name)
val sub = symbol!!.astNode as IPtSubroutine
asmgen.saveXbeforeCall(call)
asmgen.translateFunctionCall(call)
if(sub.regXasResult()) {
// store the return value in X somewhere that we can access again below
asmgen.out(" stx P8ZP_SCRATCH_REG")
}
asmgen.restoreXafterCall(call)
val returns: List<Pair<RegisterOrStatusflag, DataType>> = sub.returnsWhatWhere()
for ((reg, _) in returns) {
// result value is in cpu or status registers, put it on the stack instead (as we're evaluating an expression tree)
if (reg.registerOrPair != null) {
when (reg.registerOrPair!!) {
RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
RegisterOrPair.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
RegisterOrPair.X -> asmgen.out(" lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
RegisterOrPair.AX -> asmgen.out(" sta P8ESTACK_LO,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_HI,x | dex")
RegisterOrPair.XY -> asmgen.out(" tya | sta P8ESTACK_HI,x | lda P8ZP_SCRATCH_REG | sta P8ESTACK_LO,x | dex")
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.push_fac1")
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2")
RegisterOrPair.R0,
RegisterOrPair.R1,
RegisterOrPair.R2,
RegisterOrPair.R3,
RegisterOrPair.R4,
RegisterOrPair.R5,
RegisterOrPair.R6,
RegisterOrPair.R7,
RegisterOrPair.R8,
RegisterOrPair.R9,
RegisterOrPair.R10,
RegisterOrPair.R11,
RegisterOrPair.R12,
RegisterOrPair.R13,
RegisterOrPair.R14,
RegisterOrPair.R15 -> {
asmgen.out(
"""
lda cx16.${reg.registerOrPair.toString().lowercase()}
sta P8ESTACK_LO,x
lda cx16.${reg.registerOrPair.toString().lowercase()}+1
sta P8ESTACK_HI,x
dex
""")
}
}
} else when(reg.statusflag) {
Statusflag.Pc -> {
asmgen.out("""
lda #0
rol a
sta P8ESTACK_LO,x
dex""")
}
Statusflag.Pz -> {
asmgen.out("""
beq +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x
dex""")
}
Statusflag.Pv -> {
asmgen.out("""
bvs +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x
dex""")
}
Statusflag.Pn -> {
asmgen.out("""
bmi +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x
dex""")
}
null -> {}
}
}
}
private fun translateExpression(typecast: PtTypeCast) {
translateExpressionInternal(typecast.value)
when(typecast.value.type) {
DataType.UBYTE, DataType.BOOL -> {
when(typecast.type) {
DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ESTACK_HI+1,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
}
DataType.FLOAT -> asmgen.out(" jsr floats.stack_ub2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.BYTE -> {
when(typecast.type) {
DataType.UBYTE, DataType.BYTE -> {}
DataType.UWORD, DataType.WORD -> asmgen.signExtendStackLsb(DataType.BYTE)
DataType.FLOAT -> asmgen.out(" jsr floats.stack_b2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
when(typecast.type) {
DataType.BYTE, DataType.UBYTE -> {}
DataType.WORD, DataType.UWORD -> {}
DataType.FLOAT -> asmgen.out(" jsr floats.stack_uw2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.WORD -> {
when(typecast.type) {
DataType.BYTE, DataType.UBYTE -> {}
DataType.WORD, DataType.UWORD -> {}
DataType.FLOAT -> asmgen.out(" jsr floats.stack_w2float")
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.FLOAT -> {
when(typecast.type) {
DataType.UBYTE -> asmgen.out(" jsr floats.stack_float2uw")
DataType.BYTE -> asmgen.out(" jsr floats.stack_float2w")
DataType.UWORD -> asmgen.out(" jsr floats.stack_float2uw")
DataType.WORD -> asmgen.out(" jsr floats.stack_float2w")
DataType.FLOAT -> {}
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
else -> throw AssemblyError("weird type")
}
}
DataType.STR -> {
if (typecast.type != DataType.UWORD && typecast.type == DataType.STR)
throw AssemblyError("cannot typecast a string into another incompatitble type")
}
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast pass-by-reference value into another type")
else -> throw AssemblyError("weird type")
}
}
private fun translateExpression(expr: PtAddressOf) {
val name = asmgen.asmVariableName(expr.identifier)
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
}
private fun translateExpression(expr: PtNumber) {
when(expr.type) {
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex")
DataType.UWORD, DataType.WORD -> asmgen.out("""
lda #<${expr.number.toHex()}
sta P8ESTACK_LO,x
lda #>${expr.number.toHex()}
sta P8ESTACK_HI,x
dex
""")
DataType.FLOAT -> {
val floatConst = allocator.getFloatAsmConst(expr.number)
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
}
else -> throw AssemblyError("weird type")
}
}
private fun translateExpression(expr: PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
when(expr.type) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex")
}
DataType.UWORD, DataType.WORD -> {
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | lda $varname+1 | sta P8ESTACK_HI,x | dex")
}
DataType.FLOAT -> {
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_float")
}
in SplitWordArrayTypes -> {
throw AssemblyError("can't push address of split-word array ${expr.position}")
}
in IterableDatatypes -> {
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
}
else -> throw AssemblyError("stack push weird variable type $expr")
}
}
private fun translateExpression(expr: PtBinaryExpression) {
require(!asmgen.options.useNewExprCode)
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
if(translateSomewhatOptimized(expr.left, expr.operator, expr.right))
return
val leftDt = expr.left.type
val rightDt = expr.right.type
// compare with zero
if(expr.operator in ComparisonOperators) {
if(leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
val rightVal = expr.right.asConstInteger()
if(rightVal==0)
return translateComparisonWithZero(expr.left, leftDt, expr.operator)
}
}
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in ComparisonOperators)
return translateCompareStrings(expr.left, expr.operator, expr.right)
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
// the general, non-optimized cases
// TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
translateExpressionInternal(expr.left)
translateExpressionInternal(expr.right)
when (leftDt) {
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
else -> throw AssemblyError("non-numerical datatype")
}
}
private fun translateSomewhatOptimized(left: PtExpression, operator: String, right: PtExpression): Boolean {
val leftDt = left.type
val rightDt = right.type
when(operator) {
"+" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val leftVal = left.asConstInteger()
val rightVal = right.asConstInteger()
if (leftVal!=null && leftVal in -4..4) {
translateExpressionInternal(right)
if(rightDt in ByteDatatypes) {
val incdec = if(leftVal<0) "dec" else "inc"
repeat(leftVal.absoluteValue) {
asmgen.out(" $incdec P8ESTACK_LO+1,x")
}
} else {
// word
if(leftVal<0) {
repeat(leftVal.absoluteValue) {
asmgen.out("""
lda P8ESTACK_LO+1,x
bne +
dec P8ESTACK_HI+1,x
+ dec P8ESTACK_LO+1,x""")
}
} else {
repeat(leftVal) {
asmgen.out("""
inc P8ESTACK_LO+1,x
bne +
inc P8ESTACK_HI+1,x
+""")
}
}
}
return true
}
else if (rightVal!=null && rightVal in -4..4)
{
translateExpressionInternal(left)
if(leftDt in ByteDatatypes) {
val incdec = if(rightVal<0) "dec" else "inc"
repeat(rightVal.absoluteValue) {
asmgen.out(" $incdec P8ESTACK_LO+1,x")
}
} else {
// word
if(rightVal<0) {
repeat(rightVal.absoluteValue) {
asmgen.out("""
lda P8ESTACK_LO+1,x
bne +
dec P8ESTACK_HI+1,x
+ dec P8ESTACK_LO+1,x""")
}
} else {
repeat(rightVal) {
asmgen.out("""
inc P8ESTACK_LO+1,x
bne +
inc P8ESTACK_HI+1,x
+""")
}
}
}
return true
}
}
}
"-" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val rightVal = right.asConstInteger()
if (rightVal!=null && rightVal in -4..4)
{
translateExpressionInternal(left)
if(leftDt in ByteDatatypes) {
val incdec = if(rightVal<0) "inc" else "dec"
repeat(rightVal.absoluteValue) {
asmgen.out(" $incdec P8ESTACK_LO+1,x")
}
} else {
// word
if(rightVal>0) {
repeat(rightVal.absoluteValue) {
asmgen.out("""
lda P8ESTACK_LO+1,x
bne +
dec P8ESTACK_HI+1,x
+ dec P8ESTACK_LO+1,x""")
}
} else {
repeat(rightVal) {
asmgen.out("""
inc P8ESTACK_LO+1,x
bne +
inc P8ESTACK_HI+1,x
+""")
}
}
}
return true
}
}
}
">>" -> {
val amount = right.asConstInteger()
if(amount!=null) {
translateExpressionInternal(left)
when (leftDt) {
DataType.UBYTE -> {
if (amount <= 2)
repeat(amount) { asmgen.out(" lsr P8ESTACK_LO+1,x") }
else {
asmgen.out(" lda P8ESTACK_LO+1,x")
repeat(amount) { asmgen.out(" lsr a") }
asmgen.out(" sta P8ESTACK_LO+1,x")
}
}
DataType.BYTE -> {
if (amount <= 2)
repeat(amount) { asmgen.out(" lda P8ESTACK_LO+1,x | asl a | ror P8ESTACK_LO+1,x") }
else {
asmgen.out(" lda P8ESTACK_LO+1,x | sta P8ZP_SCRATCH_B1")
repeat(amount) { asmgen.out(" asl a | ror P8ZP_SCRATCH_B1 | lda P8ZP_SCRATCH_B1") }
asmgen.out(" sta P8ESTACK_LO+1,x")
}
}
DataType.UWORD -> {
if(amount>=16) {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
return true
}
var amountLeft = amount
while (amountLeft >= 7) {
asmgen.out(" jsr math.shift_right_uw_7")
amountLeft -= 7
}
if (amountLeft in 0..2)
repeat(amountLeft) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
else
asmgen.out(" jsr math.shift_right_uw_$amountLeft")
}
DataType.WORD -> {
if(amount>=16) {
asmgen.out("""
lda P8ESTACK_HI+1,x
bmi +
lda #0
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
beq ++
+ lda #255
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
+""")
return true
}
var amountLeft = amount
while (amountLeft >= 7) {
asmgen.out(" jsr math.shift_right_w_7")
amountLeft -= 7
}
if (amountLeft in 0..2)
repeat(amountLeft) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
else
asmgen.out(" jsr math.shift_right_w_$amountLeft")
}
else -> throw AssemblyError("weird type")
}
return true
}
}
"<<" -> {
val amount = right.asConstInteger()
if(amount!=null) {
translateExpressionInternal(left)
if (leftDt in ByteDatatypes) {
if (amount <= 2)
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
else {
asmgen.out(" lda P8ESTACK_LO+1,x")
repeat(amount) { asmgen.out(" asl a") }
asmgen.out(" sta P8ESTACK_LO+1,x")
}
} else {
var amountLeft = amount
while (amountLeft >= 7) {
asmgen.out(" jsr math.shift_left_w_7")
amountLeft -= 7
}
if (amountLeft in 0..2)
repeat(amountLeft) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
else
asmgen.out(" jsr math.shift_left_w_$amountLeft")
}
return true
}
}
"*" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val leftVar = left as? PtIdentifier
val rightVar = right as? PtIdentifier
if(leftVar!=null && rightVar!=null && leftVar==rightVar) {
translateSquared(leftVar, leftDt)
return true
}
}
val value = right as? PtNumber
if(value!=null) {
if(rightDt in IntegerDatatypes) {
val amount = value.number.toInt()
if(amount==2) {
// optimize x*2 common case
translateExpressionInternal(left)
if(leftDt in ByteDatatypes) {
asmgen.out(" asl P8ESTACK_LO+1,x")
} else {
asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x")
}
return true
}
when(rightDt) {
DataType.UBYTE -> {
if(amount in asmgen.optimizedByteMultiplications) {
translateExpressionInternal(left)
asmgen.out(" jsr math.stack_mul_byte_$amount")
return true
}
}
DataType.BYTE -> {
if(amount in asmgen.optimizedByteMultiplications) {
translateExpressionInternal(left)
asmgen.out(" jsr math.stack_mul_byte_$amount")
return true
}
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
translateExpressionInternal(left)
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
return true
}
}
DataType.UWORD -> {
if(amount in asmgen.optimizedWordMultiplications) {
translateExpressionInternal(left)
asmgen.out(" jsr math.stack_mul_word_$amount")
return true
}
}
DataType.WORD -> {
if(amount in asmgen.optimizedWordMultiplications) {
translateExpressionInternal(left)
asmgen.out(" jsr math.stack_mul_word_$amount")
return true
}
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
translateExpressionInternal(left)
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
return true
}
}
else -> {}
}
}
}
}
"/" -> {
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val rightVal = right.asConstInteger()
if(rightVal!=null && rightVal==2) {
translateExpressionInternal(left)
when (leftDt) {
DataType.UBYTE -> {
asmgen.out(" lsr P8ESTACK_LO+1,x")
}
DataType.UWORD -> {
asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
}
DataType.BYTE -> {
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
asmgen.out("""
lda P8ESTACK_LO+1,x
bpl +
inc P8ESTACK_LO+1,x
lda P8ESTACK_LO+1,x
+ asl a
ror P8ESTACK_LO+1,x""")
}
DataType.WORD -> {
// signed divide using shift needs adjusting of negative value to get correct rounding towards zero
asmgen.out("""
lda P8ESTACK_HI+1,x
bpl ++
inc P8ESTACK_LO+1,x
bne +
inc P8ESTACK_HI+1,x
+ lda P8ESTACK_HI+1,x
+ asl a
ror P8ESTACK_HI+1,x
ror P8ESTACK_LO+1,x""")
}
else -> throw AssemblyError("weird dt")
}
return true
}
}
}
}
return false
}
private fun translateComparisonWithZero(expr: PtExpression, dt: DataType, operator: String) {
if(expr.isSimple()) {
if(operator=="!=") {
when (dt) {
in ByteDatatypes -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.A, dt == DataType.BYTE)
asmgen.out("""
beq +
lda #1
+ sta P8ESTACK_LO,x
dex""")
return
}
in WordDatatypes -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, dt == DataType.WORD)
asmgen.out("""
sty P8ZP_SCRATCH_B1
ora P8ZP_SCRATCH_B1
beq +
lda #1
+ sta P8ESTACK_LO,x
dex""")
return
}
DataType.FLOAT -> {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
asmgen.out("""
jsr floats.SIGN
sta P8ESTACK_LO,x
dex""")
return
}
else -> {}
}
}
/* operator == is not worth it to special case, the code is mostly larger */
}
translateExpressionInternal(expr)
when(operator) {
"==" -> {
when(dt) {
DataType.UBYTE, DataType.BYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
DataType.UWORD, DataType.WORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
DataType.FLOAT -> asmgen.out(" jsr floats.equal_zero")
else -> throw AssemblyError("wrong dt")
}
}
"!=" -> {
when(dt) {
DataType.UBYTE, DataType.BYTE -> asmgen.out(" jsr prog8_lib.notequalzero_b")
DataType.UWORD, DataType.WORD -> asmgen.out(" jsr prog8_lib.notequalzero_w")
DataType.FLOAT -> asmgen.out(" jsr floats.notequal_zero")
else -> throw AssemblyError("wrong dt")
}
}
"<" -> {
if(dt==DataType.UBYTE || dt==DataType.UWORD)
return translateExpressionInternal(PtNumber.fromBoolean(false, expr.position))
when(dt) {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lesszero_b")
DataType.WORD -> asmgen.out(" jsr prog8_lib.lesszero_w")
DataType.FLOAT -> asmgen.out(" jsr floats.less_zero")
else -> throw AssemblyError("wrong dt")
}
}
">" -> {
when(dt) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.greaterzero_ub")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterzero_sb")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.greaterzero_uw")
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterzero_sw")
DataType.FLOAT -> asmgen.out(" jsr floats.greater_zero")
else -> throw AssemblyError("wrong dt")
}
}
"<=" -> {
when(dt) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.equalzero_b")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lessequalzero_sb")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.equalzero_w")
DataType.WORD -> asmgen.out(" jsr prog8_lib.lessequalzero_sw")
DataType.FLOAT -> asmgen.out(" jsr floats.lessequal_zero")
else -> throw AssemblyError("wrong dt")
}
}
">=" -> {
if(dt==DataType.UBYTE || dt==DataType.UWORD)
return translateExpressionInternal(PtNumber.fromBoolean(true, expr.position))
when(dt) {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterequalzero_sb")
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterequalzero_sw")
DataType.FLOAT -> asmgen.out(" jsr floats.greaterequal_zero")
else -> throw AssemblyError("wrong dt")
}
}
else -> throw AssemblyError("invalid comparison operator")
}
}
private fun translateSquared(variable: PtIdentifier, dt: DataType) {
val asmVar = asmgen.asmVariableName(variable)
when(dt) {
DataType.BYTE, DataType.UBYTE -> {
asmgen.out(" lda $asmVar")
asmgen.signExtendAYlsb(dt)
asmgen.out(" jsr math.square")
}
DataType.UWORD, DataType.WORD -> {
asmgen.out(" lda $asmVar | ldy $asmVar+1 | jsr math.square")
}
else -> throw AssemblyError("require integer dt for square")
}
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
}
private fun translateExpression(expr: PtPrefix) {
translateExpressionInternal(expr.value)
when(expr.operator) {
"+" -> {}
"-" -> {
when(expr.type) {
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.neg_b")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.neg_w")
DataType.FLOAT -> asmgen.out(" jsr floats.neg_f")
else -> throw AssemblyError("weird type")
}
}
"~" -> {
when(expr.type) {
in ByteDatatypes ->
asmgen.out("""
lda P8ESTACK_LO+1,x
eor #255
sta P8ESTACK_LO+1,x
""")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
else -> throw AssemblyError("weird type")
}
}
else -> throw AssemblyError("invalid prefix operator ${expr.operator}")
}
}
private fun translateExpression(arrayExpr: PtArrayIndexer) {
val elementDt = arrayExpr.type
val arrayVarName = asmgen.asmVariableName(arrayExpr.variable)
if(arrayExpr.variable.type==DataType.UWORD) {
// indexing a pointer var instead of a real array or string
if(elementDt !in ByteDatatypes)
throw AssemblyError("non-array var indexing requires bytes dt")
if(arrayExpr.index.type != DataType.UBYTE)
throw AssemblyError("non-array var indexing requires bytes index")
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
if(asmgen.isZpVar(arrayExpr.variable)) {
asmgen.out(" lda ($arrayVarName),y")
} else {
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
}
asmgen.out(" sta P8ESTACK_LO,x | dex")
return
}
if(arrayExpr.splitWords)
TODO("split words expression ${arrayExpr.position}")
val constIndexNum = arrayExpr.index.asConstInteger()
if(constIndexNum!=null) {
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
when(elementDt) {
in ByteDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
}
in WordDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | lda $arrayVarName+$indexValue+1 | sta P8ESTACK_HI,x | dex")
}
DataType.FLOAT -> {
asmgen.out(" lda #<($arrayVarName+$indexValue) | ldy #>($arrayVarName+$indexValue) | jsr floats.push_float")
}
else -> throw AssemblyError("weird element type")
}
} else {
when(elementDt) {
in ByteDatatypes -> {
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | dex")
}
in WordDatatypes -> {
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | lda $arrayVarName+1,y | sta P8ESTACK_HI,x | dex")
}
DataType.FLOAT -> {
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.A)
asmgen.out("""
ldy #>$arrayVarName
clc
adc #<$arrayVarName
bcc +
iny
+ jsr floats.push_float""")
}
else -> throw AssemblyError("weird dt")
}
}
}
private fun translateBinaryOperatorBytes(operator: String, types: DataType) {
when(operator) {
"*" -> asmgen.out(" jsr prog8_lib.mul_byte") // the optimized routines should have been checked earlier
"/" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
"%" -> {
if(types==DataType.BYTE)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out(" jsr prog8_lib.remainder_ub")
}
"+" -> asmgen.out("""
lda P8ESTACK_LO+2,x
clc
adc P8ESTACK_LO+1,x
inx
sta P8ESTACK_LO+1,x
""")
"-" -> asmgen.out("""
lda P8ESTACK_LO+2,x
sec
sbc P8ESTACK_LO+1,x
inx
sta P8ESTACK_LO+1,x
""")
"<<" -> asmgen.out(" jsr prog8_lib.shiftleft_b")
">>" -> asmgen.out(" jsr prog8_lib.shiftright_b")
"<" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.less_ub" else " jsr prog8_lib.less_b")
">" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greater_ub" else " jsr prog8_lib.greater_b")
"<=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.lesseq_ub" else " jsr prog8_lib.lesseq_b")
">=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greatereq_ub" else " jsr prog8_lib.greatereq_b")
"==" -> asmgen.out(" jsr prog8_lib.equal_b")
"!=" -> asmgen.out(" jsr prog8_lib.notequal_b")
"&" -> asmgen.out(" jsr prog8_lib.bitand_b")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_b")
"|" -> asmgen.out(" jsr prog8_lib.bitor_b")
else -> throw AssemblyError("invalid operator $operator")
}
}
private fun translateBinaryOperatorWords(operator: String, dt: DataType) {
when(operator) {
"*" -> asmgen.out(" jsr prog8_lib.mul_word")
"/" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
"%" -> {
if(dt==DataType.WORD)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out(" jsr prog8_lib.remainder_uw")
}
"+" -> asmgen.out(" jsr prog8_lib.add_w")
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
"<<" -> asmgen.out(" jsr math.shift_left_w")
">>" -> {
if(dt==DataType.UWORD)
asmgen.out(" jsr math.shift_right_uw")
else
asmgen.out(" jsr math.shift_right_w")
}
"<" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
">" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
"<=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
">=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
"==" -> asmgen.out(" jsr prog8_lib.equal_w")
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w")
"&" -> asmgen.out(" jsr prog8_lib.bitand_w")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
"|" -> asmgen.out(" jsr prog8_lib.bitor_w")
else -> throw AssemblyError("invalid operator $operator")
}
}
private fun translateBinaryOperatorFloats(operator: String) {
when(operator) {
"*" -> asmgen.out(" jsr floats.mul_f")
"/" -> asmgen.out(" jsr floats.div_f")
"+" -> asmgen.out(" jsr floats.add_f")
"-" -> asmgen.out(" jsr floats.sub_f")
"<" -> asmgen.out(" jsr floats.less_f")
">" -> asmgen.out(" jsr floats.greater_f")
"<=" -> asmgen.out(" jsr floats.lesseq_f")
">=" -> asmgen.out(" jsr floats.greatereq_f")
"==" -> asmgen.out(" jsr floats.equal_f")
"!=" -> asmgen.out(" jsr floats.notequal_f")
"%", "<<", ">>", "&", "^", "|" -> throw AssemblyError("requires integer datatype")
else -> throw AssemblyError("invalid operator $operator")
}
}
private fun translateCompareStrings(s1: PtExpression, operator: String, s2: PtExpression) {
asmgen.assignExpressionToVariable(s1, "prog8_lib.strcmp_expression._arg_s1", DataType.UWORD)
asmgen.assignExpressionToVariable(s2, "prog8_lib.strcmp_expression._arg_s2", DataType.UWORD)
asmgen.out(" jsr prog8_lib.strcmp_expression") // result of compare is in A
compareStringsProcessResultInA(operator)
}
private fun compareStringsProcessResultInA(operator: String) {
when(operator) {
"==" -> asmgen.out(" and #1 | eor #1 | sta P8ESTACK_LO,x")
"!=" -> asmgen.out(" and #1 | sta P8ESTACK_LO,x")
"<=" -> asmgen.out("""
bpl +
lda #1
bne ++
+ lda #0
+ sta P8ESTACK_LO,x""")
">=" -> asmgen.out("""
bmi +
lda #1
bne ++
+ lda #0
+ sta P8ESTACK_LO,x""")
"<" -> asmgen.out("""
bmi +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x""")
">" -> asmgen.out("""
bpl +
lda #0
beq ++
+ lda #1
+ sta P8ESTACK_LO,x""")
}
asmgen.out(" dex")
}
}
@@ -6,26 +6,6 @@ import prog8.code.ast.PtSub
import prog8.code.core.*
internal fun IPtSubroutine.regXasResult(): Boolean =
(this is PtAsmSub) && this.returns.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
internal fun IPtSubroutine.shouldSaveX(): Boolean =
this.regXasResult() || (this is PtAsmSub && (CpuRegister.X in this.clobbers || regXasParam()))
internal fun PtAsmSub.regXasParam(): Boolean =
parameters.any { it.first.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
internal class KeepAresult(val saveOnEntry: Boolean, val saveOnReturn: Boolean)
internal fun PtAsmSub.shouldKeepA(): KeepAresult {
// determine if A's value should be kept when preparing for calling the subroutine, and when returning from it
// it seems that we never have to save A when calling? will be loaded correctly after setup.
// but on return it depends on wether the routine returns something in A.
val saveAonReturn = returns.any { it.first.registerOrPair==RegisterOrPair.A || it.first.registerOrPair==RegisterOrPair.AY || it.first.registerOrPair==RegisterOrPair.AX }
return KeepAresult(false, saveAonReturn)
}
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> {
when(this) {
is PtAsmSub -> {
@@ -5,9 +5,10 @@ import prog8.code.ast.*
import prog8.code.core.*
import kotlin.math.absoluteValue
internal class ForLoopsAsmGen(private val program: PtProgram,
private val asmgen: AsmGen6502Internal,
private val zeropage: Zeropage) {
internal class ForLoopsAsmGen(
private val asmgen: AsmGen6502Internal,
private val zeropage: Zeropage
) {
internal fun translate(stmt: PtForLoop) {
val iterableDt = stmt.iterable.type
@@ -249,23 +250,6 @@ $endLabel""")
$loopLabel""")
asmgen.translate(stmt.statements)
if(iterableDt==DataType.ARRAY_UW) {
asmgen.out("""
lda $varname
sec
sbc #<${stepsize.absoluteValue}
sta $varname
lda $varname+1
sbc #>${stepsize.absoluteValue}
sta $varname+1
$modifiedLabel cmp #0 ; modified
bcc $endLabel
bne $loopLabel
lda $varname
$modifiedLabel2 cmp #0 ; modified
bcs $loopLabel
$endLabel""")
} else {
asmgen.out("""
lda $varname
sec
@@ -283,7 +267,6 @@ $modifiedLabel sbc #0 ; modified
eor #$80
+ bpl $loopLabel
$endLabel""")
}
}
}
}
@@ -400,7 +383,7 @@ $loopLabel sty $indexVar
// 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""") },
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
)
} else {
@@ -11,42 +11,10 @@ import prog8.codegen.cpu6502.assignment.TargetStorageKind
internal class FunctionCallAsmGen(private val program: PtProgram, private val asmgen: AsmGen6502Internal) {
internal fun translateFunctionCallStatement(stmt: PtFunctionCall) {
saveXbeforeCall(stmt)
translateFunctionCall(stmt)
restoreXafterCall(stmt)
// just ignore any result values from the function call.
}
internal fun saveXbeforeCall(stmt: PtFunctionCall) {
val symbol = asmgen.symbolTable.lookup(stmt.name)
val sub = symbol!!.astNode as IPtSubroutine
if(sub.shouldSaveX()) {
if(sub is PtAsmSub) {
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if (regSaveOnStack)
asmgen.saveRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnEntry)
else
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
} else
asmgen.saveRegisterLocal(CpuRegister.X, stmt.definingISub()!!)
}
}
internal fun restoreXafterCall(stmt: PtFunctionCall) {
val symbol = asmgen.symbolTable.lookup(stmt.name)
val sub = symbol!!.astNode as IPtSubroutine
if(sub.shouldSaveX()) {
if(sub is PtAsmSub) {
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
if (regSaveOnStack)
asmgen.restoreRegisterStack(CpuRegister.X, sub.shouldKeepA().saveOnReturn)
else
asmgen.restoreRegisterLocal(CpuRegister.X)
} else
asmgen.restoreRegisterLocal(CpuRegister.X)
}
}
internal fun optimizeIntArgsViaRegisters(sub: PtSub) =
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypes)
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypes && sub.parameters[1].type in ByteDatatypes)
@@ -63,8 +31,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
if(sub is PtAsmSub) {
argumentsViaRegisters(sub, call)
if (sub.inline && asmgen.options.optimize) {
// inline the subroutine.
if (sub.inline) {
// inline the subroutine. (regardless of optimization settings!)
// we do this by copying the subroutine's statements at the call site.
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
// (this condition has been enforced by an ast check earlier)
@@ -13,7 +13,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
val targetIdent = stmt.target.identifier
val targetMemory = stmt.target.memory
val targetArrayIdx = stmt.target.array
val scope = stmt.definingISub()
when {
targetIdent!=null -> {
val what = asmgen.asmVariableName(targetIdent)
@@ -76,7 +75,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
dec ${asmArrayvarname}_msb+$constIndex
+ dec ${asmArrayvarname}_lsb+$constIndex""")
} else {
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
if(incr)
asmgen.out(" inc ${asmArrayvarname}_lsb,x | bne + | inc ${asmArrayvarname}_msb,x |+")
@@ -86,14 +84,30 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
bne +
dec ${asmArrayvarname}_msb,x
+ dec ${asmArrayvarname}_lsb,x""")
asmgen.restoreRegisterLocal(CpuRegister.X)
}
return
}
if(constIndex!=null) {
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
when(elementDt) {
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
in ByteDatatypes -> {
if(targetArrayIdx.usesPointerVariable) {
asmgen.out("""
lda $asmArrayvarname
clc
adc #$indexValue
sta (+) +1
lda $asmArrayvarname+1
adc #0
sta (+) +2""")
if(incr)
asmgen.out("+\tinc ${'$'}ffff\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff\t; modified")
} else {
asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
}
}
in WordDatatypes -> {
if(incr)
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
@@ -113,11 +127,25 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
}
else
{
asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
when(elementDt) {
in ByteDatatypes -> {
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
if(targetArrayIdx.usesPointerVariable) {
asmgen.out("""
txa
clc
adc $asmArrayvarname
sta (+) +1
lda $asmArrayvarname+1
adc #0
sta (+) +2""")
if(incr)
asmgen.out("+\tinc ${'$'}ffff\t; modified")
else
asmgen.out("+\tdec ${'$'}ffff\t; modified")
} else {
asmgen.out(if (incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
}
}
in WordDatatypes -> {
if(incr)
@@ -127,8 +155,7 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
lda $asmArrayvarname,x
bne +
dec $asmArrayvarname+1,x
+ dec $asmArrayvarname,x
""")
+ dec $asmArrayvarname,x""")
}
DataType.FLOAT -> {
asmgen.out("""
@@ -141,7 +168,6 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
}
else -> throw AssemblyError("weird array elt dt")
}
asmgen.restoreRegisterLocal(CpuRegister.X)
}
}
}
@@ -33,8 +33,9 @@ internal class ProgramAndVarsGen(
internal fun generate() {
header()
val allBlocks = program.allBlocks()
if(allBlocks.first().name != "main")
throw AssemblyError("first block should be 'main'")
if(allBlocks.first().name != "p8_main" && allBlocks.first().name != "main")
throw AssemblyError("first block should be 'main' or 'p8_main'")
if(errors.noErrors()) {
program.allBlocks().forEach { block2asm(it) }
@@ -74,8 +75,6 @@ internal class ProgramAndVarsGen(
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
asmgen.out(".weak") // hack to allow user to override the following two with command line redefinition (however, just use '-esa' command line option instead!)
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
asmgen.out(".endweak")
if(options.symbolDefs.isNotEmpty()) {
@@ -139,24 +138,24 @@ internal class ProgramAndVarsGen(
"cx16" -> {
if(options.floats)
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
asmgen.out(" jsr main.start")
asmgen.out(" jsr p8_main.p8_start")
asmgen.out(" jmp sys.cleanup_at_exit")
}
"c64" -> {
asmgen.out(" jsr main.start | lda #31 | sta $01")
asmgen.out(" jsr p8_main.p8_start | lda #31 | sta $01")
if(!options.noSysInit)
asmgen.out(" jmp sys.cleanup_at_exit")
else
asmgen.out(" rts")
}
"c128" -> {
asmgen.out(" jsr main.start | lda #0 | sta ${"$"}ff00")
asmgen.out(" jsr p8_main.p8_start | lda #0 | sta ${"$"}ff00")
if(!options.noSysInit)
asmgen.out(" jmp sys.cleanup_at_exit")
else
asmgen.out(" rts")
}
else -> asmgen.jmp("main.start")
else -> asmgen.jmp("p8_main.p8_start")
}
}
@@ -175,7 +174,8 @@ internal class ProgramAndVarsGen(
private fun footer() {
asmgen.out("; bss sections")
if(options.varsHigh) {
asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}")
if(options.varsHighBank!=null) {
if(options.compTarget.machine.BSSHIGHRAM_START == 0u || options.compTarget.machine.BSSHIGHRAM_END==0u) {
throw AssemblyError("current compilation target hasn't got the high ram area properly defined")
}
@@ -269,9 +269,7 @@ internal class ProgramAndVarsGen(
internal fun translateAsmSubroutine(sub: PtAsmSub) {
if(sub.inline) {
if(options.optimize) {
return
}
return // subroutine gets inlined at call site.
}
asmgen.out("")
@@ -311,7 +309,9 @@ internal class ProgramAndVarsGen(
asmgen.out("${sub.name}\t$asmStartScope")
val scope = symboltable.lookupOrElse(sub.scopedName) { throw AssemblyError("lookup") }
val scope = symboltable.lookupOrElse(sub.scopedName) {
throw AssemblyError("lookup ${sub.scopedName}")
}
require(scope.type==StNodeType.SUBROUTINE)
val varsInSubroutine = getVars(scope)
@@ -331,7 +331,7 @@ internal class ProgramAndVarsGen(
asmsubs2asm(sub.children)
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
if(sub.name=="start" && sub.definingBlock()!!.name=="main")
if((sub.name=="start" || sub.name=="p8_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8_main"))
entrypointInitialization()
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
@@ -369,12 +369,6 @@ internal class ProgramAndVarsGen(
else -> throw AssemblyError("weird dt for extravar $dt")
}
}
if(asmGenInfo.usedRegsaveA) // will probably never occur
asmgen.out("prog8_regsaveA .byte ?")
if(asmGenInfo.usedRegsaveX)
asmgen.out("prog8_regsaveX .byte ?")
if(asmGenInfo.usedRegsaveY)
asmgen.out("prog8_regsaveY .byte ?")
if(asmGenInfo.usedFloatEvalResultVar1)
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
if(asmGenInfo.usedFloatEvalResultVar2)
@@ -480,8 +474,8 @@ internal class ProgramAndVarsGen(
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
for (variable in vars) {
val scopedName = variable.key
val svar = symboltable.flat.getValue(scopedName) as StStaticVariable
if(svar.onetimeInitializationStringValue!=null)
val svar = symboltable.lookup(scopedName) as? StStaticVariable
if(svar?.onetimeInitializationStringValue!=null)
result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!))
}
return result
@@ -492,8 +486,8 @@ internal class ProgramAndVarsGen(
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
for (variable in vars) {
val scopedName = variable.key
val svar = symboltable.flat.getValue(scopedName) as StStaticVariable
if(svar.onetimeInitializationArrayValue!=null)
val svar = symboltable.lookup(scopedName) as? StStaticVariable
if(svar?.onetimeInitializationArrayValue!=null)
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!))
}
return result
@@ -0,0 +1,265 @@
package prog8.codegen.cpu6502.assignment
import prog8.code.ast.PtBinaryExpression
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
//
// This contains codegen for stack-based evaluation of binary expressions.
// It uses the CPU stack so depth is limited.
// It is called "as a last resort" if the optimized codegen path is unable
// to come up with a special case of the expression.
//
internal class AnyExprAsmGen(
private val asmgen: AsmGen6502Internal
) {
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.type) {
in ByteDatatypes -> {
if(expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes)
return assignByteBinExpr(expr, assign)
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
require(expr.operator in ComparisonOperators)
TODO("words operands comparison -> byte")
}
if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
require(expr.operator in ComparisonOperators)
return assignFloatBinExpr(expr, assign)
}
TODO("weird expr operand types")
}
in WordDatatypes -> {
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) {
"both operands must be words"
}
return assignWordBinExpr(expr)
}
DataType.FLOAT -> {
require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) {
"both operands must be floats"
}
return assignFloatBinExpr(expr, assign)
}
else -> throw AssemblyError("weird expression type in assignment")
}
}
private fun assignWordBinExpr(expr: PtBinaryExpression): Boolean {
when(expr.operator) {
"+" -> {
TODO("word + at ${expr.position}")
}
"-" -> {
TODO("word - at ${expr.position}")
}
"*" -> {
TODO("word * at ${expr.position}")
}
"/" -> {
TODO("word / at ${expr.position}")
}
"<<" -> {
TODO("word << at ${expr.position}")
}
">>" -> {
TODO("word >> at ${expr.position}")
}
"%" -> {
TODO("word % at ${expr.position}")
}
"&", "and" -> {
TODO("word and at ${expr.position}")
}
"|", "or" -> {
TODO("word or at ${expr.position}")
}
"^", "xor" -> {
TODO("word xor at ${expr.position}")
}
"==" -> {
TODO("word == at ${expr.position}")
}
"!=" -> {
TODO("word != at ${expr.position}")
}
"<" -> {
TODO("word < at ${expr.position}")
}
"<=" -> {
TODO("word <= at ${expr.position}")
}
">" -> {
TODO("word > at ${expr.position}")
}
">=" -> {
TODO("word >= at ${expr.position}")
}
else -> return false
}
}
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.operator) {
"+" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"-" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"*" -> {
TODO("byte * at ${expr.position}")
}
"/" -> {
TODO("byte / at ${expr.position}")
}
"<<" -> {
TODO("byte << at ${expr.position}")
}
">>" -> {
TODO("byte >> at ${expr.position}")
}
"%" -> {
TODO("byte % at ${expr.position}")
}
"&", "and" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"|", "or" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"^", "xor" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"==" -> {
TODO("byte == at ${expr.position}")
}
"!=" -> {
TODO("byte != at ${expr.position}")
}
"<" -> {
TODO("byte < at ${expr.position}")
}
"<=" -> {
TODO("byte <= at ${expr.position}")
}
">" -> {
TODO("byte > at ${expr.position}")
}
">=" -> {
TODO("byte >= at ${expr.position}")
}
else -> return false
}
}
private fun assignFloatBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.operator) {
"+" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
if(!expr.right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC2, true)
if(!expr.right.isSimple()) asmgen.popFAC1()
asmgen.out(" jsr floats.FADDT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"-" -> {
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC1, true)
if(!expr.left.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC2, true)
if(!expr.left.isSimple()) asmgen.popFAC1()
asmgen.out(" jsr floats.FSUBT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"*" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
if(!expr.right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC2, true)
if(!expr.right.isSimple()) asmgen.popFAC1()
asmgen.out(" jsr floats.FMULTT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"/" -> {
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.FAC1, true)
if(!expr.left.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC2, true)
if(!expr.left.isSimple()) asmgen.popFAC1()
asmgen.out(" jsr floats.FDIVT")
asmgen.assignRegister(RegisterOrPair.FAC1, assign.target)
return true
}
"==" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_equal_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"!=" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_notequal_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"<" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_less_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
">" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_greater_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
"<=" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_lesseq_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
">=" -> {
setupFloatComparisonFAC1vsVarAY(expr)
asmgen.out(" jsr floats.var_fac1_greatereq_f")
asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true
}
else -> TODO("float expression operator ${expr.operator}")
}
}
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)
if(!expr.right.isSimple()) asmgen.popFAC1()
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
}
}
@@ -10,8 +10,7 @@ internal enum class TargetStorageKind {
VARIABLE,
ARRAY,
MEMORY,
REGISTER,
STACK
REGISTER
}
internal enum class SourceStorageKind {
@@ -20,7 +19,6 @@ internal enum class SourceStorageKind {
ARRAY,
MEMORY,
REGISTER,
STACK, // value is already present on stack
EXPRESSION, // expression in ast-form, still to be evaluated
}
@@ -120,7 +118,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
TargetStorageKind.MEMORY -> {
left isSameAs memory!!
}
TargetStorageKind.REGISTER, TargetStorageKind.STACK -> {
TargetStorageKind.REGISTER -> {
false
}
}
@@ -178,8 +176,8 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
}
is PtFunctionCall -> {
val symbol = asmgen.symbolTable.lookup(value.name)
val sub = symbol!!.astNode as IPtSubroutine
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
val sub = symbol.astNode as IPtSubroutine
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second
?: throw AssemblyError("can't translate zero return values in assignment")
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+4
View File
@@ -45,6 +45,10 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
warnings.add(text)
}
override fun undefined(symbol: List<String>, position: Position) {
err("undefined symbol: ${symbol.joinToString(".")}", position)
}
override fun noErrors(): Boolean = errors.isEmpty()
override fun report() {
+17 -5
View File
@@ -1,7 +1,9 @@
package prog8tests.codegencpu6502
import io.kotest.assertions.throwables.shouldNotThrowAny
import io.kotest.assertions.withClue
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
import io.kotest.matchers.shouldBe
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
@@ -40,9 +42,9 @@ class TestCodegen: FunSpec({
// xx += cx16.r0
// }
//}
val codegen = AsmGen6502()
val codegen = AsmGen6502(prefixSymbols = false)
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
@@ -92,7 +94,7 @@ class TestCodegen: FunSpec({
program.add(block)
// define the "cx16.r0" virtual register
val cx16block = PtBlock("cx16", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val cx16block = PtBlock("cx16", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
program.add(cx16block)
@@ -104,12 +106,22 @@ class TestCodegen: FunSpec({
Files.deleteIfExists(Path("${result.name}.asm"))
}
test("64tass assembler available? - if this fails you need to install 64tass in the path") {
test("64tass assembler available? - if this fails you need to install 64tass version 1.58 or newer in the path") {
val command = mutableListOf("64tass", "--version")
shouldNotThrowAny {
val proc = ProcessBuilder(command).inheritIO().start()
val proc = ProcessBuilder(command).start()
val output = String(proc.inputStream.readBytes())
val result = proc.waitFor()
result.shouldBe(0)
val (_, version) = output.split('V')
val (major, minor, _) = version.split('.')
val majorNum = major.toInt()
val minorNum = minor.toInt()
withClue("64tass version should be 1.58 or newer") {
majorNum shouldBeGreaterThanOrEqual 1
if (majorNum == 1)
minorNum shouldBeGreaterThanOrEqual 58
}
}
}
})
+1 -1
View File
@@ -29,7 +29,7 @@ dependencies {
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:1.1.17"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
}
@@ -15,7 +15,7 @@ class ExperiCodeGen: ICodeGeneratorBackend {
symbolTable: SymbolTable,
options: CompilationOptions,
errors: IErrorReporter
): IAssemblyProgram? {
): IAssemblyProgram {
// you could write a code generator directly on the PtProgram AST,
// but you can also use the Intermediate Representation to build a codegen on:
@@ -26,6 +26,15 @@ class ExperiCodeGen: ICodeGeneratorBackend {
IRFileWriter(irProgram, null).write()
println("** experimental codegen stub: no assembly generated **")
return null
return EmptyProgram
}
}
}
private object EmptyProgram : IAssemblyProgram {
override val name = "<Empty Program>"
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
println("** nothing assembled **")
return true
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ dependencies {
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:1.1.17"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
}
@@ -1,6 +1,5 @@
package prog8.codegen.intermediate
import prog8.code.StStaticVariable
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
@@ -14,7 +13,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(assignment.target.children.single() is PtMachineRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
return translateRegularAssign(assignment)
val chunks = translateRegularAssign(assignment)
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(assignment.position)
return chunks
}
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
@@ -25,7 +26,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val memory = augAssign.target.memory
val array = augAssign.target.array
return if(ident!=null) {
val chunks = if(ident!=null) {
assignVarAugmented(ident.name, augAssign)
} else if(memory != null) {
if(memory.address is PtNumber)
@@ -40,6 +41,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} else {
fallbackAssign(augAssign)
}
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(augAssign.position)
return chunks
}
private fun assignMemoryAugmented(
@@ -49,15 +52,15 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val value = assignment.value
val vmDt = irType(value.type)
return when(assignment.operator) {
"+" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
"-" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
"*" -> expressionEval.operatorMultiplyInplace(address, null, vmDt, value)
"/" -> expressionEval.operatorDivideInplace(address, null, vmDt, value.type in SignedDatatypes, value)
"|" -> expressionEval.operatorOrInplace(address, null, vmDt, value)
"&" -> expressionEval.operatorAndInplace(address, null, vmDt, value)
"^" -> expressionEval.operatorXorInplace(address, null, vmDt, value)
"<<" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value)
">>" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value)
"+=" -> expressionEval.operatorPlusInplace(address, null, vmDt, value)
"-=" -> expressionEval.operatorMinusInplace(address, null, vmDt, value)
"*=" -> expressionEval.operatorMultiplyInplace(address, null, vmDt, value)
"/=" -> expressionEval.operatorDivideInplace(address, null, vmDt, value.type in SignedDatatypes, value)
"|=" -> expressionEval.operatorOrInplace(address, null, vmDt, value)
"&=" -> expressionEval.operatorAndInplace(address, null, vmDt, value)
"^=" -> expressionEval.operatorXorInplace(address, null, vmDt, value)
"<<=" -> expressionEval.operatorShiftLeftInplace(address, null, vmDt, value)
">>=" -> expressionEval.operatorShiftRightInplace(address, null, vmDt, value.type in SignedDatatypes, value)
"%=" -> expressionEval.operatorModuloInplace(address, null, vmDt, value)
"==" -> expressionEval.operatorEqualsInplace(address, null, vmDt, value)
"!=" -> expressionEval.operatorNotEqualsInplace(address, null, vmDt, value)
@@ -97,35 +100,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
if (codeGen.options.slowCodegenWarnings)
codeGen.errors.warn("indirect code for in-place assignment", origAssign.position)
val value: PtExpression
if(origAssign.operator in PrefixOperators) {
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
value.add(origAssign.value)
} else {
require(origAssign.operator.endsWith('='))
if(codeGen.options.useNewExprCode) {
// X += Y -> temp = X, temp += Y, X = temp
val tempvar = codeGen.getReusableTempvar(origAssign.definingSub()!!, origAssign.target.type)
val assign = PtAssignment(origAssign.position)
val target = PtAssignTarget(origAssign.position)
target.add(tempvar)
assign.add(target)
assign.add(origAssign.target.children.single())
val augAssign = PtAugmentedAssign(origAssign.operator, origAssign.position)
augAssign.add(target)
augAssign.add(origAssign.value)
val assignBack = PtAssignment(origAssign.position)
assignBack.add(origAssign.target)
assignBack.add(tempvar)
return translateRegularAssign(assign) + translate(augAssign) + translateRegularAssign(assignBack)
} else {
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
val left: PtExpression = origAssign.target.children.single() as PtExpression
value.add(left)
value.add(origAssign.value)
}
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
val left: PtExpression = origAssign.target.children.single() as PtExpression
value.add(left)
value.add(origAssign.value)
}
val normalAssign = PtAssignment(origAssign.position)
normalAssign.add(origAssign.target)
@@ -185,15 +169,18 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} else false
if (assignment.value is PtMachineRegister) {
valueRegister = (assignment.value as PtMachineRegister).register
if(extendByteToWord)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister), null)
if(extendByteToWord) {
valueRegister = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister, reg2=(assignment.value as PtMachineRegister).register), null)
}
} else {
val tr = expressionEval.translateExpression(assignment.value)
valueRegister = tr.resultReg
addToResult(result, tr, valueRegister, -1)
if(extendByteToWord) {
valueRegister = codeGen.registers.nextFree()
val opcode = if(assignment.value.type in SignedDatatypes) Opcode.EXTS else Opcode.EXT
addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1 = valueRegister), null)
addInstr(result, IRInstruction(opcode, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg), null)
}
}
}
@@ -215,8 +202,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val variable = targetArray.variable.name
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type)
if(targetArray.variable.type==DataType.UWORD) {
// indexing a pointer var instead of a real array or string
if(targetArray.usesPointerVariable) {
if(itemsize!=1)
throw AssemblyError("non-array var indexing requires bytes dt")
if(targetArray.index.type!=DataType.UBYTE)
@@ -236,8 +222,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
val fixedIndex = constIntValue(targetArray.index)
val iterable = codeGen.symbolTable.flat.getValue(targetArray.variable.name) as StStaticVariable
val arrayLength = iterable.length!!
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name)
if(zero) {
if(fixedIndex!=null) {
val chunk = IRCodeChunk(null, null).also {
@@ -347,19 +332,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return Pair(result, tr.resultReg)
}
if(codeGen.options.useNewExprCode) {
val tr = expressionEval.translateExpression(array.index)
result += tr.chunks
addInstr(result, IRInstruction(Opcode.MUL, tr.dt, reg1=tr.resultReg, immediate = itemsize), null)
return Pair(result, tr.resultReg)
} else {
val mult: PtExpression
mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
val tr = expressionEval.translateExpression(mult)
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)
}
val mult: PtExpression
mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
val tr = expressionEval.translateExpression(mult)
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)
}
}
@@ -1,6 +1,5 @@
package prog8.codegen.intermediate
import prog8.code.StStaticVariable
import prog8.code.ast.*
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
@@ -25,11 +24,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"popw" -> funcPopw(call)
"push" -> funcPush(call)
"pushw" -> funcPushw(call)
"rsave",
"rsavex",
"rrestore",
"rrestorex" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
"callfar" -> throw AssemblyError("callfar() is for cx16 target only")
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
"callfar" -> funcCallfar(call)
"msb" -> funcMsb(call)
"lsb" -> funcLsb(call)
"memory" -> funcMemory(call)
@@ -53,6 +49,18 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
}
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val bankTr = exprGen.translateExpression(call.args[0])
val addressTr = exprGen.translateExpression(call.args[1])
val argumentwordTr = exprGen.translateExpression(call.args[2])
addToResult(result, bankTr, bankTr.resultReg, -1)
addToResult(result, addressTr, addressTr.resultReg, -1)
addToResult(result, argumentwordTr, argumentwordTr.resultReg, -1)
result += codeGen.makeSyscall(IMSyscall.CALLFAR, listOf(IRDataType.BYTE to bankTr.resultReg, IRDataType.WORD to addressTr.resultReg, IRDataType.WORD to argumentwordTr.resultReg), IRDataType.WORD to argumentwordTr.resultReg)
return ExpressionCodeResult(result, IRDataType.WORD, argumentwordTr.resultReg, -1)
}
private fun funcDivmod(call: PtBuiltinFunctionCall, type: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val number = call.args[0]
@@ -107,9 +115,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcAny(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
val syscall =
when (array.dt) {
when (arrayName.type) {
DataType.ARRAY_UB,
DataType.ARRAY_B -> IMSyscall.ANY_BYTE
DataType.ARRAY_UW,
@@ -121,16 +129,16 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
private fun funcAll(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
val syscall =
when(array.dt) {
when(arrayName.type) {
DataType.ARRAY_UB,
DataType.ARRAY_B -> IMSyscall.ALL_BYTE
DataType.ARRAY_UW,
@@ -142,7 +150,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = array.length), null)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
@@ -161,10 +169,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg)
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, immediate = 0x80)
it += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=compareReg, immediate = 0, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=tr.resultReg)
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=tr.resultReg)
}
result += IRCodeChunk(notNegativeLabel, null)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
@@ -174,8 +180,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg)
it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, immediate = 0x8000)
it += IRInstruction(Opcode.BEQ, IRDataType.WORD, reg1=compareReg, immediate = 0, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=tr.resultReg)
}
result += IRCodeChunk(notNegativeLabel, null)
@@ -275,9 +280,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcReverse(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
val syscall =
when(array.dt) {
when(arrayName.type) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
@@ -288,16 +293,16 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
private fun funcSort(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable
val arrayLength = codeGen.symbolTable.getLength(arrayName.name)
val syscall =
when(array.dt) {
when(arrayName.type) {
DataType.ARRAY_UB -> IMSyscall.SORT_UBYTE
DataType.ARRAY_B -> IMSyscall.SORT_BYTE
DataType.ARRAY_UW -> IMSyscall.SORT_UWORD
@@ -311,7 +316,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(array.dt==DataType.STR) array.length!!-1 else array.length), null)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null)
result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
@@ -322,10 +327,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addToResult(result, msbTr, msbTr.resultReg, -1)
val lsbTr = exprGen.translateExpression(call.args[1])
addToResult(result, lsbTr, lsbTr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1 = lsbTr.resultReg, reg2 = msbTr.resultReg)
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultReg, reg2 = lsbTr.resultReg, reg3 = msbTr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.WORD, lsbTr.resultReg, -1)
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
}
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@@ -1,7 +1,6 @@
package prog8.codegen.intermediate
import prog8.code.StRomSub
import prog8.code.StStaticVariable
import prog8.code.StSub
import prog8.code.ast.*
import prog8.code.core.AssemblyError
@@ -45,9 +44,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
}
is PtIdentifier -> {
val vmDt = irType(expr.type)
val code = IRCodeChunk(null, null)
if (expr.type in PassByValueDatatypes) {
val vmDt = irType(expr.type)
if(vmDt==IRDataType.FLOAT) {
val resultFpRegister = codeGen.registers.nextFreeFloat()
code += IRInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, labelSymbol = expr.name)
@@ -60,6 +59,7 @@ 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)
val resultRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = expr.name)
ExpressionCodeResult(code, vmDt, resultRegister, -1)
@@ -105,8 +105,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translate(check: PtContainmentCheck): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val iterable = codeGen.symbolTable.flat.getValue(check.iterable.name) as StStaticVariable
when(iterable.dt) {
when(check.iterable.type) {
DataType.STR -> {
val elementTr = translateExpression(check.element)
addToResult(result, elementTr, elementTr.resultReg, -1)
@@ -121,7 +120,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val iterableTr = translateExpression(check.iterable)
addToResult(result, iterableTr, iterableTr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
val iterableLength = codeGen.symbolTable.getLength(check.iterable.name)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
result += codeGen.makeSyscall(IMSyscall.BYTEARRAY_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
@@ -131,12 +131,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val iterableTr = translateExpression(check.iterable)
addToResult(result, iterableTr, iterableTr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterable.length!!), null)
val iterableLength = codeGen.symbolTable.getLength(check.iterable.name)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
result += codeGen.makeSyscall(IMSyscall.WORDARRAY_CONTAINS, listOf(IRDataType.WORD to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
}
DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported")
else -> throw AssemblyError("weird iterable dt ${iterable.dt} for ${check.iterable.name}")
else -> throw AssemblyError("weird iterable dt ${check.iterable.type} for ${check.iterable.name}")
}
}
@@ -146,8 +147,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val result = mutableListOf<IRCodeChunkBase>()
val arrayVarSymbol = arrayIx.variable.name
if(arrayIx.variable.type==DataType.UWORD) {
// indexing a pointer var instead of a real array or string
if(arrayIx.usesPointerVariable) {
if(eltSize!=1)
throw AssemblyError("non-array var indexing requires bytes dt")
if(arrayIx.index.type!=DataType.UBYTE)
@@ -163,16 +163,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(arrayIx.splitWords) {
require(vmDt==IRDataType.WORD)
val iterable = codeGen.symbolTable.flat.getValue(arrayIx.variable.name) as StStaticVariable
val arrayLength = iterable.length!!
val arrayLength = codeGen.symbolTable.getLength(arrayIx.variable.name)
resultRegister = codeGen.registers.nextFree()
val finalResultReg = codeGen.registers.nextFree()
if(arrayIx.index is PtNumber) {
val memOffset = (arrayIx.index as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also {
val tmpRegMsb = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb+$memOffset")
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=tmpRegMsb, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb+$memOffset")
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultRegister, reg2=tmpRegMsb)
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=resultRegister, reg3=tmpRegMsb)
}
} else {
val tr = translateExpression(arrayIx.index)
@@ -181,10 +181,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tmpRegMsb = codeGen.registers.nextFree()
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=resultRegister, reg2 = tr.resultReg, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2 = tr.resultReg, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb")
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultRegister, reg2=tmpRegMsb)
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=resultRegister, reg3=tmpRegMsb)
}
}
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
return ExpressionCodeResult(result, vmDt, finalResultReg, -1)
}
var resultFpRegister = -1
@@ -274,13 +274,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when(cast.value.type) {
DataType.BYTE -> {
// byte -> uword: sign extend
actualResultReg2 = tr.resultReg
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2), null)
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null)
}
DataType.UBYTE -> {
// ubyte -> uword: sign extend
actualResultReg2 = tr.resultReg
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2), null)
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null)
}
DataType.WORD -> {
actualResultReg2 = tr.resultReg
@@ -296,13 +296,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when(cast.value.type) {
DataType.BYTE -> {
// byte -> word: sign extend
actualResultReg2 = tr.resultReg
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2), null)
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null)
}
DataType.UBYTE -> {
// byte -> word: sign extend
actualResultReg2 = tr.resultReg
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2), null)
actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null)
}
DataType.UWORD -> {
actualResultReg2 = tr.resultReg
@@ -339,7 +339,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
require(!codeGen.options.useNewExprCode)
val vmDt = irType(binExpr.left.type)
val signed = binExpr.left.type in SignedDatatypes
return when(binExpr.operator) {
@@ -455,6 +454,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
greaterEquals: Boolean
): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val cmpResultReg = codeGen.registers.nextFree()
if(vmDt==IRDataType.FLOAT) {
val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, -1, leftTr.resultFpReg)
@@ -469,10 +469,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else {
if (greaterEquals) Opcode.SGE else Opcode.SGT
}
addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
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==DataType.STR || binExpr.right.type==DataType.STR) {
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else {
val leftTr = translateExpression(binExpr.left)
@@ -484,8 +484,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else {
if (greaterEquals) Opcode.SGE else Opcode.SGT
}
addInstr(result, IRInstruction(ins, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1)
addInstr(result, IRInstruction(ins, vmDt, reg1=cmpResultReg, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultReg, -1)
}
}
}
@@ -497,6 +497,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
lessEquals: Boolean
): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val cmpResultRegister = codeGen.registers.nextFree()
if(vmDt==IRDataType.FLOAT) {
val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, -1, leftTr.resultFpReg)
@@ -511,10 +512,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else {
if (lessEquals) Opcode.SLE else Opcode.SLT
}
addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
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==DataType.STR || binExpr.right.type==DataType.STR) {
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else {
val leftTr = translateExpression(binExpr.left)
@@ -526,8 +527,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else {
if (lessEquals) Opcode.SLE else Opcode.SLT
}
addInstr(result, IRInstruction(ins, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1)
addInstr(result, IRInstruction(ins, vmDt, reg1=cmpResultRegister, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultRegister, -1)
}
}
}
@@ -553,7 +554,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==DataType.STR || binExpr.right.type==DataType.STR) {
throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else {
return if(constValue(binExpr.right)==0.0) {
@@ -569,8 +570,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val rightTr = translateExpression(binExpr.right)
addToResult(result, rightTr, rightTr.resultReg, -1)
val opcode = if (notEquals) Opcode.SNE else Opcode.SEQ
addInstr(result, IRInstruction(opcode, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(opcode, vmDt, reg1 = resultReg, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null)
ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
}
}
@@ -1240,6 +1242,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
): MutableList<IRCodeChunkBase> {
val result = mutableListOf<IRCodeChunkBase>()
val valueReg = codeGen.registers.nextFree()
val cmpResultReg = codeGen.registers.nextFree()
if(operand is PtNumber) {
val numberReg = codeGen.registers.nextFree()
if (knownAddress != null) {
@@ -1247,16 +1250,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress)
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt())
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = numberReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, address = knownAddress)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = cmpResultReg, reg2 = valueReg, reg3 = numberReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = knownAddress)
}
} else {
// in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt())
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = numberReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = numberReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
}
}
} else {
@@ -1266,15 +1269,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
// in-place modify a memory location
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = tr.resultReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, address = knownAddress)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = knownAddress)
}
} else {
// in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = tr.resultReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
}
}
}
@@ -1293,6 +1296,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val zeroReg = codeGen.registers.nextFree()
if(operand is PtNumber) {
val numberReg = codeGen.registers.nextFreeFloat()
val cmpResultReg = codeGen.registers.nextFree()
if (knownAddress != null) {
// in-place modify a memory location
result += IRCodeChunk(null, null).also {
@@ -1300,8 +1304,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat())
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
}
} else {
@@ -1311,13 +1315,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat())
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
}
}
} else {
val tr = translateExpression(operand)
val cmpResultReg = codeGen.registers.nextFree()
addToResult(result, tr, -1, tr.resultFpReg)
if (knownAddress != null) {
// in-place modify a memory location
@@ -1325,8 +1330,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
}
} else {
@@ -1335,8 +1340,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
}
}
@@ -1,9 +1,6 @@
package prog8.codegen.intermediate
import prog8.code.StMemVar
import prog8.code.StNode
import prog8.code.StStaticVariable
import prog8.code.SymbolTable
import prog8.code.*
import prog8.code.ast.*
import prog8.code.core.*
import prog8.intermediate.*
@@ -32,9 +29,6 @@ class IRCodeGen(
irSymbolTable = IRSymbolTable(symbolTable)
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
if(options.evalStackBaseAddress!=null)
throw AssemblyError("IR doesn't use eval-stack")
// collect global variables initializers
program.allBlocks().forEach {
val result = mutableListOf<IRCodeChunkBase>()
@@ -102,14 +96,16 @@ class IRCodeGen(
// make sure that first chunks in Blocks and Subroutines share the name of the block/sub as label.
irProg.blocks.forEach { block ->
block.children.firstOrNull { it is IRInlineAsmChunk }?.let { first->
first as IRInlineAsmChunk
if(first.label==null) {
val replacement = IRInlineAsmChunk(block.label, first.assembly, first.isIR, first.next)
block.children.removeAt(0)
block.children.add(0, replacement)
} else if(first.label != block.label) {
throw AssemblyError("first chunk in block has label that differs from block name")
if(block.isNotEmpty()) {
val firstAsm = block.children[0] as? IRInlineAsmChunk
if(firstAsm!=null) {
if(firstAsm.label==null) {
val replacement = IRInlineAsmChunk(block.label, firstAsm.assembly, firstAsm.isIR, firstAsm.next)
block.children.removeAt(0)
block.children.add(0, replacement)
} else if(firstAsm.label != block.label) {
throw AssemblyError("first chunk in block has label that differs from block name")
}
}
}
@@ -168,16 +164,22 @@ class IRCodeGen(
replacements.forEach {
val old = it.first.instructions[it.second]
val formats = instructionFormats.getValue(old.opcode)
val format = formats.getOrElse(old.type) { throw IllegalArgumentException("type ${old.type} invalid for ${old.opcode}") }
val immediateValue = if(format.immediate) it.third.toInt() else null
val addressValue = if(format.immediate) null else it.third.toInt()
it.first.instructions[it.second] = IRInstruction(
old.opcode,
old.type,
old.reg1,
old.reg2,
old.reg3,
old.fpReg1,
old.fpReg2,
immediate = immediateValue,
null,
null,
address = it.third.toInt(),
address = addressValue,
null,
null
)
@@ -287,6 +289,8 @@ class IRCodeGen(
}
}
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(node.position)
return chunks
}
@@ -395,42 +399,42 @@ class IRCodeGen(
}
private fun translate(whenStmt: PtWhen): IRCodeChunks {
if(whenStmt.choices.children.isEmpty())
return emptyList()
val result = mutableListOf<IRCodeChunkBase>()
val valueDt = irType(whenStmt.value.type)
val valueTr = expressionEval.translateExpression(whenStmt.value)
addToResult(result, valueTr, valueTr.resultReg, -1)
val choices = whenStmt.choices.children.map {it as PtWhenChoice }
val choices = mutableListOf<Pair<String, PtWhenChoice>>()
val endLabel = createLabelName()
for (choice in choices) {
whenStmt.choices.children.forEach {
val choice = it as PtWhenChoice
if(choice.isElse) {
result += translateNode(choice.statements)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
} else {
val skipLabel = createLabelName()
val values = choice.values.children.map {it as PtNumber}
if(values.size==1) {
val chunk = IRCodeChunk(null, null)
chunk += IRInstruction(Opcode.BNE, valueDt, reg1=valueTr.resultReg, immediate = values[0].number.toInt(), labelSymbol = skipLabel)
result += chunk
result += translateNode(choice.statements)
if(choice.statements.children.last() !is PtReturn)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
if(choice.statements.children.isEmpty()) {
// no statements for this choice value, jump to the end immediately
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
addInstr(result, IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = endLabel), null)
}
} else {
val matchLabel = createLabelName()
val chunk = IRCodeChunk(null, null)
for (value in values) {
chunk += IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = matchLabel)
val choiceLabel = createLabelName()
choices.add(choiceLabel to choice)
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
addInstr(result, IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = choiceLabel), null)
}
chunk += IRInstruction(Opcode.JUMP, labelSymbol = skipLabel)
result += chunk
result += labelFirstChunk(translateNode(choice.statements), matchLabel)
if(choice.statements.children.last() !is PtReturn)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
}
result += IRCodeChunk(skipLabel, null)
}
}
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
choices.forEach { (label, choice) ->
result += labelFirstChunk(translateNode(choice.statements), label)
val lastStatement = choice.statements.children.last()
if(lastStatement !is PtReturn && lastStatement !is PtJump)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
}
result += IRCodeChunk(endLabel, null)
return result
}
@@ -447,60 +451,64 @@ class IRCodeGen(
result += translateForInNonConstantRange(forLoop, loopvar)
}
is PtIdentifier -> {
val iterableVar = symbolTable.lookup(iterable.name) as StStaticVariable
require(forLoop.variable.name == loopvar.scopedName)
val iterableLength = symbolTable.getLength(iterable.name)
val loopvarSymbol = forLoop.variable.name
val indexReg = registers.nextFree()
val tmpReg = registers.nextFree()
val loopLabel = createLabelName()
val endLabel = createLabelName()
if(iterableVar.dt==DataType.STR) {
// iterate over a zero-terminated string
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
it += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1 = tmpReg, immediate = 0, labelSymbol = endLabel)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tmpReg, labelSymbol = loopvarSymbol)
when (iterable.type) {
DataType.STR -> {
// iterate over a zero-terminated string
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
it += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1 = tmpReg, immediate = 0, labelSymbol = endLabel)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tmpReg, labelSymbol = loopvarSymbol)
}
result += translateNode(forLoop.statements)
val jumpChunk = IRCodeChunk(null, null)
jumpChunk += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1 = indexReg)
jumpChunk += IRInstruction(Opcode.JUMP, labelSymbol = loopLabel)
result += jumpChunk
result += IRCodeChunk(endLabel, null)
}
result += translateNode(forLoop.statements)
val jumpChunk = IRCodeChunk(null, null)
jumpChunk += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1 = indexReg)
jumpChunk += IRInstruction(Opcode.JUMP, labelSymbol = loopLabel)
result += jumpChunk
result += IRCodeChunk(endLabel, null)
} else if(iterable.type in SplitWordArrayTypes) {
// iterate over lsb/msb split word array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
if(elementDt !in WordDatatypes)
throw AssemblyError("weird dt")
val length = iterableVar.length!!
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also {
val tmpRegLsb = registers.nextFree()
val tmpRegMsb = registers.nextFree()
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, immediate = length, labelSymbol=iterable.name+"_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, immediate = length, labelSymbol=iterable.name+"_msb")
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=tmpRegLsb, reg2=tmpRegMsb)
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpRegLsb, labelSymbol = loopvarSymbol)
in SplitWordArrayTypes -> {
// iterate over lsb/msb split word array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
if(elementDt !in WordDatatypes)
throw AssemblyError("weird dt")
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also {
val tmpRegLsb = registers.nextFree()
val tmpRegMsb = registers.nextFree()
val concatReg = registers.nextFree()
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_msb")
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=concatReg, reg2=tmpRegLsb, reg3=tmpRegMsb)
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=concatReg, labelSymbol = loopvarSymbol)
}
result += translateNode(forLoop.statements)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexReg)
it += IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = if(iterableLength==256) 0 else iterableLength, labelSymbol = loopLabel)
}
}
result += translateNode(forLoop.statements)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexReg)
it += IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = if(length==256) 0 else length, labelSymbol = loopLabel)
else -> {
// iterate over regular array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
val elementSize = program.memsizer.memorySize(elementDt)
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)
}
result += translateNode(forLoop.statements)
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = if(lengthBytes==256) 0 else lengthBytes, labelSymbol = loopLabel), null)
}
} else {
// iterate over regular array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
val elementSize = program.memsizer.memorySize(elementDt)
val lengthBytes = iterableVar.length!! * 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)
}
result += translateNode(forLoop.statements)
result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = if(lengthBytes==256) 0 else lengthBytes, labelSymbol = loopLabel), null)
}
}
else -> throw AssemblyError("weird for iterable")
@@ -545,22 +553,24 @@ class IRCodeGen(
addInstr(result, IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol=loopvarSymbol), null)
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol), null)
// if endvalue >= index, iterate loop
val branchInstr = if(loopvarDt in SignedDatatypes) {
if(step>0)
IRInstruction(Opcode.BGESR, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol=loopLabel)
else
IRInstruction(Opcode.BGESR, loopvarDtIr, reg1=fromTr.resultReg, reg2=toTr.resultReg, labelSymbol=loopLabel)
if(step==1 || step==-1) {
// if endvalue == loopvar, stop loop, else iterate
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol), null)
addInstr(result, IRInstruction(Opcode.BEQR, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol = labelAfterFor), null)
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = loopLabel), null)
} else {
if(step>0)
IRInstruction(Opcode.BGER, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol=loopLabel)
// ind/dec index, then:
// ascending: if endvalue >= loopvar, iterate
// descending: if loopvar >= endvalue, iterate
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol = loopvarSymbol), null)
if(step > 0)
addInstr(result, IRInstruction(Opcode.CMP, loopvarDtIr, reg1 = toTr.resultReg, fromTr.resultReg), null)
else
IRInstruction(Opcode.BGER, loopvarDtIr, reg1=fromTr.resultReg, reg2=toTr.resultReg, labelSymbol=loopLabel)
addInstr(result, IRInstruction(Opcode.CMP, loopvarDtIr, reg1 = fromTr.resultReg, toTr.resultReg), null)
addInstr(result, IRInstruction(Opcode.BSTPOS, labelSymbol = loopLabel), null)
}
addInstr(result, branchInstr, null)
result += IRCodeChunk(labelAfterFor, null)
return result
}
@@ -576,25 +586,23 @@ class IRCodeGen(
else -> throw AssemblyError("invalid loopvar node type")
}
val loopvarDtIr = irType(loopvarDt)
val iterable = forLoop.iterable as PtRange
val step = iterable.step.number.toInt()
val rangeStart = (iterable.from as PtNumber).number.toInt()
val rangeEndUntyped = (iterable.to as PtNumber).number.toInt() + step
if(step==0)
throw AssemblyError("step 0")
if(step>0 && rangeEndUntyped<rangeStart || step<0 && rangeEndUntyped>rangeStart)
val iterable = (forLoop.iterable as PtRange).toConstantIntegerRange()!!
if(iterable.isEmpty())
throw AssemblyError("empty range")
val rangeEndWrapped = if(loopvarDtIr==IRDataType.BYTE) rangeEndUntyped and 255 else rangeEndUntyped and 65535
if(iterable.step==0)
throw AssemblyError("step 0")
val rangeEndExclusiveUntyped = iterable.last + iterable.step
val rangeEndExclusiveWrapped = if(loopvarDtIr==IRDataType.BYTE) rangeEndExclusiveUntyped and 255 else rangeEndExclusiveUntyped and 65535
val result = mutableListOf<IRCodeChunkBase>()
val chunk = IRCodeChunk(null, null)
chunk += IRInstruction(Opcode.LOAD, loopvarDtIr, reg1=indexReg, immediate = rangeStart)
chunk += IRInstruction(Opcode.LOAD, loopvarDtIr, reg1=indexReg, immediate = iterable.first)
chunk += IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=indexReg, labelSymbol=loopvarSymbol)
result += chunk
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
val chunk2 = IRCodeChunk(null, null)
val chunk2 = addConstMem(loopvarDtIr, null, loopvarSymbol, iterable.step)
chunk2 += IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = indexReg, labelSymbol = loopvarSymbol)
chunk2 += IRInstruction(Opcode.BNE, loopvarDtIr, reg1 = indexReg, immediate = rangeEndWrapped, labelSymbol = loopLabel)
chunk2 += IRInstruction(Opcode.XOR, loopvarDtIr, reg1 = 999, immediate = 111)
chunk2 += IRInstruction(Opcode.BNE, loopvarDtIr, reg1 = indexReg, immediate = rangeEndExclusiveWrapped, labelSymbol = loopLabel)
result += chunk2
return result
}
@@ -905,7 +913,6 @@ class IRCodeGen(
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
when (condition) {
is PtBinaryExpression -> {
require(!options.useNewExprCode)
if(condition.operator !in ComparisonOperators)
throw AssemblyError("if condition should only be a binary comparison expression")
@@ -919,7 +926,6 @@ class IRCodeGen(
}
else -> {
// if X --> meaning: if X!=0
require(options.useNewExprCode)
val irDt = irType(condition.type)
val signed = condition.type in SignedDatatypes
return if(goto!=null && ifElse.elseScope.children.isEmpty()) {
@@ -997,10 +1003,10 @@ class IRCodeGen(
val opcode = when (condition.operator) {
"==" -> Opcode.BEQ
"!=" -> Opcode.BNE
"<" -> if (signed) Opcode.BLTS else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
">" -> if (signed) Opcode.BGTS else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
"<=" -> if (signed) Opcode.BLES else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
">=" -> if (signed) Opcode.BGES else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
"<" -> if (signed) Opcode.BLTS else Opcode.BLT
">" -> if (signed) Opcode.BGTS else Opcode.BGT
"<=" -> if (signed) Opcode.BLES else Opcode.BLE
">=" -> if (signed) Opcode.BGES else Opcode.BGE
else -> throw AssemblyError("invalid comparison operator")
}
if (goto.address != null)
@@ -1134,10 +1140,10 @@ class IRCodeGen(
elseBranch = when (condition.operator) {
"==" -> Opcode.BNE
"!=" -> Opcode.BEQ
"<" -> if (signed) Opcode.BGES else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
">" -> if (signed) Opcode.BLES else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
"<=" -> if (signed) Opcode.BGTS else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
">=" -> if (signed) Opcode.BLTS else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
"<" -> if (signed) Opcode.BGES else Opcode.BGE
">" -> if (signed) Opcode.BLES else Opcode.BLE
"<=" -> if (signed) Opcode.BGTS else Opcode.BGT
">=" -> if (signed) Opcode.BLTS else Opcode.BLT
else -> throw AssemblyError("weird operator")
}
}
@@ -1364,7 +1370,6 @@ class IRCodeGen(
if(array?.splitWords==true) {
val variable = array.variable.name
val fixedIndex = constIntValue(array.index)
val iterable = symbolTable.flat.getValue(array.variable.name) as StStaticVariable
if(fixedIndex!=null) {
val skipLabel = createLabelName()
when(postIncrDecr.operator) {
@@ -1390,7 +1395,7 @@ class IRCodeGen(
else -> throw AssemblyError("weird operator")
}
} else {
val arrayLength = iterable.length!!
val arrayLength = symbolTable.getLength(array.variable.name)
val indexTr = expressionEval.translateExpression(array.index)
addToResult(result, indexTr, indexTr.resultReg, -1)
val incReg = registers.nextFree()
@@ -1464,7 +1469,19 @@ class IRCodeGen(
val fixedIndex = constIntValue(array.index)
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol="$variable+$offset"), null)
val indexReg = registers.nextFree()
val dataReg = registers.nextFree()
if(array.usesPointerVariable) {
// we don't have an indirect dec/inc so do it via an intermediate register
result += IRCodeChunk(null,null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = offset)
it += IRInstruction(Opcode.LOADIX, irDt, reg1 = dataReg, reg2 = indexReg, labelSymbol = variable)
it += IRInstruction(operationRegister, irDt, reg1=dataReg)
it += IRInstruction(Opcode.STOREIX, irDt, reg1 = dataReg, reg2 = indexReg, labelSymbol = variable)
}
} else {
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = "$variable+$offset"), null)
}
} else {
val indexTr = expressionEval.translateExpression(array.index)
addToResult(result, indexTr, indexTr.resultReg, -1)
@@ -1472,9 +1489,15 @@ class IRCodeGen(
result += multiplyByConst(IRDataType.BYTE, indexTr.resultReg, itemsize)
result += IRCodeChunk(null, null).also {
val incReg = registers.nextFree()
it += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
it += IRInstruction(operationRegister, irDt, reg1=incReg)
it += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
if(array.usesPointerVariable) {
it += IRInstruction(Opcode.LOADIX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
it += IRInstruction(operationRegister, irDt, reg1=incReg)
it += IRInstruction(Opcode.STOREIX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
} else {
it += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
it += IRInstruction(operationRegister, irDt, reg1=incReg)
it += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
}
}
}
} else
@@ -1500,7 +1523,10 @@ class IRCodeGen(
val result = mutableListOf<IRCodeChunkBase>()
val countTr = expressionEval.translateExpression(repeat.count)
addToResult(result, countTr, countTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.BEQ, irDt, reg1=countTr.resultReg, immediate = 0, labelSymbol = skipRepeatLabel), null)
if(constIntValue(repeat.count)==null) {
// check if the counter is already zero
addInstr(result, IRInstruction(Opcode.BEQ, irDt, reg1=countTr.resultReg, immediate = 0, labelSymbol = skipRepeatLabel), null)
}
result += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.DEC, irDt, reg1 = countTr.resultReg)
@@ -1512,17 +1538,26 @@ class IRCodeGen(
private fun translate(jump: PtJump): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val instr = if(jump.address!=null) {
IRInstruction(Opcode.JUMPA, address = jump.address!!.toInt())
val chunk = IRCodeChunk(null, null)
if(jump.address!=null) {
chunk += IRInstruction(Opcode.JUMP, address = jump.address!!.toInt())
} else {
if (jump.generatedLabel != null)
IRInstruction(Opcode.JUMP, labelSymbol = jump.generatedLabel!!)
else if (jump.identifier != null)
IRInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.name)
chunk += IRInstruction(Opcode.JUMP, labelSymbol = jump.generatedLabel!!)
else if (jump.identifier != null) {
val symbol = symbolTable.lookup(jump.identifier!!.name)
if(symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR) {
val jumpReg = registers.nextFree()
chunk += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = jumpReg, labelSymbol = jump.identifier!!.name)
chunk += IRInstruction(Opcode.JUMPI, reg1 = jumpReg)
} else {
chunk += IRInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.name)
}
}
else
throw AssemblyError("weird jump")
}
addInstr(result, instr, null)
result += chunk
return result
}
@@ -1610,7 +1645,7 @@ class IRCodeGen(
private fun translate(parameters: List<PtSubroutineParameter>) =
parameters.map {
val flattenedName = it.definingSub()!!.name + "." + it.name
val orig = symbolTable.flat.getValue(flattenedName) as StStaticVariable
val orig = symbolTable.lookup(flattenedName) as StStaticVariable
IRSubroutine.IRParam(flattenedName, orig.dt)
}
@@ -1635,24 +1670,6 @@ class IRCodeGen(
internal fun isOne(expression: PtExpression): Boolean = expression is PtNumber && expression.number==1.0
fun getReusableTempvar(scope: PtNamedNode, type: DataType): PtIdentifier {
val uniqueId = Pair(scope, type).hashCode().toUInt()
val tempvarname = "${scope.scopedName}.tempvar_${uniqueId}"
val tempvar = PtIdentifier(tempvarname, type, Position.DUMMY)
val staticVar = StStaticVariable(
tempvarname,
type,
null,
null,
null,
null,
ZeropageWish.DONTCARE,
tempvar
)
irSymbolTable.add(staticVar)
return tempvar
}
fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
return IRCodeChunk(label, null).also {
val args = params.map { (dt, reg)->
@@ -37,8 +37,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
removeEmptyChunks(sub)
joinChunks(sub)
// TODO also do register optimization step here?
sub.chunks.withIndex().forEach { (index, chunk1) ->
// we don't optimize Inline Asm chunks here.
val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null
@@ -49,17 +47,19 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
val changed = removeNops(chunk1, indexedInstructions)
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions) // TODO not yet implemented
|| removeUselessArithmetic(chunk1, indexedInstructions)
|| removeNeedlessCompares(chunk1, indexedInstructions)
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|| removeDoubleSecClc(chunk1, indexedInstructions)
|| cleanupPushPop(chunk1, indexedInstructions)
// TODO other optimizations:
// more complex optimizations such as unused registers
// TODO other optimizations
} while (changed)
}
}
removeEmptyChunks(sub)
}
// TODO also do register optimization step here at the end?
irprog.linkChunks() // re-link
}
@@ -105,7 +105,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
relabelChunks.forEach { (index, label) ->
val chunk = IRCodeChunk(label, null)
chunk.instructions += sub.chunks[index].instructions
val subChunk = sub.chunks[index]
chunk.instructions += subChunk.instructions
if(subChunk is IRCodeChunk)
chunk.appendSrcPositions(subChunk.sourceLinesPositions)
sub.chunks[index] = chunk
}
removeChunks.reversed().forEach { index -> sub.chunks.removeAt(index) }
@@ -140,6 +143,8 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
if(mayJoinCodeChunks(lastChunk, candidate)) {
lastChunk.instructions += candidate.instructions
lastChunk.next = candidate.next
if(lastChunk is IRCodeChunk)
lastChunk.appendSrcPositions(candidate.sourceLinesPositions)
}
else
chunks += candidate
@@ -267,6 +272,28 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed
}
private fun removeNeedlessCompares(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// a CMPI with 0, after an instruction like LOAD that already sets the status bits, can be removed.
// but only if the instruction after it is not using the Carry bit because that won't be set by a LOAD instruction etc.
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(idx>0 && idx<(indexedInstructions.size-1) && ins.opcode==Opcode.CMPI && ins.immediate==0) {
val previous = indexedInstructions[idx-1].value
if(previous.opcode in OpcodesThatSetStatusbitsIncludingCarry) {
chunk.instructions.removeAt(idx)
changed = true
} else if(previous.opcode in OpcodesThatSetStatusbitsButNotCarry) {
val next = indexedInstructions[idx+1].value
if(next.opcode !in arrayOf(Opcode.BSTCC, Opcode.BSTCS, Opcode.BSTPOS, Opcode.BSTNEG)) {
chunk.instructions.removeAt(idx)
changed = true
}
}
}
}
return changed
}
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
var changed = false
@@ -1,7 +1,7 @@
package prog8.codegen.intermediate
import prog8.code.core.IErrorReporter
import prog8.code.core.SourceCode.Companion.libraryFilePrefix
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
import prog8.intermediate.*
@@ -16,7 +16,7 @@ class IRUnusedCodeRemover(
irprog.blocks.reversed().forEach { block ->
if(block.isEmpty()) {
irprog.blocks.remove(block)
irprog.st.removeTree(block.label)
pruneSymboltable(block.label)
numRemoved++
}
}
@@ -24,6 +24,22 @@ class IRUnusedCodeRemover(
return numRemoved
}
private fun pruneSymboltable(blockLabel: String) {
// we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction
val prefix = "$blockLabel."
val blockVars = irprog.st.allVariables().filter { it.name.startsWith(prefix) }
blockVars.forEach { stVar ->
irprog. allSubs().flatMap { it.chunks }.forEach { chunk ->
chunk.instructions.forEach { ins ->
if(ins.labelSymbol == stVar.name) {
return
}
}
}
}
irprog.st.removeTree(blockLabel)
}
private fun removeUnusedSubroutines(): Int {
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
irprog.foreachCodeChunk { chunk ->
@@ -34,7 +50,7 @@ class IRUnusedCodeRemover(
irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) {
if(!sub.position.file.startsWith(libraryFilePrefix)) {
if(!sub.position.file.startsWith(LIBRARYFILEPREFIX)) {
errors.warn("unused subroutine ${sub.label}", sub.position)
}
block.children.remove(sub)
@@ -55,7 +71,7 @@ class IRUnusedCodeRemover(
irprog.blocks.forEach { block ->
block.children.filterIsInstance<IRAsmSubroutine>().reversed().forEach { sub ->
if(sub.isEmpty()) {
if(!sub.position.file.startsWith(libraryFilePrefix)) {
if(!sub.position.file.startsWith(LIBRARYFILEPREFIX)) {
errors.warn("unused asmsubroutine ${sub.label}", sub.position)
}
block.children.remove(sub)
@@ -119,9 +135,22 @@ class IRUnusedCodeRemover(
}
private fun removeUnreachable(allLabeledChunks: MutableMap<String, IRCodeChunkBase>): Int {
val entrypointSub = irprog.blocks.single { it.label=="main" }.children.single { it is IRSubroutine && it.label=="main.start" }
val entrypointSub = irprog.blocks.single { it.label=="main" }
.children.single { it is IRSubroutine && it.label=="main.start" }
val reachable = mutableSetOf((entrypointSub as IRSubroutine).chunks.first())
// all chunks referenced in array initializer values are also 'reachable':
irprog.st.allVariables()
.filter { !it.uninitialized }
.forEach {
it.onetimeInitializationArrayValue?.let { array ->
array.forEach {elt ->
if(elt.addressOfSymbol!=null && irprog.st.lookup(elt.addressOfSymbol!!)==null)
reachable.add(irprog.getChunkWithLabel(elt.addressOfSymbol!!))
}
}
}
fun grow() {
val new = mutableSetOf<IRCodeChunkBase>()
reachable.forEach {
@@ -150,6 +179,18 @@ class IRUnusedCodeRemover(
private fun removeSimpleUnlinked(allLabeledChunks: Map<String, IRCodeChunkBase>): Int {
val linkedChunks = mutableSetOf<IRCodeChunkBase>()
// all chunks referenced in array initializer values are linked as well!:
irprog.st.allVariables()
.filter { !it.uninitialized }
.forEach {
it.onetimeInitializationArrayValue?.let { array ->
array.forEach {elt ->
if(elt.addressOfSymbol!=null && irprog.st.lookup(elt.addressOfSymbol!!)==null)
linkedChunks += irprog.getChunkWithLabel(elt.addressOfSymbol!!)
}
}
}
irprog.foreachCodeChunk { chunk ->
chunk.next?.let { next -> linkedChunks += next }
chunk.instructions.forEach {
@@ -16,7 +16,7 @@ class VmCodeGen: ICodeGeneratorBackend {
symbolTable: SymbolTable,
options: CompilationOptions,
errors: IErrorReporter
): IAssemblyProgram? {
): IAssemblyProgram {
val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
val irProgram = irCodeGen.generate()
return VmAssemblyProgram(irProgram.name, irProgram)
+4
View File
@@ -43,6 +43,10 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
warnings.add(text)
}
override fun undefined(symbol: List<String>, position: Position) {
err("undefined symbol: ${symbol.joinToString(".")}", position)
}
override fun noErrors(): Boolean = errors.isEmpty()
override fun report() {
+9 -9
View File
@@ -41,7 +41,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("pi", DataType.UBYTE, ZeropageWish.DONTCARE, PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), null, Position.DUMMY))
sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
@@ -91,7 +91,7 @@ class TestVmCodeGen: FunSpec({
program.add(block)
// define the "cx16.r0" virtual register
val cx16block = PtBlock("cx16", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val cx16block = PtBlock("cx16", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
program.add(cx16block)
@@ -120,7 +120,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
@@ -183,7 +183,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
@@ -242,7 +242,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
@@ -289,7 +289,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
@@ -352,7 +352,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
@@ -411,7 +411,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
@@ -451,7 +451,7 @@ class TestVmCodeGen: FunSpec({
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val block = PtBlock("main", null, false, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val romsub = PtAsmSub("routine", 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
block.add(romsub)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
@@ -1,71 +0,0 @@
package prog8.optimizer
import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.BinaryExpression
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.AssignmentOrigin
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.AugmentAssignmentOperators
import prog8.code.core.CompilationOptions
import prog8.code.core.DataType
import prog8.code.target.VMTarget
class BinExprSplitter(private val program: Program, private val options: CompilationOptions) : AstWalker() {
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
if(options.compTarget.name == VMTarget.NAME)
return noModifications // don't split expressions when targeting the vm codegen, it handles nested expressions well
if(assignment.value.inferType(program) istype DataType.FLOAT && !options.optimizeFloatExpressions)
return noModifications
val binExpr = assignment.value as? BinaryExpression
if (binExpr != null) {
if(binExpr.operator in AugmentAssignmentOperators && isSimpleTarget(assignment.target)) {
if(assignment.target isSameAs binExpr.right)
return noModifications
if(assignment.target isSameAs binExpr.left) {
if(binExpr.right.isSimple)
return noModifications
val leftBx = binExpr.left as? BinaryExpression
if(leftBx!=null && (!leftBx.left.isSimple || !leftBx.right.isSimple))
return noModifications
val rightBx = binExpr.right as? BinaryExpression
if(rightBx!=null && (!rightBx.left.isSimple || !rightBx.right.isSimple))
return noModifications
}
if(binExpr.right.isSimple) {
val firstAssign = Assignment(assignment.target.copy(), binExpr.left, AssignmentOrigin.OPTIMIZER, binExpr.left.position)
val targetExpr = assignment.target.toExpression()
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
return listOf(
IAstModification.ReplaceNode(binExpr, augExpr, assignment),
IAstModification.InsertBefore(assignment, firstAssign, assignment.parent as IStatementContainer)
)
}
}
// Further unraveling of binary expressions is really complicated here and
// often results in much bigger code, thereby defeating the purpose a bit.
// All in all this should probably be fixed in a better code generation backend
// that doesn't require this at all.
}
return noModifications
}
private fun isSimpleTarget(target: AssignTarget) =
if (target.identifier!=null || target.memoryAddress!=null)
!target.isIOAddress(options.compTarget.machine)
else
false
}
@@ -3,16 +3,17 @@ package prog8.optimizer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.base.UndefinedSymbolError
import prog8.ast.expressions.*
import prog8.ast.maySwapOperandOrder
import prog8.ast.statements.ForLoop
import prog8.ast.statements.RepeatLoop
import prog8.ast.statements.VarDecl
import prog8.ast.statements.VarDeclType
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.AssociativeOperators
import prog8.code.core.DataType
import kotlin.math.floor
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
@@ -234,6 +235,47 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
}
}
if(rightconst!=null && (expr.operator=="<<" || expr.operator==">>")) {
val dt = expr.left.inferType(program)
if(dt.isBytes && rightconst.number>=8) {
if(dt.istype(DataType.UBYTE)) {
val zeroUB = NumericLiteral(DataType.UBYTE, 0.0, expr.position)
modifications.add(IAstModification.ReplaceNode(expr, zeroUB, parent))
} else {
if(leftconst!=null) {
val zeroB = NumericLiteral(DataType.BYTE, 0.0, expr.position)
val minusoneB = NumericLiteral(DataType.BYTE, -1.0, expr.position)
if(leftconst.number<0.0) {
if(expr.operator=="<<")
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
else
modifications.add(IAstModification.ReplaceNode(expr, minusoneB, parent))
} else {
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
}
}
}
}
else if(dt.isWords && rightconst.number>=16) {
if(dt.istype(DataType.UWORD)) {
val zeroUW = NumericLiteral(DataType.UWORD, 0.0, expr.position)
modifications.add(IAstModification.ReplaceNode(expr, zeroUW, parent))
} else {
if(leftconst!=null) {
val zeroW = NumericLiteral(DataType.WORD, 0.0, expr.position)
val minusoneW = NumericLiteral(DataType.WORD, -1.0, expr.position)
if(leftconst.number<0.0) {
if(expr.operator=="<<")
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
else
modifications.add(IAstModification.ReplaceNode(expr, minusoneW, parent))
} else {
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
}
}
}
}
}
return modifications
}
@@ -307,7 +349,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
val rangeTo = iterableRange.to as? NumericLiteral
if(rangeFrom==null || rangeTo==null) return noModifications
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: throw UndefinedSymbolError(forLoop.loopVar)
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: return noModifications
val stepLiteral = iterableRange.step as? NumericLiteral
when(loopvar.datatype) {
@@ -364,6 +406,16 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
return noModifications
}
override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
val count = (repeatLoop.iterations as? NumericLiteral)?.number
if(count!=null && floor(count)!=count) {
val integer = NumericLiteral.optimalInteger(count.toInt(), repeatLoop.position)
repeatLoop.iterations = integer
integer.linkParents(repeatLoop)
}
return noModifications
}
private class ShuffleOperands(val expr: BinaryExpression,
val exprOperator: String?,
val subExpr: BinaryExpression,
@@ -53,7 +53,8 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
}
if(to==null) {
if(!range.to.inferType(program).isInteger)
val toType = range.to.inferType(program)
if(toType.isKnown && !range.to.inferType(program).isInteger)
errors.err("range expression to value must be integer", range.to.position)
} else if(to-to.toInt()>0) {
errors.err("range expression to value must be integer", range.to.position)
@@ -279,10 +280,16 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
val rangeExpr = decl.value as? RangeExpression
if(rangeExpr!=null) {
// convert the initializer range expression to an actual array
val constRange = rangeExpr.toConstantIntegerRange()
if(constRange?.isEmpty()==true) {
if(constRange.first>constRange.last && constRange.step>=0)
errors.err("descending range with positive step", decl.value?.position!!)
else if(constRange.first<constRange.last && constRange.step<=0)
errors.err("ascending range with negative step", decl.value?.position!!)
}
val declArraySize = decl.arraysize?.constIndex()
if(declArraySize!=null && declArraySize!=rangeExpr.size())
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
val constRange = rangeExpr.toConstantIntegerRange()
if(constRange!=null) {
val eltType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
val newValue = if(eltType in ByteDatatypes) {
@@ -18,7 +18,7 @@ import kotlin.math.abs
import kotlin.math.log2
import kotlin.math.pow
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has?
class ExpressionSimplifier(private val program: Program,
private val errors: IErrorReporter,
@@ -43,9 +43,9 @@ fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget)
fun Program.optimizeStatements(errors: IErrorReporter,
functions: IBuiltinFunctions,
compTarget: ICompilationTarget
options: CompilationOptions
): Int {
val optimizer = StatementOptimizer(this, errors, functions, compTarget)
val optimizer = StatementOptimizer(this, errors, functions, options)
optimizer.visit(this)
val optimizationCount = optimizer.applyModifications()
@@ -54,8 +54,8 @@ fun Program.optimizeStatements(errors: IErrorReporter,
return optimizationCount
}
fun Program.inlineSubroutines(): Int {
val inliner = Inliner(this)
fun Program.inlineSubroutines(options: CompilationOptions): Int {
val inliner = Inliner(this, options)
inliner.visit(this)
return inliner.applyModifications()
}
@@ -65,9 +65,3 @@ fun Program.simplifyExpressions(errors: IErrorReporter, target: ICompilationTarg
opti.visit(this)
return opti.applyModifications()
}
fun Program.splitBinaryExpressions(options: CompilationOptions) : Int {
val opti = BinExprSplitter(this, options)
opti.visit(this)
return opti.applyModifications()
}
+17 -3
View File
@@ -8,7 +8,9 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.ast.walk.IAstVisitor
import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException
import prog8.code.target.VMTarget
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null
@@ -16,7 +18,7 @@ private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.va
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
class Inliner(val program: Program): AstWalker() {
class Inliner(private val program: Program, private val options: CompilationOptions): AstWalker() {
class DetermineInlineSubs(val program: Program): IAstVisitor {
private val modifications = mutableListOf<IAstModification>()
@@ -211,7 +213,7 @@ class Inliner(val program: Program): AstWalker() {
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
return if(sub==null)
return if(sub==null || !canInline(sub, functionCallStatement))
noModifications
else
possibleInlineFcallStmt(sub, functionCallStatement, parent)
@@ -219,7 +221,7 @@ class Inliner(val program: Program): AstWalker() {
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
if(sub!=null && sub.inline && sub.parameters.isEmpty() && canInline(sub, functionCallExpr)) {
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
"invalid inline sub at ${sub.position}"
}
@@ -246,5 +248,17 @@ class Inliner(val program: Program): AstWalker() {
return noModifications
}
private fun canInline(sub: Subroutine, fcall: IFunctionCall): Boolean {
if(!sub.inline)
return false
if(options.compTarget.name!=VMTarget.NAME) {
val stmt = sub.statements.single()
if (stmt is IFunctionCall) {
val existing = (fcall as Node).definingScope.lookup(stmt.target.nameInSource.take(1))
return existing !is VarDecl
}
}
return true
}
}
@@ -13,7 +13,7 @@ import kotlin.math.floor
class StatementOptimizer(private val program: Program,
private val errors: IErrorReporter,
private val functions: IBuiltinFunctions,
private val compTarget: ICompilationTarget
private val options: CompilationOptions
) : AstWalker() {
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
@@ -39,7 +39,7 @@ class StatementOptimizer(private val program: Program,
if(string!=null) {
val pos = functionCallStatement.position
if (string.value.length == 1) {
val firstCharEncoded = compTarget.encodeString(string.value, string.encoding)[0]
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)),
@@ -51,7 +51,7 @@ class StatementOptimizer(private val program: Program,
IAstModification.Remove(stringDecl, stringDecl.parent as IStatementContainer)
)
} else if (string.value.length == 2) {
val firstTwoCharsEncoded = compTarget.encodeString(string.value.take(2), string.encoding)
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)),
@@ -108,6 +108,14 @@ class StatementOptimizer(private val program: Program,
}
}
// remove obvious dangling elses (else after a return)
if(ifElse.elsepart.isNotEmpty() && ifElse.truepart.statements.singleOrNull() is Return) {
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
return listOf(
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
)
}
return noModifications
}
@@ -141,7 +149,7 @@ class StatementOptimizer(private val program: Program,
val size = sv.value.length
if(size==1) {
// loop over string of length 1 -> just assign the single character
val character = compTarget.encodeString(sv.value, sv.encoding)[0]
val character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
val byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position)
val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
@@ -166,16 +174,6 @@ class StatementOptimizer(private val program: Program,
}
}
val iterationCount = forLoop.constIterationCount(program)
if(iterationCount!=null) {
val loopName = forLoop.loopVar.nameInSource
if(!forLoop.iterable.referencesIdentifier(loopName) && !forLoop.body.referencesIdentifier(loopName)) {
errors.warn("for loop can be replaced with repeat loop", forLoop.position)
val repeat = RepeatLoop(NumericLiteral.optimalNumeric(iterationCount, forLoop.position), forLoop.body, forLoop.position)
return listOf(IAstModification.ReplaceNode(forLoop, repeat, parent))
}
}
return noModifications
}
@@ -326,7 +324,7 @@ class StatementOptimizer(private val program: Program,
if (rightCv == 0.0) {
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && compTarget.name!=VMTarget.NAME) {
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && options.compTarget.name!=VMTarget.NAME) {
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
val incs = AnonymousScope(mutableListOf(), assignment.position)
repeat(rightCv.toInt()) {
@@ -340,7 +338,7 @@ class StatementOptimizer(private val program: Program,
if (rightCv == 0.0) {
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && compTarget.name!=VMTarget.NAME) {
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..3.0 && options.compTarget.name!=VMTarget.NAME) {
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
val decs = AnonymousScope(mutableListOf(), assignment.position)
repeat(rightCv.toInt()) {
@@ -391,4 +389,47 @@ class StatementOptimizer(private val program: Program,
else
noModifications
}
override fun after(whenStmt: When, parent: Node): Iterable<IAstModification> {
fun replaceWithIf(condition: Expression, trueBlock: AnonymousScope, elseBlock: AnonymousScope?): List<IAstModification> {
val ifStmt = IfElse(condition, trueBlock, elseBlock ?: AnonymousScope(mutableListOf(), whenStmt.position), whenStmt.position)
errors.warn("for boolean condition a normal if statement is preferred", whenStmt.position)
return listOf(IAstModification.ReplaceNode(whenStmt, ifStmt, parent))
}
if(whenStmt.condition.inferType(program).isBool) {
if(whenStmt.choices.all { it.values?.size==1 }) {
if (whenStmt.choices.all { it.values!!.single().constValue(program)!!.number in arrayOf(0.0, 1.0) }) {
// it's a when statement on booleans that can just be replaced by an if or if..else.
if (whenStmt.choices.size == 1) {
return if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) {
replaceWithIf(whenStmt.condition, whenStmt.choices[0].statements, null)
} else {
val notCondition = BinaryExpression(whenStmt.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, whenStmt.condition.position), whenStmt.condition.position)
replaceWithIf(notCondition, whenStmt.choices[0].statements, null)
}
} else if (whenStmt.choices.size == 2) {
var trueBlock: AnonymousScope? = null
var elseBlock: AnonymousScope? = null
if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) {
trueBlock = whenStmt.choices[0].statements
} else {
elseBlock = whenStmt.choices[0].statements
}
if(whenStmt.choices[1].values!![0].constValue(program)!!.number==1.0) {
trueBlock = whenStmt.choices[1].statements
} else {
elseBlock = whenStmt.choices[1].statements
}
if(trueBlock!=null && elseBlock!=null) {
return replaceWithIf(whenStmt.condition, trueBlock, elseBlock)
}
}
}
}
}
return noModifications
}
}
@@ -75,7 +75,9 @@ class UnusedCodeRemover(private val program: Program,
if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
errors.warn("removing unused block '${block.name}'", block.position)
}
program.removeInternedStringsFromRemovedBlock(block)
if(!block.statements.any { it is Subroutine && it.hasBeenInlined }) {
program.removeInternedStringsFromRemovedBlock(block)
}
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
}
}
+17 -6
View File
@@ -1,9 +1,10 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
id 'org.jetbrains.kotlin.jvm'
id 'com.github.johnrengelman.shadow' version '7.1.2'
id "io.kotest" version "0.3.9"
id 'io.kotest' version '0.3.9'
id 'com.peterabeles.gversion' version '1.10.2'
}
java {
@@ -24,8 +25,6 @@ compileTestKotlin {
}
}
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
dependencies {
implementation project(':codeCore')
implementation project(':codeOptimizers')
@@ -38,7 +37,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5'
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.17"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
testImplementation project(':intermediate')
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
@@ -82,7 +81,7 @@ application {
shadowJar {
archiveBaseName = 'prog8compiler'
archiveVersion = prog8version
archiveVersion = version
// minimize()
}
@@ -100,4 +99,16 @@ test {
}
}
gversion {
srcDir = "src/" // path is relative to the sub-project by default
classPackage = "prog8.buildversion"
className = "BuildVersion"
language = "kotlin"
}
build.finalizedBy installDist, installShadowDist
compileKotlin.dependsOn createVersionFile // , failDirtyNotSnapshot
compileJava.dependsOn createVersionFile
+31 -1
View File
@@ -2,6 +2,7 @@
; Including memory registers, I/O registers, Basic and Kernal subroutines.
atari {
%option no_symbol_prefixing
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
@@ -9,10 +10,11 @@ atari {
}
sys {
; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 8 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL
asmsub init_system() {
@@ -216,6 +218,7 @@ _longcopy
}
cx16 {
%option no_symbol_prefixing
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the Atari as well but their location in memory is different
@@ -321,4 +324,31 @@ cx16 {
&byte r13sH = $1b1b
&byte r14sH = $1b1d
&byte r15sH = $1b1f
asmsub save_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda cx16.r0,y
sta _cx16_vreg_storage,y
dey
bpl -
rts
_cx16_vreg_storage
.word 0,0,0,0,0,0,0,0
.word 0,0,0,0,0,0,0,0
}}
}
asmsub restore_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda save_virtual_registers._cx16_vreg_storage,y
sta cx16.r0,y
dey
bpl -
rts
}}
}
}
+17 -31
View File
@@ -6,6 +6,8 @@
txt {
%option no_symbol_prefixing
const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 24
@@ -151,10 +153,9 @@ asmsub print (str text @ AY) clobbers(A,Y) {
}}
}
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.ubyte2decimal
pha
tya
@@ -162,16 +163,13 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
pla
jsr chrout
txa
jsr chrout
ldx P8ZP_SCRATCH_REG
rts
jmp chrout
}}
}
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.ubyte2decimal
_print_byte_digits
pha
@@ -187,16 +185,13 @@ _print_byte_digits
beq _ones
jsr chrout
_ones txa
jsr chrout
ldx P8ZP_SCRATCH_REG
rts
jmp chrout
}}
}
asmsub print_b (byte value @ A) clobbers(A,Y) {
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
; ---- print the byte in A in decimal form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
pha
cmp #0
bpl +
@@ -208,10 +203,9 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
}}
}
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
stx P8ZP_SCRATCH_REG
bcc +
pha
lda #'$'
@@ -220,16 +214,13 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
+ jsr conv.ubyte2hex
jsr chrout
tya
jsr chrout
ldx P8ZP_SCRATCH_REG
rts
jmp chrout
}}
}
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
stx P8ZP_SCRATCH_REG
sta P8ZP_SCRATCH_B1
bcc +
lda #'%'
@@ -242,12 +233,11 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
+ jsr chrout
dey
bne -
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
pha
@@ -259,7 +249,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
}}
}
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
@@ -272,10 +262,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
}}
}
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.uword2decimal
ldy #0
- lda conv.uword2decimal.decTenThousands,y
@@ -283,17 +272,14 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
jsr chrout
iny
bne -
+ ldx P8ZP_SCRATCH_REG
rts
+ rts
}}
}
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.uword2decimal
ldx P8ZP_SCRATCH_REG
ldy #0
- lda conv.uword2decimal.decTenThousands,y
beq _allzero
@@ -314,7 +300,7 @@ _allzero
}}
}
asmsub print_w (word value @ AY) clobbers(A,Y) {
asmsub print_w (word value @ AY) clobbers(A,X,Y) {
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
%asm {{
cpy #0
@@ -386,7 +372,7 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
}}
}
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
asmsub plot (ubyte col @ Y, ubyte row @ A) {
; ---- set cursor at specific position
; TODO
%asm {{
+2
View File
@@ -6,6 +6,8 @@
; TODO c128 actually implement the graphics routines. Ideally a way to 'borrow' the code form the C64 version without just copy-pasting that here?
graphics {
%option no_symbol_prefixing
const uword WIDTH = 320
const ubyte HEIGHT = 200
+131 -115
View File
@@ -3,6 +3,7 @@
cbm {
; Commodore (CBM) common variables, vectors and kernal routines
%option no_symbol_prefixing
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
&ubyte TIME_MID = $a1 ; .. mid byte
@@ -103,34 +104,26 @@ romsub $FFF3 = IOBASE() -> uword @ XY ; read base addr
; ---- utilities -----
asmsub STOP2() -> ubyte @A {
asmsub STOP2() clobbers(X) -> ubyte @A {
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
%asm {{
txa
pha
jsr cbm.STOP
beq +
pla
tax
lda #0
rts
+ pla
tax
lda #1
+ lda #1
rts
}}
}
asmsub RDTIM16() -> uword @AY {
asmsub RDTIM16() clobbers(X) -> uword @AY {
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
%asm {{
stx P8ZP_SCRATCH_REG
jsr cbm.RDTIM
pha
txa
tay
pla
ldx P8ZP_SCRATCH_REG
rts
}}
}
@@ -140,6 +133,7 @@ asmsub RDTIM16() -> uword @AY {
c64 {
; C64 I/O registers (VIC, SID, CIA)
%option no_symbol_prefixing
; the default locations of the 8 sprite pointers (store address of sprite / 64)
&ubyte SPRPTR0 = 2040
@@ -294,6 +288,7 @@ c64 {
c128 {
; ---- C128 specific registers ----
%option no_symbol_prefixing
&ubyte VM1 = $0A2C ; shadow for VUC $d018 in text mode
&ubyte VM2 = $0A2D ; shadow for VIC $d018 in bitmap screen mode
@@ -330,6 +325,7 @@ asmsub disable_basic() clobbers(A) {
sys {
; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 128 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
@@ -442,10 +438,6 @@ _irq_handler_init
sta IRQ_SCRATCH_ZPWORD2
lda P8ZP_SCRATCH_W2+1
sta IRQ_SCRATCH_ZPWORD2+1
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
; We assume IRQ handlers don't contain complex expressions taking up more than that.
ldx #32
cld
rts
@@ -765,113 +757,137 @@ _longcopy
}
cx16 {
%option no_symbol_prefixing
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the C128 as well but their location in memory is different
; (because there's no room for them in the zeropage)
; $1300-$1bff is unused RAM on C128. We'll use $1a00-$1bff as the lo/hi evalstack.
; the virtual registers are allocated at the bottom of the eval-stack (should be ample space unless
; you're doing insane nesting of expressions...)
; NOTE: the memory location of these registers can change based on the "-esa" compiler option
&uword r0 = $1b00
&uword r1 = $1b02
&uword r2 = $1b04
&uword r3 = $1b06
&uword r4 = $1b08
&uword r5 = $1b0a
&uword r6 = $1b0c
&uword r7 = $1b0e
&uword r8 = $1b10
&uword r9 = $1b12
&uword r10 = $1b14
&uword r11 = $1b16
&uword r12 = $1b18
&uword r13 = $1b1a
&uword r14 = $1b1c
&uword r15 = $1b1e
; $1300-$1bff is unused RAM on C128.
&uword r0 = $1be0
&uword r1 = $1be2
&uword r2 = $1be4
&uword r3 = $1be6
&uword r4 = $1be8
&uword r5 = $1bea
&uword r6 = $1bec
&uword r7 = $1bee
&uword r8 = $1bf0
&uword r9 = $1bf2
&uword r10 = $1bf4
&uword r11 = $1bf6
&uword r12 = $1bf8
&uword r13 = $1bfa
&uword r14 = $1bfc
&uword r15 = $1bfe
&word r0s = $1b00
&word r1s = $1b02
&word r2s = $1b04
&word r3s = $1b06
&word r4s = $1b08
&word r5s = $1b0a
&word r6s = $1b0c
&word r7s = $1b0e
&word r8s = $1b10
&word r9s = $1b12
&word r10s = $1b14
&word r11s = $1b16
&word r12s = $1b18
&word r13s = $1b1a
&word r14s = $1b1c
&word r15s = $1b1e
&word r0s = $1be0
&word r1s = $1be2
&word r2s = $1be4
&word r3s = $1be6
&word r4s = $1be8
&word r5s = $1bea
&word r6s = $1bec
&word r7s = $1bee
&word r8s = $1bf0
&word r9s = $1bf2
&word r10s = $1bf4
&word r11s = $1bf6
&word r12s = $1bf8
&word r13s = $1bfa
&word r14s = $1bfc
&word r15s = $1bfe
&ubyte r0L = $1b00
&ubyte r1L = $1b02
&ubyte r2L = $1b04
&ubyte r3L = $1b06
&ubyte r4L = $1b08
&ubyte r5L = $1b0a
&ubyte r6L = $1b0c
&ubyte r7L = $1b0e
&ubyte r8L = $1b10
&ubyte r9L = $1b12
&ubyte r10L = $1b14
&ubyte r11L = $1b16
&ubyte r12L = $1b18
&ubyte r13L = $1b1a
&ubyte r14L = $1b1c
&ubyte r15L = $1b1e
&ubyte r0L = $1be0
&ubyte r1L = $1be2
&ubyte r2L = $1be4
&ubyte r3L = $1be6
&ubyte r4L = $1be8
&ubyte r5L = $1bea
&ubyte r6L = $1bec
&ubyte r7L = $1bee
&ubyte r8L = $1bf0
&ubyte r9L = $1bf2
&ubyte r10L = $1bf4
&ubyte r11L = $1bf6
&ubyte r12L = $1bf8
&ubyte r13L = $1bfa
&ubyte r14L = $1bfc
&ubyte r15L = $1bfe
&ubyte r0H = $1b01
&ubyte r1H = $1b03
&ubyte r2H = $1b05
&ubyte r3H = $1b07
&ubyte r4H = $1b09
&ubyte r5H = $1b0b
&ubyte r6H = $1b0d
&ubyte r7H = $1b0f
&ubyte r8H = $1b11
&ubyte r9H = $1b13
&ubyte r10H = $1b15
&ubyte r11H = $1b17
&ubyte r12H = $1b19
&ubyte r13H = $1b1b
&ubyte r14H = $1b1d
&ubyte r15H = $1b1f
&ubyte r0H = $1be1
&ubyte r1H = $1be3
&ubyte r2H = $1be5
&ubyte r3H = $1be7
&ubyte r4H = $1be9
&ubyte r5H = $1beb
&ubyte r6H = $1bed
&ubyte r7H = $1bef
&ubyte r8H = $1bf1
&ubyte r9H = $1bf3
&ubyte r10H = $1bf5
&ubyte r11H = $1bf7
&ubyte r12H = $1bf9
&ubyte r13H = $1bfb
&ubyte r14H = $1bfd
&ubyte r15H = $1bff
&byte r0sL = $1b00
&byte r1sL = $1b02
&byte r2sL = $1b04
&byte r3sL = $1b06
&byte r4sL = $1b08
&byte r5sL = $1b0a
&byte r6sL = $1b0c
&byte r7sL = $1b0e
&byte r8sL = $1b10
&byte r9sL = $1b12
&byte r10sL = $1b14
&byte r11sL = $1b16
&byte r12sL = $1b18
&byte r13sL = $1b1a
&byte r14sL = $1b1c
&byte r15sL = $1b1e
&byte r0sL = $1be0
&byte r1sL = $1be2
&byte r2sL = $1be4
&byte r3sL = $1be6
&byte r4sL = $1be8
&byte r5sL = $1bea
&byte r6sL = $1bec
&byte r7sL = $1bee
&byte r8sL = $1bf0
&byte r9sL = $1bf2
&byte r10sL = $1bf4
&byte r11sL = $1bf6
&byte r12sL = $1bf8
&byte r13sL = $1bfa
&byte r14sL = $1bfc
&byte r15sL = $1bfe
&byte r0sH = $1b01
&byte r1sH = $1b03
&byte r2sH = $1b05
&byte r3sH = $1b07
&byte r4sH = $1b09
&byte r5sH = $1b0b
&byte r6sH = $1b0d
&byte r7sH = $1b0f
&byte r8sH = $1b11
&byte r9sH = $1b13
&byte r10sH = $1b15
&byte r11sH = $1b17
&byte r12sH = $1b19
&byte r13sH = $1b1b
&byte r14sH = $1b1d
&byte r15sH = $1b1f
&byte r0sH = $1be1
&byte r1sH = $1be3
&byte r2sH = $1be5
&byte r3sH = $1be7
&byte r4sH = $1be9
&byte r5sH = $1beb
&byte r6sH = $1bed
&byte r7sH = $1bef
&byte r8sH = $1bf1
&byte r9sH = $1bf3
&byte r10sH = $1bf5
&byte r11sH = $1bf7
&byte r12sH = $1bf9
&byte r13sH = $1bfb
&byte r14sH = $1bfd
&byte r15sH = $1bff
asmsub save_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda cx16.r0,y
sta _cx16_vreg_storage,y
dey
bpl -
rts
_cx16_vreg_storage
.word 0,0,0,0,0,0,0,0
.word 0,0,0,0,0,0,0,0
}}
}
asmsub restore_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda save_virtual_registers._cx16_vreg_storage,y
sta cx16.r0,y
dey
bpl -
rts
}}
}
}
+21 -49
View File
@@ -5,6 +5,7 @@
txt {
%option no_symbol_prefixing
const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25
@@ -96,13 +97,12 @@ sub uppercase() {
c128.VM1 &= ~2
}
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, X, Y) {
; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcc _scroll_screen
+ ; scroll the screen and the color memory
@@ -132,17 +132,15 @@ _scroll_screen ; scroll only the screen memory
dey
bpl -
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A,X) {
; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcc _scroll_screen
+ ; scroll the screen and the color memory
@@ -168,17 +166,15 @@ _scroll_screen ; scroll only the screen memory
dex
bpl -
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A,X) {
; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcc _scroll_screen
+ ; scroll the screen and the color memory
@@ -204,17 +200,15 @@ _scroll_screen ; scroll only the screen memory
dex
bpl -
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A,X) {
; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcc _scroll_screen
+ ; scroll the screen and the color memory
@@ -240,7 +234,6 @@ _scroll_screen ; scroll only the screen memory
dex
bpl -
ldx P8ZP_SCRATCH_REG
rts
}}
}
@@ -265,10 +258,9 @@ asmsub print (str text @ AY) clobbers(A,Y) {
}}
}
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.ubyte2decimal
pha
tya
@@ -276,16 +268,13 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
pla
jsr cbm.CHROUT
txa
jsr cbm.CHROUT
ldx P8ZP_SCRATCH_REG
rts
jmp cbm.CHROUT
}}
}
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.ubyte2decimal
_print_byte_digits
pha
@@ -301,16 +290,13 @@ _print_byte_digits
beq _ones
jsr cbm.CHROUT
_ones txa
jsr cbm.CHROUT
ldx P8ZP_SCRATCH_REG
rts
jmp cbm.CHROUT
}}
}
asmsub print_b (byte value @ A) clobbers(A,Y) {
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
; ---- print the byte in A in decimal form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
pha
cmp #0
bpl +
@@ -322,10 +308,9 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
}}
}
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
stx P8ZP_SCRATCH_REG
bcc +
pha
lda #'$'
@@ -334,16 +319,13 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
+ jsr conv.ubyte2hex
jsr cbm.CHROUT
tya
jsr cbm.CHROUT
ldx P8ZP_SCRATCH_REG
rts
jmp cbm.CHROUT
}}
}
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
stx P8ZP_SCRATCH_REG
sta P8ZP_SCRATCH_B1
bcc +
lda #'%'
@@ -356,12 +338,11 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
+ jsr cbm.CHROUT
dey
bne -
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
pha
@@ -373,7 +354,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
}}
}
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
@@ -386,10 +367,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
}}
}
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.uword2decimal
ldy #0
- lda conv.uword2decimal.decTenThousands,y
@@ -397,17 +377,14 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
jsr cbm.CHROUT
iny
bne -
+ ldx P8ZP_SCRATCH_REG
rts
+ rts
}}
}
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.uword2decimal
ldx P8ZP_SCRATCH_REG
ldy #0
- lda conv.uword2decimal.decTenThousands,y
beq _allzero
@@ -428,7 +405,7 @@ _allzero
}}
}
asmsub print_w (word value @ AY) clobbers(A,Y) {
asmsub print_w (word value @ AY) clobbers(A,X,Y) {
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
%asm {{
cpy #0
@@ -582,15 +559,10 @@ _colormod sta $ffff ; modified
}}
}
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
; ---- safe wrapper around PLOT kernal routine, to save the X register.
asmsub plot (ubyte col @ Y, ubyte row @ X) {
%asm {{
stx P8ZP_SCRATCH_REG
tax
clc
jsr cbm.PLOT
ldx P8ZP_SCRATCH_REG
rts
jmp cbm.PLOT
}}
}
+95 -369
View File
@@ -5,13 +5,11 @@ FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
floats_store_reg .byte 0 ; temp storage
floats_temp_var .byte 0,0,0,0,0 ; temporary storage for a float
ub2float .proc
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
; clobbers A, Y
stx P8ZP_SCRATCH_REG
; clobbers A, X, Y
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy P8ZP_SCRATCH_B1
@@ -19,15 +17,12 @@ ub2float .proc
jsr GIVAYF
_fac_to_mem ldx P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
jsr MOVMF
ldx P8ZP_SCRATCH_REG
rts
jmp MOVMF
.pend
b2float .proc
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
; clobbers A, Y
stx P8ZP_SCRATCH_REG
; clobbers A, X, Y
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda P8ZP_SCRATCH_B1
@@ -37,7 +32,7 @@ b2float .proc
uw2float .proc
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
stx P8ZP_SCRATCH_REG
; clobbers X
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda P8ZP_SCRATCH_W1
@@ -48,7 +43,7 @@ uw2float .proc
w2float .proc
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
stx P8ZP_SCRATCH_REG
; clobbers X
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy P8ZP_SCRATCH_W1
@@ -60,7 +55,7 @@ w2float .proc
cast_from_uw .proc
; -- uword in A/Y into float var at (P8ZP_SCRATCH_W2)
stx P8ZP_SCRATCH_REG
; clobbers X
jsr GIVUAYFAY
jmp ub2float._fac_to_mem
.pend
@@ -68,7 +63,7 @@ cast_from_uw .proc
cast_from_w .proc
; -- word in A/Y into float var at (P8ZP_SCRATCH_W2)
stx P8ZP_SCRATCH_REG
; clobbers X
jsr GIVAYFAY
jmp ub2float._fac_to_mem
.pend
@@ -76,7 +71,7 @@ cast_from_w .proc
cast_from_ub .proc
; -- ubyte in Y into float var at (P8ZP_SCRATCH_W2)
stx P8ZP_SCRATCH_REG
; clobbers X
jsr FREADUY
jmp ub2float._fac_to_mem
.pend
@@ -84,169 +79,41 @@ cast_from_ub .proc
cast_from_b .proc
; -- byte in A into float var at (P8ZP_SCRATCH_W2)
stx P8ZP_SCRATCH_REG
; clobbers X
jsr FREADSA
jmp ub2float._fac_to_mem
.pend
cast_as_uw_into_ya .proc ; also used for float 2 ub
; -- cast float at A/Y to uword into Y/A
; clobbers X
jsr MOVFM
jmp cast_FAC1_as_uw_into_ya
.pend
cast_as_w_into_ay .proc ; also used for float 2 b
; -- cast float at A/Y to word into A/Y
; clobbers X
jsr MOVFM
jmp cast_FAC1_as_w_into_ay
.pend
cast_FAC1_as_uw_into_ya .proc ; also used for float 2 ub
; -- cast fac1 to uword into Y/A
stx P8ZP_SCRATCH_REG
jsr GETADR ; into Y/A
ldx P8ZP_SCRATCH_REG
rts
; clobbers X
jmp GETADR ; into Y/A
.pend
cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
; -- cast fac1 to word into A/Y
stx P8ZP_SCRATCH_REG
; clobbers X
jsr AYINT
ldy floats.AYINT_facmo
lda floats.AYINT_facmo+1
ldx P8ZP_SCRATCH_REG
rts
.pend
stack_b2float .proc
; -- b2float operating on the stack
inx
lda P8ESTACK_LO,x
stx P8ZP_SCRATCH_REG
jsr FREADSA
jmp push_fac1._internal
.pend
stack_w2float .proc
; -- w2float operating on the stack
inx
ldy P8ESTACK_LO,x
lda P8ESTACK_HI,x
stx P8ZP_SCRATCH_REG
jsr GIVAYF
jmp push_fac1._internal
.pend
stack_ub2float .proc
; -- ub2float operating on the stack
inx
lda P8ESTACK_LO,x
stx P8ZP_SCRATCH_REG
tay
lda #0
jsr GIVAYF
jmp push_fac1._internal
.pend
stack_uw2float .proc
; -- uw2float operating on the stack
inx
lda P8ESTACK_LO,x
ldy P8ESTACK_HI,x
stx P8ZP_SCRATCH_REG
jsr GIVUAYFAY
jmp push_fac1._internal
.pend
stack_float2w .proc ; also used for float2b
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
jsr AYINT
ldx P8ZP_SCRATCH_REG
lda floats.AYINT_facmo
sta P8ESTACK_HI,x
lda floats.AYINT_facmo+1
sta P8ESTACK_LO,x
dex
rts
.pend
stack_float2uw .proc ; also used for float2ub
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
jsr GETADR
ldx P8ZP_SCRATCH_REG
sta P8ESTACK_HI,x
tya
sta P8ESTACK_LO,x
dex
rts
.pend
push_float .proc
; ---- push mflpt5 in A/Y onto stack
; (taking 3 stack positions = 6 bytes of which 1 is padding)
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
lda (P8ZP_SCRATCH_W1),y
sta P8ESTACK_LO,x
iny
lda (P8ZP_SCRATCH_W1),y
sta P8ESTACK_HI,x
dex
iny
lda (P8ZP_SCRATCH_W1),y
sta P8ESTACK_LO,x
iny
lda (P8ZP_SCRATCH_W1),y
sta P8ESTACK_HI,x
dex
iny
lda (P8ZP_SCRATCH_W1),y
sta P8ESTACK_LO,x
dex
rts
.pend
pop_float .proc
; ---- pops mflpt5 from stack to memory A/Y
; (frees 3 stack positions = 6 bytes of which 1 is padding)
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #4
inx
lda P8ESTACK_LO,x
sta (P8ZP_SCRATCH_W1),y
dey
inx
lda P8ESTACK_HI,x
sta (P8ZP_SCRATCH_W1),y
dey
lda P8ESTACK_LO,x
sta (P8ZP_SCRATCH_W1),y
dey
inx
lda P8ESTACK_HI,x
sta (P8ZP_SCRATCH_W1),y
dey
lda P8ESTACK_LO,x
sta (P8ZP_SCRATCH_W1),y
rts
.pend
pop_float_fac1 .proc
; -- pops float from stack into FAC1
lda #<fmath_float1
ldy #>fmath_float1
jsr pop_float
lda #<fmath_float1
ldy #>fmath_float1
jmp MOVFM
.pend
copy_float .proc
; -- copies the 5 bytes of the mflt value pointed to by P8ZP_SCRATCH_W1,
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
@@ -272,25 +139,23 @@ copy_float .proc
inc_var_f .proc
; -- add 1 to float pointed to by A/Y
; clobbers X
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
stx P8ZP_SCRATCH_REG
jsr MOVFM
lda #<FL_ONE_const
ldy #>FL_ONE_const
jsr FADD
ldx P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jsr MOVMF
ldx P8ZP_SCRATCH_REG
rts
jmp MOVMF
.pend
dec_var_f .proc
; -- subtract 1 from float pointed to by A/Y
; clobbers X
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
stx P8ZP_SCRATCH_REG
lda #<FL_ONE_const
ldy #>FL_ONE_const
jsr MOVFM
@@ -299,23 +164,7 @@ dec_var_f .proc
jsr FSUB
ldx P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jsr MOVMF
ldx P8ZP_SCRATCH_REG
rts
.pend
pop_2_floats_f2_in_fac1 .proc
; -- pop 2 floats from stack, load the second one in FAC1 as well
lda #<fmath_float2
ldy #>fmath_float2
jsr pop_float
lda #<fmath_float1
ldy #>fmath_float1
jsr pop_float
lda #<fmath_float2
ldy #>fmath_float2
jmp MOVFM
jmp MOVMF
.pend
@@ -323,71 +172,9 @@ fmath_float1 .byte 0,0,0,0,0 ; storage for a mflpt5 value
fmath_float2 .byte 0,0,0,0,0 ; storage for a mflpt5 value
push_fac1 .proc
; -- push the float in FAC1 onto the stack
stx P8ZP_SCRATCH_REG
_internal ldx #<fmath_float1
ldy #>fmath_float1
jsr MOVMF
lda #<fmath_float1
ldy #>fmath_float1
ldx P8ZP_SCRATCH_REG
jmp push_float
.pend
div_f .proc
; -- push f1/f2 on stack
jsr pop_2_floats_f2_in_fac1
stx P8ZP_SCRATCH_REG
lda #<fmath_float1
ldy #>fmath_float1
jsr FDIV
jmp push_fac1._internal
.pend
add_f .proc
; -- push f1+f2 on stack
jsr pop_2_floats_f2_in_fac1
stx P8ZP_SCRATCH_REG
lda #<fmath_float1
ldy #>fmath_float1
jsr FADD
jmp push_fac1._internal
.pend
sub_f .proc
; -- push f1-f2 on stack
jsr pop_2_floats_f2_in_fac1
stx P8ZP_SCRATCH_REG
lda #<fmath_float1
ldy #>fmath_float1
jsr FSUB
jmp push_fac1._internal
.pend
mul_f .proc
; -- push f1*f2 on stack
jsr pop_2_floats_f2_in_fac1
stx P8ZP_SCRATCH_REG
lda #<fmath_float1
ldy #>fmath_float1
jsr FMULT
jmp push_fac1._internal
.pend
neg_f .proc
; -- toggle the sign bit on the stack
lda P8ESTACK_HI+3,x
eor #$80
sta P8ESTACK_HI+3,x
rts
.pend
var_fac1_less_f .proc
; -- is the float in FAC1 < the variable AY?
stx P8ZP_SCRATCH_REG
; -- is the float in FAC1 < the variable AY? Result in A. Clobbers X.
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #255
beq +
lda #0
@@ -397,10 +184,8 @@ var_fac1_less_f .proc
.pend
var_fac1_lesseq_f .proc
; -- is the float in FAC1 <= the variable AY?
stx P8ZP_SCRATCH_REG
; -- is the float in FAC1 <= the variable AY? Result in A. Clobbers X.
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #0
beq +
cmp #255
@@ -412,10 +197,8 @@ var_fac1_lesseq_f .proc
.pend
var_fac1_greater_f .proc
; -- is the float in FAC1 > the variable AY?
stx P8ZP_SCRATCH_REG
; -- is the float in FAC1 > the variable AY? Result in A. Clobbers X.
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #1
beq +
lda #0
@@ -425,10 +208,8 @@ var_fac1_greater_f .proc
.pend
var_fac1_greatereq_f .proc
; -- is the float in FAC1 >= the variable AY?
stx P8ZP_SCRATCH_REG
; -- is the float in FAC1 >= the variable AY? Result in A. Clobbers X.
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #0
beq +
cmp #1
@@ -439,17 +220,23 @@ var_fac1_greatereq_f .proc
rts
.pend
var_fac1_equal_f .proc
; -- are the floats numbers in FAC1 and the variable AY *not* identical? Result in A. Clobbers X.
jsr FCOMP
and #1
eor #1
rts
.pend
var_fac1_notequal_f .proc
; -- are the floats numbers in FAC1 and the variable AY *not* identical?
stx P8ZP_SCRATCH_REG
; -- are the floats numbers in FAC1 and the variable AY *not* identical? Result in A. Clobbers X.
jsr FCOMP
ldx P8ZP_SCRATCH_REG
and #1
rts
.pend
vars_equal_f .proc
; -- are the mflpt5 numbers in P8ZP_SCRATCH_W1 and AY identical?
; -- are the mflpt5 numbers in P8ZP_SCRATCH_W1 and AY identical? Result in A
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #0
@@ -478,51 +265,13 @@ _false lda #0
rts
.pend
equal_f .proc
; -- are the two mflpt5 numbers on the stack identical?
inx
inx
inx
inx
lda P8ESTACK_LO-3,x
cmp P8ESTACK_LO,x
bne _equals_false
lda P8ESTACK_LO-2,x
cmp P8ESTACK_LO+1,x
bne _equals_false
lda P8ESTACK_LO-1,x
cmp P8ESTACK_LO+2,x
bne _equals_false
lda P8ESTACK_HI-2,x
cmp P8ESTACK_HI+1,x
bne _equals_false
lda P8ESTACK_HI-1,x
cmp P8ESTACK_HI+2,x
bne _equals_false
_equals_true lda #1
_equals_store inx
sta P8ESTACK_LO+1,x
rts
_equals_false lda #0
beq _equals_store
.pend
notequal_f .proc
; -- are the two mflpt5 numbers on the stack different?
jsr equal_f
eor #1 ; invert the result
sta P8ESTACK_LO+1,x
rts
.pend
vars_less_f .proc
; -- is float in AY < float in P8ZP_SCRATCH_W2 ?
; -- is float in AY < float in P8ZP_SCRATCH_W2 ? Result in A. Clobbers X.
jsr MOVFM
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #255
bne +
lda #1
@@ -532,13 +281,11 @@ vars_less_f .proc
.pend
vars_lesseq_f .proc
; -- is float in AY <= float in P8ZP_SCRATCH_W2 ?
; -- is float in AY <= float in P8ZP_SCRATCH_W2 ? Result in A. Clobbers X.
jsr MOVFM
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #255
bne +
- lda #1
@@ -550,7 +297,7 @@ vars_lesseq_f .proc
.pend
less_f .proc
; -- is f1 < f2?
; -- is f1 < f2? Result in A. Clobbers X.
jsr compare_floats
cmp #255
beq compare_floats._return_true
@@ -559,7 +306,7 @@ less_f .proc
lesseq_f .proc
; -- is f1 <= f2?
; -- is f1 <= f2? Result in A. Clobbers X.
jsr compare_floats
cmp #255
beq compare_floats._return_true
@@ -569,7 +316,7 @@ lesseq_f .proc
.pend
greater_f .proc
; -- is f1 > f2?
; -- is f1 > f2? Result in A. Clobbers X.
jsr compare_floats
cmp #1
beq compare_floats._return_true
@@ -577,7 +324,7 @@ greater_f .proc
.pend
greatereq_f .proc
; -- is f1 >= f2?
; -- is f1 >= f2? Result in A. Clobbers X.
jsr compare_floats
cmp #1
beq compare_floats._return_true
@@ -586,32 +333,9 @@ greatereq_f .proc
bne compare_floats._return_false
.pend
compare_floats .proc
lda #<fmath_float2
ldy #>fmath_float2
jsr pop_float
lda #<fmath_float1
ldy #>fmath_float1
jsr pop_float
lda #<fmath_float1
ldy #>fmath_float1
jsr MOVFM ; fac1 = flt1
lda #<fmath_float2
ldy #>fmath_float2
stx P8ZP_SCRATCH_REG
jsr FCOMP ; A = flt1 compared with flt2 (0=equal, 1=flt1>flt2, 255=flt1<flt2)
ldx P8ZP_SCRATCH_REG
rts
_return_false lda #0
_return_result sta P8ESTACK_LO,x
dex
rts
_return_true lda #1
bne _return_result
.pend
set_array_float_from_fac1 .proc
; -- set the float in FAC1 in the array (index in A, array in P8ZP_SCRATCH_W1)
; clobbers X
sta P8ZP_SCRATCH_B1
asl a
asl a
@@ -622,11 +346,8 @@ set_array_float_from_fac1 .proc
adc P8ZP_SCRATCH_W1
bcc +
iny
+ stx floats_store_reg
tax
jsr MOVMF
ldx floats_store_reg
rts
+ tax
jmp MOVMF
.pend
@@ -669,54 +390,59 @@ set_array_float .proc
.pend
equal_zero .proc
jsr floats.pop_float_fac1
jsr floats.SIGN
beq _true
bne _false
_true lda #1
sta P8ESTACK_LO,x
dex
rts
_false lda #0
sta P8ESTACK_LO,x
dex
rts
.pend
pushFAC1 .proc
;-- push floating point in FAC onto the cpu stack
; save return address
pla
sta P8ZP_SCRATCH_W2
pla
sta P8ZP_SCRATCH_W2+1
ldx #<floats.floats_temp_var
ldy #>floats.floats_temp_var
jsr floats.MOVMF
lda floats.floats_temp_var
pha
lda floats.floats_temp_var+1
pha
lda floats.floats_temp_var+2
pha
lda floats.floats_temp_var+3
pha
lda floats.floats_temp_var+4
pha
; re-push return address
lda P8ZP_SCRATCH_W2+1
pha
lda P8ZP_SCRATCH_W2
pha
rts
.pend
notequal_zero .proc
jsr floats.pop_float_fac1
jsr floats.SIGN
bne equal_zero._true
beq equal_zero._false
.pend
popFAC1 .proc
; -- pop floating point value from cpu stack into FAC1
; save return address
pla
sta P8ZP_SCRATCH_W2
pla
sta P8ZP_SCRATCH_W2+1
pla
sta floats.floats_temp_var+4
pla
sta floats.floats_temp_var+3
pla
sta floats.floats_temp_var+2
pla
sta floats.floats_temp_var+1
pla
sta floats.floats_temp_var
lda #<floats.floats_temp_var
ldy #>floats.floats_temp_var
jsr floats.MOVFM
; re-push return address
lda P8ZP_SCRATCH_W2+1
pha
lda P8ZP_SCRATCH_W2
pha
rts
.pend
greater_zero .proc
jsr floats.pop_float_fac1
jsr floats.SIGN
beq equal_zero._false
bpl equal_zero._true
jmp equal_zero._false
.pend
less_zero .proc
jsr floats.pop_float_fac1
jsr floats.SIGN
bmi equal_zero._true
jmp equal_zero._false
.pend
greaterequal_zero .proc
jsr floats.pop_float_fac1
jsr floats.SIGN
bpl equal_zero._true
jmp equal_zero._false
.pend
lessequal_zero .proc
jsr floats.pop_float_fac1
jsr floats.SIGN
beq equal_zero._true
bmi equal_zero._true
jmp equal_zero._false
.pend
+2 -4
View File
@@ -5,6 +5,7 @@
floats {
; ---- this block contains C-64 floating point related functions ----
%option no_symbol_prefixing
const float PI = 3.141592653589793
const float TWOPI = 6.283185307179586
@@ -158,12 +159,9 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
sub rndf() -> float {
%asm {{
stx P8ZP_SCRATCH_REG
lda #1
jsr FREADSA
jsr RND ; rng into fac1
ldx P8ZP_SCRATCH_REG
rts
jmp RND ; rng into fac1
}}
}
+2 -15
View File
@@ -1,13 +1,6 @@
; --- floating point builtin functions
func_sign_f_stack .proc
jsr func_sign_f_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_sign_f_into_A .proc
jsr MOVFM
jmp SIGN
@@ -146,17 +139,11 @@ func_all_f_stack .proc
.pend
func_abs_f_into_FAC1 .proc
stx P8ZP_SCRATCH_REG
jsr MOVFM
jsr ABS
ldx P8ZP_SCRATCH_REG
rts
jmp ABS
.pend
func_sqrt_into_FAC1 .proc
stx P8ZP_SCRATCH_REG
jsr MOVFM
jsr SQR
ldx P8ZP_SCRATCH_REG
rts
jmp SQR
.pend
+3 -3
View File
@@ -5,6 +5,8 @@
; assumes bitmap screen memory is $2000-$3fff
graphics {
%option no_symbol_prefixing
const uword BITMAP_ADDRESS = $2000
const uword WIDTH = 320
const ubyte HEIGHT = 200
@@ -183,7 +185,6 @@ graphics {
lda length
and #7
sta separate_pixels
stx P8ZP_SCRATCH_REG
lsr length+1
ror length
lsr length+1
@@ -208,8 +209,7 @@ _modified stx $ffff ; modified
inc _modified+2
+ dey
bne _modified
_zero ldx P8ZP_SCRATCH_REG
_zero
ldy separate_pixels
beq hline_zero2
lda _modified+1
+137 -115
View File
@@ -6,6 +6,8 @@ cbm {
; Commodore (CBM) common variables, vectors and kernal routines
%option no_symbol_prefixing
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
&ubyte TIME_MID = $a1 ; .. mid byte
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
@@ -102,34 +104,26 @@ romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, bool dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
asmsub STOP2() -> ubyte @A {
asmsub STOP2() clobbers(X) -> ubyte @A {
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
%asm {{
txa
pha
jsr cbm.STOP
beq +
pla
tax
lda #0
rts
+ pla
tax
lda #1
+ lda #1
rts
}}
}
asmsub RDTIM16() -> uword @AY {
asmsub RDTIM16() clobbers(X) -> uword @AY {
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
%asm {{
stx P8ZP_SCRATCH_REG
jsr cbm.RDTIM
pha
txa
tay
pla
ldx P8ZP_SCRATCH_REG
rts
}}
}
@@ -139,6 +133,8 @@ asmsub RDTIM16() -> uword @AY {
c64 {
; C64 I/O registers (VIC, SID, CIA)
%option no_symbol_prefixing
; the default locations of the 8 sprite pointers (store address of sprite / 64)
&ubyte SPRPTR0 = 2040
&ubyte SPRPTR1 = 2041
@@ -293,6 +289,8 @@ c64 {
sys {
; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 64 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
@@ -407,10 +405,6 @@ _irq_handler_init
sta IRQ_SCRATCH_ZPWORD2
lda P8ZP_SCRATCH_W2+1
sta IRQ_SCRATCH_ZPWORD2+1
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
; We assume IRQ handlers don't contain complex expressions taking up more than that.
ldx #32
cld
rts
@@ -732,111 +726,139 @@ _longcopy
cx16 {
%option no_symbol_prefixing
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage
; they are simulated on the C64 as well but their location in memory is different
; (because there's no room for them in the zeropage)
; they are allocated at the bottom of the eval-stack (should be ample space unless
; you're doing insane nesting of expressions...)
; NOTE: the memory location of these registers can change based on the "-esa" compiler option
&uword r0 = $cf00
&uword r1 = $cf02
&uword r2 = $cf04
&uword r3 = $cf06
&uword r4 = $cf08
&uword r5 = $cf0a
&uword r6 = $cf0c
&uword r7 = $cf0e
&uword r8 = $cf10
&uword r9 = $cf12
&uword r10 = $cf14
&uword r11 = $cf16
&uword r12 = $cf18
&uword r13 = $cf1a
&uword r14 = $cf1c
&uword r15 = $cf1e
; (because there's no room for them in the zeropage in the default configuration)
; Note that when using ZP options that free up more of the zeropage (such as %zeropage kernalsafe)
; there might be enough space to put them there after all, and the compiler will change these addresses!
&uword r0 = $cfe0
&uword r1 = $cfe2
&uword r2 = $cfe4
&uword r3 = $cfe6
&uword r4 = $cfe8
&uword r5 = $cfea
&uword r6 = $cfec
&uword r7 = $cfee
&uword r8 = $cff0
&uword r9 = $cff2
&uword r10 = $cff4
&uword r11 = $cff6
&uword r12 = $cff8
&uword r13 = $cffa
&uword r14 = $cffc
&uword r15 = $cffe
&word r0s = $cf00
&word r1s = $cf02
&word r2s = $cf04
&word r3s = $cf06
&word r4s = $cf08
&word r5s = $cf0a
&word r6s = $cf0c
&word r7s = $cf0e
&word r8s = $cf10
&word r9s = $cf12
&word r10s = $cf14
&word r11s = $cf16
&word r12s = $cf18
&word r13s = $cf1a
&word r14s = $cf1c
&word r15s = $cf1e
&word r0s = $cfe0
&word r1s = $cfe2
&word r2s = $cfe4
&word r3s = $cfe6
&word r4s = $cfe8
&word r5s = $cfea
&word r6s = $cfec
&word r7s = $cfee
&word r8s = $cff0
&word r9s = $cff2
&word r10s = $cff4
&word r11s = $cff6
&word r12s = $cff8
&word r13s = $cffa
&word r14s = $cffc
&word r15s = $cffe
&ubyte r0L = $cf00
&ubyte r1L = $cf02
&ubyte r2L = $cf04
&ubyte r3L = $cf06
&ubyte r4L = $cf08
&ubyte r5L = $cf0a
&ubyte r6L = $cf0c
&ubyte r7L = $cf0e
&ubyte r8L = $cf10
&ubyte r9L = $cf12
&ubyte r10L = $cf14
&ubyte r11L = $cf16
&ubyte r12L = $cf18
&ubyte r13L = $cf1a
&ubyte r14L = $cf1c
&ubyte r15L = $cf1e
&ubyte r0L = $cfe0
&ubyte r1L = $cfe2
&ubyte r2L = $cfe4
&ubyte r3L = $cfe6
&ubyte r4L = $cfe8
&ubyte r5L = $cfea
&ubyte r6L = $cfec
&ubyte r7L = $cfee
&ubyte r8L = $cff0
&ubyte r9L = $cff2
&ubyte r10L = $cff4
&ubyte r11L = $cff6
&ubyte r12L = $cff8
&ubyte r13L = $cffa
&ubyte r14L = $cffc
&ubyte r15L = $cffe
&ubyte r0H = $cf01
&ubyte r1H = $cf03
&ubyte r2H = $cf05
&ubyte r3H = $cf07
&ubyte r4H = $cf09
&ubyte r5H = $cf0b
&ubyte r6H = $cf0d
&ubyte r7H = $cf0f
&ubyte r8H = $cf11
&ubyte r9H = $cf13
&ubyte r10H = $cf15
&ubyte r11H = $cf17
&ubyte r12H = $cf19
&ubyte r13H = $cf1b
&ubyte r14H = $cf1d
&ubyte r15H = $cf1f
&ubyte r0H = $cfe1
&ubyte r1H = $cfe3
&ubyte r2H = $cfe5
&ubyte r3H = $cfe7
&ubyte r4H = $cfe9
&ubyte r5H = $cfeb
&ubyte r6H = $cfed
&ubyte r7H = $cfef
&ubyte r8H = $cff1
&ubyte r9H = $cff3
&ubyte r10H = $cff5
&ubyte r11H = $cff7
&ubyte r12H = $cff9
&ubyte r13H = $cffb
&ubyte r14H = $cffd
&ubyte r15H = $cfff
&byte r0sL = $cf00
&byte r1sL = $cf02
&byte r2sL = $cf04
&byte r3sL = $cf06
&byte r4sL = $cf08
&byte r5sL = $cf0a
&byte r6sL = $cf0c
&byte r7sL = $cf0e
&byte r8sL = $cf10
&byte r9sL = $cf12
&byte r10sL = $cf14
&byte r11sL = $cf16
&byte r12sL = $cf18
&byte r13sL = $cf1a
&byte r14sL = $cf1c
&byte r15sL = $cf1e
&byte r0sL = $cfe0
&byte r1sL = $cfe2
&byte r2sL = $cfe4
&byte r3sL = $cfe6
&byte r4sL = $cfe8
&byte r5sL = $cfea
&byte r6sL = $cfec
&byte r7sL = $cfee
&byte r8sL = $cff0
&byte r9sL = $cff2
&byte r10sL = $cff4
&byte r11sL = $cff6
&byte r12sL = $cff8
&byte r13sL = $cffa
&byte r14sL = $cffc
&byte r15sL = $cffe
&byte r0sH = $cfe1
&byte r1sH = $cfe3
&byte r2sH = $cfe5
&byte r3sH = $cfe7
&byte r4sH = $cfe9
&byte r5sH = $cfeb
&byte r6sH = $cfed
&byte r7sH = $cfef
&byte r8sH = $cff1
&byte r9sH = $cff3
&byte r10sH = $cff5
&byte r11sH = $cff7
&byte r12sH = $cff9
&byte r13sH = $cffb
&byte r14sH = $cffd
&byte r15sH = $cfff
asmsub save_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda cx16.r0,y
sta _cx16_vreg_storage,y
dey
bpl -
rts
_cx16_vreg_storage
.word 0,0,0,0,0,0,0,0
.word 0,0,0,0,0,0,0,0
}}
}
asmsub restore_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda save_virtual_registers._cx16_vreg_storage,y
sta cx16.r0,y
dey
bpl -
rts
}}
}
&byte r0sH = $cf01
&byte r1sH = $cf03
&byte r2sH = $cf05
&byte r3sH = $cf07
&byte r4sH = $cf09
&byte r5sH = $cf0b
&byte r6sH = $cf0d
&byte r7sH = $cf0f
&byte r8sH = $cf11
&byte r9sH = $cf13
&byte r10sH = $cf15
&byte r11sH = $cf17
&byte r12sH = $cf19
&byte r13sH = $cf1b
&byte r14sH = $cf1d
&byte r15sH = $cf1f
}
+22 -49
View File
@@ -6,6 +6,8 @@
txt {
%option no_symbol_prefixing
const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25
@@ -94,13 +96,12 @@ sub uppercase() {
c64.VMCSB &= ~2
}
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, X, Y) {
; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcc _scroll_screen
+ ; scroll the screen and the color memory
@@ -130,17 +131,15 @@ _scroll_screen ; scroll only the screen memory
dey
bpl -
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
asmsub scroll_right (bool alsocolors @ Pc) clobbers(A,X) {
; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcc _scroll_screen
+ ; scroll the screen and the color memory
@@ -166,17 +165,15 @@ _scroll_screen ; scroll only the screen memory
dex
bpl -
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
asmsub scroll_up (bool alsocolors @ Pc) clobbers(A,X) {
; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcc _scroll_screen
+ ; scroll the screen and the color memory
@@ -202,17 +199,15 @@ _scroll_screen ; scroll only the screen memory
dex
bpl -
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
asmsub scroll_down (bool alsocolors @ Pc) clobbers(A,X) {
; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcc _scroll_screen
+ ; scroll the screen and the color memory
@@ -238,7 +233,6 @@ _scroll_screen ; scroll only the screen memory
dex
bpl -
ldx P8ZP_SCRATCH_REG
rts
}}
}
@@ -263,10 +257,9 @@ asmsub print (str text @ AY) clobbers(A,Y) {
}}
}
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.ubyte2decimal
pha
tya
@@ -274,16 +267,13 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
pla
jsr cbm.CHROUT
txa
jsr cbm.CHROUT
ldx P8ZP_SCRATCH_REG
rts
jmp cbm.CHROUT
}}
}
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.ubyte2decimal
_print_byte_digits
pha
@@ -299,16 +289,13 @@ _print_byte_digits
beq _ones
jsr cbm.CHROUT
_ones txa
jsr cbm.CHROUT
ldx P8ZP_SCRATCH_REG
rts
jmp cbm.CHROUT
}}
}
asmsub print_b (byte value @ A) clobbers(A,Y) {
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
; ---- print the byte in A in decimal form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
pha
cmp #0
bpl +
@@ -320,10 +307,9 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
}}
}
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
stx P8ZP_SCRATCH_REG
bcc +
pha
lda #'$'
@@ -332,16 +318,13 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
+ jsr conv.ubyte2hex
jsr cbm.CHROUT
tya
jsr cbm.CHROUT
ldx P8ZP_SCRATCH_REG
rts
jmp cbm.CHROUT
}}
}
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
stx P8ZP_SCRATCH_REG
sta P8ZP_SCRATCH_B1
bcc +
lda #'%'
@@ -354,12 +337,11 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
+ jsr cbm.CHROUT
dey
bne -
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
pha
@@ -371,7 +353,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
}}
}
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
@@ -384,10 +366,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
}}
}
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.uword2decimal
ldy #0
- lda conv.uword2decimal.decTenThousands,y
@@ -395,17 +376,14 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
jsr cbm.CHROUT
iny
bne -
+ ldx P8ZP_SCRATCH_REG
rts
+ rts
}}
}
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.uword2decimal
ldx P8ZP_SCRATCH_REG
ldy #0
- lda conv.uword2decimal.decTenThousands,y
beq _allzero
@@ -426,7 +404,7 @@ _allzero
}}
}
asmsub print_w (word value @ AY) clobbers(A,Y) {
asmsub print_w (word value @ AY) clobbers(A,X,Y) {
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
%asm {{
cpy #0
@@ -580,15 +558,10 @@ _colormod sta $ffff ; modified
}}
}
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
; ---- safe wrapper around PLOT kernal routine, to save the X register.
asmsub plot (ubyte col @ Y, ubyte row @ X) {
%asm {{
stx P8ZP_SCRATCH_REG
tax
clc
jsr cbm.PLOT
ldx P8ZP_SCRATCH_REG
rts
jmp cbm.PLOT
}}
}
+14 -25
View File
@@ -2,29 +2,28 @@
conv {
%option no_symbol_prefixing
; ----- number conversions to decimal strings ----
str @shared string_out = "????????????????" ; result buffer for the string conversion routines
asmsub str_ub0 (ubyte value @ A) clobbers(A,Y) {
asmsub str_ub0 (ubyte value @ A) clobbers(A,X,Y) {
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.ubyte2decimal
sty string_out
sta string_out+1
stx string_out+2
lda #0
sta string_out+3
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub str_ub (ubyte value @ A) clobbers(A,Y) {
asmsub str_ub (ubyte value @ A) clobbers(A,X,Y) {
; ---- convert the ubyte in A in decimal string form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
ldy #0
sty P8ZP_SCRATCH_B1
jsr conv.ubyte2decimal
@@ -50,15 +49,13 @@ _output_byte_digits
iny
lda #0
sta string_out,y
ldx P8ZP_SCRATCH_REG
rts
}}
}
asmsub str_b (byte value @ A) clobbers(A,Y) {
asmsub str_b (byte value @ A) clobbers(A,X,Y) {
; ---- convert the byte in A in decimal string form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
ldy #0
sty P8ZP_SCRATCH_B1
cmp #0
@@ -73,7 +70,7 @@ asmsub str_b (byte value @ A) clobbers(A,Y) {
}}
}
asmsub str_ubhex (ubyte value @ A) clobbers(A,Y) {
asmsub str_ubhex (ubyte value @ A) clobbers(A,X,Y) {
; ---- convert the ubyte in A in hex string form
%asm {{
jsr conv.ubyte2hex
@@ -85,7 +82,7 @@ asmsub str_ubhex (ubyte value @ A) clobbers(A,Y) {
}}
}
asmsub str_ubbin (ubyte value @ A) clobbers(A,Y) {
asmsub str_ubbin (ubyte value @ A) clobbers(A,X,Y) {
; ---- convert the ubyte in A in binary string form
%asm {{
sta P8ZP_SCRATCH_B1
@@ -104,7 +101,7 @@ _digit sta string_out,y
}}
}
asmsub str_uwbin (uword value @ AY) clobbers(A,Y) {
asmsub str_uwbin (uword value @ AY) clobbers(A,X,Y) {
; ---- convert the uword in A/Y in binary string form
%asm {{
sta P8ZP_SCRATCH_REG
@@ -143,10 +140,9 @@ asmsub str_uwhex (uword value @ AY) clobbers(A,Y) {
}}
}
asmsub str_uw0 (uword value @ AY) clobbers(A,Y) {
asmsub str_uw0 (uword value @ AY) clobbers(A,X,Y) {
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.uword2decimal
ldy #0
- lda conv.uword2decimal.decTenThousands,y
@@ -154,15 +150,13 @@ asmsub str_uw0 (uword value @ AY) clobbers(A,Y) {
beq +
iny
bne -
+ ldx P8ZP_SCRATCH_REG
rts
+ rts
}}
}
asmsub str_uw (uword value @ AY) clobbers(A,Y) {
asmsub str_uw (uword value @ AY) clobbers(A,X,Y) {
; ---- convert the uword in A/Y in decimal string form, without left padding 0s
%asm {{
stx P8ZP_SCRATCH_REG
jsr conv.uword2decimal
ldx #0
_output_digits
@@ -180,7 +174,6 @@ _gotdigit sta string_out,x
bne _gotdigit
_end lda #0
sta string_out,x
ldx P8ZP_SCRATCH_REG
rts
_allzero lda #'0'
@@ -190,12 +183,11 @@ _allzero lda #'0'
}}
}
asmsub str_w (word value @ AY) clobbers(A,Y) {
asmsub str_w (word value @ AY) clobbers(A,X,Y) {
; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's
%asm {{
cpy #0
bpl str_uw
stx P8ZP_SCRATCH_REG
pha
lda #'-'
sta string_out
@@ -506,8 +498,7 @@ asmsub ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
%asm {{
ldy #uword2decimal.ASCII_0_OFFSET
bne uword2decimal.hex_try200
rts
jmp uword2decimal.hex_try200
}}
}
@@ -699,10 +690,9 @@ asmsub byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
}}
}
asmsub ubyte2hex (ubyte value @A) -> ubyte @A, ubyte @Y {
asmsub ubyte2hex (ubyte value @A) clobbers(X) -> ubyte @A, ubyte @Y {
; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y)
%asm {{
stx P8ZP_SCRATCH_REG
pha
and #$0f
tax
@@ -714,7 +704,6 @@ asmsub ubyte2hex (ubyte value @A) -> ubyte @A, ubyte @Y {
lsr a
tax
lda _hex_digits,x
ldx P8ZP_SCRATCH_REG
rts
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
+95 -25
View File
@@ -6,6 +6,7 @@
%import syslib
diskio {
%option no_symbol_prefixing
ubyte drivenumber = 8
@@ -73,39 +74,42 @@ io_error:
}
sub diskname() -> uword {
; -- Returns pointer to disk name string or 0 if failure.
cbm.SETNAM(1, "$")
cbm.SETLFS(12, drivenumber, 0)
ubyte okay = false
void cbm.OPEN() ; open 12,8,0,"$"
; returns disk label name or 0 if error
cbm.SETNAM(3, "$")
cbm.SETLFS(12, diskio.drivenumber, 0)
ubyte status = 1
void cbm.OPEN() ; open 12,8,0,"$=c"
if_cs
goto io_error
void cbm.CHKIN(12) ; use #12 as input channel
if_cs
goto io_error
repeat 6 {
void cbm.CHRIN() ; skip the 6 prologue bytes
while cbm.CHRIN()!='"' {
; skip up to entry name
}
if cbm.READST()!=0
goto io_error
cx16.r0 = &list_filename
cx16.r0 = &diskio.list_filename
repeat {
@(cx16.r0) = cbm.CHRIN()
if @(cx16.r0)==0
if @(cx16.r0)=='"' {
@(cx16.r0) = ' '
while @(cx16.r0)==' ' and cx16.r0>=&diskio.list_filename {
@(cx16.r0) = 0
cx16.r0--
}
break
}
cx16.r0++
}
okay = true
status = cbm.READST()
io_error:
cbm.CLRCHN() ; restore default i/o devices
cbm.CLRCHN()
cbm.CLOSE(12)
if okay
return &list_filename
return 0
if status and status & $40 == 0
return 0
return diskio.list_filename
}
; internal variables for the iterative file lister / loader
@@ -286,6 +290,7 @@ close_end:
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
; -- read from the currently open file, up to the given number of bytes.
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.macptr() yourself with the vera data register as address.
if not iteration_in_progress or not num_bytes
return 0
@@ -343,6 +348,7 @@ m_in_buffer sta $ffff
; optimized for Commander X16 to use MACPTR block read kernal call
sub f_read_all(uword bufferpointer) -> uword {
; -- read the full contents of the file, returns number of bytes read.
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.macptr() yourself with the vera data register as address.
if not iteration_in_progress
return 0
@@ -484,7 +490,6 @@ io_error:
sta P8ZP_SCRATCH_W1
lda address+1
sta P8ZP_SCRATCH_W1+1
stx P8ZP_SCRATCH_REG
ldx end_address
ldy end_address+1
lda headerless
@@ -495,7 +500,6 @@ io_error:
+ lda #<P8ZP_SCRATCH_W1
jsr cbm.SAVE
+ php
ldx P8ZP_SCRATCH_REG
plp
}}
@@ -543,7 +547,6 @@ io_error:
secondary |= %00000010 ; activate cx16 kernal headerless load support
cbm.SETLFS(1, drivenumber, secondary)
%asm {{
stx P8ZP_SCRATCH_REG
lda #0
ldx address_override
ldy address_override+1
@@ -551,7 +554,7 @@ io_error:
bcs +
stx cx16.r1
sty cx16.r1+1
+ ldx P8ZP_SCRATCH_REG
+
}}
cbm.CLRCHN()
@@ -603,14 +606,13 @@ io_error:
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
}
asmsub vload(str name @R0, ubyte bank @A, uword address @R1) -> ubyte @A {
asmsub vload(str name @R0, ubyte bank @A, uword address @R1) clobbers(X, Y) -> ubyte @A {
; -- like the basic command VLOAD "filename",drivenumber,bank,address
; loads a file into Vera's video memory in the given bank:address, returns success in A
; the file has to have the usual 2 byte header (which will be skipped)
%asm {{
clc
internal_vload:
phx
pha
ldx drivenumber
bcc +
@@ -638,13 +640,12 @@ internal_vload:
+ jsr cbm.CLRCHN
lda #1
jsr cbm.CLOSE
plx
lda P8ZP_SCRATCH_B1
rts
}}
}
asmsub vload_raw(str name @R0, ubyte bank @A, uword address @R1) -> ubyte @A {
asmsub vload_raw(str name @R0, ubyte bank @A, uword address @R1) clobbers(X, Y) -> ubyte @A {
; -- like the basic command BVLOAD "filename",drivenumber,bank,address
; loads a file into Vera's video memory in the given bank:address, returns success in A
; the file is read fully including the first two bytes.
@@ -684,6 +685,75 @@ internal_vload:
send_command(list_filename)
}
sub curdir() -> uword {
; return current directory name or 0 if error
; special X16 dos command to only return the current path in the entry list (R42+)
const ubyte MAX_PATH_LEN=80
uword reversebuffer = memory("curdir_buffer", MAX_PATH_LEN, 0)
cx16.r12 = reversebuffer + MAX_PATH_LEN-1
@(cx16.r12)=0
cbm.SETNAM(3, "$=c")
cbm.SETLFS(12, diskio.drivenumber, 0)
void cbm.OPEN() ; open 12,8,0,"$=c"
if_cs
goto io_error
void cbm.CHKIN(12) ; use #12 as input channel
if_cs
goto io_error
repeat 6 {
void cbm.CHRIN()
}
while cbm.CHRIN() {
; skip first line (drive label)
}
while cbm.CHRIN()!='"' {
; skip to first name
}
ubyte status = cbm.READST()
cx16.r10 = &list_filename
while status==0 {
repeat {
@(cx16.r10) = cbm.CHRIN()
if @(cx16.r10)==0
break
cx16.r10++
}
while @(cx16.r10)!='"' and cx16.r10>=&list_filename {
@(cx16.r10)=0
cx16.r10--
}
@(cx16.r10)=0
prepend(list_filename)
cx16.r10 = &list_filename
while cbm.CHRIN()!='"' and status==0 {
status = cbm.READST()
; skipping up to next entry name
}
}
io_error:
cbm.CLRCHN()
cbm.CLOSE(12)
if status and status & $40 == 0
return 0
if @(cx16.r12)==0 {
cx16.r12--
@(cx16.r12)='/'
}
return cx16.r12
sub prepend(str dir) {
if dir[0]=='/' and dir[1]==0
return
cx16.r9L = string.length(dir)
cx16.r12 -= cx16.r9L
sys.memcopy(dir, cx16.r12, cx16.r9L)
cx16.r12--
@(cx16.r12)='/'
}
}
sub relabel(str name) {
; -- change the disk label.
list_filename[0] = 'r'
+5 -9
View File
@@ -6,6 +6,8 @@
floats {
; ---- this block contains C-64 compatible floating point related functions ----
%option no_symbol_prefixing
const float PI = 3.141592653589793
const float TWOPI = 6.283185307179586
@@ -42,7 +44,7 @@ romsub $fe21 = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
romsub $fe24 = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1
romsub $fe27 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 mind the order of the operands
romsub $fe2a = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
romsub $fe2d = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
romsub $fe2d = INT() clobbers(A,X,Y) ; INT() truncates, use ROUND or FADDH first to round instead of trunc
romsub $fe30 = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
romsub $fe33 = NEGOP() clobbers(A) ; switch the sign of fac1 (fac1 = -fac1)
romsub $fe36 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** float in A/Y
@@ -97,14 +99,11 @@ asmsub FREADSA (byte value @A) clobbers(A,X,Y) {
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
%asm {{
phx
sty $c4 ; facmo ($64 on c128)
sta $c5 ; facmo+1 ($65 on c128)
ldx #$90
sec
jsr FLOATC
plx
rts
jmp FLOATC
}}
}
@@ -142,11 +141,8 @@ asmsub FREADUY (ubyte value @Y) {
sub rndf() -> float {
%asm {{
phx
lda #1
jsr RND_0
plx
rts
jmp RND_0
}}
}
+263 -148
View File
@@ -13,16 +13,17 @@
; SCREEN MODE LIST:
; mode 0 = reset back to default text mode
; mode 1 = bitmap 320 x 240 monochrome
; mode 2 = bitmap 320 x 240 x 4c (TODO not yet implemented)
; mode 3 = bitmap 320 x 240 x 16c (TODO not yet implemented)
; mode 2 = bitmap 320 x 240 x 4c (not yet implemented: just use 256c, there's enough vram for that)
; mode 3 = bitmap 320 x 240 x 16c (not yet implemented: just use 256c, there's enough vram for that)
; mode 4 = bitmap 320 x 240 x 256c (like SCREEN $80 but using this api instead of kernal)
; mode 5 = bitmap 640 x 480 monochrome
; mode 6 = bitmap 640 x 480 x 4c
; higher color dephts in highres are not supported due to lack of VRAM
gfx2 {
%option no_symbol_prefixing
; read-only control variables:
ubyte active_mode = 0
uword width = 0
@@ -44,7 +45,7 @@ gfx2 {
height = 240
bpp = 1
}
; TODO modes 2, 3 not yet implemented
; TODO modes 2, 3
4 -> {
; lores 256c
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
@@ -107,7 +108,7 @@ gfx2 {
repeat 240/2/8
cs_innerloop640()
}
; TODO mode 2, 3
; TODO modes 2, 3
4 -> {
; lores 256c
repeat 240/2
@@ -155,8 +156,8 @@ gfx2 {
}
sub horizontal_line(uword x, uword y, uword length, ubyte color) {
ubyte[9] masked_ends = [ 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110, %11111111]
ubyte[9] masked_starts = [ 0, %00000001, %00000011, %00000111, %00001111, %00011111, %00111111, %01111111, %11111111]
ubyte[9] masked_ends = [ 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110, %11111111]
if length==0
return
@@ -167,11 +168,21 @@ gfx2 {
if separate_pixels as uword > length
separate_pixels = lsb(length)
if separate_pixels {
position(x,y)
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
cx16.VERA_DATA0 = cx16.VERA_DATA0 | masked_starts[separate_pixels]
if monochrome_dont_stipple_flag {
position(x,y)
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
if color
cx16.VERA_DATA0 |= masked_starts[separate_pixels]
else
cx16.VERA_DATA0 &= ~masked_starts[separate_pixels]
x += separate_pixels
} else {
repeat separate_pixels {
plot(x, y, color)
x++
}
}
length -= separate_pixels
x += separate_pixels
}
if length {
position(x, y)
@@ -210,8 +221,18 @@ _loop lda length
_done
}}
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
cx16.VERA_DATA0 = cx16.VERA_DATA0 | masked_ends[separate_pixels]
if monochrome_dont_stipple_flag {
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
if color
cx16.VERA_DATA0 |= masked_ends[separate_pixels]
else
cx16.VERA_DATA0 &= ~masked_ends[separate_pixels]
} else {
repeat separate_pixels {
plot(x, y, color)
x++
}
}
}
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off again
}
@@ -220,7 +241,6 @@ _done
position(x, y)
%asm {{
lda color
phx
ldx length+1
beq +
ldy #0
@@ -234,12 +254,11 @@ _done
- sta cx16.VERA_DATA0
dey
bne -
+ plx
+
}}
}
6 -> {
; highres 4c
; TODO also mostly usable for lores 4c?
; highres 4c ....also mostly usable for mode 2, lores 4c?
color &= 3
ubyte[4] colorbits
ubyte ii
@@ -260,7 +279,6 @@ _done
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
phx
ldx x
}}
@@ -283,10 +301,6 @@ _done
+ inx ; next pixel
}}
}
%asm {{
plx
}}
}
}
}
@@ -425,17 +439,17 @@ _done
}
word @zp d = 0
cx16.r13 = true ; 'positive_ix'
cx16.r1L = true ; 'positive_ix'
if dx < 0 {
dx = -dx
cx16.r13 = false
cx16.r1L = false
}
word @zp dx2 = dx*2
word @zp dy2 = dy*2
cx16.r14 = x1 ; internal plot X
if dx >= dy {
if cx16.r13 {
if cx16.r1L {
repeat {
plot(cx16.r14, y1, color)
if cx16.r14==x2
@@ -462,7 +476,7 @@ _done
}
}
else {
if cx16.r13 {
if cx16.r1L {
repeat {
plot(cx16.r14, y1, color)
if y1 == y2
@@ -566,39 +580,29 @@ _done
when active_mode {
1 -> {
; lores monochrome
%asm {{
lda x
eor y
ora monochrome_dont_stipple_flag
and #1
}}
if_nz {
if color {
; solid color or perhaps stipple
%asm {{
lda x
and #7
pha ; xbits
eor y
ora monochrome_dont_stipple_flag
and #1
}}
x /= 8
x += y*(320/8)
if_nz {
mode_1_prepare()
%asm {{
tsb cx16.VERA_DATA0
}}
}
} else {
; only erase
mode_1_prepare()
%asm {{
stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda x+1
sta cx16.VERA_ADDR_M
lda x
sta cx16.VERA_ADDR_L
ply ; xbits
lda bits,y
ldy color
beq +
tsb cx16.VERA_DATA0
bra ++
+ trb cx16.VERA_DATA0
+
trb cx16.VERA_DATA0
}}
}
}
; TODO mode 2,3
; TODO modes 2, 3
4 -> {
; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
@@ -617,41 +621,30 @@ _done
}
5 -> {
; highres monochrome
%asm {{
lda x
eor y
ora monochrome_dont_stipple_flag
and #1
}}
if_nz {
if color {
; solid color or perhaps stipple
%asm {{
lda x
and #7
pha ; xbits
eor y
ora monochrome_dont_stipple_flag
and #1
}}
x /= 8
x += y*(640/8)
if_nz {
mode_5_prepare()
%asm {{
tsb cx16.VERA_DATA0
}}
}
} else {
; only erase
mode_5_prepare()
%asm {{
stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda x+1
sta cx16.VERA_ADDR_M
lda x
sta cx16.VERA_ADDR_L
ply ; xbits
lda bits,y
ldy color
beq +
tsb cx16.VERA_DATA0
bra ++
+ trb cx16.VERA_DATA0
+
trb cx16.VERA_DATA0
}}
}
}
6 -> {
; highres 4c
; TODO also mostly usable for lores 4c?
; highres 4c ....also mostly usable for mode 2, lores 4c?
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
cx16.r2L = lsb(x) & 3 ; xbits
; color &= 3
@@ -677,6 +670,46 @@ _done
}}
}
}
sub mode_1_prepare() {
%asm {{
lda x
and #7
pha ; xbits
}}
x /= 8
x += y*(320/8)
%asm {{
stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda x+1
sta cx16.VERA_ADDR_M
lda x
sta cx16.VERA_ADDR_L
ply ; xbits
lda bits,y
}}
}
sub mode_5_prepare() {
%asm {{
lda x
and #7
pha ; xbits
}}
x /= 8
x += y*(640/8)
%asm {{
stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda x+1
sta cx16.VERA_ADDR_M
lda x
sta cx16.VERA_ADDR_L
ply ; xbits
lda bits,y
}}
}
}
sub pget(uword @zp x, uword y) -> ubyte {
@@ -705,7 +738,7 @@ _done
+
}}
}
; TODO mode 2 and 3
; TODO modes 2, 3
4 -> {
; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
@@ -879,19 +912,18 @@ skip:
}
sub position(uword @zp x, uword y) {
ubyte bank
when active_mode {
1 -> {
; lores monochrome
cx16.r0 = y*(320/8) + x/8
cx16.vaddr(0, cx16.r0, 0, 1)
}
; TODO modes 2,3
; TODO modes 2, 3
4 -> {
; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
bank = lsb(cx16.r1)
cx16.vaddr(bank, cx16.r0, 0, 1)
cx16.r2L = cx16.r1L
cx16.vaddr(cx16.r2L, cx16.r0, 0, 1)
}
5 -> {
; highres monochrome
@@ -901,24 +933,16 @@ skip:
6 -> {
; highres 4c
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
bank = lsb(cx16.r1)
cx16.vaddr(bank, cx16.r0, 0, 1)
cx16.r2L = cx16.r1L
cx16.vaddr(cx16.r2L, cx16.r0, 0, 1)
}
}
}
sub position2(uword @zp x, uword y, bool also_port_1) {
position(x, y)
if also_port_1 {
when active_mode {
1, 5 -> cx16.vaddr(0, cx16.r0, 1, 1)
; TODO modes 2, 3
4, 6 -> {
ubyte bank = lsb(cx16.r1)
cx16.vaddr(bank, cx16.r0, 1, 1)
}
}
}
if also_port_1
cx16.vaddr_clone(0)
}
inline asmsub next_pixel(ubyte color @A) {
@@ -931,13 +955,12 @@ skip:
}}
}
asmsub next_pixels(uword pixels @AY, uword amount @R0) clobbers(A, Y) {
asmsub next_pixels(uword pixels @AY, uword amount @R0) clobbers(A, X, Y) {
; -- sets the next bunch of pixels from a prepared array of bytes.
; for 8 bpp screens this will plot 1 pixel per byte.
; for 1 bpp screens it will plot 8 pixels at once (colors are the bit patterns per byte).
; for 2 bpp screens it will plot 4 pixels at once (colors are the bit patterns per byte).
%asm {{
phx
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldx cx16.r0+1
@@ -959,14 +982,13 @@ skip:
iny
dex
bne -
+ plx
+ rts
}}
}
asmsub set_8_pixels_from_bits(ubyte bits @R0, ubyte oncolor @A, ubyte offcolor @Y) {
asmsub set_8_pixels_from_bits(ubyte bits @R0, ubyte oncolor @A, ubyte offcolor @Y) clobbers(X) {
; this is only useful in 256 color mode where one pixel equals one byte value.
%asm {{
phx
ldx #8
- asl cx16.r0
bcc +
@@ -975,7 +997,6 @@ skip:
+ sty cx16.VERA_DATA0
+ dex
bne -
plx
rts
}}
}
@@ -993,48 +1014,94 @@ skip:
sub text(uword @zp x, uword y, ubyte color, uword sctextptr) {
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
; You must also have called text_charset() first to select and prepare the character set to use.
; NOTE: in monochrome (1bpp) screen modes, x position is currently constrained to multiples of 8 ! TODO allow per-pixel horizontal positioning
; TODO draw whole horizontal spans using vera auto increment if possible, instead of per-character columns
uword chardataptr
ubyte[8] @shared char_bitmap_bytes_left
ubyte[8] @shared char_bitmap_bytes_right
when active_mode {
1, 5 -> {
; monochrome mode, either resolution
cx16.r2 = 40
if active_mode==5
cx16.r2 = 80
while @(sctextptr) {
chardataptr = charset_addr + (@(sctextptr) as uword)*8
cx16.vaddr(charset_bank, chardataptr, 1, 1)
position(x,y)
cx16.r3 = sctextptr
while @(cx16.r3) {
chardataptr = charset_addr + @(cx16.r3) * $0008
; copy the character bitmap into RAM
cx16.vaddr_autoincr(charset_bank, chardataptr, 0, 1)
%asm {{
lda cx16.VERA_ADDR_H
and #%111 ; don't auto-increment, we have to do that manually because of the ora
sta cx16.VERA_ADDR_H
lda color
; pre-shift the bits
lda text.x
and #7
sta P8ZP_SCRATCH_B1
ldy #8
- lda P8ZP_SCRATCH_B1
bne + ; white color, plot normally
lda cx16.VERA_DATA1
eor #255 ; black color, keep only the other pixels
and cx16.VERA_DATA0
bra ++
+ lda cx16.VERA_DATA0
ora cx16.VERA_DATA1
+ sta cx16.VERA_DATA0
lda cx16.VERA_ADDR_L
clc
adc cx16.r2
sta cx16.VERA_ADDR_L
bcc +
inc cx16.VERA_ADDR_M
+ inc x
bne +
inc x+1
+ dey
ldy #0
- lda cx16.VERA_DATA0
stz P8ZP_SCRATCH_REG
ldx P8ZP_SCRATCH_B1
cpx #0
beq +
- lsr a
ror P8ZP_SCRATCH_REG
dex
bne -
+ sta char_bitmap_bytes_left,y
lda P8ZP_SCRATCH_REG
sta char_bitmap_bytes_right,y
iny
cpy #8
bne --
}}
sctextptr++
; left part of shifted char
position2(x, y, true)
set_autoincrs_mode1_or_5()
if color {
%asm {{
ldy #0
- lda char_bitmap_bytes_left,y
ora cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
cpy #8
bne -
}}
} else {
%asm {{
ldy #0
- lda char_bitmap_bytes_left,y
eor #255
and cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
cpy #8
bne -
}}
}
; right part of shifted char
if lsb(x) & 7 {
position2(x+8, y, true)
set_autoincrs_mode1_or_5()
if color {
%asm {{
ldy #0
- lda char_bitmap_bytes_right,y
ora cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
cpy #8
bne -
}}
} else {
%asm {{
ldy #0
- lda char_bitmap_bytes_right,y
eor #255
and cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
cpy #8
bne -
}}
}
}
cx16.r3++
x += 8
}
}
4 -> {
@@ -1046,7 +1113,6 @@ skip:
position(x,y)
y++
%asm {{
phx
ldx color
lda cx16.VERA_DATA1
sta P8ZP_SCRATCH_B1
@@ -1058,7 +1124,6 @@ skip:
+ lda cx16.VERA_DATA0 ; don't write a pixel, but do advance to the next address
+ dey
bne -
plx
}}
}
x+=8
@@ -1070,30 +1135,80 @@ skip:
; hires 4c
; we're going to use a few cx16 registers to make sure every variable is in zeropage in the inner loop.
cx16.r11L = color
cx16.r8 = y
while @(sctextptr) {
chardataptr = charset_addr + (@(sctextptr) as uword)*8
cx16.vaddr(charset_bank, chardataptr, 1, true) ; for reading the chardata from Vera data channel 1
position(x, y) ; only calculated once, we update vera address in the loop instead
cx16.VERA_ADDR_H &= $0f ; no auto increment
repeat 8 {
; TODO rewrite this inner loop partly in assembly
; requires expanding the charbits to 2-bits per pixel (based on color)
; also it's way more efficient to draw whole horizontal spans instead of per-character
cx16.r9L = cx16.vpeek(charset_bank, chardataptr) ; get the 8 horizontal character bits
cx16.r10L = cx16.VERA_DATA1 ; get the next 8 horizontal character bits
cx16.r7 = x
repeat 8 {
cx16.r9L <<= 1
if_cs
plot(cx16.r7, cx16.r8, cx16.r11L)
cx16.r10L <<= 1
if_cs {
cx16.r2L = cx16.r7L & 3 ; xbits
when cx16.r11L & 3 {
1 -> cx16.r12L = gfx2.plot.shiftedleft_4c_1[cx16.r2L]
2 -> cx16.r12L = gfx2.plot.shiftedleft_4c_2[cx16.r2L]
3 -> cx16.r12L = gfx2.plot.shiftedleft_4c_3[cx16.r2L]
else -> cx16.r12L = 0
}
cx16.VERA_DATA0 = cx16.VERA_DATA0 & gfx2.plot.mask4c[cx16.r2L] | cx16.r12L
}
cx16.r7++
if (cx16.r7 & 3) == 0 {
; increment the pixel address by one
%asm {{
stz cx16.VERA_CTRL
clc
lda cx16.VERA_ADDR_L
adc #1
sta cx16.VERA_ADDR_L
lda cx16.VERA_ADDR_M
adc #0
sta cx16.VERA_ADDR_M
lda cx16.VERA_ADDR_H
adc #0
sta cx16.VERA_ADDR_H
}}
}
}
chardataptr++
cx16.r8++
; increment pixel address to the next line
%asm {{
stz cx16.VERA_CTRL
clc
lda cx16.VERA_ADDR_L
adc #(640-8)/4
sta cx16.VERA_ADDR_L
lda cx16.VERA_ADDR_M
adc #0
sta cx16.VERA_ADDR_M
lda cx16.VERA_ADDR_H
adc #0
sta cx16.VERA_ADDR_H
}}
}
x+=8
cx16.r8-=8
sctextptr++
}
}
}
sub set_autoincrs_mode1_or_5() {
; set autoincrements to go to next pixel row (40 or 80 increment)
if active_mode==1 {
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (11<<4)
cx16.VERA_CTRL = 1
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (11<<4)
} else {
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (12<<4)
cx16.VERA_CTRL = 1
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (12<<4)
}
}
}
asmsub cs_innerloop640() clobbers(Y) {
+2
View File
@@ -10,6 +10,8 @@
graphics {
%option no_symbol_prefixing
const uword WIDTH = 320
const ubyte HEIGHT = 240
+18 -17
View File
@@ -2,9 +2,10 @@
; Should you want to restore the default palette, you have to reinitialize the Vera yourself.
palette {
%option no_symbol_prefixing
uword vera_palette_ptr
ubyte c
ubyte cc
sub set_color(ubyte index, uword color) {
vera_palette_ptr = $fa00+(index as uword * 2)
@@ -79,13 +80,13 @@ palette {
sub set_grayscale() {
vera_palette_ptr = $fa00
repeat 16 {
c=0
cc=0
repeat 16 {
cx16.vpoke(1, vera_palette_ptr, c)
cx16.vpoke(1, vera_palette_ptr, cc)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, c)
cx16.vpoke(1, vera_palette_ptr, cc)
vera_palette_ptr++
c += $11
cc += $11
}
}
}
@@ -150,11 +151,11 @@ palette {
sub set_c64pepto() {
vera_palette_ptr = $fa00
repeat 16 {
for c in 0 to 15 {
uword cc = C64_colorpalette_pepto[c]
cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B
for cc in 0 to 15 {
uword ccp = C64_colorpalette_pepto[cc]
cx16.vpoke(1, vera_palette_ptr, lsb(ccp)) ; G, B
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R
cx16.vpoke(1, vera_palette_ptr, msb(ccp)) ; R
vera_palette_ptr++
}
}
@@ -163,11 +164,11 @@ palette {
sub set_c64light() {
vera_palette_ptr = $fa00
repeat 16 {
for c in 0 to 15 {
uword cc = C64_colorpalette_light[c]
cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B
for cc in 0 to 15 {
uword ccp = C64_colorpalette_light[cc]
cx16.vpoke(1, vera_palette_ptr, lsb(ccp)) ; G, B
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R
cx16.vpoke(1, vera_palette_ptr, msb(ccp)) ; R
vera_palette_ptr++
}
}
@@ -176,11 +177,11 @@ palette {
sub set_c64dark() {
vera_palette_ptr = $fa00
repeat 16 {
for c in 0 to 15 {
uword cc = C64_colorpalette_dark[c]
cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B
for cc in 0 to 15 {
uword ccp = C64_colorpalette_dark[cc]
cx16.vpoke(1, vera_palette_ptr, lsb(ccp)) ; G, B
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R
cx16.vpoke(1, vera_palette_ptr, msb(ccp)) ; R
vera_palette_ptr++
}
}
+2
View File
@@ -1,6 +1,8 @@
%import syslib
psg {
%option no_symbol_prefixing
; $1F9C0 - $1F9FF 16 blocks of 4 PSG registers (16 voices)
; 00 frequency word LSB
; 01 frequency word MSB. freqword = HERZ / 0.3725290298461914
+169 -29
View File
@@ -5,6 +5,8 @@ cbm {
; Commodore (CBM) common variables, vectors and kernal routines
%option no_symbol_prefixing
; STROUT --> use txt.print
; CLEARSCR -> use txt.clear_screen
; HOMECRSR -> use txt.home or txt.plot
@@ -51,25 +53,21 @@ romsub $FFF3 = IOBASE() -> uword @ XY ; read base addr
; ---- utility
asmsub STOP2() -> ubyte @A {
asmsub STOP2() clobbers(X) -> ubyte @A {
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
%asm {{
phx
jsr cbm.STOP
beq +
plx
lda #0
rts
+ plx
lda #1
+ lda #1
rts
}}
}
asmsub RDTIM16() -> uword @AY {
asmsub RDTIM16() clobbers(X) -> uword @AY {
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience. Also avoids ram bank issue for irqs.
%asm {{
phx
php
sei
jsr cbm.RDTIM
@@ -79,7 +77,6 @@ asmsub RDTIM16() -> uword @AY {
txa
tay
pla
plx
rts
}}
}
@@ -88,6 +85,8 @@ asmsub RDTIM16() -> uword @AY {
cx16 {
%option no_symbol_prefixing
; irq, system and hardware vectors:
&uword IERROR = $0300
&uword IMAIN = $0302
@@ -324,7 +323,7 @@ cx16 {
romsub $ff4a = close_all(ubyte device @A) clobbers(A,X,Y)
romsub $ff59 = lkupla(ubyte la @A) clobbers(A,X,Y)
romsub $ff5c = lkupsa(ubyte sa @Y) clobbers(A,X,Y)
romsub $ff5f = screen_mode(ubyte mode @A, bool getCurrent @Pc) clobbers(A, X, Y) -> bool @Pc
romsub $ff5f = screen_mode(ubyte mode @A, bool getCurrent @Pc) clobbers(X, Y) -> ubyte @A, bool @Pc ; note: X,Y size result is not supported, use SCREEN or get_screen_mode routine for that
romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr()
; not yet supported: romsub $ff65 = pfkey() clobbers(A,X,Y)
romsub $ff6e = jsrfar() ; following word = address to call, byte after that=rom/ram bank it is in
@@ -403,7 +402,7 @@ romsub $ff53 = joystick_scan() clobbers(A, X, Y)
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
romsub $ff56 = joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX ; alternative to above to not have the hassle to deal with multiple return values
; Audio (bank 10)
; Audio (rom bank 10)
romsub $C04B = psg_init() clobbers(A,X,Y)
romsub $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc ; (re)init YM chip
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc ; load default YM patches
@@ -411,6 +410,25 @@ romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)ini
; TODO: add more of the audio routines?
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)
%asm {{
clc
jmp screen_mode
}}
}
asmsub get_screen_mode() -> byte @A, byte @X, byte @Y {
; -- convenience wrapper for screen_mode() to just get the current mode in A, and size in characters in X+Y
; this does need a piece of inlined asm to call it ans store the result values if you call this from prog8 code
; Note: you can also just do the SEC yourself and simply call screen_mode() directly,
; or use the existing SCREEN kernal routine for just getting the size in characters.
%asm {{
sec
jmp screen_mode
}}
}
asmsub kbdbuf_clear() {
; -- convenience helper routine to clear the keyboard buffer
%asm {{
@@ -431,15 +449,12 @@ asmsub mouse_config2(ubyte shape @A) clobbers (A, X, Y) {
}}
}
asmsub mouse_pos() -> ubyte @A {
asmsub mouse_pos() clobbers(X) -> ubyte @A {
; -- short wrapper around mouse_get() kernal routine:
; -- gets the position of the mouse cursor in cx16.r0 and cx16.r1 (x/y coordinate), returns mouse button status.
%asm {{
phx
ldx #cx16.r0
jsr cx16.mouse_get
plx
rts
jmp cx16.mouse_get
}}
}
@@ -477,7 +492,7 @@ inline asmsub getrambank() -> ubyte @A {
}}
}
asmsub numbanks() -> uword @AY {
asmsub numbanks() clobbers(X) -> uword @AY {
; -- Returns the number of available RAM banks according to the kernal (each bank is 8 Kb).
; Note that the number of such banks can be 256 so a word is returned.
; But just looking at the A register (the LSB of the result word) could suffice if you know that A=0 means 256 banks:
@@ -485,15 +500,13 @@ asmsub numbanks() -> uword @AY {
; Kernal's MEMTOP routine reports 0 in this case but that doesn't mean 'zero banks', instead it means 256 banks,
; as there is no X16 without at least 1 page of banked RAM. So this routine returns 256 instead of 0.
%asm {{
phx
sec
jsr cbm.MEMTOP
ldy #0
cmp #0
bne +
iny
+ plx
rts
+ rts
}}
}
@@ -516,6 +529,8 @@ asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrOrDecrByOne @Y) clobbers(A) {
; -- setup the VERA's data address register 0 or 1
; with optional auto increment or decrement of 1.
; Note that the vaddr_autoincr() and vaddr_autodecr() routines allow to set all possible strides, not just 1.
%asm {{
and #1
pha
@@ -539,6 +554,105 @@ asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrO
}}
}
asmsub vaddr_clone(ubyte port @A) clobbers (A,X,Y) {
; -- clones Vera addresses from the given source port to the other one.
; leaves CTRL on the destination port.
%asm {{
sta VERA_CTRL
ldx VERA_ADDR_L
ldy VERA_ADDR_H
phy
ldy VERA_ADDR_M
eor #1
sta VERA_CTRL
stx VERA_ADDR_L
sty VERA_ADDR_M
ply
sty VERA_ADDR_H
rts
}}
}
asmsub vaddr_autoincr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, uword autoIncrAmount @R2) clobbers(A,Y) {
; -- setup the VERA's data address register 0 or 1
; including setting up optional auto increment amount.
; Specifiying an unsupported amount results in amount of zero. See the Vera docs about what amounts are possible.
%asm {{
jsr _setup
lda cx16.r2H
ora cx16.r2L
beq +
jsr _determine_incr_bits
+ ora P8ZP_SCRATCH_REG
sta cx16.VERA_ADDR_H
rts
_setup and #1
sta P8ZP_SCRATCH_REG
lda cx16.r1
and #1
sta cx16.VERA_CTRL
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
rts
_determine_incr_bits
lda cx16.r2H
bne _large
lda cx16.r2L
ldy #13
- cmp _strides_lsb,y
beq +
dey
bpl -
+ tya
asl a
asl a
asl a
asl a
rts
_large ora cx16.r2L
cmp #1 ; 256
bne +
lda #9<<4
rts
+ cmp #2 ; 512
bne +
lda #10<<4
rts
+ cmp #65 ; 320
bne +
lda #14<<4
rts
+ cmp #130 ; 640
bne +
lda #15<<4
rts
+ lda #0
rts
_strides_lsb .byte 0,1,2,4,8,16,32,64,128,255,255,40,80,160,255,255
}}
}
asmsub vaddr_autodecr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, uword autoDecrAmount @R2) clobbers(A,Y) {
; -- setup the VERA's data address register 0 or 1
; including setting up optional auto decrement amount.
; Specifiying an unsupported amount results in amount of zero. See the Vera docs about what amounts are possible.
%asm {{
jsr vaddr_autoincr._setup
lda cx16.r2H
ora cx16.r2L
beq +
jsr vaddr_autoincr._determine_incr_bits
ora #%00001000 ; autodecrement
+ ora P8ZP_SCRATCH_REG
sta cx16.VERA_ADDR_H
rts
}}
}
asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
; -- write a single byte to VERA's video memory
; note: inefficient when writing multiple sequential bytes!
@@ -629,6 +743,33 @@ asmsub vpoke_mask(ubyte bank @A, uword address @R0, ubyte mask @X, ubyte value @
}}
}
asmsub save_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda cx16.r0,y
sta _cx16_vreg_storage,y
dey
bpl -
rts
_cx16_vreg_storage
.word 0,0,0,0,0,0,0,0
.word 0,0,0,0,0,0,0,0
}}
}
asmsub restore_virtual_registers() clobbers(A,Y) {
%asm {{
ldy #31
- lda save_virtual_registers._cx16_vreg_storage,y
sta cx16.r0,y
dey
bpl -
rts
}}
}
asmsub save_vera_context() clobbers(A) {
; -- use this at the start of your IRQ handler if it uses Vera registers, to save the state
%asm {{
@@ -683,6 +824,8 @@ asmsub restore_vera_context() clobbers(A) {
sys {
; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 16 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
asmsub init_system() {
@@ -712,12 +855,13 @@ asmsub init_system() {
lda #$90 ; black
jsr cbm.CHROUT
lda #1
sta $00 ; select ram bank 1
jsr cbm.CHROUT ; swap fg/bg
lda #$9e ; yellow
jsr cbm.CHROUT
lda #147 ; clear screen
jsr cbm.CHROUT
lda #PROG8_VARSHIGH_RAMBANK
sta $00 ; select ram bank
lda #0
tax
tay
@@ -735,6 +879,8 @@ asmsub init_system_phase2() {
sta restore_irq._orig_irqvec
lda cx16.CINV+1
sta restore_irq._orig_irqvec+1
lda #PROG8_VARSHIGH_RAMBANK
sta $00 ; select ram bank
cli
rts
}}
@@ -800,10 +946,6 @@ _irq_handler_init
sta IRQ_SCRATCH_ZPWORD2
lda P8ZP_SCRATCH_W2+1
sta IRQ_SCRATCH_ZPWORD2+1
; Set X to the bottom 32 bytes of the evaluation stack, to HOPEFULLY not clobber it.
; This leaves 128-32=96 stack entries for the main program, and 32 stack entries for the IRQ handler.
; We assume IRQ handlers don't contain complex expressions taking up more than that.
ldx #32
cld
rts
@@ -906,8 +1048,8 @@ asmsub set_rasterline(uword line @AY) {
%asm {{
sei
ldx #$42
ldy #1
tya
ldy #2
lda #0
jsr cx16.i2c_write_byte
bra *
}}
@@ -923,19 +1065,17 @@ asmsub set_rasterline(uword line @AY) {
void cx16.i2c_write_byte($42, $05, activity)
}
asmsub wait(uword jiffies @AY) {
asmsub wait(uword jiffies @AY) clobbers(X) {
; --- wait approximately the given number of jiffies (1/60th seconds) (N or N+1)
; note: the system irq handler has to be active for this to work as it depends on the system jiffy clock
; note: this routine cannot be used from inside a irq handler
%asm {{
phx
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
_loop lda P8ZP_SCRATCH_W1
ora P8ZP_SCRATCH_W1+1
bne +
plx
rts
+ sei
+29 -66
View File
@@ -6,6 +6,8 @@
txt {
%option no_symbol_prefixing
const ubyte DEFAULT_WIDTH = 80
const ubyte DEFAULT_HEIGHT = 60
@@ -40,11 +42,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
}}
}
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A, X) {
; ---- fill the character screen with the given fill character and character color.
%asm {{
sty _ly+1
phx
pha
jsr cbm.SCREEN ; get dimensions in X/Y
txa
@@ -73,8 +74,7 @@ _ly ldy #1 ; modified
stz cx16.VERA_ADDR_L
inc cx16.VERA_ADDR_M ; next line
bra _lx
+ plx
rts
+ rts
set_vera_textmatrix_addresses:
stz cx16.VERA_CTRL
@@ -88,11 +88,10 @@ set_vera_textmatrix_addresses:
}}
}
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
asmsub clear_screenchars (ubyte char @ A) clobbers(X, Y) {
; ---- clear the character screen with the given fill character (leaves colors)
; (assumes screen matrix is at the default address)
%asm {{
phx
pha
jsr cbm.SCREEN ; get dimensions in X/Y
txa
@@ -114,16 +113,14 @@ _lx ldx #0 ; modified
stz cx16.VERA_ADDR_L
inc cx16.VERA_ADDR_M ; next line
bra _lx
+ plx
rts
+ rts
}}
}
asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
asmsub clear_screencolors (ubyte color @ A) clobbers(X, Y) {
; ---- clear the character screen colors with the given color (leaves characters).
; (assumes color matrix is at the default address)
%asm {{
phx
sta _la+1
jsr cbm.SCREEN ; get dimensions in X/Y
txa
@@ -148,8 +145,7 @@ _la lda #0 ; modified
sta cx16.VERA_ADDR_L
inc cx16.VERA_ADDR_M ; next line
bra _lx
+ plx
rts
+ rts
}}
}
@@ -190,11 +186,10 @@ sub iso_off() {
}
asmsub scroll_left() clobbers(A, Y) {
asmsub scroll_left() clobbers(A, X, Y) {
; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself
%asm {{
phx
jsr cbm.SCREEN
dex
stx _lx+1
@@ -231,16 +226,14 @@ _lx ldx #0 ; modified
lda #0
sta cx16.VERA_CTRL
plx
rts
}}
}
asmsub scroll_right() clobbers(A) {
asmsub scroll_right() clobbers(A,X,Y) {
; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself
%asm {{
phx
jsr cbm.SCREEN
dex
stx _lx+1
@@ -285,16 +278,14 @@ _lx ldx #0 ; modified
lda #0
sta cx16.VERA_CTRL
plx
rts
}}
}
asmsub scroll_up() clobbers(A, Y) {
asmsub scroll_up() clobbers(A, X, Y) {
; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself
%asm {{
phx
jsr cbm.SCREEN
stx _nextline+1
dey
@@ -335,16 +326,14 @@ _nextline
+ lda #0
sta cx16.VERA_CTRL
plx
rts
}}
}
asmsub scroll_down() clobbers(A, Y) {
asmsub scroll_down() clobbers(A, X, Y) {
; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself
%asm {{
phx
jsr cbm.SCREEN
stx _nextline+1
dey
@@ -391,7 +380,6 @@ _nextline
+ lda #0
sta cx16.VERA_CTRL
plx
rts
}}
}
@@ -416,10 +404,9 @@ asmsub print (str text @ AY) clobbers(A,Y) {
}}
}
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
asmsub print_ub0 (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
%asm {{
phx
jsr conv.ubyte2decimal
pha
tya
@@ -427,16 +414,13 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
pla
jsr cbm.CHROUT
txa
jsr cbm.CHROUT
plx
rts
jmp cbm.CHROUT
}}
}
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
asmsub print_ub (ubyte value @ A) clobbers(A,X,Y) {
; ---- print the ubyte in A in decimal form, without left padding 0s
%asm {{
phx
jsr conv.ubyte2decimal
_print_byte_digits
pha
@@ -452,16 +436,13 @@ _print_byte_digits
beq _ones
jsr cbm.CHROUT
_ones txa
jsr cbm.CHROUT
plx
rts
jmp cbm.CHROUT
}}
}
asmsub print_b (byte value @ A) clobbers(A,Y) {
asmsub print_b (byte value @ A) clobbers(A,X,Y) {
; ---- print the byte in A in decimal form, without left padding 0s
%asm {{
phx
pha
cmp #0
bpl +
@@ -473,10 +454,9 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
}}
}
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
phx
bcc +
pha
lda #'$'
@@ -485,16 +465,13 @@ asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
+ jsr conv.ubyte2hex
jsr cbm.CHROUT
tya
jsr cbm.CHROUT
plx
rts
jmp cbm.CHROUT
}}
}
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
phx
sta P8ZP_SCRATCH_B1
bcc +
lda #'%'
@@ -507,12 +484,11 @@ asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
+ jsr cbm.CHROUT
dey
bne -
plx
rts
}}
}
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
pha
@@ -524,7 +500,7 @@ asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
}}
}
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,X,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
@@ -537,10 +513,9 @@ asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
}}
}
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
%asm {{
phx
jsr conv.uword2decimal
ldy #0
- lda conv.uword2decimal.decTenThousands,y
@@ -548,17 +523,14 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
jsr cbm.CHROUT
iny
bne -
+ plx
rts
+ rts
}}
}
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
asmsub print_uw (uword value @ AY) clobbers(A,X,Y) {
; ---- print the uword in A/Y in decimal form, without left padding 0s
%asm {{
phx
jsr conv.uword2decimal
plx
ldy #0
- lda conv.uword2decimal.decTenThousands,y
beq _allzero
@@ -579,7 +551,7 @@ _allzero
}}
}
asmsub print_w (word value @ AY) clobbers(A,Y) {
asmsub print_w (word value @ AY) clobbers(A,X,Y) {
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
%asm {{
cpy #0
@@ -702,12 +674,11 @@ asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
}}
}
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
; ---- set char+color at the given position on the screen
; note: color handling is the same as on the C64: it only sets the foreground color and leaves the background color as is.
; Use setcc2 if you want Cx-16 specific feature of setting both Bg+Fg colors (is faster as well).
%asm {{
phx
lda column
asl a
tax
@@ -730,7 +701,6 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
and #$f0
ora P8ZP_SCRATCH_B1
sta cx16.VERA_DATA0
plx
rts
}}
}
@@ -740,7 +710,6 @@ sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
; note: on the CommanderX16 this allows you to set both Fg and Bg colors;
; use the high nybble in A to set the Bg color! Is a bit faster than setcc() too.
%asm {{
phx
lda column
asl a
tax
@@ -758,20 +727,14 @@ sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
inc cx16.VERA_ADDR_L
lda colors
sta cx16.VERA_DATA0
plx
rts
}}
}
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
; ---- safe wrapper around PLOT kernal routine, to save the X register.
asmsub plot (ubyte col @ Y, ubyte row @ X) {
%asm {{
phx
tax
clc
jsr cbm.PLOT
plx
rts
jmp cbm.PLOT
}}
}
+2
View File
@@ -3,6 +3,8 @@
%import textio
cx16logo {
%option no_symbol_prefixing
sub logo_at(ubyte column, ubyte row) {
uword strptr
for strptr in logo_lines {
+11 -7
View File
@@ -5,6 +5,7 @@
%import syslib
diskio {
%option no_symbol_prefixing
ubyte drivenumber = 8
@@ -84,8 +85,8 @@ io_error:
if_cs
goto io_error
repeat 6 {
void cbm.CHRIN() ; skip the 6 prologue bytes
while cbm.CHRIN()!='"' {
; skip up to entry name
}
if cbm.READST()!=0
goto io_error
@@ -93,8 +94,14 @@ io_error:
cx16.r0 = &list_filename
repeat {
@(cx16.r0) = cbm.CHRIN()
if @(cx16.r0)==0
if @(cx16.r0)=='"' {
@(cx16.r0) = ' '
while @(cx16.r0)==' ' and cx16.r0>=&diskio.list_filename {
@(cx16.r0) = 0
cx16.r0--
}
break
}
cx16.r0++
}
okay = true
@@ -448,13 +455,11 @@ io_error:
sta P8ZP_SCRATCH_W1
lda address+1
sta P8ZP_SCRATCH_W1+1
stx P8ZP_SCRATCH_REG
lda #<P8ZP_SCRATCH_W1
ldx end_address
ldy end_address+1
jsr cbm.SAVE
php
ldx P8ZP_SCRATCH_REG
plp
}}
@@ -481,7 +486,6 @@ io_error:
secondary = 0
cbm.SETLFS(1, drivenumber, secondary)
%asm {{
stx P8ZP_SCRATCH_REG
lda #0
ldx address_override
ldy address_override+1
@@ -489,7 +493,7 @@ io_error:
bcs +
stx cx16.r1
sty cx16.r1+1
+ ldx P8ZP_SCRATCH_REG
+
}}
cbm.CLRCHN()
+14 -49
View File
@@ -1,11 +1,10 @@
floats {
; the floating point functions shared across compiler targets
%option merge
%option merge, no_symbol_prefixing
sub print_f(float value) {
; ---- prints the floating point value (without a newline).
%asm {{
stx floats_store_reg
lda #<value
ldy #>value
jsr MOVFM ; load float into fac1
@@ -18,8 +17,7 @@ sub print_f(float value) {
jsr cbm.CHROUT
iny
bne -
+ ldx floats_store_reg
rts
+ rts
}}
}
@@ -44,10 +42,7 @@ sub sin(float angle) -> float {
lda #<angle
ldy #>angle
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr SIN
ldx P8ZP_SCRATCH_REG
rts
jmp SIN
}}
}
@@ -56,9 +51,7 @@ sub cos(float angle) -> float {
lda #<angle
ldy #>angle
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr COS
ldx P8ZP_SCRATCH_REG
jmp COS
rts
}}
}
@@ -68,10 +61,7 @@ sub tan(float value) -> float {
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr TAN
ldx P8ZP_SCRATCH_REG
rts
jmp TAN
}}
}
@@ -80,10 +70,7 @@ sub atan(float value) -> float {
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr ATN
ldx P8ZP_SCRATCH_REG
rts
jmp ATN
}}
}
@@ -92,10 +79,7 @@ sub ln(float value) -> float {
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr LOG
ldx P8ZP_SCRATCH_REG
rts
jmp LOG
}}
}
@@ -104,15 +88,12 @@ sub log2(float value) -> float {
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr LOG
jsr MOVEF
lda #<FL_LOG2_const
ldy #>FL_LOG2_const
jsr MOVFM
jsr FDIVT
ldx P8ZP_SCRATCH_REG
rts
jmp FDIVT
}}
}
@@ -122,12 +103,9 @@ sub rad(float angle) -> float {
lda #<angle
ldy #>angle
jsr MOVFM
stx P8ZP_SCRATCH_REG
lda #<_pi_div_180
ldy #>_pi_div_180
jsr FMULT
ldx P8ZP_SCRATCH_REG
rts
jmp FMULT
_pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
}}
}
@@ -138,11 +116,9 @@ sub deg(float angle) -> float {
lda #<angle
ldy #>angle
jsr MOVFM
stx P8ZP_SCRATCH_REG
lda #<_one_over_pi_div_180
ldy #>_one_over_pi_div_180
jsr FMULT
ldx P8ZP_SCRATCH_REG
jmp FMULT
rts
_one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
}}
@@ -153,11 +129,8 @@ sub round(float value) -> float {
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr FADDH
jsr INT
ldx P8ZP_SCRATCH_REG
rts
jmp INT
}}
}
@@ -166,10 +139,7 @@ sub floor(float value) -> float {
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr INT
ldx P8ZP_SCRATCH_REG
rts
jmp INT
}}
}
@@ -179,7 +149,6 @@ sub ceil(float value) -> float {
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
ldx #<fmath_float1
ldy #>fmath_float1
jsr MOVMF
@@ -192,8 +161,7 @@ sub ceil(float value) -> float {
lda #<FL_ONE_const
ldy #>FL_ONE_const
jsr FADD
+ ldx P8ZP_SCRATCH_REG
rts
+ rts
}}
}
@@ -202,14 +170,11 @@ sub rndseedf(float seed) {
seed = -seed ; make sure fp seed is always negative
%asm {{
stx floats_store_reg
lda #<seed
ldy #>seed
jsr MOVFM ; load float into fac1
lda #-1
jsr floats.RND
ldx floats_store_reg
rts
jmp floats.RND
}}
}
+56 -744
View File
@@ -7,9 +7,6 @@
;
math_store_reg .byte 0 ; temporary storage
multiply_bytes .proc
; -- multiply 2 bytes A and Y, result as byte in A (signed or unsigned)
sta P8ZP_SCRATCH_B1 ; num1
@@ -30,7 +27,6 @@ multiply_bytes_into_word .proc
; -- multiply 2 bytes A and Y, result as word in A/Y (unsigned)
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
stx math_store_reg
lda #0
ldx #8
lsr P8ZP_SCRATCH_B1
@@ -43,7 +39,6 @@ multiply_bytes_into_word .proc
bne -
tay
lda P8ZP_SCRATCH_B1
ldx math_store_reg
rts
.pend
@@ -51,12 +46,10 @@ multiply_bytes_into_word .proc
multiply_words .proc
; -- multiply two 16-bit words into a 32-bit result (signed and unsigned)
; input: A/Y = first 16-bit number, P8ZP_SCRATCH_W1 in ZP = second 16-bit number
; output: multiply_words.result 4-bytes/32-bits product, LSB order (low-to-high)
; clobbers: A
; output: multiply_words.result 4-bytes/32-bits product, LSB order (low-to-high) low 16 bits also in AY.
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
stx P8ZP_SCRATCH_REG
mult16 lda #0
sta result+2 ; clear upper bits of product
@@ -78,7 +71,8 @@ mult16 lda #0
ror result
dex
bne -
ldx P8ZP_SCRATCH_REG
lda result
ldy result+1
rts
result .byte 0,0,0,0
@@ -123,7 +117,6 @@ divmod_ub_asm .proc
; division by zero will result in quotient = 255 and remainder = original number
sty P8ZP_SCRATCH_REG
sta P8ZP_SCRATCH_B1
stx math_store_reg
lda #0
ldx #8
@@ -136,7 +129,6 @@ divmod_ub_asm .proc
dex
bne -
ldy P8ZP_SCRATCH_B1
ldx math_store_reg
rts
.pend
@@ -196,7 +188,6 @@ result = dividend ;save memory by reusing divident to store the result
sta _divisor
sty _divisor+1
stx P8ZP_SCRATCH_REG
lda #0 ;preset remainder to 0
sta remainder
sta remainder+1
@@ -223,7 +214,6 @@ result = dividend ;save memory by reusing divident to store the result
lda result
ldy result+1
ldx P8ZP_SCRATCH_REG
rts
_divisor .word 0
.pend
@@ -256,501 +246,6 @@ b1=*+1
randbyte = randword ; -- 8 bit pseudo random number generator into A (by just reusing randword)
; ----------- optimized multiplications (stack) : ---------
stack_mul_byte_3 .proc
; X + X*2
lda P8ESTACK_LO+1,x
asl a
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_3 .proc
; W*2 + W
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_REG
lda P8ESTACK_LO+1,x
asl a
rol P8ZP_SCRATCH_REG
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_REG
adc P8ESTACK_HI+1,x
sta P8ESTACK_HI+1,x
rts
.pend
stack_mul_byte_5 .proc
; X*4 + X
lda P8ESTACK_LO+1,x
asl a
asl a
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_5 .proc
; W*4 + W
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_REG
lda P8ESTACK_LO+1,x
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_REG
adc P8ESTACK_HI+1,x
sta P8ESTACK_HI+1,x
rts
.pend
stack_mul_byte_6 .proc
; (X*2 + X)*2
lda P8ESTACK_LO+1,x
asl a
clc
adc P8ESTACK_LO+1,x
asl a
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_6 .proc
; (W*2 + W)*2
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_REG
lda P8ESTACK_LO+1,x
asl a
rol P8ZP_SCRATCH_REG
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_REG
adc P8ESTACK_HI+1,x
asl P8ESTACK_LO+1,x
rol a
sta P8ESTACK_HI+1,x
rts
.pend
stack_mul_byte_7 .proc
; X*8 - X
lda P8ESTACK_LO+1,x
asl a
asl a
asl a
sec
sbc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_7 .proc
; W*8 - W
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_REG
lda P8ESTACK_LO+1,x
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
sec
sbc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_REG
sbc P8ESTACK_HI+1,x
sta P8ESTACK_HI+1,x
rts
.pend
stack_mul_byte_9 .proc
; X*8 + X
lda P8ESTACK_LO+1,x
asl a
asl a
asl a
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_9 .proc
; W*8 + W
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_REG
lda P8ESTACK_LO+1,x
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_REG
adc P8ESTACK_HI+1,x
sta P8ESTACK_HI+1,x
rts
.pend
stack_mul_byte_10 .proc
; (X*4 + X)*2
lda P8ESTACK_LO+1,x
asl a
asl a
clc
adc P8ESTACK_LO+1,x
asl a
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_10 .proc
; (W*4 + W)*2
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_REG
lda P8ESTACK_LO+1,x
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_REG
adc P8ESTACK_HI+1,x
asl P8ESTACK_LO+1,x
rol a
sta P8ESTACK_HI+1,x
rts
.pend
stack_mul_byte_11 .proc
; (X*2 + X)*4 - X
lda P8ESTACK_LO+1,x
asl a
clc
adc P8ESTACK_LO+1,x
asl a
asl a
sec
sbc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
rts
.pend
; mul_word_11 is skipped (too much code)
stack_mul_byte_12 .proc
; (X*2 + X)*4
lda P8ESTACK_LO+1,x
asl a
clc
adc P8ESTACK_LO+1,x
asl a
asl a
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_12 .proc
; (W*2 + W)*4
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_REG
lda P8ESTACK_LO+1,x
asl a
rol P8ZP_SCRATCH_REG
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_REG
adc P8ESTACK_HI+1,x
asl P8ESTACK_LO+1,x
rol a
asl P8ESTACK_LO+1,x
rol a
sta P8ESTACK_HI+1,x
rts
.pend
stack_mul_byte_13 .proc
; (X*2 + X)*4 + X
lda P8ESTACK_LO+1,x
asl a
clc
adc P8ESTACK_LO+1,x
asl a
asl a
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
rts
.pend
; mul_word_13 is skipped (too much code)
stack_mul_byte_14 .proc
; (X*8 - X)*2
lda P8ESTACK_LO+1,x
asl a
asl a
asl a
sec
sbc P8ESTACK_LO+1,x
asl a
sta P8ESTACK_LO+1,x
rts
.pend
; mul_word_14 is skipped (too much code)
stack_mul_byte_15 .proc
; X*16 - X
lda P8ESTACK_LO+1,x
asl a
asl a
asl a
asl a
sec
sbc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_15 .proc
; W*16 - W
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_REG
lda P8ESTACK_LO+1,x
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
sec
sbc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_REG
sbc P8ESTACK_HI+1,x
sta P8ESTACK_HI+1,x
rts
.pend
stack_mul_byte_20 .proc
; (X*4 + X)*4
lda P8ESTACK_LO+1,x
asl a
asl a
clc
adc P8ESTACK_LO+1,x
asl a
asl a
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_20 .proc
; (W*4 + W)*4
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_REG
lda P8ESTACK_LO+1,x
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_REG
adc P8ESTACK_HI+1,x
asl P8ESTACK_LO+1,x
rol a
asl P8ESTACK_LO+1,x
rol a
sta P8ESTACK_HI+1,x
rts
.pend
stack_mul_byte_25 .proc
; (X*2 + X)*8 + X
lda P8ESTACK_LO+1,x
asl a
clc
adc P8ESTACK_LO+1,x
asl a
asl a
asl a
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_25 .proc
; W = (W*2 + W) *8 + W
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_W1+1
lda P8ESTACK_LO+1,x
asl a
rol P8ZP_SCRATCH_W1+1
clc
adc P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_W1
lda P8ZP_SCRATCH_W1+1
adc P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_W1+1
lda P8ZP_SCRATCH_W1
asl a
rol P8ZP_SCRATCH_W1+1
asl a
rol P8ZP_SCRATCH_W1+1
asl a
rol P8ZP_SCRATCH_W1+1
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_W1+1
adc P8ESTACK_HI+1,x
sta P8ESTACK_HI+1,x
rts
.pend
stack_mul_byte_40 .proc
lda P8ESTACK_LO+1,x
and #7
tay
lda mul_byte_40._forties,y
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_40 .proc
; (W*4 + W)*8
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_REG
lda P8ESTACK_LO+1,x
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
clc
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_REG
adc P8ESTACK_HI+1,x
asl P8ESTACK_LO+1,x
rol a
asl P8ESTACK_LO+1,x
rol a
asl P8ESTACK_LO+1,x
rol a
sta P8ESTACK_HI+1,x
rts
.pend
stack_mul_byte_50 .proc
lda P8ESTACK_LO+1,x
and #7
tay
lda mul_byte_50._fifties, y
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_50 .proc
; W = W * 25 * 2
jsr stack_mul_word_25
asl P8ESTACK_LO+1,x
rol P8ESTACK_HI+1,x
rts
.pend
stack_mul_byte_80 .proc
lda P8ESTACK_LO+1,x
and #3
tay
lda mul_byte_80._eighties, y
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_80 .proc
; W = W * 40 * 2
jsr stack_mul_word_40
asl P8ESTACK_LO+1,x
rol P8ESTACK_HI+1,x
rts
.pend
stack_mul_byte_100 .proc
lda P8ESTACK_LO+1,x
and #3
tay
lda mul_byte_100._hundreds, y
sta P8ESTACK_LO+1,x
rts
.pend
stack_mul_word_100 .proc
; W = W * 25 * 4
jsr stack_mul_word_25
asl P8ESTACK_LO+1,x
rol P8ESTACK_HI+1,x
asl P8ESTACK_LO+1,x
rol P8ESTACK_HI+1,x
rts
.pend
stack_mul_word_320 .proc
; stackW = stackLo * 256 + stackLo * 64 (stackHi doesn't matter)
ldy P8ESTACK_LO+1,x
lda #0
sta P8ESTACK_HI+1,x
tya
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
sta P8ESTACK_LO+1,x
tya
clc
adc P8ESTACK_HI+1,x
sta P8ESTACK_HI+1,x
rts
.pend
stack_mul_word_640 .proc
; stackW = (stackLo * 2 * 320) (stackHi doesn't matter)
asl P8ESTACK_LO+1,x
jmp stack_mul_word_320
.pend
; ----------- optimized multiplications (in-place A (byte) and ?? (word)) : ---------
mul_byte_3 .proc
; A = A + A*2
@@ -1251,250 +746,69 @@ mul_word_640 .proc
; ----------- end optimized multiplications -----------
; bit shifts.
; anything below 3 is done inline. anything above 7 is done via other optimizations.
shift_left_w_7 .proc
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_B1
lda P8ESTACK_LO+1,x
asl a
rol P8ZP_SCRATCH_B1
_shift6 asl a
rol P8ZP_SCRATCH_B1
_shift5 asl a
rol P8ZP_SCRATCH_B1
_shift4 asl a
rol P8ZP_SCRATCH_B1
_shift3 asl a
rol P8ZP_SCRATCH_B1
asl a
rol P8ZP_SCRATCH_B1
asl a
rol P8ZP_SCRATCH_B1
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_B1
sta P8ESTACK_HI+1,x
rts
.pend
shift_left_w_6 .proc
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_B1
lda P8ESTACK_LO+1,x
jmp shift_left_w_7._shift6
.pend
shift_left_w_5 .proc
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_B1
lda P8ESTACK_LO+1,x
jmp shift_left_w_7._shift5
.pend
shift_left_w_4 .proc
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_B1
lda P8ESTACK_LO+1,x
jmp shift_left_w_7._shift4
.pend
shift_left_w_3 .proc
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_B1
lda P8ESTACK_LO+1,x
jmp shift_left_w_7._shift3
.pend
shift_left_w .proc
; -- variable number of shifts left
inx
ldy P8ESTACK_LO,x
bne _shift
rts
_shift asl P8ESTACK_LO+1,x
rol P8ESTACK_HI+1,x
dey
bne _shift
rts
.pend
shift_right_uw .proc
; -- uword variable number of shifts right
inx
ldy P8ESTACK_LO,x
bne _shift
rts
_shift lsr P8ESTACK_HI+1,x
ror P8ESTACK_LO+1,x
dey
bne _shift
rts
.pend
shift_right_uw_7 .proc
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_B1
lda P8ESTACK_HI+1,x
lsr a
ror P8ZP_SCRATCH_B1
_shift6 lsr a
ror P8ZP_SCRATCH_B1
_shift5 lsr a
ror P8ZP_SCRATCH_B1
_shift4 lsr a
ror P8ZP_SCRATCH_B1
_shift3 lsr a
ror P8ZP_SCRATCH_B1
lsr a
ror P8ZP_SCRATCH_B1
lsr a
ror P8ZP_SCRATCH_B1
sta P8ESTACK_HI+1,x
lda P8ZP_SCRATCH_B1
sta P8ESTACK_LO+1,x
rts
.pend
shift_right_uw_6 .proc
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_B1
lda P8ESTACK_HI+1,x
jmp shift_right_uw_7._shift6
.pend
shift_right_uw_5 .proc
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_B1
lda P8ESTACK_HI+1,x
jmp shift_right_uw_7._shift5
.pend
shift_right_uw_4 .proc
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_B1
lda P8ESTACK_HI+1,x
jmp shift_right_uw_7._shift4
.pend
shift_right_uw_3 .proc
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_B1
lda P8ESTACK_HI+1,x
jmp shift_right_uw_7._shift3
.pend
shift_right_w_7 .proc
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_W1
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_W1+1
asl a
ror P8ZP_SCRATCH_W1+1
ror P8ZP_SCRATCH_W1
lda P8ZP_SCRATCH_W1+1
_shift6 asl a
ror P8ZP_SCRATCH_W1+1
ror P8ZP_SCRATCH_W1
lda P8ZP_SCRATCH_W1+1
_shift5 asl a
ror P8ZP_SCRATCH_W1+1
ror P8ZP_SCRATCH_W1
lda P8ZP_SCRATCH_W1+1
_shift4 asl a
ror P8ZP_SCRATCH_W1+1
ror P8ZP_SCRATCH_W1
lda P8ZP_SCRATCH_W1+1
_shift3 asl a
ror P8ZP_SCRATCH_W1+1
ror P8ZP_SCRATCH_W1
lda P8ZP_SCRATCH_W1+1
asl a
ror P8ZP_SCRATCH_W1+1
ror P8ZP_SCRATCH_W1
lda P8ZP_SCRATCH_W1+1
asl a
ror P8ZP_SCRATCH_W1+1
ror P8ZP_SCRATCH_W1
lda P8ZP_SCRATCH_W1
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_W1+1
sta P8ESTACK_HI+1,x
rts
.pend
shift_right_w_6 .proc
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_W1
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_W1+1
jmp shift_right_w_7._shift6
.pend
shift_right_w_5 .proc
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_W1
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_W1+1
jmp shift_right_w_7._shift5
.pend
shift_right_w_4 .proc
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_W1
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_W1+1
jmp shift_right_w_7._shift4
.pend
shift_right_w_3 .proc
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_W1
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_W1+1
jmp shift_right_w_7._shift3
.pend
shift_right_w .proc
; -- signed word variable number of shifts right
inx
ldy P8ESTACK_LO,x
bne _shift
rts
_shift lda P8ESTACK_HI+1,x
asl a
ror P8ESTACK_HI+1,x
ror P8ESTACK_LO+1,x
dey
bne _shift
rts
.pend
; support for bit shifting that is too large to be unrolled:
lsr_byte_A .proc
; -- lsr signed byte in A times the value in Y (assume >0)
; -- lsr signed byte in A times the value in Y (>1)
cmp #0
bmi _negative
bpl lsr_ubyte_A
- sec
ror a
dey
bne -
rts
.pend
lsr_ubyte_A .proc
; -- lsr unsigned byte in A times the value in Y (>1)
- lsr a
dey
bne -
rts
_negative lsr a
ora #$80
.pend
asl_byte_A .proc
; -- asl any byte in A times the value in Y (>1)
- asl a
dey
bne -
rts
.pend
lsr_word_AY .proc
; -- lsr signed word in AY times the value in X (>1)
cpy #0
bpl lsr_uword_AY
sty P8ZP_SCRATCH_B1
_negative sec
ror P8ZP_SCRATCH_B1
ror a
dex
bne _negative
ldy P8ZP_SCRATCH_B1
rts
.pend
lsr_uword_AY .proc
; -- lsr unsigned word in AY times the value in X (>1)
sty P8ZP_SCRATCH_B1
- lsr P8ZP_SCRATCH_B1
ror a
dex
bne -
ldy P8ZP_SCRATCH_B1
rts
.pend
asl_word_AY .proc
; -- asl any word in AY times the value in X (>1)
sty P8ZP_SCRATCH_B1
- asl a
rol P8ZP_SCRATCH_B1
dex
bne -
ldy P8ZP_SCRATCH_B1
rts
.pend
@@ -1524,7 +838,6 @@ tempsq = P8ZP_SCRATCH_B1 ; temp byte for intermediate result
sta numberl
sty numberh
stx P8ZP_SCRATCH_REG
lda #$00 ; clear a
sta squarel ; clear square low byte
@@ -1562,7 +875,6 @@ _nosqadd:
lda squarel
ldy squareh
ldx P8ZP_SCRATCH_REG
rts
.pend
+294
View File
@@ -1,6 +1,8 @@
; Internal Math library routines - always included by the compiler
math {
%option no_symbol_prefixing
%asminclude "library:math.asm"
asmsub sin8u(ubyte angle @A) clobbers(Y) -> ubyte @A {
@@ -95,4 +97,296 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
rts
}}
}
sub direction_sc(byte x1, byte y1, byte x2, byte y2) -> ubyte {
; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A.
cx16.r0L = 3 ; quadrant
cx16.r1sL = x2-x1 ; xdelta
if_neg {
cx16.r0L--
cx16.r1sL = -cx16.r1sL
}
cx16.r2sL = y2-y1 ; ydelta
if_neg {
cx16.r0L-=2
cx16.r2sL = -cx16.r2sL
}
return direction_qd(cx16.r0L, cx16.r1L, cx16.r2L)
}
sub direction(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
; From a pair of positive coordinates, calculate discrete direction between 0 and 23 into A.
cx16.r0L = 3 ; quadrant
if x2>=x1 {
cx16.r1L = x2-x1
} else {
cx16.r1L = x1-x2
cx16.r0L--
}
if y2>=y1 {
cx16.r2L = y2-y1
} else {
cx16.r2L = y1-y2
cx16.r0L -= 2
}
return direction_qd(cx16.r0L, cx16.r1L, cx16.r2L)
}
asmsub direction_qd(ubyte quadrant @A, ubyte xdelta @X, ubyte ydelta @Y) -> ubyte @A {
;Arctan https://github.com/dustmop/arctan24
; From a pair of X/Y deltas (both >=0), and quadrant 0-3, calculate discrete direction between 0 and 23 into A.
; .reg:a @in quadrant Number 0 to 3.
; .reg:x @in x_delta Delta for x direction.
; .reg:y @in y_delta Delta for y direction.
; Returns A as the direction (0-23).
%asm {{
x_delta = cx16.r0L
y_delta = cx16.r1L
quadrant = cx16.r2L
half_value = cx16.r3L
region_number = cx16.r4L
small = cx16.r5L
large = cx16.r5H
sta quadrant
sty y_delta
stx x_delta
cpx y_delta
bcs _XGreaterOrEqualY
_XLessY:
lda #16
sta region_number
stx small
sty large
bne _DetermineRegion
_XGreaterOrEqualY:
lda #0
sta region_number
stx large
sty small
_DetermineRegion:
; set A = small * 2.5
lda small
lsr a
sta half_value
lda small
asl a
bcs _SmallerQuotient
clc
adc half_value
bcs _SmallerQuotient
cmp large
bcc _LargerQuotient
; S * 2.5 > L
_SmallerQuotient:
; set A = S * 1.25
lsr half_value
lda small
clc
adc half_value
cmp large
bcc _Region1 ; if S * 1.25 < L then goto Region1 (L / S > 1.25)
bcs _Region0 ; (L / S < 1.25)
; S * 2.5 < L
_LargerQuotient:
; set A = S * 7.5
lda small
asl a
asl a
asl a
bcs _Region2
sec
sbc half_value
cmp large
bcc _Region3 ; if S * 7.5 < L then goto Region3 (L / S > 7.5)
jmp _Region2 ; (L / S < 7.5)
_Region0:
; L / S < 1.25. d=3,9,15,21
jmp _LookupResult
_Region1:
; 1.25 < L / S < 2.5. d=2,4,8,10,14,16,20,22
lda region_number
clc
adc #4
sta region_number
bpl _LookupResult
_Region2:
; 2.5 < L / S < 7.5. d=1,5,7,11,13,17,19,23
lda region_number
clc
adc #8
sta region_number
bpl _LookupResult
_Region3:
; 7.5 < L / S. d=0,6,12,18
lda region_number
clc
adc #12
sta region_number
_LookupResult:
lda quadrant
clc
adc region_number
tax
lda _quadrant_region_to_direction,x
rts
_quadrant_region_to_direction:
.byte 9, 3,15,21
.byte 10, 2,14,22
.byte 11, 1,13,23
.byte 12, 0,12, 0
.byte 9, 3,15,21
.byte 8, 4,16,20
.byte 7, 5,17,19
.byte 6, 6,18,18
}}
}
asmsub atan2(ubyte x1 @R0, ubyte y1 @R1, ubyte x2 @R2, ubyte y2 @R3) -> ubyte @A {
;; Calculate the angle, in a 256-degree circle, between two points into A.
;; The points (x1, y1) and (x2, y2) have to use *unsigned coordinates only* from the positive quadrant in the carthesian plane!
;; https://www.codebase64.org/doku.php?id=base:8bit_atan2_8-bit_angle
;; This uses 2 large lookup tables so uses a lot of memory but is super fast.
%asm {{
x1 = cx16.r0L
y1 = cx16.r1L
x2 = cx16.r2L
y2 = cx16.r3L
octant = cx16.r4L ;; temporary zeropage variable
lda x1
sec
sbc x2
bcs *+4
eor #$ff
tax
rol octant
lda y1
sec
sbc y2
bcs *+4
eor #$ff
tay
rol octant
lda log2_tab,x
sec
sbc log2_tab,y
bcc *+4
eor #$ff
tax
lda octant
rol a
and #%111
tay
lda atan_tab,x
eor octant_adjust,y
rts
octant_adjust
.byte %00111111 ;; x+,y+,|x|>|y|
.byte %00000000 ;; x+,y+,|x|<|y|
.byte %11000000 ;; x+,y-,|x|>|y|
.byte %11111111 ;; x+,y-,|x|<|y|
.byte %01000000 ;; x-,y+,|x|>|y|
.byte %01111111 ;; x-,y+,|x|<|y|
.byte %10111111 ;; x-,y-,|x|>|y|
.byte %10000000 ;; x-,y-,|x|<|y|
;;;;;;;; atan(2^(x/32))*128/pi ;;;;;;;;
atan_tab
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$01,$01,$01
.byte $01,$01,$01,$01,$01,$01,$01,$01
.byte $01,$01,$01,$01,$01,$01,$01,$01
.byte $01,$01,$01,$01,$01,$01,$01,$01
.byte $01,$01,$01,$01,$01,$02,$02,$02
.byte $02,$02,$02,$02,$02,$02,$02,$02
.byte $02,$02,$02,$02,$02,$02,$02,$02
.byte $03,$03,$03,$03,$03,$03,$03,$03
.byte $03,$03,$03,$03,$03,$04,$04,$04
.byte $04,$04,$04,$04,$04,$04,$04,$04
.byte $05,$05,$05,$05,$05,$05,$05,$05
.byte $06,$06,$06,$06,$06,$06,$06,$06
.byte $07,$07,$07,$07,$07,$07,$08,$08
.byte $08,$08,$08,$08,$09,$09,$09,$09
.byte $09,$0a,$0a,$0a,$0a,$0b,$0b,$0b
.byte $0b,$0c,$0c,$0c,$0c,$0d,$0d,$0d
.byte $0d,$0e,$0e,$0e,$0e,$0f,$0f,$0f
.byte $10,$10,$10,$11,$11,$11,$12,$12
.byte $12,$13,$13,$13,$14,$14,$15,$15
.byte $15,$16,$16,$17,$17,$17,$18,$18
.byte $19,$19,$19,$1a,$1a,$1b,$1b,$1c
.byte $1c,$1c,$1d,$1d,$1e,$1e,$1f,$1f
;;;;;;;; log2(x)*32 ;;;;;;;;
log2_tab
.byte $00,$00,$20,$32,$40,$4a,$52,$59
.byte $60,$65,$6a,$6e,$72,$76,$79,$7d
.byte $80,$82,$85,$87,$8a,$8c,$8e,$90
.byte $92,$94,$96,$98,$99,$9b,$9d,$9e
.byte $a0,$a1,$a2,$a4,$a5,$a6,$a7,$a9
.byte $aa,$ab,$ac,$ad,$ae,$af,$b0,$b1
.byte $b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9
.byte $b9,$ba,$bb,$bc,$bd,$bd,$be,$bf
.byte $c0,$c0,$c1,$c2,$c2,$c3,$c4,$c4
.byte $c5,$c6,$c6,$c7,$c7,$c8,$c9,$c9
.byte $ca,$ca,$cb,$cc,$cc,$cd,$cd,$ce
.byte $ce,$cf,$cf,$d0,$d0,$d1,$d1,$d2
.byte $d2,$d3,$d3,$d4,$d4,$d5,$d5,$d5
.byte $d6,$d6,$d7,$d7,$d8,$d8,$d9,$d9
.byte $d9,$da,$da,$db,$db,$db,$dc,$dc
.byte $dd,$dd,$dd,$de,$de,$de,$df,$df
.byte $df,$e0,$e0,$e1,$e1,$e1,$e2,$e2
.byte $e2,$e3,$e3,$e3,$e4,$e4,$e4,$e5
.byte $e5,$e5,$e6,$e6,$e6,$e7,$e7,$e7
.byte $e7,$e8,$e8,$e8,$e9,$e9,$e9,$ea
.byte $ea,$ea,$ea,$eb,$eb,$eb,$ec,$ec
.byte $ec,$ec,$ed,$ed,$ed,$ed,$ee,$ee
.byte $ee,$ee,$ef,$ef,$ef,$ef,$f0,$f0
.byte $f0,$f1,$f1,$f1,$f1,$f1,$f2,$f2
.byte $f2,$f2,$f3,$f3,$f3,$f3,$f4,$f4
.byte $f4,$f4,$f5,$f5,$f5,$f5,$f5,$f6
.byte $f6,$f6,$f6,$f7,$f7,$f7,$f7,$f7
.byte $f8,$f8,$f8,$f8,$f9,$f9,$f9,$f9
.byte $f9,$fa,$fa,$fa,$fa,$fa,$fb,$fb
.byte $fb,$fb,$fb,$fc,$fc,$fc,$fc,$fc
.byte $fd,$fd,$fd,$fd,$fd,$fd,$fe,$fe
.byte $fe,$fe,$fe,$ff,$ff,$ff,$ff,$ff
}}
}
}
-87
View File
@@ -1,20 +1,6 @@
; ---- builtin functions
func_any_b_stack .proc
jsr func_any_b_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_all_b_stack .proc
jsr func_all_b_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_any_b_into_A .proc
; -- any(array), array in P8ZP_SCRATCH_W1, num bytes in A
sta _cmp_mod+1 ; self-modifying code
@@ -49,14 +35,6 @@ func_any_w_into_A .proc
jmp func_any_b_into_A
.pend
func_any_w_stack .proc
asl a
jsr func_any_b_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_all_w_into_A .proc
; -- all(warray), array in P8ZP_SCRATCH_W1, num bytes in A
asl a ; times 2 because of word
@@ -77,22 +55,6 @@ _cmp_mod cpy #255 ; modified
rts
.pend
func_all_w_stack .proc
jsr func_all_w_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
abs_b_stack .proc
; -- push abs(A) on stack (as unsigned word)
jsr abs_b_into_A
sta P8ESTACK_LO,x
stz P8ESTACK_HI,x
dex
rts
.pend
abs_b_into_A .proc
; -- A = abs(A)
cmp #0
@@ -104,16 +66,6 @@ abs_b_into_A .proc
rts
.pend
abs_w_stack .proc
; -- push abs(AY) on stack (as word)
jsr abs_w_into_AY
sta P8ESTACK_LO,x
tya
sta P8ESTACK_HI,x
dex
rts
.pend
abs_w_into_AY .proc
; -- AY = abs(AY)
cpy #0
@@ -142,13 +94,6 @@ _neg lda #-1
rts
.pend
func_sign_b_stack .proc
jsr func_sign_b_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_sign_ub_into_A .proc
cmp #0
bne _pos
@@ -157,13 +102,6 @@ _pos lda #1
rts
.pend
func_sign_ub_stack .proc
jsr func_sign_ub_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_sign_uw_into_A .proc
cpy #0
beq _possibly_zero
@@ -174,13 +112,6 @@ _possibly_zero cmp #0
rts
.pend
func_sign_uw_stack .proc
jsr func_sign_uw_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_sign_w_into_A .proc
cpy #0
beq _possibly_zero
@@ -195,26 +126,10 @@ _possibly_zero cmp #0
.pend
func_sign_w_stack .proc
jsr func_sign_w_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_sqrt16_stack .proc
jsr func_sqrt16_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_sqrt16_into_A .proc
; integer square root from http://6502org.wikidot.com/software-math-sqrt
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
txa
pha
lda #0
sta P8ZP_SCRATCH_B1
sta P8ZP_SCRATCH_REG
@@ -237,8 +152,6 @@ func_sqrt16_into_A .proc
rol P8ZP_SCRATCH_REG
dex
bne -
pla
tax
lda P8ZP_SCRATCH_B1
rts
.pend
+5 -617
View File
@@ -4,10 +4,8 @@
orig_stackpointer .byte 0 ; stores the Stack pointer register at program start
read_byte_from_address_on_stack .proc
; -- read the byte from the memory address on the top of the stack, return in A (stack remains unchanged)
lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x
read_byte_from_address_in_AY_into_A .proc
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #0
@@ -16,319 +14,16 @@ read_byte_from_address_on_stack .proc
.pend
write_byte_to_address_on_stack .proc
; -- write the byte in A to the memory address on the top of the stack (stack remains unchanged)
ldy P8ESTACK_LO+1,x
sty P8ZP_SCRATCH_W2
ldy P8ESTACK_HI+1,x
write_byte_X_to_address_in_AY .proc
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #0
txa
sta (P8ZP_SCRATCH_W2),y
rts
.pend
neg_b .proc
lda #0
sec
sbc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
rts
.pend
neg_w .proc
sec
lda #0
sbc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
lda #0
sbc P8ESTACK_HI+1,x
sta P8ESTACK_HI+1,x
rts
.pend
inv_word .proc
lda P8ESTACK_LO+1,x
eor #255
sta P8ESTACK_LO+1,x
lda P8ESTACK_HI+1,x
eor #255
sta P8ESTACK_HI+1,x
rts
.pend
bitand_b .proc
; -- bitwise and (of 2 bytes)
lda P8ESTACK_LO+2,x
and P8ESTACK_LO+1,x
inx
sta P8ESTACK_LO+1,x
rts
.pend
bitor_b .proc
; -- bitwise or (of 2 bytes)
lda P8ESTACK_LO+2,x
ora P8ESTACK_LO+1,x
inx
sta P8ESTACK_LO+1,x
rts
.pend
bitxor_b .proc
; -- bitwise xor (of 2 bytes)
lda P8ESTACK_LO+2,x
eor P8ESTACK_LO+1,x
inx
sta P8ESTACK_LO+1,x
rts
.pend
bitand_w .proc
; -- bitwise and (of 2 words)
lda P8ESTACK_LO+2,x
and P8ESTACK_LO+1,x
sta P8ESTACK_LO+2,x
lda P8ESTACK_HI+2,x
and P8ESTACK_HI+1,x
sta P8ESTACK_HI+2,x
inx
rts
.pend
bitor_w .proc
; -- bitwise or (of 2 words)
lda P8ESTACK_LO+2,x
ora P8ESTACK_LO+1,x
sta P8ESTACK_LO+2,x
lda P8ESTACK_HI+2,x
ora P8ESTACK_HI+1,x
sta P8ESTACK_HI+2,x
inx
rts
.pend
bitxor_w .proc
; -- bitwise xor (of 2 bytes)
lda P8ESTACK_LO+2,x
eor P8ESTACK_LO+1,x
sta P8ESTACK_LO+2,x
lda P8ESTACK_HI+2,x
eor P8ESTACK_HI+1,x
sta P8ESTACK_HI+2,x
inx
rts
.pend
add_w .proc
; -- push word+word / uword+uword
inx
clc
lda P8ESTACK_LO,x
adc P8ESTACK_LO+1,x
sta P8ESTACK_LO+1,x
lda P8ESTACK_HI,x
adc P8ESTACK_HI+1,x
sta P8ESTACK_HI+1,x
rts
.pend
sub_w .proc
; -- push word-word
inx
sec
lda P8ESTACK_LO+1,x
sbc P8ESTACK_LO,x
sta P8ESTACK_LO+1,x
lda P8ESTACK_HI+1,x
sbc P8ESTACK_HI,x
sta P8ESTACK_HI+1,x
rts
.pend
mul_byte .proc
; -- b*b->b (signed and unsigned)
inx
lda P8ESTACK_LO,x
ldy P8ESTACK_LO+1,x
jsr math.multiply_bytes
sta P8ESTACK_LO+1,x
rts
.pend
mul_word .proc
inx
lda P8ESTACK_LO,x
sta P8ZP_SCRATCH_W1
lda P8ESTACK_HI,x
sta P8ZP_SCRATCH_W1+1
lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x
jsr math.multiply_words
lda math.multiply_words.result
sta P8ESTACK_LO+1,x
lda math.multiply_words.result+1
sta P8ESTACK_HI+1,x
rts
.pend
idiv_b .proc
; signed division: use unsigned division and fix sign of result afterwards
inx
lda P8ESTACK_LO,x
eor P8ESTACK_LO+1,x
php ; save sign of result
lda P8ESTACK_LO,x
bpl +
eor #$ff
sec
adc #0 ; make num1 positive
+ tay
inx
lda P8ESTACK_LO,x
bpl +
eor #$ff
sec
adc #0 ; make num2 positive
+ jsr math.divmod_ub_asm
sta _remainder
tya
plp ; get sign of result
bpl +
eor #$ff
sec
adc #0 ; negate result
+ sta P8ESTACK_LO,x
dex
rts
_remainder .byte 0
.pend
idiv_ub .proc
inx
ldy P8ESTACK_LO,x
lda P8ESTACK_LO+1,x
jsr math.divmod_ub_asm
tya
sta P8ESTACK_LO+1,x
rts
.pend
idiv_w .proc
; signed division: use unsigned division and fix sign of result afterwards
lda P8ESTACK_HI+2,x
eor P8ESTACK_HI+1,x
php ; save sign of result
lda P8ESTACK_HI+1,x
bpl +
jsr neg_w ; make value positive
+ inx
lda P8ESTACK_HI+1,x
bpl +
jsr neg_w ; make value positive
+ lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_W1
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_W1+1
lda P8ESTACK_LO,x
ldy P8ESTACK_HI,x
jsr math.divmod_uw_asm
sta P8ESTACK_LO+1,x
tya
sta P8ESTACK_HI+1,x
plp
bpl +
jmp neg_w ; negate result
+ rts
.pend
idiv_uw .proc
inx
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_W1
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_W1+1
lda P8ESTACK_LO,x
ldy P8ESTACK_HI,x
jsr math.divmod_uw_asm
sta P8ESTACK_LO+1,x
tya
sta P8ESTACK_HI+1,x
rts
.pend
remainder_ub .proc
inx
ldy P8ESTACK_LO,x ; right operand
lda P8ESTACK_LO+1,x ; left operand
jsr math.divmod_ub_asm
sta P8ESTACK_LO+1,x
rts
.pend
remainder_uw .proc
inx
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_W1
lda P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_W1+1
lda P8ESTACK_LO,x
ldy P8ESTACK_HI,x
jsr math.divmod_uw_asm
lda P8ZP_SCRATCH_W2
sta P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_W2+1
sta P8ESTACK_HI+1,x
rts
.pend
equal_w .proc
; -- are the two words on the stack identical?
lda P8ESTACK_LO+1,x
cmp P8ESTACK_LO+2,x
bne equal_b._equal_b_false
lda P8ESTACK_HI+1,x
cmp P8ESTACK_HI+2,x
bne equal_b._equal_b_false
beq equal_b._equal_b_true
.pend
notequal_b .proc
; -- are the two bytes on the stack different?
lda P8ESTACK_LO+1,x
cmp P8ESTACK_LO+2,x
beq equal_b._equal_b_false
bne equal_b._equal_b_true
.pend
notequal_w .proc
; -- are the two words on the stack different?
lda P8ESTACK_HI+1,x
cmp P8ESTACK_HI+2,x
beq notequal_b
bne equal_b._equal_b_true
.pend
less_ub .proc
lda P8ESTACK_LO+2,x
cmp P8ESTACK_LO+1,x
bcc equal_b._equal_b_true
bcs equal_b._equal_b_false
.pend
less_b .proc
; see http://www.6502.org/tutorials/compare_beyond.html
lda P8ESTACK_LO+2,x
sec
sbc P8ESTACK_LO+1,x
bvc +
eor #$80
+ bmi equal_b._equal_b_true
bpl equal_b._equal_b_false
.pend
reg_less_uw .proc
; AY < P8ZP_SCRATCH_W2?
cpy P8ZP_SCRATCH_W2+1
@@ -342,17 +37,6 @@ _true lda #1
rts
.pend
less_uw .proc
lda P8ESTACK_HI+2,x
cmp P8ESTACK_HI+1,x
bcc equal_b._equal_b_true
bne equal_b._equal_b_false
lda P8ESTACK_LO+2,x
cmp P8ESTACK_LO+1,x
bcc equal_b._equal_b_true
bcs equal_b._equal_b_false
.pend
reg_less_w .proc
; -- AY < P8ZP_SCRATCH_W2?
cmp P8ZP_SCRATCH_W2
@@ -367,48 +51,6 @@ _true lda #1
rts
.pend
less_w .proc
lda P8ESTACK_LO+2,x
cmp P8ESTACK_LO+1,x
lda P8ESTACK_HI+2,x
sbc P8ESTACK_HI+1,x
bvc +
eor #$80
+ bmi equal_b._equal_b_true
bpl equal_b._equal_b_false
.pend
equal_b .proc
; -- are the two bytes on the stack identical?
lda P8ESTACK_LO+2,x
cmp P8ESTACK_LO+1,x
bne _equal_b_false
_equal_b_true lda #1
_equal_b_store inx
sta P8ESTACK_LO+1,x
rts
_equal_b_false lda #0
beq _equal_b_store
.pend
lesseq_ub .proc
lda P8ESTACK_LO+1,x
cmp P8ESTACK_LO+2,x
bcs equal_b._equal_b_true
bcc equal_b._equal_b_false
.pend
lesseq_b .proc
; see http://www.6502.org/tutorials/compare_beyond.html
lda P8ESTACK_LO+2,x
clc
sbc P8ESTACK_LO+1,x
bvc +
eor #$80
+ bmi equal_b._equal_b_true
bpl equal_b._equal_b_false
.pend
reg_lesseq_uw .proc
; AY <= P8ZP_SCRATCH_W2?
cpy P8ZP_SCRATCH_W2+1
@@ -425,17 +67,6 @@ _true lda #1
rts
.pend
lesseq_uw .proc
lda P8ESTACK_HI+1,x
cmp P8ESTACK_HI+2,x
bcc equal_b._equal_b_false
bne equal_b._equal_b_true
lda P8ESTACK_LO+1,x
cmp P8ESTACK_LO+2,x
bcs equal_b._equal_b_true
bcc equal_b._equal_b_false
.pend
reg_lesseq_w .proc
; -- P8ZP_SCRATCH_W2 <= AY ? (note: order different from other routines)
cmp P8ZP_SCRATCH_W2
@@ -450,224 +81,6 @@ reg_lesseq_w .proc
rts
.pend
lesseq_w .proc
lda P8ESTACK_LO+1,x
cmp P8ESTACK_LO+2,x
lda P8ESTACK_HI+1,x
sbc P8ESTACK_HI+2,x
bvc +
eor #$80
+ bpl equal_b._equal_b_true
bmi equal_b._equal_b_false
.pend
greater_ub .proc
lda P8ESTACK_LO+2,x
cmp P8ESTACK_LO+1,x
beq equal_b._equal_b_false
bcs equal_b._equal_b_true
bcc equal_b._equal_b_false
.pend
greater_b .proc
; see http://www.6502.org/tutorials/compare_beyond.html
lda P8ESTACK_LO+2,x
clc
sbc P8ESTACK_LO+1,x
bvc +
eor #$80
+ bpl equal_b._equal_b_true
bmi equal_b._equal_b_false
.pend
greater_uw .proc
lda P8ESTACK_HI+1,x
cmp P8ESTACK_HI+2,x
bcc equal_b._equal_b_true
bne equal_b._equal_b_false
lda P8ESTACK_LO+1,x
cmp P8ESTACK_LO+2,x
bcc equal_b._equal_b_true
bcs equal_b._equal_b_false
.pend
greater_w .proc
lda P8ESTACK_LO+1,x
cmp P8ESTACK_LO+2,x
lda P8ESTACK_HI+1,x
sbc P8ESTACK_HI+2,x
bvc +
eor #$80
+ bmi equal_b._equal_b_true
bpl equal_b._equal_b_false
.pend
greatereq_ub .proc
lda P8ESTACK_LO+2,x
cmp P8ESTACK_LO+1,x
bcs equal_b._equal_b_true
bcc equal_b._equal_b_false
.pend
greatereq_b .proc
; see http://www.6502.org/tutorials/compare_beyond.html
lda P8ESTACK_LO+2,x
sec
sbc P8ESTACK_LO+1,x
bvc +
eor #$80
+ bpl equal_b._equal_b_true
bmi equal_b._equal_b_false
.pend
greatereq_uw .proc
lda P8ESTACK_HI+2,x
cmp P8ESTACK_HI+1,x
bcc equal_b._equal_b_false
bne equal_b._equal_b_true
lda P8ESTACK_LO+2,x
cmp P8ESTACK_LO+1,x
bcs equal_b._equal_b_true
bcc equal_b._equal_b_false
.pend
greatereq_w .proc
lda P8ESTACK_LO+2,x
cmp P8ESTACK_LO+1,x
lda P8ESTACK_HI+2,x
sbc P8ESTACK_HI+1,x
bvc +
eor #$80
+ bmi equal_b._equal_b_false
bpl equal_b._equal_b_true
.pend
shiftleft_b .proc
inx
ldy P8ESTACK_LO,x
bne +
rts
+ lda P8ESTACK_LO+1,x
- asl a
dey
bne -
sta P8ESTACK_LO+1,x
rts
.pend
shiftright_b .proc
inx
ldy P8ESTACK_LO,x
bne +
rts
+ lda P8ESTACK_LO+1,x
- lsr a
dey
bne -
sta P8ESTACK_LO+1,x
rts
.pend
equalzero_b .proc
lda P8ESTACK_LO+1,x
beq _true
bne _false
_true lda #1
sta P8ESTACK_LO+1,x
rts
_false lda #0
sta P8ESTACK_LO+1,x
rts
.pend
equalzero_w .proc
lda P8ESTACK_LO+1,x
ora P8ESTACK_HI+1,x
beq equalzero_b._true
bne equalzero_b._false
.pend
notequalzero_b .proc
lda P8ESTACK_LO+1,x
beq equalzero_b._false
bne equalzero_b._true
.pend
notequalzero_w .proc
lda P8ESTACK_LO+1,x
ora P8ESTACK_HI+1,x
beq equalzero_b._false
bne equalzero_b._true
.pend
lesszero_b .proc
lda P8ESTACK_LO+1,x
bmi equalzero_b._true
jmp equalzero_b._false
.pend
lesszero_w .proc
lda P8ESTACK_HI+1,x
bmi equalzero_b._true
jmp equalzero_b._false
.pend
greaterzero_ub .proc
lda P8ESTACK_LO+1,x
bne equalzero_b._true
beq equalzero_b._false
.pend
greaterzero_sb .proc
lda P8ESTACK_LO+1,x
beq equalzero_b._false
bpl equalzero_b._true
bmi equalzero_b._false
.pend
greaterzero_uw .proc
lda P8ESTACK_LO+1,x
ora P8ESTACK_HI+1,x
bne equalzero_b._true
beq equalzero_b._false
.pend
greaterzero_sw .proc
lda P8ESTACK_HI+1,x
bmi equalzero_b._false
ora P8ESTACK_LO+1,x
beq equalzero_b._false
bne equalzero_b._true
.pend
lessequalzero_sb .proc
lda P8ESTACK_LO+1,x
bmi equalzero_b._true
beq equalzero_b._true
bne equalzero_b._false
.pend
lessequalzero_sw .proc
lda P8ESTACK_HI+1,x
bmi equalzero_b._true
ora P8ESTACK_LO+1,x
beq equalzero_b._true
bne equalzero_b._false
.pend
greaterequalzero_sb .proc
lda P8ESTACK_LO+1,x
bpl equalzero_b._true
bmi equalzero_b._false
.pend
greaterequalzero_sw .proc
lda P8ESTACK_HI+1,x
bpl equalzero_b._true
bmi equalzero_b._false
.pend
memcopy16_up .proc
; -- copy memory UP from (P8ZP_SCRATCH_W1) to (P8ZP_SCRATCH_W2) of length X/Y (16-bit, X=lo, Y=hi)
@@ -997,21 +410,6 @@ _arg_s1 .word 0
_arg_s2 .word 0
.pend
strcmp_stack .proc
; -- compare strings, both on stack.
; Returns -1,0,1 in A, depeding on the ordering. Clobbers Y.
inx
lda P8ESTACK_LO,x
ldy P8ESTACK_HI,x
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
inx
lda P8ESTACK_LO,x
ldy P8ESTACK_HI,x
jmp strcmp_mem
.pend
strcmp_mem .proc
; -- compares strings in s1 (AY) and s2 (P8ZP_SCRATCH_W2).
; Returns -1,0,1 in A, depeding on the ordering. Clobbers Y.
@@ -1042,16 +440,6 @@ _return_minusone
.pend
sign_extend_stack_byte .proc
; -- sign extend the (signed) byte on the stack to full 16 bits
lda P8ESTACK_LO+1,x
ora #$7f
bmi +
lda #0
+ sta P8ESTACK_HI+1,x
rts
.pend
strlen .proc
; -- returns the number of bytes in the string in AY, in Y.
sta P8ZP_SCRATCH_W1
+2
View File
@@ -1,6 +1,8 @@
; Internal library routines - always included by the compiler
prog8_lib {
%option no_symbol_prefixing
%asminclude "library:prog8_lib.asm"
%asminclude "library:prog8_funcs.asm"
}
+1 -2
View File
@@ -1,6 +1,7 @@
; 0-terminated string manipulation routines.
string {
%option no_symbol_prefixing
asmsub length(uword string @AY) clobbers(A) -> ubyte @Y {
; Returns the number of bytes in the string.
@@ -281,7 +282,6 @@ _done rts
str = P8ZP_SCRATCH_W1
stx P8ZP_SCRATCH_REG
sta str
sty str+1
lda cx16.r0
@@ -293,7 +293,6 @@ str = P8ZP_SCRATCH_W1
jsr _match
lda #0
rol a
ldx P8ZP_SCRATCH_REG
rts
+2 -12
View File
@@ -1,12 +1,12 @@
; utility debug code to print the X (evalstack) and SP (cpu stack) registers.
; utility debug code to print the SP (cpu stack pointer) register.
%import textio
test_stack {
%option no_symbol_prefixing
asmsub test() {
%asm {{
stx _saveX
lda #13
jsr txt.chrout
lda #'-'
@@ -16,14 +16,6 @@ test_stack {
bne -
lda #13
jsr txt.chrout
lda #'x'
jsr txt.chrout
lda #'='
jsr txt.chrout
lda _saveX
jsr txt.print_ub
lda #' '
jsr txt.chrout
lda #'s'
jsr txt.chrout
lda #'p'
@@ -42,9 +34,7 @@ test_stack {
bne -
lda #13
jsr txt.chrout
ldx _saveX
rts
_saveX .byte 0
}}
}
}
+22 -18
View File
@@ -106,19 +106,21 @@ sub str_uwhex (uword value) {
sub str_uw0 (uword value) {
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
ubyte tenthousands = (value / 10000) as ubyte
value -= 10000*tenthousands
ubyte thousands = (value / 1000) as ubyte
value -= 1000*thousands
ubyte hundreds = (value / 100) as ubyte
value -= 100 as uword * hundreds
ubyte tens = (value / 10) as ubyte
value -= 10*tens
uword value2 = value/10
ubyte digits = value-value2*10 as ubyte
uword value3 = value2/10
ubyte tens = value2-value3*10 as ubyte
uword value4 = value3/10
ubyte hundreds = value3-value4*10 as ubyte
uword value5 = value4/10
ubyte thousands = value4-value5*10 as ubyte
uword value6 = value5/10
ubyte tenthousands = value5-value6*10 as ubyte
string_out[0] = tenthousands+'0'
string_out[1] = thousands+'0'
string_out[2] = hundreds+'0'
string_out[3] = tens+'0'
string_out[4] = value as ubyte + '0'
string_out[4] = digits+'0'
string_out[5] = 0
}
@@ -139,14 +141,16 @@ sub str_w (word value) {
}
sub internal_str_uw(uword value, uword out_ptr) {
ubyte tenthousands = (value / 10000) as ubyte
value -= 10000*tenthousands
ubyte thousands = (value / 1000) as ubyte
value -= 1000*thousands
ubyte hundreds = (value / 100) as ubyte
value -= 100 as uword * hundreds
ubyte tens = (value / 10) as ubyte
value -= 10*tens
uword value2 = value/10
ubyte digits = value-value2*10 as ubyte
uword value3 = value2/10
ubyte tens = value2-value3*10 as ubyte
uword value4 = value3/10
ubyte hundreds = value3-value4*10 as ubyte
uword value5 = value4/10
ubyte thousands = value4-value5*10 as ubyte
uword value6 = value5/10
ubyte tenthousands = value5-value6*10 as ubyte
if tenthousands
goto output_tenthousands
if thousands
@@ -169,7 +173,7 @@ output_tens:
@(out_ptr) = tens+'0'
out_ptr++
output_ones:
@(out_ptr) = value as ubyte + '0'
@(out_ptr) = digits+'0'
out_ptr++
@(out_ptr) = 0
}
+1
View File
@@ -0,0 +1 @@
; there is no diskio yet for the VM target.
+57
View File
@@ -182,4 +182,61 @@ math {
return
}}
}
sub direction(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
; From a pair of positive coordinates, calculate discrete direction between 0 and 23 into A.
; This adjusts the atan() result so that the direction N is centered on the angle=N instead of having it as a boundary
ubyte angle = atan2(x1, y1, x2, y2) - 256/48
return 23-lsb(mkword(angle,0) / 2730)
}
sub direction_sc(byte x1, byte y1, byte x2, byte y2) -> ubyte {
; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A.
; shift the points into the positive quadrant
ubyte px1
ubyte py1
ubyte px2
ubyte py2
if x1<0 or x2<0 {
px1 = x1 as ubyte + 128
px2 = x2 as ubyte + 128
} else {
px1 = x1 as ubyte
px2 = x2 as ubyte
}
if y1<0 or y2<0 {
py1 = y1 as ubyte + 128
py2 = y2 as ubyte + 128
} else {
py1 = y1 as ubyte
py2 = y2 as ubyte
}
return direction(px1, py1, px2, py2)
}
sub direction_qd(ubyte quadrant, ubyte xdelta, ubyte ydelta) -> ubyte {
; From a pair of X/Y deltas (both >=0), and quadrant 0-3, calculate discrete direction between 0 and 23.
when quadrant {
3 -> return direction(0, 0, xdelta, ydelta)
2 -> return direction(xdelta, 0, 0, ydelta)
1 -> return direction(0, ydelta, xdelta, 0)
else -> return direction(xdelta, ydelta, 0, 0)
}
}
sub atan2(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
;; Calculate the angle, in a 256-degree circle, between two points into A.
;; The points (x1, y1) and (x2, y2) have to use *unsigned coordinates only* from the positive quadrant in the carthesian plane!
%ir {{
loadm.b r65532,math.atan2.x1
loadm.b r65533,math.atan2.y1
loadm.b r65534,math.atan2.x2
loadm.b r65535,math.atan2.y2
syscall 44 (r65532.b, r65533.b, r65534.b, r65535.b): r0.b
returnr.b r0
}}
}
}
+39
View File
@@ -218,4 +218,43 @@ cx16 {
&byte r13sH = $ff1d
&byte r14sH = $ff1f
&byte r15sH = $ff21
sub save_virtual_registers() {
uword[32] storage
storage[0] = r0
storage[1] = r1
storage[2] = r2
storage[3] = r3
storage[4] = r4
storage[5] = r5
storage[6] = r6
storage[7] = r7
storage[8] = r8
storage[9] = r9
storage[10] = r10
storage[11] = r11
storage[12] = r12
storage[13] = r13
storage[14] = r14
storage[15] = r15
}
sub restore_virtual_registers() {
r0 = cx16.save_virtual_registers.storage[0]
r1 = cx16.save_virtual_registers.storage[1]
r2 = cx16.save_virtual_registers.storage[2]
r3 = cx16.save_virtual_registers.storage[3]
r4 = cx16.save_virtual_registers.storage[4]
r5 = cx16.save_virtual_registers.storage[5]
r6 = cx16.save_virtual_registers.storage[6]
r7 = cx16.save_virtual_registers.storage[7]
r8 = cx16.save_virtual_registers.storage[8]
r9 = cx16.save_virtual_registers.storage[9]
r10 = cx16.save_virtual_registers.storage[10]
r11 = cx16.save_virtual_registers.storage[11]
r12 = cx16.save_virtual_registers.storage[12]
r13 = cx16.save_virtual_registers.storage[13]
r14 = cx16.save_virtual_registers.storage[14]
r15 = cx16.save_virtual_registers.storage[15]
}
}
-1
View File
@@ -1 +0,0 @@
9.0
+32 -43
View File
@@ -3,7 +3,6 @@ package prog8
import kotlinx.cli.*
import prog8.ast.base.AstException
import prog8.code.core.CbmPrgLauncherType
import prog8.code.core.toHex
import prog8.code.target.*
import prog8.code.target.virtual.VirtualMachineDefinition
import prog8.compiler.CompilationResult
@@ -20,8 +19,10 @@ import kotlin.system.exitProcess
fun main(args: Array<String>) {
val buildVersion = object {}.javaClass.getResource("/version.txt")?.readText()?.trim()
println("\nProg8 compiler v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
println("\nProg8 compiler v${prog8.buildversion.VERSION} by Irmen de Jong (irmen@razorvine.net)")
if('-' in prog8.buildversion.VERSION) {
println("Prerelease version from git commit ${prog8.buildversion.GIT_SHA.take(8)} in branch ${prog8.buildversion.GIT_BRANCH}")
}
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
val succes = compileMain(args)
@@ -37,23 +38,20 @@ private fun compileMain(args: Array<String>): Boolean {
val cli = ArgParser("prog8compiler", prefixStyle = ArgParser.OptionPrefixStyle.JVM)
val asmListfile by cli.option(ArgType.Boolean, fullName = "asmlist", description = "make the assembler produce a listing file as well")
val symbolDefs by cli.option(ArgType.String, fullName = "D", description = "define assembly symbol(s) with -D SYMBOL=VALUE").multiple()
val evalStackAddrString by cli.option(ArgType.String, fullName = "esa", description = "override the eval-stack base address (must be page aligned)")
val startEmulator1 by cli.option(ArgType.Boolean, fullName = "emu", description = "auto-start emulator after successful compilation")
val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation")
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform any optimizations")
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
val optimizeFloatExpressions by cli.option(ArgType.Boolean, fullName = "optfloatx", description = "optimize float expressions (warning: can increase program size)")
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}')").required()
val includeSourcelines by cli.option(ArgType.Boolean, fullName = "sourcelines", description = "include original Prog8 source lines in generated asm code")
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory")
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${VMTarget.NAME}') (required)")
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
val varsHigh by cli.option(ArgType.Boolean, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program")
val useNewExprCode by cli.option(ArgType.Boolean, fullName = "newexpr", description = "use new expression code-gen (experimental)")
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory")
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
try {
@@ -79,8 +77,20 @@ private fun compileMain(args: Array<String>): Boolean {
if(srcdirs.firstOrNull()!=".")
srcdirs.add(0, ".")
if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME, AtariTarget.NAME, VMTarget.NAME)) {
System.err.println("Invalid compilation target: $compilationTarget")
if(startVm==null) {
if(compilationTarget==null) {
System.err.println("No compilation target specified")
return false
}
if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME, AtariTarget.NAME, VMTarget.NAME)) {
System.err.println("Invalid compilation target: $compilationTarget")
return false
}
}
if(varsHighBank==0 && compilationTarget==Cx16Target.NAME) {
System.err.println("On the Commander X16, HiRAM bank 0 is used by the kernal and can't be used.")
return false
}
@@ -88,25 +98,6 @@ private fun compileMain(args: Array<String>): Boolean {
return runVm(moduleFiles.first())
}
var evalStackAddr: UInt? = null
if(evalStackAddrString!=null) {
try {
evalStackAddr = if (evalStackAddrString!!.startsWith("0x"))
evalStackAddrString!!.substring(2).toUInt(16)
else if (evalStackAddrString!!.startsWith("$"))
evalStackAddrString!!.substring(1).toUInt(16)
else
evalStackAddrString!!.toUInt()
} catch(nx: NumberFormatException) {
System.err.println("invalid address for evalstack: $evalStackAddrString")
return false
}
if(evalStackAddr !in 256u..65536u-512u || (evalStackAddr and 255u != 0u)) {
System.err.println("invalid address for evalstack: ${evalStackAddr.toHex()}")
return false
}
}
val processedSymbols = processSymbolDefs(symbolDefs) ?: return false
if(watchMode==true) {
@@ -121,16 +112,13 @@ private fun compileMain(args: Array<String>): Boolean {
val compilerArgs = CompilerArguments(
filepath,
dontOptimize != true,
optimizeFloatExpressions == true,
dontWriteAssembly != true,
slowCodegenWarnings == true,
quietAssembler == true,
asmListfile == true,
includeSourcelines == true,
experimentalCodegen == true,
varsHigh == true,
useNewExprCode == true,
compilationTarget,
evalStackAddr,
varsHighBank,
compilationTarget!!,
splitWordArrays == true,
processedSymbols,
srcdirs,
@@ -180,6 +168,10 @@ private fun compileMain(args: Array<String>): Boolean {
}
} else {
if((startEmulator1==true || startEmulator2==true) && moduleFiles.size>1) {
System.err.println("can't start emulator when multiple module files are specified")
return false
}
for(filepathRaw in moduleFiles) {
val filepath = pathFrom(filepathRaw).normalize()
val compilationResult: CompilationResult
@@ -187,16 +179,13 @@ private fun compileMain(args: Array<String>): Boolean {
val compilerArgs = CompilerArguments(
filepath,
dontOptimize != true,
optimizeFloatExpressions == true,
dontWriteAssembly != true,
slowCodegenWarnings == true,
quietAssembler == true,
asmListfile == true,
includeSourcelines == true,
experimentalCodegen == true,
varsHigh == true,
useNewExprCode == true,
compilationTarget,
evalStackAddr,
varsHighBank,
compilationTarget!!,
splitWordArrays == true,
processedSymbols,
srcdirs,
@@ -0,0 +1,15 @@
package prog8.buildversion
/**
* Automatically generated file containing build version information.
*/
const val MAVEN_GROUP = "prog8"
const val MAVEN_NAME = "compiler"
const val VERSION = "9.3-SNAPSHOT"
const val GIT_REVISION = 4012
const val GIT_SHA = "9167ba499de99696aa7c6887d4867eae70eb5caf"
const val GIT_DATE = "2023-07-30T15:49:35Z"
const val GIT_BRANCH = "master"
const val BUILD_DATE = "2023-07-30T16:41:27Z"
const val BUILD_UNIX_TIME = 1690735287514L
const val DIRTY = 1
+11 -221
View File
@@ -4,12 +4,11 @@ import com.github.michaelbull.result.onFailure
import prog8.ast.IBuiltinFunctions
import prog8.ast.Program
import prog8.ast.base.AstException
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.Expression
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Directive
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
import prog8.code.ast.PtProgram
import prog8.code.core.*
import prog8.code.target.*
import prog8.codegen.vm.VmCodeGen
@@ -30,16 +29,13 @@ class CompilationResult(val compilerAst: Program, // deprecated, use codegenAs
class CompilerArguments(val filepath: Path,
val optimize: Boolean,
val optimizeFloatExpressions: Boolean,
val writeAssembly: Boolean,
val slowCodegenWarnings: Boolean,
val quietAssembler: Boolean,
val asmListfile: Boolean,
val includeSourcelines: Boolean,
val experimentalCodegen: Boolean,
val varsHigh: Boolean,
val useNewExprCode: Boolean,
val varsHighBank: Int?,
val compilationTarget: String,
val evalStackBaseAddress: UInt?,
val splitWordArrays: Boolean,
val symbolDefs: Map<String, String>,
val sourceDirs: List<String> = emptyList(),
@@ -51,8 +47,6 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
lateinit var program: Program
lateinit var importedFiles: List<Path>
val optimizeFloatExpr = if(args.optimize) args.optimizeFloatExpressions else false
val compTarget =
when(args.compilationTarget) {
C64Target.NAME -> C64Target()
@@ -72,15 +66,12 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
compilationOptions = options
with(compilationOptions) {
slowCodegenWarnings = args.slowCodegenWarnings
optimize = args.optimize
optimizeFloatExpressions = optimizeFloatExpr
asmQuiet = args.quietAssembler
asmListfile = args.asmListfile
includeSourcelines = args.includeSourcelines
experimentalCodegen = args.experimentalCodegen
varsHigh = args.varsHigh
useNewExprCode = args.useNewExprCode
evalStackBaseAddress = args.evalStackBaseAddress
varsHighBank = args.varsHighBank
splitWordArrays = args.splitWordArrays
outputDir = args.outputDir.normalize()
symbolDefs = args.symbolDefs
@@ -88,10 +79,6 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
program = programresult
importedFiles = imported
if(compilationOptions.evalStackBaseAddress!=null) {
compTarget.machine.overrideEvalStack(compilationOptions.evalStackBaseAddress!!)
}
processAst(program, args.errors, compilationOptions)
if (compilationOptions.optimize) {
// println("*********** COMPILER AST RIGHT BEFORE OPTIMIZING *************")
@@ -119,7 +106,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
program.processAstBeforeAsmGeneration(compilationOptions, args.errors)
args.errors.report()
val intermediateAst = IntermediateAstMaker(program, compilationOptions).transform()
val intermediateAst = IntermediateAstMaker(program).transform()
// println("*********** COMPILER AST RIGHT BEFORE ASM GENERATION *************")
// printProgram(program)
// println("*********** AST RIGHT BEFORE ASM GENERATION *************")
@@ -349,7 +336,7 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
errors.report()
program.desugaring(errors)
errors.report()
program.reorderStatements(errors, compilerOptions)
program.reorderStatements(errors)
errors.report()
program.changeNotExpressionAndIfComparisonExpr(errors, compilerOptions.compTarget)
errors.report()
@@ -370,12 +357,11 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
while (true) {
// keep optimizing expressions and statements until no more steps remain
val optsDone1 = program.simplifyExpressions(errors, compTarget)
val optsDone2 = program.splitBinaryExpressions(compilerOptions)
val optsDone3 = program.optimizeStatements(errors, functions, compTarget)
val optsDone4 = program.inlineSubroutines()
val optsDone2 = program.optimizeStatements(errors, functions, compilerOptions)
val optsDone3 = program.inlineSubroutines(compilerOptions)
program.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
errors.report()
if (optsDone1 + optsDone2 + optsDone3 + optsDone4 == 0)
if (optsDone1 + optsDone2 + optsDone3 == 0)
break
}
val remover2 = UnusedCodeRemover(program, errors, compTarget)
@@ -406,23 +392,12 @@ private fun createAssemblyAndAssemble(program: PtProgram,
val asmgen = if(compilerOptions.experimentalCodegen)
prog8.codegen.experimental.ExperiCodeGen()
else if (compilerOptions.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
prog8.codegen.cpu6502.AsmGen6502()
prog8.codegen.cpu6502.AsmGen6502(prefixSymbols = true)
else if (compilerOptions.compTarget.name == VMTarget.NAME)
VmCodeGen()
else
throw NotImplementedError("no code generator for cpu ${compilerOptions.compTarget.machine.cpu}")
if(compilerOptions.useNewExprCode) {
if(compilerOptions.compTarget.machine.cpu !in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) {
// the IR code gen backend has its own, better, version of dealing with binary expressions.
throw IllegalArgumentException("'newexpr' expression rewrite should not be used with compilation target ${compilerOptions.compTarget.name}")
}
transformNewExpressions(program)
}
// printAst(program, true) { println(it) }
val stMaker = SymbolTableMaker(program, compilerOptions)
val symbolTable = stMaker.make()
val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors)
@@ -434,188 +409,3 @@ private fun createAssemblyAndAssemble(program: PtProgram,
false
}
}
private fun transformNewExpressions(program: PtProgram) {
val newVariables = mutableMapOf<PtSub, MutableList<PtVariable>>()
var countByteVars = 0
var countWordVars = 0
var countFloatVars = 0
// TODO: find a reliable way to reuse more temp vars across expressions
fun getExprVar(type: DataType, pos: Position, scope: PtSub): PtIdentifier {
val count = when(type) {
in ByteDatatypes -> {
countByteVars++
countByteVars
}
in WordDatatypes -> {
countWordVars++
countWordVars
}
DataType.FLOAT -> {
countFloatVars++
countFloatVars
}
else -> throw FatalAstException("weird dt")
}
val name = "p8p_exprvar_${count}_${type.toString().lowercase()}"
var subVars = newVariables[scope]
if(subVars==null) {
subVars = mutableListOf()
newVariables[scope] = subVars
}
if(subVars.all { it.name!=name }) {
subVars.add(PtVariable(name, type, ZeropageWish.DONTCARE, null, null, pos))
}
return PtIdentifier("${scope.scopedName}.$name", type, pos)
}
fun transformExpr(expr: PtBinaryExpression): Pair<PtExpression, List<IPtAssignment>> {
// depth first process the expression tree
val scope = expr.definingSub()!!
val assignments = mutableListOf<IPtAssignment>()
fun transformOperand(node: PtExpression): PtNode {
return when(node) {
is PtNumber, is PtIdentifier, is PtArray, is PtString, is PtMachineRegister -> node
is PtBinaryExpression -> {
val (replacement, subAssigns) = transformExpr(node)
assignments.addAll(subAssigns)
replacement
}
else -> {
val variable = getExprVar(node.type, node.position, scope)
val assign = PtAssignment(node.position)
val target = PtAssignTarget(variable.position)
target.add(variable)
assign.add(target)
assign.add(node)
assignments.add(assign)
variable
}
}
}
val newLeft = transformOperand(expr.left)
val newRight = transformOperand(expr.right)
// process the binexpr
val resultVar =
if(expr.type == expr.left.type) {
getExprVar(expr.type, expr.position, scope)
} else {
if(expr.operator in ComparisonOperators && expr.type in ByteDatatypes) {
// this is very common and should be dealth with correctly; byte==0, word>42
val varType = if(expr.left.type in PassByReferenceDatatypes) DataType.UWORD else expr.left.type
getExprVar(varType, expr.position, scope)
}
else if(expr.left.type in PassByReferenceDatatypes && expr.type==DataType.UBYTE) {
// this is common and should be dealth with correctly; for instance "name"=="john"
val varType = if (expr.left.type in PassByReferenceDatatypes) DataType.UWORD else expr.left.type
getExprVar(varType, expr.position, scope)
} else if(expr.left.type equalsSize expr.type) {
getExprVar(expr.type, expr.position, scope)
} else {
TODO("expression type differs from left operand type! got ${expr.left.type} expected ${expr.type} ${expr.position}")
}
}
if(resultVar.name!=(newLeft as? PtIdentifier)?.name) {
// resultvar = left
val assign1 = PtAssignment(newLeft.position)
val target1 = PtAssignTarget(resultVar.position)
target1.add(resultVar)
assign1.add(target1)
assign1.add(newLeft)
assignments.add(assign1)
}
// resultvar {oper}= right
val operator = if(expr.operator in ComparisonOperators) expr.operator else expr.operator+'='
val assign2 = PtAugmentedAssign(operator, newRight.position)
val target2 = PtAssignTarget(resultVar.position)
target2.add(resultVar.copy())
assign2.add(target2)
assign2.add(newRight)
assignments.add(assign2)
return Pair(resultVar, assignments)
}
fun isProperStatement(node: PtNode): Boolean {
return when(node) {
is PtAssignment -> true
is PtAugmentedAssign -> true
is PtBreakpoint -> true
is PtConditionalBranch -> true
is PtForLoop -> true
is PtIfElse -> true
is PtIncludeBinary -> true
is PtInlineAssembly -> true
is PtJump -> true
is PtAsmSub -> true
is PtLabel -> true
is PtSub -> true
is PtVariable -> true
is PtNop -> true
is PtPostIncrDecr -> true
is PtRepeatLoop -> true
is PtReturn -> true
is PtWhen -> true
is PtBuiltinFunctionCall -> node.void
is PtFunctionCall -> node.void
else -> false
}
}
fun transform(node: PtNode, parent: PtNode) {
if(node is PtBinaryExpression) {
node.children.toTypedArray().forEach {
transform(it, node)
}
val (rep, assignments) = transformExpr(node)
var replacement = rep
if(!(rep.type equalsSize node.type)) {
if(rep.type in NumericDatatypes && node.type in ByteDatatypes) {
replacement = PtTypeCast(node.type, node.position)
replacement.add(rep)
} else
TODO("cast replacement type ${rep.type} -> ${node.type}")
}
var idx = parent.children.indexOf(node)
parent.children[idx] = replacement
replacement.parent = parent
// find the statement above which we should insert the assignments
var stmt = node
while(!isProperStatement(stmt))
stmt = stmt.parent
idx = stmt.parent.children.indexOf(stmt)
assignments.reversed().forEach {
stmt.parent.add(idx, it as PtNode)
}
} else {
node.children.toTypedArray().forEach { child -> transform(child, node) }
}
}
program.allBlocks().forEach { block ->
block.children.toTypedArray().forEach {
transform(it, block)
}
}
// add the new variables
newVariables.forEach { (sub, vars) ->
vars.forEach {
sub.add(0, it)
}
}
// extra check to see that all PtBinaryExpressions have been transformed
fun binExprCheck(node: PtNode) {
if(node is PtBinaryExpression)
throw IllegalArgumentException("still got binexpr $node ${node.position}")
node.children.forEach { binExprCheck(it) }
}
binExprCheck(program)
}
@@ -21,6 +21,10 @@ internal class ErrorReporter: IErrorReporter {
messages.add(CompilerMessage(MessageSeverity.WARNING, msg, position))
}
override fun undefined(symbol: List<String>, position: Position) {
err("undefined symbol: ${symbol.joinToString(".")}", position)
}
override fun report() {
var numErrors = 0
var numWarnings = 0
Loaded 100 of 184 files, more files were not shown because too many files have changed in this diff. Show more