Compare commits

...

186 Commits
v8.14 ... v9.2

Author SHA1 Message Date
b19c282269 release 9.2 2023-07-28 01:40:14 +02:00
e520921746 todo 2023-07-26 23:16:43 +02:00
970642244b optimized gfx2.text() for hires 4c mode 2023-07-26 04:17:44 +02:00
3b90be2d9e gfx2.text() per-pixel positioning implemented for screen modes 1 and 5 2023-07-25 00:43:45 +02:00
2f756f1e3a fix and optimize inplace invert and negate 2023-07-24 23:28:32 +02:00
78e84182f0 todo 2023-07-24 22:36:17 +02:00
65a7a8caf8 fix and optimize gfx2.position2(), added cx16.vaddr_clone() 2023-07-24 00:04:47 +02:00
4c6a2f5df9 emphasize index value size on pointer var indexing 2023-07-23 00:11:18 +02:00
3bbc00cc8c more caution notices about symbols in inlined asm 2023-07-22 23:22:06 +02:00
0e781d18fa cx16: added cx16.vaddr_autoincr() and cx16.vaddr_autodecr() 2023-07-21 23:04:21 +02:00
4575a8fffe cx16: added cx16.vaddr_autoincr() and cx16.vaddr_autodecr() 2023-07-21 22:40:07 +02:00
10d0ff252b ignore buildversion changes 2023-07-21 00:14:06 +02:00
c7d54570cc IR: sXX, CONCAT instructions now use 3 register format 2023-07-21 00:07:56 +02:00
7136b33f2e cx16: change reset_system() to use Reset SMC sequence instead of hard reboot 2023-07-20 01:59:20 +02:00
5659742d97 fixed assigning byte to word not clearing msb sometimes 2023-07-16 23:16:32 +02:00
450eaf7c4a fixed lsb() to uword problem 2023-07-16 20:05:59 +02:00
c1aa5d4e47 IR: optimized when statement translation 2023-07-16 12:10:46 +02:00
b3cb9b7fe2 added optimizer to remove needless pha/pla pairs 2023-07-15 22:19:48 +02:00
9cb61fa34d tweaks 2023-07-15 20:46:14 +02:00
7c219d235c fixed possible type mismatch error in when statements 2023-07-14 23:35:58 +02:00
6938c79f88 IR: added CMPI instruction 2023-07-14 23:17:29 +02:00
b8284a147d allow boolean when conditions, optimize into a regular if 2023-07-11 21:33:29 +02:00
15ee90e99c no error about missing target when -vm is used.
also version 9.1
2023-07-11 18:13:49 +02:00
795f80b4ec fix forloop 6502 codegen in case of descending word values 2023-07-11 00:33:12 +02:00
6b6427492d fix forloop 6502 codegen in case of descending word values 2023-07-10 23:10:16 +02:00
6055b8c3dc IR: fix forloop codegen for steps != 1 2023-07-10 21:36:44 +02:00
a98cb50d55 Revert "ir: SCC now sets all bits to 1 (or 0)"
This reverts commit 7245aece4f.
2023-07-09 23:16:13 +02:00
e98bbc1c52 todo 2023-07-09 22:29:54 +02:00
7245aece4f ir: SCC now sets all bits to 1 (or 0) 2023-07-08 23:16:01 +02:00
60cbb02822 vm: actually fix EXT(S) in vm too 2023-07-08 23:05:03 +02:00
4e863ecdac vm: fixed abs() and word-to-string conversion 2023-07-08 22:57:16 +02:00
5037033fcf ir: EXT and EXTS opcodes now have 2 registers to avoid type clash 2023-07-08 22:42:11 +02:00
e6b158bc97 @(..) argument must be of type UWORD 2023-07-08 22:34:47 +02:00
4cc0dfa10b comment 2023-07-08 11:42:29 +02:00
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
d26967a87d ir doc 2023-07-07 22:35:05 +02:00
fc8955941b slight optimization for certain word multiplications 2023-07-07 21:30:37 +02:00
071a80360f ir: fix some problem with comparison against zero 2023-07-07 21:17:28 +02:00
d2154f5f2e remove empty when choices, fixes ir compilation error on those 2023-07-07 20:34:24 +02:00
334d382bfa ir: JUMPI instruction added to support indirect jumps 2023-07-07 19:10:39 +02:00
90c4b00f74 ir: fix any() all() reverse() sort() on memory mapped arrays and on byte arrays 2023-07-07 17:25:32 +02:00
71261525e8 fix containment check on memory mapped arrays 2023-07-07 17:07:34 +02:00
3126959576 ir: several fixes 2023-07-07 16:53:32 +02:00
02e51d8282 ir: fix initial chunk linking 2023-07-07 00:30:56 +02:00
ffb2027a19 repeat loop count now always rounded to integer 2023-07-06 23:58:02 +02:00
70c9ab9074 upgrade libraries 2023-07-06 23:33:58 +02:00
6d1fdf1ba6 upgrade to Kotlin 1.9.0 2023-07-06 23:03:47 +02:00
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
b4e94ae4dd optimizer: avoid symbol name clash when inlining subroutine 2023-07-05 23:15:04 +02:00
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
e705a8bd89 discord info 2023-07-04 23:50:16 +02:00
b3bdfb7f1f more info about building the compiler 2023-07-04 22:41:38 +02:00
5af1aeb092 added block comment /* ...... */ 2023-07-04 00:46:29 +02:00
be64fa674a doc 2023-07-03 22:44:50 +02:00
204f5591a9 todos 2023-07-03 21:57:32 +02:00
ee3e3a3a40 optimize text rendering in gfx2 2023-07-03 21:45:09 +02:00
f9200a2b75 fix IR loader for romsub calls (calls to an address) 2023-07-02 23:41:15 +02:00
f570b70827 fix type error with returning an array from a subroutine returning uword 2023-07-02 22:09:19 +02:00
0db141eeac todo 2023-07-02 21:19:33 +02:00
acb2ee53bb Merge branch 'prefixing' 2023-07-02 21:15:30 +02:00
c544b7f5ba fixing up p8_ prefixing 2023-07-02 21:15:05 +02:00
c0024e97e5 fix doc version 2023-07-02 21:01:11 +02:00
bdf8aa9168 get rid of newexpr compiler option 2023-07-02 15:26:04 +02:00
de5ce0f515 tiny optimization and doc 2023-07-02 11:17:18 +02:00
bb95484c8a uniform symbol prefixing with p8_ 2023-07-02 06:15:09 +02:00
cad18b8a3a uniform symbol prefixing with p8_ 2023-07-02 06:15:02 +02:00
0f6a98751a tiny optimization 2023-07-02 06:13:22 +02:00
aac5a4c27f optimize word repeat loop codegen 2023-07-02 04:51:22 +02:00
d3f6415387 vm: fix repeat 256 2023-07-02 02:38:35 +02:00
04da44eb98 fix certain inefficient codegen when assigning a type casted value 2023-06-29 22:56:26 +02:00
7649be97b1 add git hash to compiler header output 2023-06-29 21:01:02 +02:00
c9ef777e0f fix rest of possible temp variable conflicts 2023-06-28 23:24:48 +02:00
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
30c531b39e attempting to fix array expression inplace assign 2023-06-28 00:38:08 +02:00
bf703a8a66 unittest 2023-06-27 23:43:35 +02:00
e7b631b087 allow comment lines inside array initializer value 2023-06-27 23:30:37 +02:00
a9f5dc036c fix cpu stack corruption in array assignment codegen 2023-06-27 18:49:49 +02:00
0a83b51e00 allow more curly brace styles 2023-06-27 01:59:22 +02:00
eab63ecc6c allow curly brace on next line also after subroutine and when 2023-06-27 01:29:25 +02:00
b0794cf35e added hiram bank number to -varshigh 2023-06-27 00:27:34 +02:00
5b9e71a27d docs 2023-06-25 21:35:30 +02:00
eae41de27d improve errors generated for undefined symbols 2023-06-25 15:19:51 +02:00
e9163aa3a7 added cx16.save_virtual_registers() and cx16.restore_virtual_registers() 2023-06-24 21:04:47 +02:00
8c617515ba don't prefix 3-letter symbols too aggressively (could cause some compilation errors) 2023-06-23 23:36:59 +02:00
04e4e71f2e uword == str is now possible (sugar for string.compare) 2023-06-22 00:20:30 +02:00
a587482edf optimize dangling else 2023-06-18 13:46:02 +02:00
0aac9350d5 rename math.atan() to math.atan2() 2023-06-18 13:05:36 +02:00
f56c12ee4e cx16 spotlight example 2023-06-18 12:49:22 +02:00
4bb9ae61f2 library source links 2023-06-18 02:31:45 +02:00
ff7f3484e4 atan 2023-06-17 23:01:47 +02:00
5da3abe6b4 fix silent typecast on return statements that could lose data (word->byte) 2023-06-17 14:44:36 +02:00
c0b398e0ce add various math.atan() routines 2023-06-17 00:43:33 +02:00
3de10adac2 bump required 64tass version 2023-06-16 23:24:31 +02:00
1b573d6552 add note about lacking fp parse routine 2023-06-16 00:12:52 +02:00
2a96f93919 vm: fix compiler error when dealing with label 2023-06-14 22:14:47 +02:00
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
b9abf37a7e fix invalid code when subroutines are defined in a repeat loop 2023-06-13 00:46:32 +02:00
373cbb4144 gradle build error explained 2023-06-11 17:51:18 +02:00
a521982576 fix subroutine inline problem with strings 2023-06-09 21:45:05 +02:00
a77fde577c update GitHub action steps 2023-06-09 19:51:04 +02:00
ea6926e57d fix float expression crash: fl = abs/sqrt (fl)+0.5 2023-06-09 19:28:34 +02:00
ba25b7fee6 fix diskio.diskname(). cx16: add diskio.curdir() 2023-06-07 22:38:51 +02:00
7ee162d98b preparing version 9.0 2023-06-05 19:47:00 +02:00
380f557c45 vm: implement split incr/decr 2023-06-03 22:22:13 +02:00
1bdae53f4e fix unit tests 2023-06-03 21:39:34 +02:00
9314c346da -target option is now required; c64 no longer the default 2023-06-03 19:14:45 +02:00
bfaad1388c IR: handle split arrays without new custom opcodes 2023-06-03 01:51:02 +02:00
0b580ad05d v9 upgrading doc 2023-06-01 20:23:04 +02:00
bb35a80177 %option splitarrays now also at module level 2023-05-31 21:50:41 +02:00
24fc95ac81 fix atari target syslib 2023-05-31 20:58:00 +02:00
8f864417c4 added %option splitarrays (block level) 2023-05-31 18:49:21 +02:00
bb9d29b061 fix an array literal assignment type error for word arrays 2023-05-30 22:46:37 +02:00
b9d8ec1463 add -splitarrays command line option 2023-05-30 19:08:34 +02:00
1842a7660d fix compiler crash on missing arguments for clamp,min,max 2023-05-30 18:13:58 +02:00
5caa2f5536 attempt to get newer 64tass from debian testing repo 2023-05-29 23:46:47 +02:00
d6078be8b7 attempt to get newer 64tass from debian testing repo 2023-05-29 23:44:10 +02:00
cf60723f14 attempt to get newer 64tass from debian testing repo 2023-05-29 23:43:08 +02:00
f7ff0a2b1d attempt to get newer 64tass from debian testing repo 2023-05-29 23:39:00 +02:00
cc49664b2f attempt to get newer 64tass from debian testing repo 2023-05-29 23:34:34 +02:00
99fe74f026 attempt to get newer 64tass from debian testing repo 2023-05-29 23:31:23 +02:00
b021869eeb attempt to get newer 64tass from debian testing repo 2023-05-29 23:24:48 +02:00
b8806d163b attempt to get newer 64tass from debian testing repo 2023-05-29 23:22:05 +02:00
1116aae1de attempt to get newer 64tass from debian testing repo 2023-05-29 23:18:15 +02:00
5e5f60253b attempt to get newer 64tass from debian testing repo 2023-05-29 23:14:22 +02:00
bbc02752c9 use split word arrays in various examples, fix codegen issue, docs 2023-05-29 15:34:33 +02:00
9896bc110e fix some split array issues in 6502 codegen 2023-05-28 22:49:33 +02:00
ca60f8ecdd Merge branch 'master' into split-arrays 2023-05-28 22:35:16 +02:00
544acd1e35 Merge branch 'v8_maintenance' 2023-05-28 22:30:52 +02:00
82898f7bba fix some split array issues in 6502 codegen 2023-05-28 22:24:56 +02:00
d61283a8bc Merge branch 'master' into split-arrays 2023-05-28 14:25:37 +02:00
1ee3f826cc fix sqrt() regression 2023-05-28 14:23:47 +02:00
4a00a5ba9e use split word arrays in various examples 2023-05-28 13:51:58 +02:00
39eda67867 Merge branch 'master' into split-arrays
# Conflicts:
#	examples/test.p8
2023-05-28 13:28:43 +02:00
a99d38fdaa Merge branch 'v8_maintenance'
# Conflicts:
#	examples/test.p8
2023-05-28 13:26:05 +02:00
3ac9036c79 more split array stuff for 6502 2023-05-27 22:44:45 +02:00
c94e292176 more split array stuff 2023-05-27 12:47:11 +02:00
91d87c2d9b Merge branch 'master' into split-arrays 2023-05-26 20:22:30 +02:00
ff472f69c0 update gradle wrapper to 8.1.1 2023-05-26 20:21:34 +02:00
e18119e24c Merge branch 'master' into split-arrays 2023-05-26 19:25:57 +02:00
4a592dc64c kotlin 1.8.21 2023-05-26 19:20:56 +02:00
5c75b19c5d fix kotlin version IDE warning 2023-05-26 19:13:21 +02:00
52a77db60f adding split array type 2023-05-26 19:11:07 +02:00
0513c250fb Merge branch 'v8_maintenance' 2023-05-23 20:42:51 +02:00
cdbccad21e optimized gfx2 plot and horizontal_line a bit more 2023-05-23 20:29:17 +02:00
e15bc68c9b added gfx2.fill() flood fill routine 2023-05-23 00:50:10 +02:00
8bffd7672d added sys.irqsafe_set_irqd()/irqsafe_clear_irqd() 2023-05-22 21:13:20 +02:00
61df5b3060 Merge branch 'v8_maintenance'
# Conflicts:
#	compiler/res/prog8lib/cx16/syslib.p8
2023-05-22 20:43:05 +02:00
0c94e377fc Merge branch 'v8_maintenance' 2023-05-21 16:09:31 +02:00
b24f2f1756 Merge branch 'v8_maintenance'
# Conflicts:
#	compiler/res/prog8lib/cx16/syslib.p8
#	examples/test.p8
2023-05-21 15:05:17 +02:00
061617122a Merge branch 'v8_maintenance'
# Conflicts:
#	examples/test.p8
2023-05-20 18:07:57 +02:00
06d1570142 cx16: added diskio.save_raw() headerless save routine 2023-05-20 00:00:50 +02:00
093c370faa todo 2023-05-19 01:26:15 +02:00
aec9574737 Merge branch 'v8_maintenance'
# Conflicts:
#	compiler/res/version.txt
#	docs/source/todo.rst
#	examples/test.p8
2023-05-18 22:47:06 +02:00
300e2fe9f8 IR: wrong attempt at optimizing register usage by reusing registers inside different code chunks 2023-05-18 21:57:21 +02:00
91e1643627 update 3rd party libraries 2023-05-18 11:47:30 +02:00
91421b0c62 IR handy sequence shortcut functions 2023-05-18 11:32:20 +02:00
40f611664f upgr 2023-05-18 00:04:31 +02:00
dcba4f4098 fix resultregister crash 2023-05-18 00:00:37 +02:00
c098ad2b3b fix vm minf/maxf 2023-05-17 23:18:14 +02:00
b43223cb7a added clamp() builtin function and floats.clampf() 2023-05-17 23:12:58 +02:00
e243531dab upgrading 2023-05-17 00:49:47 +02:00
1af38e62bc removed floats.fabs() and floats.sqrt()/fsqrt() 2023-05-17 00:46:15 +02:00
f37f062cdc fix for loop pre-check 2023-05-17 00:33:55 +02:00
05d152746f Merge branch 'master' into version_9 2023-05-15 22:43:03 +02:00
b4963b725b Merge branch 'master' into version_9
# Conflicts:
#	compiler/res/version.txt
2023-05-14 22:19:23 +02:00
6a664a7e15 Merge branch 'master' into version_9 2023-05-14 21:03:08 +02:00
85cf0e311c Merge branch 'master' into version_9
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
#	docs/source/todo.rst
#	intermediate/src/prog8/intermediate/IRInstructions.kt
2023-05-14 20:47:09 +02:00
1e469b3b0f Merge branch 'master' into version_9
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2023-05-09 22:45:21 +02:00
bd2bcb6994 Merge branch 'master' into version_9
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt
#	compiler/res/prog8lib/c128/syslib.p8
#	compiler/res/prog8lib/c64/syslib.p8
#	compiler/res/prog8lib/cx16/syslib.p8
#	docs/source/todo.rst
#	examples/test.p8
#	intermediate/src/prog8/intermediate/IRInstructions.kt
2023-05-08 23:17:52 +02:00
fd1e9971e4 asmsub Pc params and returnvalue must be boolean 2023-05-07 22:59:30 +02:00
21bc505d85 for loops no longer execute when from var already reached beyond the end 2023-05-03 00:43:03 +02:00
3d69a95c49 IR: fix for-loop codegen when step<0 2023-05-02 23:09:42 +02:00
d81fdf6d6b for loops... 2023-05-02 22:55:58 +02:00
87d3109ffb diskio f_seek_w() abandoned due to unreliability 2023-05-02 19:33:49 +02:00
180dbbb521 cleaning up the diskio modules
for cx16: removed cx16diskio (merged everything into its regular diskio module)
for cx16: the load() and load_raw() routines that took an extra ram bank parameter are gone. You have to cx16.rambank() yourself before calling load().
2023-05-02 03:31:11 +02:00
24aac7cee5 cleaning up the diskio modules 2023-05-02 02:15:22 +02:00
53e18a5387 Api change: drivenumber parameter removed from all routines in diskio and cx16diskio modules 2023-05-02 01:48:56 +02:00
92062d056d divmod() now works on multiple data types including float.
divmodw() has been removed
2023-05-02 01:19:53 +02:00
06368ab0a1 sqrt() now works on multiple data types including float.
no need to use floats.sqrtf() anymore
2023-05-02 01:19:53 +02:00
38efe25c68 abs() now works on multiple data types including float.
no need to use floats.fabs() anymore
2023-05-02 01:19:53 +02:00
319079de7a sqrt 2023-05-02 01:19:53 +02:00
025bf900a5 min max docs, added floats.minf() and maxf() 2023-05-02 01:19:53 +02:00
2885f4f7b1 fix 2023-05-02 01:19:53 +02:00
c07eda15b1 adding min() and max() 2023-05-02 01:19:53 +02:00
4274296cf3 api change: new 'cbm' module that now contains the common CBM kernal variables and routines. 2023-05-02 01:19:53 +02:00
76a203d4df api change: rename builtin func sqrt16 to sqrtw 2023-05-02 01:19:53 +02:00
246 changed files with 8068 additions and 3804 deletions

View File

@ -10,13 +10,18 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Install 64tass - name: build and install recent 64tass
run: sudo apt-get update -y && sudo apt-get install -y 64tass run: |
sudo apt-get install -y make build-essential
git clone --depth=1 https://github.com/irmen/64tass
cd 64tass
make -j4
sudo make install
- name: Set up JDK 11 - name: Set up JDK 11
uses: actions/setup-java@v2 uses: actions/setup-java@v3
with: with:
java-version: 11 java-version: 11
distribution: adopt distribution: adopt

3
.gitignore vendored
View File

@ -15,6 +15,7 @@ out/
parser/**/*.interp parser/**/*.interp
parser/**/*.tokens parser/**/*.tokens
parser/**/*.java parser/**/*.java
compiler/src/prog8/buildversion/*
*.py[cod] *.py[cod]
*.egg *.egg
*.egg-info *.egg-info
@ -29,6 +30,8 @@ parsetab.py
compiler/lib/ compiler/lib/
.gradle .gradle
**/BuildVersion.kt
/prog8compiler.jar /prog8compiler.jar
sd*.img sd*.img
*.d64 *.d64

2
.idea/kotlinc.xml generated
View File

@ -4,6 +4,6 @@
<option name="jvmTarget" value="11" /> <option name="jvmTarget" value="11" />
</component> </component>
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.21-release-380" /> <option name="version" value="1.9.0-release-358" />
</component> </component>
</project> </project>

View File

@ -1,13 +1,13 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="antlr.antlr4" type="repository"> <library name="antlr.antlr4" type="repository">
<properties maven-id="org.antlr:antlr4:4.12.0"> <properties maven-id="org.antlr:antlr4:4.13.0">
<exclude> <exclude>
<dependency maven-id="com.ibm.icu:icu4j" /> <dependency maven-id="com.ibm.icu:icu4j" />
</exclude> </exclude>
</properties> </properties>
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.12.0/antlr4-4.12.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4/4.13.0/antlr4-4.13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.12.0/antlr4-runtime-4.12.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr4-runtime/4.13.0/antlr4-runtime-4.13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/antlr/antlr-runtime/3.5.3/antlr-runtime-3.5.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/antlr/ST4/4.3.4/ST4-4.3.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/abego/treelayout/org.abego.treelayout.core/1.0.3/org.abego.treelayout.core-1.0.3.jar!/" />

View File

@ -1,21 +1,21 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="io.kotest.assertions.core.jvm" type="repository"> <library name="io.kotest.assertions.core.jvm" type="repository">
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.5.5" /> <properties maven-id="io.kotest:kotest-assertions-core-jvm:5.6.2" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.5.5/kotest-assertions-core-jvm-5.5.5.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.6.2/kotest-assertions-core-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.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.6.21/kotlin-stdlib-1.6.21.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!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.6.2/kotest-assertions-shared-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.5.5/kotest-assertions-shared-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.4/kotlinx-coroutines-jdk8-1.6.4.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.5.5/kotest-common-jvm-5.5.5.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.6.2/kotest-common-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.5.5/kotest-assertions-api-jvm-5.5.5.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.6.2/kotest-assertions-api-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />

View File

@ -1,22 +1,22 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="io.kotest.property.jvm" type="repository"> <library name="io.kotest.property.jvm" type="repository">
<properties maven-id="io.kotest:kotest-property-jvm:5.5.5" /> <properties maven-id="io.kotest:kotest-property-jvm:5.6.2" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.5.5/kotest-property-jvm-5.5.5.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.6.2/kotest-property-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.5.5/kotest-common-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.5.5/kotest-assertions-shared-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.5.5/kotest-assertions-api-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.4/kotlinx-coroutines-jdk8-1.6.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.4/rgxgen-1.4.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.4/rgxgen-1.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.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.6.21/kotlin-stdlib-1.6.21.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!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.6.2/kotest-common-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.6.2/kotest-assertions-shared-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.6.2/kotest-assertions-api-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />

View File

@ -1,55 +1,42 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="io.kotest.runner.junit5.jvm" type="repository"> <library name="io.kotest.runner.junit5.jvm" type="repository">
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.5.5" /> <properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.6.2" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.5.5/kotest-runner-junit5-jvm-5.5.5.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.6.2/kotest-runner-junit5-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.5.5/kotest-framework-api-jvm-5.5.5.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.6.2/kotest-framework-api-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.6.4/kotlinx-coroutines-test-jvm-1.6.4.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.6.2/kotest-assertions-shared-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.5.5/kotest-assertions-shared-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.7.0/kotlinx-coroutines-test-jvm-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.5.5/kotest-common-jvm-5.5.5.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.6.2/kotest-common-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.5.5/kotest-framework-engine-jvm-5.5.5.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.6.2/kotest-framework-engine-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.154/classgraph-4.8.154.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.157/classgraph-4.8.157.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.4/kotlinx-coroutines-debug-1.6.4.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.7.0/kotlinx-coroutines-debug-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.10.9/byte-buddy-1.10.9.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.10.9/byte-buddy-1.10.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.10.9/byte-buddy-agent-1.10.9.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.10.9/byte-buddy-agent-1.10.9.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.5.5/kotest-framework-discovery-jvm-5.5.5.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.6.2/kotest-framework-discovery-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.5.5/kotest-assertions-core-jvm-5.5.5.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.6.2/kotest-assertions-core-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.4/kotlinx-coroutines-jdk8-1.6.4.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.7.0/kotlinx-coroutines-jdk8-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.5.5/kotest-assertions-api-jvm-5.5.5.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.6.2/kotest-assertions-api-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.5.5/kotest-extensions-jvm-5.5.5.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.6.2/kotest-extensions-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-jvm/1.13.1/mockk-jvm-1.13.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.6.2/kotest-framework-concurrency-jvm-5.6.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.13.1/mockk-dsl-jvm-1.13.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.0/kotlinx-coroutines-core-jvm-1.7.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-jvm/1.13.1/mockk-agent-jvm-1.13.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/23.0.0/annotations-23.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.2/objenesis-3.2.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.8.2/junit-platform-engine-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-agent-api-jvm/1.13.1/mockk-agent-api-jvm-1.13.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.8.2/junit-platform-commons-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-core-jvm/1.13.1/mockk-core-jvm-1.13.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.8.2/junit-platform-suite-api-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/junit/junit/4.13.2/junit-4.13.2.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.8.2/junit-platform-launcher-1.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter/5.8.2/junit-jupiter-5.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-params/5.8.2/junit-jupiter-params-5.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-engine/5.8.2/junit-jupiter-engine-5.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.6.4/kotlinx-coroutines-core-1.6.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.5.5/kotest-framework-concurrency-jvm-5.5.5.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.4/kotlinx-coroutines-core-jvm-1.6.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.7.2/junit-platform-commons-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-suite-api/1.7.2/junit-platform-suite-api-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-launcher/1.7.2/junit-platform-launcher-1.7.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.8.2/junit-jupiter-api-5.8.2.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.8.2/junit-jupiter-api-5.8.2.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.21/kotlin-stdlib-jdk8-1.6.21.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.6.21/kotlin-stdlib-1.6.21.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!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.21/kotlin-stdlib-jdk7-1.6.21.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.21/kotlin-reflect-1.6.21.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.10/kotlin-reflect-1.8.10.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />

View File

@ -1,13 +1,13 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="michael.bull.kotlin.result.jvm" type="repository"> <library name="michael.bull.kotlin.result.jvm" type="repository">
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" /> <properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.16/kotlin-result-jvm-1.1.16.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-common/1.6.20/kotlin-stdlib-common-1.6.20.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-jdk8/1.6.20/kotlin-stdlib-jdk8-1.6.20.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/kotlin/kotlin-stdlib/1.6.20/kotlin-stdlib-1.6.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.20/kotlin-stdlib-jdk7-1.6.20.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.8.10/kotlin-stdlib-jdk7-1.8.10.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.10/kotlin-stdlib-common-1.8.10.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />

View File

@ -14,6 +14,21 @@ Documentation
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at: Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
https://prog8.readthedocs.io/ 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 Software license
---------------- ----------------
GNU GPL 3.0 (see file LICENSE), with exception for generated code: GNU GPL 3.0 (see file LICENSE), with exception for generated code:

View File

@ -26,7 +26,7 @@ compileTestKotlin {
dependencies { dependencies {
// should have no dependencies to other modules // should have no dependencies to other modules
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
} }
sourceSets { sourceSets {

View File

@ -80,6 +80,16 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
} }
override fun lookup(scopedName: String) = flat[scopedName] 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
}
}
} }

View File

@ -94,6 +94,9 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
initialString = null initialString = null
numElements = node.arraySize?.toInt() 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) StStaticVariable(node.name, node.type, initialNumeric, initialString, initialArray, numElements, node.zeropage, node)
} }
is PtBuiltinFunctionCall -> { is PtBuiltinFunctionCall -> {

View File

@ -37,16 +37,17 @@ class PtNodeGroup : PtNode(Position.DUMMY)
sealed class PtNamedNode(var name: String, position: Position): PtNode(position) { sealed class PtNamedNode(var name: String, position: Position): PtNode(position) {
// Note that as an exception, the 'name' is not read-only // Note that as an exception, the 'name' is not read-only
// but a var. This is to allow for cheap node renames. // but a var. This is to allow for cheap node renames.
val scopedName: String by lazy { val scopedName: String
var namedParent: PtNode = this.parent get() {
if(namedParent is PtProgram) var namedParent: PtNode = this.parent
name return if(namedParent is PtProgram)
else { name
while (namedParent !is PtNamedNode) else {
namedParent = namedParent.parent while (namedParent !is PtNamedNode)
namedParent.scopedName + "." + name namedParent = namedParent.parent
namedParent.scopedName + "." + name
}
} }
}
} }
@ -63,7 +64,9 @@ class PtProgram(
children.asSequence().filterIsInstance<PtBlock>() children.asSequence().filterIsInstance<PtBlock>()
fun entrypoint(): PtSub? = 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 address: UInt?,
val library: Boolean, val library: Boolean,
val forceOutput: Boolean, val forceOutput: Boolean,
val noSymbolPrefixing: Boolean,
val alignment: BlockAlignment, val alignment: BlockAlignment,
val source: SourceCode, // taken from the module the block is defined in. val source: SourceCode, // taken from the module the block is defined in.
position: Position position: Position

View File

@ -1,9 +1,6 @@
package prog8.code.ast package prog8.code.ast
import prog8.code.core.DataType import prog8.code.core.*
import prog8.code.core.Encoding
import prog8.code.core.NumericDatatypes
import prog8.code.core.Position
import java.util.* import java.util.*
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.round import kotlin.math.round
@ -28,7 +25,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
infix fun isSameAs(other: PtExpression): Boolean { infix fun isSameAs(other: PtExpression): Boolean {
return when(this) { return when(this) {
is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier is PtAddressOf -> other is PtAddressOf && other.type==type && other.identifier isSameAs identifier
is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index is PtArrayIndexer -> other is PtArrayIndexer && other.type==type && other.variable isSameAs variable && other.index isSameAs index && other.splitWords==splitWords
is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right is PtBinaryExpression -> other is PtBinaryExpression && other.left isSameAs left && other.right isSameAs right
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
@ -51,7 +48,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
this.name == target.identifier!!.name this.name == target.identifier!!.name
} }
target.array != null && this is PtArrayIndexer -> { target.array != null && this is PtArrayIndexer -> {
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
} }
else -> false else -> false
} }
@ -118,6 +115,9 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
val index: PtExpression val index: PtExpression
get() = children[1] as PtExpression get() = children[1] as PtExpression
val splitWords: Boolean
get() = variable.type in SplitWordArrayTypes
init { init {
require(elementType in NumericDatatypes) require(elementType in NumericDatatypes)
} }

View File

@ -17,7 +17,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
is PtConditionalBranch -> "if_${node.condition.name.lowercase()}" is PtConditionalBranch -> "if_${node.condition.name.lowercase()}"
is PtAddressOf -> "&" is PtAddressOf -> "&"
is PtArray -> "array len=${node.children.size} ${type(node.type)}" is PtArray -> "array len=${node.children.size} ${type(node.type)}"
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)}" is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}" is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
is PtBuiltinFunctionCall -> { is PtBuiltinFunctionCall -> {
val str = if(node.void) "void " else "" val str = if(node.void) "void " else ""
@ -96,13 +96,14 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
str str
} }
is PtVariable -> { is PtVariable -> {
val split = if(node.type in SplitWordArrayTypes) "@split" else ""
val str = if(node.arraySize!=null) { val str = if(node.arraySize!=null) {
val eltType = ArrayToElementTypes.getValue(node.type) val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[${node.arraySize}] ${node.name}" "${eltType.name.lowercase()}[${node.arraySize}] $split ${node.name}"
} }
else if(node.type in ArrayDatatypes) { else if(node.type in ArrayDatatypes) {
val eltType = ArrayToElementTypes.getValue(node.type) val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[] ${node.name}" "${eltType.name.lowercase()}[] $split ${node.name}"
} }
else else
"${node.type.name.lowercase()} ${node.name}" "${node.type.name.lowercase()} ${node.name}"

View File

@ -68,7 +68,6 @@ class FSignature(val pure: Boolean, // does it have side effects?
} }
} }
val BuiltinFunctions: Map<String, FSignature> = mapOf( val BuiltinFunctions: Map<String, FSignature> = mapOf(
// this set of function have no return value and operate in-place: // this set of function have no return value and operate in-place:
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), "rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
@ -80,19 +79,41 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
// cmp returns a status in the carry flag, but not a proper return value // cmp returns a status in the carry flag, but not a proper return value
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null), "cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE), "prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE),
"abs" to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD), "abs" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE),
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD),
"abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD), "len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
// normal functions follow: // 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), "sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
"sqrt16" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE), "sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), null),
"divmod" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null), "sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"divmodw" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null), "sqrt__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
"sqrt__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT),
"divmod" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divident", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("division", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
"divmod__ubyte" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UBYTE)), FParam("divident", arrayOf(DataType.UBYTE)), FParam("division", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null),
"divmod__uword" to FSignature(false, listOf(FParam("number", arrayOf(DataType.UWORD)), FParam("divident", arrayOf(DataType.UWORD)), FParam("division", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null),
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE), "any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE), "all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), "lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), "msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD), "mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
"clamp" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), null),
"clamp__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), DataType.BYTE),
"clamp__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE)), FParam("minimum", arrayOf(DataType.UBYTE)), FParam("maximum", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"clamp__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD)), FParam("minimum", arrayOf(DataType.WORD)), FParam("maximum", arrayOf(DataType.WORD))), DataType.WORD),
"clamp__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD)), FParam("minimum", arrayOf(DataType.UWORD)), FParam("maximum", arrayOf(DataType.UWORD))), DataType.UWORD),
"min" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
"min__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
"min__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"min__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
"min__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
"max" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null),
"max__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE),
"max__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE),
"max__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD),
"max__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD),
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE), "peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD), "peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null), "poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),

View File

@ -19,8 +19,8 @@ class CompilationOptions(val output: OutputType,
var asmQuiet: Boolean = false, var asmQuiet: Boolean = false,
var asmListfile: Boolean = false, var asmListfile: Boolean = false,
var experimentalCodegen: Boolean = false, var experimentalCodegen: Boolean = false,
var varsHigh: Boolean = false, var varsHighBank: Int? = null,
var useNewExprCode: Boolean = false, var splitWordArrays: Boolean = false,
var evalStackBaseAddress: UInt? = null, var evalStackBaseAddress: UInt? = null,
var outputDir: Path = Path(""), var outputDir: Path = Path(""),
var symbolDefs: Map<String, String> = emptyMap() var symbolDefs: Map<String, String> = emptyMap()

View File

@ -11,7 +11,9 @@ enum class DataType {
ARRAY_UB, // pass by reference ARRAY_UB, // pass by reference
ARRAY_B, // pass by reference ARRAY_B, // pass by reference
ARRAY_UW, // pass by reference ARRAY_UW, // pass by reference
ARRAY_UW_SPLIT, // pass by reference, lo/hi byte split
ARRAY_W, // pass by reference ARRAY_W, // pass by reference
ARRAY_W_SPLIT, // pass by reference, lo/hi byte split
ARRAY_F, // pass by reference ARRAY_F, // pass by reference
ARRAY_BOOL, // pass by reference ARRAY_BOOL, // pass by reference
UNDEFINED; UNDEFINED;
@ -119,12 +121,14 @@ val IntegerDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWO
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL) val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT, DataType.BOOL)
val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT) val NumericDatatypesNoBool = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT) val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F, DataType.ARRAY_BOOL) val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL)
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD) val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT)
val IterableDatatypes = arrayOf( val IterableDatatypes = arrayOf(
DataType.STR, DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
DataType.ARRAY_F, DataType.ARRAY_BOOL DataType.ARRAY_F, DataType.ARRAY_BOOL
) )
val PassByValueDatatypes = NumericDatatypes val PassByValueDatatypes = NumericDatatypes
@ -135,6 +139,8 @@ val ArrayToElementTypes = mapOf(
DataType.ARRAY_UB to DataType.UBYTE, DataType.ARRAY_UB to DataType.UBYTE,
DataType.ARRAY_W to DataType.WORD, DataType.ARRAY_W to DataType.WORD,
DataType.ARRAY_UW to DataType.UWORD, DataType.ARRAY_UW to DataType.UWORD,
DataType.ARRAY_W_SPLIT to DataType.WORD,
DataType.ARRAY_UW_SPLIT to DataType.UWORD,
DataType.ARRAY_F to DataType.FLOAT, DataType.ARRAY_F to DataType.FLOAT,
DataType.ARRAY_BOOL to DataType.BOOL DataType.ARRAY_BOOL to DataType.BOOL
) )

View File

@ -3,6 +3,7 @@ package prog8.code.core
interface IErrorReporter { interface IErrorReporter {
fun err(msg: String, position: Position) fun err(msg: String, position: Position)
fun warn(msg: String, position: Position) fun warn(msg: String, position: Position)
fun undefined(symbol: List<String>, position: Position)
fun noErrors(): Boolean fun noErrors(): Boolean
fun report() fun report()
fun finalizeNumErrors(numErrors: Int, numWarnings: Int) { fun finalizeNumErrors(numErrors: Int, numWarnings: Int) {

View File

@ -28,9 +28,9 @@ dependencies {
implementation project(':codeCore') implementation project(':codeCore')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5' testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
} }
sourceSets { sourceSets {

View File

@ -3,6 +3,7 @@ package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold import com.github.michaelbull.result.fold
import prog8.code.StNodeType import prog8.code.StNodeType
import prog8.code.SymbolTable import prog8.code.SymbolTable
import prog8.code.SymbolTableMaker
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.* import prog8.codegen.cpu6502.assignment.*
@ -14,18 +15,180 @@ import kotlin.io.path.writeLines
internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1" internal const val subroutineFloatEvalResultVar1 = "prog8_float_eval_result1"
internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2" internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
class AsmGen6502: ICodeGeneratorBackend { class AsmGen6502(val prefixSymbols: Boolean): ICodeGeneratorBackend {
override fun generate( override fun generate(
program: PtProgram, program: PtProgram,
symbolTable: SymbolTable, symbolTable: SymbolTable,
options: CompilationOptions, options: CompilationOptions,
errors: IErrorReporter errors: IErrorReporter
): IAssemblyProgram? { ): 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() 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
if(name.endsWith("concat_string"))
println("CONCAT ${this.position}")
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 ( class AsmGen6502Internal (
val program: PtProgram, val program: PtProgram,
internal val symbolTable: SymbolTable, internal val symbolTable: SymbolTable,
@ -362,13 +525,15 @@ class AsmGen6502Internal (
internal fun loadScaledArrayIndexIntoRegister( internal fun loadScaledArrayIndexIntoRegister(
expr: PtArrayIndexer, expr: PtArrayIndexer,
elementDt: DataType, elementDt: DataType,
register: CpuRegister, register: CpuRegister
addOneExtra: Boolean = false
) { ) {
val reg = register.toString().lowercase() val reg = register.toString().lowercase()
val indexnum = expr.index.asConstInteger() val indexnum = expr.index.asConstInteger()
if (indexnum != null) { if (indexnum != null) {
val indexValue = indexnum * options.compTarget.memorySize(elementDt) + if (addOneExtra) 1 else 0 val indexValue = if(expr.splitWords)
indexnum
else
indexnum * options.compTarget.memorySize(elementDt)
out(" ld$reg #$indexValue") out(" ld$reg #$indexValue")
return return
} }
@ -377,77 +542,42 @@ class AsmGen6502Internal (
?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.index.position}") ?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.index.position}")
val indexName = asmVariableName(indexVar) val indexName = asmVariableName(indexVar)
if (addOneExtra) {
// add 1 to the result if(expr.splitWords) {
when (elementDt) { when (register) {
in ByteDatatypes -> { CpuRegister.A -> out(" lda $indexName")
out(" ldy $indexName | iny") CpuRegister.X -> out(" ldx $indexName")
when (register) { CpuRegister.Y -> out(" ldy $indexName")
CpuRegister.A -> out(" tya")
CpuRegister.X -> out(" tyx")
CpuRegister.Y -> {
}
}
}
in WordDatatypes -> {
out(" lda $indexName | sec | rol a")
when (register) {
CpuRegister.A -> {
}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
DataType.FLOAT -> {
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
out(
"""
lda $indexName
asl a
asl a
sec
adc $indexName"""
)
when (register) {
CpuRegister.A -> {
}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
else -> throw AssemblyError("weird dt")
} }
} else { return
when (elementDt) { }
in ByteDatatypes -> out(" ld$reg $indexName")
in WordDatatypes -> { when (elementDt) {
out(" lda $indexName | asl a") in ByteDatatypes -> out(" ld$reg $indexName")
when (register) { in WordDatatypes -> {
CpuRegister.A -> { out(" lda $indexName | asl a")
} when (register) {
CpuRegister.X -> out(" tax") CpuRegister.A -> {}
CpuRegister.Y -> out(" tay") CpuRegister.X -> out(" tax")
} CpuRegister.Y -> out(" tay")
} }
DataType.FLOAT -> {
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
out(
"""
lda $indexName
asl a
asl a
clc
adc $indexName"""
)
when (register) {
CpuRegister.A -> {
}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
else -> throw AssemblyError("weird dt")
} }
DataType.FLOAT -> {
require(options.compTarget.memorySize(DataType.FLOAT) == 5) {"invalid float size ${expr.position}"}
out("""
lda $indexName
asl a
asl a
clc
adc $indexName"""
)
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
else -> throw AssemblyError("weird dt")
} }
} }
@ -546,7 +676,6 @@ class AsmGen6502Internal (
private fun translate(stmt: PtIfElse) { private fun translate(stmt: PtIfElse) {
val condition = stmt.condition as? PtBinaryExpression val condition = stmt.condition as? PtBinaryExpression
if(condition!=null) { if(condition!=null) {
require(!options.useNewExprCode)
requireComparisonExpression(condition) // IfStatement: condition must be of form 'x <comparison> <value>' requireComparisonExpression(condition) // IfStatement: condition must be of form 'x <comparison> <value>'
if (stmt.elseScope.children.isEmpty()) { if (stmt.elseScope.children.isEmpty()) {
val jump = stmt.ifScope.children.singleOrNull() val jump = stmt.ifScope.children.singleOrNull()
@ -674,65 +803,48 @@ class AsmGen6502Internal (
private fun repeatWordCount(count: Int, stmt: PtRepeatLoop) { private fun repeatWordCount(count: Int, stmt: PtRepeatLoop) {
require(count in 257..65535) { "invalid repeat count ${stmt.position}" } require(count in 257..65535) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
if(isTargetCpu(CpuType.CPU65c02)) { val counterVar = createRepeatCounterVar(DataType.UWORD, isTargetCpu(CpuType.CPU65c02), stmt)
val counterVar = createRepeatCounterVar(DataType.UWORD, true, stmt) // the iny + double dec is microoptimization of the 16 bit loop
out(""" out("""
lda #<$count ldy #>$count
ldy #>$count lda #<$count
sta $counterVar beq +
sty $counterVar+1 iny
+ sta $counterVar
sty $counterVar+1
$repeatLabel""") $repeatLabel""")
translate(stmt.statements) translate(stmt.statements)
out(""" out("""
lda $counterVar dec $counterVar
bne + bne $repeatLabel
dec $counterVar+1 dec $counterVar+1
+ dec $counterVar bne $repeatLabel""")
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""")
}
} }
private fun repeatWordCountInAY(endLabel: String, stmt: PtRepeatLoop) { private fun repeatWordCountInAY(endLabel: String, stmt: PtRepeatLoop) {
// note: A/Y must have been loaded with the number of iterations! // 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 repeatLabel = makeLabel("repeat")
val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt) val counterVar = createRepeatCounterVar(DataType.UWORD, false, stmt)
out(""" out("""
sta $counterVar cmp #0
sty $counterVar+1 beq +
$repeatLabel lda $counterVar iny
bne + + sta $counterVar
lda $counterVar+1 sty $counterVar+1
beq $endLabel ora $counterVar+1
lda $counterVar beq $endLabel
bne + $repeatLabel""")
dec $counterVar+1
+ dec $counterVar
""")
translate(stmt.statements) translate(stmt.statements)
jmp(repeatLabel) out("""
dec $counterVar
bne $repeatLabel
dec $counterVar+1
bne $repeatLabel""")
out(endLabel) out(endLabel)
} }
private fun repeatByteCount(count: Int, stmt: PtRepeatLoop) { private fun repeatByteCount(count: Int, stmt: PtRepeatLoop) {
require(count in 2..256) { "invalid repeat count ${stmt.position}" } require(count in 2..256) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
@ -1023,18 +1135,10 @@ $repeatLabel lda $counterVar
} }
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair<PtExpression, PtExpression>? { internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: PtExpression): Pair<PtExpression, PtExpression>? {
val left: PtExpression if (pointerOffsetExpr !is PtBinaryExpression) return null
val right: PtExpression val operator = pointerOffsetExpr.operator
val operator: String val left = pointerOffsetExpr.left
val right = pointerOffsetExpr.right
if (pointerOffsetExpr is PtBinaryExpression) {
require(!options.useNewExprCode)
operator = pointerOffsetExpr.operator
left = pointerOffsetExpr.left
right = pointerOffsetExpr.right
}
else return null
if (operator != "+") return null if (operator != "+") return null
val leftDt = left.type val leftDt = left.type
val rightDt = right.type val rightDt = right.type
@ -1146,10 +1250,7 @@ $repeatLabel lda $counterVar
} }
internal fun findSubroutineParameter(name: String, asmgen: AsmGen6502Internal): PtSubroutineParameter? { internal fun findSubroutineParameter(name: String, asmgen: AsmGen6502Internal): PtSubroutineParameter? {
val stScope = asmgen.symbolTable.lookup(name) val stScope = asmgen.symbolTable.lookup(name) ?: return null
require(stScope!=null) {
"invalid name lookup $name"
}
val node = stScope.astNode val node = stScope.astNode
if(node is PtSubroutineParameter) if(node is PtSubroutineParameter)
return node return node
@ -2797,7 +2898,6 @@ $repeatLabel lda $counterVar
out(" sta P8ESTACK_LO,x | dex") out(" sta P8ESTACK_LO,x | dex")
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!options.useNewExprCode)
val addrExpr = expr.address as PtBinaryExpression val addrExpr = expr.address as PtBinaryExpression
if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) { if(tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
if(pushResultOnEstack) if(pushResultOnEstack)

View File

@ -43,7 +43,14 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
numberOfOptimizations++ numberOfOptimizations++
} }
mods= optimizeJsrRtsAndOtherCombinations(linesByFour) mods = optimizeJsrRtsAndOtherCombinations(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
mods = optimizeUselessPushPopStack(linesByFour)
if(mods.isNotEmpty()) { if(mods.isNotEmpty()) {
apply(mods, lines) apply(mods, lines)
linesByFour = getLinesBy(lines, 4) linesByFour = getLinesBy(lines, 4)
@ -517,3 +524,40 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedVal
} }
return mods 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
}

View File

@ -31,12 +31,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"msb" -> funcMsb(fcall, resultToStack, resultRegister) "msb" -> funcMsb(fcall, resultToStack, resultRegister)
"lsb" -> funcLsb(fcall, resultToStack, resultRegister) "lsb" -> funcLsb(fcall, resultToStack, resultRegister)
"mkword" -> funcMkword(fcall, resultToStack, resultRegister) "mkword" -> funcMkword(fcall, resultToStack, resultRegister)
"abs" -> funcAbs(fcall, resultToStack, resultRegister, sscope) "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) "any", "all" -> funcAnyAll(fcall, resultToStack, resultRegister, sscope)
"sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope) "sgn" -> funcSgn(fcall, resultToStack, resultRegister, sscope)
"sqrt16" -> funcSqrt16(fcall, resultToStack, resultRegister, sscope) "sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultToStack, resultRegister, sscope)
"divmod" -> funcDivmod(fcall) "divmod__ubyte" -> funcDivmod(fcall)
"divmodw" -> funcDivmodW(fcall) "divmod__uword" -> funcDivmodW(fcall)
"rol" -> funcRol(fcall) "rol" -> funcRol(fcall)
"rol2" -> funcRol2(fcall) "rol2" -> funcRol2(fcall)
"ror" -> funcRor(fcall) "ror" -> funcRor(fcall)
@ -269,13 +272,34 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.translateNormalAssignment(assign, fcall.definingISub()) asmgen.translateNormalAssignment(assign, fcall.definingISub())
} }
private fun funcSqrt16(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) { private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope) translateArguments(fcall, scope)
if(resultToStack) when(fcall.args[0].type) {
asmgen.out(" jsr prog8_lib.func_sqrt16_stack") DataType.UBYTE -> {
else { if(resultToStack)
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A") asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_stack")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false) 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)
}
}
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)
}
}
DataType.FLOAT -> {
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
if(resultToStack)
assignAsmGen.assignFAC1float(AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.FLOAT, scope, fcall.position))
else {
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
}
}
else -> throw AssemblyError("weird dt")
} }
} }
@ -328,6 +352,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
lda #$numElements lda #$numElements
jsr floats.func_reverse_f""") jsr floats.func_reverse_f""")
} }
in SplitWordArrayTypes -> TODO("split word reverse")
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} }
@ -374,6 +399,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
jsr prog8_lib.func_sort_ub""") jsr prog8_lib.func_sort_ub""")
} }
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported") DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
in SplitWordArrayTypes -> TODO("split word sort")
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} else } else
@ -408,6 +434,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> { DataType.UWORD -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
if(what.splitWords)
TODO("ror2 split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "ror2", 'w') translateRolRorArrayArgs(what.variable, what, "ror2", 'w')
asmgen.out(" jsr prog8_lib.ror2_array_uw") asmgen.out(" jsr prog8_lib.ror2_array_uw")
} }
@ -467,6 +495,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> { DataType.UWORD -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
if(what.splitWords)
TODO("ror split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "ror", 'w') translateRolRorArrayArgs(what.variable, what, "ror", 'w')
asmgen.out(" jsr prog8_lib.ror_array_uw") asmgen.out(" jsr prog8_lib.ror_array_uw")
} }
@ -509,6 +539,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> { DataType.UWORD -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
if(what.splitWords)
TODO("rol2 split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "rol2", 'w') translateRolRorArrayArgs(what.variable, what, "rol2", 'w')
asmgen.out(" jsr prog8_lib.rol2_array_uw") asmgen.out(" jsr prog8_lib.rol2_array_uw")
} }
@ -568,6 +600,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.UWORD -> { DataType.UWORD -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
if(what.splitWords)
TODO("rol split words ${what.position}")
translateRolRorArrayArgs(what.variable, what, "rol", 'w') translateRolRorArrayArgs(what.variable, what, "rol", 'w')
asmgen.out(" jsr prog8_lib.rol_array_uw") asmgen.out(" jsr prog8_lib.rol_array_uw")
} }
@ -583,6 +617,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) { private fun translateRolRorArrayArgs(arrayvar: PtIdentifier, indexer: PtArrayIndexer, operation: String, dt: Char) {
if(indexer.splitWords)
TODO("rol/ror split words access ${indexer.position}")
if(arrayvar.type==DataType.UWORD) { if(arrayvar.type==DataType.UWORD) {
if(dt!='b') if(dt!='b')
throw AssemblyError("non-array var indexing requires bytes dt") throw AssemblyError("non-array var indexing requires bytes dt")
@ -630,6 +666,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_stack") 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_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") 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 -> throw AssemblyError("weird type $dt")
} }
} else { } else {
@ -637,6 +674,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${fcall.name}_b_into_A | ldy #0") 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_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") 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") else -> throw AssemblyError("weird type $dt")
} }
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes) assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, dt in SignedDatatypes)
@ -648,21 +686,35 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val dt = fcall.args.single().type val dt = fcall.args.single().type
if(resultToStack) { if(resultToStack) {
when (dt) { when (dt) {
DataType.UBYTE -> asmgen.out(" ldy #0")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack") DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_stack")
DataType.UWORD -> {}
DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack") DataType.WORD -> asmgen.out(" jsr prog8_lib.abs_w_stack")
else -> throw AssemblyError("weird type") else -> {
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget(TargetStorageKind.STACK, asmgen, DataType.FLOAT, scope, fcall.position))
}
} }
} else { } else {
when (dt) { when (dt) {
DataType.UBYTE -> asmgen.out(" ldy #0") DataType.BYTE -> {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.abs_b_into_AY") asmgen.out(" jsr prog8_lib.abs_b_into_A")
DataType.UWORD -> {} 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") }
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") else -> throw AssemblyError("weird type")
} }
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
} }
} }
@ -699,7 +751,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr) val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
val pointer = result?.first as? PtIdentifier val pointer = result?.first as? PtIdentifier
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) { if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
@ -761,7 +812,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else fallback() } else fallback()
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr) val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
val pointer = result?.first as? PtIdentifier val pointer = result?.first as? PtIdentifier
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) { if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
@ -796,6 +846,161 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
} }
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
when(fcall.type) {
in ByteDatatypes -> {
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W1+1", fcall.args[2].type) // maximum
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, signed) // value
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)
}
}
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)
}
}
else -> throw AssemblyError("invalid dt")
}
}
private fun funcMin(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
if(fcall.type in ByteDatatypes) {
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_B1", fcall.type) // right
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // left
asmgen.out(" cmp P8ZP_SCRATCH_B1")
if(signed) asmgen.out(" bmi +") else asmgen.out(" bcc +")
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)
}
} 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
if(signed) {
asmgen.out("""
lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
cmp P8ZP_SCRATCH_W2
tya
sbc P8ZP_SCRATCH_W2+1
bvc +
eor #$80
+ bpl +
lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jmp ++
+ lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
+""")
} else {
asmgen.out("""
lda P8ZP_SCRATCH_W1+1
cmp P8ZP_SCRATCH_W2+1
bcc ++
bne +
lda P8ZP_SCRATCH_W1
cmp P8ZP_SCRATCH_W2
bcc ++
+ lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
jmp ++
+ lda P8ZP_SCRATCH_W1
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)
}
} else {
throw AssemblyError("min float not supported")
}
}
private fun funcMax(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes
if(fcall.type in ByteDatatypes) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // right
asmgen.out(" cmp P8ZP_SCRATCH_B1")
if(signed) asmgen.out(" bpl +") else asmgen.out(" bcs +")
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)
}
} 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
if(signed) {
asmgen.out("""
lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
cmp P8ZP_SCRATCH_W2
tya
sbc P8ZP_SCRATCH_W2+1
bvc +
eor #$80
+ bmi +
lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jmp ++
+ lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
+""")
} else {
asmgen.out("""
lda P8ZP_SCRATCH_W1+1
cmp P8ZP_SCRATCH_W2+1
bcc ++
bne +
lda P8ZP_SCRATCH_W1
cmp P8ZP_SCRATCH_W2
bcc ++
+ lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jmp ++
+ lda P8ZP_SCRATCH_W2
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)
}
} else {
throw AssemblyError("max float not supported")
}
}
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) { private fun funcMkword(fcall: PtBuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
if(resultToStack) { if(resultToStack) {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
@ -955,9 +1160,27 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
// and will not generate another cmp when lsb() is directly used inside a comparison expression. // and will not generate another cmp when lsb() is directly used inside a comparison expression.
} }
RegisterOrPair.Y -> { RegisterOrPair.Y -> {
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY) asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" tay | pla | cpy #0") 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") else -> throw AssemblyError("invalid reg")
} }

View File

@ -237,6 +237,9 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.out(" lda #<$varname | ldy #>$varname| jsr floats.push_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 -> { in IterableDatatypes -> {
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex") asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
} }
@ -245,8 +248,6 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
} }
private fun translateExpression(expr: PtBinaryExpression) { private fun translateExpression(expr: PtBinaryExpression) {
require(!asmgen.options.useNewExprCode)
// Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED! // Uses evalstack to evaluate the given expression. THIS IS SLOW AND SHOULD BE AVOIDED!
if(translateSomewhatOptimized(expr.left, expr.operator, expr.right)) if(translateSomewhatOptimized(expr.left, expr.operator, expr.right))
return return
@ -761,6 +762,9 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
return return
} }
if(arrayExpr.splitWords)
TODO("split words expression ${arrayExpr.position}")
val constIndexNum = arrayExpr.index.asConstInteger() val constIndexNum = arrayExpr.index.asConstInteger()
if(constIndexNum!=null) { if(constIndexNum!=null) {
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt) val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)

View File

@ -51,7 +51,35 @@ internal class ForLoopsAsmGen(private val program: PtProgram,
// loop over byte range via loopvar // loop over byte range via loopvar
val varname = asmgen.asmVariableName(stmt.variable) val varname = asmgen.asmVariableName(stmt.variable)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt)) asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt)) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
// pre-check for end already reached
if(iterableDt==DataType.ARRAY_B) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0)
asmgen.out("""
clc
sbc $varname
bvc +
eor #${'$'}80
+ bpl $endLabel""")
else
asmgen.out("""
sec
sbc $varname
bvc +
eor #${'$'}80
+ bmi $endLabel""")
} else {
if(stepsize<0)
asmgen.out("""
cmp $varname
beq +
bcs $endLabel
+""")
else
asmgen.out(" cmp $varname | bcc $endLabel")
asmgen.out(" sta $modifiedLabel+1")
}
asmgen.out(loopLabel) asmgen.out(loopLabel)
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
asmgen.out(""" asmgen.out("""
@ -69,7 +97,35 @@ $modifiedLabel cmp #0 ; modified
// loop over byte range via loopvar // loop over byte range via loopvar
val varname = asmgen.asmVariableName(stmt.variable) val varname = asmgen.asmVariableName(stmt.variable)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt)) asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt)) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
// pre-check for end already reached
if(iterableDt==DataType.ARRAY_B) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0)
asmgen.out("""
clc
sbc $varname
bvc +
eor #${'$'}80
+ bpl $endLabel""")
else
asmgen.out("""
sec
sbc $varname
bvc +
eor #${'$'}80
+ bmi $endLabel""")
} else {
if(stepsize<0)
asmgen.out("""
cmp $varname
beq +
bcs $endLabel
+""")
else
asmgen.out(" cmp $varname | bcc $endLabel")
asmgen.out(" sta $modifiedLabel+1")
}
asmgen.out(loopLabel) asmgen.out(loopLabel)
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
if(stepsize>0) { if(stepsize>0) {
@ -100,8 +156,9 @@ $modifiedLabel cmp #0 ; modified
stepsize == 1 || stepsize == -1 -> { stepsize == 1 || stepsize == -1 -> {
val varname = asmgen.asmVariableName(stmt.variable) val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvar(stmt, range) assignLoopvarWord(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out(""" asmgen.out("""
sty $modifiedLabel+1 sty $modifiedLabel+1
sta $modifiedLabel2+1 sta $modifiedLabel2+1
@ -134,8 +191,9 @@ $modifiedLabel2 cmp #0 ; modified
// (u)words, step >= 2 // (u)words, step >= 2
val varname = asmgen.asmVariableName(stmt.variable) val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvar(stmt, range) assignLoopvarWord(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out(""" asmgen.out("""
sty $modifiedLabel+1 sty $modifiedLabel+1
sta $modifiedLabel2+1 sta $modifiedLabel2+1
@ -182,31 +240,15 @@ $endLabel""")
// (u)words, step <= -2 // (u)words, step <= -2
val varname = asmgen.asmVariableName(stmt.variable) val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvar(stmt, range) assignLoopvarWord(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out(""" asmgen.out("""
sty $modifiedLabel+1 sty $modifiedLabel+1
sta $modifiedLabel2+1 sta $modifiedLabel2+1
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
if(iterableDt==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(""" asmgen.out("""
lda $varname lda $varname
sec sec
@ -224,7 +266,6 @@ $modifiedLabel sbc #0 ; modified
eor #$80 eor #$80
+ bpl $loopLabel + bpl $loopLabel
$endLabel""") $endLabel""")
}
} }
} }
} }
@ -234,6 +275,56 @@ $endLabel""")
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
// pre-check for end already reached.
// 'to' is in AY, do NOT clobber this!
if(iterableDt==DataType.ARRAY_W) {
if(stepsize<0)
asmgen.out("""
sta P8ZP_SCRATCH_W2 ; to
sty P8ZP_SCRATCH_W2+1 ; to
lda $fromVar
cmp P8ZP_SCRATCH_W2
lda $fromVar+1
sbc P8ZP_SCRATCH_W2+1
bvc +
eor #${'$'}80
+ bmi $endLabel
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1""")
else
asmgen.out("""
sta P8ZP_SCRATCH_REG
cmp $fromVar
tya
sbc $fromVar+1
bvc +
eor #${'$'}80
+ bmi $endLabel
lda P8ZP_SCRATCH_REG""")
} else {
if(stepsize<0)
asmgen.out("""
cpy $fromVar+1
beq +
bcc ++
bcs $endLabel
+ cmp $fromVar
bcc +
beq +
bne $endLabel
+""")
else
asmgen.out("""
cpy $fromVar+1
bcc $endLabel
bne +
cmp $fromVar
bcc $endLabel
+""")
}
}
private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) { private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) {
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
@ -332,7 +423,46 @@ $loopLabel sty $indexVar
// allocate index var on ZP if possible // allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors) val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold( 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 {
asmgen.out("$indexVar .byte 0")
}
asmgen.out(endLabel)
}
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> {
numElements!!
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
ldy #0
$loopLabel sty $indexVar
lda ${iterableName}_lsb,y
sta $loopvarName
lda ${iterableName}_msb,y
sta $loopvarName+1""")
asmgen.translate(stmt.statements)
if(numElements<=255u) {
asmgen.out("""
ldy $indexVar
iny
cpy #$numElements
beq $endLabel
bne $loopLabel""")
} else {
// length is 256
asmgen.out("""
ldy $indexVar
iny
bne $loopLabel
beq $endLabel""")
}
if(numElements>=16u) {
// allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") } failure = { asmgen.out("$indexVar .byte 0") }
) )
} else { } else {
@ -590,7 +720,7 @@ $loopLabel""")
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }
private fun assignLoopvar(stmt: PtForLoop, range: PtRange) = private fun assignLoopvarWord(stmt: PtForLoop, range: PtRange) =
asmgen.assignExpressionToVariable( asmgen.assignExpressionToVariable(
range.from, range.from,
asmgen.asmVariableName(stmt.variable), asmgen.asmVariableName(stmt.variable),

View File

@ -164,7 +164,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
if(requiredDt!=value.type) if(requiredDt!=value.type)
throw AssemblyError("for statusflag, byte value is required") throw AssemblyError("for statusflag, byte value is required")
if (statusflag == Statusflag.Pc) { if (statusflag == Statusflag.Pc) {
// this param needs to be set last, right before the jsr // this boolean param needs to be set last, right before the jsr
// for now, this is already enforced on the subroutine definition by the Ast Checker // for now, this is already enforced on the subroutine definition by the Ast Checker
when(value) { when(value) {
is PtNumber -> { is PtNumber -> {
@ -183,12 +183,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} }
else -> { else -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(""" asmgen.out(" ror a")
beq +
sec
bcs ++
+ clc
+""")
} }
} }
} else throw AssemblyError("can only use Carry as status flag parameter") } else throw AssemblyError("can only use Carry as status flag parameter")

View File

@ -65,6 +65,31 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable) val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.variable)
val elementDt = targetArrayIdx.type val elementDt = targetArrayIdx.type
val constIndex = targetArrayIdx.index.asConstInteger() val constIndex = targetArrayIdx.index.asConstInteger()
if(targetArrayIdx.splitWords) {
if(constIndex!=null) {
if(incr)
asmgen.out(" inc ${asmArrayvarname}_lsb+$constIndex | bne + | inc ${asmArrayvarname}_msb+$constIndex |+")
else
asmgen.out("""
lda ${asmArrayvarname}_lsb+$constIndex
bne +
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 |+")
else
asmgen.out("""
lda ${asmArrayvarname}_lsb,x
bne +
dec ${asmArrayvarname}_msb,x
+ dec ${asmArrayvarname}_lsb,x""")
asmgen.restoreRegisterLocal(CpuRegister.X)
}
return
}
if(constIndex!=null) { if(constIndex!=null) {
val indexValue = constIndex * program.memsizer.memorySize(elementDt) val indexValue = constIndex * program.memsizer.memorySize(elementDt)
when(elementDt) { when(elementDt) {
@ -74,11 +99,10 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+") asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
else else
asmgen.out(""" asmgen.out("""
lda $asmArrayvarname+$indexValue lda $asmArrayvarname+$indexValue
bne + bne +
dec $asmArrayvarname+$indexValue+1 dec $asmArrayvarname+$indexValue+1
+ dec $asmArrayvarname+$indexValue + dec $asmArrayvarname+$indexValue""")
""")
} }
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.out(" lda #<($asmArrayvarname+$indexValue) | ldy #>($asmArrayvarname+$indexValue)") asmgen.out(" lda #<($asmArrayvarname+$indexValue) | ldy #>($asmArrayvarname+$indexValue)")
@ -89,9 +113,8 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
} }
else else
{ {
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
asmgen.saveRegisterLocal(CpuRegister.X, scope!!) asmgen.saveRegisterLocal(CpuRegister.X, scope!!)
asmgen.out(" tax") asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.X)
when(elementDt) { when(elementDt) {
in ByteDatatypes -> { in ByteDatatypes -> {
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x") asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
@ -101,20 +124,20 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+") asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
else else
asmgen.out(""" asmgen.out("""
lda $asmArrayvarname,x lda $asmArrayvarname,x
bne + bne +
dec $asmArrayvarname+1,x dec $asmArrayvarname+1,x
+ dec $asmArrayvarname,x + dec $asmArrayvarname,x
""") """)
} }
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.out(""" asmgen.out("""
ldy #>$asmArrayvarname ldy #>$asmArrayvarname
clc clc
adc #<$asmArrayvarname adc #<$asmArrayvarname
bcc + bcc +
iny iny
+ jsr floats.inc_var_f""") + jsr floats.inc_var_f""")
} }
else -> throw AssemblyError("weird array elt dt") else -> throw AssemblyError("weird array elt dt")
} }

View File

@ -33,8 +33,9 @@ internal class ProgramAndVarsGen(
internal fun generate() { internal fun generate() {
header() header()
val allBlocks = program.allBlocks() 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()) { if(errors.noErrors()) {
program.allBlocks().forEach { block2asm(it) } program.allBlocks().forEach { block2asm(it) }
@ -104,15 +105,15 @@ internal class ProgramAndVarsGen(
asmgen.out("+\t.word 0") asmgen.out("+\t.word 0")
asmgen.out("prog8_entrypoint\t; assembly code starts here") asmgen.out("prog8_entrypoint\t; assembly code starts here")
if(!options.noSysInit) if(!options.noSysInit)
asmgen.out(" jsr ${compTarget.name}.init_system") asmgen.out(" jsr sys.init_system")
asmgen.out(" jsr ${compTarget.name}.init_system_phase2") asmgen.out(" jsr sys.init_system_phase2")
} }
CbmPrgLauncherType.NONE -> { CbmPrgLauncherType.NONE -> {
asmgen.out("; ---- program without basic sys call ----") asmgen.out("; ---- program without basic sys call ----")
asmgen.out("* = ${options.loadAddress.toHex()}") asmgen.out("* = ${options.loadAddress.toHex()}")
if(!options.noSysInit) if(!options.noSysInit)
asmgen.out(" jsr ${compTarget.name}.init_system") asmgen.out(" jsr sys.init_system")
asmgen.out(" jsr ${compTarget.name}.init_system_phase2") asmgen.out(" jsr sys.init_system_phase2")
} }
} }
} }
@ -120,8 +121,8 @@ internal class ProgramAndVarsGen(
asmgen.out("; ---- atari xex program ----") asmgen.out("; ---- atari xex program ----")
asmgen.out("* = ${options.loadAddress.toHex()}") asmgen.out("* = ${options.loadAddress.toHex()}")
if(!options.noSysInit) if(!options.noSysInit)
asmgen.out(" jsr ${compTarget.name}.init_system") asmgen.out(" jsr sys.init_system")
asmgen.out(" jsr ${compTarget.name}.init_system_phase2") asmgen.out(" jsr sys.init_system_phase2")
} }
} }
@ -139,24 +140,24 @@ internal class ProgramAndVarsGen(
"cx16" -> { "cx16" -> {
if(options.floats) if(options.floats)
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in 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 ${compTarget.name}.cleanup_at_exit") asmgen.out(" jmp sys.cleanup_at_exit")
} }
"c64" -> { "c64" -> {
asmgen.out(" jsr main.start | lda #31 | sta $01") asmgen.out(" jsr p8_main.p8_start | lda #31 | sta $01")
if(!options.noSysInit) if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit") asmgen.out(" jmp sys.cleanup_at_exit")
else else
asmgen.out(" rts") asmgen.out(" rts")
} }
"c128" -> { "c128" -> {
asmgen.out(" jsr main.start | lda #0 | sta ${"$"}ff00") asmgen.out(" jsr p8_main.p8_start | lda #0 | sta ${"$"}ff00")
if(!options.noSysInit) if(!options.noSysInit)
asmgen.out(" jmp ${compTarget.name}.cleanup_at_exit") asmgen.out(" jmp sys.cleanup_at_exit")
else else
asmgen.out(" rts") asmgen.out(" rts")
} }
else -> asmgen.jmp("main.start") else -> asmgen.jmp("p8_main.p8_start")
} }
} }
@ -175,7 +176,8 @@ internal class ProgramAndVarsGen(
private fun footer() { private fun footer() {
asmgen.out("; bss sections") 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) { 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") throw AssemblyError("current compilation target hasn't got the high ram area properly defined")
} }
@ -311,7 +313,9 @@ internal class ProgramAndVarsGen(
asmgen.out("${sub.name}\t$asmStartScope") 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) require(scope.type==StNodeType.SUBROUTINE)
val varsInSubroutine = getVars(scope) val varsInSubroutine = getVars(scope)
@ -331,7 +335,7 @@ internal class ProgramAndVarsGen(
asmsubs2asm(sub.children) asmsubs2asm(sub.children)
// the main.start subroutine is the program's entrypoint and should perform some initialization logic // 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() entrypointInitialization()
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) { if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
@ -480,8 +484,8 @@ internal class ProgramAndVarsGen(
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR } val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR }
for (variable in vars) { for (variable in vars) {
val scopedName = variable.key val scopedName = variable.key
val svar = symboltable.flat.getValue(scopedName) as StStaticVariable val svar = symboltable.lookup(scopedName) as? StStaticVariable
if(svar.onetimeInitializationStringValue!=null) if(svar?.onetimeInitializationStringValue!=null)
result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!)) result.add(ZpStringWithInitial(scopedName, variable.value, svar.onetimeInitializationStringValue!!))
} }
return result return result
@ -492,8 +496,8 @@ internal class ProgramAndVarsGen(
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes } val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes }
for (variable in vars) { for (variable in vars) {
val scopedName = variable.key val scopedName = variable.key
val svar = symboltable.flat.getValue(scopedName) as StStaticVariable val svar = symboltable.lookup(scopedName) as? StStaticVariable
if(svar.onetimeInitializationArrayValue!=null) if(svar?.onetimeInitializationArrayValue!=null)
result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!)) result.add(ZpArrayWithInitial(scopedName, variable.value, svar.onetimeInitializationArrayValue!!))
} }
return result return result
@ -543,6 +547,11 @@ internal class ProgramAndVarsGen(
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?") DataType.UWORD -> asmgen.out("${variable.name}\t.word ?")
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?") DataType.WORD -> asmgen.out("${variable.name}\t.sint ?")
DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}") DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}")
in SplitWordArrayTypes -> {
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
}
in ArrayDatatypes -> { in ArrayDatatypes -> {
val numbytes = compTarget.memorySize(variable.dt, variable.length!!) val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
asmgen.out("${variable.name}\t.fill $numbytes") asmgen.out("${variable.name}\t.fill $numbytes")
@ -627,6 +636,18 @@ internal class ProgramAndVarsGen(
asmgen.out(" .sint " + chunk.joinToString()) asmgen.out(" .sint " + chunk.joinToString())
} }
} }
DataType.ARRAY_UW_SPLIT -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_W_SPLIT -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_F -> { DataType.ARRAY_F -> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!) val array = value ?: zeroFilledArray(orNumberOfZeros!!)
val floatFills = array.map { val floatFills = array.map {
@ -686,7 +707,7 @@ internal class ProgramAndVarsGen(
val number = it.number!!.toInt() val number = it.number!!.toInt()
"$"+number.toString(16).padStart(2, '0') "$"+number.toString(16).padStart(2, '0')
} }
DataType.ARRAY_UW -> array.map { DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
if(it.number!=null) { if(it.number!=null) {
"$" + it.number!!.toInt().toString(16).padStart(4, '0') "$" + it.number!!.toInt().toString(16).padStart(4, '0')
} }
@ -718,11 +739,11 @@ internal class ProgramAndVarsGen(
else else
"-$$hexnum" "-$$hexnum"
} }
DataType.ARRAY_UW -> array.map { DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map {
val number = it.number!!.toInt() val number = it.number!!.toInt()
"$" + number.toString(16).padStart(4, '0') "$" + number.toString(16).padStart(4, '0')
} }
DataType.ARRAY_W -> array.map { DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> array.map {
val number = it.number!!.toInt() val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(4, '0') val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
if(number>=0) if(number>=0)

View File

@ -115,7 +115,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
left is PtIdentifier && left.name==scopedName left is PtIdentifier && left.name==scopedName
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
left is PtArrayIndexer && left isSameAs array!! left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords
} }
TargetStorageKind.MEMORY -> { TargetStorageKind.MEMORY -> {
left isSameAs memory!! left isSameAs memory!!
@ -178,8 +178,8 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value) AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
} }
is PtFunctionCall -> { is PtFunctionCall -> {
val symbol = asmgen.symbolTable.lookup(value.name) val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
val sub = symbol!!.astNode as IPtSubroutine val sub = symbol.astNode as IPtSubroutine
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second 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") ?: throw AssemblyError("can't translate zero return values in assignment")

View File

@ -45,12 +45,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
val variable = assign.source.asmVarname val variable = assign.source.asmVarname
when (assign.target.datatype) { when (assign.target.datatype) {
DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable) DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable)
DataType.WORD -> assignVariableWord(assign.target, variable) DataType.WORD -> assignVariableWord(assign.target, variable, assign.source.datatype)
DataType.UWORD -> { DataType.UWORD -> {
if(assign.source.datatype in PassByReferenceDatatypes) if(assign.source.datatype in PassByReferenceDatatypes)
assignAddressOf(assign.target, variable) assignAddressOf(assign.target, variable)
else else
assignVariableWord(assign.target, variable) assignVariableWord(assign.target, variable, assign.source.datatype)
} }
DataType.FLOAT -> assignVariableFloat(assign.target, variable) DataType.FLOAT -> assignVariableFloat(assign.target, variable)
DataType.STR -> assignVariableString(assign.target, variable) DataType.STR -> assignVariableString(assign.target, variable)
@ -80,6 +80,20 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
val constIndex = value.index.asConstInteger() val constIndex = value.index.asConstInteger()
if(value.splitWords) {
require(elementDt in WordDatatypes)
if(constIndex!=null) {
asmgen.out(" lda ${arrayVarName}_lsb+$constIndex | ldy ${arrayVarName}_msb+$constIndex")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
} else {
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
asmgen.out(" lda ${arrayVarName}_lsb,y | pha | lda ${arrayVarName}_msb,y | tay | pla")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
return
}
if (constIndex!=null) { if (constIndex!=null) {
// constant array index value // constant array index value
val indexValue = constIndex * program.memsizer.memorySize(elementDt) val indexValue = constIndex * program.memsizer.memorySize(elementDt)
@ -144,7 +158,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignMemoryByte(assign.target, null, value.address as PtIdentifier) assignMemoryByte(assign.target, null, value.address as PtIdentifier)
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
val addrExpr = value.address as PtBinaryExpression val addrExpr = value.address as PtBinaryExpression
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) { if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
assignRegisterByte(assign.target, CpuRegister.A, false) assignRegisterByte(assign.target, CpuRegister.A, false)
@ -275,20 +288,86 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
is PtPrefix -> { is PtPrefix -> {
if(assign.target.array==null) { if(assign.target.array==null) {
// First assign the value to the target then apply the operator in place on the target. if(assign.source.datatype==assign.target.datatype) {
// This saves a temporary variable if(assign.source.datatype in IntegerDatatypes) {
translateNormalAssignment( val signed = assign.source.datatype in SignedDatatypes
AsmAssignment( if(assign.source.datatype in ByteDatatypes) {
AsmAssignSource.fromAstSource(value.value, program, asmgen), assignExpressionToRegister(value.value, RegisterOrPair.A, signed)
assign.target, program.memsizer, assign.position when(value.operator) {
), scope "+" -> {}
) "-" -> {
when (value.operator) { if(asmgen.isTargetCpu(CpuType.CPU65c02))
"+" -> {} asmgen.out(" eor #255 | ina")
"-" -> inplaceNegate(assign, true, scope) else
"~" -> inplaceInvert(assign, scope) asmgen.out(" eor #255 | clc | adc #1")
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0") }
else -> throw AssemblyError("invalid prefix operator") "~" -> asmgen.out(" eor #255")
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
assignRegisterByte(assign.target, CpuRegister.A, signed)
} else {
assignExpressionToRegister(value.value, RegisterOrPair.AY, signed)
when(value.operator) {
"+" -> {}
"-" -> {
asmgen.out("""
sec
eor #255
adc #0
pha
tya
eor #255
adc #0
tay
pla""")
}
"~" -> asmgen.out(" pha | tya | eor #255 | tay | pla | eor #255")
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
} else {
// First assign the value to the target then apply the operator in place on the target.
// This saves a temporary variable
translateNormalAssignment(
AsmAssignment(
AsmAssignSource.fromAstSource(value.value, program, asmgen),
assign.target, program.memsizer, assign.position
), scope
)
when (value.operator) {
"+" -> {}
"-" -> inplaceNegate(assign, true, scope)
"~" -> inplaceInvert(assign, scope)
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
}
} else {
// use a temporary variable
val tempvar = if(value.type in ByteDatatypes) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1"
assignExpressionToVariable(value.value, tempvar, value.type)
when (value.operator) {
"+" -> {}
"-", "~" -> {
val assignTempvar = AsmAssignment(
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = tempvar),
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, value.type, scope, assign.position, variableAsmName = tempvar),
program.memsizer, assign.position)
if(value.operator=="-")
inplaceNegate(assignTempvar, true, scope)
else
inplaceInvert(assignTempvar, scope)
}
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
if(value.type in ByteDatatypes)
assignVariableByte(assign.target, tempvar)
else
assignVariableWord(assign.target, tempvar, value.type)
} }
} else { } else {
assignPrefixedExpressionToArrayElt(assign, scope) assignPrefixedExpressionToArrayElt(assign, scope)
@ -299,7 +378,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignRegisterByte(assign.target, CpuRegister.A, false) assignRegisterByte(assign.target, CpuRegister.A, false)
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
if(!attemptAssignOptimizedBinexpr(value, assign)) { if(!attemptAssignOptimizedBinexpr(value, assign)) {
// All remaining binary expressions just evaluate via the stack for now. // All remaining binary expressions just evaluate via the stack for now.
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here, // (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
@ -326,7 +404,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.translateNormalAssignment(assignToTempvar, scope) asmgen.translateNormalAssignment(assignToTempvar, scope)
when(assign.target.datatype) { when(assign.target.datatype) {
in ByteDatatypes -> assignVariableByte(assign.target, tempvar) in ByteDatatypes -> assignVariableByte(assign.target, tempvar)
in WordDatatypes -> assignVariableWord(assign.target, tempvar) in WordDatatypes -> assignVariableWord(assign.target, tempvar, assign.source.datatype)
DataType.FLOAT -> assignVariableFloat(assign.target, tempvar) DataType.FLOAT -> assignVariableFloat(assign.target, tempvar)
else -> throw AssemblyError("weird dt") else -> throw AssemblyError("weird dt")
} }
@ -345,7 +423,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { private fun attemptAssignOptimizedBinexpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
require(!asmgen.options.useNewExprCode)
if(expr.operator in ComparisonOperators) { if(expr.operator in ComparisonOperators) {
if(expr.right.asConstInteger() == 0) { if(expr.right.asConstInteger() == 0) {
if(expr.operator == "==" || expr.operator=="!=") { if(expr.operator == "==" || expr.operator=="!=") {
@ -734,10 +811,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
in WordDatatypes -> { in WordDatatypes -> {
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1") asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
asmgen.out(""" asmgen.out(" jsr math.multiply_words")
jsr math.multiply_words
lda math.multiply_words.result
ldy math.multiply_words.result+1""")
assignRegisterpairWord(assign.target, RegisterOrPair.AY) assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true return true
} }
@ -755,18 +829,14 @@ internal class AssignmentAsmGen(private val program: PtProgram,
return true return true
} }
in WordDatatypes -> { in WordDatatypes -> {
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type in SignedDatatypes) if (value in asmgen.optimizedWordMultiplications) {
if (value in asmgen.optimizedWordMultiplications) assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type in SignedDatatypes)
asmgen.out(" jsr math.mul_word_${value}") asmgen.out(" jsr math.mul_word_${value}")
else }
asmgen.out(""" else {
sta P8ZP_SCRATCH_W1 asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
sty P8ZP_SCRATCH_W1+1 asmgen.out(" jsr math.multiply_words")
lda #<$value }
ldy #>$value
jsr math.multiply_words
lda math.multiply_words.result
ldy math.multiply_words.result+1""")
assignRegisterpairWord(assign.target, RegisterOrPair.AY) assignRegisterpairWord(assign.target, RegisterOrPair.AY)
return true return true
} }
@ -823,7 +893,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
DataType.UWORD -> { DataType.UWORD -> {
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1") asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
asmgen.out(" jsr math.divmod_uw_asm") asmgen.out(" jsr math.divmod_uw_asm")
assignVariableWord(assign.target, "P8ZP_SCRATCH_W2") assignVariableWord(assign.target, "P8ZP_SCRATCH_W2", DataType.UWORD)
return true return true
} }
else -> return false else -> return false
@ -1141,7 +1211,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { private fun attemptAssignToByteCompareZero(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
require(!asmgen.options.useNewExprCode)
when (expr.operator) { when (expr.operator) {
"==" -> { "==" -> {
when(val dt = expr.left.type) { when(val dt = expr.left.type) {
@ -1321,7 +1390,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
assignMemoryByteIntoWord(target, null, value.address as PtIdentifier) assignMemoryByteIntoWord(target, null, value.address as PtIdentifier)
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
val addrExpr = value.address as PtBinaryExpression val addrExpr = value.address as PtBinaryExpression
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) { if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
asmgen.out(" ldy #0") asmgen.out(" ldy #0")
@ -1851,20 +1919,26 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx") asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx")
} }
in WordDatatypes -> { in WordDatatypes -> {
asmgen.out(""" if(target.array!!.splitWords)
inx asmgen.out("""
lda P8ESTACK_LO,x inx
sta ${target.asmVarname}+$scaledIdx lda P8ESTACK_LO,x
lda P8ESTACK_HI,x sta ${target.asmVarname}_lsb+$scaledIdx
sta ${target.asmVarname}+$scaledIdx+1 lda P8ESTACK_HI,x
""") sta ${target.asmVarname}_msb+$scaledIdx""")
else
asmgen.out("""
inx
lda P8ESTACK_LO,x
sta ${target.asmVarname}+$scaledIdx
lda P8ESTACK_HI,x
sta ${target.asmVarname}+$scaledIdx+1""")
} }
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.out(""" asmgen.out("""
lda #<(${target.asmVarname}+$scaledIdx) lda #<(${target.asmVarname}+$scaledIdx)
ldy #>(${target.asmVarname}+$scaledIdx) ldy #>(${target.asmVarname}+$scaledIdx)
jsr floats.pop_float jsr floats.pop_float""")
""")
} }
else -> throw AssemblyError("weird target variable type ${target.datatype}") else -> throw AssemblyError("weird target variable type ${target.datatype}")
} }
@ -1878,6 +1952,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname},y") asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname},y")
} }
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
if(target.array.splitWords)
TODO("assign into split words ${target.position}")
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
asmgen.out(""" asmgen.out("""
inx inx
@ -2039,20 +2115,29 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
} }
private fun assignVariableWord(target: AsmAssignTarget, sourceName: String) { private fun assignVariableWord(target: AsmAssignTarget, sourceName: String, sourceDt: DataType) {
require(sourceDt in WordDatatypes || sourceDt==DataType.UBYTE)
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
asmgen.out(""" if(sourceDt==DataType.UBYTE) {
lda $sourceName asmgen.out(" lda $sourceName | sta ${target.asmVarname}")
ldy $sourceName+1 if(asmgen.isTargetCpu(CpuType.CPU65c02))
sta ${target.asmVarname} asmgen.out(" stz ${target.asmVarname}")
sty ${target.asmVarname}+1 else
""") asmgen.out(" lda #0 | sta ${target.asmVarname}")
}
else
asmgen.out("""
lda $sourceName
ldy $sourceName+1
sta ${target.asmVarname}
sty ${target.asmVarname}+1""")
} }
TargetStorageKind.MEMORY -> { TargetStorageKind.MEMORY -> {
throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast") throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast")
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
if(sourceDt==DataType.UBYTE) TODO("assign byte to word array")
target.array!! target.array!!
if(target.constArrayIndexValue!=null) { if(target.constArrayIndexValue!=null) {
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt() val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
@ -2061,12 +2146,18 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx") asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
} }
in WordDatatypes -> { in WordDatatypes -> {
asmgen.out(""" if(target.array.splitWords)
lda $sourceName asmgen.out("""
sta ${target.asmVarname}+$scaledIdx lda $sourceName
lda $sourceName+1 sta ${target.asmVarname}_lsb+${target.constArrayIndexValue}
sta ${target.asmVarname}+$scaledIdx+1 lda $sourceName+1
""") sta ${target.asmVarname}_msb+${target.constArrayIndexValue}""")
else
asmgen.out("""
lda $sourceName
sta ${target.asmVarname}+$scaledIdx
lda $sourceName+1
sta ${target.asmVarname}+$scaledIdx+1""")
} }
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.out(""" asmgen.out("""
@ -2076,8 +2167,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
lda #<(${target.asmVarname}+$scaledIdx) lda #<(${target.asmVarname}+$scaledIdx)
ldy #>(${target.asmVarname}+$scaledIdx) ldy #>(${target.asmVarname}+$scaledIdx)
jsr floats.copy_float jsr floats.copy_float""")
""")
} }
else -> throw AssemblyError("weird target variable type ${target.datatype}") else -> throw AssemblyError("weird target variable type ${target.datatype}")
} }
@ -2091,12 +2181,18 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
asmgen.out(""" if(target.array.splitWords)
lda $sourceName asmgen.out("""
sta ${target.asmVarname},y lda $sourceName
lda $sourceName+1 sta ${target.asmVarname}_lsb,y
sta ${target.asmVarname}+1,y lda $sourceName+1
""") sta ${target.asmVarname}_msb,y""")
else
asmgen.out("""
lda $sourceName
sta ${target.asmVarname},y
lda $sourceName+1
sta ${target.asmVarname}+1,y""")
} }
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A) asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A)
@ -2117,29 +2213,53 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
when(target.register!!) { if(sourceDt==DataType.UBYTE) {
RegisterOrPair.AX -> asmgen.out(" ldx $sourceName+1 | lda $sourceName") when(target.register!!) {
RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName") RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda $sourceName")
RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName") RegisterOrPair.AY -> asmgen.out(" ldy #0 | lda $sourceName")
in Cx16VirtualRegisters -> { RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx $sourceName")
asmgen.out( in Cx16VirtualRegisters -> {
""" asmgen.out(" lda $sourceName | sta cx16.${target.register.toString().lowercase()}")
lda $sourceName if(asmgen.isTargetCpu(CpuType.CPU65c02))
sta cx16.${target.register.toString().lowercase()} asmgen.out(" stz cx16.${target.register.toString().lowercase()}+1")
lda $sourceName+1 else
sta cx16.${target.register.toString().lowercase()}+1 asmgen.out(" lda #0 | sta cx16.${target.register.toString().lowercase()}+1")
""") }
else -> throw AssemblyError("can't load word in a single 8-bit register")
}
} else {
when(target.register!!) {
RegisterOrPair.AX -> asmgen.out(" ldx $sourceName+1 | lda $sourceName")
RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName")
RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName")
in Cx16VirtualRegisters -> {
asmgen.out(
"""
lda $sourceName
sta cx16.${target.register.toString().lowercase()}
lda $sourceName+1
sta cx16.${target.register.toString().lowercase()}+1
""")
}
else -> throw AssemblyError("can't load word in a single 8-bit register")
} }
else -> throw AssemblyError("can't load word in a single 8-bit register")
} }
} }
TargetStorageKind.STACK -> { TargetStorageKind.STACK -> {
asmgen.out(""" if(sourceDt==DataType.UBYTE) {
lda $sourceName asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x")
sta P8ESTACK_LO,x if(asmgen.isTargetCpu(CpuType.CPU65c02))
lda $sourceName+1 asmgen.out(" stz P8ESTACK_HI,x")
sta P8ESTACK_HI,x else
dex""") asmgen.out(" lda #0 | sta P8ESTACK_HI,x")
}
else
asmgen.out("""
lda $sourceName
sta P8ESTACK_LO,x
lda $sourceName+1
sta P8ESTACK_HI,x
dex""")
} }
} }
} }
@ -2295,12 +2415,14 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
return return
} }
if(target.array!!.splitWords)
TODO("assign into split words ${target.position}")
if (target.constArrayIndexValue!=null) { if (target.constArrayIndexValue!=null) {
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt() val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx") asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
} }
else { else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, target.datatype, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y") asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
} }
} }
@ -2348,6 +2470,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
""") """)
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
if(wordtarget.array!!.splitWords)
TODO("assign byte into split words ${wordtarget.position}")
if (wordtarget.constArrayIndexValue!=null) { if (wordtarget.constArrayIndexValue!=null) {
val scaledIdx = wordtarget.constArrayIndexValue!! * 2u val scaledIdx = wordtarget.constArrayIndexValue!! * 2u
asmgen.out(" lda $sourceName") asmgen.out(" lda $sourceName")
@ -2356,7 +2480,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
else { else {
asmgen.saveRegisterLocal(CpuRegister.X, wordtarget.scope!!) asmgen.saveRegisterLocal(CpuRegister.X, wordtarget.scope!!)
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array!!, wordtarget.datatype, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.X)
asmgen.out(" lda $sourceName") asmgen.out(" lda $sourceName")
asmgen.signExtendAYlsb(DataType.BYTE) asmgen.signExtendAYlsb(DataType.BYTE)
asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x") asmgen.out(" sta ${wordtarget.asmVarname},x | inx | tya | sta ${wordtarget.asmVarname},x")
@ -2426,22 +2550,41 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1") asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
if (wordtarget.constArrayIndexValue!=null) { if(wordtarget.array!!.splitWords) {
val scaledIdx = wordtarget.constArrayIndexValue!! * 2u if (wordtarget.constArrayIndexValue!=null) {
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx") val scaledIdx = wordtarget.constArrayIndexValue!!
if(asmgen.isTargetCpu(CpuType.CPU65c02)) asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}_lsb+$scaledIdx")
asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1") if(asmgen.isTargetCpu(CpuType.CPU65c02))
else asmgen.out(" stz ${wordtarget.asmVarname}_msb+$scaledIdx")
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1") else
} asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}_msb+$scaledIdx")
else { }
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array!!, wordtarget.datatype, CpuRegister.Y) else {
asmgen.out(""" asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y)
lda $sourceName asmgen.out("""
sta ${wordtarget.asmVarname},y lda $sourceName
iny sta ${wordtarget.asmVarname}_lsb,y
lda #0 lda #0
sta ${wordtarget.asmVarname},y""") sta ${wordtarget.asmVarname}_msb,y""")
}
} else {
if (wordtarget.constArrayIndexValue!=null) {
val scaledIdx = wordtarget.constArrayIndexValue!! * 2u
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1")
else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
}
else {
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y)
asmgen.out("""
lda $sourceName
sta ${wordtarget.asmVarname},y
iny
lda #0
sta ${wordtarget.asmVarname},y""")
}
} }
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
@ -2698,47 +2841,95 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
if (target.constArrayIndexValue!=null) { if(target.array!!.splitWords) {
val idx = target.constArrayIndexValue!! * 2u // assign to split lsb/msb word array
when (regs) { if (target.constArrayIndexValue!=null) {
RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname}+$idx | stx ${target.asmVarname}+$idx+1") val idx = target.constArrayIndexValue!!
RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1") when (regs) {
RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1") RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname}_lsb+$idx | stx ${target.asmVarname}_msb+$idx")
in Cx16VirtualRegisters -> { RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname}_lsb+$idx | sty ${target.asmVarname}_msb+$idx")
RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname}_lsb+$idx | sty ${target.asmVarname}_msb+$idx")
in Cx16VirtualRegisters -> {
val srcReg = asmgen.asmSymbolName(regs)
asmgen.out("""
lda $srcReg
sta ${target.asmVarname}_lsb+$idx
lda $srcReg+1
sta ${target.asmVarname}_msb+$idx""")
}
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
}
}
else {
if (regs !in Cx16VirtualRegisters) {
when (regs) {
RegisterOrPair.AX -> asmgen.out(" pha | txa | pha")
RegisterOrPair.AY -> asmgen.out(" pha | tya | pha")
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
else -> throw AssemblyError("expected reg pair")
}
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.out("""
pla
sta ${target.asmVarname}_msb,y
pla
sta ${target.asmVarname}_lsb,y""")
} else {
val srcReg = asmgen.asmSymbolName(regs) val srcReg = asmgen.asmSymbolName(regs)
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.out(""" asmgen.out("""
lda $srcReg lda $srcReg
sta ${target.asmVarname}+$idx sta ${target.asmVarname}_lsb,y
lda $srcReg+1 lda $srcReg+1
sta ${target.asmVarname}+$idx+1""") sta ${target.asmVarname}_msb,y""")
} }
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
} }
} } else {
else { // assign to normal word array
if (regs !in Cx16VirtualRegisters) { if (target.constArrayIndexValue!=null) {
val idx = target.constArrayIndexValue!! * 2u
when (regs) { when (regs) {
RegisterOrPair.AX -> asmgen.out(" pha | txa | pha") RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname}+$idx | stx ${target.asmVarname}+$idx+1")
RegisterOrPair.AY -> asmgen.out(" pha | tya | pha") RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha") RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
else -> throw AssemblyError("expected reg pair") in Cx16VirtualRegisters -> {
val srcReg = asmgen.asmSymbolName(regs)
asmgen.out("""
lda $srcReg
sta ${target.asmVarname}+$idx
lda $srcReg+1
sta ${target.asmVarname}+$idx+1""")
}
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
}
}
else {
if (regs !in Cx16VirtualRegisters) {
when (regs) {
RegisterOrPair.AX -> asmgen.out(" pha | txa | pha")
RegisterOrPair.AY -> asmgen.out(" pha | tya | pha")
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
else -> throw AssemblyError("expected reg pair")
}
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.out("""
iny
pla
sta ${target.asmVarname},y
dey
pla
sta ${target.asmVarname},y""")
} else {
val srcReg = asmgen.asmSymbolName(regs)
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.out("""
iny
lda $srcReg+1
sta ${target.asmVarname},y
dey
lda $srcReg
sta ${target.asmVarname},y""")
} }
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y, true)
asmgen.out("""
pla
sta ${target.asmVarname},y
dey
pla
sta ${target.asmVarname},y""")
} else {
val srcReg = asmgen.asmSymbolName(regs)
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y, true)
asmgen.out("""
lda $srcReg+1
sta ${target.asmVarname},y
dey
lda $srcReg
sta ${target.asmVarname},y""")
} }
} }
} }
@ -2833,11 +3024,16 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
asmgen.out(""" if(target.array.splitWords)
lda #0 asmgen.out("""
sta ${target.asmVarname},y lda #0
sta ${target.asmVarname}+1,y sta ${target.asmVarname}_lsb,y
""") sta ${target.asmVarname}_msb,y""")
else
asmgen.out("""
lda #0
sta ${target.asmVarname},y
sta ${target.asmVarname}+1,y""")
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
when(target.register!!) { when(target.register!!) {
@ -2885,12 +3081,18 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
asmgen.out(""" if(target.array.splitWords)
lda #<${word.toHex()} asmgen.out("""
sta ${target.asmVarname},y lda #<${word.toHex()}
lda #>${word.toHex()} sta ${target.asmVarname}_lsb,y
sta ${target.asmVarname}+1,y lda #>${word.toHex()}
""") sta ${target.asmVarname}_msb,y""")
else
asmgen.out("""
lda #<${word.toHex()}
sta ${target.asmVarname},y
lda #>${word.toHex()}
sta ${target.asmVarname}+1,y""")
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
when(target.register!!) { when(target.register!!) {
@ -2948,12 +3150,14 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
return return
} }
if(target.array!!.splitWords)
TODO("assign into split words ${target.position}")
if (target.constArrayIndexValue!=null) { if (target.constArrayIndexValue!=null) {
val indexValue = target.constArrayIndexValue!! val indexValue = target.constArrayIndexValue!!
asmgen.out(" stz ${target.asmVarname}+$indexValue") asmgen.out(" stz ${target.asmVarname}+$indexValue")
} }
else { else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
asmgen.out(" lda #0 | sta ${target.asmVarname},y") asmgen.out(" lda #0 | sta ${target.asmVarname},y")
} }
} }
@ -3004,12 +3208,14 @@ internal class AssignmentAsmGen(private val program: PtProgram,
} }
return return
} }
if(target.array!!.splitWords)
TODO("assign into split words ${target.position}")
if (target.constArrayIndexValue!=null) { if (target.constArrayIndexValue!=null) {
val indexValue = target.constArrayIndexValue!! val indexValue = target.constArrayIndexValue!!
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue") asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue")
} }
else { else {
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname},y") asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname},y")
} }
} }
@ -3279,6 +3485,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda ${address.toHex()}") RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda ${address.toHex()}")
RegisterOrPair.AY -> asmgen.out(" ldy #0 | lda ${address.toHex()}") RegisterOrPair.AY -> asmgen.out(" ldy #0 | lda ${address.toHex()}")
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldy ${address.toHex()}") RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldy ${address.toHex()}")
in Cx16VirtualRegisters -> {
asmgen.out(" lda ${address.toHex()} | sta cx16.${wordtarget.register.toString().lowercase()}")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz cx16.${wordtarget.register.toString().lowercase()}+1")
else
asmgen.out(" lda #0 | sta cx16.${wordtarget.register.toString().lowercase()}+1")
}
else -> throw AssemblyError("word regs can only be pair") else -> throw AssemblyError("word regs can only be pair")
} }
TargetStorageKind.STACK -> { TargetStorageKind.STACK -> {
@ -3311,6 +3524,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
RegisterOrPair.AX -> asmgen.out(" ldx #0") RegisterOrPair.AX -> asmgen.out(" ldx #0")
RegisterOrPair.AY -> asmgen.out(" ldy #0") RegisterOrPair.AY -> asmgen.out(" ldy #0")
RegisterOrPair.XY -> asmgen.out(" tax | ldy #0") RegisterOrPair.XY -> asmgen.out(" tax | ldy #0")
in Cx16VirtualRegisters -> {
asmgen.out(" sta cx16.${wordtarget.register.toString().lowercase()}")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz cx16.${wordtarget.register.toString().lowercase()}+1")
else
asmgen.out(" lda #0 | sta cx16.${wordtarget.register.toString().lowercase()}+1")
}
else -> throw AssemblyError("word regs can only be pair") else -> throw AssemblyError("word regs can only be pair")
} }
} }
@ -3355,7 +3575,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.storeAIntoPointerVar(addressExpr) asmgen.storeAIntoPointerVar(addressExpr)
} }
addressExpr is PtBinaryExpression -> { addressExpr is PtBinaryExpression -> {
require(!asmgen.options.useNewExprCode)
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true)) if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true))
storeViaExprEval() storeViaExprEval()
} }
@ -3479,11 +3698,18 @@ internal class AssignmentAsmGen(private val program: PtProgram,
DataType.BYTE -> { DataType.BYTE -> {
when (target.kind) { when (target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
asmgen.out(""" if(asmgen.isTargetCpu(CpuType.CPU65c02))
lda #0 asmgen.out("""
sec lda ${target.asmVarname}
sbc ${target.asmVarname} eor #255
sta ${target.asmVarname}""") ina
sta ${target.asmVarname}""")
else
asmgen.out("""
lda #0
sec
sbc ${target.asmVarname}
sta ${target.asmVarname}""")
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
when(target.register!!) { when(target.register!!) {
@ -3492,7 +3718,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" eor #255 | ina") asmgen.out(" eor #255 | ina")
else else
asmgen.out(" eor #255 | clc | adc #1") asmgen.out(" eor #255 | clc | adc #1")
} }
RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax | inx") RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax | inx")
RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay | iny") RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay | iny")

View File

@ -93,8 +93,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.ARRAY -> inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value.array!!) SourceStorageKind.ARRAY -> inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value.array!!)
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) { if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
return
inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value.expression) inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value.expression)
} }
else { else {
@ -178,7 +177,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.ARRAY -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.array!!) SourceStorageKind.ARRAY -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.array!!)
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) { if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression) inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression)
} else { } else {
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression!!) inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression!!)
@ -195,7 +193,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
val indexVar = target.array.index as? PtIdentifier val indexVar = target.array.index as? PtIdentifier
when { when {
indexNum!=null -> { indexNum!=null -> {
val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}" val targetVarName = if(target.array.splitWords)
"${target.asmVarname} + ${indexNum.number.toInt()}"
else
"${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
when (target.datatype) { when (target.datatype) {
in ByteDatatypes -> { in ByteDatatypes -> {
when(value.kind) { when(value.kind) {
@ -257,6 +258,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
indexVar!=null -> { indexVar!=null -> {
when (target.datatype) { when (target.datatype) {
in ByteDatatypes -> { in ByteDatatypes -> {
if(value.kind==SourceStorageKind.EXPRESSION
&& value.expression is PtTypeCast
&& tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
return
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_B1") asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_B1")
asmgen.saveRegisterLocal(CpuRegister.Y, target.scope!!) asmgen.saveRegisterLocal(CpuRegister.Y, target.scope!!)
@ -268,8 +273,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.ARRAY -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.array!!) SourceStorageKind.ARRAY -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.array!!)
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) { if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
return
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression) inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression)
} else { } else {
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression!!) inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression!!)
@ -281,10 +284,19 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda P8ZP_SCRATCH_B1 | sta ${target.array.variable.name},y") asmgen.out(" lda P8ZP_SCRATCH_B1 | sta ${target.array.variable.name},y")
} }
in WordDatatypes -> { in WordDatatypes -> {
if(value.kind==SourceStorageKind.EXPRESSION
&& value.expression is PtTypeCast
&& tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
return
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UWORD, CpuRegister.Y)
asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1") if(target.array.splitWords) {
asmgen.out(" iny | lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1+1") asmgen.out(" lda ${target.array.variable.name}_lsb,y | sta P8ZP_SCRATCH_W1")
asmgen.saveRegisterLocal(CpuRegister.Y, target.scope!!) asmgen.out(" lda ${target.array.variable.name}_msb,y | sta P8ZP_SCRATCH_W1+1")
} else {
asmgen.out(" lda ${target.array.variable.name},y | sta P8ZP_SCRATCH_W1")
asmgen.out(" lda ${target.array.variable.name}+1,y | sta P8ZP_SCRATCH_W1+1")
}
asmgen.saveRegisterStack(CpuRegister.Y, false)
when(value.kind) { when(value.kind) {
SourceStorageKind.LITERALNUMBER -> inplaceModification_word_litval_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.number!!.number.toInt()) SourceStorageKind.LITERALNUMBER -> inplaceModification_word_litval_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplaceModification_word_variable_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.asmVarname, value.datatype) SourceStorageKind.VARIABLE -> inplaceModification_word_variable_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.asmVarname, value.datatype)
@ -293,8 +305,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
SourceStorageKind.ARRAY -> inplaceModification_word_value_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.array!!) SourceStorageKind.ARRAY -> inplaceModification_word_value_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.array!!)
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) { if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator))
return
inplaceModification_word_value_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression) inplaceModification_word_value_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression)
} else { } else {
inplaceModification_word_value_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression!!) inplaceModification_word_value_to_variable("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression!!)
@ -302,9 +312,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
else -> throw AssemblyError("weird source type ${value.kind}") else -> throw AssemblyError("weird source type ${value.kind}")
} }
asmgen.restoreRegisterLocal(CpuRegister.Y) asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name},y") if(target.array.splitWords) {
asmgen.out(" lda P8ZP_SCRATCH_W1 | dey | sta ${target.array.variable.name},y") asmgen.out(" lda P8ZP_SCRATCH_W1 | sta ${target.array.variable.name}_lsb,y")
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name}_msb,y")
} else {
asmgen.out(" lda P8ZP_SCRATCH_W1 | sta ${target.array.variable.name},y")
asmgen.out(" lda P8ZP_SCRATCH_W1+1 | sta ${target.array.variable.name}+1,y")
}
} }
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A) asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.FLOAT, CpuRegister.A)
@ -585,8 +600,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" clc | adc $name | sta $name") asmgen.out(" clc | adc $name | sta $name")
} }
"-" -> { "-" -> {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", dt) val tmpByte = if(name!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG"
asmgen.out(" lda $name | sec | sbc P8ZP_SCRATCH_B1 | sta $name") asmgen.assignExpressionToVariable(value, tmpByte, dt)
asmgen.out(" lda $name | sec | sbc $tmpByte | sta $name")
} }
"*" -> { "*" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
@ -1063,11 +1079,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
"-" -> { "-" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
val tmpByte = if(name!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG"
asmgen.out(""" asmgen.out("""
sta P8ZP_SCRATCH_B1 sta $tmpByte
lda $name lda $name
sec sec
sbc P8ZP_SCRATCH_B1 sbc $tmpByte
sta $name""") sta $name""")
} }
"|" -> { "|" -> {
@ -1102,11 +1119,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
"-" -> { "-" -> {
asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false) asmgen.translateDirectMemReadExpressionToRegAorStack(memread, false)
val tmpByte = if(name!="P8ZP_SCRATCH_B1") "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_REG"
asmgen.out(""" asmgen.out("""
sta P8ZP_SCRATCH_B1 sta $tmpByte
lda $name lda $name
sec sec
sbc P8ZP_SCRATCH_B1 sbc $tmpByte
sta $name sta $name
bcc + bcc +
dec $name+1 dec $name+1
@ -1203,10 +1221,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
lda #<$value lda #<$value
ldy #>$value ldy #>$value
jsr math.multiply_words jsr math.multiply_words
lda math.multiply_words.result
sta $name sta $name
lda math.multiply_words.result+1 sty $name+1""")
sta $name+1""")
} }
} }
"/" -> { "/" -> {
@ -1664,10 +1680,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
lda $name lda $name
ldy $name+1 ldy $name+1
jsr math.multiply_words jsr math.multiply_words
lda math.multiply_words.result
sta $name sta $name
lda math.multiply_words.result+1 sty $name+1""")
sta $name+1""")
} }
"/" -> { "/" -> {
if(dt==DataType.UWORD) { if(dt==DataType.UWORD) {
@ -1804,11 +1818,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
lda $name lda $name
ldy $name+1 ldy $name+1
jsr math.multiply_words jsr math.multiply_words
lda math.multiply_words.result
sta $name sta $name
lda math.multiply_words.result+1 sty $name+1""")
sta $name+1
""")
} }
"/" -> { "/" -> {
if(dt==DataType.WORD) { if(dt==DataType.WORD) {
@ -1821,8 +1832,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
ldy $otherName+1 ldy $otherName+1
jsr math.divmod_w_asm jsr math.divmod_w_asm
sta $name sta $name
sty $name+1 sty $name+1""")
""")
} }
else { else {
asmgen.out(""" asmgen.out("""
@ -1834,8 +1844,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
ldy $otherName+1 ldy $otherName+1
jsr math.divmod_uw_asm jsr math.divmod_uw_asm
sta $name sta $name
sty $name+1 sty $name+1""")
""")
} }
} }
"%" -> { "%" -> {
@ -1991,8 +2000,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: PtExpression) { private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: PtExpression) {
// this should be the last resort for code generation for this, // this should be the last resort for code generation for this,
// because the value is evaluated onto the eval stack (=slow). // because the value is evaluated onto the eval stack (=slow).
fun multiplyVarByWordInAY() { fun multiplyVarByWordInAY() {
asmgen.out(""" asmgen.out("""
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
@ -2000,10 +2007,8 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
lda $name lda $name
ldy $name+1 ldy $name+1
jsr math.multiply_words jsr math.multiply_words
lda math.multiply_words.result
sta $name sta $name
lda math.multiply_words.result+1 sty $name+1
sta $name+1
""") """)
} }
@ -2204,8 +2209,10 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" clc | adc $name | sta $name | tya | adc $name+1 | sta $name+1") asmgen.out(" clc | adc $name | sta $name | tya | adc $name+1 | sta $name+1")
} }
"-" -> { "-" -> {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", valueDt) val tmpWord = if(name!="P8ZP_SCRATCH_W1") "P8ZP_SCRATCH_W1" else "P8ZP_SCRATCH_W2"
asmgen.out(" lda $name | sec | sbc P8ZP_SCRATCH_W1 | sta $name | lda $name+1 | sbc P8ZP_SCRATCH_W1+1 | sta $name+1") asmgen.assignExpressionToVariable(value, tmpWord, valueDt)
asmgen.out(" lda $name | sec | sbc $tmpWord | sta $name | lda $name+1 | sbc $tmpWord+1 | sta $name+1")
// TODO wrong signed word subtraction code??? ^^^
} }
"*" -> { "*" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)

View File

@ -45,6 +45,10 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
warnings.add(text) 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 noErrors(): Boolean = errors.isEmpty()
override fun report() { override fun report() {

View File

@ -1,7 +1,9 @@
package prog8tests.codegencpu6502 package prog8tests.codegencpu6502
import io.kotest.assertions.throwables.shouldNotThrowAny import io.kotest.assertions.throwables.shouldNotThrowAny
import io.kotest.assertions.withClue
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import prog8.code.SymbolTableMaker import prog8.code.SymbolTableMaker
import prog8.code.ast.* import prog8.code.ast.*
@ -40,9 +42,9 @@ class TestCodegen: FunSpec({
// xx += cx16.r0 // xx += cx16.r0
// } // }
//} //}
val codegen = AsmGen6502() val codegen = AsmGen6502(prefixSymbols = false)
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) 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) 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("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)) sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
@ -92,7 +94,7 @@ class TestCodegen: FunSpec({
program.add(block) program.add(block)
// define the "cx16.r0" virtual register // 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)) cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
program.add(cx16block) program.add(cx16block)
@ -107,9 +109,19 @@ class TestCodegen: FunSpec({
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 in the path") {
val command = mutableListOf("64tass", "--version") val command = mutableListOf("64tass", "--version")
shouldNotThrowAny { shouldNotThrowAny {
val proc = ProcessBuilder(command).inheritIO().start() val proc = ProcessBuilder(command).start()
val output = String(proc.inputStream.readBytes())
val result = proc.waitFor() val result = proc.waitFor()
result.shouldBe(0) 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
}
} }
} }
}) })

View File

@ -29,7 +29,7 @@ dependencies {
implementation project(':codeGenIntermediate') implementation project(':codeGenIntermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
} }

View File

@ -26,6 +26,15 @@ class ExperiCodeGen: ICodeGeneratorBackend {
IRFileWriter(irProgram, null).write() IRFileWriter(irProgram, null).write()
println("** experimental codegen stub: no assembly generated **") 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
}
}

View File

@ -28,9 +28,9 @@ dependencies {
implementation project(':intermediate') implementation project(':intermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5' testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
} }
sourceSets { sourceSets {

View File

@ -104,27 +104,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
value.add(origAssign.value) value.add(origAssign.value)
} else { } else {
require(origAssign.operator.endsWith('=')) require(origAssign.operator.endsWith('='))
if(codeGen.options.useNewExprCode) { value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.value.type, origAssign.value.position)
// X += Y -> temp = X, temp += Y, X = temp val left: PtExpression = origAssign.target.children.single() as PtExpression
val tempvar = codeGen.getReusableTempvar(origAssign.definingSub()!!, origAssign.target.type) value.add(left)
val assign = PtAssignment(origAssign.position) value.add(origAssign.value)
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)
}
} }
val normalAssign = PtAssignment(origAssign.position) val normalAssign = PtAssignment(origAssign.position)
normalAssign.add(origAssign.target) normalAssign.add(origAssign.target)
@ -184,15 +167,18 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} else false } else false
if (assignment.value is PtMachineRegister) { if (assignment.value is PtMachineRegister) {
valueRegister = (assignment.value as PtMachineRegister).register valueRegister = (assignment.value as PtMachineRegister).register
if(extendByteToWord) if(extendByteToWord) {
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister), null) valueRegister = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=valueRegister, reg2=(assignment.value as PtMachineRegister).register), null)
}
} else { } else {
val tr = expressionEval.translateExpression(assignment.value) val tr = expressionEval.translateExpression(assignment.value)
valueRegister = tr.resultReg valueRegister = tr.resultReg
addToResult(result, tr, valueRegister, -1) addToResult(result, tr, valueRegister, -1)
if(extendByteToWord) { if(extendByteToWord) {
valueRegister = codeGen.registers.nextFree()
val opcode = if(assignment.value.type in SignedDatatypes) Opcode.EXTS else Opcode.EXT val opcode = if(assignment.value.type 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)
} }
} }
} }
@ -235,36 +221,71 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} }
val fixedIndex = constIntValue(targetArray.index) val fixedIndex = constIntValue(targetArray.index)
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name)
if(zero) { if(zero) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize val chunk = IRCodeChunk(null, null).also {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+$offset") } if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
}
else
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+${fixedIndex*itemsize}")
}
result += chunk result += chunk
} else { } else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize) val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable) } result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_lsb")
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_msb")
}
else
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
}
} }
} else { } else {
if(targetDt== IRDataType.FLOAT) { if(targetDt== IRDataType.FLOAT) {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset") } val chunk = IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset")
}
result += chunk result += chunk
} else { } else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize) val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable) } result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable)
}
} }
} else { } else {
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize val chunk = IRCodeChunk(null, null).also {
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+$offset") } if(targetArray.splitWords) {
val msbReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
}
else
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+${fixedIndex*itemsize}")
}
result += chunk result += chunk
} else { } else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize) val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code result += code
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable) } result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val msbReg = codeGen.registers.nextFree()
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = valueRegister, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = msbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
else
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
}
} }
} }
} }
@ -301,28 +322,21 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
} }
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> { private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> {
// returns the code to load the Index into the register, which is also return\ed. // returns the code to load the Index into the register, which is also returned.
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
if(itemsize==1) { if(itemsize==1 || array.splitWords) {
val tr = expressionEval.translateExpression(array.index) val tr = expressionEval.translateExpression(array.index)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg) return Pair(result, tr.resultReg)
} }
if(codeGen.options.useNewExprCode) { val mult: PtExpression
val tr = expressionEval.translateExpression(array.index) mult = PtBinaryExpression("*", DataType.UBYTE, array.position)
result += tr.chunks mult.children += array.index
addInstr(result, IRInstruction(Opcode.MUL, tr.dt, reg1=tr.resultReg, immediate = itemsize), null) mult.children += PtNumber(DataType.UBYTE, itemsize.toDouble(), array.position)
return Pair(result, tr.resultReg) val tr = expressionEval.translateExpression(mult)
} else { addToResult(result, tr, tr.resultReg, -1)
val mult: PtExpression return Pair(result, tr.resultReg)
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)
}
} }
} }

View File

@ -1,9 +1,10 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.StStaticVariable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.code.core.DataType import prog8.code.core.DataType
import prog8.code.core.SignedDatatypes
import prog8.code.core.SplitWordArrayTypes
import prog8.intermediate.* import prog8.intermediate.*
@ -13,12 +14,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
return when(call.name) { return when(call.name) {
"any" -> funcAny(call) "any" -> funcAny(call)
"all" -> funcAll(call) "all" -> funcAll(call)
"abs" -> funcAbs(call) "abs__byte", "abs__word", "abs__float" -> funcAbs(call)
"cmp" -> funcCmp(call) "cmp" -> funcCmp(call)
"sgn" -> funcSgn(call) "sgn" -> funcSgn(call)
"sqrt16" -> funcSqrt16(call) "sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
"divmod" -> funcDivmod(call, IRDataType.BYTE) "divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE)
"divmodw" -> funcDivmod(call, IRDataType.WORD) "divmod__uword" -> funcDivmod(call, IRDataType.WORD)
"pop" -> funcPop(call) "pop" -> funcPop(call)
"popw" -> funcPopw(call) "popw" -> funcPopw(call)
"push" -> funcPush(call) "push" -> funcPush(call)
@ -27,7 +28,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"rsavex", "rsavex",
"rrestore", "rrestore",
"rrestorex" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore "rrestorex" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
"callfar" -> throw AssemblyError("callfar() is for cx16 target only") "callfar" -> funcCallfar(call)
"msb" -> funcMsb(call) "msb" -> funcMsb(call)
"lsb" -> funcLsb(call) "lsb" -> funcLsb(call)
"memory" -> funcMemory(call) "memory" -> funcMemory(call)
@ -37,6 +38,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"pokew" -> funcPokeW(call) "pokew" -> funcPokeW(call)
"pokemon" -> ExpressionCodeResult.EMPTY // easter egg function "pokemon" -> ExpressionCodeResult.EMPTY // easter egg function
"mkword" -> funcMkword(call) "mkword" -> funcMkword(call)
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(call)
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(call)
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(call)
"sort" -> funcSort(call) "sort" -> funcSort(call)
"reverse" -> funcReverse(call) "reverse" -> funcReverse(call)
"rol" -> funcRolRor(Opcode.ROXL, call) "rol" -> funcRolRor(Opcode.ROXL, call)
@ -48,6 +52,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 { private fun funcDivmod(call: PtBuiltinFunctionCall, type: IRDataType): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val number = call.args[0] val number = call.args[0]
@ -102,9 +118,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcAny(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcAny(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier 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 = val syscall =
when (array.dt) { when (arrayName.type) {
DataType.ARRAY_UB, DataType.ARRAY_UB,
DataType.ARRAY_B -> IMSyscall.ANY_BYTE DataType.ARRAY_B -> IMSyscall.ANY_BYTE
DataType.ARRAY_UW, DataType.ARRAY_UW,
@ -116,16 +132,16 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() 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) 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) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
} }
private fun funcAll(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcAll(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier 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 = val syscall =
when(array.dt) { when(arrayName.type) {
DataType.ARRAY_UB, DataType.ARRAY_UB,
DataType.ARRAY_B -> IMSyscall.ALL_BYTE DataType.ARRAY_B -> IMSyscall.ALL_BYTE
DataType.ARRAY_UW, DataType.ARRAY_UW,
@ -137,7 +153,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() 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) 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) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
} }
@ -151,21 +167,13 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
when (sourceDt) { when (sourceDt) {
DataType.UBYTE -> {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1 = tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
}
DataType.BYTE -> { DataType.BYTE -> {
val notNegativeLabel = codeGen.createLabelName() val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree() val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg) it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg)
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=compareReg, immediate = 0x80) it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=compareReg, immediate = 0, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=tr.resultReg) it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=tr.resultReg)
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=tr.resultReg)
} }
result += IRCodeChunk(notNegativeLabel, null) result += IRCodeChunk(notNegativeLabel, null)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
@ -175,14 +183,18 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val compareReg = codeGen.registers.nextFree() val compareReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg) it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg)
it += IRInstruction(Opcode.AND, IRDataType.WORD, reg1=compareReg, immediate = 0x8000) it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.BEQ, IRDataType.WORD, reg1=compareReg, immediate = 0, labelSymbol = notNegativeLabel)
it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=tr.resultReg) it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=tr.resultReg)
} }
result += IRCodeChunk(notNegativeLabel, null) result += IRCodeChunk(notNegativeLabel, null)
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
} }
else -> throw AssemblyError("weird type") DataType.FLOAT -> {
val resultFpReg = codeGen.registers.nextFreeFloat()
addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null)
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
}
else -> throw AssemblyError("weird dt")
} }
} }
@ -198,15 +210,37 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
return ExpressionCodeResult(result, vmDt, resultReg, -1) return ExpressionCodeResult(result, vmDt, resultReg, -1)
} }
private fun funcSqrt16(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcSqrt(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single()) val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1) val dt = call.args[0].type
val resultReg = codeGen.registers.nextFree() when(dt) {
result += IRCodeChunk(null, null).also { DataType.UBYTE -> {
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg) addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.BYTE, reg1=resultReg, reg2=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
DataType.UWORD -> {
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
}
DataType.FLOAT -> {
addToResult(result, tr, -1, tr.resultFpReg)
val resultFpReg = codeGen.registers.nextFreeFloat()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg)
}
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
}
else -> throw AssemblyError("invalid dt for sqrt")
} }
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
} }
private fun funcPop(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcPop(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@ -249,41 +283,43 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcReverse(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcReverse(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier 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 = val syscall =
when(array.dt) { when(arrayName.type) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES
DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS
DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS
in SplitWordArrayTypes -> TODO("split word reverse")
else -> throw IllegalArgumentException("weird type to reverse") else -> throw IllegalArgumentException("weird type to reverse")
} }
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() 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) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} }
private fun funcSort(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcSort(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val arrayName = call.args[0] as PtIdentifier 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 = val syscall =
when(array.dt) { when(arrayName.type) {
DataType.ARRAY_UB -> IMSyscall.SORT_UBYTE DataType.ARRAY_UB -> IMSyscall.SORT_UBYTE
DataType.ARRAY_B -> IMSyscall.SORT_BYTE DataType.ARRAY_B -> IMSyscall.SORT_BYTE
DataType.ARRAY_UW -> IMSyscall.SORT_UWORD DataType.ARRAY_UW -> IMSyscall.SORT_UWORD
DataType.ARRAY_W -> IMSyscall.SORT_WORD DataType.ARRAY_W -> IMSyscall.SORT_WORD
DataType.STR -> IMSyscall.SORT_UBYTE DataType.STR -> IMSyscall.SORT_UBYTE
DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported") DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported")
in SplitWordArrayTypes -> TODO("split word sort")
else -> throw IllegalArgumentException("weird type to sort") else -> throw IllegalArgumentException("weird type to sort")
} }
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() 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) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} }
@ -294,10 +330,85 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addToResult(result, msbTr, msbTr.resultReg, -1) addToResult(result, msbTr, msbTr.resultReg, -1)
val lsbTr = exprGen.translateExpression(call.args[1]) val lsbTr = exprGen.translateExpression(call.args[1])
addToResult(result, lsbTr, lsbTr.resultReg, -1) addToResult(result, lsbTr, lsbTr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += IRCodeChunk(null, null).also { 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 {
val result = mutableListOf<IRCodeChunkBase>()
val type = irType(call.type)
val valueTr = exprGen.translateExpression(call.args[0])
val minimumTr = exprGen.translateExpression(call.args[1])
val maximumTr = exprGen.translateExpression(call.args[2])
result += valueTr.chunks
result += minimumTr.chunks
result += maximumTr.chunks
if(type==IRDataType.FLOAT) {
result += codeGen.makeSyscall(
IMSyscall.CLAMP_FLOAT, listOf(
valueTr.dt to valueTr.resultFpReg,
minimumTr.dt to minimumTr.resultFpReg,
maximumTr.dt to maximumTr.resultFpReg,
), type to valueTr.resultFpReg
)
return ExpressionCodeResult(result, type, -1, valueTr.resultFpReg)
} else {
val syscall = when(call.type) {
DataType.UBYTE -> IMSyscall.CLAMP_UBYTE
DataType.BYTE -> IMSyscall.CLAMP_BYTE
DataType.UWORD -> IMSyscall.CLAMP_UWORD
DataType.WORD -> IMSyscall.CLAMP_WORD
else -> throw AssemblyError("invalid dt")
}
result += codeGen.makeSyscall(syscall, listOf(
valueTr.dt to valueTr.resultReg,
minimumTr.dt to minimumTr.resultReg,
maximumTr.dt to maximumTr.resultReg,
), type to valueTr.resultReg
)
return ExpressionCodeResult(result, type, valueTr.resultReg, -1)
}
}
private fun funcMin(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val type = irType(call.type)
val result = mutableListOf<IRCodeChunkBase>()
val leftTr = exprGen.translateExpression(call.args[0])
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1)
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
val after = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(comparisonOpcode, type, reg1 = rightTr.resultReg, reg2 = leftTr.resultReg, labelSymbol = after)
// right <= left, take right
it += IRInstruction(Opcode.LOADR, type, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
}
result += IRCodeChunk(after, null)
return ExpressionCodeResult(result, type, leftTr.resultReg, -1)
}
private fun funcMax(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val type = irType(call.type)
val result = mutableListOf<IRCodeChunkBase>()
val leftTr = exprGen.translateExpression(call.args[0])
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1)
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR
val after = codeGen.createLabelName()
result += IRCodeChunk(null, null).also {
it += IRInstruction(comparisonOpcode, type, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg, labelSymbol = after)
// right >= left, take right
it += IRInstruction(Opcode.LOADR, type, reg1=leftTr.resultReg, reg2=rightTr.resultReg)
it += IRInstruction(Opcode.JUMP, labelSymbol = after)
}
result += IRCodeChunk(after, null)
return ExpressionCodeResult(result, type, leftTr.resultReg, -1)
} }
private fun funcPokeW(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcPokeW(call: PtBuiltinFunctionCall): ExpressionCodeResult {

View File

@ -1,7 +1,6 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.StRomSub import prog8.code.StRomSub
import prog8.code.StStaticVariable
import prog8.code.StSub import prog8.code.StSub
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
@ -45,9 +44,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
} }
is PtIdentifier -> { is PtIdentifier -> {
val vmDt = irType(expr.type)
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
if (expr.type in PassByValueDatatypes) { if (expr.type in PassByValueDatatypes) {
val vmDt = irType(expr.type)
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
val resultFpRegister = codeGen.registers.nextFreeFloat() val resultFpRegister = codeGen.registers.nextFreeFloat()
code += IRInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, labelSymbol = expr.name) code += IRInstruction(Opcode.LOADM, vmDt, fpReg1 = resultFpRegister, labelSymbol = expr.name)
@ -60,6 +59,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
} else { } else {
// for strings and arrays etc., load the *address* of the value instead // 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() val resultRegister = codeGen.registers.nextFree()
code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = expr.name) code += IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, labelSymbol = expr.name)
ExpressionCodeResult(code, vmDt, resultRegister, -1) ExpressionCodeResult(code, vmDt, resultRegister, -1)
@ -105,8 +105,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translate(check: PtContainmentCheck): ExpressionCodeResult { private fun translate(check: PtContainmentCheck): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val iterable = codeGen.symbolTable.flat.getValue(check.iterable.name) as StStaticVariable when(check.iterable.type) {
when(iterable.dt) {
DataType.STR -> { DataType.STR -> {
val elementTr = translateExpression(check.element) val elementTr = translateExpression(check.element)
addToResult(result, elementTr, elementTr.resultReg, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
@ -121,7 +120,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val iterableTr = translateExpression(check.iterable) val iterableTr = translateExpression(check.iterable)
addToResult(result, iterableTr, iterableTr.resultReg, -1) addToResult(result, iterableTr, iterableTr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() 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) 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) return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
} }
@ -131,12 +131,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val iterableTr = translateExpression(check.iterable) val iterableTr = translateExpression(check.iterable)
addToResult(result, iterableTr, iterableTr.resultReg, -1) addToResult(result, iterableTr, iterableTr.resultReg, -1)
val lengthReg = codeGen.registers.nextFree() 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) 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) return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
} }
DataType.ARRAY_F -> throw AssemblyError("containment check in float-array not supported") 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}")
} }
} }
@ -160,6 +161,33 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
var resultRegister = -1 var resultRegister = -1
if(arrayIx.splitWords) {
require(vmDt==IRDataType.WORD)
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=finalResultReg, reg2=resultRegister, reg3=tmpRegMsb)
}
} else {
val tr = translateExpression(arrayIx.index)
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
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=finalResultReg, reg2=resultRegister, reg3=tmpRegMsb)
}
}
return ExpressionCodeResult(result, vmDt, finalResultReg, -1)
}
var resultFpRegister = -1 var resultFpRegister = -1
if(arrayIx.index is PtNumber) { if(arrayIx.index is PtNumber) {
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize).toString() val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize).toString()
@ -247,13 +275,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when(cast.value.type) { when(cast.value.type) {
DataType.BYTE -> { DataType.BYTE -> {
// byte -> uword: sign extend // byte -> uword: sign extend
actualResultReg2 = tr.resultReg actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2), null) addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null)
} }
DataType.UBYTE -> { DataType.UBYTE -> {
// ubyte -> uword: sign extend // ubyte -> uword: sign extend
actualResultReg2 = tr.resultReg actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2), null) addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2 = tr.resultReg), null)
} }
DataType.WORD -> { DataType.WORD -> {
actualResultReg2 = tr.resultReg actualResultReg2 = tr.resultReg
@ -269,13 +297,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when(cast.value.type) { when(cast.value.type) {
DataType.BYTE -> { DataType.BYTE -> {
// byte -> word: sign extend // byte -> word: sign extend
actualResultReg2 = tr.resultReg actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2), null) addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null)
} }
DataType.UBYTE -> { DataType.UBYTE -> {
// byte -> word: sign extend // byte -> word: sign extend
actualResultReg2 = tr.resultReg actualResultReg2 = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2), null) addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = actualResultReg2, reg2=tr.resultReg), null)
} }
DataType.UWORD -> { DataType.UWORD -> {
actualResultReg2 = tr.resultReg actualResultReg2 = tr.resultReg
@ -312,7 +340,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult { private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
require(!codeGen.options.useNewExprCode)
val vmDt = irType(binExpr.left.type) val vmDt = irType(binExpr.left.type)
val signed = binExpr.left.type in SignedDatatypes val signed = binExpr.left.type in SignedDatatypes
return when(binExpr.operator) { return when(binExpr.operator) {
@ -428,6 +455,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
greaterEquals: Boolean greaterEquals: Boolean
): ExpressionCodeResult { ): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val cmpResultReg = codeGen.registers.nextFree()
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, -1, leftTr.resultFpReg) addToResult(result, leftTr, -1, leftTr.resultFpReg)
@ -442,10 +470,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else { } else {
if (greaterEquals) Opcode.SGE else Opcode.SGT if (greaterEquals) Opcode.SGE else Opcode.SGT
} }
addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister), null) addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1=cmpResultReg, reg2 = resultRegister, reg3 = zeroRegister), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultReg, -1)
} else { } 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") throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@ -457,8 +485,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else { } else {
if (greaterEquals) Opcode.SGE else Opcode.SGT if (greaterEquals) Opcode.SGE else Opcode.SGT
} }
addInstr(result, IRInstruction(ins, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null) addInstr(result, IRInstruction(ins, vmDt, reg1=cmpResultReg, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultReg, -1)
} }
} }
} }
@ -470,6 +498,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
lessEquals: Boolean lessEquals: Boolean
): ExpressionCodeResult { ): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val cmpResultRegister = codeGen.registers.nextFree()
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, -1, leftTr.resultFpReg) addToResult(result, leftTr, -1, leftTr.resultFpReg)
@ -484,10 +513,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else { } else {
if (lessEquals) Opcode.SLE else Opcode.SLT if (lessEquals) Opcode.SLE else Opcode.SLT
} }
addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister), null) addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1=cmpResultRegister, reg2 = resultRegister, reg3 = zeroRegister), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultRegister, -1)
} else { } 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") throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@ -499,8 +528,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} else { } else {
if (lessEquals) Opcode.SLE else Opcode.SLT if (lessEquals) Opcode.SLE else Opcode.SLT
} }
addInstr(result, IRInstruction(ins, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null) addInstr(result, IRInstruction(ins, vmDt, reg1=cmpResultRegister, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultRegister, -1)
} }
} }
} }
@ -526,7 +555,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
result += IRCodeChunk(label, null) result += IRCodeChunk(label, null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} else { } 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") throw AssemblyError("str compares should have been replaced with builtin function call to do the compare")
} else { } else {
return if(constValue(binExpr.right)==0.0) { return if(constValue(binExpr.right)==0.0) {
@ -542,8 +571,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val rightTr = translateExpression(binExpr.right) val rightTr = translateExpression(binExpr.right)
addToResult(result, rightTr, rightTr.resultReg, -1) addToResult(result, rightTr, rightTr.resultReg, -1)
val opcode = if (notEquals) Opcode.SNE else Opcode.SEQ val opcode = if (notEquals) Opcode.SNE else Opcode.SEQ
addInstr(result, IRInstruction(opcode, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null) val resultReg = codeGen.registers.nextFree()
ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1) addInstr(result, IRInstruction(opcode, vmDt, reg1 = resultReg, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null)
ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
} }
} }
} }
@ -1213,6 +1243,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
): MutableList<IRCodeChunkBase> { ): MutableList<IRCodeChunkBase> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val valueReg = codeGen.registers.nextFree() val valueReg = codeGen.registers.nextFree()
val cmpResultReg = codeGen.registers.nextFree()
if(operand is PtNumber) { if(operand is PtNumber) {
val numberReg = codeGen.registers.nextFree() val numberReg = codeGen.registers.nextFree()
if (knownAddress != null) { if (knownAddress != null) {
@ -1220,16 +1251,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress)
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt()) it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt())
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = numberReg) it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = cmpResultReg, reg2 = valueReg, reg3 = numberReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = knownAddress)
} }
} else { } else {
// in-place modify a symbol (variable) // in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt()) it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt())
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = numberReg) it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = numberReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
} }
} }
} else { } else {
@ -1239,15 +1270,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
// in-place modify a memory location // in-place modify a memory location
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = tr.resultReg) it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = knownAddress)
} }
} else { } else {
// in-place modify a symbol (variable) // in-place modify a symbol (variable)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol)
it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = tr.resultReg) it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg)
it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol)
} }
} }
} }
@ -1266,6 +1297,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val zeroReg = codeGen.registers.nextFree() val zeroReg = codeGen.registers.nextFree()
if(operand is PtNumber) { if(operand is PtNumber) {
val numberReg = codeGen.registers.nextFreeFloat() val numberReg = codeGen.registers.nextFreeFloat()
val cmpResultReg = codeGen.registers.nextFree()
if (knownAddress != null) { if (knownAddress != null) {
// in-place modify a memory location // in-place modify a memory location
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
@ -1273,8 +1305,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat()) 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.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg) it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg) it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
} }
} else { } else {
@ -1284,13 +1316,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat()) 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.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg) it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg) it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
} }
} }
} else { } else {
val tr = translateExpression(operand) val tr = translateExpression(operand)
val cmpResultReg = codeGen.registers.nextFree()
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
if (knownAddress != null) { if (knownAddress != null) {
// in-place modify a memory location // in-place modify a memory location
@ -1298,8 +1331,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress) 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.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg) it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg) it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress)
} }
} else { } else {
@ -1308,8 +1341,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) 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.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0)
it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg) it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg)
it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg) it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg)
it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol)
} }
} }

View File

@ -1,9 +1,6 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.StMemVar import prog8.code.*
import prog8.code.StNode
import prog8.code.StStaticVariable
import prog8.code.SymbolTable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.intermediate.* import prog8.intermediate.*
@ -58,6 +55,10 @@ class IRCodeGen(
val optimizer = IRPeepholeOptimizer(irProg) val optimizer = IRPeepholeOptimizer(irProg)
optimizer.optimize(options.optimize, errors) optimizer.optimize(options.optimize, errors)
irProg.validate() irProg.validate()
val regOptimizer = IRRegisterOptimizer(irProg)
regOptimizer.optimize()
return irProg return irProg
} }
@ -98,14 +99,16 @@ class IRCodeGen(
// make sure that first chunks in Blocks and Subroutines share the name of the block/sub as label. // make sure that first chunks in Blocks and Subroutines share the name of the block/sub as label.
irProg.blocks.forEach { block -> irProg.blocks.forEach { block ->
block.children.firstOrNull { it is IRInlineAsmChunk }?.let { first-> if(block.isNotEmpty()) {
first as IRInlineAsmChunk val firstAsm = block.children[0] as? IRInlineAsmChunk
if(first.label==null) { if(firstAsm!=null) {
val replacement = IRInlineAsmChunk(block.label, first.assembly, first.isIR, first.next) if(firstAsm.label==null) {
block.children.removeAt(0) val replacement = IRInlineAsmChunk(block.label, firstAsm.assembly, firstAsm.isIR, firstAsm.next)
block.children.add(0, replacement) block.children.removeAt(0)
} else if(first.label != block.label) { block.children.add(0, replacement)
throw AssemblyError("first chunk in block has label that differs from block name") } else if(firstAsm.label != block.label) {
throw AssemblyError("first chunk in block has label that differs from block name")
}
} }
} }
@ -139,7 +142,7 @@ class IRCodeGen(
// note: we do still export the memory mapped symbols so a code generator can use those // note: we do still export the memory mapped symbols so a code generator can use those
// for instance when a piece of inlined assembly references them. // for instance when a piece of inlined assembly references them.
val replacements = mutableListOf<Triple<IRCodeChunkBase, Int, UInt>>() val replacements = mutableListOf<Triple<IRCodeChunkBase, Int, UInt>>()
irProg.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.flatMap { it.chunks }.forEach { chunk -> irProg.foreachCodeChunk { chunk ->
chunk.instructions.withIndex().forEach { chunk.instructions.withIndex().forEach {
(idx, instr) -> (idx, instr) ->
val symbolExpr = instr.labelSymbol val symbolExpr = instr.labelSymbol
@ -164,16 +167,22 @@ class IRCodeGen(
replacements.forEach { replacements.forEach {
val old = it.first.instructions[it.second] 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( it.first.instructions[it.second] = IRInstruction(
old.opcode, old.opcode,
old.type, old.type,
old.reg1, old.reg1,
old.reg2, old.reg2,
old.reg3,
old.fpReg1, old.fpReg1,
old.fpReg2, old.fpReg2,
immediate = immediateValue,
null, null,
null, address = addressValue,
address = it.third.toInt(),
null, null,
null null
) )
@ -391,42 +400,35 @@ class IRCodeGen(
} }
private fun translate(whenStmt: PtWhen): IRCodeChunks { private fun translate(whenStmt: PtWhen): IRCodeChunks {
if(whenStmt.choices.children.isEmpty())
return emptyList()
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val valueDt = irType(whenStmt.value.type) val valueDt = irType(whenStmt.value.type)
val valueTr = expressionEval.translateExpression(whenStmt.value) val valueTr = expressionEval.translateExpression(whenStmt.value)
addToResult(result, valueTr, valueTr.resultReg, -1) addToResult(result, valueTr, valueTr.resultReg, -1)
val choices = whenStmt.choices.children.map {it as PtWhenChoice }
val choices = mutableListOf<Pair<String, PtWhenChoice>>()
val endLabel = createLabelName() val endLabel = createLabelName()
for (choice in choices) { whenStmt.choices.children.forEach {
val choice = it as PtWhenChoice
if(choice.isElse) { if(choice.isElse) {
result += translateNode(choice.statements) result += translateNode(choice.statements)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
} else { } else {
val skipLabel = createLabelName() val choiceLabel = createLabelName()
val values = choice.values.children.map {it as PtNumber} choices.add(choiceLabel to choice)
if(values.size==1) { choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
val chunk = IRCodeChunk(null, null) addInstr(result, IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = choiceLabel), 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)
} 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)
}
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) result += IRCodeChunk(endLabel, null)
return result return result
} }
@ -443,54 +445,63 @@ class IRCodeGen(
result += translateForInNonConstantRange(forLoop, loopvar) result += translateForInNonConstantRange(forLoop, loopvar)
} }
is PtIdentifier -> { is PtIdentifier -> {
val iterableVar = symbolTable.lookup(iterable.name) as StStaticVariable
require(forLoop.variable.name == loopvar.scopedName) require(forLoop.variable.name == loopvar.scopedName)
val iterableLength = symbolTable.getLength(iterable.name)
val loopvarSymbol = forLoop.variable.name val loopvarSymbol = forLoop.variable.name
val indexReg = registers.nextFree() val indexReg = registers.nextFree()
val tmpReg = registers.nextFree() val tmpReg = registers.nextFree()
val loopLabel = createLabelName() val loopLabel = createLabelName()
val endLabel = createLabelName() val endLabel = createLabelName()
if(iterableVar.dt==DataType.STR) { when (iterable.type) {
// iterate over a zero-terminated string DataType.STR -> {
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null) // iterate over a zero-terminated string
val chunk = IRCodeChunk(loopLabel, null) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
chunk += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpReg, reg2=indexReg, labelSymbol = iterable.name) result += IRCodeChunk(loopLabel, null).also {
chunk += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1=tmpReg, immediate = 0, labelSymbol = endLabel) it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
chunk += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1=tmpReg, labelSymbol = loopvarSymbol) it += IRInstruction(Opcode.BEQ, IRDataType.BYTE, reg1 = tmpReg, immediate = 0, labelSymbol = endLabel)
result += chunk 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)
} else {
// iterate over array
val elementDt = ArrayToElementTypes.getValue(iterable.type)
val elementSize = program.memsizer.memorySize(elementDt)
val lengthBytes = iterableVar.length!! * elementSize
if(lengthBytes<256) {
val chunk = IRCodeChunk(null, null)
chunk += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0)
result += chunk
val chunk2 = IRCodeChunk(loopLabel, null)
chunk2 += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name)
chunk2 += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol)
result += chunk2
result += translateNode(forLoop.statements) result += translateNode(forLoop.statements)
result += addConstReg(IRDataType.BYTE, indexReg, elementSize) val jumpChunk = IRCodeChunk(null, null)
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = lengthBytes, labelSymbol = loopLabel), null) jumpChunk += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1 = indexReg)
} else if(lengthBytes==256) { jumpChunk += IRInstruction(Opcode.JUMP, labelSymbol = loopLabel)
result += jumpChunk
result += IRCodeChunk(endLabel, null)
}
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) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
val chunk = IRCodeChunk(loopLabel, null) result += IRCodeChunk(loopLabel, null).also {
chunk += IRInstruction(Opcode.LOADX, irType(elementDt), reg1=tmpReg, reg2=indexReg, labelSymbol=iterable.name) val tmpRegLsb = registers.nextFree()
chunk += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpReg, labelSymbol = loopvarSymbol) val tmpRegMsb = registers.nextFree()
result += chunk 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)
}
}
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 += translateNode(forLoop.statements)
result += addConstReg(IRDataType.BYTE, indexReg, elementSize) result += addConstReg(IRDataType.BYTE, indexReg, elementSize)
addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = 0, labelSymbol = loopLabel), null) addInstr(result, IRInstruction(Opcode.BNE, IRDataType.BYTE, reg1=indexReg, immediate = if(lengthBytes==256) 0 else lengthBytes, labelSymbol = loopLabel), null)
} else {
throw AssemblyError("iterator length should never exceed 256")
} }
} }
} }
@ -536,22 +547,24 @@ class IRCodeGen(
addInstr(result, IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol=loopvarSymbol), null) addInstr(result, IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol=loopvarSymbol), null)
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel) result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step) if(step==1 || step==-1) {
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol), null) // if endvalue == loopvar, stop loop, else iterate
// if endvalue >= index, iterate loop addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol), null)
val branchInstr = if(loopvarDt in SignedDatatypes) { addInstr(result, IRInstruction(Opcode.BEQR, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol = labelAfterFor), null)
if(step>0) result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
IRInstruction(Opcode.BGESR, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol=loopLabel) addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = loopLabel), null)
else
IRInstruction(Opcode.BGESR, loopvarDtIr, reg1=fromTr.resultReg, reg2=toTr.resultReg, labelSymbol=loopLabel)
} else { } else {
if(step>0) // ind/dec index, then:
IRInstruction(Opcode.BGER, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg, labelSymbol=loopLabel) // 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 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) result += IRCodeChunk(labelAfterFor, null)
return result return result
} }
@ -896,7 +909,6 @@ class IRCodeGen(
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
when (condition) { when (condition) {
is PtBinaryExpression -> { is PtBinaryExpression -> {
require(!options.useNewExprCode)
if(condition.operator !in ComparisonOperators) if(condition.operator !in ComparisonOperators)
throw AssemblyError("if condition should only be a binary comparison expression") throw AssemblyError("if condition should only be a binary comparison expression")
@ -910,7 +922,6 @@ class IRCodeGen(
} }
else -> { else -> {
// if X --> meaning: if X!=0 // if X --> meaning: if X!=0
require(options.useNewExprCode)
val irDt = irType(condition.type) val irDt = irType(condition.type)
val signed = condition.type in SignedDatatypes val signed = condition.type in SignedDatatypes
return if(goto!=null && ifElse.elseScope.children.isEmpty()) { return if(goto!=null && ifElse.elseScope.children.isEmpty()) {
@ -988,10 +999,10 @@ class IRCodeGen(
val opcode = when (condition.operator) { val opcode = when (condition.operator) {
"==" -> Opcode.BEQ "==" -> Opcode.BEQ
"!=" -> Opcode.BNE "!=" -> Opcode.BNE
"<" -> if (signed) Opcode.BLTS else throw AssemblyError("unsigned < 0 shouldn't occur in codegen") "<" -> if (signed) Opcode.BLTS else Opcode.BLT
">" -> if (signed) Opcode.BGTS else throw AssemblyError("unsigned > 0 shouldn't occur in codegen") ">" -> if (signed) Opcode.BGTS else Opcode.BGT
"<=" -> if (signed) Opcode.BLES else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen") "<=" -> if (signed) Opcode.BLES else Opcode.BLE
">=" -> if (signed) Opcode.BGES else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen") ">=" -> if (signed) Opcode.BGES else Opcode.BGE
else -> throw AssemblyError("invalid comparison operator") else -> throw AssemblyError("invalid comparison operator")
} }
if (goto.address != null) if (goto.address != null)
@ -1125,10 +1136,10 @@ class IRCodeGen(
elseBranch = when (condition.operator) { elseBranch = when (condition.operator) {
"==" -> Opcode.BNE "==" -> Opcode.BNE
"!=" -> Opcode.BEQ "!=" -> Opcode.BEQ
"<" -> if (signed) Opcode.BGES else throw AssemblyError("unsigned < 0 shouldn't occur in codegen") "<" -> if (signed) Opcode.BGES else Opcode.BGE
">" -> if (signed) Opcode.BLES else throw AssemblyError("unsigned > 0 shouldn't occur in codegen") ">" -> if (signed) Opcode.BLES else Opcode.BLE
"<=" -> if (signed) Opcode.BGTS else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen") "<=" -> if (signed) Opcode.BGTS else Opcode.BGT
">=" -> if (signed) Opcode.BLTS else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen") ">=" -> if (signed) Opcode.BLTS else Opcode.BLT
else -> throw AssemblyError("weird operator") else -> throw AssemblyError("weird operator")
} }
} }
@ -1212,7 +1223,7 @@ class IRCodeGen(
val afterIfLabel = createLabelName() val afterIfLabel = createLabelName()
addInstr( addInstr(
result, result,
IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = elseLabel), IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = elseLabel),
null null
) )
result += translateNode(ifElse.ifScope) result += translateNode(ifElse.ifScope)
@ -1224,7 +1235,7 @@ class IRCodeGen(
val afterIfLabel = createLabelName() val afterIfLabel = createLabelName()
addInstr( addInstr(
result, result,
IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, labelSymbol = afterIfLabel), IRInstruction(elseBranch, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel),
null null
) )
result += translateNode(ifElse.ifScope) result += translateNode(ifElse.ifScope)
@ -1350,59 +1361,126 @@ class IRCodeGen(
} }
private fun translate(postIncrDecr: PtPostIncrDecr): IRCodeChunks { private fun translate(postIncrDecr: PtPostIncrDecr): IRCodeChunks {
val operationMem: Opcode
val operationRegister: Opcode
when(postIncrDecr.operator) {
"++" -> {
operationMem = Opcode.INCM
operationRegister = Opcode.INC
}
"--" -> {
operationMem = Opcode.DECM
operationRegister = Opcode.DEC
}
else -> throw AssemblyError("weird operator")
}
val ident = postIncrDecr.target.identifier
val memory = postIncrDecr.target.memory
val array = postIncrDecr.target.array
val irDt = irType(postIncrDecr.target.type)
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
if(ident!=null) { val array = postIncrDecr.target.array
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = ident.name), null) if(array?.splitWords==true) {
} else if(memory!=null) {
if(memory.address is PtNumber) {
val address = (memory.address as PtNumber).number.toInt()
addInstr(result, IRInstruction(operationMem, irDt, address = address), null)
} else {
val tr = expressionEval.translateExpression(memory.address)
addToResult(result, tr, tr.resultReg, -1)
val chunk = IRCodeChunk(null, null)
val incReg = registers.nextFree()
chunk += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = tr.resultReg)
chunk += IRInstruction(operationRegister, irDt, reg1 = incReg)
chunk += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = tr.resultReg)
result += chunk
}
} else if (array!=null) {
val variable = array.variable.name val variable = array.variable.name
val itemsize = program.memsizer.memorySize(array.type)
val fixedIndex = constIntValue(array.index) val fixedIndex = constIntValue(array.index)
if(fixedIndex!=null) { if(fixedIndex!=null) {
val offset = fixedIndex*itemsize val skipLabel = createLabelName()
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol="$variable+$offset"), null) when(postIncrDecr.operator) {
"++" -> {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_msb+$fixedIndex")
}
result += IRCodeChunk(skipLabel, null)
}
"--" -> {
val valueReg=registers.nextFree()
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=valueReg, labelSymbol = "${variable}_lsb+$fixedIndex")
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_msb+$fixedIndex")
}
result += IRCodeChunk(skipLabel, null).also {
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_lsb+$fixedIndex")
}
}
else -> throw AssemblyError("weird operator")
}
} else { } else {
val arrayLength = symbolTable.getLength(array.variable.name)
val indexTr = expressionEval.translateExpression(array.index) val indexTr = expressionEval.translateExpression(array.index)
addToResult(result, indexTr, indexTr.resultReg, -1) addToResult(result, indexTr, indexTr.resultReg, -1)
val chunk = IRCodeChunk(null, null)
val incReg = registers.nextFree() val incReg = registers.nextFree()
chunk += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable) val skipLabel = createLabelName()
chunk += IRInstruction(operationRegister, irDt, reg1=incReg) when(postIncrDecr.operator) {
chunk += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable) "++" -> {
result += chunk result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
result += IRCodeChunk(skipLabel, null)
}
"--" -> {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
result += IRCodeChunk(skipLabel, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
}
}
else -> throw AssemblyError("weird operator")
}
} }
} else } else {
throw AssemblyError("weird assigntarget") val ident = postIncrDecr.target.identifier
val memory = postIncrDecr.target.memory
val irDt = irType(postIncrDecr.target.type)
val operationMem: Opcode
val operationRegister: Opcode
when(postIncrDecr.operator) {
"++" -> {
operationMem = Opcode.INCM
operationRegister = Opcode.INC
}
"--" -> {
operationMem = Opcode.DECM
operationRegister = Opcode.DEC
}
else -> throw AssemblyError("weird operator")
}
if(ident!=null) {
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = ident.name), null)
} else if(memory!=null) {
if(memory.address is PtNumber) {
val address = (memory.address as PtNumber).number.toInt()
addInstr(result, IRInstruction(operationMem, irDt, address = address), null)
} else {
val tr = expressionEval.translateExpression(memory.address)
addToResult(result, tr, tr.resultReg, -1)
result += IRCodeChunk(null, null).also {
val incReg = registers.nextFree()
it += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = tr.resultReg)
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
it += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = tr.resultReg)
}
}
} else if (array!=null) {
val variable = array.variable.name
val itemsize = program.memsizer.memorySize(array.type)
val fixedIndex = constIntValue(array.index)
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol="$variable+$offset"), null)
} else {
val indexTr = expressionEval.translateExpression(array.index)
addToResult(result, indexTr, indexTr.resultReg, -1)
if(itemsize>1)
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)
}
}
} else
throw AssemblyError("weird assigntarget")
}
return result return result
} }
@ -1423,29 +1501,41 @@ class IRCodeGen(
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val countTr = expressionEval.translateExpression(repeat.count) val countTr = expressionEval.translateExpression(repeat.count)
addToResult(result, countTr, countTr.resultReg, -1) 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 += labelFirstChunk(translateNode(repeat.statements), repeatLabel)
val chunk = IRCodeChunk(null, null) result += IRCodeChunk(null, null).also {
chunk += IRInstruction(Opcode.DEC, irDt, reg1=countTr.resultReg) it += IRInstruction(Opcode.DEC, irDt, reg1 = countTr.resultReg)
chunk += IRInstruction(Opcode.BNE, irDt, reg1=countTr.resultReg, immediate = 0, labelSymbol = repeatLabel) it += IRInstruction(Opcode.BNE, irDt, reg1 = countTr.resultReg, immediate = 0, labelSymbol = repeatLabel)
result += chunk }
result += IRCodeChunk(skipRepeatLabel, null) result += IRCodeChunk(skipRepeatLabel, null)
return result return result
} }
private fun translate(jump: PtJump): IRCodeChunks { private fun translate(jump: PtJump): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val instr = if(jump.address!=null) { val chunk = IRCodeChunk(null, null)
IRInstruction(Opcode.JUMPA, address = jump.address!!.toInt()) if(jump.address!=null) {
chunk += IRInstruction(Opcode.JUMP, address = jump.address!!.toInt())
} else { } else {
if (jump.generatedLabel != null) if (jump.generatedLabel != null)
IRInstruction(Opcode.JUMP, labelSymbol = jump.generatedLabel!!) chunk += IRInstruction(Opcode.JUMP, labelSymbol = jump.generatedLabel!!)
else if (jump.identifier != null) else if (jump.identifier != null) {
IRInstruction(Opcode.JUMP, labelSymbol = jump.identifier!!.name) 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 else
throw AssemblyError("weird jump") throw AssemblyError("weird jump")
} }
addInstr(result, instr, null) result += chunk
return result return result
} }
@ -1533,7 +1623,7 @@ class IRCodeGen(
private fun translate(parameters: List<PtSubroutineParameter>) = private fun translate(parameters: List<PtSubroutineParameter>) =
parameters.map { parameters.map {
val flattenedName = it.definingSub()!!.name + "." + it.name 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) IRSubroutine.IRParam(flattenedName, orig.dt)
} }

View File

@ -23,7 +23,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
private fun optimizeOnlyJoinChunks() { private fun optimizeOnlyJoinChunks() {
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub -> irprog.foreachSub { sub ->
joinChunks(sub) joinChunks(sub)
removeEmptyChunks(sub) removeEmptyChunks(sub)
joinChunks(sub) joinChunks(sub)
@ -32,10 +32,11 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
private fun peepholeOptimize() { private fun peepholeOptimize() {
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub -> irprog.foreachSub { sub ->
joinChunks(sub) joinChunks(sub)
removeEmptyChunks(sub) removeEmptyChunks(sub)
joinChunks(sub) joinChunks(sub)
sub.chunks.withIndex().forEach { (index, chunk1) -> sub.chunks.withIndex().forEach { (index, chunk1) ->
// we don't optimize Inline Asm chunks here. // we don't optimize Inline Asm chunks here.
val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null val chunk2 = if(index<sub.chunks.size-1) sub.chunks[index+1] else null
@ -46,17 +47,19 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
val changed = removeNops(chunk1, indexedInstructions) val changed = removeNops(chunk1, indexedInstructions)
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions) // TODO not yet implemented || removeDoubleLoadsAndStores(chunk1, indexedInstructions) // TODO not yet implemented
|| removeUselessArithmetic(chunk1, indexedInstructions) || removeUselessArithmetic(chunk1, indexedInstructions)
|| removeNeedlessCompares(chunk1, indexedInstructions)
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions) || removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|| removeDoubleSecClc(chunk1, indexedInstructions) || removeDoubleSecClc(chunk1, indexedInstructions)
|| cleanupPushPop(chunk1, indexedInstructions) || cleanupPushPop(chunk1, indexedInstructions)
// TODO other optimizations: // TODO other optimizations
// more complex optimizations such as unused registers
} while (changed) } while (changed)
} }
} }
removeEmptyChunks(sub) removeEmptyChunks(sub)
} }
// TODO also do register optimization step here at the end?
irprog.linkChunks() // re-link irprog.linkChunks() // re-link
} }
@ -264,6 +267,28 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed 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 { 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 // note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
var changed = false var changed = false

View File

@ -0,0 +1,103 @@
package prog8.codegen.intermediate
import prog8.intermediate.IRProgram
class IRRegisterOptimizer(private val irProg: IRProgram) {
fun optimize() {
// reuseRegisters()
}
/*
TODO: this register re-use renumbering isn't going to work like this,
because subroutines will be clobbering the registers that the subroutine
which is calling them might be using...
private fun reuseRegisters() {
fun addToUsage(usage: MutableMap<Pair<Int, IRDataType>, MutableSet<IRCodeChunkBase>>,
regnum: Int,
dt: IRDataType,
chunk: IRCodeChunkBase) {
val key = regnum to dt
val chunks = usage[key] ?: mutableSetOf()
chunks.add(chunk)
usage[key] = chunks
}
val usage: MutableMap<Pair<Int, IRDataType>, MutableSet<IRCodeChunkBase>> = mutableMapOf()
irProg.foreachCodeChunk { chunk ->
chunk.usedRegisters().regsTypes.forEach { (regNum, types) ->
types.forEach { dt ->
addToUsage(usage, regNum, dt, chunk)
}
}
}
val registerReplacements = usage.asSequence()
.filter { it.value.size==1 }
.map { it.key to it.value.iterator().next() }
.groupBy({ it.second }, {it.first})
.asSequence()
.associate { (chunk, registers) ->
chunk to registers.withIndex().associate { (index, reg) -> reg to 50000+index }
}
registerReplacements.forEach { replaceRegisters(it.key, it.value) }
}
private fun replaceRegisters(chunk: IRCodeChunkBase, replacements: Map<Pair<Int, IRDataType>, Int>) {
val (rF, rI) = replacements.asSequence().partition { it.key.second==IRDataType.FLOAT }
val replacementsInt = rI.associate { it.key.first to it.value }
val replacementsFloat = rF.associate { it.key.first to it.value }
fun replaceRegs(fcallArgs: FunctionCallArgs?): FunctionCallArgs? {
if(fcallArgs==null)
return null
val args = if(fcallArgs.arguments.isEmpty()) fcallArgs.arguments else {
fcallArgs.arguments.map {
FunctionCallArgs.ArgumentSpec(
it.name,
it.address,
FunctionCallArgs.RegSpec(
it.reg.dt,
if(it.reg.dt==IRDataType.FLOAT)
replacementsFloat.getOrDefault(it.reg.registerNum, it.reg.registerNum)
else
replacementsInt.getOrDefault(it.reg.registerNum, it.reg.registerNum),
it.reg.cpuRegister
)
)
}
}
val rt = fcallArgs.returns
val returns = if(rt==null) null else {
FunctionCallArgs.RegSpec(
rt.dt,
if(rt.dt==IRDataType.FLOAT)
replacementsFloat.getOrDefault(rt.registerNum, rt.registerNum)
else
replacementsInt.getOrDefault(rt.registerNum, rt.registerNum),
rt.cpuRegister
)
}
return FunctionCallArgs(args, returns)
}
fun replaceRegs(instruction: IRInstruction): IRInstruction {
val reg1 = replacementsInt.getOrDefault(instruction.reg1, instruction.reg1)
val reg2 = replacementsInt.getOrDefault(instruction.reg2, instruction.reg2)
val fpReg1 = replacementsFloat.getOrDefault(instruction.fpReg1, instruction.fpReg1)
val fpReg2 = replacementsFloat.getOrDefault(instruction.fpReg2, instruction.fpReg2)
return instruction.copy(reg1 = reg1, reg2 = reg2, fpReg1 = fpReg1, fpReg2 = fpReg2, fcallArgs = replaceRegs(instruction.fcallArgs))
}
val newInstructions = chunk.instructions.map {
replaceRegs(it)
}
chunk.instructions.clear()
chunk.instructions.addAll(newInstructions)
}
*/
}

View File

@ -16,7 +16,7 @@ class IRUnusedCodeRemover(
irprog.blocks.reversed().forEach { block -> irprog.blocks.reversed().forEach { block ->
if(block.isEmpty()) { if(block.isEmpty()) {
irprog.blocks.remove(block) irprog.blocks.remove(block)
irprog.st.removeTree(block.label) pruneSymboltable(block.label)
numRemoved++ numRemoved++
} }
} }
@ -24,12 +24,26 @@ class IRUnusedCodeRemover(
return numRemoved 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 { private fun removeUnusedSubroutines(): Int {
val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>() val allLabeledChunks = mutableMapOf<String, IRCodeChunkBase>()
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub -> irprog.foreachCodeChunk { chunk ->
sub.chunks.forEach { chunk -> chunk.label?.let { allLabeledChunks[it] = chunk }
chunk.label?.let { allLabeledChunks[it] = chunk }
}
} }
var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks) var numRemoved = removeSimpleUnlinked(allLabeledChunks) + removeUnreachable(allLabeledChunks)
@ -97,12 +111,10 @@ class IRUnusedCodeRemover(
} }
// check if asmsub is linked or called from another regular subroutine // check if asmsub is linked or called from another regular subroutine
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub -> irprog.foreachCodeChunk { chunk ->
sub.chunks.forEach { chunk -> chunk.instructions.forEach {
chunk.instructions.forEach { it.labelSymbol?.let { label -> allSubs[label]?.let { cc -> linkedAsmSubs += cc } }
it.labelSymbol?.let { label -> allSubs[label]?.let { cc -> linkedAsmSubs += cc } } // note: branchTarget can't yet point to another IRAsmSubroutine, so do nothing when it's set
// note: branchTarget can't yet point to another IRAsmSubroutine, so do nothing when it's set
}
} }
} }
@ -123,9 +135,22 @@ class IRUnusedCodeRemover(
} }
private fun removeUnreachable(allLabeledChunks: MutableMap<String, IRCodeChunkBase>): Int { 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()) 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() { fun grow() {
val new = mutableSetOf<IRCodeChunkBase>() val new = mutableSetOf<IRCodeChunkBase>()
reachable.forEach { reachable.forEach {
@ -154,19 +179,29 @@ class IRUnusedCodeRemover(
private fun removeSimpleUnlinked(allLabeledChunks: Map<String, IRCodeChunkBase>): Int { private fun removeSimpleUnlinked(allLabeledChunks: Map<String, IRCodeChunkBase>): Int {
val linkedChunks = mutableSetOf<IRCodeChunkBase>() val linkedChunks = mutableSetOf<IRCodeChunkBase>()
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub -> // all chunks referenced in array initializer values are linked as well!:
sub.chunks.forEach { chunk -> irprog.st.allVariables()
chunk.next?.let { next -> linkedChunks += next } .filter { !it.uninitialized }
chunk.instructions.forEach { .forEach {
if(it.branchTarget==null) { it.onetimeInitializationArrayValue?.let { array ->
it.labelSymbol?.let { label -> allLabeledChunks[label]?.let { cc -> linkedChunks += cc } } array.forEach {elt ->
} else { if(elt.addressOfSymbol!=null && irprog.st.lookup(elt.addressOfSymbol!!)==null)
linkedChunks += it.branchTarget!! linkedChunks += irprog.getChunkWithLabel(elt.addressOfSymbol!!)
} }
} }
if (chunk.label == "main.start")
linkedChunks += chunk
} }
irprog.foreachCodeChunk { chunk ->
chunk.next?.let { next -> linkedChunks += next }
chunk.instructions.forEach {
if(it.branchTarget==null) {
it.labelSymbol?.let { label -> allLabeledChunks[label]?.let { cc -> linkedChunks += cc } }
} else {
linkedChunks += it.branchTarget!!
}
}
if (chunk.label == "main.start")
linkedChunks += chunk
} }
return removeUnlinkedChunks(linkedChunks) return removeUnlinkedChunks(linkedChunks)
@ -176,7 +211,7 @@ class IRUnusedCodeRemover(
linkedChunks: Set<IRCodeChunkBase> linkedChunks: Set<IRCodeChunkBase>
): Int { ): Int {
var numRemoved = 0 var numRemoved = 0
irprog.blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }.forEach { sub -> irprog.foreachSub { sub ->
sub.chunks.withIndex().reversed().forEach { (index, chunk) -> sub.chunks.withIndex().reversed().forEach { (index, chunk) ->
if (chunk !in linkedChunks) { if (chunk !in linkedChunks) {
if (chunk === sub.chunks[0]) { if (chunk === sub.chunks[0]) {

View File

@ -43,6 +43,10 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
warnings.add(text) 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 noErrors(): Boolean = errors.isEmpty()
override fun report() { override fun report() {

View File

@ -36,7 +36,7 @@ class TestIRPeepholeOpt: FunSpec({
return makeIRProgram(listOf(chunk)) return makeIRProgram(listOf(chunk))
} }
fun IRProgram.chunks(): List<IRCodeChunkBase> = this.blocks.flatMap { it.children.filterIsInstance<IRSubroutine>() }.flatMap { it.chunks } fun IRProgram.chunks(): List<IRCodeChunkBase> = this.allSubs().flatMap { it.chunks }.toList()
test("remove nops") { test("remove nops") {
val irProg = makeIRProgram(listOf( val irProg = makeIRProgram(listOf(

View File

@ -41,7 +41,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) 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) 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("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)) sub.add(PtVariable("particleX", DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, 3u, Position.DUMMY))
@ -91,7 +91,7 @@ class TestVmCodeGen: FunSpec({
program.add(block) program.add(block)
// define the "cx16.r0" virtual register // 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)) cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
program.add(cx16block) program.add(cx16block)
@ -120,7 +120,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) 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) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
@ -183,7 +183,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) 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) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
@ -242,7 +242,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) 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) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
@ -289,7 +289,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) 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) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
@ -352,7 +352,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) 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) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
@ -411,7 +411,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) 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) val sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY)) sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
@ -451,7 +451,7 @@ class TestVmCodeGen: FunSpec({
//} //}
val codegen = VmCodeGen() val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) 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) val romsub = PtAsmSub("routine", 0x5000u, setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
block.add(romsub) block.add(romsub)
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), null, Position.DUMMY)

View File

@ -3,16 +3,17 @@ package prog8.optimizer
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.FatalAstException import prog8.ast.base.FatalAstException
import prog8.ast.base.UndefinedSymbolError
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.maySwapOperandOrder import prog8.ast.maySwapOperandOrder
import prog8.ast.statements.ForLoop import prog8.ast.statements.ForLoop
import prog8.ast.statements.RepeatLoop
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.ast.statements.VarDeclType import prog8.ast.statements.VarDeclType
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.AssociativeOperators import prog8.code.core.AssociativeOperators
import prog8.code.core.DataType import prog8.code.core.DataType
import kotlin.math.floor
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() { class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
@ -307,7 +308,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
val rangeTo = iterableRange.to as? NumericLiteral val rangeTo = iterableRange.to as? NumericLiteral
if(rangeFrom==null || rangeTo==null) return noModifications if(rangeFrom==null || rangeTo==null) return noModifications
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: throw UndefinedSymbolError(forLoop.loopVar) val loopvar = forLoop.loopVar.targetVarDecl(program) ?: return noModifications
val stepLiteral = iterableRange.step as? NumericLiteral val stepLiteral = iterableRange.step as? NumericLiteral
when(loopvar.datatype) { when(loopvar.datatype) {
@ -364,6 +365,16 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
return noModifications 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, private class ShuffleOperands(val expr: BinaryExpression,
val exprOperator: String?, val exprOperator: String?,
val subExpr: BinaryExpression, val subExpr: BinaryExpression,

View File

@ -53,7 +53,8 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
} }
if(to==null) { 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) errors.err("range expression to value must be integer", range.to.position)
} else if(to-to.toInt()>0) { } else if(to-to.toInt()>0) {
errors.err("range expression to value must be integer", range.to.position) errors.err("range expression to value must be integer", range.to.position)
@ -68,6 +69,124 @@ class VarConstantValueTypeAdjuster(private val program: Program, private val err
return noModifications return noModifications
} }
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
// choose specific builtin function for the given types
val func = functionCallExpr.target.nameInSource
if(func==listOf("clamp")) {
val t1 = functionCallExpr.args[0].inferType(program)
if(t1.isKnown) {
val replaceFunc: String
if(t1.isBytes) {
replaceFunc = if(t1.istype(DataType.BYTE)) "clamp__byte" else "clamp__ubyte"
} else if(t1.isInteger) {
replaceFunc = if(t1.istype(DataType.WORD)) "clamp__word" else "clamp__uword"
} else {
errors.err("clamp builtin not supported for floats, use floats.clamp", functionCallExpr.position)
return noModifications
}
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
functionCallExpr))
}
}
else if(func==listOf("min") || func==listOf("max")) {
val t1 = functionCallExpr.args[0].inferType(program)
val t2 = functionCallExpr.args[1].inferType(program)
if(t1.isKnown && t2.isKnown) {
val funcName = func[0]
val replaceFunc: String
if(t1.isBytes && t2.isBytes) {
replaceFunc = if(t1.istype(DataType.BYTE) || t2.istype(DataType.BYTE))
"${funcName}__byte"
else
"${funcName}__ubyte"
} else if(t1.isInteger && t2.isInteger) {
replaceFunc = if(t1.istype(DataType.WORD) || t2.istype(DataType.WORD))
"${funcName}__word"
else
"${funcName}__uword"
} else if(t1.isNumeric && t2.isNumeric) {
errors.err("min/max not supported for floats", functionCallExpr.position)
return noModifications
} else {
errors.err("expected numeric arguments", functionCallExpr.args[0].position)
return noModifications
}
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
functionCallExpr))
}
}
else if(func==listOf("abs")) {
val t1 = functionCallExpr.args[0].inferType(program)
if(t1.isKnown) {
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
val replaceFunc = when(dt) {
DataType.BYTE -> "abs__byte"
DataType.WORD -> "abs__word"
DataType.FLOAT -> "abs__float"
DataType.UBYTE, DataType.UWORD -> {
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
}
else -> {
errors.err("expected numeric argument", functionCallExpr.args[0].position)
return noModifications
}
}
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
functionCallExpr))
}
}
else if(func==listOf("sqrt")) {
val t1 = functionCallExpr.args[0].inferType(program)
if(t1.isKnown) {
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
val replaceFunc = when(dt) {
DataType.UBYTE -> "sqrt__ubyte"
DataType.UWORD -> "sqrt__uword"
DataType.FLOAT -> "sqrt__float"
else -> {
errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position)
return noModifications
}
}
return listOf(IAstModification.SetExpression({functionCallExpr.target = it as IdentifierReference},
IdentifierReference(listOf(replaceFunc), functionCallExpr.target.position),
functionCallExpr))
}
}
return noModifications
}
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
// choose specific builtin function for the given types
val func = functionCallStatement.target.nameInSource
if(func==listOf("divmod")) {
val argTypes = functionCallStatement.args.map {it.inferType(program)}.toSet()
if(argTypes.size!=1) {
errors.err("expected all ubyte or all uword arguments", functionCallStatement.args[0].position)
return noModifications
}
val t1 = argTypes.single()
if(t1.isKnown) {
val dt = t1.getOrElse { throw InternalCompilerException("invalid dt") }
val replaceFunc = when(dt) {
DataType.UBYTE -> "divmod__ubyte"
DataType.UWORD -> "divmod__uword"
else -> {
errors.err("expected all ubyte or all uword arguments", functionCallStatement.args[0].position)
return noModifications
}
}
return listOf(IAstModification.SetExpression({functionCallStatement.target = it as IdentifierReference},
IdentifierReference(listOf(replaceFunc), functionCallStatement.target.position),
functionCallStatement))
}
}
return noModifications
}
} }
@ -157,7 +276,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl)) return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
} }
} }
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> { DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT -> {
val rangeExpr = decl.value as? RangeExpression val rangeExpr = decl.value as? RangeExpression
if(rangeExpr!=null) { if(rangeExpr!=null) {
// convert the initializer range expression to an actual array // convert the initializer range expression to an actual array

View File

@ -18,7 +18,7 @@ import kotlin.math.abs
import kotlin.math.log2 import kotlin.math.log2
import kotlin.math.pow 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, class ExpressionSimplifier(private val program: Program,
private val errors: IErrorReporter, private val errors: IErrorReporter,

View File

@ -43,9 +43,9 @@ fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget)
fun Program.optimizeStatements(errors: IErrorReporter, fun Program.optimizeStatements(errors: IErrorReporter,
functions: IBuiltinFunctions, functions: IBuiltinFunctions,
compTarget: ICompilationTarget options: CompilationOptions
): Int { ): Int {
val optimizer = StatementOptimizer(this, errors, functions, compTarget) val optimizer = StatementOptimizer(this, errors, functions, options)
optimizer.visit(this) optimizer.visit(this)
val optimizationCount = optimizer.applyModifications() val optimizationCount = optimizer.applyModifications()
@ -54,8 +54,8 @@ fun Program.optimizeStatements(errors: IErrorReporter,
return optimizationCount return optimizationCount
} }
fun Program.inlineSubroutines(): Int { fun Program.inlineSubroutines(options: CompilationOptions): Int {
val inliner = Inliner(this) val inliner = Inliner(this, options)
inliner.visit(this) inliner.visit(this)
return inliner.applyModifications() return inliner.applyModifications()
} }

View File

@ -8,7 +8,9 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException import prog8.code.core.InternalCompilerException
import prog8.code.target.VMTarget
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null 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. // 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 { class DetermineInlineSubs(val program: Program): IAstVisitor {
private val modifications = mutableListOf<IAstModification>() private val modifications = mutableListOf<IAstModification>()
@ -211,7 +213,7 @@ class Inliner(val program: Program): AstWalker() {
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> { override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
return if(sub==null) return if(sub==null || !canInline(sub, functionCallStatement))
noModifications noModifications
else else
possibleInlineFcallStmt(sub, functionCallStatement, parent) possibleInlineFcallStmt(sub, functionCallStatement, parent)
@ -219,7 +221,7 @@ class Inliner(val program: Program): AstWalker() {
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> { override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine 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]))) { require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
"invalid inline sub at ${sub.position}" "invalid inline sub at ${sub.position}"
} }
@ -246,5 +248,17 @@ class Inliner(val program: Program): AstWalker() {
return noModifications 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
}
} }

View File

@ -13,7 +13,7 @@ import kotlin.math.floor
class StatementOptimizer(private val program: Program, class StatementOptimizer(private val program: Program,
private val errors: IErrorReporter, private val errors: IErrorReporter,
private val functions: IBuiltinFunctions, private val functions: IBuiltinFunctions,
private val compTarget: ICompilationTarget private val options: CompilationOptions
) : AstWalker() { ) : AstWalker() {
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> { override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
@ -39,7 +39,7 @@ class StatementOptimizer(private val program: Program,
if(string!=null) { if(string!=null) {
val pos = functionCallStatement.position val pos = functionCallStatement.position
if (string.value.length == 1) { 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( val chrout = FunctionCallStatement(
IdentifierReference(listOf("txt", "chrout"), pos), IdentifierReference(listOf("txt", "chrout"), pos),
mutableListOf(NumericLiteral(DataType.UBYTE, firstCharEncoded.toDouble(), 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) IAstModification.Remove(stringDecl, stringDecl.parent as IStatementContainer)
) )
} else if (string.value.length == 2) { } 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( val chrout1 = FunctionCallStatement(
IdentifierReference(listOf("txt", "chrout"), pos), IdentifierReference(listOf("txt", "chrout"), pos),
mutableListOf(NumericLiteral(DataType.UBYTE, firstTwoCharsEncoded[0].toDouble(), pos)), mutableListOf(NumericLiteral(DataType.UBYTE, firstTwoCharsEncoded[0].toDouble(), pos)),
@ -108,6 +108,16 @@ 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)
if(options.slowCodegenWarnings)
errors.warn("else can be omitted", ifElse.elsepart.position)
return listOf(
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse),
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
)
}
return noModifications return noModifications
} }
@ -141,7 +151,7 @@ class StatementOptimizer(private val program: Program,
val size = sv.value.length val size = sv.value.length
if(size==1) { if(size==1) {
// loop over string of length 1 -> just assign the single character // 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 byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position)
val scope = AnonymousScope(mutableListOf(), forLoop.position) val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position)) scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
@ -166,16 +176,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 return noModifications
} }
@ -326,7 +326,7 @@ class StatementOptimizer(private val program: Program,
if (rightCv == 0.0) { if (rightCv == 0.0) {
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer)) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) { } 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) // 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) val incs = AnonymousScope(mutableListOf(), assignment.position)
repeat(rightCv.toInt()) { repeat(rightCv.toInt()) {
@ -340,7 +340,7 @@ class StatementOptimizer(private val program: Program,
if (rightCv == 0.0) { if (rightCv == 0.0) {
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer)) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) { } 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) // 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) val decs = AnonymousScope(mutableListOf(), assignment.position)
repeat(rightCv.toInt()) { repeat(rightCv.toInt()) {
@ -391,4 +391,47 @@ class StatementOptimizer(private val program: Program,
else else
noModifications 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
}
} }

View File

@ -75,7 +75,9 @@ class UnusedCodeRemover(private val program: Program,
if(!block.statements.any { it is Subroutine && it.hasBeenInlined }) if(!block.statements.any { it is Subroutine && it.hasBeenInlined })
errors.warn("removing unused block '${block.name}'", block.position) 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)) return listOf(IAstModification.Remove(block, parent as IStatementContainer))
} }
} }

View File

@ -1,9 +1,10 @@
plugins { plugins {
id 'java' id 'java'
id 'application' id 'application'
id "org.jetbrains.kotlin.jvm" id 'org.jetbrains.kotlin.jvm'
id 'com.github.johnrengelman.shadow' version '7.1.2' 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 { java {
@ -24,8 +25,6 @@ compileTestKotlin {
} }
} }
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
dependencies { dependencies {
implementation project(':codeCore') implementation project(':codeCore')
implementation project(':codeOptimizers') implementation project(':codeOptimizers')
@ -34,14 +33,14 @@ dependencies {
implementation project(':codeGenIntermediate') implementation project(':codeGenIntermediate')
implementation project(':codeGenExperimental') implementation project(':codeGenExperimental')
implementation project(':virtualmachine') implementation project(':virtualmachine')
implementation "org.antlr:antlr4-runtime:4.12.0" implementation "org.antlr:antlr4-runtime:4.13.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5' implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.5'
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.18"
testImplementation project(':intermediate') testImplementation project(':intermediate')
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.5.5' testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.6.2'
} }
configurations.all { configurations.all {
@ -82,7 +81,7 @@ application {
shadowJar { shadowJar {
archiveBaseName = 'prog8compiler' archiveBaseName = 'prog8compiler'
archiveVersion = prog8version archiveVersion = version
// minimize() // 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 build.finalizedBy installDist, installShadowDist
compileKotlin.dependsOn createVersionFile // , failDirtyNotSnapshot
compileJava.dependsOn createVersionFile

View File

@ -2,44 +2,41 @@
; Including memory registers, I/O registers, Basic and Kernal subroutines. ; Including memory registers, I/O registers, Basic and Kernal subroutines.
atari { atari {
%option no_symbol_prefixing
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in &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 &uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in &uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
; ---- kernal routines ----
; TODO
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
; TODO
%asm {{
sei
cld
clc
; TODO reset screen mode etc etc
clv
cli
rts
}}
} }
asmsub init_system_phase2() {
%asm {{
rts ; no phase 2 steps on the Atari
}}
}
}
sys { sys {
; ------- lowlevel system routines -------- ; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 8 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL const ubyte target = 8 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16, 8 = atari800XL
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
; TODO
%asm {{
sei
cld
clc
; TODO reset screen mode etc etc
clv
cli
rts
}}
}
asmsub init_system_phase2() {
%asm {{
rts ; no phase 2 steps on the Atari
}}
}
asmsub reset_system() { asmsub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt. ; Soft-reset the system back to initial power-on Basic prompt.
@ -188,6 +185,19 @@ _longcopy
}} }}
} }
inline asmsub irqsafe_set_irqd() {
%asm {{
php
sei
}}
}
inline asmsub irqsafe_clear_irqd() {
%asm {{
plp
}}
}
inline asmsub exit(ubyte returnvalue @A) { inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register ; -- immediately exit the program with a return code in the A register
; TODO ; TODO
@ -208,6 +218,7 @@ _longcopy
} }
cx16 { cx16 {
%option no_symbol_prefixing
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage ; 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 ; they are simulated on the Atari as well but their location in memory is different
@ -313,4 +324,31 @@ cx16 {
&byte r13sH = $1b1b &byte r13sH = $1b1b
&byte r14sH = $1b1d &byte r14sH = $1b1d
&byte r15sH = $1b1f &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
}}
}
} }

View File

@ -6,6 +6,8 @@
txt { txt {
%option no_symbol_prefixing
const ubyte DEFAULT_WIDTH = 40 const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 24 const ubyte DEFAULT_HEIGHT = 24
@ -70,7 +72,7 @@ sub uppercase() {
; TODO ; TODO
} }
asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) { asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
; ---- scroll the whole screen 1 character to the left ; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself ; contents of the rightmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -81,7 +83,7 @@ asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
}} }}
} }
asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) { asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character to the right ; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself ; contents of the leftmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -91,7 +93,7 @@ asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
}} }}
} }
asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) { asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character up ; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself ; contents of the bottom row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -101,7 +103,7 @@ asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
}} }}
} }
asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) { asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character down ; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself ; contents of the top row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -118,7 +120,6 @@ romsub $F2Fd = waitkey()
asmsub chrout(ubyte char @ A) { asmsub chrout(ubyte char @ A) {
%asm {{ %asm {{
sta _tmp_outchar+1 sta _tmp_outchar+1
pha
txa txa
pha pha
tya tya
@ -130,7 +131,6 @@ _tmp_outchar
tay tay
pla pla
tax tax
pla
rts rts
}} }}
} }
@ -210,7 +210,7 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
}} }}
} }
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well) ; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
@ -228,7 +228,7 @@ asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
@ -249,7 +249,7 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
pha pha
@ -261,7 +261,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits) ; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well) ; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{

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? ; 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 { graphics {
%option no_symbol_prefixing
const uword WIDTH = 320 const uword WIDTH = 320
const ubyte HEIGHT = 200 const ubyte HEIGHT = 200

View File

@ -1,7 +1,9 @@
; Prog8 definitions for the Commodore-128 ; Prog8 definitions for the Commodore-128
; Including memory registers, I/O registers, Basic and Kernal subroutines. ; Including memory registers, I/O registers, Basic and Kernal subroutines.
c64 { cbm {
; Commodore (CBM) common variables, vectors and kernal routines
%option no_symbol_prefixing
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte &ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
&ubyte TIME_MID = $a1 ; .. mid byte &ubyte TIME_MID = $a1 ; .. mid byte
@ -47,6 +49,100 @@ c64 {
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255 const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255 const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
; STROUT --> use txt.print
; CLEARSCR -> use txt.clear_screen
; HOMECRSR -> use txt.home or txt.plot
romsub $FA65 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
romsub $FF33 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
; TODO c128 a bunch of kernal routines are missing here that are specific to the c128
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
romsub $FF8D = VECTOR(uword userptr @ XY, bool dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
romsub $FF99 = MEMTOP(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set top of memory pointer
romsub $FF9C = MEMBOT(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798 ($31E)) define an input channel
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
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
; ---- end of C64 compatible ROM kernal routines ----
; ---- utilities -----
asmsub STOP2() -> 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
rts
}}
}
asmsub RDTIM16() -> 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
}}
}
}
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) ; the default locations of the 8 sprite pointers (store address of sprite / 64)
&ubyte SPRPTR0 = 2040 &ubyte SPRPTR0 = 2040
&ubyte SPRPTR1 = 2041 &ubyte SPRPTR1 = 2041
@ -196,95 +292,93 @@ c64 {
; ---- end of SID registers ---- ; ---- end of SID registers ----
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ---- }
; STROUT --> use txt.print c128 {
; CLEARSCR -> use txt.clear_screen ; ---- C128 specific registers ----
; HOMECRSR -> use txt.home or txt.plot %option no_symbol_prefixing
romsub $FA65 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine &ubyte VM1 = $0A2C ; shadow for VUC $d018 in text mode
romsub $FF33 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup &ubyte VM2 = $0A2D ; shadow for VIC $d018 in bitmap screen mode
&ubyte VM3 = $0A2E ; starting page for VDC screen mem
&ubyte VM4 = $0A2F ; starting page for VDC attribute mem
; TODO c128 a bunch of kernal routines are missing here that are specific to the c128
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip ; ---- C128 specific system utility routines: ----
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte 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
; ---- end of C64 compatible ROM kernal routines ---- asmsub disable_basic() clobbers(A) {
; ---- utilities -----
asmsub STOP2() -> 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 {{ %asm {{
txa lda $0a04 ; disable BASIC shadow registers
pha and #$fe
jsr c64.STOP sta $0a04
beq +
pla lda #$01 ; disable BASIC IRQ service routine
tax sta $12fd
lda #$ff ; disable screen editor IRQ setup
sta $d8
lda #$b7 ; skip programmable function key check
sta $033c
lda #$0e ; bank out BASIC ROM
sta $ff00
rts
}}
}
; ---- end of C128 specific system utility routines ----
}
sys {
; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 128 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
; Also a different color scheme is chosen to identify ourselves a little.
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
%asm {{
sei
cld
lda #0 lda #0
rts sta $ff00 ; select default bank 15
+ pla jsr cbm.IOINIT
tax jsr cbm.RESTOR
lda #1 jsr cbm.CINT
lda #6
sta c64.EXTCOL
lda #7
sta cbm.COLOR
lda #0
sta c64.BGCOL0
jsr disable_runstop_and_charsetswitch
clc
clv
cli
rts rts
}} }}
} }
asmsub RDTIM16() -> uword @AY { asmsub init_system_phase2() {
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG rts ; no phase 2 steps on the C128
jsr c64.RDTIM
pha
txa
tay
pla
ldx P8ZP_SCRATCH_REG
rts
}} }}
} }
asmsub cleanup_at_exit() {
; executed when the main subroutine does rts
%asm {{
jmp sys.enable_runstop_and_charsetswitch
}}
}
; ---- system utility routines that are essentially the same as on the C64: -----
asmsub disable_runstop_and_charsetswitch() clobbers(A) { asmsub disable_runstop_and_charsetswitch() clobbers(A) {
%asm {{ %asm {{
lda #$80 lda #$80
@ -305,7 +399,7 @@ asmsub enable_runstop_and_charsetswitch() clobbers(A) {
}} }}
} }
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) { asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
%asm {{ %asm {{
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
@ -314,9 +408,9 @@ asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
sta _use_kernal sta _use_kernal
sei sei
lda #<_irq_handler lda #<_irq_handler
sta c64.CINV sta cbm.CINV
lda #>_irq_handler lda #>_irq_handler
sta c64.CINV+1 sta cbm.CINV+1
cli cli
rts rts
_irq_handler jsr _irq_handler_init _irq_handler jsr _irq_handler_init
@ -334,7 +428,7 @@ _modified jsr $ffff ; modified
tax tax
pla pla
rti rti
+ jmp c64.IRQDFRT ; continue with normal kernal irq routine + jmp cbm.IRQDFRT ; continue with normal kernal irq routine
_use_kernal .byte 0 _use_kernal .byte 0
@ -386,10 +480,10 @@ IRQ_SCRATCH_ZPWORD2 .word 0
asmsub restore_irq() clobbers(A) { asmsub restore_irq() clobbers(A) {
%asm {{ %asm {{
sei sei
lda #<c64.IRQDFRT lda #<cbm.IRQDFRT
sta c64.CINV sta cbm.CINV
lda #>c64.IRQDFRT lda #>cbm.IRQDFRT
sta c64.CINV+1 sta cbm.CINV+1
lda #0 lda #0
sta c64.IREQMASK ; disable raster irq sta c64.IREQMASK ; disable raster irq
lda #%10000001 lda #%10000001
@ -399,7 +493,7 @@ asmsub restore_irq() clobbers(A) {
}} }}
} }
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @Pc) clobbers(A) { asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) {
%asm {{ %asm {{
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
@ -411,9 +505,9 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @P
sei sei
jsr _setup_raster_irq jsr _setup_raster_irq
lda #<_raster_irq_handler lda #<_raster_irq_handler
sta c64.CINV sta cbm.CINV
lda #>_raster_irq_handler lda #>_raster_irq_handler
sta c64.CINV+1 sta cbm.CINV+1
cli cli
rts rts
@ -421,8 +515,8 @@ _raster_irq_handler
jsr set_irq._irq_handler_init jsr set_irq._irq_handler_init
_modified jsr $ffff ; modified _modified jsr $ffff ; modified
jsr set_irq._irq_handler_end jsr set_irq._irq_handler_end
lda #$ff lda #$ff
sta c64.VICIRQ ; acknowledge raster irq sta c64.VICIRQ ; acknowledge raster irq
lda set_irq._use_kernal lda set_irq._use_kernal
bne + bne +
; end irq processing - don't use kernal's irq handling ; end irq processing - don't use kernal's irq handling
@ -432,7 +526,7 @@ _modified jsr $ffff ; modified
tax tax
pla pla
rti rti
+ jmp c64.IRQDFRT ; continue with kernal irq routine + jmp cbm.IRQDFRT ; continue with kernal irq routine
_setup_raster_irq _setup_raster_irq
pha pha
@ -456,99 +550,13 @@ _setup_raster_irq
}} }}
} }
}
c128 {
; ---- C128 specific registers ----
&ubyte VM1 = $0A2C ; shadow for VUC $d018 in text mode
&ubyte VM2 = $0A2D ; shadow for VIC $d018 in bitmap screen mode
&ubyte VM3 = $0A2E ; starting page for VDC screen mem
&ubyte VM4 = $0A2F ; starting page for VDC attribute mem
; ---- C128 specific system utility routines: ----
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
; Also a different color scheme is chosen to identify ourselves a little.
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
%asm {{
sei
cld
lda #0
sta $ff00 ; select default bank 15
jsr c64.IOINIT
jsr c64.RESTOR
jsr c64.CINT
lda #6
sta c64.EXTCOL
lda #7
sta c64.COLOR
lda #0
sta c64.BGCOL0
jsr c64.disable_runstop_and_charsetswitch
clc
clv
cli
rts
}}
}
asmsub init_system_phase2() {
%asm {{
rts ; no phase 2 steps on the C128
}}
}
asmsub cleanup_at_exit() {
; executed when the main subroutine does rts
%asm {{
jmp c64.enable_runstop_and_charsetswitch
}}
}
asmsub disable_basic() clobbers(A) {
%asm {{
lda $0a04 ; disable BASIC shadow registers
and #$fe
sta $0a04
lda #$01 ; disable BASIC IRQ service routine
sta $12fd
lda #$ff ; disable screen editor IRQ setup
sta $d8
lda #$b7 ; skip programmable function key check
sta $033c
lda #$0e ; bank out BASIC ROM
sta $ff00
rts
}}
}
; ---- end of C128 specific system utility routines ----
}
sys {
; ------- lowlevel system routines --------
const ubyte target = 128 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
asmsub reset_system() { asmsub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt. ; Soft-reset the system back to initial power-on Basic prompt.
%asm {{ %asm {{
sei sei
lda #0 lda #0
sta $ff00 ; default bank 15 sta $ff00 ; default bank 15
jmp (c64.RESET_VEC) jmp (cbm.RESET_VEC)
}} }}
} }
@ -566,9 +574,9 @@ _loop lda P8ZP_SCRATCH_W1
ldx P8ZP_SCRATCH_B1 ldx P8ZP_SCRATCH_B1
rts rts
+ lda c64.TIME_LO + lda cbm.TIME_LO
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
- lda c64.TIME_LO - lda cbm.TIME_LO
cmp P8ZP_SCRATCH_B1 cmp P8ZP_SCRATCH_B1
beq - beq -
@ -725,13 +733,26 @@ _longcopy
}} }}
} }
inline asmsub irqsafe_set_irqd() {
%asm {{
php
sei
}}
}
inline asmsub irqsafe_clear_irqd() {
%asm {{
plp
}}
}
inline asmsub exit(ubyte returnvalue @A) { inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register ; -- immediately exit the program with a return code in the A register
%asm {{ %asm {{
lda #0 lda #0
sta $ff00 ; default bank 15 sta $ff00 ; default bank 15
jsr c64.CLRCHN ; reset i/o channels jsr cbm.CLRCHN ; reset i/o channels
jsr c64.enable_runstop_and_charsetswitch jsr sys.enable_runstop_and_charsetswitch
ldx prog8_lib.orig_stackpointer ldx prog8_lib.orig_stackpointer
txs txs
rts ; return to original caller rts ; return to original caller
@ -748,6 +769,7 @@ _longcopy
} }
cx16 { cx16 {
%option no_symbol_prefixing
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage ; 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 ; they are simulated on the C128 as well but their location in memory is different
@ -857,4 +879,30 @@ cx16 {
&byte r13sH = $1b1b &byte r13sH = $1b1b
&byte r14sH = $1b1d &byte r14sH = $1b1d
&byte r15sH = $1b1f &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
}}
}
} }

View File

@ -5,6 +5,7 @@
txt { txt {
%option no_symbol_prefixing
const ubyte DEFAULT_WIDTH = 40 const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25 const ubyte DEFAULT_HEIGHT = 25
@ -30,10 +31,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
; ---- set the cursor on the given column (starting with 0) on the current line ; ---- set the cursor on the given column (starting with 0) on the current line
%asm {{ %asm {{
sec sec
jsr c64.PLOT jsr cbm.PLOT
tay tay
clc clc
jmp c64.PLOT jmp cbm.PLOT
}} }}
} }
@ -57,10 +58,10 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
; (assumes screen matrix is at the default address) ; (assumes screen matrix is at the default address)
%asm {{ %asm {{
ldy #250 ldy #250
- sta c64.Screen+250*0-1,y - sta cbm.Screen+250*0-1,y
sta c64.Screen+250*1-1,y sta cbm.Screen+250*1-1,y
sta c64.Screen+250*2-1,y sta cbm.Screen+250*2-1,y
sta c64.Screen+250*3-1,y sta cbm.Screen+250*3-1,y
dey dey
bne - bne -
rts rts
@ -72,10 +73,10 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
; (assumes color matrix is at the default address) ; (assumes color matrix is at the default address)
%asm {{ %asm {{
ldy #250 ldy #250
- sta c64.Colors+250*0-1,y - sta cbm.Colors+250*0-1,y
sta c64.Colors+250*1-1,y sta cbm.Colors+250*1-1,y
sta c64.Colors+250*2-1,y sta cbm.Colors+250*2-1,y
sta c64.Colors+250*3-1,y sta cbm.Colors+250*3-1,y
dey dey
bne - bne -
rts rts
@ -83,7 +84,7 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
} }
sub color (ubyte txtcol) { sub color (ubyte txtcol) {
c64.COLOR = txtcol cbm.COLOR = txtcol
} }
sub lowercase() { sub lowercase() {
@ -96,7 +97,7 @@ sub uppercase() {
c128.VM1 &= ~2 c128.VM1 &= ~2
} }
asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) { asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
; ---- scroll the whole screen 1 character to the left ; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself ; contents of the rightmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -110,10 +111,10 @@ asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
ldy #38 ldy #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 1,x lda cbm.Screen + 40*row + 1,x
sta c64.Screen + 40*row + 0,x sta cbm.Screen + 40*row + 0,x
lda c64.Colors + 40*row + 1,x lda cbm.Colors + 40*row + 1,x
sta c64.Colors + 40*row + 0,x sta cbm.Colors + 40*row + 0,x
.next .next
inx inx
dey dey
@ -125,8 +126,8 @@ _scroll_screen ; scroll only the screen memory
ldy #38 ldy #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 1,x lda cbm.Screen + 40*row + 1,x
sta c64.Screen + 40*row + 0,x sta cbm.Screen + 40*row + 0,x
.next .next
inx inx
dey dey
@ -137,7 +138,7 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) { asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character to the right ; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself ; contents of the leftmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -149,10 +150,10 @@ asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
ldx #38 ldx #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 0,x lda cbm.Screen + 40*row + 0,x
sta c64.Screen + 40*row + 1,x sta cbm.Screen + 40*row + 1,x
lda c64.Colors + 40*row + 0,x lda cbm.Colors + 40*row + 0,x
sta c64.Colors + 40*row + 1,x sta cbm.Colors + 40*row + 1,x
.next .next
dex dex
bpl - bpl -
@ -162,8 +163,8 @@ _scroll_screen ; scroll only the screen memory
ldx #38 ldx #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 0,x lda cbm.Screen + 40*row + 0,x
sta c64.Screen + 40*row + 1,x sta cbm.Screen + 40*row + 1,x
.next .next
dex dex
bpl - bpl -
@ -173,7 +174,7 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) { asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character up ; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself ; contents of the bottom row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -185,10 +186,10 @@ asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
ldx #39 ldx #39
- -
.for row=1, row<=24, row+=1 .for row=1, row<=24, row+=1
lda c64.Screen + 40*row,x lda cbm.Screen + 40*row,x
sta c64.Screen + 40*(row-1),x sta cbm.Screen + 40*(row-1),x
lda c64.Colors + 40*row,x lda cbm.Colors + 40*row,x
sta c64.Colors + 40*(row-1),x sta cbm.Colors + 40*(row-1),x
.next .next
dex dex
bpl - bpl -
@ -198,8 +199,8 @@ _scroll_screen ; scroll only the screen memory
ldx #39 ldx #39
- -
.for row=1, row<=24, row+=1 .for row=1, row<=24, row+=1
lda c64.Screen + 40*row,x lda cbm.Screen + 40*row,x
sta c64.Screen + 40*(row-1),x sta cbm.Screen + 40*(row-1),x
.next .next
dex dex
bpl - bpl -
@ -209,7 +210,7 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) { asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character down ; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself ; contents of the top row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -221,10 +222,10 @@ asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
ldx #39 ldx #39
- -
.for row=23, row>=0, row-=1 .for row=23, row>=0, row-=1
lda c64.Colors + 40*row,x lda cbm.Colors + 40*row,x
sta c64.Colors + 40*(row+1),x sta cbm.Colors + 40*(row+1),x
lda c64.Screen + 40*row,x lda cbm.Screen + 40*row,x
sta c64.Screen + 40*(row+1),x sta cbm.Screen + 40*(row+1),x
.next .next
dex dex
bpl - bpl -
@ -234,8 +235,8 @@ _scroll_screen ; scroll only the screen memory
ldx #39 ldx #39
- -
.for row=23, row>=0, row-=1 .for row=23, row>=0, row-=1
lda c64.Screen + 40*row,x lda cbm.Screen + 40*row,x
sta c64.Screen + 40*(row+1),x sta cbm.Screen + 40*(row+1),x
.next .next
dex dex
bpl - bpl -
@ -245,20 +246,20 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse. romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
asmsub print (str text @ AY) clobbers(A,Y) { asmsub print (str text @ AY) clobbers(A,Y) {
; ---- print null terminated string from A/Y ; ---- print null terminated string from A/Y
; note: the compiler contains an optimization that will replace ; note: the compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char, ; a call to this subroutine with a string argument of just one char,
; by just one call to c64.CHROUT of that single char. ; by just one call to cbm.CHROUT of that single char.
%asm {{ %asm {{
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG sty P8ZP_SCRATCH_REG
ldy #0 ldy #0
- lda (P8ZP_SCRATCH_B1),y - lda (P8ZP_SCRATCH_B1),y
beq + beq +
jsr c64.CHROUT jsr cbm.CHROUT
iny iny
bne - bne -
+ rts + rts
@ -272,11 +273,11 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
jsr conv.ubyte2decimal jsr conv.ubyte2decimal
pha pha
tya tya
jsr c64.CHROUT jsr cbm.CHROUT
pla pla
jsr c64.CHROUT jsr cbm.CHROUT
txa txa
jsr c64.CHROUT jsr cbm.CHROUT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
@ -292,16 +293,16 @@ _print_byte_digits
cpy #'0' cpy #'0'
beq + beq +
tya tya
jsr c64.CHROUT jsr cbm.CHROUT
pla pla
jsr c64.CHROUT jsr cbm.CHROUT
jmp _ones jmp _ones
+ pla + pla
cmp #'0' cmp #'0'
beq _ones beq _ones
jsr c64.CHROUT jsr cbm.CHROUT
_ones txa _ones txa
jsr c64.CHROUT jsr cbm.CHROUT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
@ -315,45 +316,45 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
cmp #0 cmp #0
bpl + bpl +
lda #'-' lda #'-'
jsr c64.CHROUT jsr cbm.CHROUT
+ pla + pla
jsr conv.byte2decimal jsr conv.byte2decimal
jmp print_ub._print_byte_digits jmp print_ub._print_byte_digits
}} }}
} }
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well) ; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
bcc + bcc +
pha pha
lda #'$' lda #'$'
jsr c64.CHROUT jsr cbm.CHROUT
pla pla
+ jsr conv.ubyte2hex + jsr conv.ubyte2hex
jsr c64.CHROUT jsr cbm.CHROUT
tya tya
jsr c64.CHROUT jsr cbm.CHROUT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
} }
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
bcc + bcc +
lda #'%' lda #'%'
jsr c64.CHROUT jsr cbm.CHROUT
+ ldy #8 + ldy #8
- lda #'0' - lda #'0'
asl P8ZP_SCRATCH_B1 asl P8ZP_SCRATCH_B1
bcc + bcc +
lda #'1' lda #'1'
+ jsr c64.CHROUT + jsr cbm.CHROUT
dey dey
bne - bne -
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
@ -361,7 +362,7 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
pha pha
@ -373,7 +374,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits) ; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well) ; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
@ -394,7 +395,7 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
ldy #0 ldy #0
- lda conv.uword2decimal.decTenThousands,y - lda conv.uword2decimal.decTenThousands,y
beq + beq +
jsr c64.CHROUT jsr cbm.CHROUT
iny iny
bne - bne -
+ ldx P8ZP_SCRATCH_REG + ldx P8ZP_SCRATCH_REG
@ -417,14 +418,14 @@ asmsub print_uw (uword value @ AY) clobbers(A,Y) {
bne - bne -
_gotdigit _gotdigit
jsr c64.CHROUT jsr cbm.CHROUT
iny iny
lda conv.uword2decimal.decTenThousands,y lda conv.uword2decimal.decTenThousands,y
bne _gotdigit bne _gotdigit
rts rts
_allzero _allzero
lda #'0' lda #'0'
jmp c64.CHROUT jmp cbm.CHROUT
}} }}
} }
@ -435,7 +436,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
bpl + bpl +
pha pha
lda #'-' lda #'-'
jsr c64.CHROUT jsr cbm.CHROUT
tya tya
eor #255 eor #255
tay tay
@ -457,7 +458,7 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
ldy #0 ; char counter = 0 ldy #0 ; char counter = 0
- jsr c64.CHRIN - jsr cbm.CHRIN
cmp #$0d ; return (ascii 13) pressed? cmp #$0d ; return (ascii 13) pressed?
beq + ; yes, end. beq + ; yes, end.
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
@ -588,7 +589,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
tax tax
clc clc
jsr c64.PLOT jsr cbm.PLOT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
@ -597,7 +598,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
asmsub width() clobbers(X,Y) -> ubyte @A { asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns) ; -- returns the text screen width (number of columns)
%asm {{ %asm {{
jsr c64.SCREEN jsr cbm.SCREEN
txa txa
rts rts
}} }}
@ -606,7 +607,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
asmsub height() clobbers(X, Y) -> ubyte @A { asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows) ; -- returns the text screen height (number of rows)
%asm {{ %asm {{
jsr c64.SCREEN jsr cbm.SCREEN
tya tya
rts rts
}} }}

View File

@ -5,6 +5,7 @@
floats { floats {
; ---- this block contains C-64 floating point related functions ---- ; ---- this block contains C-64 floating point related functions ----
%option no_symbol_prefixing
const float PI = 3.141592653589793 const float PI = 3.141592653589793
const float TWOPI = 6.283185307179586 const float TWOPI = 6.283185307179586

View File

@ -144,3 +144,19 @@ func_all_f_stack .proc
jsr a_times_5 jsr a_times_5
jmp prog8_lib.func_all_b_stack jmp prog8_lib.func_all_b_stack
.pend .pend
func_abs_f_into_FAC1 .proc
stx P8ZP_SCRATCH_REG
jsr MOVFM
jsr ABS
ldx P8ZP_SCRATCH_REG
rts
.pend
func_sqrt_into_FAC1 .proc
stx P8ZP_SCRATCH_REG
jsr MOVFM
jsr SQR
ldx P8ZP_SCRATCH_REG
rts
.pend

View File

@ -5,6 +5,8 @@
; assumes bitmap screen memory is $2000-$3fff ; assumes bitmap screen memory is $2000-$3fff
graphics { graphics {
%option no_symbol_prefixing
const uword BITMAP_ADDRESS = $2000 const uword BITMAP_ADDRESS = $2000
const uword WIDTH = 320 const uword WIDTH = 320
const ubyte HEIGHT = 200 const ubyte HEIGHT = 200

View File

@ -1,7 +1,13 @@
; Prog8 definitions for the Commodore-64 ; Prog8 definitions for the Commodore-64
; Including memory registers, I/O registers, Basic and Kernal subroutines. ; Including memory registers, I/O registers, Basic and Kernal subroutines.
c64 {
cbm {
; Commodore (CBM) common variables, vectors and kernal routines
%option no_symbol_prefixing
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte &ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
&ubyte TIME_MID = $a1 ; .. mid byte &ubyte TIME_MID = $a1 ; .. mid byte
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec &ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
@ -50,6 +56,93 @@ c64 {
const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255 const uword Screen = $0400 ; to have this as an array[40*25] the compiler would have to support array size > 255
const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255 const uword Colors = $d800 ; to have this as an array[40*25] the compiler would have to support array size > 255
; ---- CBM ROM kernal routines (C64 addresses) ----
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
romsub $FF8D = VECTOR(uword userptr @ XY, bool dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
romsub $FF99 = MEMTOP(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set top of memory pointer
romsub $FF9C = MEMBOT(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798 ($31E)) define an input channel
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
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 {
; -- 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
rts
}}
}
asmsub RDTIM16() -> 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
}}
}
}
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) ; the default locations of the 8 sprite pointers (store address of sprite / 64)
&ubyte SPRPTR0 = 2040 &ubyte SPRPTR0 = 2040
&ubyte SPRPTR1 = 2041 &ubyte SPRPTR1 = 2041
@ -199,93 +292,15 @@ c64 {
; ---- end of SID registers ---- ; ---- end of SID registers ----
; ---- C64 ROM kernal routines ----
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte 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
; ---- end of C64 ROM kernal routines ----
; ---- utilities -----
asmsub STOP2() -> 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 c64.STOP
beq +
pla
tax
lda #0
rts
+ pla
tax
lda #1
rts
}}
} }
asmsub RDTIM16() -> uword @AY { sys {
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience ; ------- lowlevel system routines --------
%asm {{
stx P8ZP_SCRATCH_REG
jsr c64.RDTIM
pha
txa
tay
pla
ldx P8ZP_SCRATCH_REG
rts
}}
}
%option no_symbol_prefixing
const ubyte target = 64 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
; ---- C64 specific system utility routines: ----
asmsub init_system() { asmsub init_system() {
; Initializes the machine to a sane starting state. ; Initializes the machine to a sane starting state.
@ -301,13 +316,13 @@ asmsub init_system() {
sta $00 sta $00
lda #%00100111 lda #%00100111
sta $01 sta $01
jsr c64.IOINIT jsr cbm.IOINIT
jsr c64.RESTOR jsr cbm.RESTOR
jsr c64.CINT jsr cbm.CINT
lda #6 lda #6
sta c64.EXTCOL sta c64.EXTCOL
lda #7 lda #7
sta c64.COLOR sta cbm.COLOR
lda #0 lda #0
sta c64.BGCOL0 sta c64.BGCOL0
jsr disable_runstop_and_charsetswitch jsr disable_runstop_and_charsetswitch
@ -327,7 +342,7 @@ asmsub init_system_phase2() {
asmsub cleanup_at_exit() { asmsub cleanup_at_exit() {
; executed when the main subroutine does rts ; executed when the main subroutine does rts
%asm {{ %asm {{
jmp c64.enable_runstop_and_charsetswitch jmp sys.enable_runstop_and_charsetswitch
}} }}
} }
@ -351,7 +366,7 @@ asmsub enable_runstop_and_charsetswitch() clobbers(A) {
}} }}
} }
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) { asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
%asm {{ %asm {{
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
@ -360,9 +375,9 @@ asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
sta _use_kernal sta _use_kernal
sei sei
lda #<_irq_handler lda #<_irq_handler
sta c64.CINV sta cbm.CINV
lda #>_irq_handler lda #>_irq_handler
sta c64.CINV+1 sta cbm.CINV+1
cli cli
rts rts
_irq_handler jsr _irq_handler_init _irq_handler jsr _irq_handler_init
@ -380,7 +395,7 @@ _modified jsr $ffff ; modified
tax tax
pla pla
rti rti
+ jmp c64.IRQDFRT ; continue with normal kernal irq routine + jmp cbm.IRQDFRT ; continue with normal kernal irq routine
_use_kernal .byte 0 _use_kernal .byte 0
@ -432,10 +447,10 @@ IRQ_SCRATCH_ZPWORD2 .word 0
asmsub restore_irq() clobbers(A) { asmsub restore_irq() clobbers(A) {
%asm {{ %asm {{
sei sei
lda #<c64.IRQDFRT lda #<cbm.IRQDFRT
sta c64.CINV sta cbm.CINV
lda #>c64.IRQDFRT lda #>cbm.IRQDFRT
sta c64.CINV+1 sta cbm.CINV+1
lda #0 lda #0
sta c64.IREQMASK ; disable raster irq sta c64.IREQMASK ; disable raster irq
lda #%10000001 lda #%10000001
@ -445,7 +460,7 @@ asmsub restore_irq() clobbers(A) {
}} }}
} }
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @Pc) clobbers(A) { asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, bool useKernal @Pc) clobbers(A) {
%asm {{ %asm {{
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
@ -457,9 +472,9 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @P
sei sei
jsr _setup_raster_irq jsr _setup_raster_irq
lda #<_raster_irq_handler lda #<_raster_irq_handler
sta c64.CINV sta cbm.CINV
lda #>_raster_irq_handler lda #>_raster_irq_handler
sta c64.CINV+1 sta cbm.CINV+1
cli cli
rts rts
@ -467,8 +482,8 @@ _raster_irq_handler
jsr set_irq._irq_handler_init jsr set_irq._irq_handler_init
_modified jsr $ffff ; modified _modified jsr $ffff ; modified
jsr set_irq._irq_handler_end jsr set_irq._irq_handler_end
lda #$ff lda #$ff
sta c64.VICIRQ ; acknowledge raster irq sta c64.VICIRQ ; acknowledge raster irq
lda set_irq._use_kernal lda set_irq._use_kernal
bne + bne +
; end irq processing - don't use kernal's irq handling ; end irq processing - don't use kernal's irq handling
@ -478,7 +493,7 @@ _modified jsr $ffff ; modified
tax tax
pla pla
rti rti
+ jmp c64.IRQDFRT ; continue with kernal irq routine + jmp cbm.IRQDFRT ; continue with kernal irq routine
_setup_raster_irq _setup_raster_irq
pha pha
@ -502,23 +517,14 @@ _setup_raster_irq
}} }}
} }
; ---- end of C64 specific system utility routines ----
} asmsub reset_system() {
sys {
; ------- lowlevel system routines --------
const ubyte target = 64 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
asmsub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt. ; Soft-reset the system back to initial power-on Basic prompt.
%asm {{ %asm {{
sei sei
lda #14 lda #14
sta $01 ; bank the kernal in sta $01 ; bank the kernal in
jmp (c64.RESET_VEC) jmp (cbm.RESET_VEC)
}} }}
} }
@ -536,9 +542,9 @@ _loop lda P8ZP_SCRATCH_W1
ldx P8ZP_SCRATCH_B1 ldx P8ZP_SCRATCH_B1
rts rts
+ lda c64.TIME_LO + lda cbm.TIME_LO
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
- lda c64.TIME_LO - lda cbm.TIME_LO
cmp P8ZP_SCRATCH_B1 cmp P8ZP_SCRATCH_B1
beq - beq -
@ -695,13 +701,26 @@ _longcopy
}} }}
} }
inline asmsub irqsafe_set_irqd() {
%asm {{
php
sei
}}
}
inline asmsub irqsafe_clear_irqd() {
%asm {{
plp
}}
}
inline asmsub exit(ubyte returnvalue @A) { inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register ; -- immediately exit the program with a return code in the A register
%asm {{ %asm {{
lda #31 lda #31
sta $01 ; bank the kernal in sta $01 ; bank the kernal in
jsr c64.CLRCHN ; reset i/o channels jsr cbm.CLRCHN ; reset i/o channels
jsr c64.enable_runstop_and_charsetswitch jsr sys.enable_runstop_and_charsetswitch
ldx prog8_lib.orig_stackpointer ldx prog8_lib.orig_stackpointer
txs txs
rts ; return to original caller rts ; return to original caller
@ -719,6 +738,8 @@ _longcopy
cx16 { cx16 {
%option no_symbol_prefixing
; the sixteen virtual 16-bit registers that the CX16 has defined in the zeropage ; 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 ; 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) ; (because there's no room for them in the zeropage)
@ -826,4 +847,31 @@ cx16 {
&byte r13sH = $cf1b &byte r13sH = $cf1b
&byte r14sH = $cf1d &byte r14sH = $cf1d
&byte r15sH = $cf1f &byte r15sH = $cf1f
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
}}
}
} }

View File

@ -6,6 +6,8 @@
txt { txt {
%option no_symbol_prefixing
const ubyte DEFAULT_WIDTH = 40 const ubyte DEFAULT_WIDTH = 40
const ubyte DEFAULT_HEIGHT = 25 const ubyte DEFAULT_HEIGHT = 25
@ -30,10 +32,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
; ---- set the cursor on the given column (starting with 0) on the current line ; ---- set the cursor on the given column (starting with 0) on the current line
%asm {{ %asm {{
sec sec
jsr c64.PLOT jsr cbm.PLOT
tay tay
clc clc
jmp c64.PLOT jmp cbm.PLOT
}} }}
} }
@ -57,10 +59,10 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
; (assumes screen matrix is at the default address) ; (assumes screen matrix is at the default address)
%asm {{ %asm {{
ldy #250 ldy #250
- sta c64.Screen+250*0-1,y - sta cbm.Screen+250*0-1,y
sta c64.Screen+250*1-1,y sta cbm.Screen+250*1-1,y
sta c64.Screen+250*2-1,y sta cbm.Screen+250*2-1,y
sta c64.Screen+250*3-1,y sta cbm.Screen+250*3-1,y
dey dey
bne - bne -
rts rts
@ -72,10 +74,10 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
; (assumes color matrix is at the default address) ; (assumes color matrix is at the default address)
%asm {{ %asm {{
ldy #250 ldy #250
- sta c64.Colors+250*0-1,y - sta cbm.Colors+250*0-1,y
sta c64.Colors+250*1-1,y sta cbm.Colors+250*1-1,y
sta c64.Colors+250*2-1,y sta cbm.Colors+250*2-1,y
sta c64.Colors+250*3-1,y sta cbm.Colors+250*3-1,y
dey dey
bne - bne -
rts rts
@ -83,7 +85,7 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
} }
sub color (ubyte txtcol) { sub color (ubyte txtcol) {
c64.COLOR = txtcol cbm.COLOR = txtcol
} }
sub lowercase() { sub lowercase() {
@ -94,7 +96,7 @@ sub uppercase() {
c64.VMCSB &= ~2 c64.VMCSB &= ~2
} }
asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) { asmsub scroll_left (bool alsocolors @ Pc) clobbers(A, Y) {
; ---- scroll the whole screen 1 character to the left ; ---- scroll the whole screen 1 character to the left
; contents of the rightmost column are unchanged, you should clear/refill this yourself ; contents of the rightmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -108,10 +110,10 @@ asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
ldy #38 ldy #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 1,x lda cbm.Screen + 40*row + 1,x
sta c64.Screen + 40*row + 0,x sta cbm.Screen + 40*row + 0,x
lda c64.Colors + 40*row + 1,x lda cbm.Colors + 40*row + 1,x
sta c64.Colors + 40*row + 0,x sta cbm.Colors + 40*row + 0,x
.next .next
inx inx
dey dey
@ -123,8 +125,8 @@ _scroll_screen ; scroll only the screen memory
ldy #38 ldy #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 1,x lda cbm.Screen + 40*row + 1,x
sta c64.Screen + 40*row + 0,x sta cbm.Screen + 40*row + 0,x
.next .next
inx inx
dey dey
@ -135,7 +137,7 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) { asmsub scroll_right (bool alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character to the right ; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself ; contents of the leftmost column are unchanged, you should clear/refill this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -147,10 +149,10 @@ asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
ldx #38 ldx #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 0,x lda cbm.Screen + 40*row + 0,x
sta c64.Screen + 40*row + 1,x sta cbm.Screen + 40*row + 1,x
lda c64.Colors + 40*row + 0,x lda cbm.Colors + 40*row + 0,x
sta c64.Colors + 40*row + 1,x sta cbm.Colors + 40*row + 1,x
.next .next
dex dex
bpl - bpl -
@ -160,8 +162,8 @@ _scroll_screen ; scroll only the screen memory
ldx #38 ldx #38
- -
.for row=0, row<=24, row+=1 .for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 0,x lda cbm.Screen + 40*row + 0,x
sta c64.Screen + 40*row + 1,x sta cbm.Screen + 40*row + 1,x
.next .next
dex dex
bpl - bpl -
@ -171,7 +173,7 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) { asmsub scroll_up (bool alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character up ; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself ; contents of the bottom row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -183,10 +185,10 @@ asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
ldx #39 ldx #39
- -
.for row=1, row<=24, row+=1 .for row=1, row<=24, row+=1
lda c64.Screen + 40*row,x lda cbm.Screen + 40*row,x
sta c64.Screen + 40*(row-1),x sta cbm.Screen + 40*(row-1),x
lda c64.Colors + 40*row,x lda cbm.Colors + 40*row,x
sta c64.Colors + 40*(row-1),x sta cbm.Colors + 40*(row-1),x
.next .next
dex dex
bpl - bpl -
@ -196,8 +198,8 @@ _scroll_screen ; scroll only the screen memory
ldx #39 ldx #39
- -
.for row=1, row<=24, row+=1 .for row=1, row<=24, row+=1
lda c64.Screen + 40*row,x lda cbm.Screen + 40*row,x
sta c64.Screen + 40*(row-1),x sta cbm.Screen + 40*(row-1),x
.next .next
dex dex
bpl - bpl -
@ -207,7 +209,7 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) { asmsub scroll_down (bool alsocolors @ Pc) clobbers(A) {
; ---- scroll the whole screen 1 character down ; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself ; contents of the top row are unchanged, you should refill/clear this yourself
; Carry flag determines if screen color data must be scrolled too ; Carry flag determines if screen color data must be scrolled too
@ -219,10 +221,10 @@ asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
ldx #39 ldx #39
- -
.for row=23, row>=0, row-=1 .for row=23, row>=0, row-=1
lda c64.Colors + 40*row,x lda cbm.Colors + 40*row,x
sta c64.Colors + 40*(row+1),x sta cbm.Colors + 40*(row+1),x
lda c64.Screen + 40*row,x lda cbm.Screen + 40*row,x
sta c64.Screen + 40*(row+1),x sta cbm.Screen + 40*(row+1),x
.next .next
dex dex
bpl - bpl -
@ -232,8 +234,8 @@ _scroll_screen ; scroll only the screen memory
ldx #39 ldx #39
- -
.for row=23, row>=0, row-=1 .for row=23, row>=0, row-=1
lda c64.Screen + 40*row,x lda cbm.Screen + 40*row,x
sta c64.Screen + 40*(row+1),x sta cbm.Screen + 40*(row+1),x
.next .next
dex dex
bpl - bpl -
@ -243,20 +245,20 @@ _scroll_screen ; scroll only the screen memory
}} }}
} }
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse. romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
asmsub print (str text @ AY) clobbers(A,Y) { asmsub print (str text @ AY) clobbers(A,Y) {
; ---- print null terminated string from A/Y ; ---- print null terminated string from A/Y
; note: the compiler contains an optimization that will replace ; note: the compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char, ; a call to this subroutine with a string argument of just one char,
; by just one call to c64.CHROUT of that single char. ; by just one call to cbm.CHROUT of that single char.
%asm {{ %asm {{
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG sty P8ZP_SCRATCH_REG
ldy #0 ldy #0
- lda (P8ZP_SCRATCH_B1),y - lda (P8ZP_SCRATCH_B1),y
beq + beq +
jsr c64.CHROUT jsr cbm.CHROUT
iny iny
bne - bne -
+ rts + rts
@ -270,11 +272,11 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
jsr conv.ubyte2decimal jsr conv.ubyte2decimal
pha pha
tya tya
jsr c64.CHROUT jsr cbm.CHROUT
pla pla
jsr c64.CHROUT jsr cbm.CHROUT
txa txa
jsr c64.CHROUT jsr cbm.CHROUT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
@ -290,16 +292,16 @@ _print_byte_digits
cpy #'0' cpy #'0'
beq + beq +
tya tya
jsr c64.CHROUT jsr cbm.CHROUT
pla pla
jsr c64.CHROUT jsr cbm.CHROUT
jmp _ones jmp _ones
+ pla + pla
cmp #'0' cmp #'0'
beq _ones beq _ones
jsr c64.CHROUT jsr cbm.CHROUT
_ones txa _ones txa
jsr c64.CHROUT jsr cbm.CHROUT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
@ -313,45 +315,45 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
cmp #0 cmp #0
bpl + bpl +
lda #'-' lda #'-'
jsr c64.CHROUT jsr cbm.CHROUT
+ pla + pla
jsr conv.byte2decimal jsr conv.byte2decimal
jmp print_ub._print_byte_digits jmp print_ub._print_byte_digits
}} }}
} }
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well) ; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
bcc + bcc +
pha pha
lda #'$' lda #'$'
jsr c64.CHROUT jsr cbm.CHROUT
pla pla
+ jsr conv.ubyte2hex + jsr conv.ubyte2hex
jsr c64.CHROUT jsr cbm.CHROUT
tya tya
jsr c64.CHROUT jsr cbm.CHROUT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
} }
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
bcc + bcc +
lda #'%' lda #'%'
jsr c64.CHROUT jsr cbm.CHROUT
+ ldy #8 + ldy #8
- lda #'0' - lda #'0'
asl P8ZP_SCRATCH_B1 asl P8ZP_SCRATCH_B1
bcc + bcc +
lda #'1' lda #'1'
+ jsr c64.CHROUT + jsr cbm.CHROUT
dey dey
bne - bne -
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
@ -359,7 +361,7 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
pha pha
@ -371,7 +373,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits) ; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well) ; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
@ -392,7 +394,7 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
ldy #0 ldy #0
- lda conv.uword2decimal.decTenThousands,y - lda conv.uword2decimal.decTenThousands,y
beq + beq +
jsr c64.CHROUT jsr cbm.CHROUT
iny iny
bne - bne -
+ ldx P8ZP_SCRATCH_REG + ldx P8ZP_SCRATCH_REG
@ -415,14 +417,14 @@ asmsub print_uw (uword value @ AY) clobbers(A,Y) {
bne - bne -
_gotdigit _gotdigit
jsr c64.CHROUT jsr cbm.CHROUT
iny iny
lda conv.uword2decimal.decTenThousands,y lda conv.uword2decimal.decTenThousands,y
bne _gotdigit bne _gotdigit
rts rts
_allzero _allzero
lda #'0' lda #'0'
jmp c64.CHROUT jmp cbm.CHROUT
}} }}
} }
@ -433,7 +435,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
bpl + bpl +
pha pha
lda #'-' lda #'-'
jsr c64.CHROUT jsr cbm.CHROUT
tya tya
eor #255 eor #255
tay tay
@ -455,7 +457,7 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
ldy #0 ; char counter = 0 ldy #0 ; char counter = 0
- jsr c64.CHRIN - jsr cbm.CHRIN
cmp #$0d ; return (ascii 13) pressed? cmp #$0d ; return (ascii 13) pressed?
beq + ; yes, end. beq + ; yes, end.
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
@ -586,7 +588,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
tax tax
clc clc
jsr c64.PLOT jsr cbm.PLOT
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
rts rts
}} }}
@ -595,7 +597,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
asmsub width() clobbers(X,Y) -> ubyte @A { asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns) ; -- returns the text screen width (number of columns)
%asm {{ %asm {{
jsr c64.SCREEN jsr cbm.SCREEN
txa txa
rts rts
}} }}
@ -604,7 +606,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
asmsub height() clobbers(X, Y) -> ubyte @A { asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows) ; -- returns the text screen height (number of rows)
%asm {{ %asm {{
jsr c64.SCREEN jsr cbm.SCREEN
tya tya
rts rts
}} }}

View File

@ -2,6 +2,8 @@
conv { conv {
%option no_symbol_prefixing
; ----- number conversions to decimal strings ---- ; ----- number conversions to decimal strings ----
str @shared string_out = "????????????????" ; result buffer for the string conversion routines str @shared string_out = "????????????????" ; result buffer for the string conversion routines
@ -506,8 +508,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) ; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
%asm {{ %asm {{
ldy #uword2decimal.ASCII_0_OFFSET ldy #uword2decimal.ASCII_0_OFFSET
bne uword2decimal.hex_try200 jmp uword2decimal.hex_try200
rts
}} }}
} }

View File

@ -1,234 +0,0 @@
; Cx16 specific disk drive I/O routines.
%import diskio
%import string
cx16diskio {
; Same as diskio.load() but with additional bank parameter to select the Ram bank to load into.
; Use kernal LOAD routine to load the given program file in memory.
; This is similar to Basic's LOAD "filename",drive / LOAD "filename",drive,1
; If you don't give an address_override, the location in memory is taken from the 2-byte file header.
; If you specify a custom address_override, the first 2 bytes in the file are ignored
; and the rest is loaded at the given location in memory.
; Returns the end load address+1 if successful or 0 if a load error occurred.
; You can use the load_size() function to calcuate the size of the file that was loaded.
sub load(ubyte drivenumber, uword filenameptr, ubyte bank, uword address_override) -> uword {
cx16.rambank(bank)
return diskio.internal_load_routine(drivenumber, filenameptr, address_override, false)
}
; Same as diskio.load_raw() but with additional bank parameter to select the Ram bank to load into.
; Use kernal LOAD routine to load the given file in memory.
; INCLUDING the first 2 bytes in the file: no program header is assumed in the file.
; The load address is mandatory. Returns the number of bytes loaded.
; If you load into regular system ram, use cx16.getrambank() for the bank argument,
; or alternatively make sure to reset the correct ram bank yourself after the load!
; Returns the end load address+1 if successful or 0 if a load error occurred.
; You can use the load_size() function to calcuate the size of the file that was loaded.
sub load_raw(ubyte drivenumber, uword filenameptr, ubyte bank, uword address_override) -> uword {
cx16.rambank(bank)
return diskio.internal_load_routine(drivenumber, filenameptr, address_override, true)
}
; For use directly after a load or load_raw call (don't mess with the ram bank yet):
; Calculates the number of bytes loaded (files > 64Kb ar truncated to 16 bits)
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword {
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
}
asmsub vload(str name @R0, ubyte drivenumber @Y, ubyte bank @A, uword address @R1) -> 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
tya
tax
bcc +
ldy #%00000010 ; headerless load mode
bne ++
+ ldy #0 ; normal load mode
+ lda #1
jsr c64.SETLFS
lda cx16.r0
ldy cx16.r0+1
jsr prog8_lib.strlen
tya
ldx cx16.r0
ldy cx16.r0+1
jsr c64.SETNAM
pla
clc
adc #2
ldx cx16.r1
ldy cx16.r1+1
stz P8ZP_SCRATCH_B1
jsr c64.LOAD
bcs +
inc P8ZP_SCRATCH_B1
+ jsr c64.CLRCHN
lda #1
jsr c64.CLOSE
plx
lda P8ZP_SCRATCH_B1
rts
}}
}
asmsub vload_raw(str name @R0, ubyte drivenumber @Y, ubyte bank @A, uword address @R1) -> 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.
%asm {{
sec
jmp vload.internal_vload
}}
}
; Replacement function that makes use of fast block read capability of the X16,
; and can wrap over multiple ram banks while reading.
; Use this in place of regular diskio.f_read() on X16.
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)
if not diskio.iteration_in_progress or not num_bytes
return 0
diskio.list_blocks = 0 ; we reuse this variable for the total number of bytes read
; commander X16 supports fast block-read via macptr() kernal call
uword size
while num_bytes {
size = 255
if num_bytes<size
size = num_bytes
size = cx16.macptr(lsb(size), bufferpointer, false)
if_cs
goto byte_read_loop ; macptr block read not supported, do fallback loop
diskio.list_blocks += size
bufferpointer += size
if msb(bufferpointer) == $c0
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
num_bytes -= size
if c64.READST() & $40 {
diskio.f_close() ; end of file, close it
break
}
}
return diskio.list_blocks ; number of bytes read
byte_read_loop: ; fallback if macptr() isn't supported on the device
%asm {{
lda bufferpointer
sta m_in_buffer+1
lda bufferpointer+1
sta m_in_buffer+2
}}
while num_bytes {
if c64.READST() {
diskio.f_close()
if c64.READST() & $40 ; eof?
return diskio.list_blocks ; number of bytes read
return 0 ; error.
}
%asm {{
jsr c64.CHRIN
m_in_buffer sta $ffff
inc m_in_buffer+1
bne +
inc m_in_buffer+2
+
}}
diskio.list_blocks++
num_bytes--
}
return diskio.list_blocks ; number of bytes read
}
; replacement function that makes use of fast block read capability of the X16
; use this in place of regular diskio.f_read_all() on X16
sub f_read_all(uword bufferpointer) -> uword {
; -- read the full contents of the file, returns number of bytes read.
if not diskio.iteration_in_progress
return 0
uword total_read = 0
while not c64.READST() {
cx16.r0 = cx16diskio.f_read(bufferpointer, 256)
total_read += cx16.r0
bufferpointer += cx16.r0
}
return total_read
}
sub chdir(ubyte drivenumber, str path) {
; -- change current directory.
diskio.list_filename[0] = 'c'
diskio.list_filename[1] = 'd'
diskio.list_filename[2] = ':'
void string.copy(path, &diskio.list_filename+3)
diskio.send_command(drivenumber, diskio.list_filename)
}
sub mkdir(ubyte drivenumber, str name) {
; -- make a new subdirectory.
diskio.list_filename[0] = 'm'
diskio.list_filename[1] = 'd'
diskio.list_filename[2] = ':'
void string.copy(name, &diskio.list_filename+3)
diskio.send_command(drivenumber, diskio.list_filename)
}
sub rmdir(ubyte drivenumber, str name) {
; -- remove a subdirectory.
void string.find(name, '*')
if_cs
return ; refuse to act on a wildcard *
diskio.list_filename[0] = 'r'
diskio.list_filename[1] = 'd'
diskio.list_filename[2] = ':'
void string.copy(name, &diskio.list_filename+3)
diskio.send_command(drivenumber, diskio.list_filename)
}
sub relabel(ubyte drivenumber, str name) {
; -- change the disk label.
diskio.list_filename[0] = 'r'
diskio.list_filename[1] = '-'
diskio.list_filename[2] = 'h'
diskio.list_filename[3] = ':'
void string.copy(name, &diskio.list_filename+4)
diskio.send_command(drivenumber, diskio.list_filename)
}
sub f_seek(uword pos_hiword, uword pos_loword) {
; -- seek in the reading file opened with f_open, to the given 32-bits position
ubyte[6] command = ['p',0,0,0,0,0]
command[1] = 12 ; f_open uses channel 12
command[2] = lsb(pos_loword)
command[3] = msb(pos_loword)
command[4] = lsb(pos_hiword)
command[5] = msb(pos_hiword)
send_command:
c64.SETNAM(sizeof(command), &command)
c64.SETLFS(15, diskio.last_drivenumber, 15)
void c64.OPEN()
c64.CLOSE(15)
}
; TODO see if we can get this to work as well:
; sub f_seek_w(uword pos_hiword, uword pos_loword) {
; ; -- seek in the output file opened with f_open_w, to the given 32-bits position
; cx16diskio.f_seek.command[1] = 13 ; f_open_w uses channel 13
; cx16diskio.f_seek.command[2] = lsb(pos_loword)
; cx16diskio.f_seek.command[3] = msb(pos_loword)
; cx16diskio.f_seek.command[4] = lsb(pos_hiword)
; cx16diskio.f_seek.command[5] = msb(pos_hiword)
; goto cx16diskio.f_seek.send_command
; }
}

View File

@ -0,0 +1,797 @@
; Commander X16 disk drive I/O routines.
; Largely compatible with the default C64 ones, but adds more stuff specific to the X16 as well.
%import textio
%import string
%import syslib
diskio {
%option no_symbol_prefixing
ubyte drivenumber = 8
sub set_drive(ubyte number) {
drivenumber = number
}
sub directory() -> bool {
; -- Prints the directory contents to the screen. Returns success.
cbm.SETNAM(1, "$")
cbm.SETLFS(12, drivenumber, 0)
ubyte status = 1
void cbm.OPEN() ; open 12,8,0,"$"
if_cs
goto io_error
void cbm.CHKIN(12) ; use #12 as input channel
if_cs
goto io_error
repeat 4 {
void cbm.CHRIN() ; skip the 4 prologue bytes
}
; while not stop key pressed / EOF encountered, read data.
status = cbm.READST()
if status!=0 {
status = 1
goto io_error
}
while status==0 {
ubyte low = cbm.CHRIN()
ubyte high = cbm.CHRIN()
txt.print_uw(mkword(high, low))
txt.spc()
ubyte @zp char
repeat {
char = cbm.CHRIN()
if char==0
break
txt.chrout(char)
}
txt.nl()
void cbm.CHRIN() ; skip 2 bytes
void cbm.CHRIN()
status = cbm.READST()
if cbm.STOP2()
break
}
status = cbm.READST()
io_error:
cbm.CLRCHN() ; restore default i/o devices
cbm.CLOSE(12)
if status and status & $40 == 0 { ; bit 6=end of file
txt.print("\ni/o error, status: ")
txt.print_ub(status)
txt.nl()
return false
}
return true
}
sub diskname() -> uword {
; 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
while cbm.CHRIN()!='"' {
; skip up to entry name
}
cx16.r0 = &diskio.list_filename
repeat {
@(cx16.r0) = cbm.CHRIN()
if @(cx16.r0)=='"' {
@(cx16.r0) = ' '
while @(cx16.r0)==' ' and cx16.r0>=&diskio.list_filename {
@(cx16.r0) = 0
cx16.r0--
}
break
}
cx16.r0++
}
status = cbm.READST()
io_error:
cbm.CLRCHN()
cbm.CLOSE(12)
if status and status & $40 == 0
return 0
return diskio.list_filename
}
; internal variables for the iterative file lister / loader
bool list_skip_disk_name
uword list_pattern
uword list_blocks
bool iteration_in_progress = false
str list_filetype = "???" ; prg, seq, dir
str list_filename = "?" * 50
; ----- get a list of files (uses iteration functions internally) -----
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
; Files in the buffer are separeted by a 0 byte. You can provide an optional pattern to match against.
; After the last filename one additional 0 byte is placed to indicate the end of the list.
; Returns number of files (it skips 'dir' entries i.e. subdirectories).
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
uword buffer_start = filenames_buffer
ubyte files_found = 0
if lf_start_list(pattern_ptr) {
while lf_next_entry() {
if list_filetype!="dir" {
filenames_buffer += string.copy(list_filename, filenames_buffer) + 1
files_found++
if filenames_buffer - buffer_start > filenames_buf_size-20 {
@(filenames_buffer)=0
lf_end_list()
sys.set_carry()
return files_found
}
}
}
lf_end_list()
}
@(filenames_buffer)=0
sys.clear_carry()
return files_found
}
; ----- iterative file lister functions (uses io channel 12) -----
sub lf_start_list(uword pattern_ptr) -> bool {
; -- start an iterative file listing with optional pattern matching.
; note: only a single iteration loop can be active at a time!
lf_end_list()
list_pattern = pattern_ptr
list_skip_disk_name = true
iteration_in_progress = true
cbm.SETNAM(1, "$")
cbm.SETLFS(12, drivenumber, 0)
void cbm.OPEN() ; open 12,8,0,"$"
if_cs
goto io_error
void cbm.CHKIN(12) ; use #12 as input channel
if_cs
goto io_error
repeat 4 {
void cbm.CHRIN() ; skip the 4 prologue bytes
}
if cbm.READST()==0
return true
io_error:
lf_end_list()
return false
}
sub lf_next_entry() -> bool {
; -- retrieve the next entry from an iterative file listing session.
; results will be found in list_blocks, list_filename, and list_filetype.
; if it returns false though, there are no more entries (or an error occurred).
if not iteration_in_progress
return false
repeat {
void cbm.CHKIN(12) ; use #12 as input channel again
uword nameptr = &list_filename
ubyte blocks_lsb = cbm.CHRIN()
ubyte blocks_msb = cbm.CHRIN()
if cbm.READST()
goto close_end
list_blocks = mkword(blocks_msb, blocks_lsb)
; read until the filename starts after the first "
while cbm.CHRIN()!='\"' {
if cbm.READST()
goto close_end
}
; read the filename
repeat {
ubyte char = cbm.CHRIN()
if char==0
break
if char=='\"'
break
@(nameptr) = char
nameptr++
}
@(nameptr) = 0
do {
cx16.r15L = cbm.CHRIN()
} until cx16.r15L!=' ' ; skip blanks up to 3 chars entry type
list_filetype[0] = cx16.r15L
list_filetype[1] = cbm.CHRIN()
list_filetype[2] = cbm.CHRIN()
while cbm.CHRIN() {
; read the rest of the entry until the end
}
void cbm.CHRIN() ; skip 2 bytes
void cbm.CHRIN()
if not list_skip_disk_name {
if not list_pattern
return true
if string.pattern_match(list_filename, list_pattern)
return true
}
list_skip_disk_name = false
}
close_end:
lf_end_list()
return false
}
sub lf_end_list() {
; -- end an iterative file listing session (close channels).
if iteration_in_progress {
cbm.CLRCHN()
cbm.CLOSE(12)
iteration_in_progress = false
}
}
; ----- iterative file loader functions (uses io channel 12) -----
sub f_open(uword filenameptr) -> bool {
; -- open a file for iterative reading with f_read
; note: only a single iteration loop can be active at a time!
f_close()
cbm.SETNAM(string.length(filenameptr), filenameptr)
cbm.SETLFS(12, drivenumber, 12) ; note: has to be 12,x,12 because otherwise f_seek doesn't work
void cbm.OPEN() ; open 12,8,12,"filename"
if_cc {
if cbm.READST()==0 {
iteration_in_progress = true
void cbm.CHKIN(12) ; use #12 as input channel
if_cc {
void cbm.CHRIN() ; read first byte to test for file not found
if not cbm.READST() {
cbm.CLOSE(12) ; close file because we already consumed first byte
void cbm.OPEN() ; re-open the file
void cbm.CHKIN(12)
return true
}
}
}
}
f_close()
return false
}
; optimized for Commander X16 to use MACPTR block read kernal call
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)
if not iteration_in_progress or not num_bytes
return 0
list_blocks = 0 ; we reuse this variable for the total number of bytes read
; commander X16 supports fast block-read via macptr() kernal call
uword size
while num_bytes {
size = 255
if num_bytes<size
size = num_bytes
size = cx16.macptr(lsb(size), bufferpointer, false)
if_cs
goto byte_read_loop ; macptr block read not supported, do fallback loop
list_blocks += size
bufferpointer += size
if msb(bufferpointer) == $c0
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
num_bytes -= size
if cbm.READST() & $40 {
f_close() ; end of file, close it
break
}
}
return list_blocks ; number of bytes read
byte_read_loop: ; fallback if macptr() isn't supported on the device
%asm {{
lda bufferpointer
sta m_in_buffer+1
lda bufferpointer+1
sta m_in_buffer+2
}}
while num_bytes {
if cbm.READST() {
f_close()
if cbm.READST() & $40 ; eof?
return list_blocks ; number of bytes read
return 0 ; error.
}
%asm {{
jsr cbm.CHRIN
m_in_buffer sta $ffff
inc m_in_buffer+1
bne +
inc m_in_buffer+2
+
}}
list_blocks++
num_bytes--
}
return list_blocks ; number of bytes read
}
; optimized for Commander X16 to use MACPTR block read kernal call
sub f_read_all(uword bufferpointer) -> uword {
; -- read the full contents of the file, returns number of bytes read.
if not iteration_in_progress
return 0
uword total_read = 0
while not cbm.READST() {
cx16.r0 = f_read(bufferpointer, 256)
total_read += cx16.r0
bufferpointer += cx16.r0
}
return total_read
}
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y {
; Routine to read text lines from a text file. Lines must be less than 255 characters.
; Reads characters from the input file UNTIL a newline or return character (or EOF).
; The line read will be 0-terminated in the buffer (and not contain the end of line character).
; The length of the line is returned in Y. Note that an empty line is okay and is length 0!
; I/O error status should be checked by the caller itself via READST() routine.
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldx #12
jsr cbm.CHKIN ; use channel 12 again for input
ldy #0
_loop jsr cbm.CHRIN
sta (P8ZP_SCRATCH_W1),y
beq _end
iny
cmp #$0a
beq _line_end
cmp #$0d
bne _loop
_line_end dey ; get rid of the trailing end-of-line char
lda #0
sta (P8ZP_SCRATCH_W1),y
_end rts
}}
}
sub f_close() {
; -- end an iterative file loading session (close channels).
if iteration_in_progress {
cbm.CLRCHN()
cbm.CLOSE(12)
iteration_in_progress = false
}
}
; ----- iterative file writing functions (uses io channel 13) -----
sub f_open_w(uword filenameptr) -> bool {
; -- open a file for iterative writing with f_write
f_close_w()
cbm.SETNAM(string.length(filenameptr), filenameptr)
cbm.SETLFS(13, drivenumber, 1)
void cbm.OPEN() ; open 13,8,1,"filename"
if_cc {
cbm.CHKOUT(13) ; use #13 as output channel
return not cbm.READST()
}
f_close_w()
return false
}
sub f_write(uword bufferpointer, uword num_bytes) -> bool {
; -- write the given number of bytes to the currently open file
if num_bytes!=0 {
cbm.CHKOUT(13) ; use #13 as output channel again
repeat num_bytes {
cbm.CHROUT(@(bufferpointer))
bufferpointer++
}
return not cbm.READST()
}
return true
}
sub f_close_w() {
; -- end an iterative file writing session (close channels).
cbm.CLRCHN()
cbm.CLOSE(13)
}
; ---- other functions ----
sub status() -> uword {
; -- retrieve the disk drive's current status message
uword messageptr = &list_filename
cbm.SETNAM(0, list_filename)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN() ; open 15,8,15
if_cs
goto io_error
void cbm.CHKIN(15) ; use #15 as input channel
if_cs
goto io_error
while not cbm.READST() {
cx16.r5L = cbm.CHRIN()
if cx16.r5L=='\r' or cx16.r5L=='\n'
break
@(messageptr) = cx16.r5L
messageptr++
}
@(messageptr) = 0
done:
cbm.CLRCHN() ; restore default i/o devices
cbm.CLOSE(15)
return list_filename
io_error:
list_filename = "?disk error"
goto done
}
; saves a block of memory to disk, including the default 2 byte prg header.
sub save(uword filenameptr, uword address, uword size) -> bool {
return internal_save_routine(filenameptr, address, size, false)
}
; like save() but omits the 2 byte prg header.
sub save_raw(uword filenameptr, uword address, uword size) -> bool {
return internal_save_routine(filenameptr, address, size, true)
}
sub internal_save_routine(uword filenameptr, uword address, uword size, bool headerless) -> bool {
cbm.SETNAM(string.length(filenameptr), filenameptr)
cbm.SETLFS(1, drivenumber, 0)
uword @shared end_address = address + size
cx16.r0L = 0
%asm {{
lda address
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
beq +
lda #<P8ZP_SCRATCH_W1
jsr cx16.BSAVE
bra ++
+ lda #<P8ZP_SCRATCH_W1
jsr cbm.SAVE
+ php
ldx P8ZP_SCRATCH_REG
plp
}}
if_cc
cx16.r0L = cbm.READST()==0
cbm.CLRCHN()
cbm.CLOSE(1)
return cx16.r0L
}
; Use kernal LOAD routine to load the given program file in memory.
; This is similar to Basic's LOAD "filename",drive / LOAD "filename",drive,1
; If you don't give an address_override, the location in memory is taken from the 2-byte file header.
; If you specify a custom address_override, the first 2 bytes in the file are ignored
; and the rest is loaded at the given location in memory.
; Returns the end load address+1 if successful or 0 if a load error occurred.
; NOTE: when the load is larger than 64Kb and/or spans multiple RAM banks
; (which is possible on the Commander X16), the returned size is not correct,
; because it doesn't take the number of ram banks into account.
; You can use the load_size() function to calcuate the size in this case.
; NOTE: data is read into the current Ram bank if you're reading into banked ram.
; if you require loading into another ram bank, you have to set that
; yourself using cx16.rambank(bank) before calling load().
sub load(uword filenameptr, uword address_override) -> uword {
return internal_load_routine(filenameptr, address_override, false)
}
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
; No program header is assumed in the file. Everything is loaded.
; See comments on load() for more details.
sub load_raw(uword filenameptr, uword address) -> uword {
return internal_load_routine(filenameptr, address, true)
}
sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword {
cbm.SETNAM(string.length(filenameptr), filenameptr)
ubyte secondary = 1
cx16.r1 = 0
if address_override
secondary = 0
if headerless
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
jsr cbm.LOAD
bcs +
stx cx16.r1
sty cx16.r1+1
+ ldx P8ZP_SCRATCH_REG
}}
cbm.CLRCHN()
cbm.CLOSE(1)
return cx16.r1
}
sub delete(uword filenameptr) {
; -- delete a file on the drive
list_filename[0] = 's'
list_filename[1] = ':'
ubyte flen = string.copy(filenameptr, &list_filename+2)
cbm.SETNAM(flen+2, list_filename)
cbm.SETLFS(1, drivenumber, 15)
void cbm.OPEN()
cbm.CLRCHN()
cbm.CLOSE(1)
}
sub rename(uword oldfileptr, uword newfileptr) {
; -- rename a file on the drive
list_filename[0] = 'r'
list_filename[1] = ':'
ubyte flen_new = string.copy(newfileptr, &list_filename+2)
list_filename[flen_new+2] = '='
ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new)
cbm.SETNAM(3+flen_new+flen_old, list_filename)
cbm.SETLFS(1, drivenumber, 15)
void cbm.OPEN()
cbm.CLRCHN()
cbm.CLOSE(1)
}
sub send_command(uword commandptr) {
; -- send a dos command to the drive
cbm.SETNAM(string.length(commandptr), commandptr)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
cbm.CLRCHN()
cbm.CLOSE(15)
}
; CommanderX16 extensions over the basic C64/C128 diskio routines:
; For use directly after a load or load_raw call (don't mess with the ram bank yet):
; Calculates the number of bytes loaded (files > 64Kb ar truncated to 16 bits)
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword {
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
}
asmsub vload(str name @R0, ubyte bank @A, uword address @R1) -> 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 +
ldy #%00000010 ; headerless load mode
bne ++
+ ldy #0 ; normal load mode
+ lda #1
jsr cbm.SETLFS
lda cx16.r0
ldy cx16.r0+1
jsr prog8_lib.strlen
tya
ldx cx16.r0
ldy cx16.r0+1
jsr cbm.SETNAM
pla
clc
adc #2
ldx cx16.r1
ldy cx16.r1+1
stz P8ZP_SCRATCH_B1
jsr cbm.LOAD
bcs +
inc P8ZP_SCRATCH_B1
+ 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 {
; -- 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.
%asm {{
sec
jmp vload.internal_vload
}}
}
sub chdir(str path) {
; -- change current directory.
list_filename[0] = 'c'
list_filename[1] = 'd'
list_filename[2] = ':'
void string.copy(path, &list_filename+3)
send_command(list_filename)
}
sub mkdir(str name) {
; -- make a new subdirectory.
list_filename[0] = 'm'
list_filename[1] = 'd'
list_filename[2] = ':'
void string.copy(name, &list_filename+3)
send_command(list_filename)
}
sub rmdir(str name) {
; -- remove a subdirectory.
void string.find(name, '*')
if_cs
return ; refuse to act on a wildcard *
list_filename[0] = 'r'
list_filename[1] = 'd'
list_filename[2] = ':'
void string.copy(name, &list_filename+3)
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'
list_filename[1] = '-'
list_filename[2] = 'h'
list_filename[3] = ':'
void string.copy(name, &list_filename+4)
send_command(list_filename)
}
sub f_seek(uword pos_hiword, uword pos_loword) {
; -- seek in the reading file opened with f_open, to the given 32-bits position
ubyte[6] command = ['p',0,0,0,0,0]
command[1] = 12 ; f_open uses channel 12
command[2] = lsb(pos_loword)
command[3] = msb(pos_loword)
command[4] = lsb(pos_hiword)
command[5] = msb(pos_hiword)
send_command:
cbm.SETNAM(sizeof(command), &command)
cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN()
cbm.CLOSE(15)
}
; NOTE: f_seek_w() doesn't work reliably right now. I only manage to corrupt the fat32 filesystem on the sdcard with it...
; sub f_seek_w(uword pos_hiword, uword pos_loword) {
; ; -- seek in the output file opened with f_open_w, to the given 32-bits position
; diskio.f_seek.command[1] = 13 ; f_open_w uses channel 13
; diskio.f_seek.command[2] = lsb(pos_loword)
; diskio.f_seek.command[3] = msb(pos_loword)
; diskio.f_seek.command[4] = lsb(pos_hiword)
; diskio.f_seek.command[5] = msb(pos_hiword)
; goto diskio.f_seek.send_command
; }
}

View File

@ -6,6 +6,8 @@
floats { floats {
; ---- this block contains C-64 compatible floating point related functions ---- ; ---- this block contains C-64 compatible floating point related functions ----
%option no_symbol_prefixing
const float PI = 3.141592653589793 const float PI = 3.141592653589793
const float TWOPI = 6.283185307179586 const float TWOPI = 6.283185307179586

View File

@ -13,16 +13,19 @@
; SCREEN MODE LIST: ; SCREEN MODE LIST:
; mode 0 = reset back to default text mode ; mode 0 = reset back to default text mode
; mode 1 = bitmap 320 x 240 monochrome ; mode 1 = bitmap 320 x 240 monochrome
; mode 2 = bitmap 320 x 240 x 4c (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 (TODO not yet implemented) ; 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 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 5 = bitmap 640 x 480 monochrome
; mode 6 = bitmap 640 x 480 x 4c ; mode 6 = bitmap 640 x 480 x 4c
; higher color dephts in highres are not supported due to lack of VRAM ; higher color dephts in highres are not supported due to lack of VRAM
; TODO remove the phx/plx pairs in non-stack compiler version
gfx2 { gfx2 {
%option no_symbol_prefixing
; read-only control variables: ; read-only control variables:
ubyte active_mode = 0 ubyte active_mode = 0
uword width = 0 uword width = 0
@ -44,7 +47,7 @@ gfx2 {
height = 240 height = 240
bpp = 1 bpp = 1
} }
; TODO modes 2, 3 not yet implemented ; TODO modes 2, 3
4 -> { 4 -> {
; lores 256c ; lores 256c
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1 cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
@ -84,7 +87,7 @@ gfx2 {
else -> { else -> {
; back to default text mode ; back to default text mode
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode
c64.CINT() cbm.CINT()
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11111000) | cx16.r15L
width = 0 width = 0
height = 0 height = 0
@ -107,7 +110,7 @@ gfx2 {
repeat 240/2/8 repeat 240/2/8
cs_innerloop640() cs_innerloop640()
} }
; TODO mode 2, 3 ; TODO modes 2, 3
4 -> { 4 -> {
; lores 256c ; lores 256c
repeat 240/2 repeat 240/2
@ -155,6 +158,9 @@ gfx2 {
} }
sub horizontal_line(uword x, uword y, uword length, ubyte color) { 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]
if length==0 if length==0
return return
when active_mode { when active_mode {
@ -163,12 +169,13 @@ gfx2 {
ubyte separate_pixels = (8-lsb(x)) & 7 ubyte separate_pixels = (8-lsb(x)) & 7
if separate_pixels as uword > length if separate_pixels as uword > length
separate_pixels = lsb(length) separate_pixels = lsb(length)
repeat separate_pixels { if separate_pixels {
; TODO optimize this by writing a masked byte in 1 go position(x,y)
plot(x, y, color) cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
x++ cx16.VERA_DATA0 = cx16.VERA_DATA0 | masked_starts[separate_pixels]
length -= separate_pixels
x += separate_pixels
} }
length -= separate_pixels
if length { if length {
position(x, y) position(x, y)
separate_pixels = lsb(length) & 7 separate_pixels = lsb(length) & 7
@ -205,11 +212,9 @@ _loop lda length
bra _loop bra _loop
_done _done
}} }}
repeat separate_pixels {
; TODO optimize this by writing a masked byte in 1 go cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off
plot(x, y, color) cx16.VERA_DATA0 = cx16.VERA_DATA0 | masked_ends[separate_pixels]
x++
}
} }
cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off again cx16.VERA_ADDR_H &= %00000111 ; vera auto-increment off again
} }
@ -236,8 +241,7 @@ _done
}} }}
} }
6 -> { 6 -> {
; highres 4c ; highres 4c ....also mostly usable for mode 2, lores 4c?
; TODO also mostly usable for lores 4c?
color &= 3 color &= 3
ubyte[4] colorbits ubyte[4] colorbits
ubyte ii ubyte ii
@ -366,8 +370,14 @@ _done
return return
position2(x,y,true) position2(x,y,true)
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
color &= 3 ;; color &= 3
color <<= gfx2.plot.shift4c[lsb(x) & 3] ; TODO with lookup table ;; color <<= gfx2.plot.shift4c[lsb(x) & 3]
cx16.r2L = lsb(x) & 3
when color & 3 {
1 -> color = gfx2.plot.shiftedleft_4c_1[cx16.r2L]
2 -> color = gfx2.plot.shiftedleft_4c_2[cx16.r2L]
3 -> color = gfx2.plot.shiftedleft_4c_3[cx16.r2L]
}
ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3] ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3]
repeat lheight { repeat lheight {
%asm {{ %asm {{
@ -547,10 +557,13 @@ _done
} }
} }
sub plot(uword @zp x, uword y, ubyte color) { sub plot(uword @zp x, uword @zp y, ubyte @zp color) {
ubyte[8] @shared bits = [128, 64, 32, 16, 8, 4, 2, 1] ubyte[8] @shared bits = [128, 64, 32, 16, 8, 4, 2, 1]
ubyte[4] @shared mask4c = [%00111111, %11001111, %11110011, %11111100] ubyte[4] @shared mask4c = [%00111111, %11001111, %11110011, %11111100]
ubyte[4] @shared shift4c = [6,4,2,0] ubyte[4] @shared shift4c = [6,4,2,0]
ubyte[4] shiftedleft_4c_1 = [1<<6, 1<<4, 1<<2, 1<<0]
ubyte[4] shiftedleft_4c_2 = [2<<6, 2<<4, 2<<2, 2<<0]
ubyte[4] shiftedleft_4c_3 = [3<<6, 3<<4, 3<<2, 3<<0]
when active_mode { when active_mode {
1 -> { 1 -> {
@ -587,7 +600,7 @@ _done
}} }}
} }
} }
; TODO mode 2,3 ; TODO modes 2, 3
4 -> { 4 -> {
; lores 256c ; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte) void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
@ -639,12 +652,16 @@ _done
} }
} }
6 -> { 6 -> {
; highres 4c ; highres 4c ....also mostly usable for mode 2, lores 4c?
; TODO also mostly usable for lores 4c?
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte) 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 cx16.r2L = lsb(x) & 3 ; xbits
color &= 3 ; color &= 3
color <<= shift4c[cx16.r2L] ; TODO with lookup table ; color <<= shift4c[cx16.r2L]
when color & 3 {
1 -> color = shiftedleft_4c_1[cx16.r2L]
2 -> color = shiftedleft_4c_2[cx16.r2L]
3 -> color = shiftedleft_4c_3[cx16.r2L]
}
%asm {{ %asm {{
stz cx16.VERA_CTRL stz cx16.VERA_CTRL
lda cx16.r1L lda cx16.r1L
@ -689,7 +706,7 @@ _done
+ +
}} }}
} }
; TODO mode 2 and 3 ; TODO modes 2, 3
4 -> { 4 -> {
; lores 256c ; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte) void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
@ -743,27 +760,138 @@ _done
sta cx16.r0L sta cx16.r0L
}} }}
cx16.r1L = lsb(x) & 3 cx16.r1L = lsb(x) & 3
cx16.r0L >>= gfx2.plot.shift4c[cx16.r1L] ; TODO with lookup table cx16.r0L >>= gfx2.plot.shift4c[cx16.r1L]
return cx16.r0L & 3 return cx16.r0L & 3
} }
else -> return 0 else -> return 0
} }
} }
sub fill(word @zp x, word @zp y, ubyte new_color) {
; Non-recursive scanline flood fill.
; based loosely on code found here https://www.codeproject.com/Articles/6017/QuickFill-An-efficient-flood-fill-algorithm
; with the fixes applied to the seedfill_4 routine as mentioned in the comments.
const ubyte MAXDEPTH = 48
word[MAXDEPTH] @split @shared stack_xl
word[MAXDEPTH] @split @shared stack_xr
word[MAXDEPTH] @split @shared stack_y
byte[MAXDEPTH] @shared stack_dy
cx16.r12L = 0 ; stack pointer
word x1
word x2
byte dy
cx16.r10L = new_color
sub push_stack(word sxl, word sxr, word sy, byte sdy) {
if cx16.r12L==MAXDEPTH
return
cx16.r0s = sy+sdy
if cx16.r0s>=0 and cx16.r0s<=height-1 {
;; stack_xl[cx16.r12L] = sxl
;; stack_xr[cx16.r12L] = sxr
;; stack_y[cx16.r12L] = sy
;; stack_dy[cx16.r12L] = sdy
;; cx16.r12L++
%asm {{
ldy cx16.r12L
lda sxl
sta stack_xl_lsb,y
lda sxl+1
sta stack_xl_msb,y
lda sxr
sta stack_xr_lsb,y
lda sxr+1
sta stack_xr_msb,y
lda sy
sta stack_y_lsb,y
lda sy+1
sta stack_y_msb,y
ldy cx16.r12L
lda sdy
sta stack_dy,y
inc cx16.r12L
}}
}
}
sub pop_stack() {
;; cx16.r12L--
;; x1 = stack_xl[cx16.r12L]
;; x2 = stack_xr[cx16.r12L]
;; y = stack_y[cx16.r12L]
;; dy = stack_dy[cx16.r12L]
%asm {{
dec cx16.r12L
ldy cx16.r12L
lda stack_xl_lsb,y
sta x1
lda stack_xl_msb,y
sta x1+1
lda stack_xr_lsb,y
sta x2
lda stack_xr_msb,y
sta x2+1
lda stack_y_lsb,y
sta y
lda stack_y_msb,y
sta y+1
ldy cx16.r12L
lda stack_dy,y
sta dy
}}
y+=dy
}
cx16.r11L = pget(x as uword, y as uword) ; old_color
if cx16.r11L == cx16.r10L
return
if x<0 or x > width-1 or y<0 or y > height-1
return
push_stack(x, x, y, 1)
push_stack(x, x, y + 1, -1)
word left = 0
while cx16.r12L {
pop_stack()
x = x1
while x >= 0 and pget(x as uword, y as uword) == cx16.r11L {
plot(x as uword, y as uword, cx16.r10L)
x--
}
if x>= x1
goto skip
left = x + 1
if left < x1
push_stack(left, x1 - 1, y, -dy)
x = x1 + 1
do {
while x <= width-1 and pget(x as uword, y as uword) == cx16.r11L {
plot(x as uword, y as uword, cx16.r10L)
x++
}
push_stack(left, x - 1, y, dy)
if x > x2 + 1
push_stack(x2 + 1, x - 1, y, -dy)
skip:
x++
while x <= x2 and pget(x as uword, y as uword) != cx16.r11L
x++
left = x
} until x>x2
}
}
sub position(uword @zp x, uword y) { sub position(uword @zp x, uword y) {
ubyte bank
when active_mode { when active_mode {
1 -> { 1 -> {
; lores monochrome ; lores monochrome
cx16.r0 = y*(320/8) + x/8 cx16.r0 = y*(320/8) + x/8
cx16.vaddr(0, cx16.r0, 0, 1) cx16.vaddr(0, cx16.r0, 0, 1)
} }
; TODO modes 2,3 ; TODO modes 2, 3
4 -> { 4 -> {
; lores 256c ; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte) void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
bank = lsb(cx16.r1) cx16.r2L = cx16.r1L
cx16.vaddr(bank, cx16.r0, 0, 1) cx16.vaddr(cx16.r2L, cx16.r0, 0, 1)
} }
5 -> { 5 -> {
; highres monochrome ; highres monochrome
@ -773,24 +901,16 @@ _done
6 -> { 6 -> {
; highres 4c ; highres 4c
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte) void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
bank = lsb(cx16.r1) cx16.r2L = cx16.r1L
cx16.vaddr(bank, cx16.r0, 0, 1) cx16.vaddr(cx16.r2L, cx16.r0, 0, 1)
} }
} }
} }
sub position2(uword @zp x, uword y, bool also_port_1) { sub position2(uword @zp x, uword y, bool also_port_1) {
position(x, y) position(x, y)
if also_port_1 { if also_port_1
when active_mode { cx16.vaddr_clone(0)
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)
}
}
}
} }
inline asmsub next_pixel(ubyte color @A) { inline asmsub next_pixel(ubyte color @A) {
@ -865,48 +985,96 @@ _done
sub text(uword @zp x, uword y, ubyte color, uword sctextptr) { 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!). ; -- 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. ; 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 uword chardataptr
ubyte[8] @shared char_bitmap_bytes_left
ubyte[8] @shared char_bitmap_bytes_right
when active_mode { when active_mode {
1, 5 -> { 1, 5 -> {
; monochrome mode, either resolution ; monochrome mode, either resolution
cx16.r2 = 40 cx16.r3 = sctextptr
if active_mode==5 while @(cx16.r3) {
cx16.r2 = 80 chardataptr = charset_addr + @(cx16.r3) * $0008
while @(sctextptr) { ; copy the character bitmap into RAM
chardataptr = charset_addr + (@(sctextptr) as uword)*8 cx16.vaddr_autoincr(charset_bank, chardataptr, 0, 1)
cx16.vaddr(charset_bank, chardataptr, 1, 1)
position(x,y)
%asm {{ %asm {{
lda cx16.VERA_ADDR_H ; pre-shift the bits
and #%111 ; don't auto-increment, we have to do that manually because of the ora phx ; TODO remove in non-stack version
sta cx16.VERA_ADDR_H lda text.x
lda color and #7
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
ldy #8 ldy #0
- lda P8ZP_SCRATCH_B1 - lda cx16.VERA_DATA0
bne + ; white color, plot normally stz P8ZP_SCRATCH_REG
lda cx16.VERA_DATA1 ldx P8ZP_SCRATCH_B1
eor #255 ; black color, keep only the other pixels cpx #0
and cx16.VERA_DATA0 beq +
bra ++ - lsr a
+ lda cx16.VERA_DATA0 ror P8ZP_SCRATCH_REG
ora cx16.VERA_DATA1 dex
+ 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
bne - bne -
+ sta char_bitmap_bytes_left,y
lda P8ZP_SCRATCH_REG
sta char_bitmap_bytes_right,y
iny
cpy #8
bne --
plx ; TODO remove in non-stack version
}} }}
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 -> { 4 -> {
@ -918,7 +1086,7 @@ _done
position(x,y) position(x,y)
y++ y++
%asm {{ %asm {{
phx phx ; TODO remove in non-stack version
ldx color ldx color
lda cx16.VERA_DATA1 lda cx16.VERA_DATA1
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
@ -930,7 +1098,7 @@ _done
+ lda cx16.VERA_DATA0 ; don't write a pixel, but do advance to the next address + lda cx16.VERA_DATA0 ; don't write a pixel, but do advance to the next address
+ dey + dey
bne - bne -
plx plx ; TODO remove in non-stack version
}} }}
} }
x+=8 x+=8
@ -942,30 +1110,80 @@ _done
; hires 4c ; hires 4c
; we're going to use a few cx16 registers to make sure every variable is in zeropage in the inner loop. ; 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.r11L = color
cx16.r8 = y
while @(sctextptr) { while @(sctextptr) {
chardataptr = charset_addr + (@(sctextptr) as uword)*8 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 { repeat 8 {
; TODO rewrite this inner loop partly in assembly cx16.r10L = cx16.VERA_DATA1 ; get the next 8 horizontal character bits
; 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.r7 = x cx16.r7 = x
repeat 8 { repeat 8 {
cx16.r9L <<= 1 cx16.r10L <<= 1
if_cs if_cs {
plot(cx16.r7, cx16.r8, cx16.r11L) 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++ 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 x+=8
cx16.r8-=8
sctextptr++ 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) { asmsub cs_innerloop640() clobbers(Y) {

View File

@ -10,6 +10,8 @@
graphics { graphics {
%option no_symbol_prefixing
const uword WIDTH = 320 const uword WIDTH = 320
const ubyte HEIGHT = 240 const ubyte HEIGHT = 240

View File

@ -2,9 +2,10 @@
; Should you want to restore the default palette, you have to reinitialize the Vera yourself. ; Should you want to restore the default palette, you have to reinitialize the Vera yourself.
palette { palette {
%option no_symbol_prefixing
uword vera_palette_ptr uword vera_palette_ptr
ubyte c ubyte cc
sub set_color(ubyte index, uword color) { sub set_color(ubyte index, uword color) {
vera_palette_ptr = $fa00+(index as uword * 2) vera_palette_ptr = $fa00+(index as uword * 2)
@ -79,13 +80,13 @@ palette {
sub set_grayscale() { sub set_grayscale() {
vera_palette_ptr = $fa00 vera_palette_ptr = $fa00
repeat 16 { repeat 16 {
c=0 cc=0
repeat 16 { repeat 16 {
cx16.vpoke(1, vera_palette_ptr, c) cx16.vpoke(1, vera_palette_ptr, cc)
vera_palette_ptr++ vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, c) cx16.vpoke(1, vera_palette_ptr, cc)
vera_palette_ptr++ vera_palette_ptr++
c += $11 cc += $11
} }
} }
} }
@ -150,11 +151,11 @@ palette {
sub set_c64pepto() { sub set_c64pepto() {
vera_palette_ptr = $fa00 vera_palette_ptr = $fa00
repeat 16 { repeat 16 {
for c in 0 to 15 { for cc in 0 to 15 {
uword cc = C64_colorpalette_pepto[c] uword ccp = C64_colorpalette_pepto[cc]
cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B cx16.vpoke(1, vera_palette_ptr, lsb(ccp)) ; G, B
vera_palette_ptr++ vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R cx16.vpoke(1, vera_palette_ptr, msb(ccp)) ; R
vera_palette_ptr++ vera_palette_ptr++
} }
} }
@ -163,11 +164,11 @@ palette {
sub set_c64light() { sub set_c64light() {
vera_palette_ptr = $fa00 vera_palette_ptr = $fa00
repeat 16 { repeat 16 {
for c in 0 to 15 { for cc in 0 to 15 {
uword cc = C64_colorpalette_light[c] uword ccp = C64_colorpalette_light[cc]
cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B cx16.vpoke(1, vera_palette_ptr, lsb(ccp)) ; G, B
vera_palette_ptr++ vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R cx16.vpoke(1, vera_palette_ptr, msb(ccp)) ; R
vera_palette_ptr++ vera_palette_ptr++
} }
} }
@ -176,11 +177,11 @@ palette {
sub set_c64dark() { sub set_c64dark() {
vera_palette_ptr = $fa00 vera_palette_ptr = $fa00
repeat 16 { repeat 16 {
for c in 0 to 15 { for cc in 0 to 15 {
uword cc = C64_colorpalette_dark[c] uword ccp = C64_colorpalette_dark[cc]
cx16.vpoke(1, vera_palette_ptr, lsb(cc)) ; G, B cx16.vpoke(1, vera_palette_ptr, lsb(ccp)) ; G, B
vera_palette_ptr++ vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(cc)) ; R cx16.vpoke(1, vera_palette_ptr, msb(ccp)) ; R
vera_palette_ptr++ vera_palette_ptr++
} }
} }

View File

@ -1,6 +1,8 @@
%import syslib %import syslib
psg { psg {
%option no_symbol_prefixing
; $1F9C0 - $1F9FF 16 blocks of 4 PSG registers (16 voices) ; $1F9C0 - $1F9FF 16 blocks of 4 PSG registers (16 voices)
; 00 frequency word LSB ; 00 frequency word LSB
; 01 frequency word MSB. freqword = HERZ / 0.3725290298461914 ; 01 frequency word MSB. freqword = HERZ / 0.3725290298461914
@ -22,10 +24,7 @@ psg {
; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE. ; waveform = one of PULSE,SAWTOOTH,TRIANGLE,NOISE.
; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE. ; pulsewidth = 0-63. Specifies the pulse width for waveform=PULSE.
envelope_states[voice_num] = 255 envelope_states[voice_num] = 255
%asm {{ sys.irqsafe_set_irqd()
php
sei
}}
cx16.r0 = $f9c2 + voice_num * 4 cx16.r0 = $f9c2 + voice_num * 4
cx16.VERA_CTRL = 0 cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(cx16.r0) cx16.VERA_ADDR_L = lsb(cx16.r0)
@ -36,9 +35,7 @@ psg {
cx16.VERA_DATA0 = waveform | pulsewidth cx16.VERA_DATA0 = waveform | pulsewidth
envelope_volumes[voice_num] = mkword(volume, 0) envelope_volumes[voice_num] = mkword(volume, 0)
envelope_maxvolumes[voice_num] = volume envelope_maxvolumes[voice_num] = volume
%asm {{ sys.irqsafe_clear_irqd()
plp
}}
} }
; sub freq_hz(ubyte voice_num, float hertz) { ; sub freq_hz(ubyte voice_num, float hertz) {
@ -53,10 +50,7 @@ psg {
; voice_num = 0-15, vera_freq = 0-65535 calculate this via the formula given in the Vera's PSG documentation. ; voice_num = 0-15, vera_freq = 0-65535 calculate this via the formula given in the Vera's PSG documentation.
; (https://github.com/x16community/x16-docs/blob/master/VERA%20Programmer's%20Reference.md) ; (https://github.com/x16community/x16-docs/blob/master/VERA%20Programmer's%20Reference.md)
; Write freq MSB first and then LSB to reduce the chance on clicks ; Write freq MSB first and then LSB to reduce the chance on clicks
%asm {{ sys.irqsafe_set_irqd()
php
sei
}}
cx16.r0 = $f9c1 + voice_num * 4 cx16.r0 = $f9c1 + voice_num * 4
cx16.VERA_CTRL = 0 cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(cx16.r0) cx16.VERA_ADDR_L = lsb(cx16.r0)
@ -65,9 +59,7 @@ psg {
cx16.VERA_DATA0 = msb(vera_freq) cx16.VERA_DATA0 = msb(vera_freq)
cx16.VERA_ADDR_L-- cx16.VERA_ADDR_L--
cx16.VERA_DATA0 = lsb(vera_freq) cx16.VERA_DATA0 = lsb(vera_freq)
%asm {{ sys.irqsafe_clear_irqd()
plp
}}
} }
sub volume(ubyte voice_num, ubyte vol) { sub volume(ubyte voice_num, ubyte vol) {
@ -181,7 +173,7 @@ psg {
} }
ubyte[16] envelope_states ubyte[16] envelope_states
uword[16] envelope_volumes ; scaled by 256 uword[16] @split envelope_volumes ; scaled by 256
ubyte[16] envelope_attacks ubyte[16] envelope_attacks
ubyte[16] envelope_sustains ubyte[16] envelope_sustains
ubyte[16] envelope_releases ubyte[16] envelope_releases

View File

@ -1,9 +1,11 @@
; Prog8 definitions for the CommanderX16 ; Prog8 definitions for the CommanderX16
; Including memory registers, I/O registers, Basic and Kernal subroutines. ; Including memory registers, I/O registers, Basic and Kernal subroutines.
c64 { cbm {
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ---- ; Commodore (CBM) common variables, vectors and kernal routines
%option no_symbol_prefixing
; STROUT --> use txt.print ; STROUT --> use txt.print
; CLEARSCR -> use txt.clear_screen ; CLEARSCR -> use txt.clear_screen
@ -13,12 +15,12 @@ romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ) romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table romsub $FF8D = VECTOR(uword userptr @ XY, bool dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer. NOTE: as a Cx16 extension, also returns the number of RAM memory banks in register A ! See cx16.numbanks() romsub $FF99 = MEMTOP(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set top of memory pointer. NOTE: as a Cx16 extension, also returns the number of RAM memory banks in register A ! See cx16.numbanks()
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer romsub $FF9C = MEMBOT(uword address @ XY, bool dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
@ -30,23 +32,23 @@ romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file romsub $FFC0 = OPEN() clobbers(X,Y) -> bool @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> bool @Pc ; (via 798 ($31E)) define an input channel
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read. romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> bool @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) clobbers (X, Y) -> bool @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high) romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock (A=lo,X=mid,Y=high)
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A) romsub $FFE1 = STOP() clobbers(X) -> bool @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character romsub $FFE4 = GETIN() clobbers(X,Y) -> bool @Pc, ubyte @ A ; (via 810 ($32A)) get a character
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X. 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 romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
; ---- utility ; ---- utility
@ -55,7 +57,7 @@ asmsub STOP2() -> 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. ; -- 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 {{ %asm {{
phx phx
jsr c64.STOP jsr cbm.STOP
beq + beq +
plx plx
lda #0 lda #0
@ -67,13 +69,14 @@ asmsub STOP2() -> ubyte @A {
} }
asmsub RDTIM16() -> uword @AY { asmsub RDTIM16() -> uword @AY {
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience ; -- like RDTIM() but only returning the lower 16 bits in AY for convenience. Also avoids ram bank issue for irqs.
%asm {{ %asm {{
phx phx
php php
sei sei
jsr c64.RDTIM jsr cbm.RDTIM
plp plp
cli
pha pha
txa txa
tay tay
@ -87,38 +90,40 @@ asmsub RDTIM16() -> uword @AY {
cx16 { cx16 {
%option no_symbol_prefixing
; irq, system and hardware vectors: ; irq, system and hardware vectors:
&uword IERROR = $0300 &uword IERROR = $0300
&uword IMAIN = $0302 &uword IMAIN = $0302
&uword ICRNCH = $0304 &uword ICRNCH = $0304
&uword IQPLOP = $0306 &uword IQPLOP = $0306
&uword IGONE = $0308 &uword IGONE = $0308
&uword IEVAL = $030a &uword IEVAL = $030a
&ubyte SAREG = $030c ; register storage for A for SYS calls &ubyte SAREG = $030c ; register storage for A for SYS calls
&ubyte SXREG = $030d ; register storage for X for SYS calls &ubyte SXREG = $030d ; register storage for X for SYS calls
&ubyte SYREG = $030e ; register storage for Y for SYS calls &ubyte SYREG = $030e ; register storage for Y for SYS calls
&ubyte SPREG = $030f ; register storage for P (status register) for SYS calls &ubyte SPREG = $030f ; register storage for P (status register) for SYS calls
&uword USRADD = $0311 ; vector for the USR() basic command &uword USRADD = $0311 ; vector for the USR() basic command
; $0313 is unused. ; $0313 is unused.
&uword CINV = $0314 ; IRQ vector (in ram) &uword CINV = $0314 ; IRQ vector (in ram)
&uword CBINV = $0316 ; BRK vector (in ram) &uword CBINV = $0316 ; BRK vector (in ram)
&uword NMINV = $0318 ; NMI vector (in ram) &uword NMINV = $0318 ; NMI vector (in ram)
&uword IOPEN = $031a &uword IOPEN = $031a
&uword ICLOSE = $031c &uword ICLOSE = $031c
&uword ICHKIN = $031e &uword ICHKIN = $031e
&uword ICKOUT = $0320 &uword ICKOUT = $0320
&uword ICLRCH = $0322 &uword ICLRCH = $0322
&uword IBASIN = $0324 &uword IBASIN = $0324
&uword IBSOUT = $0326 &uword IBSOUT = $0326
&uword ISTOP = $0328 &uword ISTOP = $0328
&uword IGETIN = $032a &uword IGETIN = $032a
&uword ICLALL = $032c &uword ICLALL = $032c
&uword KEYHDL = $032e ; keyboard scan code handler see examples/cx16/keyboardhandler.p8 &uword KEYHDL = $032e ; keyboard scan code handler see examples/cx16/keyboardhandler.p8
&uword ILOAD = $0330 &uword ILOAD = $0330
&uword ISAVE = $0332 &uword ISAVE = $0332
&uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in &uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
&uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in &uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in
&uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in &uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in
; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s) ; the sixteen virtual 16-bit registers in both normal unsigned mode and signed mode (s)
@ -274,44 +279,44 @@ cx16 {
; I/O ; I/O
const uword VIA1_BASE = $9f00 ;VIA 6522 #1 const uword VIA1_BASE = $9f00 ;VIA 6522 #1
&ubyte via1prb = VIA1_BASE + 0 &ubyte via1prb = VIA1_BASE + 0
&ubyte via1pra = VIA1_BASE + 1 &ubyte via1pra = VIA1_BASE + 1
&ubyte via1ddrb = VIA1_BASE + 2 &ubyte via1ddrb = VIA1_BASE + 2
&ubyte via1ddra = VIA1_BASE + 3 &ubyte via1ddra = VIA1_BASE + 3
&ubyte via1t1l = VIA1_BASE + 4 &ubyte via1t1l = VIA1_BASE + 4
&ubyte via1t1h = VIA1_BASE + 5 &ubyte via1t1h = VIA1_BASE + 5
&ubyte via1t1ll = VIA1_BASE + 6 &ubyte via1t1ll = VIA1_BASE + 6
&ubyte via1t1lh = VIA1_BASE + 7 &ubyte via1t1lh = VIA1_BASE + 7
&ubyte via1t2l = VIA1_BASE + 8 &ubyte via1t2l = VIA1_BASE + 8
&ubyte via1t2h = VIA1_BASE + 9 &ubyte via1t2h = VIA1_BASE + 9
&ubyte via1sr = VIA1_BASE + 10 &ubyte via1sr = VIA1_BASE + 10
&ubyte via1acr = VIA1_BASE + 11 &ubyte via1acr = VIA1_BASE + 11
&ubyte via1pcr = VIA1_BASE + 12 &ubyte via1pcr = VIA1_BASE + 12
&ubyte via1ifr = VIA1_BASE + 13 &ubyte via1ifr = VIA1_BASE + 13
&ubyte via1ier = VIA1_BASE + 14 &ubyte via1ier = VIA1_BASE + 14
&ubyte via1ora = VIA1_BASE + 15 &ubyte via1ora = VIA1_BASE + 15
const uword VIA2_BASE = $9f10 ;VIA 6522 #2 const uword VIA2_BASE = $9f10 ;VIA 6522 #2
&ubyte via2prb = VIA2_BASE + 0 &ubyte via2prb = VIA2_BASE + 0
&ubyte via2pra = VIA2_BASE + 1 &ubyte via2pra = VIA2_BASE + 1
&ubyte via2ddrb = VIA2_BASE + 2 &ubyte via2ddrb = VIA2_BASE + 2
&ubyte via2ddra = VIA2_BASE + 3 &ubyte via2ddra = VIA2_BASE + 3
&ubyte via2t1l = VIA2_BASE + 4 &ubyte via2t1l = VIA2_BASE + 4
&ubyte via2t1h = VIA2_BASE + 5 &ubyte via2t1h = VIA2_BASE + 5
&ubyte via2t1ll = VIA2_BASE + 6 &ubyte via2t1ll = VIA2_BASE + 6
&ubyte via2t1lh = VIA2_BASE + 7 &ubyte via2t1lh = VIA2_BASE + 7
&ubyte via2t2l = VIA2_BASE + 8 &ubyte via2t2l = VIA2_BASE + 8
&ubyte via2t2h = VIA2_BASE + 9 &ubyte via2t2h = VIA2_BASE + 9
&ubyte via2sr = VIA2_BASE + 10 &ubyte via2sr = VIA2_BASE + 10
&ubyte via2acr = VIA2_BASE + 11 &ubyte via2acr = VIA2_BASE + 11
&ubyte via2pcr = VIA2_BASE + 12 &ubyte via2pcr = VIA2_BASE + 12
&ubyte via2ifr = VIA2_BASE + 13 &ubyte via2ifr = VIA2_BASE + 13
&ubyte via2ier = VIA2_BASE + 14 &ubyte via2ier = VIA2_BASE + 14
&ubyte via2ora = VIA2_BASE + 15 &ubyte via2ora = VIA2_BASE + 15
; YM-2151 sound chip ; YM-2151 sound chip
&ubyte YM_ADDRESS = $9f40 &ubyte YM_ADDRESS = $9f40
&ubyte YM_DATA = $9f41 &ubyte YM_DATA = $9f41
const uword extdev = $9f60 const uword extdev = $9f60
@ -323,7 +328,7 @@ cx16 {
romsub $ff4a = close_all(ubyte device @A) clobbers(A,X,Y) romsub $ff4a = close_all(ubyte device @A) clobbers(A,X,Y)
romsub $ff59 = lkupla(ubyte la @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 $ff5c = lkupsa(ubyte sa @Y) clobbers(A,X,Y)
romsub $ff5f = screen_mode(ubyte mode @A, ubyte getCurrent @Pc) clobbers(A, X, Y) -> ubyte @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() 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) ; 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 romsub $ff6e = jsrfar() ; following word = address to call, byte after that=rom/ram bank it is in
@ -340,12 +345,12 @@ romsub $ff23 = GRAPH_clear() clobbers(A,X,Y)
romsub $ff26 = GRAPH_set_window(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y) romsub $ff26 = GRAPH_set_window(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y) clobbers (A,X,Y) romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y) clobbers (A,X,Y)
romsub $ff2c = GRAPH_draw_line(uword x1 @R0, uword y1 @R1, uword x2 @R2, uword y2 @R3) clobbers(A,X,Y) romsub $ff2c = GRAPH_draw_line(uword x1 @R0, uword y1 @R1, uword x2 @R2, uword y2 @R3) clobbers(A,X,Y)
romsub $ff2f = GRAPH_draw_rect(uword x @R0, uword y @R1, uword width @R2, uword height @R3, uword cornerradius @R4, ubyte fill @Pc) clobbers(A,X,Y) romsub $ff2f = GRAPH_draw_rect(uword x @R0, uword y @R1, uword width @R2, uword height @R3, uword cornerradius @R4, bool fill @Pc) clobbers(A,X,Y)
romsub $ff32 = GRAPH_move_rect(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword width @R4, uword height @R5) clobbers(A,X,Y) romsub $ff32 = GRAPH_move_rect(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword width @R4, uword height @R5) clobbers(A,X,Y)
romsub $ff35 = GRAPH_draw_oval(uword x @R0, uword y @R1, uword width @R2, uword height @R3, ubyte fill @Pc) clobbers(A,X,Y) romsub $ff35 = GRAPH_draw_oval(uword x @R0, uword y @R1, uword width @R2, uword height @R3, bool fill @Pc) clobbers(A,X,Y)
romsub $ff38 = GRAPH_draw_image(uword x @R0, uword y @R1, uword ptr @R2, uword width @R3, uword height @R4) clobbers(A,X,Y) romsub $ff38 = GRAPH_draw_image(uword x @R0, uword y @R1, uword ptr @R2, uword width @R3, uword height @R4) clobbers(A,X,Y)
romsub $ff3b = GRAPH_set_font(uword fontptr @R0) clobbers(A,X,Y) romsub $ff3b = GRAPH_set_font(uword fontptr @R0) clobbers(A,X,Y)
romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, ubyte is_control @Pc) clobbers(A,X,Y) romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, bool is_control @Pc) clobbers(A,X,Y)
romsub $ff41 = GRAPH_put_char(uword x @R0, uword y @R1, ubyte char @A) clobbers(A,X,Y) romsub $ff41 = GRAPH_put_char(uword x @R0, uword y @R1, ubyte char @A) clobbers(A,X,Y)
romsub $ff41 = GRAPH_put_next_char(ubyte char @A) clobbers(A,X,Y) ; alias for the routine above that doesn't reset the position of the initial character romsub $ff41 = GRAPH_put_next_char(ubyte char @A) clobbers(A,X,Y) ; alias for the routine above that doesn't reset the position of the initial character
@ -367,16 +372,17 @@ romsub $ff1a = FB_filter_pixels(uword pointer @ R0, uword count @R1) clobbers(A
romsub $ff1d = FB_move_pixels(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword count @R4) clobbers(A,X,Y) romsub $ff1d = FB_move_pixels(uword sx @R0, uword sy @R1, uword tx @R2, uword ty @R3, uword count @R4) clobbers(A,X,Y)
; misc ; misc
romsub $fec6 = i2c_read_byte(ubyte device @X, ubyte offset @Y) clobbers (X,Y) -> ubyte @A, ubyte @Pc romsub $FEBA = BSAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) clobbers (X, Y) -> bool @ Pc, ubyte @ A ; like cbm.SAVE, but omits the 2-byte prg header
romsub $fec9 = i2c_write_byte(ubyte device @X, ubyte offset @Y, ubyte data @A) clobbers (A,X,Y) -> ubyte @Pc romsub $fec6 = i2c_read_byte(ubyte device @X, ubyte offset @Y) clobbers (X,Y) -> ubyte @A, bool @Pc
romsub $fef0 = sprite_set_image(uword pixels @R0, uword mask @R1, ubyte bpp @R2, ubyte number @A, ubyte width @X, ubyte height @Y, ubyte apply_mask @Pc) clobbers(A,X,Y) -> ubyte @Pc romsub $fec9 = i2c_write_byte(ubyte device @X, ubyte offset @Y, ubyte data @A) clobbers (A,X,Y) -> bool @Pc
romsub $fef0 = sprite_set_image(uword pixels @R0, uword mask @R1, ubyte bpp @R2, ubyte number @A, ubyte width @X, ubyte height @Y, bool apply_mask @Pc) clobbers(A,X,Y) -> bool @Pc
romsub $fef3 = sprite_set_position(uword x @R0, uword y @R1, ubyte number @A) clobbers(A,X,Y) romsub $fef3 = sprite_set_position(uword x @R0, uword y @R1, ubyte number @A) clobbers(A,X,Y)
romsub $fee4 = memory_fill(uword address @R0, uword num_bytes @R1, ubyte value @A) clobbers(A,X,Y) romsub $fee4 = memory_fill(uword address @R0, uword num_bytes @R1, ubyte value @A) clobbers(A,X,Y)
romsub $fee7 = memory_copy(uword source @R0, uword target @R1, uword num_bytes @R2) clobbers(A,X,Y) romsub $fee7 = memory_copy(uword source @R0, uword target @R1, uword num_bytes @R2) clobbers(A,X,Y)
romsub $feea = memory_crc(uword address @R0, uword num_bytes @R1) clobbers(A,X,Y) -> uword @R2 romsub $feea = memory_crc(uword address @R0, uword num_bytes @R1) clobbers(A,X,Y) -> uword @R2
romsub $feed = memory_decompress(uword input @R0, uword output @R1) clobbers(A,X,Y) -> uword @R1 ; last address +1 is result in R1 romsub $feed = memory_decompress(uword input @R0, uword output @R1) clobbers(A,X,Y) -> uword @R1 ; last address +1 is result in R1
romsub $fedb = console_init(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y) romsub $fedb = console_init(uword x @R0, uword y @R1, uword width @R2, uword height @R3) clobbers(A,X,Y)
romsub $fede = console_put_char(ubyte char @A, ubyte wrapping @Pc) clobbers(A,X,Y) romsub $fede = console_put_char(ubyte char @A, bool wrapping @Pc) clobbers(A,X,Y)
romsub $fee1 = console_get_char() clobbers(X,Y) -> ubyte @A romsub $fee1 = console_get_char() clobbers(X,Y) -> ubyte @A
romsub $fed8 = console_put_image(uword pointer @R0, uword width @R1, uword height @R2) clobbers(A,X,Y) romsub $fed8 = console_put_image(uword pointer @R0, uword width @R1, uword height @R2) clobbers(A,X,Y)
romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y) romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y)
@ -384,7 +390,7 @@ romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
romsub $fecc = monitor() clobbers(A,X,Y) romsub $fecc = monitor() clobbers(A,X,Y)
romsub $ff44 = macptr(ubyte length @A, uword buffer @XY, bool dontAdvance @Pc) clobbers(A) -> bool @Pc, uword @XY romsub $ff44 = macptr(ubyte length @A, uword buffer @XY, bool dontAdvance @Pc) clobbers(A) -> bool @Pc, uword @XY
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y) romsub $ff47 = enter_basic(bool cold_or_warm @Pc) clobbers(A,X,Y)
romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y) romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y)
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time() romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time()
@ -401,18 +407,37 @@ romsub $ff53 = joystick_scan() clobbers(A, X, Y)
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @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 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 $C04B = psg_init() clobbers(A,X,Y)
romsub $C063 = ym_init() clobbers(A,X,Y) -> ubyte @Pc ; (re)init YM chip romsub $C063 = ym_init() clobbers(A,X,Y) -> bool @Pc ; (re)init YM chip
romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> ubyte @Pc ; load default YM patches romsub $C066 = ym_loaddefpatches() clobbers(A,X,Y) -> bool @Pc ; load default YM patches
romsub $C09F = audio_init() clobbers(A,X,Y) -> ubyte @Pc ; (re)initialize PSG and YM audio chips romsub $C09F = audio_init() clobbers(A,X,Y) -> bool @Pc ; (re)initialize PSG and YM audio chips
; TODO: add more of the audio routines? ; 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() { asmsub kbdbuf_clear() {
; -- convenience helper routine to clear the keyboard buffer ; -- convenience helper routine to clear the keyboard buffer
%asm {{ %asm {{
- jsr c64.GETIN - jsr cbm.GETIN
bne - bne -
rts rts
}} }}
@ -485,7 +510,7 @@ asmsub numbanks() -> uword @AY {
%asm {{ %asm {{
phx phx
sec sec
jsr c64.MEMTOP jsr cbm.MEMTOP
ldy #0 ldy #0
cmp #0 cmp #0
bne + bne +
@ -514,6 +539,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) { 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 ; -- 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 {{ %asm {{
and #1 and #1
pha pha
@ -537,6 +564,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) { asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
; -- write a single byte to VERA's video memory ; -- write a single byte to VERA's video memory
; note: inefficient when writing multiple sequential bytes! ; note: inefficient when writing multiple sequential bytes!
@ -627,7 +753,91 @@ asmsub vpoke_mask(ubyte bank @A, uword address @R0, ubyte mask @X, ubyte value @
}} }}
} }
; ---- system stuff ----- 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 {{
; note cannot store this on cpu hardware stack because this gets called as a subroutine
lda cx16.VERA_ADDR_L
sta _vera_storage
lda cx16.VERA_ADDR_M
sta _vera_storage+1
lda cx16.VERA_ADDR_H
sta _vera_storage+2
lda cx16.VERA_CTRL
sta _vera_storage+3
eor #1
sta _vera_storage+7
sta cx16.VERA_CTRL
lda cx16.VERA_ADDR_L
sta _vera_storage+4
lda cx16.VERA_ADDR_M
sta _vera_storage+5
lda cx16.VERA_ADDR_H
sta _vera_storage+6
rts
_vera_storage: .byte 0,0,0,0,0,0,0,0
}}
}
asmsub restore_vera_context() clobbers(A) {
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
%asm {{
lda cx16.save_vera_context._vera_storage+7
sta cx16.VERA_CTRL
lda cx16.save_vera_context._vera_storage+6
sta cx16.VERA_ADDR_H
lda cx16.save_vera_context._vera_storage+5
sta cx16.VERA_ADDR_M
lda cx16.save_vera_context._vera_storage+4
sta cx16.VERA_ADDR_L
lda cx16.save_vera_context._vera_storage+3
sta cx16.VERA_CTRL
lda cx16.save_vera_context._vera_storage+2
sta cx16.VERA_ADDR_H
lda cx16.save_vera_context._vera_storage+1
sta cx16.VERA_ADDR_M
lda cx16.save_vera_context._vera_storage+0
sta cx16.VERA_ADDR_L
rts
}}
}
}
sys {
; ------- lowlevel system routines --------
%option no_symbol_prefixing
const ubyte target = 16 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
asmsub init_system() { asmsub init_system() {
; Initializes the machine to a sane starting state. ; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic. ; Called automatically by the loader program logic.
@ -638,29 +848,30 @@ asmsub init_system() {
tay tay
jsr cx16.mouse_config ; disable mouse jsr cx16.mouse_config ; disable mouse
cld cld
lda VERA_DC_VIDEO lda cx16.VERA_DC_VIDEO
and #%00000111 ; retain chroma + output mode and #%00000111 ; retain chroma + output mode
sta P8ZP_SCRATCH_REG sta P8ZP_SCRATCH_REG
lda #$0a lda #$0a
sta $01 ; rom bank 10 (audio) sta $01 ; rom bank 10 (audio)
jsr audio_init ; silence jsr cx16.audio_init ; silence
stz $01 ; rom bank 0 (kernal) stz $01 ; rom bank 0 (kernal)
jsr c64.IOINIT jsr cbm.IOINIT
jsr c64.RESTOR jsr cbm.RESTOR
jsr c64.CINT jsr cbm.CINT
lda VERA_DC_VIDEO lda cx16.VERA_DC_VIDEO
and #%11111000 and #%11111000
ora P8ZP_SCRATCH_REG ora P8ZP_SCRATCH_REG
sta VERA_DC_VIDEO ; restore old output mode sta cx16.VERA_DC_VIDEO ; restore old output mode
lda #$90 ; black lda #$90 ; black
jsr c64.CHROUT jsr cbm.CHROUT
lda #1 lda #1
sta $00 ; select ram bank 1 jsr cbm.CHROUT ; swap fg/bg
jsr c64.CHROUT ; swap fg/bg
lda #$9e ; yellow lda #$9e ; yellow
jsr c64.CHROUT jsr cbm.CHROUT
lda #147 ; clear screen lda #147 ; clear screen
jsr c64.CHROUT jsr cbm.CHROUT
lda #PROG8_VARSHIGH_RAMBANK
sta $00 ; select ram bank
lda #0 lda #0
tax tax
tay tay
@ -678,6 +889,8 @@ asmsub init_system_phase2() {
sta restore_irq._orig_irqvec sta restore_irq._orig_irqvec
lda cx16.CINV+1 lda cx16.CINV+1
sta restore_irq._orig_irqvec+1 sta restore_irq._orig_irqvec+1
lda #PROG8_VARSHIGH_RAMBANK
sta $00 ; select ram bank
cli cli
rts rts
}} }}
@ -695,7 +908,7 @@ asmsub cleanup_at_exit() {
}} }}
} }
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) { asmsub set_irq(uword handler @AY, bool useKernal @Pc) clobbers(A) {
%asm {{ %asm {{
sta _modified+1 sta _modified+1
sty _modified+2 sty _modified+2
@ -774,56 +987,6 @@ IRQ_SCRATCH_ZPWORD2 .word 0
}} }}
} }
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 {{
; note cannot store this on cpu hardware stack because this gets called as a subroutine
lda cx16.VERA_ADDR_L
sta _vera_storage
lda cx16.VERA_ADDR_M
sta _vera_storage+1
lda cx16.VERA_ADDR_H
sta _vera_storage+2
lda cx16.VERA_CTRL
sta _vera_storage+3
eor #1
sta _vera_storage+7
sta cx16.VERA_CTRL
lda cx16.VERA_ADDR_L
sta _vera_storage+4
lda cx16.VERA_ADDR_M
sta _vera_storage+5
lda cx16.VERA_ADDR_H
sta _vera_storage+6
rts
_vera_storage: .byte 0,0,0,0,0,0,0,0
}}
}
asmsub restore_vera_context() clobbers(A) {
; -- use this at the end of your IRQ handler if it uses Vera registers, to restore the state
%asm {{
lda cx16.save_vera_context._vera_storage+7
sta cx16.VERA_CTRL
lda cx16.save_vera_context._vera_storage+6
sta cx16.VERA_ADDR_H
lda cx16.save_vera_context._vera_storage+5
sta cx16.VERA_ADDR_M
lda cx16.save_vera_context._vera_storage+4
sta cx16.VERA_ADDR_L
lda cx16.save_vera_context._vera_storage+3
sta cx16.VERA_CTRL
lda cx16.save_vera_context._vera_storage+2
sta cx16.VERA_ADDR_H
lda cx16.save_vera_context._vera_storage+1
sta cx16.VERA_ADDR_M
lda cx16.save_vera_context._vera_storage+0
sta cx16.VERA_ADDR_L
rts
}}
}
asmsub restore_irq() clobbers(A) { asmsub restore_irq() clobbers(A) {
%asm {{ %asm {{
sei sei
@ -893,23 +1056,14 @@ asmsub set_rasterline(uword line @AY) {
}} }}
} }
}
sys {
; ------- lowlevel system routines --------
const ubyte target = 16 ; compilation target specifier. 64 = C64, 128 = C128, 16 = CommanderX16.
asmsub reset_system() { asmsub reset_system() {
; Soft-reset the system back to initial power-on Basic prompt. ; Soft-reset the system back to initial power-on Basic prompt.
; We do this via the SMC so that a true reset is performed that also resets the Vera fully. ; We do this via the SMC so that a true reset is performed that also resets the Vera fully.
%asm {{ %asm {{
sei sei
ldx #$42 ldx #$42
ldy #1 ldy #2
tya lda #0
jsr cx16.i2c_write_byte jsr cx16.i2c_write_byte
bra * bra *
}} }}
@ -941,11 +1095,11 @@ _loop lda P8ZP_SCRATCH_W1
rts rts
+ sei + sei
jsr c64.RDTIM jsr cbm.RDTIM
cli cli
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
- sei - sei
jsr c64.RDTIM jsr cbm.RDTIM
cli cli
cmp P8ZP_SCRATCH_B1 cmp P8ZP_SCRATCH_B1
beq - beq -
@ -1086,10 +1240,23 @@ _longcopy
}} }}
} }
inline asmsub irqsafe_set_irqd() {
%asm {{
php
sei
}}
}
inline asmsub irqsafe_clear_irqd() {
%asm {{
plp
}}
}
inline asmsub exit(ubyte returnvalue @A) { inline asmsub exit(ubyte returnvalue @A) {
; -- immediately exit the program with a return code in the A register ; -- immediately exit the program with a return code in the A register
%asm {{ %asm {{
jsr c64.CLRCHN ; reset i/o channels jsr cbm.CLRCHN ; reset i/o channels
ldx prog8_lib.orig_stackpointer ldx prog8_lib.orig_stackpointer
txs txs
rts ; return to original caller rts ; return to original caller

View File

@ -6,6 +6,8 @@
txt { txt {
%option no_symbol_prefixing
const ubyte DEFAULT_WIDTH = 80 const ubyte DEFAULT_WIDTH = 80
const ubyte DEFAULT_HEIGHT = 60 const ubyte DEFAULT_HEIGHT = 60
@ -33,10 +35,10 @@ asmsub column(ubyte col @A) clobbers(A, X, Y) {
; ---- set the cursor on the given column (starting with 0) on the current line ; ---- set the cursor on the given column (starting with 0) on the current line
%asm {{ %asm {{
sec sec
jsr c64.PLOT jsr cbm.PLOT
tay tay
clc clc
jmp c64.PLOT jmp cbm.PLOT
}} }}
} }
@ -46,7 +48,7 @@ asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
sty _ly+1 sty _ly+1
phx phx
pha pha
jsr c64.SCREEN ; get dimensions in X/Y jsr cbm.SCREEN ; get dimensions in X/Y
txa txa
lsr a lsr a
lsr a lsr a
@ -94,7 +96,7 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
%asm {{ %asm {{
phx phx
pha pha
jsr c64.SCREEN ; get dimensions in X/Y jsr cbm.SCREEN ; get dimensions in X/Y
txa txa
lsr a lsr a
lsr a lsr a
@ -125,7 +127,7 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
%asm {{ %asm {{
phx phx
sta _la+1 sta _la+1
jsr c64.SCREEN ; get dimensions in X/Y jsr cbm.SCREEN ; get dimensions in X/Y
txa txa
lsr a lsr a
lsr a lsr a
@ -158,35 +160,35 @@ ubyte[16] color_to_charcode = [$90,$05,$1c,$9f,$9c,$1e,$1f,$9e,$81,$95,$96,$97,$
sub color (ubyte txtcol) { sub color (ubyte txtcol) {
txtcol &= 15 txtcol &= 15
c64.CHROUT(color_to_charcode[txtcol]) cbm.CHROUT(color_to_charcode[txtcol])
} }
sub color2 (ubyte txtcol, ubyte bgcol) { sub color2 (ubyte txtcol, ubyte bgcol) {
txtcol &= 15 txtcol &= 15
bgcol &= 15 bgcol &= 15
c64.CHROUT(color_to_charcode[bgcol]) cbm.CHROUT(color_to_charcode[bgcol])
c64.CHROUT(1) ; switch fg and bg colors cbm.CHROUT(1) ; switch fg and bg colors
c64.CHROUT(color_to_charcode[txtcol]) cbm.CHROUT(color_to_charcode[txtcol])
} }
sub lowercase() { sub lowercase() {
c64.CHROUT($0e) cbm.CHROUT($0e)
; this is not 100% compatible: cx16.screen_set_charset(3, 0) ; lowercase petscii charset ; this is not 100% compatible: cx16.screen_set_charset(3, 0) ; lowercase petscii charset
} }
sub uppercase() { sub uppercase() {
c64.CHROUT($8e) cbm.CHROUT($8e)
; this is not 100% compatible: cx16.screen_set_charset(2, 0) ; uppercase petscii charset ; this is not 100% compatible: cx16.screen_set_charset(2, 0) ; uppercase petscii charset
} }
sub iso() { sub iso() {
c64.CHROUT($0f) cbm.CHROUT($0f)
; This doesn't enable it completely: cx16.screen_set_charset(1, 0) ; iso charset ; This doesn't enable it completely: cx16.screen_set_charset(1, 0) ; iso charset
} }
sub iso_off() { sub iso_off() {
; -- you have to call this first when switching back from iso charset to regular charset. ; -- you have to call this first when switching back from iso charset to regular charset.
c64.CHROUT($8f) cbm.CHROUT($8f)
} }
@ -195,7 +197,7 @@ asmsub scroll_left() clobbers(A, Y) {
; contents of the rightmost column are unchanged, you should clear/refill this yourself ; contents of the rightmost column are unchanged, you should clear/refill this yourself
%asm {{ %asm {{
phx phx
jsr c64.SCREEN jsr cbm.SCREEN
dex dex
stx _lx+1 stx _lx+1
dey dey
@ -236,12 +238,12 @@ _lx ldx #0 ; modified
}} }}
} }
asmsub scroll_right() clobbers(A) { asmsub scroll_right() clobbers(A,Y) {
; ---- scroll the whole screen 1 character to the right ; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself ; contents of the leftmost column are unchanged, you should clear/refill this yourself
%asm {{ %asm {{
phx phx
jsr c64.SCREEN jsr cbm.SCREEN
dex dex
stx _lx+1 stx _lx+1
txa txa
@ -295,7 +297,7 @@ asmsub scroll_up() clobbers(A, Y) {
; contents of the bottom row are unchanged, you should refill/clear this yourself ; contents of the bottom row are unchanged, you should refill/clear this yourself
%asm {{ %asm {{
phx phx
jsr c64.SCREEN jsr cbm.SCREEN
stx _nextline+1 stx _nextline+1
dey dey
sty P8ZP_SCRATCH_B1 sty P8ZP_SCRATCH_B1
@ -345,7 +347,7 @@ asmsub scroll_down() clobbers(A, Y) {
; contents of the top row are unchanged, you should refill/clear this yourself ; contents of the top row are unchanged, you should refill/clear this yourself
%asm {{ %asm {{
phx phx
jsr c64.SCREEN jsr cbm.SCREEN
stx _nextline+1 stx _nextline+1
dey dey
sty P8ZP_SCRATCH_B1 sty P8ZP_SCRATCH_B1
@ -396,20 +398,20 @@ _nextline
}} }}
} }
romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use c64.CHROUT directly ofcourse. romsub $FFD2 = chrout(ubyte char @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse.
asmsub print (str text @ AY) clobbers(A,Y) { asmsub print (str text @ AY) clobbers(A,Y) {
; ---- print null terminated string from A/Y ; ---- print null terminated string from A/Y
; note: the compiler contains an optimization that will replace ; note: the compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char, ; a call to this subroutine with a string argument of just one char,
; by just one call to c64.CHROUT of that single char. ; by just one call to cbm.CHROUT of that single char.
%asm {{ %asm {{
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG sty P8ZP_SCRATCH_REG
ldy #0 ldy #0
- lda (P8ZP_SCRATCH_B1),y - lda (P8ZP_SCRATCH_B1),y
beq + beq +
jsr c64.CHROUT jsr cbm.CHROUT
iny iny
bne - bne -
+ rts + rts
@ -423,11 +425,11 @@ asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
jsr conv.ubyte2decimal jsr conv.ubyte2decimal
pha pha
tya tya
jsr c64.CHROUT jsr cbm.CHROUT
pla pla
jsr c64.CHROUT jsr cbm.CHROUT
txa txa
jsr c64.CHROUT jsr cbm.CHROUT
plx plx
rts rts
}} }}
@ -443,16 +445,16 @@ _print_byte_digits
cpy #'0' cpy #'0'
beq + beq +
tya tya
jsr c64.CHROUT jsr cbm.CHROUT
pla pla
jsr c64.CHROUT jsr cbm.CHROUT
bra _ones bra _ones
+ pla + pla
cmp #'0' cmp #'0'
beq _ones beq _ones
jsr c64.CHROUT jsr cbm.CHROUT
_ones txa _ones txa
jsr c64.CHROUT jsr cbm.CHROUT
plx plx
rts rts
}} }}
@ -466,45 +468,45 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
cmp #0 cmp #0
bpl + bpl +
lda #'-' lda #'-'
jsr c64.CHROUT jsr cbm.CHROUT
+ pla + pla
jsr conv.byte2decimal jsr conv.byte2decimal
bra print_ub._print_byte_digits bra print_ub._print_byte_digits
}} }}
} }
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_ubhex (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well) ; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
phx phx
bcc + bcc +
pha pha
lda #'$' lda #'$'
jsr c64.CHROUT jsr cbm.CHROUT
pla pla
+ jsr conv.ubyte2hex + jsr conv.ubyte2hex
jsr c64.CHROUT jsr cbm.CHROUT
tya tya
jsr c64.CHROUT jsr cbm.CHROUT
plx plx
rts rts
}} }}
} }
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_ubbin (ubyte value @ A, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
phx phx
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
bcc + bcc +
lda #'%' lda #'%'
jsr c64.CHROUT jsr cbm.CHROUT
+ ldy #8 + ldy #8
- lda #'0' - lda #'0'
asl P8ZP_SCRATCH_B1 asl P8ZP_SCRATCH_B1
bcc + bcc +
lda #'1' lda #'1'
+ jsr c64.CHROUT + jsr cbm.CHROUT
dey dey
bne - bne -
plx plx
@ -512,7 +514,7 @@ asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_uwbin (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well) ; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{ %asm {{
pha pha
@ -524,7 +526,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
}} }}
} }
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) { asmsub print_uwhex (uword value @ AY, bool prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits) ; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well) ; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{ %asm {{
@ -545,7 +547,7 @@ asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
ldy #0 ldy #0
- lda conv.uword2decimal.decTenThousands,y - lda conv.uword2decimal.decTenThousands,y
beq + beq +
jsr c64.CHROUT jsr cbm.CHROUT
iny iny
bne - bne -
+ plx + plx
@ -568,14 +570,14 @@ asmsub print_uw (uword value @ AY) clobbers(A,Y) {
bne - bne -
_gotdigit _gotdigit
jsr c64.CHROUT jsr cbm.CHROUT
iny iny
lda conv.uword2decimal.decTenThousands,y lda conv.uword2decimal.decTenThousands,y
bne _gotdigit bne _gotdigit
rts rts
_allzero _allzero
lda #'0' lda #'0'
jmp c64.CHROUT jmp cbm.CHROUT
}} }}
} }
@ -586,7 +588,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
bpl + bpl +
pha pha
lda #'-' lda #'-'
jsr c64.CHROUT jsr cbm.CHROUT
tya tya
eor #255 eor #255
tay tay
@ -607,7 +609,7 @@ asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
ldy #0 ; char counter = 0 ldy #0 ; char counter = 0
- jsr c64.CHRIN - jsr cbm.CHRIN
cmp #$0d ; return (ascii 13) pressed? cmp #$0d ; return (ascii 13) pressed?
beq + ; yes, end. beq + ; yes, end.
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
@ -769,7 +771,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
phx phx
tax tax
clc clc
jsr c64.PLOT jsr cbm.PLOT
plx plx
rts rts
}} }}
@ -778,7 +780,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
asmsub width() clobbers(X,Y) -> ubyte @A { asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns) ; -- returns the text screen width (number of columns)
%asm {{ %asm {{
jsr c64.SCREEN jsr cbm.SCREEN
txa txa
rts rts
}} }}
@ -787,7 +789,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
asmsub height() clobbers(X, Y) -> ubyte @A { asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows) ; -- returns the text screen height (number of rows)
%asm {{ %asm {{
jsr c64.SCREEN jsr cbm.SCREEN
tya tya
rts rts
}} }}

View File

@ -3,6 +3,8 @@
%import textio %import textio
cx16logo { cx16logo {
%option no_symbol_prefixing
sub logo_at(ubyte column, ubyte row) { sub logo_at(ubyte column, ubyte row) {
uword strptr uword strptr
for strptr in logo_lines { for strptr in logo_lines {

View File

@ -1,59 +1,66 @@
; C64 and Cx16 disk drive I/O routines. ; C64/C128 disk drive I/O routines.
%import textio %import textio
%import string %import string
%import syslib %import syslib
diskio { diskio {
%option no_symbol_prefixing
sub directory(ubyte drivenumber) -> bool { ubyte drivenumber = 8
; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
c64.SETNAM(1, "$") sub set_drive(ubyte number) {
c64.SETLFS(12, drivenumber, 0) drivenumber = number
}
sub directory() -> bool {
; -- Prints the directory contents to the screen. Returns success.
cbm.SETNAM(1, "$")
cbm.SETLFS(12, drivenumber, 0)
ubyte status = 1 ubyte status = 1
void c64.OPEN() ; open 12,8,0,"$" void cbm.OPEN() ; open 12,8,0,"$"
if_cs if_cs
goto io_error goto io_error
void c64.CHKIN(12) ; use #12 as input channel void cbm.CHKIN(12) ; use #12 as input channel
if_cs if_cs
goto io_error goto io_error
repeat 4 { repeat 4 {
void c64.CHRIN() ; skip the 4 prologue bytes void cbm.CHRIN() ; skip the 4 prologue bytes
} }
; while not stop key pressed / EOF encountered, read data. ; while not stop key pressed / EOF encountered, read data.
status = c64.READST() status = cbm.READST()
if status!=0 { if status!=0 {
status = 1 status = 1
goto io_error goto io_error
} }
while status==0 { while status==0 {
ubyte low = c64.CHRIN() ubyte low = cbm.CHRIN()
ubyte high = c64.CHRIN() ubyte high = cbm.CHRIN()
txt.print_uw(mkword(high, low)) txt.print_uw(mkword(high, low))
txt.spc() txt.spc()
ubyte @zp char ubyte @zp char
repeat { repeat {
char = c64.CHRIN() char = cbm.CHRIN()
if char==0 if char==0
break break
txt.chrout(char) txt.chrout(char)
} }
txt.nl() txt.nl()
void c64.CHRIN() ; skip 2 bytes void cbm.CHRIN() ; skip 2 bytes
void c64.CHRIN() void cbm.CHRIN()
status = c64.READST() status = cbm.READST()
if c64.STOP2() if cbm.STOP2()
break break
} }
status = c64.READST() status = cbm.READST()
io_error: io_error:
c64.CLRCHN() ; restore default i/o devices cbm.CLRCHN() ; restore default i/o devices
c64.CLOSE(12) cbm.CLOSE(12)
if status and status & $40 == 0 { ; bit 6=end of file if status and status & $40 == 0 { ; bit 6=end of file
txt.print("\ni/o error, status: ") txt.print("\ni/o error, status: ")
@ -65,37 +72,43 @@ io_error:
return true return true
} }
sub diskname(ubyte drivenumber) -> uword { sub diskname() -> uword {
; -- Returns pointer to disk name string or 0 if failure. ; -- Returns pointer to disk name string or 0 if failure.
c64.SETNAM(1, "$") cbm.SETNAM(1, "$")
c64.SETLFS(12, drivenumber, 0) cbm.SETLFS(12, drivenumber, 0)
ubyte okay = false ubyte okay = false
void c64.OPEN() ; open 12,8,0,"$" void cbm.OPEN() ; open 12,8,0,"$"
if_cs if_cs
goto io_error goto io_error
void c64.CHKIN(12) ; use #12 as input channel void cbm.CHKIN(12) ; use #12 as input channel
if_cs if_cs
goto io_error goto io_error
repeat 6 { while cbm.CHRIN()!='"' {
void c64.CHRIN() ; skip the 6 prologue bytes ; skip up to entry name
} }
if c64.READST()!=0 if cbm.READST()!=0
goto io_error goto io_error
cx16.r0 = &list_filename cx16.r0 = &list_filename
repeat { repeat {
@(cx16.r0) = c64.CHRIN() @(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 break
}
cx16.r0++ cx16.r0++
} }
okay = true okay = true
io_error: io_error:
c64.CLRCHN() ; restore default i/o devices cbm.CLRCHN() ; restore default i/o devices
c64.CLOSE(12) cbm.CLOSE(12)
if okay if okay
return &list_filename return &list_filename
return 0 return 0
@ -106,13 +119,12 @@ io_error:
uword list_pattern uword list_pattern
uword list_blocks uword list_blocks
bool iteration_in_progress = false bool iteration_in_progress = false
ubyte last_drivenumber = 8 ; which drive was last used for a f_open operation?
str list_filetype = "???" ; prg, seq, dir str list_filetype = "???" ; prg, seq, dir
str list_filename = "?" * 50 str list_filename = "?" * 50
; ----- get a list of files (uses iteration functions internally) ----- ; ----- get a list of files (uses iteration functions internally) -----
sub list_filenames(ubyte drivenumber, uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte { sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
; -- fill the provided buffer with the names of the files on the disk (until buffer is full). ; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
; Files in the buffer are separeted by a 0 byte. You can provide an optional pattern to match against. ; Files in the buffer are separeted by a 0 byte. You can provide an optional pattern to match against.
; After the last filename one additional 0 byte is placed to indicate the end of the list. ; After the last filename one additional 0 byte is placed to indicate the end of the list.
@ -120,10 +132,10 @@ io_error:
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer. ; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
uword buffer_start = filenames_buffer uword buffer_start = filenames_buffer
ubyte files_found = 0 ubyte files_found = 0
if lf_start_list(drivenumber, pattern_ptr) { if lf_start_list(pattern_ptr) {
while lf_next_entry() { while lf_next_entry() {
if list_filetype!="dir" { if list_filetype!="dir" {
filenames_buffer += string.copy(diskio.list_filename, filenames_buffer) + 1 filenames_buffer += string.copy(list_filename, filenames_buffer) + 1
files_found++ files_found++
if filenames_buffer - buffer_start > filenames_buf_size-20 { if filenames_buffer - buffer_start > filenames_buf_size-20 {
@(filenames_buffer)=0 @(filenames_buffer)=0
@ -142,7 +154,7 @@ io_error:
; ----- iterative file lister functions (uses io channel 12) ----- ; ----- iterative file lister functions (uses io channel 12) -----
sub lf_start_list(ubyte drivenumber, uword pattern_ptr) -> bool { sub lf_start_list(uword pattern_ptr) -> bool {
; -- start an iterative file listing with optional pattern matching. ; -- start an iterative file listing with optional pattern matching.
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
lf_end_list() lf_end_list()
@ -150,20 +162,20 @@ io_error:
list_skip_disk_name = true list_skip_disk_name = true
iteration_in_progress = true iteration_in_progress = true
c64.SETNAM(1, "$") cbm.SETNAM(1, "$")
c64.SETLFS(12, drivenumber, 0) cbm.SETLFS(12, drivenumber, 0)
void c64.OPEN() ; open 12,8,0,"$" void cbm.OPEN() ; open 12,8,0,"$"
if_cs if_cs
goto io_error goto io_error
void c64.CHKIN(12) ; use #12 as input channel void cbm.CHKIN(12) ; use #12 as input channel
if_cs if_cs
goto io_error goto io_error
repeat 4 { repeat 4 {
void c64.CHRIN() ; skip the 4 prologue bytes void cbm.CHRIN() ; skip the 4 prologue bytes
} }
if c64.READST()==0 if cbm.READST()==0
return true return true
io_error: io_error:
@ -180,26 +192,26 @@ io_error:
return false return false
repeat { repeat {
void c64.CHKIN(12) ; use #12 as input channel again void cbm.CHKIN(12) ; use #12 as input channel again
uword nameptr = &list_filename uword nameptr = &list_filename
ubyte blocks_lsb = c64.CHRIN() ubyte blocks_lsb = cbm.CHRIN()
ubyte blocks_msb = c64.CHRIN() ubyte blocks_msb = cbm.CHRIN()
if c64.READST() if cbm.READST()
goto close_end goto close_end
list_blocks = mkword(blocks_msb, blocks_lsb) list_blocks = mkword(blocks_msb, blocks_lsb)
; read until the filename starts after the first " ; read until the filename starts after the first "
while c64.CHRIN()!='\"' { while cbm.CHRIN()!='\"' {
if c64.READST() if cbm.READST()
goto close_end goto close_end
} }
; read the filename ; read the filename
repeat { repeat {
ubyte char = c64.CHRIN() ubyte char = cbm.CHRIN()
if char==0 if char==0
break break
if char=='\"' if char=='\"'
@ -211,17 +223,17 @@ io_error:
@(nameptr) = 0 @(nameptr) = 0
do { do {
cx16.r15L = c64.CHRIN() cx16.r15L = cbm.CHRIN()
} until cx16.r15L!=' ' ; skip blanks up to 3 chars entry type } until cx16.r15L!=' ' ; skip blanks up to 3 chars entry type
list_filetype[0] = cx16.r15L list_filetype[0] = cx16.r15L
list_filetype[1] = c64.CHRIN() list_filetype[1] = cbm.CHRIN()
list_filetype[2] = c64.CHRIN() list_filetype[2] = cbm.CHRIN()
while c64.CHRIN() { while cbm.CHRIN() {
; read the rest of the entry until the end ; read the rest of the entry until the end
} }
void c64.CHRIN() ; skip 2 bytes void cbm.CHRIN() ; skip 2 bytes
void c64.CHRIN() void cbm.CHRIN()
if not list_skip_disk_name { if not list_skip_disk_name {
if not list_pattern if not list_pattern
@ -240,8 +252,8 @@ close_end:
sub lf_end_list() { sub lf_end_list() {
; -- end an iterative file listing session (close channels). ; -- end an iterative file listing session (close channels).
if iteration_in_progress { if iteration_in_progress {
c64.CLRCHN() cbm.CLRCHN()
c64.CLOSE(12) cbm.CLOSE(12)
iteration_in_progress = false iteration_in_progress = false
} }
} }
@ -249,25 +261,24 @@ close_end:
; ----- iterative file loader functions (uses io channel 12) ----- ; ----- iterative file loader functions (uses io channel 12) -----
sub f_open(ubyte drivenumber, uword filenameptr) -> bool { sub f_open(uword filenameptr) -> bool {
; -- open a file for iterative reading with f_read ; -- open a file for iterative reading with f_read
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
f_close() f_close()
c64.SETNAM(string.length(filenameptr), filenameptr) cbm.SETNAM(string.length(filenameptr), filenameptr)
c64.SETLFS(12, drivenumber, 12) ; note: has to be 12,x,12 because otherwise f_seek doesn't work cbm.SETLFS(12, drivenumber, 12) ; note: has to be 12,x,12 because otherwise f_seek doesn't work
last_drivenumber = drivenumber void cbm.OPEN() ; open 12,8,12,"filename"
void c64.OPEN() ; open 12,8,12,"filename"
if_cc { if_cc {
if c64.READST()==0 { if cbm.READST()==0 {
iteration_in_progress = true iteration_in_progress = true
void c64.CHKIN(12) ; use #12 as input channel void cbm.CHKIN(12) ; use #12 as input channel
if_cc { if_cc {
void c64.CHRIN() ; read first byte to test for file not found void cbm.CHRIN() ; read first byte to test for file not found
if not c64.READST() { if not cbm.READST() {
c64.CLOSE(12) ; close file because we already consumed first byte cbm.CLOSE(12) ; close file because we already consumed first byte
void c64.OPEN() ; re-open the file void cbm.OPEN() ; re-open the file
void c64.CHKIN(12) void cbm.CHKIN(12)
return true return true
} }
} }
@ -280,9 +291,6 @@ close_end:
sub f_read(uword bufferpointer, uword num_bytes) -> uword { sub f_read(uword bufferpointer, uword num_bytes) -> uword {
; -- read from the currently open file, up to the given number of bytes. ; -- 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) ; returns the actual number of bytes read. (checks for End-of-file and error conditions)
; NOTE: on systems with banked ram (such as Commander X16) this routine DOES NOT
; automatically load into subsequent banks if it reaches a bank boundary!
; Consider using cx16diskio.f_read() on X16.
if not iteration_in_progress or not num_bytes if not iteration_in_progress or not num_bytes
return 0 return 0
@ -295,14 +303,14 @@ close_end:
sta m_in_buffer+2 sta m_in_buffer+2
}} }}
while num_bytes { while num_bytes {
if c64.READST() { if cbm.READST() {
f_close() f_close()
if c64.READST() & $40 ; eof? if cbm.READST() & $40 ; eof?
return list_blocks ; number of bytes read return list_blocks ; number of bytes read
return 0 ; error. return 0 ; error.
} }
%asm {{ %asm {{
jsr c64.CHRIN jsr cbm.CHRIN
m_in_buffer sta $ffff m_in_buffer sta $ffff
inc m_in_buffer+1 inc m_in_buffer+1
bne + bne +
@ -317,12 +325,11 @@ m_in_buffer sta $ffff
sub f_read_all(uword bufferpointer) -> uword { sub f_read_all(uword bufferpointer) -> uword {
; -- read the full contents of the file, returns number of bytes read. ; -- read the full contents of the file, returns number of bytes read.
; Note: Consider using cx16diskio.f_read_all() on X16!
if not iteration_in_progress if not iteration_in_progress
return 0 return 0
uword total_read = 0 uword total_read = 0
while not c64.READST() { while not cbm.READST() {
cx16.r0 = f_read(bufferpointer, 256) cx16.r0 = f_read(bufferpointer, 256)
total_read += cx16.r0 total_read += cx16.r0
bufferpointer += cx16.r0 bufferpointer += cx16.r0
@ -340,9 +347,9 @@ m_in_buffer sta $ffff
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
ldx #12 ldx #12
jsr c64.CHKIN ; use channel 12 again for input jsr cbm.CHKIN ; use channel 12 again for input
ldy #0 ldy #0
_loop jsr c64.CHRIN _loop jsr cbm.CHRIN
sta (P8ZP_SCRATCH_W1),y sta (P8ZP_SCRATCH_W1),y
beq _end beq _end
iny iny
@ -357,12 +364,11 @@ _end rts
}} }}
} }
sub f_close() { sub f_close() {
; -- end an iterative file loading session (close channels). ; -- end an iterative file loading session (close channels).
if iteration_in_progress { if iteration_in_progress {
c64.CLRCHN() cbm.CLRCHN()
c64.CLOSE(12) cbm.CLOSE(12)
iteration_in_progress = false iteration_in_progress = false
} }
} }
@ -370,16 +376,16 @@ _end rts
; ----- iterative file writing functions (uses io channel 13) ----- ; ----- iterative file writing functions (uses io channel 13) -----
sub f_open_w(ubyte drivenumber, uword filenameptr) -> bool { sub f_open_w(uword filenameptr) -> bool {
; -- open a file for iterative writing with f_write ; -- open a file for iterative writing with f_write
f_close_w() f_close_w()
c64.SETNAM(string.length(filenameptr), filenameptr) cbm.SETNAM(string.length(filenameptr), filenameptr)
c64.SETLFS(13, drivenumber, 1) cbm.SETLFS(13, drivenumber, 1)
void c64.OPEN() ; open 13,8,1,"filename" void cbm.OPEN() ; open 13,8,1,"filename"
if_cc { if_cc {
c64.CHKOUT(13) ; use #13 as output channel cbm.CHKOUT(13) ; use #13 as output channel
return not c64.READST() return not cbm.READST()
} }
f_close_w() f_close_w()
return false return false
@ -388,39 +394,39 @@ _end rts
sub f_write(uword bufferpointer, uword num_bytes) -> bool { sub f_write(uword bufferpointer, uword num_bytes) -> bool {
; -- write the given number of bytes to the currently open file ; -- write the given number of bytes to the currently open file
if num_bytes!=0 { if num_bytes!=0 {
c64.CHKOUT(13) ; use #13 as output channel again cbm.CHKOUT(13) ; use #13 as output channel again
repeat num_bytes { repeat num_bytes {
c64.CHROUT(@(bufferpointer)) cbm.CHROUT(@(bufferpointer))
bufferpointer++ bufferpointer++
} }
return not c64.READST() return not cbm.READST()
} }
return true return true
} }
sub f_close_w() { sub f_close_w() {
; -- end an iterative file writing session (close channels). ; -- end an iterative file writing session (close channels).
c64.CLRCHN() cbm.CLRCHN()
c64.CLOSE(13) cbm.CLOSE(13)
} }
; ---- other functions ---- ; ---- other functions ----
sub status(ubyte drivenumber) -> uword { sub status() -> uword {
; -- retrieve the disk drive's current status message ; -- retrieve the disk drive's current status message
uword messageptr = &list_filename uword messageptr = &list_filename
c64.SETNAM(0, list_filename) cbm.SETNAM(0, list_filename)
c64.SETLFS(15, drivenumber, 15) cbm.SETLFS(15, drivenumber, 15)
void c64.OPEN() ; open 15,8,15 void cbm.OPEN() ; open 15,8,15
if_cs if_cs
goto io_error goto io_error
void c64.CHKIN(15) ; use #15 as input channel void cbm.CHKIN(15) ; use #15 as input channel
if_cs if_cs
goto io_error goto io_error
while not c64.READST() { while not cbm.READST() {
cx16.r5L = c64.CHRIN() cx16.r5L = cbm.CHRIN()
if cx16.r5L=='\r' or cx16.r5L=='\n' if cx16.r5L=='\r' or cx16.r5L=='\n'
break break
@(messageptr) = cx16.r5L @(messageptr) = cx16.r5L
@ -429,8 +435,8 @@ _end rts
@(messageptr) = 0 @(messageptr) = 0
done: done:
c64.CLRCHN() ; restore default i/o devices cbm.CLRCHN() ; restore default i/o devices
c64.CLOSE(15) cbm.CLOSE(15)
return list_filename return list_filename
io_error: io_error:
@ -438,9 +444,9 @@ io_error:
goto done goto done
} }
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> bool { sub save(uword filenameptr, uword address, uword size) -> bool {
c64.SETNAM(string.length(filenameptr), filenameptr) cbm.SETNAM(string.length(filenameptr), filenameptr)
c64.SETLFS(1, drivenumber, 0) cbm.SETLFS(1, drivenumber, 0)
uword @shared end_address = address + size uword @shared end_address = address + size
cx16.r0L = 0 cx16.r0L = 0
@ -453,17 +459,17 @@ io_error:
lda #<P8ZP_SCRATCH_W1 lda #<P8ZP_SCRATCH_W1
ldx end_address ldx end_address
ldy end_address+1 ldy end_address+1
jsr c64.SAVE jsr cbm.SAVE
php php
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
plp plp
}} }}
if_cc if_cc
cx16.r0L = c64.READST()==0 cx16.r0L = cbm.READST()==0
c64.CLRCHN() cbm.CLRCHN()
c64.CLOSE(1) cbm.CLOSE(1)
return cx16.r0L return cx16.r0L
} }
@ -474,99 +480,77 @@ io_error:
; If you specify a custom address_override, the first 2 bytes in the file are ignored ; If you specify a custom address_override, the first 2 bytes in the file are ignored
; and the rest is loaded at the given location in memory. ; and the rest is loaded at the given location in memory.
; Returns the end load address+1 if successful or 0 if a load error occurred. ; Returns the end load address+1 if successful or 0 if a load error occurred.
; NOTE: when the load is larger than 64Kb and/or spans multiple RAM banks sub load(uword filenameptr, uword address_override) -> uword {
; (which is possible on the Commander X16), the returned size is not correct, cbm.SETNAM(string.length(filenameptr), filenameptr)
; because it doesn't take the number of ram banks into account.
; Also consider using cx16diskio.load() instead on the Commander X16.
sub load(ubyte drivenumber, uword filenameptr, uword address_override) -> uword {
return internal_load_routine(drivenumber, filenameptr, address_override, false)
}
; Use kernal LOAD routine to load the given file in memory.
; INCLUDING the first 2 bytes in the file: no program header is assumed in the file.
; This is different from Basic's LOAD instruction which always skips the first two bytes.
; The load address is mandatory.
; Returns the end load address+1 if successful or 0 if a load error occurred.
; NOTE: when the load is larger than 64Kb and/or spans multiple RAM banks
; (which is possible on the Commander X16), the returned size is not correct,
; because it doesn't take the number of ram banks into account.
; Also consider using cx16diskio.load_raw() instead on the Commander X16.
sub load_raw(ubyte drivenumber, uword filenameptr, uword address) -> uword {
if sys.target==16 ; are we on commander X16?
return internal_load_routine(drivenumber, filenameptr, address, true)
; fallback to reading the 2 header bytes separately
if not f_open(drivenumber, filenameptr)
return 0
cx16.r1 = f_read(address, 2)
f_close()
if cx16.r1!=2
return 0
address += 2
return load(drivenumber, filenameptr, address)
}
; Internal routine, only to be used on Commander X16 platform if headerless=true,
; because this routine uses kernal support for that to load headerless files.
; On C64 it will always be called with headerless=false.
sub internal_load_routine(ubyte drivenumber, uword filenameptr, uword address_override, bool headerless) -> uword {
c64.SETNAM(string.length(filenameptr), filenameptr)
ubyte secondary = 1 ubyte secondary = 1
cx16.r1 = 0 cx16.r1 = 0
if address_override if address_override
secondary = 0 secondary = 0
if headerless cbm.SETLFS(1, drivenumber, secondary)
secondary |= %00000010 ; activate cx16 kernal headerless load support
c64.SETLFS(1, drivenumber, secondary)
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
lda #0 lda #0
ldx address_override ldx address_override
ldy address_override+1 ldy address_override+1
jsr c64.LOAD jsr cbm.LOAD
bcs + bcs +
stx cx16.r1 stx cx16.r1
sty cx16.r1+1 sty cx16.r1+1
+ ldx P8ZP_SCRATCH_REG + ldx P8ZP_SCRATCH_REG
}} }}
c64.CLRCHN() cbm.CLRCHN()
c64.CLOSE(1) cbm.CLOSE(1)
return cx16.r1 return cx16.r1
} }
sub delete(ubyte drivenumber, uword filenameptr) { ; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
; No program header is assumed in the file. Everything is loaded.
; See comments on load() for more details.
sub load_raw(uword filenameptr, uword address) -> uword {
; read the 2 header bytes separately to skip them
if not f_open(filenameptr)
return 0
cx16.r1 = f_read(address, 2)
f_close()
if cx16.r1!=2
return 0
address += 2
return load(filenameptr, address)
}
sub delete(uword filenameptr) {
; -- delete a file on the drive ; -- delete a file on the drive
list_filename[0] = 's' list_filename[0] = 's'
list_filename[1] = ':' list_filename[1] = ':'
ubyte flen = string.copy(filenameptr, &list_filename+2) ubyte flen = string.copy(filenameptr, &list_filename+2)
c64.SETNAM(flen+2, list_filename) cbm.SETNAM(flen+2, list_filename)
c64.SETLFS(1, drivenumber, 15) cbm.SETLFS(1, drivenumber, 15)
void c64.OPEN() void cbm.OPEN()
c64.CLRCHN() cbm.CLRCHN()
c64.CLOSE(1) cbm.CLOSE(1)
} }
sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) { sub rename(uword oldfileptr, uword newfileptr) {
; -- rename a file on the drive ; -- rename a file on the drive
list_filename[0] = 'r' list_filename[0] = 'r'
list_filename[1] = ':' list_filename[1] = ':'
ubyte flen_new = string.copy(newfileptr, &list_filename+2) ubyte flen_new = string.copy(newfileptr, &list_filename+2)
list_filename[flen_new+2] = '=' list_filename[flen_new+2] = '='
ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new) ubyte flen_old = string.copy(oldfileptr, &list_filename+3+flen_new)
c64.SETNAM(3+flen_new+flen_old, list_filename) cbm.SETNAM(3+flen_new+flen_old, list_filename)
c64.SETLFS(1, drivenumber, 15) cbm.SETLFS(1, drivenumber, 15)
void c64.OPEN() void cbm.OPEN()
c64.CLRCHN() cbm.CLRCHN()
c64.CLOSE(1) cbm.CLOSE(1)
} }
sub send_command(ubyte drivenumber, uword commandptr) { sub send_command(uword commandptr) {
; -- send a dos command to the drive ; -- send a dos command to the drive
c64.SETNAM(string.length(commandptr), commandptr) cbm.SETNAM(string.length(commandptr), commandptr)
c64.SETLFS(15, drivenumber, 15) cbm.SETLFS(15, drivenumber, 15)
void c64.OPEN() void cbm.OPEN()
c64.CLRCHN() cbm.CLRCHN()
c64.CLOSE(15) cbm.CLOSE(15)
} }
} }

View File

@ -1,6 +1,6 @@
floats { floats {
; the floating point functions shared across compiler targets ; the floating point functions shared across compiler targets
%option merge %option merge, no_symbol_prefixing
sub print_f(float value) { sub print_f(float value) {
; ---- prints the floating point value (without a newline). ; ---- prints the floating point value (without a newline).
@ -15,7 +15,7 @@ sub print_f(float value) {
ldy #0 ldy #0
- lda (P8ZP_SCRATCH_W1),y - lda (P8ZP_SCRATCH_W1),y
beq + beq +
jsr c64.CHROUT jsr cbm.CHROUT
iny iny
bne - bne -
+ ldx floats_store_reg + ldx floats_store_reg
@ -39,18 +39,6 @@ sub pow(float value, float power) -> float {
}} }}
} }
sub fabs(float value) -> float {
%asm {{
stx P8ZP_SCRATCH_REG
lda #<value
ldy #>value
jsr MOVFM
jsr ABS
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub sin(float angle) -> float { sub sin(float angle) -> float {
%asm {{ %asm {{
lda #<angle lda #<angle
@ -128,18 +116,6 @@ sub log2(float value) -> float {
}} }}
} }
sub sqrt(float value) -> float {
%asm {{
lda #<value
ldy #>value
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr SQR
ldx P8ZP_SCRATCH_REG
rts
}}
}
sub rad(float angle) -> float { sub rad(float angle) -> float {
; -- convert degrees to radians (d * pi / 180) ; -- convert degrees to radians (d * pi / 180)
%asm {{ %asm {{
@ -237,4 +213,27 @@ sub rndseedf(float seed) {
}} }}
} }
sub minf(float f1, float f2) -> float {
if f1<f2
return f1
return f2
}
sub maxf(float f1, float f2) -> float {
if f1>f2
return f1
return f2
}
sub clampf(float value, float minimum, float maximum) -> float {
if value>maximum
value=maximum
if value>minimum
return value
return minimum
}
} }

View File

@ -51,8 +51,7 @@ multiply_bytes_into_word .proc
multiply_words .proc multiply_words .proc
; -- multiply two 16-bit words into a 32-bit result (signed and unsigned) ; -- 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 ; 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) ; output: multiply_words.result 4-bytes/32-bits product, LSB order (low-to-high) low 16 bits also in AY.
; clobbers: A
sta P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1 sty P8ZP_SCRATCH_W2+1
@ -79,6 +78,8 @@ mult16 lda #0
dex dex
bne - bne -
ldx P8ZP_SCRATCH_REG ldx P8ZP_SCRATCH_REG
lda result
ldy result+1
rts rts
result .byte 0,0,0,0 result .byte 0,0,0,0

View File

@ -1,6 +1,8 @@
; Internal Math library routines - always included by the compiler ; Internal Math library routines - always included by the compiler
math { math {
%option no_symbol_prefixing
%asminclude "library:math.asm" %asminclude "library:math.asm"
asmsub sin8u(ubyte angle @A) clobbers(Y) -> ubyte @A { 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 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
}}
}
} }

View File

@ -86,16 +86,15 @@ func_all_w_stack .proc
abs_b_stack .proc abs_b_stack .proc
; -- push abs(A) on stack (as unsigned word) ; -- push abs(A) on stack (as unsigned word)
jsr abs_b_into_AY jsr abs_b_into_A
sta P8ESTACK_LO,x sta P8ESTACK_LO,x
stz P8ESTACK_HI,x stz P8ESTACK_HI,x
dex dex
rts rts
.pend .pend
abs_b_into_AY .proc abs_b_into_A .proc
; -- AY = abs(A) (abs always returns unsigned word) ; -- A = abs(A)
ldy #0
cmp #0 cmp #0
bmi + bmi +
rts rts
@ -556,3 +555,111 @@ func_pokew .proc
sta (P8ZP_SCRATCH_W1),y sta (P8ZP_SCRATCH_W1),y
rts rts
.pend .pend
func_clamp_byte .proc
; signed value in A, result in A
; minimum in P8ZP_SCRATCH_W1
; maximum in P8ZP_SCRATCH_W1+1
tay
sec
sbc P8ZP_SCRATCH_W1+1
bvc +
eor #$80
+ bmi +
lda P8ZP_SCRATCH_W1+1
tay
jmp ++
+ tya
+ sec
sbc P8ZP_SCRATCH_W1
bvc +
eor #$80
+ bmi +
tya
rts
+ lda P8ZP_SCRATCH_W1
rts
.pend
func_clamp_ubyte .proc
; value in A, result in A
; minimum in P8ZP_SCRATCH_W1
; maximum in P8ZP_SCRATCH_W1+1
cmp P8ZP_SCRATCH_W1+1
bcc +
lda P8ZP_SCRATCH_W1+1
+ cmp P8ZP_SCRATCH_W1
bcc +
rts
+ lda P8ZP_SCRATCH_W1
rts
.pend
func_clamp_word .proc
; signed value in AY, result in AY
; minimum in P8ZP_SCRATCH_W1
; maximum in P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
ldy P8ZP_SCRATCH_W2+1
lda P8ZP_SCRATCH_W2
cmp P8ZP_SCRATCH_B1
tya
sbc P8ZP_SCRATCH_REG
bvc +
eor #$80
+ bpl +
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
+ ldy P8ZP_SCRATCH_W1+1
lda P8ZP_SCRATCH_W1
cmp P8ZP_SCRATCH_B1
tya
sbc P8ZP_SCRATCH_REG
bvc +
eor #$80
+ bpl +
ldy P8ZP_SCRATCH_REG
lda P8ZP_SCRATCH_B1
rts
+ ldy P8ZP_SCRATCH_W1+1
lda P8ZP_SCRATCH_W1
rts
.pend
func_clamp_uword .proc
; value in AY, result in AY
; minimum in P8ZP_SCRATCH_W1
; maximum in P8ZP_SCRATCH_W2
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
cpy P8ZP_SCRATCH_W2+1
bcc ++
bne +
cmp P8ZP_SCRATCH_W2
bcc ++
+ beq +
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
sta P8ZP_SCRATCH_B1
sty P8ZP_SCRATCH_REG
+ ldy P8ZP_SCRATCH_REG
lda P8ZP_SCRATCH_B1
cpy P8ZP_SCRATCH_W1+1
bcc ++
bne +
cmp P8ZP_SCRATCH_W1
bcc ++
+ beq +
ldy P8ZP_SCRATCH_REG
lda P8ZP_SCRATCH_B1
rts
+ ldy P8ZP_SCRATCH_W1+1
lda P8ZP_SCRATCH_W1
rts
.pend

View File

@ -167,9 +167,8 @@ mul_word .proc
lda P8ESTACK_LO+1,x lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x ldy P8ESTACK_HI+1,x
jsr math.multiply_words jsr math.multiply_words
lda math.multiply_words.result
sta P8ESTACK_LO+1,x sta P8ESTACK_LO+1,x
lda math.multiply_words.result+1 tya
sta P8ESTACK_HI+1,x sta P8ESTACK_HI+1,x
rts rts
.pend .pend

View File

@ -1,6 +1,8 @@
; Internal library routines - always included by the compiler ; Internal library routines - always included by the compiler
prog8_lib { prog8_lib {
%option no_symbol_prefixing
%asminclude "library:prog8_lib.asm" %asminclude "library:prog8_lib.asm"
%asminclude "library:prog8_funcs.asm" %asminclude "library:prog8_funcs.asm"
} }

View File

@ -1,6 +1,7 @@
; 0-terminated string manipulation routines. ; 0-terminated string manipulation routines.
string { string {
%option no_symbol_prefixing
asmsub length(uword string @AY) clobbers(A) -> ubyte @Y { asmsub length(uword string @AY) clobbers(A) -> ubyte @Y {
; Returns the number of bytes in the string. ; Returns the number of bytes in the string.
@ -127,7 +128,7 @@ _startloop dey
}} }}
} }
asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, ubyte @Pc { asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, bool @Pc {
; Locates the first position of the given character in the string, ; Locates the first position of the given character in the string,
; returns Carry set if found + index in A, or A=0 + Carry clear if not found. ; returns Carry set if found + index in A, or A=0 + Carry clear if not found.
%asm {{ %asm {{

View File

@ -3,6 +3,7 @@
%import textio %import textio
test_stack { test_stack {
%option no_symbol_prefixing
asmsub test() { asmsub test() {
%asm {{ %asm {{

View File

@ -106,19 +106,21 @@ sub str_uwhex (uword value) {
sub str_uw0 (uword value) { sub str_uw0 (uword value) {
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total) ; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
ubyte tenthousands = (value / 10000) as ubyte uword value2 = value/10
value -= 10000*tenthousands ubyte digits = value-value2*10 as ubyte
ubyte thousands = (value / 1000) as ubyte uword value3 = value2/10
value -= 1000*thousands ubyte tens = value2-value3*10 as ubyte
ubyte hundreds = (value / 100) as ubyte uword value4 = value3/10
value -= 100 as uword * hundreds ubyte hundreds = value3-value4*10 as ubyte
ubyte tens = (value / 10) as ubyte uword value5 = value4/10
value -= 10*tens 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[0] = tenthousands+'0'
string_out[1] = thousands+'0' string_out[1] = thousands+'0'
string_out[2] = hundreds+'0' string_out[2] = hundreds+'0'
string_out[3] = tens+'0' string_out[3] = tens+'0'
string_out[4] = value as ubyte + '0' string_out[4] = digits+'0'
string_out[5] = 0 string_out[5] = 0
} }
@ -139,14 +141,16 @@ sub str_w (word value) {
} }
sub internal_str_uw(uword value, uword out_ptr) { sub internal_str_uw(uword value, uword out_ptr) {
ubyte tenthousands = (value / 10000) as ubyte uword value2 = value/10
value -= 10000*tenthousands ubyte digits = value-value2*10 as ubyte
ubyte thousands = (value / 1000) as ubyte uword value3 = value2/10
value -= 1000*thousands ubyte tens = value2-value3*10 as ubyte
ubyte hundreds = (value / 100) as ubyte uword value4 = value3/10
value -= 100 as uword * hundreds ubyte hundreds = value3-value4*10 as ubyte
ubyte tens = (value / 10) as ubyte uword value5 = value4/10
value -= 10*tens ubyte thousands = value4-value5*10 as ubyte
uword value6 = value5/10
ubyte tenthousands = value5-value6*10 as ubyte
if tenthousands if tenthousands
goto output_tenthousands goto output_tenthousands
if thousands if thousands
@ -169,7 +173,7 @@ output_tens:
@(out_ptr) = tens+'0' @(out_ptr) = tens+'0'
out_ptr++ out_ptr++
output_ones: output_ones:
@(out_ptr) = value as ubyte + '0' @(out_ptr) = digits+'0'
out_ptr++ out_ptr++
@(out_ptr) = 0 @(out_ptr) = 0
} }

View File

@ -0,0 +1 @@
; there is no diskio yet for the VM target.

View File

@ -25,14 +25,6 @@ sub pow(float value, float power) -> float {
}} }}
} }
sub fabs(float value) -> float {
%ir {{
loadm.f fr0,floats.fabs.value
fabs.f fr0,fr0
returnr.f fr0
}}
}
sub sin(float angle) -> float { sub sin(float angle) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.sin.angle loadm.f fr0,floats.sin.angle
@ -81,14 +73,6 @@ sub log2(float value) -> float {
}} }}
} }
sub sqrt(float value) -> float {
%ir {{
loadm.f fr0,floats.sqrt.value
sqrt.f fr0,fr0
returnr.f fr0
}}
}
sub rad(float angle) -> float { sub rad(float angle) -> float {
; -- convert degrees to radians (d * pi / 180) ; -- convert degrees to radians (d * pi / 180)
return angle * PI / 180.0 return angle * PI / 180.0
@ -139,4 +123,27 @@ sub rndseedf(float seed) {
}} }}
} }
sub minf(float f1, float f2) -> float {
if f1<f2
return f1
return f2
}
sub maxf(float f1, float f2) -> float {
if f1>f2
return f1
return f2
}
sub clampf(float value, float minimum, float maximum) -> float {
if value<minimum
value=minimum
if value<maximum
return value
return maximum
}
} }

View File

@ -182,4 +182,61 @@ math {
return 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
}}
}
} }

View File

@ -218,4 +218,43 @@ cx16 {
&byte r13sH = $ff1d &byte r13sH = $ff1d
&byte r14sH = $ff1f &byte r14sH = $ff1f
&byte r15sH = $ff21 &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]
}
} }

View File

@ -1 +0,0 @@
8.14-dev

View File

@ -20,8 +20,10 @@ import kotlin.system.exitProcess
fun main(args: Array<String>) { fun main(args: Array<String>) {
val buildVersion = object {}.javaClass.getResource("/version.txt")?.readText()?.trim() println("\nProg8 compiler v${prog8.buildversion.VERSION} by Irmen de Jong (irmen@razorvine.net)")
println("\nProg8 compiler v$buildVersion 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") println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
val succes = compileMain(args) val succes = compileMain(args)
@ -48,11 +50,11 @@ private fun compileMain(args: Array<String>): Boolean {
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results") 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 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 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}')").default(C64Target.NAME) 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 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 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 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 (0=keep active), on other systems it is ignored.")
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 moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999) val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
try { try {
@ -78,8 +80,20 @@ private fun compileMain(args: Array<String>): Boolean {
if(srcdirs.firstOrNull()!=".") if(srcdirs.firstOrNull()!=".")
srcdirs.add(0, ".") srcdirs.add(0, ".")
if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME, AtariTarget.NAME, VMTarget.NAME)) { if(startVm==null) {
System.err.println("Invalid compilation target: $compilationTarget") 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 return false
} }
@ -126,10 +140,10 @@ private fun compileMain(args: Array<String>): Boolean {
quietAssembler == true, quietAssembler == true,
asmListfile == true, asmListfile == true,
experimentalCodegen == true, experimentalCodegen == true,
varsHigh == true, varsHighBank,
useNewExprCode == true, compilationTarget!!,
compilationTarget,
evalStackAddr, evalStackAddr,
splitWordArrays == true,
processedSymbols, processedSymbols,
srcdirs, srcdirs,
outputPath outputPath
@ -178,6 +192,10 @@ private fun compileMain(args: Array<String>): Boolean {
} }
} else { } 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) { for(filepathRaw in moduleFiles) {
val filepath = pathFrom(filepathRaw).normalize() val filepath = pathFrom(filepathRaw).normalize()
val compilationResult: CompilationResult val compilationResult: CompilationResult
@ -191,10 +209,10 @@ private fun compileMain(args: Array<String>): Boolean {
quietAssembler == true, quietAssembler == true,
asmListfile == true, asmListfile == true,
experimentalCodegen == true, experimentalCodegen == true,
varsHigh == true, varsHighBank,
useNewExprCode == true, compilationTarget!!,
compilationTarget,
evalStackAddr, evalStackAddr,
splitWordArrays == true,
processedSymbols, processedSymbols,
srcdirs, srcdirs,
outputPath outputPath

View File

@ -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.2-SNAPSHOT"
const val GIT_REVISION = 3964
const val GIT_SHA = "aaa30e4a583fa7c8da61998cf7cbb2a60b52afb0"
const val GIT_DATE = "2023-07-16T21:16:18Z"
const val GIT_BRANCH = "master"
const val BUILD_DATE = "2023-07-16T21:16:23Z"
const val BUILD_UNIX_TIME = 1689542183583L
const val DIRTY = 1

View File

@ -7,10 +7,7 @@ import prog8.ast.base.SyntaxError
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.code.core.* import prog8.code.core.*
import kotlin.math.abs import kotlin.math.*
import kotlin.math.sign
import kotlin.math.sqrt
private typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteral private typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteral
@ -19,12 +16,26 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
"len" to ::builtinLen, "len" to ::builtinLen,
"sizeof" to ::builtinSizeof, "sizeof" to ::builtinSizeof,
"sgn" to ::builtinSgn, "sgn" to ::builtinSgn,
"sqrt16" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } }, "sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } },
"sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } },
"sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) { sqrt(it) } },
"any" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) }, "any" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
"all" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) }, "all" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
"lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } }, "lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } },
"msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} }, "msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} },
"mkword" to ::builtinMkword "mkword" to ::builtinMkword,
"clamp__ubyte" to ::builtinClampUByte,
"clamp__byte" to ::builtinClampByte,
"clamp__uword" to ::builtinClampUWord,
"clamp__word" to ::builtinClampWord,
"min__ubyte" to ::builtinMinUByte,
"min__byte" to ::builtinMinByte,
"min__uword" to ::builtinMinUWord,
"min__word" to ::builtinMinWord,
"max__ubyte" to ::builtinMaxUByte,
"max__byte" to ::builtinMaxByte,
"max__uword" to ::builtinMaxUWord,
"max__word" to ::builtinMaxWord
) )
private fun builtinAny(array: List<Double>): Double = if(array.any { it!=0.0 }) 1.0 else 0.0 private fun builtinAny(array: List<Double>): Double = if(array.any { it!=0.0 }) 1.0 else 0.0
@ -59,6 +70,16 @@ private fun oneIntArgOutputInt(args: List<Expression>, position: Position, progr
return NumericLiteral.optimalInteger(function(integer).toInt(), args[0].position) return NumericLiteral.optimalInteger(function(integer).toInt(), args[0].position)
} }
private fun oneFloatArgOutputFloat(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Double): NumericLiteral {
if(args.size!=1)
throw SyntaxError("built-in function requires one float argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
if(constval.type != DataType.FLOAT)
throw SyntaxError("built-in function requires one float argument", position)
return NumericLiteral(DataType.FLOAT, function(constval.number), args[0].position)
}
private fun collectionArg(args: List<Expression>, position: Position, program: Program, function: (arg: List<Double>)->Double): NumericLiteral { private fun collectionArg(args: List<Expression>, position: Position, program: Program, function: (arg: List<Double>)->Double): NumericLiteral {
if(args.size!=1) if(args.size!=1)
throw SyntaxError("builtin function requires one non-scalar argument", position) throw SyntaxError("builtin function requires one non-scalar argument", position)
@ -156,3 +177,116 @@ private fun builtinSgn(args: List<Expression>, position: Position, program: Prog
val constval = args[0].constValue(program) ?: throw NotConstArgumentException() val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
return NumericLiteral(DataType.BYTE, constval.number.sign, position) return NumericLiteral(DataType.BYTE, constval.number.sign, position)
} }
private fun builtinMinByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("min requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = min(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.BYTE, result.toDouble(), position)
}
private fun builtinMinUByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("min requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = min(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.UBYTE, result.toDouble(), position)
}
private fun builtinMinWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("min requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = min(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.WORD, result.toDouble(), position)
}
private fun builtinMinUWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("min requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = min(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.UWORD, result.toDouble(), position)
}
private fun builtinMaxByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("max requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = max(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.BYTE, result.toDouble(), position)
}
private fun builtinMaxUByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("max requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = max(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.UBYTE, result.toDouble(), position)
}
private fun builtinMaxWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("max requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = max(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.WORD, result.toDouble(), position)
}
private fun builtinMaxUWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("max requires 2 arguments", position)
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
val result = max(val1.number.toInt(), val2.number.toInt())
return NumericLiteral(DataType.UWORD, result.toDouble(), position)
}
private fun builtinClampUByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if(args.size!=3)
throw SyntaxError("clamp requires 3 arguments", position)
val value = args[0].constValue(program) ?: throw NotConstArgumentException()
val minimum = args[1].constValue(program) ?: throw NotConstArgumentException()
val maximum = args[2].constValue(program) ?: throw NotConstArgumentException()
val result = min(max(value.number, minimum.number), maximum.number)
return NumericLiteral(DataType.UBYTE, result, position)
}
private fun builtinClampByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if(args.size!=3)
throw SyntaxError("clamp requires 3 arguments", position)
val value = args[0].constValue(program) ?: throw NotConstArgumentException()
val minimum = args[1].constValue(program) ?: throw NotConstArgumentException()
val maximum = args[2].constValue(program) ?: throw NotConstArgumentException()
val result = min(max(value.number, minimum.number), maximum.number)
return NumericLiteral(DataType.BYTE, result, position)
}
private fun builtinClampUWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if(args.size!=3)
throw SyntaxError("clamp requires 3 arguments", position)
val value = args[0].constValue(program) ?: throw NotConstArgumentException()
val minimum = args[1].constValue(program) ?: throw NotConstArgumentException()
val maximum = args[2].constValue(program) ?: throw NotConstArgumentException()
val result = min(max(value.number, minimum.number), maximum.number)
return NumericLiteral(DataType.UWORD, result, position)
}
private fun builtinClampWord(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if(args.size!=3)
throw SyntaxError("clamp requires 3 arguments", position)
val value = args[0].constValue(program) ?: throw NotConstArgumentException()
val minimum = args[1].constValue(program) ?: throw NotConstArgumentException()
val maximum = args[2].constValue(program) ?: throw NotConstArgumentException()
val result = min(max(value.number, minimum.number), maximum.number)
return NumericLiteral(DataType.WORD, result, position)
}

View File

@ -4,12 +4,11 @@ import com.github.michaelbull.result.onFailure
import prog8.ast.IBuiltinFunctions import prog8.ast.IBuiltinFunctions
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.AstException import prog8.ast.base.AstException
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.Expression import prog8.ast.expressions.Expression
import prog8.ast.expressions.NumericLiteral import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Directive import prog8.ast.statements.Directive
import prog8.code.SymbolTableMaker import prog8.code.SymbolTableMaker
import prog8.code.ast.* import prog8.code.ast.PtProgram
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.* import prog8.code.target.*
import prog8.codegen.vm.VmCodeGen import prog8.codegen.vm.VmCodeGen
@ -36,10 +35,10 @@ class CompilerArguments(val filepath: Path,
val quietAssembler: Boolean, val quietAssembler: Boolean,
val asmListfile: Boolean, val asmListfile: Boolean,
val experimentalCodegen: Boolean, val experimentalCodegen: Boolean,
val varsHigh: Boolean, val varsHighBank: Int?,
val useNewExprCode: Boolean,
val compilationTarget: String, val compilationTarget: String,
val evalStackBaseAddress: UInt?, val evalStackBaseAddress: UInt?,
val splitWordArrays: Boolean,
val symbolDefs: Map<String, String>, val symbolDefs: Map<String, String>,
val sourceDirs: List<String> = emptyList(), val sourceDirs: List<String> = emptyList(),
val outputDir: Path = Path(""), val outputDir: Path = Path(""),
@ -77,9 +76,9 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
asmQuiet = args.quietAssembler asmQuiet = args.quietAssembler
asmListfile = args.asmListfile asmListfile = args.asmListfile
experimentalCodegen = args.experimentalCodegen experimentalCodegen = args.experimentalCodegen
varsHigh = args.varsHigh varsHighBank = args.varsHighBank
useNewExprCode = args.useNewExprCode
evalStackBaseAddress = args.evalStackBaseAddress evalStackBaseAddress = args.evalStackBaseAddress
splitWordArrays = args.splitWordArrays
outputDir = args.outputDir.normalize() outputDir = args.outputDir.normalize()
symbolDefs = args.symbolDefs symbolDefs = args.symbolDefs
} }
@ -281,10 +280,9 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
val launcherTypeStr = launcherDirective?.args?.single()?.name?.uppercase() val launcherTypeStr = launcherDirective?.args?.single()?.name?.uppercase()
val zpoption: String? = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" } val zpoption: String? = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
as? Directive)?.args?.single()?.name?.uppercase() as? Directive)?.args?.single()?.name?.uppercase()
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" } val allOptions = program.modules.flatMap { it.options() }.toSet()
.flatMap { (it as Directive).args }.toSet() val floatsEnabled = "enable_floats" in allOptions
val floatsEnabled = allOptions.any { it.name == "enable_floats" } val noSysInit = "no_sysinit" in allOptions
val noSysInit = allOptions.any { it.name == "no_sysinit" }
val zpType: ZeropageType = val zpType: ZeropageType =
if (zpoption == null) if (zpoption == null)
if (floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE if (floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
@ -370,8 +368,8 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
// keep optimizing expressions and statements until no more steps remain // keep optimizing expressions and statements until no more steps remain
val optsDone1 = program.simplifyExpressions(errors, compTarget) val optsDone1 = program.simplifyExpressions(errors, compTarget)
val optsDone2 = program.splitBinaryExpressions(compilerOptions) val optsDone2 = program.splitBinaryExpressions(compilerOptions)
val optsDone3 = program.optimizeStatements(errors, functions, compTarget) val optsDone3 = program.optimizeStatements(errors, functions, compilerOptions)
val optsDone4 = program.inlineSubroutines() val optsDone4 = program.inlineSubroutines(compilerOptions)
program.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away program.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
errors.report() errors.report()
if (optsDone1 + optsDone2 + optsDone3 + optsDone4 == 0) if (optsDone1 + optsDone2 + optsDone3 + optsDone4 == 0)
@ -405,23 +403,12 @@ private fun createAssemblyAndAssemble(program: PtProgram,
val asmgen = if(compilerOptions.experimentalCodegen) val asmgen = if(compilerOptions.experimentalCodegen)
prog8.codegen.experimental.ExperiCodeGen() prog8.codegen.experimental.ExperiCodeGen()
else if (compilerOptions.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) 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) else if (compilerOptions.compTarget.name == VMTarget.NAME)
VmCodeGen() VmCodeGen()
else else
throw NotImplementedError("no code generator for cpu ${compilerOptions.compTarget.machine.cpu}") 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 stMaker = SymbolTableMaker(program, compilerOptions)
val symbolTable = stMaker.make() val symbolTable = stMaker.make()
val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors) val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors)
@ -433,188 +420,3 @@ private fun createAssemblyAndAssemble(program: PtProgram,
false 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)
}

View File

@ -21,6 +21,10 @@ internal class ErrorReporter: IErrorReporter {
messages.add(CompilerMessage(MessageSeverity.WARNING, msg, position)) messages.add(CompilerMessage(MessageSeverity.WARNING, msg, position))
} }
override fun undefined(symbol: List<String>, position: Position) {
err("undefined symbol: ${symbol.joinToString(".")}", position)
}
override fun report() { override fun report() {
var numErrors = 0 var numErrors = 0
var numWarnings = 0 var numWarnings = 0

View File

@ -32,6 +32,7 @@ class ModuleImporter(private val program: Program,
val programPath = path.resolve(normalizedFilePath) val programPath = path.resolve(normalizedFilePath)
if(programPath.exists()) { if(programPath.exists()) {
println("Compiling program ${Path("").absolute().relativize(programPath)}") println("Compiling program ${Path("").absolute().relativize(programPath)}")
println("Compiler target: $compilationTargetName")
val source = SourceCode.File(programPath) val source = SourceCode.File(programPath)
return Ok(importModule(source)) return Ok(importModule(source))
} }

View File

@ -1,49 +0,0 @@
package prog8.compiler.astprocessing
import prog8.ast.statements.Block
import prog8.ast.statements.Label
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.ast.walk.IAstVisitor
import prog8.code.core.ICompilationTarget
import prog8.code.target.VMTarget
class AsmInstructionNamesFinder(val target: ICompilationTarget): IAstVisitor {
val blocks = mutableSetOf<Block>()
val variables = mutableSetOf<VarDecl>()
val labels = mutableSetOf<Label>()
val subroutines = mutableSetOf<Subroutine>()
private fun isPossibleInstructionName(name: String) = name.length==3 && name.all { it.isLetter() }
fun foundAny(): Boolean = blocks.isNotEmpty() || variables.isNotEmpty() || subroutines.isNotEmpty() || labels.isNotEmpty()
override fun visit(block: Block) {
if(target.name!=VMTarget.NAME && !block.isInLibrary && isPossibleInstructionName(block.name)) {
blocks += block
}
super.visit(block)
}
override fun visit(decl: VarDecl) {
if(target.name!=VMTarget.NAME && !decl.definingModule.isLibrary && isPossibleInstructionName(decl.name)) {
variables += decl
}
super.visit(decl)
}
override fun visit(label: Label) {
if(target.name!=VMTarget.NAME && !label.definingModule.isLibrary && isPossibleInstructionName(label.name)) {
labels += label
}
super.visit(label)
}
override fun visit(subroutine: Subroutine) {
if(target.name!=VMTarget.NAME && !subroutine.definingModule.isLibrary && isPossibleInstructionName(subroutine.name)) {
subroutines += subroutine
}
super.visit(subroutine)
}
}

View File

@ -1,101 +0,0 @@
package prog8.compiler.astprocessing
import prog8.ast.Node
import prog8.ast.expressions.IdentifierReference
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
class AsmInstructionNamesReplacer(
val blocks: Set<Block>,
val subroutines: Set<Subroutine>,
val variables: Set<VarDecl>,
val labels: Set<Label>): AstWalker() {
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
val newName = identifier.nameInSource.map { ident ->
if(ident.length==3 && !identifier.definingModule.isLibrary) {
val blockTarget = blocks.firstOrNull { it.name==ident }
val subTarget = subroutines.firstOrNull {it.name==ident }
val varTarget = variables.firstOrNull { it.name==ident }
val labelTarget = labels.firstOrNull { it.name==ident}
if(blockTarget!=null || subTarget!=null || varTarget!=null || labelTarget!=null) {
"p8p_$ident"
} else
ident
} else
ident
}
return if(newName!=identifier.nameInSource)
listOf(IAstModification.ReplaceNode(identifier, IdentifierReference(newName, identifier.position), parent))
else
noModifications
}
override fun after(label: Label, parent: Node): Iterable<IAstModification> {
return if(label in labels)
listOf(IAstModification.ReplaceNode(label, Label("p8p_${label.name}", label.position), parent))
else
noModifications
}
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
return if(block in blocks)
listOf(IAstModification.ReplaceNode(block, Block("p8p_${block.name}", block.address, block.statements, block.isInLibrary, block.position), parent))
else
noModifications
}
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
return if(decl in variables)
listOf(IAstModification.ReplaceNode(decl, decl.renamed("p8p_${decl.name}"), parent))
else
noModifications
}
private val subsWithParamRefsToFix = mutableListOf<Subroutine>()
override fun applyModifications(): Int {
var count = super.applyModifications()
subsWithParamRefsToFix.forEach { subroutine ->
subroutine.statements.withIndex().reversed().forEach { (index,stmt) ->
if(stmt is VarDecl && stmt.origin==VarDeclOrigin.SUBROUTINEPARAM) {
val param = subroutine.parameters.single { it.name == stmt.name}
val decl = VarDecl.fromParameter(param)
subroutine.statements[index] = decl
decl.linkParents(subroutine)
count++
}
}
}
return count
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
val changedParams = mutableListOf<Pair<Int, SubroutineParameter>>()
subroutine.parameters.withIndex().forEach { (index, param) ->
if(param.name.length==3 && param.name.all { it.isLetter() } && !param.definingModule.isLibrary) {
changedParams.add(index to SubroutineParameter("p8p_${param.name}", param.type, param.position))
}
}
changedParams.forEach { (index, newParam) -> subroutine.parameters[index] = newParam }
val newName = if(subroutine in subroutines) "p8p_${subroutine.name}" else subroutine.name
return if(newName!=subroutine.name || changedParams.isNotEmpty()) {
val newSub = Subroutine(newName, subroutine.parameters, subroutine.returntypes,
subroutine.asmParameterRegisters, subroutine.asmReturnvaluesRegisters, subroutine.asmClobbers, subroutine.asmAddress, subroutine.isAsmSubroutine,
subroutine.inline, false, subroutine.statements, subroutine.position)
if(changedParams.isNotEmpty())
subsWithParamRefsToFix += newSub
listOf(IAstModification.ReplaceNode(subroutine, newSub, parent))
} else {
if(changedParams.isNotEmpty())
subsWithParamRefsToFix += subroutine
noModifications
}
}
}

View File

@ -12,6 +12,7 @@ import prog8.compiler.builtinFunctionReturnType
import java.io.CharConversionException import java.io.CharConversionException
import java.io.File import java.io.File
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.math.floor
/** /**
* Semantic analysis. * Semantic analysis.
@ -62,7 +63,7 @@ internal class AstChecker(private val program: Program,
override fun visit(identifier: IdentifierReference) { override fun visit(identifier: IdentifierReference) {
val stmt = identifier.targetStatement(program) val stmt = identifier.targetStatement(program)
if(stmt==null) if(stmt==null)
errors.err("undefined symbol: ${identifier.nameInSource.joinToString(".")}", identifier.position) errors.undefined(identifier.nameInSource, identifier.position)
else { else {
val target = stmt as? VarDecl val target = stmt as? VarDecl
if (target != null && target.origin == VarDeclOrigin.SUBROUTINEPARAM) { if (target != null && target.origin == VarDeclOrigin.SUBROUTINEPARAM) {
@ -114,6 +115,8 @@ internal class AstChecker(private val program: Program,
if (returnValue == null || returnValue.type != DataType.UBYTE || (returnValue.number!=0.0 && returnValue.number!=1.0)) { if (returnValue == null || returnValue.type != DataType.UBYTE || (returnValue.number!=0.0 && returnValue.number!=1.0)) {
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position) errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
} }
} else if(valueDt.isIterable && expectedReturnValues[0]==DataType.UWORD) {
// you can return a string or array when a uword (pointer) is returned
} }
else { else {
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position) errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
@ -126,8 +129,11 @@ internal class AstChecker(private val program: Program,
override fun visit(ifElse: IfElse) { override fun visit(ifElse: IfElse) {
val dt = ifElse.condition.inferType(program) val dt = ifElse.condition.inferType(program)
if(!dt.isInteger && !dt.istype(DataType.BOOL)) if(!dt.isInteger && !dt.istype(DataType.BOOL)) {
errors.err("condition value should be an integer type or bool", ifElse.condition.position) val identifier = ifElse.condition as? IdentifierReference
if(identifier==null || identifier.targetStatement(program)!=null)
errors.err("condition value should be an integer type or bool", ifElse.condition.position)
}
super.visit(ifElse) super.visit(ifElse)
} }
@ -165,7 +171,8 @@ internal class AstChecker(private val program: Program,
} }
DataType.UWORD -> { DataType.UWORD -> {
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR && if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW) iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_UW &&
iterableDt != DataType.ARRAY_UW_SPLIT)
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position) errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression) checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpression)
@ -176,7 +183,8 @@ internal class AstChecker(private val program: Program,
} }
DataType.WORD -> { DataType.WORD -> {
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD && if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD &&
iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W) iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W &&
iterableDt != DataType.ARRAY_W_SPLIT)
errors.err("word loop variable can only loop over bytes or words", forLoop.position) errors.err("word loop variable can only loop over bytes or words", forLoop.position)
} }
DataType.FLOAT -> { DataType.FLOAT -> {
@ -333,7 +341,7 @@ internal class AstChecker(private val program: Program,
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) { for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
if(param.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) { if(param.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
if (param.first.type != DataType.UBYTE && param.first.type != DataType.BYTE && param.first.type != DataType.BOOL) if (param.first.type != DataType.UBYTE && param.first.type != DataType.BYTE && param.first.type != DataType.BOOL)
err("parameter '${param.first.name}' should be (u)byte or bool") errors.err("parameter '${param.first.name}' should be (u)byte or bool", param.first.position)
} }
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) { else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD
@ -341,8 +349,8 @@ internal class AstChecker(private val program: Program,
err("parameter '${param.first.name}' should be (u)word (an address) or str") err("parameter '${param.first.name}' should be (u)word (an address) or str")
} }
else if(param.second.statusflag!=null) { else if(param.second.statusflag!=null) {
if (param.first.type != DataType.UBYTE && param.first.type != DataType.BOOL) if (param.first.type != DataType.BOOL)
err("parameter '${param.first.name}' should be bool or ubyte") errors.err("parameter '${param.first.name}' should be of type bool", param.first.position)
} }
} }
subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair -> subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair ->
@ -356,8 +364,8 @@ internal class AstChecker(private val program: Program,
err("return type #${index + 1} should be (u)word/address") err("return type #${index + 1} should be (u)word/address")
} }
else if(pair.second.statusflag!=null) { else if(pair.second.statusflag!=null) {
if (pair.first != DataType.UBYTE && pair.first != DataType.BOOL) if (pair.first != DataType.BOOL)
err("return type #${index + 1} should be bool or ubyte") err("return type #${index + 1} should be bool")
} }
} }
@ -428,7 +436,6 @@ internal class AstChecker(private val program: Program,
val statusFlagsNoCarry = subroutine.asmParameterRegisters.mapNotNull { it.statusflag }.toSet() - Statusflag.Pc val statusFlagsNoCarry = subroutine.asmParameterRegisters.mapNotNull { it.statusflag }.toSet() - Statusflag.Pc
if(statusFlagsNoCarry.isNotEmpty()) if(statusFlagsNoCarry.isNotEmpty())
err("can only use Carry as status flag parameter") err("can only use Carry as status flag parameter")
} }
// Non-string and non-ubytearray Pass-by-reference datatypes can not occur as parameters to a subroutine directly // Non-string and non-ubytearray Pass-by-reference datatypes can not occur as parameters to a subroutine directly
@ -442,22 +449,30 @@ internal class AstChecker(private val program: Program,
override fun visit(untilLoop: UntilLoop) { override fun visit(untilLoop: UntilLoop) {
val dt = untilLoop.condition.inferType(program) val dt = untilLoop.condition.inferType(program)
if(!dt.isInteger && !dt.istype(DataType.BOOL)) if(!dt.isInteger && !dt.istype(DataType.BOOL)) {
errors.err("condition value should be an integer type or bool", untilLoop.condition.position) val identifier = untilLoop.condition as? IdentifierReference
if(identifier==null || identifier.targetStatement(program)!=null)
errors.err("condition value should be an integer type or bool", untilLoop.condition.position)
}
super.visit(untilLoop) super.visit(untilLoop)
} }
override fun visit(whileLoop: WhileLoop) { override fun visit(whileLoop: WhileLoop) {
val dt = whileLoop.condition.inferType(program) val dt = whileLoop.condition.inferType(program)
if(!dt.isInteger && !dt.istype(DataType.BOOL)) if(!dt.isInteger && !dt.istype(DataType.BOOL)) {
errors.err("condition value should be an integer type or bool", whileLoop.condition.position) val identifier = whileLoop.condition as? IdentifierReference
if(identifier==null || identifier.targetStatement(program)!=null)
errors.err("condition value should be an integer type or bool", whileLoop.condition.position)
}
super.visit(whileLoop) super.visit(whileLoop)
} }
override fun visit(repeatLoop: RepeatLoop) { override fun visit(repeatLoop: RepeatLoop) {
val iterations = repeatLoop.iterations?.constValue(program) val iterations = repeatLoop.iterations?.constValue(program)
if(iterations != null && iterations.number.toInt() > 65535) if (iterations != null) {
errors.err("repeat cannot go over 65535 iterations", iterations.position) require(floor(iterations.number)==iterations.number)
if (iterations.number.toInt() > 65535) errors.err("repeat cannot go over 65535 iterations", iterations.position)
}
val ident = repeatLoop.iterations as? IdentifierReference val ident = repeatLoop.iterations as? IdentifierReference
if(ident!=null) { if(ident!=null) {
@ -521,7 +536,7 @@ internal class AstChecker(private val program: Program,
val targetName = targetIdentifier.nameInSource val targetName = targetIdentifier.nameInSource
when (val targetSymbol = assignment.definingScope.lookup(targetName)) { when (val targetSymbol = assignment.definingScope.lookup(targetName)) {
null -> { null -> {
errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position) errors.undefined(targetIdentifier.nameInSource, targetIdentifier.position)
return return
} }
!is VarDecl -> { !is VarDecl -> {
@ -629,6 +644,9 @@ internal class AstChecker(private val program: Program,
DataType.ARRAY_B, DataType.ARRAY_UB -> DataType.ARRAY_B, DataType.ARRAY_UB ->
if(arraySize > 256) if(arraySize > 256)
err("byte array length must be 1-256") err("byte array length must be 1-256")
in SplitWordArrayTypes ->
if(arraySize > 256)
err("split word array length must be 1-256")
DataType.ARRAY_W, DataType.ARRAY_UW -> DataType.ARRAY_W, DataType.ARRAY_UW ->
if(arraySize > 128) if(arraySize > 128)
err("word array length must be 1-128") err("word array length must be 1-128")
@ -677,6 +695,10 @@ internal class AstChecker(private val program: Program,
if (length == 0 || length > 256) if (length == 0 || length > 256)
err("string and byte array length must be 1-256") err("string and byte array length must be 1-256")
} }
in SplitWordArrayTypes -> {
if (length == 0 || length > 256)
err("split word array length must be 1-256")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> { DataType.ARRAY_UW, DataType.ARRAY_W -> {
if (length == 0 || length > 128) if (length == 0 || length > 128)
err("word array length must be 1-128") err("word array length must be 1-128")
@ -689,6 +711,9 @@ internal class AstChecker(private val program: Program,
} }
} }
} }
if(decl.splitArray && decl.type==VarDeclType.MEMORY)
err("@split can't be used on memory mapped arrays")
} }
if(decl.datatype==DataType.STR) { if(decl.datatype==DataType.STR) {
@ -721,13 +746,13 @@ internal class AstChecker(private val program: Program,
"%output" -> { "%output" -> {
if(directive.parent !is Module) if(directive.parent !is Module)
err("this directive may only occur at module level") err("this directive may only occur at module level")
if(directive.args.size!=1 || directive.args[0].name !in OutputType.values().map {it.name.lowercase()}) if(directive.args.size!=1 || directive.args[0].name !in OutputType.entries.map {it.name.lowercase()})
err("invalid output directive type") err("invalid output directive type")
} }
"%launcher" -> { "%launcher" -> {
if(directive.parent !is Module) if(directive.parent !is Module)
err("this directive may only occur at module level") err("this directive may only occur at module level")
if(directive.args.size!=1 || directive.args[0].name !in CbmPrgLauncherType.values().map{it.name.lowercase()}) if(directive.args.size!=1 || directive.args[0].name !in CbmPrgLauncherType.entries.map{it.name.lowercase()})
err("invalid launcher directive type") err("invalid launcher directive type")
} }
"%zeropage" -> { "%zeropage" -> {
@ -792,7 +817,7 @@ internal class AstChecker(private val program: Program,
err("this directive may only occur in a block or at module level") err("this directive may only occur in a block or at module level")
if(directive.args.isEmpty()) if(directive.args.isEmpty())
err("missing option directive argument(s)") err("missing option directive argument(s)")
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge")}.any { !it }) else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page", "merge", "splitarrays", "no_symbol_prefixing")}.any { !it })
err("invalid option directive argument(s)") err("invalid option directive argument(s)")
} }
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position) else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)
@ -955,6 +980,8 @@ internal class AstChecker(private val program: Program,
// expression with one side BOOL other side (U)BYTE is allowed; bool==byte // expression with one side BOOL other side (U)BYTE is allowed; bool==byte
} else if((expr.operator == "<<" || expr.operator == ">>") && (leftDt in WordDatatypes && rightDt in ByteDatatypes)) { } else if((expr.operator == "<<" || expr.operator == ">>") && (leftDt in WordDatatypes && rightDt in ByteDatatypes)) {
// exception allowed: shifting a word by a byte // exception allowed: shifting a word by a byte
} else if((leftDt==DataType.UWORD && rightDt==DataType.STR) || (leftDt==DataType.STR && rightDt==DataType.UWORD)) {
// exception allowed: comparing uword (pointer) with string
} else { } else {
errors.err("left and right operands aren't the same type", expr.left.position) errors.err("left and right operands aren't the same type", expr.left.position)
} }
@ -1140,21 +1167,13 @@ internal class AstChecker(private val program: Program,
} }
} }
} }
else if(funcName[0] == "divmod") { else if(funcName[0].startsWith("divmod")) {
if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference) if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression) {
errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position) errors.err("arguments must be all ubyte or all uword", functionCallStatement.position)
if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression) } else {
errors.err("all arguments must be ubyte", functionCallStatement.position) if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference)
if(!functionCallStatement.args.all {it.inferType(program) istype DataType.UBYTE}) errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position)
errors.err("all arguments must be ubyte", functionCallStatement.position) }
}
else if(funcName[0] == "divmodw") {
if(functionCallStatement.args[2] !is IdentifierReference || functionCallStatement.args[3] !is IdentifierReference)
errors.err("arguments 3 and 4 must be variables to receive the division and remainder", functionCallStatement.position)
if(functionCallStatement.args[2] is TypecastExpression || functionCallStatement.args[3] is TypecastExpression)
errors.err("all arguments must be uword", functionCallStatement.position)
if(!functionCallStatement.args.all {it.inferType(program) istype DataType.UWORD})
errors.err("all arguments must be uword", functionCallStatement.position)
} }
if(funcName[0] in InplaceModifyingBuiltinFunctions) { if(funcName[0] in InplaceModifyingBuiltinFunctions) {
@ -1232,7 +1251,7 @@ internal class AstChecker(private val program: Program,
val target = postIncrDecr.definingScope.lookup(targetName) val target = postIncrDecr.definingScope.lookup(targetName)
if(target==null) { if(target==null) {
val symbol = postIncrDecr.target.identifier!! val symbol = postIncrDecr.target.identifier!!
errors.err("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position) errors.undefined(symbol.nameInSource, symbol.position)
} else { } else {
if(target !is VarDecl || target.type== VarDeclType.CONST) { if(target !is VarDecl || target.type== VarDeclType.CONST) {
errors.err("can only increment or decrement a variable", postIncrDecr.position) errors.err("can only increment or decrement a variable", postIncrDecr.position)
@ -1285,7 +1304,7 @@ internal class AstChecker(private val program: Program,
// check index value 0..255 // check index value 0..255
val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program) val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program)
if(dtxNum isnot DataType.UBYTE && dtxNum isnot DataType.BYTE) if(dtxNum.isKnown && dtxNum isnot DataType.UBYTE && dtxNum isnot DataType.BYTE)
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position) errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
super.visit(arrayIndexedExpression) super.visit(arrayIndexedExpression)
@ -1322,8 +1341,14 @@ internal class AstChecker(private val program: Program,
constvalue == null -> errors.err("choice value must be a constant", whenChoice.position) constvalue == null -> errors.err("choice value must be a constant", whenChoice.position)
constvalue.type !in IntegerDatatypes -> errors.err("choice value must be a byte or word", whenChoice.position) constvalue.type !in IntegerDatatypes -> errors.err("choice value must be a byte or word", whenChoice.position)
conditionType isnot constvalue.type -> { conditionType isnot constvalue.type -> {
if(conditionType.isKnown) if(conditionType.isKnown) {
errors.err("choice value datatype differs from condition value", whenChoice.position) if(conditionType.istype(DataType.BOOL)) {
if(constvalue.number!=0.0 && constvalue.number!=1.0)
errors.err("choice value datatype differs from condition value", whenChoice.position)
} else {
errors.err("choice value datatype differs from condition value", whenChoice.position)
}
}
} }
} }
} }
@ -1365,6 +1390,32 @@ internal class AstChecker(private val program: Program,
super.visit(containment) super.visit(containment)
} }
override fun visit(memread: DirectMemoryRead) {
if(!memread.addressExpression.inferType(program).istype(DataType.UWORD)) {
errors.err("address for memory access isn't uword", memread.position)
}
val tc = memread.addressExpression as? TypecastExpression
if(tc!=null && tc.implicit) {
if(!tc.expression.inferType(program).istype(DataType.UWORD)) {
errors.err("address for memory access isn't uword", memread.position)
}
}
super.visit(memread)
}
override fun visit(memwrite: DirectMemoryWrite) {
if(!memwrite.addressExpression.inferType(program).istype(DataType.UWORD)) {
errors.err("address for memory access isn't uword", memwrite.position)
}
val tc = memwrite.addressExpression as? TypecastExpression
if(tc!=null && tc.implicit) {
if(!tc.expression.inferType(program).istype(DataType.UWORD)) {
errors.err("address for memory access isn't uword", memwrite.position)
}
}
super.visit(memwrite)
}
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? { private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
when (val targetStatement = target.targetStatement(program)) { when (val targetStatement = target.targetStatement(program)) {
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
@ -1378,7 +1429,7 @@ internal class AstChecker(private val program: Program,
else else
errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position) errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
} }
null -> errors.err("undefined symbol: ${target.nameInSource.joinToString(".")}", target.position) null -> errors.undefined(target.nameInSource, target.position)
else -> errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position) else -> errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
} }
return null return null
@ -1435,22 +1486,23 @@ internal class AstChecker(private val program: Program,
} }
return err("invalid byte array initialization value ${value.type}, expected $targetDt") return err("invalid byte array initialization value ${value.type}, expected $targetDt")
} }
DataType.ARRAY_UW, DataType.ARRAY_W -> { DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_UW_SPLIT-> {
// value may be either a single word, or a word arraysize, or a range // value may be either a single word, or a word arraysize, or a range
if(value.type istype targetDt) { if(value.type istype targetDt) {
if(!checkArrayValues(value, targetDt)) if(!checkArrayValues(value, targetDt))
return false return false
val arraySpecSize = arrayspec.constIndex() val arraySpecSize = arrayspec.constIndex()
val arraySize = value.value.size val arraySize = value.value.size
val maxLength = if(targetDt in SplitWordArrayTypes) 256 else 128
if(arraySpecSize!=null && arraySpecSize>0) { if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize>128) if(arraySpecSize>maxLength)
return err("word array length must be 1-128") return err("array length must be 1-$maxLength")
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value") val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
if (arraySize != expectedSize) if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)") return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
return true return true
} }
return err("invalid word array size, must be 1-128") return err("invalid array size, must be 1-$maxLength")
} }
return err("invalid word array initialization value ${value.type}, expected $targetDt") return err("invalid word array initialization value ${value.type}, expected $targetDt")
} }
@ -1538,6 +1590,7 @@ internal class AstChecker(private val program: Program,
when (it) { when (it) {
is NumericLiteral -> it.number.toInt() is NumericLiteral -> it.number.toInt()
is AddressOf -> it.identifier.hashCode() and 0xffff is AddressOf -> it.identifier.hashCode() and 0xffff
is IdentifierReference -> it.hashCode() and 0xffff
is TypecastExpression -> { is TypecastExpression -> {
val constVal = it.expression.constValue(program) val constVal = it.expression.constValue(program)
val cast = constVal?.cast(it.type) val cast = constVal?.cast(it.type)
@ -1557,10 +1610,10 @@ internal class AstChecker(private val program: Program,
DataType.ARRAY_B -> { DataType.ARRAY_B -> {
correct = array.all { it in -128..127 } correct = array.all { it in -128..127 }
} }
DataType.ARRAY_UW -> { DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> {
correct = array.all { (it in 0..65535) } correct = array.all { (it in 0..65535) }
} }
DataType.ARRAY_W -> { DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> {
correct = array.all { it in -32768..32767 } correct = array.all { it in -32768..32767 }
} }
DataType.ARRAY_F -> correct = true DataType.ARRAY_F -> correct = true

View File

@ -12,7 +12,6 @@ import prog8.ast.statements.VarDeclOrigin
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.VMTarget
internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) { internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) {
@ -28,20 +27,6 @@ internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationO
boolRemover.visit(this) boolRemover.visit(this)
boolRemover.applyModifications() boolRemover.applyModifications()
if(compilerOptions.compTarget.name!=VMTarget.NAME) {
val finder = AsmInstructionNamesFinder(compilerOptions.compTarget)
finder.visit(this)
if(finder.foundAny()) {
val replacer = AsmInstructionNamesReplacer(
finder.blocks,
finder.subroutines,
finder.variables,
finder.labels)
replacer.visit(this)
replacer.applyModifications()
}
}
val fixer = BeforeAsmAstChanger(this, compilerOptions, errors) val fixer = BeforeAsmAstChanger(this, compilerOptions, errors)
fixer.visit(this) fixer.visit(this)
while (errors.noErrors() && fixer.applyModifications() > 0) { while (errors.noErrors() && fixer.applyModifications() > 0) {

Some files were not shown because too many files have changed in this diff Show More