mirror of
https://github.com/irmen/prog8.git
synced 2026-10-23 12:02:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4615591c9 | ||
|
|
25e3b599e7 | ||
|
|
5502a3e3ee | ||
|
|
62ceace941 | ||
|
|
7114d3193c | ||
|
|
f6bc69139d | ||
|
|
f3fc2fe523 | ||
|
|
1e045b6a62 | ||
|
|
747c9604dd | ||
|
|
1e5b2e0be3 | ||
|
|
0820716e7b | ||
|
|
191707cd37 | ||
|
|
223bab21aa | ||
|
|
563122ac92 | ||
|
|
bc9d00922e | ||
|
|
d9d83248fe | ||
|
|
f2397527f1 | ||
|
|
bf3caaefe1 | ||
|
|
1aaf854ef7 | ||
|
|
ce40f6f862 | ||
|
|
a349599943 | ||
|
|
ac7faa8d25 | ||
|
|
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 |
No files matched your search
@@ -109,3 +109,6 @@ 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:
|
||||
|
||||

|
||||
+10
-5
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm" version "1.3.20"
|
||||
id "org.jetbrains.kotlin.jvm" version "1.3.21"
|
||||
id 'application'
|
||||
}
|
||||
|
||||
@@ -8,12 +8,15 @@ repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
def kotlinVersion = '1.3.20'
|
||||
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'
|
||||
@@ -61,8 +64,10 @@ task fatJar(type: Jar) {
|
||||
attributes 'Main-Class': 'prog8.CompilerMainKt'
|
||||
}
|
||||
archiveBaseName = 'prog8compiler'
|
||||
destinationDir = rootProject.projectDir
|
||||
from { project.configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) } }
|
||||
destinationDirectory = rootProject.projectDir
|
||||
from {
|
||||
project.configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
with jar
|
||||
}
|
||||
// build.finalizedBy(fatJar)
|
||||
// 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
|
||||
@@ -235,7 +235,7 @@ asmsub GETIN () -> clobbers(X,Y) -> (ubyte @ A) = $FFE4 ; (via 810 ($32A))
|
||||
asmsub CLALL () -> clobbers(A,X) -> () = $FFE7 ; (via 812 ($32C)) close all files
|
||||
asmsub UDTIM () -> clobbers(A,X) -> () = $FFEA ; update the software clock
|
||||
asmsub SCREEN () -> clobbers() -> (ubyte @ X, ubyte @ Y) = $FFED ; read number of screen rows and columns
|
||||
asmsub PLOT (ubyte dir @ Pc, ubyte col @ Y, ubyte row @ X) -> clobbers() -> (ubyte @ X, ubyte @ Y) = $FFF0 ; read/set position of cursor on screen. See c64scr.PLOT for a 'safe' wrapper that preserves X.
|
||||
asmsub PLOT (ubyte dir @ Pc, ubyte col @ Y, ubyte row @ X) -> clobbers() -> (ubyte @ X, ubyte @ Y) = $FFF0 ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
|
||||
asmsub IOBASE () -> clobbers() -> (uword @ XY) = $FFF3 ; read base address of I/O devices
|
||||
|
||||
; ---- end of C64 kernal routines ----
|
||||
|
||||
+257
-192
@@ -54,40 +54,39 @@ asmsub ubyte2hex (ubyte value @ A) -> clobbers() -> (ubyte @ A, ubyte @ Y) {
|
||||
pha
|
||||
and #$0f
|
||||
tax
|
||||
ldy hex_digits,x
|
||||
ldy _hex_digits,x
|
||||
pla
|
||||
lsr a
|
||||
lsr a
|
||||
lsr a
|
||||
lsr a
|
||||
tax
|
||||
lda hex_digits,x
|
||||
lda _hex_digits,x
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
rts
|
||||
|
||||
hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
|
||||
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
str word2hex_output = "1234" ; 0-terminated, to make printing easier
|
||||
asmsub uword2hex (uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string into memory 'word2hex_output'
|
||||
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
|
||||
%asm {{
|
||||
sta c64.SCRATCH_ZPREG
|
||||
tya
|
||||
jsr ubyte2hex
|
||||
sta word2hex_output
|
||||
sty word2hex_output+1
|
||||
sta output
|
||||
sty output+1
|
||||
lda c64.SCRATCH_ZPREG
|
||||
jsr ubyte2hex
|
||||
sta word2hex_output+2
|
||||
sty word2hex_output+3
|
||||
sta output+2
|
||||
sty output+3
|
||||
rts
|
||||
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
|
||||
}}
|
||||
}
|
||||
|
||||
ubyte[3] word2bcd_bcdbuff = [0, 0, 0]
|
||||
asmsub uword2bcd (uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
; Convert an 16 bit binary value to BCD
|
||||
;
|
||||
@@ -99,47 +98,55 @@ 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
|
||||
sta word2bcd_bcdbuff+0
|
||||
sta word2bcd_bcdbuff+1
|
||||
sta word2bcd_bcdbuff+2
|
||||
sta bcdbuff+0
|
||||
sta bcdbuff+1
|
||||
sta bcdbuff+2
|
||||
ldy #16 ; the number of source bits
|
||||
|
||||
- asl c64.SCRATCH_ZPB1 ; shift out one bit
|
||||
rol c64.SCRATCH_ZPREG
|
||||
lda word2bcd_bcdbuff+0 ; and add into result
|
||||
adc word2bcd_bcdbuff+0
|
||||
sta word2bcd_bcdbuff+0
|
||||
lda word2bcd_bcdbuff+1 ; propagating any carry
|
||||
adc word2bcd_bcdbuff+1
|
||||
sta word2bcd_bcdbuff+1
|
||||
lda word2bcd_bcdbuff+2 ; ... thru whole result
|
||||
adc word2bcd_bcdbuff+2
|
||||
sta word2bcd_bcdbuff+2
|
||||
lda bcdbuff+0 ; and add into result
|
||||
adc bcdbuff+0
|
||||
sta bcdbuff+0
|
||||
lda bcdbuff+1 ; propagating any carry
|
||||
adc bcdbuff+1
|
||||
sta bcdbuff+1
|
||||
lda bcdbuff+2 ; ... thru whole result
|
||||
adc bcdbuff+2
|
||||
sta bcdbuff+2
|
||||
dey ; and repeat for next bit
|
||||
bne -
|
||||
cld ; back to binary
|
||||
cli ; enable interrupts again @todo don't re-enable if it wasn't enabled before
|
||||
rts
|
||||
lda _had_irqd
|
||||
bne +
|
||||
cli ; enable interrupts again (only if they were enabled before)
|
||||
+ rts
|
||||
_had_irqd .byte 0
|
||||
bcdbuff .byte 0,0,0
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
ubyte[5] word2decimal_output = 0
|
||||
asmsub uword2decimal (uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
; ---- convert 16 bit uword in A/Y into decimal string into memory 'word2decimal_output'
|
||||
asmsub uword2decimal (uword value @ AY) -> clobbers(A) -> (ubyte @ Y) {
|
||||
; ---- convert 16 bit uword in A/Y into 0-terminated decimal string into memory 'uword2decimal.output'
|
||||
; returns length of resulting string in Y
|
||||
%asm {{
|
||||
jsr uword2bcd
|
||||
lda word2bcd_bcdbuff+2
|
||||
lda uword2bcd.bcdbuff+2
|
||||
clc
|
||||
adc #'0'
|
||||
sta word2decimal_output
|
||||
sta output
|
||||
ldy #1
|
||||
lda word2bcd_bcdbuff+1
|
||||
lda uword2bcd.bcdbuff+1
|
||||
jsr +
|
||||
lda word2bcd_bcdbuff+0
|
||||
lda uword2bcd.bcdbuff+0
|
||||
|
||||
+ pha
|
||||
lsr a
|
||||
@@ -148,153 +155,135 @@ asmsub uword2decimal (uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
lsr a
|
||||
clc
|
||||
adc #'0'
|
||||
sta word2decimal_output,y
|
||||
sta output,y
|
||||
iny
|
||||
pla
|
||||
and #$0f
|
||||
adc #'0'
|
||||
sta word2decimal_output,y
|
||||
sta output,y
|
||||
iny
|
||||
rts
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
asmsub str2byte (str string @ AY) -> clobbers(Y) -> (byte @ A) {
|
||||
%asm {{
|
||||
; -- convert string (address in A/Y) to byte in A
|
||||
; doesn't use any kernal routines
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
ldy #0
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
cmp #'-'
|
||||
beq +
|
||||
jmp str2ubyte._enter
|
||||
+ inc c64.SCRATCH_ZPWORD1
|
||||
bne +
|
||||
inc c64.SCRATCH_ZPWORD1+1
|
||||
+ jsr str2ubyte._enter
|
||||
eor #$ff
|
||||
sec
|
||||
adc #0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str2ubyte (str string @ AY) -> clobbers(Y) -> (ubyte @ A) {
|
||||
%asm {{
|
||||
; -- convert string (address in A/Y) to ubyte in A
|
||||
; doesn't use any kernal routines
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
_enter jsr _numlen ; Y= slen
|
||||
lda #0
|
||||
dey
|
||||
bpl +
|
||||
sta output,y
|
||||
rts
|
||||
+ lda (c64.SCRATCH_ZPWORD1),y
|
||||
sec
|
||||
sbc #'0'
|
||||
dey
|
||||
bpl +
|
||||
rts
|
||||
+ sta c64.SCRATCH_ZPREG ;result
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sec
|
||||
sbc #'0'
|
||||
asl a
|
||||
sta c64.SCRATCH_ZPB1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1
|
||||
clc
|
||||
adc c64.SCRATCH_ZPREG
|
||||
dey
|
||||
bpl +
|
||||
rts
|
||||
+ sta c64.SCRATCH_ZPREG
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
tay
|
||||
lda _hundreds-'0',y
|
||||
clc
|
||||
adc c64.SCRATCH_ZPREG
|
||||
rts
|
||||
_hundreds .byte 0, 100, 200
|
||||
|
||||
_numlen
|
||||
;-- return the length of the numeric string at ZPWORD1, in Y
|
||||
output .text "00000", $00 ; 0 terminated
|
||||
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
|
||||
asmsub str2uword(str string @ AY) -> clobbers() -> (uword @ AY) {
|
||||
; -- returns the unsigned word value of the string number argument in AY
|
||||
; the number may NOT be preceded by a + sign and may NOT contain spaces
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
%asm {{
|
||||
_result = c64.SCRATCH_ZPWORD2
|
||||
sta _mod+1
|
||||
sty _mod+2
|
||||
ldy #0
|
||||
- lda (c64.SCRATCH_ZPWORD1),y
|
||||
cmp #'0'
|
||||
bmi +
|
||||
cmp #':' ; one after '9'
|
||||
sty _result
|
||||
sty _result+1
|
||||
_mod lda $ffff,y ; modified
|
||||
sec
|
||||
sbc #48
|
||||
bpl +
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
_done ; return result
|
||||
lda _result
|
||||
ldy _result+1
|
||||
rts
|
||||
+ cmp #10
|
||||
bcs _done
|
||||
; add digit to result
|
||||
pha
|
||||
jsr _result_times_10
|
||||
pla
|
||||
clc
|
||||
adc _result
|
||||
sta _result
|
||||
bcc +
|
||||
inc _result+1
|
||||
+ iny
|
||||
bne _mod
|
||||
; never reached
|
||||
|
||||
_result_times_10 ; (W*4 + W)*2
|
||||
lda _result+1
|
||||
sta c64.SCRATCH_ZPREG
|
||||
lda _result
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
asl a
|
||||
rol c64.SCRATCH_ZPREG
|
||||
clc
|
||||
adc _result
|
||||
sta _result
|
||||
lda c64.SCRATCH_ZPREG
|
||||
adc _result+1
|
||||
asl _result
|
||||
rol a
|
||||
sta _result+1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub c64flt_FREADSTR (ubyte length @ A) -> clobbers(A,X,Y) -> () = $b7b5 ; @todo needed for (slow) str conversion below
|
||||
asmsub c64flt_GETADR () -> clobbers(X) -> (ubyte @ Y, ubyte @ A) = $b7f7 ; @todo needed for (slow) str conversion below
|
||||
asmsub c64flt_FTOSWORDYA () -> clobbers(X) -> (ubyte @ Y, ubyte @ A) = $b1aa ; @todo needed for (slow) str conversion below
|
||||
|
||||
asmsub str2uword(str string @ AY) -> clobbers() -> (uword @ AY) {
|
||||
asmsub str2word(str string @ AY) -> clobbers() -> (word @ AY) {
|
||||
; -- returns the signed word value of the string number argument in AY
|
||||
; the number may be preceded by a + or - sign but may NOT contain spaces
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
%asm {{
|
||||
;-- convert string (address in A/Y) to uword number in A/Y
|
||||
; @todo don't use the (slow) kernel floating point conversion
|
||||
sta $22
|
||||
sty $23
|
||||
jsr _strlen2233
|
||||
tya
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr c64flt_FREADSTR ; string to fac1
|
||||
jsr c64flt_GETADR ; fac1 to unsigned word in Y/A
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
sta c64.SCRATCH_ZPREG
|
||||
tya
|
||||
ldy c64.SCRATCH_ZPREG
|
||||
rts
|
||||
|
||||
_strlen2233
|
||||
;-- return the length of the (zero-terminated) string at $22/$23, in Y
|
||||
_result = c64.SCRATCH_ZPWORD2
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
ldy #0
|
||||
- lda ($22),y
|
||||
sty _result
|
||||
sty _result+1
|
||||
sty _negative
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
cmp #'+'
|
||||
bne +
|
||||
iny
|
||||
+ cmp #'-'
|
||||
bne _parse
|
||||
inc _negative
|
||||
iny
|
||||
_parse lda (c64.SCRATCH_ZPWORD1),y
|
||||
sec
|
||||
sbc #48
|
||||
bpl _digit
|
||||
_done ; return result
|
||||
lda _negative
|
||||
beq +
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str2word(str string @ AY) -> clobbers() -> (word @ AY) {
|
||||
%asm {{
|
||||
;-- convert string (address in A/Y) to signed word number in A/Y
|
||||
; @todo don't use the (slow) kernel floating point conversion
|
||||
sta $22
|
||||
sty $23
|
||||
jsr str2uword._strlen2233
|
||||
tya
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr c64flt_FREADSTR ; string to fac1
|
||||
jsr c64flt_FTOSWORDYA ; fac1 to unsigned word in Y/A
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
sta c64.SCRATCH_ZPREG
|
||||
tya
|
||||
ldy c64.SCRATCH_ZPREG
|
||||
sec
|
||||
lda #0
|
||||
sbc _result
|
||||
sta _result
|
||||
lda #0
|
||||
sbc _result+1
|
||||
sta _result+1
|
||||
+ lda _result
|
||||
ldy _result+1
|
||||
rts
|
||||
_digit cmp #10
|
||||
bcs _done
|
||||
; add digit to result
|
||||
pha
|
||||
jsr str2uword._result_times_10
|
||||
pla
|
||||
clc
|
||||
adc _result
|
||||
sta _result
|
||||
bcc +
|
||||
inc _result+1
|
||||
+ iny
|
||||
bne _parse
|
||||
; never reached
|
||||
_negative .byte 0
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
; @todo string to 32 bit unsigned integer http://www.6502.org/source/strings/ascii-to-32bit.html
|
||||
|
||||
|
||||
asmsub set_irqvec_excl() -> clobbers(A) -> () {
|
||||
%asm {{
|
||||
sei
|
||||
@@ -304,7 +293,9 @@ asmsub set_irqvec_excl() -> clobbers(A) -> () {
|
||||
sta c64.CINV+1
|
||||
cli
|
||||
rts
|
||||
_irq_handler jsr irq.irq
|
||||
_irq_handler jsr set_irqvec._irq_handler_init
|
||||
jsr irq.irq
|
||||
jsr set_irqvec._irq_handler_end
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
lda c64.CIA1ICR ; acknowledge CIA1 interrupt
|
||||
@@ -321,10 +312,64 @@ asmsub set_irqvec() -> clobbers(A) -> () {
|
||||
sta c64.CINV+1
|
||||
cli
|
||||
rts
|
||||
_irq_handler jsr irq.irq
|
||||
_irq_handler jsr _irq_handler_init
|
||||
jsr irq.irq
|
||||
jsr _irq_handler_end
|
||||
jmp c64.IRQDFRT ; continue with normal kernel irq routine
|
||||
|
||||
}}
|
||||
_irq_handler_init
|
||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
||||
stx IRQ_X_REG
|
||||
lda c64.SCRATCH_ZPB1
|
||||
sta IRQ_SCRATCH_ZPB1
|
||||
lda c64.SCRATCH_ZPREG
|
||||
sta IRQ_SCRATCH_ZPREG
|
||||
lda c64.SCRATCH_ZPREGX
|
||||
sta IRQ_SCRATCH_ZPREGX
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
sta IRQ_SCRATCH_ZPWORD1
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
sta IRQ_SCRATCH_ZPWORD1+1
|
||||
lda c64.SCRATCH_ZPWORD2
|
||||
sta IRQ_SCRATCH_ZPWORD2
|
||||
lda c64.SCRATCH_ZPWORD2+1
|
||||
sta IRQ_SCRATCH_ZPWORD2+1
|
||||
; stack protector; make sure we don't clobber the top of the evaluation stack
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
rts
|
||||
|
||||
_irq_handler_end
|
||||
; restore all zp scratch registers and the X register
|
||||
lda IRQ_SCRATCH_ZPB1
|
||||
sta c64.SCRATCH_ZPB1
|
||||
lda IRQ_SCRATCH_ZPREG
|
||||
sta c64.SCRATCH_ZPREG
|
||||
lda IRQ_SCRATCH_ZPREGX
|
||||
sta c64.SCRATCH_ZPREGX
|
||||
lda IRQ_SCRATCH_ZPWORD1
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
lda IRQ_SCRATCH_ZPWORD1+1
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
lda IRQ_SCRATCH_ZPWORD2
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
lda IRQ_SCRATCH_ZPWORD2+1
|
||||
sta c64.SCRATCH_ZPWORD2+1
|
||||
ldx IRQ_X_REG
|
||||
rts
|
||||
|
||||
IRQ_X_REG .byte 0
|
||||
IRQ_SCRATCH_ZPB1 .byte 0
|
||||
IRQ_SCRATCH_ZPREG .byte 0
|
||||
IRQ_SCRATCH_ZPREGX .byte 0
|
||||
IRQ_SCRATCH_ZPWORD1 .word 0
|
||||
IRQ_SCRATCH_ZPWORD2 .word 0
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -357,7 +402,9 @@ asmsub set_rasterirq(uword rasterpos @ AY) -> clobbers(A) -> () {
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
jsr set_irqvec._irq_handler_init
|
||||
jsr irq.irq
|
||||
jsr set_irqvec._irq_handler_end
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
jmp c64.IRQDFRT
|
||||
@@ -396,7 +443,9 @@ asmsub set_rasterirq_excl(uword rasterpos @ AY) -> clobbers(A) -> () {
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
jsr set_irqvec._irq_handler_init
|
||||
jsr irq.irq
|
||||
jsr set_irqvec._irq_handler_end
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
jmp c64.IRQDFEND ; end irq processing - don't call kernel
|
||||
@@ -473,7 +522,7 @@ _loop sta c64.Colors,y
|
||||
}
|
||||
|
||||
|
||||
asmsub scroll_left_full (ubyte alsocolors @ Pc) -> clobbers(A, Y) -> () {
|
||||
asmsub scroll_left_full (ubyte alsocolors @ Pc) -> clobbers(A, Y) -> () {
|
||||
; ---- scroll the whole screen 1 character to the left
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -534,7 +583,7 @@ _scroll_screen ; scroll the screen memory
|
||||
}
|
||||
|
||||
|
||||
asmsub scroll_right_full (ubyte alsocolors @ Pc) -> clobbers(A) -> () {
|
||||
asmsub scroll_right_full (ubyte alsocolors @ Pc) -> clobbers(A) -> () {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -587,7 +636,7 @@ _scroll_screen ; scroll the screen memory
|
||||
}
|
||||
|
||||
|
||||
asmsub scroll_up_full (ubyte alsocolors @ Pc) -> clobbers(A) -> () {
|
||||
asmsub scroll_up_full (ubyte alsocolors @ Pc) -> clobbers(A) -> () {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -640,7 +689,7 @@ _scroll_screen ; scroll the screen memory
|
||||
}
|
||||
|
||||
|
||||
asmsub scroll_down_full (ubyte alsocolors @ Pc) -> clobbers(A) -> () {
|
||||
asmsub scroll_down_full (ubyte alsocolors @ Pc) -> clobbers(A) -> () {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -713,27 +762,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 {{
|
||||
@@ -866,7 +894,7 @@ asmsub print_uw0 (uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
%asm {{
|
||||
jsr c64utils.uword2decimal
|
||||
ldy #0
|
||||
- lda c64utils.word2decimal_output,y
|
||||
- lda c64utils.uword2decimal.output,y
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
cpy #5
|
||||
@@ -881,25 +909,25 @@ asmsub print_uw (uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
%asm {{
|
||||
jsr c64utils.uword2decimal
|
||||
ldy #0
|
||||
lda c64utils.word2decimal_output
|
||||
lda c64utils.uword2decimal.output
|
||||
cmp #'0'
|
||||
bne _pr_decimal
|
||||
iny
|
||||
lda c64utils.word2decimal_output+1
|
||||
lda c64utils.uword2decimal.output+1
|
||||
cmp #'0'
|
||||
bne _pr_decimal
|
||||
iny
|
||||
lda c64utils.word2decimal_output+2
|
||||
lda c64utils.uword2decimal.output+2
|
||||
cmp #'0'
|
||||
bne _pr_decimal
|
||||
iny
|
||||
lda c64utils.word2decimal_output+3
|
||||
lda c64utils.uword2decimal.output+3
|
||||
cmp #'0'
|
||||
bne _pr_decimal
|
||||
iny
|
||||
|
||||
_pr_decimal
|
||||
lda c64utils.word2decimal_output,y
|
||||
lda c64utils.uword2decimal.output,y
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
cpy #5
|
||||
@@ -909,7 +937,7 @@ _pr_decimal
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) -> clobbers(A,Y) -> () {
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0s
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
bpl +
|
||||
@@ -930,7 +958,7 @@ asmsub print_w (word value @ AY) -> clobbers(A,Y) -> () {
|
||||
}
|
||||
|
||||
asmsub input_chars (uword buffer @ AY) -> clobbers(A) -> (ubyte @ Y) {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y.
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
|
||||
%asm {{
|
||||
@@ -972,6 +1000,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 {{
|
||||
@@ -994,6 +1041,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
|
||||
@@ -1021,7 +1086,7 @@ _colormod sta $ffff ; modified
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub PLOT (ubyte col @ Y, ubyte row @ A) -> clobbers(A) -> () {
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) -> clobbers(A) -> () {
|
||||
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
||||
%asm {{
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
|
||||
@@ -105,6 +105,161 @@ not_word .proc
|
||||
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)
|
||||
@@ -485,7 +640,66 @@ 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_sqrt16 .proc
|
||||
lda c64.ESTACK_LO+1,x
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
lda c64.ESTACK_HI+1,x
|
||||
sta c64.SCRATCH_ZPWORD2+1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
ldy #$00 ; r = 0
|
||||
ldx #$07
|
||||
clc ; clear bit 16 of m
|
||||
_loop
|
||||
tya
|
||||
ora _stab-1,x
|
||||
sta c64.SCRATCH_ZPB1 ; (r asl 8) | (d asl 7)
|
||||
lda c64.SCRATCH_ZPWORD2+1
|
||||
bcs _skip0 ; m >= 65536? then t <= m is always true
|
||||
cmp c64.SCRATCH_ZPB1
|
||||
bcc _skip1 ; t <= m
|
||||
_skip0
|
||||
sbc c64.SCRATCH_ZPB1
|
||||
sta c64.SCRATCH_ZPWORD2+1 ; m = m - t
|
||||
tya
|
||||
ora _stab,x
|
||||
tay ; r = r or d
|
||||
_skip1
|
||||
asl c64.SCRATCH_ZPWORD2
|
||||
rol c64.SCRATCH_ZPWORD2+1 ; m = m asl 1
|
||||
dex
|
||||
bne _loop
|
||||
|
||||
; last iteration
|
||||
bcs _skip2
|
||||
sty c64.SCRATCH_ZPB1
|
||||
lda c64.SCRATCH_ZPWORD2
|
||||
cmp #$80
|
||||
lda c64.SCRATCH_ZPWORD2+1
|
||||
sbc c64.SCRATCH_ZPB1
|
||||
bcc _skip3
|
||||
_skip2
|
||||
iny ; r = r or d (d is 1 here)
|
||||
_skip3
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
tya
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda #0
|
||||
sta c64.ESTACK_HI+1,x
|
||||
rts
|
||||
_stab .byte $01,$02,$04,$08,$10,$20,$40,$80
|
||||
.pend
|
||||
|
||||
|
||||
func_sin8 .proc
|
||||
ldy c64.ESTACK_LO+1,x
|
||||
lda _sinecos8,y
|
||||
@@ -948,8 +1162,7 @@ _gtequ dey
|
||||
_result_minw .word 0
|
||||
.pend
|
||||
|
||||
|
||||
func_len_str .proc
|
||||
func_strlen .proc
|
||||
; -- push length of 0-terminated string on stack
|
||||
jsr peek_address
|
||||
ldy #0
|
||||
@@ -962,15 +1175,6 @@ func_len_str .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_len_strp .proc
|
||||
; -- push length of pascal-string on stack
|
||||
jsr peek_address
|
||||
ldy #0
|
||||
lda (c64.SCRATCH_ZPWORD1),y ; first byte is length
|
||||
sta c64.ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_rnd .proc
|
||||
; -- put a random ubyte on the estack
|
||||
jsr math.randbyte
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.2 (beta)
|
||||
1.5 (beta)
|
||||
@@ -46,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"
|
||||
@@ -55,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
|
||||
@@ -85,7 +88,7 @@ private fun compileMain(args: Array<String>) {
|
||||
println("Syntax check...")
|
||||
val heap = HeapValues()
|
||||
val time1= measureTimeMillis {
|
||||
moduleAst.checkIdentifiers(heap)
|
||||
moduleAst.checkIdentifiers(namespace)
|
||||
}
|
||||
//println(" time1: $time1")
|
||||
val time2 = measureTimeMillis {
|
||||
@@ -101,15 +104,17 @@ 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.checkIdentifiers(namespace)
|
||||
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
|
||||
@@ -121,7 +126,8 @@ private fun compileMain(args: Array<String>) {
|
||||
// compile the syntax tree into stackvmProg form, and optimize that
|
||||
val compiler = Compiler(moduleAst, namespace, heap)
|
||||
val intermediate = compiler.compile(compilerOptions)
|
||||
intermediate.optimize()
|
||||
if(optimize)
|
||||
intermediate.optimize()
|
||||
|
||||
if(writeVmCode) {
|
||||
val stackVmFilename = intermediate.name + ".vm.txt"
|
||||
@@ -134,7 +140,7 @@ private fun compileMain(args: Array<String>) {
|
||||
if(writeAssembly) {
|
||||
val zeropage = C64Zeropage(compilerOptions)
|
||||
intermediate.allocateZeropage(zeropage)
|
||||
val assembly = AsmGen(compilerOptions, intermediate, heap, zeropage).compileToAssembly()
|
||||
val assembly = AsmGen(compilerOptions, intermediate, heap, zeropage).compileToAssembly(optimize)
|
||||
assembly.assemble(compilerOptions)
|
||||
programname = assembly.name
|
||||
}
|
||||
@@ -211,6 +217,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)
|
||||
}
|
||||
@@ -26,9 +26,7 @@ enum class DataType {
|
||||
WORD,
|
||||
FLOAT,
|
||||
STR,
|
||||
STR_P,
|
||||
STR_S,
|
||||
STR_PS,
|
||||
ARRAY_UB,
|
||||
ARRAY_B,
|
||||
ARRAY_UW,
|
||||
@@ -44,9 +42,7 @@ enum class DataType {
|
||||
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 || targetType==ARRAY_UB
|
||||
ARRAY_B -> targetType == UWORD || targetType==ARRAY_B
|
||||
ARRAY_UW -> targetType == UWORD || targetType==ARRAY_UW
|
||||
@@ -98,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)
|
||||
|
||||
|
||||
@@ -328,8 +325,11 @@ inline fun <reified T> findParentNode(node: Node): T? {
|
||||
|
||||
interface IStatement : Node {
|
||||
fun process(processor: IAstProcessor) : IStatement
|
||||
fun makeScopedName(name: String): List<String> {
|
||||
// this is usually cached in a lazy property on the statement object itself
|
||||
fun makeScopedName(name: String): String {
|
||||
// easy way out is to always return the full scoped name.
|
||||
// it would be nicer to find only the minimal prefixed scoped name, but that's too much hassle for now.
|
||||
// and like this, we can cache the name even,
|
||||
// like in a lazy property on the statement object itself (label, subroutine, vardecl)
|
||||
val scope = mutableListOf<String>()
|
||||
var statementScope = this.parent
|
||||
while(statementScope !is ParentSentinel && statementScope !is Module) {
|
||||
@@ -338,8 +338,9 @@ interface IStatement : Node {
|
||||
}
|
||||
statementScope = statementScope.parent
|
||||
}
|
||||
scope.add(name)
|
||||
return scope
|
||||
if(name.isNotEmpty())
|
||||
scope.add(name)
|
||||
return scope.joinToString(".")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,7 +511,6 @@ class Block(override val name: String,
|
||||
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
|
||||
@@ -550,7 +550,6 @@ data class DirectiveArg(val str: String?, val name: String?, val int: Int?, over
|
||||
|
||||
data class Label(val name: String, override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
val scopedname: String by lazy { makeScopedName(name).joinToString(".") }
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
@@ -561,6 +560,8 @@ data class Label(val name: String, override val position: Position) : IStatement
|
||||
override fun toString(): String {
|
||||
return "Label(name=$name, pos=$position)"
|
||||
}
|
||||
|
||||
val scopedname: String by lazy { makeScopedName(name) }
|
||||
}
|
||||
|
||||
|
||||
@@ -688,7 +689,7 @@ class VarDecl(val type: VarDeclType,
|
||||
|
||||
override fun process(processor: IAstProcessor) = processor.process(this)
|
||||
|
||||
val scopedname: String by lazy { makeScopedName(name).joinToString(".") }
|
||||
val scopedname: String by lazy { makeScopedName(name) }
|
||||
|
||||
override fun toString(): String {
|
||||
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, value=$value, pos=$position)"
|
||||
@@ -790,7 +791,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)
|
||||
@@ -918,8 +919,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")
|
||||
}
|
||||
@@ -930,7 +931,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")
|
||||
}
|
||||
@@ -1073,8 +1074,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")
|
||||
}
|
||||
@@ -1110,8 +1111,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")
|
||||
@@ -1159,14 +1160,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)"
|
||||
}
|
||||
@@ -1272,7 +1274,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
|
||||
}
|
||||
@@ -1314,9 +1316,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
|
||||
@@ -1588,7 +1588,7 @@ class AnonymousScope(override var statements: MutableList<IStatement>,
|
||||
override lateinit var parent: Node
|
||||
|
||||
init {
|
||||
name = "<<<anonymous-$sequenceNumber>>>"
|
||||
name = "<anon-$sequenceNumber>" // make sure it's an invalid soruce code identifier so user source code can never produce it
|
||||
sequenceNumber++
|
||||
}
|
||||
|
||||
@@ -1629,7 +1629,7 @@ class Subroutine(override val name: String,
|
||||
override var statements: MutableList<IStatement>,
|
||||
override val position: Position) : IStatement, INameScope {
|
||||
override lateinit var parent: Node
|
||||
val scopedname: String by lazy { makeScopedName(name).joinToString(".") }
|
||||
val scopedname: String by lazy { makeScopedName(name) }
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
@@ -1649,7 +1649,7 @@ class Subroutine(override val name: String,
|
||||
.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 see below
|
||||
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 }))
|
||||
@@ -2078,7 +2078,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
|
||||
@@ -2090,12 +2094,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")
|
||||
}
|
||||
@@ -2275,31 +2287,6 @@ private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
|
||||
}
|
||||
|
||||
|
||||
internal fun registerSet(asmReturnvaluesRegisters: Iterable<RegisterOrStatusflag>): Set<Register> {
|
||||
val resultRegisters = mutableSetOf<Register>()
|
||||
for(x in asmReturnvaluesRegisters) {
|
||||
when(x.registerOrPair) {
|
||||
RegisterOrPair.A -> resultRegisters.add(Register.A)
|
||||
RegisterOrPair.X -> resultRegisters.add(Register.X)
|
||||
RegisterOrPair.Y -> resultRegisters.add(Register.Y)
|
||||
RegisterOrPair.AX -> {
|
||||
resultRegisters.add(Register.A)
|
||||
resultRegisters.add(Register.X)
|
||||
}
|
||||
RegisterOrPair.AY -> {
|
||||
resultRegisters.add(Register.A)
|
||||
resultRegisters.add(Register.Y)
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
resultRegisters.add(Register.X)
|
||||
resultRegisters.add(Register.Y)
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultRegisters
|
||||
}
|
||||
|
||||
|
||||
internal fun escape(str: String) = str.replace("\t", "\\t").replace("\n", "\\n").replace("\r", "\\r")
|
||||
|
||||
internal fun unescape(str: String, position: Position): String {
|
||||
|
||||
@@ -139,9 +139,6 @@ private class AstChecker(private val namespace: INameScope,
|
||||
if(forLoop.body.isEmpty())
|
||||
printWarning("for loop body is empty", forLoop.position)
|
||||
|
||||
if(forLoop.iterable is LiteralValue)
|
||||
checkResult.add(SyntaxError("currently not possible to loop over a literal value directly, define it as a variable instead", forLoop.position)) // todo loop over literals (by creating a generated variable)
|
||||
|
||||
if(!forLoop.iterable.isIterable(namespace, heap)) {
|
||||
checkResult.add(ExpressionError("can only loop over an iterable type", forLoop.position))
|
||||
} else {
|
||||
@@ -358,23 +355,17 @@ private class AstChecker(private val namespace: INameScope,
|
||||
// assigning from a functioncall COULD return multiple values (from an asm subroutine)
|
||||
if(assignment.value is FunctionCall) {
|
||||
val stmt = (assignment.value as FunctionCall).target.targetStatement(namespace)
|
||||
if (stmt is Subroutine && stmt.returntypes.size > 1) {
|
||||
if (stmt is Subroutine) {
|
||||
if (stmt.isAsmSubroutine) {
|
||||
if (stmt.returntypes.size != assignment.targets.size)
|
||||
checkResult.add(ExpressionError("number of return values doesn't match number of assignment targets", assignment.value.position))
|
||||
else {
|
||||
if (assignment.targets.all { it.register != null }) {
|
||||
val returnRegisters = registerSet(stmt.asmReturnvaluesRegisters)
|
||||
val targetRegisters = assignment.targets.filter { it.register != null }.map { it.register }.toSet()
|
||||
if (returnRegisters != targetRegisters)
|
||||
checkResult.add(ExpressionError("asmsub return registers $returnRegisters don't match assignment target registers", assignment.position))
|
||||
}
|
||||
for (thing in stmt.returntypes.zip(assignment.targets)) {
|
||||
if (thing.second.determineDatatype(namespace, heap, assignment) != thing.first)
|
||||
checkResult.add(ExpressionError("return type mismatch for target ${thing.second.shortString()}", assignment.value.position))
|
||||
}
|
||||
}
|
||||
} else
|
||||
} else if(assignment.targets.size>1)
|
||||
checkResult.add(ExpressionError("only asmsub subroutines can return multiple values", assignment.value.position))
|
||||
}
|
||||
}
|
||||
@@ -695,17 +686,28 @@ 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", "&", "|", "^" -> {
|
||||
"and", "or", "xor" -> {
|
||||
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
|
||||
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 operator can only be used on boolean operands", expr.right.position))
|
||||
val constLeft = expr.left.constValue(namespace, heap)
|
||||
val constRight = expr.right.constValue(namespace, heap)
|
||||
if(constLeft!=null && constLeft.asIntegerValue !in 0..1 || constRight!=null && constRight.asIntegerValue !in 0..1)
|
||||
checkResult.add(ExpressionError("const literal argument of logical operator must be boolean (0 or 1)", expr.position))
|
||||
}
|
||||
"&", "|", "^" -> {
|
||||
// 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))
|
||||
checkResult.add(ExpressionError("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)
|
||||
@@ -716,12 +718,6 @@ private class AstChecker(private val namespace: INameScope,
|
||||
override fun process(typecast: TypecastExpression): IExpression {
|
||||
if(typecast.type in IterableDatatypes)
|
||||
checkResult.add(ExpressionError("cannot type cast to string or array type", typecast.position))
|
||||
val funcTarget = (typecast.expression as? IFunctionCall)?.target?.targetStatement(namespace)
|
||||
if(funcTarget is Subroutine &&
|
||||
funcTarget.asmReturnvaluesRegisters.isNotEmpty() &&
|
||||
funcTarget.asmReturnvaluesRegisters.all { it.stack!=true }) {
|
||||
checkResult.add(ExpressionError("cannot type cast a call to an asmsub that returns value in register - use a variable to store it first", typecast.position))
|
||||
}
|
||||
return super.process(typecast)
|
||||
}
|
||||
|
||||
@@ -830,6 +826,19 @@ private class AstChecker(private val namespace: INameScope,
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -877,9 +886,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
|
||||
@@ -902,7 +908,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
|
||||
}
|
||||
|
||||
@@ -954,8 +960,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")
|
||||
}
|
||||
@@ -996,7 +1002,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)
|
||||
@@ -1122,8 +1128,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))
|
||||
}
|
||||
|
||||
@@ -1137,9 +1141,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
|
||||
}
|
||||
|
||||
@@ -1,49 +1,53 @@
|
||||
package prog8.ast
|
||||
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.functions.BuiltinFunctions
|
||||
|
||||
/**
|
||||
* Checks the validity of all identifiers (no conflicts)
|
||||
* Also builds a list of all (scoped) symbol definitions
|
||||
* Also makes sure that subroutine's parameters also become local variable decls in the subroutine's scope.
|
||||
* Finally, it also makes sure the datatype of all Var decls and sub Return values is set correctly.
|
||||
*/
|
||||
|
||||
fun Module.checkIdentifiers(heap: HeapValues): MutableMap<String, IStatement> {
|
||||
val checker = AstIdentifiersChecker(heap)
|
||||
fun Module.checkIdentifiers(namespace: INameScope) {
|
||||
val checker = AstIdentifiersChecker(namespace)
|
||||
this.process(checker)
|
||||
|
||||
// add any anonymous variables for heap values that are used, and replace literalvalue by identifierref
|
||||
// add any anonymous variables for heap values that are used,
|
||||
// and replace an iterable literalvalue by identifierref to new local variable
|
||||
for (variable in checker.anonymousVariablesFromHeap) {
|
||||
val scope = variable.first.definingScope()
|
||||
scope.statements.add(variable.second)
|
||||
val parent = variable.first.parent
|
||||
when {
|
||||
parent is Assignment && parent.value === variable.first -> {
|
||||
val idref = IdentifierReference(listOf("auto_heap_value_${variable.first.heapId}"), variable.first.position)
|
||||
val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
|
||||
idref.linkParents(parent)
|
||||
parent.value = idref
|
||||
}
|
||||
parent is IFunctionCall -> {
|
||||
val parameterPos = parent.arglist.indexOf(variable.first)
|
||||
val idref = IdentifierReference(listOf("auto_heap_value_${variable.first.heapId}"), variable.first.position)
|
||||
val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
|
||||
idref.linkParents(parent)
|
||||
parent.arglist[parameterPos] = idref
|
||||
}
|
||||
parent is ForLoop -> {
|
||||
val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
|
||||
idref.linkParents(parent)
|
||||
parent.iterable = idref
|
||||
}
|
||||
else -> TODO("replace literalvalue by identifierref: $variable (in $parent)")
|
||||
}
|
||||
variable.second.linkParents(scope as Node)
|
||||
}
|
||||
|
||||
printErrors(checker.result(), name)
|
||||
return checker.symbols
|
||||
}
|
||||
|
||||
|
||||
private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
|
||||
private class AstIdentifiersChecker(private val namespace: INameScope) : IAstProcessor {
|
||||
private val checkResult: MutableList<AstException> = mutableListOf()
|
||||
|
||||
var symbols: MutableMap<String, IStatement> = mutableMapOf()
|
||||
var blocks: MutableMap<String, Block> = mutableMapOf()
|
||||
private set
|
||||
|
||||
fun result(): List<AstException> {
|
||||
@@ -51,17 +55,16 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
|
||||
}
|
||||
|
||||
private fun nameError(name: String, position: Position, existing: IStatement) {
|
||||
checkResult.add(NameError("name conflict '$name', first defined in ${existing.position.file} line ${existing.position.line}", position))
|
||||
checkResult.add(NameError("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position))
|
||||
}
|
||||
|
||||
override fun process(block: Block): IStatement {
|
||||
val scopedName = block.scopedname
|
||||
val existing = symbols[scopedName]
|
||||
if(existing!=null) {
|
||||
val existing = blocks[block.name]
|
||||
if(existing!=null)
|
||||
nameError(block.name, block.position, existing)
|
||||
} else {
|
||||
symbols[scopedName] = block
|
||||
}
|
||||
else
|
||||
blocks[block.name] = block
|
||||
|
||||
return super.process(block)
|
||||
}
|
||||
|
||||
@@ -84,13 +87,10 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
|
||||
// the builtin functions can't be redefined
|
||||
checkResult.add(NameError("builtin function cannot be redefined", decl.position))
|
||||
|
||||
val scopedName = decl.scopedname
|
||||
val existing = symbols[scopedName]
|
||||
if(existing!=null) {
|
||||
val existing = namespace.lookup(listOf(decl.name), decl)
|
||||
if (existing != null && existing !== decl)
|
||||
nameError(decl.name, decl.position, existing)
|
||||
} else {
|
||||
symbols[scopedName] = decl
|
||||
}
|
||||
|
||||
return super.process(decl)
|
||||
}
|
||||
|
||||
@@ -102,13 +102,9 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
|
||||
if (subroutine.parameters.any { it.name in BuiltinFunctions })
|
||||
checkResult.add(NameError("builtin function name cannot be used as parameter", subroutine.position))
|
||||
|
||||
val scopedName = subroutine.scopedname
|
||||
val existing = symbols[scopedName]
|
||||
if (existing != null) {
|
||||
val existing = namespace.lookup(listOf(subroutine.name), subroutine)
|
||||
if (existing != null && existing !== subroutine)
|
||||
nameError(subroutine.name, subroutine.position, existing)
|
||||
} else {
|
||||
symbols[scopedName] = subroutine
|
||||
}
|
||||
|
||||
// check that there are no local variables that redefine the subroutine's parameters
|
||||
val allDefinedNames = subroutine.allLabelsAndVariables()
|
||||
@@ -145,13 +141,9 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
|
||||
// the builtin functions can't be redefined
|
||||
checkResult.add(NameError("builtin function cannot be redefined", label.position))
|
||||
} else {
|
||||
val scopedName = label.scopedname
|
||||
val existing = symbols[scopedName]
|
||||
if (existing != null) {
|
||||
val existing = namespace.lookup(listOf(label.name), label)
|
||||
if (existing != null && existing !== label)
|
||||
nameError(label.name, label.position, existing)
|
||||
} else {
|
||||
symbols[scopedName] = label
|
||||
}
|
||||
}
|
||||
return super.process(label)
|
||||
}
|
||||
@@ -227,13 +219,16 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
|
||||
|
||||
internal val anonymousVariablesFromHeap = mutableSetOf<Pair<LiteralValue, VarDecl>>()
|
||||
|
||||
|
||||
override fun process(literalValue: LiteralValue): LiteralValue {
|
||||
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, false, null, "auto_heap_value_${literalValue.heapId}", literalValue, literalValue.position)
|
||||
val variable = VarDecl(VarDeclType.VAR, literalValue.type, false, null, "$autoHeapValuePrefix${literalValue.heapId}", literalValue, literalValue.position)
|
||||
anonymousVariablesFromHeap.add(Pair(literalValue, variable))
|
||||
}
|
||||
return super.process(literalValue)
|
||||
}
|
||||
}
|
||||
|
||||
private const val autoHeapValuePrefix = "auto_heap_value_"
|
||||
@@ -233,7 +233,7 @@ private class VarInitValueCreator: IAstProcessor {
|
||||
val scope = decl.definingScope()
|
||||
if(scope !in vardeclsToAdd)
|
||||
vardeclsToAdd[scope] = mutableListOf()
|
||||
vardeclsToAdd[scope]!!.add(decl.asDefaultValueDecl(null))
|
||||
vardeclsToAdd.getValue(scope).add(decl.asDefaultValueDecl(null))
|
||||
val declvalue = decl.value!!
|
||||
val value =
|
||||
if(declvalue is LiteralValue) {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -20,8 +20,8 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
||||
|
||||
val size =
|
||||
when (datatype) {
|
||||
DataType.UBYTE, DataType.BYTE -> 1
|
||||
DataType.UWORD, DataType.WORD -> 2
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes -> 2
|
||||
DataType.FLOAT -> {
|
||||
if (options.floats) {
|
||||
if(position!=null)
|
||||
|
||||
@@ -8,25 +8,31 @@ open class Instruction(val opcode: Opcode,
|
||||
val callLabel: String? = null,
|
||||
val callLabel2: String? = null)
|
||||
{
|
||||
lateinit var next: Instruction
|
||||
var nextAlt: Instruction? = null
|
||||
var branchAddress: Int? = null
|
||||
|
||||
override fun toString(): String {
|
||||
val argStr = arg?.toString() ?: ""
|
||||
val result =
|
||||
when {
|
||||
opcode==Opcode.LINE -> "_line $callLabel"
|
||||
opcode==Opcode.INLINE_ASSEMBLY -> "inline_assembly"
|
||||
opcode==Opcode.INLINE_ASSEMBLY -> {
|
||||
// inline assembly is not written out (it can't be processed as intermediate language)
|
||||
// instead, it is converted into a system call that can be intercepted by the vm
|
||||
if(callLabel!=null)
|
||||
"syscall SYSASM.$callLabel\n return"
|
||||
else
|
||||
"inline_assembly"
|
||||
}
|
||||
opcode==Opcode.SYSCALL -> {
|
||||
val syscall = Syscall.values().find { it.callNr==arg!!.numericValue() }
|
||||
"syscall $syscall"
|
||||
}
|
||||
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()
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@ import java.nio.file.Path
|
||||
|
||||
class IntermediateProgram(val name: String, var loadAddress: Int, val heap: HeapValues, val importedFrom: Path) {
|
||||
|
||||
class ProgramBlock(val scopedname: String,
|
||||
val shortname: String,
|
||||
class ProgramBlock(val name: String,
|
||||
var address: Int?,
|
||||
val instructions: MutableList<Instruction> = mutableListOf(),
|
||||
val variables: MutableMap<String, Value> = mutableMapOf(),
|
||||
@@ -389,20 +388,20 @@ 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)
|
||||
@@ -447,8 +446,8 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
currentBlock.memoryPointers[name] = Pair(address, datatype)
|
||||
}
|
||||
|
||||
fun newBlock(scopedname: String, shortname: String, address: Int?, options: Set<String>) {
|
||||
currentBlock = ProgramBlock(scopedname, shortname, address, force_output="force_output" in options)
|
||||
fun newBlock(name: String, address: Int?, options: Set<String>) {
|
||||
currentBlock = ProgramBlock(name, address, force_output="force_output" in options)
|
||||
blocks.add(currentBlock)
|
||||
}
|
||||
|
||||
@@ -462,27 +461,27 @@ 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")
|
||||
}
|
||||
}
|
||||
out.println("%end_heap")
|
||||
for(blk in blocks) {
|
||||
out.println("\n%block ${blk.scopedname} ${blk.address?.toString(16) ?: ""}")
|
||||
out.println("\n%block ${blk.name} ${blk.address?.toString(16) ?: ""}")
|
||||
|
||||
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")
|
||||
|
||||
@@ -244,7 +244,6 @@ enum class Opcode {
|
||||
JZW, // branch if value is zero (word)
|
||||
JNZW, // branch if value is not zero (word)
|
||||
|
||||
|
||||
// subroutines
|
||||
CALL,
|
||||
RETURN,
|
||||
@@ -257,6 +256,7 @@ enum class Opcode {
|
||||
CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
|
||||
SEI, // set irq-disable status flag
|
||||
CLI, // clear irq-disable status flag
|
||||
CARRY_TO_A, // load var/register A with carry status bit
|
||||
RSAVE, // save all internal registers and status flags
|
||||
RSAVEX, // save just X (the evaluation stack pointer)
|
||||
RRESTORE, // restore all internal registers and status flags
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -16,9 +16,6 @@ import kotlin.math.abs
|
||||
class AssemblyError(msg: String) : RuntimeException(msg)
|
||||
|
||||
|
||||
// TODO: code generation for POW instruction
|
||||
|
||||
|
||||
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>()
|
||||
@@ -26,10 +23,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
private var breakpointCounter = 0
|
||||
|
||||
init {
|
||||
// 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!
|
||||
// Convert invalid label names (such as "<anon-1>") to something that's allowed.
|
||||
val newblocks = mutableListOf<IntermediateProgram.ProgramBlock>()
|
||||
for(block in program.blocks) {
|
||||
val newvars = block.variables.map { symname(it.key, block) to it.value }.toMap().toMutableMap()
|
||||
@@ -45,14 +39,13 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
callLabel2 = if (it.callLabel2 != null) symname(it.callLabel2, block) else null)
|
||||
}
|
||||
}.toMutableList()
|
||||
val newConstants = block.memoryPointers.map { symname(it.key, block) to it.value }.toMap().toMutableMap()
|
||||
val newMempointers = block.memoryPointers.map { symname(it.key, block) to it.value }.toMap().toMutableMap()
|
||||
val newblock = IntermediateProgram.ProgramBlock(
|
||||
block.scopedname,
|
||||
block.shortname,
|
||||
block.name,
|
||||
block.address,
|
||||
newinstructions,
|
||||
newvars,
|
||||
newConstants,
|
||||
newMempointers,
|
||||
newlabels,
|
||||
force_output = block.force_output)
|
||||
newblock.variablesMarkedForZeropage.clear()
|
||||
@@ -76,7 +69,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
}
|
||||
}
|
||||
|
||||
fun compileToAssembly(): AssemblyProgram {
|
||||
fun compileToAssembly(optimize: Boolean): AssemblyProgram {
|
||||
println("Generating assembly code from intermediate code... ")
|
||||
|
||||
assemblyLines.clear()
|
||||
@@ -84,9 +77,11 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
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 {
|
||||
@@ -107,29 +102,20 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
}
|
||||
|
||||
|
||||
// convert a fully scoped name (defined in the given block) to a valid assembly symbol name
|
||||
private fun symname(scoped: String, block: IntermediateProgram.ProgramBlock?): String {
|
||||
if(' ' in scoped)
|
||||
return scoped
|
||||
val blockLocal: Boolean
|
||||
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
|
||||
}
|
||||
var name = if (block!=null && scoped.startsWith("${block.name}.")) {
|
||||
blockLocal = true
|
||||
scoped.substring(block.name.length+1)
|
||||
}
|
||||
name = name.replace("<<<", "prog8_").replace(">>>", "")
|
||||
else {
|
||||
blockLocal = false
|
||||
scoped
|
||||
}
|
||||
name = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
|
||||
if(name=="-")
|
||||
return "-"
|
||||
if(blockLocal)
|
||||
@@ -200,7 +186,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
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(" jsr ${block.name}.${initVarsLabel.name}")
|
||||
}
|
||||
out(" clc")
|
||||
when(zeropage.exitProgramStrategy) {
|
||||
@@ -223,9 +209,9 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
|
||||
private fun block2asm(blk: IntermediateProgram.ProgramBlock) {
|
||||
block = blk
|
||||
out("\n; ---- block: '${block.shortname}' ----")
|
||||
out("\n; ---- block: '${block.name}' ----")
|
||||
if(!blk.force_output)
|
||||
out("${block.shortname}\t.proc\n")
|
||||
out("${block.name}\t.proc\n")
|
||||
if(block.address!=null) {
|
||||
out(".cerror * > ${block.address?.toHex()}, 'block address overlaps by ', *-${block.address?.toHex()},' bytes'")
|
||||
out("* = ${block.address?.toHex()}")
|
||||
@@ -233,14 +219,14 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
|
||||
// deal with zeropage variables
|
||||
for(variable in blk.variables) {
|
||||
val sym = symname(blk.scopedname+"."+variable.key, null)
|
||||
val sym = symname(blk.name+"."+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}")
|
||||
out("${variable.key} = $address\t; auto 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)
|
||||
@@ -294,13 +280,10 @@ 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)}\"")
|
||||
out("${v.first}\t; ${v.second.type} \"${escape(rawStr).replace("\u0000", "<NULL>")}\"")
|
||||
for (chunk in bytes.chunked(16))
|
||||
out(" .byte " + chunk.joinToString())
|
||||
}
|
||||
@@ -361,24 +344,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")
|
||||
}
|
||||
@@ -441,12 +414,9 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
private fun simpleInstr2Asm(ins: Instruction): String? {
|
||||
// a label 'instruction' is simply translated into a asm label
|
||||
if(ins is LabelInstr) {
|
||||
if(ins.name.startsWith("block."))
|
||||
return ""
|
||||
|
||||
val labelresult =
|
||||
if(ins.name.startsWith("${block.shortname}."))
|
||||
ins.name.substring(block.shortname.length+1)
|
||||
if(ins.name.startsWith("${block.name}."))
|
||||
ins.name.substring(block.name.length+1)
|
||||
else
|
||||
ins.name
|
||||
return if(ins.asmProc) labelresult+"\t\t.proc" else labelresult
|
||||
@@ -463,6 +433,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
Opcode.CLC -> " clc"
|
||||
Opcode.SEI -> " sei"
|
||||
Opcode.CLI -> " cli"
|
||||
Opcode.CARRY_TO_A -> " lda #0 | adc #0"
|
||||
Opcode.JUMP -> {
|
||||
if(ins.callLabel!=null)
|
||||
" jmp ${ins.callLabel}"
|
||||
@@ -490,7 +461,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
Opcode.DISCARD_BYTE -> " inx"
|
||||
Opcode.DISCARD_WORD -> " inx"
|
||||
Opcode.DISCARD_FLOAT -> " inx | inx | inx"
|
||||
Opcode.INLINE_ASSEMBLY -> "@inline@" + (ins.callLabel ?: "") // All of the inline assembly is stored in the calllabel property. the '@inline@' is a special marker to process it.
|
||||
Opcode.INLINE_ASSEMBLY -> "@inline@" + (ins.callLabel2 ?: "") // All of the inline assembly is stored in the calllabel2 property. the '@inline@' is a special marker to process it.
|
||||
Opcode.SYSCALL -> {
|
||||
if (ins.arg!!.numericValue() in syscallsForStackVm.map { it.callNr })
|
||||
throw CompilerException("cannot translate vm syscalls to real assembly calls - use *real* subroutine calls instead. Syscall ${ins.arg.numericValue()}")
|
||||
@@ -515,8 +486,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 -> {
|
||||
@@ -887,30 +859,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"
|
||||
|
||||
@@ -1278,9 +1240,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
Opcode.ROR2_WORD -> {
|
||||
AsmFragment(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+", 30)
|
||||
}
|
||||
// Opcode.SYSCALL -> {
|
||||
// TODO("optimize SYSCALL $ins in-place on variable $variable")
|
||||
// }
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -3327,44 +3286,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)
|
||||
|
||||
@@ -1055,11 +1055,12 @@ class Petscii {
|
||||
val lookup = if(lowercase) encodingPetsciiLowercase else encodingPetsciiUppercase
|
||||
return text.map {
|
||||
val petscii = lookup[it]
|
||||
if(petscii==null) {
|
||||
val case = if(lowercase) "lower" else "upper"
|
||||
petscii?.toShort() ?: if(it=='\u0000')
|
||||
0.toShort()
|
||||
else {
|
||||
val case = if (lowercase) "lower" else "upper"
|
||||
throw CharConversionException("no ${case}case Petscii character for '$it'")
|
||||
}
|
||||
petscii.toShort()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1072,11 +1073,12 @@ class Petscii {
|
||||
val lookup = if(lowercase) encodingScreencodeLowercase else encodingScreencodeUppercase
|
||||
return text.map{
|
||||
val screencode = lookup[it]
|
||||
if(screencode==null) {
|
||||
val case = if(lowercase) "lower" else "upper"
|
||||
screencode?.toShort() ?: if(it=='\u0000')
|
||||
0.toShort()
|
||||
else {
|
||||
val case = if (lowercase) "lower" else "upper"
|
||||
throw CharConversionException("no ${case}Screencode character for '$it'")
|
||||
}
|
||||
screencode.toShort()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,7 +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
|
||||
"sqrt16" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { Math.sqrt(it.toDouble()).toInt() } },
|
||||
"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) },
|
||||
@@ -53,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.UWORD, ::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 }},
|
||||
@@ -70,19 +70,21 @@ 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)),
|
||||
BuiltinFunctionParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
|
||||
"strlen" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", StringDatatypes)), DataType.UBYTE, ::builtinStrlen),
|
||||
"vm_write_memchr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", setOf(DataType.UWORD))), null),
|
||||
"vm_write_memstr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", setOf(DataType.UWORD))), null),
|
||||
"vm_write_num" to FunctionSignature(false, listOf(BuiltinFunctionParam("number", NumericDatatypes)), null),
|
||||
@@ -126,14 +128,14 @@ 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 '$function' requires one argument which is an iterable")
|
||||
else -> throw FatalAstException("function '$function' requires one argument which is an iterable")
|
||||
}
|
||||
}
|
||||
throw FatalAstException("function '$function' requires one argument which is an iterable")
|
||||
@@ -148,8 +150,8 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
|
||||
"abs" -> {
|
||||
val dt = args.single().resultingDatatype(namespace, heap)
|
||||
when(dt) {
|
||||
DataType.UBYTE, DataType.BYTE -> DataType.UBYTE
|
||||
DataType.UWORD, DataType.WORD -> DataType.UWORD
|
||||
in ByteDatatypes -> DataType.UBYTE
|
||||
in WordDatatypes -> DataType.UWORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("weird datatype passed to abs $dt")
|
||||
}
|
||||
@@ -157,13 +159,14 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
|
||||
"max", "min" -> {
|
||||
val dt = datatypeFromIterableArg(args.single())
|
||||
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 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" -> {
|
||||
@@ -175,9 +178,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
|
||||
}
|
||||
}
|
||||
@@ -294,8 +303,22 @@ private fun builtinAvg(args: List<IExpression>, position: Position, namespace:IN
|
||||
return numericLiteral(result, args[0].position)
|
||||
}
|
||||
|
||||
private fun builtinStrlen(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("strlen requires one argument", position)
|
||||
val argument = args[0].constValue(namespace, heap) ?: throw NotConstArgumentException()
|
||||
if(argument.type !in StringDatatypes)
|
||||
throw SyntaxError("strlen must have string argument", position)
|
||||
val string = argument.strvalue(heap)
|
||||
val zeroIdx = string.indexOf('\u0000')
|
||||
return if(zeroIdx>=0)
|
||||
LiteralValue.optimalInteger(zeroIdx, position=position)
|
||||
else
|
||||
LiteralValue.optimalInteger(string.length, position=position)
|
||||
}
|
||||
|
||||
private fun builtinLen(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
|
||||
// note: in some cases the length is > 255 and so we have to return a UWORD type instead of a byte.
|
||||
// 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)
|
||||
@@ -312,23 +335,22 @@ private fun builtinLen(args: List<IExpression>, position: Position, namespace:IN
|
||||
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).arraysize
|
||||
if(arraySize>256)
|
||||
throw CompilerException("array length exceeds byte limit ${argument.position}")
|
||||
LiteralValue(DataType.UWORD, wordvalue=arraySize, 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>256)
|
||||
throw CompilerException("array length exceeds byte limit ${argument.position}")
|
||||
LiteralValue(DataType.UWORD, wordvalue=arraySize, 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.UWORD, wordvalue=str.length, 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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -534,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?)
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import prog8.ast.*
|
||||
import prog8.compiler.LauncherType
|
||||
import prog8.compiler.OutputType
|
||||
import prog8.determineCompilationOptions
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
@@ -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
|
||||
@@ -13,7 +11,7 @@ class Program (val name: String,
|
||||
val program: MutableList<Instruction>,
|
||||
val variables: Map<String, Value>,
|
||||
val memoryPointers: Map<String, Pair<Int, DataType>>,
|
||||
val labels: Map<String, Instruction>,
|
||||
val labels: Map<String, Int>,
|
||||
val memory: Map<Int, List<Value>>,
|
||||
val heap: HeapValues)
|
||||
{
|
||||
@@ -22,7 +20,6 @@ class Program (val name: String,
|
||||
program.add(LabelInstr("____program_end", false))
|
||||
program.add(Instruction(Opcode.TERMINATE))
|
||||
program.add(Instruction(Opcode.NOP))
|
||||
connect()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -33,7 +30,7 @@ class Program (val name: String,
|
||||
val program = mutableListOf<Instruction>()
|
||||
val variables = mutableMapOf<String, Value>()
|
||||
val memoryPointers = mutableMapOf<String, Pair<Int, DataType>>()
|
||||
val labels = mutableMapOf<String, Instruction>()
|
||||
val labels = mutableMapOf<String, Int>()
|
||||
|
||||
while(lines.hasNext()) {
|
||||
val (lineNr, line) = lines.next()
|
||||
@@ -55,7 +52,7 @@ class Program (val name: String,
|
||||
program: MutableList<Instruction>,
|
||||
variables: MutableMap<String, Value>,
|
||||
memoryPointers: MutableMap<String, Pair<Int, DataType>>,
|
||||
labels: MutableMap<String, Instruction>)
|
||||
labels: MutableMap<String, Int>)
|
||||
{
|
||||
while(true) {
|
||||
val (_, line) = lines.next()
|
||||
@@ -69,8 +66,10 @@ class Program (val name: String,
|
||||
loadMemoryPointers(lines, memoryPointers, heap)
|
||||
else if(line=="%instructions") {
|
||||
val (blockInstructions, blockLabels) = loadInstructions(lines, heap)
|
||||
val baseIndex = program.size
|
||||
program.addAll(blockInstructions)
|
||||
labels.putAll(blockLabels)
|
||||
val labelsWithIndex = blockLabels.mapValues { baseIndex+blockInstructions.indexOf(it.value) }
|
||||
labels.putAll(labelsWithIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,10 +87,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 +99,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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,8 +143,19 @@ class Program (val name: String,
|
||||
Instruction(opcode, callLabel = withoutQuotes)
|
||||
}
|
||||
Opcode.SYSCALL -> {
|
||||
val call = Syscall.valueOf(args!!)
|
||||
Instruction(opcode, Value(DataType.UBYTE, call.callNr))
|
||||
if(args!! in syscallNames) {
|
||||
val call = Syscall.valueOf(args)
|
||||
Instruction(opcode, Value(DataType.UBYTE, call.callNr))
|
||||
} else {
|
||||
val args2 = args.replace('.', '_')
|
||||
if(args2 in syscallNames) {
|
||||
val call = Syscall.valueOf(args2)
|
||||
Instruction(opcode, Value(DataType.UBYTE, call.callNr))
|
||||
} else {
|
||||
// the syscall is not yet implemented. emit a stub.
|
||||
Instruction(Opcode.SYSCALL, Value(DataType.UBYTE, Syscall.SYSCALLSTUB.callNr), callLabel = args2)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Instruction(opcode, getArgValue(args, heap))
|
||||
@@ -200,7 +208,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 +218,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 +226,7 @@ class Program (val name: String,
|
||||
Value(type, heapId)
|
||||
}
|
||||
}
|
||||
else -> throw VmExecutionException("weird datatype")
|
||||
}
|
||||
vars[name] = value
|
||||
}
|
||||
@@ -268,50 +273,4 @@ class Program (val name: String,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun connect() {
|
||||
val it1 = program.iterator()
|
||||
val it2 = program.iterator()
|
||||
it2.next()
|
||||
|
||||
while(it1.hasNext() && it2.hasNext()) {
|
||||
val instr = it1.next()
|
||||
val nextInstr = it2.next()
|
||||
when(instr.opcode) {
|
||||
Opcode.TERMINATE -> instr.next = instr // won't ever execute a next instruction
|
||||
Opcode.RETURN -> instr.next = instr // kinda a special one, in actuality the return instruction is dynamic
|
||||
Opcode.JUMP -> {
|
||||
if(instr.callLabel==null) {
|
||||
throw VmExecutionException("stackVm doesn't support JUMP to memory address")
|
||||
} else {
|
||||
// jump to label
|
||||
val target = labels[instr.callLabel] ?: throw VmExecutionException("undefined label: ${instr.callLabel}")
|
||||
instr.next = target
|
||||
}
|
||||
}
|
||||
Opcode.BCC, Opcode.BCS, Opcode.BZ, Opcode.BNZ, Opcode.BNEG, Opcode.BPOS, Opcode.JZ, Opcode.JNZ, Opcode.JZW, Opcode.JNZW -> {
|
||||
if(instr.callLabel==null) {
|
||||
throw VmExecutionException("stackVm doesn't support branch to memory address")
|
||||
} else {
|
||||
// branch to label
|
||||
val jumpInstr = labels[instr.callLabel] ?: throw VmExecutionException("undefined label: ${instr.callLabel}")
|
||||
instr.next = jumpInstr
|
||||
instr.nextAlt = nextInstr
|
||||
}
|
||||
}
|
||||
Opcode.CALL -> {
|
||||
if(instr.callLabel==null) {
|
||||
throw VmExecutionException("stackVm doesn't support CALL to memory address")
|
||||
} else {
|
||||
// call label
|
||||
val jumpInstr = labels[instr.callLabel] ?: throw VmExecutionException("undefined label: ${instr.callLabel}")
|
||||
instr.next = jumpInstr
|
||||
instr.nextAlt = nextInstr // instruction to return to
|
||||
}
|
||||
}
|
||||
else -> instr.next = nextInstr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ class BitmapScreenPanel : KeyListener, JPanel() {
|
||||
|
||||
private val image = BufferedImage(SCREENWIDTH, SCREENHEIGHT, BufferedImage.TYPE_INT_ARGB)
|
||||
private val g2d = image.graphics as Graphics2D
|
||||
private var cursorX: Int=0
|
||||
private var cursorY: Int=0
|
||||
|
||||
init {
|
||||
val size = Dimension(image.width * SCALING, image.height * SCALING)
|
||||
@@ -48,6 +50,8 @@ class BitmapScreenPanel : KeyListener, JPanel() {
|
||||
fun clearScreen(color: Int) {
|
||||
g2d.background = palette[color and 15]
|
||||
g2d.clearRect(0, 0, BitmapScreenPanel.SCREENWIDTH, BitmapScreenPanel.SCREENHEIGHT)
|
||||
cursorX = 0
|
||||
cursorY = 0
|
||||
}
|
||||
fun setPixel(x: Int, y: Int, color: Int) {
|
||||
image.setRGB(x, y, palette[color and 15].rgb)
|
||||
@@ -56,25 +60,74 @@ class BitmapScreenPanel : KeyListener, JPanel() {
|
||||
g2d.color = palette[color and 15]
|
||||
g2d.drawLine(x1, y1, x2, y2)
|
||||
}
|
||||
fun writeText(x: Int, y: Int, text: String, color: Int) {
|
||||
if(color!=1) {
|
||||
TODO("text can only be white for now")
|
||||
}
|
||||
var xx=x
|
||||
var yy=y
|
||||
for(sc in Petscii.encodeScreencode(text, true)) {
|
||||
setChar(xx, yy, sc)
|
||||
xx++
|
||||
if(xx>=(SCREENWIDTH/8)) {
|
||||
yy++
|
||||
xx=0
|
||||
fun printText(text: String, color: Int, lowercase: Boolean) {
|
||||
val lines = text.split('\n')
|
||||
for(line in lines.withIndex()) {
|
||||
printTextSingleLine(line.value, color, lowercase)
|
||||
if(line.index<lines.size-1) {
|
||||
cursorX=0
|
||||
cursorY++
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun printTextSingleLine(text: String, color: Int, lowercase: Boolean) {
|
||||
if(color!=1) {
|
||||
TODO("text can only be white for now")
|
||||
}
|
||||
for(clearx in cursorX until cursorX+text.length) {
|
||||
g2d.clearRect(8*clearx, 8*y, 8, 8)
|
||||
}
|
||||
for(sc in Petscii.encodeScreencode(text, lowercase)) {
|
||||
setChar(cursorX, cursorY, sc)
|
||||
cursorX++
|
||||
if(cursorX>=(SCREENWIDTH/8)) {
|
||||
cursorY++
|
||||
cursorX=0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun printChar(char: Short) {
|
||||
if(char==13.toShort() || char==141.toShort()) {
|
||||
cursorX=0
|
||||
cursorY++
|
||||
} else {
|
||||
setChar(cursorX, cursorY, char)
|
||||
cursorX++
|
||||
if (cursorX >= (SCREENWIDTH / 8)) {
|
||||
cursorY++
|
||||
cursorX = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setChar(x: Int, y: Int, screenCode: Short) {
|
||||
g2d.clearRect(8*x, 8*y, 8, 8)
|
||||
g2d.drawImage(Charset.shiftedChars[screenCode.toInt()], 8*x, 8*y , null)
|
||||
}
|
||||
|
||||
fun setCursorPos(x: Int, y: Int) {
|
||||
cursorX = x
|
||||
cursorY = y
|
||||
}
|
||||
|
||||
fun getCursorPos(): Pair<Int, Int> {
|
||||
return Pair(cursorX, cursorY)
|
||||
}
|
||||
|
||||
fun writeText(x: Int, y: Int, text: String, color: Int, lowercase: Boolean) {
|
||||
var xx=x
|
||||
if(color!=1) {
|
||||
TODO("text can only be white for now")
|
||||
}
|
||||
for(clearx in xx until xx+text.length) {
|
||||
g2d.clearRect(8*clearx, 8*y, 8, 8)
|
||||
}
|
||||
for(sc in Petscii.encodeScreencode(text, lowercase)) {
|
||||
setChar(xx++, y, sc)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
const val SCREENWIDTH = 320
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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
|
||||
@@ -55,7 +58,7 @@ class TestStackVmOpcodes {
|
||||
private fun makeProg(ins: MutableList<Instruction>,
|
||||
vars: Map<String, Value>?=null,
|
||||
memoryPointers: Map<String, Pair<Int, DataType>>?=null,
|
||||
labels: Map<String, Instruction>?=null,
|
||||
labels: Map<String, Int>?=null,
|
||||
mem: Map<Int, List<Value>>?=null) : Program {
|
||||
val heap = HeapValues()
|
||||
return Program("test", ins, vars ?: mapOf(), memoryPointers ?: mapOf(), labels ?: mapOf(), mem ?: mapOf(), heap)
|
||||
@@ -748,7 +751,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.LINE, callLabel = "string1"),
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, callLabel = "string2"))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
vm.step(2)
|
||||
assertEquals("", vm.sourceLine)
|
||||
@@ -768,7 +771,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.LINE, callLabel = "string1"),
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, callLabel = "string2"))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
assertFalse(vm.P_carry)
|
||||
vm.step(2)
|
||||
@@ -789,7 +792,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.LINE, callLabel = "string1"),
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, callLabel = "string2"))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
vm.step(2)
|
||||
assertEquals("", vm.sourceLine)
|
||||
@@ -809,7 +812,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.LINE, callLabel = "string1"),
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, callLabel = "string2"))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
vm.step(2)
|
||||
assertEquals("", vm.sourceLine)
|
||||
@@ -829,7 +832,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.LINE, callLabel = "string1"),
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, callLabel = "string2"))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
vm.step(2)
|
||||
assertEquals("", vm.sourceLine)
|
||||
@@ -849,7 +852,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.LINE, callLabel = "string1"),
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, callLabel = "string2"))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
vm.step(2)
|
||||
assertEquals("", vm.sourceLine)
|
||||
@@ -866,7 +869,7 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.LINE, callLabel = "string1"),
|
||||
Instruction(Opcode.TERMINATE),
|
||||
Instruction(Opcode.LINE, callLabel = "string2"))
|
||||
val labels = mapOf("label" to ins.last()) // points to the second LINE instruction
|
||||
val labels = mapOf("label" to ins.size-1) // points to the second LINE instruction
|
||||
vm.load(makeProg(ins, labels=labels), null)
|
||||
vm.step(2)
|
||||
assertEquals("string2", vm.sourceLine)
|
||||
@@ -886,7 +889,7 @@ class TestStackVmOpcodes {
|
||||
vm.step(1)
|
||||
}
|
||||
|
||||
vm.callstack.add(ins[2]) // set the LINE opcode as return instruction
|
||||
vm.callstack.add(2) // set the LINE opcode as return instruction
|
||||
assertEquals("", vm.sourceLine)
|
||||
vm.step(2)
|
||||
assertEquals("string1", vm.sourceLine)
|
||||
@@ -903,12 +906,12 @@ class TestStackVmOpcodes {
|
||||
Instruction(Opcode.LINE, callLabel = "called"),
|
||||
Instruction(Opcode.RETURN)
|
||||
)
|
||||
val labels = mapOf("label" to ins[3]) // points to the LINE instruction
|
||||
val labels = mapOf("label" to 3) // points to the LINE instruction
|
||||
vm.load(makeProg(ins, labels = labels), null)
|
||||
vm.step(1)
|
||||
assertEquals("", vm.sourceLine)
|
||||
assertEquals(1, vm.callstack.size)
|
||||
assertSame(ins[1], vm.callstack.peek())
|
||||
assertSame(1, vm.callstack.peek())
|
||||
vm.step(1)
|
||||
assertEquals("called", vm.sourceLine)
|
||||
vm.step(1)
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,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: 64 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 49 KiB |
@@ -25,6 +25,18 @@ The project is on github: https://github.com/irmen/prog8.git
|
||||
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
|
||||
.. image:: _static/cube3d.png
|
||||
:width: 33%
|
||||
:alt: 3d rotating sprites
|
||||
.. image:: _static/wizzine.png
|
||||
:width: 33%
|
||||
:alt: Simple wizzine sprite effect
|
||||
.. image:: _static/tehtriz.png
|
||||
:width: 33%
|
||||
:alt: Fully playable tetris clone
|
||||
|
||||
|
||||
Code example
|
||||
------------
|
||||
|
||||
|
||||
+63
-10
@@ -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
|
||||
@@ -462,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
|
||||
@@ -494,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
|
||||
-----------
|
||||
@@ -517,7 +544,9 @@ will issue a warning then telling you the result values of a subroutine call are
|
||||
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.
|
||||
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:
|
||||
@@ -575,8 +604,11 @@ ln(x)
|
||||
log2(x)
|
||||
Base 2 logarithm.
|
||||
|
||||
sqrt16(w)
|
||||
16 bit unsigned integer Square root. Result is unsigned byte.
|
||||
|
||||
sqrt(x)
|
||||
Square root.
|
||||
Floating point Square root.
|
||||
|
||||
round(x)
|
||||
Rounds the floating point to the closest integer.
|
||||
@@ -610,6 +642,11 @@ len(x)
|
||||
Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte.
|
||||
Note: lengths of strings and arrays are determined at compile-time! If your program modifies the actual
|
||||
length of the string during execution, the value of len(string) may no longer be correct!
|
||||
(use strlen function if you want to dynamically determine the length)
|
||||
|
||||
strlen(str)
|
||||
Number of bytes in the string. This value is determined during runtime and counts upto
|
||||
the first terminating 0 byte in the string, regardless of the size of the string during compilation time.
|
||||
|
||||
lsb(x)
|
||||
Get the least significant byte of the word x. Equivalent to the cast "x as ubyte".
|
||||
@@ -708,3 +745,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.
|
||||
@@ -213,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::
|
||||
|
||||
@@ -228,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
|
||||
@@ -254,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.
|
||||
@@ -361,8 +361,12 @@ 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: ``+`` ``-`` ``*`` ``/`` ``**`` ``%``
|
||||
@@ -431,10 +435,20 @@ You call a subroutine like this::
|
||||
[ result = ] subroutinename_or_address ( [argument...] )
|
||||
|
||||
; example:
|
||||
resultvariable = subroutine ( arg1, arg2, arg3 )
|
||||
resultvariable = subroutine(arg1, arg2, arg3)
|
||||
|
||||
Arguments are separated by commas. The argument list can also be empty if the subroutine
|
||||
takes no parameters.
|
||||
takes no parameters. If the subroutine returns a value, you can still omit the assignment to
|
||||
a result variable (but the compiler will warn you about discarding the result of the call).
|
||||
|
||||
Normal subroutines can only return zero or one return values.
|
||||
However, the special ``asmsub`` routines (implemented in assembly code or referencing
|
||||
a routine in kernel ROM) can return more than one return values, for instance a status
|
||||
in the carry bit and a number in A, or a 16-bit value in A/Y registers.
|
||||
Only for these kind of subroutines it is possible to write a multi value assignment to
|
||||
store the resulting values::
|
||||
|
||||
var1, var2, var3 = asmsubroutine()
|
||||
|
||||
|
||||
|
||||
@@ -510,6 +524,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
|
||||
^^^^^^^^^^
|
||||
|
||||
|
||||
@@ -117,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.
|
||||
@@ -144,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
|
||||
+2
-2
@@ -44,10 +44,10 @@ sub start() {
|
||||
|
||||
sub print_notes(ubyte n1, ubyte n2) {
|
||||
c64.CHROUT('\n')
|
||||
c64scr.PLOT(n1/2, 24)
|
||||
c64scr.plot(n1/2, 24)
|
||||
c64.COLOR=7
|
||||
c64.CHROUT('Q')
|
||||
c64scr.PLOT(n2/2, 24)
|
||||
c64scr.plot(n2/2, 24)
|
||||
c64.COLOR=4
|
||||
c64.CHROUT('Q')
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -24,7 +24,7 @@
|
||||
c64scr.clear_screenchars(32)
|
||||
draw_edges()
|
||||
time+=0.2
|
||||
c64scr.PLOT(0,0)
|
||||
c64scr.plot(0,0)
|
||||
c64scr.print("3d cube! (float) ")
|
||||
c64scr.print_ub(c64.TIME_LO)
|
||||
c64scr.print(" jiffies/frame")
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
anglex-=500
|
||||
angley+=217
|
||||
anglez+=452
|
||||
c64scr.PLOT(0,0)
|
||||
c64scr.plot(0,0)
|
||||
c64scr.print("3d cube! (sprites) ")
|
||||
c64scr.print_ub(c64.TIME_LO)
|
||||
c64scr.print(" jiffies/frame ")
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@
|
||||
anglex+=1000
|
||||
angley+=433
|
||||
anglez+=907
|
||||
c64scr.PLOT(0,0)
|
||||
c64scr.plot(0,0)
|
||||
c64scr.print("3d cube! (integer) ")
|
||||
c64scr.print_ub(c64.TIME_LO)
|
||||
c64scr.print(" jiffies/frame")
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
float minutes = floor(clock_seconds / 60)
|
||||
clock_seconds = floor(clock_seconds - minutes * 60.0)
|
||||
|
||||
; @todo implement strcpy/strcat/strlen?
|
||||
c64scr.print("system time in ti$ is ")
|
||||
c64flt.print_f(hours)
|
||||
c64.CHROUT(':')
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
~ main {
|
||||
const uword width = 30
|
||||
@@ -8,7 +10,7 @@
|
||||
const ubyte max_iter = 16
|
||||
|
||||
sub start() {
|
||||
c64scr.print("calculating mandelbrot fractal...\n")
|
||||
c64scr.print("calculating mandelbrot fractal...")
|
||||
|
||||
c64.TIME_HI=0
|
||||
c64.TIME_MID=0
|
||||
@@ -40,7 +42,7 @@
|
||||
float duration = floor(((c64.TIME_LO as float)
|
||||
+ 256.0*(c64.TIME_MID as float)
|
||||
+ 65536.0*(c64.TIME_HI as float))/60.0)
|
||||
c64scr.PLOT(0, 21)
|
||||
c64scr.plot(0, 21)
|
||||
c64scr.print("finished in ")
|
||||
c64flt.print_f(duration)
|
||||
c64scr.print(" seconds!\n")
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
c64scr.print("es")
|
||||
c64scr.print(" left.\nWhat is your next guess? ")
|
||||
c64scr.input_chars(input)
|
||||
ubyte guess = c64utils.str2ubyte(input)
|
||||
ubyte guess = lsb(c64utils.str2uword(input))
|
||||
|
||||
if guess==secretnumber {
|
||||
return ending(true)
|
||||
|
||||
+9
-3
@@ -4,25 +4,31 @@
|
||||
~ main {
|
||||
|
||||
ubyte[256] sieve
|
||||
ubyte candidate_prime = 2
|
||||
ubyte candidate_prime = 2 ; is increased in the loop
|
||||
|
||||
sub start() {
|
||||
memset(sieve, 256, false) ; clear the sieve
|
||||
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
|
||||
@@ -31,7 +37,7 @@
|
||||
|
||||
; found next one, mark the multiples and return it.
|
||||
sieve[candidate_prime] = true
|
||||
uword multiple = candidate_prime**2
|
||||
uword multiple = candidate_prime
|
||||
while multiple < len(sieve) {
|
||||
sieve[lsb(multiple)] = true
|
||||
multiple += candidate_prime
|
||||
|
||||
@@ -23,23 +23,19 @@
|
||||
ubyte ypos = 0
|
||||
|
||||
sub irq() {
|
||||
Y++ ; delay for alignment
|
||||
Y++ ; delay for alignment
|
||||
Y++ ; delay for alignment
|
||||
Y++ ; delay for alignment
|
||||
Y++ ; slight timing delay to avoid rasterline transition issues
|
||||
ubyte rasterpos = c64.RASTER
|
||||
|
||||
if color!=len(colors) {
|
||||
c64.EXTCOL = colors[color]
|
||||
c64.RASTER = rasterpos+barheight
|
||||
color++
|
||||
c64.RASTER = rasterpos+barheight
|
||||
}
|
||||
else {
|
||||
Y++ ; delay for alignment
|
||||
Y++ ; delay for alignment
|
||||
ypos += 2
|
||||
c64.EXTCOL = 0
|
||||
c64.RASTER = sin8u(ypos)/2+40
|
||||
color = 0
|
||||
c64.RASTER = sin8u(ypos)/2+40
|
||||
}
|
||||
}
|
||||
}
|
||||
+595
-59
@@ -1,100 +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
|
||||
|
||||
; 3x3, rotating around their center square:
|
||||
ubyte[4] blockJ = [0, 4, 5, 6]
|
||||
ubyte[4] blockL = [2, 4, 5, 6]
|
||||
ubyte[4] blockS = [1, 2, 4, 5]
|
||||
ubyte[4] blockT = [1, 4, 5, 6]
|
||||
ubyte[4] blockZ = [0, 1, 5, 6]
|
||||
;4x4, rotating around center:
|
||||
ubyte[4] blockI = [4, 5, 6, 7]
|
||||
ubyte[4] blockO = [1, 2, 5, 6]
|
||||
|
||||
; block colors I, J, L, O, S, T, Z: cyan, blue, orange, yellow, green, purple, red
|
||||
ubyte[7] blockColors = [3, 6, 8, 7, 5, 4, 2]
|
||||
ubyte[7] blockSizes = [4, 3, 3, 4, 3, 3, 3] ; needed for proper rotation? (or just use block num?)
|
||||
|
||||
ubyte[16] currentBlock
|
||||
ubyte currentBlockSize ; 3 or 4
|
||||
ubyte currentBlockNum
|
||||
|
||||
sub start() {
|
||||
@(650) = 128 ; set all keys to repeat
|
||||
sound.init()
|
||||
newGame()
|
||||
drawBoard()
|
||||
gameOver()
|
||||
|
||||
for ubyte b in 7 to 0 step -1 {
|
||||
newCurrentBlock(b)
|
||||
drawBlock(3, 2+b*3, 102) ; 102 = stipple
|
||||
drawBlock(boardOffsetX+3, 1+b*3, 160) ; 160 = block, 32 = erase (space)
|
||||
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()
|
||||
}
|
||||
|
||||
while(true) {
|
||||
; loop
|
||||
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() {
|
||||
c64scr.PLOT(1,1)
|
||||
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
|
||||
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 newCurrentBlock(ubyte block) {
|
||||
memset(currentBlock, len(currentBlock), 0)
|
||||
currentBlockNum = block
|
||||
currentBlockSize = blockSizes[block]
|
||||
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)
|
||||
}
|
||||
|
||||
; @todo would be nice to have an explicit pointer type to reference the array, and code the loop only once...
|
||||
ubyte blockCol = blockColors[block]
|
||||
ubyte i
|
||||
if block==0 { ; I
|
||||
for i in blockI
|
||||
currentBlock[i] = blockCol
|
||||
; 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)
|
||||
}
|
||||
else if block==1 { ; J
|
||||
for i in blockJ
|
||||
currentBlock[i] = blockCol
|
||||
}
|
||||
else if block==2 { ; L
|
||||
for i in blockL
|
||||
currentBlock[i] = blockCol
|
||||
}
|
||||
else if block==3 { ; O
|
||||
for i in blockO
|
||||
currentBlock[i] = blockCol
|
||||
}
|
||||
else if block==4 { ; S
|
||||
for i in blockS
|
||||
currentBlock[i] = blockCol
|
||||
}
|
||||
else if block==5 { ; T
|
||||
for i in blockT
|
||||
currentBlock[i] = blockCol
|
||||
}
|
||||
else if block==6 { ; Z
|
||||
for i in blockZ
|
||||
currentBlock[i] = blockCol
|
||||
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=currentBlock[i]
|
||||
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
|
||||
}
|
||||
}
|
||||
+6
-33
@@ -1,42 +1,15 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
~ main {
|
||||
|
||||
; mul_word_3
|
||||
ubyte @zp var1
|
||||
|
||||
sub start() {
|
||||
|
||||
byte b1
|
||||
byte b2 = -3
|
||||
|
||||
ubyte ub1
|
||||
ubyte ub2 = 4
|
||||
|
||||
word w1
|
||||
word w2 = -499
|
||||
|
||||
uword uw1
|
||||
uword uw2 = 1199
|
||||
|
||||
b1 = b2*40
|
||||
ub1 = ub2*40
|
||||
w1 = w2*40
|
||||
uw1 = uw2*40
|
||||
|
||||
c64scr.print_b(b1)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_ub(ub1)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_w(w1)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_uw(uw1)
|
||||
c64.CHROUT('\n')
|
||||
c64.CHROUT('\n')
|
||||
|
||||
|
||||
c64scr.print_ub(X)
|
||||
c64.CHROUT('\n')
|
||||
ubyte @zp var1
|
||||
A=20
|
||||
A=var1
|
||||
Y=main.var1
|
||||
}
|
||||
|
||||
}
|
||||
@@ -111,7 +111,7 @@ 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 ']' ;
|
||||
|
||||
|
||||
+4
-2
@@ -8,7 +8,9 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
antlr 'org.antlr:antlr4:4.7.2'
|
||||
antlr('org.antlr:antlr4:4.7.2') {
|
||||
exclude group: 'com.ibm.icu', module: 'icu4j'
|
||||
}
|
||||
}
|
||||
|
||||
compileJava {
|
||||
@@ -29,4 +31,4 @@ sourceSets {
|
||||
srcDirs = ["${project.projectDir}/antlr"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated from prog8.g4 by ANTLR 4.7.2
|
||||
// Generated from ./parser/antlr/prog8.g4 by ANTLR 4.7.2
|
||||
|
||||
package prog8.parser;
|
||||
|
||||
@@ -34,10 +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,
|
||||
ZEROPAGE=125;
|
||||
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"
|
||||
};
|
||||
@@ -61,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", "ZEROPAGE"
|
||||
"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();
|
||||
@@ -74,19 +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'", null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, "'@zp'"
|
||||
"'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();
|
||||
@@ -101,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", "ZEROPAGE"
|
||||
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();
|
||||
@@ -167,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;
|
||||
}
|
||||
@@ -213,299 +212,292 @@ public class prog8Lexer extends Lexer {
|
||||
}
|
||||
|
||||
public static final String _serializedATN =
|
||||
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\177\u0373\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\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t"+
|
||||
" \4!\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t"+
|
||||
"+\4,\t,\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64"+
|
||||
"\t\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\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\4"+
|
||||
"I\tI\4J\tJ\4K\tK\4L\tL\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\t"+
|
||||
"T\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"+
|
||||
"\tk\4l\tl\4m\tm\4n\tn\4o\to\4p\tp\4q\tq\4r\tr\4s\ts\4t\tt\4u\tu\4v\tv"+
|
||||
"\4w\tw\4x\tx\4y\ty\4z\tz\4{\t{\4|\t|\4}\t}\4~\t~\4\177\t\177\4\u0080\t"+
|
||||
"\u0080\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\3"+
|
||||
"9\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\3B\3B\3C\3C\3D\3D\3D\3E\3E\3F\3F\3F\3F\3F\3F\3F\3G\3"+
|
||||
"G\3G\3G\3G\3G\3H\3H\3H\3H\3H\3H\3H\3H\3H\3I\3I\3J\3J\3K\3K\3L\3L\3M\3"+
|
||||
"M\3M\3N\3N\3N\3O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3R\3R\3R\3S\3S\3S\3T\3T\3T\3"+
|
||||
"U\3U\3U\3U\3U\3V\3V\3V\3V\3V\3V\3W\3W\3W\3W\3W\3X\3X\3X\3X\3Y\3Y\3Y\3"+
|
||||
"Z\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\3"+
|
||||
"b\3b\3c\3c\3c\3c\3c\3c\3d\3d\3d\3d\3d\3e\3e\3e\3e\3e\3e\3f\3f\3f\3f\3"+
|
||||
"f\3f\3g\3g\3g\3g\3g\3g\3h\3h\3h\3h\3h\3h\3h\3i\3i\3i\3i\3i\3i\3j\3j\3"+
|
||||
"j\3j\3j\3j\3j\3k\3k\3k\3k\3k\3k\3l\3l\3l\3l\3l\3l\3m\3m\3m\3m\3n\3n\3"+
|
||||
"n\3o\3o\3o\3o\3o\3o\3p\3p\3p\3p\3p\3p\3p\3q\3q\3q\3q\3q\3q\3r\3r\7r\u02fe"+
|
||||
"\nr\fr\16r\u0301\13r\3r\3r\3r\3r\3s\3s\7s\u0309\ns\fs\16s\u030c\13s\3"+
|
||||
"s\3s\3t\3t\3t\3t\3u\6u\u0315\nu\ru\16u\u0316\3v\3v\7v\u031b\nv\fv\16v"+
|
||||
"\u031e\13v\3w\3w\3w\6w\u0323\nw\rw\16w\u0324\5w\u0327\nw\3x\3x\6x\u032b"+
|
||||
"\nx\rx\16x\u032c\3y\3y\6y\u0331\ny\ry\16y\u0332\3z\3z\3z\5z\u0338\nz\3"+
|
||||
"z\5z\u033b\nz\3{\6{\u033e\n{\r{\16{\u033f\3{\3{\6{\u0344\n{\r{\16{\u0345"+
|
||||
"\5{\u0348\n{\3|\3|\3|\3|\5|\u034e\n|\3}\3}\3}\7}\u0353\n}\f}\16}\u0356"+
|
||||
"\13}\3}\3}\3}\3~\3~\3~\3~\6~\u035f\n~\r~\16~\u0360\3~\3~\3~\3~\3~\3\177"+
|
||||
"\3\177\3\177\5\177\u036b\n\177\3\177\3\177\3\177\3\u0080\3\u0080\3\u0080"+
|
||||
"\3\u0080\3\u0360\2\u0081\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\62"+
|
||||
"c\63e\64g\65i\66k\67m8o9q:s;u<w=y>{?}@\177A\u0081B\u0083C\u0085D\u0087"+
|
||||
"E\u0089F\u008bG\u008dH\u008fI\u0091J\u0093K\u0095L\u0097M\u0099N\u009b"+
|
||||
"O\u009dP\u009fQ\u00a1R\u00a3S\u00a5T\u00a7U\u00a9V\u00abW\u00adX\u00af"+
|
||||
"Y\u00b1Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb_\u00bd`\u00bfa\u00c1b\u00c3"+
|
||||
"c\u00c5d\u00c7e\u00c9f\u00cbg\u00cdh\u00cfi\u00d1j\u00d3k\u00d5l\u00d7"+
|
||||
"m\u00d9n\u00dbo\u00ddp\u00dfq\u00e1r\u00e3s\u00e5t\u00e7u\u00e9v\u00eb"+
|
||||
"w\u00edx\u00efy\u00f1z\u00f3{\u00f5\2\u00f7\2\u00f9|\u00fb}\u00fd~\u00ff"+
|
||||
"\177\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\u0382\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\2"+
|
||||
"Y\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\2\u00ff\3\2\2"+
|
||||
"\2\3\u0101\3\2\2\2\5\u0103\3\2\2\2\7\u0105\3\2\2\2\t\u010a\3\2\2\2\13"+
|
||||
"\u0112\3\2\2\2\r\u011c\3\2\2\2\17\u0126\3\2\2\2\21\u0132\3\2\2\2\23\u013b"+
|
||||
"\3\2\2\2\25\u0143\3\2\2\2\27\u014f\3\2\2\2\31\u015b\3\2\2\2\33\u0166\3"+
|
||||
"\2\2\2\35\u016e\3\2\2\2\37\u0170\3\2\2\2!\u0172\3\2\2\2#\u0178\3\2\2\2"+
|
||||
"%\u017f\3\2\2\2\'\u0185\3\2\2\2)\u018a\3\2\2\2+\u0190\3\2\2\2-\u0195\3"+
|
||||
"\2\2\2/\u019b\3\2\2\2\61\u019f\3\2\2\2\63\u01a5\3\2\2\2\65\u01ab\3\2\2"+
|
||||
"\2\67\u01b2\3\2\2\29\u01b4\3\2\2\2;\u01b6\3\2\2\2=\u01b9\3\2\2\2?\u01bc"+
|
||||
"\3\2\2\2A\u01bf\3\2\2\2C\u01c2\3\2\2\2E\u01c6\3\2\2\2G\u01c9\3\2\2\2I"+
|
||||
"\u01cc\3\2\2\2K\u01cf\3\2\2\2M\u01d2\3\2\2\2O\u01d6\3\2\2\2Q\u01da\3\2"+
|
||||
"\2\2S\u01dd\3\2\2\2U\u01e0\3\2\2\2W\u01e2\3\2\2\2Y\u01e4\3\2\2\2[\u01e7"+
|
||||
"\3\2\2\2]\u01e9\3\2\2\2_\u01eb\3\2\2\2a\u01ed\3\2\2\2c\u01f0\3\2\2\2e"+
|
||||
"\u01f3\3\2\2\2g\u01f5\3\2\2\2i\u01f7\3\2\2\2k\u01fa\3\2\2\2m\u01fd\3\2"+
|
||||
"\2\2o\u0200\3\2\2\2q\u0203\3\2\2\2s\u0205\3\2\2\2u\u0207\3\2\2\2w\u0209"+
|
||||
"\3\2\2\2y\u020c\3\2\2\2{\u0211\3\2\2\2}\u0215\3\2\2\2\177\u0218\3\2\2"+
|
||||
"\2\u0081\u021c\3\2\2\2\u0083\u0220\3\2\2\2\u0085\u0222\3\2\2\2\u0087\u0224"+
|
||||
"\3\2\2\2\u0089\u0227\3\2\2\2\u008b\u0229\3\2\2\2\u008d\u0230\3\2\2\2\u008f"+
|
||||
"\u0236\3\2\2\2\u0091\u023f\3\2\2\2\u0093\u0241\3\2\2\2\u0095\u0243\3\2"+
|
||||
"\2\2\u0097\u0245\3\2\2\2\u0099\u0247\3\2\2\2\u009b\u024a\3\2\2\2\u009d"+
|
||||
"\u024d\3\2\2\2\u009f\u0250\3\2\2\2\u00a1\u0253\3\2\2\2\u00a3\u0256\3\2"+
|
||||
"\2\2\u00a5\u0259\3\2\2\2\u00a7\u025c\3\2\2\2\u00a9\u025f\3\2\2\2\u00ab"+
|
||||
"\u0264\3\2\2\2\u00ad\u026a\3\2\2\2\u00af\u026f\3\2\2\2\u00b1\u0273\3\2"+
|
||||
"\2\2\u00b3\u0276\3\2\2\2\u00b5\u0278\3\2\2\2\u00b7\u027a\3\2\2\2\u00b9"+
|
||||
"\u0281\3\2\2\2\u00bb\u028a\3\2\2\2\u00bd\u0290\3\2\2\2\u00bf\u0293\3\2"+
|
||||
"\2\2\u00c1\u0298\3\2\2\2\u00c3\u029e\3\2\2\2\u00c5\u02a4\3\2\2\2\u00c7"+
|
||||
"\u02aa\3\2\2\2\u00c9\u02af\3\2\2\2\u00cb\u02b5\3\2\2\2\u00cd\u02bb\3\2"+
|
||||
"\2\2\u00cf\u02c1\3\2\2\2\u00d1\u02c8\3\2\2\2\u00d3\u02ce\3\2\2\2\u00d5"+
|
||||
"\u02d5\3\2\2\2\u00d7\u02db\3\2\2\2\u00d9\u02e1\3\2\2\2\u00db\u02e5\3\2"+
|
||||
"\2\2\u00dd\u02e8\3\2\2\2\u00df\u02ee\3\2\2\2\u00e1\u02f5\3\2\2\2\u00e3"+
|
||||
"\u02fb\3\2\2\2\u00e5\u0306\3\2\2\2\u00e7\u030f\3\2\2\2\u00e9\u0314\3\2"+
|
||||
"\2\2\u00eb\u0318\3\2\2\2\u00ed\u0326\3\2\2\2\u00ef\u0328\3\2\2\2\u00f1"+
|
||||
"\u032e\3\2\2\2\u00f3\u0334\3\2\2\2\u00f5\u033d\3\2\2\2\u00f7\u034d\3\2"+
|
||||
"\2\2\u00f9\u034f\3\2\2\2\u00fb\u035a\3\2\2\2\u00fd\u0367\3\2\2\2\u00ff"+
|
||||
"\u036f\3\2\2\2\u0101\u0102\7\u0080\2\2\u0102\4\3\2\2\2\u0103\u0104\7<"+
|
||||
"\2\2\u0104\6\3\2\2\2\u0105\u0106\7i\2\2\u0106\u0107\7q\2\2\u0107\u0108"+
|
||||
"\7v\2\2\u0108\u0109\7q\2\2\u0109\b\3\2\2\2\u010a\u010b\7\'\2\2\u010b\u010c"+
|
||||
"\7q\2\2\u010c\u010d\7w\2\2\u010d\u010e\7v\2\2\u010e\u010f\7r\2\2\u010f"+
|
||||
"\u0110\7w\2\2\u0110\u0111\7v\2\2\u0111\n\3\2\2\2\u0112\u0113\7\'\2\2\u0113"+
|
||||
"\u0114\7n\2\2\u0114\u0115\7c\2\2\u0115\u0116\7w\2\2\u0116\u0117\7p\2\2"+
|
||||
"\u0117\u0118\7e\2\2\u0118\u0119\7j\2\2\u0119\u011a\7g\2\2\u011a\u011b"+
|
||||
"\7t\2\2\u011b\f\3\2\2\2\u011c\u011d\7\'\2\2\u011d\u011e\7|\2\2\u011e\u011f"+
|
||||
"\7g\2\2\u011f\u0120\7t\2\2\u0120\u0121\7q\2\2\u0121\u0122\7r\2\2\u0122"+
|
||||
"\u0123\7c\2\2\u0123\u0124\7i\2\2\u0124\u0125\7g\2\2\u0125\16\3\2\2\2\u0126"+
|
||||
"\u0127\7\'\2\2\u0127\u0128\7|\2\2\u0128\u0129\7r\2\2\u0129\u012a\7t\2"+
|
||||
"\2\u012a\u012b\7g\2\2\u012b\u012c\7u\2\2\u012c\u012d\7g\2\2\u012d\u012e"+
|
||||
"\7t\2\2\u012e\u012f\7x\2\2\u012f\u0130\7g\2\2\u0130\u0131\7f\2\2\u0131"+
|
||||
"\20\3\2\2\2\u0132\u0133\7\'\2\2\u0133\u0134\7c\2\2\u0134\u0135\7f\2\2"+
|
||||
"\u0135\u0136\7f\2\2\u0136\u0137\7t\2\2\u0137\u0138\7g\2\2\u0138\u0139"+
|
||||
"\7u\2\2\u0139\u013a\7u\2\2\u013a\22\3\2\2\2\u013b\u013c\7\'\2\2\u013c"+
|
||||
"\u013d\7k\2\2\u013d\u013e\7o\2\2\u013e\u013f\7r\2\2\u013f\u0140\7q\2\2"+
|
||||
"\u0140\u0141\7t\2\2\u0141\u0142\7v\2\2\u0142\24\3\2\2\2\u0143\u0144\7"+
|
||||
"\'\2\2\u0144\u0145\7d\2\2\u0145\u0146\7t\2\2\u0146\u0147\7g\2\2\u0147"+
|
||||
"\u0148\7c\2\2\u0148\u0149\7m\2\2\u0149\u014a\7r\2\2\u014a\u014b\7q\2\2"+
|
||||
"\u014b\u014c\7k\2\2\u014c\u014d\7p\2\2\u014d\u014e\7v\2\2\u014e\26\3\2"+
|
||||
"\2\2\u014f\u0150\7\'\2\2\u0150\u0151\7c\2\2\u0151\u0152\7u\2\2\u0152\u0153"+
|
||||
"\7o\2\2\u0153\u0154\7k\2\2\u0154\u0155\7p\2\2\u0155\u0156\7e\2\2\u0156"+
|
||||
"\u0157\7n\2\2\u0157\u0158\7w\2\2\u0158\u0159\7f\2\2\u0159\u015a\7g\2\2"+
|
||||
"\u015a\30\3\2\2\2\u015b\u015c\7\'\2\2\u015c\u015d\7c\2\2\u015d\u015e\7"+
|
||||
"u\2\2\u015e\u015f\7o\2\2\u015f\u0160\7d\2\2\u0160\u0161\7k\2\2\u0161\u0162"+
|
||||
"\7p\2\2\u0162\u0163\7c\2\2\u0163\u0164\7t\2\2\u0164\u0165\7{\2\2\u0165"+
|
||||
"\32\3\2\2\2\u0166\u0167\7\'\2\2\u0167\u0168\7q\2\2\u0168\u0169\7r\2\2"+
|
||||
"\u0169\u016a\7v\2\2\u016a\u016b\7k\2\2\u016b\u016c\7q\2\2\u016c\u016d"+
|
||||
"\7p\2\2\u016d\34\3\2\2\2\u016e\u016f\7.\2\2\u016f\36\3\2\2\2\u0170\u0171"+
|
||||
"\7?\2\2\u0171 \3\2\2\2\u0172\u0173\7e\2\2\u0173\u0174\7q\2\2\u0174\u0175"+
|
||||
"\7p\2\2\u0175\u0176\7u\2\2\u0176\u0177\7v\2\2\u0177\"\3\2\2\2\u0178\u0179"+
|
||||
"\7o\2\2\u0179\u017a\7g\2\2\u017a\u017b\7o\2\2\u017b\u017c\7q\2\2\u017c"+
|
||||
"\u017d\7t\2\2\u017d\u017e\7{\2\2\u017e$\3\2\2\2\u017f\u0180\7w\2\2\u0180"+
|
||||
"\u0181\7d\2\2\u0181\u0182\7{\2\2\u0182\u0183\7v\2\2\u0183\u0184\7g\2\2"+
|
||||
"\u0184&\3\2\2\2\u0185\u0186\7d\2\2\u0186\u0187\7{\2\2\u0187\u0188\7v\2"+
|
||||
"\2\u0188\u0189\7g\2\2\u0189(\3\2\2\2\u018a\u018b\7w\2\2\u018b\u018c\7"+
|
||||
"y\2\2\u018c\u018d\7q\2\2\u018d\u018e\7t\2\2\u018e\u018f\7f\2\2\u018f*"+
|
||||
"\3\2\2\2\u0190\u0191\7y\2\2\u0191\u0192\7q\2\2\u0192\u0193\7t\2\2\u0193"+
|
||||
"\u0194\7f\2\2\u0194,\3\2\2\2\u0195\u0196\7h\2\2\u0196\u0197\7n\2\2\u0197"+
|
||||
"\u0198\7q\2\2\u0198\u0199\7c\2\2\u0199\u019a\7v\2\2\u019a.\3\2\2\2\u019b"+
|
||||
"\u019c\7u\2\2\u019c\u019d\7v\2\2\u019d\u019e\7t\2\2\u019e\60\3\2\2\2\u019f"+
|
||||
"\u01a0\7u\2\2\u01a0\u01a1\7v\2\2\u01a1\u01a2\7t\2\2\u01a2\u01a3\7a\2\2"+
|
||||
"\u01a3\u01a4\7r\2\2\u01a4\62\3\2\2\2\u01a5\u01a6\7u\2\2\u01a6\u01a7\7"+
|
||||
"v\2\2\u01a7\u01a8\7t\2\2\u01a8\u01a9\7a\2\2\u01a9\u01aa\7u\2\2\u01aa\64"+
|
||||
"\3\2\2\2\u01ab\u01ac\7u\2\2\u01ac\u01ad\7v\2\2\u01ad\u01ae\7t\2\2\u01ae"+
|
||||
"\u01af\7a\2\2\u01af\u01b0\7r\2\2\u01b0\u01b1\7u\2\2\u01b1\66\3\2\2\2\u01b2"+
|
||||
"\u01b3\7]\2\2\u01b38\3\2\2\2\u01b4\u01b5\7_\2\2\u01b5:\3\2\2\2\u01b6\u01b7"+
|
||||
"\7-\2\2\u01b7\u01b8\7?\2\2\u01b8<\3\2\2\2\u01b9\u01ba\7/\2\2\u01ba\u01bb"+
|
||||
"\7?\2\2\u01bb>\3\2\2\2\u01bc\u01bd\7\61\2\2\u01bd\u01be\7?\2\2\u01be@"+
|
||||
"\3\2\2\2\u01bf\u01c0\7,\2\2\u01c0\u01c1\7?\2\2\u01c1B\3\2\2\2\u01c2\u01c3"+
|
||||
"\7,\2\2\u01c3\u01c4\7,\2\2\u01c4\u01c5\7?\2\2\u01c5D\3\2\2\2\u01c6\u01c7"+
|
||||
"\7(\2\2\u01c7\u01c8\7?\2\2\u01c8F\3\2\2\2\u01c9\u01ca\7~\2\2\u01ca\u01cb"+
|
||||
"\7?\2\2\u01cbH\3\2\2\2\u01cc\u01cd\7`\2\2\u01cd\u01ce\7?\2\2\u01ceJ\3"+
|
||||
"\2\2\2\u01cf\u01d0\7\'\2\2\u01d0\u01d1\7?\2\2\u01d1L\3\2\2\2\u01d2\u01d3"+
|
||||
"\7>\2\2\u01d3\u01d4\7>\2\2\u01d4\u01d5\7?\2\2\u01d5N\3\2\2\2\u01d6\u01d7"+
|
||||
"\7@\2\2\u01d7\u01d8\7@\2\2\u01d8\u01d9\7?\2\2\u01d9P\3\2\2\2\u01da\u01db"+
|
||||
"\7-\2\2\u01db\u01dc\7-\2\2\u01dcR\3\2\2\2\u01dd\u01de\7/\2\2\u01de\u01df"+
|
||||
"\7/\2\2\u01dfT\3\2\2\2\u01e0\u01e1\7-\2\2\u01e1V\3\2\2\2\u01e2\u01e3\7"+
|
||||
"/\2\2\u01e3X\3\2\2\2\u01e4\u01e5\7,\2\2\u01e5\u01e6\7,\2\2\u01e6Z\3\2"+
|
||||
"\2\2\u01e7\u01e8\7,\2\2\u01e8\\\3\2\2\2\u01e9\u01ea\7\61\2\2\u01ea^\3"+
|
||||
"\2\2\2\u01eb\u01ec\7\'\2\2\u01ec`\3\2\2\2\u01ed\u01ee\7>\2\2\u01ee\u01ef"+
|
||||
"\7>\2\2\u01efb\3\2\2\2\u01f0\u01f1\7@\2\2\u01f1\u01f2\7@\2\2\u01f2d\3"+
|
||||
"\2\2\2\u01f3\u01f4\7>\2\2\u01f4f\3\2\2\2\u01f5\u01f6\7@\2\2\u01f6h\3\2"+
|
||||
"\2\2\u01f7\u01f8\7>\2\2\u01f8\u01f9\7?\2\2\u01f9j\3\2\2\2\u01fa\u01fb"+
|
||||
"\7@\2\2\u01fb\u01fc\7?\2\2\u01fcl\3\2\2\2\u01fd\u01fe\7?\2\2\u01fe\u01ff"+
|
||||
"\7?\2\2\u01ffn\3\2\2\2\u0200\u0201\7#\2\2\u0201\u0202\7?\2\2\u0202p\3"+
|
||||
"\2\2\2\u0203\u0204\7(\2\2\u0204r\3\2\2\2\u0205\u0206\7`\2\2\u0206t\3\2"+
|
||||
"\2\2\u0207\u0208\7~\2\2\u0208v\3\2\2\2\u0209\u020a\7v\2\2\u020a\u020b"+
|
||||
"\7q\2\2\u020bx\3\2\2\2\u020c\u020d\7u\2\2\u020d\u020e\7v\2\2\u020e\u020f"+
|
||||
"\7g\2\2\u020f\u0210\7r\2\2\u0210z\3\2\2\2\u0211\u0212\7c\2\2\u0212\u0213"+
|
||||
"\7p\2\2\u0213\u0214\7f\2\2\u0214|\3\2\2\2\u0215\u0216\7q\2\2\u0216\u0217"+
|
||||
"\7t\2\2\u0217~\3\2\2\2\u0218\u0219\7z\2\2\u0219\u021a\7q\2\2\u021a\u021b"+
|
||||
"\7t\2\2\u021b\u0080\3\2\2\2\u021c\u021d\7p\2\2\u021d\u021e\7q\2\2\u021e"+
|
||||
"\u021f\7v\2\2\u021f\u0082\3\2\2\2\u0220\u0221\7*\2\2\u0221\u0084\3\2\2"+
|
||||
"\2\u0222\u0223\7+\2\2\u0223\u0086\3\2\2\2\u0224\u0225\7c\2\2\u0225\u0226"+
|
||||
"\7u\2\2\u0226\u0088\3\2\2\2\u0227\u0228\7B\2\2\u0228\u008a\3\2\2\2\u0229"+
|
||||
"\u022a\7t\2\2\u022a\u022b\7g\2\2\u022b\u022c\7v\2\2\u022c\u022d\7w\2\2"+
|
||||
"\u022d\u022e\7t\2\2\u022e\u022f\7p\2\2\u022f\u008c\3\2\2\2\u0230\u0231"+
|
||||
"\7d\2\2\u0231\u0232\7t\2\2\u0232\u0233\7g\2\2\u0233\u0234\7c\2\2\u0234"+
|
||||
"\u0235\7m\2\2\u0235\u008e\3\2\2\2\u0236\u0237\7e\2\2\u0237\u0238\7q\2"+
|
||||
"\2\u0238\u0239\7p\2\2\u0239\u023a\7v\2\2\u023a\u023b\7k\2\2\u023b\u023c"+
|
||||
"\7p\2\2\u023c\u023d\7w\2\2\u023d\u023e\7g\2\2\u023e\u0090\3\2\2\2\u023f"+
|
||||
"\u0240\7\60\2\2\u0240\u0092\3\2\2\2\u0241\u0242\7C\2\2\u0242\u0094\3\2"+
|
||||
"\2\2\u0243\u0244\7Z\2\2\u0244\u0096\3\2\2\2\u0245\u0246\7[\2\2\u0246\u0098"+
|
||||
"\3\2\2\2\u0247\u0248\7C\2\2\u0248\u0249\7Z\2\2\u0249\u009a\3\2\2\2\u024a"+
|
||||
"\u024b\7C\2\2\u024b\u024c\7[\2\2\u024c\u009c\3\2\2\2\u024d\u024e\7Z\2"+
|
||||
"\2\u024e\u024f\7[\2\2\u024f\u009e\3\2\2\2\u0250\u0251\7R\2\2\u0251\u0252"+
|
||||
"\7e\2\2\u0252\u00a0\3\2\2\2\u0253\u0254\7R\2\2\u0254\u0255\7|\2\2\u0255"+
|
||||
"\u00a2\3\2\2\2\u0256\u0257\7R\2\2\u0257\u0258\7p\2\2\u0258\u00a4\3\2\2"+
|
||||
"\2\u0259\u025a\7R\2\2\u025a\u025b\7x\2\2\u025b\u00a6\3\2\2\2\u025c\u025d"+
|
||||
"\7\60\2\2\u025d\u025e\7y\2\2\u025e\u00a8\3\2\2\2\u025f\u0260\7v\2\2\u0260"+
|
||||
"\u0261\7t\2\2\u0261\u0262\7w\2\2\u0262\u0263\7g\2\2\u0263\u00aa\3\2\2"+
|
||||
"\2\u0264\u0265\7h\2\2\u0265\u0266\7c\2\2\u0266\u0267\7n\2\2\u0267\u0268"+
|
||||
"\7u\2\2\u0268\u0269\7g\2\2\u0269\u00ac\3\2\2\2\u026a\u026b\7\'\2\2\u026b"+
|
||||
"\u026c\7c\2\2\u026c\u026d\7u\2\2\u026d\u026e\7o\2\2\u026e\u00ae\3\2\2"+
|
||||
"\2\u026f\u0270\7u\2\2\u0270\u0271\7w\2\2\u0271\u0272\7d\2\2\u0272\u00b0"+
|
||||
"\3\2\2\2\u0273\u0274\7/\2\2\u0274\u0275\7@\2\2\u0275\u00b2\3\2\2\2\u0276"+
|
||||
"\u0277\7}\2\2\u0277\u00b4\3\2\2\2\u0278\u0279\7\177\2\2\u0279\u00b6\3"+
|
||||
"\2\2\2\u027a\u027b\7c\2\2\u027b\u027c\7u\2\2\u027c\u027d\7o\2\2\u027d"+
|
||||
"\u027e\7u\2\2\u027e\u027f\7w\2\2\u027f\u0280\7d\2\2\u0280\u00b8\3\2\2"+
|
||||
"\2\u0281\u0282\7e\2\2\u0282\u0283\7n\2\2\u0283\u0284\7q\2\2\u0284\u0285"+
|
||||
"\7d\2\2\u0285\u0286\7d\2\2\u0286\u0287\7g\2\2\u0287\u0288\7t\2\2\u0288"+
|
||||
"\u0289\7u\2\2\u0289\u00ba\3\2\2\2\u028a\u028b\7u\2\2\u028b\u028c\7v\2"+
|
||||
"\2\u028c\u028d\7c\2\2\u028d\u028e\7e\2\2\u028e\u028f\7m\2\2\u028f\u00bc"+
|
||||
"\3\2\2\2\u0290\u0291\7k\2\2\u0291\u0292\7h\2\2\u0292\u00be\3\2\2\2\u0293"+
|
||||
"\u0294\7g\2\2\u0294\u0295\7n\2\2\u0295\u0296\7u\2\2\u0296\u0297\7g\2\2"+
|
||||
"\u0297\u00c0\3\2\2\2\u0298\u0299\7k\2\2\u0299\u029a\7h\2\2\u029a\u029b"+
|
||||
"\7a\2\2\u029b\u029c\7e\2\2\u029c\u029d\7u\2\2\u029d\u00c2\3\2\2\2\u029e"+
|
||||
"\u029f\7k\2\2\u029f\u02a0\7h\2\2\u02a0\u02a1\7a\2\2\u02a1\u02a2\7e\2\2"+
|
||||
"\u02a2\u02a3\7e\2\2\u02a3\u00c4\3\2\2\2\u02a4\u02a5\7k\2\2\u02a5\u02a6"+
|
||||
"\7h\2\2\u02a6\u02a7\7a\2\2\u02a7\u02a8\7g\2\2\u02a8\u02a9\7s\2\2\u02a9"+
|
||||
"\u00c6\3\2\2\2\u02aa\u02ab\7k\2\2\u02ab\u02ac\7h\2\2\u02ac\u02ad\7a\2"+
|
||||
"\2\u02ad\u02ae\7|\2\2\u02ae\u00c8\3\2\2\2\u02af\u02b0\7k\2\2\u02b0\u02b1"+
|
||||
"\7h\2\2\u02b1\u02b2\7a\2\2\u02b2\u02b3\7p\2\2\u02b3\u02b4\7g\2\2\u02b4"+
|
||||
"\u00ca\3\2\2\2\u02b5\u02b6\7k\2\2\u02b6\u02b7\7h\2\2\u02b7\u02b8\7a\2"+
|
||||
"\2\u02b8\u02b9\7p\2\2\u02b9\u02ba\7|\2\2\u02ba\u00cc\3\2\2\2\u02bb\u02bc"+
|
||||
"\7k\2\2\u02bc\u02bd\7h\2\2\u02bd\u02be\7a\2\2\u02be\u02bf\7r\2\2\u02bf"+
|
||||
"\u02c0\7n\2\2\u02c0\u00ce\3\2\2\2\u02c1\u02c2\7k\2\2\u02c2\u02c3\7h\2"+
|
||||
"\2\u02c3\u02c4\7a\2\2\u02c4\u02c5\7r\2\2\u02c5\u02c6\7q\2\2\u02c6\u02c7"+
|
||||
"\7u\2\2\u02c7\u00d0\3\2\2\2\u02c8\u02c9\7k\2\2\u02c9\u02ca\7h\2\2\u02ca"+
|
||||
"\u02cb\7a\2\2\u02cb\u02cc\7o\2\2\u02cc\u02cd\7k\2\2\u02cd\u00d2\3\2\2"+
|
||||
"\2\u02ce\u02cf\7k\2\2\u02cf\u02d0\7h\2\2\u02d0\u02d1\7a\2\2\u02d1\u02d2"+
|
||||
"\7p\2\2\u02d2\u02d3\7g\2\2\u02d3\u02d4\7i\2\2\u02d4\u00d4\3\2\2\2\u02d5"+
|
||||
"\u02d6\7k\2\2\u02d6\u02d7\7h\2\2\u02d7\u02d8\7a\2\2\u02d8\u02d9\7x\2\2"+
|
||||
"\u02d9\u02da\7u\2\2\u02da\u00d6\3\2\2\2\u02db\u02dc\7k\2\2\u02dc\u02dd"+
|
||||
"\7h\2\2\u02dd\u02de\7a\2\2\u02de\u02df\7x\2\2\u02df\u02e0\7e\2\2\u02e0"+
|
||||
"\u00d8\3\2\2\2\u02e1\u02e2\7h\2\2\u02e2\u02e3\7q\2\2\u02e3\u02e4\7t\2"+
|
||||
"\2\u02e4\u00da\3\2\2\2\u02e5\u02e6\7k\2\2\u02e6\u02e7\7p\2\2\u02e7\u00dc"+
|
||||
"\3\2\2\2\u02e8\u02e9\7y\2\2\u02e9\u02ea\7j\2\2\u02ea\u02eb\7k\2\2\u02eb"+
|
||||
"\u02ec\7n\2\2\u02ec\u02ed\7g\2\2\u02ed\u00de\3\2\2\2\u02ee\u02ef\7t\2"+
|
||||
"\2\u02ef\u02f0\7g\2\2\u02f0\u02f1\7r\2\2\u02f1\u02f2\7g\2\2\u02f2\u02f3"+
|
||||
"\7c\2\2\u02f3\u02f4\7v\2\2\u02f4\u00e0\3\2\2\2\u02f5\u02f6\7w\2\2\u02f6"+
|
||||
"\u02f7\7p\2\2\u02f7\u02f8\7v\2\2\u02f8\u02f9\7k\2\2\u02f9\u02fa\7n\2\2"+
|
||||
"\u02fa\u00e2\3\2\2\2\u02fb\u02ff\t\2\2\2\u02fc\u02fe\t\3\2\2\u02fd\u02fc"+
|
||||
"\3\2\2\2\u02fe\u0301\3\2\2\2\u02ff\u02fd\3\2\2\2\u02ff\u0300\3\2\2\2\u0300"+
|
||||
"\u0302\3\2\2\2\u0301\u02ff\3\2\2\2\u0302\u0303\5\u00e5s\2\u0303\u0304"+
|
||||
"\3\2\2\2\u0304\u0305\br\2\2\u0305\u00e4\3\2\2\2\u0306\u030a\7=\2\2\u0307"+
|
||||
"\u0309\n\2\2\2\u0308\u0307\3\2\2\2\u0309\u030c\3\2\2\2\u030a\u0308\3\2"+
|
||||
"\2\2\u030a\u030b\3\2\2\2\u030b\u030d\3\2\2\2\u030c\u030a\3\2\2\2\u030d"+
|
||||
"\u030e\bs\2\2\u030e\u00e6\3\2\2\2\u030f\u0310\t\3\2\2\u0310\u0311\3\2"+
|
||||
"\2\2\u0311\u0312\bt\3\2\u0312\u00e8\3\2\2\2\u0313\u0315\t\2\2\2\u0314"+
|
||||
"\u0313\3\2\2\2\u0315\u0316\3\2\2\2\u0316\u0314\3\2\2\2\u0316\u0317\3\2"+
|
||||
"\2\2\u0317\u00ea\3\2\2\2\u0318\u031c\t\4\2\2\u0319\u031b\t\5\2\2\u031a"+
|
||||
"\u0319\3\2\2\2\u031b\u031e\3\2\2\2\u031c\u031a\3\2\2\2\u031c\u031d\3\2"+
|
||||
"\2\2\u031d\u00ec\3\2\2\2\u031e\u031c\3\2\2\2\u031f\u0327\4\62;\2\u0320"+
|
||||
"\u0322\4\63;\2\u0321\u0323\4\62;\2\u0322\u0321\3\2\2\2\u0323\u0324\3\2"+
|
||||
"\2\2\u0324\u0322\3\2\2\2\u0324\u0325\3\2\2\2\u0325\u0327\3\2\2\2\u0326"+
|
||||
"\u031f\3\2\2\2\u0326\u0320\3\2\2\2\u0327\u00ee\3\2\2\2\u0328\u032a\7&"+
|
||||
"\2\2\u0329\u032b\t\6\2\2\u032a\u0329\3\2\2\2\u032b\u032c\3\2\2\2\u032c"+
|
||||
"\u032a\3\2\2\2\u032c\u032d\3\2\2\2\u032d\u00f0\3\2\2\2\u032e\u0330\7\'"+
|
||||
"\2\2\u032f\u0331\4\62\63\2\u0330\u032f\3\2\2\2\u0331\u0332\3\2\2\2\u0332"+
|
||||
"\u0330\3\2\2\2\u0332\u0333\3\2\2\2\u0333\u00f2\3\2\2\2\u0334\u033a\5\u00f5"+
|
||||
"{\2\u0335\u0337\t\7\2\2\u0336\u0338\t\b\2\2\u0337\u0336\3\2\2\2\u0337"+
|
||||
"\u0338\3\2\2\2\u0338\u0339\3\2\2\2\u0339\u033b\5\u00f5{\2\u033a\u0335"+
|
||||
"\3\2\2\2\u033a\u033b\3\2\2\2\u033b\u00f4\3\2\2\2\u033c\u033e\4\62;\2\u033d"+
|
||||
"\u033c\3\2\2\2\u033e\u033f\3\2\2\2\u033f\u033d\3\2\2\2\u033f\u0340\3\2"+
|
||||
"\2\2\u0340\u0347\3\2\2\2\u0341\u0343\7\60\2\2\u0342\u0344\4\62;\2\u0343"+
|
||||
"\u0342\3\2\2\2\u0344\u0345\3\2\2\2\u0345\u0343\3\2\2\2\u0345\u0346\3\2"+
|
||||
"\2\2\u0346\u0348\3\2\2\2\u0347\u0341\3\2\2\2\u0347\u0348\3\2\2\2\u0348"+
|
||||
"\u00f6\3\2\2\2\u0349\u034a\7^\2\2\u034a\u034e\13\2\2\2\u034b\u034c\7^"+
|
||||
"\2\2\u034c\u034e\5\u00e9u\2\u034d\u0349\3\2\2\2\u034d\u034b\3\2\2\2\u034e"+
|
||||
"\u00f8\3\2\2\2\u034f\u0354\7$\2\2\u0350\u0353\5\u00f7|\2\u0351\u0353\n"+
|
||||
"\t\2\2\u0352\u0350\3\2\2\2\u0352\u0351\3\2\2\2\u0353\u0356\3\2\2\2\u0354"+
|
||||
"\u0352\3\2\2\2\u0354\u0355\3\2\2\2\u0355\u0357\3\2\2\2\u0356\u0354\3\2"+
|
||||
"\2\2\u0357\u0358\7$\2\2\u0358\u0359\b}\4\2\u0359\u00fa\3\2\2\2\u035a\u035b"+
|
||||
"\7}\2\2\u035b\u035c\7}\2\2\u035c\u035e\3\2\2\2\u035d\u035f\13\2\2\2\u035e"+
|
||||
"\u035d\3\2\2\2\u035f\u0360\3\2\2\2\u0360\u0361\3\2\2\2\u0360\u035e\3\2"+
|
||||
"\2\2\u0361\u0362\3\2\2\2\u0362\u0363\7\177\2\2\u0363\u0364\7\177\2\2\u0364"+
|
||||
"\u0365\3\2\2\2\u0365\u0366\b~\5\2\u0366\u00fc\3\2\2\2\u0367\u036a\7)\2"+
|
||||
"\2\u0368\u036b\5\u00f7|\2\u0369\u036b\n\t\2\2\u036a\u0368\3\2\2\2\u036a"+
|
||||
"\u0369\3\2\2\2\u036b\u036c\3\2\2\2\u036c\u036d\7)\2\2\u036d\u036e\b\177"+
|
||||
"\6\2\u036e\u00fe\3\2\2\2\u036f\u0370\7B\2\2\u0370\u0371\7|\2\2\u0371\u0372"+
|
||||
"\7r\2\2\u0372\u0100\3\2\2\2\26\2\u02ff\u030a\u0316\u031c\u0324\u0326\u032a"+
|
||||
"\u032c\u0332\u0337\u033a\u033f\u0345\u0347\u034d\u0352\u0354\u0360\u036a"+
|
||||
"\7\2\3\2\b\2\2\3}\2\3~\3\3\177\4";
|
||||
"\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"+
|
||||
"\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t \4!"+
|
||||
"\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4"+
|
||||
",\t,\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64\t"+
|
||||
"\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\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\tK\4L\tL\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\tT"+
|
||||
"\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~\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
@@ -1,10 +1,5 @@
|
||||
<?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="FacetManager">
|
||||
<facet type="Python" name="Python">
|
||||
<configuration sdkName="Python 3.7" />
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
@@ -17,6 +12,5 @@
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Python 3.7 interpreter library" level="application" />
|
||||
</component>
|
||||
</module>
|
||||
Reference in new issue
Block a user