Compare commits

...

64 Commits
v5.0 ... v5.2

Author SHA1 Message Date
895b30f7e5 version 5.2 2020-12-01 22:49:08 +01:00
b985604e22 slight tweak to word bitshift for large shift values 2020-12-01 22:48:02 +01:00
f7953e4ef3 fix float comparison error that creeped in with no longer using the stack for that 2020-12-01 22:19:03 +01:00
63483d1f0e warnings, errors and todos 2020-12-01 03:24:06 +01:00
8b981f03bf optimized reg_lesseq_w (word <= word) to avoid using extra zp word, by swapping operands 2020-12-01 02:09:48 +01:00
d0d0910bf2 corrected greatereq_w (word >= word) 2020-12-01 01:57:12 +01:00
57ac820767 readme 2020-11-30 22:42:51 +01:00
b8bda867b6 optimized reg_lesseq_w (word <= word) 2020-11-30 02:26:00 +01:00
05d3a2450c optimized reg_less_w (word < word) 2020-11-30 01:53:44 +01:00
d40788adfa optimized in-place array element modification to use simpler assignment asm code 2020-11-28 00:44:38 +01:00
83fbf86b1c no longer generate double assignment to the indexer var for in-place modifying array variable 2020-11-27 23:46:01 +01:00
e876008427 tiny tweak of typecasting str to uword 2020-11-26 19:21:07 +01:00
2b43353eb4 readme 2020-11-26 02:04:01 +01:00
a74403c347 float typecasts optimization 2020-11-26 01:52:48 +01:00
2f4c6c8697 float typecasts optimization 2020-11-26 01:39:27 +01:00
238d8197f5 byte/word typecasts optimized even further to just use cpu registers (and fixed sign extending AY) 2020-11-26 01:33:45 +01:00
53a600d87b fix typecasting of signed byte to signed word in a variable 2020-11-25 22:33:41 +01:00
2a0ffaf45d started to optimize typecasts to use translateExpression() less 2020-11-25 00:17:42 +01:00
936b046ed9 optimize word [operator] byte, without translateExpression() 2020-11-24 23:41:10 +01:00
378dcfe351 fix computation error of word - byte 2020-11-24 22:23:16 +01:00
0a330b9288 warmings 2020-11-24 22:21:54 +01:00
a88b40d6c1 fix stack corruption with bitshifts 2020-11-24 21:58:14 +01:00
09f25ffbd9 optimized in-place memory var modification, not using translateExpression() 2020-11-24 21:41:44 +01:00
ab1232d742 optimized in-place float var modification, not using translateExpression() 2020-11-24 01:09:24 +01:00
a7f56fe0fc remaining float comparisons with expression now without translateExpression() 2020-11-24 00:35:30 +01:00
58a9452c36 fixed the YSCROLL graphics mode on the C64 (mistake in 5.1) 2020-11-23 23:05:51 +01:00
6d8c4f403f updated Kotlin version to 1.4.20, updated targeted JDK version to 11 (LTS) 2020-11-23 22:28:24 +01:00
88b80fed90 returning float values now via fac1 instead of stack 2020-11-23 22:14:45 +01:00
acdbd0c391 todos for next version 2020-11-22 19:18:57 +01:00
d9a8cfed8c updated the compiled examples disk 2020-11-22 18:45:40 +01:00
122796fbba version 5.1 2020-11-22 18:36:04 +01:00
510ca042c9 stack tested for most example programs 2020-11-22 18:35:43 +01:00
125f6205f2 optimizing assigning an array value to a var 2020-11-22 17:44:55 +01:00
8136f3df5c float-const comparison optimizations 2020-11-22 16:54:02 +01:00
38d06a7e94 optimized float var comparison without translateExpression() 2020-11-22 15:05:45 +01:00
49db10539a optimized float var equality comparison without translateExpression() 2020-11-22 14:33:03 +01:00
8efe4c6267 Fixed compiler watch to work with multiple compilation modules 2020-11-22 13:11:33 +01:00
04d8bb8fbf Fixed compiler watch flag crashing when not used on a subdirectory. Fixes #20 2020-11-22 12:07:14 +01:00
08aa13c90c rnd() functions marked as having (internal) side effect 2020-11-22 02:09:32 +01:00
d1febc0208 all in-place byte assignments now without translateExpression() 2020-11-22 01:38:53 +01:00
5980e58ac6 word comparison jumps now without translateExpression() 2020-11-22 01:15:05 +01:00
e1dc283d4b byte comparison jumps now without translateExpression() 2020-11-21 23:31:26 +01:00
8be234973c rollback failed optimization of memory expressions (code size got too large) 2020-11-21 19:09:02 +01:00
7def8ff2cd beginning to optimize comparisons more 2020-11-21 18:44:17 +01:00
340b1c2e42 added balls demo/benchmark 2020-11-21 18:03:57 +01:00
7e0f7ba438 todos 2020-11-20 23:46:14 +01:00
fefd9b52a8 fix for loop with signed byte loopvar over non-const 2020-11-20 22:54:24 +01:00
afd155ac4f optimize for loops over non const range, without translateExpression() 2020-11-20 22:44:16 +01:00
ee724eb4f1 float variable casts without translateExpression() 2020-11-19 22:58:38 +01:00
2f1f20ea11 rename 2020-11-19 00:28:49 +01:00
063bcf17d8 various inplace modification for word vars now without translateExpression() 2020-11-19 00:08:10 +01:00
72509eef44 inplace modification for memory now without translateExpression() 2020-11-18 23:23:06 +01:00
2da28864e9 inplace not and invert for memory now without translateExpression() 2020-11-18 23:13:07 +01:00
4278f64682 fixed invalid value push for memreads with expression 2020-11-18 22:45:04 +01:00
59ae3c3fcd << and >> for byte values slightly optimized, no longer use translateExpression(). preparing for more operator optimizations. 2020-11-18 01:27:02 +01:00
7fa21fbdff @(...) in an expression is now more efficient, without translateExpression() 2020-11-18 00:58:04 +01:00
e95af7498e comparing function call result to 0 now more efficient, without translateExpression() 2020-11-18 00:05:48 +01:00
79c75adac1 repeat and when without translateExpression() 2020-11-17 23:52:13 +01:00
d212f69d89 ++/-- and @Pc without translateExpression() 2020-11-17 23:40:42 +01:00
edf5e69d39 optimized swap() 2020-11-15 18:04:54 +01:00
574eb0d174 refactoring asmassignment code blocks into utility functions 2020-11-15 17:44:47 +01:00
8bd4914e2f fix stack error for float casts 2020-11-15 17:34:27 +01:00
5ebaaff64b refactoring asmassignment code blocks into utility functions 2020-11-15 15:07:55 +01:00
5c9e0c9f51 emit extra nop for breakpoints so vice label list works again (requires 64tass 1.55.2257 or newer!) 2020-11-15 14:31:06 +01:00
88 changed files with 2897 additions and 899 deletions

2
.idea/misc.xml generated
View File

@ -16,7 +16,7 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@ -20,36 +20,36 @@ Full documentation (syntax reference, how to use the language and the compiler,
https://prog8.readthedocs.io/
What use Prog8 provide?
-----------------------
What does Prog8 provide?
------------------------
- reduction of source code length over raw assembly
- big reduction of source code length over raw assembly
- modularity, symbol scoping, subroutines
- various data types other than just bytes (16-bit words, floats, strings)
- automatic variable allocations, automatic string and array variables and string sharing
- subroutines with an input- and output parameter signature
- constant folding in expressions
- no stack frame allocations because parameters and local variables are automatically allocated statically
- constant folding in expressions and other high-level program optimizations
- conditional branches
- 'when' statement to provide a concise jump table alternative to if/elseif chains
- structs to group together sets of variables and manipulate them at once
- floating point operations (requires the C64 Basic ROM routines for this)
- abstracting away low level aspects such as ZeroPage handling, program startup, explicit memory addresses
- various code optimizations (code structure, logical and numerical expressions, unused code removal...)
- inline assembly allows you to have full control when every cycle or byte matters
- 'when' statement to provide a concise jump table alternative to if/elseif chains
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
- structs to group together sets of variables and manipulate them at once
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
- fast execution speed due to compilation to native assembly code
- inline assembly allows you to have full control when every cycle or byte matters
*Rapid edit-compile-run-debug cycle:*
- use a modern PC to do the work on
- very quick compilation times
- use a modern PC to do the work on, use nice editors and enjoy quick compilation times
- can automatically run the program in the Vice emulator after succesful compilation
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
*Two supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
- "c64": Commodore-64 (6510 CPU = almost a 6502) premium support.
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) experimental support.
- "c64": Commodore-64 (6510 CPU = almost a 6502), the main target.
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) .
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
@ -61,7 +61,7 @@ Additional required tools
A recent .exe version of this tool for Windows can be obtained from my [clone](https://github.com/irmen/64tass/releases) of this project.
For other platforms it is very easy to compile it yourself (make ; make install).
A **Java runtime (jre or jdk), version 8 or newer** is required to run a prepackaged version of the compiler.
A **Java runtime (jre or jdk), version 11 or newer** is required to run a prepackaged version of the compiler.
If you want to build it from source, you'll need a Java SDK + Kotlin 1.3.x SDK (or for instance,
IntelliJ IDEA with the Kotlin plugin).

View File

@ -1,11 +1,11 @@
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20"
}
}
plugins {
// id "org.jetbrains.kotlin.jvm" version "1.4.10"
// id "org.jetbrains.kotlin.jvm" version "1.4.20"
id 'application'
id 'org.jetbrains.dokka' version "0.9.18"
id 'com.github.johnrengelman.shadow' version '5.2.0'
@ -15,8 +15,8 @@ plugins {
apply plugin: "kotlin"
apply plugin: "java"
targetCompatibility = 1.8
sourceCompatibility = 1.8
targetCompatibility = 11
sourceCompatibility = 11
repositories {
mavenLocal()
@ -45,7 +45,7 @@ dependencies {
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "11"
// verbose = true
// freeCompilerArgs += "-XXLanguage:+NewInference"
}
@ -53,7 +53,7 @@ compileKotlin {
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "11"
}
}

View File

@ -55,6 +55,69 @@ 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
@ -96,8 +159,8 @@ stack_uw2float .proc
.pend
stack_float2w .proc ; also used for float2b
stx P8ZP_SCRATCH_REG
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
jsr AYINT
ldx P8ZP_SCRATCH_REG
lda $64
@ -109,8 +172,8 @@ stack_float2w .proc ; also used for float2b
.pend
stack_float2uw .proc ; also used for float2ub
stx P8ZP_SCRATCH_REG
jsr pop_float_fac1
stx P8ZP_SCRATCH_REG
jsr GETADR
ldx P8ZP_SCRATCH_REG
sta P8ESTACK_HI,x
@ -327,6 +390,99 @@ neg_f .proc
rts
.pend
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
equal_f .proc
; -- are the two mflpt5 numbers on the stack identical?
inx
@ -364,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

View File

@ -12,11 +12,20 @@ graphics {
sub enable_bitmap_mode() {
; enable bitmap screen, erase it and set colors to black/white.
c64.SCROLY |= %00100000
c64.SCROLY = %00111011
c64.SCROLX = %00001000
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
clear_screen(1, 0)
}
sub disable_bitmap_mode() {
; enables text mode, erase the text screen, color white
c64.SCROLY = %00011011
c64.SCROLX = %00001000
c64.VMCSB = (c64.VMCSB & %11110000) | %00000100 ; $1000-$2fff
txt.fill_screen(' ', 1)
}
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
memset(BITMAP_ADDRESS, 320*200/8, 0)
txt.fill_screen(pixelcolor << 4 | bgcolor, 0)

View File

@ -254,7 +254,6 @@ asmsub str2ubyte(str string @ AY) clobbers(Y) -> ubyte @A {
; -- returns the unsigned byte value of the string number argument in AY
; the number may NOT be preceded by a + sign and may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
; TODO implement optimized custom version of this instead of simply reusing str2uword
%asm {{
jmp str2uword
}}
@ -264,7 +263,6 @@ asmsub str2byte(str string @ AY) clobbers(Y) -> ubyte @A {
; -- returns the signed byte value of the string number argument in AY
; the number may be preceded by a + or - sign but may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
; TODO implement optimized custom version of this instead of simply reusing str2word
%asm {{
jmp str2word
}}

View File

@ -1,5 +1,6 @@
%target cx16
%import syslib
%import textio
; bitmap pixel graphics module for the CommanderX16
; wraps the graphics functions that are in ROM.
@ -17,6 +18,13 @@ graphics {
clear_screen(1, 0)
}
sub disable_bitmap_mode() {
; enables text mode, erase the text screen, color white
void cx16.screen_set_mode(2)
txt.fill_screen(' ', 1) ; TODO doesn't seem to fully clear the text screen after returning from gfx mode
}
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
cx16.GRAPH_set_colors(pixelcolor, pixelcolor, bgcolor)
cx16.GRAPH_clear()

View File

@ -30,6 +30,7 @@ write_byte_to_address_on_stack .proc
.pend
neg_b .proc
lda #0
sec
@ -443,6 +444,19 @@ less_b .proc
bpl equal_b._equal_b_false
.pend
reg_less_uw .proc
; AY < P8ZP_SCRATCH_W2?
cpy P8ZP_SCRATCH_W2+1
bcc _true
bne _false
cmp P8ZP_SCRATCH_W2
bcc _true
_false lda #0
rts
_true lda #1
rts
.pend
less_uw .proc
lda P8ESTACK_HI+2,x
cmp P8ESTACK_HI+1,x
@ -454,6 +468,20 @@ less_uw .proc
bcs equal_b._equal_b_false
.pend
reg_less_w .proc
; -- AY < P8ZP_SCRATCH_W2?
cmp P8ZP_SCRATCH_W2
tya
sbc P8ZP_SCRATCH_W2+1
bvc +
eor #$80
+ bmi _true
lda #0
rts
_true lda #1
rts
.pend
less_w .proc
lda P8ESTACK_LO+2,x
cmp P8ESTACK_LO+1,x
@ -496,6 +524,22 @@ lesseq_b .proc
bpl equal_b._equal_b_false
.pend
reg_lesseq_uw .proc
; AY <= P8ZP_SCRATCH_W2?
cpy P8ZP_SCRATCH_W2+1
beq +
bcc _true
lda #0
rts
+ cmp P8ZP_SCRATCH_W2
bcc _true
beq _true
lda #0
rts
_true lda #1
rts
.pend
lesseq_uw .proc
lda P8ESTACK_HI+1,x
cmp P8ESTACK_HI+2,x
@ -507,6 +551,20 @@ lesseq_uw .proc
bcc equal_b._equal_b_false
.pend
reg_lesseq_w .proc
; -- P8ZP_SCRATCH_W2 <= AY ? (note: order different from other routines)
cmp P8ZP_SCRATCH_W2
tya
sbc P8ZP_SCRATCH_W2+1
bvc +
eor #$80
+ bpl +
lda #0
rts
+ lda #1
rts
.pend
lesseq_w .proc
lda P8ESTACK_LO+1,x
cmp P8ESTACK_LO+2,x
@ -595,8 +653,8 @@ greatereq_w .proc
sbc P8ESTACK_HI+1,x
bvc +
eor #$80
+ bpl equal_b._equal_b_true
bmi equal_b._equal_b_false
+ bmi equal_b._equal_b_false
bpl equal_b._equal_b_true
.pend
@ -989,3 +1047,28 @@ _return_minusone
lda #-1
rts
.pend
sign_extend_stack_byte .proc
; -- sign extend the (signed) byte on the stack to full 16 bits
lda P8ESTACK_LO+1,x
ora #$7f
bmi +
lda #0
+ sta P8ESTACK_HI+1,x
rts
.pend
sign_extend_AY_byte .proc
; -- sign extend the (signed) byte in AY to full 16 bits
pha
and #$80
beq +
ldy #$ff
pla
rts
+ ldy #0
pla
rts
.pend

View File

@ -0,0 +1,48 @@
%import textio
test_stack {
asmsub test() {
%asm {{
stx _saveX
lda #13
jsr txt.chrout
lda #'-'
ldy #12
- jsr txt.chrout
dey
bne -
lda #13
jsr txt.chrout
lda #'x'
jsr txt.chrout
lda #'='
jsr txt.chrout
lda _saveX
jsr txt.print_ub
lda #' '
jsr txt.chrout
lda #'s'
jsr txt.chrout
lda #'p'
jsr txt.chrout
lda #'='
jsr txt.chrout
tsx
txa
jsr txt.print_ub
lda #13
jsr txt.chrout
lda #'-'
ldy #12
- jsr txt.chrout
dey
bne -
lda #13
jsr txt.chrout
ldx _saveX
rts
_saveX .byte 0
}}
}
}

View File

@ -1 +1 @@
5.0
5.2

View File

@ -55,32 +55,42 @@ private fun compileMain(args: Array<String>) {
exitProcess(1)
}
if(watchMode && moduleFiles.size<=1) {
if(watchMode) {
val watchservice = FileSystems.getDefault().newWatchService()
while(true) {
val filepath = pathFrom(moduleFiles.single()).normalize()
println("Continuous watch mode active. Main module: $filepath")
try {
println("Continuous watch mode active. Modules: $moduleFiles")
val results = mutableListOf<CompilationResult>()
for(filepathRaw in moduleFiles) {
val filepath = pathFrom(filepathRaw).normalize()
val compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, slowCodegenWarnings, compilationTarget, outputPath)
println("Imported files (now watching:)")
for (importedFile in compilationResult.importedFiles) {
print(" ")
println(importedFile)
importedFile.parent.register(watchservice, StandardWatchEventKinds.ENTRY_MODIFY)
}
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")
results.add(compilationResult)
}
val allImportedFiles = results.flatMap { it.importedFiles }
println("Imported files (now watching:)")
for (importedFile in allImportedFiles) {
print(" ")
println(importedFile)
val watchDir = importedFile.parent ?: Path.of(".")
watchDir.register(watchservice, StandardWatchEventKinds.ENTRY_MODIFY)
}
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")
var recompile=false
while(!recompile) {
val event = watchservice.take()
for(changed in event.pollEvents()) {
for (changed in event.pollEvents()) {
val changedPath = changed.context() as Path
println(" change detected: $changedPath")
if(allImportedFiles.any { it.fileName == changedPath.fileName }) {
println(" change detected: $changedPath")
recompile = true
}
}
event.reset()
println("\u001b[H\u001b[2J") // clear the screen
} catch (x: Exception) {
throw x
}
println("\u001b[H\u001b[2J") // clear the screen
}
} else {

View File

@ -216,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)

View File

@ -80,7 +80,9 @@ enum class RegisterOrPair {
Y,
AX,
AY,
XY;
XY,
FAC1,
FAC2;
companion object {
val names by lazy { values().map { it.toString()} }

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -313,6 +313,7 @@ class ArrayIndex(var origExpression: Expression?, // will be replaced
}
is IdentifierReference -> {
indexVar = replacement
indexNum = null
}
else -> {
throw FatalAstException("invalid replace")
@ -347,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() {
@ -679,16 +685,20 @@ 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 = mutableMapOf<String, MutableSet<Statement>>()
var usedAutoArrayIndexerForStatements = mutableListOf<ArrayIndexerInfo>()
var usedRegsaveA = false
var usedRegsaveX = false
var usedRegsaveY = false
var usedFloatEvalResultVar = 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,

View File

@ -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)

View File

@ -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

View File

@ -8,17 +8,15 @@ 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
@ -201,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
@ -752,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)
@ -796,8 +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.usedFloatEvalResultVar)
out("_prog8_float_eval_result .byte 0,0,0,0,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")
}
@ -902,17 +908,16 @@ internal class AsmGen(private val program: Program,
}
}
else -> {
translateExpression(stmt.iterations!!) // todo directly into AY?
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")
@ -1008,16 +1013,16 @@ $counterVar .byte 0""")
}
private fun translate(stmt: WhenStatement) {
expressionsAsmGen.translateExpression(stmt.condition) // TODO directly into AY?
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) {
@ -1131,7 +1136,9 @@ $counterVar .byte 0""")
"%breakpoint" -> {
val label = "_prog8_breakpoint_${breakpointLabels.size+1}"
breakpointLabels.add(label)
out("$label\tnop")
out("""
nop
$label nop""")
}
}
}
@ -1161,37 +1168,21 @@ $counterVar .byte 0""")
val sub = ret.definingSubroutine()!!
val returnType = sub.returntypes.single()
val returnReg = sub.asmReturnvaluesRegisters.single()
val returnValueTarget =
when {
returnReg.registerOrPair!=null -> AsmAssignTarget.fromRegisters(returnReg.registerOrPair, sub, program, this)
else -> throw AssemblyError("normal subroutines can't return value in status register directly")
}
if(returnReg.registerOrPair==null)
throw AssemblyError("normal subroutines can't return value in status register directly")
when (returnType) {
in IntegerDatatypes -> {
val src = AsmAssignSource.fromAstSource(returnvalue, program, this)
assignmentAsmGen.translateNormalAssignment(AsmAssignment(src, returnValueTarget, false, ret.position))
assignmentAsmGen.assignExpressionToRegister(returnvalue, returnReg.registerOrPair)
}
DataType.FLOAT -> {
// return the float value via FAC1
when (returnvalue) {
is NumericLiteralValue -> throw AssemblyError("float literal should have been changed to auto var")
is IdentifierReference -> {
val asmVar = asmVariableName(returnvalue)
out(" lda #<${asmVar} | ldy #>${asmVar} | jsr floats.MOVFM")
}
else -> {
// todo evaluate directly into fac1 instead of via stack intermediate
translateExpression(returnvalue)
out(" jsr floats.pop_float_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)
val src = AsmAssignSource.fromAstSource(addrofValue, program, this)
assignmentAsmGen.translateNormalAssignment(AsmAssignment(src, returnValueTarget, false, ret.position))
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnReg.registerOrPair)
}
}
}
@ -1203,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

View File

@ -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()

View File

@ -17,6 +17,7 @@ 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
@ -101,18 +102,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
when(func.name) {
"memset" -> {
// use the ROM function of the Cx16
var src = AsmAssignSource.fromAstSource(fcall.args[0], program, asmgen)
var tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, null, variableAsmName = "cx16.r0")
var assign = AsmAssignment(src, tgt, false, Position.DUMMY)
asmgen.translateNormalAssignment(assign)
src = AsmAssignSource.fromAstSource(fcall.args[1], program, asmgen)
tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, null, variableAsmName = "cx16.r1")
assign = AsmAssignment(src, tgt, false, Position.DUMMY)
asmgen.translateNormalAssignment(assign)
src = AsmAssignSource.fromAstSource(fcall.args[2], program, asmgen)
tgt = AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UBYTE, null, register = RegisterOrPair.A)
assign = AsmAssignment(src, tgt, false, Position.DUMMY)
asmgen.translateNormalAssignment(assign)
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")
@ -129,18 +121,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
// use the ROM function of the Cx16
var src = AsmAssignSource.fromAstSource(fcall.args[0], program, asmgen)
var tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, null, variableAsmName = "cx16.r0")
var assign = AsmAssignment(src, tgt, false, Position.DUMMY)
asmgen.translateNormalAssignment(assign)
src = AsmAssignSource.fromAstSource(fcall.args[1], program, asmgen)
tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, null, variableAsmName = "cx16.r1")
assign = AsmAssignment(src, tgt, false, Position.DUMMY)
asmgen.translateNormalAssignment(assign)
src = AsmAssignSource.fromAstSource(fcall.args[2], program, asmgen)
tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, null, variableAsmName = "cx16.r2")
assign = AsmAssignment(src, tgt, false, Position.DUMMY)
asmgen.translateNormalAssignment(assign)
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")
@ -271,7 +254,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UBYTE -> {
when (what) {
is ArrayIndexedExpression -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, fcall, "ror2", 'b')
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror2", 'b')
asmgen.out(" jsr prog8_lib.ror2_array_ub")
}
is DirectMemoryRead -> {
@ -279,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 {
translateRolRorMemoryArgs(what.addressExpression, fcall)
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
}
}
@ -293,7 +276,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UWORD -> {
when (what) {
is ArrayIndexedExpression -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, fcall, "ror2", 'w')
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror2", 'w')
asmgen.out(" jsr prog8_lib.ror2_array_uw")
}
is IdentifierReference -> {
@ -314,7 +297,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UBYTE -> {
when (what) {
is ArrayIndexedExpression -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, fcall, "ror", 'b')
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror", 'b')
asmgen.out(" jsr prog8_lib.ror_array_ub")
}
is DirectMemoryRead -> {
@ -322,7 +305,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" ror ${number.toHex()}")
} else {
translateRolRorMemoryArgs(what.addressExpression, fcall)
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
sta (+) + 1
sty (+) + 2
@ -339,7 +322,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UWORD -> {
when (what) {
is ArrayIndexedExpression -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, fcall, "ror", 'w')
translateRolRorArrayArgs(what.arrayvar, what.indexer, "ror", 'w')
asmgen.out(" jsr prog8_lib.ror_array_uw")
}
is IdentifierReference -> {
@ -360,7 +343,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UBYTE -> {
when (what) {
is ArrayIndexedExpression -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, fcall, "rol2", 'b')
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol2", 'b')
asmgen.out(" jsr prog8_lib.rol2_array_ub")
}
is DirectMemoryRead -> {
@ -368,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 {
translateRolRorMemoryArgs(what.addressExpression, fcall)
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
}
}
@ -382,7 +365,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UWORD -> {
when (what) {
is ArrayIndexedExpression -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, fcall, "rol2", 'w')
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol2", 'w')
asmgen.out(" jsr prog8_lib.rol2_array_uw")
}
is IdentifierReference -> {
@ -403,7 +386,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UBYTE -> {
when (what) {
is ArrayIndexedExpression -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, fcall, "rol", 'b')
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol", 'b')
asmgen.out(" jsr prog8_lib.rol_array_ub")
}
is DirectMemoryRead -> {
@ -411,7 +394,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val number = (what.addressExpression as NumericLiteralValue).number
asmgen.out(" rol ${number.toHex()}")
} else {
translateRolRorMemoryArgs(what.addressExpression, fcall)
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
asmgen.out("""
sta (+) + 1
sty (+) + 2
@ -428,7 +411,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
DataType.UWORD -> {
when (what) {
is ArrayIndexedExpression -> {
translateRolRorArrayArgs(what.arrayvar, what.indexer, fcall, "rol", 'w')
translateRolRorArrayArgs(what.arrayvar, what.indexer, "rol", 'w')
asmgen.out(" jsr prog8_lib.rol_array_uw")
}
is IdentifierReference -> {
@ -442,22 +425,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
private fun translateRolRorMemoryArgs(addressExpression: Expression, fcall: IFunctionCall) {
val src = AsmAssignSource.fromAstSource(addressExpression, program, asmgen)
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen)
val assign = AsmAssignment(src, tgt, false, (fcall as Node).position)
asmgen.translateNormalAssignment(assign)
}
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, fcall: IFunctionCall, operation: String, dt: Char) {
var src = AsmAssignSource.fromAstSource(AddressOf(arrayvar, (fcall as Node).position), program, asmgen)
var tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, null, variableAsmName = "prog8_lib.${operation}_array_u${dt}._arg_target")
var assign = AsmAssignment(src, tgt, false, (fcall as Node).position)
asmgen.translateNormalAssignment(assign)
src = AsmAssignSource.fromAstSource(indexer, program, asmgen)
tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UBYTE, null, variableAsmName = "prog8_lib.${operation}_array_u${dt}._arg_index")
assign = AsmAssignment(src, tgt, false, (fcall as Node).position)
asmgen.translateNormalAssignment(assign)
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 funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, scope: Subroutine) {
@ -671,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))
@ -681,25 +653,43 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
// TODO find alternative way to swap here without using estack
asmgen.translateExpression(first)
asmgen.translateExpression(second)
val idatatype = first.inferType(program)
if(!idatatype.isKnown)
throw AssemblyError("unknown dt")
val datatype = idatatype.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)
val datatype = first.inferType(program).typeOrElse(DataType.STRUCT)
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) {
@ -971,14 +961,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
private fun funcMkword(fcall: IFunctionCall, resultToStack: Boolean) {
var src = AsmAssignSource.fromAstSource(fcall.args[0], program, asmgen) // msb
var tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.Y, null, program, asmgen)
var assign = AsmAssignment(src, tgt, false, (fcall as Node).position)
asmgen.translateNormalAssignment(assign)
src = AsmAssignSource.fromAstSource(fcall.args[1], program, asmgen) // lsb
tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.A, null, program, asmgen)
assign = AsmAssignment(src, tgt, false, (fcall as Node).position)
asmgen.translateNormalAssignment(assign)
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")
}
@ -995,10 +979,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
if (resultToStack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
val src = AsmAssignSource.fromAstSource(fcall.args.single(), program, asmgen)
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen)
val assign = AsmAssignment(src, tgt, false, (fcall as Node).position)
asmgen.translateNormalAssignment(assign)
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
if (resultToStack)
asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
else
@ -1019,10 +1000,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
if (resultToStack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
} else {
val src = AsmAssignSource.fromAstSource(fcall.args.single(), program, asmgen)
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen)
val assign = AsmAssignment(src, tgt, false, (fcall as Node).position)
asmgen.translateNormalAssignment(assign)
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
if (resultToStack)
asmgen.out(" sta P8ESTACK_LO,x | dex")
}
@ -1055,14 +1033,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
throw AssemblyError("float literals should have been converted into autovar")
}
else -> {
scope.asmGenInfo.usedFloatEvalResultVar = true
val variable = IdentifierReference(listOf("_prog8_float_eval_result"), value.position)
variable.linkParents(value)
val assign = AsmAssignment(AsmAssignSource.fromAstSource(value, program, asmgen),
AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.FLOAT, null, variableAsmName = asmgen.asmVariableName(variable)),
false, Position.DUMMY)
asmgen.translateNormalAssignment(assign)
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)
}
}

View File

@ -1,20 +1,16 @@
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
// todo reduce use of stack eval translateExpression()
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
internal fun translate(stmt: ForLoop) {
@ -53,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 {
@ -77,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 -> {
@ -115,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)
@ -148,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) {
@ -181,7 +158,7 @@ $modifiedLabel2 lda #0 ; modified
cmp $varname
bcc $endLabel
bcs $loopLabel
$endLabel inx""")
$endLabel""")
} else {
asmgen.out("""
lda $varname
@ -198,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) {
@ -231,7 +205,7 @@ $modifiedLabel cmp #0 ; modified
lda $varname
$modifiedLabel2 cmp #0 ; modified
bcs $loopLabel
$endLabel inx""")
$endLabel""")
} else {
asmgen.out("""
lda $varname
@ -249,7 +223,7 @@ $modifiedLabel sbc #0 ; modified
bvc +
eor #$80
+ bpl $loopLabel
$endLabel inx""")
$endLabel""")
}
}
}
@ -611,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())
}

View File

@ -165,10 +165,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
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) {
@ -213,17 +210,13 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
""")
}
else -> {
asmgen.translateExpression(value) // todo directly into A
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out("""
inx
pha
lda P8ESTACK_LO,x
beq +
sec
bcs ++
+ clc
+ pla
""")
beq +
sec
bcs ++
+ clc
+""")
}
}
}
@ -231,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)

View File

@ -54,14 +54,8 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
asmgen.out("+\tdec ${'$'}ffff\t; modified")
}
else -> {
asmgen.translateExpression(addressExpr) // todo directly into AY?
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

View File

@ -49,8 +49,8 @@ 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 {
@ -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)
}
}
}

View File

@ -68,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")
}
@ -81,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)
@ -95,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!!
@ -114,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) // TODO directly into AY
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)
}
}
@ -130,7 +138,7 @@ 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 -> {
when (val sub = value.target.targetStatement(program.namespace)) {
is Subroutine -> {
@ -200,8 +208,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
else -> {
// everything else just evaluate via the stack.
// TODO byte and word values not via stack but via A / AY registers?
// 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)
@ -218,11 +231,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origAssign: AsmAssignment) {
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) {
@ -253,26 +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
// TODO byte and word values not via stack but directly via A or AY registers?
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 -> {
@ -393,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")
}
}
@ -568,7 +882,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignFAC1float(target: AsmAssignTarget) {
internal fun assignFAC1float(target: AsmAssignTarget) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
@ -595,11 +909,53 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" jsr floats.set_array_float_from_fac1")
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
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 -> {
@ -636,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")
}
}
@ -670,6 +1032,7 @@ 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 -> {
@ -784,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 -> {
@ -821,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") }
@ -829,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") }
@ -837,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")
}
}
}
@ -850,7 +1216,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignRegisterpairWord(target: AsmAssignTarget, regs: RegisterOrPair) {
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")
@ -989,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("""
@ -1046,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")
@ -1103,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")
@ -1134,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("""
@ -1164,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 -> {
@ -1242,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) // TODO directly into AY
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")
}
}
}
@ -1276,17 +1650,31 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
else -> {
asmgen.saveRegister(register, false, memoryAddress.definingSubroutine()!!)
asmgen.translateExpression(addressExpr) // TODO directly into AY
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)
}
}

View File

@ -160,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)
@ -171,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")
@ -227,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 -
@ -255,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")
@ -319,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.
"+" -> {
@ -420,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) // todo directly into Y
asmgen.assignExpressionToRegister(value, RegisterOrPair.Y)
asmgen.out("""
inx
ldy P8ESTACK_LO,x
beq +
- asl $name
dey
@ -451,11 +506,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
}
">>" -> {
asmgen.translateExpression(value) // todo directly into Y
asmgen.assignExpressionToRegister(value, RegisterOrPair.Y)
if(dt==DataType.UBYTE) {
asmgen.out("""
inx
ldy P8ESTACK_LO,x
beq +
- lsr $name
dey
@ -463,8 +516,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
} else {
asmgen.out("""
inx
ldy P8ESTACK_LO,x
beq +
- lda $name
asl a
@ -474,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) {
@ -1100,19 +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 valueiDt = value.inferType(program)
if(!valueiDt.isKnown)
throw AssemblyError("unknown dt")
val valueDt = valueiDt.typeOrElse(DataType.STRUCT)
fun multiplyWord() {
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
@ -1125,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
""")
}
@ -1177,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
@ -1189,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
@ -1200,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
@ -1226,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
@ -1250,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
@ -1270,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")
}
}
@ -1286,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")
asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1)
asmgen.saveRegister(CpuRegister.X, false, scope)
when (operator) {
"**" -> {
@ -1592,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""")
}
}
}
@ -1662,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""")
}
}
}

View File

@ -130,9 +130,9 @@ private val functionSignatures: List<FSignature> = listOf(
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" , true, emptyList(), DataType.UBYTE),
FSignature("rndw" , true, emptyList(), DataType.UWORD),
FSignature("rndf" , true, emptyList(), DataType.FLOAT),
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),

View File

@ -79,7 +79,7 @@ X = BinExpr X = LeftExpr
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

View File

@ -93,6 +93,8 @@ Almost instant compilation times (less than a second) can be achieved when using
Start the compiler with the ``-watch`` argument to enable this.
It will compile your program and then instead of exiting, it waits for any changes in the module source files.
As soon as a change happens, the program gets compiled again.
It is possible to use the watch mode with multiple modules as well, but it will
recompile everything in that list even if only of the files got updated.
Other options
^^^^^^^^^^^^^

View File

@ -38,6 +38,27 @@ This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/license
:alt: Fully playable tetris clone
Language features
-----------------
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
The generated output is a machine code program runnable on actual 8-bit 6502 hardware.
- Provide a very convenient edit/compile/run cycle by being able to directly launch
the compiled program in an emulator and provide debugging information to this emulator.
- Based on simple and familiar imperative structured programming (it looks like a mix of C and Python)
- Modular programming and scoping via modules, code blocks, and subroutines.
- Provide high level programming constructs but at the same time stay close to the metal;
still able to directly use memory addresses and ROM subroutines,
and inline assembly to have full control when every register, cycle or byte matters
- Arbitrary number of subroutine parameters, Complex nested expressions are possible
- No stack frame allocations because parameters and local variables are automatically allocated statically
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
- Variable data types include signed and unsigned bytes and words, arrays, strings and floats.
- High-level code optimizations, such as const-folding, expression and statement simplifications/rewriting.
- Many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``substr``, ``sort`` and ``reverse`` (and others)
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
Code example
------------
@ -104,37 +125,6 @@ when the exact same program is compiled for the Commander X16 target, and run on
Design principles and features
------------------------------
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
The generated output is a machine code program runnable on actual 8-bit 6502 hardware.
- Based on simple and familiar imperative structured programming (it looks like a mix of C and Python)
- 'One statement per line' code, resulting in clear readable programs.
- Modular programming and scoping via modules, code blocks, and subroutines.
- Provide high level programming constructs but at the same time stay close to the metal;
still able to directly use memory addresses and ROM subroutines,
and inline assembly to have full control when every register, cycle or byte matters
- Arbitrary number of subroutine parameters
- Complex nested expressions are possible
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
- Values are typed. Available data types include signed and unsigned bytes and words, arrays, strings and floats.
- No dynamic memory allocation or sizing! All variables stay fixed size as determined at compile time.
- Provide various quality of life language features and library subroutines specifically for the target platform.
- Provide a very convenient edit/compile/run cycle by being able to directly launch
the compiled program in an emulator and provide debugging information to this emulator.
- Arbitrary control flow jumps and branches are possible,
and will usually translate directly into the appropriate single 6502 jump/branch instruction.
- There are no complicated built-in error handling or overflow checks, you'll have to take care
of this yourself if required. This keeps the language and code simple and efficient.
- The compiler tries to optimize the program and generated code a bit, but hand-tuning of the
performance or space-critical parts will likely still be required. This is supported by
the ability to easily write embedded assembly code directly in the program source code.
- There are many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``substr``, ``sort`` and ``reverse`` (and others)
- Assembling the generated code into a program wil be done by an external cross-assembler tool.
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
.. _requirements:
Required tools
@ -143,14 +133,17 @@ Required tools
`64tass <https://sourceforge.net/projects/tass64/>`_ - cross assembler. Install this on your shell path.
It's very easy to compile yourself.
A recent precompiled .exe for Windows can be obtained from my `clone <https://github.com/irmen/64tass/releases>`_ of this project.
*You need at least version 1.55.2257 of this assembler to correctly use the breakpoints feature.*
It's possible to use older versions, but it is very likely that the automatic Vice breakpoints won't work with them.
A **Java runtime (jre or jdk), version 8 or newer** is required to run the prog8 compiler itself.
If you're scared of Oracle's licensing terms, most Linux distributions ship OpenJDK instead.
Fnd for Windows it's possible to get that as well. Check out `AdoptOpenJDK <https://adoptopenjdk.net/>`_ .
A **Java runtime (jre or jdk), version 11 or newer** is required to run the prog8 compiler itself.
If you're scared of Oracle's licensing terms, most Linux distributions ship OpenJDK in their packages repository instead.
For Windows it's possible to get that as well; check out `AdoptOpenJDK <https://adoptopenjdk.net/>`_ .
For MacOS you can use the Homebrew system to install a recent version of OpenJDK.
Finally: a **C-64 emulator** (or a real C-64 ofcourse) can be nice to test and run your programs on.
The compiler assumes the presence of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
If you're targeting the CommanderX16, there's the `x16emu <https://github.com/commanderx16/x16-emulator>`_.
Finally: an **emulator** (or a real machine ofcourse) to test and run your programs on.
In C64 mode, thhe compiler assumes the presence of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
If you're targeting the CommanderX16 instead, there's the `x16emu <https://github.com/commanderx16/x16-emulator>`_.
.. important::
**Building the compiler itself:** (*Only needed if you have not downloaded a pre-built 'fat-jar'*)

View File

@ -2,10 +2,10 @@
TODO
====
- 64tass doesn't output all labels anymore in the vice-mon-list, so %breakpoint labels are no longer present....
- 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. 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

View File

@ -1,6 +1,7 @@
%import textio
%import floats
%import syslib
%import test_stack
%zeropage basicsafe
main {
@ -11,7 +12,7 @@ main {
integers()
floatingpoint()
testX()
test_stack.test()
}
sub rotations() {
@ -251,8 +252,7 @@ main {
txt.chrout('\n')
txt.chrout('\n')
testX()
test_stack.test()
}
@ -324,7 +324,7 @@ main {
txt.print(result)
txt.chrout('\n')
testX()
test_stack.test()
}
@ -643,7 +643,7 @@ main {
reverse(uwarr)
reverse(warr)
testX()
test_stack.test()
}
sub floatingpoint() {
@ -831,36 +831,6 @@ main {
floats.print_f(fl)
txt.chrout('\n')
testX()
}
asmsub testX() {
%asm {{
stx _saveX
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
ldx _saveX
rts
_saveX .byte 0
}}
test_stack.test()
}
}

View File

@ -1,5 +1,6 @@
%import floats
%import textio
%import test_stack
%zeropage basicsafe
main {
@ -31,6 +32,8 @@ main {
minus_float(0,0,0)
minus_float(2.5,1.5,1.0)
minus_float(-1.5,3.5,-5.0)
test_stack.test()
}
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
@ -97,10 +100,11 @@ main {
float r = a1-a2
if abs(r-c)<0.00001
txt.print(" ok ")
else
else {
txt.print("err! ")
}
txt.print("float ")
txt.print(" float ")
floats.print_f(a1)
txt.print(" - ")
floats.print_f(a2)

View File

@ -1,6 +1,7 @@
%target c64
%import syslib
%import textio
%import test_stack
%zeropage basicsafe
main {
@ -23,6 +24,9 @@ main {
ubyte upwards = true
repeat {
;txt.plot(0,0)
;test_stack.test()
ubyte mountain = 223 ; slope upwards
if active_height < target_height {
active_height++

103
examples/balls.p8 Normal file
View File

@ -0,0 +1,103 @@
%import textio
%import test_stack
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
sub start() {
str input = ".........."
ubyte ballCount
ubyte[255] BX
ubyte[255] BY
ubyte[255] BC
ubyte[255] DX
ubyte[255] DY
txt.print("number of balls (1-255)? ")
void txt.input_chars(input)
ballCount = conv.str2ubyte(input)
txt.fill_screen(81, 0)
; Setup Starting Ball Positions
ubyte lp
for lp in 0 to ballCount-1 {
BX[lp] = rnd() % txt.DEFAULT_WIDTH
BY[lp] = rnd() % txt.DEFAULT_HEIGHT
BC[lp] = rnd() & 15
DX[lp] = rnd() & 1
DY[lp] = rnd() & 1
void rnd()
}
; start clock
c64.SETTIM(0,0,0)
; display balls
uword frame
for frame in 0 to 999 {
; Loop though all balls clearing current spot and setting new spot
for lp in 0 to ballCount-1 {
; Clear existing Location the ball is at
txt.setclr(BX[lp], BY[lp], 0)
if DX[lp] == 0 {
if (BX[lp] == 0)
{
DX[lp] = 1
} else {
BX[lp]=BX[lp]-1
}
} else if DX[lp] == 1 {
if (BX[lp] == txt.DEFAULT_WIDTH-1)
{
BX[lp] = txt.DEFAULT_WIDTH-2
DX[lp] = 0
} else {
BX[lp]=BX[lp]+1
}
}
if DY[lp] == 0 {
if (BY[lp] == 0)
{
DY[lp] = 1
} else {
BY[lp]=BY[lp]-1
}
} else if DY[lp] == 1 {
if (BY[lp] == txt.DEFAULT_HEIGHT-1)
{
BY[lp] = txt.DEFAULT_HEIGHT-2
DY[lp] = 0
} else {
BY[lp]=BY[lp]+1
}
}
; Put the new ball possition
txt.setclr(BX[lp], BY[lp], BC[lp])
}
;txt.plot(0,0)
;txt.print_uw(frame)
}
; read clock
uword jiffies
%asm {{
stx P8ZP_SCRATCH_REG
jsr c64.RDTIM
sta jiffies
stx jiffies+1
ldx P8ZP_SCRATCH_REG
}}
txt.print("\nbenchmark: ")
txt.print_uw(jiffies)
txt.print(" jiffies for 1000 frames.\n")
; test_stack.test()
}
}

913
examples/cmp/word_comps.p8 Normal file
View File

@ -0,0 +1,913 @@
%import textio
%import floats
%zeropage basicsafe
%import test_stack
main {
sub start() {
word_less()
word_lessequal()
word_greaterequal()
word_greater()
uword_lessequal()
}
sub uword_lessequal() {
uword lessvar
uword comparevar
txt.print("uword <=\n")
txt.print_uw(65535)
txt.chrout('\n')
check_lesseq_uw(0, 65535)
txt.print_uw(0)
txt.chrout('\n')
check_not_lesseq_uw(65535, 0)
comparevar = 65535
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-2
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-254
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-255
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-256
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-5000
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 32769
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 32768
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 1
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 0
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 11111
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 255
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 256
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
test_stack.test()
return
sub check_lesseq_uw(uword w1, uword w2) {
uword zero = 0
ubyte error=0
ubyte ub = w1<=w2
if not ub {
error++
txt.print("ub!")
}
if w1<=(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_uw(w1)
txt.print(" <= ")
txt.print_uw(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_lesseq_uw(uword w1, uword w2) {
uword zero = 0
ubyte error=0
ubyte ub = w1<=w2
if ub {
error++
txt.print("ub!")
}
if w1<=(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_uw(w1)
txt.print(" not <= ")
txt.print_uw(w2)
txt.chrout('\n')
exit(1)
}
}
}
sub word_greater() {
word biggervar
word comparevar
txt.print("word >\n")
txt.print_w(-32767)
txt.chrout('\n')
check_greater_w(32767, -32767)
txt.print_w(32766)
txt.chrout('\n')
check_not_greater_w(-32766, 32766)
comparevar = 32765
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 32760
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
test_stack.test()
return
sub check_greater_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1>(w2+zero)
if not ub {
error++
txt.print("ubz!")
}
ub = w1>w2
if not ub {
error++
txt.print("ub!")
}
if w1>(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" > ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_greater_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1>w2
if ub {
error++
txt.print("ub!")
}
if w1>(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if w1>w2 {
error++
txt.print("c2!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" not > ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
}
sub word_greaterequal() {
word biggervar
word comparevar
txt.print("word >=\n")
txt.print_w(-32767)
txt.chrout('\n')
check_greatereq_w(32767, -32767)
txt.print_w(32766)
txt.chrout('\n')
check_not_greatereq_w(-32766, 32766)
comparevar = 32765
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 32767
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
test_stack.test()
return
sub check_greatereq_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1>=(w2+zero)
if not ub {
error++
txt.print("ubz!")
}
ub = w1>=w2
if not ub {
error++
txt.print("ub!")
}
if w1>=(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" >= ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_greatereq_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1>=w2
if ub {
error++
txt.print("ub!")
}
if w1>=(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if w1>=w2 {
error++
txt.print("c2!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" not >= ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
}
sub word_lessequal() {
word lessvar
word comparevar
txt.print("word <=\n")
txt.print_w(32767)
txt.chrout('\n')
check_lesseq_w(-32767, 32767)
txt.print_w(-32767)
txt.chrout('\n')
check_not_lesseq_w(32767, -32767)
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 32767
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -32768
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
test_stack.test()
return
sub check_lesseq_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1<=w2
if not ub {
error++
txt.print("ub!")
}
if w1<=(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" <= ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_lesseq_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1<=w2
if ub {
error++
txt.print("ub!")
}
if w1<=(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" not <= ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
}
sub word_less() {
word lessvar
word comparevar
txt.print("word <\n")
txt.print_w(32767)
txt.chrout('\n')
check_less_w(-32767, 32767)
txt.print_w(-32767)
txt.chrout('\n')
check_not_less_w(32767, -32767)
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -1 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -3 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -255 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -256 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -257 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -5001 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 0 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 254 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 255 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 256 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 32767
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -32768
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto -32768 {
check_not_less_w(lessvar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto -1 {
check_not_less_w(lessvar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto 0 {
check_not_less_w(lessvar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto 11111 {
check_not_less_w(lessvar, comparevar)
}
test_stack.test()
return
sub check_less_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1<w2
if not ub {
error++
txt.print("ub!")
}
if w1<(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" < ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_less_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1<w2
if ub {
error++
txt.print("ub!")
}
if w1<(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" not < ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
}
}

Binary file not shown.

BIN
examples/compiled/balls.prg Normal file

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.

View File

@ -1,5 +1,6 @@
%import floats
%import textio
%import test_stack
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
@ -41,6 +42,8 @@ main {
txt.print(" jiffies/fr = ")
txt.print_ub(60/timer_jiffies)
txt.print(" fps")
;test_stack.test()
}
}

View File

@ -1,5 +1,6 @@
%import syslib
%import graphics
%import test_stack
; Note: this program is compatible with C64 and CX16.
@ -37,6 +38,8 @@ main {
anglez+=452
wait_a_little_bit()
; test_stack.test()
}
}

View File

@ -1,6 +1,8 @@
%target c64
%import syslib
%import textio
%import test_stack
spritedata $2000 {
; this memory block contains the sprite data
@ -95,6 +97,8 @@ main {
txt.print(" jiffies/fr = ")
txt.print_ub(60/c64.TIME_LO)
txt.print(" fps")
; test_stack.test()
}
}

View File

@ -1,7 +1,9 @@
%target c64
%import syslib
%import test_stack
%import textio
main {
; vertices
@ -33,6 +35,8 @@ main {
txt.print_ub(60/c64.TIME_LO)
txt.print(" fps")
c64.TIME_LO=0
; test_stack.test()
}
}

View File

@ -1,5 +1,6 @@
%target cx16
%import syslib
%import test_stack
%import conv
; TODO add all other Elite's ships, show their name, advance to next ship on keypress
@ -41,6 +42,8 @@ main {
anglex += 217
angley -= 505
anglez += 452
; test_stack.test()
}
}

View File

@ -1,4 +1,5 @@
%import graphics
%import test_stack
; Note: this program is compatible with C64 and CX16.
@ -6,8 +7,13 @@ main {
sub start() {
graphics.enable_bitmap_mode()
draw_lines()
draw_circles()
; graphics.disable_bitmap_mode()
; test_stack.test()
repeat {
}
}

View File

@ -1,5 +1,6 @@
%import textio
%import syslib
%import test_stack
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
@ -40,6 +41,8 @@ main {
rect(10, 10, 10, 10, false)
rect(6, 0, 16, 20, true)
; test_stack.test()
sub rect(ubyte x1, ubyte y1, ubyte x2, ubyte y2, ubyte fill) {
ubyte x

View File

@ -1,5 +1,6 @@
%import graphics
%import floats
%import test_stack
%zeropage floatsafe
; Draw a mandelbrot in graphics mode (the image will be 256 x 200 pixels).
@ -44,6 +45,9 @@ main {
}
}
; graphics.disable_bitmap_mode()
; test_stack.test()
repeat {
}
}

View File

@ -1,5 +1,6 @@
%import textio
%import floats
%import test_stack
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
@ -57,5 +58,7 @@ main {
txt.print("finished in ")
floats.print_f(duration)
txt.print(" seconds!\n")
; test_stack.test()
}
}

View File

@ -1,5 +1,6 @@
%import textio
%import conv
%import test_stack
%zeropage basicsafe
; The classic number guessing game.
@ -59,6 +60,8 @@ main {
txt.print("Thanks for playing, ")
txt.print(name)
txt.print(".\n")
; test_stack.test()
}
}
}

View File

@ -1,5 +1,6 @@
%target c64
%import syslib
%import test_stack
%import textio
; converted from plasma test program for cc65.
@ -9,6 +10,7 @@
; Converted to prog8 by Irmen de Jong.
main {
const uword SCREEN1 = $E000
const uword SCREEN2 = $E400
@ -37,6 +39,9 @@ main {
;c64.VMCSB = v
;c64.CIA2PRA = block
;txt.print("done!\n")
;test_stack.test()
;repeat {
;}
}
; several variables outside of doplasma to make them retain their value

View File

@ -1,4 +1,5 @@
%import textio
%import test_stack
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
@ -26,6 +27,8 @@ main {
txt.print("number of primes (expected 54): ")
txt.print_ub(amount)
txt.chrout('\n')
; test_stack.test()
}

View File

@ -1,4 +1,5 @@
%import textio
%import test_stack
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
@ -31,6 +32,7 @@ main {
txt.print("reversed\n")
print_arrays()
;test_stack.test()
return

View File

@ -10,6 +10,7 @@
%target c64
%import syslib
%import textio
%import test_stack
main {
@ -67,6 +68,9 @@ waitkey:
}
drawScore()
; txt.plot(0,0)
; test_stack.test()
}
ubyte key=c64.GETIN()

View File

@ -1,51 +1,11 @@
%import textio
%import floats
%import syslib
%zeropage basicsafe
%import test_stack
main {
sub start() {
str s1 = "irmen"
str s2 = "hello"
; byte c = strcmp(s1, s2)
; txt.print_b(c)
; txt.chrout('\n')
txt.print_ub(s1<=s2)
txt.chrout('\n')
testX()
}
asmsub testX() {
%asm {{
stx _saveX
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
ldx _saveX
rts
_saveX .byte 0
}}
}
}

View File

@ -1,4 +1,5 @@
%import textio
%import test_stack
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
@ -963,6 +964,9 @@ main {
txt.print("ok\n")
else
txt.print("fail!!!\n")
test_stack.test()
}
sub wait_input() {

View File

@ -1,6 +1,7 @@
%import textio
%import conv
%import diskio
%import test_stack
%option no_sysinit
%zeropage basicsafe
@ -26,6 +27,8 @@ main {
planet.display(false)
repeat {
; test_stack.test()
str input = "????????"
txt.print("\nCash: ")
util.print_10s(ship.cash)
@ -981,19 +984,4 @@ util {
}}
}
asmsub testX() {
%asm {{
stx _saveX
lda #13
jsr txt.chrout
lda _saveX
jsr txt.print_ub
lda #13
jsr txt.chrout
ldx _saveX
rts
_saveX .byte 0
}}
}
}

View File

@ -1,6 +1,7 @@
%target c64
%import floats
%import graphics
%import test_stack
%zeropage floatsafe
main {
@ -18,6 +19,8 @@ main {
turtle.rt(94)
}
; test_stack.test()
repeat {
}
}

View File

@ -3,8 +3,8 @@ plugins {
id 'java'
}
targetCompatibility = 1.8
sourceCompatibility = 1.8
targetCompatibility = 11
sourceCompatibility = 11
repositories {
mavenCentral()