mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 20:02:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
895b30f7e5 | ||
|
|
b985604e22 | ||
|
|
f7953e4ef3 | ||
|
|
63483d1f0e | ||
|
|
8b981f03bf | ||
|
|
d0d0910bf2 | ||
|
|
57ac820767 | ||
|
|
b8bda867b6 | ||
|
|
05d3a2450c | ||
|
|
d40788adfa | ||
|
|
83fbf86b1c | ||
|
|
e876008427 | ||
|
|
2b43353eb4 | ||
|
|
a74403c347 | ||
|
|
2f4c6c8697 | ||
|
|
238d8197f5 | ||
|
|
53a600d87b | ||
|
|
2a0ffaf45d | ||
|
|
936b046ed9 | ||
|
|
378dcfe351 | ||
|
|
0a330b9288 | ||
|
|
a88b40d6c1 | ||
|
|
09f25ffbd9 | ||
|
|
ab1232d742 | ||
|
|
a7f56fe0fc | ||
|
|
58a9452c36 | ||
|
|
6d8c4f403f | ||
|
|
88b80fed90 | ||
|
|
acdbd0c391 | ||
|
|
d9a8cfed8c | ||
|
|
122796fbba | ||
|
|
510ca042c9 | ||
|
|
125f6205f2 | ||
|
|
8136f3df5c | ||
|
|
38d06a7e94 | ||
|
|
49db10539a | ||
|
|
8efe4c6267 | ||
|
|
04d8bb8fbf | ||
|
|
08aa13c90c | ||
|
|
d1febc0208 | ||
|
|
5980e58ac6 | ||
|
|
e1dc283d4b | ||
|
|
8be234973c | ||
|
|
7def8ff2cd | ||
|
|
340b1c2e42 | ||
|
|
7e0f7ba438 | ||
|
|
fefd9b52a8 | ||
|
|
afd155ac4f | ||
|
|
ee724eb4f1 | ||
|
|
2f1f20ea11 | ||
|
|
063bcf17d8 | ||
|
|
72509eef44 | ||
|
|
2da28864e9 | ||
|
|
4278f64682 | ||
|
|
59ae3c3fcd | ||
|
|
7fa21fbdff | ||
|
|
e95af7498e | ||
|
|
79c75adac1 | ||
|
|
d212f69d89 | ||
|
|
edf5e69d39 | ||
|
|
574eb0d174 | ||
|
|
8bd4914e2f | ||
|
|
5ebaaff64b | ||
|
|
5c9e0c9f51 | ||
|
|
8132edbb08 | ||
|
|
d29ce78c86 | ||
|
|
94bc9d7a69 | ||
|
|
e8faec0932 | ||
|
|
69ca4fe304 | ||
|
|
cd99fe46fd | ||
|
|
4825b4dc68 | ||
|
|
8d0607ef58 | ||
|
|
225295a7d8 | ||
|
|
4cd74daf53 | ||
|
|
6eb9118197 | ||
|
|
d0bd2f522c | ||
|
|
661c757236 | ||
|
|
aaa20093ef | ||
|
|
1eecdd6fa3 | ||
|
|
800b5b2a43 | ||
|
|
9d17421c66 | ||
|
|
0edd50e956 | ||
|
|
288d4f08b3 | ||
|
|
526e4b8bdc | ||
|
|
e0c5ccc16b | ||
|
|
ebc2c614d7 | ||
|
|
29f5a85158 | ||
|
|
8af2380a47 | ||
|
|
431f2a2088 | ||
|
|
e05ea887f6 | ||
|
|
95c0425151 | ||
|
|
47cbc7b1f9 | ||
|
|
e7b75d591c | ||
|
|
99f7d469f4 | ||
|
|
8a6ef17fbf | ||
|
|
5f337a0bd9 | ||
|
|
87862f772a | ||
|
|
3ab641aa21 | ||
|
|
3efa8da8e0 | ||
|
|
3e28ed4fe4 | ||
|
|
44949460ed | ||
|
|
83cc19ad6f | ||
|
|
66bb98c479 | ||
|
|
ff3f985658 |
No files matched your search
Generated
+1
-1
@@ -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>
|
||||
@@ -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).
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -213,5 +213,6 @@ sub print_f (float value) {
|
||||
}
|
||||
|
||||
%asminclude "library:c64/floats.asm", ""
|
||||
%asminclude "library:c64/floats_funcs.asm", ""
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}}
|
||||
|
||||
@@ -149,5 +149,6 @@ sub print_f (float value) {
|
||||
}
|
||||
|
||||
%asminclude "library:c64/floats.asm", ""
|
||||
%asminclude "library:c64/floats_funcs.asm", ""
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
+159
-1222
File diff suppressed because it is too large.
Load diff
@@ -6,4 +6,5 @@
|
||||
|
||||
prog8_lib {
|
||||
%asminclude "library:prog8_lib.asm", ""
|
||||
%asminclude "library:prog8_funcs.asm", ""
|
||||
}
|
||||
@@ -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 @@
|
||||
4.6
|
||||
5.2
|
||||
@@ -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 -> "?????"
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
+246
-170
@@ -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
|
||||
|
||||
|
||||
@@ -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())) {
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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) }
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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'*)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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++
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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.
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in new issue
Block a user