Compare commits

...
117 Commits
Author SHA1 Message Date
Irmen de Jong 7eb079050c fix strings.hash() on 6502 CPU's. NOTE: now takes string pointer in AY instead of R0 on all platforms 2025-05-01 22:47:43 +02:00
Irmen de Jong 2fdd5543b2 release 11.3.1 2025-04-30 22:40:54 +02:00
Irmen de Jong d04164c0a6 fix const evaluation of bitwise logical expressions (&, |, ^, <<, >>) of signed operands 2025-04-30 22:27:31 +02:00
Irmen de Jong b047731f82 removed some redundant arguments 2025-04-25 23:27:06 +02:00
Irmen de Jong 4d91f92a2e clearer error message 2025-04-25 19:38:45 +02:00
Irmen de Jong 98505d27b1 IR: fix invalid removal of label chunks and include asmsubs when looking for labels 2025-04-24 23:30:51 +02:00
Irmen de Jong cd63a58ad9 IR: get rid of problematic PREPARECALL pseudo instruction 2025-04-24 22:32:40 +02:00
Irmen de Jong 170f8dd092 IR: new CALLFAR instructions to support callfar into another bank 2025-04-24 21:17:48 +02:00
Irmen de Jong 619dcb6a84 fix error in IR register typing 2025-04-24 20:50:19 +02:00
Irmen de Jong 99ae8ea52e code cleanup 2025-04-24 18:51:03 +02:00
Irmen de Jong dc031c30eb add -version option to just print the compiler version and then exit 2025-04-24 18:13:35 +02:00
Irmen de Jong 1e702439b7 avoid JDK 21+/Kotlin method conflict of removeLast()/removeFirst()
background: see https://www.reddit.com/r/androiddev/comments/1gspjrs/dont_use_kotlins_removefirst_and_removelast_when/
https://youtrack.jetbrains.com/issue/KT-71375/Prevent-Kotlins-removeFirst-and-removeLast-from-causing-crashes-on-Android-14-and-below-after-upgrading-to-Android-API-Level-35

it's about Android but the problem also occurs on desktop JDKs for example when running a Java21 compiled prog8 with Java17
2025-04-24 18:11:42 +02:00
Irmen de Jong 8debc42381 slightly stricter assignment type compatibility checking 2025-04-24 15:05:35 +02:00
Irmen de Jong 532d719089 more optimal math.randrange() routines 2025-04-24 04:04:14 +02:00
Irmen de Jong b40860aca4 get rid of RTS after JMP 2025-04-23 17:08:16 +02:00
Irmen de Jong 2cbe6b5f7f info message when more optimal goto array[idx] is possible 2025-04-23 16:59:18 +02:00
Irmen de Jong d2cc7ccdfa remove redundant variable=0 initializations (BSS clear takes care of them) 2025-04-23 14:45:38 +02:00
Irmen de Jong 2cb183c6d8 fix regression for goto array[idx] on 6502 cpu which doesn't have jmp (ptr,x) 2025-04-23 02:56:10 +02:00
Irmen de Jong 84026b105f smaller code for goto nosplitptrarray[index] 2025-04-22 20:01:16 +02:00
Irmen de Jong a4d0589f10 fix errors like parse error still printed in color when -plaintext is set 2025-04-22 19:26:11 +02:00
Irmen de Jong e375f6afce fix diskio.f_read() returning 1 less than the actual size read.
fix diskio.f_read_all() more robust error status end of loop checking.
2025-04-21 05:02:16 +02:00
Irmen de Jong 5a7bc04816 update docs about library jump table 2025-04-19 13:49:03 +02:00
Irmen de Jong bd1894580e allow floating point value as part of a multi-value return 2025-04-18 22:45:05 +02:00
Irmen de Jong 9e694c0337 doc 2025-04-18 21:11:27 +02:00
Irmen de Jong c82586db28 print copyright message better 2025-04-17 23:09:28 +02:00
Irmen de Jong dd2d466350 code cleanups 2025-04-17 22:58:02 +02:00
Irmen de Jong 830da8de0a memorymapped vars in ZP are now treated as ZP-variables by prog8 itself too 2025-04-17 22:19:01 +02:00
Irmen de Jong 4e5ee333c8 preparing release 11.3 2025-04-17 21:16:52 +02:00
Irmen de Jong 9df899eb63 document romable option and that strings+initialized arrays become read-only 2025-04-17 21:05:46 +02:00
Irmen de Jong ca7491a702 cx16: sys.enable_irq_handlers() and associated functions are now romable 2025-04-17 00:25:25 +02:00
Irmen de Jong 1a07129865 c64: graphics.horizontal_line() is romable 2025-04-17 00:02:17 +02:00
Irmen de Jong 4fbd67ff99 txt.setcc() is romable 2025-04-16 23:44:50 +02:00
Irmen de Jong 5bc6c50f42 txt.setclr() and getclr() are romable 2025-04-16 23:38:16 +02:00
Irmen de Jong 063de3801d txt.getchr() is romable 2025-04-16 23:32:18 +02:00
Irmen de Jong ae65266a4a txt.setchr() is romable 2025-04-16 23:27:23 +02:00
Irmen de Jong 8ed2401e0b cx16: txt.scroll_left(), right, up and down are now romable 2025-04-16 23:11:27 +02:00
Irmen de Jong d2e8ee8269 cx16: txt.fill_screen, txt.clear_screenchars, clear_screencolors are now romable 2025-04-16 22:59:36 +02:00
Irmen de Jong 1f996e3b8b sorting.gnomesort_ub(), compression.decode_rle() and decode_rle_srcfunc() are now romable 2025-04-16 22:47:34 +02:00
Irmen de Jong 7108b74105 string.rfind() is now romable and now works correctly on empty strings.
added some comments to strings.pattern_match
2025-04-16 21:55:55 +02:00
Irmen de Jong 801fe1b604 c64: callfar() is now romable 2025-04-16 21:13:59 +02:00
Irmen de Jong fb44c87597 make diskio.f_read() ROM-compatible 2025-04-15 00:23:23 +02:00
Irmen de Jong 6b9cdbd482 remove unused arraycopy routines 2025-04-14 22:10:54 +02:00
Irmen de Jong 0ab98033b5 add rom-compatible random number routines 2025-04-14 22:04:11 +02:00
Irmen de Jong 14a2b96609 scanned libraries for self-modifying code/inline vars (romable problem) 2025-04-14 21:15:32 +02:00
Irmen de Jong f829b689db update zsmkit example to zsmkit release 2.6 2025-04-14 19:28:12 +02:00
Irmen de Jong dfda8b7ed5 remove problematic rewriting of X=value-X 2025-04-14 02:35:37 +02:00
Irmen de Jong 4388466451 romable comments 2025-04-10 22:21:27 +02:00
Irmen de Jong 5c2f509a52 also hide emulator process output when using -quiet 2025-04-10 21:26:48 +02:00
Irmen de Jong 59582f5210 added -quiet flag to suppres all compiler and assembler messages 2025-04-10 21:16:26 +02:00
Irmen de Jong e2a8bdbdfb romable comments 2025-04-09 22:35:23 +02:00
Irmen de Jong 0916b943da sys.exit(), exit2(), exit3() now romable 2025-04-09 22:13:52 +02:00
Irmen de Jong 9c7ebc883c fixed: memsetw() invalid asm, c128: sys.restore_irq() 2025-04-09 21:29:48 +02:00
Irmen de Jong 0ee42b9aa0 output_type is now part of custom target config and atari again defaults to .xex 2025-04-09 20:43:44 +02:00
Irmen de Jong 37b3868ca3 symboldump now also includes aliased symbols (like palette.set_default) 2025-04-08 21:40:28 +02:00
Irmen de Jong a6835ce3f0 fix signed word value range check error, fix rol2() on array element 2025-04-08 21:05:50 +02:00
Irmen de Jong 69c96ad99b cx16: added cx16.push_rambank/rombank and cx16.pop_rambank/rombank for easy temporary bank switching 2025-04-06 15:33:35 +02:00
Irmen de Jong b72877d59d cx16: added routines to get and set the default palette (new rom 49+ extapi) 2025-04-06 15:14:04 +02:00
Irmen de Jong 05eb15d4f7 add cx16.memory_decompress_from_func() 2025-04-06 13:48:08 +02:00
Irmen de Jong f1fec37c79 rename examples/vm to examples/virtual 2025-04-04 20:02:45 +02:00
Irmen de Jong 73f6880ff8 fix irq related crash by no longer zeroing out stored vector 2025-04-02 22:22:21 +02:00
Irmen de Jong 8a53742f31 fix notreached error 2025-04-01 23:12:08 +02:00
adiee5 9be40e85ff Improve romability of low-level libs (#160)
* Improve romability of low-level libs

* Improve ROMability of targeted syslibs

Previous commit just did that for multitarget libs, now it's also syslibs of built-in targets that get this treatment too.
2025-04-01 23:00:14 +02:00
Irmen de Jong 61079c1eb7 errormessage for assignment to str/arrays in ROM 2025-03-31 23:57:04 +02:00
Irmen de Jong 1075ee8fc3 errormessage for non romable extsub bank declaration 2025-03-31 23:05:58 +02:00
Irmen de Jong a28b265197 fix diskio merge issue on c64 and c128. Fixes #167 2025-03-31 22:06:49 +02:00
Irmen de Jong 20e534c468 fix memtop limit when using -varshigh option 2025-03-27 23:31:05 +01:00
Irmen de Jong da7aa5dc49 forloop temp index variables no longer inline when romable 2025-03-26 22:38:50 +01:00
Irmen de Jong 8f2a43ca0a temp vars for for loops indices in romable mode, are no longer allocated from the precious Zeropage 2025-03-26 22:12:22 +01:00
Irmen de Jong d0909d7810 added diskio.loadlib() convenience function to load library blobs 2025-03-24 22:35:34 +01:00
Irmen de Jong 1641999d20 upgrade to kotlin 2.1.20 2025-03-22 23:06:44 +01:00
Irmen de Jong e16452037c no more self modifying code for loops over non-const word range 2025-03-22 22:51:30 +01:00
Irmen de Jong 344d79684a get rid of more modifying code in forloops (byte ranges with step 1/-1)
fix invalid stack corrupting loops
2025-03-22 20:45:28 +01:00
Irmen de Jong 573a1d9b7b refactor for loop over byte ranges 2025-03-22 16:07:46 +01:00
Irmen de Jong 25ab57580c for loop over string no longer requires self-modifying code 2025-03-22 13:38:49 +01:00
Irmen de Jong a332e0e3d1 remove self-modifying code from @(ptr)++ and @(ptr)--
romable errors for string and array variables turned into read-only warnings

remove self-modifying code from @(ptr)++ and @(ptr)--
2025-03-21 23:54:19 +01:00
Irmen de Jong 376f1cb139 remove self-modifying code from rol(ptr) and ror(ptr) 2025-03-21 23:16:40 +01:00
Irmen de Jong 90f80558d7 add more 'modified' comments to self-modifying code lines 2025-03-21 17:59:49 +01:00
Irmen de Jong e281994898 intellij syntax def file works for all jetbrains ides 2025-03-21 01:32:24 +01:00
markjreed 29fac122e1 fix: ignore EOF when looking for error status after reading first byte of newly-opened file (#166)
* fix: ignore EOF when looking for error status after reading first byte of newly-opened file

* fix: replace hard-coded $40 and $BF with STATUS_EOF and ~STATUS_EOF
2025-03-19 23:00:55 +01:00
Irmen de Jong 1dc412eb90 simplify 2025-03-18 23:50:51 +01:00
Irmen de Jong 3770a4fe0c easier datatype notation by just using the type objects directly 2025-03-18 23:33:04 +01:00
Irmen de Jong 79cda544c8 allow integer range as when choice value 2025-03-17 22:26:27 +01:00
Irmen de Jong f04b97d890 support symlinks in paths (triggered by symlinked tmp on mac os) 2025-03-15 14:33:33 +01:00
Irmen de Jong 3e9b4ccc45 getting rid of needless absolute pathings 2025-03-14 20:39:18 +01:00
Irmen de Jong 2c3d838dd8 get rid of deprecated usage of thread id to create temporary test output file names 2025-03-14 01:23:01 +01:00
Irmen de Jong 7668a3c660 use kotest tempdir instead of hardcoded output directory 2025-03-14 01:18:33 +01:00
Irmen de Jong 5dd45b714a psg module: the envelope handler no longer writes to vera PSG voices that haven't been enabled 2025-03-12 23:07:13 +01:00
Irmen de Jong 8b08895d0f also add strings.ncompare() to virtual lib 2025-03-09 14:33:31 +01:00
Irmen de Jong 8f8d99e3ed updated symboldumps 2025-03-07 23:58:06 +01:00
Irmen de Jong 23474360ec fix ncompare calls 2025-03-07 23:56:13 +01:00
gillham 81c255c450 Add strings.ncompare (and backing strncmp_mem) to compare up to n characters of a string. (#164)
* Add strings.ncompare (and backing strncmp_mem) to compare up to n characters of a string.

* Document strings.ncompare.
2025-03-07 23:53:56 +01:00
Irmen de Jong ef23d52ed7 better error if module name clash occurs caused by case-insensitive names 2025-03-07 21:35:35 +01:00
Irmen de Jong 220ab773aa fix asmgen error when assigning address of split word array without explicit adressof syntax 2025-03-06 23:20:42 +01:00
Irmen de Jong e3e5bff7bb add missing sprites benchmark module 2025-03-06 19:39:55 +01:00
Irmen de Jong 7b9a841b2a fix C64 graphics.plot() it was broken since 10.5 2025-03-06 00:42:17 +01:00
Irmen de Jong 40423911ef added footgun warning when calling labels as subroutine 2025-03-04 22:14:21 +01:00
Irmen de Jong 582a70b046 fix calling label as subroutine (JSR label) 2025-03-04 21:53:53 +01:00
Irmen de Jong 5b63590ebf fix symbol prefixing bug triggered by certain usage of %option no_symbol_prefixing 2025-03-03 22:26:19 +01:00
Irmen de Jong 2b6510dc19 improve error message for non-numeric when choice values 2025-03-03 19:48:45 +01:00
Irmen de Jong 9d49589d73 fix codegen for @(ptr-offset)=value writing bogus values 2025-03-02 23:40:14 +01:00
Irmen de Jong 125b66c929 fix crash in asmgen for boolean comparison with false, when not using optimizations 2025-03-01 23:57:55 +01:00
Irmen de Jong 5255f1c052 print offending source position in more of the TODO errors to help diagnosing 2025-03-01 23:16:34 +01:00
Irmen de Jong a6ba05d60c compile time evaluation of constant rangecheck 2025-03-01 13:45:11 +01:00
Irmen de Jong 41e963b04b memory() name argument should be string literal, nice error message 2025-03-01 12:38:42 +01:00
Irmen de Jong 6ff75bef29 fix verafx register address typo VERA_FX_POLY_FILL_H 2025-02-27 21:59:04 +01:00
Irmen de Jong 72c16d0d32 avoid doing useless jsr for reboot/poweroff calls 2025-02-26 20:42:38 +01:00
Irmen de Jong 94653e5c8c possible workaround for SMC issue that could make sys.reset_system() and sys.poweroff_system() not work properly 2025-02-24 23:07:51 +01:00
Irmen de Jong 3e2b2a698d Separate simple Ast and Symboltable from codeCore into new simpleAst module. VirtualMachine and Intermediate do not need them, just codeCore. 2025-02-24 22:06:52 +01:00
Irmen de Jong ae04f5aee8 %option romable now disables floating point support
because if it's your code that is running in the rom bank you can't have the floating point routines available there
2025-02-23 10:21:55 +01:00
Irmen de Jong 5c56267662 Also romable warning for inline variables. Added TODO: Romable in library files where applicable 2025-02-21 21:58:31 +01:00
Irmen de Jong e55ce5504e added %option romable to enable romable mode, but only generate a bunch of warnings for problematic codegeneration atm 2025-02-20 23:40:44 +01:00
Irmen de Jong fb1e89d9ef update docs about call convention for multi-value results (first is in A or AY, then R15...R0)
added sprites+coroutines+defer part to benchmark program
2025-02-19 22:10:12 +01:00
Irmen de Jong bc550a4549 fix optimized multi-value call result assignment 2025-02-19 20:19:20 +01:00
Irmen de Jong ebdea9cf76 optimized call convention for multi-value return and assign on regular asmsubs (6502) 2025-02-19 20:19:20 +01:00
Irmen de Jong 09ec508f82 avoid unnecessary word extension when assigning a register byte to a byte target 2025-02-19 20:19:20 +01:00
Irmen de Jong d06e9ea7f6 allow comparisons against constant values with different type 2025-02-19 20:19:20 +01:00
Mia McMahill a36bdc54fd Add Notepad++ syntax highlighting file for dark mode (#161) 2025-02-19 18:33:31 +01:00
249 changed files with 6498 additions and 3317 deletions

No files matched your search

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