mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 20:02:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52e8a44517 | ||
|
|
59f33658ad | ||
|
|
e0315bffdc | ||
|
|
cd28d0c0e0 | ||
|
|
0baa2c8b23 | ||
|
|
4977d1fbd5 | ||
|
|
3b7a92f1b4 | ||
|
|
f6920172dd | ||
|
|
93bfc8f5f4 | ||
|
|
39b7655264 | ||
|
|
8b75ceb412 | ||
|
|
c39fc4010d | ||
|
|
8df778a515 | ||
|
|
5134ea76bf | ||
|
|
3ba37df29d | ||
|
|
e221d674d9 | ||
|
|
251f947293 | ||
|
|
41e1e1cbb0 | ||
|
|
da1bc351d2 | ||
|
|
43c0afdea0 | ||
|
|
add5bfa2ec | ||
|
|
34babfb5de | ||
|
|
4f6c45c86c | ||
|
|
e6220a464c | ||
|
|
8dcd49934a | ||
|
|
bedc3bdb56 | ||
|
|
83ceb0fde9 | ||
|
|
1d299c56e0 | ||
|
|
0d735c2ccc | ||
|
|
4094f89d4a | ||
|
|
cf1e8b194a | ||
|
|
74e5644f55 | ||
|
|
b5dc5fc615 | ||
|
|
7a7270d769 | ||
|
|
7549ddcd2b | ||
|
|
08f0303178 | ||
|
|
0d7a291b81 | ||
|
|
2265ae9600 | ||
|
|
cba502e87a | ||
|
|
ac94236614 | ||
|
|
ddf1be2a13 | ||
|
|
b7694686c2 | ||
|
|
63332c0530 | ||
|
|
8a504f8eee | ||
|
|
106fc5daa4 | ||
|
|
7accb73993 | ||
|
|
e9aa6a0956 | ||
|
|
df20467e03 | ||
|
|
ecbd9d739e | ||
|
|
8af17c295a | ||
|
|
329b28cad1 | ||
|
|
452c29574d | ||
|
|
5bedc1b333 | ||
|
|
0bf6d2f72c | ||
|
|
c09b8af491 | ||
|
|
260bcd3a55 | ||
|
|
6b5211ad12 | ||
|
|
a92ec14989 | ||
|
|
b3348eb22b | ||
|
|
bec5a261e5 | ||
|
|
4b53641e1d | ||
|
|
00071d53d5 | ||
|
|
6902834568 | ||
|
|
fa2d87f3dd | ||
|
|
44019d1a61 | ||
|
|
6f74fb49bd | ||
|
|
a303b39cf0 | ||
|
|
3e63a29c59 | ||
|
|
261c0fc9b6 | ||
|
|
895b30f7e5 | ||
|
|
b985604e22 | ||
|
|
f7953e4ef3 | ||
|
|
63483d1f0e | ||
|
|
8b981f03bf | ||
|
|
d0d0910bf2 | ||
|
|
57ac820767 | ||
|
|
b8bda867b6 | ||
|
|
05d3a2450c | ||
|
|
d40788adfa | ||
|
|
83fbf86b1c | ||
|
|
e876008427 | ||
|
|
2b43353eb4 | ||
|
|
a74403c347 | ||
|
|
2f4c6c8697 | ||
|
|
238d8197f5 | ||
|
|
53a600d87b | ||
|
|
2a0ffaf45d | ||
|
|
936b046ed9 | ||
|
|
378dcfe351 | ||
|
|
0a330b9288 | ||
|
|
a88b40d6c1 | ||
|
|
09f25ffbd9 | ||
|
|
ab1232d742 | ||
|
|
a7f56fe0fc | ||
|
|
58a9452c36 | ||
|
|
6d8c4f403f | ||
|
|
88b80fed90 | ||
|
|
acdbd0c391 |
No files matched your search
Generated
+1
-1
@@ -16,7 +16,7 @@
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -20,36 +20,36 @@ Full documentation (syntax reference, how to use the language and the compiler,
|
||||
https://prog8.readthedocs.io/
|
||||
|
||||
|
||||
What use Prog8 provide?
|
||||
-----------------------
|
||||
What does Prog8 provide?
|
||||
------------------------
|
||||
|
||||
- reduction of source code length over raw assembly
|
||||
- big reduction of source code length over raw assembly
|
||||
- modularity, symbol scoping, subroutines
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- automatic variable allocations, automatic string and array variables and string sharing
|
||||
- subroutines with an input- and output parameter signature
|
||||
- constant folding in expressions
|
||||
- no stack frame allocations because parameters and local variables are automatically allocated statically
|
||||
- constant folding in expressions and other high-level program optimizations
|
||||
- conditional branches
|
||||
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
||||
- structs to group together sets of variables and manipulate them at once
|
||||
- floating point operations (requires the C64 Basic ROM routines for this)
|
||||
- abstracting away low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
||||
- various code optimizations (code structure, logical and numerical expressions, unused code removal...)
|
||||
- inline assembly allows you to have full control when every cycle or byte matters
|
||||
- 'when' statement to provide a concise jump table alternative to if/elseif chains
|
||||
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
|
||||
- structs to group together sets of variables and manipulate them at once
|
||||
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
|
||||
- fast execution speed due to compilation to native assembly code
|
||||
- inline assembly allows you to have full control when every cycle or byte matters
|
||||
|
||||
*Rapid edit-compile-run-debug cycle:*
|
||||
|
||||
- use a modern PC to do the work on
|
||||
- very quick compilation times
|
||||
- use a modern PC to do the work on, use nice editors and enjoy quick compilation times
|
||||
- can automatically run the program in the Vice emulator after succesful compilation
|
||||
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
||||
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
||||
|
||||
*Two supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
||||
|
||||
- "c64": Commodore-64 (6510 CPU = almost a 6502) premium support.
|
||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) experimental support.
|
||||
- "c64": Commodore-64 (6510 CPU = almost a 6502), the main target.
|
||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) .
|
||||
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ Additional required tools
|
||||
A recent .exe version of this tool for Windows can be obtained from my [clone](https://github.com/irmen/64tass/releases) of this project.
|
||||
For other platforms it is very easy to compile it yourself (make ; make install).
|
||||
|
||||
A **Java runtime (jre or jdk), version 8 or newer** is required to run a prepackaged version of the compiler.
|
||||
A **Java runtime (jre or jdk), version 11 or newer** is required to run a prepackaged version of the compiler.
|
||||
If you want to build it from source, you'll need a Java SDK + Kotlin 1.3.x SDK (or for instance,
|
||||
IntelliJ IDEA with the Kotlin plugin).
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20"
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
// id "org.jetbrains.kotlin.jvm" version "1.4.10"
|
||||
// id "org.jetbrains.kotlin.jvm" version "1.4.20"
|
||||
id 'application'
|
||||
id 'org.jetbrains.dokka' version "0.9.18"
|
||||
id 'com.github.johnrengelman.shadow' version '5.2.0'
|
||||
@@ -15,8 +15,8 @@ plugins {
|
||||
apply plugin: "kotlin"
|
||||
apply plugin: "java"
|
||||
|
||||
targetCompatibility = 1.8
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 11
|
||||
sourceCompatibility = 11
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
@@ -45,7 +45,7 @@ dependencies {
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = "11"
|
||||
// verbose = true
|
||||
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
||||
}
|
||||
@@ -53,7 +53,7 @@ compileKotlin {
|
||||
|
||||
compileTestKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = "11"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,17 +89,27 @@ cast_from_b .proc
|
||||
|
||||
cast_as_uw_into_ya .proc ; also used for float 2 ub
|
||||
; -- cast float at A/Y to uword into Y/A
|
||||
stx P8ZP_SCRATCH_REG
|
||||
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_as_w_into_ay .proc ; also used for float 2 b
|
||||
; -- cast float at A/Y to word into A/Y
|
||||
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 MOVFM
|
||||
jsr AYINT
|
||||
ldy $64
|
||||
lda $65
|
||||
@@ -380,6 +390,69 @@ 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
|
||||
|
||||
@@ -12,7 +12,7 @@ graphics {
|
||||
|
||||
sub enable_bitmap_mode() {
|
||||
; enable bitmap screen, erase it and set colors to black/white.
|
||||
c64.SCROLY = %00111000
|
||||
c64.SCROLY = %00111011
|
||||
c64.SCROLX = %00001000
|
||||
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
|
||||
clear_screen(1, 0)
|
||||
@@ -20,7 +20,7 @@ graphics {
|
||||
|
||||
sub disable_bitmap_mode() {
|
||||
; enables text mode, erase the text screen, color white
|
||||
c64.SCROLY = %00011000
|
||||
c64.SCROLY = %00011011
|
||||
c64.SCROLX = %00001000
|
||||
c64.VMCSB = (c64.VMCSB & %11110000) | %00000100 ; $1000-$2fff
|
||||
txt.fill_screen(' ', 1)
|
||||
@@ -158,21 +158,17 @@ graphics {
|
||||
ubyte ycenter_plus_xx = ycenter + xx
|
||||
ubyte ycenter_min_xx = ycenter - xx
|
||||
|
||||
for internal_plotx in xcenter to xcenter+xx {
|
||||
internal_plotx = xcenter-xx
|
||||
repeat xx*2+1 {
|
||||
internal_plot(ycenter_plus_yy)
|
||||
internal_plot(ycenter_min_yy)
|
||||
internal_plotx++
|
||||
}
|
||||
for internal_plotx in xcenter-xx to xcenter-1 {
|
||||
internal_plot(ycenter_plus_yy)
|
||||
internal_plot(ycenter_min_yy)
|
||||
}
|
||||
for internal_plotx in xcenter to xcenter+yy {
|
||||
internal_plot(ycenter_plus_xx)
|
||||
internal_plot(ycenter_min_xx)
|
||||
}
|
||||
for internal_plotx in xcenter-yy to xcenter {
|
||||
internal_plotx = xcenter-yy
|
||||
repeat yy*2+1 {
|
||||
internal_plot(ycenter_plus_xx)
|
||||
internal_plot(ycenter_min_xx)
|
||||
internal_plotx++
|
||||
}
|
||||
yy++
|
||||
if decisionOver2<=0
|
||||
|
||||
@@ -269,6 +269,22 @@ asmsub reset_system() {
|
||||
}}
|
||||
}
|
||||
|
||||
sub wait(uword jiffies) {
|
||||
uword current_time = 0
|
||||
c64.SETTIM(0,0,0)
|
||||
|
||||
while current_time < jiffies {
|
||||
; read clock
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr c64.RDTIM
|
||||
sta current_time
|
||||
stx current_time+1
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
asmsub disable_runstop_and_charsetswitch() {
|
||||
%asm {{
|
||||
lda #$80
|
||||
|
||||
@@ -16,7 +16,7 @@ const ubyte DEFAULT_HEIGHT = 25
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
clear_screenchars(' ')
|
||||
txt.chrout(147)
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
@@ -38,13 +38,13 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
ldy #0
|
||||
_loop sta c64.Screen,y
|
||||
sta c64.Screen+$0100,y
|
||||
sta c64.Screen+$0200,y
|
||||
sta c64.Screen+$02e8,y
|
||||
iny
|
||||
bne _loop
|
||||
ldy #250
|
||||
- sta c64.Screen+250*0-1,y
|
||||
sta c64.Screen+250*1-1,y
|
||||
sta c64.Screen+250*2-1,y
|
||||
sta c64.Screen+250*3-1,y
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -53,13 +53,13 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||
; ---- clear the character screen colors with the given color (leaves characters).
|
||||
; (assumes color matrix is at the default address)
|
||||
%asm {{
|
||||
ldy #0
|
||||
_loop sta c64.Colors,y
|
||||
sta c64.Colors+$0100,y
|
||||
sta c64.Colors+$0200,y
|
||||
sta c64.Colors+$02e8,y
|
||||
iny
|
||||
bne _loop
|
||||
ldy #250
|
||||
- sta c64.Colors+250*0-1,y
|
||||
sta c64.Colors+250*1-1,y
|
||||
sta c64.Colors+250*2-1,y
|
||||
sta c64.Colors+250*3-1,y
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -83,29 +83,31 @@ asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
||||
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcs +
|
||||
jmp _scroll_screen
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the color memory
|
||||
+ ; scroll the screen and the color memory
|
||||
ldx #0
|
||||
ldy #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Colors + 40*row + 1,x
|
||||
sta c64.Colors + 40*row,x
|
||||
.next
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 1,x
|
||||
sta c64.Screen + 40*row + 0,x
|
||||
lda c64.Colors + 40*row + 1,x
|
||||
sta c64.Colors + 40*row + 0,x
|
||||
.next
|
||||
inx
|
||||
dey
|
||||
bpl -
|
||||
rts
|
||||
|
||||
_scroll_screen ; scroll the screen memory
|
||||
_scroll_screen ; scroll only the screen memory
|
||||
ldx #0
|
||||
ldy #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 1,x
|
||||
sta c64.Screen + 40*row,x
|
||||
.next
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 1,x
|
||||
sta c64.Screen + 40*row + 0,x
|
||||
.next
|
||||
inx
|
||||
dey
|
||||
bpl -
|
||||
@@ -121,26 +123,28 @@ asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcs +
|
||||
jmp _scroll_screen
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the color memory
|
||||
+ ; scroll the screen and the color memory
|
||||
ldx #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Colors + 40*row + 0,x
|
||||
sta c64.Colors + 40*row + 1,x
|
||||
.next
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 0,x
|
||||
sta c64.Screen + 40*row + 1,x
|
||||
lda c64.Colors + 40*row + 0,x
|
||||
sta c64.Colors + 40*row + 1,x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
rts
|
||||
|
||||
_scroll_screen ; scroll the screen memory
|
||||
_scroll_screen ; scroll only the screen memory
|
||||
ldx #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 0,x
|
||||
sta c64.Screen + 40*row + 1,x
|
||||
.next
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 0,x
|
||||
sta c64.Screen + 40*row + 1,x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
@@ -155,26 +159,28 @@ asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcs +
|
||||
jmp _scroll_screen
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the color memory
|
||||
+ ; scroll the screen and the color memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=1, row<=24, row+=1
|
||||
lda c64.Colors + 40*row,x
|
||||
sta c64.Colors + 40*(row-1),x
|
||||
.next
|
||||
.for row=1, row<=24, row+=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row-1),x
|
||||
lda c64.Colors + 40*row,x
|
||||
sta c64.Colors + 40*(row-1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
rts
|
||||
|
||||
_scroll_screen ; scroll the screen memory
|
||||
_scroll_screen ; scroll only the screen memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=1, row<=24, row+=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row-1),x
|
||||
.next
|
||||
.for row=1, row<=24, row+=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row-1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
@@ -189,26 +195,28 @@ asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcs +
|
||||
jmp _scroll_screen
|
||||
bcc _scroll_screen
|
||||
|
||||
+ ; scroll the color memory
|
||||
+ ; scroll the screen and the color memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=23, row>=0, row-=1
|
||||
lda c64.Colors + 40*row,x
|
||||
sta c64.Colors + 40*(row+1),x
|
||||
.next
|
||||
.for row=23, row>=0, row-=1
|
||||
lda c64.Colors + 40*row,x
|
||||
sta c64.Colors + 40*(row+1),x
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row+1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
rts
|
||||
|
||||
_scroll_screen ; scroll the screen memory
|
||||
_scroll_screen ; scroll only the screen memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=23, row>=0, row-=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row+1),x
|
||||
.next
|
||||
.for row=23, row>=0, row-=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row+1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
%target cx16
|
||||
|
||||
c64colors {
|
||||
uword[] colorpalette_dark = [ ; this is a darker palette with more contrast
|
||||
$000, ; 0 = black
|
||||
$FFF, ; 1 = white
|
||||
$632, ; 2 = red
|
||||
$7AB, ; 3 = cyan
|
||||
$638, ; 4 = purple
|
||||
$584, ; 5 = green
|
||||
$327, ; 6 = blue
|
||||
$BC6, ; 7 = yellow
|
||||
$642, ; 8 = orange
|
||||
$430, ; 9 = brown
|
||||
$965, ; 10 = light red
|
||||
$444, ; 11 = dark grey
|
||||
$666, ; 12 = medium grey
|
||||
$9D8, ; 13 = light green
|
||||
$65B, ; 14 = light blue
|
||||
$999 ; 15 = light grey
|
||||
]
|
||||
|
||||
uword[] colorpalette_pepto = [ ; # this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
|
||||
$000, ; 0 = black
|
||||
$FFF, ; 1 = white
|
||||
$833, ; 2 = red
|
||||
$7cc, ; 3 = cyan
|
||||
$839, ; 4 = purple
|
||||
$5a4, ; 5 = green
|
||||
$229, ; 6 = blue
|
||||
$ef7, ; 7 = yellow
|
||||
$852, ; 8 = orange
|
||||
$530, ; 9 = brown
|
||||
$c67, ; 10 = light red
|
||||
$444, ; 11 = dark grey
|
||||
$777, ; 12 = medium grey
|
||||
$af9, ; 13 = light green
|
||||
$76e, ; 14 = light blue
|
||||
$bbb ; 15 = light grey
|
||||
]
|
||||
|
||||
uword[] colorpalette_light = [ ; this is a lighter palette
|
||||
$000, ; 0 = black
|
||||
$FFF, ; 1 = white
|
||||
$944, ; 2 = red
|
||||
$7CC, ; 3 = cyan
|
||||
$95A, ; 4 = purple
|
||||
$6A5, ; 5 = green
|
||||
$549, ; 6 = blue
|
||||
$CD8, ; 7 = yellow
|
||||
$963, ; 8 = orange
|
||||
$650, ; 9 = brown
|
||||
$C77, ; 10 = light red
|
||||
$666, ; 11 = dark grey
|
||||
$888, ; 12 = medium grey
|
||||
$AE9, ; 13 = light green
|
||||
$87C, ; 14 = light blue
|
||||
$AAA ; 15 = light grey
|
||||
]
|
||||
|
||||
ubyte color
|
||||
|
||||
sub set_palette_pepto() {
|
||||
for color in 0 to 15 {
|
||||
uword cc = colorpalette_pepto[color]
|
||||
cx16.vpoke(1, $fa00 + color*2, lsb(cc)) ; G, B
|
||||
cx16.vpoke(1, $fa01 + color*2, msb(cc)) ; R
|
||||
}
|
||||
}
|
||||
|
||||
sub set_palette_light() {
|
||||
for color in 0 to 15 {
|
||||
uword cc = colorpalette_light[color]
|
||||
cx16.vpoke(1, $fa00 + color*2, lsb(cc)) ; G, B
|
||||
cx16.vpoke(1, $fa01 + color*2, msb(cc)) ; R
|
||||
}
|
||||
}
|
||||
|
||||
sub set_palette_dark() {
|
||||
for color in 0 to 15 {
|
||||
uword cc = colorpalette_dark[color]
|
||||
cx16.vpoke(1, $fa00 + color*2, lsb(cc)) ; G, B
|
||||
cx16.vpoke(1, $fa01 + color*2, msb(cc)) ; R
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -64,6 +64,7 @@ romsub $fe7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += sign
|
||||
romsub $fe81 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
||||
romsub $fe8a = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||
romsub $fe8d = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
||||
; note: there is no FPWR() on the Cx16
|
||||
romsub $fe93 = NEGOP() clobbers(A) ; switch the sign of fac1
|
||||
romsub $fe96 = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
||||
romsub $fe9f = RND2(byte value @A) clobbers(A,X,Y) ; fac1 = RND(A) float random number generator
|
||||
|
||||
@@ -21,7 +21,7 @@ graphics {
|
||||
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
|
||||
txt.fill_screen(' ', 1) ; doesn't seem to fully clear the text screen after returning from gfx mode
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ graphics {
|
||||
;cx16.r1 = ycenter - radius/2
|
||||
;cx16.r2 = radius*2
|
||||
;cx16.r3 = radius*2
|
||||
;cx16.GRAPH_draw_oval(false) ; TODO currently is not implemented on cx16, does a BRK
|
||||
;cx16.GRAPH_draw_oval(false) ; currently this call is not implemented on cx16, does a BRK
|
||||
|
||||
; Midpoint algorithm
|
||||
ubyte @zp xx = radius
|
||||
@@ -94,7 +94,7 @@ graphics {
|
||||
; cx16.r1 = ycenter - radius/2
|
||||
; cx16.r2 = radius*2
|
||||
; cx16.r3 = radius*2
|
||||
; cx16.GRAPH_draw_oval(true) ; TODO currently is not implemented on cx16, does a BRK
|
||||
; cx16.GRAPH_draw_oval(true) ; currently this call is not implemented on cx16, does a BRK
|
||||
|
||||
ubyte xx = radius
|
||||
ubyte yy = 0
|
||||
@@ -107,42 +107,30 @@ graphics {
|
||||
ubyte ycenter_min_xx = ycenter - xx
|
||||
uword @zp plotx
|
||||
|
||||
for plotx in xcenter to xcenter+xx {
|
||||
cx16.r0 = plotx
|
||||
cx16.r1 = ycenter_plus_yy
|
||||
cx16.FB_cursor_position()
|
||||
cx16.r0 = xcenter-xx
|
||||
cx16.r1 = ycenter_plus_yy
|
||||
cx16.FB_cursor_position()
|
||||
repeat xx*2+1
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.r1 = ycenter_min_yy
|
||||
cx16.FB_cursor_position()
|
||||
|
||||
cx16.r0 = xcenter-xx
|
||||
cx16.r1 = ycenter_min_yy
|
||||
cx16.FB_cursor_position()
|
||||
repeat xx*2+1
|
||||
cx16.FB_set_pixel(1)
|
||||
}
|
||||
for plotx in xcenter-xx to xcenter-1 {
|
||||
cx16.r0 = plotx
|
||||
cx16.r1 = ycenter_plus_yy
|
||||
cx16.FB_cursor_position()
|
||||
|
||||
cx16.r0 = xcenter-yy
|
||||
cx16.r1 = ycenter_plus_xx
|
||||
cx16.FB_cursor_position()
|
||||
repeat yy*2+1
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.r1 = ycenter_min_yy
|
||||
cx16.FB_cursor_position()
|
||||
|
||||
cx16.r0 = xcenter-yy
|
||||
cx16.r1 = ycenter_min_xx
|
||||
cx16.FB_cursor_position()
|
||||
repeat yy*2+1
|
||||
cx16.FB_set_pixel(1)
|
||||
}
|
||||
for plotx in xcenter to xcenter+yy {
|
||||
cx16.r0 = plotx
|
||||
cx16.r1 = ycenter_plus_xx
|
||||
cx16.FB_cursor_position()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.r1 = ycenter_min_xx
|
||||
cx16.FB_cursor_position()
|
||||
cx16.FB_set_pixel(1)
|
||||
}
|
||||
for plotx in xcenter-yy to xcenter {
|
||||
cx16.r0 = plotx
|
||||
cx16.r1 = ycenter_plus_xx
|
||||
cx16.FB_cursor_position()
|
||||
cx16.FB_set_pixel(1)
|
||||
cx16.r1 = ycenter_min_xx
|
||||
cx16.FB_cursor_position()
|
||||
cx16.FB_set_pixel(1)
|
||||
}
|
||||
|
||||
yy++
|
||||
if decisionOver2<=0
|
||||
decisionOver2 += 2*yy+1
|
||||
|
||||
@@ -220,7 +220,7 @@ romsub $ff08 = FB_get_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
|
||||
romsub $ff0b = FB_set_pixel(ubyte color @A) clobbers(A,X,Y)
|
||||
romsub $ff0e = FB_set_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
|
||||
romsub $ff11 = FB_set_8_pixels(ubyte pattern @A, ubyte color @X) clobbers(A,X,Y)
|
||||
romsub $ff14 = FB_set_8_pixels_opaque(ubyte pattern @A, ubyte color1 @X, ubyte color2 @Y) clobbers(A,X,Y) ; also uses mask=r0L
|
||||
romsub $ff14 = FB_set_8_pixels_opaque(ubyte mask @A, ubyte color1 @X, ubyte color2 @Y) clobbers(A,X,Y) ; also uses r0L=pattern
|
||||
romsub $ff17 = FB_fill_pixels(ubyte color @A) clobbers(A,X,Y) ; also uses count=r0, step=r1
|
||||
romsub $ff1a = FB_filter_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
|
||||
romsub $ff1d = FB_move_pixels() clobbers(A,X,Y) ; uses sx=r0, sy=r1, tx=r2, ty=r3, count=r4
|
||||
@@ -242,9 +242,96 @@ romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
|
||||
romsub $fecc = monitor() clobbers(A,X,Y)
|
||||
|
||||
|
||||
|
||||
; ---- end of kernal routines ----
|
||||
|
||||
; ---- utilities -----
|
||||
asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
|
||||
; -- get a byte from VERA's video memory
|
||||
; note: inefficient when reading multiple sequential bytes!
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
sty cx16.VERA_ADDR_M
|
||||
stx cx16.VERA_ADDR_L
|
||||
lda cx16.VERA_DATA0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub vpoke(ubyte bank, uword address, ubyte value) {
|
||||
; -- write a single byte to VERA's video memory
|
||||
; note: inefficient when writing multiple sequential bytes!
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
lda bank
|
||||
and #1
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda address
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda address+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda value
|
||||
sta cx16.VERA_DATA0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub FB_set_pixels_from_buf(uword buffer, uword count) {
|
||||
%asm {{
|
||||
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM
|
||||
; However that routine contains a bug in the current v38 ROM that makes it crash when count > 255.
|
||||
; So the code below replaces that. Once the ROM is patched this routine is no longer necessary.
|
||||
; See https://github.com/commanderx16/x16-rom/issues/179
|
||||
phx
|
||||
lda buffer
|
||||
ldy buffer+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
jsr _pixels
|
||||
plx
|
||||
rts
|
||||
|
||||
_pixels lda count+1
|
||||
beq +
|
||||
ldx #0
|
||||
- jsr _loop
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
dec count+1
|
||||
bne -
|
||||
|
||||
+ ldx count
|
||||
_loop ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta cx16.VERA_DATA0
|
||||
iny
|
||||
dex
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
|
||||
sub wait(uword jiffies) {
|
||||
uword current_time = 0
|
||||
c64.SETTIM(0,0,0)
|
||||
|
||||
while current_time < jiffies {
|
||||
; read clock
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.RDTIM
|
||||
sta current_time
|
||||
stx current_time+1
|
||||
plx
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; ---- system stuff -----
|
||||
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
|
||||
@@ -16,7 +16,7 @@ const ubyte DEFAULT_HEIGHT = 60
|
||||
|
||||
|
||||
sub clear_screen() {
|
||||
clear_screenchars(' ')
|
||||
txt.chrout(147)
|
||||
}
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
@@ -154,10 +154,9 @@ sub uppercase() {
|
||||
cx16.screen_set_charset(2, 0) ; uppercase charset
|
||||
}
|
||||
|
||||
asmsub scroll_left (ubyte dummy @ Pc) clobbers(A, Y) {
|
||||
asmsub scroll_left() clobbers(A, Y) {
|
||||
; ---- scroll the whole screen 1 character to the left
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag is a dummy on the cx16
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.SCREEN
|
||||
@@ -198,10 +197,9 @@ _lx ldx #0 ; modified
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right (ubyte dummy @ Pc) clobbers(A) {
|
||||
asmsub scroll_right() clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag is a dummy on the cx16
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.SCREEN
|
||||
@@ -250,10 +248,9 @@ _lx ldx #0 ; modified
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up (ubyte dummy @ Pc) clobbers(A, Y) {
|
||||
asmsub scroll_up() clobbers(A, Y) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag is a dummy on the cx16
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.SCREEN
|
||||
@@ -300,10 +297,9 @@ _nextline
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down (ubyte dummy @ Pc) clobbers(A, Y) {
|
||||
asmsub scroll_down() clobbers(A, Y) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag is a dummy on the cx16
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.SCREEN
|
||||
|
||||
+228
-16
@@ -6,15 +6,15 @@
|
||||
diskio {
|
||||
|
||||
|
||||
sub directory(ubyte drivenumber) -> byte {
|
||||
; -- Shows the directory contents of disk drive 8-11 (provide as argument).
|
||||
sub directory(ubyte drivenumber) -> ubyte {
|
||||
; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
|
||||
|
||||
c64.SETNAM(1, "$")
|
||||
c64.SETLFS(1, drivenumber, 0)
|
||||
void c64.OPEN() ; open 1,8,0,"$"
|
||||
c64.SETLFS(13, drivenumber, 0)
|
||||
void c64.OPEN() ; open 13,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void c64.CHKIN(1) ; use #1 as input channel
|
||||
void c64.CHKIN(13) ; use #13 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
@@ -25,7 +25,9 @@ diskio {
|
||||
; while not key pressed / EOF encountered, read data.
|
||||
ubyte status = c64.READST()
|
||||
while not status {
|
||||
txt.print_uw(mkword(c64.CHRIN(), c64.CHRIN()))
|
||||
ubyte low = c64.CHRIN()
|
||||
ubyte high = c64.CHRIN()
|
||||
txt.print_uw(mkword(high, low))
|
||||
txt.chrout(' ')
|
||||
ubyte @zp char
|
||||
do {
|
||||
@@ -37,14 +39,14 @@ diskio {
|
||||
void c64.CHRIN()
|
||||
status = c64.READST()
|
||||
void c64.STOP()
|
||||
if_nz
|
||||
if_z
|
||||
break
|
||||
}
|
||||
|
||||
io_error:
|
||||
status = c64.READST()
|
||||
c64.CLRCHN() ; restore default i/o devices
|
||||
c64.CLOSE(1)
|
||||
c64.CLOSE(13)
|
||||
|
||||
if status and status != 64 { ; 64=end of file
|
||||
txt.print("\ni/o error, status: ")
|
||||
@@ -57,9 +59,208 @@ io_error:
|
||||
}
|
||||
|
||||
|
||||
sub status(ubyte drivenumber) {
|
||||
; -- display the disk drive's current status message
|
||||
c64.SETNAM(0, $0000)
|
||||
; internal variables for the iterative file lister / loader
|
||||
ubyte list_suffixmatch
|
||||
ubyte list_pattern_size
|
||||
ubyte list_skip_disk_name
|
||||
uword list_pattern
|
||||
uword list_blocks
|
||||
ubyte iteration_in_progress = false
|
||||
str list_filename = "?" * 32
|
||||
|
||||
|
||||
; ----- get a list of files (uses iteration functions internally -----
|
||||
|
||||
sub list_files(ubyte drivenumber, uword pattern, ubyte suffixmatch, uword name_ptrs, ubyte max_names) -> ubyte {
|
||||
; -- fill the array 'name_ptrs' with (pointers to) the names of the files requested.
|
||||
ubyte[256] names_buffer
|
||||
ubyte[256] names_buffer1 ; to store a bit more names
|
||||
uword buf_ptr = &names_buffer
|
||||
ubyte files_found = 0
|
||||
if lf_start_list(drivenumber, pattern, suffixmatch) {
|
||||
while lf_next_entry() {
|
||||
@(name_ptrs) = lsb(buf_ptr)
|
||||
name_ptrs++
|
||||
@(name_ptrs) = msb(buf_ptr)
|
||||
name_ptrs++
|
||||
buf_ptr += strcopy(diskio.list_filename, buf_ptr) + 1
|
||||
files_found++
|
||||
if buf_ptr - &names_buffer > (len(names_buffer) + len(names_buffer1) - 18)
|
||||
break
|
||||
if files_found == max_names
|
||||
break
|
||||
}
|
||||
lf_end_list()
|
||||
}
|
||||
return files_found
|
||||
}
|
||||
|
||||
; ----- iterative file lister functions -----
|
||||
|
||||
sub lf_start_list(ubyte drivenumber, uword pattern, ubyte suffixmatch) -> ubyte {
|
||||
; -- start an iterative file listing with optional prefix or suffix name matching.
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
lf_end_list()
|
||||
list_pattern = pattern
|
||||
list_suffixmatch = suffixmatch
|
||||
list_skip_disk_name = true
|
||||
iteration_in_progress = true
|
||||
if pattern==0
|
||||
list_pattern_size = 0
|
||||
else
|
||||
list_pattern_size = strlen(pattern)
|
||||
|
||||
c64.SETNAM(1, "$")
|
||||
c64.SETLFS(12, drivenumber, 0)
|
||||
void c64.OPEN() ; open 12,8,0,"$"
|
||||
if_cs
|
||||
goto io_error
|
||||
void c64.CHKIN(12) ; use #12 as input channel
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
repeat 4 {
|
||||
void c64.CHRIN() ; skip the 4 prologue bytes
|
||||
}
|
||||
|
||||
if c64.READST()==0
|
||||
return true
|
||||
|
||||
io_error:
|
||||
lf_end_list()
|
||||
return false
|
||||
}
|
||||
|
||||
sub lf_next_entry() -> ubyte {
|
||||
; -- retrieve the next entry from an iterative file listing session.
|
||||
; results will be found in list_blocks and list_filename.
|
||||
; if it returns false though, there are no more entries (or an error occurred).
|
||||
|
||||
if not iteration_in_progress
|
||||
return false
|
||||
|
||||
repeat {
|
||||
void c64.CHKIN(12) ; use #12 as input channel again
|
||||
|
||||
uword nameptr = &list_filename
|
||||
ubyte blocks_lsb = c64.CHRIN()
|
||||
ubyte blocks_msb = c64.CHRIN()
|
||||
|
||||
if c64.READST()
|
||||
goto close_end
|
||||
|
||||
list_blocks = mkword(blocks_msb, blocks_lsb)
|
||||
|
||||
; read until the filename starts after the first "
|
||||
while c64.CHRIN()!='\"' {
|
||||
if c64.READST()
|
||||
goto close_end
|
||||
}
|
||||
|
||||
; read the filename
|
||||
repeat {
|
||||
ubyte char = c64.CHRIN()
|
||||
if_z
|
||||
break
|
||||
if char=='\"'
|
||||
break
|
||||
@(nameptr) = char
|
||||
nameptr++
|
||||
}
|
||||
|
||||
@(nameptr) = 0
|
||||
|
||||
while c64.CHRIN() {
|
||||
; read the rest of the entry until the end
|
||||
}
|
||||
|
||||
void c64.CHRIN() ; skip 2 bytes
|
||||
void c64.CHRIN()
|
||||
|
||||
if not list_skip_disk_name {
|
||||
if list_pattern_size {
|
||||
; do filename matching
|
||||
if list_suffixmatch
|
||||
rightstr(list_filename, filename, list_pattern_size)
|
||||
else
|
||||
leftstr(list_filename, filename, list_pattern_size)
|
||||
if strcmp(filename, list_pattern)==0
|
||||
return true
|
||||
} else
|
||||
return true
|
||||
}
|
||||
list_skip_disk_name = false
|
||||
}
|
||||
|
||||
close_end:
|
||||
lf_end_list()
|
||||
return false
|
||||
}
|
||||
|
||||
sub lf_end_list() {
|
||||
; -- end an iterative file listing session (close channels).
|
||||
if iteration_in_progress {
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(12)
|
||||
iteration_in_progress = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; ----- iterative file loader functions -----
|
||||
|
||||
sub f_open(ubyte drivenumber, uword filenameptr) -> ubyte {
|
||||
; -- open a file for iterative reading with f_read
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
f_close()
|
||||
|
||||
c64.SETNAM(strlen(filenameptr), filenameptr)
|
||||
c64.SETLFS(11, drivenumber, 0)
|
||||
void c64.OPEN() ; open 11,8,0,"filename"
|
||||
if_cc {
|
||||
iteration_in_progress = true
|
||||
void c64.CHKIN(11) ; use #11 as input channel
|
||||
if_cc
|
||||
return true
|
||||
}
|
||||
f_close()
|
||||
return false
|
||||
}
|
||||
|
||||
sub f_read(uword bufferpointer, uword buffersize) -> uword {
|
||||
if not iteration_in_progress
|
||||
return 0
|
||||
|
||||
uword actual = 0
|
||||
void c64.CHKIN(11) ; use #11 as input channel again
|
||||
repeat buffersize {
|
||||
ubyte data = c64.CHRIN()
|
||||
@(bufferpointer) = data
|
||||
bufferpointer++
|
||||
actual++
|
||||
ubyte status = c64.READST()
|
||||
if status==64
|
||||
f_close() ; end of file, close it
|
||||
if status
|
||||
return actual
|
||||
}
|
||||
return actual
|
||||
}
|
||||
|
||||
sub f_close() {
|
||||
; -- end an iterative file loading session (close channels).
|
||||
if iteration_in_progress {
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(11)
|
||||
iteration_in_progress = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub status(ubyte drivenumber) -> uword {
|
||||
; -- retrieve the disk drive's current status message
|
||||
uword messageptr = &filename
|
||||
c64.SETNAM(0, filename)
|
||||
c64.SETLFS(15, drivenumber, 15)
|
||||
void c64.OPEN() ; open 15,8,15
|
||||
if_cs
|
||||
@@ -68,16 +269,20 @@ io_error:
|
||||
if_cs
|
||||
goto io_error
|
||||
|
||||
while not c64.READST()
|
||||
txt.chrout(c64.CHRIN())
|
||||
while not c64.READST() {
|
||||
@(messageptr) = c64.CHRIN()
|
||||
messageptr++
|
||||
}
|
||||
|
||||
io_error:
|
||||
@(messageptr) = 0
|
||||
c64.CLRCHN() ; restore default i/o devices
|
||||
c64.CLOSE(15)
|
||||
return filename
|
||||
}
|
||||
|
||||
|
||||
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> byte {
|
||||
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte {
|
||||
c64.SETNAM(strlen(filenameptr), filenameptr)
|
||||
c64.SETLFS(1, drivenumber, 0)
|
||||
uword end_address = address + size
|
||||
@@ -97,10 +302,14 @@ io_error:
|
||||
plp
|
||||
}}
|
||||
|
||||
ubyte result=0
|
||||
if_cc
|
||||
return c64.READST()==0
|
||||
result = c64.READST()==0
|
||||
|
||||
return false
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(1)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
sub load(ubyte drivenumber, uword filenameptr, uword address_override) -> uword {
|
||||
@@ -122,6 +331,9 @@ io_error:
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
}}
|
||||
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(1)
|
||||
|
||||
if end_of_load
|
||||
return end_of_load - address_override
|
||||
|
||||
|
||||
@@ -774,6 +774,31 @@ stack_mul_word_100 .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_320 .proc
|
||||
; stackW = stackLo * 256 + stackLo * 64 (stackHi doesn't matter)
|
||||
ldy P8ESTACK_LO+1,x
|
||||
lda #0
|
||||
sta P8ESTACK_HI+1,x
|
||||
tya
|
||||
asl a
|
||||
rol P8ESTACK_HI+1,x
|
||||
asl a
|
||||
rol P8ESTACK_HI+1,x
|
||||
asl a
|
||||
rol P8ESTACK_HI+1,x
|
||||
asl a
|
||||
rol P8ESTACK_HI+1,x
|
||||
asl a
|
||||
rol P8ESTACK_HI+1,x
|
||||
asl a
|
||||
rol P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
tya
|
||||
clc
|
||||
adc P8ESTACK_HI+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
; ----------- optimized multiplications (in-place A (byte) and ?? (word)) : ---------
|
||||
mul_byte_3 .proc
|
||||
@@ -1241,6 +1266,32 @@ mul_word_100 .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_word_320 .proc
|
||||
; AY = A * 256 + A * 64 (msb doesn't matter)
|
||||
sta P8ZP_SCRATCH_B1
|
||||
ldy #0
|
||||
sty P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
pha
|
||||
clc
|
||||
lda P8ZP_SCRATCH_B1
|
||||
adc P8ZP_SCRATCH_REG
|
||||
tay
|
||||
pla
|
||||
rts
|
||||
.pend
|
||||
|
||||
; ----------- end optimized multiplications -----------
|
||||
|
||||
|
||||
@@ -1301,6 +1352,33 @@ shift_left_w_3 .proc
|
||||
jmp shift_left_w_7._shift3
|
||||
.pend
|
||||
|
||||
|
||||
shift_left_w .proc
|
||||
; -- variable number of shifts left
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
bne _shift
|
||||
rts
|
||||
_shift asl P8ESTACK_LO+1,x
|
||||
rol P8ESTACK_HI+1,x
|
||||
dey
|
||||
bne _shift
|
||||
rts
|
||||
.pend
|
||||
|
||||
shift_right_uw .proc
|
||||
; -- uword variable number of shifts right
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
bne _shift
|
||||
rts
|
||||
_shift lsr P8ESTACK_HI+1,x
|
||||
ror P8ESTACK_LO+1,x
|
||||
dey
|
||||
bne _shift
|
||||
rts
|
||||
.pend
|
||||
|
||||
shift_right_uw_7 .proc
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta P8ZP_SCRATCH_B1
|
||||
@@ -1431,6 +1509,21 @@ shift_right_w_3 .proc
|
||||
.pend
|
||||
|
||||
|
||||
shift_right_w .proc
|
||||
; -- signed word variable number of shifts right
|
||||
inx
|
||||
ldy P8ESTACK_LO,x
|
||||
bne _shift
|
||||
rts
|
||||
_shift lda P8ESTACK_HI+1,x
|
||||
asl a
|
||||
ror P8ESTACK_HI+1,x
|
||||
ror P8ESTACK_LO+1,x
|
||||
dey
|
||||
bne _shift
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
; support for bit shifting that is too large to be unrolled:
|
||||
|
||||
|
||||
@@ -1346,3 +1346,24 @@ func_strcmp_stack .proc
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_strcopy .proc
|
||||
lda _arg_to
|
||||
ldy _arg_to+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda _arg_from
|
||||
ldy _arg_from+1
|
||||
jsr strcpy
|
||||
tya
|
||||
rts
|
||||
_arg_from .word 0
|
||||
_arg_to .word 0
|
||||
.pend
|
||||
|
||||
func_strcopy_to_stack .proc
|
||||
jsr func_strcopy
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
@@ -470,27 +470,19 @@ less_uw .proc
|
||||
|
||||
reg_less_w .proc
|
||||
; -- AY < P8ZP_SCRATCH_W2?
|
||||
sta P8ZP_SCRATCH_B1
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
tya
|
||||
sec
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi _true
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bne _false
|
||||
lda P8ZP_SCRATCH_B1
|
||||
sbc P8ZP_SCRATCH_W2
|
||||
bcs _false
|
||||
_true lda #1
|
||||
lda #0
|
||||
rts
|
||||
_false lda #0
|
||||
_true lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
less_w .proc
|
||||
; TODO is this word comparison < correct? reg_less_w is a lot larger...
|
||||
lda P8ESTACK_LO+2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
lda P8ESTACK_HI+2,x
|
||||
@@ -549,7 +541,6 @@ _true lda #1
|
||||
.pend
|
||||
|
||||
lesseq_uw .proc
|
||||
; TODO is this comparison uword <= correct????
|
||||
lda P8ESTACK_HI+1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
bcc equal_b._equal_b_false
|
||||
@@ -561,29 +552,20 @@ lesseq_uw .proc
|
||||
.pend
|
||||
|
||||
reg_lesseq_w .proc
|
||||
; -- AY <= P8ZP_SCRATCH_W2?
|
||||
sta P8ZP_SCRATCH_B1
|
||||
; -- P8ZP_SCRATCH_W2 <= AY ? (note: order different from other routines)
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
tya
|
||||
sec
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi _true
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bne _false
|
||||
lda P8ZP_SCRATCH_B1
|
||||
sbc P8ZP_SCRATCH_W2
|
||||
beq _true ; TODO is this correct word compare <= for all cases?
|
||||
bcs _false
|
||||
_true lda #1
|
||||
+ bpl +
|
||||
lda #0
|
||||
rts
|
||||
_false lda #0
|
||||
+ lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
lesseq_w .proc
|
||||
; TODO is this word compare <= correct? it is different from reg_lesseq_w
|
||||
lda P8ESTACK_LO+1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
lda P8ESTACK_HI+1,x
|
||||
@@ -671,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
|
||||
|
||||
|
||||
@@ -1011,6 +993,7 @@ _arg_index .byte 0
|
||||
strcpy .proc
|
||||
; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
|
||||
; it is assumed the target string is large enough.
|
||||
; returns the length of the string that was copied in Y.
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #$ff
|
||||
@@ -1065,3 +1048,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
|
||||
@@ -1 +1 @@
|
||||
5.1
|
||||
5.4
|
||||
@@ -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)
|
||||
|
||||
@@ -30,8 +30,8 @@ enum class DataType {
|
||||
UWORD -> targetType in setOf(UWORD, FLOAT)
|
||||
WORD -> targetType in setOf(WORD, FLOAT)
|
||||
FLOAT -> targetType == FLOAT
|
||||
STR -> targetType == STR || targetType == UWORD
|
||||
in ArrayDatatypes -> targetType == this || targetType == UWORD
|
||||
STR -> targetType == STR
|
||||
in ArrayDatatypes -> targetType == this
|
||||
else -> false
|
||||
}
|
||||
|
||||
@@ -80,7 +80,9 @@ enum class RegisterOrPair {
|
||||
Y,
|
||||
AX,
|
||||
AY,
|
||||
XY;
|
||||
XY,
|
||||
FAC1,
|
||||
FAC2;
|
||||
|
||||
companion object {
|
||||
val names by lazy { values().map { it.toString()} }
|
||||
|
||||
@@ -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
|
||||
@@ -383,7 +384,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt)) {
|
||||
if(targetDt.typeOrElse(DataType.STRUCT) in IterableDatatypes)
|
||||
errors.err("cannot assign value to string or array", assignment.value.position)
|
||||
else
|
||||
else if(!(valueDt.istype(DataType.STR) && targetDt.istype(DataType.UWORD)))
|
||||
errors.err("value's type doesn't match target", assignment.value.position)
|
||||
}
|
||||
|
||||
@@ -454,15 +455,12 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(addressOf: AddressOf) {
|
||||
val variable=addressOf.identifier.targetVarDecl(program.namespace)
|
||||
if(variable==null)
|
||||
errors.err("pointer-of operand must be the name of a heap variable", addressOf.position)
|
||||
else {
|
||||
if(variable.datatype !in ArrayDatatypes
|
||||
&& variable.type!=VarDeclType.MEMORY
|
||||
&& variable.struct == null
|
||||
&& variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
|
||||
if(variable!=null
|
||||
&& variable.datatype !in ArrayDatatypes
|
||||
&& variable.type!=VarDeclType.MEMORY
|
||||
&& variable.struct == null
|
||||
&& variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
|
||||
errors.err("invalid pointer-of operand type", addressOf.position)
|
||||
}
|
||||
super.visit(addressOf)
|
||||
}
|
||||
|
||||
@@ -603,23 +601,28 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
// array length limits
|
||||
// array length limits and constant lenghts
|
||||
if(decl.isArray) {
|
||||
val length = decl.arraysize!!.constIndex() ?: 1
|
||||
when (decl.datatype) {
|
||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
if(length==0 || length>256)
|
||||
err("string and byte array length must be 1-256")
|
||||
val length = decl.arraysize!!.constIndex()
|
||||
if(length==null)
|
||||
err("array length must be a constant")
|
||||
else {
|
||||
when (decl.datatype) {
|
||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
if (length == 0 || length > 256)
|
||||
err("string and byte array length must be 1-256")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
if (length == 0 || length > 128)
|
||||
err("word array length must be 1-128")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
if (length == 0 || length > 51)
|
||||
err("float array length must be 1-51")
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
if(length==0 || length>128)
|
||||
err("word array length must be 1-128")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
if(length==0 || length>51)
|
||||
err("float array length must be 1-51")
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.functions.BuiltinFunctions
|
||||
|
||||
|
||||
internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
||||
@@ -91,41 +92,84 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
|
||||
}
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
// when using a simple bit shift and assigning it to a variable of a different type,
|
||||
// try to make the bit shifting 'wide enough' to fall into the variable's type.
|
||||
// with this, for instance, uword x = 1 << 10 will result in 1024 rather than 0 (the ubyte result).
|
||||
if(expr.operator=="<<" || expr.operator==">>") {
|
||||
val leftDt = expr.left.inferType(program)
|
||||
when (parent) {
|
||||
is Assignment -> {
|
||||
val targetDt = parent.target.inferType(program)
|
||||
if(leftDt != targetDt) {
|
||||
val cast = TypecastExpression(expr.left, targetDt.typeOrElse(DataType.STRUCT), true, parent.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
}
|
||||
}
|
||||
is VarDecl -> {
|
||||
if(!leftDt.istype(parent.datatype)) {
|
||||
val cast = TypecastExpression(expr.left, parent.datatype, true, parent.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
}
|
||||
}
|
||||
is IFunctionCall -> {
|
||||
val argnum = parent.args.indexOf(expr)
|
||||
when (val callee = parent.target.targetStatement(program.namespace)) {
|
||||
is Subroutine -> {
|
||||
val paramType = callee.parameters[argnum].type
|
||||
if(leftDt isAssignableTo paramType) {
|
||||
val cast = TypecastExpression(expr.left, paramType, true, parent.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
}
|
||||
}
|
||||
is BuiltinFunctionStatementPlaceholder -> {
|
||||
val func = BuiltinFunctions.getValue(callee.name)
|
||||
val paramTypes = func.parameters[argnum].possibleDatatypes
|
||||
for(type in paramTypes) {
|
||||
if(leftDt isAssignableTo type) {
|
||||
val cast = TypecastExpression(expr.left, type, true, parent.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw FatalAstException("weird callee")
|
||||
}
|
||||
}
|
||||
else -> return noModifications
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
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
|
||||
|
||||
@@ -41,8 +41,9 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
|
||||
|
||||
fun checkTypes(call: IFunctionCall, scope: INameScope, program: Program): String? {
|
||||
val argITypes = call.args.map { it.inferType(program) }
|
||||
if(argITypes.any { !it.isKnown })
|
||||
throw FatalAstException("unknown dt")
|
||||
val firstUnknownDt = argITypes.indexOfFirst { it.isUnknown }
|
||||
if(firstUnknownDt>=0)
|
||||
return "argument ${firstUnknownDt+1} invalid argument type"
|
||||
val argtypes = argITypes.map { it.typeOrElse(DataType.STRUCT) }
|
||||
val target = call.target.targetStatement(scope)
|
||||
if (target is Subroutine) {
|
||||
|
||||
@@ -200,7 +200,7 @@ open class VarDecl(val type: VarDeclType,
|
||||
fun defaultZero(dt: DataType, position: Position) = when(dt) {
|
||||
DataType.UBYTE -> NumericLiteralValue(DataType.UBYTE, 0, position)
|
||||
DataType.BYTE -> NumericLiteralValue(DataType.BYTE, 0, position)
|
||||
DataType.UWORD -> NumericLiteralValue(DataType.UWORD, 0, position)
|
||||
DataType.UWORD, DataType.STR -> NumericLiteralValue(DataType.UWORD, 0, position)
|
||||
DataType.WORD -> NumericLiteralValue(DataType.WORD, 0, position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, 0.0, position)
|
||||
else -> throw FatalAstException("can only determine default zero value for a numeric type")
|
||||
@@ -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,
|
||||
@@ -747,6 +757,8 @@ class Subroutine(override val name: String,
|
||||
|
||||
fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||
fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||
fun shouldPreserveStatusRegisterAfterCall() = asmReturnvaluesRegisters.any { it.statusflag != null } || shouldSaveX()
|
||||
fun shouldSaveX() = CpuRegister.X in asmClobbers || regXasResult() || regXasParam()
|
||||
|
||||
fun amountOfRtsInAsm(): Int = statements
|
||||
.asSequence()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,6 +8,7 @@ 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
|
||||
@@ -35,7 +36,7 @@ internal class AsmGen(private val program: Program,
|
||||
|
||||
// for expressions and augmented assignments:
|
||||
val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
|
||||
val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100)
|
||||
val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320)
|
||||
|
||||
private val assemblyLines = mutableListOf<String>()
|
||||
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
|
||||
@@ -159,6 +160,7 @@ internal class AsmGen(private val program: Program,
|
||||
val floatvalue = flt.key
|
||||
out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||
}
|
||||
out("prog8_program_end\t; end of program label for progend()")
|
||||
}
|
||||
|
||||
private fun block2asm(block: Block) {
|
||||
@@ -799,8 +801,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")
|
||||
}
|
||||
@@ -811,12 +815,12 @@ internal class AsmGen(private val program: Program,
|
||||
when (condition) {
|
||||
BranchCondition.CS -> "bcc"
|
||||
BranchCondition.CC -> "bcs"
|
||||
BranchCondition.EQ, BranchCondition.Z -> "beq"
|
||||
BranchCondition.NE, BranchCondition.NZ -> "bne"
|
||||
BranchCondition.EQ, BranchCondition.Z -> "bne"
|
||||
BranchCondition.NE, BranchCondition.NZ -> "beq"
|
||||
BranchCondition.VS -> "bvc"
|
||||
BranchCondition.VC -> "bvs"
|
||||
BranchCondition.MI, BranchCondition.NEG -> "bmi"
|
||||
BranchCondition.PL, BranchCondition.POS -> "bpl"
|
||||
BranchCondition.MI, BranchCondition.NEG -> "bpl"
|
||||
BranchCondition.PL, BranchCondition.POS -> "bmi"
|
||||
}
|
||||
} else {
|
||||
when (condition) {
|
||||
@@ -1068,26 +1072,48 @@ $counterVar .byte 0""")
|
||||
|
||||
val jump = stmt.truepart.statements.first() as? Jump
|
||||
if(jump!=null) {
|
||||
// branch with only a jump
|
||||
// branch with only a jump (goto)
|
||||
val instruction = branchInstruction(stmt.condition, false)
|
||||
out(" $instruction ${getJumpTarget(jump)}")
|
||||
translate(stmt.elsepart)
|
||||
} else {
|
||||
val truePartIsJustBreak = stmt.truepart.statements.firstOrNull() is Break
|
||||
val elsePartIsJustBreak = stmt.elsepart.statements.firstOrNull() is Break
|
||||
if(stmt.elsepart.containsNoCodeNorVars()) {
|
||||
if(truePartIsJustBreak) {
|
||||
// branch with just a break (jump out of loop)
|
||||
val instruction = branchInstruction(stmt.condition, false)
|
||||
val loopEndLabel = loopEndLabels.peek()
|
||||
out(" $instruction $loopEndLabel")
|
||||
} else {
|
||||
val instruction = branchInstruction(stmt.condition, true)
|
||||
val elseLabel = makeLabel("branch_else")
|
||||
out(" $instruction $elseLabel")
|
||||
translate(stmt.truepart)
|
||||
out(elseLabel)
|
||||
}
|
||||
}
|
||||
else if(truePartIsJustBreak) {
|
||||
// branch with just a break (jump out of loop)
|
||||
val instruction = branchInstruction(stmt.condition, false)
|
||||
val loopEndLabel = loopEndLabels.peek()
|
||||
out(" $instruction $loopEndLabel")
|
||||
translate(stmt.elsepart)
|
||||
} else if(elsePartIsJustBreak) {
|
||||
// branch with just a break (jump out of loop) but true/false inverted
|
||||
val instruction = branchInstruction(stmt.condition, true)
|
||||
val loopEndLabel = loopEndLabels.peek()
|
||||
out(" $instruction $loopEndLabel")
|
||||
translate(stmt.truepart)
|
||||
} else {
|
||||
val instruction = branchInstruction(stmt.condition, true)
|
||||
val elseLabel = makeLabel("branch_else")
|
||||
val endLabel = makeLabel("branch_end")
|
||||
out(" $instruction $elseLabel")
|
||||
translate(stmt.truepart)
|
||||
out(elseLabel)
|
||||
} else {
|
||||
val instruction = branchInstruction(stmt.condition, false)
|
||||
val trueLabel = makeLabel("branch_true")
|
||||
val endLabel = makeLabel("branch_end")
|
||||
out(" $instruction $trueLabel")
|
||||
translate(stmt.elsepart)
|
||||
out(" jmp $endLabel")
|
||||
out(trueLabel)
|
||||
translate(stmt.truepart)
|
||||
out(elseLabel)
|
||||
translate(stmt.elsepart)
|
||||
out(endLabel)
|
||||
}
|
||||
}
|
||||
@@ -1174,18 +1200,7 @@ $label nop""")
|
||||
}
|
||||
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 (add RegisterOrPair.FAC1/FAC2 ??)
|
||||
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
|
||||
@@ -1202,33 +1217,41 @@ $label nop""")
|
||||
assemblyLines.add(assembly)
|
||||
}
|
||||
|
||||
internal fun signExtendAYlsb(valueDt: DataType) {
|
||||
// sign extend signed byte in A to full word in AY
|
||||
when(valueDt) {
|
||||
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
|
||||
// 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("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
ora #$7f
|
||||
bmi +
|
||||
lda #0
|
||||
+ sta P8ESTACK_HI+1,x""")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
out(" stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
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")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
out(" stz $asmvar+1")
|
||||
else
|
||||
out(" lda #0 | sta $asmvar+1")
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
out("""
|
||||
lda $asmvar+1
|
||||
lda $asmvar
|
||||
ora #$7f
|
||||
bmi +
|
||||
lda #0
|
||||
|
||||
@@ -75,11 +75,11 @@ private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||
|
||||
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// the when statement (on bytes) generates a sequence of:
|
||||
// lda $ce01,x
|
||||
// cmp #$20
|
||||
// lda $ce01,x
|
||||
// cmp #$20
|
||||
// beq check_prog8_s72choice_32
|
||||
// lda $ce01,x
|
||||
// cmp #$21
|
||||
// lda $ce01,x
|
||||
// cmp #$21
|
||||
// beq check_prog8_s73choice_33
|
||||
// the repeated lda can be removed
|
||||
val mods = mutableListOf<Modification>()
|
||||
|
||||
@@ -5,10 +5,7 @@ import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.ArrayIndex
|
||||
import prog8.ast.statements.DirectMemoryWrite
|
||||
import prog8.ast.statements.FunctionCallStatement
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.CompilationTarget
|
||||
import prog8.compiler.target.Cx16Target
|
||||
@@ -17,6 +14,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
|
||||
|
||||
@@ -83,6 +81,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
"set_irqd" -> asmgen.out(" sei")
|
||||
"strlen" -> funcStrlen(fcall, resultToStack)
|
||||
"strcmp" -> funcStrcmp(fcall, func, resultToStack, scope)
|
||||
"strcopy" -> {
|
||||
translateArguments(fcall.args, func, scope)
|
||||
if(resultToStack)
|
||||
asmgen.out(" jsr prog8_lib.func_strcopy_to_stack")
|
||||
else
|
||||
asmgen.out(" jsr prog8_lib.func_strcopy")
|
||||
}
|
||||
"memcopy", "memset", "memsetw" -> funcMemSetCopy(fcall, func, scope)
|
||||
"substr", "leftstr", "rightstr" -> {
|
||||
translateArguments(fcall.args, func, scope)
|
||||
@@ -92,6 +97,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
translateArguments(fcall.args, func, scope)
|
||||
asmgen.out(" jmp prog8_lib.func_exit")
|
||||
}
|
||||
"progend" -> {
|
||||
if(resultToStack)
|
||||
asmgen.out("""
|
||||
lda #<prog8_program_end
|
||||
sta P8ESTACK_LO,x
|
||||
lda #>prog8_program_end
|
||||
sta P8ESTACK_HI,x
|
||||
dex""")
|
||||
else
|
||||
asmgen.out(" lda #<prog8_program_end | ldy #>prog8_program_end")
|
||||
}
|
||||
else -> TODO("missing asmgen for builtin func ${func.name}")
|
||||
}
|
||||
}
|
||||
@@ -531,14 +547,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
|
||||
private fun funcStrlen(fcall: IFunctionCall, resultToStack: Boolean) {
|
||||
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
|
||||
val type = fcall.args[0].inferType(program)
|
||||
when {
|
||||
type.istype(DataType.STR) -> asmgen.out(" lda #<$name | ldy #>$name")
|
||||
type.istype(DataType.UWORD) -> asmgen.out(" lda $name | ldy $name+1")
|
||||
else -> throw AssemblyError("strlen requires str or uword arg")
|
||||
if (fcall.args[0] is IdentifierReference) {
|
||||
// use the address of the variable
|
||||
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
|
||||
val type = fcall.args[0].inferType(program)
|
||||
when {
|
||||
type.istype(DataType.STR) -> asmgen.out(" lda #<$name | ldy #>$name")
|
||||
type.istype(DataType.UWORD) -> asmgen.out(" lda $name | ldy $name+1")
|
||||
else -> throw AssemblyError("strlen requires str or uword arg")
|
||||
}
|
||||
}
|
||||
if(resultToStack)
|
||||
else {
|
||||
// use the expression value as address of the string
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
}
|
||||
if (resultToStack)
|
||||
asmgen.out(" jsr prog8_lib.func_strlen_stack")
|
||||
else
|
||||
asmgen.out(" jsr prog8_lib.func_strlen_into_A")
|
||||
@@ -1032,8 +1055,8 @@ 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)
|
||||
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)
|
||||
|
||||
@@ -9,6 +9,7 @@ import prog8.ast.statements.Subroutine
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.CompilationTarget
|
||||
import prog8.compiler.target.CpuType
|
||||
import prog8.compiler.target.subroutineFloatEvalResultVar1
|
||||
import prog8.compiler.toHex
|
||||
import prog8.functions.BuiltinFunctions
|
||||
import kotlin.math.absoluteValue
|
||||
@@ -215,11 +216,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
jsr floats.vars_less_f
|
||||
beq $jumpIfFalseLabel""")
|
||||
} else {
|
||||
if (asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (e1): '<' at ${left.position}") // TODO float via func args?
|
||||
translateExpression(left)
|
||||
translateExpression(right)
|
||||
asmgen.out(" jsr floats.less_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
||||
val subroutine = left.definingSubroutine()!!
|
||||
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
|
||||
asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||
asmgen.out("""
|
||||
lda #<$subroutineFloatEvalResultVar1
|
||||
ldy #>$subroutineFloatEvalResultVar1
|
||||
jsr floats.var_fac1_less_f
|
||||
beq $jumpIfFalseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,11 +261,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
jsr floats.vars_lesseq_f
|
||||
beq $jumpIfFalseLabel""")
|
||||
} else {
|
||||
if (asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (e1): '<=' at ${left.position}") // TODO float via func args?
|
||||
translateExpression(left)
|
||||
translateExpression(right)
|
||||
asmgen.out(" jsr floats.lesseq_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
||||
val subroutine = left.definingSubroutine()!!
|
||||
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
|
||||
asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||
asmgen.out("""
|
||||
lda #<$subroutineFloatEvalResultVar1
|
||||
ldy #>$subroutineFloatEvalResultVar1
|
||||
jsr floats.var_fac1_lesseq_f
|
||||
beq $jumpIfFalseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,11 +306,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
jsr floats.vars_less_f
|
||||
beq $jumpIfFalseLabel""")
|
||||
} else {
|
||||
if (asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (e1): '>' at ${left.position}") // TODO float via func args?
|
||||
translateExpression(left)
|
||||
translateExpression(right)
|
||||
asmgen.out(" jsr floats.greater_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
||||
val subroutine = left.definingSubroutine()!!
|
||||
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
|
||||
asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||
asmgen.out("""
|
||||
lda #<$subroutineFloatEvalResultVar1
|
||||
ldy #>$subroutineFloatEvalResultVar1
|
||||
jsr floats.var_fac1_greater_f
|
||||
beq $jumpIfFalseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,11 +351,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
jsr floats.vars_lesseq_f
|
||||
beq $jumpIfFalseLabel""")
|
||||
} else {
|
||||
if (asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (e1): '>=' at ${left.position}") // TODO float via func args
|
||||
translateExpression(left)
|
||||
translateExpression(right)
|
||||
asmgen.out(" jsr floats.greatereq_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
||||
val subroutine = left.definingSubroutine()!!
|
||||
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
|
||||
asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||
asmgen.out("""
|
||||
lda #<$subroutineFloatEvalResultVar1
|
||||
ldy #>$subroutineFloatEvalResultVar1
|
||||
jsr floats.var_fac1_greatereq_f
|
||||
beq $jumpIfFalseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,8 +789,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
}
|
||||
}
|
||||
|
||||
asmgen.assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
asmgen.assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@@ -899,8 +916,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
}
|
||||
}
|
||||
|
||||
asmgen.assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
asmgen.assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@@ -1115,11 +1132,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
jsr floats.vars_equal_f
|
||||
beq $jumpIfFalseLabel""")
|
||||
} else {
|
||||
if (asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (e22): '==' at ${left.position}") // TODO float via func args?
|
||||
translateExpression(left)
|
||||
translateExpression(right)
|
||||
asmgen.out(" jsr floats.equal_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
||||
val subroutine = left.definingSubroutine()!!
|
||||
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
|
||||
asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||
asmgen.out("""
|
||||
lda #<$subroutineFloatEvalResultVar1
|
||||
ldy #>$subroutineFloatEvalResultVar1
|
||||
jsr floats.var_fac1_notequal_f
|
||||
bne $jumpIfFalseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1196,11 +1217,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
jsr floats.vars_equal_f
|
||||
bne $jumpIfFalseLabel""")
|
||||
} else {
|
||||
if (asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (e23): '!=' at ${left.position}") // TODO float via func args?
|
||||
translateExpression(left)
|
||||
translateExpression(right)
|
||||
asmgen.out(" jsr floats.notequal_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
||||
val subroutine = left.definingSubroutine()!!
|
||||
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
|
||||
asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||
asmgen.out("""
|
||||
lda #<$subroutineFloatEvalResultVar1
|
||||
ldy #>$subroutineFloatEvalResultVar1
|
||||
jsr floats.var_fac1_notequal_f
|
||||
beq $jumpIfFalseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1304,7 +1329,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
asmgen.translateBuiltinFunctionCallExpression(expression, builtinFunc, true)
|
||||
} else {
|
||||
sub as Subroutine
|
||||
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any {it.statusflag!=null}
|
||||
val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall()
|
||||
asmgen.translateFunctionCall(expression, preserveStatusRegisterAfterCall)
|
||||
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
||||
for ((_, reg) in returns) {
|
||||
@@ -1339,6 +1364,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_LO,x")
|
||||
asmgen.out(" tya | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.push_fac1")
|
||||
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2")
|
||||
}
|
||||
}
|
||||
else if(reg.statusflag!=null) {
|
||||
@@ -1592,9 +1619,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
}
|
||||
}
|
||||
">>" -> {
|
||||
translateExpression(expr.left)
|
||||
val amount = expr.right.constValue(program)?.number?.toInt()
|
||||
if(amount!=null) {
|
||||
translateExpression(expr.left)
|
||||
when (leftDt) {
|
||||
DataType.UBYTE -> {
|
||||
if (amount <= 2)
|
||||
@@ -1615,6 +1642,13 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(amount>=16) {
|
||||
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
|
||||
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
|
||||
return
|
||||
}
|
||||
var left = amount
|
||||
while (left >= 7) {
|
||||
asmgen.out(" jsr math.shift_right_uw_7")
|
||||
@@ -1626,6 +1660,20 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
asmgen.out(" jsr math.shift_right_uw_$left")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(amount>=16) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_HI+1,x
|
||||
bmi +
|
||||
lda #0
|
||||
sta P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
beq ++
|
||||
+ lda #255
|
||||
sta P8ESTACK_LO+1,x
|
||||
sta P8ESTACK_HI+1,x
|
||||
+""")
|
||||
return
|
||||
}
|
||||
var left = amount
|
||||
while (left >= 7) {
|
||||
asmgen.out(" jsr math.shift_right_w_7")
|
||||
@@ -1642,9 +1690,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
}
|
||||
}
|
||||
"<<" -> {
|
||||
translateExpression(expr.left)
|
||||
val amount = expr.right.constValue(program)?.number?.toInt()
|
||||
if(amount!=null) {
|
||||
translateExpression(expr.left)
|
||||
if (leftDt in ByteDatatypes) {
|
||||
if (amount <= 2)
|
||||
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
|
||||
@@ -1899,24 +1947,29 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateBinaryOperatorWords(operator: String, types: DataType) {
|
||||
private fun translateBinaryOperatorWords(operator: String, dt: DataType) {
|
||||
when(operator) {
|
||||
"**" -> throw AssemblyError("** operator requires floats")
|
||||
"*" -> asmgen.out(" jsr prog8_lib.mul_word")
|
||||
"/" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
|
||||
"/" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
|
||||
"%" -> {
|
||||
if(types==DataType.WORD)
|
||||
if(dt==DataType.WORD)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
asmgen.out(" jsr prog8_lib.remainder_uw")
|
||||
}
|
||||
"+" -> asmgen.out(" jsr prog8_lib.add_w")
|
||||
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
|
||||
"<<" -> throw AssemblyError("<< should not operate via stack")
|
||||
">>" -> throw AssemblyError(">> should not operate via stack")
|
||||
"<" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
|
||||
">" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
|
||||
"<=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
|
||||
">=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
|
||||
"<<" -> asmgen.out(" jsr math.shift_left_w")
|
||||
">>" -> {
|
||||
if(dt==DataType.UWORD)
|
||||
asmgen.out(" jsr math.shift_right_uw")
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_w")
|
||||
}
|
||||
"<" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
|
||||
">" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
|
||||
"<=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
|
||||
">=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
|
||||
"==" -> asmgen.out(" jsr prog8_lib.equal_w")
|
||||
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w") "&" -> asmgen.out(" jsr prog8_lib.bitand_w")
|
||||
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
|
||||
|
||||
@@ -16,11 +16,11 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
|
||||
internal fun translateFunctionCallStatement(stmt: IFunctionCall) {
|
||||
val sub = stmt.target.targetSubroutine(program.namespace)!!
|
||||
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any {it.statusflag!=null}
|
||||
val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall()
|
||||
translateFunctionCall(stmt, preserveStatusRegisterAfterCall)
|
||||
// functioncalls no longer return results on the stack, so simply ignore the results in the registers
|
||||
if(preserveStatusRegisterAfterCall)
|
||||
asmgen.out(" plp\t; restore status flags from call")
|
||||
asmgen.out(" plp") // restore status flags from call
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
// output the code to setup the parameters and perform the actual call
|
||||
// does NOT output the code to deal with the result values!
|
||||
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||
val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult() || sub.regXasParam()
|
||||
val saveX = sub.shouldSaveX()
|
||||
if(saveX)
|
||||
asmgen.saveRegister(CpuRegister.X, preserveStatusRegisterAfterCall, (stmt as Node).definingSubroutine()!!)
|
||||
|
||||
@@ -69,7 +69,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
asmgen.out(" jsr $subName")
|
||||
|
||||
if(preserveStatusRegisterAfterCall) {
|
||||
asmgen.out(" php\t; save status flags from call")
|
||||
asmgen.out(" php") // save status flags from call
|
||||
// note: the containing statement (such as the FunctionCallStatement or the Assignment or the Expression)
|
||||
// must take care of popping this value again at the end!
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
assignMemoryByte(assign.target, null, value.addressExpression as IdentifierReference)
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionToVariable(value.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope)
|
||||
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)
|
||||
}
|
||||
@@ -138,11 +138,11 @@ 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 -> {
|
||||
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any { it.statusflag != null }
|
||||
val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall()
|
||||
asmgen.translateFunctionCall(value, preserveStatusRegisterAfterCall)
|
||||
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.registerOrPair!=null }
|
||||
when (returnValue.first) {
|
||||
@@ -183,7 +183,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
if (preserveStatusRegisterAfterCall)
|
||||
asmgen.out(" plp\t; restore status flags from call")
|
||||
asmgen.out(" plp") // restore status flags from call
|
||||
}
|
||||
is BuiltinFunctionStatementPlaceholder -> {
|
||||
val signature = BuiltinFunctions.getValue(sub.name)
|
||||
@@ -211,6 +211,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
// 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)
|
||||
@@ -227,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) {
|
||||
@@ -262,37 +269,62 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
else -> {}
|
||||
}
|
||||
|
||||
when(value) {
|
||||
is IdentifierReference -> {
|
||||
if(target.kind==TargetStorageKind.VARIABLE) {
|
||||
val sourceDt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
if (sourceDt != DataType.STRUCT)
|
||||
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), sourceDt, origAssign.source.expression!!)
|
||||
|
||||
// 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")
|
||||
}
|
||||
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: $value into $targetDt at ${value.position}")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// give up, do it via eval stack
|
||||
asmgen.translateExpression(origAssign.source.expression!!)
|
||||
// TODO optimize typecasts for more special cases?
|
||||
// note: cannot use assignTypeCastedValue because that is ourselves :P
|
||||
if(this.asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used for typecast: $value into $targetDt (target=${target.kind} at ${value.position}")
|
||||
asmgen.translateExpression(origTypeCastExpression) // this performs the actual type cast in translateExpression(Typecast)
|
||||
assignStackValue(target)
|
||||
}
|
||||
|
||||
private fun assignTypecastedFloatFAC1(targetAsmVarName: String, targetDt: DataType) {
|
||||
|
||||
if(targetDt==DataType.FLOAT)
|
||||
throw AssemblyError("typecast to identical type")
|
||||
|
||||
when(targetDt) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr floats.cast_FAC1_as_uw_into_ya | sty $targetAsmVarName")
|
||||
DataType.BYTE -> asmgen.out(" jsr floats.cast_FAC1_as_w_into_ay | sta $targetAsmVarName")
|
||||
DataType.UWORD -> asmgen.out(" jsr floats.cast_FAC1_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
|
||||
DataType.WORD -> asmgen.out(" jsr floats.cast_FAC1_as_w_into_ay | sta $targetAsmVarName | sty $targetAsmVarName+1")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun assignTypeCastedIdentifier(targetAsmVarName: String, targetDt: DataType,
|
||||
sourceAsmVarName: String, sourceDt: DataType,
|
||||
origExpr: Expression) {
|
||||
sourceAsmVarName: String, sourceDt: DataType) {
|
||||
if(sourceDt == targetDt)
|
||||
throw AssemblyError("typecast to identical value")
|
||||
throw AssemblyError("typecast to identical type")
|
||||
|
||||
// also see: ExpressionAsmGen, fun translateExpression(typecast: TypecastExpression)
|
||||
when(sourceDt) {
|
||||
@@ -324,7 +356,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
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)
|
||||
}
|
||||
@@ -383,13 +421,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
lda #<$targetAsmVarName
|
||||
ldy #>$targetAsmVarName
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda #<$sourceAsmVarName
|
||||
ldy #>$sourceAsmVarName""")
|
||||
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")
|
||||
@@ -401,6 +433,146 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
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")
|
||||
}
|
||||
@@ -527,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")
|
||||
}
|
||||
}
|
||||
@@ -545,10 +725,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
throw AssemblyError("no asm gen for assign address $sourceName to memory word $target")
|
||||
throw AssemblyError("can't store word into memory byte")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
throw AssemblyError("no asm gen for assign address $sourceName to array ${target.asmVarname}")
|
||||
asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
TargetStorageKind.REGISTER -> {
|
||||
when(target.register!!) {
|
||||
@@ -575,10 +756,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
when(target.datatype) {
|
||||
DataType.UWORD -> {
|
||||
asmgen.out("""
|
||||
lda #<$sourceName
|
||||
ldy #>$sourceName
|
||||
sta ${target.asmVarname}
|
||||
sty ${target.asmVarname}+1
|
||||
lda #<$sourceName
|
||||
ldy #>$sourceName
|
||||
sta ${target.asmVarname}
|
||||
sty ${target.asmVarname}+1
|
||||
""")
|
||||
}
|
||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
@@ -702,7 +883,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("""
|
||||
@@ -729,7 +910,10 @@ 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")
|
||||
}
|
||||
}
|
||||
@@ -762,7 +946,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(" 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")
|
||||
}
|
||||
}
|
||||
@@ -803,7 +993,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")
|
||||
}
|
||||
}
|
||||
@@ -837,6 +1033,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 -> {
|
||||
@@ -914,21 +1111,28 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
val sourceName = asmgen.asmVariableName(bytevar)
|
||||
when(wordtarget.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out("""
|
||||
lda $sourceName
|
||||
sta ${wordtarget.asmVarname}
|
||||
lda #0
|
||||
sta ${wordtarget.asmVarname}+1
|
||||
""")
|
||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz ${wordtarget.asmVarname}+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if (wordtarget.constArrayIndexValue!=null) {
|
||||
val scaledIdx = wordtarget.constArrayIndexValue!! * 2
|
||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx | lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||
}
|
||||
else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array!!, wordtarget.datatype, CpuRegister.Y)
|
||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname},y | lda #0 | iny | sta ${wordtarget.asmVarname},y")
|
||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname},y | iny")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz ${wordtarget.asmVarname},y")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname},y")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.REGISTER -> {
|
||||
@@ -940,18 +1144,17 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
TargetStorageKind.STACK -> {
|
||||
asmgen.out("""
|
||||
lda $sourceName
|
||||
sta P8ESTACK_LO,x
|
||||
lda #0
|
||||
sta P8ESTACK_HI,x
|
||||
dex""")
|
||||
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz P8ESTACK_HI,x | dex")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
else -> throw AssemblyError("target type isn't word")
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
|
||||
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
|
||||
require(target.datatype in ByteDatatypes)
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
@@ -961,22 +1164,20 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
storeRegisterInMemoryAddress(register, target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
when {
|
||||
target.constArrayIndexValue!=null -> {
|
||||
when (register) {
|
||||
CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||
CpuRegister.X -> asmgen.out(" stx ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||
CpuRegister.Y -> asmgen.out(" sty ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||
}
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
when (register) {
|
||||
CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||
CpuRegister.X -> asmgen.out(" stx ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||
CpuRegister.Y -> asmgen.out(" sty ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||
}
|
||||
else -> {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
asmgen.out(" ldy ${asmgen.asmVariableName(target.array!!.indexer.indexVar!!)} | sta ${target.asmVarname},y")
|
||||
}
|
||||
else {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
asmgen.out(" ldy ${asmgen.asmVariableName(target.array!!.indexer.indexVar!!)} | sta ${target.asmVarname},y")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.REGISTER -> {
|
||||
@@ -988,6 +1189,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") }
|
||||
@@ -996,6 +1198,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") }
|
||||
@@ -1004,6 +1207,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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1017,7 +1221,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")
|
||||
@@ -1032,20 +1236,30 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
// TODO can be a bit more optimized if the array indexer is a number
|
||||
when(regs) {
|
||||
RegisterOrPair.AX -> asmgen.out(" pha | txa | pha")
|
||||
RegisterOrPair.AY -> asmgen.out(" pha | tya | pha")
|
||||
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
|
||||
else -> throw AssemblyError("expected reg pair")
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
val idx = target.constArrayIndexValue!! * 2
|
||||
when (regs) {
|
||||
RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname}+$idx | stx ${target.asmVarname}+$idx+1")
|
||||
RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
|
||||
RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
|
||||
else -> throw AssemblyError("expected reg pair")
|
||||
}
|
||||
}
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y, true)
|
||||
asmgen.out("""
|
||||
pla
|
||||
sta ${target.asmVarname},y
|
||||
dey
|
||||
pla
|
||||
sta ${target.asmVarname},y""")
|
||||
else {
|
||||
when(regs) {
|
||||
RegisterOrPair.AX -> asmgen.out(" pha | txa | pha")
|
||||
RegisterOrPair.AY -> asmgen.out(" pha | tya | pha")
|
||||
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
|
||||
else -> throw AssemblyError("expected reg pair")
|
||||
}
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y, true)
|
||||
asmgen.out("""
|
||||
pla
|
||||
sta ${target.asmVarname},y
|
||||
dey
|
||||
pla
|
||||
sta ${target.asmVarname},y""")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.REGISTER -> {
|
||||
when(regs) {
|
||||
@@ -1156,6 +1370,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("""
|
||||
@@ -1192,14 +1407,23 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if (target.array!!.indexer.indexNum!=null) {
|
||||
val indexValue = target.array.indexer.constIndex()!! * DataType.FLOAT.memorySize()
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
sta ${target.asmVarname}+$indexValue
|
||||
sta ${target.asmVarname}+$indexValue+1
|
||||
sta ${target.asmVarname}+$indexValue+2
|
||||
sta ${target.asmVarname}+$indexValue+3
|
||||
sta ${target.asmVarname}+$indexValue+4
|
||||
""")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out("""
|
||||
stz ${target.asmVarname}+$indexValue
|
||||
stz ${target.asmVarname}+$indexValue+1
|
||||
stz ${target.asmVarname}+$indexValue+2
|
||||
stz ${target.asmVarname}+$indexValue+3
|
||||
stz ${target.asmVarname}+$indexValue+4
|
||||
""")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
sta ${target.asmVarname}+$indexValue
|
||||
sta ${target.asmVarname}+$indexValue+1
|
||||
sta ${target.asmVarname}+$indexValue+2
|
||||
sta ${target.asmVarname}+$indexValue+3
|
||||
sta ${target.asmVarname}+$indexValue+4
|
||||
""")
|
||||
} else {
|
||||
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
|
||||
asmgen.out("""
|
||||
@@ -1213,7 +1437,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")
|
||||
@@ -1270,7 +1501,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")
|
||||
@@ -1301,6 +1539,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("""
|
||||
@@ -1331,6 +1570,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 -> {
|
||||
@@ -1345,12 +1585,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
if (address != null) {
|
||||
when(wordtarget.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out("""
|
||||
lda ${address.toHex()}
|
||||
sta ${wordtarget.asmVarname}
|
||||
lda #0
|
||||
sta ${wordtarget.asmVarname}+1
|
||||
""")
|
||||
asmgen.out(" lda ${address.toHex()} | sta ${wordtarget.asmVarname}")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz ${wordtarget.asmVarname}+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
throw AssemblyError("no asm gen for assign memory byte at $address to array ${wordtarget.asmVarname}")
|
||||
@@ -1362,12 +1601,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
else -> throw AssemblyError("word regs can only be pair")
|
||||
}
|
||||
TargetStorageKind.STACK -> {
|
||||
asmgen.out("""
|
||||
lda ${address.toHex()}
|
||||
sta P8ESTACK_LO,x
|
||||
lda #0
|
||||
sta P8ESTACK_HI,x
|
||||
dex""")
|
||||
asmgen.out(" lda ${address.toHex()} | sta P8ESTACK_LO,x")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz P8ESTACK_HI,x | dex")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
else -> throw AssemblyError("other types aren't word")
|
||||
}
|
||||
@@ -1375,7 +1613,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
when(wordtarget.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.loadByteFromPointerIntoA(identifier)
|
||||
asmgen.out(" sta ${wordtarget.asmVarname} | lda #0 | sta ${wordtarget.asmVarname}+1")
|
||||
asmgen.out(" sta ${wordtarget.asmVarname}")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz ${wordtarget.asmVarname}+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
throw AssemblyError("no asm gen for assign memory byte $identifier to array ${wordtarget.asmVarname} ")
|
||||
@@ -1391,7 +1633,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
TargetStorageKind.STACK -> {
|
||||
asmgen.loadByteFromPointerIntoA(identifier)
|
||||
asmgen.out(" sta P8ESTACK_LO,x | lda #0 | sta P8ESTACK_HI,x | dex")
|
||||
asmgen.out(" sta P8ESTACK_LO,x")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz P8ESTACK_HI,x | dex")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
else -> throw AssemblyError("other types aren't word")
|
||||
}
|
||||
@@ -1403,13 +1649,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
val addressLv = addressExpr as? NumericLiteralValue
|
||||
when {
|
||||
addressLv != null -> {
|
||||
asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}")
|
||||
asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}")
|
||||
}
|
||||
addressExpr is IdentifierReference -> {
|
||||
asmgen.storeByteIntoPointer(addressExpr, ldaInstructionArg)
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
asmgen.out(" ldy #0 | lda $ldaInstructionArg | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
@@ -1434,7 +1680,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
else -> {
|
||||
asmgen.saveRegister(register, false, memoryAddress.definingSubroutine()!!)
|
||||
asmgen.assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
asmgen.restoreRegister(CpuRegister.A, false)
|
||||
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
|
||||
+318
-261
@@ -7,6 +7,7 @@ import prog8.ast.statements.Subroutine
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.CompilationTarget
|
||||
import prog8.compiler.target.CpuType
|
||||
import prog8.compiler.target.Cx16Target
|
||||
import prog8.compiler.target.c64.codegen.AsmGen
|
||||
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
|
||||
import prog8.compiler.toHex
|
||||
@@ -160,7 +161,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)
|
||||
@@ -173,7 +174,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
when {
|
||||
valueLv != null -> inplaceModification_byte_litval_to_pointer(pointer, operator, valueLv.toInt())
|
||||
ident != null -> inplaceModification_byte_variable_to_pointer(pointer, operator, ident)
|
||||
// TODO more specialized code for types such as memory read etc.
|
||||
value is TypecastExpression -> {
|
||||
if (tryRemoveRedundantCast(value, target, operator)) return
|
||||
inplaceModification_byte_value_to_pointer(pointer, operator, value)
|
||||
@@ -182,29 +182,92 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
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) // TODO directly into P8ZP_SCRATCH_W2?
|
||||
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")
|
||||
@@ -228,26 +291,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
|
||||
private fun inplaceModification_byte_value_to_pointer(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)
|
||||
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,28 +311,23 @@ 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")
|
||||
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
|
||||
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
|
||||
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
|
||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
||||
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_pointer(pointervar: IdentifierReference, operator: String, value: IdentifierReference) {
|
||||
@@ -308,9 +359,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
bne -
|
||||
+""")
|
||||
}
|
||||
"&" -> asmgen.out(" and $otherName")
|
||||
"^" -> asmgen.out(" eor $otherName")
|
||||
"|" -> asmgen.out(" ora $otherName")
|
||||
"&", "and" -> asmgen.out(" and $otherName")
|
||||
"|", "or" -> asmgen.out(" ora $otherName")
|
||||
"^", "xor" -> asmgen.out(" eor $otherName")
|
||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
if(ptrOnZp)
|
||||
@@ -389,7 +441,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
}
|
||||
"&" -> {
|
||||
"&", "and" -> {
|
||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" and #$value")
|
||||
if(ptrOnZp)
|
||||
@@ -397,15 +449,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
else
|
||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
"^" -> {
|
||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" eor #$value")
|
||||
if(ptrOnZp)
|
||||
asmgen.out(" sta ($sourceName),y")
|
||||
else
|
||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
"|" -> {
|
||||
"|", "or" -> {
|
||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" ora #$value")
|
||||
if(ptrOnZp)
|
||||
@@ -413,6 +457,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
else
|
||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
"^", "xor" -> {
|
||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" eor #$value")
|
||||
if(ptrOnZp)
|
||||
asmgen.out(" sta ($sourceName),y")
|
||||
else
|
||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
@@ -476,18 +529,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
+""")
|
||||
}
|
||||
}
|
||||
"&" -> {
|
||||
"&", "and" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" and $name | sta $name")
|
||||
}
|
||||
"^" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" eor $name | sta $name")
|
||||
}
|
||||
"|" -> {
|
||||
"|", "or" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" ora $name | sta $name")
|
||||
}
|
||||
"^", "xor" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" eor $name | sta $name")
|
||||
}
|
||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
@@ -542,27 +596,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
+""")
|
||||
}
|
||||
}
|
||||
"&" -> asmgen.out(" lda $name | and $otherName | sta $name")
|
||||
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name")
|
||||
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name")
|
||||
"and" -> asmgen.out("""
|
||||
lda $name
|
||||
and $otherName
|
||||
beq +
|
||||
lda #1
|
||||
+ sta $name""")
|
||||
"or" -> asmgen.out("""
|
||||
lda $name
|
||||
ora $otherName
|
||||
beq +
|
||||
lda #1
|
||||
+ sta $name""")
|
||||
"xor" -> asmgen.out("""
|
||||
lda $name
|
||||
eor $otherName
|
||||
beq +
|
||||
lda #1
|
||||
+ sta $name""")
|
||||
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name")
|
||||
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name")
|
||||
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name")
|
||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
@@ -594,14 +631,24 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
sta $name""")
|
||||
}
|
||||
"<<" -> {
|
||||
if(value>=8) asmgen.out(" lda #0 | sta $name")
|
||||
else repeat(value) { asmgen.out(" asl $name") }
|
||||
if(value>=8) {
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz $name")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name")
|
||||
}
|
||||
else repeat(value) { asmgen.out(" asl $name") }
|
||||
}
|
||||
">>" -> {
|
||||
if(value>0) {
|
||||
if (dt == DataType.UBYTE) {
|
||||
if(value>=8) asmgen.out(" lda #0 | sta $name")
|
||||
else repeat(value) { asmgen.out(" lsr $name") }
|
||||
if(value>=8) {
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz $name")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name")
|
||||
}
|
||||
else repeat(value) { asmgen.out(" lsr $name") }
|
||||
} else {
|
||||
when {
|
||||
value>=8 -> asmgen.out("""
|
||||
@@ -616,14 +663,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
ldy #$value
|
||||
jsr math.lsr_byte_A
|
||||
sta $name""")
|
||||
else -> repeat(value) { asmgen.out(" lda $name | asl a | ror $name") }
|
||||
else -> repeat(value) { asmgen.out(" lda $name | asl a | ror $name") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"&" -> asmgen.out(" lda $name | and #$value | sta $name")
|
||||
"^" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||
"|" -> asmgen.out(" lda $name | ora #$value | sta $name")
|
||||
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
|
||||
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
|
||||
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
@@ -809,9 +857,20 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
"<<" -> {
|
||||
when {
|
||||
value>=16 -> asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||
value==8 -> asmgen.out(" lda $name | sta $name+1 | lda #0 | sta $name")
|
||||
value>2 -> asmgen.out("""
|
||||
value>=16 -> {
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz $name | stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||
}
|
||||
value==8 -> {
|
||||
asmgen.out(" lda $name | sta $name+1")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz $name")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name")
|
||||
}
|
||||
value>3 -> asmgen.out("""
|
||||
ldy #$value
|
||||
- asl $name
|
||||
rol $name+1
|
||||
@@ -825,8 +884,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
if (value > 0) {
|
||||
if(dt==DataType.UWORD) {
|
||||
when {
|
||||
value>=16 -> asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||
value==8 -> asmgen.out(" lda $name+1 | sta $name | lda #0 | sta $name+1")
|
||||
value>=16 -> {
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz $name | stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||
}
|
||||
value==8 -> {
|
||||
asmgen.out(" lda $name+1 | sta $name")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name+1")
|
||||
}
|
||||
value>2 -> asmgen.out("""
|
||||
ldy #$value
|
||||
- lsr $name+1
|
||||
@@ -868,47 +938,48 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
}
|
||||
"&" -> {
|
||||
"&", "and" -> {
|
||||
when {
|
||||
value == 0 -> {
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz $name | stz $name+1")
|
||||
asmgen.out(" stz $name | stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||
asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||
}
|
||||
value and 255 == 0 -> {
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz $name")
|
||||
asmgen.out(" stz $name")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name")
|
||||
asmgen.out(" lda $name+1 | and #>$value | sta $name+1")
|
||||
asmgen.out(" lda #0 | sta $name")
|
||||
asmgen.out(" lda $name+1 | and #>$value | sta $name+1")
|
||||
}
|
||||
value < 0x0100 -> {
|
||||
asmgen.out(" lda $name | and #$value | sta $name")
|
||||
asmgen.out(" lda $name | and #$value | sta $name")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name+1")
|
||||
}
|
||||
else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1")
|
||||
else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1")
|
||||
}
|
||||
}
|
||||
"^" -> {
|
||||
"|", "or" -> {
|
||||
when {
|
||||
value == 0 -> {}
|
||||
value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1")
|
||||
value < 0x0100 -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||
else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1")
|
||||
value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1")
|
||||
value < 0x0100 -> asmgen.out(" lda $name | ora #$value | sta $name")
|
||||
else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1")
|
||||
}
|
||||
}
|
||||
"|" -> {
|
||||
"^", "xor" -> {
|
||||
when {
|
||||
value == 0 -> {}
|
||||
value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1")
|
||||
value < 0x0100 -> asmgen.out(" lda $name | ora #$value | sta $name")
|
||||
else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1")
|
||||
value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1")
|
||||
value < 0x0100 -> asmgen.out(" lda $name | eor #$value | sta $name")
|
||||
else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1")
|
||||
}
|
||||
}
|
||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
@@ -970,11 +1041,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
sta $name+1""")
|
||||
}
|
||||
"*" -> {
|
||||
asmgen.out(" lda $otherName | sta P8ZP_SCRATCH_W1")
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz P8ZP_SCRATCH_W1+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out("""
|
||||
lda $otherName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #0
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda $name
|
||||
ldy $name+1
|
||||
jsr math.multiply_words
|
||||
@@ -1018,13 +1090,18 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
+""")
|
||||
}
|
||||
}
|
||||
"&" -> {
|
||||
asmgen.out(" lda $otherName | and $name | sta $name")
|
||||
if(dt in WordDatatypes)
|
||||
asmgen.out(" lda #0 | sta $name+1")
|
||||
"&", "and" -> {
|
||||
asmgen.out(" lda $otherName | and $name | sta $name")
|
||||
if(dt in WordDatatypes) {
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name+1")
|
||||
}
|
||||
}
|
||||
"^" -> asmgen.out(" lda $otherName | eor $name | sta $name")
|
||||
"|" -> asmgen.out(" lda $otherName | ora $name | sta $name")
|
||||
"|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name")
|
||||
"^", "xor" -> asmgen.out(" lda $otherName | eor $name | sta $name")
|
||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
@@ -1032,8 +1109,8 @@ 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 $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
|
||||
"-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1")
|
||||
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
|
||||
"-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1")
|
||||
"*" -> {
|
||||
asmgen.out("""
|
||||
lda $otherName
|
||||
@@ -1095,9 +1172,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
""")
|
||||
}
|
||||
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
|
||||
"&" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
|
||||
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
|
||||
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
|
||||
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
|
||||
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
|
||||
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
|
||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
@@ -1116,11 +1194,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
throw AssemblyError("unknown dt")
|
||||
val valueDt = valueiDt.typeOrElse(DataType.STRUCT)
|
||||
|
||||
// TODO can use registers instead of stack value?
|
||||
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
|
||||
@@ -1133,51 +1208,36 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
""")
|
||||
}
|
||||
|
||||
// TODO can use registers instead of stack value?
|
||||
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")
|
||||
}
|
||||
|
||||
// TODO can use registers instead of stack value?
|
||||
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
|
||||
""")
|
||||
}
|
||||
|
||||
@@ -1187,14 +1247,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
when (operator) {
|
||||
// note: ** (power) operator requires floats.
|
||||
"+" -> {
|
||||
if(asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||
asmgen.translateExpression(value)
|
||||
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
|
||||
@@ -1202,7 +1260,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
|
||||
@@ -1211,63 +1269,53 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
tya
|
||||
adc $name+1
|
||||
sta $name+1""")
|
||||
asmgen.out(" inx")
|
||||
}
|
||||
"-" -> {
|
||||
if(asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||
asmgen.translateExpression(value)
|
||||
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
|
||||
sta $name+1""")
|
||||
asmgen.out(" inx")
|
||||
}
|
||||
"*" -> {
|
||||
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
|
||||
if(asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||
asmgen.translateExpression(value)
|
||||
asmgen.signExtendStackLsb(valueDt)
|
||||
multiplyWord()
|
||||
asmgen.out(" inx")
|
||||
// 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
|
||||
if(asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||
asmgen.translateExpression(value)
|
||||
asmgen.signExtendStackLsb(valueDt)
|
||||
divideWord()
|
||||
asmgen.out(" inx")
|
||||
// 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
|
||||
if(asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||
asmgen.translateExpression(value)
|
||||
asmgen.signExtendStackLsb(valueDt)
|
||||
remainderWord()
|
||||
asmgen.out(" inx")
|
||||
// TODO use an optimized word / byte divmod routine
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.signExtendAYlsb(valueDt)
|
||||
remainderVarByWordInAY()
|
||||
}
|
||||
"<<" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.Y)
|
||||
@@ -1300,20 +1348,25 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
bne -
|
||||
+""")
|
||||
}
|
||||
"&" -> {
|
||||
"&", "and" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" and $name | sta $name")
|
||||
if(dt in WordDatatypes)
|
||||
asmgen.out(" lda #0 | sta $name+1")
|
||||
if(dt in WordDatatypes) {
|
||||
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
|
||||
asmgen.out(" stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name+1")
|
||||
}
|
||||
}
|
||||
"^" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" eor $name | sta $name")
|
||||
}
|
||||
"|" -> {
|
||||
"|", "or" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" ora $name | sta $name")
|
||||
}
|
||||
"^", "xor" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out(" eor $name | sta $name")
|
||||
}
|
||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
@@ -1322,57 +1375,39 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
when (operator) {
|
||||
// note: ** (power) operator requires floats.
|
||||
"+" -> {
|
||||
if(asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||
asmgen.translateExpression(value)
|
||||
asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name | lda $name+1 | adc P8ESTACK_HI+1,x | sta $name+1 | inx")
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||
asmgen.out(" clc | adc $name | sta $name | tya | adc $name+1 | sta $name+1")
|
||||
}
|
||||
"-" -> {
|
||||
if(asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||
asmgen.translateExpression(value)
|
||||
asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name | lda $name+1 | sbc P8ESTACK_HI+1,x | sta $name+1 | inx")
|
||||
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")
|
||||
}
|
||||
"*" -> {
|
||||
if(asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||
asmgen.translateExpression(value)
|
||||
multiplyWord()
|
||||
asmgen.out(" inx")
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||
multiplyVarByWordInAY()
|
||||
}
|
||||
"/" -> {
|
||||
if(asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||
asmgen.translateExpression(value)
|
||||
divideWord()
|
||||
asmgen.out(" inx")
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||
divideVarByWordInAY()
|
||||
}
|
||||
"%" -> {
|
||||
if(asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||
asmgen.translateExpression(value)
|
||||
remainderWord()
|
||||
asmgen.out(" inx")
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||
remainderVarByWordInAY()
|
||||
}
|
||||
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
|
||||
"&" -> {
|
||||
if(asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||
asmgen.translateExpression(value)
|
||||
asmgen.out(" lda $name | and P8ESTACK_LO+1,x | sta $name | lda $name+1 | and P8ESTACK_HI+1,x | sta $name+1 | inx")
|
||||
"&", "and" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||
asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1")
|
||||
}
|
||||
"^" -> {
|
||||
if(asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||
asmgen.translateExpression(value)
|
||||
asmgen.out(" lda $name | eor P8ESTACK_LO+1,x | sta $name | lda $name+1 | eor P8ESTACK_HI+1,x | sta $name+1 | inx")
|
||||
"|", "or" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||
asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1")
|
||||
}
|
||||
"|" -> {
|
||||
if(asmgen.options.slowCodegenWarnings)
|
||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||
asmgen.translateExpression(value)
|
||||
asmgen.out(" lda $name | ora P8ESTACK_LO+1,x | sta $name | lda $name+1 | ora P8ESTACK_HI+1,x | sta $name+1 | inx")
|
||||
"^", "xor" -> {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
|
||||
}
|
||||
in comparisonOperators -> TODO("in-place modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
@@ -1381,12 +1416,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
|
||||
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) {
|
||||
"**" -> {
|
||||
@@ -1425,6 +1455,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
jsr floats.FDIV
|
||||
""")
|
||||
}
|
||||
in comparisonOperators -> TODO("in-place float modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||
}
|
||||
asmgen.out("""
|
||||
@@ -1444,14 +1475,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||
when (operator) {
|
||||
"**" -> {
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
lda #<$otherName
|
||||
ldy #>$otherName
|
||||
jsr floats.FPWR
|
||||
""")
|
||||
if(CompilationTarget.instance is Cx16Target) {
|
||||
// cx16 doesn't have FPWR() only FPWRT()
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
lda #<$otherName
|
||||
ldy #>$otherName
|
||||
jsr floats.MOVFM
|
||||
jsr floats.FPWRT
|
||||
""")
|
||||
} else
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
lda #<$otherName
|
||||
ldy #>$otherName
|
||||
jsr floats.FPWR
|
||||
""")
|
||||
}
|
||||
"+" -> {
|
||||
asmgen.out("""
|
||||
@@ -1493,6 +1536,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
jsr floats.FDIV
|
||||
""")
|
||||
}
|
||||
in comparisonOperators -> TODO("in-place float modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||
}
|
||||
// store Fac1 back into memory
|
||||
@@ -1509,14 +1553,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||
when (operator) {
|
||||
"**" -> {
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
lda #<$constValueName
|
||||
ldy #>$constValueName
|
||||
jsr floats.FPWR
|
||||
""")
|
||||
if(CompilationTarget.instance is Cx16Target) {
|
||||
// cx16 doesn't have FPWR() only FPWRT()
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
lda #<$constValueName
|
||||
ldy #>$constValueName
|
||||
jsr floats.MOVFM
|
||||
jsr floats.FPWRT
|
||||
""")
|
||||
} else
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr floats.CONUPK
|
||||
lda #<$constValueName
|
||||
ldy #>$constValueName
|
||||
jsr floats.FPWR
|
||||
""")
|
||||
}
|
||||
"+" -> {
|
||||
if (value == 0.0)
|
||||
@@ -1565,6 +1621,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
jsr floats.FDIV
|
||||
""")
|
||||
}
|
||||
in comparisonOperators -> TODO("in-place float modification for $operator")
|
||||
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
|
||||
}
|
||||
// store Fac1 back into memory
|
||||
|
||||
@@ -38,8 +38,7 @@ internal object CX16MachineDefinition: IMachineDefinition {
|
||||
override fun launchEmulator(programName: String) {
|
||||
for(emulator in listOf("x16emu")) {
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
val cmdline = listOf(emulator, "-rom", "/usr/share/x16-rom/rom.bin", "-scale", "2",
|
||||
"-run", "-prg", programName + ".prg")
|
||||
val cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "$programName.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
|
||||
@@ -141,6 +141,7 @@ private val functionSignatures: List<FSignature> = listOf(
|
||||
FSignature("set_irqd" , false, emptyList(), null),
|
||||
FSignature("clear_irqd" , false, emptyList(), null),
|
||||
FSignature("read_flags" , false, emptyList(), DataType.UBYTE),
|
||||
FSignature("progend" , true, emptyList(), DataType.UWORD),
|
||||
FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
|
||||
FSignature("memcopy" , false, listOf(
|
||||
FParam("from", IterableDatatypes + DataType.UWORD),
|
||||
@@ -155,6 +156,7 @@ private val functionSignatures: List<FSignature> = listOf(
|
||||
FParam("numwords", setOf(DataType.UWORD)),
|
||||
FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
|
||||
FSignature("strlen" , true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen),
|
||||
FSignature("strcopy" , false, listOf(FParam("from", IterableDatatypes + DataType.UWORD), FParam("to", IterableDatatypes + DataType.UWORD)), DataType.UBYTE),
|
||||
FSignature("substr" , false, listOf(
|
||||
FParam("source", IterableDatatypes + DataType.UWORD),
|
||||
FParam("target", IterableDatatypes + DataType.UWORD),
|
||||
@@ -353,7 +355,7 @@ private fun builtinStrlen(args: List<Expression>, position: Position, program: P
|
||||
val argument=args[0]
|
||||
if(argument is StringLiteralValue)
|
||||
return NumericLiteralValue.optimalInteger(argument.value.length, argument.position)
|
||||
val vardecl = (argument as IdentifierReference).targetVarDecl(program.namespace)
|
||||
val vardecl = (argument as? IdentifierReference)?.targetVarDecl(program.namespace)
|
||||
if(vardecl!=null) {
|
||||
if(vardecl.datatype!=DataType.STR && vardecl.datatype!=DataType.UWORD)
|
||||
throw SyntaxError("strlen must have string argument", position)
|
||||
|
||||
@@ -7,6 +7,7 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.processing.AstWalker
|
||||
import prog8.ast.processing.IAstModification
|
||||
import prog8.ast.statements.*
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
@@ -97,6 +98,43 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
val leftconst = expr.left.constValue(program)
|
||||
val rightconst = expr.right.constValue(program)
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
|
||||
if(expr.operator == "**" && leftconst!=null) {
|
||||
// optimize various simple cases of ** :
|
||||
// optimize away 1 ** x into just 1 and 0 ** x into just 0
|
||||
// optimize 2 ** x into (1<<x) if both operands are integer.
|
||||
val leftDt = leftconst.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
when (leftconst.number.toDouble()) {
|
||||
0.0 -> {
|
||||
val value = NumericLiteralValue(leftDt, 0, expr.position)
|
||||
modifications += IAstModification.ReplaceNode(expr, value, parent)
|
||||
}
|
||||
1.0 -> {
|
||||
val value = NumericLiteralValue(leftDt, 1, expr.position)
|
||||
modifications += IAstModification.ReplaceNode(expr, value, parent)
|
||||
}
|
||||
2.0 -> {
|
||||
if(rightconst!=null) {
|
||||
val value = NumericLiteralValue(leftDt, 2.0.pow(rightconst.number.toDouble()), expr.position)
|
||||
modifications += IAstModification.ReplaceNode(expr, value, parent)
|
||||
} else {
|
||||
val rightDt = expr.right.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val targetDt =
|
||||
when (parent) {
|
||||
is Assignment -> parent.target.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
is VarDecl -> parent.datatype
|
||||
else -> leftDt
|
||||
}
|
||||
val one = NumericLiteralValue(targetDt, 1, expr.position)
|
||||
val shift = BinaryExpression(one, "<<", expr.right, expr.position)
|
||||
modifications += IAstModification.ReplaceNode(expr, shift, parent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val subExpr: BinaryExpression? = when {
|
||||
leftconst!=null -> expr.right as? BinaryExpression
|
||||
@@ -111,7 +149,8 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
||||
val change = groupTwoConstsTogether(expr, subExpr,
|
||||
leftconst != null, rightconst != null,
|
||||
subleftconst != null, subrightconst != null)
|
||||
return change?.let { listOf(it) } ?: noModifications
|
||||
if(change!=null)
|
||||
modifications += change
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,10 +158,10 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
||||
if(leftconst != null && rightconst != null) {
|
||||
val evaluator = ConstExprEvaluator()
|
||||
val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
|
||||
return listOf(IAstModification.ReplaceNode(expr, result, parent))
|
||||
modifications += IAstModification.ReplaceNode(expr, result, parent)
|
||||
}
|
||||
|
||||
return noModifications
|
||||
return modifications
|
||||
}
|
||||
|
||||
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
@@ -6,6 +6,7 @@ import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.processing.AstWalker
|
||||
import prog8.ast.processing.IAstModification
|
||||
import prog8.ast.statements.Assignment
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.log2
|
||||
import kotlin.math.pow
|
||||
@@ -98,6 +99,12 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
if (leftVal != null && expr.operator in associativeOperators && rightVal == null)
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
|
||||
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
|
||||
if (expr.operator in associativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
|
||||
if(parent !is Assignment || !(expr.left isSameAs parent.target))
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
}
|
||||
|
||||
// X + (-A) --> X - A
|
||||
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
@@ -338,7 +345,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
if (leftVal == null && rightVal == null)
|
||||
return null
|
||||
|
||||
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal)
|
||||
val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
|
||||
if (rightVal2 != null) {
|
||||
// right value is a constant, see if we can optimize
|
||||
val rightConst: NumericLiteralValue = rightVal2
|
||||
@@ -562,7 +569,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
if (leftVal == null && rightVal == null)
|
||||
return null
|
||||
|
||||
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal)
|
||||
val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
|
||||
if (rightVal2 != null) {
|
||||
// right value is a constant, see if we can optimize
|
||||
val leftValue: Expression = expr2.left
|
||||
@@ -682,17 +689,17 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
return null
|
||||
}
|
||||
|
||||
private fun reorderAssociative(expr: BinaryExpression, leftVal: NumericLiteralValue?): ReorderedAssociativeBinaryExpr {
|
||||
private fun reorderAssociativeWithConstant(expr: BinaryExpression, leftVal: NumericLiteralValue?): BinExprWithConstants {
|
||||
if (expr.operator in associativeOperators && leftVal != null) {
|
||||
// swap left and right so that right is always the constant
|
||||
val tmp = expr.left
|
||||
expr.left = expr.right
|
||||
expr.right = tmp
|
||||
return ReorderedAssociativeBinaryExpr(expr, expr.right.constValue(program), leftVal)
|
||||
return BinExprWithConstants(expr, expr.right.constValue(program), leftVal)
|
||||
}
|
||||
return ReorderedAssociativeBinaryExpr(expr, leftVal, expr.right.constValue(program))
|
||||
return BinExprWithConstants(expr, leftVal, expr.right.constValue(program))
|
||||
}
|
||||
|
||||
private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?)
|
||||
private data class BinExprWithConstants(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?)
|
||||
|
||||
}
|
||||
@@ -323,7 +323,7 @@ internal class StatementOptimizer(private val program: Program,
|
||||
val op1 = binExpr.operator
|
||||
val op2 = rExpr.operator
|
||||
|
||||
if(rExpr.left is NumericLiteralValue && op2 in setOf("+", "*", "&", "|")) {
|
||||
if(rExpr.left is NumericLiteralValue && op2 in associativeOperators) {
|
||||
// associative operator, make sure the constant numeric value is second (right)
|
||||
return listOf(IAstModification.SwapOperands(rExpr))
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ internal class ModuleImporter {
|
||||
|
||||
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
|
||||
val fileName = "$name.p8"
|
||||
val locations = if(source.toString().isEmpty()) mutableListOf<Path>() else mutableListOf(source.parent)
|
||||
val locations = if(source.toString().isEmpty()) mutableListOf<Path>() else mutableListOf(source.parent ?: Path.of("."))
|
||||
|
||||
val propPath = System.getProperty("prog8.libdir")
|
||||
if(propPath!=null)
|
||||
|
||||
+22
-32
@@ -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
|
||||
@@ -146,7 +136,7 @@ A recent precompiled .exe for Windows can be obtained from my `clone <https://gi
|
||||
*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.
|
||||
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.
|
||||
|
||||
@@ -54,7 +54,8 @@ diskio
|
||||
------
|
||||
Provides several routines that deal with disk drive I/O, such as:
|
||||
|
||||
- show directory
|
||||
- list files on disk, optionally filtering by prefix or suffix
|
||||
- show disk directory as-is
|
||||
- display disk drive status
|
||||
- load and save data from and to the disk
|
||||
- delete and rename files on the disk
|
||||
@@ -88,6 +89,15 @@ A 'fun' module that contains the Commander X16 logo and that allows you
|
||||
to print it anywhere on the screen.
|
||||
|
||||
|
||||
c64colors
|
||||
---------
|
||||
Available for the CommanderX16 target, a module that contains a few better
|
||||
color palettes for how the colors of the VIC-II looked on the Commodore-64.
|
||||
There are subroutines to activate one of the several palettes of your liking.
|
||||
The Commander X16's default colors for this (the first 16 colors) are too saturated
|
||||
and are quite different than how a C-64 looked.
|
||||
|
||||
|
||||
prog8_lib
|
||||
---------
|
||||
Low level language support. You should not normally have to bother with this directly.
|
||||
|
||||
@@ -309,18 +309,21 @@ read the syntax reference on strings.
|
||||
|
||||
|
||||
.. hint::
|
||||
Strings and uwords (=memory address) can often be interchanged.
|
||||
Strings/arrays and uwords (=memory address) can often be interchanged.
|
||||
An array of strings is actually an array of uwords where every element is the memory
|
||||
address of the string. You can pass a memory address to assembly functions
|
||||
that require a string as an argument.
|
||||
For regular assignments you still need to use an explicit ``&`` (address-of) to take
|
||||
the address of the string or array.
|
||||
|
||||
.. caution::
|
||||
It's probably best to avoid changing strings after they've been created. This
|
||||
includes changing certain letters by index, or by assigning a new value, or by
|
||||
It's probably best to avoid changing the contents in strings and treat them as static.
|
||||
This includes changing certain letters by index, or by assigning a new value, or by
|
||||
modifying the string via other means for example ``substr`` function and its cousins.
|
||||
This is because if your program exits and is restarted (without loading it again),
|
||||
it will then start working with the changed strings instead of the original ones!
|
||||
The same is true for arrays.
|
||||
This is because the changes persist in memory. If your program exits and is restarted
|
||||
(without reloading it from disk), it will then start working with the modified strings
|
||||
instead of the original ones!
|
||||
The same is true for arrays! So be careful to (re)initialize them if needed.
|
||||
|
||||
|
||||
Structs
|
||||
@@ -811,6 +814,12 @@ substr(source, target, start, length)
|
||||
Also, you have to make sure yourself that start and length are within bounds of the strings.
|
||||
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||
|
||||
strcopy(from, to)
|
||||
Copy a string to another, overwriting that one. Returns the length of the string that was copied.
|
||||
Often you don't have to call this explicitly and can just write ``string1 = string2``
|
||||
but this function is useful if you're dealing with addresses for instance.
|
||||
|
||||
|
||||
Miscellaneous
|
||||
^^^^^^^^^^^^^
|
||||
exit(returncode)
|
||||
@@ -827,6 +836,12 @@ mkword(msb, lsb)
|
||||
Efficiently create a word value from two bytes (the msb and the lsb). Avoids multiplication and shifting.
|
||||
So mkword($80, $22) results in $8022.
|
||||
|
||||
.. note::
|
||||
The arguments to the mkword() function are in 'natural' order that is first the msb then the lsb.
|
||||
Don't get confused by how the system actually stores this 16-bit word value in memory (which is
|
||||
in little-endian format, so lsb first then msb)
|
||||
|
||||
|
||||
rnd()
|
||||
returns a pseudo-random byte from 0..255
|
||||
|
||||
@@ -891,6 +906,10 @@ set_irqd() / clear_irqd()
|
||||
swap(x, y)
|
||||
Swap the values of numerical variables (or memory locations) x and y in a fast way.
|
||||
|
||||
progend()
|
||||
Returns the last address of the program in memory + 1.
|
||||
Can be used to load dynamic data after the program, instead of hardcoding something.
|
||||
|
||||
|
||||
Library routines
|
||||
----------------
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- 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.
|
||||
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
|
||||
- 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
|
||||
|
||||
@@ -324,6 +324,10 @@ main {
|
||||
txt.print(result)
|
||||
txt.chrout('\n')
|
||||
|
||||
void strcopy(s2, s1)
|
||||
txt.print_ub(99+strcopy(s2,s1))
|
||||
txt.chrout('\n')
|
||||
|
||||
test_stack.test()
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
%import floats
|
||||
%import textio
|
||||
%import test_stack
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@@ -31,6 +32,8 @@ main {
|
||||
minus_float(0,0,0)
|
||||
minus_float(2.5,1.5,1.0)
|
||||
minus_float(-1.5,3.5,-5.0)
|
||||
|
||||
test_stack.test()
|
||||
}
|
||||
|
||||
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||
@@ -97,10 +100,11 @@ main {
|
||||
float r = a1-a2
|
||||
if abs(r-c)<0.00001
|
||||
txt.print(" ok ")
|
||||
else
|
||||
else {
|
||||
txt.print("err! ")
|
||||
}
|
||||
|
||||
txt.print("float ")
|
||||
txt.print(" float ")
|
||||
floats.print_f(a1)
|
||||
txt.print(" - ")
|
||||
floats.print_f(a2)
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
%import test_stack
|
||||
%zeropage basicsafe
|
||||
|
||||
; note: The flickering in the scrolling is caused by the CPU requiring
|
||||
; too long to scroll the characters + the colors in course scroll.
|
||||
; This takes nearly a full frame to accomplish, and causes tearing.
|
||||
; It's very difficult to remove this flicker: it requires double buffering
|
||||
; and splitting the coarse character scrolling on multiple phases...
|
||||
|
||||
main {
|
||||
|
||||
ubyte perform_scroll = false
|
||||
@@ -17,7 +23,7 @@ main {
|
||||
|
||||
c64.SCROLX &= %11110111 ; 38 column mode
|
||||
|
||||
c64.set_rasterirq(1) ; enable animation
|
||||
c64.set_rasterirq(200) ; enable animation
|
||||
|
||||
ubyte target_height = 10
|
||||
ubyte active_height = 24
|
||||
@@ -123,10 +129,10 @@ spritedata $0f00 {
|
||||
|
||||
|
||||
irq {
|
||||
ubyte smoothx=7
|
||||
ubyte smoothx=0
|
||||
sub irq() {
|
||||
smoothx = (smoothx-1) & 7
|
||||
main.perform_scroll = smoothx==0
|
||||
main.perform_scroll = smoothx==7
|
||||
c64.SCROLX = (c64.SCROLX & %11111000) | smoothx
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,913 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%zeropage basicsafe
|
||||
%import test_stack
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
word_less()
|
||||
word_lessequal()
|
||||
word_greaterequal()
|
||||
word_greater()
|
||||
uword_lessequal()
|
||||
}
|
||||
|
||||
sub uword_lessequal() {
|
||||
uword lessvar
|
||||
uword comparevar
|
||||
|
||||
txt.print("uword <=\n")
|
||||
|
||||
txt.print_uw(65535)
|
||||
txt.chrout('\n')
|
||||
check_lesseq_uw(0, 65535)
|
||||
txt.print_uw(0)
|
||||
txt.chrout('\n')
|
||||
check_not_lesseq_uw(65535, 0)
|
||||
|
||||
comparevar = 65535
|
||||
txt.print_uw(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto 0 {
|
||||
check_lesseq_uw(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 65535-2
|
||||
txt.print_uw(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto 0 {
|
||||
check_lesseq_uw(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 65535-254
|
||||
txt.print_uw(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto 0 {
|
||||
check_lesseq_uw(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 65535-255
|
||||
txt.print_uw(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto 0 {
|
||||
check_lesseq_uw(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 65535-256
|
||||
txt.print_uw(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto 0 {
|
||||
check_lesseq_uw(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 65535-5000
|
||||
txt.print_uw(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto 0 {
|
||||
check_lesseq_uw(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 32769
|
||||
txt.print_uw(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto 0 {
|
||||
check_lesseq_uw(lessvar, comparevar)
|
||||
}
|
||||
|
||||
|
||||
comparevar = 32768
|
||||
txt.print_uw(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 65535 downto comparevar+1 {
|
||||
check_not_lesseq_uw(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 1
|
||||
txt.print_uw(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 65535 downto comparevar+1 {
|
||||
check_not_lesseq_uw(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 0
|
||||
txt.print_uw(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 65535 downto comparevar+1 {
|
||||
check_not_lesseq_uw(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 11111
|
||||
txt.print_uw(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 65535 downto comparevar+1 {
|
||||
check_not_lesseq_uw(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 255
|
||||
txt.print_uw(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 65535 downto comparevar+1 {
|
||||
check_not_lesseq_uw(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 256
|
||||
txt.print_uw(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 65535 downto comparevar+1 {
|
||||
check_not_lesseq_uw(lessvar, comparevar)
|
||||
}
|
||||
|
||||
|
||||
test_stack.test()
|
||||
return
|
||||
|
||||
sub check_lesseq_uw(uword w1, uword w2) {
|
||||
uword zero = 0
|
||||
ubyte error=0
|
||||
|
||||
ubyte ub = w1<=w2
|
||||
if not ub {
|
||||
error++
|
||||
txt.print("ub!")
|
||||
}
|
||||
|
||||
if w1<=(w2+zero) {
|
||||
zero = 0 ; dummy
|
||||
} else {
|
||||
error++
|
||||
txt.print("c!")
|
||||
}
|
||||
|
||||
if error {
|
||||
txt.print(" ")
|
||||
txt.print_uw(w1)
|
||||
txt.print(" <= ")
|
||||
txt.print_uw(w2)
|
||||
txt.chrout('\n')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
sub check_not_lesseq_uw(uword w1, uword w2) {
|
||||
uword zero = 0
|
||||
ubyte error=0
|
||||
|
||||
ubyte ub = w1<=w2
|
||||
if ub {
|
||||
error++
|
||||
txt.print("ub!")
|
||||
}
|
||||
|
||||
if w1<=(w2+zero) {
|
||||
error++
|
||||
txt.print("c!")
|
||||
} else {
|
||||
zero = 0 ; dummy
|
||||
}
|
||||
|
||||
if error {
|
||||
txt.print(" ")
|
||||
txt.print_uw(w1)
|
||||
txt.print(" not <= ")
|
||||
txt.print_uw(w2)
|
||||
txt.chrout('\n')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub word_greater() {
|
||||
word biggervar
|
||||
word comparevar
|
||||
|
||||
txt.print("word >\n")
|
||||
|
||||
txt.print_w(-32767)
|
||||
txt.chrout('\n')
|
||||
check_greater_w(32767, -32767)
|
||||
txt.print_w(32766)
|
||||
txt.chrout('\n')
|
||||
check_not_greater_w(-32766, 32766)
|
||||
|
||||
comparevar = 32765
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in comparevar downto -32768 {
|
||||
check_not_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -1
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in comparevar downto -32768 {
|
||||
check_not_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 0
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in comparevar downto -32768 {
|
||||
check_not_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 11111
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in comparevar downto -32768 {
|
||||
check_not_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 0
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar+1 {
|
||||
check_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -2
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar+1 {
|
||||
check_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -254
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar+1 {
|
||||
check_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -255
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar+1 {
|
||||
check_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -256
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar+1 {
|
||||
check_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -5000
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar+1 {
|
||||
check_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 1
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar+1 {
|
||||
check_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 255
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar+1 {
|
||||
check_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 256
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar+1 {
|
||||
check_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 257
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar+1 {
|
||||
check_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 32760
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar+1 {
|
||||
check_greater_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
|
||||
test_stack.test()
|
||||
return
|
||||
|
||||
sub check_greater_w(word w1, word w2) {
|
||||
word zero = 0
|
||||
ubyte error=0
|
||||
|
||||
ubyte ub = w1>(w2+zero)
|
||||
if not ub {
|
||||
error++
|
||||
txt.print("ubz!")
|
||||
}
|
||||
|
||||
ub = w1>w2
|
||||
if not ub {
|
||||
error++
|
||||
txt.print("ub!")
|
||||
}
|
||||
|
||||
if w1>(w2+zero) {
|
||||
zero = 0 ; dummy
|
||||
} else {
|
||||
error++
|
||||
txt.print("c!")
|
||||
}
|
||||
|
||||
if error {
|
||||
txt.print(" ")
|
||||
txt.print_w(w1)
|
||||
txt.print(" > ")
|
||||
txt.print_w(w2)
|
||||
txt.chrout('\n')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
sub check_not_greater_w(word w1, word w2) {
|
||||
word zero = 0
|
||||
ubyte error=0
|
||||
|
||||
ubyte ub = w1>w2
|
||||
if ub {
|
||||
error++
|
||||
txt.print("ub!")
|
||||
}
|
||||
|
||||
if w1>(w2+zero) {
|
||||
error++
|
||||
txt.print("c!")
|
||||
} else {
|
||||
zero = 0 ; dummy
|
||||
}
|
||||
|
||||
if w1>w2 {
|
||||
error++
|
||||
txt.print("c2!")
|
||||
} else {
|
||||
zero = 0 ; dummy
|
||||
}
|
||||
|
||||
if error {
|
||||
txt.print(" ")
|
||||
txt.print_w(w1)
|
||||
txt.print(" not > ")
|
||||
txt.print_w(w2)
|
||||
txt.chrout('\n')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub word_greaterequal() {
|
||||
word biggervar
|
||||
word comparevar
|
||||
|
||||
txt.print("word >=\n")
|
||||
|
||||
txt.print_w(-32767)
|
||||
txt.chrout('\n')
|
||||
check_greatereq_w(32767, -32767)
|
||||
txt.print_w(32766)
|
||||
txt.chrout('\n')
|
||||
check_not_greatereq_w(-32766, 32766)
|
||||
|
||||
comparevar = 32765
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in comparevar-1 downto -32768 {
|
||||
check_not_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -1
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in comparevar-1 downto -32768 {
|
||||
check_not_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 0
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in comparevar-1 downto -32768 {
|
||||
check_not_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 11111
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in comparevar-1 downto -32768 {
|
||||
check_not_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 0
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar {
|
||||
check_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -2
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar {
|
||||
check_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -254
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar {
|
||||
check_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -255
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar {
|
||||
check_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -256
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar {
|
||||
check_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -5000
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar {
|
||||
check_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 1
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar {
|
||||
check_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 255
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar {
|
||||
check_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 256
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar {
|
||||
check_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 257
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar {
|
||||
check_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 32767
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for biggervar in 32767 downto comparevar {
|
||||
check_greatereq_w(biggervar, comparevar)
|
||||
}
|
||||
|
||||
|
||||
test_stack.test()
|
||||
return
|
||||
|
||||
sub check_greatereq_w(word w1, word w2) {
|
||||
word zero = 0
|
||||
ubyte error=0
|
||||
|
||||
ubyte ub = w1>=(w2+zero)
|
||||
if not ub {
|
||||
error++
|
||||
txt.print("ubz!")
|
||||
}
|
||||
|
||||
ub = w1>=w2
|
||||
if not ub {
|
||||
error++
|
||||
txt.print("ub!")
|
||||
}
|
||||
|
||||
if w1>=(w2+zero) {
|
||||
zero = 0 ; dummy
|
||||
} else {
|
||||
error++
|
||||
txt.print("c!")
|
||||
}
|
||||
|
||||
if error {
|
||||
txt.print(" ")
|
||||
txt.print_w(w1)
|
||||
txt.print(" >= ")
|
||||
txt.print_w(w2)
|
||||
txt.chrout('\n')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
sub check_not_greatereq_w(word w1, word w2) {
|
||||
word zero = 0
|
||||
ubyte error=0
|
||||
|
||||
ubyte ub = w1>=w2
|
||||
if ub {
|
||||
error++
|
||||
txt.print("ub!")
|
||||
}
|
||||
|
||||
if w1>=(w2+zero) {
|
||||
error++
|
||||
txt.print("c!")
|
||||
} else {
|
||||
zero = 0 ; dummy
|
||||
}
|
||||
|
||||
if w1>=w2 {
|
||||
error++
|
||||
txt.print("c2!")
|
||||
} else {
|
||||
zero = 0 ; dummy
|
||||
}
|
||||
|
||||
if error {
|
||||
txt.print(" ")
|
||||
txt.print_w(w1)
|
||||
txt.print(" not >= ")
|
||||
txt.print_w(w2)
|
||||
txt.chrout('\n')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub word_lessequal() {
|
||||
word lessvar
|
||||
word comparevar
|
||||
|
||||
txt.print("word <=\n")
|
||||
|
||||
txt.print_w(32767)
|
||||
txt.chrout('\n')
|
||||
check_lesseq_w(-32767, 32767)
|
||||
txt.print_w(-32767)
|
||||
txt.chrout('\n')
|
||||
check_not_lesseq_w(32767, -32767)
|
||||
|
||||
comparevar = 0
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto -32768 {
|
||||
check_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -2
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto -32768 {
|
||||
check_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -254
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto -32768 {
|
||||
check_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -255
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto -32768 {
|
||||
check_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -256
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto -32768 {
|
||||
check_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -5000
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto -32768 {
|
||||
check_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 1
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto -32768 {
|
||||
check_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 255
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto -32768 {
|
||||
check_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 256
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto -32768 {
|
||||
check_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 257
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto -32768 {
|
||||
check_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 32767
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in comparevar downto -32768 {
|
||||
check_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
|
||||
comparevar = -32768
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 32766 downto comparevar+1 {
|
||||
check_not_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -1
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 32766 downto comparevar+1 {
|
||||
check_not_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 0
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 32766 downto comparevar+1 {
|
||||
check_not_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 11111
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 32766 downto comparevar+1 {
|
||||
check_not_lesseq_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
test_stack.test()
|
||||
return
|
||||
|
||||
sub check_lesseq_w(word w1, word w2) {
|
||||
word zero = 0
|
||||
ubyte error=0
|
||||
|
||||
ubyte ub = w1<=w2
|
||||
if not ub {
|
||||
error++
|
||||
txt.print("ub!")
|
||||
}
|
||||
|
||||
if w1<=(w2+zero) {
|
||||
zero = 0 ; dummy
|
||||
} else {
|
||||
error++
|
||||
txt.print("c!")
|
||||
}
|
||||
|
||||
if error {
|
||||
txt.print(" ")
|
||||
txt.print_w(w1)
|
||||
txt.print(" <= ")
|
||||
txt.print_w(w2)
|
||||
txt.chrout('\n')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
sub check_not_lesseq_w(word w1, word w2) {
|
||||
word zero = 0
|
||||
ubyte error=0
|
||||
|
||||
ubyte ub = w1<=w2
|
||||
if ub {
|
||||
error++
|
||||
txt.print("ub!")
|
||||
}
|
||||
|
||||
if w1<=(w2+zero) {
|
||||
error++
|
||||
txt.print("c!")
|
||||
} else {
|
||||
zero = 0 ; dummy
|
||||
}
|
||||
|
||||
if error {
|
||||
txt.print(" ")
|
||||
txt.print_w(w1)
|
||||
txt.print(" not <= ")
|
||||
txt.print_w(w2)
|
||||
txt.chrout('\n')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub word_less() {
|
||||
word lessvar
|
||||
word comparevar
|
||||
|
||||
txt.print("word <\n")
|
||||
|
||||
txt.print_w(32767)
|
||||
txt.chrout('\n')
|
||||
check_less_w(-32767, 32767)
|
||||
txt.print_w(-32767)
|
||||
txt.chrout('\n')
|
||||
check_not_less_w(32767, -32767)
|
||||
|
||||
comparevar = 0
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in -1 downto -32768 {
|
||||
check_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -2
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in -3 downto -32768 {
|
||||
check_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -254
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in -255 downto -32768 {
|
||||
check_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -255
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in -256 downto -32768 {
|
||||
check_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -256
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in -257 downto -32768 {
|
||||
check_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -5000
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in -5001 downto -32768 {
|
||||
check_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 1
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 0 downto -32768 {
|
||||
check_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 255
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 254 downto -32768 {
|
||||
check_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 256
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 255 downto -32768 {
|
||||
check_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 257
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 256 downto -32768 {
|
||||
check_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 32767
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 32766 downto -32768 {
|
||||
check_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -32768
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 32766 downto -32768 {
|
||||
check_not_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = -1
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 32766 downto -1 {
|
||||
check_not_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 0
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 32766 downto 0 {
|
||||
check_not_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
comparevar = 11111
|
||||
txt.print_w(comparevar)
|
||||
txt.chrout('\n')
|
||||
for lessvar in 32766 downto 11111 {
|
||||
check_not_less_w(lessvar, comparevar)
|
||||
}
|
||||
|
||||
test_stack.test()
|
||||
return
|
||||
|
||||
sub check_less_w(word w1, word w2) {
|
||||
word zero = 0
|
||||
ubyte error=0
|
||||
|
||||
ubyte ub = w1<w2
|
||||
if not ub {
|
||||
error++
|
||||
txt.print("ub!")
|
||||
}
|
||||
|
||||
if w1<(w2+zero) {
|
||||
zero = 0 ; dummy
|
||||
} else {
|
||||
error++
|
||||
txt.print("c!")
|
||||
}
|
||||
|
||||
if error {
|
||||
txt.print(" ")
|
||||
txt.print_w(w1)
|
||||
txt.print(" < ")
|
||||
txt.print_w(w2)
|
||||
txt.chrout('\n')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
sub check_not_less_w(word w1, word w2) {
|
||||
word zero = 0
|
||||
ubyte error=0
|
||||
|
||||
ubyte ub = w1<w2
|
||||
if ub {
|
||||
error++
|
||||
txt.print("ub!")
|
||||
}
|
||||
|
||||
if w1<(w2+zero) {
|
||||
error++
|
||||
txt.print("c!")
|
||||
} else {
|
||||
zero = 0 ; dummy
|
||||
}
|
||||
|
||||
if error {
|
||||
txt.print(" ")
|
||||
txt.print_w(w1)
|
||||
txt.print(" not < ")
|
||||
txt.print_w(w2)
|
||||
txt.chrout('\n')
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,7 @@
|
||||
%import textio
|
||||
%import test_stack
|
||||
|
||||
|
||||
spritedata $2000 {
|
||||
; this memory block contains the sprite data
|
||||
; it must start on an address aligned to 64 bytes.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
%import test_stack
|
||||
%import textio
|
||||
|
||||
|
||||
main {
|
||||
|
||||
; vertices
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
%target cx16
|
||||
%import graphics
|
||||
%import diskio
|
||||
|
||||
bmp_module {
|
||||
|
||||
sub show_image(uword filenameptr) -> ubyte {
|
||||
ubyte load_ok = false
|
||||
ubyte[$36] header
|
||||
uword size
|
||||
uword width
|
||||
uword height
|
||||
ubyte bpp
|
||||
uword offsetx
|
||||
uword offsety
|
||||
ubyte[256] palette0
|
||||
ubyte[256] palette1
|
||||
ubyte[256] palette2
|
||||
ubyte[256] palette3
|
||||
uword total_read = 0
|
||||
|
||||
palette0[0] = 0
|
||||
palette1[0] = 0
|
||||
palette2[0] = 0
|
||||
palette3[0] = 0
|
||||
|
||||
if diskio.f_open(8, filenameptr) {
|
||||
size = diskio.f_read(header, $36)
|
||||
if size==$36 {
|
||||
total_read = $36
|
||||
if header[0]=='b' and header[1]=='m' {
|
||||
uword bm_data_offset = mkword(header[11], header[10])
|
||||
uword header_size = mkword(header[$f], header[$e])
|
||||
width = mkword(header[$13], header[$12])
|
||||
height = mkword(header[$17], header[$16])
|
||||
bpp = header[$1c]
|
||||
uword num_colors = header[$2e]
|
||||
if num_colors == 0
|
||||
num_colors = 2**bpp
|
||||
uword skip_hdr = header_size - 40
|
||||
repeat skip_hdr
|
||||
void c64.CHRIN()
|
||||
total_read += skip_hdr
|
||||
size = diskio.f_read(&palette0, num_colors*4)
|
||||
if size==num_colors*4 {
|
||||
total_read += size
|
||||
repeat bm_data_offset - total_read
|
||||
void c64.CHRIN()
|
||||
graphics.clear_screen(1, 0)
|
||||
palette.set_bgra(&palette0, num_colors)
|
||||
decode_bitmap()
|
||||
load_ok = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diskio.f_close()
|
||||
}
|
||||
|
||||
return load_ok
|
||||
|
||||
sub start_plot() {
|
||||
offsetx = 0
|
||||
offsety = 0
|
||||
if width < graphics.WIDTH
|
||||
offsetx = (graphics.WIDTH - width - 1) / 2
|
||||
if height < graphics.HEIGHT
|
||||
offsety = (graphics.HEIGHT - height - 1) / 2
|
||||
if width > graphics.WIDTH
|
||||
width = graphics.WIDTH
|
||||
if height > graphics.HEIGHT-1
|
||||
height = graphics.HEIGHT-1
|
||||
}
|
||||
|
||||
sub set_cursor(uword x, uword y) {
|
||||
cx16.r0=offsetx+x
|
||||
cx16.r1=offsety+y
|
||||
cx16.FB_cursor_position()
|
||||
}
|
||||
|
||||
sub decode_bitmap() {
|
||||
start_plot()
|
||||
uword bits_width = width * bpp
|
||||
ubyte pad_bytes = (((bits_width + 31) >> 5) << 2) - ((bits_width + 7) >> 3) as ubyte
|
||||
|
||||
uword x
|
||||
uword y
|
||||
ubyte b
|
||||
for y in height-1 downto 0 {
|
||||
set_cursor(0, y)
|
||||
when bpp {
|
||||
8 -> {
|
||||
for x in 0 to width-1
|
||||
cx16.FB_set_pixel(c64.CHRIN())
|
||||
}
|
||||
4 -> {
|
||||
for x in 0 to width-1 step 2 {
|
||||
b = c64.CHRIN()
|
||||
cx16.FB_set_pixel(b>>4)
|
||||
cx16.FB_set_pixel(b&15)
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
for x in 0 to width-1 step 4 {
|
||||
b = c64.CHRIN()
|
||||
cx16.FB_set_pixel(b>>6)
|
||||
cx16.FB_set_pixel(b>>4 & 3)
|
||||
cx16.FB_set_pixel(b>>2 & 3)
|
||||
cx16.FB_set_pixel(b & 3)
|
||||
}
|
||||
}
|
||||
1 -> {
|
||||
for x in 0 to width-1 step 8 {
|
||||
cx16.r0 = c64.CHRIN()
|
||||
cx16.FB_set_8_pixels_opaque(255, 255, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repeat pad_bytes
|
||||
void c64.CHRIN()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
%target cx16
|
||||
%import graphics
|
||||
%import textio
|
||||
%import diskio
|
||||
%option no_sysinit
|
||||
|
||||
; CommanderX16 Image file format. (EXPERIMENTAL/ UNFINISHED)
|
||||
; Numbers are encoded in little endian format (lsb first).
|
||||
;
|
||||
; offset value
|
||||
; -----------------
|
||||
; HEADER (12 bytes):
|
||||
; 0-1 'CI' in petscii , from "CommanderX16 Image".
|
||||
; 2 Size of the header data following this byte (always 9, could become more if format changes)
|
||||
; 3-4 Width in pixels (must be multiple of 8)
|
||||
; 5-6 Height in pixels
|
||||
; 7 Bits-per-pixel (1, 2, 4 or 8) (= 2, 4, 16 or 256 colors)
|
||||
; this also determines the number of palette entries following later.
|
||||
; 8 Settings bits.
|
||||
; bit 0 and 1 = compression. 00 = uncompressed
|
||||
; 01 = RLE [TODO not yet implemented]
|
||||
; 10 = LZSA [TODO not yet implemented]
|
||||
; 11 = Exomizer [TODO not yet implemented]
|
||||
; bit 2 = palette format. 0 = 4 bits/channel (2 bytes per color, $0R $GB)
|
||||
; 1 = 8 bits/channel (3 bytes per color, $RR $GG $BB)
|
||||
; 4 bits per channel is what the Vera in the Cx16 supports.
|
||||
; bit 3 = bitmap format. 0 = raw bitmap pixels
|
||||
; 1 = tile-based image [TODO not yet implemented]
|
||||
; bit 4 = hscale (horizontal display resulution) 0 = 320 pixels, 1 = 640 pixels
|
||||
; bit 5 = vscale (vertical display resulution) 0 = 240 pixels, 1 = 480 pixels
|
||||
; bit 6,7: reserved, set to 0
|
||||
; 9-11 Size of the bitmap data following the palette data.
|
||||
; This is a 24-bits number, can be 0 ("unknown", in which case just read until the end).
|
||||
;
|
||||
; PALETTE (always present but size varies):
|
||||
; 12-... Color palette. Number of entries = 2 ^ bits-per-pixel. Number of bytes per
|
||||
; entry is 2 or 3, depending on the chosen palette format in the setting bits.
|
||||
;
|
||||
; BITMAPDATA (size varies):
|
||||
; After this, the actual image data follows.
|
||||
; If the bitmap format is 'raw bitmap pixels', the bimap is simply written as a sequence
|
||||
; of bytes making up the image's scan lines. #bytes per scan line = width * bits-per-pixel / 8
|
||||
; If it is 'tiles', .... [TODO]
|
||||
; If a compression scheme is used, the bitmap data here has to be decompressed first.
|
||||
; TODO: with compressed files, store the data in compressed chunks of max 8kb uncompressed?
|
||||
; (it is a problem to load let alone decompress a full bitmap at once because there will likely not be enough ram to do that)
|
||||
; (doing it in chunks of 8 kb allows for sticking each chunk in one of the banked 8kb ram blocks, or even copy it directly to the screen)
|
||||
|
||||
ci_module {
|
||||
%option force_output
|
||||
ubyte[256] buffer
|
||||
ubyte[256] buffer2 ; add two more buffers to make enough space
|
||||
ubyte[256] buffer3 ; to store a 256 color palette
|
||||
ubyte[256] buffer4 ; .. and some more to be able to store 1280=
|
||||
ubyte[256] buffer5 ; two 640 bytes worth of bitmap scanline data
|
||||
|
||||
sub show_image(uword filename) -> ubyte {
|
||||
ubyte read_success = false
|
||||
uword bitmap_load_address = progend()
|
||||
; uword max_bitmap_size = $9eff - bitmap_load_address
|
||||
|
||||
if(diskio.f_open(8, filename)) {
|
||||
uword size = diskio.f_read(buffer, 12) ; read the header
|
||||
if size==12 {
|
||||
if buffer[0]=='c' and buffer[1]=='i' and buffer[2] == 9 {
|
||||
uword width = mkword(buffer[4], buffer[3])
|
||||
uword height = mkword(buffer[6], buffer[5])
|
||||
ubyte bpp = buffer[7]
|
||||
uword num_colors = 2 ** bpp
|
||||
ubyte flags = buffer[8]
|
||||
ubyte compression = flags & %00000011
|
||||
ubyte palette_format = (flags & %00000100) >> 2
|
||||
ubyte bitmap_format = (flags & %00001000) >> 3
|
||||
; ubyte hscale = (flags & %00010000) >> 4
|
||||
; ubyte vscale = (flags & %00100000) >> 5
|
||||
uword bitmap_size = mkword(buffer[10], buffer[9])
|
||||
uword palette_size = num_colors*2
|
||||
if palette_format
|
||||
palette_size += num_colors ; 3
|
||||
if width > graphics.WIDTH {
|
||||
txt.print("image is too wide for the display!\n")
|
||||
} else if compression!=0 {
|
||||
txt.print("compressed image not yet supported!\n") ; TODO implement the various decompressions
|
||||
} else if bitmap_format==1 {
|
||||
txt.print("tiled bitmap not yet supported!\n") ; TODO implement tiled image
|
||||
} else {
|
||||
size = diskio.f_read(buffer, palette_size)
|
||||
if size==palette_size {
|
||||
if compression {
|
||||
txt.print("todo: compressed image support\n")
|
||||
} else {
|
||||
; uncompressed bitmap data. read it a scanline at a time and display as we go.
|
||||
; restrict height to the maximun that can be displayed
|
||||
if height > graphics.HEIGHT
|
||||
height = graphics.HEIGHT
|
||||
graphics.enable_bitmap_mode()
|
||||
if palette_format
|
||||
palette.set_rgb8(buffer, num_colors)
|
||||
else
|
||||
palette.set_rgb4(buffer, num_colors)
|
||||
graphics.clear_screen(1,0)
|
||||
cx16.r0 = 0
|
||||
cx16.r1 = 0
|
||||
cx16.FB_cursor_position()
|
||||
uword scanline_size = width * bpp / 8
|
||||
ubyte y
|
||||
for y in 0 to lsb(height)-1 {
|
||||
void diskio.f_read(buffer, scanline_size)
|
||||
when bpp {
|
||||
8 -> cx16.FB_set_pixels_from_buf(buffer, scanline_size) ; FB_set_pixels in rom v38 crashes with a size > 255 so we use our own replacement for now
|
||||
4 -> display_scanline_16c(buffer, scanline_size)
|
||||
2 -> display_scanline_4c(buffer, scanline_size)
|
||||
1 -> display_scanline_2c(buffer, scanline_size)
|
||||
}
|
||||
}
|
||||
read_success = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diskio.f_close()
|
||||
}
|
||||
|
||||
return read_success
|
||||
}
|
||||
|
||||
sub display_scanline_16c(uword dataptr, uword numbytes) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub display_scanline_4c(uword dataptr, uword numbytes) {
|
||||
; TODO
|
||||
}
|
||||
|
||||
sub display_scanline_2c(uword dataptr, uword numbytes) {
|
||||
; TODO
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
%target cx16
|
||||
%import graphics
|
||||
%import textio
|
||||
%import diskio
|
||||
|
||||
iff_module {
|
||||
sub show_image(uword filenameptr) -> ubyte {
|
||||
ubyte load_ok = false
|
||||
uword size
|
||||
ubyte[32] buffer
|
||||
ubyte[256] cmap
|
||||
ubyte[256] cmap1
|
||||
ubyte[256] cmap2
|
||||
uword camg = 0
|
||||
str chunk_id = "????"
|
||||
uword chunk_size_hi
|
||||
uword chunk_size_lo
|
||||
uword scanline_data_ptr = progend()
|
||||
|
||||
uword width
|
||||
uword height
|
||||
ubyte num_planes
|
||||
uword num_colors
|
||||
ubyte compression
|
||||
ubyte have_cmap = false
|
||||
cmap[0] = 0
|
||||
cmap1[0] = 0
|
||||
cmap2[0] = 0
|
||||
|
||||
if diskio.f_open(8, filenameptr) {
|
||||
size = diskio.f_read(buffer, 12)
|
||||
if size==12 {
|
||||
if buffer[0]=='f' and buffer[1]=='o' and buffer[2]=='r' and buffer[3]=='m'
|
||||
and buffer[8]=='i' and buffer[9]=='l' and buffer[10]=='b' and buffer[11]=='m'{
|
||||
|
||||
while read_chunk_header() {
|
||||
if chunk_id == "bmhd" {
|
||||
void diskio.f_read(buffer, chunk_size_lo)
|
||||
width = mkword(buffer[0], buffer[1])
|
||||
height = mkword(buffer[2], buffer[3])
|
||||
num_planes = buffer[8]
|
||||
num_colors = 2 ** num_planes
|
||||
compression = buffer[10]
|
||||
}
|
||||
else if chunk_id == "camg" {
|
||||
void diskio.f_read(buffer, chunk_size_lo)
|
||||
camg = mkword(buffer[2], buffer[3])
|
||||
if camg & $0800 {
|
||||
txt.print("ham mode not supported!\n")
|
||||
break
|
||||
}
|
||||
}
|
||||
else if chunk_id == "cmap" {
|
||||
have_cmap = true
|
||||
void diskio.f_read(&cmap, chunk_size_lo)
|
||||
}
|
||||
else if chunk_id == "body" {
|
||||
graphics.clear_screen(1, 0)
|
||||
if camg & $0004
|
||||
height /= 2 ; interlaced: just skip every odd scanline later
|
||||
if camg & $0080 and have_cmap
|
||||
make_ehb_palette()
|
||||
palette.set_rgb8(&cmap, num_colors)
|
||||
if compression
|
||||
decode_rle()
|
||||
else
|
||||
decode_raw()
|
||||
load_ok = true
|
||||
break ; done after body
|
||||
}
|
||||
else {
|
||||
skip_chunk()
|
||||
}
|
||||
}
|
||||
} else
|
||||
txt.print("not an iff ilbm file!\n")
|
||||
}
|
||||
|
||||
diskio.f_close()
|
||||
}
|
||||
|
||||
return load_ok
|
||||
|
||||
sub read_chunk_header() -> ubyte {
|
||||
size = diskio.f_read(buffer, 8)
|
||||
if size==8 {
|
||||
chunk_id[0] = buffer[0]
|
||||
chunk_id[1] = buffer[1]
|
||||
chunk_id[2] = buffer[2]
|
||||
chunk_id[3] = buffer[3]
|
||||
chunk_size_hi = mkword(buffer[4], buffer[5])
|
||||
chunk_size_lo = mkword(buffer[6], buffer[7])
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub skip_chunk() {
|
||||
repeat lsb(chunk_size_hi)*8 + (chunk_size_lo >> 13)
|
||||
void diskio.f_read(scanline_data_ptr, $2000)
|
||||
|
||||
void diskio.f_read(scanline_data_ptr, chunk_size_lo & $1fff)
|
||||
}
|
||||
|
||||
sub make_ehb_palette() {
|
||||
; generate 32 additional Extra-Halfbrite colors in the cmap
|
||||
uword palletteptr = &cmap
|
||||
uword ehbptr = palletteptr + 32*3
|
||||
repeat 32 {
|
||||
@(ehbptr) = @(palletteptr)>>1
|
||||
ehbptr++
|
||||
palletteptr++
|
||||
@(ehbptr) = @(palletteptr)>>1
|
||||
ehbptr++
|
||||
palletteptr++
|
||||
@(ehbptr) = @(palletteptr)>>1
|
||||
ehbptr++
|
||||
palletteptr++
|
||||
}
|
||||
}
|
||||
|
||||
uword bitplane_stride
|
||||
uword interleave_stride
|
||||
uword offsetx
|
||||
uword offsety
|
||||
|
||||
sub start_plot() {
|
||||
bitplane_stride = width>>3
|
||||
interleave_stride = bitplane_stride * num_planes
|
||||
offsetx = 0
|
||||
offsety = 0
|
||||
if width < graphics.WIDTH
|
||||
offsetx = (graphics.WIDTH - width - 1) / 2
|
||||
if height < graphics.HEIGHT
|
||||
offsety = (graphics.HEIGHT - height - 1) / 2
|
||||
if width > graphics.WIDTH
|
||||
width = graphics.WIDTH
|
||||
if height > graphics.HEIGHT-1
|
||||
height = graphics.HEIGHT-1
|
||||
}
|
||||
|
||||
sub set_cursor(uword x, uword y) {
|
||||
cx16.r0=offsetx+x
|
||||
cx16.r1=offsety+y
|
||||
cx16.FB_cursor_position()
|
||||
}
|
||||
|
||||
sub decode_raw() {
|
||||
start_plot()
|
||||
ubyte interlaced = (camg & $0004) != 0
|
||||
uword y
|
||||
for y in 0 to height-1 {
|
||||
void diskio.f_read(scanline_data_ptr, interleave_stride)
|
||||
if interlaced
|
||||
void diskio.f_read(scanline_data_ptr, interleave_stride)
|
||||
set_cursor(0, y)
|
||||
planar_to_chunky_scanline()
|
||||
}
|
||||
}
|
||||
|
||||
sub decode_rle() {
|
||||
start_plot()
|
||||
ubyte interlaced = (camg & $0004) != 0
|
||||
uword y
|
||||
for y in 0 to height-1 {
|
||||
decode_rle_scanline()
|
||||
if interlaced
|
||||
decode_rle_scanline()
|
||||
set_cursor(0, y)
|
||||
planar_to_chunky_scanline()
|
||||
}
|
||||
}
|
||||
|
||||
sub decode_rle_scanline() {
|
||||
uword x = interleave_stride
|
||||
uword plane_ptr = scanline_data_ptr
|
||||
|
||||
while x {
|
||||
ubyte b = c64.CHRIN()
|
||||
if b > 128 {
|
||||
ubyte b2 = c64.CHRIN()
|
||||
repeat 2+(b^255) {
|
||||
@(plane_ptr) = b2
|
||||
plane_ptr++
|
||||
x--
|
||||
}
|
||||
} else if b < 128 {
|
||||
repeat b+1 {
|
||||
@(plane_ptr) = c64.CHRIN()
|
||||
plane_ptr++
|
||||
x--
|
||||
}
|
||||
} else
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
sub planar_to_chunky_scanline() {
|
||||
uword x
|
||||
for x in 0 to width-1 {
|
||||
ubyte bitnr = ((lsb(x) ^ 255) & 7) + 1
|
||||
uword pixptr = x/8 + scanline_data_ptr
|
||||
ubyte bits=0
|
||||
repeat num_planes {
|
||||
ubyte bb = @(pixptr) >> bitnr ; extract bit
|
||||
ror(bits) ; shift it into the byte
|
||||
pixptr += bitplane_stride
|
||||
}
|
||||
bits >>= 8-num_planes
|
||||
cx16.FB_set_pixel(bits)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
%target cx16
|
||||
%import graphics
|
||||
%import textio
|
||||
%import diskio
|
||||
%import koala_module
|
||||
%import iff_module
|
||||
%import pcx_module
|
||||
%import bmp_module
|
||||
;; %import ci_module
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
; trick to check if we're running on sdcard or host system shared folder
|
||||
txt.print("\nimage viewer for commander x16\nformats supported: .iff, .pcx, .bmp, .koa (c64 koala)\n\n")
|
||||
if strlen(diskio.status(8)) {
|
||||
txt.print("enter image file name or just enter for all on disk: ")
|
||||
ubyte i = txt.input_chars(diskio.filename)
|
||||
graphics.enable_bitmap_mode()
|
||||
if i
|
||||
attempt_load(diskio.filename)
|
||||
else
|
||||
show_pics_sdcard()
|
||||
|
||||
txt.print("\nnothing more to do.\n")
|
||||
}
|
||||
else
|
||||
txt.print("files are read with sequential file loading.\nin the emulator this currently only works with files on an sd-card image.\nsorry :(\n")
|
||||
|
||||
repeat {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
sub show_pics_sdcard() {
|
||||
|
||||
; load and show the images on the disk with the given extensions.
|
||||
; this only works in the emulator V38 with an sd-card image with the files on it.
|
||||
|
||||
str[40] filename_ptrs
|
||||
ubyte num_files = diskio.list_files(8, 0, false, &filename_ptrs, len(filename_ptrs))
|
||||
if num_files {
|
||||
while num_files {
|
||||
num_files--
|
||||
attempt_load(filename_ptrs[num_files])
|
||||
}
|
||||
} else
|
||||
txt.print("no files in directory!\n")
|
||||
|
||||
}
|
||||
|
||||
sub attempt_load(uword filenameptr) {
|
||||
txt.print(">> ")
|
||||
txt.print(filenameptr)
|
||||
txt.chrout('\n')
|
||||
uword extension = filenameptr + rfind(filenameptr, '.')
|
||||
if strcmp(extension, ".iff")==0 {
|
||||
txt.print("loading ")
|
||||
txt.print("iff\n")
|
||||
if iff_module.show_image(filenameptr)
|
||||
txt.clear_screen()
|
||||
else
|
||||
txt.print("load error!\n")
|
||||
cx16.wait(120)
|
||||
}
|
||||
else if strcmp(extension, ".pcx")==0 {
|
||||
txt.print("loading ")
|
||||
txt.print("pcx\n")
|
||||
if pcx_module.show_image(filenameptr)
|
||||
txt.clear_screen()
|
||||
else
|
||||
txt.print("load error!\n")
|
||||
cx16.wait(120)
|
||||
}
|
||||
else if strcmp(extension, ".koa")==0 {
|
||||
txt.print("loading ")
|
||||
txt.print("koala\n")
|
||||
if koala_module.show_image(filenameptr)
|
||||
txt.clear_screen()
|
||||
else
|
||||
txt.print("load error!\n")
|
||||
cx16.wait(120)
|
||||
}
|
||||
else if strcmp(extension, ".bmp")==0 {
|
||||
txt.print("loading ")
|
||||
txt.print("bmp\n")
|
||||
if bmp_module.show_image(filenameptr)
|
||||
txt.clear_screen()
|
||||
else
|
||||
txt.print("load error!\n")
|
||||
cx16.wait(120)
|
||||
}
|
||||
; else if strcmp(extension, ".ci")==0 {
|
||||
; txt.print("loading ")
|
||||
; txt.print("ci\n")
|
||||
; if ci_module.show_image(filenameptr)
|
||||
; txt.clear_screen()
|
||||
; else
|
||||
; txt.print("load error!\n")
|
||||
; cx16.wait(120)
|
||||
; }
|
||||
}
|
||||
|
||||
sub extension_equals(uword stringptr, uword extensionptr) -> ubyte {
|
||||
ubyte ix = rfind(stringptr, '.')
|
||||
return ix<255 and strcmp(stringptr+ix, extensionptr)==0
|
||||
}
|
||||
|
||||
sub rfind(uword stringptr, ubyte char) -> ubyte {
|
||||
ubyte i
|
||||
for i in strlen(stringptr)-1 downto 0 {
|
||||
if @(stringptr+i)==char
|
||||
return i
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
palette {
|
||||
|
||||
sub set_rgb4(uword palletteptr, uword num_colors) {
|
||||
; 2 bytes per color entry, the Vera uses this, but the R/GB bytes order is swapped
|
||||
uword vera_palette_ptr = $fa00
|
||||
repeat num_colors {
|
||||
cx16.vpoke(1, vera_palette_ptr+1, @(palletteptr))
|
||||
palletteptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, @(palletteptr))
|
||||
palletteptr++
|
||||
vera_palette_ptr+=2
|
||||
}
|
||||
}
|
||||
|
||||
sub set_rgb8(uword palletteptr, uword num_colors) {
|
||||
; 3 bytes per color entry, adjust color depth from 8 to 4 bits per channel.
|
||||
uword vera_palette_ptr = $fa00
|
||||
ubyte red
|
||||
ubyte greenblue
|
||||
repeat num_colors {
|
||||
red = @(palletteptr) >> 4
|
||||
palletteptr++
|
||||
greenblue = @(palletteptr) & %11110000
|
||||
palletteptr++
|
||||
greenblue |= @(palletteptr) >> 4 ; add Blue
|
||||
palletteptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, greenblue)
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, red)
|
||||
vera_palette_ptr++
|
||||
}
|
||||
}
|
||||
|
||||
sub set_bgra(uword palletteptr, uword num_colors) {
|
||||
uword vera_palette_ptr = $fa00
|
||||
ubyte red
|
||||
ubyte greenblue
|
||||
|
||||
; 4 bytes per color entry (BGRA), adjust color depth from 8 to 4 bits per channel.
|
||||
repeat num_colors {
|
||||
red = @(palletteptr+2) >> 4
|
||||
greenblue = @(palletteptr+1) & %11110000
|
||||
greenblue |= @(palletteptr+0) >> 4 ; add Blue
|
||||
palletteptr+=4
|
||||
cx16.vpoke(1, vera_palette_ptr, greenblue)
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, red)
|
||||
vera_palette_ptr++
|
||||
}
|
||||
}
|
||||
|
||||
sub set_monochrome() {
|
||||
uword vera_palette_ptr = $fa00
|
||||
cx16.vpoke(1, vera_palette_ptr, 0)
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, 0)
|
||||
vera_palette_ptr++
|
||||
repeat 255 {
|
||||
cx16.vpoke(1, vera_palette_ptr, 255)
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, 255)
|
||||
vera_palette_ptr++
|
||||
}
|
||||
}
|
||||
|
||||
sub set_grayscale() {
|
||||
ubyte c = 0
|
||||
uword vera_palette_ptr = $fa00
|
||||
repeat 16 {
|
||||
repeat 16 {
|
||||
cx16.vpoke(1, vera_palette_ptr, c)
|
||||
vera_palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, c)
|
||||
vera_palette_ptr++
|
||||
}
|
||||
c += $11
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
%target cx16
|
||||
%import graphics
|
||||
%import diskio
|
||||
%import c64colors
|
||||
|
||||
koala_module {
|
||||
const uword load_location = $6000
|
||||
|
||||
sub show_image(uword filenameptr) -> ubyte {
|
||||
ubyte load_ok=false
|
||||
if diskio.f_open(8, filenameptr) {
|
||||
uword size = diskio.f_read(load_location, 2) ; skip the first 2 bytes (load address)
|
||||
if size==2 {
|
||||
size = diskio.f_read(load_location, 10001)
|
||||
if size == 10001 {
|
||||
; set a better C64 color palette, the Cx16's default is too saturated
|
||||
c64colors.set_palette_pepto()
|
||||
convert_koalapic()
|
||||
load_ok = true
|
||||
}
|
||||
}
|
||||
diskio.f_close()
|
||||
}
|
||||
|
||||
return load_ok
|
||||
}
|
||||
|
||||
sub convert_koalapic() {
|
||||
ubyte cy
|
||||
ubyte @zp cx
|
||||
uword @zp cy_times_forty = 0
|
||||
ubyte @zp d
|
||||
uword bitmap_ptr = load_location
|
||||
|
||||
; theoretically you could put the 8-pixel array in zeropage to squeeze out another tiny bit of performance
|
||||
ubyte[8] pixels
|
||||
|
||||
graphics.clear_screen(1, 0)
|
||||
|
||||
for cy in 0 to 24*8 step 8 {
|
||||
for cx in 0 to 39 {
|
||||
for d in 0 to 7 {
|
||||
cx16.r0 = cx as uword * 8
|
||||
cx16.r1 = cy as uword + d
|
||||
cx16.FB_cursor_position()
|
||||
get_8_pixels()
|
||||
cx16.r0 = &pixels
|
||||
cx16.r1 = 8
|
||||
cx16.FB_set_pixels()
|
||||
}
|
||||
}
|
||||
cy_times_forty += 40
|
||||
}
|
||||
|
||||
sub get_8_pixels() {
|
||||
ubyte bm = @(bitmap_ptr)
|
||||
ubyte @zp m = mcol(bm)
|
||||
pixels[7] = m
|
||||
pixels[6] = m
|
||||
bm >>= 2
|
||||
m = mcol(bm)
|
||||
pixels[5] = m
|
||||
pixels[4] = m
|
||||
bm >>= 2
|
||||
m = mcol(bm)
|
||||
pixels[3] = m
|
||||
pixels[2] = m
|
||||
bm >>= 2
|
||||
m = mcol(bm)
|
||||
pixels[1] = m
|
||||
pixels[0] = m
|
||||
bitmap_ptr++
|
||||
|
||||
sub mcol(ubyte b) -> ubyte {
|
||||
ubyte @zp color
|
||||
when b & 3 {
|
||||
0 -> color = @(load_location + 8000 + 1000 + 1000) & 15
|
||||
1 -> color = @(load_location + 8000 + cy_times_forty + cx) >>4
|
||||
2 -> color = @(load_location + 8000 + cy_times_forty + cx) & 15
|
||||
else -> color = @(load_location + 8000 + 1000 + cy_times_forty + cx) & 15
|
||||
}
|
||||
return color
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
%target cx16
|
||||
%import graphics
|
||||
%import textio
|
||||
%import diskio
|
||||
|
||||
pcx_module {
|
||||
|
||||
sub show_image(uword filenameptr) -> ubyte {
|
||||
ubyte load_ok = false
|
||||
|
||||
if diskio.f_open(8, filenameptr) {
|
||||
ubyte[128] header
|
||||
uword size = diskio.f_read(header, 128)
|
||||
if size==128 {
|
||||
if header[0] == $0a and header[2] == 1 {
|
||||
ubyte bits_per_pixel = header[3]
|
||||
if bits_per_pixel==1 or bits_per_pixel==4 or bits_per_pixel==8 {
|
||||
uword width = mkword(header[$09], header[$08]) - mkword(header[$05], header[$04]) + 1
|
||||
uword height = mkword(header[$0b], header[$0a]) - mkword(header[$07], header[$06]) + 1
|
||||
ubyte number_of_planes = header[$41]
|
||||
uword palette_format = mkword(header[$45], header[$44])
|
||||
uword num_colors = 2**bits_per_pixel
|
||||
if number_of_planes == 1 {
|
||||
if (width & 7) == 0 {
|
||||
graphics.clear_screen(1, 0)
|
||||
if palette_format==2
|
||||
palette.set_grayscale()
|
||||
else if num_colors == 16
|
||||
palette.set_rgb8(&header + $10, 16)
|
||||
else if num_colors == 2
|
||||
palette.set_monochrome()
|
||||
when bits_per_pixel {
|
||||
8 -> load_ok = bitmap.do8bpp(width, height)
|
||||
4 -> load_ok = bitmap.do4bpp(width, height)
|
||||
1 -> load_ok = bitmap.do1bpp(width, height)
|
||||
}
|
||||
if load_ok {
|
||||
load_ok = c64.CHRIN()
|
||||
if load_ok == 12 {
|
||||
; there is 256 colors of palette data at the end
|
||||
uword palette_mem = progend()
|
||||
load_ok = false
|
||||
size = diskio.f_read(palette_mem, 3*256)
|
||||
if size==3*256 {
|
||||
load_ok = true
|
||||
palette.set_rgb8(palette_mem, num_colors)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
txt.print("width not multiple of 8!\n")
|
||||
} else
|
||||
txt.print("has >256 colors!\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
diskio.f_close()
|
||||
}
|
||||
|
||||
return load_ok
|
||||
}
|
||||
}
|
||||
|
||||
bitmap {
|
||||
|
||||
uword offsetx
|
||||
uword offsety
|
||||
uword py
|
||||
uword px
|
||||
ubyte y_ok
|
||||
ubyte status
|
||||
|
||||
sub start_plot(uword width, uword height) {
|
||||
offsetx = 0
|
||||
offsety = 0
|
||||
y_ok = true
|
||||
py = 0
|
||||
px = 0
|
||||
if width < graphics.WIDTH
|
||||
offsetx = (graphics.WIDTH - width - 1) / 2
|
||||
if height < graphics.HEIGHT
|
||||
offsety = (graphics.HEIGHT - height - 1) / 2
|
||||
status = (not c64.READST()) or c64.READST()==64
|
||||
}
|
||||
|
||||
sub set_cursor(uword x, uword y) {
|
||||
cx16.r0=offsetx+x
|
||||
cx16.r1=offsety+y
|
||||
cx16.FB_cursor_position()
|
||||
}
|
||||
|
||||
sub next_scanline() {
|
||||
px = 0
|
||||
py++
|
||||
y_ok = py < graphics.HEIGHT-1
|
||||
set_cursor(0, py)
|
||||
status = (not c64.READST()) or c64.READST()==64
|
||||
}
|
||||
|
||||
sub do1bpp(uword width, uword height) -> ubyte {
|
||||
start_plot(width, height)
|
||||
set_cursor(0, 0)
|
||||
while py < height and status {
|
||||
ubyte b = c64.CHRIN()
|
||||
if b>>6==3 {
|
||||
b &= %00111111
|
||||
ubyte dat = c64.CHRIN()
|
||||
repeat b {
|
||||
if y_ok {
|
||||
cx16.r0 = dat
|
||||
cx16.FB_set_8_pixels_opaque(255, 255, 0)
|
||||
}
|
||||
px += 8
|
||||
}
|
||||
} else {
|
||||
if y_ok {
|
||||
cx16.r0 = b
|
||||
cx16.FB_set_8_pixels_opaque(255, 255, 0)
|
||||
}
|
||||
px += 8
|
||||
}
|
||||
if px==width
|
||||
next_scanline()
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
sub do4bpp(uword width, uword height) -> ubyte {
|
||||
start_plot(width, height)
|
||||
set_cursor(0, 0)
|
||||
while py < height and status {
|
||||
ubyte b = c64.CHRIN()
|
||||
if b>>6==3 {
|
||||
b &= %00111111
|
||||
ubyte dat = c64.CHRIN()
|
||||
if y_ok
|
||||
repeat b {
|
||||
cx16.FB_set_pixel(dat>>4)
|
||||
cx16.FB_set_pixel(dat & 15)
|
||||
}
|
||||
px += b*2
|
||||
} else {
|
||||
if y_ok {
|
||||
cx16.FB_set_pixel(b>>4)
|
||||
cx16.FB_set_pixel(b & 15)
|
||||
}
|
||||
px += 2
|
||||
}
|
||||
if px==width
|
||||
next_scanline()
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
sub do8bpp(uword width, uword height) -> ubyte {
|
||||
start_plot(width, height)
|
||||
set_cursor(0, 0)
|
||||
while py < height and status {
|
||||
ubyte b = c64.CHRIN()
|
||||
if b>>6==3 {
|
||||
b &= %00111111
|
||||
ubyte dat = c64.CHRIN()
|
||||
if y_ok
|
||||
repeat b
|
||||
cx16.FB_set_pixel(dat)
|
||||
px += b
|
||||
} else {
|
||||
if y_ok
|
||||
cx16.FB_set_pixel(b)
|
||||
px++
|
||||
}
|
||||
if px==width
|
||||
next_scanline()
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
; CommanderX16 simple graphics example!
|
||||
|
||||
%target cx16
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
void cx16.screen_set_mode($80)
|
||||
cx16.r0=0
|
||||
void cx16.screen_set_mode(0)
|
||||
|
||||
cx16.FB_init()
|
||||
cx16.GRAPH_init()
|
||||
cx16.r0 = 0
|
||||
cx16.r1 = 0
|
||||
cx16.FB_cursor_position()
|
||||
ubyte color
|
||||
repeat 320*199 {
|
||||
cx16.FB_set_pixel(color)
|
||||
color++
|
||||
}
|
||||
|
||||
uword xx
|
||||
for xx in 0 to 319 step 32 {
|
||||
ubyte q
|
||||
for q in 0 to 31 {
|
||||
cx16.GRAPH_set_colors(q, 2, 0)
|
||||
cx16.r0 = xx+q
|
||||
cx16.r1=0
|
||||
cx16.r2=rnd()
|
||||
cx16.r3=199
|
||||
cx16.GRAPH_draw_line()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
%import textio
|
||||
%import diskio
|
||||
%zeropage basicsafe
|
||||
%import test_stack
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.print("showing the full directory of drive 8:\n")
|
||||
void diskio.directory(8)
|
||||
txt.chrout('\n')
|
||||
|
||||
if diskio.lf_start_list(8, "cub", false) {
|
||||
txt.print("\nfiles starting with 'cub':\n")
|
||||
while diskio.lf_next_entry() {
|
||||
txt.print(diskio.list_filename)
|
||||
txt.print(" ")
|
||||
txt.print_uw(diskio.list_blocks)
|
||||
txt.print(" blocks\n")
|
||||
}
|
||||
diskio.lf_end_list()
|
||||
} else {
|
||||
txt.print("error\n")
|
||||
}
|
||||
|
||||
if diskio.lf_start_list(8, "gfx", true) {
|
||||
txt.print("\nfiles ending with 'gfx':\n")
|
||||
while diskio.lf_next_entry() {
|
||||
txt.print(diskio.list_filename)
|
||||
txt.print(" ")
|
||||
txt.print_uw(diskio.list_blocks)
|
||||
txt.print(" blocks\n")
|
||||
}
|
||||
diskio.lf_end_list()
|
||||
} else {
|
||||
txt.print("error\n")
|
||||
}
|
||||
|
||||
if diskio.lf_start_list(8, 0, false) {
|
||||
txt.print("\nfirst 10 files:\n")
|
||||
ubyte counter=0
|
||||
while counter < 10 and diskio.lf_next_entry() {
|
||||
txt.print(diskio.list_filename)
|
||||
txt.print(" ")
|
||||
txt.print_uw(diskio.list_blocks)
|
||||
txt.print(" blocks\n")
|
||||
counter++
|
||||
}
|
||||
diskio.lf_end_list()
|
||||
} else {
|
||||
txt.print("error\n")
|
||||
}
|
||||
|
||||
; test_stack.test()
|
||||
}
|
||||
}
|
||||
+11
-15
@@ -41,7 +41,7 @@ main {
|
||||
rect(10, 10, 10, 10, false)
|
||||
rect(6, 0, 16, 20, true)
|
||||
|
||||
; test_stack.test()
|
||||
test_stack.test()
|
||||
|
||||
|
||||
sub rect(ubyte x1, ubyte y1, ubyte x2, ubyte y2, ubyte fill) {
|
||||
@@ -140,21 +140,17 @@ main {
|
||||
ubyte xx
|
||||
|
||||
while x>=y {
|
||||
for xx in cx to cx+x {
|
||||
txt.setcc(xx, cy + y as ubyte, 81, 1)
|
||||
txt.setcc(xx, cy - y as ubyte, 81, 2)
|
||||
xx = cx-x
|
||||
repeat 2*x+1 {
|
||||
txt.setcc(xx, cy + y as ubyte, 81, 11)
|
||||
txt.setcc(xx, cy - y as ubyte, 81, 12)
|
||||
xx++
|
||||
}
|
||||
for xx in cx-x to cx-1 {
|
||||
txt.setcc(xx, cy + y as ubyte, 81, 3)
|
||||
txt.setcc(xx, cy - y as ubyte, 81, 4)
|
||||
}
|
||||
for xx in cx to cx+y {
|
||||
txt.setcc(xx, cy + x as ubyte, 81, 5)
|
||||
txt.setcc(xx, cy - x as ubyte, 81, 6)
|
||||
}
|
||||
for xx in cx-y to cx {
|
||||
txt.setcc(xx, cy + x as ubyte, 81, 7)
|
||||
txt.setcc(xx, cy - x as ubyte, 81, 8)
|
||||
xx = cx-y
|
||||
repeat 2*y+1 {
|
||||
txt.setcc(xx, cy + x as ubyte, 81, 13)
|
||||
txt.setcc(xx, cy - x as ubyte, 81, 14)
|
||||
xx++
|
||||
}
|
||||
y++
|
||||
if decisionOver2<=0
|
||||
|
||||
@@ -45,6 +45,7 @@ main {
|
||||
}
|
||||
}
|
||||
|
||||
; graphics.disable_bitmap_mode()
|
||||
; test_stack.test()
|
||||
|
||||
repeat {
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@
|
||||
; Converted to prog8 by Irmen de Jong.
|
||||
|
||||
|
||||
|
||||
main {
|
||||
const uword SCREEN1 = $E000
|
||||
const uword SCREEN2 = $E400
|
||||
@@ -27,7 +28,7 @@ main {
|
||||
; ubyte v = c64.VMCSB
|
||||
c64.CIA2PRA = (block & $FC) | (lsb(SCREEN1 >> 14) ^ $03)
|
||||
|
||||
repeat 100 {
|
||||
repeat {
|
||||
doplasma(SCREEN1)
|
||||
c64.VMCSB = PAGE1
|
||||
doplasma(SCREEN2)
|
||||
|
||||
+4
-38
@@ -1,48 +1,14 @@
|
||||
%import textio
|
||||
%import diskio
|
||||
%import floats
|
||||
%import graphics
|
||||
%zeropage basicsafe
|
||||
%import test_stack
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
ubyte[] ubarray = [1,2,3,4]
|
||||
byte[] barray = [1,2,3,4]
|
||||
uword[] uwarray = [1,2,3,4]
|
||||
word[] warray = [1,2,3,4]
|
||||
float[] farray = [1.1,2.2,3.3,4.4]
|
||||
|
||||
ubyte ub
|
||||
byte bb
|
||||
uword uw
|
||||
word ww
|
||||
float fl
|
||||
|
||||
const ubyte i = 2
|
||||
const ubyte j = 3
|
||||
|
||||
ub = ubarray[i]
|
||||
txt.print_ub(ub)
|
||||
txt.chrout('\n')
|
||||
|
||||
bb = barray[i]
|
||||
txt.print_b(bb)
|
||||
txt.chrout('\n')
|
||||
|
||||
uw = uwarray[i]
|
||||
txt.print_uw(uw)
|
||||
txt.chrout('\n')
|
||||
|
||||
ww = warray[i]
|
||||
txt.print_w(ww)
|
||||
txt.chrout('\n')
|
||||
|
||||
fl = farray[i]
|
||||
floats.print_f(fl)
|
||||
txt.chrout('\n')
|
||||
sub start () {
|
||||
|
||||
test_stack.test()
|
||||
txt.chrout('\n')
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ turtle {
|
||||
uword xx = xpos as uword
|
||||
c64.SPXY[0] = lsb(xx) + 12
|
||||
c64.MSIGX = msb(xx) > 0
|
||||
c64.SPXY[1] = ypos as ubyte + 37
|
||||
c64.SPXY[1] = ypos as ubyte + 40
|
||||
}
|
||||
|
||||
sub pos(float x, float y) {
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@ plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
targetCompatibility = 1.8
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 11
|
||||
sourceCompatibility = 11
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<filetype binary="false" description="Prog8 source file" name="Prog8">
|
||||
<highlighting>
|
||||
<options>
|
||||
<option name="LINE_COMMENT" value=";" />
|
||||
<option name="COMMENT_START" value="{{" />
|
||||
<option name="COMMENT_END" value="}}" />
|
||||
<option name="HEX_PREFIX" value="$" />
|
||||
<option name="NUM_POSTFIXES" value="" />
|
||||
<option name="HAS_BRACES" value="true" />
|
||||
<option name="HAS_BRACKETS" value="true" />
|
||||
<option name="HAS_PARENS" value="true" />
|
||||
<option name="HAS_STRING_ESCAPES" value="true" />
|
||||
</options>
|
||||
<keywords keywords="&;->;@;\$;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" />
|
||||
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%target;%zeropage;%zpreserved" />
|
||||
<keywords3 keywords="byte;const;float;str;struct;ubyte;uword;void;word;zp" />
|
||||
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;clear_carry;clear_irqd;cos;cos16;cos16u;cos8;cos8u;deg;exit;floor;leftstr;len;ln;log2;lsb;lsl;lsr;max;memcopy;memset;memsetw;min;mkword;msb;progend;rad;read_flags;reverse;rightstr;rnd;rndf;rndw;rol;rol2;ror;ror2;round;rrestore;rsave;set_carry;set_irqd;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;strcmp;strlen;substr;sum;swap;tan" />
|
||||
</highlighting>
|
||||
<extensionMap>
|
||||
<mapping ext="p8" />
|
||||
<mapping ext="prog8" />
|
||||
</extensionMap>
|
||||
</filetype>
|
||||
@@ -0,0 +1,11 @@
|
||||
Prog8 syntax highlighting file for IntelliJ IDEA.
|
||||
|
||||
Copy the file Prog8.xml to your IDEA filetypes folder.
|
||||
|
||||
The exact path may vary with the version of the IDE,
|
||||
but for me it is currently this on Linux:
|
||||
|
||||
$HOME/.config/JetBrains/IntelliJIdea2020.2/filetypes/
|
||||
|
||||
|
||||
(note the version number in the path, adjust accordingly)
|
||||
@@ -0,0 +1 @@
|
||||
TODO
|
||||
@@ -0,0 +1 @@
|
||||
TODO
|
||||
@@ -0,0 +1,4 @@
|
||||
A contributed syntax definition file for Sublime Text 3 can be obtained from:
|
||||
|
||||
https://github.com/akubiczek/Prog8-TmLanguage-VsCode/tree/master/sublime3
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
A contributed syntax definition file for Visual Studio Code can be obtained from:
|
||||
|
||||
https://github.com/akubiczek/Prog8-TmLanguage-VsCode
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
TODO
|
||||
Reference in new issue
Block a user