Compare commits

...
104 Commits
Author SHA1 Message Date
Irmen de Jong 895b30f7e5 version 5.2 2020-12-01 22:49:08 +01:00
Irmen de Jong b985604e22 slight tweak to word bitshift for large shift values 2020-12-01 22:48:02 +01:00
Irmen de Jong f7953e4ef3 fix float comparison error that creeped in with no longer using the stack for that 2020-12-01 22:19:03 +01:00
Irmen de Jong 63483d1f0e warnings, errors and todos 2020-12-01 03:24:06 +01:00
Irmen de Jong 8b981f03bf optimized reg_lesseq_w (word <= word) to avoid using extra zp word, by swapping operands 2020-12-01 02:09:48 +01:00
Irmen de Jong d0d0910bf2 corrected greatereq_w (word >= word) 2020-12-01 01:57:12 +01:00
Irmen de Jong 57ac820767 readme 2020-11-30 22:42:51 +01:00
Irmen de Jong b8bda867b6 optimized reg_lesseq_w (word <= word) 2020-11-30 02:26:00 +01:00
Irmen de Jong 05d3a2450c optimized reg_less_w (word < word) 2020-11-30 01:53:44 +01:00
Irmen de Jong d40788adfa optimized in-place array element modification to use simpler assignment asm code 2020-11-28 00:44:38 +01:00
Irmen de Jong 83fbf86b1c no longer generate double assignment to the indexer var for in-place modifying array variable 2020-11-27 23:46:01 +01:00
Irmen de Jong e876008427 tiny tweak of typecasting str to uword 2020-11-26 19:21:07 +01:00
Irmen de Jong 2b43353eb4 readme 2020-11-26 02:04:01 +01:00
Irmen de Jong a74403c347 float typecasts optimization 2020-11-26 01:52:48 +01:00
Irmen de Jong 2f4c6c8697 float typecasts optimization 2020-11-26 01:39:27 +01:00
Irmen de Jong 238d8197f5 byte/word typecasts optimized even further to just use cpu registers (and fixed sign extending AY) 2020-11-26 01:33:45 +01:00
Irmen de Jong 53a600d87b fix typecasting of signed byte to signed word in a variable 2020-11-25 22:33:41 +01:00
Irmen de Jong 2a0ffaf45d started to optimize typecasts to use translateExpression() less 2020-11-25 00:17:42 +01:00
Irmen de Jong 936b046ed9 optimize word [operator] byte, without translateExpression() 2020-11-24 23:41:10 +01:00
Irmen de Jong 378dcfe351 fix computation error of word - byte 2020-11-24 22:23:16 +01:00
Irmen de Jong 0a330b9288 warmings 2020-11-24 22:21:54 +01:00
Irmen de Jong a88b40d6c1 fix stack corruption with bitshifts 2020-11-24 21:58:14 +01:00
Irmen de Jong 09f25ffbd9 optimized in-place memory var modification, not using translateExpression() 2020-11-24 21:41:44 +01:00
Irmen de Jong ab1232d742 optimized in-place float var modification, not using translateExpression() 2020-11-24 01:09:24 +01:00
Irmen de Jong a7f56fe0fc remaining float comparisons with expression now without translateExpression() 2020-11-24 00:35:30 +01:00
Irmen de Jong 58a9452c36 fixed the YSCROLL graphics mode on the C64 (mistake in 5.1) 2020-11-23 23:05:51 +01:00
Irmen de Jong 6d8c4f403f updated Kotlin version to 1.4.20, updated targeted JDK version to 11 (LTS) 2020-11-23 22:28:24 +01:00
Irmen de Jong 88b80fed90 returning float values now via fac1 instead of stack 2020-11-23 22:14:45 +01:00
Irmen de Jong acdbd0c391 todos for next version 2020-11-22 19:18:57 +01:00
Irmen de Jong d9a8cfed8c updated the compiled examples disk 2020-11-22 18:45:40 +01:00
Irmen de Jong 122796fbba version 5.1 2020-11-22 18:36:04 +01:00
Irmen de Jong 510ca042c9 stack tested for most example programs 2020-11-22 18:35:43 +01:00
Irmen de Jong 125f6205f2 optimizing assigning an array value to a var 2020-11-22 17:44:55 +01:00
Irmen de Jong 8136f3df5c float-const comparison optimizations 2020-11-22 16:54:02 +01:00
Irmen de Jong 38d06a7e94 optimized float var comparison without translateExpression() 2020-11-22 15:05:45 +01:00
Irmen de Jong 49db10539a optimized float var equality comparison without translateExpression() 2020-11-22 14:33:03 +01:00
Irmen de Jong 8efe4c6267 Fixed compiler watch to work with multiple compilation modules 2020-11-22 13:11:33 +01:00
Irmen de Jong 04d8bb8fbf Fixed compiler watch flag crashing when not used on a subdirectory. Fixes #20 2020-11-22 12:07:14 +01:00
Irmen de Jong 08aa13c90c rnd() functions marked as having (internal) side effect 2020-11-22 02:09:32 +01:00
Irmen de Jong d1febc0208 all in-place byte assignments now without translateExpression() 2020-11-22 01:38:53 +01:00
Irmen de Jong 5980e58ac6 word comparison jumps now without translateExpression() 2020-11-22 01:15:05 +01:00
Irmen de Jong e1dc283d4b byte comparison jumps now without translateExpression() 2020-11-21 23:31:26 +01:00
Irmen de Jong 8be234973c rollback failed optimization of memory expressions (code size got too large) 2020-11-21 19:09:02 +01:00
Irmen de Jong 7def8ff2cd beginning to optimize comparisons more 2020-11-21 18:44:17 +01:00
Irmen de Jong 340b1c2e42 added balls demo/benchmark 2020-11-21 18:03:57 +01:00
Irmen de Jong 7e0f7ba438 todos 2020-11-20 23:46:14 +01:00
Irmen de Jong fefd9b52a8 fix for loop with signed byte loopvar over non-const 2020-11-20 22:54:24 +01:00
Irmen de Jong afd155ac4f optimize for loops over non const range, without translateExpression() 2020-11-20 22:44:16 +01:00
Irmen de Jong ee724eb4f1 float variable casts without translateExpression() 2020-11-19 22:58:38 +01:00
Irmen de Jong 2f1f20ea11 rename 2020-11-19 00:28:49 +01:00
Irmen de Jong 063bcf17d8 various inplace modification for word vars now without translateExpression() 2020-11-19 00:08:10 +01:00
Irmen de Jong 72509eef44 inplace modification for memory now without translateExpression() 2020-11-18 23:23:06 +01:00
Irmen de Jong 2da28864e9 inplace not and invert for memory now without translateExpression() 2020-11-18 23:13:07 +01:00
Irmen de Jong 4278f64682 fixed invalid value push for memreads with expression 2020-11-18 22:45:04 +01:00
Irmen de Jong 59ae3c3fcd << and >> for byte values slightly optimized, no longer use translateExpression(). preparing for more operator optimizations. 2020-11-18 01:27:02 +01:00
Irmen de Jong 7fa21fbdff @(...) in an expression is now more efficient, without translateExpression() 2020-11-18 00:58:04 +01:00
Irmen de Jong e95af7498e comparing function call result to 0 now more efficient, without translateExpression() 2020-11-18 00:05:48 +01:00
Irmen de Jong 79c75adac1 repeat and when without translateExpression() 2020-11-17 23:52:13 +01:00
Irmen de Jong d212f69d89 ++/-- and @Pc without translateExpression() 2020-11-17 23:40:42 +01:00
Irmen de Jong edf5e69d39 optimized swap() 2020-11-15 18:04:54 +01:00
Irmen de Jong 574eb0d174 refactoring asmassignment code blocks into utility functions 2020-11-15 17:44:47 +01:00
Irmen de Jong 8bd4914e2f fix stack error for float casts 2020-11-15 17:34:27 +01:00
Irmen de Jong 5ebaaff64b refactoring asmassignment code blocks into utility functions 2020-11-15 15:07:55 +01:00
Irmen de Jong 5c9e0c9f51 emit extra nop for breakpoints so vice label list works again (requires 64tass 1.55.2257 or newer!) 2020-11-15 14:31:06 +01:00
Irmen de Jong 8132edbb08 updated some compiled example 2020-11-10 22:51:01 +01:00
Irmen de Jong d29ce78c86 todos and version 2020-11-10 22:44:48 +01:00
Irmen de Jong 94bc9d7a69 string compare in expression no longer via stack args 2020-11-10 21:48:28 +01:00
Irmen de Jong e8faec0932 re-introduced more aggressive binexpr splitting optimization 2020-11-10 21:17:33 +01:00
Irmen de Jong 69ca4fe304 cleanup 2020-11-10 21:02:12 +01:00
Irmen de Jong cd99fe46fd finished call convention change for builtin functions now no longer via stack 2020-11-10 00:43:45 +01:00
Irmen de Jong 4825b4dc68 fix passing address of pass-by-reference assignment to a UWORD 2020-11-10 00:35:24 +01:00
Irmen de Jong 8d0607ef58 fix missing float casts 2020-11-09 23:57:50 +01:00
Irmen de Jong 225295a7d8 fix float casts 2020-11-09 01:18:22 +01:00
Irmen de Jong 4cd74daf53 float eval result var added, but some examples are broken 2020-11-08 18:54:02 +01:00
Irmen de Jong 6eb9118197 example 2020-11-07 01:08:56 +01:00
Irmen de Jong d0bd2f522c rol and ror 2020-11-07 00:56:54 +01:00
Irmen de Jong 661c757236 fix string compare in expressions 2020-11-06 22:59:56 +01:00
Irmen de Jong aaa20093ef cleaning up and correcting cc for builtin functions 2020-11-06 00:56:26 +01:00
Irmen de Jong 1eecdd6fa3 fix error when taking address of struct var 2020-11-05 02:39:04 +01:00
Irmen de Jong 800b5b2a43 cleaning up and correcting cc for builtin functions 2020-11-05 02:29:33 +01:00
Irmen de Jong 9d17421c66 implemented the arithmetic functions with new cc. fixed sgn(). 2020-11-04 02:27:29 +01:00
Irmen de Jong 0edd50e956 implemented cc for abs() 2020-11-03 23:01:23 +01:00
Irmen de Jong 288d4f08b3 implemented cc for integer sin and cos variants 2020-11-03 22:42:59 +01:00
Irmen de Jong 526e4b8bdc fix faulty binexpr splitting 2020-11-03 21:31:08 +01:00
Irmen de Jong e0c5ccc16b begun with converting builtin functions to new call convention 2020-11-02 23:00:20 +01:00
Irmen de Jong ebc2c614d7 use non-stack call conv for builtin functions 2020-11-02 19:59:27 +01:00
Irmen de Jong 29f5a85158 callconv 2020-11-01 19:25:23 +01:00
Irmen de Jong 8af2380a47 pair 2020-11-01 18:00:20 +01:00
Irmen de Jong 431f2a2088 optimized memset and memcopy on CX16, memcopy can deal with any size now 2020-11-01 08:00:32 +01:00
Irmen de Jong e05ea887f6 implement proper returning of float values via FAC1 2020-11-01 06:27:17 +01:00
Irmen de Jong 95c0425151 improved sqrt16 2020-11-01 05:45:49 +01:00
Irmen de Jong 47cbc7b1f9 added a custom-charset example for the c64 2020-10-31 02:26:59 +01:00
Irmen de Jong e7b75d591c assigning float results from functions (from FAC1) 2020-10-31 01:22:19 +01:00
Irmen de Jong 99f7d469f4 assigning string result from subroutine 2020-10-30 22:22:06 +01:00
Irmen de Jong 8a6ef17fbf option 2020-10-30 21:51:15 +01:00
Irmen de Jong 5f337a0bd9 fix typecheck of multiple returnvalues 2020-10-30 21:45:37 +01:00
Irmen de Jong 87862f772a better handling of inferred type errors 2020-10-30 21:24:49 +01:00
Irmen de Jong 3ab641aa21 removed @stack in subroutine args and returnvalues, can only use variables or registers now 2020-10-30 15:02:42 +01:00
Irmen de Jong 3efa8da8e0 made versions of various builtin funcs returning value in registers 2020-10-30 14:35:20 +01:00
Irmen de Jong 3e28ed4fe4 mader versions of abs() and sgn() returning value in register 2020-10-28 22:56:13 +01:00
Irmen de Jong 44949460ed change for subroutine return values via registers instead of stack 2020-10-28 00:29:34 +01:00
Irmen de Jong 83cc19ad6f preparing for subroutine return values via registers instead of stack 2020-10-23 20:56:10 +02:00
Irmen de Jong 66bb98c479 fixed bugs in code assigning values from eval stack 2020-10-23 03:45:09 +02:00
Irmen de Jong ff3f985658 refactoring 2020-10-22 23:41:16 +02:00
112 changed files with 6715 additions and 2791 deletions

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+14 -14
View File
@@ -20,36 +20,36 @@ Full documentation (syntax reference, how to use the language and the compiler,
https://prog8.readthedocs.io/
What use Prog8 provide?
-----------------------
What does Prog8 provide?
------------------------
- reduction of source code length over raw assembly
- big reduction of source code length over raw assembly
- modularity, symbol scoping, subroutines
- various data types other than just bytes (16-bit words, floats, strings)
- automatic variable allocations, automatic string and array variables and string sharing
- subroutines with an input- and output parameter signature
- constant folding in expressions
- no stack frame allocations because parameters and local variables are automatically allocated statically
- constant folding in expressions and other high-level program optimizations
- conditional branches
- 'when' statement to provide a concise jump table alternative to if/elseif chains
- structs to group together sets of variables and manipulate them at once
- floating point operations (requires the C64 Basic ROM routines for this)
- abstracting away low level aspects such as ZeroPage handling, program startup, explicit memory addresses
- various code optimizations (code structure, logical and numerical expressions, unused code removal...)
- inline assembly allows you to have full control when every cycle or byte matters
- 'when' statement to provide a concise jump table alternative to if/elseif chains
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
- structs to group together sets of variables and manipulate them at once
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
- fast execution speed due to compilation to native assembly code
- inline assembly allows you to have full control when every cycle or byte matters
*Rapid edit-compile-run-debug cycle:*
- use a modern PC to do the work on
- very quick compilation times
- use a modern PC to do the work on, use nice editors and enjoy quick compilation times
- can automatically run the program in the Vice emulator after succesful compilation
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
*Two supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
- "c64": Commodore-64 (6510 CPU = almost a 6502) premium support.
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) experimental support.
- "c64": Commodore-64 (6510 CPU = almost a 6502), the main target.
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) .
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
@@ -61,7 +61,7 @@ Additional required tools
A recent .exe version of this tool for Windows can be obtained from my [clone](https://github.com/irmen/64tass/releases) of this project.
For other platforms it is very easy to compile it yourself (make ; make install).
A **Java runtime (jre or jdk), version 8 or newer** is required to run a prepackaged version of the compiler.
A **Java runtime (jre or jdk), version 11 or newer** is required to run a prepackaged version of the compiler.
If you want to build it from source, you'll need a Java SDK + Kotlin 1.3.x SDK (or for instance,
IntelliJ IDEA with the Kotlin plugin).
+6 -6
View File
@@ -1,11 +1,11 @@
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20"
}
}
plugins {
// id "org.jetbrains.kotlin.jvm" version "1.4.10"
// id "org.jetbrains.kotlin.jvm" version "1.4.20"
id 'application'
id 'org.jetbrains.dokka' version "0.9.18"
id 'com.github.johnrengelman.shadow' version '5.2.0'
@@ -15,8 +15,8 @@ plugins {
apply plugin: "kotlin"
apply plugin: "java"
targetCompatibility = 1.8
sourceCompatibility = 1.8
targetCompatibility = 11
sourceCompatibility = 11
repositories {
mavenLocal()
@@ -45,7 +45,7 @@ dependencies {
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "11"
// verbose = true
// freeCompilerArgs += "-XXLanguage:+NewInference"
}
@@ -53,7 +53,7 @@ compileKotlin {
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "11"
}
}
+210 -283
View File
@@ -55,13 +55,76 @@ w2float .proc
jmp ub2float._fac_to_mem
.pend
cast_from_uw .proc
; -- uword in A/Y into float var at (P8ZP_SCRATCH_W2)
stx P8ZP_SCRATCH_REG
jsr GIVUAYFAY
jmp ub2float._fac_to_mem
.pend
cast_from_w .proc
; -- word in A/Y into float var at (P8ZP_SCRATCH_W2)
stx P8ZP_SCRATCH_REG
jsr GIVAYFAY
jmp ub2float._fac_to_mem
.pend
cast_from_ub .proc
; -- ubyte in Y into float var at (P8ZP_SCRATCH_W2)
stx P8ZP_SCRATCH_REG
jsr FREADUY
jmp ub2float._fac_to_mem
.pend
cast_from_b .proc
; -- byte in A into float var at (P8ZP_SCRATCH_W2)
stx P8ZP_SCRATCH_REG
jsr FREADSA
jmp ub2float._fac_to_mem
.pend
cast_as_uw_into_ya .proc ; also used for float 2 ub
; -- cast float at A/Y to uword into Y/A
jsr MOVFM
jmp cast_FAC1_as_uw_into_ya
.pend
cast_as_w_into_ay .proc ; also used for float 2 b
; -- cast float at A/Y to word into A/Y
jsr MOVFM
jmp cast_FAC1_as_w_into_ay
.pend
cast_FAC1_as_uw_into_ya .proc ; also used for float 2 ub
; -- cast fac1 to uword into Y/A
stx P8ZP_SCRATCH_REG
jsr GETADR ; into Y/A
ldx P8ZP_SCRATCH_REG
rts
.pend
cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
; -- cast fac1 to word into A/Y
stx P8ZP_SCRATCH_REG
jsr AYINT
ldy $64
lda $65
ldx P8ZP_SCRATCH_REG
rts
.pend
stack_b2float .proc
; -- b2float operating on the stack
inx
lda P8ESTACK_LO,x
stx P8ZP_SCRATCH_REG
jsr FREADSA
jmp push_fac1_as_result
jmp push_fac1._internal
.pend
stack_w2float .proc
@@ -71,7 +134,7 @@ stack_w2float .proc
lda P8ESTACK_HI,x
stx P8ZP_SCRATCH_REG
jsr GIVAYF
jmp push_fac1_as_result
jmp push_fac1._internal
.pend
stack_ub2float .proc
@@ -82,7 +145,7 @@ stack_ub2float .proc
tay
lda #0
jsr GIVAYF
jmp push_fac1_as_result
jmp push_fac1._internal
.pend
stack_uw2float .proc
@@ -92,7 +155,7 @@ stack_uw2float .proc
ldy P8ESTACK_HI,x
stx P8ZP_SCRATCH_REG
jsr GIVUAYFAY
jmp push_fac1_as_result
jmp push_fac1._internal
.pend
stack_float2w .proc ; also used for float2b
@@ -146,22 +209,6 @@ push_float .proc
rts
.pend
func_rndf .proc
; -- put a random floating point value on the stack
stx P8ZP_SCRATCH_REG
lda #1
jsr FREADSA
jsr RND ; rng into fac1
ldx #<_rndf_rnum5
ldy #>_rndf_rnum5
jsr MOVMF ; fac1 to mem X/Y
ldx P8ZP_SCRATCH_REG
lda #<_rndf_rnum5
ldy #>_rndf_rnum5
jmp push_float
_rndf_rnum5 .byte 0,0,0,0,0
.pend
pop_float .proc
; ---- pops mflpt5 from stack to memory A/Y
; (frees 3 stack positions = 6 bytes of which 1 is padding)
@@ -198,16 +245,6 @@ pop_float_fac1 .proc
jmp MOVFM
.pend
pop_float_fac2 .proc
; -- pops float from stack into FAC2
lda #<fmath_float1
ldy #>fmath_float1
jsr pop_float
lda #<fmath_float1
ldy #>fmath_float1
jmp CONUPK
.pend
copy_float .proc
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
@@ -273,9 +310,11 @@ pop_2_floats_f2_in_fac1 .proc
fmath_float1 .byte 0,0,0,0,0 ; storage for a mflpt5 value
fmath_float2 .byte 0,0,0,0,0 ; storage for a mflpt5 value
push_fac1_as_result .proc
; -- push the float in FAC1 onto the stack, and return from calculation
ldx #<fmath_float1
push_fac1 .proc
; -- push the float in FAC1 onto the stack
stx P8ZP_SCRATCH_REG
_internal ldx #<fmath_float1
ldy #>fmath_float1
jsr MOVMF
lda #<fmath_float1
@@ -284,6 +323,7 @@ push_fac1_as_result .proc
jmp push_float
.pend
pow_f .proc
; -- push f1 ** f2 on stack
lda #<fmath_float2
@@ -299,8 +339,7 @@ pow_f .proc
lda #<fmath_float2
ldy #>fmath_float2
jsr FPWR
ldx P8ZP_SCRATCH_REG
jmp push_fac1_as_result
jmp push_fac1._internal
.pend
div_f .proc
@@ -310,7 +349,7 @@ div_f .proc
lda #<fmath_float1
ldy #>fmath_float1
jsr FDIV
jmp push_fac1_as_result
jmp push_fac1._internal
.pend
add_f .proc
@@ -320,7 +359,7 @@ add_f .proc
lda #<fmath_float1
ldy #>fmath_float1
jsr FADD
jmp push_fac1_as_result
jmp push_fac1._internal
.pend
sub_f .proc
@@ -330,7 +369,7 @@ sub_f .proc
lda #<fmath_float1
ldy #>fmath_float1
jsr FSUB
jmp push_fac1_as_result
jmp push_fac1._internal
.pend
mul_f .proc
@@ -340,7 +379,7 @@ mul_f .proc
lda #<fmath_float1
ldy #>fmath_float1
jsr FMULT
jmp push_fac1_as_result
jmp push_fac1._internal
.pend
neg_f .proc
@@ -351,11 +390,96 @@ neg_f .proc
rts
.pend
abs_f .proc
; -- strip the sign bit on the stack
lda P8ESTACK_HI+3,x
and #$7f
sta P8ESTACK_HI+3,x
var_fac1_less_f .proc
; -- is the float in FAC1 < the variable AY?
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #255
beq +
lda #0
rts
+ lda #1
rts
.pend
var_fac1_lesseq_f .proc
; -- is the float in FAC1 <= the variable AY?
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #0
beq +
cmp #255
beq +
lda #0
rts
+ lda #1
rts
.pend
var_fac1_greater_f .proc
; -- is the float in FAC1 > the variable AY?
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #1
beq +
lda #0
+ rts
.pend
var_fac1_greatereq_f .proc
; -- is the float in FAC1 >= the variable AY?
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #0
beq +
cmp #1
beq +
lda #0
rts
+ lda #1
rts
.pend
var_fac1_notequal_f .proc
; -- are the floats numbers in FAC1 and the variable AY *not* identical?
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
and #1
rts
.pend
vars_equal_f .proc
; -- are the mflpt5 numbers in P8ZP_SCRATCH_W1 and AY identical?
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #0
lda (P8ZP_SCRATCH_W1),y
cmp (P8ZP_SCRATCH_W2),y
bne _false
iny
lda (P8ZP_SCRATCH_W1),y
cmp (P8ZP_SCRATCH_W2),y
bne _false
iny
lda (P8ZP_SCRATCH_W1),y
cmp (P8ZP_SCRATCH_W2),y
bne _false
iny
lda (P8ZP_SCRATCH_W1),y
cmp (P8ZP_SCRATCH_W2),y
bne _false
iny
lda (P8ZP_SCRATCH_W1),y
cmp (P8ZP_SCRATCH_W2),y
bne _false
lda #1
rts
_false lda #0
rts
.pend
@@ -396,6 +520,40 @@ notequal_f .proc
rts
.pend
vars_less_f .proc
; -- is float in AY < float in P8ZP_SCRATCH_W2 ?
jsr MOVFM
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #255
bne +
lda #1
rts
+ lda #0
rts
.pend
vars_lesseq_f .proc
; -- is float in AY <= float in P8ZP_SCRATCH_W2 ?
jsr MOVFM
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #255
bne +
- lda #1
rts
+ cmp #0
beq -
lda #0
rts
.pend
less_f .proc
; -- is f1 < f2?
jsr compare_floats
@@ -457,240 +615,22 @@ _return_true lda #1
bne _return_result
.pend
func_sin .proc
; -- push sin(f) back onto stack
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
jsr SIN
jmp push_fac1_as_result
.pend
func_cos .proc
; -- push cos(f) back onto stack
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
jsr COS
jmp push_fac1_as_result
.pend
func_tan .proc
; -- push tan(f) back onto stack
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
jsr TAN
jmp push_fac1_as_result
.pend
func_atan .proc
; -- push atan(f) back onto stack
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
jsr ATN
jmp push_fac1_as_result
.pend
func_ln .proc
; -- push ln(f) back onto stack
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
jsr LOG
jmp push_fac1_as_result
.pend
func_log2 .proc
; -- push log base 2, ln(f)/ln(2), back onto stack
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
jsr LOG
jsr MOVEF
lda #<c64.FL_LOG2
ldy #>c64.FL_LOG2
jsr MOVFM
jsr FDIVT
jmp push_fac1_as_result
.pend
func_sqrt .proc
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
jsr SQR
jmp push_fac1_as_result
.pend
func_rad .proc
; -- convert degrees to radians (d * pi / 180)
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
lda #<_pi_div_180
ldy #>_pi_div_180
jsr FMULT
jmp push_fac1_as_result
_pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
.pend
func_deg .proc
; -- convert radians to degrees (d * (1/ pi * 180))
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
lda #<_one_over_pi_div_180
ldy #>_one_over_pi_div_180
jsr FMULT
jmp push_fac1_as_result
_one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
.pend
func_round .proc
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
jsr FADDH
jsr INT
jmp push_fac1_as_result
.pend
func_floor .proc
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
jsr INT
jmp push_fac1_as_result
.pend
func_ceil .proc
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
ldx #<fmath_float1
ldy #>fmath_float1
jsr MOVMF
jsr INT
lda #<fmath_float1
ldy #>fmath_float1
jsr FCOMP
cmp #0
beq +
lda #<FL_ONE_const
ldy #>FL_ONE_const
jsr FADD
+ jmp push_fac1_as_result
.pend
func_any_f .proc
inx
lda P8ESTACK_LO,x ; array size
set_array_float_from_fac1 .proc
; -- set the float in FAC1 in the array (index in A, array in P8ZP_SCRATCH_W1)
sta P8ZP_SCRATCH_B1
asl a
asl a
clc
adc P8ZP_SCRATCH_B1 ; times 5 because of float
jmp prog8_lib.func_any_b._entry
.pend
func_all_f .proc
inx
jsr prog8_lib.peek_address
lda P8ESTACK_LO,x ; array size
sta P8ZP_SCRATCH_B1
asl a
asl a
clc
adc P8ZP_SCRATCH_B1 ; times 5 because of float
tay
dey
- lda (P8ZP_SCRATCH_W1),y
clc
dey
adc (P8ZP_SCRATCH_W1),y
dey
adc (P8ZP_SCRATCH_W1),y
dey
adc (P8ZP_SCRATCH_W1),y
dey
adc (P8ZP_SCRATCH_W1),y
dey
cmp #0
beq +
cpy #255
bne -
lda #1
sta P8ESTACK_LO+1,x
rts
+ sta P8ESTACK_LO+1,x
rts
.pend
func_max_f .proc
lda #255
sta _minmax_cmp+1
lda #<_largest_neg_float
ldy #>_largest_neg_float
_minmax_entry jsr MOVFM
jsr prog8_lib.pop_array_and_lengthmin1Y
stx floats_store_reg
- sty P8ZP_SCRATCH_REG
lda P8ZP_SCRATCH_W1
adc P8ZP_SCRATCH_B1
ldy P8ZP_SCRATCH_W1+1
jsr FCOMP
_minmax_cmp cmp #255 ; modified
bne +
lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jsr MOVFM
+ lda P8ZP_SCRATCH_W1
clc
adc #5
sta P8ZP_SCRATCH_W1
adc P8ZP_SCRATCH_W1
bcc +
inc P8ZP_SCRATCH_W1+1
+ ldy P8ZP_SCRATCH_REG
dey
cpy #255
bne -
iny
+ stx floats_store_reg
tax
jsr MOVMF
ldx floats_store_reg
stx P8ZP_SCRATCH_REG
jmp push_fac1_as_result
_largest_neg_float .byte 255,255,255,255,255 ; largest negative float -1.7014118345e+38
.pend
func_min_f .proc
lda #1
sta func_max_f._minmax_cmp+1
lda #<_largest_pos_float
ldy #>_largest_pos_float
jmp func_max_f._minmax_entry
_largest_pos_float .byte 255,127,255,255,255 ; largest positive float
rts
.pend
func_sum_f .proc
lda #<FL_ZERO_const
ldy #>FL_ZERO_const
jsr MOVFM
jsr prog8_lib.pop_array_and_lengthmin1Y
stx floats_store_reg
- sty P8ZP_SCRATCH_REG
lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jsr FADD
ldy P8ZP_SCRATCH_REG
dey
cpy #255
beq +
lda P8ZP_SCRATCH_W1
clc
adc #5
sta P8ZP_SCRATCH_W1
bcc -
inc P8ZP_SCRATCH_W1+1
bne -
+ ldx floats_store_reg
stx P8ZP_SCRATCH_REG
jmp push_fac1_as_result
.pend
sign_f .proc
jsr pop_float_fac1
jsr SIGN
sta P8ESTACK_LO,x
dex
rts
.pend
@@ -734,16 +674,3 @@ set_array_float .proc
.pend
swap_floats .proc
; -- swap floats pointed to by SCRATCH_ZPWORD1, SCRATCH_ZPWORD2
ldy #4
- lda (P8ZP_SCRATCH_W1),y
pha
lda (P8ZP_SCRATCH_W2),y
sta (P8ZP_SCRATCH_W1),y
pla
sta (P8ZP_SCRATCH_W2),y
dey
bpl -
rts
.pend
+1
View File
@@ -213,5 +213,6 @@ sub print_f (float value) {
}
%asminclude "library:c64/floats.asm", ""
%asminclude "library:c64/floats_funcs.asm", ""
}
+437
View File
@@ -0,0 +1,437 @@
; --- floating point builtin functions
abs_f_stack .proc
; -- push abs(AY) on stack
jsr floats.MOVFM
jsr floats.ABS
jmp push_fac1
.pend
abs_f_fac1 .proc
; -- FAC1 = abs(AY)
jsr floats.MOVFM
jmp floats.ABS
.pend
func_atan_stack .proc
jsr func_atan_fac1
jmp push_fac1
.pend
func_atan_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr ATN
ldx P8ZP_SCRATCH_REG
rts
.pend
func_ceil_stack .proc
jsr func_ceil_fac1
jmp push_fac1
.pend
func_ceil_fac1 .proc
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
jsr MOVFM
stx P8ZP_SCRATCH_REG
ldx #<fmath_float1
ldy #>fmath_float1
jsr MOVMF
jsr INT
lda #<fmath_float1
ldy #>fmath_float1
jsr FCOMP
cmp #0
beq +
lda #<FL_ONE_const
ldy #>FL_ONE_const
jsr FADD
+ ldx P8ZP_SCRATCH_REG
rts
.pend
func_floor_stack .proc
jsr func_floor_fac1
jmp push_fac1
.pend
func_floor_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr INT
ldx P8ZP_SCRATCH_REG
rts
.pend
func_round_stack .proc
jsr func_round_fac1
jmp push_fac1
.pend
func_round_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr FADDH
jsr INT
ldx P8ZP_SCRATCH_REG
rts
.pend
func_sin_stack .proc
jsr func_sin_fac1
jmp push_fac1
.pend
func_sin_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr SIN
ldx P8ZP_SCRATCH_REG
rts
.pend
func_cos_stack .proc
jsr func_cos_fac1
jmp push_fac1
.pend
func_cos_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr COS
ldx P8ZP_SCRATCH_REG
rts
.pend
func_tan_stack .proc
jsr func_tan_fac1
jmp push_fac1
.pend
func_tan_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr TAN
ldx P8ZP_SCRATCH_REG
rts
.pend
func_rad_stack .proc
jsr func_rad_fac1
jmp push_fac1
.pend
func_rad_fac1 .proc
; -- convert degrees to radians (d * pi / 180)
jsr MOVFM
stx P8ZP_SCRATCH_REG
lda #<_pi_div_180
ldy #>_pi_div_180
jsr FMULT
ldx P8ZP_SCRATCH_REG
rts
_pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
.pend
func_deg_stack .proc
jsr func_deg_fac1
jmp push_fac1
.pend
func_deg_fac1 .proc
; -- convert radians to degrees (d * (1/ pi * 180))
jsr MOVFM
stx P8ZP_SCRATCH_REG
lda #<_one_over_pi_div_180
ldy #>_one_over_pi_div_180
jsr FMULT
ldx P8ZP_SCRATCH_REG
rts
_one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
.pend
func_ln_stack .proc
jsr func_ln_fac1
jmp push_fac1
.pend
func_ln_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr LOG
ldx P8ZP_SCRATCH_REG
rts
.pend
func_log2_stack .proc
jsr func_log2_fac1
jmp push_fac1
.pend
func_log2_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr LOG
jsr MOVEF
lda #<FL_LOG2
ldy #>FL_LOG2
jsr MOVFM
jsr FDIVT
ldx P8ZP_SCRATCH_REG
rts
.pend
func_sign_f_stack .proc
jsr func_sign_f_into_A
sta P8ESTACK_LO,x
dex
rts
.pend
func_sign_f_into_A .proc
jsr MOVFM
jmp SIGN
.pend
func_sqrt_stack .proc
jsr func_sqrt_fac1
jmp push_fac1
.pend
func_sqrt_fac1 .proc
jsr MOVFM
stx P8ZP_SCRATCH_REG
jsr SQR
ldx P8ZP_SCRATCH_REG
rts
.pend
func_rndf_stack .proc
jsr func_rndf_fac1
jmp push_fac1
.pend
func_rndf_fac1 .proc
stx P8ZP_SCRATCH_REG
lda #1
jsr FREADSA
jsr RND ; rng into fac1
ldx P8ZP_SCRATCH_REG
rts
.pend
func_swap_f .proc
; -- swap floats pointed to by SCRATCH_ZPWORD1, SCRATCH_ZPWORD2
ldy #4
- lda (P8ZP_SCRATCH_W1),y
pha
lda (P8ZP_SCRATCH_W2),y
sta (P8ZP_SCRATCH_W1),y
pla
sta (P8ZP_SCRATCH_W2),y
dey
bpl -
rts
.pend
func_reverse_f .proc
; --- reverse an array of floats (array in P8ZP_SCRATCH_W1, num elements in A)
_left_index = P8ZP_SCRATCH_W2
_right_index = P8ZP_SCRATCH_W2+1
_loop_count = P8ZP_SCRATCH_REG
pha
jsr a_times_5
sec
sbc #5
sta _right_index
lda #0
sta _left_index
pla
lsr a
sta _loop_count
_loop ; push the left indexed float on the stack
ldy _left_index
lda (P8ZP_SCRATCH_W1),y
pha
iny
lda (P8ZP_SCRATCH_W1),y
pha
iny
lda (P8ZP_SCRATCH_W1),y
pha
iny
lda (P8ZP_SCRATCH_W1),y
pha
iny
lda (P8ZP_SCRATCH_W1),y
pha
; copy right index float to left index float
ldy _right_index
lda (P8ZP_SCRATCH_W1),y
ldy _left_index
sta (P8ZP_SCRATCH_W1),y
inc _left_index
inc _right_index
ldy _right_index
lda (P8ZP_SCRATCH_W1),y
ldy _left_index
sta (P8ZP_SCRATCH_W1),y
inc _left_index
inc _right_index
ldy _right_index
lda (P8ZP_SCRATCH_W1),y
ldy _left_index
sta (P8ZP_SCRATCH_W1),y
inc _left_index
inc _right_index
ldy _right_index
lda (P8ZP_SCRATCH_W1),y
ldy _left_index
sta (P8ZP_SCRATCH_W1),y
inc _left_index
inc _right_index
ldy _right_index
lda (P8ZP_SCRATCH_W1),y
ldy _left_index
sta (P8ZP_SCRATCH_W1),y
; pop the float off the stack into the right index float
ldy _right_index
pla
sta (P8ZP_SCRATCH_W1),y
dey
pla
sta (P8ZP_SCRATCH_W1),y
dey
pla
sta (P8ZP_SCRATCH_W1),y
dey
pla
sta (P8ZP_SCRATCH_W1),y
dey
pla
sta (P8ZP_SCRATCH_W1),y
inc _left_index
lda _right_index
sec
sbc #9
sta _right_index
dec _loop_count
bne _loop
rts
.pend
a_times_5 .proc
sta P8ZP_SCRATCH_B1
asl a
asl a
clc
adc P8ZP_SCRATCH_B1
rts
.pend
func_any_f_into_A .proc
jsr a_times_5
jmp prog8_lib.func_any_b_into_A
.pend
func_all_f_into_A .proc
jsr a_times_5
jmp prog8_lib.func_all_b_into_A
.pend
func_any_f_stack .proc
jsr a_times_5
jmp prog8_lib.func_any_b_stack
.pend
func_all_f_stack .proc
jsr a_times_5
jmp prog8_lib.func_all_b_stack
.pend
func_max_f_stack .proc
jsr func_max_f_fac1
jmp push_fac1
.pend
func_max_f_fac1 .proc
; -- max(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A
_loop_count = P8ZP_SCRATCH_REG
stx floats_store_reg
sta _loop_count
lda #255
sta _minmax_cmp+1 ; modifying
lda #<_largest_neg_float
ldy #>_largest_neg_float
_minmax_entry jsr MOVFM
- lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jsr FCOMP
_minmax_cmp cmp #255 ; modified
bne +
lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jsr MOVFM
+ lda P8ZP_SCRATCH_W1
clc
adc #5
sta P8ZP_SCRATCH_W1
bcc +
inc P8ZP_SCRATCH_W1+1
+ dec _loop_count
bne -
ldx floats_store_reg
rts
_largest_neg_float .byte 255,255,255,255,255 ; largest negative float -1.7014118345e+38
.pend
func_min_f_stack .proc
jsr func_min_f_fac1
jmp push_fac1
.pend
func_min_f_fac1 .proc
; -- min(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A
sta func_max_f_fac1._loop_count
lda #1
sta func_max_f_fac1._minmax_cmp+1
lda #<_largest_pos_float
ldy #>_largest_pos_float
jmp func_max_f_fac1._minmax_entry
_largest_pos_float .byte 255,127,255,255,255 ; largest positive float
rts
.pend
func_sum_f_stack .proc
jsr func_sum_f_fac1
jmp push_fac1
.pend
func_sum_f_fac1 .proc
; -- sum(array) -> fac1, array in P8ZP_SCRATCH_W1, num elts in A
_loop_count = P8ZP_SCRATCH_REG
stx floats_store_reg
sta _loop_count
lda #<FL_ZERO_const
ldy #>FL_ZERO_const
jsr MOVFM
- lda P8ZP_SCRATCH_W1
ldy P8ZP_SCRATCH_W1+1
jsr FADD
lda P8ZP_SCRATCH_W1
clc
adc #5
sta P8ZP_SCRATCH_W1
bcc +
inc P8ZP_SCRATCH_W1+1
+ dec _loop_count
bne -
ldx floats_store_reg
rts
.pend
+10 -1
View File
@@ -12,11 +12,20 @@ graphics {
sub enable_bitmap_mode() {
; enable bitmap screen, erase it and set colors to black/white.
c64.SCROLY |= %00100000
c64.SCROLY = %00111011
c64.SCROLX = %00001000
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
clear_screen(1, 0)
}
sub disable_bitmap_mode() {
; enables text mode, erase the text screen, color white
c64.SCROLY = %00011011
c64.SCROLX = %00001000
c64.VMCSB = (c64.VMCSB & %11110000) | %00000100 ; $1000-$2fff
txt.fill_screen(' ', 1)
}
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
memset(BITMAP_ADDRESS, 320*200/8, 0)
txt.fill_screen(pixelcolor << 4 | bgcolor, 0)
-2
View File
@@ -254,7 +254,6 @@ asmsub str2ubyte(str string @ AY) clobbers(Y) -> ubyte @A {
; -- returns the unsigned byte value of the string number argument in AY
; the number may NOT be preceded by a + sign and may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
; TODO implement optimized custom version of this instead of simply reusing str2uword
%asm {{
jmp str2uword
}}
@@ -264,7 +263,6 @@ asmsub str2byte(str string @ AY) clobbers(Y) -> ubyte @A {
; -- returns the signed byte value of the string number argument in AY
; the number may be preceded by a + or - sign but may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
; TODO implement optimized custom version of this instead of simply reusing str2word
%asm {{
jmp str2word
}}
+1
View File
@@ -149,5 +149,6 @@ sub print_f (float value) {
}
%asminclude "library:c64/floats.asm", ""
%asminclude "library:c64/floats_funcs.asm", ""
}
+8
View File
@@ -1,5 +1,6 @@
%target cx16
%import syslib
%import textio
; bitmap pixel graphics module for the CommanderX16
; wraps the graphics functions that are in ROM.
@@ -17,6 +18,13 @@ graphics {
clear_screen(1, 0)
}
sub disable_bitmap_mode() {
; enables text mode, erase the text screen, color white
void cx16.screen_set_mode(2)
txt.fill_screen(' ', 1) ; TODO doesn't seem to fully clear the text screen after returning from gfx mode
}
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
cx16.GRAPH_set_colors(pixelcolor, pixelcolor, bgcolor)
cx16.GRAPH_clear()
+1 -2
View File
@@ -151,8 +151,7 @@ io_error:
filename[0] = 'r'
filename[1] = ':'
memcopy(newfileptr, &filename+2, flen_new)
ubyte fis_ix = flen_new+2 ; TODO is temp var for array indexing
filename[fis_ix] = '='
filename[flen_new+2] = '='
memcopy(oldfileptr, &filename+3+flen_new, flen_old+1)
c64.SETNAM(3+flen_new+flen_old, filename)
c64.SETLFS(1, drivenumber, 15)
-39
View File
@@ -1244,45 +1244,6 @@ mul_word_100 .proc
; ----------- end optimized multiplications -----------
sign_b .proc
lda P8ESTACK_LO+1,x
beq _sign_zero
bmi _sign_neg
_sign_pos lda #1
sta P8ESTACK_LO+1,x
rts
_sign_neg lda #-1
_sign_zero sta P8ESTACK_LO+1,x
rts
.pend
sign_ub .proc
lda P8ESTACK_LO+1,x
beq sign_b._sign_zero
bne sign_b._sign_pos
.pend
sign_w .proc
lda P8ESTACK_HI+1,x
bmi sign_b._sign_neg
beq sign_ub
bne sign_b._sign_pos
.pend
sign_uw .proc
lda P8ESTACK_HI+1,x
beq _sign_possibly_zero
_sign_pos lda #1
sta P8ESTACK_LO+1,x
rts
_sign_possibly_zero lda P8ESTACK_LO+1,x
bne _sign_pos
sta P8ESTACK_LO+1,x
rts
.pend
; bit shifts.
; anything below 3 is done inline. anything above 7 is done via other optimizations.
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1
View File
@@ -6,4 +6,5 @@
prog8_lib {
%asminclude "library:prog8_lib.asm", ""
%asminclude "library:prog8_funcs.asm", ""
}
+48
View File
@@ -0,0 +1,48 @@
%import textio
test_stack {
asmsub test() {
%asm {{
stx _saveX
lda #13
jsr txt.chrout
lda #'-'
ldy #12
- jsr txt.chrout
dey
bne -
lda #13
jsr txt.chrout
lda #'x'
jsr txt.chrout
lda #'='
jsr txt.chrout
lda _saveX
jsr txt.print_ub
lda #' '
jsr txt.chrout
lda #'s'
jsr txt.chrout
lda #'p'
jsr txt.chrout
lda #'='
jsr txt.chrout
tsx
txa
jsr txt.print_ub
lda #13
jsr txt.chrout
lda #'-'
ldy #12
- jsr txt.chrout
dey
bne -
lda #13
jsr txt.chrout
ldx _saveX
rts
_saveX .byte 0
}}
}
}
+1 -1
View File
@@ -1 +1 @@
4.6
5.2
+27 -17
View File
@@ -55,32 +55,42 @@ private fun compileMain(args: Array<String>) {
exitProcess(1)
}
if(watchMode && moduleFiles.size<=1) {
if(watchMode) {
val watchservice = FileSystems.getDefault().newWatchService()
while(true) {
val filepath = pathFrom(moduleFiles.single()).normalize()
println("Continuous watch mode active. Main module: $filepath")
try {
println("Continuous watch mode active. Modules: $moduleFiles")
val results = mutableListOf<CompilationResult>()
for(filepathRaw in moduleFiles) {
val filepath = pathFrom(filepathRaw).normalize()
val compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, slowCodegenWarnings, compilationTarget, outputPath)
println("Imported files (now watching:)")
for (importedFile in compilationResult.importedFiles) {
print(" ")
println(importedFile)
importedFile.parent.register(watchservice, StandardWatchEventKinds.ENTRY_MODIFY)
}
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")
results.add(compilationResult)
}
val allImportedFiles = results.flatMap { it.importedFiles }
println("Imported files (now watching:)")
for (importedFile in allImportedFiles) {
print(" ")
println(importedFile)
val watchDir = importedFile.parent ?: Path.of(".")
watchDir.register(watchservice, StandardWatchEventKinds.ENTRY_MODIFY)
}
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")
var recompile=false
while(!recompile) {
val event = watchservice.take()
for(changed in event.pollEvents()) {
for (changed in event.pollEvents()) {
val changedPath = changed.context() as Path
println(" change detected: $changedPath")
if(allImportedFiles.any { it.fileName == changedPath.fileName }) {
println(" change detected: $changedPath")
recompile = true
}
}
event.reset()
println("\u001b[H\u001b[2J") // clear the screen
} catch (x: Exception) {
throw x
}
println("\u001b[H\u001b[2J") // clear the screen
}
} else {
@@ -143,7 +143,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
val reg =
when {
param.second.stack -> "stack"
param.second.registerOrPair!=null -> param.second.registerOrPair.toString()
param.second.statusflag!=null -> param.second.statusflag.toString()
else -> "?????"
+8 -11
View File
@@ -57,19 +57,11 @@ interface IFunctionCall {
}
class AsmGenInfo {
var usedAutoArrayIndexerForStatements = mutableMapOf<String, MutableSet<Statement>>()
var usedRegsaveA = false
var usedRegsaveX = false
var usedRegsaveY = false
}
interface INameScope {
val name: String
val position: Position
val statements: MutableList<Statement>
val parent: Node
val asmGenInfo: AsmGenInfo
fun linkParents(parent: Node)
@@ -224,6 +216,14 @@ interface INameScope {
null
}
fun previousSibling(stmt: Statement): Statement? {
val previousIdx = statements.indexOfFirst { it===stmt } - 1
return if(previousIdx>=0)
statements[previousIdx]
else
null
}
fun indexOfChild(stmt: Statement): Int {
val idx = statements.indexOfFirst { it===stmt }
if(idx>=0)
@@ -288,7 +288,6 @@ class Module(override val name: String,
override lateinit var parent: Node
lateinit var program: Program
override val asmGenInfo = AsmGenInfo()
val importedBy = mutableListOf<Module>()
val imports = mutableSetOf<Module>()
@@ -322,7 +321,6 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
override val position = Position("<<<global>>>", 0, 0, 0)
override val statements = mutableListOf<Statement>() // not used
override var parent: Node = ParentSentinel
override val asmGenInfo = AsmGenInfo()
override fun linkParents(parent: Node) {
modules.forEach { it.linkParents(this) }
@@ -375,7 +373,6 @@ object BuiltinFunctionScopePlaceholder : INameScope {
override val position = Position("<<placeholder>>", 0, 0, 0)
override var statements = mutableListOf<Statement>()
override var parent: Node = ParentSentinel
override val asmGenInfo = AsmGenInfo()
override fun linkParents(parent: Node) {}
}
+6 -11
View File
@@ -254,8 +254,8 @@ private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
val clobbers = asmsub_clobbers()?.clobber()?.toAst() ?: emptySet()
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.position) }
val normalReturntypes = returns.map { it.type }
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, false) }
val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag) }
val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag) }
return AsmsubDecl(name, normalParameters, normalReturntypes, paramRegisters, returnRegisters, clobbers)
}
@@ -263,13 +263,11 @@ private class AsmSubroutineParameter(name: String,
type: DataType,
val registerOrPair: RegisterOrPair?,
val statusflag: Statusflag?,
// TODO implement: val stack: Boolean,
position: Position) : SubroutineParameter(name, type, position)
private class AsmSubroutineReturn(val type: DataType,
val registerOrPair: RegisterOrPair?,
val statusflag: Statusflag?,
val stack: Boolean,
val position: Position)
private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
@@ -288,7 +286,7 @@ private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
it.datatype().toAst(),
registerorpair,
statusregister,
!it.stack?.text.isNullOrEmpty(), toPosition())
toPosition())
}
private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
@@ -342,14 +340,11 @@ private fun prog8Parser.LabeldefContext.toAst(): Statement =
Label(children[0].text, toPosition())
private fun prog8Parser.SubroutineContext.toAst() : Subroutine {
// non-asm subroutine
val returntypes = sub_return_part()?.toAst() ?: emptyList()
return Subroutine(identifier().text,
sub_params()?.toAst() ?: emptyList(),
sub_return_part()?.toAst() ?: emptyList(),
emptyList(),
emptyList(),
emptySet(),
null,
false,
returntypes,
statement_block()?.toAst() ?: mutableListOf(),
toPosition())
}
+7 -4
View File
@@ -30,13 +30,14 @@ enum class DataType {
UWORD -> targetType in setOf(UWORD, FLOAT)
WORD -> targetType in setOf(WORD, FLOAT)
FLOAT -> targetType == FLOAT
STR -> targetType == STR
in ArrayDatatypes -> targetType == this
STR -> targetType == STR || targetType == UWORD
in ArrayDatatypes -> targetType == this || targetType == UWORD
else -> false
}
infix fun isAssignableTo(targetTypes: Set<DataType>) = targetTypes.any { this isAssignableTo it }
infix fun isNotAssignableTo(targetType: DataType) = !this.isAssignableTo(targetType)
infix fun isNotAssignableTo(targetTypes: Set<DataType>) = !this.isAssignableTo(targetTypes)
infix fun largerThan(other: DataType) =
when {
@@ -79,7 +80,9 @@ enum class RegisterOrPair {
Y,
AX,
AY,
XY;
XY,
FAC1,
FAC2;
companion object {
val names by lazy { values().map { it.toString()} }
@@ -17,7 +17,7 @@ import kotlin.math.abs
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
val comparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val augmentAssignmentOperators = setOf("+", "-", "/", "*", "**", "&", "|", "^", "<<", ">>")
val augmentAssignmentOperators = setOf("+", "-", "/", "*", "**", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
sealed class Expression: Node {
abstract fun constValue(program: Program): NumericLiteralValue?
@@ -779,6 +779,9 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
// if it's just a regular variable, return null.
val struct = memberOfStruct(namespace) ?: return null
val decl = targetVarDecl(namespace)!!
if(decl.datatype!=DataType.STRUCT)
return null
val firstStructMember = struct.nameOfFirstMember()
// find the flattened var that belongs to this first struct member
val firstVarName = listOf(decl.name, firstStructMember)
@@ -821,7 +824,7 @@ class FunctionCall(override var target: IdentifierReference,
val exprfunc = func.constExpressionFunc
if(exprfunc!=null)
resultValue = exprfunc(args, position, program)
else if(func.returntype==null)
else if(func.known_returntype==null)
throw ExpressionError("builtin function ${target.nameInSource[0]} can't be used here because it doesn't return a value", position)
}
@@ -35,6 +35,13 @@ object InferredTypes {
}
override fun hashCode(): Int = Objects.hash(isVoid, datatype)
infix fun isAssignableTo(targetDt: InferredType): Boolean =
isKnown && targetDt.isKnown && (datatype!! isAssignableTo targetDt.datatype!!)
infix fun isAssignableTo(targetDt: DataType): Boolean =
isKnown && (datatype!! isAssignableTo targetDt)
infix fun isNotAssignableTo(targetDt: InferredType): Boolean = !this.isAssignableTo(targetDt)
infix fun isNotAssignableTo(targetDt: DataType): Boolean = !this.isAssignableTo(targetDt)
}
private val unknownInstance = InferredType.unknown()
+28 -25
View File
@@ -250,19 +250,19 @@ internal class AstChecker(private val program: Program,
err("parameter '${param.first.name}' should be ubyte")
}
}
for(ret in subroutine.returntypes.withIndex().zip(subroutine.asmReturnvaluesRegisters)) {
if(ret.second.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
if (ret.first.value != DataType.UBYTE && ret.first.value != DataType.BYTE)
err("return value #${ret.first.index + 1} should be (u)byte")
subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair ->
if(pair.second.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
if (pair.first != DataType.UBYTE && pair.first != DataType.BYTE)
err("return value #${index + 1} should be (u)byte")
}
else if(ret.second.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (ret.first.value != DataType.UWORD && ret.first.value != DataType.WORD
&& ret.first.value != DataType.STR && ret.first.value !in ArrayDatatypes && ret.first.value != DataType.FLOAT)
err("return value #${ret.first.index + 1} should be (u)word/address")
else if(pair.second.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (pair.first != DataType.UWORD && pair.first != DataType.WORD
&& pair.first != DataType.STR && pair.first !in ArrayDatatypes && pair.first != DataType.FLOAT)
err("return value #${index + 1} should be (u)word/address")
}
else if(ret.second.statusflag!=null) {
if (ret.first.value != DataType.UBYTE)
err("return value #${ret.first.index + 1} should be ubyte")
else if(pair.second.statusflag!=null) {
if (pair.first != DataType.UBYTE)
err("return value #${index + 1} should be ubyte")
}
}
@@ -288,6 +288,7 @@ internal class AstChecker(private val program: Program,
regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
}
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> { /* no sensible way to count this */ }
null ->
if(p.statusflag!=null)
statusflagCounts[p.statusflag] = statusflagCounts.getValue(p.statusflag) + 1
@@ -344,11 +345,11 @@ internal class AstChecker(private val program: Program,
if(assignment.value is FunctionCall) {
val stmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace)
if (stmt is Subroutine) {
val idt = assignment.target.inferType(program, assignment)
val idt = assignment.target.inferType(program)
if(!idt.isKnown) {
errors.err("return type mismatch", assignment.value.position)
}
if(stmt.returntypes.size <= 1 && stmt.returntypes.single()!=idt.typeOrElse(DataType.BYTE)) {
if(stmt.returntypes.size <= 1 && stmt.returntypes.single() isNotAssignableTo idt.typeOrElse(DataType.BYTE)) {
errors.err("return type mismatch", assignment.value.position)
}
}
@@ -378,9 +379,9 @@ internal class AstChecker(private val program: Program,
}
}
val targetDt = assignment.target.inferType(program, assignment)
val targetDt = assignment.target.inferType(program)
val valueDt = assignment.value.inferType(program)
if(valueDt.isKnown && valueDt != targetDt) {
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt)) {
if(targetDt.typeOrElse(DataType.STRUCT) in IterableDatatypes)
errors.err("cannot assign value to string or array", assignment.value.position)
else
@@ -433,7 +434,7 @@ internal class AstChecker(private val program: Program,
if (assignment is Assignment) {
val targetDatatype = assignTarget.inferType(program, assignment)
val targetDatatype = assignTarget.inferType(program)
if (targetDatatype.isKnown) {
val constVal = assignment.value.constValue(program)
if (constVal != null) {
@@ -593,8 +594,8 @@ internal class AstChecker(private val program: Program,
if(declValue!=null && decl.type==VarDeclType.VAR) {
if(decl.datatype==DataType.STRUCT) {
val valueIdt = declValue.inferType(program)
if(valueIdt.isUnknown)
throw AstException("invalid value type")
if(!valueIdt.isKnown)
throw AstException("unknown dt")
val valueDt = valueIdt.typeOrElse(DataType.STRUCT)
if(valueDt !in ArrayDatatypes)
err("initialisation of struct should be with array value", declValue.position)
@@ -769,7 +770,11 @@ internal class AstChecker(private val program: Program,
}
override fun visit(expr: PrefixExpression) {
val dt = expr.inferType(program).typeOrElse(DataType.STRUCT)
val idt = expr.inferType(program)
if(!idt.isKnown)
return // any error should be reported elsewhere
val dt = idt.typeOrElse(DataType.STRUCT)
if(expr.operator=="-") {
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
errors.err("can only take negative of a signed number type", expr.position)
@@ -984,8 +989,8 @@ internal class AstChecker(private val program: Program,
if(target.regXasResult())
errors.warn("subroutine call return value in X register is discarded and replaced by 0", position)
if(target.isAsmSubroutine) {
for (arg in args.withIndex().zip(target.parameters)) {
val argIDt = arg.first.value.inferType(program)
for (arg in args.zip(target.parameters)) {
val argIDt = arg.first.inferType(program)
if (!argIDt.isKnown)
return
}
@@ -1218,7 +1223,7 @@ internal class AstChecker(private val program: Program,
for(elt in value.value.zip(struct.statements)) {
val vardecl = elt.second as VarDecl
val valuetype = elt.first.inferType(program)
if (!valuetype.isKnown || !(valuetype.typeOrElse(DataType.STRUCT) isAssignableTo vardecl.datatype)) {
if (!valuetype.isKnown || valuetype isNotAssignableTo vardecl.datatype) {
errors.err("invalid struct member init value type $valuetype, expected ${vardecl.datatype}", elt.first.position)
return false
}
@@ -1350,9 +1355,7 @@ internal class AstChecker(private val program: Program,
else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
errors.err("cannot assign float to ${targetDatatype.name.toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
else {
if(targetDatatype==DataType.UWORD && sourceDatatype in PassByReferenceDatatypes)
errors.err("cannot assign ${sourceDatatype.name.toLowerCase()} to ${targetDatatype.name.toLowerCase()}, perhaps forgot '&' ?", position)
else
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes)
errors.err("cannot assign ${sourceDatatype.name.toLowerCase()} to ${targetDatatype.name.toLowerCase()}", position)
}
@@ -96,36 +96,30 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
val subroutine = expr.definingSubroutine()!!
val statement = expr.containingStatement()
val indexerVarPrefix = "prog8_autovar_index_"
val indexerVarName: String
val repo = subroutine.asmGenInfo.usedAutoArrayIndexerForStatements
val freeVar = repo.filter { statement !in it.value }.map { it.key }.firstOrNull()
if(freeVar==null) {
// TODO make this even smarter so that an indexerVar can be reused for a different following statement... requires updating the partOfStatement?
var indexerVar = repo.firstOrNull { it.replaces isSameAs expr.indexer }
if(indexerVar==null) {
// add another loop index var to be used for this expression
val statementId = expr.hashCode()
indexerVarName = "$indexerVarPrefix$statementId"
val indexerVarName = "$indexerVarPrefix${expr.indexer.hashCode()}"
indexerVar = AsmGenInfo.ArrayIndexerInfo(indexerVarName, expr.indexer, statement)
repo.add(indexerVar)
// create the indexer var at block level scope
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE,
null, indexerVarName, null, null, isArray = false, autogeneratedDontRemove = true, position = expr.position)
modifications.add(IAstModification.InsertFirst(vardecl, subroutine))
var statements = repo[indexerVarName]
if(statements==null) {
statements = mutableSetOf()
repo[indexerVarName] = statements
}
statements.add(statement)
} else {
// reuse an already created indexer autovar
repo.getValue(freeVar).add(statement)
indexerVarName = freeVar
}
indexerVar.used++ // keep track of how many times it it used, to avoid assigning it multiple times
// assign the indexing expression to the helper variable, and replace the indexer with just the variable
// replace the indexer with just the variable
// assign the indexing expression to the helper variable, but only if that hasn't been done already
val indexerExpression = expr.indexer.origExpression!!
val target = AssignTarget(IdentifierReference(listOf(indexerVarName), indexerExpression.position), null, null, indexerExpression.position)
val assign = Assignment(target, indexerExpression, indexerExpression.position)
val beforeWhat = expr.containingStatement()
modifications.add(IAstModification.InsertBefore(beforeWhat, assign, beforeWhat.definingScope()))
val target = AssignTarget(IdentifierReference(listOf(indexerVar.name), indexerExpression.position), null, null, indexerExpression.position)
if(indexerVar.used==1) {
val assign = Assignment(target, indexerExpression, indexerExpression.position)
modifications.add(IAstModification.InsertBefore(statement, assign, statement.definingScope()))
}
modifications.add(IAstModification.SetExpression( {
expr.indexer.indexVar = it as IdentifierReference
expr.indexer.indexNum = null
@@ -168,7 +162,7 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
val valueType = assignment.value.inferType(program)
val targetType = assignment.target.inferType(program, assignment)
val targetType = assignment.target.inferType(program)
var assignments = emptyList<Assignment>()
if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) {
@@ -279,7 +273,7 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
}
// TODO use a pointer loop instead of individual assignments
return alv.value.withIndex().map { (index, value)->
return alv.value.mapIndexed { index, value ->
val idx = ArrayIndexedExpression(identifier, ArrayIndex(NumericLiteralValue(DataType.UBYTE, index, position), position), position)
Assignment(AssignTarget(null, idx, null, position), value, value.position)
}
@@ -60,12 +60,15 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
// see if a typecast is needed to convert the value's type into the proper target type
val valueItype = assignment.value.inferType(program)
val targetItype = assignment.target.inferType(program, assignment)
val targetItype = assignment.target.inferType(program)
if(targetItype.isKnown && valueItype.isKnown) {
val targettype = targetItype.typeOrElse(DataType.STRUCT)
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
if (valuetype != targettype) {
if (valuetype isAssignableTo targettype) {
if(valuetype in IterableDatatypes && targettype==DataType.UWORD)
// special case, don't typecast STR/arrays to UWORD, we support those assignments "directly"
return noModifications
return listOf(IAstModification.ReplaceNode(
assignment.value,
TypecastExpression(assignment.value, targettype, true, assignment.value.position),
@@ -116,30 +119,30 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
when(val sub = call.target.targetStatement(scope)) {
is Subroutine -> {
for(arg in sub.parameters.zip(call.args.withIndex())) {
val argItype = arg.second.value.inferType(program)
sub.parameters.zip(call.args).forEachIndexed { index, pair ->
val argItype = pair.second.inferType(program)
if(argItype.isKnown) {
val argtype = argItype.typeOrElse(DataType.STRUCT)
val requiredType = arg.first.type
val requiredType = pair.first.type
if (requiredType != argtype) {
if (argtype isAssignableTo requiredType) {
modifications += IAstModification.ReplaceNode(
call.args[arg.second.index],
TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position),
call.args[index],
TypecastExpression(pair.second, requiredType, true, pair.second.position),
call as Node)
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
// we allow STR/ARRAY values in place of UWORD parameters. Take their address instead.
if(arg.second.value is IdentifierReference) {
if(pair.second is IdentifierReference) {
modifications += IAstModification.ReplaceNode(
call.args[arg.second.index],
AddressOf(arg.second.value as IdentifierReference, arg.second.value.position),
call.args[index],
AddressOf(pair.second as IdentifierReference, pair.second.position),
call as Node)
}
} else if(arg.second.value is NumericLiteralValue) {
val cast = (arg.second.value as NumericLiteralValue).cast(requiredType)
} else if(pair.second is NumericLiteralValue) {
val cast = (pair.second as NumericLiteralValue).cast(requiredType)
if(cast.isValid)
modifications += IAstModification.ReplaceNode(
call.args[arg.second.index],
call.args[index],
cast.valueOrZero(),
call as Node)
}
@@ -149,19 +152,19 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
}
is BuiltinFunctionStatementPlaceholder -> {
val func = BuiltinFunctions.getValue(sub.name)
for (arg in func.parameters.zip(call.args.withIndex())) {
val argItype = arg.second.value.inferType(program)
func.parameters.zip(call.args).forEachIndexed { index, pair ->
val argItype = pair.second.inferType(program)
if (argItype.isKnown) {
val argtype = argItype.typeOrElse(DataType.STRUCT)
if (arg.first.possibleDatatypes.any { argtype == it })
continue
for (possibleType in arg.first.possibleDatatypes) {
if (argtype isAssignableTo possibleType) {
modifications += IAstModification.ReplaceNode(
call.args[arg.second.index],
TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position),
call as Node)
break
if (pair.first.possibleDatatypes.all { argtype != it }) {
for (possibleType in pair.first.possibleDatatypes) {
if (argtype isAssignableTo possibleType) {
modifications += IAstModification.ReplaceNode(
call.args[index],
TypecastExpression(pair.second, possibleType, true, pair.second.position),
call as Node)
break
}
}
}
}
@@ -4,6 +4,7 @@ import prog8.ast.IFunctionCall
import prog8.ast.INameScope
import prog8.ast.Program
import prog8.ast.base.DataType
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.Expression
import prog8.ast.expressions.FunctionCall
import prog8.ast.statements.*
@@ -39,7 +40,10 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
}
fun checkTypes(call: IFunctionCall, scope: INameScope, program: Program): String? {
val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) }
val argITypes = call.args.map { it.inferType(program) }
if(argITypes.any { !it.isKnown })
throw FatalAstException("unknown dt")
val argtypes = argITypes.map { it.typeOrElse(DataType.STRUCT) }
val target = call.target.targetStatement(scope)
if (target is Subroutine) {
if(call.args.size != target.parameters.size)
@@ -67,12 +71,12 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
if(call.args.size != func.parameters.size)
return "invalid number of arguments"
val paramtypes = func.parameters.map { it.possibleDatatypes }
for (x in argtypes.zip(paramtypes).withIndex()) {
val anyCompatible = x.value.second.any { argTypeCompatible(x.value.first, it) }
argtypes.zip(paramtypes).forEachIndexed { index, pair ->
val anyCompatible = pair.second.any { argTypeCompatible(pair.first, it) }
if (!anyCompatible) {
val actual = x.value.first.toString()
val expected = x.value.second.toString()
return "argument ${x.index + 1} type mismatch, was: $actual expected: $expected"
val actual = pair.first.toString()
val expected = pair.second.toString()
return "argument ${index + 1} type mismatch, was: $actual expected: $expected"
}
}
}
@@ -44,7 +44,7 @@ class BuiltinFunctionStatementPlaceholder(val name: String, override val positio
}
}
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean)
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?)
class Block(override val name: String,
val address: Int?,
@@ -52,7 +52,6 @@ class Block(override val name: String,
val isInLibrary: Boolean,
override val position: Position) : Statement(), INameScope {
override lateinit var parent: Node
override val asmGenInfo = AsmGenInfo()
override fun linkParents(parent: Node) {
this.parent = parent
@@ -187,7 +186,12 @@ open class VarDecl(val type: VarDeclType,
fun createAuto(array: ArrayLiteralValue): VarDecl {
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
val declaredType = ArrayElementTypes.getValue(array.type.typeOrElse(DataType.STRUCT))
val arrayDt =
if(!array.type.isKnown)
throw FatalAstException("unknown dt")
else
array.type.typeOrElse(DataType.STRUCT)
val declaredType = ArrayElementTypes.getValue(arrayDt)
val arraysize = ArrayIndex.forArray(array)
return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, null, array,
isArray = true, autogeneratedDontRemove = true, position = array.position)
@@ -252,9 +256,9 @@ open class VarDecl(val type: VarDeclType,
}
fun flattenStructMembers(): MutableList<Statement> {
val result = struct!!.statements.withIndex().map {
val member = it.value as VarDecl
val initvalue = if(value!=null) (value as ArrayLiteralValue).value[it.index] else null
val result = struct!!.statements.mapIndexed { index, statement ->
val member = statement as VarDecl
val initvalue = if(value!=null) (value as ArrayLiteralValue).value[index] else null
VarDecl(
VarDeclType.VAR,
member.datatype,
@@ -309,6 +313,7 @@ class ArrayIndex(var origExpression: Expression?, // will be replaced
}
is IdentifierReference -> {
indexVar = replacement
indexNum = null
}
else -> {
throw FatalAstException("invalid replace")
@@ -343,7 +348,12 @@ class ArrayIndex(var origExpression: Expression?, // will be replaced
fun constIndex() = indexNum?.number?.toInt()
infix fun isSameAs(other: ArrayIndex) = indexNum==other.indexNum && indexVar == other.indexVar
infix fun isSameAs(other: ArrayIndex): Boolean {
return if(indexNum!=null || indexVar!=null)
indexNum==other.indexNum && indexVar == other.indexVar
else
other.origExpression!=null && origExpression!! isSameAs other.origExpression!!
}
}
open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() {
@@ -444,9 +454,9 @@ data class AssignTarget(var identifier: IdentifierReference?,
fun accept(visitor: IAstVisitor) = visitor.visit(this)
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType { // TODO why does this have the extra 'stmt' scope parameter???
fun inferType(program: Program): InferredTypes.InferredType {
if (identifier != null) {
val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown()
val symbol = program.namespace.lookup(identifier!!.nameInSource, this) ?: return InferredTypes.unknown()
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
}
@@ -637,7 +647,6 @@ class AnonymousScope(override var statements: MutableList<Statement>,
override val position: Position) : INameScope, Statement() {
override val name: String
override lateinit var parent: Node
override val asmGenInfo = AsmGenInfo()
companion object {
private var sequenceNumber = 1
@@ -676,6 +685,22 @@ class NopStatement(override val position: Position): Statement() {
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
}
class AsmGenInfo {
// This class contains various attributes that influence the assembly code generator.
// Conceptually it should be part of any INameScope.
// But because the resulting code only creates "real" scopes on a subroutine level,
// it's more consistent to only define these attributes on a Subroutine node.
var usedAutoArrayIndexerForStatements = mutableListOf<ArrayIndexerInfo>()
var usedRegsaveA = false
var usedRegsaveX = false
var usedRegsaveY = false
var usedFloatEvalResultVar1 = false
var usedFloatEvalResultVar2 = false
class ArrayIndexerInfo(val name: String, val replaces: ArrayIndex, val partOfStatement: Statement, var used: Int=0)
}
// the subroutine class covers both the normal user-defined subroutines,
// and also the predefined/ROM/register-based subroutines.
// (multiple return types can only occur for the latter type)
@@ -690,8 +715,24 @@ class Subroutine(override val name: String,
override var statements: MutableList<Statement>,
override val position: Position) : Statement(), INameScope {
constructor(name: String, parameters: List<SubroutineParameter>, returntypes: List<DataType>, statements: MutableList<Statement>, position: Position)
: this(name, parameters, returntypes, emptyList(), determineReturnRegisters(returntypes), emptySet(), null, false, statements, position)
companion object {
private fun determineReturnRegisters(returntypes: List<DataType>): List<RegisterOrStatusflag> {
// for non-asm subroutines, determine the return registers based on the type of the return value
return when(returntypes.singleOrNull()) {
in ByteDatatypes -> listOf(RegisterOrStatusflag(RegisterOrPair.A, null))
in WordDatatypes -> listOf(RegisterOrStatusflag(RegisterOrPair.AY, null))
DataType.FLOAT -> listOf(RegisterOrStatusflag(RegisterOrPair.AY, null))
null -> emptyList()
else -> listOf(RegisterOrStatusflag(RegisterOrPair.AY, null))
}
}
}
override lateinit var parent: Node
override val asmGenInfo = AsmGenInfo()
val asmGenInfo = AsmGenInfo()
val scopedname: String by lazy { makeScopedName(name) }
override fun linkParents(parent: Node) {
@@ -975,7 +1016,6 @@ class StructDecl(override val name: String,
override val position: Position): Statement(), INameScope {
override lateinit var parent: Node
override val asmGenInfo = AsmGenInfo()
override fun linkParents(parent: Node) {
this.parent = parent
@@ -15,7 +15,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
private val noModifications = emptyList<IAstModification>()
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
subroutineVariables.add(Pair(decl.name, decl))
subroutineVariables.add(decl.name to decl)
if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
// a numeric vardecl without an initial value is initialized with zero,
// unless there's already an assignment below, that initializes the value
@@ -74,7 +74,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
val decls = scope.statements.filterIsInstance<VarDecl>()
subroutineVariables.addAll(decls.map { Pair(it.name, it) })
subroutineVariables.addAll(decls.map { it.name to it })
val sub = scope.definingSubroutine()
if (sub != null) {
@@ -170,6 +170,12 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
AddressOf(typecast.expression as IdentifierReference, typecast.position),
parent
))
} else if(typecast.expression is IFunctionCall) {
return listOf(IAstModification.ReplaceNode(
typecast,
typecast.expression,
parent
))
}
} else {
errors.err("cannot cast pass-by-reference value to type ${typecast.type} (only to UWORD)", typecast.position)
+1 -1
View File
@@ -64,7 +64,7 @@ abstract class Zeropage(protected val options: CompilationOptions) {
private fun makeAllocation(address: Int, size: Int, datatype: DataType, name: String?): Int {
free.removeAll(address until address+size)
allocations[address] = Pair(name ?: "<unnamed>", datatype)
allocations[address] = (name ?: "<unnamed>") to datatype
return address
}
@@ -7,6 +7,8 @@ internal interface IAssemblyGenerator {
}
internal const val generatedLabelPrefix = "_prog8_label_"
internal const val subroutineFloatEvalResultVar1 = "_prog8_float_eval_result1"
internal const val subroutineFloatEvalResultVar2 = "_prog8_float_eval_result2"
internal interface IAssemblyProgram {
val name: String
@@ -110,7 +110,7 @@ internal object C64MachineDefinition: IMachineDefinition {
internal class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x02 // temp storage for a single byte
override val SCRATCH_REG = 0x03 // temp storage for a register
override val SCRATCH_REG = 0x03 // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0xfb // temp storage 1 for a word $fb+$fc
override val SCRATCH_W2 = 0xfd // temp storage 2 for a word $fb+$fc
@@ -8,20 +8,19 @@ import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.compiler.*
import prog8.compiler.target.*
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
import prog8.compiler.target.IAssemblyGenerator
import prog8.compiler.target.IAssemblyProgram
import prog8.compiler.target.c64.AssemblyProgram
import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
import prog8.compiler.target.c64.codegen.assignment.AssignmentAsmGen
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
import prog8.compiler.target.generatedLabelPrefix
import prog8.functions.BuiltinFunctions
import prog8.functions.FSignature
import java.io.CharConversionException
import java.nio.file.Path
import java.time.LocalDate
import java.time.LocalDateTime
@@ -200,11 +199,7 @@ internal class AsmGen(private val program: Program,
private fun assignInitialValueToVar(decl: VarDecl, variableName: List<String>) {
val asmName = asmVariableName(variableName)
val asgn = AsmAssignment(
AsmAssignSource.fromAstSource(decl.value!!, program, this),
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, decl.datatype, decl.definingSubroutine(), variableAsmName = asmName),
false, decl.position)
assignmentAsmGen.translateNormalAssignment(asgn)
assignmentAsmGen.assignExpressionToVariable(decl.value!!, asmName, decl.datatype, decl.definingSubroutine())
}
private var generatedLabelSequenceNumber: Int = 0
@@ -231,8 +226,12 @@ internal class AsmGen(private val program: Program,
}
private fun encode(str: String, altEncoding: Boolean): List<Short> {
val bytes = if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
return bytes.plus(0)
try {
val bytes = if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
return bytes.plus(0)
} catch(x: CharConversionException) {
throw AssemblyError("There was a problem converting a string to the target machine's char encoding: ${x.message}")
}
}
private fun zeropagevars2asm(statements: List<Statement>) {
@@ -253,7 +252,7 @@ internal class AsmGen(private val program: Program,
errors.handle()
out("${variable.name} = $address\t; auto zp ${variable.datatype}")
// make sure we add the var to the set of zpvars for this block
allocatedZeropageVariables[fullName] = Pair(address, variable.datatype)
allocatedZeropageVariables[fullName] = address to variable.datatype
} catch (x: ZeropageDepletedError) {
// leave it as it is.
}
@@ -550,23 +549,20 @@ internal class AsmGen(private val program: Program,
private fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
internal fun saveRegister(register: CpuRegister, dontUseStack: Boolean, scope: Subroutine?) {
internal fun saveRegister(register: CpuRegister, dontUseStack: Boolean, scope: Subroutine) {
if(dontUseStack) {
when (register) {
CpuRegister.A -> {
out(" sta _prog8_regsaveA")
if (scope != null)
scope.asmGenInfo.usedRegsaveA = true
scope.asmGenInfo.usedRegsaveA = true
}
CpuRegister.X -> {
out(" stx _prog8_regsaveX")
if (scope != null)
scope.asmGenInfo.usedRegsaveX = true
scope.asmGenInfo.usedRegsaveX = true
}
CpuRegister.Y -> {
out(" sty _prog8_regsaveY")
if (scope != null)
scope.asmGenInfo.usedRegsaveY = true
scope.asmGenInfo.usedRegsaveY = true
}
}
@@ -577,16 +573,14 @@ internal class AsmGen(private val program: Program,
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phx")
else {
out(" stx _prog8_regsaveX")
if (scope != null)
scope.asmGenInfo.usedRegsaveX = true
scope.asmGenInfo.usedRegsaveX = true
}
}
CpuRegister.Y -> {
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phy")
else {
out(" sty _prog8_regsaveY")
if (scope != null)
scope.asmGenInfo.usedRegsaveY = true
scope.asmGenInfo.usedRegsaveY = true
}
}
}
@@ -633,19 +627,7 @@ internal class AsmGen(private val program: Program,
if (builtinFunc != null) {
builtinFunctionsAsmGen.translateFunctioncallStatement(stmt, builtinFunc)
} else {
val sub = stmt.target.targetSubroutine(program.namespace)!!
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any {it.statusflag!=null}
functioncallAsmGen.translateFunctionCall(stmt, preserveStatusRegisterAfterCall)
// discard any results from the stack:
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
for ((t, reg) in returns) {
if (reg.stack) {
if (t in IntegerDatatypes || t in PassByReferenceDatatypes) out(" inx")
else if (t == DataType.FLOAT) out(" inx | inx | inx")
}
}
if(preserveStatusRegisterAfterCall)
out(" plp\t; restore status flags from call")
functioncallAsmGen.translateFunctionCallStatement(stmt)
}
}
is Assignment -> assignmentAsmGen.translate(stmt)
@@ -755,8 +737,8 @@ internal class AsmGen(private val program: Program,
internal fun translateExpression(indexer: ArrayIndex) =
expressionsAsmGen.translateExpression(indexer)
internal fun translateFunctioncallExpression(functionCall: FunctionCall, signature: FSignature) =
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature)
internal fun translateBuiltinFunctionCallExpression(functionCall: FunctionCall, signature: FSignature, resultToStack: Boolean) =
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature, resultToStack)
internal fun translateFunctionCall(functionCall: FunctionCall, preserveStatusRegisterAfterCall: Boolean) =
functioncallAsmGen.translateFunctionCall(functionCall, preserveStatusRegisterAfterCall)
@@ -764,6 +746,16 @@ internal class AsmGen(private val program: Program,
internal fun translateNormalAssignment(assign: AsmAssignment) =
assignmentAsmGen.translateNormalAssignment(assign)
internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair) =
assignmentAsmGen.assignExpressionToRegister(expr, register)
internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) =
assignmentAsmGen.assignExpressionToVariable(expr, asmVarName, dt, scope)
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair) =
assignmentAsmGen.assignVariableToRegister(asmVarName, register)
private fun translateSubroutine(sub: Subroutine) {
out("")
outputSourceLine(sub)
@@ -808,6 +800,10 @@ internal class AsmGen(private val program: Program,
out("_prog8_regsaveX .byte 0")
if(sub.asmGenInfo.usedRegsaveY)
out("_prog8_regsaveY .byte 0")
if(sub.asmGenInfo.usedFloatEvalResultVar1)
out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0")
if(sub.asmGenInfo.usedFloatEvalResultVar2)
out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
vardecls2asm(sub.statements)
out(" .pend\n")
}
@@ -912,15 +908,16 @@ internal class AsmGen(private val program: Program,
}
}
else -> {
translateExpression(stmt.iterations!!)
val dt = stmt.iterations!!.inferType(program).typeOrElse(DataType.STRUCT)
when (dt) {
val dt = stmt.iterations!!.inferType(program)
if(!dt.isKnown)
throw AssemblyError("unknown dt")
when (dt.typeOrElse(DataType.STRUCT)) {
in ByteDatatypes -> {
out(" inx | lda P8ESTACK_LO,x")
assignExpressionToRegister(stmt.iterations!!, RegisterOrPair.A)
repeatByteCountInA(null, repeatLabel, endLabel, stmt.body)
}
in WordDatatypes -> {
out(" inx | lda P8ESTACK_LO,x | ldy P8ESTACK_HI,x")
assignExpressionToRegister(stmt.iterations!!, RegisterOrPair.AY)
repeatWordCountInAY(null, repeatLabel, endLabel, stmt.body)
}
else -> throw AssemblyError("invalid loop expression datatype $dt")
@@ -1016,16 +1013,16 @@ $counterVar .byte 0""")
}
private fun translate(stmt: WhenStatement) {
expressionsAsmGen.translateExpression(stmt.condition)
val endLabel = makeLabel("choice_end")
val choiceBlocks = mutableListOf<Pair<String, AnonymousScope>>()
val conditionDt = stmt.condition.inferType(program)
if(!conditionDt.isKnown)
throw AssemblyError("unknown condition dt")
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes)
out(" inx | lda P8ESTACK_LO,x")
assignExpressionToRegister(stmt.condition, RegisterOrPair.A)
else
out(" inx | lda P8ESTACK_LO,x | ldy P8ESTACK_HI,x")
assignExpressionToRegister(stmt.condition, RegisterOrPair.AY)
for(choice in stmt.choices) {
val choiceLabel = makeLabel("choice")
if(choice.values==null) {
@@ -1033,7 +1030,7 @@ $counterVar .byte 0""")
translate(choice.statements)
out(" jmp $endLabel")
} else {
choiceBlocks.add(Pair(choiceLabel, choice.statements))
choiceBlocks.add(choiceLabel to choice.statements)
for (cv in choice.values!!) {
val value = (cv as NumericLiteralValue).number.toInt()
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes) {
@@ -1139,7 +1136,9 @@ $counterVar .byte 0""")
"%breakpoint" -> {
val label = "_prog8_breakpoint_${breakpointLabels.size+1}"
breakpointLabels.add(label)
out("$label\tnop")
out("""
nop
$label nop""")
}
}
}
@@ -1165,7 +1164,28 @@ $counterVar .byte 0""")
}
private fun translate(ret: Return) {
ret.value?.let { expressionsAsmGen.translateExpression(it) }
ret.value?.let { returnvalue ->
val sub = ret.definingSubroutine()!!
val returnType = sub.returntypes.single()
val returnReg = sub.asmReturnvaluesRegisters.single()
if(returnReg.registerOrPair==null)
throw AssemblyError("normal subroutines can't return value in status register directly")
when (returnType) {
in IntegerDatatypes -> {
assignmentAsmGen.assignExpressionToRegister(returnvalue, returnReg.registerOrPair)
}
DataType.FLOAT -> {
// return the float value via FAC1
assignExpressionToRegister(returnvalue, RegisterOrPair.FAC1)
}
else -> {
// all else take its address and assign that also to AY register pair
val addrofValue = AddressOf(returnvalue as IdentifierReference, returnvalue.position)
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnReg.registerOrPair)
}
}
}
out(" rts")
}
@@ -1174,33 +1194,33 @@ $counterVar .byte 0""")
assemblyLines.add(assembly)
}
internal fun signExtendStackLsb(valueDt: DataType) {
// sign extend signed byte on stack to signed word
internal fun signExtendAYlsb(valueDt: DataType) {
// sign extend signed byte in A to full word in AY
when(valueDt) {
DataType.UBYTE -> {
out(" lda #0 | sta P8ESTACK_HI+1,x")
}
DataType.BYTE -> {
out("""
lda P8ESTACK_LO+1,x
ora #$7f
bmi +
lda #0
+ sta P8ESTACK_HI+1,x""")
}
DataType.UBYTE -> out(" ldy #0")
DataType.BYTE -> out(" jsr prog8_lib.sign_extend_AY_byte")
else -> throw AssemblyError("need byte type")
}
}
internal fun signExtendStackLsb(valueDt: DataType) {
// sign extend signed byte on stack to signed word on stack
when(valueDt) {
DataType.UBYTE -> out(" lda #0 | sta P8ESTACK_HI+1,x")
DataType.BYTE -> out(" jsr prog8_lib.sign_extend_stack_byte")
else -> throw AssemblyError("need byte type")
}
}
internal fun signExtendVariableLsb(asmvar: String, valueDt: DataType) {
// sign extend signed byte in a word variable
// sign extend signed byte in a var to a full word in that variable
when(valueDt) {
DataType.UBYTE -> {
out(" lda #0 | sta $asmvar+1")
}
DataType.BYTE -> {
out("""
lda $asmvar+1
lda $asmvar
ora #$7f
bmi +
lda #0
@@ -179,8 +179,8 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
}
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
// TODO not sure if this is correct in all situations....:
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated
// TODO this is not true if X is not a regular RAM memory address (but instead mapped I/O or ROM)
val mods = mutableListOf<Modification>()
for (pair in linesByFour) {
val first = pair[0].value.trimStart()
@@ -1,53 +1,62 @@
package prog8.compiler.target.c64.codegen
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.ArrayIndex
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.statements.Subroutine
import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.Cx16Target
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
import prog8.compiler.target.c64.codegen.assignment.SourceStorageKind
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
import prog8.compiler.target.subroutineFloatEvalResultVar2
import prog8.compiler.toHex
import prog8.functions.FSignature
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen) {
internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FSignature) {
translateFunctioncall(fcall, func, false)
internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FSignature, resultToStack: Boolean) {
translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack)
}
internal fun translateFunctioncallStatement(fcall: FunctionCallStatement, func: FSignature) {
translateFunctioncall(fcall, func, true)
translateFunctioncall(fcall, func, discardResult = true, resultToStack = false)
}
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean) {
val functionName = fcall.target.nameInSource.last()
if (discardResult) {
if (func.pure)
return // can just ignore the whole function call altogether
else if (func.returntype != null)
throw AssemblyError("discarding result of non-pure function $fcall")
}
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean) {
if (discardResult && func.pure)
return // can just ignore the whole function call altogether
when (functionName) {
"msb" -> funcMsb(fcall)
"lsb" -> funcLsb(fcall)
"mkword" -> funcMkword(fcall, func)
"abs" -> funcAbs(fcall, func)
if(discardResult && resultToStack)
throw AssemblyError("cannot both discard the result AND put it onto stack")
val scope = (fcall as Node).definingSubroutine()!!
when (func.name) {
"msb" -> funcMsb(fcall, resultToStack)
"lsb" -> funcLsb(fcall, resultToStack)
"mkword" -> funcMkword(fcall, resultToStack)
"abs" -> funcAbs(fcall, func, resultToStack, scope)
"swap" -> funcSwap(fcall)
"strlen" -> funcStrlen(fcall)
"min", "max", "sum" -> funcMinMaxSum(fcall, functionName)
"any", "all" -> funcAnyAll(fcall, functionName)
"sgn" -> funcSgn(fcall, func)
"min", "max" -> funcMinMax(fcall, func, resultToStack)
"sum" -> funcSum(fcall, resultToStack)
"any", "all" -> funcAnyAll(fcall, func, resultToStack)
"sin8", "sin8u", "sin16", "sin16u",
"cos8", "cos8u", "cos16", "cos16u" -> funcSinCosInt(fcall, func, resultToStack, scope)
"sgn" -> funcSgn(fcall, func, resultToStack, scope)
"sin", "cos", "tan", "atan",
"ln", "log2", "sqrt", "rad",
"deg", "round", "floor", "ceil",
"rdnf" -> funcVariousFloatFuncs(fcall, func, functionName)
"rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, scope)
"rnd", "rndw" -> funcRnd(func, resultToStack)
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, scope)
"rol" -> funcRol(fcall)
"rol2" -> funcRol2(fcall)
"ror" -> funcRor(fcall)
@@ -63,17 +72,110 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
// restore all registers and cpu status flag
asmgen.out(" pla | tay | pla | tax | pla | plp")
}
"read_flags" -> {
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_read_flags_stack")
else
asmgen.out(" php | pla")
}
"clear_carry" -> asmgen.out(" clc")
"set_carry" -> asmgen.out(" sec")
"clear_irqd" -> asmgen.out(" cli")
"set_irqd" -> asmgen.out(" sei")
else -> {
translateFunctionArguments(fcall.args, func)
asmgen.out(" jsr prog8_lib.func_$functionName")
"strlen" -> funcStrlen(fcall, resultToStack)
"strcmp" -> funcStrcmp(fcall, func, resultToStack, scope)
"memcopy", "memset", "memsetw" -> funcMemSetCopy(fcall, func, scope)
"substr", "leftstr", "rightstr" -> {
translateArguments(fcall.args, func, scope)
asmgen.out(" jsr prog8_lib.func_${func.name}")
}
"exit" -> {
translateArguments(fcall.args, func, scope)
asmgen.out(" jmp prog8_lib.func_exit")
}
else -> TODO("missing asmgen for builtin func ${func.name}")
}
}
private fun funcMemSetCopy(fcall: IFunctionCall, func: FSignature, scope: Subroutine) {
if(CompilationTarget.instance is Cx16Target) {
when(func.name) {
"memset" -> {
// use the ROM function of the Cx16
asmgen.assignExpressionToVariable(fcall.args[0], "cx16.r0", DataType.UWORD, scope)
asmgen.assignExpressionToVariable(fcall.args[1], "cx16.r1", DataType.UWORD, scope)
asmgen.assignExpressionToRegister(fcall.args[2], RegisterOrPair.A)
val sub = (fcall as FunctionCallStatement).definingSubroutine()!!
asmgen.saveRegister(CpuRegister.X, false, sub)
asmgen.out(" jsr cx16.memory_fill")
asmgen.restoreRegister(CpuRegister.X, false)
}
"memcopy" -> {
val count = fcall.args[2].constValue(program)?.number?.toInt()
val countDt = fcall.args[2].inferType(program)
if((count!=null && count <= 255) || countDt.istype(DataType.UBYTE) || countDt.istype(DataType.BYTE)) {
// fast memcopy of up to 255
translateArguments(fcall.args, func, scope)
asmgen.out(" jsr prog8_lib.func_memcopy255")
return
}
// use the ROM function of the Cx16
asmgen.assignExpressionToVariable(fcall.args[0], "cx16.r0", DataType.UWORD, scope)
asmgen.assignExpressionToVariable(fcall.args[1], "cx16.r1", DataType.UWORD, scope)
asmgen.assignExpressionToVariable(fcall.args[2], "cx16.r2", DataType.UWORD, scope)
val sub = (fcall as FunctionCallStatement).definingSubroutine()!!
asmgen.saveRegister(CpuRegister.X, false, sub)
asmgen.out(" jsr cx16.memory_copy")
asmgen.restoreRegister(CpuRegister.X, false)
}
"memsetw" -> {
translateArguments(fcall.args, func, scope)
asmgen.out(" jsr prog8_lib.func_memsetw")
}
}
} else {
if(func.name=="memcopy") {
val count = fcall.args[2].constValue(program)?.number?.toInt()
val countDt = fcall.args[2].inferType(program)
if((count!=null && count <= 255) || countDt.istype(DataType.UBYTE) || countDt.istype(DataType.BYTE)) {
translateArguments(fcall.args, func, scope)
asmgen.out(" jsr prog8_lib.func_memcopy255")
return
}
}
translateArguments(fcall.args, func, scope)
asmgen.out(" jsr prog8_lib.func_${func.name}")
}
}
private fun funcStrcmp(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine) {
translateArguments(fcall.args, func, scope)
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_strcmp_stack")
else
asmgen.out(" jsr prog8_lib.func_strcmp")
}
private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine) {
translateArguments(fcall.args, func, scope)
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
else
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
}
private fun funcSinCosInt(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine) {
translateArguments(fcall.args, func, scope)
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_${func.name}_stack")
else
when(func.name) {
"sin8", "sin8u", "cos8", "cos8u" -> asmgen.out(" jsr prog8_lib.func_${func.name}_into_A")
"sin16", "sin16u", "cos16", "cos16u" -> asmgen.out(" jsr prog8_lib.func_${func.name}_into_AY")
}
}
private fun funcReverse(fcall: IFunctionCall) {
val variable = fcall.args.single()
if (variable is IdentifierReference) {
@@ -83,33 +185,30 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
when (decl.datatype) {
DataType.ARRAY_UB, DataType.ARRAY_B -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.reverse_b
""")
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.func_reverse_b""")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.reverse_w
""")
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.func_reverse_w""")
}
DataType.ARRAY_F -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr prog8_lib.reverse_f
""")
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
jsr floats.func_reverse_f""")
}
else -> throw AssemblyError("weird type")
}
@@ -125,25 +224,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
when (decl.datatype) {
DataType.ARRAY_UB, DataType.ARRAY_B -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
sta P8ZP_SCRATCH_B1
""")
asmgen.out(if (decl.datatype == DataType.ARRAY_UB) " jsr prog8_lib.sort_ub" else " jsr prog8_lib.sort_b")
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements""")
asmgen.out(if (decl.datatype == DataType.ARRAY_UB) " jsr prog8_lib.func_sort_ub" else " jsr prog8_lib.func_sort_b")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements
sta P8ZP_SCRATCH_B1
""")
asmgen.out(if (decl.datatype == DataType.ARRAY_UW) " jsr prog8_lib.sort_uw" else " jsr prog8_lib.sort_w")
lda #<$varName
ldy #>$varName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$numElements""")
asmgen.out(if (decl.datatype == DataType.ARRAY_UW) " jsr prog8_lib.func_sort_uw" else " jsr prog8_lib.func_sort_w")
}
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
else -> throw AssemblyError("weird type")
@@ -159,8 +254,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UBYTE -> {
when (what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.arrayvar)
asmgen.translateExpression(what.indexer)
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror2", 'b')
asmgen.out(" jsr prog8_lib.ror2_array_ub")
}
is DirectMemoryRead -> {
@@ -168,7 +262,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
} else {
asmgen.translateExpression(what.addressExpression)
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
}
}
@@ -182,8 +276,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UWORD -> {
when (what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.arrayvar)
asmgen.translateExpression(what.indexer)
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror2", 'w')
asmgen.out(" jsr prog8_lib.ror2_array_uw")
}
is IdentifierReference -> {
@@ -204,8 +297,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UBYTE -> {
when (what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.arrayvar)
asmgen.translateExpression(what.indexer)
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror", 'b')
asmgen.out(" jsr prog8_lib.ror_array_ub")
}
is DirectMemoryRead -> {
@@ -213,15 +305,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" ror ${number.toHex()}")
} else {
asmgen.translateExpression(what.addressExpression)
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
inx
lda P8ESTACK_LO,x
sta (+) + 1
lda P8ESTACK_HI,x
sta (+) + 2
+ ror ${'$'}ffff ; modified
""")
sta (+) + 1
sty (+) + 2
+ ror ${'$'}ffff ; modified""")
}
}
is IdentifierReference -> {
@@ -234,8 +322,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UWORD -> {
when (what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.arrayvar)
asmgen.translateExpression(what.indexer)
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror", 'w')
asmgen.out(" jsr prog8_lib.ror_array_uw")
}
is IdentifierReference -> {
@@ -256,8 +343,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UBYTE -> {
when (what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.arrayvar)
asmgen.translateExpression(what.indexer)
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol2", 'b')
asmgen.out(" jsr prog8_lib.rol2_array_ub")
}
is DirectMemoryRead -> {
@@ -265,7 +351,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
} else {
asmgen.translateExpression(what.addressExpression)
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
}
}
@@ -279,8 +365,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UWORD -> {
when (what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.arrayvar)
asmgen.translateExpression(what.indexer)
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol2", 'w')
asmgen.out(" jsr prog8_lib.rol2_array_uw")
}
is IdentifierReference -> {
@@ -301,8 +386,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UBYTE -> {
when (what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.arrayvar)
asmgen.translateExpression(what.indexer)
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol", 'b')
asmgen.out(" jsr prog8_lib.rol_array_ub")
}
is DirectMemoryRead -> {
@@ -310,15 +394,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" rol ${number.toHex()}")
} else {
asmgen.translateExpression(what.addressExpression)
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
inx
lda P8ESTACK_LO,x
sta (+) + 1
lda P8ESTACK_HI,x
sta (+) + 2
+ rol ${'$'}ffff ; modified
""")
sta (+) + 1
sty (+) + 2
+ rol ${'$'}ffff ; modified""")
}
}
is IdentifierReference -> {
@@ -331,8 +411,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UWORD -> {
when (what) {
is ArrayIndexedExpression -> {
asmgen.translateExpression(what.arrayvar)
asmgen.translateExpression(what.indexer)
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol", 'w')
asmgen.out(" jsr prog8_lib.rol_array_uw")
}
is IdentifierReference -> {
@@ -346,66 +425,124 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, functionName: String) {
translateFunctionArguments(fcall.args, func)
asmgen.out(" jsr floats.func_$functionName")
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) {
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
val indexerExpr = if(indexer.indexVar!=null) indexer.indexVar!! else indexer.indexNum!!
asmgen.assignExpressionToVariable(indexerExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
}
private fun funcSgn(fcall: IFunctionCall, func: FSignature) {
translateFunctionArguments(fcall.args, func)
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine) {
translateArguments(fcall.args, func, scope)
if(resultToStack)
asmgen.out(" jsr floats.func_${func.name}_stack")
else
asmgen.out(" jsr floats.func_${func.name}_fac1")
}
private fun funcSgn(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine) {
translateArguments(fcall.args, func, scope)
val dt = fcall.args.single().inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> asmgen.out(" jsr math.sign_ub")
DataType.BYTE -> asmgen.out(" jsr math.sign_b")
DataType.UWORD -> asmgen.out(" jsr math.sign_uw")
DataType.WORD -> asmgen.out(" jsr math.sign_w")
DataType.FLOAT -> asmgen.out(" jsr floats.sign_f")
else -> throw AssemblyError("weird type $dt")
if(resultToStack) {
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_stack")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_stack")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_stack")
DataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_stack")
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_stack")
else -> throw AssemblyError("weird type $dt")
}
} else {
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
DataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_into_A")
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
else -> throw AssemblyError("weird type $dt")
}
}
}
private fun funcAnyAll(fcall: IFunctionCall, functionName: String) {
outputPushAddressAndLenghtOfArray(fcall.args[0])
private fun funcAnyAll(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean) {
outputAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${functionName}_f")
else -> throw AssemblyError("weird type $dt")
if(resultToStack) {
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_stack")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_stack")
else -> throw AssemblyError("weird type $dt")
}
} else {
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A")
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_A")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A")
else -> throw AssemblyError("weird type $dt")
}
}
}
private fun funcMinMaxSum(fcall: IFunctionCall, functionName: String) {
outputPushAddressAndLenghtOfArray(fcall.args[0])
private fun funcMinMax(fcall: IFunctionCall, function: FSignature, resultToStack: Boolean) {
outputAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_ub")
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${functionName}_f")
else -> throw AssemblyError("weird type $dt")
if(resultToStack) {
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_ub_stack")
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack")
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${function.name}_uw_stack")
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_stack")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_stack")
else -> throw AssemblyError("weird type $dt")
}
} else {
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_ub_into_A")
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A")
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY")
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_fac1")
else -> throw AssemblyError("weird type $dt")
}
}
}
private fun funcStrlen(fcall: IFunctionCall) {
private fun funcSum(fcall: IFunctionCall, resultToStack: Boolean) {
outputAddressAndLenghtOfArray(fcall.args[0])
val dt = fcall.args.single().inferType(program)
if(resultToStack) {
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_sum_ub_stack")
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_sum_b_stack")
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_sum_uw_stack")
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_sum_w_stack")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_sum_f_stack")
else -> throw AssemblyError("weird type $dt")
}
} else {
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY")
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_sum_b_into_AY")
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY")
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_sum_w_into_AY")
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_sum_f_fac1")
else -> throw AssemblyError("weird type $dt")
}
}
}
private fun funcStrlen(fcall: IFunctionCall, resultToStack: Boolean) {
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
val type = fcall.args[0].inferType(program)
when {
type.istype(DataType.STR) -> asmgen.out("""
lda #<$name
ldy #>$name
jsr prog8_lib.strlen
sta P8ESTACK_LO,x
dex""")
type.istype(DataType.UWORD) -> asmgen.out("""
lda $name
ldy $name+1
jsr prog8_lib.strlen
sta P8ESTACK_LO,x
dex""")
type.istype(DataType.STR) -> asmgen.out(" lda #<$name | ldy #>$name")
type.istype(DataType.UWORD) -> asmgen.out(" lda $name | ldy $name+1")
else -> throw AssemblyError("strlen requires str or uword arg")
}
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_strlen_stack")
else
asmgen.out(" jsr prog8_lib.func_strlen_into_A")
}
private fun funcSwap(fcall: IFunctionCall) {
@@ -444,7 +581,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
sta P8ZP_SCRATCH_W2
lda #>$secondName
sta P8ZP_SCRATCH_W2+1
jsr floats.swap_floats
jsr floats.func_swap_f
""")
return
}
@@ -480,7 +617,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
if(first is ArrayIndexedExpression && second is ArrayIndexedExpression) {
val arrayVarName1 = asmgen.asmVariableName(first.arrayvar)
val arrayVarName2 = asmgen.asmVariableName(second.arrayvar)
val elementDt = first.inferType(program).typeOrElse(DataType.STRUCT)
val elementIDt = first.inferType(program)
if(!elementIDt.isKnown)
throw AssemblyError("unknown dt")
val elementDt = elementIDt.typeOrElse(DataType.STRUCT)
val firstNum = first.indexer.indexNum
val firstVar = first.indexer.indexVar
@@ -502,7 +642,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
// all other types of swap() calls are done via the evaluation stack
// all other types of swap() calls are done via a temporary variable
fun targetFromExpr(expr: Expression, datatype: DataType): AsmAssignTarget {
return when (expr) {
is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, expr.definingSubroutine(), variableAsmName = asmgen.asmVariableName(expr))
@@ -512,21 +653,43 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
asmgen.translateExpression(first)
asmgen.translateExpression(second)
val datatype = first.inferType(program).typeOrElse(DataType.STRUCT)
val assignFirst = AsmAssignment(
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, datatype),
targetFromExpr(first, datatype),
false, first.position
)
val assignSecond = AsmAssignment(
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, datatype),
targetFromExpr(second, datatype),
false, second.position
)
asmgen.translateNormalAssignment(assignFirst)
asmgen.translateNormalAssignment(assignSecond)
when(datatype) {
in ByteDatatypes, in WordDatatypes -> {
asmgen.assignExpressionToVariable(first, "P8ZP_SCRATCH_W1", datatype, null)
asmgen.assignExpressionToVariable(second, "P8ZP_SCRATCH_W2", datatype, null)
val assignFirst = AsmAssignment(
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W2"),
targetFromExpr(first, datatype),
false, first.position
)
val assignSecond = AsmAssignment(
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W1"),
targetFromExpr(second, datatype),
false, second.position
)
asmgen.translateNormalAssignment(assignFirst)
asmgen.translateNormalAssignment(assignSecond)
}
DataType.FLOAT -> {
// via evaluation stack
asmgen.translateExpression(first)
asmgen.translateExpression(second)
val assignFirst = AsmAssignment(
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT),
targetFromExpr(first, datatype),
false, first.position
)
val assignSecond = AsmAssignment(
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT),
targetFromExpr(second, datatype),
false, second.position
)
asmgen.translateNormalAssignment(assignFirst)
asmgen.translateNormalAssignment(assignSecond)
}
else -> throw AssemblyError("weird swap dt")
}
}
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexValue2: NumericLiteralValue) {
@@ -759,25 +922,52 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
translateFunctionArguments(fcall.args, func)
val dt = fcall.args.single().inferType(program)
when (dt.typeOrElse(DataType.STRUCT)) {
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w")
DataType.FLOAT -> asmgen.out(" jsr floats.abs_f")
else -> throw AssemblyError("weird type")
private fun funcAbs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine) {
translateArguments(fcall.args, func, scope)
val dt = fcall.args.single().inferType(program).typeOrElse(DataType.STRUCT)
if(resultToStack) {
when (dt) {
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b_stack")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w_stack")
DataType.FLOAT -> asmgen.out(" jsr floats.abs_f_stack")
else -> throw AssemblyError("weird type")
}
} else {
when (dt) {
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b_into_A")
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w_into_AY")
DataType.FLOAT -> asmgen.out(" jsr floats.abs_f_fac1")
else -> throw AssemblyError("weird type")
}
}
}
private fun funcMkword(fcall: IFunctionCall, func: FSignature) {
// trick: push the args in reverse order (msb first, lsb second) this saves some instructions
asmgen.translateExpression(fcall.args[1])
asmgen.translateExpression(fcall.args[0])
asmgen.out(" inx | lda P8ESTACK_LO,x | sta P8ESTACK_HI+1,x")
private fun funcRnd(func: FSignature, resultToStack: Boolean) {
when(func.name) {
"rnd" -> {
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_rnd_stack")
else
asmgen.out(" jsr math.randbyte")
}
"rndw" -> {
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_rndw_stack")
else
asmgen.out(" jsr math.randword")
}
else -> throw AssemblyError("wrong func")
}
}
private fun funcMsb(fcall: IFunctionCall) {
private fun funcMkword(fcall: IFunctionCall, resultToStack: Boolean) {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if(resultToStack)
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
}
private fun funcMsb(fcall: IFunctionCall, resultToStack: Boolean) {
val arg = fcall.args.single()
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
throw AssemblyError("msb required word argument")
@@ -785,47 +975,114 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
throw AssemblyError("msb(const) should have been const-folded away")
if (arg is IdentifierReference) {
val sourceName = asmgen.asmVariableName(arg)
asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex")
asmgen.out(" lda $sourceName+1")
if (resultToStack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
asmgen.translateExpression(arg)
asmgen.out(" lda P8ESTACK_HI+1,x | sta P8ESTACK_LO+1,x")
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
if (resultToStack)
asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
else
asmgen.out(" tya")
}
}
private fun funcLsb(fcall: IFunctionCall) {
private fun funcLsb(fcall: IFunctionCall, resultToStack: Boolean) {
val arg = fcall.args.single()
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
throw AssemblyError("lsb required word argument")
if (arg is NumericLiteralValue)
throw AssemblyError("lsb(const) should have been const-folded away")
if (arg is IdentifierReference) {
val sourceName = asmgen.asmVariableName(arg)
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex")
asmgen.out(" lda $sourceName")
if (resultToStack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
asmgen.translateExpression(arg)
// just ignore any high-byte
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
if (resultToStack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
}
}
private fun outputPushAddressAndLenghtOfArray(arg: Expression) {
private fun outputAddressAndLenghtOfArray(arg: Expression) {
// address in P8ZP_SCRATCH_W1, number of elements in A
arg as IdentifierReference
val identifierName = asmgen.asmVariableName(arg)
val size = arg.targetVarDecl(program.namespace)!!.arraysize!!.constIndex()!!
asmgen.out("""
lda #<$identifierName
sta P8ESTACK_LO,x
lda #>$identifierName
sta P8ESTACK_HI,x
dex
ldy #>$identifierName
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #$size
sta P8ESTACK_LO,x
dex
""")
}
private fun translateFunctionArguments(args: MutableList<Expression>, signature: FSignature) {
args.forEach {
asmgen.translateExpression(it)
private fun translateArguments(args: MutableList<Expression>, signature: FSignature, scope: Subroutine) {
val callConv = signature.callConvention(args.map { it.inferType(program).typeOrElse(DataType.STRUCT) })
fun getSourceForFloat(value: Expression): AsmAssignSource {
return when (value) {
is IdentifierReference -> {
val addr = AddressOf(value, value.position)
AsmAssignSource.fromAstSource(addr, program, asmgen)
}
is NumericLiteralValue -> {
throw AssemblyError("float literals should have been converted into autovar")
}
else -> {
scope.asmGenInfo.usedFloatEvalResultVar2 = true
val variable = IdentifierReference(listOf(subroutineFloatEvalResultVar2), value.position)
val addr = AddressOf(variable, value.position)
addr.linkParents(value)
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT, scope)
AsmAssignSource.fromAstSource(addr, program, asmgen)
}
}
}
args.zip(callConv.params).zip(signature.parameters).forEach {
val paramName = it.second.name
val conv = it.first.second
val value = it.first.first
when {
conv.variable -> {
val varname = "prog8_lib.func_${signature.name}._arg_${paramName}"
val src = when (conv.dt) {
DataType.FLOAT -> getSourceForFloat(value)
in PassByReferenceDatatypes -> {
// put the address of the argument in AY
val addr = AddressOf(value as IdentifierReference, value.position)
AsmAssignSource.fromAstSource(addr, program, asmgen)
}
else -> {
AsmAssignSource.fromAstSource(value, program, asmgen)
}
}
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, conv.dt, null, variableAsmName = varname)
val assign = AsmAssignment(src, tgt, false, value.position)
asmgen.translateNormalAssignment(assign)
}
conv.reg != null -> {
val src = when (conv.dt) {
DataType.FLOAT -> getSourceForFloat(value)
in PassByReferenceDatatypes -> {
// put the address of the argument in AY
val addr = AddressOf(value as IdentifierReference, value.position)
AsmAssignSource.fromAstSource(addr, program, asmgen)
}
else -> {
AsmAssignSource.fromAstSource(value, program, asmgen)
}
}
val tgt = AsmAssignTarget.fromRegisters(conv.reg, null, program, asmgen)
val assign = AsmAssignment(src, tgt, false, value.position)
asmgen.translateNormalAssignment(assign)
}
else -> throw AssemblyError("callconv")
}
}
}
File diff suppressed because it is too large. Load diff
@@ -1,15 +1,13 @@
package prog8.compiler.target.c64.codegen
import prog8.ast.Program
import prog8.ast.base.ArrayElementTypes
import prog8.ast.base.DataType
import prog8.ast.base.RegisterOrPair
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.RangeExpr
import prog8.ast.statements.ForLoop
import prog8.compiler.AssemblyError
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
import prog8.compiler.toHex
import kotlin.math.absoluteValue
@@ -18,7 +16,7 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
internal fun translate(stmt: ForLoop) {
val iterableDt = stmt.iterable.inferType(program)
if(!iterableDt.isKnown)
throw AssemblyError("can't determine iterable dt")
throw AssemblyError("unknown dt")
when(stmt.iterable) {
is RangeExpr -> {
val range = (stmt.iterable as RangeExpr).toConstantIntegerRange()
@@ -51,23 +49,17 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
val incdec = if(stepsize==1) "inc" else "dec"
// loop over byte range via loopvar
val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.assignExpressionToVariable(range.from, varname, ArrayElementTypes.getValue(iterableDt), null)
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayElementTypes.getValue(iterableDt), null)
asmgen.out(loopLabel)
asmgen.translate(stmt.body)
asmgen.out("""
inx
lda P8ESTACK_LO,x
sta $varname
lda P8ESTACK_LO+1,x
sta $modifiedLabel+1
$loopLabel""")
asmgen.translate(stmt.body)
asmgen.out("""
lda $varname
$modifiedLabel cmp #0 ; modified
beq $endLabel
$incdec $varname
jmp $loopLabel
$endLabel inx""")
lda $varname
$modifiedLabel cmp #0 ; modified
beq $endLabel
$incdec $varname
jmp $loopLabel
$endLabel""")
} else {
@@ -75,36 +67,29 @@ $endLabel inx""")
// loop over byte range via loopvar
val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.translateExpression(range.to)
asmgen.translateExpression(range.from)
asmgen.out("""
inx
lda P8ESTACK_LO,x
sta $varname
lda P8ESTACK_LO+1,x
sta $modifiedLabel+1
$loopLabel""")
asmgen.assignExpressionToVariable(range.from, varname, ArrayElementTypes.getValue(iterableDt), null)
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayElementTypes.getValue(iterableDt), null)
asmgen.out(loopLabel)
asmgen.translate(stmt.body)
asmgen.out("""
lda $varname""")
if(stepsize>0) {
asmgen.out("""
clc
adc #$stepsize
sta $varname
$modifiedLabel cmp #0 ; modified
bcc $loopLabel
beq $loopLabel""")
lda $varname
clc
adc #$stepsize
sta $varname
$modifiedLabel cmp #0 ; modified
bmi $loopLabel
beq $loopLabel""")
} else {
asmgen.out("""
sec
sbc #${stepsize.absoluteValue}
sta $varname
$modifiedLabel cmp #0 ; modified
bcs $loopLabel""")
lda $varname
sec
sbc #${stepsize.absoluteValue}
sta $varname
$modifiedLabel cmp #0 ; modified
bpl $loopLabel""")
}
asmgen.out("""
$endLabel inx""")
asmgen.out(endLabel)
}
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
@@ -113,13 +98,11 @@ $endLabel inx""")
// words, step 1 or -1
stepsize == 1 || stepsize == -1 -> {
asmgen.translateExpression(range.to)
assignLoopvar(stmt, range)
val varname = asmgen.asmVariableName(stmt.loopVar)
assignLoopvar(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
asmgen.out("""
lda P8ESTACK_HI+1,x
sta $modifiedLabel+1
lda P8ESTACK_LO+1,x
sty $modifiedLabel+1
sta $modifiedLabel2+1
$loopLabel""")
asmgen.translate(stmt.body)
@@ -146,21 +129,17 @@ $modifiedLabel2 cmp #0 ; modified
jmp $loopLabel""")
}
asmgen.out(endLabel)
asmgen.out(" inx")
}
stepsize > 0 -> {
// (u)words, step >= 2
asmgen.translateExpression(range.to)
asmgen.out("""
lda P8ESTACK_HI+1,x
sta $modifiedLabel+1
lda P8ESTACK_LO+1,x
sta $modifiedLabel2+1
""")
assignLoopvar(stmt, range)
val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.out(loopLabel)
assignLoopvar(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
asmgen.out("""
sty $modifiedLabel+1
sta $modifiedLabel2+1
$loopLabel""")
asmgen.translate(stmt.body)
if (iterableDt == DataType.ARRAY_UW) {
@@ -179,7 +158,7 @@ $modifiedLabel2 lda #0 ; modified
cmp $varname
bcc $endLabel
bcs $loopLabel
$endLabel inx""")
$endLabel""")
} else {
asmgen.out("""
lda $varname
@@ -196,22 +175,19 @@ $modifiedLabel lda #0 ; modified
bvc +
eor #$80
+ bpl $loopLabel
$endLabel inx""")
$endLabel""")
}
}
else -> {
// (u)words, step <= -2
asmgen.translateExpression(range.to)
asmgen.out("""
lda P8ESTACK_HI+1,x
sta $modifiedLabel+1
lda P8ESTACK_LO+1,x
sta $modifiedLabel2+1
""")
assignLoopvar(stmt, range)
val varname = asmgen.asmVariableName(stmt.loopVar)
asmgen.out(loopLabel)
assignLoopvar(stmt, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
asmgen.out("""
sty $modifiedLabel+1
sta $modifiedLabel2+1
$loopLabel""")
asmgen.translate(stmt.body)
if(iterableDt==DataType.ARRAY_UW) {
@@ -229,7 +205,7 @@ $modifiedLabel cmp #0 ; modified
lda $varname
$modifiedLabel2 cmp #0 ; modified
bcs $loopLabel
$endLabel inx""")
$endLabel""")
} else {
asmgen.out("""
lda $varname
@@ -247,7 +223,7 @@ $modifiedLabel sbc #0 ; modified
bvc +
eor #$80
+ bpl $loopLabel
$endLabel inx""")
$endLabel""")
}
}
}
@@ -609,10 +585,6 @@ $endLabel""")
asmgen.loopEndLabels.pop()
}
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) {
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), stmt.definingSubroutine(), variableAsmName=asmgen.asmVariableName(stmt.loopVar))
val src = AsmAssignSource.fromAstSource(range.from, program, asmgen).adjustSignedUnsigned(target)
val assign = AsmAssignment(src, target, false, range.position)
asmgen.translateNormalAssignment(assign)
}
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) =
asmgen.assignExpressionToVariable(range.from, asmgen.asmVariableName(stmt.loopVar), stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), stmt.definingSubroutine())
}
@@ -14,13 +14,23 @@ import prog8.compiler.target.c64.codegen.assignment.*
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
internal fun translateFunctionCallStatement(stmt: IFunctionCall) {
val sub = stmt.target.targetSubroutine(program.namespace)!!
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any {it.statusflag!=null}
translateFunctionCall(stmt, preserveStatusRegisterAfterCall)
// functioncalls no longer return results on the stack, so simply ignore the results in the registers
if(preserveStatusRegisterAfterCall)
asmgen.out(" plp\t; restore status flags from call")
}
internal fun translateFunctionCall(stmt: IFunctionCall, preserveStatusRegisterAfterCall: Boolean) {
// output the code to setup the parameters and perform the actual call
// does NOT output the code to deal with the result values!
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult() || sub.regXasParam()
if(saveX)
asmgen.saveRegister(CpuRegister.X, preserveStatusRegisterAfterCall, (stmt as Node).definingSubroutine())
asmgen.saveRegister(CpuRegister.X, preserveStatusRegisterAfterCall, (stmt as Node).definingSubroutine()!!)
val subName = asmgen.asmSymbolName(stmt.target)
if(stmt.args.isNotEmpty()) {
@@ -33,7 +43,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// via registers
if(sub.parameters.size==1) {
// just a single parameter, no risk of clobbering registers
argumentViaRegister(sub, sub.parameters.withIndex().single(), stmt.args[0])
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), stmt.args[0])
} else {
// multiple register arguments, risk of register clobbering.
// evaluate arguments onto the stack, and load the registers from the evaluated values on the stack.
@@ -87,7 +97,6 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
for(argi in stmt.args.zip(sub.asmParameterRegisters).withIndex()) {
when {
argi.value.second.stack -> TODO("asmsub @stack parameter")
argi.value.second.statusflag == Statusflag.Pc -> {
require(argForCarry == null)
argForCarry = argi
@@ -150,23 +159,20 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// pass parameter via a regular variable (not via registers)
val valueIDt = value.inferType(program)
if(!valueIDt.isKnown)
throw AssemblyError("arg type unknown")
throw AssemblyError("unknown dt")
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
throw AssemblyError("argument type incompatible")
val varName = asmgen.asmVariableName(sub.scopedname+"."+parameter.value.name)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, parameter.value.type, sub, variableAsmName = varName)
val source = AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(tgt)
val asgn = AsmAssignment(source, tgt, false, Position.DUMMY)
asmgen.translateNormalAssignment(asgn)
asmgen.assignExpressionToVariable(value, varName, parameter.value.type, sub)
}
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
// pass argument via a register parameter
val valueIDt = value.inferType(program)
if(!valueIDt.isKnown)
throw AssemblyError("arg type unknown")
throw AssemblyError("unknown dt")
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
throw AssemblyError("argument type incompatible")
@@ -174,20 +180,12 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
val paramRegister = sub.asmParameterRegisters[parameter.index]
val statusflag = paramRegister.statusflag
val register = paramRegister.registerOrPair
val stack = paramRegister.stack
val requiredDt = parameter.value.type
if(requiredDt!=valueDt) {
if(valueDt largerThan requiredDt)
throw AssemblyError("can only convert byte values to word param types")
}
when {
stack -> {
// push arg onto the stack
// note: argument order is reversed (first argument will be deepest on the stack)
asmgen.translateExpression(value)
if(requiredDt!=valueDt)
asmgen.signExtendStackLsb(valueDt)
}
statusflag!=null -> {
if(requiredDt!=valueDt)
throw AssemblyError("for statusflag, byte value is required")
@@ -212,17 +210,13 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
""")
}
else -> {
asmgen.translateExpression(value)
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out("""
inx
pha
lda P8ESTACK_LO,x
beq +
sec
bcs ++
+ clc
+ pla
""")
beq +
sec
bcs ++
+ clc
+""")
}
}
}
@@ -230,18 +224,16 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
}
else -> {
// via register or register pair
val target = AsmAssignTarget.fromRegisters(register!!, sub, program, asmgen)
register!!
if(requiredDt largerThan valueDt) {
// we need to sign extend the source, do this via temporary word variable
val scratchVar = asmgen.asmVariableName("P8ZP_SCRATCH_W1")
val scratchTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UBYTE, sub, scratchVar)
val source = AsmAssignSource.fromAstSource(value, program, asmgen)
asmgen.translateNormalAssignment(AsmAssignment(source, scratchTarget, false, value.position))
asmgen.assignExpressionToVariable(value, scratchVar, DataType.UBYTE, sub)
asmgen.signExtendVariableLsb(scratchVar, valueDt)
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, DataType.UWORD, scratchVar)
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY))
asmgen.assignVariableToRegister(scratchVar, register)
}
else {
val target = AsmAssignTarget.fromRegisters(register, sub, program, asmgen)
val src = if(valueDt in PassByReferenceDatatypes) {
if(value is IdentifierReference) {
val addr = AddressOf(value, Position.DUMMY)
@@ -19,7 +19,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
when {
targetIdent!=null -> {
val what = asmgen.asmVariableName(targetIdent)
when (stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)) {
when (stmt.target.inferType(program).typeOrElse(DataType.STRUCT)) {
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
in WordDatatypes -> {
if(incr)
@@ -54,14 +54,8 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
asmgen.out("+\tdec ${'$'}ffff\t; modified")
}
else -> {
asmgen.translateExpression(addressExpr)
asmgen.out("""
inx
lda P8ESTACK_LO,x
sta (+) + 1
lda P8ESTACK_HI,x
sta (+) + 2
""")
asmgen.assignExpressionToRegister(addressExpr, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
if(incr)
asmgen.out("+\tinc ${'$'}ffff\t; modified")
else
@@ -97,7 +91,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
else
{
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
asmgen.saveRegister(CpuRegister.X, false, scope)
asmgen.saveRegister(CpuRegister.X, false, scope!!)
asmgen.out(" tax")
when(elementDt) {
in ByteDatatypes -> {
@@ -3,10 +3,7 @@ package prog8.compiler.target.c64.codegen.assignment
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.Subroutine
import prog8.ast.statements.*
import prog8.compiler.AssemblyError
import prog8.compiler.target.c64.codegen.AsmGen
@@ -52,13 +49,16 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
lateinit var origAssign: AsmAssignment
init {
if(register!=null && datatype !in IntegerDatatypes)
throw AssemblyError("register must be integer type")
if(register!=null && datatype !in NumericDatatypes)
throw AssemblyError("register must be integer or float type")
}
companion object {
fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget = with(assign.target) {
val dt = inferType(program, assign).typeOrElse(DataType.STRUCT)
val idt = inferType(program)
if(!idt.isKnown)
throw AssemblyError("unknown dt")
val dt = idt.typeOrElse(DataType.STRUCT)
when {
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine(), array = arrayindexed, origAstTarget = this)
@@ -75,6 +75,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
RegisterOrPair.AX,
RegisterOrPair.AY,
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UWORD, scope, register = registers)
RegisterOrPair.FAC1,
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.FLOAT, scope, register = registers)
}
}
}
@@ -101,6 +103,14 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
asmgen.asmVariableName(array.arrayvar)
companion object {
fun fromAstSource(indexer: ArrayIndex, program: Program, asmgen: AsmGen): AsmAssignSource {
return when {
indexer.indexNum!=null -> fromAstSource(indexer.indexNum!!, program, asmgen)
indexer.indexVar!=null -> fromAstSource(indexer.indexVar!!, program, asmgen)
else -> throw AssemblyError("weird indexer")
}
}
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
val cv = value.constValue(program)
if(cv!=null)
@@ -121,33 +131,30 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
}
else -> {
if(value is FunctionCall) {
// functioncall.
val asmSub = value.target.targetStatement(program.namespace)
if(asmSub is Subroutine && asmSub.isAsmSubroutine) {
when (asmSub.asmReturnvaluesRegisters.count { rr -> rr.registerOrPair!=null }) {
0 -> throw AssemblyError("can't translate zero return values in assignment")
1 -> {
// assignment generation itself must make sure the status register is correct after the subroutine call, if status register is involved!
val reg = asmSub.asmReturnvaluesRegisters.single { rr->rr.registerOrPair!=null }.registerOrPair!!
val dt = when(reg) {
RegisterOrPair.A,
RegisterOrPair.X,
RegisterOrPair.Y -> DataType.UBYTE
RegisterOrPair.AX,
RegisterOrPair.AY,
RegisterOrPair.XY -> DataType.UWORD
}
return AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt, expression = value)
}
else -> throw AssemblyError("can't translate multiple return values in assignment")
}
is FunctionCall -> {
when (val sub = value.target.targetStatement(program.namespace)) {
is Subroutine -> {
val returnType = sub.returntypes.zip(sub.asmReturnvaluesRegisters).firstOrNull { rr -> rr.second.registerOrPair != null }?.first
?: throw AssemblyError("can't translate zero return values in assignment")
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
}
is BuiltinFunctionStatementPlaceholder -> {
val returnType = value.inferType(program)
if(!returnType.isKnown)
throw AssemblyError("unknown dt")
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.typeOrElse(DataType.STRUCT), expression = value)
}
else -> {
throw AssemblyError("weird call")
}
}
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
return AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt, expression = value)
}
else -> {
val dt = value.inferType(program)
if(!dt.isKnown)
throw AssemblyError("unknown dt")
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt.typeOrElse(DataType.STRUCT), expression = value)
}
}
}
@@ -10,6 +10,8 @@ import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
import prog8.compiler.toHex
import prog8.functions.BuiltinFunctions
import prog8.functions.builtinFunctionReturnType
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen, private val exprAsmgen: ExpressionsAsmGen) {
@@ -46,7 +48,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val variable = assign.source.asmVarname
when (assign.target.datatype) {
DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable)
DataType.UWORD, DataType.WORD -> assignVariableWord(assign.target, variable)
DataType.WORD -> assignVariableWord(assign.target, variable)
DataType.UWORD -> {
if(assign.source.datatype in PassByReferenceDatatypes)
assignAddressOf(assign.target, variable)
else
assignVariableWord(assign.target, variable)
}
DataType.FLOAT -> assignVariableFloat(assign.target, variable)
DataType.STR -> assignVariableString(assign.target, variable)
else -> throw AssemblyError("unsupported assignment target type ${assign.target.datatype}")
@@ -60,12 +68,18 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
// constant array index value
val indexValue = value.indexer.constIndex()!! * elementDt.memorySize()
when (elementDt) {
in ByteDatatypes ->
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
in WordDatatypes ->
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | lda $arrayVarName+$indexValue+1 | sta P8ESTACK_HI,x | dex")
DataType.FLOAT ->
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr floats.push_float")
in ByteDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue")
assignRegisterByte(assign.target, CpuRegister.A)
}
in WordDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue | ldy $arrayVarName+$indexValue+1")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
DataType.FLOAT -> {
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue")
assignFloatFromAY(assign.target)
}
else ->
throw AssemblyError("weird array type")
}
@@ -73,11 +87,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
when (elementDt) {
in ByteDatatypes -> {
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | dex")
asmgen.out(" lda $arrayVarName,y")
assignRegisterByte(assign.target, CpuRegister.A)
}
in WordDatatypes -> {
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | lda $arrayVarName+1,y | sta P8ESTACK_HI,x | dex")
asmgen.out(" lda $arrayVarName,y | pha | lda $arrayVarName+1,y | tay | pla")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
DataType.FLOAT -> {
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.A)
@@ -87,13 +103,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
adc #<$arrayVarName
bcc +
iny
+ jsr floats.push_float""")
+""")
assignFloatFromAY(assign.target)
}
else ->
throw AssemblyError("weird array elt type")
}
}
assignStackValue(assign.target)
}
SourceStorageKind.MEMORY -> {
val value = assign.source.memory!!
@@ -106,8 +122,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
assignMemoryByte(assign.target, null, value.addressExpression as IdentifierReference)
}
else -> {
asmgen.translateExpression(value.addressExpression)
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | inx")
assignExpressionToVariable(value.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope)
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
assignRegisterByte(assign.target, CpuRegister.A)
}
}
@@ -122,34 +138,83 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
is IdentifierReference -> throw AssemblyError("source kind should have been variable")
is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array")
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory")
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, assign)
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value)
is FunctionCall -> {
if(value.target.targetSubroutine(program.namespace)?.isAsmSubroutine==true) {
// handle asmsub functioncalls specifically, without shoving stuff on the estack
val sub = value.target.targetSubroutine(program.namespace)!!
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any { it.statusflag != null }
asmgen.translateFunctionCall(value, preserveStatusRegisterAfterCall)
when((sub.asmReturnvaluesRegisters.single { it.registerOrPair!=null }).registerOrPair) {
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A)
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X)
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y)
RegisterOrPair.AX -> assignRegisterpairWord(assign.target, RegisterOrPair.AX)
RegisterOrPair.AY -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
RegisterOrPair.XY -> assignRegisterpairWord(assign.target, RegisterOrPair.XY)
else -> throw AssemblyError("should be just one register byte result value")
when (val sub = value.target.targetStatement(program.namespace)) {
is Subroutine -> {
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any { it.statusflag != null }
asmgen.translateFunctionCall(value, preserveStatusRegisterAfterCall)
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.registerOrPair!=null }
when (returnValue.first) {
DataType.STR -> {
when(assign.target.datatype) {
DataType.UWORD -> {
// assign the address of the string result value
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
// copy the actual string result into the target string variable
asmgen.out("""
pha
lda #<${assign.target.asmVarname}
sta P8ZP_SCRATCH_W1
lda #>${assign.target.asmVarname}
sta P8ZP_SCRATCH_W1+1
pla
jsr prog8_lib.strcpy""")
}
else -> throw AssemblyError("weird target dt")
}
}
DataType.FLOAT -> {
// float result from function sits in FAC1
assignFAC1float(assign.target)
}
else -> {
when (returnValue.second.registerOrPair) {
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A)
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X)
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y)
RegisterOrPair.AX -> assignRegisterpairWord(assign.target, RegisterOrPair.AX)
RegisterOrPair.AY -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
RegisterOrPair.XY -> assignRegisterpairWord(assign.target, RegisterOrPair.XY)
else -> throw AssemblyError("should be just one register byte result value")
}
}
}
if (preserveStatusRegisterAfterCall)
asmgen.out(" plp\t; restore status flags from call")
}
is BuiltinFunctionStatementPlaceholder -> {
val signature = BuiltinFunctions.getValue(sub.name)
asmgen.translateBuiltinFunctionCallExpression(value, signature, false)
val returntype = builtinFunctionReturnType(sub.name, value.args, program)
if(!returntype.isKnown)
throw AssemblyError("unknown dt")
when(returntype.typeOrElse(DataType.STRUCT)) {
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY
DataType.STR -> throw AssemblyError("missing code for assign string from builtin func => copy string or assign string address")
DataType.FLOAT -> {
// float result from function sits in FAC1
assignFAC1float(assign.target)
}
else -> throw AssemblyError("weird result type")
}
}
else -> {
throw AssemblyError("weird func call")
}
if(preserveStatusRegisterAfterCall)
asmgen.out(" plp\t; restore status flags from call")
} else {
// regular subroutine, return values are (for now) always done via the stack... TODO optimize this
asmgen.translateExpression(value)
if(assign.target.datatype in WordDatatypes && assign.source.datatype in ByteDatatypes)
asmgen.signExtendStackLsb(assign.source.datatype)
assignStackValue(assign.target)
}
}
else -> {
// everything else just evaluate via the stack.
// Everything else just evaluate via the stack.
// (we can't use the assignment helper functions to do it via registers here,
// because the code here is the implementation of exactly that...)
if(value.parent is Return) {
if (this.asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used for return: $value target=${assign.target.kind} at ${value.position}")
}
asmgen.translateExpression(value)
if(assign.target.datatype in WordDatatypes && assign.source.datatype in ByteDatatypes)
asmgen.signExtendStackLsb(assign.source.datatype)
@@ -166,8 +231,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origAssign: AsmAssignment) {
val valueDt = value.inferType(program).typeOrElse(DataType.STRUCT)
private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origTypeCastExpression: TypecastExpression) {
val valueIDt = value.inferType(program)
if(!valueIDt.isKnown)
throw AssemblyError("unknown dt")
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
if(valueDt==targetDt)
throw AssemblyError("type cast to identical dt should have been removed")
when(value) {
is IdentifierReference -> {
if(targetDt in WordDatatypes) {
@@ -198,25 +269,316 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
else -> {}
}
when(value) {
is PrefixExpression -> {}
is BinaryExpression -> {}
is ArrayIndexedExpression -> {}
is TypecastExpression -> {}
is RangeExpr -> {}
is FunctionCall -> {}
else -> {
// TODO optimize the others further?
if(this.asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used for typecast: into $targetDt at ${value.position}")
// special case optimizations
if(target.kind==TargetStorageKind.VARIABLE) {
if(value is IdentifierReference && valueDt != DataType.STRUCT)
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
when (valueDt) {
in ByteDatatypes -> {
assignExpressionToRegister(value, RegisterOrPair.A)
assignTypeCastedRegisters(target.asmVarname, targetDt, RegisterOrPair.A, valueDt)
}
in WordDatatypes -> {
assignExpressionToRegister(value, RegisterOrPair.AY)
assignTypeCastedRegisters(target.asmVarname, targetDt, RegisterOrPair.AY, valueDt)
}
DataType.FLOAT -> {
assignExpressionToRegister(value, RegisterOrPair.FAC1)
assignTypecastedFloatFAC1(target.asmVarname, targetDt)
}
in PassByReferenceDatatypes -> {
// str/array value cast (most likely to UWORD, take address-of)
assignExpressionToVariable(value, target.asmVarname, targetDt, null)
}
else -> throw AssemblyError("strange dt in typecast assign to var: $valueDt --> $targetDt")
}
return
}
// give up, do it via eval stack
asmgen.translateExpression(origAssign.source.expression!!)
// TODO optimize typecasts for more special cases?
// note: cannot use assignTypeCastedValue because that is ourselves :P
if(this.asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used for typecast: $value into $targetDt (target=${target.kind} at ${value.position}")
asmgen.translateExpression(origTypeCastExpression) // this performs the actual type cast in translateExpression(Typecast)
assignStackValue(target)
}
private fun assignTypecastedFloatFAC1(targetAsmVarName: String, targetDt: DataType) {
if(targetDt==DataType.FLOAT)
throw AssemblyError("typecast to identical type")
when(targetDt) {
DataType.UBYTE -> asmgen.out(" jsr floats.cast_FAC1_as_uw_into_ya | sty $targetAsmVarName")
DataType.BYTE -> asmgen.out(" jsr floats.cast_FAC1_as_w_into_ay | sta $targetAsmVarName")
DataType.UWORD -> asmgen.out(" jsr floats.cast_FAC1_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
DataType.WORD -> asmgen.out(" jsr floats.cast_FAC1_as_w_into_ay | sta $targetAsmVarName | sty $targetAsmVarName+1")
else -> throw AssemblyError("weird type")
}
}
private fun assignTypeCastedIdentifier(targetAsmVarName: String, targetDt: DataType,
sourceAsmVarName: String, sourceDt: DataType) {
if(sourceDt == targetDt)
throw AssemblyError("typecast to identical type")
// also see: ExpressionAsmGen, fun translateExpression(typecast: TypecastExpression)
when(sourceDt) {
DataType.UBYTE -> {
when(targetDt) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
}
DataType.UWORD, DataType.WORD -> {
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | stz $targetAsmVarName+1")
else
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
DataType.FLOAT -> {
asmgen.out("""
lda #<$targetAsmVarName
ldy #>$targetAsmVarName
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy $sourceAsmVarName
jsr floats.cast_from_ub""")
}
else -> throw AssemblyError("weird type")
}
}
DataType.BYTE -> {
when(targetDt) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
}
DataType.UWORD -> {
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | stz $targetAsmVarName+1")
else
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
DataType.WORD -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
asmgen.signExtendVariableLsb(targetAsmVarName, DataType.BYTE)
}
DataType.FLOAT -> {
asmgen.out("""
lda #<$targetAsmVarName
ldy #>$targetAsmVarName
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda $sourceAsmVarName
jsr floats.cast_from_b""")
}
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
when(targetDt) {
DataType.BYTE, DataType.UBYTE -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
}
DataType.WORD, DataType.UWORD -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
}
DataType.FLOAT -> {
asmgen.out("""
lda #<$targetAsmVarName
ldy #>$targetAsmVarName
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda $sourceAsmVarName
ldy $sourceAsmVarName+1
jsr floats.cast_from_uw""")
}
else -> throw AssemblyError("weird type")
}
}
DataType.WORD -> {
when(targetDt) {
DataType.BYTE, DataType.UBYTE -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
}
DataType.WORD, DataType.UWORD -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
}
DataType.FLOAT -> {
asmgen.out("""
lda #<$targetAsmVarName
ldy #>$targetAsmVarName
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda $sourceAsmVarName
ldy $sourceAsmVarName+1
jsr floats.cast_from_w""")
}
else -> throw AssemblyError("weird type")
}
}
DataType.FLOAT -> {
asmgen.out(" lda #<$sourceAsmVarName | ldy #>$sourceAsmVarName")
when(targetDt) {
DataType.UBYTE -> asmgen.out(" jsr floats.cast_as_uw_into_ya | sty $targetAsmVarName")
DataType.BYTE -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName")
DataType.UWORD -> asmgen.out(" jsr floats.cast_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
DataType.WORD -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName | sty $targetAsmVarName+1")
else -> throw AssemblyError("weird type")
}
}
DataType.STR -> {
if (targetDt != DataType.UWORD && targetDt == DataType.STR)
throw AssemblyError("cannot typecast a string into another incompatitble type")
TODO("assign typecasted string into target var")
}
else -> throw AssemblyError("weird type")
}
}
private fun assignTypeCastedRegisters(targetAsmVarName: String, targetDt: DataType,
regs: RegisterOrPair, sourceDt: DataType) {
if(sourceDt == targetDt)
throw AssemblyError("typecast to identical type")
// also see: ExpressionAsmGen, fun translateExpression(typecast: TypecastExpression)
when(sourceDt) {
DataType.UBYTE -> {
when(targetDt) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
}
DataType.UWORD, DataType.WORD -> {
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
else
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
DataType.FLOAT -> {
when(regs) {
RegisterOrPair.A -> asmgen.out(" tay")
RegisterOrPair.X -> asmgen.out(" txa | tay")
RegisterOrPair.Y -> {}
else -> throw AssemblyError("non-byte regs")
}
asmgen.out("""
lda #<$targetAsmVarName
sta P8ZP_SCRATCH_W2
lda #>$targetAsmVarName
sta P8ZP_SCRATCH_W2+1
jsr floats.cast_from_ub""")
}
else -> throw AssemblyError("weird type")
}
}
DataType.BYTE -> {
when(targetDt) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
}
DataType.UWORD -> {
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
else
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
DataType.WORD -> {
when(regs) {
RegisterOrPair.A -> {}
RegisterOrPair.X -> asmgen.out(" txa")
RegisterOrPair.Y -> asmgen.out(" tya")
else -> throw AssemblyError("non-byte regs")
}
asmgen.signExtendAYlsb(sourceDt)
asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
}
DataType.FLOAT -> {
when(regs) {
RegisterOrPair.A -> {}
RegisterOrPair.X -> asmgen.out(" txa")
RegisterOrPair.Y -> asmgen.out(" tya")
else -> throw AssemblyError("non-byte regs")
}
asmgen.out("""
ldy #<$targetAsmVarName
sty P8ZP_SCRATCH_W2
ldy #>$targetAsmVarName
sty P8ZP_SCRATCH_W2+1
jsr floats.cast_from_b""")
}
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
when(targetDt) {
DataType.BYTE, DataType.UBYTE -> {
asmgen.out(" st${regs.toString().toLowerCase().first()} $targetAsmVarName")
}
DataType.WORD, DataType.UWORD -> {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
RegisterOrPair.XY -> asmgen.out(" stx $targetAsmVarName | sty $targetAsmVarName+1")
else -> throw AssemblyError("non-word regs")
}
}
DataType.FLOAT -> {
if(regs!=RegisterOrPair.AY)
throw AssemblyError("only supports AY here")
asmgen.out("""
pha
lda #<$targetAsmVarName
sta P8ZP_SCRATCH_W2
lda #>$targetAsmVarName
sta P8ZP_SCRATCH_W2+1
pla
jsr floats.cast_from_uw""")
}
else -> throw AssemblyError("weird type")
}
}
DataType.WORD -> {
when(targetDt) {
DataType.BYTE, DataType.UBYTE -> {
asmgen.out(" st${regs.toString().toLowerCase().first()} $targetAsmVarName")
}
DataType.WORD, DataType.UWORD -> {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
RegisterOrPair.XY -> asmgen.out(" stx $targetAsmVarName | sty $targetAsmVarName+1")
else -> throw AssemblyError("non-word regs")
}
}
DataType.FLOAT -> {
if(regs!=RegisterOrPair.AY)
throw AssemblyError("only supports AY here")
asmgen.out("""
pha
lda #<$targetAsmVarName
sta P8ZP_SCRATCH_W2
lda #>$targetAsmVarName
sta P8ZP_SCRATCH_W2+1
pla
jsr floats.cast_from_w""")
}
else -> throw AssemblyError("weird type")
}
}
DataType.STR -> {
if (targetDt != DataType.UWORD && targetDt == DataType.STR)
throw AssemblyError("cannot typecast a string into another incompatitble type")
TODO("assign typecasted string into target var")
}
else -> throw AssemblyError("weird type")
}
}
private fun assignStackValue(target: AsmAssignTarget) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
@@ -243,9 +605,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
DataType.STR -> {
asmgen.out("""
lda #<${target.asmVarname}
ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W1
lda #>${target.asmVarname}
sta P8ZP_SCRATCH_W1+1
sty P8ZP_SCRATCH_W1+1
inx
lda P8ESTACK_HI,x
tay
@@ -337,6 +699,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
else -> throw AssemblyError("can't assign word to single byte register")
}
}
DataType.FLOAT -> {
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.pop_float_fac1")
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.pop_float_fac2")
else -> throw AssemblyError("can only assign float to Fac1 or 2")
}
}
else -> throw AssemblyError("weird dt")
}
}
@@ -386,17 +756,17 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
DataType.UWORD -> {
asmgen.out("""
lda #<$sourceName
ldy #>$sourceName
sta ${target.asmVarname}
lda #>$sourceName
sta ${target.asmVarname}+1
sty ${target.asmVarname}+1
""")
}
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
asmgen.out("""
lda #<${target.asmVarname}
ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W1
lda #>${target.asmVarname}
sta P8ZP_SCRATCH_W1+1
sty P8ZP_SCRATCH_W1+1
lda #<$sourceName
ldy #>$sourceName
jsr prog8_lib.strcpy""")
@@ -407,9 +777,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
TargetStorageKind.STACK -> {
asmgen.out("""
lda #<$sourceName
ldy #>$sourceName+1
sta P8ESTACK_LO,x
lda #>$sourceName+1
sta P8ESTACK_HI,x
sty P8ESTACK_HI,x
dex""")
}
else -> throw AssemblyError("string-assign to weird target")
@@ -503,15 +873,89 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
TargetStorageKind.STACK -> {
asmgen.out("""
lda #$sourceName
lda $sourceName
sta P8ESTACK_LO,x
lda #$sourceName+1
lda $sourceName+1
sta P8ESTACK_HI,x
dex""")
}
}
}
internal fun assignFAC1float(target: AsmAssignTarget) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
stx P8ZP_SCRATCH_REG
ldx #<${target.asmVarname}
ldy #>${target.asmVarname}
jsr floats.MOVMF
ldx P8ZP_SCRATCH_REG
""")
}
TargetStorageKind.ARRAY -> {
asmgen.out("""
lda #<${target.asmVarname}
ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1""")
if(target.array!!.indexer.indexNum!=null) {
val index = target.array.indexer.constIndex()!!
asmgen.out(" lda #$index")
} else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
asmgen.out(" lda $asmvarname")
}
asmgen.out(" jsr floats.set_array_float_from_fac1")
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
TargetStorageKind.REGISTER -> {
if (target.register!! != RegisterOrPair.FAC1)
throw AssemblyError("can't assign Fac1 float to another fac register")
}
TargetStorageKind.STACK -> asmgen.out(" jsr floats.push_fac1")
}
}
private fun assignFloatFromAY(target: AsmAssignTarget) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${target.asmVarname}
ldy #>${target.asmVarname}
jsr floats.copy_float""")
}
TargetStorageKind.ARRAY -> {
asmgen.out("""
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${target.asmVarname}
ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1""")
if(target.array!!.indexer.indexNum!=null) {
val index = target.array.indexer.constIndex()!!
asmgen.out(" lda #$index")
} else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
asmgen.out(" lda $asmvarname")
}
asmgen.out(" jsr floats.set_array_float")
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.MOVFM")
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.CONUPK")
else -> throw AssemblyError("can only assign float to Fac1 or 2")
}
}
TargetStorageKind.STACK -> asmgen.out(" jsr floats.push_float")
}
}
private fun assignVariableFloat(target: AsmAssignTarget, sourceName: String) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
@@ -548,7 +992,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" jsr floats.set_array_float")
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr floats.MOVFM")
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr floats.CONUPK")
else -> throw AssemblyError("can only assign float to Fac1 or 2")
}
}
TargetStorageKind.STACK -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr floats.push_float")
}
}
@@ -582,11 +1032,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
}
}
TargetStorageKind.STACK -> {
asmgen.out("""
lda #$sourceName
lda $sourceName
sta P8ESTACK_LO,x
dex""")
}
@@ -696,7 +1147,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
require(target.datatype in ByteDatatypes)
when(target.kind) {
TargetStorageKind.VARIABLE -> {
@@ -733,6 +1184,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AY -> { asmgen.out(" ldy #0") }
RegisterOrPair.AX -> { asmgen.out(" ldx #0") }
RegisterOrPair.XY -> { asmgen.out(" tax | ldy #0") }
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
}
CpuRegister.X -> when(target.register!!) {
RegisterOrPair.A -> { asmgen.out(" txa") }
@@ -741,6 +1193,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AY -> { asmgen.out(" txa | ldy #0") }
RegisterOrPair.AX -> { asmgen.out(" txa | ldx #0") }
RegisterOrPair.XY -> { asmgen.out(" ldy #0") }
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
}
CpuRegister.Y -> when(target.register!!) {
RegisterOrPair.A -> { asmgen.out(" tya") }
@@ -749,6 +1202,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AY -> { asmgen.out(" tya | ldy #0") }
RegisterOrPair.AX -> { asmgen.out(" tya | ldx #0") }
RegisterOrPair.XY -> { asmgen.out(" tya | tax | ldy #0") }
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
}
}
}
@@ -762,8 +1216,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignRegisterpairWord(target: AsmAssignTarget, regs: RegisterOrPair) {
require(target.datatype in WordDatatypes)
internal fun assignRegisterpairWord(target: AsmAssignTarget, regs: RegisterOrPair) {
require(target.datatype in NumericDatatypes)
if(target.datatype==DataType.FLOAT)
throw AssemblyError("float value should be from FAC1 not from registerpair memory pointer")
when(target.kind) {
TargetStorageKind.VARIABLE -> {
when(regs) {
@@ -774,7 +1231,20 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
TargetStorageKind.ARRAY -> {
TODO("store register pair $regs into word-array ${target.array}")
// TODO can be a bit more optimized if the array indexer is a number
when(regs) {
RegisterOrPair.AX -> asmgen.out(" pha | txa | pha")
RegisterOrPair.AY -> asmgen.out(" pha | tya | pha")
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
else -> throw AssemblyError("expected reg pair")
}
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y, true)
asmgen.out("""
pla
sta ${target.asmVarname},y
dey
pla
sta ${target.asmVarname},y""")
}
TargetStorageKind.REGISTER -> {
when(regs) {
@@ -885,6 +1355,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AX -> asmgen.out(" lda #${byte.toHex()} | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda #${byte.toHex()} | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx #${byte.toHex()} | ldy #0")
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
}
TargetStorageKind.STACK -> {
asmgen.out("""
@@ -942,7 +1413,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
TargetStorageKind.REGISTER -> {
val floatConst = asmgen.getFloatAsmConst(float)
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
else -> throw AssemblyError("can only assign float to Fac1 or 2")
}
}
TargetStorageKind.STACK -> {
val floatConst = asmgen.getFloatAsmConst(float)
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
@@ -999,7 +1477,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
TargetStorageKind.REGISTER -> {
val floatConst = asmgen.getFloatAsmConst(float)
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
else -> throw AssemblyError("can only assign float to Fac1 or 2")
}
}
TargetStorageKind.STACK -> {
val floatConst = asmgen.getFloatAsmConst(float)
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
@@ -1030,6 +1515,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AX -> asmgen.out(" lda ${address.toHex()} | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda ${address.toHex()} | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldy ${address.toHex()} | ldy #0")
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
}
TargetStorageKind.STACK -> {
asmgen.out("""
@@ -1060,6 +1546,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AX -> asmgen.out(" ldx #0")
RegisterOrPair.AY -> asmgen.out(" ldy #0")
RegisterOrPair.XY -> asmgen.out(" tax | ldy #0")
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
}
}
TargetStorageKind.STACK -> {
@@ -1138,17 +1625,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.storeByteIntoPointer(addressExpr, ldaInstructionArg)
}
else -> {
asmgen.out(" lda $ldaInstructionArg | pha")
asmgen.translateExpression(addressExpr)
asmgen.out("""
inx
lda P8ESTACK_LO,x
sta P8ZP_SCRATCH_W2
lda P8ESTACK_HI,x
sta P8ZP_SCRATCH_W2+1
ldy #0
pla
sta (P8ZP_SCRATCH_W2),y""")
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.out(" ldy #0 | lda $ldaInstructionArg | sta (P8ZP_SCRATCH_W2),y")
}
}
}
@@ -1171,18 +1649,32 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.storeByteIntoPointer(addressExpr, null)
}
else -> {
asmgen.saveRegister(register, false, memoryAddress.definingSubroutine())
asmgen.translateExpression(addressExpr)
asmgen.saveRegister(register, false, memoryAddress.definingSubroutine()!!)
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.restoreRegister(CpuRegister.A, false)
asmgen.out("""
inx
ldy P8ESTACK_LO,x
sty P8ZP_SCRATCH_W2
ldy P8ESTACK_HI,x
sty P8ZP_SCRATCH_W2+1
ldy #0
sta (P8ZP_SCRATCH_W2),y""")
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
}
}
}
internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair) {
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen)
val assign = AsmAssignment(src, tgt, false, expr.position)
translateNormalAssignment(assign)
}
internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) {
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, scope, variableAsmName = asmVarName)
val assign = AsmAssignment(src, tgt, false, expr.position)
translateNormalAssignment(assign)
}
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair) {
val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen)
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName)
val assign = AsmAssignment(src, tgt, false, Position.DUMMY)
translateNormalAssignment(assign)
}
}
@@ -22,7 +22,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when (val value = assign.source.expression!!) {
is PrefixExpression -> {
// A = -A , A = +A, A = ~A, A = not A
val type = value.inferType(program).typeOrElse(DataType.STRUCT)
val itype = value.inferType(program)
if(!itype.isKnown)
throw AssemblyError("unknown dt")
val type = itype.typeOrElse(DataType.STRUCT)
when (value.operator) {
"+" -> {}
"-" -> inplaceNegate(assign.target, type)
@@ -136,13 +139,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
DataType.FLOAT -> {
when {
valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble(), target.scope)
ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident, target.scope)
valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble(), target.scope!!)
ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident, target.scope!!)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope)
inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope!!)
}
else -> inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope)
else -> inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope!!)
}
}
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
@@ -157,7 +160,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when {
valueLv != null -> inplaceModification_byte_litval_to_variable(addr.toHex(), DataType.UBYTE, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_variable(addr.toHex(), DataType.UBYTE, operator, ident)
// TODO more specialized code for types such as memory read etc.
// TODO more specialized code for types such as memory read etc. -> inplaceModification_byte_memread_to_variable()
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
@@ -168,40 +171,102 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
is IdentifierReference -> {
val pointer = memory.addressExpression as IdentifierReference
when {
valueLv != null -> inplaceModification_byte_litval_to_memory(pointer, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_memory(pointer, operator, ident)
// TODO more specialized code for types such as memory read etc.
valueLv != null -> inplaceModification_byte_litval_to_pointer(pointer, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_pointer(pointer, operator, ident)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_memory(pointer, operator, value)
inplaceModification_byte_value_to_pointer(pointer, operator, value)
}
else -> inplaceModification_byte_value_to_memory(pointer, operator, value)
else -> inplaceModification_byte_value_to_pointer(pointer, operator, value)
}
}
else -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (1): ${memory.addressExpression::class.simpleName} at ${memory.addressExpression.position}") // TODO optimize...
asmgen.translateExpression(memory.addressExpression)
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
val zp = CompilationTarget.instance.machine.zeropage
when {
valueLv != null -> inplaceModification_byte_litval_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, ident)
memread != null -> inplaceModification_byte_memread_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, memread)
valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, ident)
memread != null -> inplaceModification_byte_memread_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, memread)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, value)
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
}
else -> inplaceModification_byte_value_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, value)
else -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
}
asmgen.out(" lda P8ZP_SCRATCH_B1 | jsr prog8_lib.write_byte_to_address_on_stack | inx")
}
}
}
TargetStorageKind.ARRAY -> {
if(asmgen.options.slowCodegenWarnings)
println("*** TODO optimize simple inplace array assignment ${target.array} $operator= $value")
assignmentAsmGen.translateNormalAssignment(target.origAssign) // TODO get rid of this fallback for the most common cases here
with(target.array!!.indexer) {
when {
indexNum!=null -> {
val targetVarName = "${target.asmVarname} + ${indexNum!!.number.toInt()*target.datatype.memorySize()}"
when(target.datatype) {
in ByteDatatypes -> {
when {
valueLv != null -> inplaceModification_byte_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_variable(targetVarName, target.datatype, operator, ident)
memread != null -> inplaceModification_byte_memread_to_variable(targetVarName, target.datatype, operator, memread)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
}
else -> inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
}
}
in WordDatatypes -> {
when {
valueLv != null -> inplaceModification_word_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt())
ident != null -> inplaceModification_word_variable_to_variable(targetVarName, target.datatype, operator, ident)
memread != null -> inplaceModification_word_memread_to_variable(targetVarName, target.datatype, operator, memread)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
}
else -> inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
}
}
DataType.FLOAT -> {
when {
valueLv != null -> inplaceModification_float_litval_to_variable(targetVarName, operator, valueLv.toDouble(), target.scope!!)
ident != null -> inplaceModification_float_variable_to_variable(targetVarName, operator, ident, target.scope!!)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
}
else -> inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
}
}
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
}
}
indexVar!=null -> {
when(target.datatype) {
in ByteDatatypes -> {
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.A, null, program, asmgen)
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position)
assignmentAsmGen.translateNormalAssignment(assign)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A)
}
in WordDatatypes -> {
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen)
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position)
assignmentAsmGen.translateNormalAssignment(assign)
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
}
DataType.FLOAT -> {
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.FAC1, null, program, asmgen)
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position)
assignmentAsmGen.translateNormalAssignment(assign)
assignmentAsmGen.assignFAC1float(target)
}
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
}
}
else -> throw AssemblyError("indexer expression should have been replaced by auto indexer var")
}
}
}
TargetStorageKind.REGISTER -> TODO("reg in-place modification")
TargetStorageKind.STACK -> TODO("stack in-place modification")
@@ -210,9 +275,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
private fun tryRemoveRedundantCast(value: TypecastExpression, target: AsmAssignTarget, operator: String): Boolean {
if (target.datatype == value.type) {
val childDt = value.expression.inferType(program).typeOrElse(DataType.STRUCT)
if (value.type.equalsSize(childDt) || value.type.largerThan(childDt)) {
val childIDt = value.expression.inferType(program)
if(!childIDt.isKnown)
throw AssemblyError("unknown dt")
val childDt = childIDt.typeOrElse(DataType.STRUCT)
if (value.type!=DataType.FLOAT && (value.type.equalsSize(childDt) || value.type.largerThan(childDt))) {
// this typecast is redundant here; the rest of the code knows how to deal with the uncasted value.
// (works for integer types, not for float.)
inplaceModification(target, operator, value.expression)
return true
}
@@ -220,27 +289,20 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
return false
}
private fun inplaceModification_byte_value_to_memory(pointervar: IdentifierReference, operator: String, value: Expression) {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (3): @(${pointervar.nameInSource.last()}) $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
private fun inplaceModification_byte_value_to_pointer(pointervar: IdentifierReference, operator: String, value: Expression) {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
when (operator) {
// note: ** (power) operator requires floats.
"+" -> asmgen.out(" clc | adc P8ESTACK_LO+1,x")
"-" -> asmgen.out(" sec | sbc P8ESTACK_LO+1,x")
"*" -> asmgen.out(" pha | lda P8ESTACK_LO+1,x | tay | pla | jsr math.multiply_bytes | ldy #0")
"/" -> asmgen.out(" pha | lda P8ESTACK_LO+1,x | tay | pla | jsr math.divmod_ub_asm | tya | ldy #0")
"%" -> asmgen.out(" pha | lda P8ESTACK_LO+1,x | tay | pla | jsr math.divmod_ub_asm | ldy #0")
"+" -> asmgen.out(" clc | adc P8ZP_SCRATCH_B1")
"-" -> asmgen.out(" sec | sbc P8ZP_SCRATCH_B1")
"*" -> asmgen.out(" ldy P8ZP_SCRATCH_B1 | jsr math.multiply_bytes | ldy #0")
"/" -> asmgen.out(" ldy P8ZP_SCRATCH_B1 | jsr math.divmod_ub_asm | tya | ldy #0")
"%" -> asmgen.out(" ldy P8ZP_SCRATCH_B1 | jsr math.divmod_ub_asm | ldy #0")
"<<" -> {
asmgen.out("""
pha
lda P8ESTACK_LO+1,x
bne +
pla
rts
+ tay
pla
ldy P8ZP_SCRATCH_B1
beq +
- asl a
dey
bne -
@@ -248,38 +310,32 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
">>" -> {
asmgen.out("""
pha
lda P8ESTACK_LO+1,x
bne +
pla
rts
+ tay
pla
ldy P8ZP_SCRATCH_B1
beq +
- lsr a
dey
bne -
+""")
}
"&" -> asmgen.out(" and P8ESTACK_LO+1,x")
"^" -> asmgen.out(" eor P8ESTACK_LO+1,x")
"|" -> asmgen.out(" ora P8ESTACK_LO+1,x")
"&" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"^" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
"|" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
if(ptrOnZp)
asmgen.out(" sta ($sourceName),y")
else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
asmgen.out(" inx")
}
private fun inplaceModification_byte_variable_to_memory(pointervar: IdentifierReference, operator: String, value: IdentifierReference) {
private fun inplaceModification_byte_variable_to_pointer(pointervar: IdentifierReference, operator: String, value: IdentifierReference) {
val otherName = asmgen.asmVariableName(value)
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
when (operator) {
// note: ** (power) operator requires floats.
"+" -> asmgen.out(" clc | adc $otherName")
"-" -> asmgen.out(" sec | sbc $otherName")
"+" -> asmgen.out(" clc | adc $otherName")
"-" -> asmgen.out(" sec | sbc $otherName")
"*" -> asmgen.out(" ldy $otherName | jsr math.multiply_bytes | ldy #0")
"/" -> asmgen.out(" ldy $otherName | jsr math.divmod_ub_asm | tya | ldy #0")
"%" -> asmgen.out(" ldy $otherName | jsr math.divmod_ub_asm | ldy #0")
@@ -312,7 +368,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
private fun inplaceModification_byte_litval_to_memory(pointervar: IdentifierReference, operator: String, value: Int) {
private fun inplaceModification_byte_litval_to_pointer(pointervar: IdentifierReference, operator: String, value: Int) {
when (operator) {
// note: ** (power) operator requires floats.
"+" -> {
@@ -413,30 +469,36 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
private fun inplaceModification_byte_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) {
// this should be the last resort for code generation for this,
// because the value is evaluated onto the eval stack (=slow).
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (5): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
when (operator) {
// note: ** (power) operator requires floats.
"+" -> asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name")
"-" -> asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name")
"*" -> asmgen.out(" lda P8ESTACK_LO+1,x | ldy $name | jsr math.multiply_bytes | sta $name")
"+" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" clc | adc $name | sta $name")
}
"-" -> {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", dt, null)
asmgen.out(" lda $name | sec | sbc P8ZP_SCRATCH_B1 | sta $name")
}
"*" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ldy $name | jsr math.multiply_bytes | sta $name")
}
"/" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.Y)
if(dt==DataType.UBYTE)
asmgen.out(" lda P8ESTACK_LO+1,x | tay | lda $name | jsr math.divmod_ub_asm | sty $name")
asmgen.out(" lda $name | jsr math.divmod_ub_asm | sty $name")
else
asmgen.out(" lda P8ESTACK_LO+1,x | tay | lda $name | jsr math.divmod_b_asm | sty $name")
asmgen.out(" lda $name | jsr math.divmod_b_asm | sty $name")
}
"%" -> {
if(dt==DataType.BYTE)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out(" lda P8ESTACK_LO+1,x | tay | lda $name | jsr math.divmod_ub_asm | sta $name")
asmgen.assignExpressionToRegister(value, RegisterOrPair.Y)
asmgen.out(" lda $name | jsr math.divmod_ub_asm | sta $name")
}
"<<" -> {
asmgen.translateExpression(value)
asmgen.assignExpressionToRegister(value, RegisterOrPair.Y)
asmgen.out("""
inx
ldy P8ESTACK_LO,x
beq +
- asl $name
dey
@@ -444,11 +506,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
}
">>" -> {
asmgen.translateExpression(value)
asmgen.assignExpressionToRegister(value, RegisterOrPair.Y)
if(dt==DataType.UBYTE) {
asmgen.out("""
inx
ldy P8ESTACK_LO,x
beq +
- lsr $name
dey
@@ -456,8 +516,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
} else {
asmgen.out("""
inx
ldy P8ESTACK_LO,x
beq +
- lda $name
asl a
@@ -467,12 +525,20 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
}
}
"&" -> asmgen.out(" lda $name | and P8ESTACK_LO+1,x | sta $name")
"^" -> asmgen.out(" lda $name | eor P8ESTACK_LO+1,x | sta $name")
"|" -> asmgen.out(" lda $name | ora P8ESTACK_LO+1,x | sta $name")
"&" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name")
}
"^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
"|" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ora $name | sta $name")
}
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
asmgen.out(" inx")
}
private fun inplaceModification_byte_variable_to_variable(name: String, dt: DataType, operator: String, ident: IdentifierReference) {
@@ -1093,15 +1159,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) {
// this should be the last resort for code generation for this,
// because the value is evaluated onto the eval stack (=slow).
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
val valueDt = value.inferType(program).typeOrElse(DataType.STRUCT)
fun multiplyWord() {
val valueiDt = value.inferType(program)
if(!valueiDt.isKnown)
throw AssemblyError("unknown dt")
val valueDt = valueiDt.typeOrElse(DataType.STRUCT)
fun multiplyVarByWordInAY() {
asmgen.out("""
lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda $name
@@ -1114,49 +1179,36 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
""")
}
fun divideWord() {
if (dt == DataType.WORD) {
asmgen.out("""
fun divideVarByWordInAY() {
asmgen.out("""
pha
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x
jsr math.divmod_w_asm
sta $name
sty $name+1
""")
} else {
asmgen.out("""
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x
jsr math.divmod_uw_asm
sta $name
sty $name+1
""")
}
lda $name+1
sta P8ZP_SCRATCH_W1+1
pla""")
if (dt == DataType.WORD)
asmgen.out(" jsr math.divmod_w_asm")
else
asmgen.out(" jsr math.divmod_uw_asm")
asmgen.out(" sta $name | sty $name+1")
}
fun remainderWord() {
fun remainderVarByWordInAY() {
if(dt==DataType.WORD)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out("""
pha
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x
lda $name+1
sta P8ZP_SCRATCH_W1+1
pla
jsr math.divmod_uw_asm
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
sta $name
lda P8ZP_SCRATCH_W2+1
sta $name+1
sty $name+1
""")
}
@@ -1166,11 +1218,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when (operator) {
// note: ** (power) operator requires floats.
"+" -> {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt, null)
if(valueDt==DataType.UBYTE)
asmgen.out("""
lda $name
clc
adc P8ESTACK_LO+1,x
adc P8ZP_SCRATCH_B1
sta $name
bcc +
inc $name+1
@@ -1178,7 +1231,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else
asmgen.out("""
ldy #0
lda P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_B1
bpl +
dey ; sign extend
+ clc
@@ -1189,25 +1242,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta $name+1""")
}
"-" -> {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_REG", valueDt, null)
if(valueDt==DataType.UBYTE)
asmgen.out("""
lda $name
sec
sbc P8ESTACK_LO+1,x
sbc P8ZP_SCRATCH_REG
sta $name
bcc +
bcs +
dec $name+1
+""")
else
asmgen.out("""
ldy #0
lda P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_REG
bpl +
dey ; sign extend
+ sty P8ZP_SCRATCH_B1
lda $name
sec
sbc P8ESTACK_LO+1,x
sbc P8ZP_SCRATCH_REG
sta $name
lda $name+1
sbc P8ZP_SCRATCH_B1
@@ -1215,22 +1269,28 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
"*" -> {
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
asmgen.signExtendStackLsb(valueDt)
multiplyWord()
// TODO use an optimized word * byte multiplication routine
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.signExtendAYlsb(valueDt)
multiplyVarByWordInAY()
}
"/" -> {
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
asmgen.signExtendStackLsb(valueDt)
divideWord()
// TODO use an optimized word / byte divmod routine
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.signExtendAYlsb(valueDt)
divideVarByWordInAY()
}
"%" -> {
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
asmgen.signExtendStackLsb(valueDt)
remainderWord()
// TODO use an optimized word / byte divmod routine
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.signExtendAYlsb(valueDt)
remainderVarByWordInAY()
}
"<<" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.Y)
asmgen.out("""
ldy P8ESTACK_LO+1,x
beq +
- asl $name
rol $name+1
@@ -1239,18 +1299,17 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
}
">>" -> {
if(dt==DataType.UWORD) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.Y)
if(dt==DataType.UWORD)
asmgen.out("""
ldy P8ESTACK_LO+1,x
beq +
- lsr $name+1
ror $name
dey
bne -
+""") }
else {
+""")
else
asmgen.out("""
ldy P8ESTACK_LO+1,x
beq +
- lda $name+1
asl a
@@ -1259,15 +1318,21 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
dey
bne -
+""")
}
}
"&" -> {
asmgen.out(" lda P8ESTACK_LO+1,x | and $name | sta $name")
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name")
if(dt in WordDatatypes)
asmgen.out(" lda #0 | sta $name+1")
}
"^" -> asmgen.out(" lda P8ESTACK_LO+1,x | eor $name | sta $name")
"|" -> asmgen.out(" lda P8ESTACK_LO+1,x | ora $name | sta $name")
"^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
"|" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ora $name | sta $name")
}
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -1275,33 +1340,48 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
// the value is a proper 16-bit word, so use both bytes of it.
when (operator) {
// note: ** (power) operator requires floats.
"+" -> asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name | lda $name+1 | adc P8ESTACK_HI+1,x | sta $name+1")
"-" -> asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name | lda $name+1 | sbc P8ESTACK_HI+1,x | sta $name+1")
"*" -> multiplyWord()
"/" -> divideWord()
"%" -> remainderWord()
"+" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" clc | adc $name | sta $name | tya | adc $name+1 | sta $name+1")
}
"-" -> {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", valueDt, null)
asmgen.out(" lda $name | sec | sbc P8ZP_SCRATCH_W1 | sta $name | lda $name+1 | sbc P8ZP_SCRATCH_W1+1 | sta $name+1")
}
"*" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
multiplyVarByWordInAY()
}
"/" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
divideVarByWordInAY()
}
"%" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
remainderVarByWordInAY()
}
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
"&" -> asmgen.out(" lda $name | and P8ESTACK_LO+1,x | sta $name | lda $name+1 | and P8ESTACK_HI+1,x | sta $name+1")
"^" -> asmgen.out(" lda $name | eor P8ESTACK_LO+1,x | sta $name | lda $name+1 | eor P8ESTACK_HI+1,x | sta $name+1")
"|" -> asmgen.out(" lda $name | ora P8ESTACK_LO+1,x | sta $name | lda $name+1 | ora P8ESTACK_HI+1,x | sta $name+1")
"&" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1")
}
"^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
}
"|" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1")
}
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
else -> {
throw AssemblyError("can only use integer datatypes here")
}
else -> throw AssemblyError("can only use integer datatypes here")
}
asmgen.out(" inx")
}
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: Expression, scope: Subroutine?) {
// this should be the last resort for code generation for this,
// because the value is evaluated onto the eval stack (=slow).
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (2): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.out(" jsr floats.pop_float_fac1")
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: Expression, scope: Subroutine) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1)
asmgen.saveRegister(CpuRegister.X, false, scope)
when (operator) {
"**" -> {
@@ -1350,7 +1430,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.restoreRegister(CpuRegister.X, false)
}
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference, scope: Subroutine?) {
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference, scope: Subroutine) {
val valueDt = ident.targetVarDecl(program.namespace)!!.datatype
if(valueDt != DataType.FLOAT)
throw AssemblyError("float variable expected")
@@ -1419,7 +1499,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.restoreRegister(CpuRegister.X, false)
}
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: Subroutine?) {
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: Subroutine) {
val constValueName = asmgen.getFloatAsmConst(value)
asmgen.saveRegister(CpuRegister.X, false, scope)
when (operator) {
@@ -1581,16 +1661,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
else -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (6): ${mem.addressExpression::class.simpleName} at ${mem.addressExpression.position}") // TODO
asmgen.translateExpression(mem.addressExpression)
asmgen.assignExpressionToVariable(mem.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, target.scope)
asmgen.out("""
jsr prog8_lib.read_byte_from_address_on_stack
ldy #0
lda (P8ZP_SCRATCH_W2),y
beq +
lda #1
+ eor #1
jsr prog8_lib.write_byte_to_address_on_stack
inx""")
sta (P8ZP_SCRATCH_W2),y""")
}
}
}
@@ -1651,14 +1729,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
else -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (7): ${memory.addressExpression::class.simpleName} at ${memory.addressExpression.position}") // TODO
asmgen.translateExpression(memory.addressExpression)
asmgen.assignExpressionToVariable(memory.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, target.scope)
asmgen.out("""
jsr prog8_lib.read_byte_from_address_on_stack
ldy #0
lda (P8ZP_SCRATCH_W2),y
eor #255
jsr prog8_lib.write_byte_to_address_on_stack
inx""")
sta (P8ZP_SCRATCH_W2),y""")
}
}
}
@@ -75,7 +75,7 @@ internal object CX16MachineDefinition: IMachineDefinition {
internal class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x79 // temp storage for a single byte
override val SCRATCH_REG = 0x7a // temp storage for a register
override val SCRATCH_REG = 0x7a // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0x7c // temp storage 1 for a word $7c+$7d
override val SCRATCH_W2 = 0x7e // temp storage 2 for a word $7e+$7f
+139 -70
View File
@@ -15,96 +15,165 @@ class FParam(val name: String, val possibleDatatypes: Set<DataType>)
typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteralValue
class FSignature(val pure: Boolean, // does it have side effects?
class ReturnConvention(val dt: DataType, val reg: RegisterOrPair?, val floatFac1: Boolean)
class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean)
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
override fun toString(): String {
val paramConvs = params.mapIndexed { index, it ->
when {
it.reg!=null -> "$index:${it.reg}"
it.variable -> "$index:variable"
else -> "$index:???"
}
}
val returnConv =
when {
returns.reg!=null -> returns.reg.toString()
returns.floatFac1 -> "floatFAC1"
else -> "<no returnvalue>"
}
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
}
}
class FSignature(val name: String,
val pure: Boolean, // does it have side effects?
val parameters: List<FParam>,
val returntype: DataType?,
val constExpressionFunc: ConstExpressionCaller? = null)
val known_returntype: DataType?, // specify return type if fixed, otherwise null if it depends on the arguments
val constExpressionFunc: ConstExpressionCaller? = null) {
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
val returns = when(known_returntype) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(known_returntype, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ReturnConvention(known_returntype, RegisterOrPair.AY, false)
DataType.FLOAT -> ReturnConvention(known_returntype, null, true)
in PassByReferenceDatatypes -> ReturnConvention(known_returntype!!, RegisterOrPair.AY, false)
else -> {
val paramType = actualParamTypes.first()
if(pure)
// return type depends on arg type
when(paramType) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ReturnConvention(paramType, null, true)
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
else -> ReturnConvention(paramType, null, false)
}
else {
ReturnConvention(paramType, null, false)
}
}
}
return when {
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
actualParamTypes.size==1 -> {
// one parameter? via register/registerpair
val paramConv = when(val paramType = actualParamTypes[0]) {
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false)
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
else -> ParamConvention(paramType, null, false)
}
CallConvention(listOf(paramConv), returns)
}
else -> {
// multiple parameters? via variables
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
CallConvention(paramConvs, returns)
}
}
}
}
val BuiltinFunctions = mapOf(
private val functionSignatures: List<FSignature> = listOf(
// this set of function have no return value and operate in-place:
"rol" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
"ror" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
"rol2" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
"ror2" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
FSignature("rol" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("ror" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("rol2" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("ror2" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null),
FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null),
// these few have a return value depending on the argument(s):
"max" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args
"min" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args
"sum" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
"sizeof" to FSignature(true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof),
FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args
FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args
FSignature("sum" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
FSignature("sizeof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof),
// normal functions follow:
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
"sin" to FSignature(true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
"sin8" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
"sin8u" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
"sin16" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
"sin16u" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
"cos" to FSignature(true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) },
"cos8" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
"cos8u" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
"cos16" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
"cos16u" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
"tan" to FSignature(true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) },
"atan" to FSignature(true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) },
"ln" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) },
"log2" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) },
"sqrt16" to FSignature(true, listOf(FParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
"sqrt" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) },
"rad" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) },
"deg" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) },
"round" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
"floor" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
"ceil" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
"lsb" to FSignature(true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 }},
"msb" to FSignature(true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255}},
"mkword" to FSignature(true, listOf(FParam("msb", setOf(DataType.UBYTE)), FParam("lsb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
"rnd" to FSignature(true, emptyList(), DataType.UBYTE),
"rndw" to FSignature(true, emptyList(), DataType.UWORD),
"rndf" to FSignature(true, emptyList(), DataType.FLOAT),
"exit" to FSignature(false, listOf(FParam("returnvalue", setOf(DataType.UBYTE))), null),
"rsave" to FSignature(false, emptyList(), null),
"rrestore" to FSignature(false, emptyList(), null),
"set_carry" to FSignature(false, emptyList(), null),
"clear_carry" to FSignature(false, emptyList(), null),
"set_irqd" to FSignature(false, emptyList(), null),
"clear_irqd" to FSignature(false, emptyList(), null),
"read_flags" to FSignature(false, emptyList(), DataType.UBYTE),
"swap" to FSignature(false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
"memcopy" to FSignature(false, listOf(
FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
FSignature("sin" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
FSignature("sin8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
FSignature("sin8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
FSignature("sin16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
FSignature("sin16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
FSignature("cos" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) },
FSignature("cos8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
FSignature("cos8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
FSignature("cos16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
FSignature("cos16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
FSignature("tan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) },
FSignature("atan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) },
FSignature("ln" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) },
FSignature("log2" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) },
FSignature("sqrt16" , true, listOf(FParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
FSignature("sqrt" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) },
FSignature("rad" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) },
FSignature("deg" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) },
FSignature("round" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
FSignature("floor" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
FSignature("ceil" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
FSignature("lsb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 }},
FSignature("msb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255}},
FSignature("mkword" , true, listOf(FParam("msb", setOf(DataType.UBYTE)), FParam("lsb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
FSignature("rnd" , false, emptyList(), DataType.UBYTE),
FSignature("rndw" , false, emptyList(), DataType.UWORD),
FSignature("rndf" , false, emptyList(), DataType.FLOAT),
FSignature("exit" , false, listOf(FParam("returnvalue", setOf(DataType.UBYTE))), null),
FSignature("rsave" , false, emptyList(), null),
FSignature("rrestore" , false, emptyList(), null),
FSignature("set_carry" , false, emptyList(), null),
FSignature("clear_carry" , false, emptyList(), null),
FSignature("set_irqd" , false, emptyList(), null),
FSignature("clear_irqd" , false, emptyList(), null),
FSignature("read_flags" , false, emptyList(), DataType.UBYTE),
FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
FSignature("memcopy" , false, listOf(
FParam("from", IterableDatatypes + DataType.UWORD),
FParam("to", IterableDatatypes + DataType.UWORD),
FParam("numbytes", setOf(DataType.UBYTE))), null),
"memset" to FSignature(false, listOf(
FParam("numbytes", setOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("memset" , false, listOf(
FParam("address", IterableDatatypes + DataType.UWORD),
FParam("numbytes", setOf(DataType.UWORD)),
FParam("bytevalue", ByteDatatypes)), null),
"memsetw" to FSignature(false, listOf(
FSignature("memsetw" , false, listOf(
FParam("address", IterableDatatypes + DataType.UWORD),
FParam("numwords", setOf(DataType.UWORD)),
FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
"strlen" to FSignature(true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen),
"substr" to FSignature(false, listOf(
FSignature("strlen" , true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen),
FSignature("substr" , false, listOf(
FParam("source", IterableDatatypes + DataType.UWORD),
FParam("target", IterableDatatypes + DataType.UWORD),
FParam("start", setOf(DataType.UBYTE)),
FParam("length", setOf(DataType.UBYTE))), null),
"leftstr" to FSignature(false, listOf(
FSignature("leftstr" , false, listOf(
FParam("source", IterableDatatypes + DataType.UWORD),
FParam("target", IterableDatatypes + DataType.UWORD),
FParam("length", setOf(DataType.UBYTE))), null),
"rightstr" to FSignature(false, listOf(
FSignature("rightstr" , false, listOf(
FParam("source", IterableDatatypes + DataType.UWORD),
FParam("target", IterableDatatypes + DataType.UWORD),
FParam("length", setOf(DataType.UBYTE))), null),
"strcmp" to FSignature(false, listOf(FParam("s1", IterableDatatypes + DataType.UWORD), FParam("s2", IterableDatatypes + DataType.UWORD)), DataType.BYTE, null)
FSignature("strcmp" , false, listOf(FParam("s1", IterableDatatypes + DataType.UWORD), FParam("s2", IterableDatatypes + DataType.UWORD)), DataType.BYTE, null)
)
val BuiltinFunctions = functionSignatures.associateBy { it.name }
fun builtinMax(array: List<Number>): Number = array.maxByOrNull { it.toDouble() }!!
fun builtinMin(array: List<Number>): Number = array.minByOrNull { it.toDouble() }!!
@@ -144,17 +213,17 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
}
val func = BuiltinFunctions.getValue(function)
if(func.returntype!=null)
return InferredTypes.knownFor(func.returntype)
// function has return values, but the return type depends on the arguments
if(func.known_returntype!=null)
return InferredTypes.knownFor(func.known_returntype)
// function has return values, but the return type depends on the arguments
return when (function) {
"abs" -> {
val dt = args.single().inferType(program)
if(dt.typeOrElse(DataType.STRUCT) in NumericDatatypes)
return dt
return if(dt.typeOrElse(DataType.STRUCT) in NumericDatatypes)
dt
else
throw FatalAstException("weird datatype passed to abs $dt")
InferredTypes.InferredType.unknown()
}
"max", "min" -> {
when(val dt = datatypeFromIterableArg(args.single())) {
+17 -10
View File
@@ -38,22 +38,26 @@ internal class BinExprSplitter(private val program: Program) : AstWalker() {
if (binExpr != null) {
/*
reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
by attempting to splitting it up into individual simple steps:
Reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
by attempting to splitting it up into individual simple steps.
We only consider a binary expression *one* level deep (so the operands must not be a combined expression)
X = BinExpr X = LeftExpr
<operator> followed by
/ \ IF 'X' not used X = BinExpr
/ \ IN LEFTEXPR ==> <operator>
X = BinExpr X = LeftExpr
<operator> followed by
/ \ IF 'X' not used X = BinExpr
/ \ IN expression ==> <operator>
/ \ / \
LeftExpr. RightExpr. / \
/ \ / \ X RightExpr.
.. .. .. ..
X RightExpr.
*/
if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target, program.namespace)) {
if (!assignment.isAugmentable) {
if(assignment.target isSameAs binExpr.left || assignment.target isSameAs binExpr.right)
return noModifications
if(isSimpleExpression(binExpr.right) && !assignment.isAugmentable) {
val firstAssign = Assignment(assignment.target, binExpr.left, binExpr.left.position)
val targetExpr = assignment.target.toExpression()
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
@@ -71,8 +75,11 @@ X = BinExpr X = LeftExpr
return noModifications
}
private fun isSimpleExpression(expr: Expression) =
expr is IdentifierReference || expr is NumericLiteralValue || expr is AddressOf || expr is DirectMemoryRead || expr is StringLiteralValue || expr is ArrayLiteralValue || expr is RangeExpr
private fun isSimpleTarget(target: AssignTarget, namespace: INameScope) =
if (target.identifier!=null || target.memoryAddress!=null || target.arrayindexed!=null)
if (target.identifier!=null || target.memoryAddress!=null)
target.isInRegularRAM(namespace)
else
false
@@ -477,7 +477,10 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
when (expr.operator) {
"%" -> {
if (cv == 1.0) {
return NumericLiteralValue(expr.inferType(program).typeOrElse(DataType.STRUCT), 0, expr.position)
val idt = expr.inferType(program)
if(!idt.isKnown)
throw FatalAstException("unknown dt")
return NumericLiteralValue(idt.typeOrElse(DataType.STRUCT), 0, expr.position)
} else if (cv == 2.0) {
expr.operator = "&"
expr.right = NumericLiteralValue.optimalInteger(1, expr.position)
@@ -606,8 +609,10 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (amount == 0) {
return expr.left
}
val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT)
when (targetDt) {
val targetIDt = expr.left.inferType(program)
if(!targetIDt.isKnown)
throw FatalAstException("unknown dt")
when (val targetDt = targetIDt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE, DataType.BYTE -> {
if (amount >= 8) {
return NumericLiteralValue(targetDt, 0, expr.position)
@@ -617,8 +622,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (amount >= 16) {
return NumericLiteralValue(targetDt, 0, expr.position)
} else if (amount >= 8) {
// TODO is this correct???
val lsb = TypecastExpression(expr.left, DataType.UBYTE, true, expr.position)
val lsb = FunctionCall(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
if (amount == 8) {
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteralValue.optimalInteger(0, expr.position)), expr.position)
}
@@ -640,7 +644,10 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (amount == 0) {
return expr.left
}
when (expr.left.inferType(program).typeOrElse(DataType.STRUCT)) {
val idt = expr.left.inferType(program)
if(!idt.isKnown)
throw FatalAstException("unknown dt")
when (idt.typeOrElse(DataType.STRUCT)) {
DataType.UBYTE -> {
if (amount >= 8) {
return NumericLiteralValue.optimalInteger(0, expr.position)
@@ -377,7 +377,7 @@ internal class StatementOptimizer(private val program: Program,
return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
}
val targetIDt = assignment.target.inferType(program, assignment)
val targetIDt = assignment.target.inferType(program)
if(!targetIDt.isKnown)
throw FatalAstException("can't infer type of assignment target")
+1 -1
View File
@@ -92,7 +92,7 @@ internal class ModuleImporter {
// accept additional imports
val lines = moduleAst.statements.toMutableList()
lines.asSequence()
.mapIndexed { i, it -> Pair(i, it) }
.mapIndexed { i, it -> i to it }
.filter { (it.second as? Directive)?.directive == "%import" }
.forEach { executeImportDirective(program, it.second as Directive, modulePath) }
+17 -3
View File
@@ -5,8 +5,6 @@ import org.hamcrest.Matchers.closeTo
import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import prog8.ast.Module
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
@@ -18,8 +16,8 @@ import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.cx16.CX16MachineDefinition
import java.io.CharConversionException
import java.nio.file.Path
import kotlin.test.*
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@@ -288,6 +286,22 @@ class TestC64Zeropage {
assertEquals(0xf9, zp.allocate("", DataType.UBYTE, null, errors))
assertEquals(0, zp.available())
}
@Test
fun testReservedLocations() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word")
}
}
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestCx16Zeropage {
@Test
fun testReservedLocations() {
val zp = CX16MachineDefinition.CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false))
assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word")
}
}
+2
View File
@@ -93,6 +93,8 @@ Almost instant compilation times (less than a second) can be achieved when using
Start the compiler with the ``-watch`` argument to enable this.
It will compile your program and then instead of exiting, it waits for any changes in the module source files.
As soon as a change happens, the program gets compiled again.
It is possible to use the watch mode with multiple modules as well, but it will
recompile everything in that list even if only of the files got updated.
Other options
^^^^^^^^^^^^^
+30 -37
View File
@@ -38,6 +38,27 @@ This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/license
:alt: Fully playable tetris clone
Language features
-----------------
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
The generated output is a machine code program runnable on actual 8-bit 6502 hardware.
- Provide a very convenient edit/compile/run cycle by being able to directly launch
the compiled program in an emulator and provide debugging information to this emulator.
- Based on simple and familiar imperative structured programming (it looks like a mix of C and Python)
- Modular programming and scoping via modules, code blocks, and subroutines.
- Provide high level programming constructs but at the same time stay close to the metal;
still able to directly use memory addresses and ROM subroutines,
and inline assembly to have full control when every register, cycle or byte matters
- Arbitrary number of subroutine parameters, Complex nested expressions are possible
- No stack frame allocations because parameters and local variables are automatically allocated statically
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
- Variable data types include signed and unsigned bytes and words, arrays, strings and floats.
- High-level code optimizations, such as const-folding, expression and statement simplifications/rewriting.
- Many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``substr``, ``sort`` and ``reverse`` (and others)
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
Code example
------------
@@ -104,37 +125,6 @@ when the exact same program is compiled for the Commander X16 target, and run on
Design principles and features
------------------------------
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
The generated output is a machine code program runnable on actual 8-bit 6502 hardware.
- Based on simple and familiar imperative structured programming (it looks like a mix of C and Python)
- 'One statement per line' code, resulting in clear readable programs.
- Modular programming and scoping via modules, code blocks, and subroutines.
- Provide high level programming constructs but at the same time stay close to the metal;
still able to directly use memory addresses and ROM subroutines,
and inline assembly to have full control when every register, cycle or byte matters
- Arbitrary number of subroutine parameters
- Complex nested expressions are possible
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
- Values are typed. Available data types include signed and unsigned bytes and words, arrays, strings and floats.
- No dynamic memory allocation or sizing! All variables stay fixed size as determined at compile time.
- Provide various quality of life language features and library subroutines specifically for the target platform.
- Provide a very convenient edit/compile/run cycle by being able to directly launch
the compiled program in an emulator and provide debugging information to this emulator.
- Arbitrary control flow jumps and branches are possible,
and will usually translate directly into the appropriate single 6502 jump/branch instruction.
- There are no complicated built-in error handling or overflow checks, you'll have to take care
of this yourself if required. This keeps the language and code simple and efficient.
- The compiler tries to optimize the program and generated code a bit, but hand-tuning of the
performance or space-critical parts will likely still be required. This is supported by
the ability to easily write embedded assembly code directly in the program source code.
- There are many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``substr``, ``sort`` and ``reverse`` (and others)
- Assembling the generated code into a program wil be done by an external cross-assembler tool.
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
.. _requirements:
Required tools
@@ -143,14 +133,17 @@ Required tools
`64tass <https://sourceforge.net/projects/tass64/>`_ - cross assembler. Install this on your shell path.
It's very easy to compile yourself.
A recent precompiled .exe for Windows can be obtained from my `clone <https://github.com/irmen/64tass/releases>`_ of this project.
*You need at least version 1.55.2257 of this assembler to correctly use the breakpoints feature.*
It's possible to use older versions, but it is very likely that the automatic Vice breakpoints won't work with them.
A **Java runtime (jre or jdk), version 8 or newer** is required to run the prog8 compiler itself.
If you're scared of Oracle's licensing terms, most Linux distributions ship OpenJDK instead.
Fnd for Windows it's possible to get that as well. Check out `AdoptOpenJDK <https://adoptopenjdk.net/>`_ .
A **Java runtime (jre or jdk), version 11 or newer** is required to run the prog8 compiler itself.
If you're scared of Oracle's licensing terms, most Linux distributions ship OpenJDK in their packages repository instead.
For Windows it's possible to get that as well; check out `AdoptOpenJDK <https://adoptopenjdk.net/>`_ .
For MacOS you can use the Homebrew system to install a recent version of OpenJDK.
Finally: a **C-64 emulator** (or a real C-64 ofcourse) can be nice to test and run your programs on.
The compiler assumes the presence of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
If you're targeting the CommanderX16, there's the `x16emu <https://github.com/commanderx16/x16-emulator>`_.
Finally: an **emulator** (or a real machine ofcourse) to test and run your programs on.
In C64 mode, thhe compiler assumes the presence of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
If you're targeting the CommanderX16 instead, there's the `x16emu <https://github.com/commanderx16/x16-emulator>`_.
.. important::
**Building the compiler itself:** (*Only needed if you have not downloaded a pre-built 'fat-jar'*)
+7 -4
View File
@@ -765,18 +765,18 @@ sort(array)
Strings and memory blocks
^^^^^^^^^^^^^^^^^^^^^^^^^
memcopy(from, to, numbytes)
Efficiently copy a number of bytes (1 - 256) from a memory location to another.
Efficiently copy a number of bytes from a memory location to another.
NOTE: 'to' must NOT overlap with 'from', unless it is *before* 'from'.
Because this function imposes some overhead to handle the parameters,
it is only faster if the number of bytes is larger than a certain threshold.
Compare the generated code to see if it was beneficial or not.
The most efficient will always be to write a specialized copy routine in assembly yourself!
The most efficient will often be to write a specialized copy routine in assembly yourself!
memset(address, numbytes, bytevalue)
Efficiently set a part of memory to the given (u)byte value.
But the most efficient will always be to write a specialized fill routine in assembly yourself!
Note that for clearing the character screen, very fast specialized subroutines are
available in the ``txt`` block (part of the ``textio`` module)
Note that for clearing the screen, very fast specialized subroutines are
available in the ``textio`` and ``graphics`` library modules.
memsetw(address, numwords, wordvalue)
Efficiently set a part of memory to the given (u)word value.
@@ -785,11 +785,13 @@ memsetw(address, numwords, wordvalue)
leftstr(source, target, length)
Copies the left side of the source string of the given length to target string.
It is assumed the target string buffer is large enough to contain the result.
Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
Modifies in-place, doesn't return a value (so can't be used in an expression).
rightstr(source, target, length)
Copies the right side of the source string of the given length to target string.
It is assumed the target string buffer is large enough to contain the result.
Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
Modifies in-place, doesn't return a value (so can't be used in an expression).
strlen(str)
@@ -806,6 +808,7 @@ substr(source, target, start, length)
Copies a segment from the source string, starting at the given index,
and of the given length to target string.
It is assumed the target string buffer is large enough to contain the result.
Also, you have to make sure yourself that start and length are within bounds of the strings.
Modifies in-place, doesn't return a value (so can't be used in an expression).
Miscellaneous
+2 -1
View File
@@ -122,7 +122,8 @@ Directives
- For a module option, there is ``enable_floats``, which will tell the compiler
to deal with floating point numbers (by using various subroutines from the Commodore-64 kernal).
Otherwise, floating point support is not enabled.
Otherwise, floating point support is not enabled. Normally you don't have to use this yourself as
importing the ``floats`` library is required anyway and that will enable it for you automatically.
- There's also ``no_sysinit`` which cause the resulting program to *not* include
the system re-initialization logic of clearing the screen, resetting I/O config etc. You'll have to
take care of that yourself. The program will just start running from whatever state the machine is in when the
+8 -8
View File
@@ -2,14 +2,15 @@
TODO
====
- get rid of all other TODO's in the code ;-)
- implement @stack for asmsub parameters
- see if we can group some errors together for instance the (now single) errors about unidentified symbols
- Cx16 target: support full-screen 640x480 and 320x240 graphics? That requires our own custom graphics routines though to draw lines.
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
- see if we can group some errors together for instance the (now single) errors about unidentified symbols
- use VIC banking to move up the graphics bitmap memory location. Don't move it under the ROM though as that would require IRQ disabling and memory bank swapping for every bitmap manipulation
- add some primitives/subroutines/examples for using custom char sets, copying the default charset.
- recursive subroutines? via %option recursive, allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters
- use VIC banking to move up the graphics bitmap memory location. Move it to $e000 under the kernal rom?
- some support for recursive subroutines?
- via %option recursive?: allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters
- Or via a special recursive call operation that copies the current values of all local vars (including arguments) to the stack, replaces the arguments, jsr subroutine, and after returning copy the stack back to the local variables
- get rid of all other TODO's in the code ;-)
More optimizations
^^^^^^^^^^^^^^^^^^
@@ -18,8 +19,7 @@ Add more compiler optimizations to the existing ones.
- further optimize assignment codegeneration, such as the following:
- binexpr splitting (beware self-referencing expressions and asm code ballooning though)
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise. Especially for built-in functions!
- can such parameter passing to subroutines be optimized to avoid copying?
- detect var->var argument passing to subroutines and avoid the second variable and copying of the value
- more optimizations on the language AST level
- more optimizations on the final assembly source level
- note: subroutine inlining is abandoned because of problems referencing non-local stuff. Can't move everything around.
+836
View File
@@ -0,0 +1,836 @@
%import textio
%import floats
%import syslib
%import test_stack
%zeropage basicsafe
main {
sub start() {
rotations()
strings()
integers()
floatingpoint()
test_stack.test()
}
sub rotations() {
ubyte[] ubarr = [%11000111]
uword[] uwarr = [%1100111110101010]
repeat(10) {
txt.chrout('\n')
}
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
set_carry()
rol(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
set_carry()
rol(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
set_carry()
rol(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
set_carry()
rol(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
txt.chrout('\n')
uwarr[0] = %1100111110101010
ror(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
set_carry()
ror(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
set_carry()
ror(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
set_carry()
ror(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
set_carry()
ror(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
txt.chrout('\n')
clear_carry()
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
rol2(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
rol2(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
rol2(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
rol2(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
txt.chrout('\n')
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
ror2(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
ror2(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
ror2(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
ror2(uwarr[0])
txt.print_uwbin(uwarr[0], true)
txt.chrout('\n')
txt.chrout('\n')
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
set_carry()
rol(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
set_carry()
rol(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
set_carry()
rol(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
set_carry()
rol(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
txt.chrout('\n')
set_carry()
ror(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
set_carry()
ror(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
set_carry()
ror(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
set_carry()
ror(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
set_carry()
ror(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
txt.chrout('\n')
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
rol2(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
rol2(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
rol2(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
rol2(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
txt.chrout('\n')
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
ror2(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
ror2(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
ror2(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
ror2(ubarr[0])
txt.print_ubbin(ubarr[0], true)
txt.chrout('\n')
txt.chrout('\n')
&ubyte membyte = $c000
uword addr = $c000
@(addr) = %10110101
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
set_carry()
rol(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
set_carry()
rol(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
set_carry()
rol(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
set_carry()
rol(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
txt.chrout('\n')
@(addr) = %10110101
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
set_carry()
ror(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
set_carry()
ror(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
set_carry()
ror(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
set_carry()
ror(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
txt.chrout('\n')
@(addr) = %10110101
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
rol2(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
rol2(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
rol2(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
rol2(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
txt.chrout('\n')
@(addr) = %10110101
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
ror2(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
ror2(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
ror2(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
ror2(@(addr))
txt.print_ubbin(@(addr), true)
txt.chrout('\n')
txt.chrout('\n')
test_stack.test()
}
sub strings() {
const uword ADDR = $8400
const uword ADDR2 = $8000
memset(ADDR2, 40*25, '*')
memset(ADDR2, 40, '1')
memset(ADDR2+24*40, 39, '2')
memsetw(ADDR2, 40*25/2, $3132)
memsetw(ADDR2, 20, $4142)
memsetw(ADDR2+24*40, 19, $4241)
memcopy(ADDR2, ADDR, 200)
str result = "?" *10
str s1 = "irmen"
str s2 = "hello"
str dots = "....."
ubyte ub
byte bb
ubyte zero=0
bb = strcmp(s1, s2)
txt.print_b(bb)
txt.chrout('\n')
bb = strcmp(s2, s1)
txt.print_b(bb)
txt.chrout('\n')
txt.print_ub(s1==s2)
txt.chrout('\n')
txt.print_ub(s1<s2)
txt.chrout('\n')
txt.print_ub(s1>s2)
txt.chrout('\n')
bb = zero+strcmp(s1,s2)*1+zero
txt.print_b(bb)
txt.chrout('\n')
bb = zero+strcmp(s2,s1)*1+zero
txt.print_b(bb)
txt.chrout('\n')
ub = strlen(s1)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+strlen(s1)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
leftstr(s1, result, 3)
txt.print(result)
txt.chrout('\n')
leftstr(s1, result, len(s1))
txt.print(result)
txt.chrout('\n')
txt.chrout('\n')
result = "x"*8
rightstr(s2, result, 3)
txt.print(result)
txt.chrout('\n')
rightstr(s2, result, len(s1))
txt.print(result)
txt.chrout('\n')
result = "y"*10
substr(s2, result, 1, 3)
txt.print(result)
txt.chrout('\n')
test_stack.test()
}
sub integers() {
ubyte[] ubarr = [1,2,3,4,5,0,4,3,2,1, 255, 255, 255]
byte[] barr = [1,2,3,4,5,-4,0,-3,2,1, -128, -128, -127]
uword[] uwarr = [100,200,300,400,0,500,400,300,200,100]
word[] warr = [100,200,300,400,500,0,-400,-300,200,100,-99, -4096]
ubyte zero=0
ubyte ub
ubyte ub2
byte bb
uword uw
word ww
repeat(20) {
txt.chrout('\n')
}
ub = read_flags()
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+read_flags()*1+zero
txt.print_ub(ub)
txt.chrout('\n')
ub = rnd()
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+rnd()*1+zero
txt.print_ub(ub)
txt.chrout('\n')
uw = rndw()
txt.print_uw(uw)
txt.chrout('\n')
uw = zero+rndw()*1+zero
txt.print_uw(uw)
txt.chrout('\n')
uw = 50000
ub = sqrt16(uw)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+sqrt16(uw)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
bb = -100
bb = sgn(bb)
txt.print_b(bb)
txt.chrout('\n')
bb = -100
bb = zero+sgn(bb)*1+zero
txt.print_b(bb)
txt.chrout('\n')
ub = 100
bb = sgn(ub)
txt.print_b(bb)
txt.chrout('\n')
ub = 100
bb = zero+sgn(ub)*1+zero
txt.print_b(bb)
txt.chrout('\n')
ww = -1000
bb = sgn(ww)
txt.print_b(bb)
txt.chrout('\n')
bb = zero+sgn(ww)*1+zero
txt.print_b(bb)
txt.chrout('\n')
uw = 1000
bb = sgn(uw)
txt.print_b(bb)
txt.chrout('\n')
bb = zero+sgn(uw)*1+zero
txt.print_b(bb)
txt.chrout('\n')
ub = 0
uw = sin16u(ub)
txt.print_uw(uw)
txt.chrout('\n')
uw = zero+sin16u(ub)*1+zero
txt.print_uw(uw)
txt.chrout('\n')
ub = 0
uw = cos16u(ub)
txt.print_uw(uw)
txt.chrout('\n')
uw = zero+cos16u(ub)*1+zero
txt.print_uw(uw)
txt.chrout('\n')
ub = 0
ww = sin16(ub)
txt.print_w(ww)
txt.chrout('\n')
ww = zero+sin16(ub)*1+zero
txt.print_w(ww)
txt.chrout('\n')
ub = 0
ww = cos16(ub)
txt.print_w(ww)
txt.chrout('\n')
uw = 0
ww = zero+cos16(ub)*1+zero
txt.print_w(ww)
txt.chrout('\n')
ub2 = 0
ub = sin8u(ub2)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+sin8u(ub2)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
ub2 = 0
ub = cos8u(ub2)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+cos8u(ub2)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
ub2 = 0
bb = sin8(ub2)
txt.print_b(bb)
txt.chrout('\n')
bb = zero+sin8(ub2)*1+zero
txt.print_b(bb)
txt.chrout('\n')
ub2 = 0
bb = cos8(ub2)
txt.print_b(bb)
txt.chrout('\n')
bb = zero+cos8(ub2)*1+zero
txt.print_b(bb)
txt.chrout('\n')
bb = -100
bb = abs(bb)
txt.print_b(bb)
txt.chrout('\n')
bb = -100
bb = zero+abs(bb)*1+zero
txt.print_b(bb)
txt.chrout('\n')
ww = -1000
ww = abs(ww)
txt.print_w(ww)
txt.chrout('\n')
ww = -1000
ww = zero+abs(ww)*1+zero
txt.print_w(ww)
txt.chrout('\n')
ub = min(ubarr)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+min(ubarr)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
bb = min(barr)
txt.print_b(bb)
txt.chrout('\n')
bb = zero+min(barr)*1+zero
txt.print_b(bb)
txt.chrout('\n')
uw = min(uwarr)
txt.print_uw(uw)
txt.chrout('\n')
uw = zero+min(uwarr)*1+zero
txt.print_uw(uw)
txt.chrout('\n')
ww = min(warr)
txt.print_w(ww)
txt.chrout('\n')
ww = zero+min(warr)*1+zero
txt.print_w(ww)
txt.chrout('\n')
ub = max(ubarr)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+max(ubarr)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
bb = max(barr)
txt.print_b(bb)
txt.chrout('\n')
bb = zero+max(barr)*1+zero
txt.print_b(bb)
txt.chrout('\n')
uw = max(uwarr)
txt.print_uw(uw)
txt.chrout('\n')
uw = zero+max(uwarr)*1+zero
txt.print_uw(uw)
txt.chrout('\n')
ww = max(warr)
txt.print_w(ww)
txt.chrout('\n')
ww = zero+max(warr)*1+zero
txt.print_w(ww)
txt.chrout('\n')
ub = any(ubarr)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+any(ubarr)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
ub = any(barr)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+any(barr)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
ub = any(uwarr)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+any(uwarr)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
ub = any(warr)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+any(warr)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
ub = all(ubarr)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+all(ubarr)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
ub = all(barr)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+all(barr)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
ub = all(uwarr)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+all(uwarr)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
ub = all(warr)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+all(warr)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
uw = sum(ubarr)
txt.print_uw(uw)
txt.chrout('\n')
uw = zero+sum(ubarr)*1+zero
txt.print_uw(uw)
txt.chrout('\n')
ww = sum(barr)
txt.print_w(ww)
txt.chrout('\n')
ww = zero+sum(barr)*1+zero
txt.print_w(ww)
txt.chrout('\n')
uw = sum(uwarr)
txt.print_uw(uw)
txt.chrout('\n')
uw = zero+sum(uwarr)*1+zero
txt.print_uw(uw)
txt.chrout('\n')
ww = sum(warr)
txt.print_w(ww)
txt.chrout('\n')
ww = zero+sum(warr)*1+zero
txt.print_w(ww)
txt.chrout('\n')
sort(ubarr)
sort(barr)
sort(uwarr)
sort(warr)
reverse(ubarr)
reverse(barr)
reverse(uwarr)
reverse(warr)
test_stack.test()
}
sub floatingpoint() {
ubyte[] barr = [1,2,3,4,5,0,4,3,2,1]
float[] flarr = [1.1, 2.2, 3.3, 0.0, -9.9, 5.5, 4.4]
ubyte zero=0
ubyte ub
byte bb
uword uw
float fl
float fzero=0.0
fl = -9.9
fl = abs(fl)
floats.print_f(fl)
txt.chrout('\n')
fl = -9.9
fl = fzero+abs(fl)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = 9.9
fl = atan(fl)
floats.print_f(fl)
txt.chrout('\n')
fl = 9.9
fl = fzero+atan(fl)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = -9.9
fl = ceil(fl)
floats.print_f(fl)
txt.chrout('\n')
fl = -9.9
fl = fzero+ceil(fl)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = -9.9
fl = cos(fl)
floats.print_f(fl)
txt.chrout('\n')
fl = -9.9
fl = fzero+cos(fl)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = -9.9
fl = sin(fl)
floats.print_f(fl)
txt.chrout('\n')
fl = -9.9
fl = fzero+sin(fl)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = 9.9
fl = tan(fl)
floats.print_f(fl)
txt.chrout('\n')
fl = 9.9
fl = fzero+tan(fl)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = 3.1415927
fl = deg(fl)
floats.print_f(fl)
txt.chrout('\n')
fl = 3.1415927
fl = fzero+deg(fl)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = 90
fl = rad(fl)
floats.print_f(fl)
txt.chrout('\n')
fl = 90
fl = fzero+rad(fl)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = -9.9
fl = floor(fl)
floats.print_f(fl)
txt.chrout('\n')
fl = -9.9
fl = fzero+floor(fl)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = 3.1415927
fl = ln(fl)
floats.print_f(fl)
txt.chrout('\n')
fl = 3.1415927
fl = fzero+ln(fl)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = 3.1415927
fl = log2(fl)
floats.print_f(fl)
txt.chrout('\n')
fl = 3.1415927
fl = fzero+log2(fl)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = -9.9
fl = round(fl)
floats.print_f(fl)
txt.chrout('\n')
fl = -9.9
fl = fzero+round(fl)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = -9.9
bb = sgn(fl)
txt.print_b(bb)
txt.chrout('\n')
fl = -9.9
bb = zero+sgn(fl)*1+zero
txt.print_b(bb)
txt.chrout('\n')
fl = 3.1415927
fl = sqrt(fl)
floats.print_f(fl)
txt.chrout('\n')
fl = 3.1415927
fl = fzero+sqrt(fl)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = rndf()
floats.print_f(fl)
txt.chrout('\n')
fl = fzero+rndf()*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
swap(fl, fzero)
swap(fzero, fl)
ub = any(flarr)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+any(flarr)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
ub = all(flarr)
txt.print_ub(ub)
txt.chrout('\n')
ub = zero+all(flarr)*1+zero
txt.print_ub(ub)
txt.chrout('\n')
reverse(flarr)
for ub in 0 to len(flarr)-1 {
floats.print_f(flarr[ub])
txt.chrout(',')
}
txt.chrout('\n')
fl = max(flarr)
floats.print_f(fl)
txt.chrout('\n')
fl = fzero+max(flarr)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = min(flarr)
floats.print_f(fl)
txt.chrout('\n')
fl = fzero+min(flarr)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
fl = sum(flarr)
floats.print_f(fl)
txt.chrout('\n')
fl = fzero+sum(flarr)*1.0+fzero
floats.print_f(fl)
txt.chrout('\n')
test_stack.test()
}
}
+6 -2
View File
@@ -1,5 +1,6 @@
%import floats
%import textio
%import test_stack
%zeropage basicsafe
main {
@@ -31,6 +32,8 @@ main {
minus_float(0,0,0)
minus_float(2.5,1.5,1.0)
minus_float(-1.5,3.5,-5.0)
test_stack.test()
}
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
@@ -97,10 +100,11 @@ main {
float r = a1-a2
if abs(r-c)<0.00001
txt.print(" ok ")
else
else {
txt.print("err! ")
}
txt.print("float ")
txt.print(" float ")
floats.print_f(a1)
txt.print(" - ")
floats.print_f(a2)
+4
View File
@@ -1,6 +1,7 @@
%target c64
%import syslib
%import textio
%import test_stack
%zeropage basicsafe
main {
@@ -23,6 +24,9 @@ main {
ubyte upwards = true
repeat {
;txt.plot(0,0)
;test_stack.test()
ubyte mountain = 223 ; slope upwards
if active_height < target_height {
active_height++
+103
View File
@@ -0,0 +1,103 @@
%import textio
%import test_stack
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
sub start() {
str input = ".........."
ubyte ballCount
ubyte[255] BX
ubyte[255] BY
ubyte[255] BC
ubyte[255] DX
ubyte[255] DY
txt.print("number of balls (1-255)? ")
void txt.input_chars(input)
ballCount = conv.str2ubyte(input)
txt.fill_screen(81, 0)
; Setup Starting Ball Positions
ubyte lp
for lp in 0 to ballCount-1 {
BX[lp] = rnd() % txt.DEFAULT_WIDTH
BY[lp] = rnd() % txt.DEFAULT_HEIGHT
BC[lp] = rnd() & 15
DX[lp] = rnd() & 1
DY[lp] = rnd() & 1
void rnd()
}
; start clock
c64.SETTIM(0,0,0)
; display balls
uword frame
for frame in 0 to 999 {
; Loop though all balls clearing current spot and setting new spot
for lp in 0 to ballCount-1 {
; Clear existing Location the ball is at
txt.setclr(BX[lp], BY[lp], 0)
if DX[lp] == 0 {
if (BX[lp] == 0)
{
DX[lp] = 1
} else {
BX[lp]=BX[lp]-1
}
} else if DX[lp] == 1 {
if (BX[lp] == txt.DEFAULT_WIDTH-1)
{
BX[lp] = txt.DEFAULT_WIDTH-2
DX[lp] = 0
} else {
BX[lp]=BX[lp]+1
}
}
if DY[lp] == 0 {
if (BY[lp] == 0)
{
DY[lp] = 1
} else {
BY[lp]=BY[lp]-1
}
} else if DY[lp] == 1 {
if (BY[lp] == txt.DEFAULT_HEIGHT-1)
{
BY[lp] = txt.DEFAULT_HEIGHT-2
DY[lp] = 0
} else {
BY[lp]=BY[lp]+1
}
}
; Put the new ball possition
txt.setclr(BX[lp], BY[lp], BC[lp])
}
;txt.plot(0,0)
;txt.print_uw(frame)
}
; read clock
uword jiffies
%asm {{
stx P8ZP_SCRATCH_REG
jsr c64.RDTIM
sta jiffies
stx jiffies+1
ldx P8ZP_SCRATCH_REG
}}
txt.print("\nbenchmark: ")
txt.print_uw(jiffies)
txt.print(" jiffies for 1000 frames.\n")
; test_stack.test()
}
}
+85
View File
@@ -0,0 +1,85 @@
%target c64
%import syslib
%import textio
%zeropage basicsafe
%option no_sysinit
; Create a custom character set on the C64.
main {
sub start() {
txt.color(1)
txt.print("creating charset...\n")
charset.make_custom_charset()
; activate the new charset in RAM
ubyte block = c64.CIA2PRA
const ubyte PAGE1 = ((c64.Screen >> 6) & $F0) | ((charset.CHARSET >> 10) & $0E)
c64.CIA2PRA = (block & $FC) | (lsb(c64.Screen >> 14) ^ $03)
c64.VMCSB = PAGE1
txt.print("\n @ @ @ @\n")
}
}
charset {
const uword CHARSET = $2000
sub copy_rom_charset() {
; copies the charset from ROM to RAM so we can modify it
set_irqd()
ubyte bank = @($0001)
@($0001) = bank & %11111011 ; enable CHAREN, so the character rom accessible at $d000
memcopy($d000, CHARSET, 256*8*2) ; copy the charset to RAM
@($0001) = bank ; reset previous memory banking
clear_irqd()
}
sub make_custom_charset() {
copy_rom_charset()
; make all characters italic
ubyte c
for c in 0 to 255 {
uword ptr = CHARSET + c*$0008
@(ptr) >>= 2
@(ptr+1) >>= 2
@(ptr+2) >>= 1
@(ptr+3) >>= 1
;@(ptr+4) >>= 0
;@(ptr+5) >>= 0
@(ptr+6) <<= 1
@(ptr+7) <<= 1
ptr = CHARSET + 256*8 + c*$0008
@(ptr) >>= 2
@(ptr+1) >>= 2
@(ptr+2) >>= 1
@(ptr+3) >>= 1
;@(ptr+4) >>= 0
;@(ptr+5) >>= 0
@(ptr+6) <<= 1
@(ptr+7) <<= 1
}
; add a smiley over the '@'
ubyte[] smiley = [
%00111100,
%01000010,
%10100101,
%10000001,
%10100101,
%10011001,
%01000010,
%00111100
]
memcopy(smiley, CHARSET, len(smiley))
}
}
+913
View File
@@ -0,0 +1,913 @@
%import textio
%import floats
%zeropage basicsafe
%import test_stack
main {
sub start() {
word_less()
word_lessequal()
word_greaterequal()
word_greater()
uword_lessequal()
}
sub uword_lessequal() {
uword lessvar
uword comparevar
txt.print("uword <=\n")
txt.print_uw(65535)
txt.chrout('\n')
check_lesseq_uw(0, 65535)
txt.print_uw(0)
txt.chrout('\n')
check_not_lesseq_uw(65535, 0)
comparevar = 65535
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-2
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-254
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-255
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-256
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-5000
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 32769
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 32768
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 1
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 0
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 11111
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 255
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 256
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
test_stack.test()
return
sub check_lesseq_uw(uword w1, uword w2) {
uword zero = 0
ubyte error=0
ubyte ub = w1<=w2
if not ub {
error++
txt.print("ub!")
}
if w1<=(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_uw(w1)
txt.print(" <= ")
txt.print_uw(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_lesseq_uw(uword w1, uword w2) {
uword zero = 0
ubyte error=0
ubyte ub = w1<=w2
if ub {
error++
txt.print("ub!")
}
if w1<=(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_uw(w1)
txt.print(" not <= ")
txt.print_uw(w2)
txt.chrout('\n')
exit(1)
}
}
}
sub word_greater() {
word biggervar
word comparevar
txt.print("word >\n")
txt.print_w(-32767)
txt.chrout('\n')
check_greater_w(32767, -32767)
txt.print_w(32766)
txt.chrout('\n')
check_not_greater_w(-32766, 32766)
comparevar = 32765
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 32760
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
test_stack.test()
return
sub check_greater_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1>(w2+zero)
if not ub {
error++
txt.print("ubz!")
}
ub = w1>w2
if not ub {
error++
txt.print("ub!")
}
if w1>(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" > ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_greater_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1>w2
if ub {
error++
txt.print("ub!")
}
if w1>(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if w1>w2 {
error++
txt.print("c2!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" not > ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
}
sub word_greaterequal() {
word biggervar
word comparevar
txt.print("word >=\n")
txt.print_w(-32767)
txt.chrout('\n')
check_greatereq_w(32767, -32767)
txt.print_w(32766)
txt.chrout('\n')
check_not_greatereq_w(-32766, 32766)
comparevar = 32765
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 32767
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
test_stack.test()
return
sub check_greatereq_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1>=(w2+zero)
if not ub {
error++
txt.print("ubz!")
}
ub = w1>=w2
if not ub {
error++
txt.print("ub!")
}
if w1>=(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" >= ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_greatereq_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1>=w2
if ub {
error++
txt.print("ub!")
}
if w1>=(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if w1>=w2 {
error++
txt.print("c2!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" not >= ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
}
sub word_lessequal() {
word lessvar
word comparevar
txt.print("word <=\n")
txt.print_w(32767)
txt.chrout('\n')
check_lesseq_w(-32767, 32767)
txt.print_w(-32767)
txt.chrout('\n')
check_not_lesseq_w(32767, -32767)
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 32767
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -32768
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
test_stack.test()
return
sub check_lesseq_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1<=w2
if not ub {
error++
txt.print("ub!")
}
if w1<=(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" <= ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_lesseq_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1<=w2
if ub {
error++
txt.print("ub!")
}
if w1<=(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" not <= ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
}
sub word_less() {
word lessvar
word comparevar
txt.print("word <\n")
txt.print_w(32767)
txt.chrout('\n')
check_less_w(-32767, 32767)
txt.print_w(-32767)
txt.chrout('\n')
check_not_less_w(32767, -32767)
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -1 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -3 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -255 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -256 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -257 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -5001 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 0 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 254 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 255 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 256 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 32767
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -32768
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto -32768 {
check_not_less_w(lessvar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto -1 {
check_not_less_w(lessvar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto 0 {
check_not_less_w(lessvar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto 11111 {
check_not_less_w(lessvar, comparevar)
}
test_stack.test()
return
sub check_less_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1<w2
if not ub {
error++
txt.print("ub!")
}
if w1<(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" < ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_less_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1<w2
if ub {
error++
txt.print("ub!")
}
if w1<(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" not < ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3
View File
@@ -1,5 +1,6 @@
%import floats
%import textio
%import test_stack
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
@@ -41,6 +42,8 @@ main {
txt.print(" jiffies/fr = ")
txt.print_ub(60/timer_jiffies)
txt.print(" fps")
;test_stack.test()
}
}
+3
View File
@@ -1,5 +1,6 @@
%import syslib
%import graphics
%import test_stack
; Note: this program is compatible with C64 and CX16.
@@ -37,6 +38,8 @@ main {
anglez+=452
wait_a_little_bit()
; test_stack.test()
}
}
+4
View File
@@ -1,6 +1,8 @@
%target c64
%import syslib
%import textio
%import test_stack
spritedata $2000 {
; this memory block contains the sprite data
@@ -95,6 +97,8 @@ main {
txt.print(" jiffies/fr = ")
txt.print_ub(60/c64.TIME_LO)
txt.print(" fps")
; test_stack.test()
}
}
+4
View File
@@ -1,7 +1,9 @@
%target c64
%import syslib
%import test_stack
%import textio
main {
; vertices
@@ -33,6 +35,8 @@ main {
txt.print_ub(60/c64.TIME_LO)
txt.print(" fps")
c64.TIME_LO=0
; test_stack.test()
}
}
+6 -2
View File
@@ -1,5 +1,6 @@
%target cx16
%import syslib
%import test_stack
%import conv
; TODO add all other Elite's ships, show their name, advance to next ship on keypress
@@ -36,11 +37,13 @@ main {
cx16.GRAPH_set_colors(1, 0, 0)
draw_lines_hiddenremoval()
;draw_lines()
; draw_lines()
anglex += 217
angley -= 505
anglez += 452
; test_stack.test()
}
}
@@ -191,6 +194,7 @@ main {
sub facing_away(ubyte edgePointsIdx) -> ubyte {
; simplistic visibility determination by checking the Z component of the surface normal
; TODO: actually take the line of sight vector into account
ubyte p1 = shipdata.facesPoints[edgePointsIdx]
edgePointsIdx++
ubyte p2 = shipdata.facesPoints[edgePointsIdx]
@@ -223,7 +227,7 @@ main {
shipdata {
; Ship model data converted from BBC Elite's Cobra MK 3
; Ship model data converted from BBC Elite's Cobra MK 3
; downloaded from http://www.elitehomepage.org/archive/index.htm
const ubyte totalNumberOfEdges = 51
+6
View File
@@ -1,4 +1,5 @@
%import graphics
%import test_stack
; Note: this program is compatible with C64 and CX16.
@@ -6,8 +7,13 @@ main {
sub start() {
graphics.enable_bitmap_mode()
draw_lines()
draw_circles()
; graphics.disable_bitmap_mode()
; test_stack.test()
repeat {
}
}
+3
View File
@@ -1,5 +1,6 @@
%import textio
%import syslib
%import test_stack
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
@@ -40,6 +41,8 @@ main {
rect(10, 10, 10, 10, false)
rect(6, 0, 16, 20, true)
; test_stack.test()
sub rect(ubyte x1, ubyte y1, ubyte x2, ubyte y2, ubyte fill) {
ubyte x
+4
View File
@@ -1,5 +1,6 @@
%import graphics
%import floats
%import test_stack
%zeropage floatsafe
; Draw a mandelbrot in graphics mode (the image will be 256 x 200 pixels).
@@ -44,6 +45,9 @@ main {
}
}
; graphics.disable_bitmap_mode()
; test_stack.test()
repeat {
}
}
+3
View File
@@ -1,5 +1,6 @@
%import textio
%import floats
%import test_stack
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
@@ -57,5 +58,7 @@ main {
txt.print("finished in ")
floats.print_f(duration)
txt.print(" seconds!\n")
; test_stack.test()
}
}
Loaded 100 of 112 files, more files were not shown because too many files have changed in this diff. Show more