mirror of
https://github.com/irmen/prog8.git
synced 2026-10-23 12:02:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
747ee32e81 | ||
|
|
75fadaa24f | ||
|
|
e4ea1f1014 | ||
|
|
cd2c4e13da | ||
|
|
f5ba072294 | ||
|
|
87d6312a37 | ||
|
|
3af7d4c930 | ||
|
|
0fc3071a21 | ||
|
|
7f36d08acc | ||
|
|
b040e5ddad | ||
|
|
f36ce5e0ee | ||
|
|
ffbdac7e9a | ||
|
|
f2b03342ac | ||
|
|
52ff61470b | ||
|
|
28277469b6 | ||
|
|
aa98104d54 | ||
|
|
9be70bcbe7 | ||
|
|
3a6fae4447 | ||
|
|
06f0984fa1 | ||
|
|
77dc35dc6a | ||
|
|
ed43f7cd9b | ||
|
|
32405a1637 | ||
|
|
43cab3f247 | ||
|
|
5ea2f2d4db | ||
|
|
b8ae808b65 | ||
|
|
96ecbc9fe4 | ||
|
|
588133d418 | ||
|
|
2f1249489b | ||
|
|
95f7c9bad0 | ||
|
|
8811d2f7c5 | ||
|
|
d6ca1e6a12 | ||
|
|
b0ad66bd04 | ||
|
|
c1d2b4601b | ||
|
|
c265625ed1 | ||
|
|
52352d9d04 | ||
|
|
cc5898d010 | ||
|
|
8684f0c8f5 | ||
|
|
d05d8de447 | ||
|
|
29b7d91293 | ||
|
|
bcdf3f2b83 | ||
|
|
ee497d2ffb | ||
|
|
9f8ae485c3 | ||
|
|
3b32fb74f7 | ||
|
|
7ff1af3934 | ||
|
|
ae21e03e1d | ||
|
|
f0a504baec | ||
|
|
f83b9732ee | ||
|
|
86ff08e854 | ||
|
|
b911a95fc2 | ||
|
|
73b0cc4056 | ||
|
|
8e36a64d49 | ||
|
|
255c808b16 | ||
|
|
b311b5ef4a | ||
|
|
9a8ada7e75 | ||
|
|
32a9ab30fb | ||
|
|
5c8d7c1255 | ||
|
|
53045b9e36 | ||
|
|
d35d28ac5a | ||
|
|
993dcde985 | ||
|
|
0ecef00b13 | ||
|
|
13a5eabc09 | ||
|
|
b8624c72d9 | ||
|
|
49c5b2b107 | ||
|
|
1e89bea288 | ||
|
|
2c64f95b06 | ||
|
|
037c78cfaf | ||
|
|
de777b1308 | ||
|
|
b3df3c999d | ||
|
|
4790c3a793 | ||
|
|
1a2137c228 | ||
|
|
7459896155 | ||
|
|
0219c69446 | ||
|
|
798c4d7902 | ||
|
|
c4a28b8502 | ||
|
|
3a8f069854 | ||
|
|
204cc03fc8 | ||
|
|
953cf9a335 | ||
|
|
6f583e7092 |
No files matched your search
@@ -29,3 +29,6 @@ compiler/src/prog8_kotlin.jar
|
||||
compiler/src/compiled_java
|
||||
.attach_pid*
|
||||
|
||||
.gradle
|
||||
build/
|
||||
/prog8compiler.jar
|
||||
@@ -99,3 +99,16 @@ When this code is compiled::
|
||||
you get a program that outputs this when loaded on a C-64:
|
||||
|
||||

|
||||
|
||||
|
||||
One of the included examples (wizzine.p8) animates a bunch of sprite balloons and looks like this:
|
||||
|
||||

|
||||
|
||||
Another example (cube3d-sprites.p8) draws the vertices of a rotating 3d cube:
|
||||
|
||||

|
||||
|
||||
If you want to play a video game, a fully working Tetris clone is included in the examples:
|
||||
|
||||

|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "Compiling the parser..."
|
||||
java -jar ./parser/antlr/lib/antlr-4.7.2-complete.jar -o ./parser/src/prog8/parser -Xexact-output-dir -no-listener -no-visitor -package prog8.parser ./parser/antlr/prog8.g4
|
||||
java -jar ./parser/antlr/lib/antlr-4.7.2-complete.jar -o ./parser/src/prog8/parser -Xexact-output-dir -no-listener -no-visitor ./parser/antlr/prog8.g4
|
||||
|
||||
|
||||
PARSER_CLASSES=./out/production/parser
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm" version "1.3.21"
|
||||
id 'application'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
def kotlinVersion = '1.3.21'
|
||||
|
||||
dependencies {
|
||||
implementation project(':parser')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||
runtime "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||
runtime 'org.antlr:antlr4-runtime:4.7.2'
|
||||
runtime project(':parser')
|
||||
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5:$kotlinVersion"
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2'
|
||||
testImplementation 'org.hamcrest:hamcrest-junit:2.0.0.0'
|
||||
}
|
||||
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
}
|
||||
resources {
|
||||
srcDirs = ["${project.projectDir}/res"]
|
||||
}
|
||||
}
|
||||
test {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/test"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
application {
|
||||
mainClassName = 'prog8.CompilerMainKt'
|
||||
applicationName = 'p8compile'
|
||||
}
|
||||
|
||||
task p8vmScript(type: CreateStartScripts) {
|
||||
mainClassName = "prog8.StackVmMainKt"
|
||||
applicationName = "p8vm"
|
||||
outputDir = new File(project.buildDir, 'scripts')
|
||||
classpath = jar.outputs.files + project.configurations.runtime
|
||||
}
|
||||
|
||||
applicationDistribution.into("bin") {
|
||||
from(p8vmScript)
|
||||
fileMode = 0755
|
||||
}
|
||||
|
||||
task fatJar(type: Jar) {
|
||||
manifest {
|
||||
attributes 'Main-Class': 'prog8.CompilerMainKt'
|
||||
}
|
||||
archiveBaseName = 'prog8compiler'
|
||||
destinationDirectory = rootProject.projectDir
|
||||
from {
|
||||
project.configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
with jar
|
||||
}
|
||||
// build.finalizedBy(fatJar)
|
||||
@@ -43,7 +43,7 @@
|
||||
memory ubyte SPRPTR6 = 2046
|
||||
memory ubyte SPRPTR7 = 2047
|
||||
memory ubyte[8] SPRPTR = 2040 ; the 8 sprite pointers as an array.
|
||||
|
||||
|
||||
|
||||
; ---- VIC-II 6567/6569/856x registers ----
|
||||
|
||||
@@ -99,14 +99,14 @@
|
||||
memory ubyte SP6COL = $d02d
|
||||
memory ubyte SP7COL = $d02e
|
||||
memory ubyte[8] SPCOL = $d027
|
||||
|
||||
|
||||
|
||||
; ---- end of VIC-II registers ----
|
||||
|
||||
; ---- CIA 6526 1 & 2 registers ----
|
||||
|
||||
memory ubyte CIA1PRA = $DC00 ; CIA 1 DRA, keyboard column drive
|
||||
memory ubyte CIA1PRB = $DC01 ; CIA 1 DRB, keyboard row port
|
||||
memory ubyte CIA1PRA = $DC00 ; CIA 1 DRA, keyboard column drive (and joystick control port #2)
|
||||
memory ubyte CIA1PRB = $DC01 ; CIA 1 DRB, keyboard row port (and joystick control port #1)
|
||||
memory ubyte CIA1DDRA = $DC02 ; CIA 1 DDRA, keyboard column
|
||||
memory ubyte CIA1DDRB = $DC03 ; CIA 1 DDRB, keyboard row
|
||||
memory ubyte CIA1TAL = $DC04 ; CIA 1 timer A low byte
|
||||
|
||||
@@ -99,6 +99,10 @@ asmsub uword2bcd (uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
%asm {{
|
||||
sta c64.SCRATCH_ZPB1
|
||||
sty c64.SCRATCH_ZPREG
|
||||
php
|
||||
pla ; read status register
|
||||
and #%00000100
|
||||
sta _had_irqd
|
||||
sei ; disable interrupts because of bcd math
|
||||
sed ; switch to decimal mode
|
||||
lda #0 ; ensure the result is clear
|
||||
@@ -121,8 +125,11 @@ asmsub uword2bcd (uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
dey ; and repeat for next bit
|
||||
bne -
|
||||
cld ; back to binary
|
||||
cli ; enable interrupts again
|
||||
rts
|
||||
lda _had_irqd
|
||||
bne +
|
||||
cli ; enable interrupts again (only if they were enabled before)
|
||||
+ rts
|
||||
_had_irqd .byte 0
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -713,27 +720,6 @@ asmsub print (str text @ AY) -> clobbers(A,Y) -> () {
|
||||
}
|
||||
|
||||
|
||||
asmsub print_p (str_p text @ AY) -> clobbers(A) -> (ubyte @ Y) {
|
||||
; ---- print pstring (length as first byte) from A/Y, returns str len in Y
|
||||
%asm {{
|
||||
sta c64.SCRATCH_ZPB1
|
||||
sty c64.SCRATCH_ZPREG
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
ldy #0
|
||||
lda (c64.SCRATCH_ZPB1),y
|
||||
beq +
|
||||
tax
|
||||
- iny
|
||||
lda (c64.SCRATCH_ZPB1),y
|
||||
jsr c64.CHROUT
|
||||
dex
|
||||
bne -
|
||||
+ ldx c64.SCRATCH_ZPREGX
|
||||
rts ; output string length is in Y
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) -> clobbers(A,Y) -> () {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
@@ -763,13 +749,12 @@ _print_byte_digits
|
||||
bne _print_hundreds
|
||||
cpx #'0'
|
||||
bne _print_tens
|
||||
pla
|
||||
jmp c64.CHROUT
|
||||
jmp _end
|
||||
_print_hundreds tya
|
||||
jsr c64.CHROUT
|
||||
_print_tens txa
|
||||
jsr c64.CHROUT
|
||||
pla
|
||||
_end pla
|
||||
jsr c64.CHROUT
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
rts
|
||||
@@ -973,6 +958,25 @@ _screenrows .word $0400 + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub getchr (ubyte col @Y, ubyte row @A) -> clobbers(Y) -> (ubyte @ A) {
|
||||
; ---- get the character in the screen matrix at the given location
|
||||
%asm {{
|
||||
sty c64.SCRATCH_ZPB1
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _mod+2
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub setclr (ubyte col @Y, ubyte row @A) -> clobbers(A) -> () {
|
||||
; ---- set the color in SCRATCH_ZPB1 on the screen matrix at the given position
|
||||
%asm {{
|
||||
@@ -995,6 +999,24 @@ _colorrows .word $d800 + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub getclr (ubyte col @Y, ubyte row @A) -> clobbers(Y) -> (ubyte @ A) {
|
||||
; ---- get the color in the screen color matrix at the given location
|
||||
%asm {{
|
||||
sty c64.SCRATCH_ZPB1
|
||||
asl a
|
||||
tay
|
||||
lda setclr._colorrows+1,y
|
||||
sta _mod+2
|
||||
lda setclr._colorrows,y
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte color) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
|
||||
+169
-228
@@ -241,26 +241,24 @@ mul_byte_3 .proc
|
||||
.pend
|
||||
|
||||
mul_word_3 .proc
|
||||
; W + W*2
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
; W*2 + W
|
||||
lda c64.ESTACK_HI+1,x
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
sta c64.SCRATCH_ZPREG
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
clc
|
||||
lda c64.ESTACK_LO+1,x
|
||||
adc c64.SCRATCH_ZPWORD1
|
||||
adc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda c64.ESTACK_HI+1,x
|
||||
adc c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPREG
|
||||
adc c64.ESTACK_HI+1,x
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
mul_byte_5 .proc
|
||||
; X + X*4
|
||||
; X*4 + X
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
@@ -271,58 +269,49 @@ mul_byte_5 .proc
|
||||
.pend
|
||||
|
||||
mul_word_5 .proc
|
||||
; W + W*4
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
; W*4 + W
|
||||
lda c64.ESTACK_HI+1,x
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
sta c64.SCRATCH_ZPREG
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
clc
|
||||
lda c64.ESTACK_LO+1,x
|
||||
adc c64.SCRATCH_ZPWORD1
|
||||
adc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda c64.ESTACK_HI+1,x
|
||||
adc c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPREG
|
||||
adc c64.ESTACK_HI+1,x
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
mul_byte_6 .proc
|
||||
; X*2 + X*4
|
||||
; (X*2 + X)*2
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
sta c64.SCRATCH_ZPREG
|
||||
clc
|
||||
adc c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPREG
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_word_6 .proc
|
||||
; W*2 + W*4
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
; (W*2 + W)*2
|
||||
lda c64.ESTACK_HI+1,x
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
sta c64.SCRATCH_ZPWORD2+1
|
||||
asl c64.SCRATCH_ZPWORD2
|
||||
rol c64.SCRATCH_ZPWORD2+1
|
||||
sta c64.SCRATCH_ZPREG
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
clc
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
adc c64.SCRATCH_ZPWORD2
|
||||
adc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
adc c64.SCRATCH_ZPWORD2+1
|
||||
lda c64.SCRATCH_ZPREG
|
||||
adc c64.ESTACK_HI+1,x
|
||||
asl c64.ESTACK_LO+1,x
|
||||
rol a
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
@@ -341,29 +330,26 @@ mul_byte_7 .proc
|
||||
|
||||
mul_word_7 .proc
|
||||
; W*8 - W
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
lda c64.ESTACK_HI+1,x
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
sta c64.SCRATCH_ZPREG
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
sec
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
sbc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPREG
|
||||
sbc c64.ESTACK_HI+1,x
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_byte_9 .proc
|
||||
; X + X*8
|
||||
; X*8 + X
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
@@ -375,80 +361,67 @@ mul_byte_9 .proc
|
||||
.pend
|
||||
|
||||
mul_word_9 .proc
|
||||
; W + W*8
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
; W*8 + W
|
||||
lda c64.ESTACK_HI+1,x
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
sta c64.SCRATCH_ZPREG
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
clc
|
||||
lda c64.ESTACK_LO+1,x
|
||||
adc c64.SCRATCH_ZPWORD1
|
||||
adc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda c64.ESTACK_HI+1,x
|
||||
adc c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPREG
|
||||
adc c64.ESTACK_HI+1,x
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_byte_10 .proc
|
||||
; X + X + X*8
|
||||
; (X*4 + X)*2
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
clc
|
||||
adc c64.ESTACK_LO+1,x
|
||||
clc
|
||||
adc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_word_10 .proc
|
||||
; W*2 + W*8
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
; (W*4 + W)*2
|
||||
lda c64.ESTACK_HI+1,x
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
sta c64.SCRATCH_ZPWORD2+1
|
||||
asl c64.SCRATCH_ZPWORD2
|
||||
rol c64.SCRATCH_ZPWORD2+1
|
||||
asl c64.SCRATCH_ZPWORD2
|
||||
rol c64.SCRATCH_ZPWORD2+1
|
||||
sta c64.SCRATCH_ZPREG
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
clc
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
adc c64.SCRATCH_ZPWORD2
|
||||
adc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
adc c64.SCRATCH_ZPWORD2+1
|
||||
lda c64.SCRATCH_ZPREG
|
||||
adc c64.ESTACK_HI+1,x
|
||||
asl c64.ESTACK_LO+1,x
|
||||
rol a
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_byte_11 .proc
|
||||
; X + X + X + X*8
|
||||
; (X*2 + X)*4 - X
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPREG
|
||||
clc
|
||||
adc c64.SCRATCH_ZPREG
|
||||
clc
|
||||
adc c64.SCRATCH_ZPREG
|
||||
adc c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
sec
|
||||
sbc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
@@ -456,58 +429,47 @@ mul_byte_11 .proc
|
||||
; mul_word_11 is skipped (too much code)
|
||||
|
||||
mul_byte_12 .proc
|
||||
; X*4 + X*8
|
||||
; (X*2 + X)*4
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
sta c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPREG
|
||||
adc c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_word_12 .proc
|
||||
; W*4 + W*8
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
; (W*2 + W)*4
|
||||
lda c64.ESTACK_HI+1,x
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
sta c64.SCRATCH_ZPWORD2+1
|
||||
asl c64.SCRATCH_ZPWORD2
|
||||
rol c64.SCRATCH_ZPWORD2+1
|
||||
sta c64.SCRATCH_ZPREG
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
clc
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
adc c64.SCRATCH_ZPWORD2
|
||||
adc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
adc c64.SCRATCH_ZPWORD2+1
|
||||
lda c64.SCRATCH_ZPREG
|
||||
adc c64.ESTACK_HI+1,x
|
||||
asl c64.ESTACK_LO+1,x
|
||||
rol a
|
||||
asl c64.ESTACK_LO+1,x
|
||||
rol a
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_byte_13 .proc
|
||||
; X*16 - X -X -X
|
||||
; (X*2 + X)*4 + X
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
clc
|
||||
adc c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
sec
|
||||
sbc c64.SCRATCH_ZPREG
|
||||
sec
|
||||
sbc c64.SCRATCH_ZPREG
|
||||
sec
|
||||
sbc c64.SCRATCH_ZPREG
|
||||
clc
|
||||
adc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
@@ -515,16 +477,14 @@ mul_byte_13 .proc
|
||||
; mul_word_13 is skipped (too much code)
|
||||
|
||||
mul_byte_14 .proc
|
||||
; X*16 - X -X
|
||||
; (X*8 - X)*2
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
sec
|
||||
sbc c64.ESTACK_LO+1,x
|
||||
sec
|
||||
sec
|
||||
sbc c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
@@ -534,7 +494,6 @@ mul_byte_14 .proc
|
||||
mul_byte_15 .proc
|
||||
; X*16 - X
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
@@ -547,80 +506,70 @@ mul_byte_15 .proc
|
||||
|
||||
mul_word_15 .proc
|
||||
; W*16 - W
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
lda c64.ESTACK_HI+1,x
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
sta c64.SCRATCH_ZPREG
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
sec
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
sbc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPREG
|
||||
sbc c64.ESTACK_HI+1,x
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_byte_20 .proc
|
||||
; X*4 + X*16
|
||||
; (X*4 + X)*4
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
sta c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPREG
|
||||
adc c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_word_20 .proc
|
||||
; W*4 + W*16
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
; (W*4 + W)*4
|
||||
lda c64.ESTACK_HI+1,x
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
sta c64.SCRATCH_ZPWORD2+1
|
||||
asl c64.SCRATCH_ZPWORD2
|
||||
rol c64.SCRATCH_ZPWORD2+1
|
||||
asl c64.SCRATCH_ZPWORD2
|
||||
rol c64.SCRATCH_ZPWORD2+1
|
||||
sta c64.SCRATCH_ZPREG
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
clc
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
adc c64.SCRATCH_ZPWORD2
|
||||
adc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
adc c64.SCRATCH_ZPWORD2+1
|
||||
lda c64.SCRATCH_ZPREG
|
||||
adc c64.ESTACK_HI+1,x
|
||||
asl c64.ESTACK_LO+1,x
|
||||
rol a
|
||||
asl c64.ESTACK_LO+1,x
|
||||
rol a
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_byte_25 .proc
|
||||
; X + X*8 + X*16
|
||||
; (X*2 + X)*8 + X
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
sta c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPREG
|
||||
adc c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
@@ -629,76 +578,68 @@ mul_byte_25 .proc
|
||||
|
||||
mul_word_25 .proc
|
||||
; W + W*8 + W*16
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
lda c64.ESTACK_HI+1,x
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
clc
|
||||
lda c64.ESTACK_LO+1,x
|
||||
adc c64.SCRATCH_ZPWORD1
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
clc
|
||||
adc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda c64.ESTACK_HI+1,x
|
||||
adc c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
adc c64.ESTACK_HI+1,x
|
||||
sta c64.ESTACK_HI+1,x
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
clc
|
||||
lda c64.ESTACK_LO+1,x
|
||||
adc c64.SCRATCH_ZPWORD1
|
||||
adc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda c64.ESTACK_HI+1,x
|
||||
adc c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
adc c64.ESTACK_HI+1,x
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_byte_40 .proc
|
||||
; X*8 + X*32
|
||||
; (X*4 + X)*8
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
sta c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPREG
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_word_40 .proc
|
||||
; W*8 + W*32
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
; (W*4 + W)*8
|
||||
lda c64.ESTACK_HI+1,x
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
sta c64.ESTACK_HI+1,x
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
asl c64.SCRATCH_ZPWORD1
|
||||
rol c64.SCRATCH_ZPWORD1+1
|
||||
sta c64.SCRATCH_ZPREG
|
||||
lda c64.ESTACK_LO+1,x
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
clc
|
||||
lda c64.ESTACK_LO+1,x
|
||||
adc c64.SCRATCH_ZPWORD1
|
||||
adc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda c64.ESTACK_HI+1,x
|
||||
adc c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPREG
|
||||
adc c64.ESTACK_HI+1,x
|
||||
asl c64.ESTACK_LO+1,x
|
||||
rol a
|
||||
asl c64.ESTACK_LO+1,x
|
||||
rol a
|
||||
asl c64.ESTACK_LO+1,x
|
||||
rol a
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
@@ -49,19 +49,17 @@ add_a_to_zpword .proc
|
||||
pop_index_times_5 .proc
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
sta c64.SCRATCH_ZPB1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1 ; A*=5
|
||||
adc c64.ESTACK_LO,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
neg_b .proc
|
||||
lda c64.ESTACK_LO+1,x
|
||||
eor #255
|
||||
clc
|
||||
adc #1
|
||||
lda #0
|
||||
sec
|
||||
sbc c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
@@ -90,10 +88,9 @@ inv_word .proc
|
||||
not_byte .proc
|
||||
lda c64.ESTACK_LO+1,x
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta c64.ESTACK_LO+1,x
|
||||
lda #1
|
||||
+ eor #1
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
@@ -101,13 +98,168 @@ not_word .proc
|
||||
lda c64.ESTACK_LO + 1,x
|
||||
ora c64.ESTACK_HI + 1,x
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+ sta c64.ESTACK_LO + 1,x
|
||||
lda #1
|
||||
+ eor #1
|
||||
sta c64.ESTACK_LO + 1,x
|
||||
lsr a
|
||||
sta c64.ESTACK_HI + 1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitand_b .proc
|
||||
; -- bitwise and (of 2 bytes)
|
||||
lda c64.ESTACK_LO+2,x
|
||||
and c64.ESTACK_LO+1,x
|
||||
inx
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitor_b .proc
|
||||
; -- bitwise or (of 2 bytes)
|
||||
lda c64.ESTACK_LO+2,x
|
||||
ora c64.ESTACK_LO+1,x
|
||||
inx
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitxor_b .proc
|
||||
; -- bitwise xor (of 2 bytes)
|
||||
lda c64.ESTACK_LO+2,x
|
||||
eor c64.ESTACK_LO+1,x
|
||||
inx
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitand_w .proc
|
||||
; -- bitwise and (of 2 words)
|
||||
lda c64.ESTACK_LO+2,x
|
||||
and c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+2,x
|
||||
lda c64.ESTACK_HI+2,x
|
||||
and c64.ESTACK_HI+1,x
|
||||
sta c64.ESTACK_HI+2,x
|
||||
inx
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitor_w .proc
|
||||
; -- bitwise or (of 2 words)
|
||||
lda c64.ESTACK_LO+2,x
|
||||
ora c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+2,x
|
||||
lda c64.ESTACK_HI+2,x
|
||||
ora c64.ESTACK_HI+1,x
|
||||
sta c64.ESTACK_HI+2,x
|
||||
inx
|
||||
rts
|
||||
.pend
|
||||
|
||||
bitxor_w .proc
|
||||
; -- bitwise xor (of 2 bytes)
|
||||
lda c64.ESTACK_LO+2,x
|
||||
eor c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+2,x
|
||||
lda c64.ESTACK_HI+2,x
|
||||
eor c64.ESTACK_HI+1,x
|
||||
sta c64.ESTACK_HI+2,x
|
||||
inx
|
||||
rts
|
||||
.pend
|
||||
|
||||
and_b .proc
|
||||
; -- logical and (of 2 bytes)
|
||||
lda c64.ESTACK_LO+2,x
|
||||
beq +
|
||||
lda #1
|
||||
+ sta c64.SCRATCH_ZPB1
|
||||
lda c64.ESTACK_LO+1,x
|
||||
beq +
|
||||
lda #1
|
||||
+ and c64.SCRATCH_ZPB1
|
||||
inx
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
or_b .proc
|
||||
; -- logical or (of 2 bytes)
|
||||
lda c64.ESTACK_LO+2,x
|
||||
ora c64.ESTACK_LO+1,x
|
||||
beq +
|
||||
lda #1
|
||||
+ inx
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
xor_b .proc
|
||||
; -- logical xor (of 2 bytes)
|
||||
lda c64.ESTACK_LO+2,x
|
||||
beq +
|
||||
lda #1
|
||||
+ sta c64.SCRATCH_ZPB1
|
||||
lda c64.ESTACK_LO+1,x
|
||||
beq +
|
||||
lda #1
|
||||
+ eor c64.SCRATCH_ZPB1
|
||||
inx
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
and_w .proc
|
||||
; -- logical and (word and word -> byte)
|
||||
lda c64.ESTACK_LO+2,x
|
||||
ora c64.ESTACK_HI+2,x
|
||||
beq +
|
||||
lda #1
|
||||
+ sta c64.SCRATCH_ZPB1
|
||||
lda c64.ESTACK_LO+1,x
|
||||
ora c64.ESTACK_HI+1,x
|
||||
beq +
|
||||
lda #1
|
||||
+ and c64.SCRATCH_ZPB1
|
||||
inx
|
||||
sta c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
or_w .proc
|
||||
; -- logical or (word or word -> byte)
|
||||
lda c64.ESTACK_LO+2,x
|
||||
ora c64.ESTACK_LO+1,x
|
||||
ora c64.ESTACK_HI+2,x
|
||||
ora c64.ESTACK_HI+1,x
|
||||
beq +
|
||||
lda #1
|
||||
+ inx
|
||||
sta c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
xor_w .proc
|
||||
; -- logical xor (word xor word -> byte)
|
||||
lda c64.ESTACK_LO+2,x
|
||||
ora c64.ESTACK_HI+2,x
|
||||
beq +
|
||||
lda #1
|
||||
+ sta c64.SCRATCH_ZPB1
|
||||
lda c64.ESTACK_LO+1,x
|
||||
ora c64.ESTACK_HI+1,x
|
||||
beq +
|
||||
lda #1
|
||||
+ eor c64.SCRATCH_ZPB1
|
||||
inx
|
||||
sta c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
abs_b .proc
|
||||
; -- push abs(byte) on stack (as byte)
|
||||
@@ -300,25 +452,18 @@ equal_w .proc
|
||||
|
||||
notequal_b .proc
|
||||
; -- are the two bytes on the stack different?
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
eor c64.ESTACK_LO+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
lda c64.ESTACK_LO+1,x
|
||||
cmp c64.ESTACK_LO+2,x
|
||||
beq equal_b._equal_b_false
|
||||
bne equal_b._equal_b_true
|
||||
.pend
|
||||
|
||||
notequal_w .proc
|
||||
; -- are the two words on the stack different?
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
eor c64.ESTACK_LO+1,x
|
||||
beq +
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
+ lda c64.ESTACK_HI,x
|
||||
eor c64.ESTACK_HI+1,x
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
lda c64.ESTACK_HI+1,x
|
||||
cmp c64.ESTACK_HI+2,x
|
||||
beq notequal_b
|
||||
bne equal_b._equal_b_true
|
||||
.pend
|
||||
|
||||
less_ub .proc
|
||||
@@ -375,11 +520,10 @@ _equal_b_false lda #0
|
||||
.pend
|
||||
|
||||
lesseq_ub .proc
|
||||
lda c64.ESTACK_LO+2,x
|
||||
cmp c64.ESTACK_LO+1,x
|
||||
bcc equal_b._equal_b_true
|
||||
beq equal_b._equal_b_true
|
||||
bcs equal_b._equal_b_false
|
||||
lda c64.ESTACK_LO+1,x
|
||||
cmp c64.ESTACK_LO+2,x
|
||||
bcs equal_b._equal_b_true
|
||||
bcc equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
lesseq_b .proc
|
||||
@@ -496,6 +640,15 @@ greatereq_w .proc
|
||||
bmi equal_b._equal_b_false
|
||||
.pend
|
||||
|
||||
func_read_flags .proc
|
||||
; -- put the processor status register on the stack
|
||||
php
|
||||
pla
|
||||
sta c64.ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
func_sin8 .proc
|
||||
ldy c64.ESTACK_LO+1,x
|
||||
@@ -790,17 +943,15 @@ _loop lda (c64.SCRATCH_ZPWORD1),y
|
||||
func_sum_ub .proc
|
||||
jsr pop_array_and_lengthmin1Y
|
||||
lda #0
|
||||
sta c64.ESTACK_LO,x
|
||||
sta c64.ESTACK_HI,x
|
||||
- lda (c64.SCRATCH_ZPWORD1),y
|
||||
clc
|
||||
adc c64.ESTACK_LO,x
|
||||
sta c64.ESTACK_LO,x
|
||||
- clc
|
||||
adc (c64.SCRATCH_ZPWORD1),y
|
||||
bcc +
|
||||
inc c64.ESTACK_HI,x
|
||||
+ dey
|
||||
cpy #255
|
||||
bne -
|
||||
sta c64.ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.0 (beta)
|
||||
1.4 (beta)
|
||||
@@ -3,6 +3,7 @@ package prog8
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.*
|
||||
import prog8.compiler.target.c64.AsmGen
|
||||
import prog8.compiler.target.c64.C64Zeropage
|
||||
import prog8.optimizing.constantFold
|
||||
import prog8.optimizing.optimizeStatements
|
||||
import prog8.optimizing.simplifyExpressions
|
||||
@@ -45,6 +46,7 @@ private fun compileMain(args: Array<String>) {
|
||||
var moduleFile = ""
|
||||
var writeVmCode = false
|
||||
var writeAssembly = true
|
||||
var optimize = true
|
||||
for (arg in args) {
|
||||
if(arg=="-emu")
|
||||
emulatorToStart = "x64"
|
||||
@@ -54,6 +56,8 @@ private fun compileMain(args: Array<String>) {
|
||||
writeVmCode = true
|
||||
else if(arg=="-noasm")
|
||||
writeAssembly = false
|
||||
else if(arg=="-noopt")
|
||||
optimize = false
|
||||
else if(!arg.startsWith("-"))
|
||||
moduleFile = arg
|
||||
else
|
||||
@@ -100,15 +104,19 @@ private fun compileMain(args: Array<String>) {
|
||||
}
|
||||
//println(" time4: $time4")
|
||||
|
||||
// optimize the parse tree
|
||||
println("Optimizing...")
|
||||
val allScopedSymbolDefinitions = moduleAst.checkIdentifiers(heap) // useful for checking symbol usage later?
|
||||
while (true) {
|
||||
// keep optimizing expressions and statements until no more steps remain
|
||||
val optsDone1 = moduleAst.simplifyExpressions(namespace, heap)
|
||||
val optsDone2 = moduleAst.optimizeStatements(namespace, heap)
|
||||
if (optsDone1 + optsDone2 == 0)
|
||||
break
|
||||
// moduleAst.simplifyExpressions(namespace, heap)
|
||||
// moduleAst.optimizeStatements(namespace, heap)
|
||||
if(optimize) {
|
||||
// optimize the parse tree
|
||||
println("Optimizing...")
|
||||
while (true) {
|
||||
// keep optimizing expressions and statements until no more steps remain
|
||||
val optsDone1 = moduleAst.simplifyExpressions(namespace, heap)
|
||||
val optsDone2 = moduleAst.optimizeStatements(namespace, heap)
|
||||
if (optsDone1 + optsDone2 == 0)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
namespace = moduleAst.definingScope() // create it again, it could have changed in the meantime
|
||||
@@ -118,9 +126,10 @@ private fun compileMain(args: Array<String>) {
|
||||
// namespace.debugPrint()
|
||||
|
||||
// compile the syntax tree into stackvmProg form, and optimize that
|
||||
val compiler = Compiler(compilerOptions)
|
||||
val intermediate = compiler.compile(moduleAst, heap)
|
||||
intermediate.optimize()
|
||||
val compiler = Compiler(moduleAst, namespace, heap)
|
||||
val intermediate = compiler.compile(compilerOptions)
|
||||
if(optimize)
|
||||
intermediate.optimize()
|
||||
|
||||
if(writeVmCode) {
|
||||
val stackVmFilename = intermediate.name + ".vm.txt"
|
||||
@@ -131,7 +140,9 @@ private fun compileMain(args: Array<String>) {
|
||||
}
|
||||
|
||||
if(writeAssembly) {
|
||||
val assembly = AsmGen(compilerOptions, intermediate, heap).compileToAssembly()
|
||||
val zeropage = C64Zeropage(compilerOptions)
|
||||
intermediate.allocateZeropage(zeropage)
|
||||
val assembly = AsmGen(compilerOptions, intermediate, heap, zeropage).compileToAssembly(optimize)
|
||||
assembly.assemble(compilerOptions)
|
||||
programname = assembly.name
|
||||
}
|
||||
@@ -176,9 +187,10 @@ fun determineCompilationOptions(moduleAst: Module): CompilationOptions {
|
||||
as? Directive)?.args?.single()?.int ?: 0
|
||||
val zpoption: String? = (moduleAst.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||
val floatsEnabled = options.any { it.name == "enable_floats" }
|
||||
val zpType: ZeropageType =
|
||||
if (zpoption == null)
|
||||
ZeropageType.KERNALSAFE
|
||||
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
||||
else
|
||||
try {
|
||||
ZeropageType.valueOf(zpoption)
|
||||
@@ -196,8 +208,8 @@ fun determineCompilationOptions(moduleAst: Module): CompilationOptions {
|
||||
return CompilationOptions(
|
||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||
zpType, zpReserved,
|
||||
options.any { it.name == "enable_floats" })
|
||||
zpType, zpReserved, floatsEnabled
|
||||
)
|
||||
}
|
||||
|
||||
private fun usage() {
|
||||
@@ -207,6 +219,7 @@ private fun usage() {
|
||||
System.err.println(" [-writevm] write intermediate vm code to a file as well")
|
||||
System.err.println(" [-noasm] don't create assembly code")
|
||||
System.err.println(" [-vm] launch the prog8 virtual machine instead of the compiler")
|
||||
System.err.println(" [-noopt] don't perform optimizations")
|
||||
System.err.println(" modulefile main module file to compile")
|
||||
exitProcess(1)
|
||||
}
|
||||
+116
-62
@@ -9,6 +9,7 @@ import prog8.compiler.target.c64.Petscii
|
||||
import prog8.functions.BuiltinFunctions
|
||||
import prog8.functions.NotConstArgumentException
|
||||
import prog8.functions.builtinFunctionReturnType
|
||||
import prog8.parser.CustomLexer
|
||||
import prog8.parser.prog8Parser
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
@@ -25,9 +26,7 @@ enum class DataType {
|
||||
WORD,
|
||||
FLOAT,
|
||||
STR,
|
||||
STR_P,
|
||||
STR_S,
|
||||
STR_PS,
|
||||
ARRAY_UB,
|
||||
ARRAY_B,
|
||||
ARRAY_UW,
|
||||
@@ -37,20 +36,18 @@ enum class DataType {
|
||||
fun assignableTo(targetType: DataType) =
|
||||
// what types are assignable to others without loss of precision?
|
||||
when(this) {
|
||||
UBYTE -> targetType == BYTE || targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
|
||||
BYTE -> targetType == BYTE || targetType == UBYTE || targetType == WORD || targetType == FLOAT
|
||||
UBYTE -> targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
|
||||
BYTE -> targetType == BYTE || targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
|
||||
UWORD -> targetType == UWORD || targetType == FLOAT
|
||||
WORD -> targetType == WORD || targetType == FLOAT
|
||||
WORD -> targetType == WORD || targetType==UWORD || targetType == FLOAT
|
||||
FLOAT -> targetType == FLOAT
|
||||
STR -> targetType == STR || targetType==STR_S || targetType == UWORD
|
||||
STR_P -> targetType == STR_P || targetType==STR_PS || targetType == UWORD
|
||||
STR_S -> targetType == STR || targetType==STR_S || targetType == UWORD
|
||||
STR_PS -> targetType == STR_P || targetType==STR_PS || targetType == UWORD
|
||||
ARRAY_UB -> targetType == UWORD
|
||||
ARRAY_B -> targetType == UWORD
|
||||
ARRAY_UW -> targetType == UWORD
|
||||
ARRAY_W -> targetType == UWORD
|
||||
ARRAY_F -> targetType == UWORD
|
||||
ARRAY_UB -> targetType == UWORD || targetType==ARRAY_UB
|
||||
ARRAY_B -> targetType == UWORD || targetType==ARRAY_B
|
||||
ARRAY_UW -> targetType == UWORD || targetType==ARRAY_UW
|
||||
ARRAY_W -> targetType == UWORD || targetType==ARRAY_W
|
||||
ARRAY_F -> targetType == UWORD || targetType==ARRAY_F
|
||||
}
|
||||
|
||||
|
||||
@@ -97,14 +94,15 @@ enum class BranchCondition {
|
||||
|
||||
val IterableDatatypes = setOf(
|
||||
DataType.STR, DataType.STR_S,
|
||||
DataType.STR_P, DataType.STR_PS, // note: these are a bit weird they store their length as the first byte
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W,
|
||||
DataType.ARRAY_F)
|
||||
|
||||
val StringDatatypes = setOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)
|
||||
val NumericDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
||||
val ByteDatatypes = setOf(DataType.UBYTE, DataType.BYTE)
|
||||
val WordDatatypes = setOf(DataType.UWORD, DataType.WORD)
|
||||
val IntegerDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
|
||||
val NumericDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
||||
val StringDatatypes = setOf(DataType.STR, DataType.STR_S)
|
||||
val ArrayDatatypes = setOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
|
||||
|
||||
|
||||
@@ -174,9 +172,9 @@ interface IAstProcessor {
|
||||
return functionCall
|
||||
}
|
||||
|
||||
fun process(functionCall: FunctionCallStatement): IStatement {
|
||||
functionCall.arglist = functionCall.arglist.map { it.process(this) }.toMutableList()
|
||||
return functionCall
|
||||
fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
functionCallStatement.arglist = functionCallStatement.arglist.map { it.process(this) }.toMutableList()
|
||||
return functionCallStatement
|
||||
}
|
||||
|
||||
fun process(identifier: IdentifierReference): IExpression {
|
||||
@@ -506,11 +504,11 @@ private class GlobalNamespace(override val name: String,
|
||||
class Block(override val name: String,
|
||||
val address: Int?,
|
||||
override var statements: MutableList<IStatement>,
|
||||
val isInLibrary: Boolean,
|
||||
override val position: Position) : IStatement, INameScope {
|
||||
override lateinit var parent: Node
|
||||
val scopedname: String by lazy { makeScopedName(name).joinToString(".") }
|
||||
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
statements.forEach {it.linkParents(this)}
|
||||
@@ -657,6 +655,7 @@ enum class VarDeclType {
|
||||
|
||||
class VarDecl(val type: VarDeclType,
|
||||
private val declaredDatatype: DataType,
|
||||
val zeropage: Boolean,
|
||||
val arrayspec: ArraySpec?,
|
||||
val name: String,
|
||||
var value: IExpression?,
|
||||
@@ -701,7 +700,7 @@ class VarDecl(val type: VarDeclType,
|
||||
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue=0.0, position=position)
|
||||
else -> throw FatalAstException("can only set a default value for a numeric type")
|
||||
}
|
||||
val decl = VarDecl(type, declaredDatatype, arrayspec, name, constValue, position)
|
||||
val decl = VarDecl(type, declaredDatatype, zeropage, arrayspec, name, constValue, position)
|
||||
if(parent!=null)
|
||||
decl.linkParents(parent)
|
||||
return decl
|
||||
@@ -788,7 +787,7 @@ data class AssignTarget(val register: Register?,
|
||||
|
||||
fun shortString(withTypePrefix: Boolean=false): String {
|
||||
if(register!=null)
|
||||
return (if(withTypePrefix) "0register::" else "") + register.toString()
|
||||
return (if(withTypePrefix) "0register::" else "") + register.name
|
||||
if(identifier!=null)
|
||||
return (if(withTypePrefix) "3identifier::" else "") + identifier.nameInSource.last()
|
||||
if(arrayindexed!=null)
|
||||
@@ -916,8 +915,8 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.BYTE -> when(rightDt) {
|
||||
DataType.BYTE, DataType.UBYTE -> DataType.BYTE
|
||||
DataType.WORD, DataType.UWORD -> DataType.WORD
|
||||
in ByteDatatypes -> DataType.BYTE
|
||||
in WordDatatypes -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
@@ -928,7 +927,7 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.WORD -> when(rightDt) {
|
||||
DataType.BYTE, DataType.UBYTE, DataType.WORD, DataType.UWORD -> DataType.WORD
|
||||
in IntegerDatatypes -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
@@ -1071,8 +1070,8 @@ class LiteralValue(val type: DataType,
|
||||
|
||||
fun fromNumber(value: Number, type: DataType, position: Position) : LiteralValue {
|
||||
return when(type) {
|
||||
DataType.UBYTE, DataType.BYTE -> LiteralValue(type, bytevalue = value.toShort(), position = position)
|
||||
DataType.UWORD, DataType.WORD -> LiteralValue(type, wordvalue = value.toInt(), position = position)
|
||||
in ByteDatatypes -> LiteralValue(type, bytevalue = value.toShort(), position = position)
|
||||
in WordDatatypes -> LiteralValue(type, wordvalue = value.toInt(), position = position)
|
||||
DataType.FLOAT -> LiteralValue(type, floatvalue = value.toDouble(), position = position)
|
||||
else -> throw FatalAstException("non numeric datatype")
|
||||
}
|
||||
@@ -1108,8 +1107,8 @@ class LiteralValue(val type: DataType,
|
||||
|
||||
init {
|
||||
when(type){
|
||||
DataType.UBYTE, DataType.BYTE -> if(bytevalue==null) throw FatalAstException("literal value missing bytevalue")
|
||||
DataType.UWORD, DataType.WORD -> if(wordvalue==null) throw FatalAstException("literal value missing wordvalue")
|
||||
in ByteDatatypes -> if(bytevalue==null) throw FatalAstException("literal value missing bytevalue")
|
||||
in WordDatatypes -> if(wordvalue==null) throw FatalAstException("literal value missing wordvalue")
|
||||
DataType.FLOAT -> if(floatvalue==null) throw FatalAstException("literal value missing floatvalue")
|
||||
in StringDatatypes ->
|
||||
if(initialstrvalue==null && heapId==null) throw FatalAstException("literal value missing strvalue/heapId")
|
||||
@@ -1157,14 +1156,15 @@ class LiteralValue(val type: DataType,
|
||||
DataType.UWORD -> "uword:$wordvalue"
|
||||
DataType.WORD -> "word:$wordvalue"
|
||||
DataType.FLOAT -> "float:$floatvalue"
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS-> {
|
||||
in StringDatatypes -> {
|
||||
if(heapId!=null) "str:#$heapId"
|
||||
else "str:$initialstrvalue"
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F -> {
|
||||
in ArrayDatatypes -> {
|
||||
if(heapId!=null) "arrayspec:#$heapId"
|
||||
else "arrayspec:$arrayvalue"
|
||||
}
|
||||
else -> throw FatalAstException("weird datatype")
|
||||
}
|
||||
return "LiteralValue($vstr)"
|
||||
}
|
||||
@@ -1270,7 +1270,7 @@ class LiteralValue(val type: DataType,
|
||||
return LiteralValue(targettype, wordvalue = value, position = position)
|
||||
}
|
||||
}
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
||||
in StringDatatypes -> {
|
||||
if(targettype in StringDatatypes)
|
||||
return this
|
||||
}
|
||||
@@ -1312,9 +1312,7 @@ class RangeExpr(var from: IExpression,
|
||||
fromDt==DataType.UBYTE && toDt==DataType.UBYTE -> DataType.UBYTE
|
||||
fromDt==DataType.UWORD && toDt==DataType.UWORD -> DataType.UWORD
|
||||
fromDt==DataType.STR && toDt==DataType.STR -> DataType.STR
|
||||
fromDt==DataType.STR_P && toDt==DataType.STR_P -> DataType.STR_P
|
||||
fromDt==DataType.STR_S && toDt==DataType.STR_S -> DataType.STR_S
|
||||
fromDt==DataType.STR_PS && toDt==DataType.STR_PS -> DataType.STR_PS
|
||||
fromDt==DataType.WORD || toDt==DataType.WORD -> DataType.WORD
|
||||
fromDt==DataType.BYTE || toDt==DataType.BYTE -> DataType.BYTE
|
||||
else -> DataType.UBYTE
|
||||
@@ -1646,6 +1644,40 @@ class Subroutine(override val name: String,
|
||||
.filter { it is InlineAssembly }
|
||||
.map { (it as InlineAssembly).assembly }
|
||||
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it }
|
||||
|
||||
val canBeAsmSubroutine =false // TODO disabled for now, see below about problem with converting to asm subroutine
|
||||
// !isAsmSubroutine
|
||||
// && ((parameters.size == 1 && parameters[0].type in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD, DataType.UWORD))
|
||||
// || (parameters.size == 2 && parameters.map { it.type }.all { it == DataType.BYTE || it == DataType.UBYTE }))
|
||||
|
||||
fun intoAsmSubroutine(): Subroutine {
|
||||
// TODO turn subroutine into asm calling convention. Requires rethinking of how parameters are handled (conflicts with local vardefs now, see AstIdentifierChecker...)
|
||||
return this // TODO
|
||||
|
||||
// println("TO ASM $this") // TODO
|
||||
// val paramregs = if (parameters.size == 1 && parameters[0].type in setOf(DataType.BYTE, DataType.UBYTE))
|
||||
// listOf(RegisterOrStatusflag(RegisterOrPair.Y, null, null))
|
||||
// else if (parameters.size == 1 && parameters[0].type in setOf(DataType.WORD, DataType.UWORD))
|
||||
// listOf(RegisterOrStatusflag(RegisterOrPair.AY, null, null))
|
||||
// else if (parameters.size == 2 && parameters.map { it.type }.all { it == DataType.BYTE || it == DataType.UBYTE })
|
||||
// listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null), RegisterOrStatusflag(RegisterOrPair.Y, null, null))
|
||||
// else throw FatalAstException("cannot convert subroutine to asm parameters")
|
||||
//
|
||||
// val asmsub=Subroutine(
|
||||
// name,
|
||||
// parameters,
|
||||
// returntypes,
|
||||
// paramregs,
|
||||
// emptyList(),
|
||||
// emptySet(),
|
||||
// null,
|
||||
// true,
|
||||
// statements,
|
||||
// position
|
||||
// )
|
||||
// asmsub.linkParents(parent)
|
||||
// return asmsub
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1694,6 +1726,7 @@ class BranchStatement(var condition: BranchCondition,
|
||||
|
||||
class ForLoop(val loopRegister: Register?,
|
||||
val decltype: DataType?,
|
||||
val zeropage: Boolean,
|
||||
val loopVar: IdentifierReference?,
|
||||
var iterable: IExpression,
|
||||
var body: AnonymousScope,
|
||||
@@ -1751,34 +1784,38 @@ class RepeatLoop(var body: AnonymousScope,
|
||||
|
||||
/***************** Antlr Extension methods to create AST ****************/
|
||||
|
||||
fun prog8Parser.ModuleContext.toAst(name: String, isLibrary: Boolean, importedFrom: Path) : Module =
|
||||
Module(name, modulestatement().asSequence().map { it.toAst() }.toMutableList(), toPosition(), isLibrary, importedFrom)
|
||||
|
||||
|
||||
private fun ParserRuleContext.toPosition() : Position {
|
||||
val file =
|
||||
if(start.inputStream.sourceName == IntStream.UNKNOWN_SOURCE_NAME)
|
||||
"<internal>"
|
||||
else
|
||||
File(start.inputStream.sourceName).name
|
||||
// note: be ware of TAB characters in the source text, they count as 1 column...
|
||||
return Position(file, start.line, start.charPositionInLine, stop.charPositionInLine+stop.text.length)
|
||||
fun prog8Parser.ModuleContext.toAst(name: String, isLibrary: Boolean, importedFrom: Path) : Module {
|
||||
val nameWithoutSuffix = if(name.endsWith(".p8")) name.substringBeforeLast('.') else name
|
||||
return Module(nameWithoutSuffix, modulestatement().asSequence().map { it.toAst(isLibrary) }.toMutableList(), toPosition(), isLibrary, importedFrom)
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.ModulestatementContext.toAst() : IStatement {
|
||||
private fun ParserRuleContext.toPosition() : Position {
|
||||
val customTokensource = this.start.tokenSource as? CustomLexer
|
||||
val filename =
|
||||
when {
|
||||
customTokensource!=null -> customTokensource.modulePath.fileName.toString()
|
||||
start.tokenSource.sourceName == IntStream.UNKNOWN_SOURCE_NAME -> "@internal@"
|
||||
else -> File(start.inputStream.sourceName).name
|
||||
}
|
||||
// note: be ware of TAB characters in the source text, they count as 1 column...
|
||||
return Position(filename, start.line, start.charPositionInLine, stop.charPositionInLine+stop.text.length)
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.ModulestatementContext.toAst(isInLibrary: Boolean) : IStatement {
|
||||
val directive = directive()?.toAst()
|
||||
if(directive!=null) return directive
|
||||
|
||||
val block = block()?.toAst()
|
||||
val block = block()?.toAst(isInLibrary)
|
||||
if(block!=null) return block
|
||||
|
||||
throw FatalAstException(text)
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.BlockContext.toAst() : IStatement =
|
||||
Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), statement_block().toAst(), toPosition())
|
||||
private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean) : IStatement =
|
||||
Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), statement_block().toAst(), isInLibrary, toPosition())
|
||||
|
||||
|
||||
private fun prog8Parser.Statement_blockContext.toAst(): MutableList<IStatement> =
|
||||
@@ -1789,6 +1826,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
|
||||
vardecl()?.let {
|
||||
return VarDecl(VarDeclType.VAR,
|
||||
it.datatype().toAst(),
|
||||
it.ZEROPAGE()!=null,
|
||||
it.arrayspec()?.toAst(),
|
||||
it.identifier().text,
|
||||
null,
|
||||
@@ -1797,9 +1835,10 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
|
||||
|
||||
varinitializer()?.let {
|
||||
return VarDecl(VarDeclType.VAR,
|
||||
it.datatype().toAst(),
|
||||
it.arrayspec()?.toAst(),
|
||||
it.identifier().text,
|
||||
it.vardecl().datatype().toAst(),
|
||||
it.vardecl().ZEROPAGE()!=null,
|
||||
it.vardecl().arrayspec()?.toAst(),
|
||||
it.vardecl().identifier().text,
|
||||
it.expression().toAst(),
|
||||
it.toPosition())
|
||||
}
|
||||
@@ -1807,9 +1846,10 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
|
||||
constdecl()?.let {
|
||||
val cvarinit = it.varinitializer()
|
||||
return VarDecl(VarDeclType.CONST,
|
||||
cvarinit.datatype().toAst(),
|
||||
cvarinit.arrayspec()?.toAst(),
|
||||
cvarinit.identifier().text,
|
||||
cvarinit.vardecl().datatype().toAst(),
|
||||
cvarinit.vardecl().ZEROPAGE()!=null,
|
||||
cvarinit.vardecl().arrayspec()?.toAst(),
|
||||
cvarinit.vardecl().identifier().text,
|
||||
cvarinit.expression().toAst(),
|
||||
cvarinit.toPosition())
|
||||
}
|
||||
@@ -1817,9 +1857,10 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
|
||||
memoryvardecl()?.let {
|
||||
val mvarinit = it.varinitializer()
|
||||
return VarDecl(VarDeclType.MEMORY,
|
||||
mvarinit.datatype().toAst(),
|
||||
mvarinit.arrayspec()?.toAst(),
|
||||
mvarinit.identifier().text,
|
||||
mvarinit.vardecl().datatype().toAst(),
|
||||
mvarinit.vardecl().ZEROPAGE()!=null,
|
||||
mvarinit.vardecl().arrayspec()?.toAst(),
|
||||
mvarinit.vardecl().identifier().text,
|
||||
mvarinit.expression().toAst(),
|
||||
mvarinit.toPosition())
|
||||
}
|
||||
@@ -2033,7 +2074,11 @@ private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
||||
var datatype = DataType.UBYTE
|
||||
when (radix) {
|
||||
10 -> {
|
||||
integer = text.toInt()
|
||||
integer = try {
|
||||
text.toInt()
|
||||
} catch(x: NumberFormatException) {
|
||||
throw AstException("${toPosition()} invalid decimal literal ${x.message}")
|
||||
}
|
||||
datatype = when(integer) {
|
||||
in 0..255 -> DataType.UBYTE
|
||||
in -128..127 -> DataType.BYTE
|
||||
@@ -2045,12 +2090,20 @@ private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
||||
2 -> {
|
||||
if(text.length>8)
|
||||
datatype = DataType.UWORD
|
||||
integer = text.toInt(2)
|
||||
try {
|
||||
integer = text.toInt(2)
|
||||
} catch(x: NumberFormatException) {
|
||||
throw AstException("${toPosition()} invalid binary literal ${x.message}")
|
||||
}
|
||||
}
|
||||
16 -> {
|
||||
if(text.length>2)
|
||||
datatype = DataType.UWORD
|
||||
integer = text.toInt(16)
|
||||
try {
|
||||
integer = text.toInt(16)
|
||||
} catch(x: NumberFormatException) {
|
||||
throw AstException("${toPosition()} invalid hexadecimal literal ${x.message}")
|
||||
}
|
||||
}
|
||||
else -> throw FatalAstException("invalid radix")
|
||||
}
|
||||
@@ -2197,6 +2250,7 @@ private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf
|
||||
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
||||
val loopregister = register()?.toAst()
|
||||
val datatype = datatype()?.toAst()
|
||||
val zeropage = ZEROPAGE()!=null
|
||||
val loopvar = identifier()?.toAst()
|
||||
val iterable = expression()!!.toAst()
|
||||
val scope =
|
||||
@@ -2204,7 +2258,7 @@ private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
||||
AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
|
||||
else
|
||||
AnonymousScope(statement_block().toAst(), statement_block().toPosition())
|
||||
return ForLoop(loopregister, datatype, loopvar, iterable, scope, toPosition())
|
||||
return ForLoop(loopregister, datatype, zeropage, loopvar, iterable, scope, toPosition())
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -41,10 +41,15 @@ fun printWarning(msg: String, position: Position, detailInfo: String?=null) {
|
||||
if(detailInfo==null)
|
||||
print("\n")
|
||||
else
|
||||
println(": $detailInfo")
|
||||
print("\u001b[0m") // bright yellow
|
||||
println(": $detailInfo\n")
|
||||
print("\u001b[0m") // normal
|
||||
}
|
||||
|
||||
fun printWarning(msg: String) {
|
||||
print("\u001b[93m") // bright yellow
|
||||
print("Warning: $msg")
|
||||
print("\u001b[0m\n") // normal
|
||||
}
|
||||
|
||||
private class AstChecker(private val namespace: INameScope,
|
||||
private val compilerOptions: CompilationOptions,
|
||||
@@ -587,9 +592,10 @@ private class AstChecker(private val namespace: INameScope,
|
||||
if(directive.parent !is Module) err("this directive may only occur at module level")
|
||||
if(directive.args.size!=1 ||
|
||||
directive.args[0].name != "basicsafe" &&
|
||||
directive.args[0].name != "floatsafe" &&
|
||||
directive.args[0].name != "kernalsafe" &&
|
||||
directive.args[0].name != "full")
|
||||
err("invalid zp type, expected basicsafe, kernalsafe, or full")
|
||||
err("invalid zp type, expected basicsafe, floatsafe, kernalsafe, or full")
|
||||
}
|
||||
"%zpreserved" -> {
|
||||
if(directive.parent !is Module) err("this directive may only occur at module level")
|
||||
@@ -689,10 +695,17 @@ private class AstChecker(private val namespace: INameScope,
|
||||
checkResult.add(ExpressionError("remainder can only be used on unsigned integer operands", expr.right.position))
|
||||
}
|
||||
}
|
||||
"and", "or", "xor", "&", "|", "^" -> {
|
||||
// only integer numeric operands accepted
|
||||
val rightDt = expr.right.resultingDatatype(namespace, heap)
|
||||
val leftDt = expr.left.resultingDatatype(namespace, heap)
|
||||
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
||||
checkResult.add(ExpressionError("logical or bitwise operator can only be used on integer operands", expr.right.position))
|
||||
}
|
||||
}
|
||||
|
||||
val leftDt = expr.left.resultingDatatype(namespace, heap)!!
|
||||
val rightDt = expr.right.resultingDatatype(namespace, heap)!!
|
||||
val leftDt = expr.left.resultingDatatype(namespace, heap)
|
||||
val rightDt = expr.right.resultingDatatype(namespace, heap)
|
||||
if(leftDt !in NumericDatatypes)
|
||||
checkResult.add(ExpressionError("left operand is not numeric", expr.left.position))
|
||||
if(rightDt!in NumericDatatypes)
|
||||
@@ -764,13 +777,13 @@ private class AstChecker(private val namespace: INameScope,
|
||||
return super.process(functionCall)
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCallStatement): IStatement {
|
||||
val targetStatement = checkFunctionOrLabelExists(functionCall.target, functionCall)
|
||||
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
|
||||
if(targetStatement!=null)
|
||||
checkFunctionCall(targetStatement, functionCall.arglist, functionCall.position)
|
||||
checkFunctionCall(targetStatement, functionCallStatement.arglist, functionCallStatement.position)
|
||||
if(targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty())
|
||||
printWarning("result value of subroutine call is discarded", functionCall.position)
|
||||
return super.process(functionCall)
|
||||
printWarning("result value of subroutine call is discarded", functionCallStatement.position)
|
||||
return super.process(functionCallStatement)
|
||||
}
|
||||
|
||||
private fun checkFunctionCall(target: IStatement, args: List<IExpression>, position: Position) {
|
||||
@@ -779,14 +792,14 @@ private class AstChecker(private val namespace: INameScope,
|
||||
|
||||
if(target is BuiltinFunctionStatementPlaceholder) {
|
||||
// it's a call to a builtin function.
|
||||
val func = BuiltinFunctions[target.name]!!
|
||||
val func = BuiltinFunctions.getValue(target.name)
|
||||
if(args.size!=func.parameters.size)
|
||||
checkResult.add(SyntaxError("invalid number of arguments", position))
|
||||
else {
|
||||
for (arg in args.withIndex().zip(func.parameters)) {
|
||||
val argDt=arg.first.value.resultingDatatype(namespace, heap)
|
||||
if(argDt!=null && !argDt.assignableTo(arg.second.possibleDatatypes)) {
|
||||
checkResult.add(ExpressionError("builtin function argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position))
|
||||
checkResult.add(ExpressionError("builtin function '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position))
|
||||
}
|
||||
}
|
||||
if(target.name=="swap") {
|
||||
@@ -810,13 +823,26 @@ private class AstChecker(private val namespace: INameScope,
|
||||
for (arg in args.withIndex().zip(target.parameters)) {
|
||||
val argDt = arg.first.value.resultingDatatype(namespace, heap)
|
||||
if(argDt!=null && !argDt.assignableTo(arg.second.type))
|
||||
checkResult.add(ExpressionError("subroutine argument ${arg.first.index+1} has invalid type $argDt, expected ${arg.second.type}", position))
|
||||
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index+1} has invalid type $argDt, expected ${arg.second.type}", position))
|
||||
|
||||
if(target.isAsmSubroutine) {
|
||||
if (target.asmParameterRegisters[arg.first.index].registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.XY, RegisterOrPair.X)) {
|
||||
if (arg.first.value !is LiteralValue && arg.first.value !is IdentifierReference)
|
||||
printWarning("calling a subroutine that expects X as a parameter is problematic, more so when providing complex arguments. If you see a compiler error/crash about this later, try to simplify this call", position)
|
||||
}
|
||||
|
||||
// check if the argument types match the register(pairs)
|
||||
val asmParamReg = target.asmParameterRegisters[arg.first.index]
|
||||
if(asmParamReg.statusflag!=null) {
|
||||
if(argDt !in ByteDatatypes)
|
||||
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index+1} must be byte type for statusflag", position))
|
||||
} else if(asmParamReg.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
|
||||
if(argDt !in ByteDatatypes)
|
||||
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index+1} must be byte type for single register", position))
|
||||
} else if(asmParamReg.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
||||
if(argDt !in WordDatatypes+ IterableDatatypes)
|
||||
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index+1} must be word type for register pair", position))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -864,9 +890,6 @@ private class AstChecker(private val namespace: INameScope,
|
||||
if(index!=null && (index<0 || index>=arraysize))
|
||||
checkResult.add(ExpressionError("array index out of bounds", arrayIndexedExpression.arrayspec.position))
|
||||
} else if(target.datatype in StringDatatypes) {
|
||||
// check supported string tyep
|
||||
if(target.datatype == DataType.STR_P || target.datatype==DataType.STR_PS)
|
||||
checkResult.add(ExpressionError("indexing pascal-strings is not supported, use regular str type instead", arrayIndexedExpression.arrayspec.position))
|
||||
// check string lengths
|
||||
val heapId = (target.value as LiteralValue).heapId!!
|
||||
val stringLen = heap.get(heapId).str!!.length
|
||||
@@ -889,7 +912,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
val targetStatement = target.targetStatement(namespace)
|
||||
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
|
||||
return targetStatement
|
||||
checkResult.add(SyntaxError("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position))
|
||||
checkResult.add(NameError("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position))
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -941,8 +964,8 @@ private class AstChecker(private val namespace: INameScope,
|
||||
when (targetDt) {
|
||||
DataType.FLOAT -> {
|
||||
val number = when(value.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> value.bytevalue!!.toDouble()
|
||||
DataType.UWORD, DataType.WORD -> value.wordvalue!!.toDouble()
|
||||
in ByteDatatypes -> value.bytevalue!!.toDouble()
|
||||
in WordDatatypes -> value.wordvalue!!.toDouble()
|
||||
DataType.FLOAT -> value.floatvalue!!
|
||||
else -> return err("numeric value expected")
|
||||
}
|
||||
@@ -983,7 +1006,7 @@ private class AstChecker(private val namespace: INameScope,
|
||||
if (number < -32768 || number > 32767)
|
||||
return err("value '$number' out of range for word")
|
||||
}
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
||||
DataType.STR, DataType.STR_S -> {
|
||||
if(!value.isString)
|
||||
return err("string value expected")
|
||||
val str = value.strvalue(heap)
|
||||
@@ -1109,8 +1132,6 @@ private class AstChecker(private val namespace: INameScope,
|
||||
DataType.FLOAT -> sourceDatatype in NumericDatatypes
|
||||
DataType.STR -> sourceDatatype==DataType.STR
|
||||
DataType.STR_S -> sourceDatatype==DataType.STR_S
|
||||
DataType.STR_P -> sourceDatatype==DataType.STR_P
|
||||
DataType.STR_PS -> sourceDatatype==DataType.STR_PS
|
||||
else -> checkResult.add(SyntaxError("cannot assign new value to variable of type $targetDatatype", position))
|
||||
}
|
||||
|
||||
@@ -1124,9 +1145,9 @@ private class AstChecker(private val namespace: INameScope,
|
||||
checkResult.add(ExpressionError("cannot assign word to byte, use msb() or lsb()?", position))
|
||||
}
|
||||
else if(sourceDatatype==DataType.FLOAT && targetDatatype in IntegerDatatypes)
|
||||
checkResult.add(ExpressionError("cannot assign float to ${targetDatatype.toString().toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position))
|
||||
checkResult.add(ExpressionError("cannot assign float to ${targetDatatype.name.toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position))
|
||||
else
|
||||
checkResult.add(ExpressionError("cannot assign ${sourceDatatype.toString().toLowerCase()} to ${targetDatatype.toString().toLowerCase()}", position))
|
||||
checkResult.add(ExpressionError("cannot assign ${sourceDatatype.name.toLowerCase()} to ${targetDatatype.name.toLowerCase()}", position))
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ fun Module.checkIdentifiers(heap: HeapValues): MutableMap<String, IStatement> {
|
||||
}
|
||||
else -> TODO("replace literalvalue by identifierref: $variable (in $parent)")
|
||||
}
|
||||
variable.second.linkParents(scope as Node)
|
||||
}
|
||||
|
||||
printErrors(checker.result(), name)
|
||||
@@ -124,12 +125,13 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
|
||||
// NOTE:
|
||||
// - numeric types BYTE and WORD and FLOAT are passed by value;
|
||||
// - strings, arrays, matrices are passed by reference (their 16-bit address is passed as an uword parameter)
|
||||
if(subroutine.asmAddress==null) {
|
||||
// - do NOT do this is the statement can be transformed into an asm subroutine later!
|
||||
if(subroutine.asmAddress==null && !subroutine.canBeAsmSubroutine) {
|
||||
if(subroutine.asmParameterRegisters.isEmpty()) {
|
||||
subroutine.parameters
|
||||
.filter { it.name !in allDefinedNames }
|
||||
.forEach {
|
||||
val vardecl = VarDecl(VarDeclType.VAR, it.type, null, it.name, null, subroutine.position)
|
||||
val vardecl = VarDecl(VarDeclType.VAR, it.type, false, null, it.name, null, subroutine.position)
|
||||
vardecl.linkParents(subroutine)
|
||||
subroutine.statements.add(0, vardecl)
|
||||
}
|
||||
@@ -171,7 +173,7 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
|
||||
val existing = if(forLoop.body.isEmpty()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first())
|
||||
if(existing==null) {
|
||||
// create the local scoped for loop variable itself
|
||||
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, null, varName, null, forLoop.loopVar.position)
|
||||
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, true, null, varName, null, forLoop.loopVar.position)
|
||||
vardecl.linkParents(forLoop.body)
|
||||
forLoop.body.statements.add(0, vardecl)
|
||||
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
||||
@@ -183,7 +185,7 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
|
||||
val existing = if(forLoop.body.isEmpty()) null else forLoop.body.lookup(listOf(ForLoop.iteratorLoopcounterVarname), forLoop.body.statements.first())
|
||||
if(existing==null) {
|
||||
// create loop iteration counter variable (without value, to avoid an assignment)
|
||||
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, null, ForLoop.iteratorLoopcounterVarname, null, forLoop.loopVar.position)
|
||||
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, true, null, ForLoop.iteratorLoopcounterVarname, null, forLoop.loopVar.position)
|
||||
vardecl.linkParents(forLoop.body)
|
||||
forLoop.body.statements.add(0, vardecl)
|
||||
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
|
||||
@@ -230,7 +232,7 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
|
||||
if(literalValue.heapId!=null && literalValue.parent !is VarDecl) {
|
||||
// a literal value that's not declared as a variable, which refers to something on the heap.
|
||||
// we need to introduce an auto-generated variable for this to be able to refer to the value!
|
||||
val variable = VarDecl(VarDeclType.VAR, literalValue.type, null, "auto_heap_value_${literalValue.heapId}", literalValue, literalValue.position)
|
||||
val variable = VarDecl(VarDeclType.VAR, literalValue.type, false, null, "auto_heap_value_${literalValue.heapId}", literalValue, literalValue.position)
|
||||
anonymousVariablesFromHeap.add(Pair(literalValue, variable))
|
||||
}
|
||||
return super.process(literalValue)
|
||||
|
||||
@@ -32,7 +32,7 @@ private class DirectedGraph<VT> {
|
||||
println("#vertices: $numVertices")
|
||||
graph.forEach { from, to ->
|
||||
println("$from CALLS:")
|
||||
to.forEach { it -> println(" $it") }
|
||||
to.forEach { println(" $it") }
|
||||
}
|
||||
val cycle = checkForCycle()
|
||||
if(cycle.isNotEmpty()) {
|
||||
@@ -92,9 +92,9 @@ private class AstRecursionChecker(private val namespace: INameScope) : IAstProce
|
||||
return listOf(AstException("Program contains recursive subroutine calls, this is not supported. Recursive chain:\n (a subroutine call in) $chain"))
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCallStatement): IStatement {
|
||||
val scope = functionCall.definingScope()
|
||||
val targetStatement = functionCall.target.targetStatement(namespace)
|
||||
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
val scope = functionCallStatement.definingScope()
|
||||
val targetStatement = functionCallStatement.target.targetStatement(namespace)
|
||||
if(targetStatement!=null) {
|
||||
val targetScope = when (targetStatement) {
|
||||
is Subroutine -> targetStatement
|
||||
@@ -102,7 +102,7 @@ private class AstRecursionChecker(private val namespace: INameScope) : IAstProce
|
||||
}
|
||||
callGraph.add(scope, targetScope)
|
||||
}
|
||||
return super.process(functionCall)
|
||||
return super.process(functionCallStatement)
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCall): IExpression {
|
||||
|
||||
@@ -10,6 +10,9 @@ fun Module.reorderStatements(namespace: INameScope, heap: HeapValues) {
|
||||
this.process(checker)
|
||||
}
|
||||
|
||||
const val initvarsSubName="prog8_init_vars" // the name of the subroutine that should be called for every block to initialize its variables
|
||||
|
||||
|
||||
private class StatementReorderer(private val namespace: INameScope, private val heap: HeapValues): IAstProcessor {
|
||||
// Reorders the statements in a way the compiler needs.
|
||||
// - 'main' block must be the very first statement UNLESS it has an address set.
|
||||
@@ -30,11 +33,21 @@ private class StatementReorderer(private val namespace: INameScope, private val
|
||||
val (blocks, other) = module.statements.partition { it is Block }
|
||||
module.statements = other.asSequence().plus(blocks.sortedBy { (it as Block).address ?: Int.MAX_VALUE }).toMutableList()
|
||||
|
||||
// make sure user-defined blocks come BEFORE library blocks, and move the "main" block to the top of everything
|
||||
val nonLibraryBlocks = module.statements.withIndex()
|
||||
.filter { it.value is Block && !(it.value as Block).isInLibrary }
|
||||
.map { it.index to it.value }
|
||||
.reversed()
|
||||
for(nonLibBlock in nonLibraryBlocks)
|
||||
module.statements.removeAt(nonLibBlock.first)
|
||||
for(nonLibBlock in nonLibraryBlocks)
|
||||
module.statements.add(0, nonLibBlock.second)
|
||||
val mainBlock = module.statements.single { it is Block && it.name=="main" }
|
||||
if((mainBlock as Block).address==null) {
|
||||
module.statements.remove(mainBlock)
|
||||
module.statements.add(0, mainBlock)
|
||||
}
|
||||
|
||||
val varDecls = module.statements.filterIsInstance<VarDecl>()
|
||||
module.statements.removeAll(varDecls)
|
||||
module.statements.addAll(0, varDecls)
|
||||
@@ -93,6 +106,19 @@ private class StatementReorderer(private val namespace: INameScope, private val
|
||||
|
||||
sortConstantAssignments(block.statements)
|
||||
|
||||
val varInits = block.statements.withIndex().filter { it.value is VariableInitializationAssignment }
|
||||
if(varInits.isNotEmpty()) {
|
||||
val statements = varInits.map{it.value}.toMutableList()
|
||||
val varInitSub = Subroutine(initvarsSubName, emptyList(), emptyList(), emptyList(), emptyList(),
|
||||
emptySet(), null, false, statements, block.position)
|
||||
varInitSub.linkParents(block)
|
||||
block.statements.add(varInitSub)
|
||||
|
||||
// remove the varinits from the block's statements
|
||||
for(index in varInits.map{it.index}.reversed())
|
||||
block.statements.removeAt(index)
|
||||
}
|
||||
|
||||
return super.process(block)
|
||||
}
|
||||
|
||||
|
||||
@@ -92,17 +92,12 @@ class HeapValues {
|
||||
|
||||
fun update(heapId: Int, str: String) {
|
||||
val oldVal = heap[heapId] ?: throw IllegalArgumentException("heapId not found in heap")
|
||||
when(oldVal.type){
|
||||
DataType.STR,
|
||||
DataType.STR_P,
|
||||
DataType.STR_S,
|
||||
DataType.STR_PS -> {
|
||||
if(oldVal.str!!.length!=str.length)
|
||||
throw IllegalArgumentException("heap string length mismatch")
|
||||
heap[heapId] = oldVal.copy(str=str)
|
||||
}
|
||||
else-> throw IllegalArgumentException("heap data type mismatch")
|
||||
if(oldVal.type in StringDatatypes) {
|
||||
if (oldVal.str!!.length != str.length)
|
||||
throw IllegalArgumentException("heap string length mismatch")
|
||||
heap[heapId] = oldVal.copy(str = str)
|
||||
}
|
||||
else throw IllegalArgumentException("heap data type mismatch")
|
||||
}
|
||||
|
||||
fun update(heapId: Int, heapval: HeapValue) {
|
||||
@@ -132,6 +127,7 @@ enum class LauncherType {
|
||||
|
||||
enum class ZeropageType {
|
||||
BASICSAFE,
|
||||
FLOATSAFE,
|
||||
KERNALSAFE,
|
||||
FULL
|
||||
}
|
||||
@@ -144,28 +140,21 @@ data class CompilationOptions(val output: OutputType,
|
||||
val floats: Boolean)
|
||||
|
||||
|
||||
class Compiler(private val options: CompilationOptions) {
|
||||
fun compile(module: Module, heap: HeapValues) : IntermediateProgram {
|
||||
println("\nCreating stackVM code...")
|
||||
internal class Compiler(private val rootModule: Module,
|
||||
private val namespace: INameScope,
|
||||
private val heap: HeapValues): IAstProcessor {
|
||||
|
||||
val namespace = module.definingScope()
|
||||
val program = IntermediateProgram(module.name, module.loadAddress, heap, module.importedFrom)
|
||||
val prog: IntermediateProgram = IntermediateProgram(rootModule.name, rootModule.loadAddress, heap, rootModule.importedFrom)
|
||||
|
||||
val translator = StatementTranslator(program, namespace, heap)
|
||||
translator.process(module)
|
||||
private var generatedLabelSequenceNumber = 0
|
||||
private val breakStmtLabelStack : Stack<String> = Stack()
|
||||
private val continueStmtLabelStack : Stack<String> = Stack()
|
||||
|
||||
return program
|
||||
fun compile(options: CompilationOptions) : IntermediateProgram {
|
||||
println("Creating stackVM code...")
|
||||
process(rootModule)
|
||||
return prog
|
||||
}
|
||||
}
|
||||
|
||||
private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
private val namespace: INameScope,
|
||||
private val heap: HeapValues): IAstProcessor {
|
||||
var generatedLabelSequenceNumber = 0
|
||||
private set
|
||||
|
||||
val breakStmtLabelStack : Stack<String> = Stack()
|
||||
val continueStmtLabelStack : Stack<String> = Stack()
|
||||
|
||||
override fun process(block: Block): IStatement {
|
||||
prog.newBlock(block.scopedname, block.name, block.address, block.options())
|
||||
@@ -243,12 +232,11 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
|
||||
private fun opcodePush(dt: DataType): Opcode {
|
||||
return when (dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.PUSH_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.PUSH_WORD
|
||||
in ByteDatatypes -> Opcode.PUSH_BYTE
|
||||
in WordDatatypes -> Opcode.PUSH_WORD
|
||||
in IterableDatatypes -> Opcode.PUSH_WORD
|
||||
DataType.FLOAT -> Opcode.PUSH_FLOAT
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS,
|
||||
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F,
|
||||
DataType.ARRAY_B, DataType.ARRAY_W -> Opcode.PUSH_WORD
|
||||
else -> throw CompilerException("invalid dt $dt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,12 +274,11 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
|
||||
private fun opcodePushvar(dt: DataType): Opcode {
|
||||
return when (dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.PUSH_VAR_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.PUSH_VAR_WORD
|
||||
in ByteDatatypes -> Opcode.PUSH_VAR_BYTE
|
||||
in WordDatatypes -> Opcode.PUSH_VAR_WORD
|
||||
in IterableDatatypes -> Opcode.PUSH_ADDR_HEAPVAR
|
||||
DataType.FLOAT -> Opcode.PUSH_VAR_FLOAT
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS,
|
||||
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F,
|
||||
DataType.ARRAY_B, DataType.ARRAY_W -> Opcode.PUSH_ADDR_HEAPVAR
|
||||
else -> throw CompilerException("invalid dt $dt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +288,6 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> Opcode.READ_INDEXED_VAR_WORD
|
||||
DataType.ARRAY_F -> Opcode.READ_INDEXED_VAR_FLOAT
|
||||
DataType.STR, DataType.STR_S -> Opcode.READ_INDEXED_VAR_BYTE
|
||||
DataType.STR_P, DataType.STR_PS -> throw CompilerException("cannot index on type $dt - use regular 0-terminated str type")
|
||||
else -> throw CompilerException("invalid dt for indexed access $dt")
|
||||
}
|
||||
}
|
||||
@@ -312,41 +298,37 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> Opcode.WRITE_INDEXED_VAR_WORD
|
||||
DataType.ARRAY_F -> Opcode.WRITE_INDEXED_VAR_FLOAT
|
||||
DataType.STR, DataType.STR_S -> Opcode.WRITE_INDEXED_VAR_BYTE
|
||||
DataType.STR_P, DataType.STR_PS -> TODO("cannot index on type $dt - use regular str type")
|
||||
else -> throw CompilerException("invalid dt for indexed access $dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun opcodeDiscard(dt: DataType): Opcode {
|
||||
return when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.DISCARD_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.DISCARD_WORD
|
||||
in ByteDatatypes -> Opcode.DISCARD_BYTE
|
||||
in WordDatatypes -> Opcode.DISCARD_WORD
|
||||
in IterableDatatypes -> Opcode.DISCARD_WORD
|
||||
DataType.FLOAT -> Opcode.DISCARD_FLOAT
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS,
|
||||
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F,
|
||||
DataType.ARRAY_B, DataType.ARRAY_W -> Opcode.DISCARD_WORD
|
||||
else -> throw CompilerException("invalid dt $dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun opcodePopvar(dt: DataType): Opcode {
|
||||
return when (dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.POP_VAR_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.POP_VAR_WORD
|
||||
in ByteDatatypes -> Opcode.POP_VAR_BYTE
|
||||
in WordDatatypes -> Opcode.POP_VAR_WORD
|
||||
in IterableDatatypes -> Opcode.POP_VAR_WORD
|
||||
DataType.FLOAT -> Opcode.POP_VAR_FLOAT
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS,
|
||||
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F,
|
||||
DataType.ARRAY_B, DataType.ARRAY_W -> Opcode.POP_VAR_WORD
|
||||
else -> throw CompilerException("invalid dt $dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun opcodePopmem(dt: DataType): Opcode {
|
||||
return when (dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.POP_MEM_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.POP_MEM_WORD
|
||||
in ByteDatatypes -> Opcode.POP_MEM_BYTE
|
||||
in WordDatatypes -> Opcode.POP_MEM_WORD
|
||||
in IterableDatatypes -> Opcode.POP_MEM_WORD
|
||||
DataType.FLOAT -> Opcode.POP_MEM_FLOAT
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS,
|
||||
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F,
|
||||
DataType.ARRAY_B, DataType.ARRAY_W -> Opcode.POP_MEM_WORD
|
||||
else -> throw CompilerException("invalid dt $dt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,8 +505,8 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
if(trueGoto!=null) {
|
||||
// optimization for if (condition) goto ....
|
||||
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(namespace, heap)) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.JNZ
|
||||
DataType.UWORD, DataType.WORD -> Opcode.JNZW
|
||||
in ByteDatatypes -> Opcode.JNZ
|
||||
in WordDatatypes -> Opcode.JNZW
|
||||
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
|
||||
}
|
||||
translate(trueGoto, conditionJumpOpcode)
|
||||
@@ -533,8 +515,8 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
}
|
||||
|
||||
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(namespace, heap)) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.JZ
|
||||
DataType.UWORD, DataType.WORD -> Opcode.JZW
|
||||
in ByteDatatypes -> Opcode.JZ
|
||||
in WordDatatypes -> Opcode.JZW
|
||||
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
|
||||
}
|
||||
val labelEnd = makeLabel("end")
|
||||
@@ -579,8 +561,8 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
when(rightDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> DataType.BYTE
|
||||
DataType.UWORD, DataType.WORD -> DataType.WORD
|
||||
in ByteDatatypes -> DataType.BYTE
|
||||
in WordDatatypes -> DataType.WORD
|
||||
DataType.FLOAT -> {
|
||||
printWarning(floatWarning, leftpos)
|
||||
DataType.FLOAT
|
||||
@@ -621,7 +603,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
private fun translate(expr: IExpression) {
|
||||
when(expr) {
|
||||
is RegisterExpr -> {
|
||||
prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = expr.register.toString())
|
||||
prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = expr.register.name)
|
||||
}
|
||||
is PrefixExpression -> {
|
||||
translate(expr.expression)
|
||||
@@ -651,7 +633,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
if(target is BuiltinFunctionStatementPlaceholder) {
|
||||
// call to a builtin function (some will just be an opcode!)
|
||||
val funcname = expr.target.nameInSource[0]
|
||||
translateFunctionCall(funcname, expr.arglist)
|
||||
translateBuiltinFunctionCall(funcname, expr.arglist)
|
||||
} else {
|
||||
when(target) {
|
||||
is Subroutine -> translateSubroutineCall(target, expr.arglist, expr.position)
|
||||
@@ -668,20 +650,20 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
else -> {
|
||||
val lv = expr.constValue(namespace, heap) ?: throw CompilerException("constant expression required, not $expr")
|
||||
when(lv.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> prog.instr(Opcode.PUSH_BYTE, Value(lv.type, lv.bytevalue!!))
|
||||
DataType.UWORD, DataType.WORD -> prog.instr(Opcode.PUSH_WORD, Value(lv.type, lv.wordvalue!!))
|
||||
in ByteDatatypes -> prog.instr(Opcode.PUSH_BYTE, Value(lv.type, lv.bytevalue!!))
|
||||
in WordDatatypes -> prog.instr(Opcode.PUSH_WORD, Value(lv.type, lv.wordvalue!!))
|
||||
DataType.FLOAT -> prog.instr(Opcode.PUSH_FLOAT, Value(lv.type, lv.floatvalue!!))
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
||||
in StringDatatypes -> {
|
||||
if(lv.heapId==null)
|
||||
throw CompilerException("string should have been moved into heap ${lv.position}")
|
||||
TODO("push address of string with PUSH_ADDR_HEAPVAR")
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F,
|
||||
DataType.ARRAY_B, DataType.ARRAY_W -> {
|
||||
in ArrayDatatypes -> {
|
||||
if(lv.heapId==null)
|
||||
throw CompilerException("array should have been moved into heap ${lv.position}")
|
||||
TODO("push address of array with PUSH_WORD")
|
||||
}
|
||||
else -> throw CompilerException("weird datatype")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -720,12 +702,12 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
else -> {}
|
||||
}
|
||||
DataType.UWORD -> when(targetDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> throw CompilerException("narrowing type")
|
||||
in ByteDatatypes -> throw CompilerException("narrowing type")
|
||||
DataType.FLOAT -> prog.instr(Opcode.CAST_UW_TO_F)
|
||||
else -> {}
|
||||
}
|
||||
DataType.WORD -> when(targetDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> throw CompilerException("narrowing type")
|
||||
in ByteDatatypes -> throw CompilerException("narrowing type")
|
||||
DataType.FLOAT -> prog.instr(Opcode.CAST_W_TO_F)
|
||||
else -> {}
|
||||
}
|
||||
@@ -767,7 +749,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
val targetStmt = stmt.target.targetStatement(namespace)!!
|
||||
if(targetStmt is BuiltinFunctionStatementPlaceholder) {
|
||||
val funcname = stmt.target.nameInSource[0]
|
||||
translateFunctionCall(funcname, stmt.arglist)
|
||||
translateBuiltinFunctionCall(funcname, stmt.arglist)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -777,20 +759,18 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
is Subroutine -> {
|
||||
translateSubroutineCall(targetStmt, stmt.arglist, stmt.position)
|
||||
// make sure we clean up the unused result values from the stack
|
||||
// only if they're non-register return values!
|
||||
if(targetStmt.asmReturnvaluesRegisters.isEmpty())
|
||||
for(rv in targetStmt.returntypes) {
|
||||
val opcode=opcodeDiscard(rv)
|
||||
prog.instr(opcode)
|
||||
}
|
||||
for(rv in targetStmt.returntypes) {
|
||||
val opcode=opcodeDiscard(rv)
|
||||
prog.instr(opcode)
|
||||
}
|
||||
}
|
||||
else ->
|
||||
throw AstException("invalid call target node type: ${targetStmt::class}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateFunctionCall(funcname: String, args: List<IExpression>) {
|
||||
// some functions are implemented as vm opcodes
|
||||
private fun translateBuiltinFunctionCall(funcname: String, args: List<IExpression>) {
|
||||
// some builtin functions are implemented directly as vm opcodes
|
||||
|
||||
if(funcname == "swap") {
|
||||
translateSwap(args)
|
||||
@@ -818,10 +798,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
// 1 argument, type determines the exact syscall to use
|
||||
val arg=args.single()
|
||||
when (arg.resultingDatatype(namespace, heap)) {
|
||||
DataType.STR -> createSyscall("${funcname}_str")
|
||||
DataType.STR_P -> createSyscall("${funcname}_strp")
|
||||
DataType.STR_S -> createSyscall("${funcname}_str")
|
||||
DataType.STR_PS -> createSyscall("${funcname}_strp")
|
||||
DataType.STR, DataType.STR_S -> createSyscall("${funcname}_str")
|
||||
else -> throw CompilerException("wrong datatype for len()")
|
||||
}
|
||||
}
|
||||
@@ -901,12 +878,12 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
val arg = args.single()
|
||||
val dt = arg.resultingDatatype(namespace, heap)
|
||||
when (dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> prog.instr(Opcode.SHL_BYTE)
|
||||
DataType.UWORD, DataType.WORD -> prog.instr(Opcode.SHL_WORD)
|
||||
in ByteDatatypes -> prog.instr(Opcode.SHL_BYTE)
|
||||
in WordDatatypes -> prog.instr(Opcode.SHL_WORD)
|
||||
else -> throw CompilerException("wrong datatype")
|
||||
}
|
||||
// this function doesn't return a value on the stack so we pop it directly into the argument register/variable again
|
||||
popValueIntoTarget(AssignTarget.fromExpr(arg), dt)
|
||||
popValueIntoTarget(AssignTarget.fromExpr(arg), dt!!)
|
||||
}
|
||||
"lsr" -> {
|
||||
val arg = args.single()
|
||||
@@ -936,34 +913,34 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
val arg = args.single()
|
||||
val dt = arg.resultingDatatype(namespace, heap)
|
||||
when (dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> prog.instr(Opcode.ROR_BYTE)
|
||||
DataType.UWORD, DataType.WORD -> prog.instr(Opcode.ROR_WORD)
|
||||
in ByteDatatypes -> prog.instr(Opcode.ROR_BYTE)
|
||||
in WordDatatypes -> prog.instr(Opcode.ROR_WORD)
|
||||
else -> throw CompilerException("wrong datatype")
|
||||
}
|
||||
// this function doesn't return a value on the stack so we pop it directly into the argument register/variable again
|
||||
popValueIntoTarget(AssignTarget.fromExpr(arg), dt)
|
||||
popValueIntoTarget(AssignTarget.fromExpr(arg), dt!!)
|
||||
}
|
||||
"rol2" -> {
|
||||
val arg = args.single()
|
||||
val dt = arg.resultingDatatype(namespace, heap)
|
||||
when (dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> prog.instr(Opcode.ROL2_BYTE)
|
||||
DataType.UWORD, DataType.WORD -> prog.instr(Opcode.ROL2_WORD)
|
||||
in ByteDatatypes -> prog.instr(Opcode.ROL2_BYTE)
|
||||
in WordDatatypes -> prog.instr(Opcode.ROL2_WORD)
|
||||
else -> throw CompilerException("wrong datatype")
|
||||
}
|
||||
// this function doesn't return a value on the stack so we pop it directly into the argument register/variable again
|
||||
popValueIntoTarget(AssignTarget.fromExpr(arg), dt)
|
||||
popValueIntoTarget(AssignTarget.fromExpr(arg), dt!!)
|
||||
}
|
||||
"ror2" -> {
|
||||
val arg = args.single()
|
||||
val dt = arg.resultingDatatype(namespace, heap)
|
||||
when (dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> prog.instr(Opcode.ROR2_BYTE)
|
||||
DataType.UWORD, DataType.WORD -> prog.instr(Opcode.ROR2_WORD)
|
||||
in ByteDatatypes -> prog.instr(Opcode.ROR2_BYTE)
|
||||
in WordDatatypes -> prog.instr(Opcode.ROR2_WORD)
|
||||
else -> throw CompilerException("wrong datatype")
|
||||
}
|
||||
// this function doesn't return a value on the stack so we pop it directly into the argument register/variable again
|
||||
popValueIntoTarget(AssignTarget.fromExpr(arg), dt)
|
||||
popValueIntoTarget(AssignTarget.fromExpr(arg), dt!!)
|
||||
}
|
||||
"set_carry" -> prog.instr(Opcode.SEC)
|
||||
"clear_carry" -> prog.instr(Opcode.CLC)
|
||||
@@ -1006,157 +983,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
// We don't bother about saving A and Y. They're considered expendable.
|
||||
|
||||
if(subroutine.isAsmSubroutine) {
|
||||
if(subroutine.parameters.size!=subroutine.asmParameterRegisters.size)
|
||||
throw CompilerException("no support for mix of register and non-register subroutine arguments")
|
||||
|
||||
// only register arguments (or status-flag bits)
|
||||
var carryParam: Boolean? = null
|
||||
for(arg in arguments.zip(subroutine.asmParameterRegisters)) {
|
||||
if(arg.second.statusflag!=null) {
|
||||
if(arg.second.statusflag==Statusflag.Pc)
|
||||
carryParam = arg.first.constValue(namespace, heap)!!.asBooleanValue
|
||||
else
|
||||
throw CompilerException("no support for status flag parameter: ${arg.second.statusflag}")
|
||||
} else {
|
||||
when (arg.second.registerOrPair!!) {
|
||||
A -> {
|
||||
val assign = Assignment(listOf(AssignTarget(Register.A, null, null, null, callPosition)), null, arg.first, callPosition)
|
||||
assign.linkParents(arguments[0].parent)
|
||||
translate(assign)
|
||||
}
|
||||
X -> {
|
||||
if(!restoreX) {
|
||||
prog.instr(Opcode.RSAVEX)
|
||||
restoreX = true
|
||||
}
|
||||
val assign = Assignment(listOf(AssignTarget(Register.X, null, null, null, callPosition)), null, arg.first, callPosition)
|
||||
assign.linkParents(arguments[0].parent)
|
||||
translate(assign)
|
||||
}
|
||||
Y -> {
|
||||
val assign = Assignment(listOf(AssignTarget(Register.Y, null, null, null, callPosition)), null, arg.first, callPosition)
|
||||
assign.linkParents(arguments[0].parent)
|
||||
translate(assign)
|
||||
}
|
||||
AX -> {
|
||||
if(!restoreX) {
|
||||
prog.instr(Opcode.RSAVEX)
|
||||
restoreX = true
|
||||
}
|
||||
val valueA: IExpression
|
||||
val valueX: IExpression
|
||||
val paramDt = arg.first.resultingDatatype(namespace, heap)
|
||||
when (paramDt) {
|
||||
DataType.UBYTE -> {
|
||||
valueA=arg.first
|
||||
valueX=LiteralValue.optimalInteger(0, callPosition)
|
||||
val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, null, callPosition)), null, valueA, callPosition)
|
||||
val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, null, callPosition)), null, valueX, callPosition)
|
||||
assignA.linkParents(arguments[0].parent)
|
||||
assignX.linkParents(arguments[0].parent)
|
||||
translate(assignA)
|
||||
translate(assignX)
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
translate(arg.first)
|
||||
prog.instr(Opcode.POP_REGAX_WORD)
|
||||
}
|
||||
DataType.STR, DataType.STR_S -> {
|
||||
pushStringAddress(arg.first, false)
|
||||
prog.instr(Opcode.POP_REGAX_WORD)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
pushFloatAddress(arg.first)
|
||||
prog.instr(Opcode.POP_REGAX_WORD)
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
pushStringAddress(arg.first, false)
|
||||
prog.instr(Opcode.POP_REGAX_WORD)
|
||||
}
|
||||
else -> TODO("pass parameter of type $paramDt in registers AX at $callPosition")
|
||||
}
|
||||
}
|
||||
AY -> {
|
||||
val valueA: IExpression
|
||||
val valueY: IExpression
|
||||
val paramDt = arg.first.resultingDatatype(namespace, heap)
|
||||
when (paramDt) {
|
||||
DataType.UBYTE -> {
|
||||
valueA=arg.first
|
||||
valueY=LiteralValue.optimalInteger(0, callPosition)
|
||||
val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, null, callPosition)), null, valueA, callPosition)
|
||||
val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, null, callPosition)), null, valueY, callPosition)
|
||||
assignA.linkParents(arguments[0].parent)
|
||||
assignY.linkParents(arguments[0].parent)
|
||||
translate(assignA)
|
||||
translate(assignY)
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
translate(arg.first)
|
||||
prog.instr(Opcode.POP_REGAY_WORD)
|
||||
}
|
||||
DataType.STR, DataType.STR_S -> {
|
||||
pushStringAddress(arg.first, false)
|
||||
prog.instr(Opcode.POP_REGAY_WORD)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
pushFloatAddress(arg.first)
|
||||
prog.instr(Opcode.POP_REGAY_WORD)
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
pushStringAddress(arg.first, false)
|
||||
prog.instr(Opcode.POP_REGAY_WORD)
|
||||
}
|
||||
else -> TODO("pass parameter of type $paramDt in registers AY at $callPosition")
|
||||
}
|
||||
}
|
||||
XY -> {
|
||||
if(!restoreX) {
|
||||
prog.instr(Opcode.RSAVEX)
|
||||
restoreX = true
|
||||
}
|
||||
val valueX: IExpression
|
||||
val valueY: IExpression
|
||||
val paramDt = arg.first.resultingDatatype(namespace, heap)
|
||||
when (paramDt) {
|
||||
DataType.UBYTE -> {
|
||||
valueX=arg.first
|
||||
valueY=LiteralValue.optimalInteger(0, callPosition)
|
||||
val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, null, callPosition)), null, valueX, callPosition)
|
||||
val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, null, callPosition)), null, valueY, callPosition)
|
||||
assignX.linkParents(arguments[0].parent)
|
||||
assignY.linkParents(arguments[0].parent)
|
||||
translate(assignX)
|
||||
translate(assignY)
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
translate(arg.first)
|
||||
prog.instr(Opcode.POP_REGXY_WORD)
|
||||
}
|
||||
DataType.STR, DataType.STR_S -> {
|
||||
pushStringAddress(arg.first, false)
|
||||
prog.instr(Opcode.POP_REGXY_WORD)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
pushFloatAddress(arg.first)
|
||||
prog.instr(Opcode.POP_REGXY_WORD)
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
pushStringAddress(arg.first, false)
|
||||
prog.instr(Opcode.POP_REGXY_WORD)
|
||||
}
|
||||
else -> TODO("pass parameter of type $paramDt in registers XY at $callPosition")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// carry is set last, to avoid clobbering it when loading the other parameters
|
||||
when(carryParam) {
|
||||
true -> prog.instr(Opcode.SEC)
|
||||
false -> prog.instr(Opcode.CLC)
|
||||
}
|
||||
restoreX = translateAsmSubCallArguments(subroutine, arguments, callPosition, restoreX)
|
||||
} else {
|
||||
// only regular (non-register) arguments
|
||||
// "assign" the arguments to the locally scoped parameter variables for this subroutine
|
||||
@@ -1171,6 +998,176 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
prog.instr(Opcode.CALL, callLabel = subroutine.scopedname)
|
||||
if(restoreX)
|
||||
prog.instr(Opcode.RRESTOREX)
|
||||
|
||||
if(subroutine.isAsmSubroutine && subroutine.asmReturnvaluesRegisters.isNotEmpty()) {
|
||||
// the result values of the asm-subroutine that are returned in registers, have to be pushed on the stack
|
||||
// (in reversed order) otherwise the asm-subroutine can't be used in expressions.
|
||||
for(rv in subroutine.asmReturnvaluesRegisters.reversed()) {
|
||||
if(rv.statusflag!=null)
|
||||
TODO("not yet supported: return values in cpu status flag $rv $subroutine")
|
||||
when(rv.registerOrPair) {
|
||||
A,X,Y -> prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = rv.registerOrPair.name)
|
||||
AX, AY, XY -> prog.instr(Opcode.PUSH_VAR_WORD, callLabel = rv.registerOrPair.name)
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateAsmSubCallArguments(subroutine: Subroutine, arguments: List<IExpression>, callPosition: Position, restoreXinitial: Boolean): Boolean {
|
||||
var restoreX = restoreXinitial
|
||||
if (subroutine.parameters.size != subroutine.asmParameterRegisters.size)
|
||||
TODO("no support yet for mix of register and non-register subroutine arguments")
|
||||
|
||||
// only register arguments (or status-flag bits)
|
||||
var carryParam: Boolean? = null
|
||||
for (arg in arguments.zip(subroutine.asmParameterRegisters)) {
|
||||
if (arg.second.statusflag != null) {
|
||||
if (arg.second.statusflag == Statusflag.Pc)
|
||||
carryParam = arg.first.constValue(namespace, heap)!!.asBooleanValue
|
||||
else
|
||||
throw CompilerException("no support for status flag parameter: ${arg.second.statusflag}")
|
||||
} else {
|
||||
when (arg.second.registerOrPair!!) {
|
||||
A -> {
|
||||
val assign = Assignment(listOf(AssignTarget(Register.A, null, null, null, callPosition)), null, arg.first, callPosition)
|
||||
assign.linkParents(arguments[0].parent)
|
||||
translate(assign)
|
||||
}
|
||||
X -> {
|
||||
if (!restoreX) {
|
||||
prog.instr(Opcode.RSAVEX)
|
||||
restoreX = true
|
||||
}
|
||||
val assign = Assignment(listOf(AssignTarget(Register.X, null, null, null, callPosition)), null, arg.first, callPosition)
|
||||
assign.linkParents(arguments[0].parent)
|
||||
translate(assign)
|
||||
}
|
||||
Y -> {
|
||||
val assign = Assignment(listOf(AssignTarget(Register.Y, null, null, null, callPosition)), null, arg.first, callPosition)
|
||||
assign.linkParents(arguments[0].parent)
|
||||
translate(assign)
|
||||
}
|
||||
AX -> {
|
||||
if (!restoreX) {
|
||||
prog.instr(Opcode.RSAVEX)
|
||||
restoreX = true
|
||||
}
|
||||
val valueA: IExpression
|
||||
val valueX: IExpression
|
||||
val paramDt = arg.first.resultingDatatype(namespace, heap)
|
||||
when (paramDt) {
|
||||
DataType.UBYTE -> {
|
||||
valueA = arg.first
|
||||
valueX = LiteralValue.optimalInteger(0, callPosition)
|
||||
val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, null, callPosition)), null, valueA, callPosition)
|
||||
val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, null, callPosition)), null, valueX, callPosition)
|
||||
assignA.linkParents(arguments[0].parent)
|
||||
assignX.linkParents(arguments[0].parent)
|
||||
translate(assignA)
|
||||
translate(assignX)
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
translate(arg.first)
|
||||
prog.instr(Opcode.POP_REGAX_WORD)
|
||||
}
|
||||
DataType.STR, DataType.STR_S -> {
|
||||
pushStringAddress(arg.first, false)
|
||||
prog.instr(Opcode.POP_REGAX_WORD)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
pushFloatAddress(arg.first)
|
||||
prog.instr(Opcode.POP_REGAX_WORD)
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
pushStringAddress(arg.first, false)
|
||||
prog.instr(Opcode.POP_REGAX_WORD)
|
||||
}
|
||||
else -> TODO("pass parameter of type $paramDt in registers AX at $callPosition")
|
||||
}
|
||||
}
|
||||
AY -> {
|
||||
val valueA: IExpression
|
||||
val valueY: IExpression
|
||||
val paramDt = arg.first.resultingDatatype(namespace, heap)
|
||||
when (paramDt) {
|
||||
DataType.UBYTE -> {
|
||||
valueA = arg.first
|
||||
valueY = LiteralValue.optimalInteger(0, callPosition)
|
||||
val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, null, callPosition)), null, valueA, callPosition)
|
||||
val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, null, callPosition)), null, valueY, callPosition)
|
||||
assignA.linkParents(arguments[0].parent)
|
||||
assignY.linkParents(arguments[0].parent)
|
||||
translate(assignA)
|
||||
translate(assignY)
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
translate(arg.first)
|
||||
prog.instr(Opcode.POP_REGAY_WORD)
|
||||
}
|
||||
DataType.STR, DataType.STR_S -> {
|
||||
pushStringAddress(arg.first, false)
|
||||
prog.instr(Opcode.POP_REGAY_WORD)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
pushFloatAddress(arg.first)
|
||||
prog.instr(Opcode.POP_REGAY_WORD)
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
pushStringAddress(arg.first, false)
|
||||
prog.instr(Opcode.POP_REGAY_WORD)
|
||||
}
|
||||
else -> TODO("pass parameter of type $paramDt in registers AY at $callPosition")
|
||||
}
|
||||
}
|
||||
XY -> {
|
||||
if (!restoreX) {
|
||||
prog.instr(Opcode.RSAVEX)
|
||||
restoreX = true
|
||||
}
|
||||
val valueX: IExpression
|
||||
val valueY: IExpression
|
||||
val paramDt = arg.first.resultingDatatype(namespace, heap)
|
||||
when (paramDt) {
|
||||
DataType.UBYTE -> {
|
||||
valueX = arg.first
|
||||
valueY = LiteralValue.optimalInteger(0, callPosition)
|
||||
val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, null, callPosition)), null, valueX, callPosition)
|
||||
val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, null, callPosition)), null, valueY, callPosition)
|
||||
assignX.linkParents(arguments[0].parent)
|
||||
assignY.linkParents(arguments[0].parent)
|
||||
translate(assignX)
|
||||
translate(assignY)
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
translate(arg.first)
|
||||
prog.instr(Opcode.POP_REGXY_WORD)
|
||||
}
|
||||
DataType.STR, DataType.STR_S -> {
|
||||
pushStringAddress(arg.first, false)
|
||||
prog.instr(Opcode.POP_REGXY_WORD)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
pushFloatAddress(arg.first)
|
||||
prog.instr(Opcode.POP_REGXY_WORD)
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
pushStringAddress(arg.first, false)
|
||||
prog.instr(Opcode.POP_REGXY_WORD)
|
||||
}
|
||||
else -> TODO("pass parameter of type $paramDt in registers XY at $callPosition")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// carry is set last, to avoid clobbering it when loading the other parameters
|
||||
when (carryParam) {
|
||||
true -> prog.instr(Opcode.SEC)
|
||||
false -> prog.instr(Opcode.CLC)
|
||||
}
|
||||
return restoreX
|
||||
}
|
||||
|
||||
private fun translateBinaryOperator(operator: String, dt: DataType) {
|
||||
@@ -1237,43 +1234,43 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
}
|
||||
"&" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.BITAND_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.BITAND_WORD
|
||||
in ByteDatatypes -> Opcode.BITAND_BYTE
|
||||
in WordDatatypes -> Opcode.BITAND_WORD
|
||||
else -> throw CompilerException("only byte/word possible")
|
||||
}
|
||||
}
|
||||
"|" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.BITOR_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.BITOR_WORD
|
||||
in ByteDatatypes -> Opcode.BITOR_BYTE
|
||||
in WordDatatypes -> Opcode.BITOR_WORD
|
||||
else -> throw CompilerException("only byte/word possible")
|
||||
}
|
||||
}
|
||||
"^" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.BITXOR_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.BITXOR_WORD
|
||||
in ByteDatatypes -> Opcode.BITXOR_BYTE
|
||||
in WordDatatypes -> Opcode.BITXOR_WORD
|
||||
else -> throw CompilerException("only byte/word possible")
|
||||
}
|
||||
}
|
||||
"and" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.AND_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.AND_WORD
|
||||
in ByteDatatypes -> Opcode.AND_BYTE
|
||||
in WordDatatypes -> Opcode.AND_WORD
|
||||
else -> throw CompilerException("only byte/word possible")
|
||||
}
|
||||
}
|
||||
"or" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.OR_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.OR_WORD
|
||||
in ByteDatatypes -> Opcode.OR_BYTE
|
||||
in WordDatatypes -> Opcode.OR_WORD
|
||||
else -> throw CompilerException("only byte/word possible")
|
||||
}
|
||||
}
|
||||
"xor" -> {
|
||||
when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.XOR_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.XOR_WORD
|
||||
in ByteDatatypes -> Opcode.XOR_BYTE
|
||||
in WordDatatypes -> Opcode.XOR_WORD
|
||||
else -> throw CompilerException("only byte/word possible")
|
||||
}
|
||||
}
|
||||
@@ -1319,16 +1316,16 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
}
|
||||
"==" -> {
|
||||
when (dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.EQUAL_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.EQUAL_WORD
|
||||
in ByteDatatypes -> Opcode.EQUAL_BYTE
|
||||
in WordDatatypes -> Opcode.EQUAL_WORD
|
||||
DataType.FLOAT -> Opcode.EQUAL_F
|
||||
else -> throw CompilerException("only byte/word/lfoat possible")
|
||||
}
|
||||
}
|
||||
"!=" -> {
|
||||
when (dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.NOTEQUAL_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.NOTEQUAL_WORD
|
||||
in ByteDatatypes -> Opcode.NOTEQUAL_BYTE
|
||||
in WordDatatypes -> Opcode.NOTEQUAL_WORD
|
||||
DataType.FLOAT -> Opcode.NOTEQUAL_F
|
||||
else -> throw CompilerException("only byte/word/lfoat possible")
|
||||
}
|
||||
@@ -1359,8 +1356,8 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
}
|
||||
} else if(operator=="<<") {
|
||||
when (leftDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> prog.instr(Opcode.SHIFTEDL_BYTE)
|
||||
DataType.UWORD, DataType.WORD -> prog.instr(Opcode.SHIFTEDL_WORD)
|
||||
in ByteDatatypes -> prog.instr(Opcode.SHIFTEDL_BYTE)
|
||||
in WordDatatypes -> prog.instr(Opcode.SHIFTEDL_WORD)
|
||||
else -> throw CompilerException("wrong datatype")
|
||||
}
|
||||
}
|
||||
@@ -1383,15 +1380,15 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
}
|
||||
"~" -> {
|
||||
when(operandDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.INV_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.INV_WORD
|
||||
in ByteDatatypes -> Opcode.INV_BYTE
|
||||
in WordDatatypes -> Opcode.INV_WORD
|
||||
else -> throw CompilerException("only byte/word possible")
|
||||
}
|
||||
}
|
||||
"not" -> {
|
||||
when(operandDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.NOT_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.NOT_WORD
|
||||
in ByteDatatypes -> Opcode.NOT_BYTE
|
||||
in WordDatatypes -> Opcode.NOT_WORD
|
||||
else -> throw CompilerException("only byte/word possible")
|
||||
}
|
||||
}
|
||||
@@ -1448,8 +1445,8 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
prog.line(stmt.position)
|
||||
when {
|
||||
stmt.target.register != null -> when(stmt.operator) {
|
||||
"++" -> prog.instr(Opcode.INC_VAR_UB, callLabel = stmt.target.register.toString())
|
||||
"--" -> prog.instr(Opcode.DEC_VAR_UB, callLabel = stmt.target.register.toString())
|
||||
"++" -> prog.instr(Opcode.INC_VAR_UB, callLabel = stmt.target.register!!.name)
|
||||
"--" -> prog.instr(Opcode.DEC_VAR_UB, callLabel = stmt.target.register!!.name)
|
||||
}
|
||||
stmt.target.identifier != null -> {
|
||||
val targetStatement = stmt.target.identifier!!.targetStatement(namespace) as VarDecl
|
||||
@@ -1502,7 +1499,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
// convert value to target datatype if possible
|
||||
// @todo use convertType()????
|
||||
when(targetDt) {
|
||||
DataType.UBYTE, DataType.BYTE ->
|
||||
in ByteDatatypes ->
|
||||
if(valueDt!=DataType.BYTE && valueDt!=DataType.UBYTE)
|
||||
throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
||||
DataType.WORD -> {
|
||||
@@ -1538,24 +1535,15 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
||||
}
|
||||
}
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
||||
null -> throw CompilerException("could not determine targetdt")
|
||||
in StringDatatypes -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
||||
in ArrayDatatypes -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
||||
else -> throw CompilerException("weird/unknonwn targetdt")
|
||||
}
|
||||
}
|
||||
|
||||
if(stmt.aug_op!=null)
|
||||
throw CompilerException("augmented assignment should have been converted to regular assignment already")
|
||||
|
||||
if(stmt.value is FunctionCall) {
|
||||
val sub = (stmt.value as FunctionCall).target.targetStatement(namespace)
|
||||
if(sub is Subroutine && sub.asmReturnvaluesRegisters.isNotEmpty()) {
|
||||
// the subroutine call returns its values in registers
|
||||
storeRegisterIntoTarget(sub.asmReturnvaluesRegisters.single(), assignTarget, stmt)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// pop the result value back into the assignment target
|
||||
val datatype = assignTarget.determineDatatype(namespace, heap, stmt)!!
|
||||
popValueIntoTarget(assignTarget, datatype)
|
||||
@@ -1587,6 +1575,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
private fun translateMultiReturnAssignment(stmt: Assignment) {
|
||||
val targetStmt = (stmt.value as? FunctionCall)?.target?.targetStatement(namespace)
|
||||
if(targetStmt is Subroutine && targetStmt.isAsmSubroutine) {
|
||||
// TODO check correctness of multi-return values (they should be on the stack rather than directly assigned!)
|
||||
// we're dealing with the one case where multiple assignment targets are allowed: a call to an asmsub with multiple return values
|
||||
// for now, we only support multiple return values as long as they're returned in registers as well.
|
||||
if(targetStmt.asmReturnvaluesRegisters.isEmpty())
|
||||
@@ -1673,7 +1662,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
}
|
||||
} else throw CompilerException("invalid assignment target type ${target::class}")
|
||||
}
|
||||
assignTarget.register != null -> prog.instr(Opcode.POP_VAR_BYTE, callLabel = assignTarget.register.toString())
|
||||
assignTarget.register != null -> prog.instr(Opcode.POP_VAR_BYTE, callLabel = assignTarget.register.name)
|
||||
assignTarget.arrayindexed != null -> translate(assignTarget.arrayindexed, true) // write value to it
|
||||
assignTarget.memoryAddress != null -> {
|
||||
val address = assignTarget.memoryAddress?.addressExpression?.constValue(namespace, heap)?.asIntegerValue
|
||||
@@ -1709,7 +1698,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
|
||||
if(loop.loopRegister!=null) {
|
||||
val reg = loop.loopRegister
|
||||
loopVarName = reg.toString()
|
||||
loopVarName = reg.name
|
||||
loopVarDt = DataType.UBYTE
|
||||
} else {
|
||||
val loopvar = (loop.loopVar!!.targetStatement(namespace) as VarDecl)
|
||||
@@ -1774,7 +1763,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
}
|
||||
|
||||
private fun translateForOverIterableVar(loop: ForLoop, loopvarDt: DataType, iterableValue: LiteralValue) {
|
||||
if(loopvarDt==DataType.UBYTE && iterableValue.type !in setOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.ARRAY_UB))
|
||||
if(loopvarDt==DataType.UBYTE && iterableValue.type !in setOf(DataType.STR, DataType.STR_S, DataType.ARRAY_UB))
|
||||
throw CompilerException("loop variable type doesn't match iterableValue type")
|
||||
else if(loopvarDt==DataType.UWORD && iterableValue.type != DataType.ARRAY_UW)
|
||||
throw CompilerException("loop variable type doesn't match iterableValue type")
|
||||
@@ -1783,10 +1772,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
|
||||
val numElements: Int
|
||||
when(iterableValue.type) {
|
||||
DataType.UBYTE, DataType.BYTE,
|
||||
DataType.UWORD, DataType.WORD,
|
||||
DataType.FLOAT -> throw CompilerException("non-iterableValue type")
|
||||
DataType.STR_P, DataType.STR_PS -> throw CompilerException("can't iterate string type ${iterableValue.type}")
|
||||
!in IterableDatatypes -> throw CompilerException("non-iterableValue type")
|
||||
DataType.STR, DataType.STR_S -> {
|
||||
numElements = iterableValue.strvalue(heap).length
|
||||
if(numElements>255) throw CompilerException("string length > 255")
|
||||
@@ -1800,6 +1786,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
numElements = iterableValue.arrayvalue?.size ?: heap.get(iterableValue.heapId!!).arraysize
|
||||
if(numElements>255) throw CompilerException("string length > 255")
|
||||
}
|
||||
else -> throw CompilerException("weird datatype")
|
||||
}
|
||||
|
||||
if(loop.loopRegister!=null && loop.loopRegister==Register.X)
|
||||
@@ -2042,15 +2029,15 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
postIncr.linkParents(body)
|
||||
translate(postIncr)
|
||||
if(lvTarget.register!=null)
|
||||
prog.instr(Opcode.PUSH_VAR_BYTE, callLabel =lvTarget.register.toString())
|
||||
prog.instr(Opcode.PUSH_VAR_BYTE, callLabel =lvTarget.register.name)
|
||||
else {
|
||||
val opcode = opcodePushvar(targetStatement!!.datatype)
|
||||
prog.instr(opcode, callLabel = targetStatement.scopedname)
|
||||
}
|
||||
// TODO: optimize this to use a compare + branch opcode somehow?
|
||||
val conditionJumpOpcode = when(targetStatement!!.datatype) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.JNZ
|
||||
DataType.UWORD, DataType.WORD -> Opcode.JNZW
|
||||
in ByteDatatypes -> Opcode.JNZ
|
||||
in WordDatatypes -> Opcode.JNZW
|
||||
else -> throw CompilerException("invalid loopvar datatype (expected byte or word) $lvTarget")
|
||||
}
|
||||
prog.instr(conditionJumpOpcode, callLabel = loopLabel)
|
||||
@@ -2101,8 +2088,8 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
prog.label(continueLabel)
|
||||
translate(stmt.condition)
|
||||
val conditionJumpOpcode = when(stmt.condition.resultingDatatype(namespace, heap)) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.JNZ
|
||||
DataType.UWORD, DataType.WORD -> Opcode.JNZW
|
||||
in ByteDatatypes -> Opcode.JNZ
|
||||
in WordDatatypes -> Opcode.JNZW
|
||||
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
|
||||
}
|
||||
prog.instr(conditionJumpOpcode, callLabel = loopLabel)
|
||||
@@ -2110,7 +2097,6 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
prog.instr(Opcode.NOP)
|
||||
breakStmtLabelStack.pop()
|
||||
continueStmtLabelStack.pop()
|
||||
|
||||
}
|
||||
|
||||
private fun translate(stmt: RepeatLoop)
|
||||
@@ -2139,8 +2125,8 @@ private class StatementTranslator(private val prog: IntermediateProgram,
|
||||
prog.label(continueLabel)
|
||||
translate(stmt.untilCondition)
|
||||
val conditionJumpOpcode = when(stmt.untilCondition.resultingDatatype(namespace, heap)) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.JZ
|
||||
DataType.UWORD, DataType.WORD -> Opcode.JZW
|
||||
in ByteDatatypes -> Opcode.JZ
|
||||
in WordDatatypes -> Opcode.JZW
|
||||
else -> throw CompilerException("invalid condition datatype (expected byte or word) $stmt")
|
||||
}
|
||||
prog.instr(conditionJumpOpcode, callLabel = loopLabel)
|
||||
|
||||
@@ -3,65 +3,70 @@ package prog8.compiler
|
||||
import prog8.ast.*
|
||||
|
||||
|
||||
abstract class Zeropage(private val options: CompilationOptions) {
|
||||
class ZeropageDepletedError(message: String) : Exception(message)
|
||||
|
||||
|
||||
abstract class Zeropage(protected val options: CompilationOptions) {
|
||||
|
||||
private val allocations = mutableMapOf<Int, Pair<String, DataType>>()
|
||||
val free = mutableListOf<Int>() // subclasses must set this to the appropriate free locations.
|
||||
|
||||
val allowedDatatypes = NumericDatatypes
|
||||
|
||||
fun available() = free.size
|
||||
|
||||
fun allocate(vardecl: VarDecl) : Int {
|
||||
assert(vardecl.name.isEmpty() || !allocations.values.any { it.first==vardecl.name } ) {"same name can't be allocated twice"}
|
||||
assert(vardecl.type== VarDeclType.VAR) {"can only allocate VAR type"}
|
||||
fun allocate(scopedname: String, datatype: DataType, position: Position?): Int {
|
||||
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"same scopedname can't be allocated twice"}
|
||||
|
||||
val size =
|
||||
if(vardecl.arrayspec!=null) {
|
||||
printWarning("allocating a large value (arrayspec) in zeropage", vardecl.position)
|
||||
when(vardecl.datatype) {
|
||||
DataType.UBYTE -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!!
|
||||
DataType.UWORD -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 2
|
||||
DataType.FLOAT -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 5
|
||||
else -> throw CompilerException("array can only be of byte, word, float")
|
||||
}
|
||||
} else {
|
||||
when (vardecl.datatype) {
|
||||
DataType.UBYTE -> 1
|
||||
DataType.UWORD -> 2
|
||||
when (datatype) {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes -> 2
|
||||
DataType.FLOAT -> {
|
||||
if (options.floats) {
|
||||
printWarning("allocating a large value (float) in zeropage", vardecl.position)
|
||||
if(position!=null)
|
||||
printWarning("allocated a large value (float) in zeropage", position)
|
||||
else
|
||||
printWarning("$scopedname: allocated a large value (float) in zeropage")
|
||||
5
|
||||
} else throw CompilerException("floating point option not enabled")
|
||||
}
|
||||
else -> throw CompilerException("cannot put datatype ${vardecl.datatype} in zeropage")
|
||||
else -> throw CompilerException("cannot put datatype $datatype in zeropage")
|
||||
}
|
||||
}
|
||||
|
||||
if(free.size > 0) {
|
||||
if(size==1) {
|
||||
for(candidate in free.min()!! .. free.max()!!+1) {
|
||||
if(loneByte(candidate))
|
||||
return makeAllocation(candidate, 1, vardecl.datatype, vardecl.name)
|
||||
return makeAllocation(candidate, 1, datatype, scopedname)
|
||||
}
|
||||
return makeAllocation(free[0], 1, vardecl.datatype, vardecl.name)
|
||||
return makeAllocation(free[0], 1, datatype, scopedname)
|
||||
}
|
||||
for(candidate in free.min()!! .. free.max()!!+1) {
|
||||
if (sequentialFree(candidate, size))
|
||||
return makeAllocation(candidate, size, vardecl.datatype, vardecl.name)
|
||||
return makeAllocation(candidate, size, datatype, scopedname)
|
||||
}
|
||||
}
|
||||
|
||||
throw CompilerException("ERROR: no free space in ZP to allocate $size sequential bytes")
|
||||
throw ZeropageDepletedError("ERROR: no free space in ZP to allocate $size sequential bytes")
|
||||
}
|
||||
|
||||
protected fun reserve(range: IntRange) = free.removeAll(range)
|
||||
|
||||
private fun makeAllocation(location: Int, size: Int, datatype: DataType, name: String?): Int {
|
||||
free.removeAll(location until location+size)
|
||||
allocations[location] = Pair(name ?: "<unnamed>", datatype)
|
||||
return location
|
||||
private fun makeAllocation(address: Int, size: Int, datatype: DataType, name: String?): Int {
|
||||
free.removeAll(address until address+size)
|
||||
allocations[address] = Pair(name ?: "<unnamed>", datatype)
|
||||
return address
|
||||
}
|
||||
|
||||
private fun loneByte(location: Int) = location in free && location-1 !in free && location+1 !in free
|
||||
private fun sequentialFree(location: Int, size: Int) = free.containsAll((location until location+size).toList())
|
||||
private fun loneByte(address: Int) = address in free && address-1 !in free && address+1 !in free
|
||||
private fun sequentialFree(address: Int, size: Int) = free.containsAll((address until address+size).toList())
|
||||
|
||||
enum class ExitProgramStrategy {
|
||||
CLEAN_EXIT,
|
||||
SYSTEM_RESET
|
||||
}
|
||||
|
||||
abstract val exitProgramStrategy: ExitProgramStrategy
|
||||
|
||||
}
|
||||
@@ -23,10 +23,10 @@ open class Instruction(val opcode: Opcode,
|
||||
}
|
||||
opcode in opcodesWithVarArgument -> {
|
||||
// opcodes that manipulate a variable
|
||||
"${opcode.toString().toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd()
|
||||
"${opcode.name.toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd()
|
||||
}
|
||||
callLabel==null -> "${opcode.toString().toLowerCase()} $argStr"
|
||||
else -> "${opcode.toString().toLowerCase()} $callLabel $argStr"
|
||||
callLabel==null -> "${opcode.name.toLowerCase()} $argStr"
|
||||
else -> "${opcode.name.toLowerCase()} $callLabel $argStr"
|
||||
}
|
||||
.trimEnd()
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ package prog8.compiler.intermediate
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.CompilerException
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.Zeropage
|
||||
import prog8.compiler.ZeropageDepletedError
|
||||
import java.io.PrintStream
|
||||
import java.nio.file.Path
|
||||
|
||||
@@ -22,8 +24,10 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
get() { return variables.size }
|
||||
val numInstructions: Int
|
||||
get() { return instructions.filter { it.opcode!= Opcode.LINE }.size }
|
||||
val variablesMarkedForZeropage: MutableSet<String> = mutableSetOf()
|
||||
}
|
||||
|
||||
val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>()
|
||||
val blocks = mutableListOf<ProgramBlock>()
|
||||
val memory = mutableMapOf<Int, List<Value>>()
|
||||
private lateinit var currentBlock: ProgramBlock
|
||||
@@ -33,6 +37,27 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
val numInstructions: Int
|
||||
get() = blocks.sumBy { it.numInstructions }
|
||||
|
||||
fun allocateZeropage(zeropage: Zeropage) {
|
||||
// allocates all @zp marked variables on the zeropage (for all blocks, as long as there is space in the ZP)
|
||||
var notAllocated = 0
|
||||
for(block in blocks) {
|
||||
val zpVariables = block.variables.filter { it.key in block.variablesMarkedForZeropage }
|
||||
if (zpVariables.isNotEmpty()) {
|
||||
for (variable in zpVariables) {
|
||||
try {
|
||||
val address = zeropage.allocate(variable.key, variable.value.type, null)
|
||||
allocatedZeropageVariables[variable.key] = Pair(address, variable.value.type)
|
||||
} catch (x: ZeropageDepletedError) {
|
||||
printWarning(x.toString() + " variable ${variable.key} type ${variable.value.type}")
|
||||
notAllocated++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(notAllocated>0)
|
||||
printWarning("$notAllocated variables marked for Zeropage could not be allocated there")
|
||||
}
|
||||
|
||||
fun optimize() {
|
||||
println("Optimizing stackVM code...")
|
||||
// remove nops (that are not a label)
|
||||
@@ -44,6 +69,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
optimizeVariableCopying()
|
||||
optimizeMultipleSequentialLineInstrs()
|
||||
optimizeCallReturnIntoJump()
|
||||
optimizeConditionalBranches()
|
||||
// todo: add more optimizations to stackvm code
|
||||
|
||||
optimizeRemoveNops() // must be done as the last step
|
||||
@@ -51,6 +77,57 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
optimizeRemoveNops() // once more
|
||||
}
|
||||
|
||||
private fun optimizeConditionalBranches() {
|
||||
// conditional branches that consume the value on the stack
|
||||
// sometimes these are just constant values, so we can statically determine the branch
|
||||
// or, they are preceded by a NOT instruction so we can simply remove that and flip the branch condition
|
||||
val pushvalue = setOf(Opcode.PUSH_BYTE, Opcode.PUSH_WORD)
|
||||
val notvalue = setOf(Opcode.NOT_BYTE, Opcode.NOT_WORD)
|
||||
val branchOpcodes = setOf(Opcode.JZ, Opcode.JNZ, Opcode.JZW, Opcode.JNZW)
|
||||
for(blk in blocks) {
|
||||
val instructionsToReplace = mutableMapOf<Int, Instruction>()
|
||||
blk.instructions.asSequence().withIndex().filter {it.value.opcode!=Opcode.LINE}.windowed(2).toList().forEach {
|
||||
if (it[1].value.opcode in branchOpcodes) {
|
||||
if (it[0].value.opcode in pushvalue) {
|
||||
val value = it[0].value.arg!!.asBooleanValue
|
||||
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
||||
val replacement: Instruction =
|
||||
if (value) {
|
||||
when (it[1].value.opcode) {
|
||||
Opcode.JNZ -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel)
|
||||
Opcode.JNZW -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel)
|
||||
else -> Instruction(Opcode.NOP)
|
||||
}
|
||||
} else {
|
||||
when (it[1].value.opcode) {
|
||||
Opcode.JZ -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel)
|
||||
Opcode.JZW -> Instruction(Opcode.JUMP, callLabel = it[1].value.callLabel)
|
||||
else -> Instruction(Opcode.NOP)
|
||||
}
|
||||
}
|
||||
instructionsToReplace[it[1].index] = replacement
|
||||
}
|
||||
else if (it[0].value.opcode in notvalue) {
|
||||
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
||||
val replacement: Instruction =
|
||||
when (it[1].value.opcode) {
|
||||
Opcode.JZ -> Instruction(Opcode.JNZ, callLabel = it[1].value.callLabel)
|
||||
Opcode.JZW -> Instruction(Opcode.JNZW, callLabel = it[1].value.callLabel)
|
||||
Opcode.JNZ -> Instruction(Opcode.JZ, callLabel = it[1].value.callLabel)
|
||||
Opcode.JNZW -> Instruction(Opcode.JZW, callLabel = it[1].value.callLabel)
|
||||
else -> Instruction(Opcode.NOP)
|
||||
}
|
||||
instructionsToReplace[it[1].index] = replacement
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (rins in instructionsToReplace) {
|
||||
blk.instructions[rins.key] = rins.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun optimizeRemoveNops() {
|
||||
// remove nops (that are not a label)
|
||||
for (blk in blocks)
|
||||
@@ -312,22 +389,24 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
when(decl.type) {
|
||||
VarDeclType.VAR -> {
|
||||
val value = when(decl.datatype) {
|
||||
DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT -> Value(decl.datatype, (decl.value as LiteralValue).asNumericValue!!)
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
||||
in NumericDatatypes -> Value(decl.datatype, (decl.value as LiteralValue).asNumericValue!!)
|
||||
in StringDatatypes -> {
|
||||
val litval = (decl.value as LiteralValue)
|
||||
if(litval.heapId==null)
|
||||
throw CompilerException("string should already be in the heap")
|
||||
Value(decl.datatype, litval.heapId)
|
||||
}
|
||||
DataType.ARRAY_B, DataType.ARRAY_W,
|
||||
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F -> {
|
||||
in ArrayDatatypes -> {
|
||||
val litval = (decl.value as LiteralValue)
|
||||
if(litval.heapId==null)
|
||||
throw CompilerException("array should already be in the heap")
|
||||
Value(decl.datatype, litval.heapId)
|
||||
}
|
||||
else -> throw CompilerException("weird datatype")
|
||||
}
|
||||
currentBlock.variables[scopedname] = value
|
||||
if(decl.zeropage)
|
||||
currentBlock.variablesMarkedForZeropage.add(scopedname)
|
||||
}
|
||||
VarDeclType.MEMORY -> {
|
||||
// note that constants are all folded away, but assembly code may still refer to them
|
||||
@@ -383,11 +462,11 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
heap.allEntries().forEach {
|
||||
when {
|
||||
it.value.str!=null ->
|
||||
out.println("${it.key} ${it.value.type.toString().toLowerCase()} \"${escape(it.value.str!!)}\"")
|
||||
out.println("${it.key} ${it.value.type.name.toLowerCase()} \"${escape(it.value.str!!)}\"")
|
||||
it.value.array!=null ->
|
||||
out.println("${it.key} ${it.value.type.toString().toLowerCase()} ${it.value.array!!.toList()}")
|
||||
out.println("${it.key} ${it.value.type.name.toLowerCase()} ${it.value.array!!.toList()}")
|
||||
it.value.doubleArray!=null ->
|
||||
out.println("${it.key} ${it.value.type.toString().toLowerCase()} ${it.value.doubleArray!!.toList()}")
|
||||
out.println("${it.key} ${it.value.type.name.toLowerCase()} ${it.value.doubleArray!!.toList()}")
|
||||
else -> throw CompilerException("invalid heap entry $it")
|
||||
}
|
||||
}
|
||||
@@ -398,12 +477,12 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
out.println("%variables")
|
||||
for(variable in blk.variables) {
|
||||
val valuestr = variable.value.toString()
|
||||
out.println("${variable.key} ${variable.value.type.toString().toLowerCase()} $valuestr")
|
||||
out.println("${variable.key} ${variable.value.type.name.toLowerCase()} $valuestr")
|
||||
}
|
||||
out.println("%end_variables")
|
||||
out.println("%memorypointers")
|
||||
for(iconst in blk.memoryPointers) {
|
||||
out.println("${iconst.key} ${iconst.value.second.toString().toLowerCase()} uw:${iconst.value.first.toString(16)}")
|
||||
out.println("${iconst.key} ${iconst.value.second.name.toLowerCase()} uw:${iconst.value.first.toString(16)}")
|
||||
}
|
||||
out.println("%end_memorypointers")
|
||||
out.println("%instructions")
|
||||
|
||||
@@ -79,8 +79,8 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
|
||||
|
||||
fun numericValue(): Number {
|
||||
return when(type) {
|
||||
DataType.UBYTE, DataType.BYTE -> byteval!!
|
||||
DataType.UWORD, DataType.WORD -> wordval!!
|
||||
in ByteDatatypes -> byteval!!
|
||||
in WordDatatypes -> wordval!!
|
||||
DataType.FLOAT -> floatval!!
|
||||
else -> throw ValueException("invalid datatype for numeric value: $type")
|
||||
}
|
||||
@@ -88,8 +88,8 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
|
||||
|
||||
fun integerValue(): Int {
|
||||
return when(type) {
|
||||
DataType.UBYTE, DataType.BYTE -> byteval!!.toInt()
|
||||
DataType.UWORD, DataType.WORD -> wordval!!
|
||||
in ByteDatatypes -> byteval!!.toInt()
|
||||
in WordDatatypes -> wordval!!
|
||||
DataType.FLOAT -> throw ValueException("float to integer loss of precision")
|
||||
else -> throw ValueException("invalid datatype for integer value: $type")
|
||||
}
|
||||
@@ -393,8 +393,8 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
|
||||
|
||||
fun msb(): Value {
|
||||
return when(type) {
|
||||
DataType.UBYTE, DataType.BYTE -> Value(DataType.UBYTE, 0)
|
||||
DataType.UWORD, DataType.WORD -> Value(DataType.UBYTE, wordval!! ushr 8 and 255)
|
||||
in ByteDatatypes -> Value(DataType.UBYTE, 0)
|
||||
in WordDatatypes -> Value(DataType.UBYTE, wordval!! ushr 8 and 255)
|
||||
else -> throw ValueException("msb can only work on (u)byte/(u)word")
|
||||
}
|
||||
}
|
||||
@@ -428,7 +428,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
when (targetType) {
|
||||
DataType.BYTE, DataType.UBYTE -> Value(DataType.UBYTE, integerValue() and 255)
|
||||
in ByteDatatypes -> Value(DataType.UBYTE, integerValue() and 255)
|
||||
DataType.UWORD -> this
|
||||
DataType.WORD -> {
|
||||
if(integerValue()<=32767)
|
||||
@@ -442,7 +442,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
|
||||
}
|
||||
DataType.WORD -> {
|
||||
when (targetType) {
|
||||
DataType.BYTE, DataType.UBYTE -> Value(DataType.UBYTE, integerValue() and 255)
|
||||
in ByteDatatypes -> Value(DataType.UBYTE, integerValue() and 255)
|
||||
DataType.UWORD -> Value(DataType.UWORD, integerValue() and 65535)
|
||||
DataType.WORD -> this
|
||||
DataType.FLOAT -> Value(DataType.FLOAT, numericValue())
|
||||
|
||||
@@ -3,8 +3,7 @@ package prog8.compiler.target.c64
|
||||
// note: to put stuff on the stack, we use Absolute,X addressing mode which is 3 bytes / 4 cycles
|
||||
// possible space optimization is to use zeropage (indirect),Y which is 2 bytes, but 5 cycles
|
||||
|
||||
import prog8.ast.DataType
|
||||
import prog8.ast.escape
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.*
|
||||
import prog8.compiler.intermediate.*
|
||||
import prog8.stackvm.Syscall
|
||||
@@ -17,7 +16,7 @@ import kotlin.math.abs
|
||||
class AssemblyError(msg: String) : RuntimeException(msg)
|
||||
|
||||
|
||||
class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, val heap: HeapValues) {
|
||||
class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, val heap: HeapValues, val zeropage: Zeropage) {
|
||||
private val globalFloatConsts = mutableMapOf<Double, String>()
|
||||
private val assemblyLines = mutableListOf<String>()
|
||||
private lateinit var block: IntermediateProgram.ProgramBlock
|
||||
@@ -27,9 +26,11 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
// Because 64tass understands scoped names via .proc / .block,
|
||||
// we'll strip the block prefix from all scoped names in the program.
|
||||
// Also, convert invalid label names (such as "<<<anonymous-1>>>") to something that's allowed.
|
||||
// Also have to do that for the variablesMarkedForZeropage!
|
||||
val newblocks = mutableListOf<IntermediateProgram.ProgramBlock>()
|
||||
for(block in program.blocks) {
|
||||
val newvars = block.variables.map { symname(it.key, block) to it.value }.toMap().toMutableMap()
|
||||
val newvarsZeropaged = block.variablesMarkedForZeropage.map{symname(it, block)}.toMutableSet()
|
||||
val newlabels = block.labels.map { symname(it.key, block) to it.value}.toMap().toMutableMap()
|
||||
val newinstructions = block.instructions.asSequence().map {
|
||||
when {
|
||||
@@ -42,7 +43,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
}
|
||||
}.toMutableList()
|
||||
val newConstants = block.memoryPointers.map { symname(it.key, block) to it.value }.toMap().toMutableMap()
|
||||
newblocks.add(IntermediateProgram.ProgramBlock(
|
||||
val newblock = IntermediateProgram.ProgramBlock(
|
||||
block.scopedname,
|
||||
block.shortname,
|
||||
block.address,
|
||||
@@ -50,11 +51,18 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
newvars,
|
||||
newConstants,
|
||||
newlabels,
|
||||
force_output = block.force_output))
|
||||
force_output = block.force_output)
|
||||
newblock.variablesMarkedForZeropage.clear()
|
||||
newblock.variablesMarkedForZeropage.addAll(newvarsZeropaged)
|
||||
newblocks.add(newblock)
|
||||
}
|
||||
program.blocks.clear()
|
||||
program.blocks.addAll(newblocks)
|
||||
|
||||
val newAllocatedZp = program.allocatedZeropageVariables.map { symname(it.key, null) to it.value}
|
||||
program.allocatedZeropageVariables.clear()
|
||||
program.allocatedZeropageVariables.putAll(newAllocatedZp)
|
||||
|
||||
// make a list of all const floats that are used
|
||||
for(block in program.blocks) {
|
||||
for(ins in block.instructions.filter{it.arg?.type==DataType.FLOAT}) {
|
||||
@@ -65,17 +73,19 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
}
|
||||
}
|
||||
|
||||
fun compileToAssembly(): AssemblyProgram {
|
||||
println("\nGenerating assembly code from intermediate code... ")
|
||||
fun compileToAssembly(optimize: Boolean): AssemblyProgram {
|
||||
println("Generating assembly code from intermediate code... ")
|
||||
|
||||
assemblyLines.clear()
|
||||
header()
|
||||
for(b in program.blocks)
|
||||
block2asm(b)
|
||||
|
||||
var optimizationsDone=1
|
||||
while(optimizationsDone>0) {
|
||||
optimizationsDone = optimizeAssembly(assemblyLines)
|
||||
if(optimize) {
|
||||
var optimizationsDone = 1
|
||||
while (optimizationsDone > 0) {
|
||||
optimizationsDone = optimizeAssembly(assemblyLines)
|
||||
}
|
||||
}
|
||||
|
||||
File("${program.name}.asm").printWriter().use {
|
||||
@@ -96,20 +106,27 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
}
|
||||
|
||||
|
||||
private fun symname(scoped: String, block: IntermediateProgram.ProgramBlock): String {
|
||||
private fun symname(scoped: String, block: IntermediateProgram.ProgramBlock?): String {
|
||||
if(' ' in scoped)
|
||||
return scoped
|
||||
|
||||
val blockLocal: Boolean
|
||||
var name = if (scoped.startsWith("${block.shortname}.")) {
|
||||
blockLocal = true
|
||||
scoped.substring(block.shortname.length+1)
|
||||
} else if (scoped.startsWith("block.")) {
|
||||
blockLocal = false
|
||||
scoped
|
||||
} else {
|
||||
blockLocal = false
|
||||
scoped
|
||||
var name = when {
|
||||
block==null -> {
|
||||
blockLocal=true
|
||||
scoped
|
||||
}
|
||||
scoped.startsWith("${block.shortname}.") -> {
|
||||
blockLocal = true
|
||||
scoped.substring(block.shortname.length+1)
|
||||
}
|
||||
scoped.startsWith("block.") -> {
|
||||
blockLocal = false
|
||||
scoped
|
||||
}
|
||||
else -> {
|
||||
blockLocal = false
|
||||
scoped
|
||||
}
|
||||
}
|
||||
name = name.replace("<<<", "prog8_").replace(">>>", "")
|
||||
if(name=="-")
|
||||
@@ -168,9 +185,32 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
}
|
||||
}
|
||||
|
||||
if(zeropage.exitProgramStrategy!=Zeropage.ExitProgramStrategy.CLEAN_EXIT) {
|
||||
// disable shift-commodore charset switching and run/stop key
|
||||
out(" lda #$80")
|
||||
out(" lda #$80")
|
||||
out(" sta 657\t; disable charset switching")
|
||||
out(" lda #239")
|
||||
out(" sta 808\t; disable run/stop key")
|
||||
}
|
||||
|
||||
out(" ldx #\$ff\t; init estack pointer")
|
||||
out(" ; initialize the variables in each block")
|
||||
for(block in program.blocks) {
|
||||
val initVarsLabel = block.instructions.firstOrNull { it is LabelInstr && it.name==initvarsSubName } as? LabelInstr
|
||||
if(initVarsLabel!=null)
|
||||
out(" jsr ${block.scopedname}.${initVarsLabel.name}")
|
||||
}
|
||||
out(" clc")
|
||||
out(" jmp main.start\t; jump to program entrypoint")
|
||||
when(zeropage.exitProgramStrategy) {
|
||||
Zeropage.ExitProgramStrategy.CLEAN_EXIT -> {
|
||||
out(" jmp main.start\t; jump to program entrypoint")
|
||||
}
|
||||
Zeropage.ExitProgramStrategy.SYSTEM_RESET -> {
|
||||
out(" jsr main.start\t; call program entrypoint")
|
||||
out(" jmp (c64.RESET_VEC)\t; cold reset")
|
||||
}
|
||||
}
|
||||
out("")
|
||||
|
||||
// the global list of all floating point constants for the whole program
|
||||
@@ -189,9 +229,34 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
out(".cerror * > ${block.address?.toHex()}, 'block address overlaps by ', *-${block.address?.toHex()},' bytes'")
|
||||
out("* = ${block.address?.toHex()}")
|
||||
}
|
||||
|
||||
// deal with zeropage variables
|
||||
for(variable in blk.variables) {
|
||||
val sym = symname(blk.scopedname+"."+variable.key, null)
|
||||
val zpVar = program.allocatedZeropageVariables[sym]
|
||||
if(zpVar==null) {
|
||||
// This var is not on the ZP yet. Attempt to move it there (if it's not a float, those take up too much space)
|
||||
if(variable.value.type in zeropage.allowedDatatypes && variable.value.type != DataType.FLOAT) {
|
||||
try {
|
||||
val address = zeropage.allocate(sym, variable.value.type, null)
|
||||
out("${variable.key} = $address\t; zp ${variable.value.type}")
|
||||
// make sure we add the var to the set of zpvars for this block
|
||||
blk.variablesMarkedForZeropage.add(variable.key)
|
||||
program.allocatedZeropageVariables[sym] = Pair(address, variable.value.type)
|
||||
} catch (x: ZeropageDepletedError) {
|
||||
// leave it as it is.
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// it was already allocated on the zp
|
||||
out("${variable.key} = ${zpVar.first}\t; zp ${zpVar.second}")
|
||||
}
|
||||
}
|
||||
|
||||
out("\n; memdefs and kernel subroutines")
|
||||
memdefs2asm(block)
|
||||
out("\n; variables")
|
||||
out("\n; non-zeropage variables")
|
||||
vardecls2asm(block)
|
||||
out("")
|
||||
|
||||
@@ -219,7 +284,8 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
}
|
||||
|
||||
private fun vardecls2asm(block: IntermediateProgram.ProgramBlock) {
|
||||
val sortedVars = block.variables.toList().sortedBy { it.second.type }
|
||||
// these are the non-zeropage variables
|
||||
val sortedVars = block.variables.filter{it.key !in block.variablesMarkedForZeropage}.toList().sortedBy { it.second.type }
|
||||
for (v in sortedVars) {
|
||||
when (v.second.type) {
|
||||
DataType.UBYTE -> out("${v.first}\t.byte 0")
|
||||
@@ -227,10 +293,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
DataType.UWORD -> out("${v.first}\t.word 0")
|
||||
DataType.WORD -> out("${v.first}\t.sint 0")
|
||||
DataType.FLOAT -> out("${v.first}\t.byte 0,0,0,0,0 ; float")
|
||||
DataType.STR,
|
||||
DataType.STR_P,
|
||||
DataType.STR_S,
|
||||
DataType.STR_PS -> {
|
||||
DataType.STR, DataType.STR_S -> {
|
||||
val rawStr = heap.get(v.second.heapId).str!!
|
||||
val bytes = encodeStr(rawStr, v.second.type).map { "$" + it.toString(16).padStart(2, '0') }
|
||||
out("${v.first}\t; ${v.second.type} \"${escape(rawStr)}\"")
|
||||
@@ -294,24 +357,14 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
}
|
||||
|
||||
private fun encodeStr(str: String, dt: DataType): List<Short> {
|
||||
when(dt) {
|
||||
return when(dt) {
|
||||
DataType.STR -> {
|
||||
val bytes = Petscii.encodePetscii(str, true)
|
||||
return bytes.plus(0)
|
||||
}
|
||||
DataType.STR_P -> {
|
||||
val result = listOf(str.length.toShort())
|
||||
val bytes = Petscii.encodePetscii(str, true)
|
||||
return result.plus(bytes)
|
||||
bytes.plus(0)
|
||||
}
|
||||
DataType.STR_S -> {
|
||||
val bytes = Petscii.encodeScreencode(str, true)
|
||||
return bytes.plus(0)
|
||||
}
|
||||
DataType.STR_PS -> {
|
||||
val result = listOf(str.length.toShort())
|
||||
val bytes = Petscii.encodeScreencode(str, true)
|
||||
return result.plus(bytes)
|
||||
bytes.plus(0)
|
||||
}
|
||||
else -> throw AssemblyError("invalid str type")
|
||||
}
|
||||
@@ -448,8 +501,9 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
Syscall.FUNC_MAX_F,
|
||||
Syscall.FUNC_MIN_F,
|
||||
Syscall.FUNC_AVG_F,
|
||||
Syscall.FUNC_SUM_F -> " jsr c64flt.${call.toString().toLowerCase()}"
|
||||
else -> " jsr prog8_lib.${call.toString().toLowerCase()}"
|
||||
Syscall.FUNC_SUM_F -> " jsr c64flt.${call.name.toLowerCase()}"
|
||||
null -> ""
|
||||
else -> " jsr prog8_lib.${call.name.toLowerCase()}"
|
||||
}
|
||||
}
|
||||
Opcode.BREAKPOINT -> {
|
||||
@@ -820,30 +874,20 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
Opcode.IDIV_W -> " jsr prog8_lib.idiv_w"
|
||||
Opcode.IDIV_UW -> " jsr prog8_lib.idiv_uw"
|
||||
|
||||
Opcode.AND_BYTE -> {
|
||||
"""
|
||||
lda ${(ESTACK_LO + 2).toHex()},x
|
||||
and ${(ESTACK_LO + 1).toHex()},x
|
||||
inx
|
||||
sta ${(ESTACK_LO + 1).toHex()},x
|
||||
"""
|
||||
}
|
||||
Opcode.OR_BYTE -> {
|
||||
"""
|
||||
lda ${(ESTACK_LO + 2).toHex()},x
|
||||
ora ${(ESTACK_LO + 1).toHex()},x
|
||||
inx
|
||||
sta ${(ESTACK_LO + 1).toHex()},x
|
||||
"""
|
||||
}
|
||||
Opcode.XOR_BYTE -> {
|
||||
"""
|
||||
lda ${(ESTACK_LO + 2).toHex()},x
|
||||
eor ${(ESTACK_LO + 1).toHex()},x
|
||||
inx
|
||||
sta ${(ESTACK_LO + 1).toHex()},x
|
||||
"""
|
||||
}
|
||||
Opcode.AND_BYTE -> " jsr prog8_lib.and_b"
|
||||
Opcode.OR_BYTE -> " jsr prog8_lib.or_b"
|
||||
Opcode.XOR_BYTE -> " jsr prog8_lib.xor_b"
|
||||
Opcode.AND_WORD -> " jsr prog8_lib.and_w"
|
||||
Opcode.OR_WORD -> " jsr prog8_lib.or_w"
|
||||
Opcode.XOR_WORD -> " jsr prog8_lib.xor_w"
|
||||
|
||||
Opcode.BITAND_BYTE -> " jsr prog8_lib.bitand_b"
|
||||
Opcode.BITOR_BYTE -> " jsr prog8_lib.bitor_b"
|
||||
Opcode.BITXOR_BYTE -> " jsr prog8_lib.bitxor_b"
|
||||
Opcode.BITAND_WORD -> " jsr prog8_lib.bitand_w"
|
||||
Opcode.BITOR_WORD -> " jsr prog8_lib.bitor_w"
|
||||
Opcode.BITXOR_WORD -> " jsr prog8_lib.bitxor_w"
|
||||
|
||||
Opcode.REMAINDER_UB -> " jsr prog8_lib.remainder_ub"
|
||||
Opcode.REMAINDER_UW -> " jsr prog8_lib.remainder_uw"
|
||||
|
||||
@@ -3260,44 +3304,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
null
|
||||
},
|
||||
|
||||
// 16 bit addition avoiding excessive stack usage
|
||||
// @todo optimize 8 and 16 bit adds and subs even more with longer asmpatterns (avoid stack use altogether on most common operations)
|
||||
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.ADD_UW),
|
||||
listOf(Opcode.PUSH_VAR_WORD, Opcode.ADD_W)) { segment ->
|
||||
"""
|
||||
clc
|
||||
lda ${segment[0].callLabel}
|
||||
adc ${(ESTACK_LO+1).toHex()},x
|
||||
sta ${(ESTACK_LO+1).toHex()},x
|
||||
lda ${segment[0].callLabel}+1
|
||||
adc ${(ESTACK_HI+1).toHex()},x
|
||||
sta ${(ESTACK_HI+1).toHex()},x
|
||||
"""
|
||||
},
|
||||
AsmPattern(listOf(Opcode.PUSH_MEM_UW, Opcode.ADD_UW),
|
||||
listOf(Opcode.PUSH_MEM_W, Opcode.ADD_W)) { segment ->
|
||||
"""
|
||||
clc
|
||||
lda ${hexVal(segment[0])}
|
||||
adc ${(ESTACK_LO + 1).toHex()},x
|
||||
sta ${(ESTACK_LO + 1).toHex()},x
|
||||
lda ${hexValPlusOne(segment[0])}
|
||||
adc ${(ESTACK_HI + 1).toHex()},x
|
||||
sta ${(ESTACK_HI + 1).toHex()},x
|
||||
"""
|
||||
},
|
||||
AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.ADD_UW),
|
||||
listOf(Opcode.PUSH_WORD, Opcode.ADD_W)) { segment ->
|
||||
"""
|
||||
clc
|
||||
lda #<${hexVal(segment[0])}
|
||||
adc ${(ESTACK_LO+1).toHex()},x
|
||||
sta ${(ESTACK_LO+1).toHex()},x
|
||||
lda #>${hexVal(segment[0])}
|
||||
adc ${(ESTACK_HI+1).toHex()},x
|
||||
sta ${(ESTACK_HI+1).toHex()},x
|
||||
"""
|
||||
},
|
||||
// @todo optimize 8 and 16 bit adds and subs (avoid stack use altogether on most common operations)
|
||||
|
||||
AsmPattern(listOf(Opcode.PUSH_VAR_BYTE, Opcode.CMP_B), listOf(Opcode.PUSH_VAR_BYTE, Opcode.CMP_UB)) { segment ->
|
||||
// this pattern is encountered as part of the loop bound condition in for loops (var + cmp + jz/jnz)
|
||||
|
||||
@@ -25,35 +25,60 @@ const val ESTACK_HI = 0xcf00 // $cf00-$cfff inclusive
|
||||
|
||||
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
|
||||
// @todo: actually USE the zero page when allocating variables at code generation time
|
||||
// (ideally, the variables that are used 'most' / inside long loops are allocated in ZP first)
|
||||
|
||||
companion object {
|
||||
const val SCRATCH_B1 = 0x02
|
||||
const val SCRATCH_REG = 0x03 // temp storage for a register
|
||||
const val SCRATCH_REG_X = 0xfa // temp storage for register X (the evaluation stack pointer)
|
||||
const val SCRATCH_W1 = 0xfb // $fb/$fc
|
||||
const val SCRATCH_W2 = 0xfd // $fd/$fe
|
||||
const val SCRATCH_W1 = 0xfb // $fb+$fc
|
||||
const val SCRATCH_W2 = 0xfd // $fd+$fe
|
||||
}
|
||||
|
||||
override val exitProgramStrategy: ExitProgramStrategy = when(options.zeropage) {
|
||||
ZeropageType.BASICSAFE -> ExitProgramStrategy.CLEAN_EXIT
|
||||
ZeropageType.FLOATSAFE, ZeropageType.KERNALSAFE, ZeropageType.FULL -> ExitProgramStrategy.SYSTEM_RESET
|
||||
}
|
||||
|
||||
|
||||
init {
|
||||
if(options.zeropage== ZeropageType.FULL) {
|
||||
if(options.floats && options.zeropage!=ZeropageType.FLOATSAFE && options.zeropage!=ZeropageType.BASICSAFE)
|
||||
throw CompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe'")
|
||||
|
||||
if(options.zeropage == ZeropageType.FULL) {
|
||||
free.addAll(0x04 .. 0xf9)
|
||||
free.add(0xff)
|
||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_REG_X, SCRATCH_W1, SCRATCH_W1+1, SCRATCH_W2, SCRATCH_W2+1))
|
||||
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
||||
} else {
|
||||
if(options.zeropage== ZeropageType.KERNALSAFE) {
|
||||
// add the Zp addresses that are just used by BASIC routines to the free list
|
||||
free.addAll(listOf(0x09, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
||||
if(options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
free.addAll(listOf(0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
||||
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
|
||||
0x22, 0x23, 0x24, 0x25,
|
||||
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
||||
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53, 0x6f, 0x70))
|
||||
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
|
||||
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
|
||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||
// 0x90-0xfa is 'kernel work storage area'
|
||||
))
|
||||
}
|
||||
// add the other free Zp addresses
|
||||
// these are valid for the C-64 (when no RS232 I/O is performed):
|
||||
free.addAll(listOf(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0d, 0x0e,
|
||||
|
||||
if(options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
// remove the zero page locations used for floating point operations from the free list
|
||||
free.removeAll(listOf(
|
||||
0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
|
||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xf
|
||||
))
|
||||
}
|
||||
|
||||
// add the other free Zp addresses,
|
||||
// these are valid for the C-64 (when no RS232 I/O is performed) but to keep BASIC running fully:
|
||||
free.addAll(listOf(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0d, 0x0e,
|
||||
0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa,
|
||||
0xb5, 0xb6, 0xf7, 0xf8, 0xf9))
|
||||
}
|
||||
|
||||
@@ -26,10 +26,11 @@ val BuiltinFunctions = mapOf(
|
||||
"lsl" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
|
||||
"lsr" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
|
||||
// these few have a return value depending on the argument(s):
|
||||
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.max()!! }}, // type depends on args
|
||||
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.min()!! }}, // type depends on args
|
||||
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.sum() }}, // type depends on args
|
||||
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.max()!! }}, // type depends on args
|
||||
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.min()!! }}, // type depends on args
|
||||
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.sum() }}, // type depends on args
|
||||
"abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
||||
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
||||
// normal functions follow:
|
||||
"sin" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::sin) },
|
||||
"sin8" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
|
||||
@@ -45,6 +46,7 @@ val BuiltinFunctions = mapOf(
|
||||
"atan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::atan) },
|
||||
"ln" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::log) },
|
||||
"log2" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, ::log2) },
|
||||
// TODO: sqrt() should have integer versions too
|
||||
"sqrt" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::sqrt) },
|
||||
"rad" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::toRadians) },
|
||||
"deg" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::toDegrees) },
|
||||
@@ -52,7 +54,6 @@ val BuiltinFunctions = mapOf(
|
||||
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::round) },
|
||||
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::floor) },
|
||||
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::ceil) },
|
||||
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), DataType.UBYTE, ::builtinLen),
|
||||
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.any { v -> v != 0.0} }},
|
||||
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.all { v -> v != 0.0} }},
|
||||
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { x: Int -> x and 255 }},
|
||||
@@ -69,15 +70,16 @@ val BuiltinFunctions = mapOf(
|
||||
"clear_carry" to FunctionSignature(false, emptyList(), null),
|
||||
"set_irqd" to FunctionSignature(false, emptyList(), null),
|
||||
"clear_irqd" to FunctionSignature(false, emptyList(), null),
|
||||
"read_flags" to FunctionSignature(false, emptyList(), DataType.UBYTE),
|
||||
"swap" to FunctionSignature(false, listOf(BuiltinFunctionParam("first", NumericDatatypes), BuiltinFunctionParam("second", NumericDatatypes)), null),
|
||||
"memcopy" to FunctionSignature(false, listOf(
|
||||
BuiltinFunctionParam("from", IterableDatatypes + setOf(DataType.UWORD)),
|
||||
BuiltinFunctionParam("to", IterableDatatypes + setOf(DataType.UWORD)),
|
||||
BuiltinFunctionParam("numbytes", IntegerDatatypes)), null),
|
||||
BuiltinFunctionParam("numbytes", setOf(DataType.UBYTE))), null),
|
||||
"memset" to FunctionSignature(false, listOf(
|
||||
BuiltinFunctionParam("address", IterableDatatypes + setOf(DataType.UWORD)),
|
||||
BuiltinFunctionParam("numbytes", setOf(DataType.UWORD)),
|
||||
BuiltinFunctionParam("bytevalue", setOf(DataType.UBYTE, DataType.BYTE))), null),
|
||||
BuiltinFunctionParam("bytevalue", ByteDatatypes)), null),
|
||||
"memsetw" to FunctionSignature(false, listOf(
|
||||
BuiltinFunctionParam("address", IterableDatatypes + setOf(DataType.UWORD)),
|
||||
BuiltinFunctionParam("numwords", setOf(DataType.UWORD)),
|
||||
@@ -110,7 +112,7 @@ val BuiltinFunctions = mapOf(
|
||||
|
||||
fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespace: INameScope, heap: HeapValues): DataType? {
|
||||
|
||||
fun datatypeFromListArg(arglist: IExpression): DataType {
|
||||
fun datatypeFromIterableArg(arglist: IExpression): DataType {
|
||||
if(arglist is LiteralValue) {
|
||||
if(arglist.type==DataType.ARRAY_UB || arglist.type==DataType.ARRAY_UW || arglist.type==DataType.ARRAY_F) {
|
||||
val dt = arglist.arrayvalue!!.map {it.resultingDatatype(namespace, heap)}
|
||||
@@ -125,39 +127,49 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
|
||||
if(arglist is IdentifierReference) {
|
||||
val dt = arglist.resultingDatatype(namespace, heap)
|
||||
return when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT,
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> dt
|
||||
in NumericDatatypes -> dt!!
|
||||
in StringDatatypes -> dt!!
|
||||
DataType.ARRAY_UB -> DataType.UBYTE
|
||||
DataType.ARRAY_B -> DataType.BYTE
|
||||
DataType.ARRAY_UW -> DataType.UWORD
|
||||
DataType.ARRAY_W -> DataType.WORD
|
||||
DataType.ARRAY_F -> DataType.FLOAT
|
||||
null -> throw FatalAstException("function requires one argument which is an arrayspec $function")
|
||||
else -> throw FatalAstException("function '$function' requires one argument which is an iterable")
|
||||
}
|
||||
}
|
||||
throw FatalAstException("function requires one argument which is an arrayspec $function")
|
||||
throw FatalAstException("function '$function' requires one argument which is an iterable")
|
||||
}
|
||||
|
||||
val func = BuiltinFunctions[function]!!
|
||||
val func = BuiltinFunctions.getValue(function)
|
||||
if(func.returntype!=null)
|
||||
return func.returntype
|
||||
// function has return values, but the return type depends on the arguments
|
||||
|
||||
return when (function) {
|
||||
"max", "min", "abs" -> {
|
||||
val dt = datatypeFromListArg(args.single())
|
||||
"abs" -> {
|
||||
val dt = args.single().resultingDatatype(namespace, heap)
|
||||
when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT -> dt
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.UBYTE
|
||||
in ByteDatatypes -> DataType.UBYTE
|
||||
in WordDatatypes -> DataType.UWORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("weird datatype passed to abs $dt")
|
||||
}
|
||||
}
|
||||
"max", "min" -> {
|
||||
val dt = datatypeFromIterableArg(args.single())
|
||||
when(dt) {
|
||||
in NumericDatatypes -> dt
|
||||
in StringDatatypes -> DataType.UBYTE
|
||||
DataType.ARRAY_UB -> DataType.UBYTE
|
||||
DataType.ARRAY_B -> DataType.BYTE
|
||||
DataType.ARRAY_UW -> DataType.UWORD
|
||||
DataType.ARRAY_W -> DataType.WORD
|
||||
DataType.ARRAY_F -> DataType.FLOAT
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
"sum" -> {
|
||||
val dt=datatypeFromListArg(args.single())
|
||||
val dt=datatypeFromIterableArg(args.single())
|
||||
when(dt) {
|
||||
DataType.UBYTE, DataType.UWORD -> DataType.UWORD
|
||||
DataType.BYTE, DataType.WORD -> DataType.WORD
|
||||
@@ -165,9 +177,15 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
|
||||
DataType.ARRAY_UB, DataType.ARRAY_UW -> DataType.UWORD
|
||||
DataType.ARRAY_B, DataType.ARRAY_W -> DataType.WORD
|
||||
DataType.ARRAY_F -> DataType.FLOAT
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.UWORD
|
||||
in StringDatatypes -> DataType.UWORD
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
"len" -> {
|
||||
// a length can be >255 so in that case, the result is an UWORD instead of an UBYTE
|
||||
// but to avoid a lot of code duplication we simply assume UWORD in all cases for now
|
||||
return DataType.UWORD
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
@@ -285,6 +303,7 @@ private fun builtinAvg(args: List<IExpression>, position: Position, namespace:IN
|
||||
}
|
||||
|
||||
private fun builtinLen(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("len requires one argument", position)
|
||||
var argument = args[0].constValue(namespace, heap)
|
||||
@@ -299,25 +318,24 @@ private fun builtinLen(args: List<IExpression>, position: Position, namespace:IN
|
||||
return when(argument.type) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).arraysize
|
||||
if(arraySize>255)
|
||||
if(arraySize>256)
|
||||
throw CompilerException("array length exceeds byte limit ${argument.position}")
|
||||
LiteralValue(DataType.UBYTE, bytevalue=arraySize.toShort(), position=args[0].position)
|
||||
LiteralValue.optimalInteger(arraySize, args[0].position)
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).arraysize
|
||||
if(arraySize>255)
|
||||
if(arraySize>256)
|
||||
throw CompilerException("array length exceeds byte limit ${argument.position}")
|
||||
LiteralValue(DataType.UBYTE, bytevalue=arraySize.toShort(), position=args[0].position)
|
||||
LiteralValue.optimalInteger(arraySize, args[0].position)
|
||||
}
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
||||
in StringDatatypes -> {
|
||||
val str = argument.strvalue(heap)
|
||||
if(str.length>255)
|
||||
throw CompilerException("string length exceeds byte limit ${argument.position}")
|
||||
LiteralValue(DataType.UBYTE, bytevalue=str.length.toShort(), position=args[0].position)
|
||||
LiteralValue.optimalInteger(str.length, args[0].position)
|
||||
}
|
||||
DataType.UBYTE, DataType.BYTE,
|
||||
DataType.UWORD, DataType.WORD,
|
||||
DataType.FLOAT -> throw SyntaxError("len of weird argument ${args[0]}", position)
|
||||
in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
|
||||
else -> throw CompilerException("weird datatype")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
|
||||
|
||||
if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) {
|
||||
val litval = decl.value as? LiteralValue
|
||||
if(litval!=null && litval.isArray)
|
||||
if(litval!=null && litval.isArray && litval.heapId!=null)
|
||||
fixupArrayTypeOnHeap(decl, litval)
|
||||
when(decl.datatype) {
|
||||
DataType.FLOAT -> {
|
||||
@@ -97,9 +97,10 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
|
||||
private fun fixupArrayTypeOnHeap(decl: VarDecl, litval: LiteralValue) {
|
||||
// fix the type of the array value that's on the heap, to match the vardecl.
|
||||
// notice that checking the bounds of the actual values is not done here, but in the AstChecker later.
|
||||
|
||||
if(decl.datatype==litval.type)
|
||||
return // already correct datatype
|
||||
val heapId = litval.heapId!!
|
||||
val heapId = litval.heapId ?: throw FatalAstException("expected array to be on heap $litval")
|
||||
val array=heap.get(heapId)
|
||||
when(decl.datatype) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
@@ -141,6 +142,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
|
||||
override fun process(functionCall: FunctionCall): IExpression {
|
||||
return try {
|
||||
super.process(functionCall)
|
||||
typeCastConstArguments(functionCall)
|
||||
functionCall.constValue(namespace, heap) ?: functionCall
|
||||
} catch (ax: AstException) {
|
||||
addError(ax)
|
||||
@@ -148,6 +150,31 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
super.process(functionCallStatement)
|
||||
typeCastConstArguments(functionCallStatement)
|
||||
return functionCallStatement
|
||||
}
|
||||
|
||||
private fun typeCastConstArguments(functionCall: IFunctionCall) {
|
||||
val subroutine = functionCall.target.targetStatement(namespace) as? Subroutine
|
||||
if(subroutine!=null) {
|
||||
// if types differ, try to typecast constant arguments to the function call to the desired data type of the parameter
|
||||
for(arg in functionCall.arglist.withIndex().zip(subroutine.parameters)) {
|
||||
val expectedDt = arg.second.type
|
||||
val argConst = arg.first.value.constValue(namespace, heap)
|
||||
if(argConst!=null && argConst.type!=expectedDt) {
|
||||
val convertedValue = argConst.intoDatatype(expectedDt)
|
||||
if(convertedValue!=null) {
|
||||
functionCall.arglist[arg.first.index] = convertedValue
|
||||
optimizationsDone++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to process a unary prefix expression.
|
||||
* Compile-time constant sub expressions will be evaluated on the spot.
|
||||
@@ -507,7 +534,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
|
||||
if(literalValue.strvalue(heap).length !in 1..255)
|
||||
addError(ExpressionError("string literal length must be between 1 and 255", literalValue.position))
|
||||
else {
|
||||
val heapId = heap.add(literalValue.type, literalValue.strvalue(heap)) // TODO: we don't know the actual string type yet, STR != STR_P etc...
|
||||
val heapId = heap.add(literalValue.type, literalValue.strvalue(heap)) // TODO: we don't know the actual string type yet, STR != STR_S etc...
|
||||
val newValue = LiteralValue(literalValue.type, heapId = heapId, position = literalValue.position)
|
||||
return super.process(newValue)
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
|
||||
val leftDt = expr.left.resultingDatatype(namespace, heap)
|
||||
val rightDt = expr.right.resultingDatatype(namespace, heap)
|
||||
if (leftDt != null && rightDt != null && leftDt != rightDt) {
|
||||
// try to convert a datatype into the other
|
||||
// try to convert a datatype into the other (where ddd
|
||||
if (adjustDatatypes(expr, leftVal, leftDt, rightVal, rightDt)) {
|
||||
optimizationsDone++
|
||||
return expr
|
||||
@@ -347,26 +347,37 @@ class SimplifyExpressions(private val namespace: INameScope, private val heap: H
|
||||
}
|
||||
|
||||
if(leftConstVal==null && rightConstVal!=null) {
|
||||
val (adjusted, newValue) = adjust(rightConstVal, leftDt)
|
||||
if(adjusted) {
|
||||
expr.right = newValue
|
||||
optimizationsDone++
|
||||
return true
|
||||
if(isBiggerType(leftDt, rightDt)) {
|
||||
val (adjusted, newValue) = adjust(rightConstVal, leftDt)
|
||||
if (adjusted) {
|
||||
expr.right = newValue
|
||||
optimizationsDone++
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
} else if(leftConstVal!=null && rightConstVal==null) {
|
||||
val (adjusted, newValue) = adjust(leftConstVal, rightDt)
|
||||
if(adjusted) {
|
||||
expr.left = newValue
|
||||
optimizationsDone++
|
||||
return true
|
||||
if(isBiggerType(rightDt, leftDt)) {
|
||||
val (adjusted, newValue) = adjust(leftConstVal, rightDt)
|
||||
if (adjusted) {
|
||||
expr.left = newValue
|
||||
optimizationsDone++
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
return false
|
||||
return false // two const values, don't adjust (should have been const-folded away)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isBiggerType(type: DataType, other: DataType) =
|
||||
when(type) {
|
||||
in ByteDatatypes -> false
|
||||
in WordDatatypes -> other in ByteDatatypes
|
||||
else -> true
|
||||
}
|
||||
|
||||
|
||||
private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: LiteralValue?, val rightVal: LiteralValue?)
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ import kotlin.math.floor
|
||||
|
||||
|
||||
/*
|
||||
todo: subroutines with 1 or 2 byte args or 1 word arg can be converted to asm sub calling convention (args in registers)
|
||||
|
||||
|
||||
todo: implement usage counters for blocks, variables, subroutines, heap variables. Then:
|
||||
todo remove unused blocks
|
||||
todo remove unused variables
|
||||
@@ -44,13 +47,6 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
// remove empty subroutine
|
||||
optimizationsDone++
|
||||
statementsToRemove.add(subroutine)
|
||||
} else if(subroutine.statements.size==1) {
|
||||
val stmt = subroutine.statements[0]
|
||||
if(stmt is ReturnFromIrq || stmt is Return) {
|
||||
// remove empty subroutine
|
||||
optimizationsDone++
|
||||
statementsToRemove.add(subroutine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +55,18 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
linesToRemove.reversed().forEach{subroutine.statements.removeAt(it)}
|
||||
}
|
||||
|
||||
if(subroutine.canBeAsmSubroutine) {
|
||||
optimizationsDone++
|
||||
return subroutine.intoAsmSubroutine() // TODO this doesn't work yet due to parameter vardecl issue
|
||||
|
||||
// TODO fix parameter passing so this also works:
|
||||
// asmsub aa(byte arg @ Y) -> clobbers() -> () {
|
||||
// byte local = arg ; @todo fix 'undefined symbol arg' by some sort of alias name for the parameter
|
||||
// A=44
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
return subroutine
|
||||
}
|
||||
|
||||
@@ -87,16 +95,6 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
return linesToRemove
|
||||
}
|
||||
|
||||
private fun returnregisters(subroutine: Subroutine): List<RegisterOrStatusflag> {
|
||||
return when {
|
||||
subroutine.returntypes.isEmpty() -> listOf()
|
||||
subroutine.returntypes.size==1 && subroutine.returntypes[0] in setOf(DataType.BYTE, DataType.UBYTE) -> listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null))
|
||||
subroutine.returntypes.size==1 && subroutine.returntypes[0] in setOf(DataType.WORD, DataType.UWORD) -> listOf(RegisterOrStatusflag(RegisterOrPair.AY, null, null))
|
||||
subroutine.returntypes.size==2 && subroutine.returntypes.all { it in setOf(DataType.BYTE, DataType.UBYTE)} -> listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null), RegisterOrStatusflag(RegisterOrPair.Y, null, null))
|
||||
else -> throw FatalAstException("can't convert return values to registers")
|
||||
}
|
||||
}
|
||||
|
||||
private fun isNotMemory(target: AssignTarget): Boolean {
|
||||
if(target.register!=null)
|
||||
return true
|
||||
@@ -116,39 +114,39 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
}
|
||||
|
||||
|
||||
override fun process(functionCall: FunctionCallStatement): IStatement {
|
||||
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0] in BuiltinFunctions) {
|
||||
val functionName = functionCall.target.nameInSource[0]
|
||||
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
||||
val functionName = functionCallStatement.target.nameInSource[0]
|
||||
if (functionName in pureBuiltinFunctions) {
|
||||
printWarning("statement has no effect (function return value is discarded)", functionCall.position)
|
||||
statementsToRemove.add(functionCall)
|
||||
return functionCall
|
||||
printWarning("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
||||
statementsToRemove.add(functionCallStatement)
|
||||
return functionCallStatement
|
||||
}
|
||||
}
|
||||
|
||||
if(functionCall.target.nameInSource==listOf("c64scr", "print") ||
|
||||
functionCall.target.nameInSource==listOf("c64scr", "print_p")) {
|
||||
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
|
||||
functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
|
||||
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
||||
if(functionCall.arglist.single() is LiteralValue)
|
||||
if(functionCallStatement.arglist.single() is LiteralValue)
|
||||
throw AstException("string argument should be on heap already")
|
||||
val stringVar = functionCall.arglist.single() as? IdentifierReference
|
||||
val stringVar = functionCallStatement.arglist.single() as? IdentifierReference
|
||||
if(stringVar!=null) {
|
||||
val heapId = stringVar.heapId(namespace)
|
||||
val string = heap.get(heapId).str!!
|
||||
if(string.length==1) {
|
||||
val petscii = Petscii.encodePetscii(string, true)[0]
|
||||
functionCall.arglist.clear()
|
||||
functionCall.arglist.add(LiteralValue.optimalInteger(petscii, functionCall.position))
|
||||
functionCall.target = IdentifierReference(listOf("c64", "CHROUT"), functionCall.target.position)
|
||||
functionCallStatement.arglist.clear()
|
||||
functionCallStatement.arglist.add(LiteralValue.optimalInteger(petscii, functionCallStatement.position))
|
||||
functionCallStatement.target = IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position)
|
||||
optimizationsDone++
|
||||
return functionCall
|
||||
return functionCallStatement
|
||||
} else if(string.length==2) {
|
||||
val petscii = Petscii.encodePetscii(string, true)
|
||||
val scope = AnonymousScope(mutableListOf(), functionCall.position)
|
||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCall.target.position),
|
||||
mutableListOf(LiteralValue.optimalInteger(petscii[0], functionCall.position)), functionCall.position))
|
||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCall.target.position),
|
||||
mutableListOf(LiteralValue.optimalInteger(petscii[1], functionCall.position)), functionCall.position))
|
||||
val scope = AnonymousScope(mutableListOf(), functionCallStatement.position)
|
||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
|
||||
mutableListOf(LiteralValue.optimalInteger(petscii[0], functionCallStatement.position)), functionCallStatement.position))
|
||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
|
||||
mutableListOf(LiteralValue.optimalInteger(petscii[1], functionCallStatement.position)), functionCallStatement.position))
|
||||
optimizationsDone++
|
||||
return scope
|
||||
}
|
||||
@@ -157,21 +155,27 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
|
||||
// if it calls a subroutine,
|
||||
// and the first instruction in the subroutine is a jump, call that jump target instead
|
||||
val subroutine = functionCall.target.targetStatement(namespace) as? Subroutine
|
||||
// if the first instruction in the subroutine is a return statement, replace with a nop instruction
|
||||
val subroutine = functionCallStatement.target.targetStatement(namespace) as? Subroutine
|
||||
if(subroutine!=null) {
|
||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
if(first is Jump && first.identifier!=null) {
|
||||
optimizationsDone++
|
||||
return FunctionCallStatement(first.identifier, functionCall.arglist, functionCall.position)
|
||||
return FunctionCallStatement(first.identifier, functionCallStatement.arglist, functionCallStatement.position)
|
||||
}
|
||||
if(first is ReturnFromIrq || first is Return) {
|
||||
optimizationsDone++
|
||||
return NopStatement(functionCallStatement.position)
|
||||
}
|
||||
}
|
||||
|
||||
return super.process(functionCall)
|
||||
return super.process(functionCallStatement)
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCall): IExpression {
|
||||
// if it calls a subroutine,
|
||||
// and the first instruction in the subroutine is a jump, call that jump target instead
|
||||
// if the first instruction in the subroutine is a return statement with constant value, replace with the constant value
|
||||
val subroutine = functionCall.target.targetStatement(namespace) as? Subroutine
|
||||
if(subroutine!=null) {
|
||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
@@ -179,6 +183,11 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
optimizationsDone++
|
||||
return FunctionCall(first.identifier, functionCall.arglist, functionCall.position)
|
||||
}
|
||||
if(first is Return && first.values.size==1) {
|
||||
val constval = first.values[0].constValue(namespace, heap)
|
||||
if(constval!=null)
|
||||
return constval
|
||||
}
|
||||
}
|
||||
return super.process(functionCall)
|
||||
}
|
||||
@@ -255,8 +264,10 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
val constvalue = whileLoop.condition.constValue(namespace, heap)
|
||||
if(constvalue!=null) {
|
||||
return if(constvalue.asBooleanValue){
|
||||
// always true -> print a warning, and optimize into body + jump
|
||||
// always true -> print a warning, and optimize into body + jump (if there are no continue and break statements)
|
||||
printWarning("condition is always true", whileLoop.position)
|
||||
if(hasContinueOrBreak(whileLoop.body))
|
||||
return whileLoop
|
||||
val label = Label("__back", whileLoop.condition.position)
|
||||
whileLoop.body.statements.add(0, label)
|
||||
whileLoop.body.statements.add(Jump(null,
|
||||
@@ -279,13 +290,19 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
val constvalue = repeatLoop.untilCondition.constValue(namespace, heap)
|
||||
if(constvalue!=null) {
|
||||
return if(constvalue.asBooleanValue){
|
||||
// always true -> keep only the statement block
|
||||
// always true -> keep only the statement block (if there are no continue and break statements)
|
||||
printWarning("condition is always true", repeatLoop.position)
|
||||
optimizationsDone++
|
||||
repeatLoop.body
|
||||
if(hasContinueOrBreak(repeatLoop.body))
|
||||
repeatLoop
|
||||
else {
|
||||
optimizationsDone++
|
||||
repeatLoop.body
|
||||
}
|
||||
} else {
|
||||
// always false -> print a warning, and optimize into body + jump
|
||||
// always false -> print a warning, and optimize into body + jump (if there are no continue and break statements)
|
||||
printWarning("condition is always false", repeatLoop.position)
|
||||
if(hasContinueOrBreak(repeatLoop.body))
|
||||
return repeatLoop
|
||||
val label = Label("__back", repeatLoop.untilCondition.position)
|
||||
repeatLoop.body.statements.add(0, label)
|
||||
repeatLoop.body.statements.add(Jump(null,
|
||||
@@ -298,6 +315,31 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
|
||||
return repeatLoop
|
||||
}
|
||||
|
||||
private fun hasContinueOrBreak(scope: INameScope): Boolean {
|
||||
|
||||
class Searcher:IAstProcessor
|
||||
{
|
||||
var count=0
|
||||
|
||||
override fun process(breakStmt: Break): IStatement {
|
||||
count++
|
||||
return super.process(breakStmt)
|
||||
}
|
||||
|
||||
override fun process(contStmt: Continue): IStatement {
|
||||
count++
|
||||
return super.process(contStmt)
|
||||
}
|
||||
}
|
||||
val s=Searcher()
|
||||
for(stmt in scope.statements) {
|
||||
stmt.process(s)
|
||||
if(s.count>0)
|
||||
return true
|
||||
}
|
||||
return s.count > 0
|
||||
}
|
||||
|
||||
override fun process(jump: Jump): IStatement {
|
||||
val subroutine = jump.identifier?.targetStatement(namespace) as? Subroutine
|
||||
if(subroutine!=null) {
|
||||
|
||||
@@ -6,7 +6,6 @@ import prog8.compiler.LauncherType
|
||||
import prog8.compiler.OutputType
|
||||
import prog8.determineCompilationOptions
|
||||
import java.io.InputStream
|
||||
import java.net.URL
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
@@ -27,8 +26,12 @@ private class LexerErrorListener: BaseErrorListener() {
|
||||
}
|
||||
|
||||
|
||||
fun importModule(stream: CharStream, moduleName: String, isLibrary: Boolean): Module {
|
||||
val lexer = prog8Lexer(stream)
|
||||
internal class CustomLexer(val modulePath: Path, input: CharStream?) : prog8Lexer(input)
|
||||
|
||||
|
||||
fun importModule(stream: CharStream, modulePath: Path, isLibrary: Boolean): Module {
|
||||
val moduleName = modulePath.fileName
|
||||
val lexer = CustomLexer(modulePath, stream)
|
||||
val lexerErrors = LexerErrorListener()
|
||||
lexer.addErrorListener(lexerErrors)
|
||||
val tokens = CommentHandlingTokenStream(lexer)
|
||||
@@ -36,13 +39,13 @@ fun importModule(stream: CharStream, moduleName: String, isLibrary: Boolean): Mo
|
||||
val parseTree = parser.module()
|
||||
val numberOfErrors = parser.numberOfSyntaxErrors + lexerErrors.numberOfErrors
|
||||
if(numberOfErrors > 0)
|
||||
throw ParsingFailedError("There are $numberOfErrors errors in '$moduleName.p8'.")
|
||||
throw ParsingFailedError("There are $numberOfErrors errors in '$moduleName'.")
|
||||
|
||||
// You can do something with the parsed comments:
|
||||
// tokens.commentTokens().forEach { println(it) }
|
||||
|
||||
// convert to Ast
|
||||
val moduleAst = parseTree.toAst(moduleName, isLibrary, Paths.get(stream.sourceName))
|
||||
val moduleAst = parseTree.toAst(moduleName.toString(), isLibrary, modulePath)
|
||||
importedModules[moduleAst.name] = moduleAst
|
||||
|
||||
// process imports
|
||||
@@ -64,7 +67,7 @@ fun importModule(stream: CharStream, moduleName: String, isLibrary: Boolean): Mo
|
||||
.asSequence()
|
||||
.mapIndexed { i, it -> Pair(i, it) }
|
||||
.filter { (it.second as? Directive)?.directive == "%import" }
|
||||
.map { Pair(it.first, executeImportDirective(it.second as Directive, Paths.get("$moduleName.p8"))) }
|
||||
.map { Pair(it.first, executeImportDirective(it.second as Directive, modulePath)) }
|
||||
.toList()
|
||||
|
||||
imports.reversed().forEach {
|
||||
@@ -96,9 +99,8 @@ fun importModule(filePath: Path) : Module {
|
||||
if(!Files.isReadable(filePath))
|
||||
throw ParsingFailedError("No such file: $filePath")
|
||||
|
||||
val moduleName = filePath.fileName.toString().substringBeforeLast('.')
|
||||
val input = CharStreams.fromPath(filePath)
|
||||
return importModule(input, moduleName, filePath.parent==null)
|
||||
return importModule(input, filePath, filePath.parent==null)
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +139,7 @@ private fun executeImportDirective(import: Directive, importedFrom: Path): Modul
|
||||
// load the module from the embedded resource
|
||||
resource.use {
|
||||
println("importing '$moduleName' (embedded library)")
|
||||
importModule(CharStreams.fromStream(it), moduleName, true)
|
||||
importModule(CharStreams.fromStream(it), Paths.get("@embedded@/$moduleName"), true)
|
||||
}
|
||||
} else {
|
||||
val modulePath = discoverImportedModuleFile(moduleName, importedFrom, import.position)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package prog8.stackvm
|
||||
|
||||
import prog8.ast.DataType
|
||||
import prog8.ast.Position
|
||||
import prog8.ast.unescape
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.intermediate.*
|
||||
import java.io.File
|
||||
@@ -88,10 +86,7 @@ class Program (val name: String,
|
||||
}
|
||||
heapvalues.sortedBy { it.first }.forEach {
|
||||
when(it.second) {
|
||||
DataType.STR,
|
||||
DataType.STR_P,
|
||||
DataType.STR_S,
|
||||
DataType.STR_PS -> heap.add(it.second, unescape(it.third.substring(1, it.third.length-1), Position("<stackvmsource>", 0, 0, 0)))
|
||||
DataType.STR, DataType.STR_S -> heap.add(it.second, unescape(it.third.substring(1, it.third.length-1), Position("<stackvmsource>", 0, 0, 0)))
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
val numbers = it.third.substring(1, it.third.length-1).split(',')
|
||||
@@ -103,7 +98,8 @@ class Program (val name: String,
|
||||
val doublearray = numbers.map{number->number.trim().toDouble()}.toDoubleArray()
|
||||
heap.add(it.second, doublearray)
|
||||
}
|
||||
DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT -> throw VmExecutionException("invalid heap value type ${it.second}")
|
||||
in NumericDatatypes -> throw VmExecutionException("invalid heap value type ${it.second}")
|
||||
else -> throw VmExecutionException("weird datatype")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,7 +196,7 @@ class Program (val name: String,
|
||||
DataType.UWORD -> Value(DataType.UWORD, valueStr.substring(3).toInt(16))
|
||||
DataType.WORD -> Value(DataType.WORD, valueStr.substring(2).toInt(16))
|
||||
DataType.FLOAT -> Value(DataType.FLOAT, valueStr.substring(2).toDouble())
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
||||
in StringDatatypes -> {
|
||||
if(valueStr.startsWith('"') && valueStr.endsWith('"'))
|
||||
throw VmExecutionException("encountered a var with a string value, but all string values should already have been moved into the heap")
|
||||
else if(!valueStr.startsWith("heap:"))
|
||||
@@ -210,11 +206,7 @@ class Program (val name: String,
|
||||
Value(type, heapId)
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_UB,
|
||||
DataType.ARRAY_B,
|
||||
DataType.ARRAY_UW,
|
||||
DataType.ARRAY_W,
|
||||
DataType.ARRAY_F -> {
|
||||
in ArrayDatatypes -> {
|
||||
if(!valueStr.startsWith("heap:"))
|
||||
throw VmExecutionException("invalid array value, should be a heap reference")
|
||||
else {
|
||||
@@ -222,6 +214,7 @@ class Program (val name: String,
|
||||
Value(type, heapId)
|
||||
}
|
||||
}
|
||||
else -> throw VmExecutionException("weird datatype")
|
||||
}
|
||||
vars[name] = value
|
||||
}
|
||||
|
||||
@@ -49,9 +49,7 @@ enum class Syscall(val callNr: Short) {
|
||||
FUNC_RNDW(90), // push a random word on the stack
|
||||
FUNC_RNDF(91), // push a random float on the stack (between 0.0 and 1.0)
|
||||
FUNC_LEN_STR(105),
|
||||
FUNC_LEN_STRP(106),
|
||||
FUNC_LEN_STRS(107),
|
||||
FUNC_LEN_STRPS(108),
|
||||
FUNC_LEN_STRS(106),
|
||||
FUNC_ANY_B(109),
|
||||
FUNC_ANY_W(110),
|
||||
FUNC_ANY_F(111),
|
||||
@@ -80,7 +78,8 @@ enum class Syscall(val callNr: Short) {
|
||||
FUNC_SUM_F(134),
|
||||
FUNC_MEMCOPY(138),
|
||||
FUNC_MEMSET(139),
|
||||
FUNC_MEMSETW(140)
|
||||
FUNC_MEMSETW(140),
|
||||
FUNC_READ_FLAGS(141)
|
||||
|
||||
// note: not all builtin functions of the Prog8 language are present as functions:
|
||||
// some of them are straight opcodes (such as MSB, LSB, LSL, LSR, ROL_BYTE, ROR, ROL2, ROR2, and FLT)!
|
||||
@@ -173,6 +172,23 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
sourceLine = ""
|
||||
currentIns = this.program[0]
|
||||
irqStartInstruction = labels["irq.irq"] // set to first instr of irq routine, if any
|
||||
|
||||
initBlockVars()
|
||||
}
|
||||
|
||||
private fun initBlockVars() {
|
||||
// initialize the global variables in each block.
|
||||
// this is done by calling the special init subroutine of each block that has one.
|
||||
val initVarsSubs = labels.filter { it.key.endsWith("."+ initvarsSubName) }
|
||||
for(init in initVarsSubs) {
|
||||
currentIns = init.value
|
||||
try {
|
||||
step(Int.MAX_VALUE)
|
||||
} catch(x: VmTerminationException) {
|
||||
// init subroutine finished
|
||||
}
|
||||
}
|
||||
currentIns = program[0]
|
||||
}
|
||||
|
||||
fun step(instructionCount: Int = 5000) {
|
||||
@@ -905,9 +921,9 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
}
|
||||
Opcode.POP_VAR_WORD -> {
|
||||
val value = evalstack.pop()
|
||||
checkDt(value, DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)
|
||||
checkDt(value, DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_S)
|
||||
val variable = getVar(ins.callLabel!!)
|
||||
checkDt(variable, DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)
|
||||
checkDt(variable, DataType.UWORD, DataType.WORD, DataType.STR, DataType.STR_S)
|
||||
if(value.type!=variable.type)
|
||||
throw VmExecutionException("datatype mismatch")
|
||||
variables[ins.callLabel] = value
|
||||
@@ -1305,7 +1321,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
when (array.type) {
|
||||
DataType.ARRAY_UB -> evalstack.push(Value(DataType.UBYTE, array.array!![index]))
|
||||
DataType.ARRAY_B -> evalstack.push(Value(DataType.BYTE, array.array!![index]))
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> evalstack.push(Value(DataType.UBYTE, Petscii.encodePetscii(array.str!![index].toString(), true)[0]))
|
||||
DataType.STR, DataType.STR_S -> evalstack.push(Value(DataType.UBYTE, Petscii.encodePetscii(array.str!![index].toString(), true)[0]))
|
||||
else -> throw VmExecutionException("not a proper array/string variable with byte elements")
|
||||
}
|
||||
}
|
||||
@@ -1396,10 +1412,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
when (array.type) {
|
||||
DataType.ARRAY_UB -> array.array!![index] = value.integerValue()
|
||||
DataType.ARRAY_B -> array.array!![index] = value.integerValue()
|
||||
DataType.STR,
|
||||
DataType.STR_P,
|
||||
DataType.STR_S,
|
||||
DataType.STR_PS -> {
|
||||
DataType.STR, DataType.STR_S -> {
|
||||
val chars = array.str!!.toCharArray()
|
||||
chars[index] = Petscii.decodePetscii(listOf(value.integerValue().toShort()), true)[0]
|
||||
heap.update(variable.heapId, chars.joinToString(""))
|
||||
@@ -1591,11 +1604,19 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
Syscall.FUNC_RND -> evalstack.push(Value(DataType.UBYTE, rnd.nextInt() and 255))
|
||||
Syscall.FUNC_RNDW -> evalstack.push(Value(DataType.UWORD, rnd.nextInt() and 65535))
|
||||
Syscall.FUNC_RNDF -> evalstack.push(Value(DataType.FLOAT, rnd.nextDouble()))
|
||||
Syscall.FUNC_LEN_STR, Syscall.FUNC_LEN_STRS, Syscall.FUNC_LEN_STRP, Syscall.FUNC_LEN_STRPS -> {
|
||||
Syscall.FUNC_LEN_STR, Syscall.FUNC_LEN_STRS -> {
|
||||
val strPtr = evalstack.pop().integerValue()
|
||||
val text = heap.get(strPtr).str!!
|
||||
evalstack.push(Value(DataType.UBYTE, text.length))
|
||||
}
|
||||
Syscall.FUNC_READ_FLAGS -> {
|
||||
val carry = if(P_carry) 1 else 0
|
||||
val zero = if(P_zero) 2 else 0
|
||||
val irqd = if(P_irqd) 4 else 0
|
||||
val negative = if(P_negative) 128 else 0
|
||||
val flags = carry or zero or irqd or negative
|
||||
evalstack.push(Value(DataType.UBYTE, flags))
|
||||
}
|
||||
Syscall.FUNC_SIN -> evalstack.push(Value(DataType.FLOAT, sin(evalstack.pop().numericValue().toDouble())))
|
||||
Syscall.FUNC_COS -> evalstack.push(Value(DataType.FLOAT, cos(evalstack.pop().numericValue().toDouble())))
|
||||
Syscall.FUNC_SIN8 -> {
|
||||
|
||||
@@ -4,7 +4,10 @@ import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.Matchers.empty
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import prog8.ast.ByteDatatypes
|
||||
import prog8.ast.DataType
|
||||
import prog8.ast.IterableDatatypes
|
||||
import prog8.ast.WordDatatypes
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.intermediate.Instruction
|
||||
import prog8.compiler.intermediate.Opcode
|
||||
@@ -1234,12 +1237,11 @@ class TestStackVmOpcodes {
|
||||
|
||||
private fun pushOpcode(dt: DataType): Opcode {
|
||||
return when (dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> Opcode.PUSH_BYTE
|
||||
DataType.UWORD, DataType.WORD -> Opcode.PUSH_WORD
|
||||
in ByteDatatypes -> Opcode.PUSH_BYTE
|
||||
in WordDatatypes -> Opcode.PUSH_WORD
|
||||
in IterableDatatypes -> Opcode.PUSH_WORD
|
||||
DataType.FLOAT -> Opcode.PUSH_FLOAT
|
||||
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS,
|
||||
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F,
|
||||
DataType.ARRAY_B, DataType.ARRAY_W -> Opcode.PUSH_WORD
|
||||
else -> throw IllegalArgumentException("invalid datatype")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+65
-58
@@ -117,51 +117,62 @@ class TestCompiler {
|
||||
}
|
||||
|
||||
|
||||
private val dummypos = Position("test", 0, 0, 0)
|
||||
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class TestZeropage {
|
||||
@Test
|
||||
fun testNames() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false))
|
||||
|
||||
zp.allocate("", DataType.UBYTE, null)
|
||||
zp.allocate("", DataType.UBYTE, null)
|
||||
zp.allocate("varname", DataType.UBYTE, null)
|
||||
assertFailsWith<AssertionError> {
|
||||
zp.allocate(VarDecl(VarDeclType.MEMORY, DataType.UBYTE, null, "", null, dummypos))
|
||||
zp.allocate("varname", DataType.UBYTE, null)
|
||||
}
|
||||
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, null, "", null, dummypos))
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, null, "", null, dummypos))
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, null, "varname", null, dummypos))
|
||||
assertFailsWith<AssertionError> {
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, null, "varname", null, dummypos))
|
||||
}
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, null, "varname2", null, dummypos))
|
||||
zp.allocate("varname2", DataType.UBYTE, null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testZpFloatEnable() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||
assertFailsWith<CompilerException> {
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
|
||||
zp.allocate("", DataType.FLOAT, null)
|
||||
}
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true))
|
||||
zp2.allocate("", DataType.FLOAT, null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testZpModesWithFloats() {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true))
|
||||
assertFailsWith<CompilerException> {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true))
|
||||
}
|
||||
assertFailsWith<CompilerException> {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true))
|
||||
}
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true))
|
||||
zp2.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFreeSpaces() {
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
||||
assertEquals(19, zp1.available())
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true))
|
||||
assertEquals(67, zp2.available())
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true))
|
||||
assertEquals(238, zp3.available())
|
||||
assertEquals(20, zp1.available())
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false))
|
||||
assertEquals(95, zp2.available())
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false))
|
||||
assertEquals(129, zp3.available())
|
||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||
assertEquals(238, zp4.available())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReservedSpace() {
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true))
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||
assertEquals(238, zp1.available())
|
||||
assertTrue(50 in zp1.free)
|
||||
assertTrue(100 in zp1.free)
|
||||
@@ -170,8 +181,7 @@ class TestZeropage {
|
||||
assertTrue(200 in zp1.free)
|
||||
assertTrue(255 in zp1.free)
|
||||
assertTrue(199 in zp1.free)
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL,
|
||||
listOf(50 .. 100, 200..255), true))
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false))
|
||||
assertEquals(139, zp2.available())
|
||||
assertFalse(50 in zp2.free)
|
||||
assertFalse(100 in zp2.free)
|
||||
@@ -185,74 +195,71 @@ class TestZeropage {
|
||||
@Test
|
||||
fun testBasicsafeAllocation() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
||||
assertEquals(19, zp.available())
|
||||
assertEquals(20, zp.available())
|
||||
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
|
||||
assertFailsWith<CompilerException> {
|
||||
zp.allocate("", DataType.FLOAT, null)
|
||||
assertFailsWith<ZeropageDepletedError> {
|
||||
// in regular zp there aren't 5 sequential bytes free after we take the first sequence
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
|
||||
zp.allocate("", DataType.FLOAT, null)
|
||||
}
|
||||
|
||||
for (i in 0 until zp.available()) {
|
||||
val loc = zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, null, "", null, dummypos))
|
||||
val loc = zp.allocate("", DataType.UBYTE, null)
|
||||
assertTrue(loc > 0)
|
||||
}
|
||||
assertEquals(0, zp.available())
|
||||
assertFailsWith<CompilerException> {
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, null, "", null, dummypos))
|
||||
assertFailsWith<ZeropageDepletedError> {
|
||||
zp.allocate("", DataType.UBYTE, null)
|
||||
}
|
||||
assertFailsWith<CompilerException> {
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, null, "", null, dummypos))
|
||||
assertFailsWith<ZeropageDepletedError> {
|
||||
zp.allocate("", DataType.UWORD, null)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFullAllocation() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||
assertEquals(238, zp.available())
|
||||
val loc = zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
|
||||
val loc = zp.allocate("", DataType.UWORD, null)
|
||||
assertTrue(loc > 3)
|
||||
assertFalse(loc in zp.free)
|
||||
val num = zp.available() / 5
|
||||
val rest = zp.available() % 5
|
||||
val num = zp.available() / 2
|
||||
|
||||
for(i in 0..num-4) {
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
|
||||
zp.allocate("", DataType.UWORD, null)
|
||||
}
|
||||
assertEquals(18,zp.available())
|
||||
assertEquals(6,zp.available())
|
||||
|
||||
assertFailsWith<CompilerException> {
|
||||
assertFailsWith<ZeropageDepletedError> {
|
||||
// can't allocate because no more sequential bytes, only fragmented
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos))
|
||||
zp.allocate("", DataType.UWORD, null)
|
||||
}
|
||||
|
||||
for(i in 0..13) {
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, null, "", null, dummypos))
|
||||
for(i in 0..5) {
|
||||
zp.allocate("", DataType.UBYTE, null)
|
||||
}
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, null, "", null, dummypos))
|
||||
|
||||
assertEquals(2, zp.available())
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, null, "", null, dummypos))
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, null, "", null, dummypos))
|
||||
assertFailsWith<CompilerException> {
|
||||
assertEquals(0, zp.available())
|
||||
assertFailsWith<ZeropageDepletedError> {
|
||||
// no more space
|
||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, null, "", null, dummypos))
|
||||
zp.allocate("", DataType.UBYTE, null)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEfficientAllocation() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
||||
assertEquals(19, zp.available())
|
||||
assertEquals(0x04, zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null, dummypos)))
|
||||
assertEquals(0x09, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, null, "", null, dummypos)))
|
||||
assertEquals(0x0d, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, null, "", null, dummypos)))
|
||||
assertEquals(0x94, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, null, "", null, dummypos)))
|
||||
assertEquals(0xa7, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, null, "", null, dummypos)))
|
||||
assertEquals(0xa9, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, null, "", null, dummypos)))
|
||||
assertEquals(0xb5, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, null, "", null, dummypos)))
|
||||
assertEquals(0xf7, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, null, "", null, dummypos)))
|
||||
assertEquals(0xf9, zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, null, "", null, dummypos)))
|
||||
assertEquals(20, zp.available())
|
||||
assertEquals(0x04, zp.allocate("", DataType.FLOAT, null))
|
||||
assertEquals(0x09, zp.allocate("", DataType.UBYTE, null))
|
||||
assertEquals(0x0d, zp.allocate("", DataType.UWORD, null))
|
||||
assertEquals(0x94, zp.allocate("", DataType.UWORD, null))
|
||||
assertEquals(0xa7, zp.allocate("", DataType.UWORD, null))
|
||||
assertEquals(0xa9, zp.allocate("", DataType.UWORD, null))
|
||||
assertEquals(0xb5, zp.allocate("", DataType.UWORD, null))
|
||||
assertEquals(0xf7, zp.allocate("", DataType.UWORD, null))
|
||||
assertEquals(0x0a, zp.allocate("", DataType.UBYTE, null))
|
||||
assertEquals(0xf9, zp.allocate("", DataType.UBYTE, null))
|
||||
assertEquals(0, zp.available())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import prog8.ast.LiteralValue
|
||||
import prog8.ast.Position
|
||||
import prog8.compiler.intermediate.Value
|
||||
import prog8.compiler.intermediate.ValueException
|
||||
import prog8.stackvm.VmExecutionException
|
||||
import kotlin.test.*
|
||||
|
||||
|
||||
@@ -83,7 +82,6 @@ class TestStackVmValue {
|
||||
fun testEqualsAndNotEqualsHeapTypes()
|
||||
{
|
||||
assertTrue(sameValueAndType(Value(DataType.STR, 999), Value(DataType.STR, 999)))
|
||||
assertFalse(sameValueAndType(Value(DataType.STR, 999), Value(DataType.STR_P, 999)))
|
||||
assertFalse(sameValueAndType(Value(DataType.STR, 999), Value(DataType.STR, 222)))
|
||||
|
||||
assertTrue(sameValueAndType(Value(DataType.ARRAY_UB, 99), Value(DataType.ARRAY_UB, 99)))
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.7" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.7 (py3)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
@@ -2,8 +2,33 @@
|
||||
Writing and building a program
|
||||
==============================
|
||||
|
||||
What is a "Program" anyway?
|
||||
---------------------------
|
||||
First, getting a working compiler
|
||||
---------------------------------
|
||||
|
||||
Before you can compile Prog8 programs, you'll have to build the compiler itself.
|
||||
First make sure you have installed the :ref:`requirements`.
|
||||
Then you can choose a few ways to create the compiler:
|
||||
|
||||
**Using the shell script:**
|
||||
|
||||
#. run the "build_the_compiler.sh" shell script
|
||||
#. it will create a "prog8compiler.jar" file which contains everything.
|
||||
#. run the compiler with "java -jar prog8compiler.jar" to see how you can use it.
|
||||
|
||||
**using the Gradle build system:**
|
||||
|
||||
#. run the command "./gradlew installDist"
|
||||
#. it will create the commands and required libraries in the "./compiler/build/install/p8compile/" directory
|
||||
#. run the compiler with the "./compiler/build/install/p8compile/bin/p8compile" command to see how you can use it.
|
||||
|
||||
**download a precompiled version from github:**
|
||||
|
||||
#. download a recent "prog8compiler.jar" from `the releases on Github <https://github.com/irmen/prog8/releases>`_
|
||||
#. run the compiler with "java -jar prog8compiler.jar" to see how you can use it.
|
||||
|
||||
|
||||
What is a Prog8 "Program" anyway?
|
||||
---------------------------------
|
||||
|
||||
A "complete runnable program" is a compiled, assembled, and linked together single unit.
|
||||
It contains all of the program's code and data and has a certain file format that
|
||||
|
||||
+12
-3
@@ -120,8 +120,12 @@ A recent .exe version of this tool for Windows can be obtained from my `clone <h
|
||||
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 the packaged compiler.
|
||||
If you're scared of Oracle's licensing terms, most Linux distributions ship OpenJDK instead
|
||||
and for Windows it's possible to get that as well: for instance,
|
||||
`Azul's Zulu <https://www.azul.com/downloads/zulu/>`_ is a certified OpenJDK
|
||||
implementation available for various platforms.
|
||||
|
||||
A C-64 emulator or a real C-64 to run the programs on. The compiler assumes the presence
|
||||
Finally: a **C-64 emulator** (or a real C-64 ofcourse) to run the programs on. The compiler assumes the presence
|
||||
of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
|
||||
|
||||
.. hint::
|
||||
@@ -130,11 +134,16 @@ of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
|
||||
packaged jar file.
|
||||
|
||||
.. note::
|
||||
To build the compiler from source, Kotlin SDK version 1.3 or newer is needed.
|
||||
Building the compiler itself:
|
||||
|
||||
(re)building the compiler itself requires a Kotlin SDK version 1.3.
|
||||
The compiler is developed using the `IntelliJ IDEA <https://www.jetbrains.com/idea/>`_
|
||||
IDE from Jetbrains, with the Kotlin plugin (free community edition of this IDE is available).
|
||||
But a bare Kotlin SDK installation should work just as well.
|
||||
A shell script is provided to build and package the compiler from the command line.
|
||||
A shell script (``build_the_compiler.sh``) is provided to build and package the compiler from the command line.
|
||||
You can also use the Gradle build system to build the compiler (it will take care of
|
||||
downloading all required libraries for you) by typing ``gradle installDist`` for instance.
|
||||
The output of this gradle build will appear in the "./compiler/build/install/p8compile/" directory.
|
||||
|
||||
.. note::
|
||||
Development and testing is done on Linux, but the compiler should run on most
|
||||
|
||||
+61
-12
@@ -187,9 +187,13 @@ Values will usually be part of an expression or assignment statement::
|
||||
byte counter = 42 ; variable of size 8 bits, with initial value 42
|
||||
|
||||
|
||||
.. todo::
|
||||
There must be a way to tell the compiler which variables you require to be in Zeropage:
|
||||
``zeropage`` modifier keyword on vardecl perhaps?
|
||||
*zeropage tag:*
|
||||
If you add the ``@zp`` tag to the variable declaration, the compiler will prioritize this variable
|
||||
when selecting variables to put into zero page. If there are enough free locations in the zeropage,
|
||||
it will then try to fill it with as much other variables as possible (before they will be put in regular memory pages).
|
||||
Example::
|
||||
|
||||
byte @zp zeropageCounter = 42
|
||||
|
||||
|
||||
Variables that represent CPU hardware registers
|
||||
@@ -321,6 +325,11 @@ be set to zero only for the first run of the program. A second run will utilize
|
||||
where it left off (but your code will be a bit smaller because no initialization instructions
|
||||
are generated)
|
||||
|
||||
.. caution::
|
||||
variables that get allocated in zero-page will *not* have a zero starting value when you omit
|
||||
the variable's initialization. They'll be whatever the last value in that zero page
|
||||
location was. So it's best to don't depend on the uninitialized starting value!
|
||||
|
||||
.. warning::
|
||||
this behavior may change in a future version so that subsequent runs always
|
||||
use the same initial values
|
||||
@@ -373,8 +382,11 @@ rather than having fixed gotos or subroutine calls::
|
||||
Conditional jumps (``if condition goto label``) are compiled using 6502's branching instructions (such as ``bne`` and ``bcc``) so
|
||||
the rather strict limit on how *far* it can jump applies. The compiler itself can't figure this
|
||||
out unfortunately, so it is entirely possible to create code that cannot be assembled successfully.
|
||||
You'll have to restructure your gotos in the code (place target labels closer to the branch)
|
||||
if you run into this type of assembler error.
|
||||
Thankfully the ``64tass`` assembler that is used has the option to automatically
|
||||
convert such branches to their opposite + a normal jmp. This is slower and takes up more space
|
||||
and you will get warning printed if this happens. You may then want to restructure your branches (place target labels closer to the branch,
|
||||
or reduce code complexity).
|
||||
|
||||
|
||||
There is a special form of the if-statement that immediately translates into one of the 6502's branching instructions.
|
||||
This allows you to write a conditional jump or block execution directly acting on the current values of the CPU's status register bits.
|
||||
@@ -459,11 +471,7 @@ There are various built-in functions such as sin(), cos(), min(), max() that can
|
||||
You can also reference idendifiers defined elsewhere in your code.
|
||||
|
||||
.. attention::
|
||||
**Data type conversion (during calculations) and floating point handling:**
|
||||
|
||||
BYTE values used in arithmetic expressions (calculations) will be automatically converted into WORD values
|
||||
if the calculation needs that to store the resulting value. Once a WORD value is used, all other results will be WORDs as well
|
||||
(there's no automatic conversion of WORD into BYTE).
|
||||
**Floating points used in expressions:**
|
||||
|
||||
When a floating point value is used in a calculation, the result will be a floating point, and byte or word values
|
||||
will be automatically converted into floats in this case. The compiler will issue a warning though when this happens, because floating
|
||||
@@ -491,6 +499,28 @@ Usually the normal precedence rules apply (``*`` goes before ``+`` etc.) but sub
|
||||
within parentheses will be evaluated first. So ``(4 + 8) * 2`` is 24 and not 20,
|
||||
and ``(true or false) and false`` is false instead of true.
|
||||
|
||||
.. attention::
|
||||
**calculations keep their datatype:**
|
||||
When you do calculations on a BYTE type, the result will remain a BYTE.
|
||||
When you do calculations on a WORD type, the result will remain a WORD.
|
||||
For instance::
|
||||
|
||||
byte b = 44
|
||||
word w = b*55 ; the result will be 116! (even though the target variable is a word)
|
||||
w *= 999 ; the result will be -15188 (the multiplication stays within a word)
|
||||
|
||||
The compiler will NOT give a warning about this! It's doing this for
|
||||
performance reasons - so you won't get sudden 16 bit (or even float)
|
||||
calculations where you needed only simple fast byte arithmetic.
|
||||
If you do need the extended resulting value, cast at least one of the
|
||||
operands of an operator to the larger datatype. For example::
|
||||
|
||||
byte b = 44
|
||||
word w = b*55.w ; the result will be 2420
|
||||
w = (b as word)*55 ; same result
|
||||
|
||||
|
||||
|
||||
|
||||
Subroutines
|
||||
-----------
|
||||
@@ -502,7 +532,6 @@ Subroutines are parts of the code that can be repeatedly invoked using a subrout
|
||||
Their definition, using the ``sub`` statement, includes the specification of the required parameters and return value.
|
||||
Subroutines can be defined in a Block, but also nested inside another subroutine. Everything is scoped accordingly.
|
||||
|
||||
|
||||
Calling a subroutine
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -511,9 +540,13 @@ It is possible to not store the return value but the compiler
|
||||
will issue a warning then telling you the result values of a subroutine call are discarded.
|
||||
|
||||
.. caution::
|
||||
Note that *recursive* subroutine calls are not supported at this time.
|
||||
Note that due to the way parameters are processed by the compiler,
|
||||
subroutines are *non-reentrant*. This means you cannot create recursive calls.
|
||||
If you do need a recursive algorithm, you'll have to hand code it in embedded assembly for now,
|
||||
or rewrite it into an iterative algorithm.
|
||||
Also, subroutines used in the main program should not be used from an IRQ handler. This is because
|
||||
the subroutine may be interrupted, and will then call itself from the IRQ handler. Results are
|
||||
then undefined because the variables will get overwritten.
|
||||
|
||||
|
||||
.. _builtinfunctions:
|
||||
@@ -704,3 +737,19 @@ rsave()
|
||||
|
||||
rrestore()
|
||||
Restores the CPU registers and the status flags from previously saved values.
|
||||
|
||||
read_flags()
|
||||
Returns the current value of the CPU status register.
|
||||
|
||||
|
||||
|
||||
Library routines
|
||||
----------------
|
||||
|
||||
There are many routines available in the compiler libraries.
|
||||
Some are used internally by the compiler as well.
|
||||
There's too many to list here, just have a look through the source code
|
||||
of the library modules to see what's there.
|
||||
(They can be found in the compiler/res directory)
|
||||
The example programs also use a small set of the library routines, you can study
|
||||
their source code to see how they might be used.
|
||||
@@ -60,16 +60,20 @@ Directives
|
||||
- style ``kernalsafe`` -- use the part of the ZP that is 'free' or only used by BASIC routines,
|
||||
and don't change anything else. This allows full use of KERNAL ROM routines (but not BASIC routines),
|
||||
including default IRQs during normal system operation.
|
||||
When the program exits, a system reset is performed (because BASIC will be in a corrupt state).
|
||||
- style ``floatsafe`` -- like the previous one but also reserves the addresses that
|
||||
are required to perform floating point operations (from the BASIC kernel). No clean exit is possible.
|
||||
- style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't
|
||||
touch change anything else. This allows full use of BASIC and KERNAL ROM routines including default IRQs
|
||||
during normal system operation.
|
||||
When the program exits, it simply returns to the BASIC ready prompt.
|
||||
- style ``full`` -- claim the whole ZP for variables for the program, overwriting everything,
|
||||
except the few addresses mentioned above that are used by the system's IRQ routine.
|
||||
Even though the default IRQ routine is still active, it is impossible to use most BASIC and KERNAL ROM routines.
|
||||
This includes many floating point operations and several utility routines that do I/O, such as ``print_string``.
|
||||
It is also not possible to cleanly exit the program, other than resetting the machine.
|
||||
This option makes programs smaller and faster because many more variables can
|
||||
be stored in the ZP, which is more efficient.
|
||||
As with ``kernalsafe``, it is not possible to cleanly exit the program, other than to reset the machine.
|
||||
This option makes programs smaller and faster because even more variables can
|
||||
be stored in the ZP (which allows for more efficient assembly code).
|
||||
|
||||
Also read :ref:`zeropage`.
|
||||
|
||||
@@ -209,9 +213,11 @@ Variable declarations
|
||||
|
||||
Variables should be declared with their exact type and size so the compiler can allocate storage
|
||||
for them. You must give them an initial value as well. That value can be a simple literal value,
|
||||
or an expression. The syntax is::
|
||||
or an expression. You can add a ``@zp`` zeropage-tag, to tell the compiler to prioritize it
|
||||
when selecting variables to be put into zeropage.
|
||||
The syntax is::
|
||||
|
||||
<datatype> <variable name> [ = <initial value> ]
|
||||
<datatype> [ @zp ] <variable name> [ = <initial value> ]
|
||||
|
||||
Various examples::
|
||||
|
||||
@@ -224,6 +230,8 @@ Various examples::
|
||||
byte[5] values = [11, 22, 33, 44, 55]
|
||||
byte[5] values = 255 ; initialize with five 255 bytes
|
||||
|
||||
word @zp zpword = 9999 ; prioritize this when selecting vars for zeropage storage
|
||||
|
||||
|
||||
|
||||
Data types
|
||||
@@ -250,12 +258,8 @@ type identifier type storage size example var declara
|
||||
``float[x]`` floating-point array 5*x bytes ``float[4] myvar = [1.1, 2.2, 3.3, 4.4]``
|
||||
``str`` string (petscii) varies ``str myvar = "hello."``
|
||||
implicitly terminated by a 0-byte
|
||||
``str_p`` pascal-string (petscii) varies ``str_p myvar = "hello."``
|
||||
implicit first byte = length, no 0-byte
|
||||
``str_s`` string (screencodes) varies ``str_s myvar = "hello."``
|
||||
implicitly terminated by a 0-byte
|
||||
``str_ps`` pascal-string varies ``str_ps myvar = "hello."``
|
||||
(screencodes) implicit first byte = length, no 0-byte
|
||||
=============== ======================= ================= =========================================
|
||||
|
||||
**arrays:** you can split an array initializer list over several lines if you want.
|
||||
@@ -357,8 +361,13 @@ Operators
|
||||
---------
|
||||
|
||||
.. todo::
|
||||
address-of: ``#``
|
||||
Takes the address of the symbol following it: ``word address = #somevar``
|
||||
address-of: ``#`` or ``&`` (to stay close to C)
|
||||
Takes the address of the symbol following it: ``word address = &somevar``
|
||||
Perhaps requires an explicit pointer type as well instead of just word?
|
||||
|
||||
This can replace the ``memory`` var decl prefix as well, instead of
|
||||
``memory uword var = $c000`` we could write ``&uword var = $c000``
|
||||
|
||||
|
||||
|
||||
arithmetic: ``+`` ``-`` ``*`` ``/`` ``**`` ``%``
|
||||
@@ -506,6 +515,13 @@ And this is a loop over the values of the array ``fibonacci_numbers`` where the
|
||||
}
|
||||
|
||||
|
||||
You can inline the loop variable declaration in the for statement, including optional zp-tag::
|
||||
|
||||
for ubyte @zp fastindex in 10 to 20 {
|
||||
; do something
|
||||
}
|
||||
|
||||
|
||||
while loop
|
||||
^^^^^^^^^^
|
||||
|
||||
|
||||
@@ -77,11 +77,6 @@ Theoretically they can all be used in a program, with the follwoing limitations:
|
||||
- it's more convenient and safe to let the compiler allocate these addresses for you and just
|
||||
use symbolic names in the program code.
|
||||
|
||||
.. todo::
|
||||
There must be a way to tell the compiler which variables you require to be in Zeropage:
|
||||
``zeropage`` modifier keyword on vardecl perhaps?
|
||||
|
||||
|
||||
Prog8 knows what addresses are safe to use in the various ZP handling configurations.
|
||||
It will use the free ZP addresses to place its ZP variables in,
|
||||
until they're all used up. If instructed to output a program that takes over the entire
|
||||
@@ -93,6 +88,7 @@ treats the ZP for the program. The default is to be reasonably restrictive to us
|
||||
part of the ZP that is not used by the C64's kernal routines.
|
||||
It's possible to claim the whole ZP as well (by disabling the operating system or kernal).
|
||||
If you want, it's also possible to be more restricive and stay clear of the addresses used by BASIC routines too.
|
||||
This allows the program to exit cleanly back to a BASIC ready prompt - something that is not possible in the other modes.
|
||||
|
||||
|
||||
IRQs and the ZeroPage
|
||||
@@ -121,7 +117,8 @@ The following 6502 CPU hardware registers are directly usable in program code (a
|
||||
|
||||
- ``A``, ``X``, ``Y`` the three main cpu registers (8 bits)
|
||||
- the status register (P) carry flag and interrupt disable flag can be written via a couple of special
|
||||
builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``)
|
||||
builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``),
|
||||
and read via the ``read_flags()`` function.
|
||||
|
||||
However, you must assume that the 3 hardware registers ``A``, ``X`` and ``Y``
|
||||
are volatile. Their values cannot be depended upon, the compiler will use them as required.
|
||||
@@ -148,3 +145,35 @@ Arguments and result values are passed via global variables stored in memory
|
||||
*These are not allocated on a stack* so it is not possible to create recursive calls!
|
||||
The result value(s) of a subroutine are returned on the evaluation stack,
|
||||
to make it possible to use subroutines in expressions.
|
||||
|
||||
|
||||
IRQ Handling
|
||||
============
|
||||
|
||||
Normally, the system's default IRQ handling is not interfered with.
|
||||
You can however install your own IRQ handler.
|
||||
This is possible ofcourse by doing it all using customized inline assembly,
|
||||
but there are a few library routines available to make setting up C-64 IRQs and raster IRQs a lot easier (no assembly code required).
|
||||
|
||||
These routines are::
|
||||
|
||||
c64utils.set_irqvec()
|
||||
c64utils.set_irqvec_excl()
|
||||
|
||||
c64utils.set_rasterirq( <raster line> )
|
||||
c64utils.set_rasterirq_excl( <raster line> )
|
||||
|
||||
c64utils.restore_irqvec() ; set it back to the systems default irq handler
|
||||
|
||||
If you activate an IRQ handler with one of these, it expects the handler to be defined
|
||||
as a subroutine ``irq`` in the module ``irq`` so like this::
|
||||
|
||||
~ irq {
|
||||
sub irq() {
|
||||
; ... irq handling here ...
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.. todo::
|
||||
@todo the irq handler should use its own eval-stack to avoid stack interference issues
|
||||
+53
-4
@@ -29,14 +29,63 @@ Add more compiler optimizations to the existing ones.
|
||||
- on the language AST level
|
||||
- on the StackVM intermediate code level
|
||||
- on the final assembly source level
|
||||
- can the parameter passing to subroutines be optimized to avoid copying?
|
||||
|
||||
- subroutines with 1 or 2 byte args (or 1 word arg) should be converted to asm calling convention with the args in A/Y register
|
||||
this requires rethinking the way parameters are represented, simply injecting vardecls to
|
||||
declare local variables for them is not always correct anymore
|
||||
|
||||
|
||||
Also some library routines and code patterns could perhaps be optimized further
|
||||
|
||||
|
||||
Should use the zeropage for variables
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Eval stack redesign? (lot of work)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Variables should be allocated in the zeropage as long as it has space.
|
||||
- add some sort of ``zp`` modifier keyword on vardecls to force them into zeropage?
|
||||
The eval stack is now a split lsb/msb stack using X as the stackpointer.
|
||||
Is it easier/faster to just use a single page unsplit stack?
|
||||
It could then even be moved into the zeropage to greatly reduce code size and slowness.
|
||||
|
||||
Or just move the LSB portion into a slab of the zeropage.
|
||||
|
||||
Allocate a fixed word in ZP that is the TOS so we can operate on TOS directly
|
||||
without having to to index into the stack?
|
||||
|
||||
|
||||
More flexible (non-const) arrays?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Currently, array literals can only be constants
|
||||
Allow for non-const arrays? Such as::
|
||||
|
||||
ubyte[16] block1
|
||||
ubyte[16] block2
|
||||
ubyte[16] block3
|
||||
ubyte[3] blocks = [block1, block2, block3]
|
||||
|
||||
|
||||
structs?
|
||||
^^^^^^^^
|
||||
|
||||
A user defined struct type would be nice to group a bunch
|
||||
of values together (and use it multiple times). Something like::
|
||||
|
||||
struct Point {
|
||||
ubyte color
|
||||
word[3] vec = [0,0,0]
|
||||
}
|
||||
|
||||
Point p1
|
||||
Point p2
|
||||
Point p3
|
||||
|
||||
p1.color = 3
|
||||
p1.vec[2] = 2
|
||||
|
||||
|
||||
Misc
|
||||
^^^^
|
||||
|
||||
- sqrt() should have integer implementation as well, instead of relying on float SQRT for all argument types
|
||||
- code generation for POW instruction
|
||||
- are there any other missing instructions in the code generator?
|
||||
- implement %asmbinary
|
||||
@@ -1,4 +1,6 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
~ main {
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%zeropage basicsafe
|
||||
|
||||
~ main {
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
~ main {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
~ main {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
~ main {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
~ main {
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%zeropage basicsafe
|
||||
|
||||
~ main {
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
~ main {
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
~ main {
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
~ main {
|
||||
|
||||
|
||||
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.
Executable
+5
@@ -0,0 +1,5 @@
|
||||
c1541 -format examples,01 d64 examples.d64
|
||||
for filename in *.prg; do
|
||||
c1541 examples.d64 -write $filename ${filename%.*}
|
||||
done
|
||||
c1541 examples.d64 -dir
|
||||
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.
@@ -0,0 +1,34 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
; This example computes the first 20 values of the Fibonacci sequence.
|
||||
; It uses the feature that variables that don't have an initialization value,
|
||||
; will retain their previous value over multiple invocations of the program or subroutine.
|
||||
; This is extremely handy for the Fibonacci sequence because it is defined
|
||||
; in terms of 'the next value is the sum of the previous two values'
|
||||
|
||||
~ main {
|
||||
sub start() {
|
||||
c64scr.print("fibonacci sequence\n")
|
||||
fib_setup()
|
||||
for ubyte i in 0 to 20 {
|
||||
c64scr.print_uw(fib_next())
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
}
|
||||
|
||||
sub fib_setup() {
|
||||
; (re)start the sequence
|
||||
main.fib_next.prev=0
|
||||
main.fib_next.current=1
|
||||
}
|
||||
|
||||
sub fib_next() -> uword {
|
||||
uword prev ; no init value so will retain previous value
|
||||
uword current ; no init value so will retain previous value
|
||||
uword new = current + prev
|
||||
prev = current
|
||||
current = new
|
||||
return prev
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
~ main {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
~ main {
|
||||
|
||||
sub start() {
|
||||
|
||||
if A>10 {
|
||||
A=44
|
||||
while true {
|
||||
;derp
|
||||
}
|
||||
} else {
|
||||
|
||||
gameover:
|
||||
goto gameover
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
~ main {
|
||||
const uword width = 30
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
%import c64utils
|
||||
%import c64lib
|
||||
%zeropage basicsafe
|
||||
|
||||
; The classic number guessing game.
|
||||
; This version uses mostly high level subroutine calls and loops.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
~ main {
|
||||
|
||||
ubyte[256] sieve
|
||||
ubyte candidate_prime = 2 ; is increased in the loop
|
||||
|
||||
sub start() {
|
||||
memset(sieve, 256, false) ; clear the sieve, to reset starting situation on subsequent runs
|
||||
|
||||
; calculate primes
|
||||
c64scr.print("prime numbers up to 255:\n\n")
|
||||
ubyte amount=0
|
||||
while true {
|
||||
ubyte prime = find_next_prime()
|
||||
if prime==0
|
||||
break
|
||||
c64scr.print_ub(prime)
|
||||
c64scr.print(", ")
|
||||
amount++
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print("number of primes (expected 54): ")
|
||||
c64scr.print_ub(amount)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
|
||||
sub find_next_prime() -> ubyte {
|
||||
|
||||
while sieve[candidate_prime] {
|
||||
candidate_prime++
|
||||
if candidate_prime==0
|
||||
return 0 ; we wrapped; no more primes available in the sieve
|
||||
}
|
||||
|
||||
; found next one, mark the multiples and return it.
|
||||
sieve[candidate_prime] = true
|
||||
uword multiple = candidate_prime
|
||||
while multiple < len(sieve) {
|
||||
sieve[lsb(multiple)] = true
|
||||
multiple += candidate_prime
|
||||
}
|
||||
return candidate_prime
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@
|
||||
Y++ ; delay for alignment
|
||||
Y++ ; delay for alignment
|
||||
Y++ ; delay for alignment
|
||||
Y++ ; delay for alignment
|
||||
ubyte rasterpos = c64.RASTER
|
||||
if color!=len(colors) {
|
||||
c64.EXTCOL = colors[color]
|
||||
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
%import c64utils
|
||||
%import c64lib
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
~ spritedata $0a00 {
|
||||
@@ -56,9 +57,9 @@ sub irq() {
|
||||
c64.EXTCOL--
|
||||
|
||||
; float up & wobble horizontally
|
||||
for ubyte i in 0 to 14 step 2 {
|
||||
for ubyte @zp i in 0 to 14 step 2 {
|
||||
c64.SPXY[i+1]--
|
||||
ubyte r = rnd()
|
||||
ubyte @zp r = rnd()
|
||||
if r>200
|
||||
c64.SPXY[i]++
|
||||
else if r<40
|
||||
|
||||
@@ -0,0 +1,636 @@
|
||||
; TehTriz - a Tetris clone.
|
||||
;
|
||||
; features:
|
||||
; holding area
|
||||
; wall kick rotations
|
||||
; shows next piece
|
||||
; staged speed increase
|
||||
; some simple sound effects
|
||||
;
|
||||
; @todo show ghost?
|
||||
|
||||
|
||||
~ main {
|
||||
|
||||
const ubyte boardOffsetX = 14
|
||||
const ubyte boardOffsetY = 3
|
||||
const ubyte boardWidth = 10
|
||||
const ubyte boardHeight = 20
|
||||
const ubyte startXpos = boardOffsetX + 3
|
||||
const ubyte startYpos = boardOffsetY - 2
|
||||
uword lines
|
||||
uword score
|
||||
ubyte xpos
|
||||
ubyte ypos
|
||||
ubyte nextBlock
|
||||
ubyte speedlevel
|
||||
ubyte holding
|
||||
ubyte holdingAllowed
|
||||
|
||||
|
||||
sub start() {
|
||||
@(650) = 128 ; set all keys to repeat
|
||||
sound.init()
|
||||
newGame()
|
||||
drawBoard()
|
||||
gameOver()
|
||||
|
||||
newgame:
|
||||
newGame()
|
||||
drawBoard()
|
||||
spawnNextBlock()
|
||||
|
||||
waitkey:
|
||||
if c64.TIME_LO>=(60-4*speedlevel) {
|
||||
c64.TIME_LO = 0
|
||||
|
||||
drawBlock(xpos, ypos, 32) ; hide block
|
||||
if blocklogic.noCollision(xpos, ypos+1) {
|
||||
; slowly move the block down
|
||||
ypos++
|
||||
drawBlock(xpos, ypos, 160) ; show block on new position
|
||||
} else {
|
||||
; block can't move further down!
|
||||
; check if the game area is full, if not, spawn the next block at the top.
|
||||
if blocklogic.isGameOver(xpos, ypos) {
|
||||
gameOver()
|
||||
goto newgame
|
||||
} else {
|
||||
sound.blockrotate()
|
||||
checkForLines()
|
||||
spawnNextBlock()
|
||||
score++
|
||||
}
|
||||
}
|
||||
|
||||
drawScore()
|
||||
}
|
||||
|
||||
ubyte key=c64.GETIN()
|
||||
if key==0 goto waitkey
|
||||
|
||||
keypress(key)
|
||||
|
||||
goto waitkey
|
||||
|
||||
}
|
||||
|
||||
sub keypress(ubyte key) {
|
||||
if key==157 or key==',' {
|
||||
; move left
|
||||
drawBlock(xpos, ypos, 32)
|
||||
if blocklogic.noCollision(xpos-1, ypos) {
|
||||
xpos--
|
||||
}
|
||||
drawBlock(xpos, ypos, 160)
|
||||
}
|
||||
else if key==29 or key=='/' {
|
||||
; move right
|
||||
drawBlock(xpos, ypos, 32)
|
||||
if blocklogic.noCollision(xpos+1, ypos) {
|
||||
xpos++
|
||||
}
|
||||
drawBlock(xpos, ypos, 160)
|
||||
}
|
||||
else if key==17 or key=='.' {
|
||||
; move down faster
|
||||
drawBlock(xpos, ypos, 32)
|
||||
if blocklogic.noCollision(xpos, ypos+1) {
|
||||
ypos++
|
||||
}
|
||||
drawBlock(xpos, ypos, 160)
|
||||
}
|
||||
else if key==145 or key==' ' {
|
||||
; drop down immediately
|
||||
drawBlock(xpos, ypos, 32)
|
||||
ubyte dropypos
|
||||
for dropypos in ypos+1 to boardOffsetY+boardHeight-1 {
|
||||
if not blocklogic.noCollision(xpos, dropypos) {
|
||||
dropypos-- ; the furthest down that still fits
|
||||
break
|
||||
}
|
||||
}
|
||||
if dropypos>ypos {
|
||||
ypos = dropypos
|
||||
sound.blockdrop()
|
||||
drawBlock(xpos, ypos, 160)
|
||||
checkForLines()
|
||||
spawnNextBlock()
|
||||
score++
|
||||
drawScore()
|
||||
}
|
||||
}
|
||||
else if key=='z' { ; no joystick equivalent (there is only 1 fire button)
|
||||
; rotate counter clockwise
|
||||
drawBlock(xpos, ypos, 32)
|
||||
if blocklogic.canRotateCCW(xpos, ypos) {
|
||||
blocklogic.rotateCCW()
|
||||
sound.blockrotate()
|
||||
}
|
||||
else if blocklogic.canRotateCCW(xpos-1, ypos) {
|
||||
xpos--
|
||||
blocklogic.rotateCCW()
|
||||
sound.blockrotate()
|
||||
}
|
||||
else if blocklogic.canRotateCCW(xpos+1, ypos) {
|
||||
xpos++
|
||||
blocklogic.rotateCCW()
|
||||
sound.blockrotate()
|
||||
}
|
||||
drawBlock(xpos, ypos, 160)
|
||||
}
|
||||
else if key=='x' {
|
||||
; rotate clockwise
|
||||
drawBlock(xpos, ypos, 32)
|
||||
if blocklogic.canRotateCW(xpos, ypos) {
|
||||
blocklogic.rotateCW()
|
||||
sound.blockrotate()
|
||||
}
|
||||
else if blocklogic.canRotateCW(xpos-1, ypos) {
|
||||
xpos--
|
||||
blocklogic.rotateCW()
|
||||
sound.blockrotate()
|
||||
}
|
||||
else if blocklogic.canRotateCW(xpos+1, ypos) {
|
||||
xpos++
|
||||
blocklogic.rotateCW()
|
||||
sound.blockrotate()
|
||||
}
|
||||
drawBlock(xpos, ypos, 160)
|
||||
}
|
||||
else if key=='c' {
|
||||
; hold
|
||||
if holdingAllowed {
|
||||
sound.swapping()
|
||||
if holding<7 {
|
||||
drawBlock(xpos, ypos, 32)
|
||||
ubyte newholding = blocklogic.currentBlockNum
|
||||
swapBlock(holding)
|
||||
holding = newholding
|
||||
holdingAllowed = false
|
||||
} else {
|
||||
holding = blocklogic.currentBlockNum
|
||||
drawBlock(xpos, ypos, 32)
|
||||
spawnNextBlock()
|
||||
}
|
||||
drawHoldBlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub checkForLines() {
|
||||
; check if line(s) are full -> flash/clear line(s) + add score + move rest down
|
||||
ubyte[boardHeight] complete_lines
|
||||
ubyte num_lines=0
|
||||
memset(complete_lines, len(complete_lines), 0)
|
||||
for ubyte linepos in boardOffsetY to boardOffsetY+boardHeight-1 {
|
||||
if blocklogic.isLineFull(linepos) {
|
||||
complete_lines[num_lines]=linepos
|
||||
num_lines++
|
||||
for ubyte x in boardOffsetX to boardOffsetX+boardWidth-1
|
||||
c64scr.setcc(x, linepos, 160, 1)
|
||||
}
|
||||
}
|
||||
if num_lines {
|
||||
if num_lines>3
|
||||
sound.lineclear_big()
|
||||
else
|
||||
sound.lineclear()
|
||||
c64.TIME_LO=0
|
||||
while c64.TIME_LO<20 {
|
||||
; slight delay to flash the line
|
||||
}
|
||||
c64.TIME_LO=0
|
||||
for ubyte linepos in complete_lines
|
||||
if linepos and blocklogic.isLineFull(linepos)
|
||||
blocklogic.collapse(linepos)
|
||||
lines += num_lines
|
||||
uword[4] scores = [10, 25, 50, 100] ; can never clear more than 4 lines
|
||||
score += scores[num_lines-1]
|
||||
speedlevel = 1+lsb(lines/10)
|
||||
drawScore()
|
||||
}
|
||||
}
|
||||
|
||||
sub gameOver() {
|
||||
sound.gameover()
|
||||
c64scr.PLOT(7, 7)
|
||||
c64.CHROUT('U')
|
||||
c64scr.print("────────────────────────")
|
||||
c64.CHROUT('I')
|
||||
c64scr.PLOT(7, 8)
|
||||
c64scr.print("│*** g a m e o v e r ***│")
|
||||
c64scr.PLOT(7, 9)
|
||||
c64.CHROUT('J')
|
||||
c64scr.print("────────────────────────")
|
||||
c64.CHROUT('K')
|
||||
|
||||
c64scr.PLOT(7, 18)
|
||||
c64.CHROUT('U')
|
||||
c64scr.print("────────────────────────")
|
||||
c64.CHROUT('I')
|
||||
c64scr.PLOT(7, 19)
|
||||
c64scr.print("│ f1 for new game │")
|
||||
c64scr.PLOT(7, 20)
|
||||
c64.CHROUT('J')
|
||||
c64scr.print("────────────────────────")
|
||||
c64.CHROUT('K')
|
||||
|
||||
while(c64.GETIN()!=133) {
|
||||
; endless loop until user presses F1 to restart the game
|
||||
}
|
||||
}
|
||||
|
||||
sub newGame() {
|
||||
lines = 0
|
||||
score = 0
|
||||
xpos = startXpos
|
||||
ypos = startYpos
|
||||
speedlevel = 1
|
||||
nextBlock = rnd() % 7
|
||||
holding = 255
|
||||
holdingAllowed = true
|
||||
}
|
||||
|
||||
sub swapBlock(ubyte newblock) {
|
||||
c64.TIME_LO = 0
|
||||
blocklogic.newCurrentBlock(newblock)
|
||||
xpos = startXpos
|
||||
ypos = startYpos
|
||||
drawBlock(xpos, ypos, 160)
|
||||
}
|
||||
|
||||
sub spawnNextBlock() {
|
||||
swapBlock(nextBlock)
|
||||
nextBlock = (rnd() + c64.RASTER) % 7
|
||||
drawNextBlock()
|
||||
holdingAllowed = true
|
||||
}
|
||||
|
||||
sub drawBoard() {
|
||||
c64.CLEARSCR()
|
||||
c64.COLOR = 7
|
||||
c64scr.PLOT(1,1)
|
||||
c64scr.print("irmen's")
|
||||
c64scr.PLOT(2,2)
|
||||
c64scr.print("teh▁triz")
|
||||
c64.COLOR = 5
|
||||
c64scr.PLOT(6,4)
|
||||
c64scr.print("hold:")
|
||||
c64scr.PLOT(2,22)
|
||||
c64scr.print("speed: ")
|
||||
c64scr.PLOT(28,3)
|
||||
c64scr.print("next:")
|
||||
c64scr.PLOT(28,10)
|
||||
c64scr.print("lines:")
|
||||
c64scr.PLOT(28,14)
|
||||
c64scr.print("score:")
|
||||
c64.COLOR = 12
|
||||
c64scr.PLOT(27,18)
|
||||
c64scr.print("controls:")
|
||||
c64.COLOR = 11
|
||||
c64scr.PLOT(28,19)
|
||||
c64scr.print(",/ move")
|
||||
c64scr.PLOT(28,20)
|
||||
c64scr.print("zx rotate")
|
||||
c64scr.PLOT(29,21)
|
||||
c64scr.print(". descend")
|
||||
c64scr.PLOT(27,22)
|
||||
c64scr.print("spc drop")
|
||||
c64scr.PLOT(29,23)
|
||||
c64scr.print("c hold")
|
||||
|
||||
c64scr.setcc(boardOffsetX-1, boardOffsetY-2, 255, 0) ; invisible barrier
|
||||
c64scr.setcc(boardOffsetX-1, boardOffsetY-3, 255, 0) ; invisible barrier
|
||||
c64scr.setcc(boardOffsetX+boardWidth, boardOffsetY-2, 255, 0) ; invisible barrier
|
||||
c64scr.setcc(boardOffsetX+boardWidth, boardOffsetY-3, 255, 0) ; invisible barrier
|
||||
|
||||
c64scr.setcc(boardOffsetX-1, boardOffsetY-1, 108, 12)
|
||||
c64scr.setcc(boardOffsetX+boardWidth, boardOffsetY-1, 123, 12)
|
||||
c64scr.setcc(boardOffsetX+boardWidth, boardOffsetY-1, 123, 12)
|
||||
c64scr.setcc(boardOffsetX-1, boardOffsetY+boardHeight, 124, 12)
|
||||
c64scr.setcc(boardOffsetX+boardWidth, boardOffsetY+boardHeight, 126, 12)
|
||||
ubyte i
|
||||
for i in boardOffsetX+boardWidth-1 to boardOffsetX step -1 {
|
||||
c64scr.setcc(i, boardOffsetY-3, 255, 0) ; invisible barrier
|
||||
c64scr.setcc(i, boardOffsetY+boardHeight, 69, 11)
|
||||
}
|
||||
for i in boardOffsetY+boardHeight-1 to boardOffsetY step -1 {
|
||||
c64scr.setcc(boardOffsetX-1, i, 89, 11)
|
||||
c64scr.setcc(boardOffsetX+boardWidth, i, 84, 11)
|
||||
}
|
||||
|
||||
ubyte[5] colors = [6,8,7,5,4]
|
||||
for i in len(colors)-1 to 0 step -1 {
|
||||
for ubyte x in 5 to 0 step -1 {
|
||||
c64scr.setcc(6+x-i, 11+2*i, 102, colors[i])
|
||||
}
|
||||
}
|
||||
drawScore()
|
||||
}
|
||||
|
||||
sub drawScore() {
|
||||
c64.COLOR=1
|
||||
c64scr.PLOT(30,11)
|
||||
c64scr.print_uw(lines)
|
||||
c64scr.PLOT(30,15)
|
||||
c64scr.print_uw(score)
|
||||
c64scr.PLOT(9,22)
|
||||
c64scr.print_ub(speedlevel)
|
||||
}
|
||||
|
||||
sub drawNextBlock() {
|
||||
const ubyte nextBlockXpos = 29
|
||||
const ubyte nextBlockYpos = 5
|
||||
for ubyte x in nextBlockXpos+3 to nextBlockXpos step -1 {
|
||||
c64scr.setcc(x, nextBlockYpos, ' ', 0)
|
||||
c64scr.setcc(x, nextBlockYpos+1, ' ', 0)
|
||||
}
|
||||
|
||||
; reuse the normal block draw routine (because we can't manipulate array pointers yet)
|
||||
ubyte prev = blocklogic.currentBlockNum
|
||||
blocklogic.newCurrentBlock(nextBlock)
|
||||
drawBlock(nextBlockXpos, nextBlockYpos, 160)
|
||||
blocklogic.newCurrentBlock(prev)
|
||||
}
|
||||
|
||||
sub drawHoldBlock() {
|
||||
const ubyte holdBlockXpos = 7
|
||||
const ubyte holdBlockYpos = 6
|
||||
for ubyte x in holdBlockXpos+3 to holdBlockXpos step -1 {
|
||||
c64scr.setcc(x, holdBlockYpos, '@', 0)
|
||||
c64scr.setcc(x, holdBlockYpos+1, '@', 0)
|
||||
}
|
||||
if holding < 7 {
|
||||
; reuse the normal block draw routine (because we can't manipulate array pointers yet)
|
||||
ubyte prev = blocklogic.currentBlockNum
|
||||
blocklogic.newCurrentBlock(holding)
|
||||
drawBlock(holdBlockXpos, holdBlockYpos, 160)
|
||||
blocklogic.newCurrentBlock(prev)
|
||||
}
|
||||
}
|
||||
|
||||
sub drawBlock(ubyte x, ubyte y, ubyte character) {
|
||||
for ubyte i in 15 to 0 step -1 {
|
||||
ubyte c=blocklogic.currentBlock[i]
|
||||
if c
|
||||
c64scr.setcc((i&3)+x, (i/4)+y, character, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
~ blocklogic {
|
||||
|
||||
ubyte currentBlockNum
|
||||
ubyte[16] currentBlock
|
||||
ubyte[16] rotated
|
||||
|
||||
; the 7 tetrominos
|
||||
ubyte[16] blockI = [0,0,0,0, ; cyan ; note: special rotation (around matrix center)
|
||||
3,3,3,3,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
ubyte[16] blockJ = [6,0,0,0, ; blue
|
||||
6,6,6,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
ubyte[16] blockL = [0,0,8,0, ; orange
|
||||
8,8,8,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
ubyte[16] blockO = [0,7,7,0, ; yellow ; note: no rotation (square)
|
||||
0,7,7,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
ubyte[16] blockS = [0,5,5,0, ; green
|
||||
5,5,0,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
ubyte[16] blockT = [0,4,0,0, ; purple
|
||||
4,4,4,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
ubyte[16] blockZ = [2,2,0,0, ; red
|
||||
0,2,2,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0]
|
||||
|
||||
; @todo would be nice to have a pointer type, like so:
|
||||
; uword[7] blocks = [&blockI, &blockJ, &blockL, &blockO, &blockS, &blockT, &blockZ]
|
||||
|
||||
sub newCurrentBlock(ubyte block) {
|
||||
currentBlockNum = block
|
||||
if block==0
|
||||
memcopy(blockI, currentBlock, len(currentBlock))
|
||||
else if block==1
|
||||
memcopy(blockJ, currentBlock, len(currentBlock))
|
||||
else if block==2
|
||||
memcopy(blockL, currentBlock, len(currentBlock))
|
||||
else if block==3
|
||||
memcopy(blockO, currentBlock, len(currentBlock))
|
||||
else if block==4
|
||||
memcopy(blockS, currentBlock, len(currentBlock))
|
||||
else if block==5
|
||||
memcopy(blockT, currentBlock, len(currentBlock))
|
||||
else if block==6
|
||||
memcopy(blockZ, currentBlock, len(currentBlock))
|
||||
}
|
||||
|
||||
sub rotateCW() {
|
||||
; rotates the current block clockwise.
|
||||
if currentBlockNum==0 {
|
||||
; the 'I' block rotates a 4x4 matrix around the center
|
||||
rotated[0] = currentBlock[12]
|
||||
rotated[1] = currentBlock[8]
|
||||
rotated[2] = currentBlock[4]
|
||||
rotated[3] = currentBlock[0]
|
||||
rotated[4] = currentBlock[13]
|
||||
rotated[5] = currentBlock[9]
|
||||
rotated[6] = currentBlock[5]
|
||||
rotated[7] = currentBlock[1]
|
||||
rotated[8] = currentBlock[14]
|
||||
rotated[9] = currentBlock[10]
|
||||
rotated[10] = currentBlock[6]
|
||||
rotated[11] = currentBlock[2]
|
||||
rotated[12] = currentBlock[15]
|
||||
rotated[13] = currentBlock[11]
|
||||
rotated[14] = currentBlock[7]
|
||||
rotated[15] = currentBlock[3]
|
||||
memcopy(rotated, currentBlock, len(currentBlock))
|
||||
}
|
||||
else if currentBlockNum!=3 {
|
||||
; rotate all blocks (except 3, the square) around their center square in a 3x3 matrix
|
||||
memset(rotated, len(rotated), 0)
|
||||
rotated[0] = currentBlock[8]
|
||||
rotated[1] = currentBlock[4]
|
||||
rotated[2] = currentBlock[0]
|
||||
rotated[4] = currentBlock[9]
|
||||
rotated[5] = currentBlock[5]
|
||||
rotated[6] = currentBlock[1]
|
||||
rotated[8] = currentBlock[10]
|
||||
rotated[9] = currentBlock[6]
|
||||
rotated[10] = currentBlock[2]
|
||||
memcopy(rotated, currentBlock, len(currentBlock))
|
||||
}
|
||||
}
|
||||
|
||||
sub rotateCCW() {
|
||||
; rotates the current block counterclockwise.
|
||||
if currentBlockNum==0 {
|
||||
; the 'I' block rotates a 4x4 matrix around the center
|
||||
rotated[0] = currentBlock[3]
|
||||
rotated[1] = currentBlock[7]
|
||||
rotated[2] = currentBlock[11]
|
||||
rotated[3] = currentBlock[15]
|
||||
rotated[4] = currentBlock[2]
|
||||
rotated[5] = currentBlock[6]
|
||||
rotated[6] = currentBlock[10]
|
||||
rotated[7] = currentBlock[14]
|
||||
rotated[8] = currentBlock[1]
|
||||
rotated[9] = currentBlock[5]
|
||||
rotated[10] = currentBlock[9]
|
||||
rotated[11] = currentBlock[13]
|
||||
rotated[12] = currentBlock[0]
|
||||
rotated[13] = currentBlock[4]
|
||||
rotated[14] = currentBlock[8]
|
||||
rotated[15] = currentBlock[12]
|
||||
memcopy(rotated, currentBlock, len(currentBlock))
|
||||
}
|
||||
else if currentBlockNum!=3 {
|
||||
; rotate all blocks (except 3, the square) around their center square in a 3x3 matrix
|
||||
memset(rotated, len(rotated), 0)
|
||||
rotated[0] = currentBlock[2]
|
||||
rotated[1] = currentBlock[6]
|
||||
rotated[2] = currentBlock[10]
|
||||
rotated[4] = currentBlock[1]
|
||||
rotated[5] = currentBlock[5]
|
||||
rotated[6] = currentBlock[9]
|
||||
rotated[8] = currentBlock[0]
|
||||
rotated[9] = currentBlock[4]
|
||||
rotated[10] = currentBlock[8]
|
||||
memcopy(rotated, currentBlock, len(currentBlock))
|
||||
}
|
||||
}
|
||||
|
||||
; For movement checking it is not needed to clamp the x/y coordinates,
|
||||
; because we have to check for brick collisions anyway.
|
||||
; The full play area is bordered by (in)visible characters that will collide.
|
||||
; Collision is determined by reading the screen data directly.
|
||||
|
||||
sub canRotateCW(ubyte xpos, ubyte ypos) -> ubyte {
|
||||
rotateCW()
|
||||
ubyte nocollision = noCollision(xpos, ypos)
|
||||
rotateCCW()
|
||||
return nocollision
|
||||
}
|
||||
|
||||
sub canRotateCCW(ubyte xpos, ubyte ypos) -> ubyte {
|
||||
rotateCCW()
|
||||
ubyte nocollision = noCollision(xpos, ypos)
|
||||
rotateCW()
|
||||
return nocollision
|
||||
}
|
||||
|
||||
sub noCollision(ubyte xpos, ubyte ypos) -> ubyte {
|
||||
for ubyte i in 15 to 0 step -1 {
|
||||
if currentBlock[i] and c64scr.getchr(xpos + (i&3), ypos+i/4)!=32
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sub isGameOver(ubyte xpos, ubyte ypos) -> ubyte {
|
||||
main.drawBlock(xpos, ypos, 32)
|
||||
ubyte result = ypos==main.startYpos and not noCollision(xpos, ypos+1)
|
||||
main.drawBlock(xpos, ypos, 160)
|
||||
return result
|
||||
}
|
||||
|
||||
sub isLineFull(ubyte ypos) -> ubyte {
|
||||
for ubyte x in main.boardOffsetX to main.boardOffsetX+main.boardWidth-1 {
|
||||
if c64scr.getchr(x, ypos)==32
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sub collapse(ubyte ypos) {
|
||||
while(ypos>main.startYpos+1) {
|
||||
for ubyte x in main.boardOffsetX+main.boardWidth-1 to main.boardOffsetX step -1 {
|
||||
ubyte char = c64scr.getchr(x, ypos-1)
|
||||
ubyte color = c64scr.getclr(x, ypos-1)
|
||||
c64scr.setcc(x, ypos, char, color)
|
||||
}
|
||||
ypos--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
~ sound {
|
||||
|
||||
sub init() {
|
||||
c64.MVOL = 15
|
||||
}
|
||||
|
||||
sub blockrotate() {
|
||||
; soft click
|
||||
c64.MVOL = 5
|
||||
c64.AD1 = %00100010
|
||||
c64.SR1 = %00000000
|
||||
c64.FREQ1 = 15600
|
||||
c64.CR1 = %10000000
|
||||
c64.CR1 = %10000001
|
||||
}
|
||||
|
||||
sub blockdrop() {
|
||||
; swish
|
||||
c64.MVOL = 5
|
||||
c64.AD1 = %01010111
|
||||
c64.SR1 = %00000000
|
||||
c64.FREQ1 = 4600
|
||||
c64.CR1 = %10000000
|
||||
c64.CR1 = %10000001
|
||||
}
|
||||
|
||||
sub swapping() {
|
||||
; beep
|
||||
c64.MVOL = 8
|
||||
c64.AD1 = %01010111
|
||||
c64.SR1 = %00000000
|
||||
c64.FREQ1 = 5500
|
||||
c64.CR1 = %00010000
|
||||
c64.CR1 = %00010001
|
||||
}
|
||||
|
||||
sub lineclear() {
|
||||
; explosion
|
||||
c64.MVOL = 15
|
||||
c64.AD1 = %01100110
|
||||
c64.SR1 = %00000000
|
||||
c64.FREQ1 = 1600
|
||||
c64.CR1 = %10000000
|
||||
c64.CR1 = %10000001
|
||||
}
|
||||
|
||||
sub lineclear_big() {
|
||||
; big explosion
|
||||
c64.MVOL = 15
|
||||
c64.AD1 = %01101010
|
||||
c64.SR1 = %00000000
|
||||
c64.FREQ1 = 2600
|
||||
c64.CR1 = %10000000
|
||||
c64.CR1 = %10000001
|
||||
}
|
||||
|
||||
sub gameover() {
|
||||
; buzz
|
||||
c64.MVOL = 15
|
||||
c64.FREQ2 = 600
|
||||
c64.AD2 = %00111010
|
||||
c64.SR2 = %00000000
|
||||
c64.CR2 = %00110000
|
||||
c64.CR2 = %00110001
|
||||
}
|
||||
}
|
||||
+7
-36
@@ -1,41 +1,12 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
~ main {
|
||||
sub start() {
|
||||
; set text color and activate lowercase charset
|
||||
c64.COLOR = 13
|
||||
c64.VMCSB |= 2
|
||||
~ main {
|
||||
|
||||
; use optimized routine to write text
|
||||
c64scr.print("Hello!\n")
|
||||
; @todo see problem in looplabelproblem.p8
|
||||
; @todo compiler error for using literal values other than 0 or 1 with boolean expressions
|
||||
|
||||
; use iteration to write text
|
||||
str question = "How are you?\n"
|
||||
for ubyte char in question
|
||||
c64.CHROUT(char)
|
||||
sub start() {
|
||||
|
||||
; use indexed loop to write characters
|
||||
str bye = "Goodbye!\n"
|
||||
for ubyte c in 0 to len(bye)
|
||||
c64.CHROUT(bye[c])
|
||||
|
||||
|
||||
float clock_seconds = ((mkword(c64.TIME_LO, c64.TIME_MID) as float)
|
||||
+ (c64.TIME_HI as float)*65536.0)
|
||||
/ 60
|
||||
float hours = floor(clock_seconds / 3600)
|
||||
clock_seconds -= hours*3600
|
||||
float minutes = floor(clock_seconds / 60)
|
||||
clock_seconds = floor(clock_seconds - minutes * 60.0)
|
||||
|
||||
c64scr.print("system time in ti$ is ")
|
||||
c64flt.print_f(hours)
|
||||
c64.CHROUT(':')
|
||||
c64flt.print_f(minutes)
|
||||
c64.CHROUT(':')
|
||||
c64flt.print_f(clock_seconds)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -1,5 +1,6 @@
|
||||
%import c64utils
|
||||
%import c64lib
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
~ spritedata $0a00 {
|
||||
@@ -53,9 +54,9 @@
|
||||
angle++
|
||||
c64.MSIGX=0
|
||||
|
||||
for ubyte i in 7 to 0 step -1 {
|
||||
uword x = sin8u(angle*2-i*16) as uword + 50
|
||||
ubyte y = cos8u(angle*3-i*16) / 2 + 70
|
||||
for ubyte @zp i in 7 to 0 step -1 {
|
||||
uword @zp x = sin8u(angle*2-i*16) as uword + 50
|
||||
ubyte @zp y = cos8u(angle*3-i*16) / 2 + 70
|
||||
c64.SPXYW[i] = mkword(lsb(x), y)
|
||||
lsl(c64.MSIGX)
|
||||
if msb(x) c64.MSIGX++
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
org.gradle.caching=true
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+5
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
Vendored
+84
@@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
+3
-1
@@ -1,4 +1,6 @@
|
||||
set PROG8CLASSPATH=./out/production/compiler;./out/production/parser
|
||||
@echo off
|
||||
|
||||
set PROG8CLASSPATH=./out/production/compiler_main;./out/production/parser_main
|
||||
set KOTLINPATH=%USERPROFILE%/.IdeaIC2018.3/config/plugins/Kotlin
|
||||
set LIBJARS=%KOTLINPATH%/lib/kotlin-stdlib.jar;%KOTLINPATH%/lib/kotlin-reflect.jar;./parser/antlr/lib/antlr-runtime-4.7.2.jar
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
PROG8CLASSPATH=./out/production/compiler:./out/production/parser
|
||||
PROG8CLASSPATH=./out/production/compiler_main:./out/production/parser_main
|
||||
KOTLINPATH=${HOME}/.IntelliJIdea2018.3/config/plugins/Kotlin
|
||||
LIBJARS=${KOTLINPATH}/lib/kotlin-stdlib.jar:${KOTLINPATH}/lib/kotlin-reflect.jar:./parser/antlr/lib/antlr-runtime-4.7.2.jar
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
set PROG8CLASSPATH=./out/production/compiler
|
||||
@echo off
|
||||
|
||||
set PROG8CLASSPATH=./out/production/compiler_main
|
||||
set KOTLINPATH=%USERPROFILE%/.IdeaIC2018.3/config/plugins/Kotlin
|
||||
set LIBJARS=%KOTLINPATH%/lib/kotlin-stdlib.jar;%KOTLINPATH%/lib/kotlin-reflect.jar
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
PROG8CLASSPATH=./out/production/compiler
|
||||
PROG8CLASSPATH=./out/production/compiler_main
|
||||
KOTLINPATH=${HOME}/.IntelliJIdea2018.3/config/plugins/Kotlin
|
||||
LIBJARS=${KOTLINPATH}/lib/kotlin-stdlib.jar:${KOTLINPATH}/lib/kotlin-reflect.jar
|
||||
|
||||
|
||||
+11
-4
@@ -10,6 +10,9 @@ NOTES:
|
||||
|
||||
grammar prog8;
|
||||
|
||||
@header {
|
||||
package prog8.parser;
|
||||
}
|
||||
|
||||
LINECOMMENT : [\r\n][ \t]* COMMENT -> channel(HIDDEN);
|
||||
COMMENT : ';' ~[\r\n]* -> channel(HIDDEN) ;
|
||||
@@ -51,6 +54,10 @@ SINGLECHAR :
|
||||
}
|
||||
;
|
||||
|
||||
ZEROPAGE :
|
||||
'@zp'
|
||||
;
|
||||
|
||||
|
||||
module : (modulestatement | EOL)* EOF ;
|
||||
|
||||
@@ -96,15 +103,15 @@ directive :
|
||||
|
||||
directivearg : stringliteral | identifier | integerliteral ;
|
||||
|
||||
vardecl: datatype arrayspec? identifier ;
|
||||
vardecl: datatype ZEROPAGE? arrayspec? identifier ;
|
||||
|
||||
varinitializer : datatype arrayspec? identifier '=' expression ;
|
||||
varinitializer : vardecl '=' expression ;
|
||||
|
||||
constdecl: 'const' varinitializer ;
|
||||
|
||||
memoryvardecl: 'memory' varinitializer;
|
||||
|
||||
datatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'float' | 'str' | 'str_p' | 'str_s' | 'str_ps' ;
|
||||
datatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'float' | 'str' | 'str_s' ;
|
||||
|
||||
arrayspec: '[' expression ']' ;
|
||||
|
||||
@@ -256,7 +263,7 @@ branch_stmt : branchcondition EOL? (statement | statement_block) EOL? else_part?
|
||||
branchcondition: 'if_cs' | 'if_cc' | 'if_eq' | 'if_z' | 'if_ne' | 'if_nz' | 'if_pl' | 'if_pos' | 'if_mi' | 'if_neg' | 'if_vs' | 'if_vc' ;
|
||||
|
||||
|
||||
forloop : 'for' datatype? (register | identifier) 'in' expression EOL? (statement | statement_block) ;
|
||||
forloop : 'for' datatype? ZEROPAGE? (register | identifier) 'in' expression EOL? (statement | statement_block) ;
|
||||
|
||||
whileloop: 'while' expression EOL? (statement | statement_block) ;
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
plugins {
|
||||
id 'antlr'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
antlr('org.antlr:antlr4:4.7.2') {
|
||||
exclude group: 'com.ibm.icu', module: 'icu4j'
|
||||
}
|
||||
}
|
||||
|
||||
compileJava {
|
||||
dependsOn tasks.withType(AntlrTask)
|
||||
}
|
||||
|
||||
generateGrammarSource {
|
||||
outputDirectory = file("src/prog8/parser")
|
||||
arguments += ["-no-listener","-no-visitor"]
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ["${project.projectDir}/src"]
|
||||
}
|
||||
antlr {
|
||||
srcDirs = ["${project.projectDir}/antlr"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
// Generated from /home/irmen/Projects/prog8/parser/antlr/prog8.g4 by ANTLR 4.7.2
|
||||
// Generated from ./parser/antlr/prog8.g4 by ANTLR 4.7.2
|
||||
|
||||
package prog8.parser;
|
||||
|
||||
import org.antlr.v4.runtime.Lexer;
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
@@ -32,9 +34,9 @@ public class prog8Lexer extends Lexer {
|
||||
T__87=88, T__88=89, T__89=90, T__90=91, T__91=92, T__92=93, T__93=94,
|
||||
T__94=95, T__95=96, T__96=97, T__97=98, T__98=99, T__99=100, T__100=101,
|
||||
T__101=102, T__102=103, T__103=104, T__104=105, T__105=106, T__106=107,
|
||||
T__107=108, T__108=109, T__109=110, T__110=111, T__111=112, LINECOMMENT=113,
|
||||
COMMENT=114, WS=115, EOL=116, NAME=117, DEC_INTEGER=118, HEX_INTEGER=119,
|
||||
BIN_INTEGER=120, FLOAT_NUMBER=121, STRING=122, INLINEASMBLOCK=123, SINGLECHAR=124;
|
||||
T__107=108, T__108=109, T__109=110, LINECOMMENT=111, COMMENT=112, WS=113,
|
||||
EOL=114, NAME=115, DEC_INTEGER=116, HEX_INTEGER=117, BIN_INTEGER=118,
|
||||
FLOAT_NUMBER=119, STRING=120, INLINEASMBLOCK=121, SINGLECHAR=122, ZEROPAGE=123;
|
||||
public static String[] channelNames = {
|
||||
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
|
||||
};
|
||||
@@ -58,10 +60,10 @@ public class prog8Lexer extends Lexer {
|
||||
"T__81", "T__82", "T__83", "T__84", "T__85", "T__86", "T__87", "T__88",
|
||||
"T__89", "T__90", "T__91", "T__92", "T__93", "T__94", "T__95", "T__96",
|
||||
"T__97", "T__98", "T__99", "T__100", "T__101", "T__102", "T__103", "T__104",
|
||||
"T__105", "T__106", "T__107", "T__108", "T__109", "T__110", "T__111",
|
||||
"LINECOMMENT", "COMMENT", "WS", "EOL", "NAME", "DEC_INTEGER", "HEX_INTEGER",
|
||||
"BIN_INTEGER", "FLOAT_NUMBER", "FNUMBER", "STRING_ESCAPE_SEQ", "STRING",
|
||||
"INLINEASMBLOCK", "SINGLECHAR"
|
||||
"T__105", "T__106", "T__107", "T__108", "T__109", "LINECOMMENT", "COMMENT",
|
||||
"WS", "EOL", "NAME", "DEC_INTEGER", "HEX_INTEGER", "BIN_INTEGER", "FLOAT_NUMBER",
|
||||
"FNUMBER", "STRING_ESCAPE_SEQ", "STRING", "INLINEASMBLOCK", "SINGLECHAR",
|
||||
"ZEROPAGE"
|
||||
};
|
||||
}
|
||||
public static final String[] ruleNames = makeRuleNames();
|
||||
@@ -71,18 +73,19 @@ public class prog8Lexer extends Lexer {
|
||||
null, "'~'", "':'", "'goto'", "'%output'", "'%launcher'", "'%zeropage'",
|
||||
"'%zpreserved'", "'%address'", "'%import'", "'%breakpoint'", "'%asminclude'",
|
||||
"'%asmbinary'", "'%option'", "','", "'='", "'const'", "'memory'", "'ubyte'",
|
||||
"'byte'", "'uword'", "'word'", "'float'", "'str'", "'str_p'", "'str_s'",
|
||||
"'str_ps'", "'['", "']'", "'+='", "'-='", "'/='", "'*='", "'**='", "'&='",
|
||||
"'|='", "'^='", "'%='", "'<<='", "'>>='", "'++'", "'--'", "'+'", "'-'",
|
||||
"'**'", "'*'", "'/'", "'%'", "'<<'", "'>>'", "'<'", "'>'", "'<='", "'>='",
|
||||
"'=='", "'!='", "'&'", "'^'", "'|'", "'to'", "'step'", "'and'", "'or'",
|
||||
"'xor'", "'not'", "'('", "')'", "'as'", "'@'", "'return'", "'break'",
|
||||
"'continue'", "'.'", "'A'", "'X'", "'Y'", "'AX'", "'AY'", "'XY'", "'Pc'",
|
||||
"'Pz'", "'Pn'", "'Pv'", "'.w'", "'true'", "'false'", "'%asm'", "'sub'",
|
||||
"'->'", "'{'", "'}'", "'asmsub'", "'clobbers'", "'stack'", "'if'", "'else'",
|
||||
"'if_cs'", "'if_cc'", "'if_eq'", "'if_z'", "'if_ne'", "'if_nz'", "'if_pl'",
|
||||
"'if_pos'", "'if_mi'", "'if_neg'", "'if_vs'", "'if_vc'", "'for'", "'in'",
|
||||
"'while'", "'repeat'", "'until'"
|
||||
"'byte'", "'uword'", "'word'", "'float'", "'str'", "'str_s'", "'['",
|
||||
"']'", "'+='", "'-='", "'/='", "'*='", "'**='", "'&='", "'|='", "'^='",
|
||||
"'%='", "'<<='", "'>>='", "'++'", "'--'", "'+'", "'-'", "'**'", "'*'",
|
||||
"'/'", "'%'", "'<<'", "'>>'", "'<'", "'>'", "'<='", "'>='", "'=='", "'!='",
|
||||
"'&'", "'^'", "'|'", "'to'", "'step'", "'and'", "'or'", "'xor'", "'not'",
|
||||
"'('", "')'", "'as'", "'@'", "'return'", "'break'", "'continue'", "'.'",
|
||||
"'A'", "'X'", "'Y'", "'AX'", "'AY'", "'XY'", "'Pc'", "'Pz'", "'Pn'",
|
||||
"'Pv'", "'.w'", "'true'", "'false'", "'%asm'", "'sub'", "'->'", "'{'",
|
||||
"'}'", "'asmsub'", "'clobbers'", "'stack'", "'if'", "'else'", "'if_cs'",
|
||||
"'if_cc'", "'if_eq'", "'if_z'", "'if_ne'", "'if_nz'", "'if_pl'", "'if_pos'",
|
||||
"'if_mi'", "'if_neg'", "'if_vs'", "'if_vc'", "'for'", "'in'", "'while'",
|
||||
"'repeat'", "'until'", null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, "'@zp'"
|
||||
};
|
||||
}
|
||||
private static final String[] _LITERAL_NAMES = makeLiteralNames();
|
||||
@@ -97,9 +100,9 @@ public class prog8Lexer extends Lexer {
|
||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, "LINECOMMENT", "COMMENT", "WS", "EOL",
|
||||
"NAME", "DEC_INTEGER", "HEX_INTEGER", "BIN_INTEGER", "FLOAT_NUMBER",
|
||||
"STRING", "INLINEASMBLOCK", "SINGLECHAR"
|
||||
null, null, null, "LINECOMMENT", "COMMENT", "WS", "EOL", "NAME", "DEC_INTEGER",
|
||||
"HEX_INTEGER", "BIN_INTEGER", "FLOAT_NUMBER", "STRING", "INLINEASMBLOCK",
|
||||
"SINGLECHAR", "ZEROPAGE"
|
||||
};
|
||||
}
|
||||
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
|
||||
@@ -163,13 +166,13 @@ public class prog8Lexer extends Lexer {
|
||||
@Override
|
||||
public void action(RuleContext _localctx, int ruleIndex, int actionIndex) {
|
||||
switch (ruleIndex) {
|
||||
case 123:
|
||||
case 121:
|
||||
STRING_action((RuleContext)_localctx, actionIndex);
|
||||
break;
|
||||
case 124:
|
||||
case 122:
|
||||
INLINEASMBLOCK_action((RuleContext)_localctx, actionIndex);
|
||||
break;
|
||||
case 125:
|
||||
case 123:
|
||||
SINGLECHAR_action((RuleContext)_localctx, actionIndex);
|
||||
break;
|
||||
}
|
||||
@@ -209,7 +212,7 @@ public class prog8Lexer extends Lexer {
|
||||
}
|
||||
|
||||
public static final String _serializedATN =
|
||||
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2~\u036d\b\1\4\2\t"+
|
||||
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2}\u0362\b\1\4\2\t"+
|
||||
"\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+
|
||||
"\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
|
||||
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
|
||||
@@ -222,284 +225,279 @@ public class prog8Lexer extends Lexer {
|
||||
"\4U\tU\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4^\t^\4_\t_\4"+
|
||||
"`\t`\4a\ta\4b\tb\4c\tc\4d\td\4e\te\4f\tf\4g\tg\4h\th\4i\ti\4j\tj\4k\t"+
|
||||
"k\4l\tl\4m\tm\4n\tn\4o\to\4p\tp\4q\tq\4r\tr\4s\ts\4t\tt\4u\tu\4v\tv\4"+
|
||||
"w\tw\4x\tx\4y\ty\4z\tz\4{\t{\4|\t|\4}\t}\4~\t~\4\177\t\177\3\2\3\2\3\3"+
|
||||
"\3\3\3\4\3\4\3\4\3\4\3\4\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3"+
|
||||
"\6\3\6\3\6\3\6\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\b"+
|
||||
"\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\t\3\t\3"+
|
||||
"\t\3\t\3\t\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3\13\3\13\3"+
|
||||
"\13\3\13\3\13\3\13\3\13\3\13\3\13\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f"+
|
||||
"\3\f\3\f\3\f\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\16\3\16\3\16"+
|
||||
"\3\16\3\16\3\16\3\16\3\16\3\17\3\17\3\20\3\20\3\21\3\21\3\21\3\21\3\21"+
|
||||
"\3\21\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\23"+
|
||||
"\3\24\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26"+
|
||||
"\3\26\3\26\3\27\3\27\3\27\3\27\3\27\3\27\3\30\3\30\3\30\3\30\3\31\3\31"+
|
||||
"\3\31\3\31\3\31\3\31\3\32\3\32\3\32\3\32\3\32\3\32\3\33\3\33\3\33\3\33"+
|
||||
"\3\33\3\33\3\33\3\34\3\34\3\35\3\35\3\36\3\36\3\36\3\37\3\37\3\37\3 \3"+
|
||||
" \3 \3!\3!\3!\3\"\3\"\3\"\3\"\3#\3#\3#\3$\3$\3$\3%\3%\3%\3&\3&\3&\3\'"+
|
||||
"\3\'\3\'\3\'\3(\3(\3(\3(\3)\3)\3)\3*\3*\3*\3+\3+\3,\3,\3-\3-\3-\3.\3."+
|
||||
"\3/\3/\3\60\3\60\3\61\3\61\3\61\3\62\3\62\3\62\3\63\3\63\3\64\3\64\3\65"+
|
||||
"\3\65\3\65\3\66\3\66\3\66\3\67\3\67\3\67\38\38\38\39\39\3:\3:\3;\3;\3"+
|
||||
"<\3<\3<\3=\3=\3=\3=\3=\3>\3>\3>\3>\3?\3?\3?\3@\3@\3@\3@\3A\3A\3A\3A\3"+
|
||||
"B\3B\3C\3C\3D\3D\3D\3E\3E\3F\3F\3F\3F\3F\3F\3F\3G\3G\3G\3G\3G\3G\3H\3"+
|
||||
"H\3H\3H\3H\3H\3H\3H\3H\3I\3I\3J\3J\3K\3K\3L\3L\3M\3M\3M\3N\3N\3N\3O\3"+
|
||||
"O\3O\3P\3P\3P\3Q\3Q\3Q\3R\3R\3R\3S\3S\3S\3T\3T\3T\3U\3U\3U\3U\3U\3V\3"+
|
||||
"V\3V\3V\3V\3V\3W\3W\3W\3W\3W\3X\3X\3X\3X\3Y\3Y\3Y\3Z\3Z\3[\3[\3\\\3\\"+
|
||||
"\3\\\3\\\3\\\3\\\3\\\3]\3]\3]\3]\3]\3]\3]\3]\3]\3^\3^\3^\3^\3^\3^\3_\3"+
|
||||
"_\3_\3`\3`\3`\3`\3`\3a\3a\3a\3a\3a\3a\3b\3b\3b\3b\3b\3b\3c\3c\3c\3c\3"+
|
||||
"c\3c\3d\3d\3d\3d\3d\3e\3e\3e\3e\3e\3e\3f\3f\3f\3f\3f\3f\3g\3g\3g\3g\3"+
|
||||
"g\3g\3h\3h\3h\3h\3h\3h\3h\3i\3i\3i\3i\3i\3i\3j\3j\3j\3j\3j\3j\3j\3k\3"+
|
||||
"k\3k\3k\3k\3k\3l\3l\3l\3l\3l\3l\3m\3m\3m\3m\3n\3n\3n\3o\3o\3o\3o\3o\3"+
|
||||
"o\3p\3p\3p\3p\3p\3p\3p\3q\3q\3q\3q\3q\3q\3r\3r\7r\u02fc\nr\fr\16r\u02ff"+
|
||||
"\13r\3r\3r\3r\3r\3s\3s\7s\u0307\ns\fs\16s\u030a\13s\3s\3s\3t\3t\3t\3t"+
|
||||
"\3u\6u\u0313\nu\ru\16u\u0314\3v\3v\7v\u0319\nv\fv\16v\u031c\13v\3w\3w"+
|
||||
"\3w\6w\u0321\nw\rw\16w\u0322\5w\u0325\nw\3x\3x\6x\u0329\nx\rx\16x\u032a"+
|
||||
"\3y\3y\6y\u032f\ny\ry\16y\u0330\3z\3z\3z\5z\u0336\nz\3z\5z\u0339\nz\3"+
|
||||
"{\6{\u033c\n{\r{\16{\u033d\3{\3{\6{\u0342\n{\r{\16{\u0343\5{\u0346\n{"+
|
||||
"\3|\3|\3|\3|\5|\u034c\n|\3}\3}\3}\7}\u0351\n}\f}\16}\u0354\13}\3}\3}\3"+
|
||||
"}\3~\3~\3~\3~\6~\u035d\n~\r~\16~\u035e\3~\3~\3~\3~\3~\3\177\3\177\3\177"+
|
||||
"\5\177\u0369\n\177\3\177\3\177\3\177\3\u035e\2\u0080\3\3\5\4\7\5\t\6\13"+
|
||||
"\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35\20\37\21!\22#\23%\24\'"+
|
||||
"\25)\26+\27-\30/\31\61\32\63\33\65\34\67\359\36;\37= ?!A\"C#E$G%I&K\'"+
|
||||
"M(O)Q*S+U,W-Y.[/]\60_\61a\62c\63e\64g\65i\66k\67m8o9q:s;u<w=y>{?}@\177"+
|
||||
"A\u0081B\u0083C\u0085D\u0087E\u0089F\u008bG\u008dH\u008fI\u0091J\u0093"+
|
||||
"K\u0095L\u0097M\u0099N\u009bO\u009dP\u009fQ\u00a1R\u00a3S\u00a5T\u00a7"+
|
||||
"U\u00a9V\u00abW\u00adX\u00afY\u00b1Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb"+
|
||||
"_\u00bd`\u00bfa\u00c1b\u00c3c\u00c5d\u00c7e\u00c9f\u00cbg\u00cdh\u00cf"+
|
||||
"i\u00d1j\u00d3k\u00d5l\u00d7m\u00d9n\u00dbo\u00ddp\u00dfq\u00e1r\u00e3"+
|
||||
"s\u00e5t\u00e7u\u00e9v\u00ebw\u00edx\u00efy\u00f1z\u00f3{\u00f5\2\u00f7"+
|
||||
"\2\u00f9|\u00fb}\u00fd~\3\2\n\4\2\f\f\17\17\4\2\13\13\"\"\5\2C\\aac|\6"+
|
||||
"\2\62;C\\aac|\5\2\62;CHch\4\2GGgg\4\2--//\6\2\f\f\16\17$$^^\2\u037c\2"+
|
||||
"\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2"+
|
||||
"\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2"+
|
||||
"\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2"+
|
||||
"\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2"+
|
||||
"\2\61\3\2\2\2\2\63\3\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3\2"+
|
||||
"\2\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2\2G\3\2\2\2"+
|
||||
"\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2U"+
|
||||
"\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3\2"+
|
||||
"\2\2\2c\3\2\2\2\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2\2\2k\3\2\2\2\2m\3\2\2\2"+
|
||||
"\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2\2w\3\2\2\2\2y\3\2\2\2\2{"+
|
||||
"\3\2\2\2\2}\3\2\2\2\2\177\3\2\2\2\2\u0081\3\2\2\2\2\u0083\3\2\2\2\2\u0085"+
|
||||
"\3\2\2\2\2\u0087\3\2\2\2\2\u0089\3\2\2\2\2\u008b\3\2\2\2\2\u008d\3\2\2"+
|
||||
"\2\2\u008f\3\2\2\2\2\u0091\3\2\2\2\2\u0093\3\2\2\2\2\u0095\3\2\2\2\2\u0097"+
|
||||
"\3\2\2\2\2\u0099\3\2\2\2\2\u009b\3\2\2\2\2\u009d\3\2\2\2\2\u009f\3\2\2"+
|
||||
"\2\2\u00a1\3\2\2\2\2\u00a3\3\2\2\2\2\u00a5\3\2\2\2\2\u00a7\3\2\2\2\2\u00a9"+
|
||||
"\3\2\2\2\2\u00ab\3\2\2\2\2\u00ad\3\2\2\2\2\u00af\3\2\2\2\2\u00b1\3\2\2"+
|
||||
"\2\2\u00b3\3\2\2\2\2\u00b5\3\2\2\2\2\u00b7\3\2\2\2\2\u00b9\3\2\2\2\2\u00bb"+
|
||||
"\3\2\2\2\2\u00bd\3\2\2\2\2\u00bf\3\2\2\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2"+
|
||||
"\2\2\u00c5\3\2\2\2\2\u00c7\3\2\2\2\2\u00c9\3\2\2\2\2\u00cb\3\2\2\2\2\u00cd"+
|
||||
"\3\2\2\2\2\u00cf\3\2\2\2\2\u00d1\3\2\2\2\2\u00d3\3\2\2\2\2\u00d5\3\2\2"+
|
||||
"\2\2\u00d7\3\2\2\2\2\u00d9\3\2\2\2\2\u00db\3\2\2\2\2\u00dd\3\2\2\2\2\u00df"+
|
||||
"\3\2\2\2\2\u00e1\3\2\2\2\2\u00e3\3\2\2\2\2\u00e5\3\2\2\2\2\u00e7\3\2\2"+
|
||||
"\2\2\u00e9\3\2\2\2\2\u00eb\3\2\2\2\2\u00ed\3\2\2\2\2\u00ef\3\2\2\2\2\u00f1"+
|
||||
"\3\2\2\2\2\u00f3\3\2\2\2\2\u00f9\3\2\2\2\2\u00fb\3\2\2\2\2\u00fd\3\2\2"+
|
||||
"\2\3\u00ff\3\2\2\2\5\u0101\3\2\2\2\7\u0103\3\2\2\2\t\u0108\3\2\2\2\13"+
|
||||
"\u0110\3\2\2\2\r\u011a\3\2\2\2\17\u0124\3\2\2\2\21\u0130\3\2\2\2\23\u0139"+
|
||||
"\3\2\2\2\25\u0141\3\2\2\2\27\u014d\3\2\2\2\31\u0159\3\2\2\2\33\u0164\3"+
|
||||
"\2\2\2\35\u016c\3\2\2\2\37\u016e\3\2\2\2!\u0170\3\2\2\2#\u0176\3\2\2\2"+
|
||||
"%\u017d\3\2\2\2\'\u0183\3\2\2\2)\u0188\3\2\2\2+\u018e\3\2\2\2-\u0193\3"+
|
||||
"\2\2\2/\u0199\3\2\2\2\61\u019d\3\2\2\2\63\u01a3\3\2\2\2\65\u01a9\3\2\2"+
|
||||
"\2\67\u01b0\3\2\2\29\u01b2\3\2\2\2;\u01b4\3\2\2\2=\u01b7\3\2\2\2?\u01ba"+
|
||||
"\3\2\2\2A\u01bd\3\2\2\2C\u01c0\3\2\2\2E\u01c4\3\2\2\2G\u01c7\3\2\2\2I"+
|
||||
"\u01ca\3\2\2\2K\u01cd\3\2\2\2M\u01d0\3\2\2\2O\u01d4\3\2\2\2Q\u01d8\3\2"+
|
||||
"\2\2S\u01db\3\2\2\2U\u01de\3\2\2\2W\u01e0\3\2\2\2Y\u01e2\3\2\2\2[\u01e5"+
|
||||
"\3\2\2\2]\u01e7\3\2\2\2_\u01e9\3\2\2\2a\u01eb\3\2\2\2c\u01ee\3\2\2\2e"+
|
||||
"\u01f1\3\2\2\2g\u01f3\3\2\2\2i\u01f5\3\2\2\2k\u01f8\3\2\2\2m\u01fb\3\2"+
|
||||
"\2\2o\u01fe\3\2\2\2q\u0201\3\2\2\2s\u0203\3\2\2\2u\u0205\3\2\2\2w\u0207"+
|
||||
"\3\2\2\2y\u020a\3\2\2\2{\u020f\3\2\2\2}\u0213\3\2\2\2\177\u0216\3\2\2"+
|
||||
"\2\u0081\u021a\3\2\2\2\u0083\u021e\3\2\2\2\u0085\u0220\3\2\2\2\u0087\u0222"+
|
||||
"\3\2\2\2\u0089\u0225\3\2\2\2\u008b\u0227\3\2\2\2\u008d\u022e\3\2\2\2\u008f"+
|
||||
"\u0234\3\2\2\2\u0091\u023d\3\2\2\2\u0093\u023f\3\2\2\2\u0095\u0241\3\2"+
|
||||
"\2\2\u0097\u0243\3\2\2\2\u0099\u0245\3\2\2\2\u009b\u0248\3\2\2\2\u009d"+
|
||||
"\u024b\3\2\2\2\u009f\u024e\3\2\2\2\u00a1\u0251\3\2\2\2\u00a3\u0254\3\2"+
|
||||
"\2\2\u00a5\u0257\3\2\2\2\u00a7\u025a\3\2\2\2\u00a9\u025d\3\2\2\2\u00ab"+
|
||||
"\u0262\3\2\2\2\u00ad\u0268\3\2\2\2\u00af\u026d\3\2\2\2\u00b1\u0271\3\2"+
|
||||
"\2\2\u00b3\u0274\3\2\2\2\u00b5\u0276\3\2\2\2\u00b7\u0278\3\2\2\2\u00b9"+
|
||||
"\u027f\3\2\2\2\u00bb\u0288\3\2\2\2\u00bd\u028e\3\2\2\2\u00bf\u0291\3\2"+
|
||||
"\2\2\u00c1\u0296\3\2\2\2\u00c3\u029c\3\2\2\2\u00c5\u02a2\3\2\2\2\u00c7"+
|
||||
"\u02a8\3\2\2\2\u00c9\u02ad\3\2\2\2\u00cb\u02b3\3\2\2\2\u00cd\u02b9\3\2"+
|
||||
"\2\2\u00cf\u02bf\3\2\2\2\u00d1\u02c6\3\2\2\2\u00d3\u02cc\3\2\2\2\u00d5"+
|
||||
"\u02d3\3\2\2\2\u00d7\u02d9\3\2\2\2\u00d9\u02df\3\2\2\2\u00db\u02e3\3\2"+
|
||||
"\2\2\u00dd\u02e6\3\2\2\2\u00df\u02ec\3\2\2\2\u00e1\u02f3\3\2\2\2\u00e3"+
|
||||
"\u02f9\3\2\2\2\u00e5\u0304\3\2\2\2\u00e7\u030d\3\2\2\2\u00e9\u0312\3\2"+
|
||||
"\2\2\u00eb\u0316\3\2\2\2\u00ed\u0324\3\2\2\2\u00ef\u0326\3\2\2\2\u00f1"+
|
||||
"\u032c\3\2\2\2\u00f3\u0332\3\2\2\2\u00f5\u033b\3\2\2\2\u00f7\u034b\3\2"+
|
||||
"\2\2\u00f9\u034d\3\2\2\2\u00fb\u0358\3\2\2\2\u00fd\u0365\3\2\2\2\u00ff"+
|
||||
"\u0100\7\u0080\2\2\u0100\4\3\2\2\2\u0101\u0102\7<\2\2\u0102\6\3\2\2\2"+
|
||||
"\u0103\u0104\7i\2\2\u0104\u0105\7q\2\2\u0105\u0106\7v\2\2\u0106\u0107"+
|
||||
"\7q\2\2\u0107\b\3\2\2\2\u0108\u0109\7\'\2\2\u0109\u010a\7q\2\2\u010a\u010b"+
|
||||
"\7w\2\2\u010b\u010c\7v\2\2\u010c\u010d\7r\2\2\u010d\u010e\7w\2\2\u010e"+
|
||||
"\u010f\7v\2\2\u010f\n\3\2\2\2\u0110\u0111\7\'\2\2\u0111\u0112\7n\2\2\u0112"+
|
||||
"\u0113\7c\2\2\u0113\u0114\7w\2\2\u0114\u0115\7p\2\2\u0115\u0116\7e\2\2"+
|
||||
"\u0116\u0117\7j\2\2\u0117\u0118\7g\2\2\u0118\u0119\7t\2\2\u0119\f\3\2"+
|
||||
"\2\2\u011a\u011b\7\'\2\2\u011b\u011c\7|\2\2\u011c\u011d\7g\2\2\u011d\u011e"+
|
||||
"\7t\2\2\u011e\u011f\7q\2\2\u011f\u0120\7r\2\2\u0120\u0121\7c\2\2\u0121"+
|
||||
"\u0122\7i\2\2\u0122\u0123\7g\2\2\u0123\16\3\2\2\2\u0124\u0125\7\'\2\2"+
|
||||
"\u0125\u0126\7|\2\2\u0126\u0127\7r\2\2\u0127\u0128\7t\2\2\u0128\u0129"+
|
||||
"\7g\2\2\u0129\u012a\7u\2\2\u012a\u012b\7g\2\2\u012b\u012c\7t\2\2\u012c"+
|
||||
"\u012d\7x\2\2\u012d\u012e\7g\2\2\u012e\u012f\7f\2\2\u012f\20\3\2\2\2\u0130"+
|
||||
"\u0131\7\'\2\2\u0131\u0132\7c\2\2\u0132\u0133\7f\2\2\u0133\u0134\7f\2"+
|
||||
"\2\u0134\u0135\7t\2\2\u0135\u0136\7g\2\2\u0136\u0137\7u\2\2\u0137\u0138"+
|
||||
"\7u\2\2\u0138\22\3\2\2\2\u0139\u013a\7\'\2\2\u013a\u013b\7k\2\2\u013b"+
|
||||
"\u013c\7o\2\2\u013c\u013d\7r\2\2\u013d\u013e\7q\2\2\u013e\u013f\7t\2\2"+
|
||||
"\u013f\u0140\7v\2\2\u0140\24\3\2\2\2\u0141\u0142\7\'\2\2\u0142\u0143\7"+
|
||||
"d\2\2\u0143\u0144\7t\2\2\u0144\u0145\7g\2\2\u0145\u0146\7c\2\2\u0146\u0147"+
|
||||
"\7m\2\2\u0147\u0148\7r\2\2\u0148\u0149\7q\2\2\u0149\u014a\7k\2\2\u014a"+
|
||||
"\u014b\7p\2\2\u014b\u014c\7v\2\2\u014c\26\3\2\2\2\u014d\u014e\7\'\2\2"+
|
||||
"\u014e\u014f\7c\2\2\u014f\u0150\7u\2\2\u0150\u0151\7o\2\2\u0151\u0152"+
|
||||
"\7k\2\2\u0152\u0153\7p\2\2\u0153\u0154\7e\2\2\u0154\u0155\7n\2\2\u0155"+
|
||||
"\u0156\7w\2\2\u0156\u0157\7f\2\2\u0157\u0158\7g\2\2\u0158\30\3\2\2\2\u0159"+
|
||||
"\u015a\7\'\2\2\u015a\u015b\7c\2\2\u015b\u015c\7u\2\2\u015c\u015d\7o\2"+
|
||||
"\2\u015d\u015e\7d\2\2\u015e\u015f\7k\2\2\u015f\u0160\7p\2\2\u0160\u0161"+
|
||||
"\7c\2\2\u0161\u0162\7t\2\2\u0162\u0163\7{\2\2\u0163\32\3\2\2\2\u0164\u0165"+
|
||||
"\7\'\2\2\u0165\u0166\7q\2\2\u0166\u0167\7r\2\2\u0167\u0168\7v\2\2\u0168"+
|
||||
"\u0169\7k\2\2\u0169\u016a\7q\2\2\u016a\u016b\7p\2\2\u016b\34\3\2\2\2\u016c"+
|
||||
"\u016d\7.\2\2\u016d\36\3\2\2\2\u016e\u016f\7?\2\2\u016f \3\2\2\2\u0170"+
|
||||
"\u0171\7e\2\2\u0171\u0172\7q\2\2\u0172\u0173\7p\2\2\u0173\u0174\7u\2\2"+
|
||||
"\u0174\u0175\7v\2\2\u0175\"\3\2\2\2\u0176\u0177\7o\2\2\u0177\u0178\7g"+
|
||||
"\2\2\u0178\u0179\7o\2\2\u0179\u017a\7q\2\2\u017a\u017b\7t\2\2\u017b\u017c"+
|
||||
"\7{\2\2\u017c$\3\2\2\2\u017d\u017e\7w\2\2\u017e\u017f\7d\2\2\u017f\u0180"+
|
||||
"\7{\2\2\u0180\u0181\7v\2\2\u0181\u0182\7g\2\2\u0182&\3\2\2\2\u0183\u0184"+
|
||||
"\7d\2\2\u0184\u0185\7{\2\2\u0185\u0186\7v\2\2\u0186\u0187\7g\2\2\u0187"+
|
||||
"(\3\2\2\2\u0188\u0189\7w\2\2\u0189\u018a\7y\2\2\u018a\u018b\7q\2\2\u018b"+
|
||||
"\u018c\7t\2\2\u018c\u018d\7f\2\2\u018d*\3\2\2\2\u018e\u018f\7y\2\2\u018f"+
|
||||
"\u0190\7q\2\2\u0190\u0191\7t\2\2\u0191\u0192\7f\2\2\u0192,\3\2\2\2\u0193"+
|
||||
"\u0194\7h\2\2\u0194\u0195\7n\2\2\u0195\u0196\7q\2\2\u0196\u0197\7c\2\2"+
|
||||
"\u0197\u0198\7v\2\2\u0198.\3\2\2\2\u0199\u019a\7u\2\2\u019a\u019b\7v\2"+
|
||||
"\2\u019b\u019c\7t\2\2\u019c\60\3\2\2\2\u019d\u019e\7u\2\2\u019e\u019f"+
|
||||
"\7v\2\2\u019f\u01a0\7t\2\2\u01a0\u01a1\7a\2\2\u01a1\u01a2\7r\2\2\u01a2"+
|
||||
"\62\3\2\2\2\u01a3\u01a4\7u\2\2\u01a4\u01a5\7v\2\2\u01a5\u01a6\7t\2\2\u01a6"+
|
||||
"\u01a7\7a\2\2\u01a7\u01a8\7u\2\2\u01a8\64\3\2\2\2\u01a9\u01aa\7u\2\2\u01aa"+
|
||||
"\u01ab\7v\2\2\u01ab\u01ac\7t\2\2\u01ac\u01ad\7a\2\2\u01ad\u01ae\7r\2\2"+
|
||||
"\u01ae\u01af\7u\2\2\u01af\66\3\2\2\2\u01b0\u01b1\7]\2\2\u01b18\3\2\2\2"+
|
||||
"\u01b2\u01b3\7_\2\2\u01b3:\3\2\2\2\u01b4\u01b5\7-\2\2\u01b5\u01b6\7?\2"+
|
||||
"\2\u01b6<\3\2\2\2\u01b7\u01b8\7/\2\2\u01b8\u01b9\7?\2\2\u01b9>\3\2\2\2"+
|
||||
"\u01ba\u01bb\7\61\2\2\u01bb\u01bc\7?\2\2\u01bc@\3\2\2\2\u01bd\u01be\7"+
|
||||
",\2\2\u01be\u01bf\7?\2\2\u01bfB\3\2\2\2\u01c0\u01c1\7,\2\2\u01c1\u01c2"+
|
||||
"\7,\2\2\u01c2\u01c3\7?\2\2\u01c3D\3\2\2\2\u01c4\u01c5\7(\2\2\u01c5\u01c6"+
|
||||
"\7?\2\2\u01c6F\3\2\2\2\u01c7\u01c8\7~\2\2\u01c8\u01c9\7?\2\2\u01c9H\3"+
|
||||
"\2\2\2\u01ca\u01cb\7`\2\2\u01cb\u01cc\7?\2\2\u01ccJ\3\2\2\2\u01cd\u01ce"+
|
||||
"\7\'\2\2\u01ce\u01cf\7?\2\2\u01cfL\3\2\2\2\u01d0\u01d1\7>\2\2\u01d1\u01d2"+
|
||||
"\7>\2\2\u01d2\u01d3\7?\2\2\u01d3N\3\2\2\2\u01d4\u01d5\7@\2\2\u01d5\u01d6"+
|
||||
"\7@\2\2\u01d6\u01d7\7?\2\2\u01d7P\3\2\2\2\u01d8\u01d9\7-\2\2\u01d9\u01da"+
|
||||
"\7-\2\2\u01daR\3\2\2\2\u01db\u01dc\7/\2\2\u01dc\u01dd\7/\2\2\u01ddT\3"+
|
||||
"\2\2\2\u01de\u01df\7-\2\2\u01dfV\3\2\2\2\u01e0\u01e1\7/\2\2\u01e1X\3\2"+
|
||||
"\2\2\u01e2\u01e3\7,\2\2\u01e3\u01e4\7,\2\2\u01e4Z\3\2\2\2\u01e5\u01e6"+
|
||||
"\7,\2\2\u01e6\\\3\2\2\2\u01e7\u01e8\7\61\2\2\u01e8^\3\2\2\2\u01e9\u01ea"+
|
||||
"\7\'\2\2\u01ea`\3\2\2\2\u01eb\u01ec\7>\2\2\u01ec\u01ed\7>\2\2\u01edb\3"+
|
||||
"\2\2\2\u01ee\u01ef\7@\2\2\u01ef\u01f0\7@\2\2\u01f0d\3\2\2\2\u01f1\u01f2"+
|
||||
"\7>\2\2\u01f2f\3\2\2\2\u01f3\u01f4\7@\2\2\u01f4h\3\2\2\2\u01f5\u01f6\7"+
|
||||
">\2\2\u01f6\u01f7\7?\2\2\u01f7j\3\2\2\2\u01f8\u01f9\7@\2\2\u01f9\u01fa"+
|
||||
"\7?\2\2\u01fal\3\2\2\2\u01fb\u01fc\7?\2\2\u01fc\u01fd\7?\2\2\u01fdn\3"+
|
||||
"\2\2\2\u01fe\u01ff\7#\2\2\u01ff\u0200\7?\2\2\u0200p\3\2\2\2\u0201\u0202"+
|
||||
"\7(\2\2\u0202r\3\2\2\2\u0203\u0204\7`\2\2\u0204t\3\2\2\2\u0205\u0206\7"+
|
||||
"~\2\2\u0206v\3\2\2\2\u0207\u0208\7v\2\2\u0208\u0209\7q\2\2\u0209x\3\2"+
|
||||
"\2\2\u020a\u020b\7u\2\2\u020b\u020c\7v\2\2\u020c\u020d\7g\2\2\u020d\u020e"+
|
||||
"\7r\2\2\u020ez\3\2\2\2\u020f\u0210\7c\2\2\u0210\u0211\7p\2\2\u0211\u0212"+
|
||||
"\7f\2\2\u0212|\3\2\2\2\u0213\u0214\7q\2\2\u0214\u0215\7t\2\2\u0215~\3"+
|
||||
"\2\2\2\u0216\u0217\7z\2\2\u0217\u0218\7q\2\2\u0218\u0219\7t\2\2\u0219"+
|
||||
"\u0080\3\2\2\2\u021a\u021b\7p\2\2\u021b\u021c\7q\2\2\u021c\u021d\7v\2"+
|
||||
"\2\u021d\u0082\3\2\2\2\u021e\u021f\7*\2\2\u021f\u0084\3\2\2\2\u0220\u0221"+
|
||||
"\7+\2\2\u0221\u0086\3\2\2\2\u0222\u0223\7c\2\2\u0223\u0224\7u\2\2\u0224"+
|
||||
"\u0088\3\2\2\2\u0225\u0226\7B\2\2\u0226\u008a\3\2\2\2\u0227\u0228\7t\2"+
|
||||
"\2\u0228\u0229\7g\2\2\u0229\u022a\7v\2\2\u022a\u022b\7w\2\2\u022b\u022c"+
|
||||
"\7t\2\2\u022c\u022d\7p\2\2\u022d\u008c\3\2\2\2\u022e\u022f\7d\2\2\u022f"+
|
||||
"\u0230\7t\2\2\u0230\u0231\7g\2\2\u0231\u0232\7c\2\2\u0232\u0233\7m\2\2"+
|
||||
"\u0233\u008e\3\2\2\2\u0234\u0235\7e\2\2\u0235\u0236\7q\2\2\u0236\u0237"+
|
||||
"\7p\2\2\u0237\u0238\7v\2\2\u0238\u0239\7k\2\2\u0239\u023a\7p\2\2\u023a"+
|
||||
"\u023b\7w\2\2\u023b\u023c\7g\2\2\u023c\u0090\3\2\2\2\u023d\u023e\7\60"+
|
||||
"\2\2\u023e\u0092\3\2\2\2\u023f\u0240\7C\2\2\u0240\u0094\3\2\2\2\u0241"+
|
||||
"\u0242\7Z\2\2\u0242\u0096\3\2\2\2\u0243\u0244\7[\2\2\u0244\u0098\3\2\2"+
|
||||
"\2\u0245\u0246\7C\2\2\u0246\u0247\7Z\2\2\u0247\u009a\3\2\2\2\u0248\u0249"+
|
||||
"\7C\2\2\u0249\u024a\7[\2\2\u024a\u009c\3\2\2\2\u024b\u024c\7Z\2\2\u024c"+
|
||||
"\u024d\7[\2\2\u024d\u009e\3\2\2\2\u024e\u024f\7R\2\2\u024f\u0250\7e\2"+
|
||||
"\2\u0250\u00a0\3\2\2\2\u0251\u0252\7R\2\2\u0252\u0253\7|\2\2\u0253\u00a2"+
|
||||
"\3\2\2\2\u0254\u0255\7R\2\2\u0255\u0256\7p\2\2\u0256\u00a4\3\2\2\2\u0257"+
|
||||
"\u0258\7R\2\2\u0258\u0259\7x\2\2\u0259\u00a6\3\2\2\2\u025a\u025b\7\60"+
|
||||
"\2\2\u025b\u025c\7y\2\2\u025c\u00a8\3\2\2\2\u025d\u025e\7v\2\2\u025e\u025f"+
|
||||
"\7t\2\2\u025f\u0260\7w\2\2\u0260\u0261\7g\2\2\u0261\u00aa\3\2\2\2\u0262"+
|
||||
"\u0263\7h\2\2\u0263\u0264\7c\2\2\u0264\u0265\7n\2\2\u0265\u0266\7u\2\2"+
|
||||
"\u0266\u0267\7g\2\2\u0267\u00ac\3\2\2\2\u0268\u0269\7\'\2\2\u0269\u026a"+
|
||||
"\7c\2\2\u026a\u026b\7u\2\2\u026b\u026c\7o\2\2\u026c\u00ae\3\2\2\2\u026d"+
|
||||
"\u026e\7u\2\2\u026e\u026f\7w\2\2\u026f\u0270\7d\2\2\u0270\u00b0\3\2\2"+
|
||||
"\2\u0271\u0272\7/\2\2\u0272\u0273\7@\2\2\u0273\u00b2\3\2\2\2\u0274\u0275"+
|
||||
"\7}\2\2\u0275\u00b4\3\2\2\2\u0276\u0277\7\177\2\2\u0277\u00b6\3\2\2\2"+
|
||||
"\u0278\u0279\7c\2\2\u0279\u027a\7u\2\2\u027a\u027b\7o\2\2\u027b\u027c"+
|
||||
"\7u\2\2\u027c\u027d\7w\2\2\u027d\u027e\7d\2\2\u027e\u00b8\3\2\2\2\u027f"+
|
||||
"\u0280\7e\2\2\u0280\u0281\7n\2\2\u0281\u0282\7q\2\2\u0282\u0283\7d\2\2"+
|
||||
"\u0283\u0284\7d\2\2\u0284\u0285\7g\2\2\u0285\u0286\7t\2\2\u0286\u0287"+
|
||||
"\7u\2\2\u0287\u00ba\3\2\2\2\u0288\u0289\7u\2\2\u0289\u028a\7v\2\2\u028a"+
|
||||
"\u028b\7c\2\2\u028b\u028c\7e\2\2\u028c\u028d\7m\2\2\u028d\u00bc\3\2\2"+
|
||||
"\2\u028e\u028f\7k\2\2\u028f\u0290\7h\2\2\u0290\u00be\3\2\2\2\u0291\u0292"+
|
||||
"\7g\2\2\u0292\u0293\7n\2\2\u0293\u0294\7u\2\2\u0294\u0295\7g\2\2\u0295"+
|
||||
"\u00c0\3\2\2\2\u0296\u0297\7k\2\2\u0297\u0298\7h\2\2\u0298\u0299\7a\2"+
|
||||
"\2\u0299\u029a\7e\2\2\u029a\u029b\7u\2\2\u029b\u00c2\3\2\2\2\u029c\u029d"+
|
||||
"\7k\2\2\u029d\u029e\7h\2\2\u029e\u029f\7a\2\2\u029f\u02a0\7e\2\2\u02a0"+
|
||||
"\u02a1\7e\2\2\u02a1\u00c4\3\2\2\2\u02a2\u02a3\7k\2\2\u02a3\u02a4\7h\2"+
|
||||
"\2\u02a4\u02a5\7a\2\2\u02a5\u02a6\7g\2\2\u02a6\u02a7\7s\2\2\u02a7\u00c6"+
|
||||
"\3\2\2\2\u02a8\u02a9\7k\2\2\u02a9\u02aa\7h\2\2\u02aa\u02ab\7a\2\2\u02ab"+
|
||||
"\u02ac\7|\2\2\u02ac\u00c8\3\2\2\2\u02ad\u02ae\7k\2\2\u02ae\u02af\7h\2"+
|
||||
"\2\u02af\u02b0\7a\2\2\u02b0\u02b1\7p\2\2\u02b1\u02b2\7g\2\2\u02b2\u00ca"+
|
||||
"\3\2\2\2\u02b3\u02b4\7k\2\2\u02b4\u02b5\7h\2\2\u02b5\u02b6\7a\2\2\u02b6"+
|
||||
"\u02b7\7p\2\2\u02b7\u02b8\7|\2\2\u02b8\u00cc\3\2\2\2\u02b9\u02ba\7k\2"+
|
||||
"\2\u02ba\u02bb\7h\2\2\u02bb\u02bc\7a\2\2\u02bc\u02bd\7r\2\2\u02bd\u02be"+
|
||||
"\7n\2\2\u02be\u00ce\3\2\2\2\u02bf\u02c0\7k\2\2\u02c0\u02c1\7h\2\2\u02c1"+
|
||||
"\u02c2\7a\2\2\u02c2\u02c3\7r\2\2\u02c3\u02c4\7q\2\2\u02c4\u02c5\7u\2\2"+
|
||||
"\u02c5\u00d0\3\2\2\2\u02c6\u02c7\7k\2\2\u02c7\u02c8\7h\2\2\u02c8\u02c9"+
|
||||
"\7a\2\2\u02c9\u02ca\7o\2\2\u02ca\u02cb\7k\2\2\u02cb\u00d2\3\2\2\2\u02cc"+
|
||||
"\u02cd\7k\2\2\u02cd\u02ce\7h\2\2\u02ce\u02cf\7a\2\2\u02cf\u02d0\7p\2\2"+
|
||||
"\u02d0\u02d1\7g\2\2\u02d1\u02d2\7i\2\2\u02d2\u00d4\3\2\2\2\u02d3\u02d4"+
|
||||
"\7k\2\2\u02d4\u02d5\7h\2\2\u02d5\u02d6\7a\2\2\u02d6\u02d7\7x\2\2\u02d7"+
|
||||
"\u02d8\7u\2\2\u02d8\u00d6\3\2\2\2\u02d9\u02da\7k\2\2\u02da\u02db\7h\2"+
|
||||
"\2\u02db\u02dc\7a\2\2\u02dc\u02dd\7x\2\2\u02dd\u02de\7e\2\2\u02de\u00d8"+
|
||||
"\3\2\2\2\u02df\u02e0\7h\2\2\u02e0\u02e1\7q\2\2\u02e1\u02e2\7t\2\2\u02e2"+
|
||||
"\u00da\3\2\2\2\u02e3\u02e4\7k\2\2\u02e4\u02e5\7p\2\2\u02e5\u00dc\3\2\2"+
|
||||
"\2\u02e6\u02e7\7y\2\2\u02e7\u02e8\7j\2\2\u02e8\u02e9\7k\2\2\u02e9\u02ea"+
|
||||
"\7n\2\2\u02ea\u02eb\7g\2\2\u02eb\u00de\3\2\2\2\u02ec\u02ed\7t\2\2\u02ed"+
|
||||
"\u02ee\7g\2\2\u02ee\u02ef\7r\2\2\u02ef\u02f0\7g\2\2\u02f0\u02f1\7c\2\2"+
|
||||
"\u02f1\u02f2\7v\2\2\u02f2\u00e0\3\2\2\2\u02f3\u02f4\7w\2\2\u02f4\u02f5"+
|
||||
"\7p\2\2\u02f5\u02f6\7v\2\2\u02f6\u02f7\7k\2\2\u02f7\u02f8\7n\2\2\u02f8"+
|
||||
"\u00e2\3\2\2\2\u02f9\u02fd\t\2\2\2\u02fa\u02fc\t\3\2\2\u02fb\u02fa\3\2"+
|
||||
"\2\2\u02fc\u02ff\3\2\2\2\u02fd\u02fb\3\2\2\2\u02fd\u02fe\3\2\2\2\u02fe"+
|
||||
"\u0300\3\2\2\2\u02ff\u02fd\3\2\2\2\u0300\u0301\5\u00e5s\2\u0301\u0302"+
|
||||
"\3\2\2\2\u0302\u0303\br\2\2\u0303\u00e4\3\2\2\2\u0304\u0308\7=\2\2\u0305"+
|
||||
"\u0307\n\2\2\2\u0306\u0305\3\2\2\2\u0307\u030a\3\2\2\2\u0308\u0306\3\2"+
|
||||
"\2\2\u0308\u0309\3\2\2\2\u0309\u030b\3\2\2\2\u030a\u0308\3\2\2\2\u030b"+
|
||||
"\u030c\bs\2\2\u030c\u00e6\3\2\2\2\u030d\u030e\t\3\2\2\u030e\u030f\3\2"+
|
||||
"\2\2\u030f\u0310\bt\3\2\u0310\u00e8\3\2\2\2\u0311\u0313\t\2\2\2\u0312"+
|
||||
"\u0311\3\2\2\2\u0313\u0314\3\2\2\2\u0314\u0312\3\2\2\2\u0314\u0315\3\2"+
|
||||
"\2\2\u0315\u00ea\3\2\2\2\u0316\u031a\t\4\2\2\u0317\u0319\t\5\2\2\u0318"+
|
||||
"\u0317\3\2\2\2\u0319\u031c\3\2\2\2\u031a\u0318\3\2\2\2\u031a\u031b\3\2"+
|
||||
"\2\2\u031b\u00ec\3\2\2\2\u031c\u031a\3\2\2\2\u031d\u0325\4\62;\2\u031e"+
|
||||
"\u0320\4\63;\2\u031f\u0321\4\62;\2\u0320\u031f\3\2\2\2\u0321\u0322\3\2"+
|
||||
"\2\2\u0322\u0320\3\2\2\2\u0322\u0323\3\2\2\2\u0323\u0325\3\2\2\2\u0324"+
|
||||
"\u031d\3\2\2\2\u0324\u031e\3\2\2\2\u0325\u00ee\3\2\2\2\u0326\u0328\7&"+
|
||||
"\2\2\u0327\u0329\t\6\2\2\u0328\u0327\3\2\2\2\u0329\u032a\3\2\2\2\u032a"+
|
||||
"\u0328\3\2\2\2\u032a\u032b\3\2\2\2\u032b\u00f0\3\2\2\2\u032c\u032e\7\'"+
|
||||
"\2\2\u032d\u032f\4\62\63\2\u032e\u032d\3\2\2\2\u032f\u0330\3\2\2\2\u0330"+
|
||||
"\u032e\3\2\2\2\u0330\u0331\3\2\2\2\u0331\u00f2\3\2\2\2\u0332\u0338\5\u00f5"+
|
||||
"{\2\u0333\u0335\t\7\2\2\u0334\u0336\t\b\2\2\u0335\u0334\3\2\2\2\u0335"+
|
||||
"\u0336\3\2\2\2\u0336\u0337\3\2\2\2\u0337\u0339\5\u00f5{\2\u0338\u0333"+
|
||||
"\3\2\2\2\u0338\u0339\3\2\2\2\u0339\u00f4\3\2\2\2\u033a\u033c\4\62;\2\u033b"+
|
||||
"\u033a\3\2\2\2\u033c\u033d\3\2\2\2\u033d\u033b\3\2\2\2\u033d\u033e\3\2"+
|
||||
"\2\2\u033e\u0345\3\2\2\2\u033f\u0341\7\60\2\2\u0340\u0342\4\62;\2\u0341"+
|
||||
"\u0340\3\2\2\2\u0342\u0343\3\2\2\2\u0343\u0341\3\2\2\2\u0343\u0344\3\2"+
|
||||
"\2\2\u0344\u0346\3\2\2\2\u0345\u033f\3\2\2\2\u0345\u0346\3\2\2\2\u0346"+
|
||||
"\u00f6\3\2\2\2\u0347\u0348\7^\2\2\u0348\u034c\13\2\2\2\u0349\u034a\7^"+
|
||||
"\2\2\u034a\u034c\5\u00e9u\2\u034b\u0347\3\2\2\2\u034b\u0349\3\2\2\2\u034c"+
|
||||
"\u00f8\3\2\2\2\u034d\u0352\7$\2\2\u034e\u0351\5\u00f7|\2\u034f\u0351\n"+
|
||||
"\t\2\2\u0350\u034e\3\2\2\2\u0350\u034f\3\2\2\2\u0351\u0354\3\2\2\2\u0352"+
|
||||
"\u0350\3\2\2\2\u0352\u0353\3\2\2\2\u0353\u0355\3\2\2\2\u0354\u0352\3\2"+
|
||||
"\2\2\u0355\u0356\7$\2\2\u0356\u0357\b}\4\2\u0357\u00fa\3\2\2\2\u0358\u0359"+
|
||||
"\7}\2\2\u0359\u035a\7}\2\2\u035a\u035c\3\2\2\2\u035b\u035d\13\2\2\2\u035c"+
|
||||
"\u035b\3\2\2\2\u035d\u035e\3\2\2\2\u035e\u035f\3\2\2\2\u035e\u035c\3\2"+
|
||||
"\2\2\u035f\u0360\3\2\2\2\u0360\u0361\7\177\2\2\u0361\u0362\7\177\2\2\u0362"+
|
||||
"\u0363\3\2\2\2\u0363\u0364\b~\5\2\u0364\u00fc\3\2\2\2\u0365\u0368\7)\2"+
|
||||
"\2\u0366\u0369\5\u00f7|\2\u0367\u0369\n\t\2\2\u0368\u0366\3\2\2\2\u0368"+
|
||||
"\u0367\3\2\2\2\u0369\u036a\3\2\2\2\u036a\u036b\7)\2\2\u036b\u036c\b\177"+
|
||||
"\6\2\u036c\u00fe\3\2\2\2\26\2\u02fd\u0308\u0314\u031a\u0322\u0324\u0328"+
|
||||
"\u032a\u0330\u0335\u0338\u033d\u0343\u0345\u034b\u0350\u0352\u035e\u0368"+
|
||||
"\7\2\3\2\b\2\2\3}\2\3~\3\3\177\4";
|
||||
"w\tw\4x\tx\4y\ty\4z\tz\4{\t{\4|\t|\4}\t}\4~\t~\3\2\3\2\3\3\3\3\3\4\3\4"+
|
||||
"\3\4\3\4\3\4\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3\6\3\6\3\6\3"+
|
||||
"\6\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\b"+
|
||||
"\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3"+
|
||||
"\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3\13\3\13\3\13\3\13\3\13"+
|
||||
"\3\13\3\13\3\13\3\13\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3"+
|
||||
"\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\16\3\16\3\16\3\16\3\16\3"+
|
||||
"\16\3\16\3\16\3\17\3\17\3\20\3\20\3\21\3\21\3\21\3\21\3\21\3\21\3\22\3"+
|
||||
"\22\3\22\3\22\3\22\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\23\3\24\3\24\3"+
|
||||
"\24\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\26\3"+
|
||||
"\27\3\27\3\27\3\27\3\27\3\27\3\30\3\30\3\30\3\30\3\31\3\31\3\31\3\31\3"+
|
||||
"\31\3\31\3\32\3\32\3\33\3\33\3\34\3\34\3\34\3\35\3\35\3\35\3\36\3\36\3"+
|
||||
"\36\3\37\3\37\3\37\3 \3 \3 \3 \3!\3!\3!\3\"\3\"\3\"\3#\3#\3#\3$\3$\3$"+
|
||||
"\3%\3%\3%\3%\3&\3&\3&\3&\3\'\3\'\3\'\3(\3(\3(\3)\3)\3*\3*\3+\3+\3+\3,"+
|
||||
"\3,\3-\3-\3.\3.\3/\3/\3/\3\60\3\60\3\60\3\61\3\61\3\62\3\62\3\63\3\63"+
|
||||
"\3\63\3\64\3\64\3\64\3\65\3\65\3\65\3\66\3\66\3\66\3\67\3\67\38\38\39"+
|
||||
"\39\3:\3:\3:\3;\3;\3;\3;\3;\3<\3<\3<\3<\3=\3=\3=\3>\3>\3>\3>\3?\3?\3?"+
|
||||
"\3?\3@\3@\3A\3A\3B\3B\3B\3C\3C\3D\3D\3D\3D\3D\3D\3D\3E\3E\3E\3E\3E\3E"+
|
||||
"\3F\3F\3F\3F\3F\3F\3F\3F\3F\3G\3G\3H\3H\3I\3I\3J\3J\3K\3K\3K\3L\3L\3L"+
|
||||
"\3M\3M\3M\3N\3N\3N\3O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3R\3R\3R\3S\3S\3S\3S\3S"+
|
||||
"\3T\3T\3T\3T\3T\3T\3U\3U\3U\3U\3U\3V\3V\3V\3V\3W\3W\3W\3X\3X\3Y\3Y\3Z"+
|
||||
"\3Z\3Z\3Z\3Z\3Z\3Z\3[\3[\3[\3[\3[\3[\3[\3[\3[\3\\\3\\\3\\\3\\\3\\\3\\"+
|
||||
"\3]\3]\3]\3^\3^\3^\3^\3^\3_\3_\3_\3_\3_\3_\3`\3`\3`\3`\3`\3`\3a\3a\3a"+
|
||||
"\3a\3a\3a\3b\3b\3b\3b\3b\3c\3c\3c\3c\3c\3c\3d\3d\3d\3d\3d\3d\3e\3e\3e"+
|
||||
"\3e\3e\3e\3f\3f\3f\3f\3f\3f\3f\3g\3g\3g\3g\3g\3g\3h\3h\3h\3h\3h\3h\3h"+
|
||||
"\3i\3i\3i\3i\3i\3i\3j\3j\3j\3j\3j\3j\3k\3k\3k\3k\3l\3l\3l\3m\3m\3m\3m"+
|
||||
"\3m\3m\3n\3n\3n\3n\3n\3n\3n\3o\3o\3o\3o\3o\3o\3p\3p\7p\u02ed\np\fp\16"+
|
||||
"p\u02f0\13p\3p\3p\3p\3p\3q\3q\7q\u02f8\nq\fq\16q\u02fb\13q\3q\3q\3r\3"+
|
||||
"r\3r\3r\3s\6s\u0304\ns\rs\16s\u0305\3t\3t\7t\u030a\nt\ft\16t\u030d\13"+
|
||||
"t\3u\3u\3u\6u\u0312\nu\ru\16u\u0313\5u\u0316\nu\3v\3v\6v\u031a\nv\rv\16"+
|
||||
"v\u031b\3w\3w\6w\u0320\nw\rw\16w\u0321\3x\3x\3x\5x\u0327\nx\3x\5x\u032a"+
|
||||
"\nx\3y\6y\u032d\ny\ry\16y\u032e\3y\3y\6y\u0333\ny\ry\16y\u0334\5y\u0337"+
|
||||
"\ny\3z\3z\3z\3z\5z\u033d\nz\3{\3{\3{\7{\u0342\n{\f{\16{\u0345\13{\3{\3"+
|
||||
"{\3{\3|\3|\3|\3|\6|\u034e\n|\r|\16|\u034f\3|\3|\3|\3|\3|\3}\3}\3}\5}\u035a"+
|
||||
"\n}\3}\3}\3}\3~\3~\3~\3~\3\u034f\2\177\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21"+
|
||||
"\n\23\13\25\f\27\r\31\16\33\17\35\20\37\21!\22#\23%\24\'\25)\26+\27-\30"+
|
||||
"/\31\61\32\63\33\65\34\67\359\36;\37= ?!A\"C#E$G%I&K\'M(O)Q*S+U,W-Y.["+
|
||||
"/]\60_\61a\62c\63e\64g\65i\66k\67m8o9q:s;u<w=y>{?}@\177A\u0081B\u0083"+
|
||||
"C\u0085D\u0087E\u0089F\u008bG\u008dH\u008fI\u0091J\u0093K\u0095L\u0097"+
|
||||
"M\u0099N\u009bO\u009dP\u009fQ\u00a1R\u00a3S\u00a5T\u00a7U\u00a9V\u00ab"+
|
||||
"W\u00adX\u00afY\u00b1Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb_\u00bd`\u00bf"+
|
||||
"a\u00c1b\u00c3c\u00c5d\u00c7e\u00c9f\u00cbg\u00cdh\u00cfi\u00d1j\u00d3"+
|
||||
"k\u00d5l\u00d7m\u00d9n\u00dbo\u00ddp\u00dfq\u00e1r\u00e3s\u00e5t\u00e7"+
|
||||
"u\u00e9v\u00ebw\u00edx\u00efy\u00f1\2\u00f3\2\u00f5z\u00f7{\u00f9|\u00fb"+
|
||||
"}\3\2\n\4\2\f\f\17\17\4\2\13\13\"\"\5\2C\\aac|\6\2\62;C\\aac|\5\2\62;"+
|
||||
"CHch\4\2GGgg\4\2--//\6\2\f\f\16\17$$^^\2\u0371\2\3\3\2\2\2\2\5\3\2\2\2"+
|
||||
"\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3"+
|
||||
"\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2\33\3\2\2"+
|
||||
"\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3\2"+
|
||||
"\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2\2\63\3\2"+
|
||||
"\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3\2\2\2\2=\3\2\2\2\2?\3\2"+
|
||||
"\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2\2"+
|
||||
"\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y"+
|
||||
"\3\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3\2\2\2\2c\3\2\2\2\2e\3\2"+
|
||||
"\2\2\2g\3\2\2\2\2i\3\2\2\2\2k\3\2\2\2\2m\3\2\2\2\2o\3\2\2\2\2q\3\2\2\2"+
|
||||
"\2s\3\2\2\2\2u\3\2\2\2\2w\3\2\2\2\2y\3\2\2\2\2{\3\2\2\2\2}\3\2\2\2\2\177"+
|
||||
"\3\2\2\2\2\u0081\3\2\2\2\2\u0083\3\2\2\2\2\u0085\3\2\2\2\2\u0087\3\2\2"+
|
||||
"\2\2\u0089\3\2\2\2\2\u008b\3\2\2\2\2\u008d\3\2\2\2\2\u008f\3\2\2\2\2\u0091"+
|
||||
"\3\2\2\2\2\u0093\3\2\2\2\2\u0095\3\2\2\2\2\u0097\3\2\2\2\2\u0099\3\2\2"+
|
||||
"\2\2\u009b\3\2\2\2\2\u009d\3\2\2\2\2\u009f\3\2\2\2\2\u00a1\3\2\2\2\2\u00a3"+
|
||||
"\3\2\2\2\2\u00a5\3\2\2\2\2\u00a7\3\2\2\2\2\u00a9\3\2\2\2\2\u00ab\3\2\2"+
|
||||
"\2\2\u00ad\3\2\2\2\2\u00af\3\2\2\2\2\u00b1\3\2\2\2\2\u00b3\3\2\2\2\2\u00b5"+
|
||||
"\3\2\2\2\2\u00b7\3\2\2\2\2\u00b9\3\2\2\2\2\u00bb\3\2\2\2\2\u00bd\3\2\2"+
|
||||
"\2\2\u00bf\3\2\2\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2\2\2\u00c5\3\2\2\2\2\u00c7"+
|
||||
"\3\2\2\2\2\u00c9\3\2\2\2\2\u00cb\3\2\2\2\2\u00cd\3\2\2\2\2\u00cf\3\2\2"+
|
||||
"\2\2\u00d1\3\2\2\2\2\u00d3\3\2\2\2\2\u00d5\3\2\2\2\2\u00d7\3\2\2\2\2\u00d9"+
|
||||
"\3\2\2\2\2\u00db\3\2\2\2\2\u00dd\3\2\2\2\2\u00df\3\2\2\2\2\u00e1\3\2\2"+
|
||||
"\2\2\u00e3\3\2\2\2\2\u00e5\3\2\2\2\2\u00e7\3\2\2\2\2\u00e9\3\2\2\2\2\u00eb"+
|
||||
"\3\2\2\2\2\u00ed\3\2\2\2\2\u00ef\3\2\2\2\2\u00f5\3\2\2\2\2\u00f7\3\2\2"+
|
||||
"\2\2\u00f9\3\2\2\2\2\u00fb\3\2\2\2\3\u00fd\3\2\2\2\5\u00ff\3\2\2\2\7\u0101"+
|
||||
"\3\2\2\2\t\u0106\3\2\2\2\13\u010e\3\2\2\2\r\u0118\3\2\2\2\17\u0122\3\2"+
|
||||
"\2\2\21\u012e\3\2\2\2\23\u0137\3\2\2\2\25\u013f\3\2\2\2\27\u014b\3\2\2"+
|
||||
"\2\31\u0157\3\2\2\2\33\u0162\3\2\2\2\35\u016a\3\2\2\2\37\u016c\3\2\2\2"+
|
||||
"!\u016e\3\2\2\2#\u0174\3\2\2\2%\u017b\3\2\2\2\'\u0181\3\2\2\2)\u0186\3"+
|
||||
"\2\2\2+\u018c\3\2\2\2-\u0191\3\2\2\2/\u0197\3\2\2\2\61\u019b\3\2\2\2\63"+
|
||||
"\u01a1\3\2\2\2\65\u01a3\3\2\2\2\67\u01a5\3\2\2\29\u01a8\3\2\2\2;\u01ab"+
|
||||
"\3\2\2\2=\u01ae\3\2\2\2?\u01b1\3\2\2\2A\u01b5\3\2\2\2C\u01b8\3\2\2\2E"+
|
||||
"\u01bb\3\2\2\2G\u01be\3\2\2\2I\u01c1\3\2\2\2K\u01c5\3\2\2\2M\u01c9\3\2"+
|
||||
"\2\2O\u01cc\3\2\2\2Q\u01cf\3\2\2\2S\u01d1\3\2\2\2U\u01d3\3\2\2\2W\u01d6"+
|
||||
"\3\2\2\2Y\u01d8\3\2\2\2[\u01da\3\2\2\2]\u01dc\3\2\2\2_\u01df\3\2\2\2a"+
|
||||
"\u01e2\3\2\2\2c\u01e4\3\2\2\2e\u01e6\3\2\2\2g\u01e9\3\2\2\2i\u01ec\3\2"+
|
||||
"\2\2k\u01ef\3\2\2\2m\u01f2\3\2\2\2o\u01f4\3\2\2\2q\u01f6\3\2\2\2s\u01f8"+
|
||||
"\3\2\2\2u\u01fb\3\2\2\2w\u0200\3\2\2\2y\u0204\3\2\2\2{\u0207\3\2\2\2}"+
|
||||
"\u020b\3\2\2\2\177\u020f\3\2\2\2\u0081\u0211\3\2\2\2\u0083\u0213\3\2\2"+
|
||||
"\2\u0085\u0216\3\2\2\2\u0087\u0218\3\2\2\2\u0089\u021f\3\2\2\2\u008b\u0225"+
|
||||
"\3\2\2\2\u008d\u022e\3\2\2\2\u008f\u0230\3\2\2\2\u0091\u0232\3\2\2\2\u0093"+
|
||||
"\u0234\3\2\2\2\u0095\u0236\3\2\2\2\u0097\u0239\3\2\2\2\u0099\u023c\3\2"+
|
||||
"\2\2\u009b\u023f\3\2\2\2\u009d\u0242\3\2\2\2\u009f\u0245\3\2\2\2\u00a1"+
|
||||
"\u0248\3\2\2\2\u00a3\u024b\3\2\2\2\u00a5\u024e\3\2\2\2\u00a7\u0253\3\2"+
|
||||
"\2\2\u00a9\u0259\3\2\2\2\u00ab\u025e\3\2\2\2\u00ad\u0262\3\2\2\2\u00af"+
|
||||
"\u0265\3\2\2\2\u00b1\u0267\3\2\2\2\u00b3\u0269\3\2\2\2\u00b5\u0270\3\2"+
|
||||
"\2\2\u00b7\u0279\3\2\2\2\u00b9\u027f\3\2\2\2\u00bb\u0282\3\2\2\2\u00bd"+
|
||||
"\u0287\3\2\2\2\u00bf\u028d\3\2\2\2\u00c1\u0293\3\2\2\2\u00c3\u0299\3\2"+
|
||||
"\2\2\u00c5\u029e\3\2\2\2\u00c7\u02a4\3\2\2\2\u00c9\u02aa\3\2\2\2\u00cb"+
|
||||
"\u02b0\3\2\2\2\u00cd\u02b7\3\2\2\2\u00cf\u02bd\3\2\2\2\u00d1\u02c4\3\2"+
|
||||
"\2\2\u00d3\u02ca\3\2\2\2\u00d5\u02d0\3\2\2\2\u00d7\u02d4\3\2\2\2\u00d9"+
|
||||
"\u02d7\3\2\2\2\u00db\u02dd\3\2\2\2\u00dd\u02e4\3\2\2\2\u00df\u02ea\3\2"+
|
||||
"\2\2\u00e1\u02f5\3\2\2\2\u00e3\u02fe\3\2\2\2\u00e5\u0303\3\2\2\2\u00e7"+
|
||||
"\u0307\3\2\2\2\u00e9\u0315\3\2\2\2\u00eb\u0317\3\2\2\2\u00ed\u031d\3\2"+
|
||||
"\2\2\u00ef\u0323\3\2\2\2\u00f1\u032c\3\2\2\2\u00f3\u033c\3\2\2\2\u00f5"+
|
||||
"\u033e\3\2\2\2\u00f7\u0349\3\2\2\2\u00f9\u0356\3\2\2\2\u00fb\u035e\3\2"+
|
||||
"\2\2\u00fd\u00fe\7\u0080\2\2\u00fe\4\3\2\2\2\u00ff\u0100\7<\2\2\u0100"+
|
||||
"\6\3\2\2\2\u0101\u0102\7i\2\2\u0102\u0103\7q\2\2\u0103\u0104\7v\2\2\u0104"+
|
||||
"\u0105\7q\2\2\u0105\b\3\2\2\2\u0106\u0107\7\'\2\2\u0107\u0108\7q\2\2\u0108"+
|
||||
"\u0109\7w\2\2\u0109\u010a\7v\2\2\u010a\u010b\7r\2\2\u010b\u010c\7w\2\2"+
|
||||
"\u010c\u010d\7v\2\2\u010d\n\3\2\2\2\u010e\u010f\7\'\2\2\u010f\u0110\7"+
|
||||
"n\2\2\u0110\u0111\7c\2\2\u0111\u0112\7w\2\2\u0112\u0113\7p\2\2\u0113\u0114"+
|
||||
"\7e\2\2\u0114\u0115\7j\2\2\u0115\u0116\7g\2\2\u0116\u0117\7t\2\2\u0117"+
|
||||
"\f\3\2\2\2\u0118\u0119\7\'\2\2\u0119\u011a\7|\2\2\u011a\u011b\7g\2\2\u011b"+
|
||||
"\u011c\7t\2\2\u011c\u011d\7q\2\2\u011d\u011e\7r\2\2\u011e\u011f\7c\2\2"+
|
||||
"\u011f\u0120\7i\2\2\u0120\u0121\7g\2\2\u0121\16\3\2\2\2\u0122\u0123\7"+
|
||||
"\'\2\2\u0123\u0124\7|\2\2\u0124\u0125\7r\2\2\u0125\u0126\7t\2\2\u0126"+
|
||||
"\u0127\7g\2\2\u0127\u0128\7u\2\2\u0128\u0129\7g\2\2\u0129\u012a\7t\2\2"+
|
||||
"\u012a\u012b\7x\2\2\u012b\u012c\7g\2\2\u012c\u012d\7f\2\2\u012d\20\3\2"+
|
||||
"\2\2\u012e\u012f\7\'\2\2\u012f\u0130\7c\2\2\u0130\u0131\7f\2\2\u0131\u0132"+
|
||||
"\7f\2\2\u0132\u0133\7t\2\2\u0133\u0134\7g\2\2\u0134\u0135\7u\2\2\u0135"+
|
||||
"\u0136\7u\2\2\u0136\22\3\2\2\2\u0137\u0138\7\'\2\2\u0138\u0139\7k\2\2"+
|
||||
"\u0139\u013a\7o\2\2\u013a\u013b\7r\2\2\u013b\u013c\7q\2\2\u013c\u013d"+
|
||||
"\7t\2\2\u013d\u013e\7v\2\2\u013e\24\3\2\2\2\u013f\u0140\7\'\2\2\u0140"+
|
||||
"\u0141\7d\2\2\u0141\u0142\7t\2\2\u0142\u0143\7g\2\2\u0143\u0144\7c\2\2"+
|
||||
"\u0144\u0145\7m\2\2\u0145\u0146\7r\2\2\u0146\u0147\7q\2\2\u0147\u0148"+
|
||||
"\7k\2\2\u0148\u0149\7p\2\2\u0149\u014a\7v\2\2\u014a\26\3\2\2\2\u014b\u014c"+
|
||||
"\7\'\2\2\u014c\u014d\7c\2\2\u014d\u014e\7u\2\2\u014e\u014f\7o\2\2\u014f"+
|
||||
"\u0150\7k\2\2\u0150\u0151\7p\2\2\u0151\u0152\7e\2\2\u0152\u0153\7n\2\2"+
|
||||
"\u0153\u0154\7w\2\2\u0154\u0155\7f\2\2\u0155\u0156\7g\2\2\u0156\30\3\2"+
|
||||
"\2\2\u0157\u0158\7\'\2\2\u0158\u0159\7c\2\2\u0159\u015a\7u\2\2\u015a\u015b"+
|
||||
"\7o\2\2\u015b\u015c\7d\2\2\u015c\u015d\7k\2\2\u015d\u015e\7p\2\2\u015e"+
|
||||
"\u015f\7c\2\2\u015f\u0160\7t\2\2\u0160\u0161\7{\2\2\u0161\32\3\2\2\2\u0162"+
|
||||
"\u0163\7\'\2\2\u0163\u0164\7q\2\2\u0164\u0165\7r\2\2\u0165\u0166\7v\2"+
|
||||
"\2\u0166\u0167\7k\2\2\u0167\u0168\7q\2\2\u0168\u0169\7p\2\2\u0169\34\3"+
|
||||
"\2\2\2\u016a\u016b\7.\2\2\u016b\36\3\2\2\2\u016c\u016d\7?\2\2\u016d \3"+
|
||||
"\2\2\2\u016e\u016f\7e\2\2\u016f\u0170\7q\2\2\u0170\u0171\7p\2\2\u0171"+
|
||||
"\u0172\7u\2\2\u0172\u0173\7v\2\2\u0173\"\3\2\2\2\u0174\u0175\7o\2\2\u0175"+
|
||||
"\u0176\7g\2\2\u0176\u0177\7o\2\2\u0177\u0178\7q\2\2\u0178\u0179\7t\2\2"+
|
||||
"\u0179\u017a\7{\2\2\u017a$\3\2\2\2\u017b\u017c\7w\2\2\u017c\u017d\7d\2"+
|
||||
"\2\u017d\u017e\7{\2\2\u017e\u017f\7v\2\2\u017f\u0180\7g\2\2\u0180&\3\2"+
|
||||
"\2\2\u0181\u0182\7d\2\2\u0182\u0183\7{\2\2\u0183\u0184\7v\2\2\u0184\u0185"+
|
||||
"\7g\2\2\u0185(\3\2\2\2\u0186\u0187\7w\2\2\u0187\u0188\7y\2\2\u0188\u0189"+
|
||||
"\7q\2\2\u0189\u018a\7t\2\2\u018a\u018b\7f\2\2\u018b*\3\2\2\2\u018c\u018d"+
|
||||
"\7y\2\2\u018d\u018e\7q\2\2\u018e\u018f\7t\2\2\u018f\u0190\7f\2\2\u0190"+
|
||||
",\3\2\2\2\u0191\u0192\7h\2\2\u0192\u0193\7n\2\2\u0193\u0194\7q\2\2\u0194"+
|
||||
"\u0195\7c\2\2\u0195\u0196\7v\2\2\u0196.\3\2\2\2\u0197\u0198\7u\2\2\u0198"+
|
||||
"\u0199\7v\2\2\u0199\u019a\7t\2\2\u019a\60\3\2\2\2\u019b\u019c\7u\2\2\u019c"+
|
||||
"\u019d\7v\2\2\u019d\u019e\7t\2\2\u019e\u019f\7a\2\2\u019f\u01a0\7u\2\2"+
|
||||
"\u01a0\62\3\2\2\2\u01a1\u01a2\7]\2\2\u01a2\64\3\2\2\2\u01a3\u01a4\7_\2"+
|
||||
"\2\u01a4\66\3\2\2\2\u01a5\u01a6\7-\2\2\u01a6\u01a7\7?\2\2\u01a78\3\2\2"+
|
||||
"\2\u01a8\u01a9\7/\2\2\u01a9\u01aa\7?\2\2\u01aa:\3\2\2\2\u01ab\u01ac\7"+
|
||||
"\61\2\2\u01ac\u01ad\7?\2\2\u01ad<\3\2\2\2\u01ae\u01af\7,\2\2\u01af\u01b0"+
|
||||
"\7?\2\2\u01b0>\3\2\2\2\u01b1\u01b2\7,\2\2\u01b2\u01b3\7,\2\2\u01b3\u01b4"+
|
||||
"\7?\2\2\u01b4@\3\2\2\2\u01b5\u01b6\7(\2\2\u01b6\u01b7\7?\2\2\u01b7B\3"+
|
||||
"\2\2\2\u01b8\u01b9\7~\2\2\u01b9\u01ba\7?\2\2\u01baD\3\2\2\2\u01bb\u01bc"+
|
||||
"\7`\2\2\u01bc\u01bd\7?\2\2\u01bdF\3\2\2\2\u01be\u01bf\7\'\2\2\u01bf\u01c0"+
|
||||
"\7?\2\2\u01c0H\3\2\2\2\u01c1\u01c2\7>\2\2\u01c2\u01c3\7>\2\2\u01c3\u01c4"+
|
||||
"\7?\2\2\u01c4J\3\2\2\2\u01c5\u01c6\7@\2\2\u01c6\u01c7\7@\2\2\u01c7\u01c8"+
|
||||
"\7?\2\2\u01c8L\3\2\2\2\u01c9\u01ca\7-\2\2\u01ca\u01cb\7-\2\2\u01cbN\3"+
|
||||
"\2\2\2\u01cc\u01cd\7/\2\2\u01cd\u01ce\7/\2\2\u01ceP\3\2\2\2\u01cf\u01d0"+
|
||||
"\7-\2\2\u01d0R\3\2\2\2\u01d1\u01d2\7/\2\2\u01d2T\3\2\2\2\u01d3\u01d4\7"+
|
||||
",\2\2\u01d4\u01d5\7,\2\2\u01d5V\3\2\2\2\u01d6\u01d7\7,\2\2\u01d7X\3\2"+
|
||||
"\2\2\u01d8\u01d9\7\61\2\2\u01d9Z\3\2\2\2\u01da\u01db\7\'\2\2\u01db\\\3"+
|
||||
"\2\2\2\u01dc\u01dd\7>\2\2\u01dd\u01de\7>\2\2\u01de^\3\2\2\2\u01df\u01e0"+
|
||||
"\7@\2\2\u01e0\u01e1\7@\2\2\u01e1`\3\2\2\2\u01e2\u01e3\7>\2\2\u01e3b\3"+
|
||||
"\2\2\2\u01e4\u01e5\7@\2\2\u01e5d\3\2\2\2\u01e6\u01e7\7>\2\2\u01e7\u01e8"+
|
||||
"\7?\2\2\u01e8f\3\2\2\2\u01e9\u01ea\7@\2\2\u01ea\u01eb\7?\2\2\u01ebh\3"+
|
||||
"\2\2\2\u01ec\u01ed\7?\2\2\u01ed\u01ee\7?\2\2\u01eej\3\2\2\2\u01ef\u01f0"+
|
||||
"\7#\2\2\u01f0\u01f1\7?\2\2\u01f1l\3\2\2\2\u01f2\u01f3\7(\2\2\u01f3n\3"+
|
||||
"\2\2\2\u01f4\u01f5\7`\2\2\u01f5p\3\2\2\2\u01f6\u01f7\7~\2\2\u01f7r\3\2"+
|
||||
"\2\2\u01f8\u01f9\7v\2\2\u01f9\u01fa\7q\2\2\u01fat\3\2\2\2\u01fb\u01fc"+
|
||||
"\7u\2\2\u01fc\u01fd\7v\2\2\u01fd\u01fe\7g\2\2\u01fe\u01ff\7r\2\2\u01ff"+
|
||||
"v\3\2\2\2\u0200\u0201\7c\2\2\u0201\u0202\7p\2\2\u0202\u0203\7f\2\2\u0203"+
|
||||
"x\3\2\2\2\u0204\u0205\7q\2\2\u0205\u0206\7t\2\2\u0206z\3\2\2\2\u0207\u0208"+
|
||||
"\7z\2\2\u0208\u0209\7q\2\2\u0209\u020a\7t\2\2\u020a|\3\2\2\2\u020b\u020c"+
|
||||
"\7p\2\2\u020c\u020d\7q\2\2\u020d\u020e\7v\2\2\u020e~\3\2\2\2\u020f\u0210"+
|
||||
"\7*\2\2\u0210\u0080\3\2\2\2\u0211\u0212\7+\2\2\u0212\u0082\3\2\2\2\u0213"+
|
||||
"\u0214\7c\2\2\u0214\u0215\7u\2\2\u0215\u0084\3\2\2\2\u0216\u0217\7B\2"+
|
||||
"\2\u0217\u0086\3\2\2\2\u0218\u0219\7t\2\2\u0219\u021a\7g\2\2\u021a\u021b"+
|
||||
"\7v\2\2\u021b\u021c\7w\2\2\u021c\u021d\7t\2\2\u021d\u021e\7p\2\2\u021e"+
|
||||
"\u0088\3\2\2\2\u021f\u0220\7d\2\2\u0220\u0221\7t\2\2\u0221\u0222\7g\2"+
|
||||
"\2\u0222\u0223\7c\2\2\u0223\u0224\7m\2\2\u0224\u008a\3\2\2\2\u0225\u0226"+
|
||||
"\7e\2\2\u0226\u0227\7q\2\2\u0227\u0228\7p\2\2\u0228\u0229\7v\2\2\u0229"+
|
||||
"\u022a\7k\2\2\u022a\u022b\7p\2\2\u022b\u022c\7w\2\2\u022c\u022d\7g\2\2"+
|
||||
"\u022d\u008c\3\2\2\2\u022e\u022f\7\60\2\2\u022f\u008e\3\2\2\2\u0230\u0231"+
|
||||
"\7C\2\2\u0231\u0090\3\2\2\2\u0232\u0233\7Z\2\2\u0233\u0092\3\2\2\2\u0234"+
|
||||
"\u0235\7[\2\2\u0235\u0094\3\2\2\2\u0236\u0237\7C\2\2\u0237\u0238\7Z\2"+
|
||||
"\2\u0238\u0096\3\2\2\2\u0239\u023a\7C\2\2\u023a\u023b\7[\2\2\u023b\u0098"+
|
||||
"\3\2\2\2\u023c\u023d\7Z\2\2\u023d\u023e\7[\2\2\u023e\u009a\3\2\2\2\u023f"+
|
||||
"\u0240\7R\2\2\u0240\u0241\7e\2\2\u0241\u009c\3\2\2\2\u0242\u0243\7R\2"+
|
||||
"\2\u0243\u0244\7|\2\2\u0244\u009e\3\2\2\2\u0245\u0246\7R\2\2\u0246\u0247"+
|
||||
"\7p\2\2\u0247\u00a0\3\2\2\2\u0248\u0249\7R\2\2\u0249\u024a\7x\2\2\u024a"+
|
||||
"\u00a2\3\2\2\2\u024b\u024c\7\60\2\2\u024c\u024d\7y\2\2\u024d\u00a4\3\2"+
|
||||
"\2\2\u024e\u024f\7v\2\2\u024f\u0250\7t\2\2\u0250\u0251\7w\2\2\u0251\u0252"+
|
||||
"\7g\2\2\u0252\u00a6\3\2\2\2\u0253\u0254\7h\2\2\u0254\u0255\7c\2\2\u0255"+
|
||||
"\u0256\7n\2\2\u0256\u0257\7u\2\2\u0257\u0258\7g\2\2\u0258\u00a8\3\2\2"+
|
||||
"\2\u0259\u025a\7\'\2\2\u025a\u025b\7c\2\2\u025b\u025c\7u\2\2\u025c\u025d"+
|
||||
"\7o\2\2\u025d\u00aa\3\2\2\2\u025e\u025f\7u\2\2\u025f\u0260\7w\2\2\u0260"+
|
||||
"\u0261\7d\2\2\u0261\u00ac\3\2\2\2\u0262\u0263\7/\2\2\u0263\u0264\7@\2"+
|
||||
"\2\u0264\u00ae\3\2\2\2\u0265\u0266\7}\2\2\u0266\u00b0\3\2\2\2\u0267\u0268"+
|
||||
"\7\177\2\2\u0268\u00b2\3\2\2\2\u0269\u026a\7c\2\2\u026a\u026b\7u\2\2\u026b"+
|
||||
"\u026c\7o\2\2\u026c\u026d\7u\2\2\u026d\u026e\7w\2\2\u026e\u026f\7d\2\2"+
|
||||
"\u026f\u00b4\3\2\2\2\u0270\u0271\7e\2\2\u0271\u0272\7n\2\2\u0272\u0273"+
|
||||
"\7q\2\2\u0273\u0274\7d\2\2\u0274\u0275\7d\2\2\u0275\u0276\7g\2\2\u0276"+
|
||||
"\u0277\7t\2\2\u0277\u0278\7u\2\2\u0278\u00b6\3\2\2\2\u0279\u027a\7u\2"+
|
||||
"\2\u027a\u027b\7v\2\2\u027b\u027c\7c\2\2\u027c\u027d\7e\2\2\u027d\u027e"+
|
||||
"\7m\2\2\u027e\u00b8\3\2\2\2\u027f\u0280\7k\2\2\u0280\u0281\7h\2\2\u0281"+
|
||||
"\u00ba\3\2\2\2\u0282\u0283\7g\2\2\u0283\u0284\7n\2\2\u0284\u0285\7u\2"+
|
||||
"\2\u0285\u0286\7g\2\2\u0286\u00bc\3\2\2\2\u0287\u0288\7k\2\2\u0288\u0289"+
|
||||
"\7h\2\2\u0289\u028a\7a\2\2\u028a\u028b\7e\2\2\u028b\u028c\7u\2\2\u028c"+
|
||||
"\u00be\3\2\2\2\u028d\u028e\7k\2\2\u028e\u028f\7h\2\2\u028f\u0290\7a\2"+
|
||||
"\2\u0290\u0291\7e\2\2\u0291\u0292\7e\2\2\u0292\u00c0\3\2\2\2\u0293\u0294"+
|
||||
"\7k\2\2\u0294\u0295\7h\2\2\u0295\u0296\7a\2\2\u0296\u0297\7g\2\2\u0297"+
|
||||
"\u0298\7s\2\2\u0298\u00c2\3\2\2\2\u0299\u029a\7k\2\2\u029a\u029b\7h\2"+
|
||||
"\2\u029b\u029c\7a\2\2\u029c\u029d\7|\2\2\u029d\u00c4\3\2\2\2\u029e\u029f"+
|
||||
"\7k\2\2\u029f\u02a0\7h\2\2\u02a0\u02a1\7a\2\2\u02a1\u02a2\7p\2\2\u02a2"+
|
||||
"\u02a3\7g\2\2\u02a3\u00c6\3\2\2\2\u02a4\u02a5\7k\2\2\u02a5\u02a6\7h\2"+
|
||||
"\2\u02a6\u02a7\7a\2\2\u02a7\u02a8\7p\2\2\u02a8\u02a9\7|\2\2\u02a9\u00c8"+
|
||||
"\3\2\2\2\u02aa\u02ab\7k\2\2\u02ab\u02ac\7h\2\2\u02ac\u02ad\7a\2\2\u02ad"+
|
||||
"\u02ae\7r\2\2\u02ae\u02af\7n\2\2\u02af\u00ca\3\2\2\2\u02b0\u02b1\7k\2"+
|
||||
"\2\u02b1\u02b2\7h\2\2\u02b2\u02b3\7a\2\2\u02b3\u02b4\7r\2\2\u02b4\u02b5"+
|
||||
"\7q\2\2\u02b5\u02b6\7u\2\2\u02b6\u00cc\3\2\2\2\u02b7\u02b8\7k\2\2\u02b8"+
|
||||
"\u02b9\7h\2\2\u02b9\u02ba\7a\2\2\u02ba\u02bb\7o\2\2\u02bb\u02bc\7k\2\2"+
|
||||
"\u02bc\u00ce\3\2\2\2\u02bd\u02be\7k\2\2\u02be\u02bf\7h\2\2\u02bf\u02c0"+
|
||||
"\7a\2\2\u02c0\u02c1\7p\2\2\u02c1\u02c2\7g\2\2\u02c2\u02c3\7i\2\2\u02c3"+
|
||||
"\u00d0\3\2\2\2\u02c4\u02c5\7k\2\2\u02c5\u02c6\7h\2\2\u02c6\u02c7\7a\2"+
|
||||
"\2\u02c7\u02c8\7x\2\2\u02c8\u02c9\7u\2\2\u02c9\u00d2\3\2\2\2\u02ca\u02cb"+
|
||||
"\7k\2\2\u02cb\u02cc\7h\2\2\u02cc\u02cd\7a\2\2\u02cd\u02ce\7x\2\2\u02ce"+
|
||||
"\u02cf\7e\2\2\u02cf\u00d4\3\2\2\2\u02d0\u02d1\7h\2\2\u02d1\u02d2\7q\2"+
|
||||
"\2\u02d2\u02d3\7t\2\2\u02d3\u00d6\3\2\2\2\u02d4\u02d5\7k\2\2\u02d5\u02d6"+
|
||||
"\7p\2\2\u02d6\u00d8\3\2\2\2\u02d7\u02d8\7y\2\2\u02d8\u02d9\7j\2\2\u02d9"+
|
||||
"\u02da\7k\2\2\u02da\u02db\7n\2\2\u02db\u02dc\7g\2\2\u02dc\u00da\3\2\2"+
|
||||
"\2\u02dd\u02de\7t\2\2\u02de\u02df\7g\2\2\u02df\u02e0\7r\2\2\u02e0\u02e1"+
|
||||
"\7g\2\2\u02e1\u02e2\7c\2\2\u02e2\u02e3\7v\2\2\u02e3\u00dc\3\2\2\2\u02e4"+
|
||||
"\u02e5\7w\2\2\u02e5\u02e6\7p\2\2\u02e6\u02e7\7v\2\2\u02e7\u02e8\7k\2\2"+
|
||||
"\u02e8\u02e9\7n\2\2\u02e9\u00de\3\2\2\2\u02ea\u02ee\t\2\2\2\u02eb\u02ed"+
|
||||
"\t\3\2\2\u02ec\u02eb\3\2\2\2\u02ed\u02f0\3\2\2\2\u02ee\u02ec\3\2\2\2\u02ee"+
|
||||
"\u02ef\3\2\2\2\u02ef\u02f1\3\2\2\2\u02f0\u02ee\3\2\2\2\u02f1\u02f2\5\u00e1"+
|
||||
"q\2\u02f2\u02f3\3\2\2\2\u02f3\u02f4\bp\2\2\u02f4\u00e0\3\2\2\2\u02f5\u02f9"+
|
||||
"\7=\2\2\u02f6\u02f8\n\2\2\2\u02f7\u02f6\3\2\2\2\u02f8\u02fb\3\2\2\2\u02f9"+
|
||||
"\u02f7\3\2\2\2\u02f9\u02fa\3\2\2\2\u02fa\u02fc\3\2\2\2\u02fb\u02f9\3\2"+
|
||||
"\2\2\u02fc\u02fd\bq\2\2\u02fd\u00e2\3\2\2\2\u02fe\u02ff\t\3\2\2\u02ff"+
|
||||
"\u0300\3\2\2\2\u0300\u0301\br\3\2\u0301\u00e4\3\2\2\2\u0302\u0304\t\2"+
|
||||
"\2\2\u0303\u0302\3\2\2\2\u0304\u0305\3\2\2\2\u0305\u0303\3\2\2\2\u0305"+
|
||||
"\u0306\3\2\2\2\u0306\u00e6\3\2\2\2\u0307\u030b\t\4\2\2\u0308\u030a\t\5"+
|
||||
"\2\2\u0309\u0308\3\2\2\2\u030a\u030d\3\2\2\2\u030b\u0309\3\2\2\2\u030b"+
|
||||
"\u030c\3\2\2\2\u030c\u00e8\3\2\2\2\u030d\u030b\3\2\2\2\u030e\u0316\4\62"+
|
||||
";\2\u030f\u0311\4\63;\2\u0310\u0312\4\62;\2\u0311\u0310\3\2\2\2\u0312"+
|
||||
"\u0313\3\2\2\2\u0313\u0311\3\2\2\2\u0313\u0314\3\2\2\2\u0314\u0316\3\2"+
|
||||
"\2\2\u0315\u030e\3\2\2\2\u0315\u030f\3\2\2\2\u0316\u00ea\3\2\2\2\u0317"+
|
||||
"\u0319\7&\2\2\u0318\u031a\t\6\2\2\u0319\u0318\3\2\2\2\u031a\u031b\3\2"+
|
||||
"\2\2\u031b\u0319\3\2\2\2\u031b\u031c\3\2\2\2\u031c\u00ec\3\2\2\2\u031d"+
|
||||
"\u031f\7\'\2\2\u031e\u0320\4\62\63\2\u031f\u031e\3\2\2\2\u0320\u0321\3"+
|
||||
"\2\2\2\u0321\u031f\3\2\2\2\u0321\u0322\3\2\2\2\u0322\u00ee\3\2\2\2\u0323"+
|
||||
"\u0329\5\u00f1y\2\u0324\u0326\t\7\2\2\u0325\u0327\t\b\2\2\u0326\u0325"+
|
||||
"\3\2\2\2\u0326\u0327\3\2\2\2\u0327\u0328\3\2\2\2\u0328\u032a\5\u00f1y"+
|
||||
"\2\u0329\u0324\3\2\2\2\u0329\u032a\3\2\2\2\u032a\u00f0\3\2\2\2\u032b\u032d"+
|
||||
"\4\62;\2\u032c\u032b\3\2\2\2\u032d\u032e\3\2\2\2\u032e\u032c\3\2\2\2\u032e"+
|
||||
"\u032f\3\2\2\2\u032f\u0336\3\2\2\2\u0330\u0332\7\60\2\2\u0331\u0333\4"+
|
||||
"\62;\2\u0332\u0331\3\2\2\2\u0333\u0334\3\2\2\2\u0334\u0332\3\2\2\2\u0334"+
|
||||
"\u0335\3\2\2\2\u0335\u0337\3\2\2\2\u0336\u0330\3\2\2\2\u0336\u0337\3\2"+
|
||||
"\2\2\u0337\u00f2\3\2\2\2\u0338\u0339\7^\2\2\u0339\u033d\13\2\2\2\u033a"+
|
||||
"\u033b\7^\2\2\u033b\u033d\5\u00e5s\2\u033c\u0338\3\2\2\2\u033c\u033a\3"+
|
||||
"\2\2\2\u033d\u00f4\3\2\2\2\u033e\u0343\7$\2\2\u033f\u0342\5\u00f3z\2\u0340"+
|
||||
"\u0342\n\t\2\2\u0341\u033f\3\2\2\2\u0341\u0340\3\2\2\2\u0342\u0345\3\2"+
|
||||
"\2\2\u0343\u0341\3\2\2\2\u0343\u0344\3\2\2\2\u0344\u0346\3\2\2\2\u0345"+
|
||||
"\u0343\3\2\2\2\u0346\u0347\7$\2\2\u0347\u0348\b{\4\2\u0348\u00f6\3\2\2"+
|
||||
"\2\u0349\u034a\7}\2\2\u034a\u034b\7}\2\2\u034b\u034d\3\2\2\2\u034c\u034e"+
|
||||
"\13\2\2\2\u034d\u034c\3\2\2\2\u034e\u034f\3\2\2\2\u034f\u0350\3\2\2\2"+
|
||||
"\u034f\u034d\3\2\2\2\u0350\u0351\3\2\2\2\u0351\u0352\7\177\2\2\u0352\u0353"+
|
||||
"\7\177\2\2\u0353\u0354\3\2\2\2\u0354\u0355\b|\5\2\u0355\u00f8\3\2\2\2"+
|
||||
"\u0356\u0359\7)\2\2\u0357\u035a\5\u00f3z\2\u0358\u035a\n\t\2\2\u0359\u0357"+
|
||||
"\3\2\2\2\u0359\u0358\3\2\2\2\u035a\u035b\3\2\2\2\u035b\u035c\7)\2\2\u035c"+
|
||||
"\u035d\b}\6\2\u035d\u00fa\3\2\2\2\u035e\u035f\7B\2\2\u035f\u0360\7|\2"+
|
||||
"\2\u0360\u0361\7r\2\2\u0361\u00fc\3\2\2\2\26\2\u02ee\u02f9\u0305\u030b"+
|
||||
"\u0313\u0315\u0319\u031b\u0321\u0326\u0329\u032e\u0334\u0336\u033c\u0341"+
|
||||
"\u0343\u034f\u0359\7\2\3\2\b\2\2\3{\2\3|\3\3}\4";
|
||||
public static final ATN _ATN =
|
||||
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
|
||||
static {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id="prog8" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.idea" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/docs/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/out" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,2 @@
|
||||
include ':parser'
|
||||
include ':compiler'
|
||||
Reference in new issue
Block a user