mirror of
https://github.com/irmen/prog8.git
synced 2025-06-18 23:23:40 +00:00
Compare commits
103 Commits
Author | SHA1 | Date | |
---|---|---|---|
2ba6c9ccbe | |||
3eaf111e7d | |||
30da26b9a9 | |||
e35ad0cc8f | |||
1a36302cf1 | |||
82a28bb555 | |||
c1ce0be451 | |||
c0a5f8fef0 | |||
702cf304d0 | |||
4dee8b6048 | |||
ec665e0cc1 | |||
aec3b82476 | |||
e83796b5b9 | |||
8eb69d6eda | |||
74b5124a42 | |||
b9706a180b | |||
8aeb8a9bb7 | |||
8f2e166a22 | |||
fdd91170dc | |||
c40ddb061b | |||
353d6cfc55 | |||
f37564c49c | |||
157484d94b | |||
7626c9fff7 | |||
1f55f9fc49 | |||
2554bc7ef8 | |||
7cb4100419 | |||
2d3b7eb878 | |||
4d01a78731 | |||
a03e36828a | |||
260fb65b06 | |||
9fb8526136 | |||
26fc5ff5e2 | |||
5060f0bb19 | |||
beaf6d449b | |||
4d68b508a2 | |||
cd825e386d | |||
095c8b2309 | |||
8b6eb74c58 | |||
aba437e5a2 | |||
efe3ed499b | |||
5595564a1f | |||
439761cb67 | |||
bee6c65293 | |||
10145b946b | |||
ebf4b50059 | |||
07cce3b3fc | |||
f2c19afd95 | |||
d159e70e1c | |||
ac693a2541 | |||
1e988116ce | |||
ec9e722927 | |||
4cd5e8c378 | |||
b759d5e06a | |||
1469033c1e | |||
c15fd75df7 | |||
73524e01a6 | |||
9e54e11113 | |||
01ac5f29db | |||
67a2241e32 | |||
72b6dc3de7 | |||
6f5b645995 | |||
458ad1de57 | |||
216f48b7c1 | |||
b2d1757e5a | |||
6e53eb9d5c | |||
e5ee5be9c5 | |||
bd237b2b95 | |||
d31cf766eb | |||
56d530ff04 | |||
0bbb2240f2 | |||
1c8e4dba73 | |||
4a9956c4a4 | |||
59c0e6ae32 | |||
94c30fc21e | |||
8bb3b3be20 | |||
85e3c2c5a2 | |||
4be381c597 | |||
6ff5470cf1 | |||
151dcfdef9 | |||
c282b4cb9f | |||
c426f4626c | |||
0e3c92626e | |||
5099525e24 | |||
e22b4cbb67 | |||
2b48828179 | |||
3e181362dd | |||
71fd98e39e | |||
71cd8b6d51 | |||
ad75fcbf7e | |||
f8b04a6357 | |||
d8fcbb78d3 | |||
8408bf3789 | |||
3e1185658e | |||
d778cdcd61 | |||
90b303fc03 | |||
eb86b1270d | |||
a1f0cc878b | |||
f2e2720b15 | |||
ec8cfe1591 | |||
22eac159e5 | |||
956b0c3fa7 | |||
a6427e0949 |
@ -8,7 +8,7 @@
|
|||||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
<orderEntry type="jdk" jdkName="11" jdkType="JavaSDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
<orderEntry type="module" module-name="parser" />
|
<orderEntry type="module" module-name="parser" />
|
||||||
|
@ -208,17 +208,6 @@ pop_float_fac2 .proc
|
|||||||
jmp CONUPK
|
jmp CONUPK
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
pop_float_to_indexed_var .proc
|
|
||||||
; -- pop the float on the stack, to the memory in the array at A/Y indexed by the byte on stack
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
sty P8ZP_SCRATCH_W1+1
|
|
||||||
jsr prog8_lib.pop_index_times_5
|
|
||||||
jsr prog8_lib.add_a_to_zpword
|
|
||||||
lda P8ZP_SCRATCH_W1
|
|
||||||
ldy P8ZP_SCRATCH_W1+1
|
|
||||||
jmp pop_float
|
|
||||||
.pend
|
|
||||||
|
|
||||||
copy_float .proc
|
copy_float .proc
|
||||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
||||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||||
@ -707,13 +696,12 @@ sign_f .proc
|
|||||||
|
|
||||||
|
|
||||||
set_0_array_float .proc
|
set_0_array_float .proc
|
||||||
; -- set a float in an array to zero (index on stack, array in SCRATCH_ZPWORD1)
|
; -- set a float in an array to zero (index in A, array in P8ZP_SCRATCH_W1)
|
||||||
inx
|
sta P8ZP_SCRATCH_B1
|
||||||
lda P8ESTACK_LO,x
|
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
clc
|
clc
|
||||||
adc P8ESTACK_LO,x
|
adc P8ZP_SCRATCH_B1
|
||||||
tay
|
tay
|
||||||
lda #0
|
lda #0
|
||||||
sta (P8ZP_SCRATCH_W1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
@ -730,13 +718,12 @@ set_0_array_float .proc
|
|||||||
|
|
||||||
|
|
||||||
set_array_float .proc
|
set_array_float .proc
|
||||||
; -- set a float in an array to a value (index on stack, float in SCRATCH_ZPWORD1, array in SCRATCH_ZPWORD2)
|
; -- set a float in an array to a value (index in A, float in P8ZP_SCRATCH_W1, array in P8ZP_SCRATCH_W2)
|
||||||
inx
|
sta P8ZP_SCRATCH_B1
|
||||||
lda P8ESTACK_LO,x
|
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
clc
|
clc
|
||||||
adc P8ESTACK_LO,x
|
adc P8ZP_SCRATCH_B1
|
||||||
adc P8ZP_SCRATCH_W2
|
adc P8ZP_SCRATCH_W2
|
||||||
ldy P8ZP_SCRATCH_W2+1
|
ldy P8ZP_SCRATCH_W2+1
|
||||||
bcc +
|
bcc +
|
||||||
|
@ -33,7 +33,7 @@ graphics {
|
|||||||
}
|
}
|
||||||
word @zp d = 0
|
word @zp d = 0
|
||||||
ubyte positive_ix = true
|
ubyte positive_ix = true
|
||||||
word @zp dx = x2-x1
|
word @zp dx = x2-x1 as word
|
||||||
word @zp dy = y2-y1
|
word @zp dy = y2-y1
|
||||||
if dx < 0 {
|
if dx < 0 {
|
||||||
dx = -dx
|
dx = -dx
|
||||||
|
@ -204,23 +204,23 @@ romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial
|
|||||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
|
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
|
||||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||||
romsub $FFC0 = OPEN() clobbers(A,X,Y) ; (via 794 ($31A)) open a logical file
|
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) ; (via 798 ($31E)) define an input channel
|
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
|
||||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||||
romsub $FFCF = CHRIN() clobbers(Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
|
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
|
||||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock
|
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock
|
||||||
romsub $FFE1 = STOP() clobbers(A,X) -> ubyte @ Pz, ubyte @ Pc ; (via 808 ($328)) check the STOP key
|
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @ A ; (via 810 ($32A)) get a character
|
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
|
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||||
|
|
||||||
; ---- end of C64 ROM kernal routines ----
|
; ---- end of C64 ROM kernal routines ----
|
||||||
|
@ -249,7 +249,28 @@ output .text "0000", $00 ; 0-terminated output buffer (to make printing ea
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub str2uword(str string @ AY) -> uword @ AY {
|
|
||||||
|
asmsub str2ubyte(str string @ AY) clobbers(Y) -> ubyte @A {
|
||||||
|
; -- returns the unsigned byte value of the string number argument in AY
|
||||||
|
; the number may NOT be preceded by a + sign and may NOT contain spaces
|
||||||
|
; (any non-digit character will terminate the number string that is parsed)
|
||||||
|
; TODO implement optimized custom version of this instead of simply reusing str2uword
|
||||||
|
%asm {{
|
||||||
|
jmp str2uword
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub str2byte(str string @ AY) clobbers(Y) -> ubyte @A {
|
||||||
|
; -- returns the signed byte value of the string number argument in AY
|
||||||
|
; the number may be preceded by a + or - sign but may NOT contain spaces
|
||||||
|
; (any non-digit character will terminate the number string that is parsed)
|
||||||
|
; TODO implement optimized custom version of this instead of simply reusing str2word
|
||||||
|
%asm {{
|
||||||
|
jmp str2word
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub str2uword(str string @ AY) -> uword @ AY {
|
||||||
; -- returns the unsigned word value of the string number argument in 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
|
; 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)
|
; (any non-digit character will terminate the number string that is parsed)
|
||||||
@ -303,7 +324,7 @@ _result_times_10 ; (W*4 + W)*2
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub str2word(str string @ AY) -> word @ AY {
|
asmsub str2word(str string @ AY) -> word @ AY {
|
||||||
; -- returns the signed word value of the string number argument in 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
|
; 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)
|
; (any non-digit character will terminate the number string that is parsed)
|
||||||
@ -358,4 +379,77 @@ _negative .byte 0
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub hex2uword(str string @ AY) -> uword @AY {
|
||||||
|
; -- hexadecimal string with or without '$' to uword.
|
||||||
|
; string may be in petscii or c64-screencode encoding.
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
sty P8ZP_SCRATCH_W2+1
|
||||||
|
ldy #0
|
||||||
|
sty P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
_loop ldy #0
|
||||||
|
sty P8ZP_SCRATCH_B1
|
||||||
|
lda (P8ZP_SCRATCH_W2),y
|
||||||
|
beq _stop
|
||||||
|
cmp #'$'
|
||||||
|
beq _skip
|
||||||
|
cmp #7
|
||||||
|
bcc _add_nine
|
||||||
|
cmp #'9'
|
||||||
|
beq _calc
|
||||||
|
bcs _add_nine
|
||||||
|
_calc asl P8ZP_SCRATCH_W1
|
||||||
|
rol P8ZP_SCRATCH_W1+1
|
||||||
|
asl P8ZP_SCRATCH_W1
|
||||||
|
rol P8ZP_SCRATCH_W1+1
|
||||||
|
asl P8ZP_SCRATCH_W1
|
||||||
|
rol P8ZP_SCRATCH_W1+1
|
||||||
|
asl P8ZP_SCRATCH_W1
|
||||||
|
rol P8ZP_SCRATCH_W1+1
|
||||||
|
and #$0f
|
||||||
|
clc
|
||||||
|
adc P8ZP_SCRATCH_B1
|
||||||
|
ora P8ZP_SCRATCH_W1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
_skip inc P8ZP_SCRATCH_W2
|
||||||
|
bne _loop
|
||||||
|
inc P8ZP_SCRATCH_W2+1
|
||||||
|
bne _loop
|
||||||
|
_stop lda P8ZP_SCRATCH_W1
|
||||||
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
|
rts
|
||||||
|
_add_nine ldy #9
|
||||||
|
sty P8ZP_SCRATCH_B1
|
||||||
|
bne _calc
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub bin2uword(str string @ AY) -> uword @AY {
|
||||||
|
; -- binary string with or without '%' to uword.
|
||||||
|
%asm {{
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
sty P8ZP_SCRATCH_W2+1
|
||||||
|
ldy #0
|
||||||
|
sty P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
_loop lda (P8ZP_SCRATCH_W2),y
|
||||||
|
beq _stop
|
||||||
|
cmp #'%'
|
||||||
|
beq +
|
||||||
|
asl P8ZP_SCRATCH_W1
|
||||||
|
rol P8ZP_SCRATCH_W1+1
|
||||||
|
and #1
|
||||||
|
ora P8ZP_SCRATCH_W1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
+ inc P8ZP_SCRATCH_W2
|
||||||
|
bne _loop
|
||||||
|
inc P8ZP_SCRATCH_W2+1
|
||||||
|
bne _loop
|
||||||
|
_stop lda P8ZP_SCRATCH_W1
|
||||||
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -78,10 +78,10 @@ asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
|||||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||||
%asm {{
|
%asm {{
|
||||||
phx
|
phx
|
||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_W2
|
||||||
sty P8ZP_SCRATCH_B1
|
sty P8ZP_SCRATCH_B1
|
||||||
tya
|
tya
|
||||||
ldy P8ZP_SCRATCH_REG
|
ldy P8ZP_SCRATCH_W2
|
||||||
jsr GIVAYF ; load it as signed... correct afterwards
|
jsr GIVAYF ; load it as signed... correct afterwards
|
||||||
lda P8ZP_SCRATCH_B1
|
lda P8ZP_SCRATCH_B1
|
||||||
bpl +
|
bpl +
|
||||||
@ -98,9 +98,9 @@ _flt65536 .byte 145,0,0,0,0 ; 65536.0
|
|||||||
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||||
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
||||||
%asm {{
|
%asm {{
|
||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_W2
|
||||||
tya
|
tya
|
||||||
ldy P8ZP_SCRATCH_REG
|
ldy P8ZP_SCRATCH_W2
|
||||||
jmp GIVAYF ; this uses the inverse order, Y/A
|
jmp GIVAYF ; this uses the inverse order, Y/A
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -12,48 +12,48 @@ c64 {
|
|||||||
|
|
||||||
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
|
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
|
||||||
|
|
||||||
; STROUT --> use screen.print
|
; STROUT --> use txt.print
|
||||||
; CLEARSCR -> use screen.clear_screen
|
; CLEARSCR -> use txt.clear_screen
|
||||||
; HOMECRSR -> use screen.plot
|
; HOMECRSR -> use txt.plot
|
||||||
|
|
||||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||||
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||||
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||||
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||||
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||||
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||||
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||||
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> ubyte @A, uword @ XY ; read/set top of memory pointer, returns number of banks in A
|
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
||||||
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||||
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||||
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||||
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
||||||
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
||||||
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||||
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
|
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
|
||||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||||
romsub $FFC0 = OPEN() clobbers(A,X,Y) ; (via 794 ($31A)) open a logical file
|
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
|
||||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) ; (via 798 ($31E)) define an input channel
|
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) -> ubyte @Pc ; (via 798 ($31E)) define an input channel
|
||||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||||
romsub $FFCF = CHRIN() clobbers(Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
|
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
|
||||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock
|
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock
|
||||||
romsub $FFE1 = STOP() clobbers(A,X) -> ubyte @ Pz, ubyte @ Pc ; (via 808 ($328)) check the STOP key
|
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
|
||||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @ A ; (via 810 ($32A)) get a character
|
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @Pc, ubyte @ A ; (via 810 ($32A)) get a character
|
||||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use screen.plot for a 'safe' wrapper that preserves X.
|
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -134,13 +134,16 @@ _la lda #0 ; modified
|
|||||||
ubyte[16] color_to_charcode = [$90,$05,$1c,$9f,$9c,$1e,$1f,$9e,$81,$95,$96,$97,$98,$99,$9a,$9b]
|
ubyte[16] color_to_charcode = [$90,$05,$1c,$9f,$9c,$1e,$1f,$9e,$81,$95,$96,$97,$98,$99,$9a,$9b]
|
||||||
|
|
||||||
sub color (ubyte txtcol) {
|
sub color (ubyte txtcol) {
|
||||||
c64.CHROUT(color_to_charcode[txtcol & 15])
|
txtcol &= 15
|
||||||
|
c64.CHROUT(color_to_charcode[txtcol])
|
||||||
}
|
}
|
||||||
|
|
||||||
sub color2 (ubyte txtcol, ubyte bgcol) {
|
sub color2 (ubyte txtcol, ubyte bgcol) {
|
||||||
c64.CHROUT(color_to_charcode[bgcol & 15])
|
txtcol &= 15
|
||||||
|
bgcol &= 15
|
||||||
|
c64.CHROUT(color_to_charcode[bgcol])
|
||||||
c64.CHROUT(1) ; switch fg and bg colors
|
c64.CHROUT(1) ; switch fg and bg colors
|
||||||
c64.CHROUT(color_to_charcode[txtcol & 15])
|
c64.CHROUT(color_to_charcode[txtcol])
|
||||||
}
|
}
|
||||||
|
|
||||||
sub lowercase() {
|
sub lowercase() {
|
||||||
|
29
compiler/res/prog8lib/cx16logo.p8
Normal file
29
compiler/res/prog8lib/cx16logo.p8
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
%import textio
|
||||||
|
|
||||||
|
cx16logo {
|
||||||
|
sub logo_at(ubyte column, ubyte row) {
|
||||||
|
uword strptr
|
||||||
|
for strptr in logo_lines {
|
||||||
|
txt.plot(column, row)
|
||||||
|
txt.print(strptr)
|
||||||
|
row++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub logo() {
|
||||||
|
uword strptr
|
||||||
|
for strptr in logo_lines
|
||||||
|
txt.print(strptr)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
str[] logo_lines = [
|
||||||
|
"\uf10d\uf11a\uf139\uf11b \uf11a\uf13a\uf11b\n",
|
||||||
|
"\uf10b\uf11a▎\uf139\uf11b \uf11a\uf13a\uf130\uf11b\n",
|
||||||
|
"\uf10f\uf11a▌ \uf139\uf11b \uf11a\uf13a \uf11b▌\n",
|
||||||
|
"\uf102 \uf132\uf11a▖\uf11b \uf11a▗\uf11b\uf132\n",
|
||||||
|
"\uf10e ▂\uf11a▘\uf11b \uf11a▝\uf11b▂\n",
|
||||||
|
"\uf104 \uf11a \uf11b\uf13a\uf11b \uf139\uf11a \uf11b\n",
|
||||||
|
"\uf101\uf130\uf13a \uf139▎\uf100"
|
||||||
|
]
|
||||||
|
}
|
163
compiler/res/prog8lib/diskio.p8
Normal file
163
compiler/res/prog8lib/diskio.p8
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
%import textio
|
||||||
|
%import syslib
|
||||||
|
|
||||||
|
; Note: this code is compatible with C64 and CX16.
|
||||||
|
|
||||||
|
diskio {
|
||||||
|
|
||||||
|
|
||||||
|
sub directory(ubyte drivenumber) -> byte {
|
||||||
|
; -- Shows the directory contents of disk drive 8-11 (provide as argument).
|
||||||
|
|
||||||
|
c64.SETNAM(1, "$")
|
||||||
|
c64.SETLFS(1, drivenumber, 0)
|
||||||
|
void c64.OPEN() ; open 1,8,0,"$"
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
void c64.CHKIN(1) ; use #1 as input channel
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
|
||||||
|
repeat 4 {
|
||||||
|
void c64.CHRIN() ; skip the 4 prologue bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
; while not key pressed / EOF encountered, read data.
|
||||||
|
ubyte status = c64.READST()
|
||||||
|
while not status {
|
||||||
|
txt.print_uw(mkword(c64.CHRIN(), c64.CHRIN()))
|
||||||
|
txt.chrout(' ')
|
||||||
|
ubyte @zp char
|
||||||
|
do {
|
||||||
|
char = c64.CHRIN()
|
||||||
|
txt.chrout(char)
|
||||||
|
} until char==0
|
||||||
|
txt.chrout('\n')
|
||||||
|
void c64.CHRIN() ; skip 2 bytes
|
||||||
|
void c64.CHRIN()
|
||||||
|
status = c64.READST()
|
||||||
|
void c64.STOP()
|
||||||
|
if_nz
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
io_error:
|
||||||
|
status = c64.READST()
|
||||||
|
c64.CLRCHN() ; restore default i/o devices
|
||||||
|
c64.CLOSE(1)
|
||||||
|
|
||||||
|
if status and status != 64 { ; 64=end of file
|
||||||
|
txt.print("\ni/o error, status: ")
|
||||||
|
txt.print_ub(status)
|
||||||
|
txt.chrout('\n')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub status(ubyte drivenumber) {
|
||||||
|
; -- display the disk drive's current status message
|
||||||
|
c64.SETNAM(0, $0000)
|
||||||
|
c64.SETLFS(15, drivenumber, 15)
|
||||||
|
void c64.OPEN() ; open 15,8,15
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
void c64.CHKIN(15) ; use #15 as input channel
|
||||||
|
if_cs
|
||||||
|
goto io_error
|
||||||
|
|
||||||
|
while not c64.READST()
|
||||||
|
txt.chrout(c64.CHRIN())
|
||||||
|
|
||||||
|
io_error:
|
||||||
|
c64.CLRCHN() ; restore default i/o devices
|
||||||
|
c64.CLOSE(15)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> byte {
|
||||||
|
c64.SETNAM(strlen(filenameptr), filenameptr)
|
||||||
|
c64.SETLFS(1, drivenumber, 0)
|
||||||
|
uword end_address = address + size
|
||||||
|
|
||||||
|
%asm {{
|
||||||
|
lda address
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda address+1
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
lda #<P8ZP_SCRATCH_W1
|
||||||
|
ldx end_address
|
||||||
|
ldy end_address+1
|
||||||
|
jsr c64.SAVE
|
||||||
|
php
|
||||||
|
ldx P8ZP_SCRATCH_REG
|
||||||
|
plp
|
||||||
|
}}
|
||||||
|
|
||||||
|
if_cc
|
||||||
|
return c64.READST()==0
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sub load(ubyte drivenumber, uword filenameptr, uword address_override) -> uword {
|
||||||
|
c64.SETNAM(strlen(filenameptr), filenameptr)
|
||||||
|
ubyte secondary = 1
|
||||||
|
uword end_of_load = 0
|
||||||
|
if address_override
|
||||||
|
secondary = 0
|
||||||
|
c64.SETLFS(1, drivenumber, secondary)
|
||||||
|
%asm {{
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
lda #0
|
||||||
|
ldx address_override
|
||||||
|
ldy address_override+1
|
||||||
|
jsr c64.LOAD
|
||||||
|
bcs +
|
||||||
|
stx end_of_load
|
||||||
|
sty end_of_load+1
|
||||||
|
+ ldx P8ZP_SCRATCH_REG
|
||||||
|
}}
|
||||||
|
|
||||||
|
if end_of_load
|
||||||
|
return end_of_load - address_override
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
str filename = "0:??????????????????????????????????????"
|
||||||
|
|
||||||
|
sub delete(ubyte drivenumber, uword filenameptr) {
|
||||||
|
; -- delete a file on the drive
|
||||||
|
ubyte flen = strlen(filenameptr)
|
||||||
|
filename[0] = 's'
|
||||||
|
filename[1] = ':'
|
||||||
|
memcopy(filenameptr, &filename+2, flen+1)
|
||||||
|
c64.SETNAM(flen+2, filename)
|
||||||
|
c64.SETLFS(1, drivenumber, 15)
|
||||||
|
void c64.OPEN()
|
||||||
|
c64.CLRCHN()
|
||||||
|
c64.CLOSE(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub rename(ubyte drivenumber, uword oldfileptr, uword newfileptr) {
|
||||||
|
; -- rename a file on the drive
|
||||||
|
ubyte flen_old = strlen(oldfileptr)
|
||||||
|
ubyte flen_new = strlen(newfileptr)
|
||||||
|
filename[0] = 'r'
|
||||||
|
filename[1] = ':'
|
||||||
|
memcopy(newfileptr, &filename+2, flen_new)
|
||||||
|
ubyte fis_ix = flen_new+2 ; TODO is temp var for array indexing
|
||||||
|
filename[fis_ix] = '='
|
||||||
|
memcopy(oldfileptr, &filename+3+flen_new, flen_old+1)
|
||||||
|
c64.SETNAM(3+flen_new+flen_old, filename)
|
||||||
|
c64.SETLFS(1, drivenumber, 15)
|
||||||
|
void c64.OPEN()
|
||||||
|
c64.CLRCHN()
|
||||||
|
c64.CLOSE(1)
|
||||||
|
}
|
||||||
|
}
|
@ -781,7 +781,7 @@ mul_byte_3 .proc
|
|||||||
sta P8ZP_SCRATCH_REG
|
sta P8ZP_SCRATCH_REG
|
||||||
asl a
|
asl a
|
||||||
clc
|
clc
|
||||||
adc P8P_P8ZP_SCRATCH_REG
|
adc P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
@ -1469,3 +1469,22 @@ shift_right_w_3 .proc
|
|||||||
jmp shift_right_w_7._shift3
|
jmp shift_right_w_7._shift3
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; support for bit shifting that is too large to be unrolled:
|
||||||
|
|
||||||
|
lsr_byte_A .proc
|
||||||
|
; -- lsr signed byte in A times the value in Y (assume >0)
|
||||||
|
cmp #0
|
||||||
|
bmi _negative
|
||||||
|
- lsr a
|
||||||
|
dey
|
||||||
|
bne -
|
||||||
|
rts
|
||||||
|
_negative lsr a
|
||||||
|
ora #$80
|
||||||
|
dey
|
||||||
|
bne _negative
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
@ -40,16 +40,6 @@ add_a_to_zpword .proc
|
|||||||
+ rts
|
+ rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
pop_index_times_5 .proc
|
|
||||||
inx
|
|
||||||
lda P8ESTACK_LO,x
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc P8ESTACK_LO,x
|
|
||||||
rts
|
|
||||||
.pend
|
|
||||||
|
|
||||||
neg_b .proc
|
neg_b .proc
|
||||||
lda #0
|
lda #0
|
||||||
sec
|
sec
|
||||||
@ -682,6 +672,7 @@ func_read_flags .proc
|
|||||||
|
|
||||||
|
|
||||||
func_sqrt16 .proc
|
func_sqrt16 .proc
|
||||||
|
; TODO is this one faster? http://6502org.wikidot.com/software-math-sqrt
|
||||||
lda P8ESTACK_LO+1,x
|
lda P8ESTACK_LO+1,x
|
||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
lda P8ESTACK_HI+1,x
|
lda P8ESTACK_HI+1,x
|
||||||
@ -1976,7 +1967,7 @@ ror2_array_uw .proc
|
|||||||
|
|
||||||
|
|
||||||
strcpy .proc
|
strcpy .proc
|
||||||
; copy a string (0-terminated) from A/Y to (ZPWORD1)
|
; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
|
||||||
; it is assumed the target string is large enough.
|
; it is assumed the target string is large enough.
|
||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty P8ZP_SCRATCH_W2+1
|
sty P8ZP_SCRATCH_W2+1
|
||||||
@ -1989,6 +1980,38 @@ strcpy .proc
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
strcmp_mem .proc
|
||||||
|
; -- compares strings in s1 (AY) and s2 (P8ZP_SCRATCH_W2).
|
||||||
|
; Returns -1,0,1 in A, depeding on the ordering. Clobbers Y.
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
_loop ldy #0
|
||||||
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
|
bne +
|
||||||
|
lda (P8ZP_SCRATCH_W2),y
|
||||||
|
bne _return_minusone
|
||||||
|
beq _return
|
||||||
|
+ lda (P8ZP_SCRATCH_W2),y
|
||||||
|
sec
|
||||||
|
sbc (P8ZP_SCRATCH_W1),y
|
||||||
|
bmi _return_one
|
||||||
|
bne _return_minusone
|
||||||
|
inc P8ZP_SCRATCH_W1
|
||||||
|
bne +
|
||||||
|
inc P8ZP_SCRATCH_W1+1
|
||||||
|
+ inc P8ZP_SCRATCH_W2
|
||||||
|
bne _loop
|
||||||
|
inc P8ZP_SCRATCH_W2+1
|
||||||
|
bne _loop
|
||||||
|
_return_one
|
||||||
|
lda #1
|
||||||
|
_return rts
|
||||||
|
_return_minusone
|
||||||
|
lda #-1
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
func_leftstr .proc
|
func_leftstr .proc
|
||||||
; leftstr(source, target, length) with params on stack
|
; leftstr(source, target, length) with params on stack
|
||||||
inx
|
inx
|
||||||
@ -2097,3 +2120,18 @@ _startloop dey
|
|||||||
rts
|
rts
|
||||||
|
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
func_strcmp .proc
|
||||||
|
inx
|
||||||
|
lda P8ESTACK_LO,x
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda P8ESTACK_HI,x
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
lda P8ESTACK_HI+1,x
|
||||||
|
tay
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
jsr strcmp_mem
|
||||||
|
sta P8ESTACK_LO+1,x
|
||||||
|
rts
|
||||||
|
.pend
|
@ -5,5 +5,5 @@
|
|||||||
; indent format: TABS, size=8
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
prog8_lib {
|
prog8_lib {
|
||||||
%asminclude "library:prog8lib.asm", ""
|
%asminclude "library:prog8_lib.asm", ""
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
4.4
|
4.6
|
||||||
|
@ -38,6 +38,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
|
val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
|
||||||
val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")
|
val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")
|
||||||
val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes), greatly increases compilation speed")
|
val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes), greatly increases compilation speed")
|
||||||
|
val slowCodegenWarnings by cli.flagArgument("-slowwarn", "show debug warnings about slow/problematic assembly code generation")
|
||||||
val compilationTarget by cli.flagValueArgument("-target", "compilertarget",
|
val compilationTarget by cli.flagValueArgument("-target", "compilertarget",
|
||||||
"target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available", C64Target.name)
|
"target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available", C64Target.name)
|
||||||
val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
|
val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
|
||||||
@ -62,7 +63,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
println("Continuous watch mode active. Main module: $filepath")
|
println("Continuous watch mode active. Main module: $filepath")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, compilationTarget, outputPath)
|
val compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, slowCodegenWarnings, compilationTarget, outputPath)
|
||||||
println("Imported files (now watching:)")
|
println("Imported files (now watching:)")
|
||||||
for (importedFile in compilationResult.importedFiles) {
|
for (importedFile in compilationResult.importedFiles) {
|
||||||
print(" ")
|
print(" ")
|
||||||
@ -87,7 +88,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
val filepath = pathFrom(filepathRaw).normalize()
|
val filepath = pathFrom(filepathRaw).normalize()
|
||||||
val compilationResult: CompilationResult
|
val compilationResult: CompilationResult
|
||||||
try {
|
try {
|
||||||
compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, compilationTarget, outputPath)
|
compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, slowCodegenWarnings, compilationTarget, outputPath)
|
||||||
if(!compilationResult.success)
|
if(!compilationResult.success)
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
} catch (x: ParsingFailedError) {
|
} catch (x: ParsingFailedError) {
|
||||||
|
@ -121,7 +121,8 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
|
|
||||||
output(datatypeString(decl.datatype))
|
output(datatypeString(decl.datatype))
|
||||||
if(decl.arraysize!=null) {
|
if(decl.arraysize!=null) {
|
||||||
decl.arraysize!!.index.accept(this)
|
decl.arraysize!!.indexNum?.accept(this)
|
||||||
|
decl.arraysize!!.indexVar?.accept(this)
|
||||||
}
|
}
|
||||||
if(decl.isArray)
|
if(decl.isArray)
|
||||||
output("]")
|
output("]")
|
||||||
@ -352,9 +353,10 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||||
arrayIndexedExpression.identifier.accept(this)
|
arrayIndexedExpression.arrayvar.accept(this)
|
||||||
output("[")
|
output("[")
|
||||||
arrayIndexedExpression.arrayspec.index.accept(this)
|
arrayIndexedExpression.indexer.indexNum?.accept(this)
|
||||||
|
arrayIndexedExpression.indexer.indexVar?.accept(this)
|
||||||
output("]")
|
output("]")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,18 @@ interface Node {
|
|||||||
throw FatalAstException("scope missing from $this")
|
throw FatalAstException("scope missing from $this")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun definingBlock(): Block {
|
||||||
|
if(this is Block)
|
||||||
|
return this
|
||||||
|
return findParentNode<Block>(this)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun containingStatement(): Statement {
|
||||||
|
if(this is Statement)
|
||||||
|
return this
|
||||||
|
return findParentNode<Statement>(this)!!
|
||||||
|
}
|
||||||
|
|
||||||
fun replaceChildNode(node: Node, replacement: Node)
|
fun replaceChildNode(node: Node, replacement: Node)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,11 +56,20 @@ interface IFunctionCall {
|
|||||||
var args: MutableList<Expression>
|
var args: MutableList<Expression>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AsmGenInfo {
|
||||||
|
var usedAutoArrayIndexerForStatements = mutableMapOf<String, MutableSet<Statement>>()
|
||||||
|
var usedRegsaveA = false
|
||||||
|
var usedRegsaveX = false
|
||||||
|
var usedRegsaveY = false
|
||||||
|
}
|
||||||
|
|
||||||
interface INameScope {
|
interface INameScope {
|
||||||
val name: String
|
val name: String
|
||||||
val position: Position
|
val position: Position
|
||||||
val statements: MutableList<Statement>
|
val statements: MutableList<Statement>
|
||||||
val parent: Node
|
val parent: Node
|
||||||
|
val asmGenInfo: AsmGenInfo
|
||||||
|
|
||||||
fun linkParents(parent: Node)
|
fun linkParents(parent: Node)
|
||||||
|
|
||||||
@ -163,7 +184,6 @@ interface INameScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
||||||
fun containsNoVars() = statements.all { it !is VarDecl }
|
|
||||||
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
||||||
|
|
||||||
fun remove(stmt: Statement) {
|
fun remove(stmt: Statement) {
|
||||||
@ -203,6 +223,14 @@ interface INameScope {
|
|||||||
else
|
else
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun indexOfChild(stmt: Statement): Int {
|
||||||
|
val idx = statements.indexOfFirst { it===stmt }
|
||||||
|
if(idx>=0)
|
||||||
|
return idx
|
||||||
|
else
|
||||||
|
throw FatalAstException("attempt to find a non-child")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IAssignable {
|
interface IAssignable {
|
||||||
@ -236,7 +264,7 @@ class Program(val name: String, val modules: MutableList<Module>): Node {
|
|||||||
override val position: Position = Position.DUMMY
|
override val position: Position = Position.DUMMY
|
||||||
override var parent: Node
|
override var parent: Node
|
||||||
get() = throw FatalAstException("program has no parent")
|
get() = throw FatalAstException("program has no parent")
|
||||||
set(value) = throw FatalAstException("can't set parent of program")
|
set(_) = throw FatalAstException("can't set parent of program")
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
modules.forEach {
|
modules.forEach {
|
||||||
@ -260,10 +288,14 @@ class Module(override val name: String,
|
|||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
lateinit var program: Program
|
lateinit var program: Program
|
||||||
|
override val asmGenInfo = AsmGenInfo()
|
||||||
val importedBy = mutableListOf<Module>()
|
val importedBy = mutableListOf<Module>()
|
||||||
val imports = mutableSetOf<Module>()
|
val imports = mutableSetOf<Module>()
|
||||||
|
|
||||||
var loadAddress: Int = 0 // can be set with the %address directive
|
val loadAddress: Int by lazy {
|
||||||
|
val address = (statements.singleOrNull { it is Directive && it.directive == "%address" } as? Directive)?.args?.single()?.int ?: 0
|
||||||
|
address
|
||||||
|
}
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -288,8 +320,9 @@ class Module(override val name: String,
|
|||||||
class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
||||||
override val name = "<<<global>>>"
|
override val name = "<<<global>>>"
|
||||||
override val position = Position("<<<global>>>", 0, 0, 0)
|
override val position = Position("<<<global>>>", 0, 0, 0)
|
||||||
override val statements = mutableListOf<Statement>()
|
override val statements = mutableListOf<Statement>() // not used
|
||||||
override var parent: Node = ParentSentinel
|
override var parent: Node = ParentSentinel
|
||||||
|
override val asmGenInfo = AsmGenInfo()
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
modules.forEach { it.linkParents(this) }
|
modules.forEach { it.linkParents(this) }
|
||||||
@ -342,6 +375,7 @@ object BuiltinFunctionScopePlaceholder : INameScope {
|
|||||||
override val position = Position("<<placeholder>>", 0, 0, 0)
|
override val position = Position("<<placeholder>>", 0, 0, 0)
|
||||||
override var statements = mutableListOf<Statement>()
|
override var statements = mutableListOf<Statement>()
|
||||||
override var parent: Node = ParentSentinel
|
override var parent: Node = ParentSentinel
|
||||||
|
override val asmGenInfo = AsmGenInfo()
|
||||||
override fun linkParents(parent: Node) {}
|
override fun linkParents(parent: Node) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,7 +470,6 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
|||||||
litval.stringliteral()!=null -> litval.stringliteral().toAst()
|
litval.stringliteral()!=null -> litval.stringliteral().toAst()
|
||||||
litval.charliteral()!=null -> {
|
litval.charliteral()!=null -> {
|
||||||
try {
|
try {
|
||||||
val cc=litval.charliteral()
|
|
||||||
NumericLiteralValue(DataType.UBYTE, CompilationTarget.instance.encodeString(
|
NumericLiteralValue(DataType.UBYTE, CompilationTarget.instance.encodeString(
|
||||||
unescape(litval.charliteral().SINGLECHAR().text, litval.toPosition()),
|
unescape(litval.charliteral().SINGLECHAR().text, litval.toPosition()),
|
||||||
litval.charliteral().ALT_STRING_ENCODING()!=null)[0], litval.toPosition())
|
litval.charliteral().ALT_STRING_ENCODING()!=null)[0], litval.toPosition())
|
||||||
@ -647,7 +646,20 @@ private fun prog8Parser.VardeclContext.toAst(): VarDecl {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun escape(str: String) = str.replace("\t", "\\t").replace("\n", "\\n").replace("\r", "\\r")
|
internal fun escape(str: String): String {
|
||||||
|
val es = str.map {
|
||||||
|
when(it) {
|
||||||
|
'\t' -> "\\t"
|
||||||
|
'\n' -> "\\n"
|
||||||
|
'\r' -> "\\r"
|
||||||
|
'"' -> "\\\""
|
||||||
|
in '\u8000'..'\u80ff' -> "\\x" + (it.toInt() - 0x8000).toString(16).padStart(2, '0')
|
||||||
|
in '\u0000'..'\u00ff' -> it.toString()
|
||||||
|
else -> "\\u" + it.toInt().toString(16).padStart(4, '0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return es.joinToString("")
|
||||||
|
}
|
||||||
|
|
||||||
internal fun unescape(str: String, position: Position): String {
|
internal fun unescape(str: String, position: Position): String {
|
||||||
val result = mutableListOf<Char>()
|
val result = mutableListOf<Char>()
|
||||||
@ -661,9 +673,15 @@ internal fun unescape(str: String, position: Position): String {
|
|||||||
'n' -> '\n'
|
'n' -> '\n'
|
||||||
'r' -> '\r'
|
'r' -> '\r'
|
||||||
'"' -> '"'
|
'"' -> '"'
|
||||||
|
'\'' -> '\''
|
||||||
'u' -> {
|
'u' -> {
|
||||||
"${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
|
"${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
|
||||||
}
|
}
|
||||||
|
'x' -> {
|
||||||
|
// special hack 0x8000..0x80ff will be outputted verbatim without encoding
|
||||||
|
val hex = ("" + iter.nextChar() + iter.nextChar()).toInt(16)
|
||||||
|
(0x8000 + hex).toChar()
|
||||||
|
}
|
||||||
else -> throw SyntaxError("invalid escape char in string: \\$ec", position)
|
else -> throw SyntaxError("invalid escape char in string: \\$ec", position)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -39,16 +39,20 @@ enum class DataType {
|
|||||||
infix fun isAssignableTo(targetTypes: Set<DataType>) = targetTypes.any { this isAssignableTo it }
|
infix fun isAssignableTo(targetTypes: Set<DataType>) = targetTypes.any { this isAssignableTo it }
|
||||||
|
|
||||||
infix fun largerThan(other: DataType) =
|
infix fun largerThan(other: DataType) =
|
||||||
when(this) {
|
when {
|
||||||
in ByteDatatypes -> false
|
this == other -> false
|
||||||
in WordDatatypes -> other in ByteDatatypes
|
this in ByteDatatypes -> false
|
||||||
|
this in WordDatatypes -> other in ByteDatatypes
|
||||||
|
this==STR && other==UWORD || this==UWORD && other==STR -> false
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun equalsSize(other: DataType) =
|
infix fun equalsSize(other: DataType) =
|
||||||
when(this) {
|
when {
|
||||||
in ByteDatatypes -> other in ByteDatatypes
|
this == other -> true
|
||||||
in WordDatatypes -> other in WordDatatypes
|
this in ByteDatatypes -> other in ByteDatatypes
|
||||||
|
this in WordDatatypes -> other in WordDatatypes
|
||||||
|
this==STR && other==UWORD || this==UWORD && other==STR -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package prog8.ast.base
|
|||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.processing.*
|
import prog8.ast.processing.*
|
||||||
|
import prog8.ast.statements.Directive
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.BeforeAsmGenerationAstChanger
|
import prog8.compiler.BeforeAsmGenerationAstChanger
|
||||||
|
|
||||||
@ -18,8 +19,8 @@ internal fun Program.processAstBeforeAsmGeneration(errors: ErrorReporter) {
|
|||||||
fixer.applyModifications()
|
fixer.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.reorderStatements() {
|
internal fun Program.reorderStatements(errors: ErrorReporter) {
|
||||||
val reorder = StatementReorderer(this)
|
val reorder = StatementReorderer(this, errors)
|
||||||
reorder.visit(this)
|
reorder.visit(this)
|
||||||
reorder.applyModifications()
|
reorder.applyModifications()
|
||||||
}
|
}
|
||||||
@ -41,12 +42,6 @@ internal fun Module.checkImportedValid() {
|
|||||||
imr.applyModifications()
|
imr.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.checkRecursion(errors: ErrorReporter) {
|
|
||||||
val checker = AstRecursionChecker(namespace, errors)
|
|
||||||
checker.visit(this)
|
|
||||||
checker.processMessages(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Program.checkIdentifiers(errors: ErrorReporter) {
|
internal fun Program.checkIdentifiers(errors: ErrorReporter) {
|
||||||
|
|
||||||
val checker2 = AstIdentifiersChecker(this, errors)
|
val checker2 = AstIdentifiersChecker(this, errors)
|
||||||
@ -56,6 +51,9 @@ internal fun Program.checkIdentifiers(errors: ErrorReporter) {
|
|||||||
val transforms = AstVariousTransforms(this)
|
val transforms = AstVariousTransforms(this)
|
||||||
transforms.visit(this)
|
transforms.visit(this)
|
||||||
transforms.applyModifications()
|
transforms.applyModifications()
|
||||||
|
val lit2decl = LiteralsToAutoVars(this)
|
||||||
|
lit2decl.visit(this)
|
||||||
|
lit2decl.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modules.map { it.name }.toSet().size != modules.size) {
|
if (modules.map { it.name }.toSet().size != modules.size) {
|
||||||
@ -68,3 +66,37 @@ internal fun Program.variousCleanups() {
|
|||||||
process.visit(this)
|
process.visit(this)
|
||||||
process.applyModifications()
|
process.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun Program.moveMainAndStartToFirst() {
|
||||||
|
// the module containing the program entrypoint is moved to the first in the sequence.
|
||||||
|
// the "main" block containing the entrypoint is moved to the top in there,
|
||||||
|
// and finally the entrypoint subroutine "start" itself is moved to the top in that block.
|
||||||
|
|
||||||
|
val directives = modules[0].statements.filterIsInstance<Directive>()
|
||||||
|
val start = this.entrypoint()
|
||||||
|
if(start!=null) {
|
||||||
|
val mod = start.definingModule()
|
||||||
|
val block = start.definingBlock()
|
||||||
|
if(!modules.remove(mod))
|
||||||
|
throw FatalAstException("module wrong")
|
||||||
|
modules.add(0, mod)
|
||||||
|
mod.remove(block)
|
||||||
|
var afterDirective = mod.statements.indexOfFirst { it !is Directive }
|
||||||
|
if(afterDirective<0)
|
||||||
|
mod.statements.add(block)
|
||||||
|
else
|
||||||
|
mod.statements.add(afterDirective, block)
|
||||||
|
block.remove(start)
|
||||||
|
afterDirective = block.statements.indexOfFirst { it !is Directive }
|
||||||
|
if(afterDirective<0)
|
||||||
|
block.statements.add(start)
|
||||||
|
else
|
||||||
|
block.statements.add(afterDirective, start)
|
||||||
|
|
||||||
|
// overwrite the directives in the module containing the entrypoint
|
||||||
|
for(directive in directives) {
|
||||||
|
modules[0].statements.removeAll { it is Directive && it.directive == directive.directive }
|
||||||
|
modules[0].statements.add(0, directive)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -41,8 +41,8 @@ sealed class Expression: Node {
|
|||||||
&& other.left isSameAs left
|
&& other.left isSameAs left
|
||||||
&& other.right isSameAs right)
|
&& other.right isSameAs right)
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
(other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
|
(other is ArrayIndexedExpression && other.arrayvar.nameInSource == arrayvar.nameInSource
|
||||||
&& other.arrayspec.index isSameAs arrayspec.index)
|
&& other.indexer isSameAs indexer)
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
(other is DirectMemoryRead && other.addressExpression isSameAs addressExpression)
|
(other is DirectMemoryRead && other.addressExpression isSameAs addressExpression)
|
||||||
@ -232,20 +232,19 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArrayIndexedExpression(var identifier: IdentifierReference,
|
class ArrayIndexedExpression(var arrayvar: IdentifierReference,
|
||||||
val arrayspec: ArrayIndex,
|
val indexer: ArrayIndex,
|
||||||
override val position: Position) : Expression(), IAssignable {
|
override val position: Position) : Expression(), IAssignable {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
identifier.linkParents(this)
|
arrayvar.linkParents(this)
|
||||||
arrayspec.linkParents(this)
|
indexer.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
when {
|
when {
|
||||||
node===identifier -> identifier = replacement as IdentifierReference
|
node===arrayvar -> arrayvar = replacement as IdentifierReference
|
||||||
node===arrayspec.index -> arrayspec.index = replacement as Expression
|
|
||||||
else -> throw FatalAstException("invalid replace")
|
else -> throw FatalAstException("invalid replace")
|
||||||
}
|
}
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
@ -255,10 +254,10 @@ class ArrayIndexedExpression(var identifier: IdentifierReference,
|
|||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifier(vararg scopedName: String) = identifier.referencesIdentifier(*scopedName)
|
override fun referencesIdentifier(vararg scopedName: String) = arrayvar.referencesIdentifier(*scopedName)
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val target = identifier.targetStatement(program.namespace)
|
val target = arrayvar.targetStatement(program.namespace)
|
||||||
if (target is VarDecl) {
|
if (target is VarDecl) {
|
||||||
return when (target.datatype) {
|
return when (target.datatype) {
|
||||||
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
|
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
|
||||||
@ -270,7 +269,7 @@ class ArrayIndexedExpression(var identifier: IdentifierReference,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ArrayIndexed(ident=$identifier, arraysize=$arrayspec; pos=$position)"
|
return "ArrayIndexed(ident=$arrayvar, arraysize=$indexer; pos=$position)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -872,6 +871,12 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
return InferredTypes.void() // no return value
|
return InferredTypes.void() // no return value
|
||||||
if(stmt.returntypes.size==1)
|
if(stmt.returntypes.size==1)
|
||||||
return InferredTypes.knownFor(stmt.returntypes[0])
|
return InferredTypes.knownFor(stmt.returntypes[0])
|
||||||
|
|
||||||
|
// multiple return values. Can occur for asmsub routines. If there is exactly one register return value, take that.
|
||||||
|
val numRegisterReturns = stmt.asmReturnvaluesRegisters.count { it.registerOrPair!=null }
|
||||||
|
if(numRegisterReturns==1)
|
||||||
|
return InferredTypes.InferredType.known(DataType.UBYTE)
|
||||||
|
|
||||||
return InferredTypes.unknown() // has multiple return types... so not a single resulting datatype possible
|
return InferredTypes.unknown() // has multiple return types... so not a single resulting datatype possible
|
||||||
}
|
}
|
||||||
else -> return InferredTypes.unknown()
|
else -> return InferredTypes.unknown()
|
||||||
|
@ -120,11 +120,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
||||||
errors.err("for loop requires a variable to loop with", forLoop.position)
|
errors.err("for loop requires a variable to loop with", forLoop.position)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
fun checkLoopRangeValues() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
when (loopvar.datatype) {
|
when (loopvar.datatype) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
|
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
|
||||||
@ -346,17 +341,15 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(assignment: Assignment) {
|
override fun visit(assignment: Assignment) {
|
||||||
// assigning from a functioncall COULD return multiple values (from an asm subroutine)
|
|
||||||
if(assignment.value is FunctionCall) {
|
if(assignment.value is FunctionCall) {
|
||||||
val stmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace)
|
val stmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace)
|
||||||
if (stmt is Subroutine && stmt.isAsmSubroutine) {
|
if (stmt is Subroutine) {
|
||||||
if(stmt.returntypes.size>1)
|
val idt = assignment.target.inferType(program, assignment)
|
||||||
errors.err("It's not possible to store the multiple results of this asmsub call; you should use a small block of custom inline assembly for this.", assignment.value.position)
|
if(!idt.isKnown) {
|
||||||
else {
|
errors.err("return type mismatch", assignment.value.position)
|
||||||
val idt = assignment.target.inferType(program, assignment)
|
}
|
||||||
if(!idt.isKnown || stmt.returntypes.single()!=idt.typeOrElse(DataType.BYTE)) {
|
if(stmt.returntypes.size <= 1 && stmt.returntypes.single()!=idt.typeOrElse(DataType.BYTE)) {
|
||||||
errors.err("return type mismatch", assignment.value.position)
|
errors.err("return type mismatch", assignment.value.position)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -386,7 +379,8 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
val targetDt = assignment.target.inferType(program, assignment)
|
val targetDt = assignment.target.inferType(program, assignment)
|
||||||
if(assignment.value.inferType(program) != targetDt) {
|
val valueDt = assignment.value.inferType(program)
|
||||||
|
if(valueDt.isKnown && valueDt != targetDt) {
|
||||||
if(targetDt.typeOrElse(DataType.STRUCT) in IterableDatatypes)
|
if(targetDt.typeOrElse(DataType.STRUCT) in IterableDatatypes)
|
||||||
errors.err("cannot assign value to string or array", assignment.value.position)
|
errors.err("cannot assign value to string or array", assignment.value.position)
|
||||||
else
|
else
|
||||||
@ -446,12 +440,8 @@ internal class AstChecker(private val program: Program,
|
|||||||
checkValueTypeAndRange(targetDatatype.typeOrElse(DataType.BYTE), constVal)
|
checkValueTypeAndRange(targetDatatype.typeOrElse(DataType.BYTE), constVal)
|
||||||
} else {
|
} else {
|
||||||
val sourceDatatype = assignment.value.inferType(program)
|
val sourceDatatype = assignment.value.inferType(program)
|
||||||
if (!sourceDatatype.isKnown) {
|
if (sourceDatatype.isUnknown) {
|
||||||
if (assignment.value is FunctionCall) {
|
if (assignment.value !is FunctionCall)
|
||||||
val targetStmt = (assignment.value as FunctionCall).target.targetStatement(program.namespace)
|
|
||||||
if (targetStmt != null)
|
|
||||||
errors.err("function call doesn't return a suitable value to use in assignment", assignment.value.position)
|
|
||||||
} else
|
|
||||||
errors.err("assignment value is invalid or has no proper datatype", assignment.value.position)
|
errors.err("assignment value is invalid or has no proper datatype", assignment.value.position)
|
||||||
} else {
|
} else {
|
||||||
checkAssignmentCompatible(targetDatatype.typeOrElse(DataType.BYTE), assignTarget,
|
checkAssignmentCompatible(targetDatatype.typeOrElse(DataType.BYTE), assignTarget,
|
||||||
@ -469,6 +459,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
else {
|
else {
|
||||||
if(variable.datatype !in ArrayDatatypes
|
if(variable.datatype !in ArrayDatatypes
|
||||||
&& variable.type!=VarDeclType.MEMORY
|
&& variable.type!=VarDeclType.MEMORY
|
||||||
|
&& variable.struct == null
|
||||||
&& variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
|
&& variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
|
||||||
errors.err("invalid pointer-of operand type", addressOf.position)
|
errors.err("invalid pointer-of operand type", addressOf.position)
|
||||||
}
|
}
|
||||||
@ -479,7 +470,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
fun err(msg: String, position: Position?=null) = errors.err(msg, position ?: decl.position)
|
fun err(msg: String, position: Position?=null) = errors.err(msg, position ?: decl.position)
|
||||||
|
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.index?.referencesIdentifier(decl.name) == true)
|
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexVar?.referencesIdentifier(decl.name) == true)
|
||||||
err("recursive var declaration")
|
err("recursive var declaration")
|
||||||
|
|
||||||
// CONST can only occur on simple types (byte, word, float)
|
// CONST can only occur on simple types (byte, word, float)
|
||||||
@ -632,6 +623,14 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// string assignment is not supported in a vard
|
||||||
|
if(decl.datatype==DataType.STR) {
|
||||||
|
if(decl.value==null)
|
||||||
|
err("string var must be initialized with a string literal")
|
||||||
|
else if (decl.type==VarDeclType.VAR && decl.value !is StringLiteralValue)
|
||||||
|
err("string var can only be initialized with a string literal")
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(decl)
|
super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -753,8 +752,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
return e is StringLiteralValue
|
return e is StringLiteralValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!array.value.all { it is NumericLiteralValue || it is AddressOf || isPassByReferenceElement(it) })
|
if(array.parent is VarDecl) {
|
||||||
errors.err("array literal contains invalid types", array.position)
|
if (!array.value.all { it is NumericLiteralValue || it is AddressOf || isPassByReferenceElement(it) })
|
||||||
|
errors.err("array literal for variable initialization contains invalid types", array.position)
|
||||||
|
} else if(array.parent is ForLoop) {
|
||||||
|
if (!array.value.all { it.constValue(program) != null })
|
||||||
|
errors.err("array literal for iteration must contain constants. Try using a separate array variable instead?", array.position)
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(array)
|
super.visit(array)
|
||||||
}
|
}
|
||||||
@ -783,6 +787,8 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: BinaryExpression) {
|
override fun visit(expr: BinaryExpression) {
|
||||||
|
super.visit(expr)
|
||||||
|
|
||||||
val leftIDt = expr.left.inferType(program)
|
val leftIDt = expr.left.inferType(program)
|
||||||
val rightIDt = expr.right.inferType(program)
|
val rightIDt = expr.right.inferType(program)
|
||||||
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
||||||
@ -822,13 +828,12 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(leftDt !in NumericDatatypes)
|
if(leftDt !in NumericDatatypes && leftDt != DataType.STR)
|
||||||
errors.err("left operand is not numeric", expr.left.position)
|
errors.err("left operand is not numeric or str", expr.left.position)
|
||||||
if(rightDt!in NumericDatatypes)
|
if(rightDt!in NumericDatatypes && rightDt != DataType.STR)
|
||||||
errors.err("right operand is not numeric", expr.right.position)
|
errors.err("right operand is not numeric or str", expr.right.position)
|
||||||
if(leftDt!=rightDt)
|
if(leftDt!=rightDt)
|
||||||
errors.err("left and right operands aren't the same type", expr.left.position)
|
errors.err("left and right operands aren't the same type", expr.left.position)
|
||||||
super.visit(expr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(typecast: TypecastExpression) {
|
override fun visit(typecast: TypecastExpression) {
|
||||||
@ -888,7 +893,29 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
val error = VerifyFunctionArgTypes.checkTypes(functionCall, functionCall.definingScope(), program)
|
val error = VerifyFunctionArgTypes.checkTypes(functionCall, functionCall.definingScope(), program)
|
||||||
if(error!=null)
|
if(error!=null)
|
||||||
errors.err(error, functionCall.args.first().position)
|
errors.err(error, functionCall.position)
|
||||||
|
|
||||||
|
// check the functions that return multiple returnvalues.
|
||||||
|
val stmt = functionCall.target.targetStatement(program.namespace)
|
||||||
|
if (stmt is Subroutine) {
|
||||||
|
if (stmt.returntypes.size > 1) {
|
||||||
|
// Currently, it's only possible to handle ONE (or zero) return values from a subroutine.
|
||||||
|
// asmsub routines can have multiple return values, for instance in 2 different registers.
|
||||||
|
// It's not (yet) possible to handle these multiple return values because assignments
|
||||||
|
// are only to a single unique target at the same time.
|
||||||
|
// EXCEPTION:
|
||||||
|
// if the asmsub returns multiple values and one of them is via a status register bit,
|
||||||
|
// it *is* possible to handle them by just actually assigning the register value and
|
||||||
|
// dealing with the status bit as just being that, the status bit after the call.
|
||||||
|
val (returnRegisters, returnStatusflags) = stmt.asmReturnvaluesRegisters.partition { rr -> rr.registerOrPair != null }
|
||||||
|
if (returnRegisters.isEmpty() || returnRegisters.size == 1) {
|
||||||
|
if (returnStatusflags.any())
|
||||||
|
errors.warn("this asmsub also has one or more return 'values' in one of the status flags", functionCall.position)
|
||||||
|
} else {
|
||||||
|
errors.err("It's not possible to store the multiple result values of this asmsub call; you should use a small block of custom inline assembly for this.", functionCall.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.visit(functionCall)
|
super.visit(functionCall)
|
||||||
}
|
}
|
||||||
@ -981,7 +1008,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(postIncrDecr.target.arrayindexed != null) {
|
} else if(postIncrDecr.target.arrayindexed != null) {
|
||||||
val target = postIncrDecr.target.arrayindexed?.identifier?.targetStatement(program.namespace)
|
val target = postIncrDecr.target.arrayindexed?.arrayvar?.targetStatement(program.namespace)
|
||||||
if(target==null) {
|
if(target==null) {
|
||||||
errors.err("undefined symbol", postIncrDecr.position)
|
errors.err("undefined symbol", postIncrDecr.position)
|
||||||
}
|
}
|
||||||
@ -996,32 +1023,38 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||||
val target = arrayIndexedExpression.identifier.targetStatement(program.namespace)
|
val target = arrayIndexedExpression.arrayvar.targetStatement(program.namespace)
|
||||||
if(target is VarDecl) {
|
if(target is VarDecl) {
|
||||||
if(target.datatype !in IterableDatatypes)
|
if(target.datatype !in IterableDatatypes)
|
||||||
errors.err("indexing requires an iterable variable", arrayIndexedExpression.position)
|
errors.err("indexing requires an iterable variable", arrayIndexedExpression.position)
|
||||||
val arraysize = target.arraysize?.constIndex()
|
val arraysize = target.arraysize?.constIndex()
|
||||||
if(arraysize!=null) {
|
if(arraysize!=null) {
|
||||||
// check out of bounds
|
// check out of bounds
|
||||||
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
|
val index = arrayIndexedExpression.indexer.constIndex()
|
||||||
if(index!=null && (index<0 || index>=arraysize))
|
if(index!=null && (index<0 || index>=arraysize))
|
||||||
errors.err("array index out of bounds", arrayIndexedExpression.arrayspec.position)
|
errors.err("array index out of bounds", arrayIndexedExpression.indexer.position)
|
||||||
} else if(target.datatype == DataType.STR) {
|
} else if(target.datatype == DataType.STR) {
|
||||||
if(target.value is StringLiteralValue) {
|
if(target.value is StringLiteralValue) {
|
||||||
// check string lengths for non-memory mapped strings
|
// check string lengths for non-memory mapped strings
|
||||||
val stringLen = (target.value as StringLiteralValue).value.length
|
val stringLen = (target.value as StringLiteralValue).value.length
|
||||||
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
|
val index = arrayIndexedExpression.indexer.constIndex()
|
||||||
if (index != null && (index < 0 || index >= stringLen))
|
if (index != null && (index < 0 || index >= stringLen))
|
||||||
errors.err("index out of bounds", arrayIndexedExpression.arrayspec.position)
|
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
|
errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
|
||||||
|
|
||||||
// check index value 0..255
|
// check index value 0..255
|
||||||
val dtx = arrayIndexedExpression.arrayspec.index.inferType(program).typeOrElse(DataType.STRUCT)
|
val dtxNum = arrayIndexedExpression.indexer.indexNum?.inferType(program)?.typeOrElse(DataType.STRUCT)
|
||||||
if(dtx!= DataType.UBYTE && dtx!= DataType.BYTE)
|
if(dtxNum!=null && dtxNum != DataType.UBYTE && dtxNum != DataType.BYTE)
|
||||||
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
|
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
|
||||||
|
val dtxVar = arrayIndexedExpression.indexer.indexVar?.inferType(program)?.typeOrElse(DataType.STRUCT)
|
||||||
|
if(dtxVar!=null && dtxVar != DataType.UBYTE && dtxVar != DataType.BYTE)
|
||||||
|
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
|
||||||
|
|
||||||
|
if(arrayIndexedExpression.indexer.origExpression!=null)
|
||||||
|
throw FatalAstException("array indexer should have been replaced with a temp var @ ${arrayIndexedExpression.indexer.position}")
|
||||||
|
|
||||||
super.visit(arrayIndexedExpression)
|
super.visit(arrayIndexedExpression)
|
||||||
}
|
}
|
||||||
@ -1126,10 +1159,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
if(arraySpecSize<1 || arraySpecSize>256)
|
if(arraySpecSize<1 || arraySpecSize>256)
|
||||||
return err("byte array length must be 1-256")
|
return err("byte array length must be 1-256")
|
||||||
val constX = arrayspec.index.constValue(program)
|
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
|
||||||
if(constX?.type !in IntegerDatatypes)
|
|
||||||
return err("array size specifier must be constant integer value")
|
|
||||||
val expectedSize = constX!!.number.toInt()
|
|
||||||
if (arraySize != expectedSize)
|
if (arraySize != expectedSize)
|
||||||
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||||
return true
|
return true
|
||||||
@ -1148,10 +1178,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
if(arraySpecSize<1 || arraySpecSize>128)
|
if(arraySpecSize<1 || arraySpecSize>128)
|
||||||
return err("word array length must be 1-128")
|
return err("word array length must be 1-128")
|
||||||
val constX = arrayspec.index.constValue(program)
|
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
|
||||||
if(constX?.type !in IntegerDatatypes)
|
|
||||||
return err("array size specifier must be constant integer value")
|
|
||||||
val expectedSize = constX!!.number.toInt()
|
|
||||||
if (arraySize != expectedSize)
|
if (arraySize != expectedSize)
|
||||||
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||||
return true
|
return true
|
||||||
@ -1170,10 +1197,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
if(arraySpecSize < 1 || arraySpecSize>51)
|
if(arraySpecSize < 1 || arraySpecSize>51)
|
||||||
return err("float array length must be 1-51")
|
return err("float array length must be 1-51")
|
||||||
val constX = arrayspec.index.constValue(program)
|
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
|
||||||
if(constX?.type !in IntegerDatatypes)
|
|
||||||
return err("array size specifier must be constant integer value")
|
|
||||||
val expectedSize = constX!!.number.toInt()
|
|
||||||
if (arraySize != expectedSize)
|
if (arraySize != expectedSize)
|
||||||
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||||
} else
|
} else
|
||||||
|
@ -80,6 +80,11 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
|||||||
if (existing != null && existing !== decl)
|
if (existing != null && existing !== decl)
|
||||||
nameError(decl.name, decl.position, existing)
|
nameError(decl.name, decl.position, existing)
|
||||||
|
|
||||||
|
if(decl.definingBlock().name==decl.name)
|
||||||
|
nameError(decl.name, decl.position, decl.definingBlock())
|
||||||
|
if(decl.definingSubroutine()?.name==decl.name)
|
||||||
|
nameError(decl.name, decl.position, decl.definingSubroutine()!!)
|
||||||
|
|
||||||
super.visit(decl)
|
super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,8 +148,8 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(string: StringLiteralValue) {
|
override fun visit(string: StringLiteralValue) {
|
||||||
if (string.value.length !in 1..255)
|
if (string.value.length > 255)
|
||||||
errors.err("string literal length must be between 1 and 255", string.position)
|
errors.err("string literal length max is 255", string.position)
|
||||||
|
|
||||||
super.visit(string)
|
super.visit(string)
|
||||||
}
|
}
|
||||||
|
@ -1,118 +0,0 @@
|
|||||||
package prog8.ast.processing
|
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
|
||||||
import prog8.ast.base.ErrorReporter
|
|
||||||
import prog8.ast.base.Position
|
|
||||||
import prog8.ast.expressions.FunctionCall
|
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
|
||||||
import prog8.ast.statements.Subroutine
|
|
||||||
|
|
||||||
|
|
||||||
internal class AstRecursionChecker(private val namespace: INameScope,
|
|
||||||
private val errors: ErrorReporter) : IAstVisitor {
|
|
||||||
private val callGraph = DirectedGraph<INameScope>()
|
|
||||||
|
|
||||||
fun processMessages(modulename: String) {
|
|
||||||
val cycle = callGraph.checkForCycle()
|
|
||||||
if(cycle.isEmpty())
|
|
||||||
return
|
|
||||||
val chain = cycle.joinToString(" <-- ") { "${it.name} at ${it.position}" }
|
|
||||||
errors.err("Program contains recursive subroutine calls, this is not supported. Recursive chain:\n (a subroutine call in) $chain", Position.DUMMY)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
|
||||||
val scope = functionCallStatement.definingScope()
|
|
||||||
val targetStatement = functionCallStatement.target.targetStatement(namespace)
|
|
||||||
if(targetStatement!=null) {
|
|
||||||
val targetScope = when (targetStatement) {
|
|
||||||
is Subroutine -> targetStatement
|
|
||||||
else -> targetStatement.definingScope()
|
|
||||||
}
|
|
||||||
callGraph.add(scope, targetScope)
|
|
||||||
}
|
|
||||||
super.visit(functionCallStatement)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall) {
|
|
||||||
val scope = functionCall.definingScope()
|
|
||||||
val targetStatement = functionCall.target.targetStatement(namespace)
|
|
||||||
if(targetStatement!=null) {
|
|
||||||
val targetScope = when (targetStatement) {
|
|
||||||
is Subroutine -> targetStatement
|
|
||||||
else -> targetStatement.definingScope()
|
|
||||||
}
|
|
||||||
callGraph.add(scope, targetScope)
|
|
||||||
}
|
|
||||||
super.visit(functionCall)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DirectedGraph<VT> {
|
|
||||||
private val graph = mutableMapOf<VT, MutableSet<VT>>()
|
|
||||||
private var uniqueVertices = mutableSetOf<VT>()
|
|
||||||
val numVertices : Int
|
|
||||||
get() = uniqueVertices.size
|
|
||||||
|
|
||||||
fun add(from: VT, to: VT) {
|
|
||||||
var targets = graph[from]
|
|
||||||
if(targets==null) {
|
|
||||||
targets = mutableSetOf()
|
|
||||||
graph[from] = targets
|
|
||||||
}
|
|
||||||
targets.add(to)
|
|
||||||
uniqueVertices.add(from)
|
|
||||||
uniqueVertices.add(to)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun print() {
|
|
||||||
println("#vertices: $numVertices")
|
|
||||||
graph.forEach { (from, to) ->
|
|
||||||
println("$from CALLS:")
|
|
||||||
to.forEach { println(" $it") }
|
|
||||||
}
|
|
||||||
val cycle = checkForCycle()
|
|
||||||
if(cycle.isNotEmpty()) {
|
|
||||||
println("CYCLIC! $cycle")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun checkForCycle(): MutableList<VT> {
|
|
||||||
val visited = uniqueVertices.associateWith { false }.toMutableMap()
|
|
||||||
val recStack = uniqueVertices.associateWith { false }.toMutableMap()
|
|
||||||
val cycle = mutableListOf<VT>()
|
|
||||||
for(node in uniqueVertices) {
|
|
||||||
if(isCyclicUntil(node, visited, recStack, cycle))
|
|
||||||
return cycle
|
|
||||||
}
|
|
||||||
return mutableListOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isCyclicUntil(node: VT,
|
|
||||||
visited: MutableMap<VT, Boolean>,
|
|
||||||
recStack: MutableMap<VT, Boolean>,
|
|
||||||
cycleNodes: MutableList<VT>): Boolean {
|
|
||||||
|
|
||||||
if(recStack[node]==true) return true
|
|
||||||
if(visited[node]==true) return false
|
|
||||||
|
|
||||||
// mark current node as visited and add to recursion stack
|
|
||||||
visited[node] = true
|
|
||||||
recStack[node] = true
|
|
||||||
|
|
||||||
// recurse for all neighbours
|
|
||||||
val neighbors = graph[node]
|
|
||||||
if(neighbors!=null) {
|
|
||||||
for (neighbour in neighbors) {
|
|
||||||
if (isCyclicUntil(neighbour, visited, recStack, cycleNodes)) {
|
|
||||||
cycleNodes.add(node)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pop node from recursion stack
|
|
||||||
recStack[node] = false
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -47,78 +47,59 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
when {
|
val leftStr = expr.left as? StringLiteralValue
|
||||||
expr.left is StringLiteralValue ->
|
val rightStr = expr.right as? StringLiteralValue
|
||||||
return listOf(IAstModification.ReplaceNode(
|
if(expr.operator == "+") {
|
||||||
expr,
|
val concatenatedString = concatString(expr)
|
||||||
processBinaryExprWithString(expr.left as StringLiteralValue, expr.right, expr),
|
if(concatenatedString!=null)
|
||||||
parent
|
return listOf(IAstModification.ReplaceNode(expr, concatenatedString, parent))
|
||||||
))
|
|
||||||
expr.right is StringLiteralValue ->
|
|
||||||
return listOf(IAstModification.ReplaceNode(
|
|
||||||
expr,
|
|
||||||
processBinaryExprWithString(expr.right as StringLiteralValue, expr.left, expr),
|
|
||||||
parent
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
else if(expr.operator == "*") {
|
||||||
return noModifications
|
if (leftStr!=null) {
|
||||||
}
|
val amount = expr.right.constValue(program)
|
||||||
|
if(amount!=null) {
|
||||||
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
|
val string = leftStr.value.repeat(amount.number.toInt())
|
||||||
if(string.parent !is VarDecl) {
|
val strval = StringLiteralValue(string, leftStr.altEncoding, expr.position)
|
||||||
// replace the literal string by a identifier reference to a new local vardecl
|
return listOf(IAstModification.ReplaceNode(expr, strval, parent))
|
||||||
val vardecl = VarDecl.createAuto(string)
|
}
|
||||||
val identifier = IdentifierReference(listOf(vardecl.name), vardecl.position)
|
|
||||||
return listOf(
|
|
||||||
IAstModification.ReplaceNode(string, identifier, parent),
|
|
||||||
IAstModification.InsertFirst(vardecl, string.definingScope() as Node)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
|
|
||||||
val vardecl = array.parent as? VarDecl
|
|
||||||
if(vardecl!=null) {
|
|
||||||
// adjust the datatype of the array (to an educated guess)
|
|
||||||
val arrayDt = array.type
|
|
||||||
if(!arrayDt.istype(vardecl.datatype)) {
|
|
||||||
val cast = array.cast(vardecl.datatype)
|
|
||||||
if (cast != null && cast!=array)
|
|
||||||
return listOf(IAstModification.ReplaceNode(vardecl.value!!, cast, vardecl))
|
|
||||||
}
|
}
|
||||||
} else {
|
else if (rightStr!=null) {
|
||||||
val arrayDt = array.guessDatatype(program)
|
val amount = expr.right.constValue(program)
|
||||||
if(arrayDt.isKnown) {
|
if(amount!=null) {
|
||||||
// this array literal is part of an expression, turn it into an identifier reference
|
val string = rightStr.value.repeat(amount.number.toInt())
|
||||||
val litval2 = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
val strval = StringLiteralValue(string, rightStr.altEncoding, expr.position)
|
||||||
if(litval2!=null && litval2!=array) {
|
return listOf(IAstModification.ReplaceNode(expr, strval, parent))
|
||||||
val vardecl2 = VarDecl.createAuto(litval2)
|
|
||||||
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
|
|
||||||
return listOf(
|
|
||||||
IAstModification.ReplaceNode(array, identifier, parent),
|
|
||||||
IAstModification.InsertFirst(vardecl2, array.definingScope() as Node)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processBinaryExprWithString(string: StringLiteralValue, operand: Expression, expr: BinaryExpression): Expression {
|
private fun concatString(expr: BinaryExpression): StringLiteralValue? {
|
||||||
val constvalue = operand.constValue(program)
|
val rightStrval = expr.right as? StringLiteralValue
|
||||||
if(constvalue!=null) {
|
val leftStrval = expr.left as? StringLiteralValue
|
||||||
if (expr.operator == "*") {
|
return when {
|
||||||
// repeat a string a number of times
|
expr.operator!="+" -> null
|
||||||
return StringLiteralValue(string.value.repeat(constvalue.number.toInt()), string.altEncoding, expr.position)
|
expr.left is BinaryExpression && rightStrval!=null -> {
|
||||||
|
val subStrVal = concatString(expr.left as BinaryExpression)
|
||||||
|
if(subStrVal==null)
|
||||||
|
null
|
||||||
|
else
|
||||||
|
StringLiteralValue("${subStrVal.value}${rightStrval.value}", subStrVal.altEncoding, rightStrval.position)
|
||||||
}
|
}
|
||||||
|
expr.right is BinaryExpression && leftStrval!=null -> {
|
||||||
|
val subStrVal = concatString(expr.right as BinaryExpression)
|
||||||
|
if(subStrVal==null)
|
||||||
|
null
|
||||||
|
else
|
||||||
|
StringLiteralValue("${leftStrval.value}${subStrVal.value}", subStrVal.altEncoding, leftStrval.position)
|
||||||
|
}
|
||||||
|
leftStrval!=null && rightStrval!=null -> {
|
||||||
|
StringLiteralValue("${leftStrval.value}${rightStrval.value}", leftStrval.altEncoding, leftStrval.position)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
}
|
}
|
||||||
if(expr.operator == "+" && operand is StringLiteralValue) {
|
|
||||||
// concatenate two strings
|
|
||||||
return StringLiteralValue("${string.value}${operand.value}", string.altEncoding, expr.position)
|
|
||||||
}
|
|
||||||
return expr
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,78 +9,58 @@ import prog8.ast.statements.*
|
|||||||
interface IAstModification {
|
interface IAstModification {
|
||||||
fun perform()
|
fun perform()
|
||||||
|
|
||||||
class Remove(val node: Node, val parent: Node) : IAstModification {
|
class Remove(val node: Node, val parent: INameScope) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
if(parent is INameScope) {
|
if (!parent.statements.remove(node) && parent !is GlobalNamespace)
|
||||||
if (!parent.statements.remove(node) && parent !is GlobalNamespace)
|
throw FatalAstException("attempt to remove non-existing node $node")
|
||||||
throw FatalAstException("attempt to remove non-existing node $node")
|
|
||||||
} else {
|
|
||||||
throw FatalAstException("parent of a remove modification is not an INameScope")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SetExpression(val setter: (newExpr: Expression) -> Unit, val newExpr: Expression, val parent: Node) : IAstModification {
|
class SetExpression(private val setter: (newExpr: Expression) -> Unit, private val newExpr: Expression, private val parent: Node) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
setter(newExpr)
|
setter(newExpr)
|
||||||
newExpr.linkParents(parent)
|
newExpr.linkParents(parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InsertFirst(val stmt: Statement, val parent: Node) : IAstModification {
|
class InsertFirst(private val stmt: Statement, private val parent: INameScope) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
if(parent is INameScope) {
|
parent.statements.add(0, stmt)
|
||||||
parent.statements.add(0, stmt)
|
stmt.linkParents(parent as Node)
|
||||||
stmt.linkParents(parent)
|
|
||||||
} else {
|
|
||||||
throw FatalAstException("parent of an insert modification is not an INameScope")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InsertLast(val stmt: Statement, val parent: Node) : IAstModification {
|
class InsertLast(private val stmt: Statement, private val parent: INameScope) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
if(parent is INameScope) {
|
parent.statements.add(stmt)
|
||||||
parent.statements.add(stmt)
|
stmt.linkParents(parent as Node)
|
||||||
stmt.linkParents(parent)
|
|
||||||
} else {
|
|
||||||
throw FatalAstException("parent of an insert modification is not an INameScope")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InsertAfter(val after: Statement, val stmt: Statement, val parent: Node) : IAstModification {
|
class InsertAfter(private val after: Statement, private val stmt: Statement, private val parent: INameScope) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
if(parent is INameScope) {
|
val idx = parent.statements.indexOfFirst { it===after } + 1
|
||||||
val idx = parent.statements.indexOfFirst { it===after } + 1
|
parent.statements.add(idx, stmt)
|
||||||
parent.statements.add(idx, stmt)
|
stmt.linkParents(parent as Node)
|
||||||
stmt.linkParents(parent)
|
|
||||||
} else {
|
|
||||||
throw FatalAstException("parent of an insert modification is not an INameScope")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InsertBefore(val before: Statement, val stmt: Statement, val parent: Node) : IAstModification {
|
class InsertBefore(private val before: Statement, private val stmt: Statement, private val parent: INameScope) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
if(parent is INameScope) {
|
val idx = parent.statements.indexOfFirst { it===before }
|
||||||
val idx = parent.statements.indexOfFirst { it===before }
|
parent.statements.add(idx, stmt)
|
||||||
parent.statements.add(idx, stmt)
|
stmt.linkParents(parent as Node)
|
||||||
stmt.linkParents(parent)
|
|
||||||
} else {
|
|
||||||
throw FatalAstException("parent of an insert modification is not an INameScope")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReplaceNode(val node: Node, val replacement: Node, val parent: Node) : IAstModification {
|
class ReplaceNode(private val node: Node, private val replacement: Node, private val parent: Node) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
parent.replaceChildNode(node, replacement)
|
parent.replaceChildNode(node, replacement)
|
||||||
replacement.linkParents(parent)
|
replacement.linkParents(parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SwapOperands(val expr: BinaryExpression): IAstModification {
|
class SwapOperands(private val expr: BinaryExpression): IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
require(expr.operator in associativeOperators)
|
require(expr.operator in associativeOperators)
|
||||||
val tmp = expr.left
|
val tmp = expr.left
|
||||||
@ -363,8 +343,8 @@ abstract class AstWalker {
|
|||||||
|
|
||||||
fun visit(arrayIndexedExpression: ArrayIndexedExpression, parent: Node) {
|
fun visit(arrayIndexedExpression: ArrayIndexedExpression, parent: Node) {
|
||||||
track(before(arrayIndexedExpression, parent), arrayIndexedExpression, parent)
|
track(before(arrayIndexedExpression, parent), arrayIndexedExpression, parent)
|
||||||
arrayIndexedExpression.identifier.accept(this, arrayIndexedExpression)
|
arrayIndexedExpression.arrayvar.accept(this, arrayIndexedExpression)
|
||||||
arrayIndexedExpression.arrayspec.accept(this, arrayIndexedExpression)
|
arrayIndexedExpression.indexer.accept(this, arrayIndexedExpression)
|
||||||
track(after(arrayIndexedExpression, parent), arrayIndexedExpression, parent)
|
track(after(arrayIndexedExpression, parent), arrayIndexedExpression, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,8 +125,8 @@ interface IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||||
arrayIndexedExpression.identifier.accept(this)
|
arrayIndexedExpression.arrayvar.accept(this)
|
||||||
arrayIndexedExpression.arrayspec.accept(this)
|
arrayIndexedExpression.indexer.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(assignTarget: AssignTarget) {
|
fun visit(assignTarget: AssignTarget) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.INameScope
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ internal class ImportedModuleDirectiveRemover: AstWalker() {
|
|||||||
|
|
||||||
override fun before(directive: Directive, parent: Node): Iterable<IAstModification> {
|
override fun before(directive: Directive, parent: Node): Iterable<IAstModification> {
|
||||||
if(directive.directive in moduleLevelDirectives) {
|
if(directive.directive in moduleLevelDirectives) {
|
||||||
return listOf(IAstModification.Remove(directive, parent))
|
return listOf(IAstModification.Remove(directive, parent as INameScope))
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
53
compiler/src/prog8/ast/processing/LiteralsToAutoVars.kt
Normal file
53
compiler/src/prog8/ast/processing/LiteralsToAutoVars.kt
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
|
|
||||||
|
internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(string.parent !is VarDecl && string.parent !is WhenChoice) {
|
||||||
|
// replace the literal string by a identifier reference to a new local vardecl
|
||||||
|
val vardecl = VarDecl.createAuto(string)
|
||||||
|
val identifier = IdentifierReference(listOf(vardecl.name), vardecl.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(string, identifier, parent),
|
||||||
|
IAstModification.InsertFirst(vardecl, string.definingScope())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||||
|
val vardecl = array.parent as? VarDecl
|
||||||
|
if(vardecl!=null) {
|
||||||
|
// adjust the datatype of the array (to an educated guess)
|
||||||
|
val arrayDt = array.type
|
||||||
|
if(!arrayDt.istype(vardecl.datatype)) {
|
||||||
|
val cast = array.cast(vardecl.datatype)
|
||||||
|
if (cast != null && cast !== array)
|
||||||
|
return listOf(IAstModification.ReplaceNode(vardecl.value!!, cast, vardecl))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val arrayDt = array.guessDatatype(program)
|
||||||
|
if(arrayDt.isKnown) {
|
||||||
|
// this array literal is part of an expression, turn it into an identifier reference
|
||||||
|
val litval2 = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
||||||
|
if(litval2!=null) {
|
||||||
|
val vardecl2 = VarDecl.createAuto(litval2)
|
||||||
|
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(array, identifier, parent),
|
||||||
|
IAstModification.InsertFirst(vardecl2, array.definingScope())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
@ -6,12 +6,12 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
|
|
||||||
internal class StatementReorderer(val program: Program) : AstWalker() {
|
internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
||||||
// Reorders the statements in a way the compiler needs.
|
// Reorders the statements in a way the compiler needs.
|
||||||
// - 'main' block must be the very first statement UNLESS it has an address set.
|
// - 'main' block must be the very first statement UNLESS it has an address set.
|
||||||
// - library blocks are put last.
|
// - library blocks are put last.
|
||||||
// - blocks are ordered by address, where blocks without address are placed last.
|
// - blocks are ordered by address, where blocks without address are placed last.
|
||||||
// - in every scope, most directives and vardecls are moved to the top.
|
// - in every block and module, most directives and vardecls are moved to the top. (not in subroutines!)
|
||||||
// - the 'start' subroutine is moved to the top.
|
// - the 'start' subroutine is moved to the top.
|
||||||
// - (syntax desugaring) a vardecl with a non-const initializer value is split into a regular vardecl and an assignment statement.
|
// - (syntax desugaring) a vardecl with a non-const initializer value is split into a regular vardecl and an assignment statement.
|
||||||
// - (syntax desugaring) struct value assignment is expanded into several struct member assignments.
|
// - (syntax desugaring) struct value assignment is expanded into several struct member assignments.
|
||||||
@ -71,6 +71,69 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
when (val expr2 = arrayIndexedExpression.indexer.origExpression) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
arrayIndexedExpression.indexer.indexNum = expr2
|
||||||
|
arrayIndexedExpression.indexer.origExpression = null
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
arrayIndexedExpression.indexer.indexVar = expr2
|
||||||
|
arrayIndexedExpression.indexer.origExpression = null
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
is Expression -> {
|
||||||
|
// replace complex indexing with a temp variable
|
||||||
|
return getAutoIndexerVarFor(arrayIndexedExpression)
|
||||||
|
}
|
||||||
|
else -> return noModifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
|
||||||
|
val modifications = mutableListOf<IAstModification>()
|
||||||
|
val subroutine = expr.definingSubroutine()!!
|
||||||
|
val statement = expr.containingStatement()
|
||||||
|
val indexerVarPrefix = "prog8_autovar_index_"
|
||||||
|
val indexerVarName: String
|
||||||
|
val repo = subroutine.asmGenInfo.usedAutoArrayIndexerForStatements
|
||||||
|
|
||||||
|
val freeVar = repo.filter { statement !in it.value }.map { it.key }.firstOrNull()
|
||||||
|
if(freeVar==null) {
|
||||||
|
// add another loop index var to be used for this expression
|
||||||
|
val statementId = expr.hashCode()
|
||||||
|
indexerVarName = "$indexerVarPrefix$statementId"
|
||||||
|
// create the indexer var at block level scope
|
||||||
|
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE,
|
||||||
|
null, indexerVarName, null, null, isArray = false, autogeneratedDontRemove = true, position = expr.position)
|
||||||
|
modifications.add(IAstModification.InsertFirst(vardecl, subroutine))
|
||||||
|
var statements = repo[indexerVarName]
|
||||||
|
if(statements==null) {
|
||||||
|
statements = mutableSetOf()
|
||||||
|
repo[indexerVarName] = statements
|
||||||
|
}
|
||||||
|
statements.add(statement)
|
||||||
|
} else {
|
||||||
|
// reuse an already created indexer autovar
|
||||||
|
repo.getValue(freeVar).add(statement)
|
||||||
|
indexerVarName = freeVar
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign the indexing expression to the helper variable, and replace the indexer with just the variable
|
||||||
|
val indexerExpression = expr.indexer.origExpression!!
|
||||||
|
val target = AssignTarget(IdentifierReference(listOf(indexerVarName), indexerExpression.position), null, null, indexerExpression.position)
|
||||||
|
val assign = Assignment(target, indexerExpression, indexerExpression.position)
|
||||||
|
val beforeWhat = expr.containingStatement()
|
||||||
|
modifications.add(IAstModification.InsertBefore(beforeWhat, assign, beforeWhat.definingScope()))
|
||||||
|
modifications.add(IAstModification.SetExpression( {
|
||||||
|
expr.indexer.indexVar = it as IdentifierReference
|
||||||
|
expr.indexer.indexNum = null
|
||||||
|
expr.indexer.origExpression = null
|
||||||
|
}, target.identifier!!.copy(), expr.indexer))
|
||||||
|
|
||||||
|
return modifications
|
||||||
|
}
|
||||||
|
|
||||||
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
|
||||||
val choices = whenStatement.choiceValues(program).sortedBy {
|
val choices = whenStatement.choiceValues(program).sortedBy {
|
||||||
@ -81,23 +144,57 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
val declValue = decl.value
|
||||||
|
if(declValue!=null && decl.type== VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||||
|
val declConstValue = declValue.constValue(program)
|
||||||
|
if(declConstValue==null) {
|
||||||
|
// move the vardecl (without value) to the scope and replace this with a regular assignment
|
||||||
|
// Unless we're dealing with a floating point variable because that will actually make things less efficient at the moment (because floats are mostly calcualated via the stack)
|
||||||
|
if(decl.datatype!=DataType.FLOAT) {
|
||||||
|
decl.value = null
|
||||||
|
decl.allowInitializeWithZero = false
|
||||||
|
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||||
|
val assign = Assignment(target, declValue, decl.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.ReplaceNode(decl, assign, parent),
|
||||||
|
IAstModification.InsertFirst(decl, decl.definingScope())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
val valueType = assignment.value.inferType(program)
|
val valueType = assignment.value.inferType(program)
|
||||||
val targetType = assignment.target.inferType(program, assignment)
|
val targetType = assignment.target.inferType(program, assignment)
|
||||||
|
var assignments = emptyList<Assignment>()
|
||||||
|
|
||||||
if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) {
|
if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) {
|
||||||
val assignments = if (assignment.value is ArrayLiteralValue) {
|
assignments = if (assignment.value is ArrayLiteralValue) {
|
||||||
flattenStructAssignmentFromStructLiteral(assignment, program) // 'structvar = [ ..... ] '
|
flattenStructAssignmentFromStructLiteral(assignment) // 'structvar = [ ..... ] '
|
||||||
} else {
|
} else {
|
||||||
flattenStructAssignmentFromIdentifier(assignment, program) // 'structvar1 = structvar2'
|
flattenStructAssignmentFromIdentifier(assignment) // 'structvar1 = structvar2'
|
||||||
}
|
}
|
||||||
if(assignments.isNotEmpty()) {
|
}
|
||||||
val modifications = mutableListOf<IAstModification>()
|
|
||||||
assignments.reversed().mapTo(modifications) { IAstModification.InsertAfter(assignment, it, parent) }
|
if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) {
|
||||||
modifications.add(IAstModification.Remove(assignment, parent))
|
assignments = if (assignment.value is ArrayLiteralValue) {
|
||||||
return modifications
|
flattenArrayAssignmentFromArrayLiteral(assignment) // 'arrayvar = [ ..... ] '
|
||||||
|
} else {
|
||||||
|
flattenArrayAssignmentFromIdentifier(assignment) // 'arrayvar1 = arrayvar2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(assignments.isNotEmpty()) {
|
||||||
|
val modifications = mutableListOf<IAstModification>()
|
||||||
|
val scope = assignment.definingScope()
|
||||||
|
assignments.reversed().mapTo(modifications) { IAstModification.InsertAfter(assignment, it, scope) }
|
||||||
|
modifications.add(IAstModification.Remove(assignment, scope))
|
||||||
|
return modifications
|
||||||
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,15 +247,55 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment, program: Program): List<Assignment> {
|
private fun flattenArrayAssignmentFromArrayLiteral(assign: Assignment): List<Assignment> {
|
||||||
|
val identifier = assign.target.identifier!!
|
||||||
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
|
val alv = assign.value as? ArrayLiteralValue
|
||||||
|
return flattenArrayAssign(targetVar, alv, identifier, assign.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun flattenArrayAssignmentFromIdentifier(assign: Assignment): List<Assignment> {
|
||||||
|
val identifier = assign.target.identifier!!
|
||||||
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
|
val sourceIdent = assign.value as IdentifierReference
|
||||||
|
val sourceVar = sourceIdent.targetVarDecl(program.namespace)!!
|
||||||
|
if(!sourceVar.isArray) {
|
||||||
|
errors.err("value must be an array", sourceIdent.position)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
val alv = sourceVar.value as? ArrayLiteralValue
|
||||||
|
return flattenArrayAssign(targetVar, alv, identifier, assign.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun flattenArrayAssign(targetVar: VarDecl, alv: ArrayLiteralValue?, identifier: IdentifierReference, position: Position): List<Assignment> {
|
||||||
|
if(targetVar.arraysize==null) {
|
||||||
|
errors.err("array has no defined size", identifier.position)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
if(alv==null || alv.value.size != targetVar.arraysize!!.constIndex()) {
|
||||||
|
errors.err("element count mismatch", position)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO use a pointer loop instead of individual assignments
|
||||||
|
return alv.value.withIndex().map { (index, value)->
|
||||||
|
val idx = ArrayIndexedExpression(identifier, ArrayIndex(NumericLiteralValue(DataType.UBYTE, index, position), position), position)
|
||||||
|
Assignment(AssignTarget(null, idx, null, position), value, value.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment): List<Assignment> {
|
||||||
val identifier = structAssignment.target.identifier!!
|
val identifier = structAssignment.target.identifier!!
|
||||||
val identifierName = identifier.nameInSource.single()
|
val identifierName = identifier.nameInSource.single()
|
||||||
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
val struct = targetVar.struct!!
|
val struct = targetVar.struct!!
|
||||||
|
|
||||||
val slv = structAssignment.value as? ArrayLiteralValue
|
val slv = structAssignment.value as? ArrayLiteralValue
|
||||||
if(slv==null || slv.value.size != struct.numberOfElements)
|
if(slv==null || slv.value.size != struct.numberOfElements) {
|
||||||
throw FatalAstException("element count mismatch")
|
errors.err("element count mismatch", structAssignment.position)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
return struct.statements.zip(slv.value).map { (targetDecl, sourceValue) ->
|
return struct.statements.zip(slv.value).map { (targetDecl, sourceValue) ->
|
||||||
targetDecl as VarDecl
|
targetDecl as VarDecl
|
||||||
@ -171,7 +308,8 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment, program: Program): List<Assignment> {
|
private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment): List<Assignment> {
|
||||||
|
// TODO use memcopy beyond a certain number of elements
|
||||||
val identifier = structAssignment.target.identifier!!
|
val identifier = structAssignment.target.identifier!!
|
||||||
val identifierName = identifier.nameInSource.single()
|
val identifierName = identifier.nameInSource.single()
|
||||||
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
|
@ -23,6 +23,11 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
if(decl.type==VarDeclType.VAR && declValue!=null && decl.struct==null) {
|
if(decl.type==VarDeclType.VAR && declValue!=null && decl.struct==null) {
|
||||||
val valueDt = declValue.inferType(program)
|
val valueDt = declValue.inferType(program)
|
||||||
if(!valueDt.istype(decl.datatype)) {
|
if(!valueDt.istype(decl.datatype)) {
|
||||||
|
|
||||||
|
// don't add a typecast on an array initializer value
|
||||||
|
if(valueDt.typeOrElse(DataType.STRUCT) in IntegerDatatypes && decl.datatype in ArrayDatatypes)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
declValue,
|
declValue,
|
||||||
TypecastExpression(declValue, decl.datatype, true, declValue.position),
|
TypecastExpression(declValue, decl.datatype, true, declValue.position),
|
||||||
@ -124,10 +129,12 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
call as Node)
|
call as Node)
|
||||||
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
|
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
|
||||||
// we allow STR/ARRAY values in place of UWORD parameters. Take their address instead.
|
// we allow STR/ARRAY values in place of UWORD parameters. Take their address instead.
|
||||||
modifications += IAstModification.ReplaceNode(
|
if(arg.second.value is IdentifierReference) {
|
||||||
call.args[arg.second.index],
|
modifications += IAstModification.ReplaceNode(
|
||||||
AddressOf(arg.second.value as IdentifierReference, arg.second.value.position),
|
call.args[arg.second.index],
|
||||||
call as Node)
|
AddressOf(arg.second.value as IdentifierReference, arg.second.value.position),
|
||||||
|
call as Node)
|
||||||
|
}
|
||||||
} else if(arg.second.value is NumericLiteralValue) {
|
} else if(arg.second.value is NumericLiteralValue) {
|
||||||
val cast = (arg.second.value as NumericLiteralValue).cast(requiredType)
|
val cast = (arg.second.value as NumericLiteralValue).cast(requiredType)
|
||||||
if(cast.isValid)
|
if(cast.isValid)
|
||||||
|
@ -12,7 +12,7 @@ internal class VariousCleanups: AstWalker() {
|
|||||||
private val noModifications = emptyList<IAstModification>()
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
|
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
|
||||||
return listOf(IAstModification.Remove(nopStatement, parent))
|
return listOf(IAstModification.Remove(nopStatement, parent as INameScope))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
|
@ -4,10 +4,9 @@ import prog8.ast.IFunctionCall
|
|||||||
import prog8.ast.INameScope
|
import prog8.ast.INameScope
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.FunctionCall
|
import prog8.ast.expressions.FunctionCall
|
||||||
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
|
||||||
import prog8.ast.statements.Subroutine
|
|
||||||
import prog8.compiler.CompilerException
|
import prog8.compiler.CompilerException
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
@ -43,7 +42,6 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
|
|||||||
val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) }
|
val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) }
|
||||||
val target = call.target.targetStatement(scope)
|
val target = call.target.targetStatement(scope)
|
||||||
if (target is Subroutine) {
|
if (target is Subroutine) {
|
||||||
// asmsub types are not checked specifically at this time
|
|
||||||
if(call.args.size != target.parameters.size)
|
if(call.args.size != target.parameters.size)
|
||||||
return "invalid number of arguments"
|
return "invalid number of arguments"
|
||||||
val paramtypes = target.parameters.map { it.type }
|
val paramtypes = target.parameters.map { it.type }
|
||||||
@ -53,6 +51,16 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
|
|||||||
val expected = paramtypes[mismatch].toString()
|
val expected = paramtypes[mismatch].toString()
|
||||||
return "argument ${mismatch + 1} type mismatch, was: $actual expected: $expected"
|
return "argument ${mismatch + 1} type mismatch, was: $actual expected: $expected"
|
||||||
}
|
}
|
||||||
|
if(target.isAsmSubroutine) {
|
||||||
|
if(target.asmReturnvaluesRegisters.size>1) {
|
||||||
|
// multiple return values will NOT work inside an expression.
|
||||||
|
// they MIGHT work in a regular assignment or just a function call statement.
|
||||||
|
val parent = if(call is Statement) call.parent else if(call is Expression) call.parent else null
|
||||||
|
if(call !is FunctionCallStatement && parent !is Assignment && parent !is VarDecl) {
|
||||||
|
return "can't use subroutine call that returns multiple return values here (try moving it into a separate assignment)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (target is BuiltinFunctionStatementPlaceholder) {
|
else if (target is BuiltinFunctionStatementPlaceholder) {
|
||||||
val func = BuiltinFunctions.getValue(target.name)
|
val func = BuiltinFunctions.getValue(target.name)
|
||||||
|
@ -5,6 +5,7 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
|
import prog8.compiler.CompilerException
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
|
|
||||||
|
|
||||||
@ -29,12 +30,6 @@ sealed class Statement : Node {
|
|||||||
scope.add(name)
|
scope.add(name)
|
||||||
return scope.joinToString(".")
|
return scope.joinToString(".")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun definingBlock(): Block {
|
|
||||||
if(this is Block)
|
|
||||||
return this
|
|
||||||
return findParentNode<Block>(this)!!
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -57,6 +52,7 @@ class Block(override val name: String,
|
|||||||
val isInLibrary: Boolean,
|
val isInLibrary: Boolean,
|
||||||
override val position: Position) : Statement(), INameScope {
|
override val position: Position) : Statement(), INameScope {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
override val asmGenInfo = AsmGenInfo()
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
@ -176,6 +172,7 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
private set
|
private set
|
||||||
var structHasBeenFlattened = false // set later
|
var structHasBeenFlattened = false // set later
|
||||||
private set
|
private set
|
||||||
|
var allowInitializeWithZero = true
|
||||||
|
|
||||||
// prefix for literal values that are turned into a variable on the heap
|
// prefix for literal values that are turned into a variable on the heap
|
||||||
|
|
||||||
@ -234,7 +231,8 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Expression && node===value)
|
// TODO the check that node===value is too strict sometimes, but leaving it out allows for bugs to creep through ... :( Perhaps check when adding the replace if there is already a replace on the same node?
|
||||||
|
require(replacement is Expression)
|
||||||
value = replacement
|
value = replacement
|
||||||
replacement.parent = this
|
replacement.parent = this
|
||||||
}
|
}
|
||||||
@ -246,7 +244,12 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, struct=$structName, value=$value, pos=$position)"
|
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, struct=$structName, value=$value, pos=$position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun zeroElementValue() = defaultZero(declaredDatatype, position)
|
fun zeroElementValue(): NumericLiteralValue {
|
||||||
|
if(allowInitializeWithZero)
|
||||||
|
return defaultZero(declaredDatatype, position)
|
||||||
|
else
|
||||||
|
throw CompilerException("attempt to get zero value for vardecl that shouldn't get it")
|
||||||
|
}
|
||||||
|
|
||||||
fun flattenStructMembers(): MutableList<Statement> {
|
fun flattenStructMembers(): MutableList<Statement> {
|
||||||
val result = struct!!.statements.withIndex().map {
|
val result = struct!!.statements.withIndex().map {
|
||||||
@ -275,36 +278,72 @@ class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Posit
|
|||||||
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, null, false, true, position)
|
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, null, false, true, position)
|
||||||
|
|
||||||
|
|
||||||
class ArrayIndex(var index: Expression, override val position: Position) : Node {
|
class ArrayIndex(var origExpression: Expression?, // will be replaced later by either the number or the identifier
|
||||||
|
override val position: Position) : Node {
|
||||||
|
// for code simplicity, either indexed via a constant number or via a variable (no arbitrary expressions)
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
var indexNum: NumericLiteralValue? = origExpression as? NumericLiteralValue
|
||||||
|
var indexVar: IdentifierReference? = origExpression as? IdentifierReference
|
||||||
|
|
||||||
|
init {
|
||||||
|
if(indexNum!=null || indexVar!=null)
|
||||||
|
origExpression = null
|
||||||
|
}
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
index.linkParents(this)
|
origExpression?.linkParents(this)
|
||||||
|
indexNum?.linkParents(this)
|
||||||
|
indexVar?.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Expression && node===index)
|
require(replacement is Expression)
|
||||||
index = replacement
|
when {
|
||||||
replacement.parent = this
|
node===origExpression -> origExpression = replacement
|
||||||
|
node===indexVar -> {
|
||||||
|
when (replacement) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
indexVar = null
|
||||||
|
indexNum = replacement
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
indexVar = replacement
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
throw FatalAstException("invalid replace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw FatalAstException("invalid replace")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun forArray(v: ArrayLiteralValue): ArrayIndex {
|
fun forArray(v: ArrayLiteralValue): ArrayIndex {
|
||||||
return ArrayIndex(NumericLiteralValue.optimalNumeric(v.value.size, v.position), v.position)
|
val indexnum = NumericLiteralValue.optimalNumeric(v.value.size, v.position)
|
||||||
|
return ArrayIndex(indexnum, v.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstVisitor) = index.accept(visitor)
|
fun accept(visitor: IAstVisitor) {
|
||||||
fun accept(visitor: AstWalker, parent: Node) = index.accept(visitor, this)
|
origExpression?.accept(visitor)
|
||||||
|
indexNum?.accept(visitor)
|
||||||
override fun toString(): String {
|
indexVar?.accept(visitor)
|
||||||
return("ArrayIndex($index, pos=$position)")
|
}
|
||||||
|
fun accept(visitor: AstWalker, parent: Node) {
|
||||||
|
origExpression?.accept(visitor, this)
|
||||||
|
indexNum?.accept(visitor, this)
|
||||||
|
indexVar?.accept(visitor, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun constIndex() = (index as? NumericLiteralValue)?.number?.toInt()
|
override fun toString(): String {
|
||||||
|
return("ArrayIndex($indexNum, $indexVar, pos=$position)")
|
||||||
|
}
|
||||||
|
|
||||||
infix fun isSameAs(other: ArrayIndex) = index.isSameAs(other.index)
|
fun constIndex() = indexNum?.number?.toInt()
|
||||||
|
|
||||||
|
infix fun isSameAs(other: ArrayIndex) = indexNum==other.indexNum && indexVar == other.indexVar
|
||||||
}
|
}
|
||||||
|
|
||||||
open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() {
|
open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() {
|
||||||
@ -405,18 +444,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
|||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
companion object {
|
fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType { // TODO why does this have the extra 'stmt' scope parameter???
|
||||||
fun fromExpr(expr: Expression): AssignTarget {
|
|
||||||
return when (expr) {
|
|
||||||
is IdentifierReference -> AssignTarget(expr, null, null, expr.position)
|
|
||||||
is ArrayIndexedExpression -> AssignTarget(null, expr, null, expr.position)
|
|
||||||
is DirectMemoryRead -> AssignTarget(null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position)
|
|
||||||
else -> throw FatalAstException("invalid expression object $expr")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun inferType(program: Program, stmt: Statement): InferredTypes.InferredType {
|
|
||||||
if (identifier != null) {
|
if (identifier != null) {
|
||||||
val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown()
|
val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return InferredTypes.unknown()
|
||||||
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
|
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
|
||||||
@ -452,8 +480,8 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
|||||||
}
|
}
|
||||||
identifier != null -> value is IdentifierReference && value.nameInSource == identifier!!.nameInSource
|
identifier != null -> value is IdentifierReference && value.nameInSource == identifier!!.nameInSource
|
||||||
arrayindexed != null -> {
|
arrayindexed != null -> {
|
||||||
if(value is ArrayIndexedExpression && value.identifier.nameInSource == arrayindexed!!.identifier.nameInSource)
|
if(value is ArrayIndexedExpression && value.arrayvar.nameInSource == arrayindexed!!.arrayvar.nameInSource)
|
||||||
arrayindexed!!.arrayspec isSameAs value.arrayspec
|
arrayindexed!!.indexer isSameAs value.indexer
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -472,9 +500,9 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
|||||||
return addr1 != null && addr2 != null && addr1 == addr2
|
return addr1 != null && addr2 != null && addr1 == addr2
|
||||||
}
|
}
|
||||||
if (this.arrayindexed != null && other.arrayindexed != null) {
|
if (this.arrayindexed != null && other.arrayindexed != null) {
|
||||||
if (this.arrayindexed!!.identifier.nameInSource == other.arrayindexed!!.identifier.nameInSource) {
|
if (this.arrayindexed!!.arrayvar.nameInSource == other.arrayindexed!!.arrayvar.nameInSource) {
|
||||||
val x1 = this.arrayindexed!!.arrayspec.index.constValue(program)
|
val x1 = this.arrayindexed!!.indexer.constIndex()
|
||||||
val x2 = other.arrayindexed!!.arrayspec.index.constValue(program)
|
val x2 = other.arrayindexed!!.indexer.constIndex()
|
||||||
return x1 != null && x2 != null && x1 == x2
|
return x1 != null && x2 != null && x1 == x2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -499,7 +527,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.arrayindexed != null -> {
|
this.arrayindexed != null -> {
|
||||||
val targetStmt = this.arrayindexed!!.identifier.targetVarDecl(namespace)
|
val targetStmt = this.arrayindexed!!.arrayvar.targetVarDecl(namespace)
|
||||||
return if (targetStmt?.type == VarDeclType.MEMORY) {
|
return if (targetStmt?.type == VarDeclType.MEMORY) {
|
||||||
val addr = targetStmt.value as? NumericLiteralValue
|
val addr = targetStmt.value as? NumericLiteralValue
|
||||||
if (addr != null)
|
if (addr != null)
|
||||||
@ -609,6 +637,7 @@ class AnonymousScope(override var statements: MutableList<Statement>,
|
|||||||
override val position: Position) : INameScope, Statement() {
|
override val position: Position) : INameScope, Statement() {
|
||||||
override val name: String
|
override val name: String
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
override val asmGenInfo = AsmGenInfo()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var sequenceNumber = 1
|
private var sequenceNumber = 1
|
||||||
@ -662,6 +691,7 @@ class Subroutine(override val name: String,
|
|||||||
override val position: Position) : Statement(), INameScope {
|
override val position: Position) : Statement(), INameScope {
|
||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
override val asmGenInfo = AsmGenInfo()
|
||||||
val scopedname: String by lazy { makeScopedName(name) }
|
val scopedname: String by lazy { makeScopedName(name) }
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -894,11 +924,11 @@ class WhenStatement(var condition: Expression,
|
|||||||
if(choice.values==null)
|
if(choice.values==null)
|
||||||
result.add(null to choice)
|
result.add(null to choice)
|
||||||
else {
|
else {
|
||||||
val values = choice.values!!.map { it.constValue(program)?.number?.toInt() }
|
val values = choice.values!!.map {
|
||||||
if(values.contains(null))
|
val cv = it.constValue(program)
|
||||||
result.add(null to choice)
|
cv?.number?.toInt() ?: it.hashCode() // the hashcode is a nonsensical number but it avoids weird AST validation errors later
|
||||||
else
|
}
|
||||||
result.add(values.filterNotNull() to choice)
|
result.add(values to choice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@ -920,9 +950,15 @@ class WhenChoice(var values: List<Expression>?, // if null, this is t
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is AnonymousScope && node===statements)
|
val choiceValues = values
|
||||||
statements = replacement
|
if(replacement is AnonymousScope && node===statements) {
|
||||||
replacement.parent = this
|
statements = replacement
|
||||||
|
replacement.parent = this
|
||||||
|
} else if(choiceValues!=null && node in choiceValues) {
|
||||||
|
throw FatalAstException("cannot replace choice values")
|
||||||
|
} else {
|
||||||
|
throw FatalAstException("invalid replacement")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
@ -939,6 +975,7 @@ class StructDecl(override val name: String,
|
|||||||
override val position: Position): Statement(), INameScope {
|
override val position: Position): Statement(), INameScope {
|
||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
override val asmGenInfo = AsmGenInfo()
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
|
@ -19,11 +19,14 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||||
// a numeric vardecl without an initial value is initialized with zero,
|
// a numeric vardecl without an initial value is initialized with zero,
|
||||||
// unless there's already an assignment below, that initializes the value
|
// unless there's already an assignment below, that initializes the value
|
||||||
val nextAssign = decl.definingScope().nextSibling(decl) as? Assignment
|
if(decl.allowInitializeWithZero)
|
||||||
if(nextAssign!=null && nextAssign.target.isSameAs(IdentifierReference(listOf(decl.name), Position.DUMMY)))
|
{
|
||||||
decl.value = null
|
val nextAssign = decl.definingScope().nextSibling(decl) as? Assignment
|
||||||
else
|
if (nextAssign != null && nextAssign.target.isSameAs(IdentifierReference(listOf(decl.name), Position.DUMMY)))
|
||||||
decl.value = decl.zeroElementValue()
|
decl.value = null
|
||||||
|
else
|
||||||
|
decl.value = decl.zeroElementValue()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
@ -46,14 +49,14 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
// use the other part of the expression to split.
|
// use the other part of the expression to split.
|
||||||
val assignRight = Assignment(assignment.target, binExpr.right, assignment.position)
|
val assignRight = Assignment(assignment.target, binExpr.right, assignment.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(assignment, assignRight, parent),
|
IAstModification.InsertBefore(assignment, assignRight, assignment.definingScope()),
|
||||||
IAstModification.ReplaceNode(binExpr.right, binExpr.left, binExpr),
|
IAstModification.ReplaceNode(binExpr.right, binExpr.left, binExpr),
|
||||||
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
|
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val assignLeft = Assignment(assignment.target, binExpr.left, assignment.position)
|
val assignLeft = Assignment(assignment.target, binExpr.left, assignment.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(assignment, assignLeft, parent),
|
IAstModification.InsertBefore(assignment, assignLeft, assignment.definingScope()),
|
||||||
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
|
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,7 +127,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
&& outerStatements[subroutineStmtIdx - 1] !is Subroutine
|
&& outerStatements[subroutineStmtIdx - 1] !is Subroutine
|
||||||
&& outerStatements[subroutineStmtIdx - 1] !is Return
|
&& outerStatements[subroutineStmtIdx - 1] !is Return
|
||||||
&& outerScope !is Block) {
|
&& outerScope !is Block) {
|
||||||
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope as Node)
|
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope)
|
||||||
}
|
}
|
||||||
return mods
|
return mods
|
||||||
}
|
}
|
||||||
@ -161,11 +164,13 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
|
|
||||||
if(sourceDt in PassByReferenceDatatypes) {
|
if(sourceDt in PassByReferenceDatatypes) {
|
||||||
if(typecast.type==DataType.UWORD) {
|
if(typecast.type==DataType.UWORD) {
|
||||||
return listOf(IAstModification.ReplaceNode(
|
if(typecast.expression is IdentifierReference) {
|
||||||
typecast,
|
return listOf(IAstModification.ReplaceNode(
|
||||||
AddressOf(typecast.expression as IdentifierReference, typecast.position),
|
typecast,
|
||||||
parent
|
AddressOf(typecast.expression as IdentifierReference, typecast.position),
|
||||||
))
|
parent
|
||||||
|
))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
errors.err("cannot cast pass-by-reference value to type ${typecast.type} (only to UWORD)", typecast.position)
|
errors.err("cannot cast pass-by-reference value to type ${typecast.type} (only to UWORD)", typecast.position)
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,9 @@ data class CompilationOptions(val output: OutputType,
|
|||||||
val zeropage: ZeropageType,
|
val zeropage: ZeropageType,
|
||||||
val zpReserved: List<IntRange>,
|
val zpReserved: List<IntRange>,
|
||||||
val floats: Boolean,
|
val floats: Boolean,
|
||||||
val noSysInit: Boolean)
|
val noSysInit: Boolean) {
|
||||||
|
var slowCodegenWarnings = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class CompilerException(message: String?) : Exception(message)
|
class CompilerException(message: String?) : Exception(message)
|
||||||
|
@ -29,6 +29,7 @@ class CompilationResult(val success: Boolean,
|
|||||||
fun compileProgram(filepath: Path,
|
fun compileProgram(filepath: Path,
|
||||||
optimize: Boolean,
|
optimize: Boolean,
|
||||||
writeAssembly: Boolean,
|
writeAssembly: Boolean,
|
||||||
|
slowCodegenWarnings: Boolean,
|
||||||
compilationTarget: String,
|
compilationTarget: String,
|
||||||
outputDir: Path): CompilationResult {
|
outputDir: Path): CompilationResult {
|
||||||
var programName = ""
|
var programName = ""
|
||||||
@ -49,6 +50,7 @@ fun compileProgram(filepath: Path,
|
|||||||
val totalTime = measureTimeMillis {
|
val totalTime = measureTimeMillis {
|
||||||
// import main module and everything it needs
|
// import main module and everything it needs
|
||||||
val (ast, compilationOptions, imported) = parseImports(filepath, errors)
|
val (ast, compilationOptions, imported) = parseImports(filepath, errors)
|
||||||
|
compilationOptions.slowCodegenWarnings = slowCodegenWarnings
|
||||||
programAst = ast
|
programAst = ast
|
||||||
importedFiles = imported
|
importedFiles = imported
|
||||||
processAst(programAst, errors, compilationOptions)
|
processAst(programAst, errors, compilationOptions)
|
||||||
@ -107,9 +109,9 @@ private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program,
|
|||||||
// depending on the machine and compiler options we may have to include some libraries
|
// depending on the machine and compiler options we may have to include some libraries
|
||||||
CompilationTarget.instance.machine.importLibs(compilerOptions, importer, programAst)
|
CompilationTarget.instance.machine.importLibs(compilerOptions, importer, programAst)
|
||||||
|
|
||||||
// always import prog8lib and math
|
// always import prog8_lib and math
|
||||||
importer.importLibraryModule(programAst, "math")
|
importer.importLibraryModule(programAst, "math")
|
||||||
importer.importLibraryModule(programAst, "prog8lib")
|
importer.importLibraryModule(programAst, "prog8_lib")
|
||||||
errors.handle()
|
errors.handle()
|
||||||
return Triple(programAst, compilerOptions, importedFiles)
|
return Triple(programAst, compilerOptions, importedFiles)
|
||||||
}
|
}
|
||||||
@ -120,8 +122,6 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
|
|||||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||||
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
||||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||||
mainModule.loadAddress = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%address" }
|
|
||||||
as? Directive)?.args?.single()?.int ?: 0
|
|
||||||
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
||||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||||
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
|
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
|
||||||
@ -173,7 +173,8 @@ private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptio
|
|||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.constantFold(errors)
|
programAst.constantFold(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.reorderStatements()
|
programAst.reorderStatements(errors)
|
||||||
|
errors.handle()
|
||||||
programAst.addTypecasts(errors)
|
programAst.addTypecasts(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.variousCleanups()
|
programAst.variousCleanups()
|
||||||
@ -189,10 +190,11 @@ private fun optimizeAst(programAst: Program, errors: ErrorReporter) {
|
|||||||
while (true) {
|
while (true) {
|
||||||
// keep optimizing expressions and statements until no more steps remain
|
// keep optimizing expressions and statements until no more steps remain
|
||||||
val optsDone1 = programAst.simplifyExpressions()
|
val optsDone1 = programAst.simplifyExpressions()
|
||||||
val optsDone2 = programAst.optimizeStatements(errors)
|
val optsDone2 = programAst.splitBinaryExpressions()
|
||||||
|
val optsDone3 = programAst.optimizeStatements(errors)
|
||||||
programAst.constantFold(errors) // because simplified statements and expressions can result in more constants that can be folded away
|
programAst.constantFold(errors) // because simplified statements and expressions can result in more constants that can be folded away
|
||||||
errors.handle()
|
errors.handle()
|
||||||
if (optsDone1 + optsDone2 == 0)
|
if (optsDone1 + optsDone2 + optsDone3 == 0)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,9 +210,11 @@ private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerO
|
|||||||
programAst.variousCleanups()
|
programAst.variousCleanups()
|
||||||
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
|
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.checkRecursion(errors) // check if there are recursive subroutine calls
|
val callGraph = CallGraph(programAst)
|
||||||
|
callGraph.checkRecursiveCalls(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.verifyFunctionArgTypes()
|
programAst.verifyFunctionArgTypes()
|
||||||
|
programAst.moveMainAndStartToFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
||||||
|
@ -126,8 +126,10 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
||||||
} else {
|
} else {
|
||||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
free.addAll(listOf(0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
free.addAll(listOf(
|
||||||
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
|
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
||||||
|
0x16, 0x17, 0x18, 0x19, 0x1a,
|
||||||
|
0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
|
||||||
0x22, 0x23, 0x24, 0x25,
|
0x22, 0x23, 0x24, 0x25,
|
||||||
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
||||||
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53,
|
||||||
@ -148,16 +150,16 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
|
||||||
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
||||||
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72,
|
||||||
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xf
|
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
if(options.zeropage!=ZeropageType.DONTUSE) {
|
if(options.zeropage!=ZeropageType.DONTUSE) {
|
||||||
// add the other free Zp addresses,
|
// add the free Zp addresses
|
||||||
// these are valid for the C-64 (when no RS232 I/O is performed) but to keep BASIC running fully:
|
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
|
||||||
free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e,
|
free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e,
|
||||||
0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa,
|
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa5, 0xa6,
|
||||||
0xb5, 0xb6, 0xf7, 0xf8, 0xf9))
|
0xb0, 0xb1, 0xbe, 0xbf, 0xf9))
|
||||||
} else {
|
} else {
|
||||||
// don't use the zeropage at all
|
// don't use the zeropage at all
|
||||||
free.clear()
|
free.clear()
|
||||||
|
@ -1054,11 +1054,16 @@ object Petscii {
|
|||||||
val lookup = if(lowercase) encodingPetsciiLowercase else encodingPetsciiUppercase
|
val lookup = if(lowercase) encodingPetsciiLowercase else encodingPetsciiUppercase
|
||||||
return text.map {
|
return text.map {
|
||||||
val petscii = lookup[it]
|
val petscii = lookup[it]
|
||||||
petscii?.toShort() ?: if(it=='\u0000')
|
petscii?.toShort() ?: when (it) {
|
||||||
0.toShort()
|
'\u0000' -> 0.toShort()
|
||||||
else {
|
in '\u8000'..'\u80ff' -> {
|
||||||
val case = if (lowercase) "lower" else "upper"
|
// special case: take the lower 8 bit hex value directly
|
||||||
throw CharConversionException("no ${case}case Petscii character for '$it' (${it.toShort()})")
|
(it.toInt() - 0x8000).toShort()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val case = if (lowercase) "lower" else "upper"
|
||||||
|
throw CharConversionException("no ${case}case Petscii character for '$it' (${it.toShort()})")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1072,11 +1077,16 @@ object Petscii {
|
|||||||
val lookup = if(lowercase) encodingScreencodeLowercase else encodingScreencodeUppercase
|
val lookup = if(lowercase) encodingScreencodeLowercase else encodingScreencodeUppercase
|
||||||
return text.map{
|
return text.map{
|
||||||
val screencode = lookup[it]
|
val screencode = lookup[it]
|
||||||
screencode?.toShort() ?: if(it=='\u0000')
|
screencode?.toShort() ?: when (it) {
|
||||||
0.toShort()
|
'\u0000' -> 0.toShort()
|
||||||
else {
|
in '\u8000'..'\u80ff' -> {
|
||||||
val case = if (lowercase) "lower" else "upper"
|
// special case: take the lower 8 bit hex value directly
|
||||||
throw CharConversionException("no ${case}Screencode character for '$it' (${it.toShort()})")
|
(it.toInt() - 0x8000).toShort()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val case = if (lowercase) "lower" else "upper"
|
||||||
|
throw CharConversionException("no ${case}Screencode character for '$it' (${it.toShort()})")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,16 +66,21 @@ internal class AsmGen(private val program: Program,
|
|||||||
block2asm(b)
|
block2asm(b)
|
||||||
footer()
|
footer()
|
||||||
|
|
||||||
|
val outputFile = outputDir.resolve("${program.name}.asm").toFile()
|
||||||
|
outputFile.printWriter().use {
|
||||||
|
for (line in assemblyLines) { it.println(line) }
|
||||||
|
}
|
||||||
|
|
||||||
if(optimize) {
|
if(optimize) {
|
||||||
|
assemblyLines.clear()
|
||||||
|
assemblyLines.addAll(outputFile.readLines())
|
||||||
var optimizationsDone = 1
|
var optimizationsDone = 1
|
||||||
while (optimizationsDone > 0) {
|
while (optimizationsDone > 0) {
|
||||||
optimizationsDone = optimizeAssembly(assemblyLines)
|
optimizationsDone = optimizeAssembly(assemblyLines)
|
||||||
}
|
}
|
||||||
}
|
outputFile.printWriter().use {
|
||||||
|
for (line in assemblyLines) { it.println(line) }
|
||||||
val outputFile = outputDir.resolve("${program.name}.asm").toFile()
|
}
|
||||||
outputFile.printWriter().use {
|
|
||||||
for (line in assemblyLines) { it.println(line) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return AssemblyProgram(program.name, outputDir)
|
return AssemblyProgram(program.name, outputDir)
|
||||||
@ -194,11 +199,10 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun assignInitialValueToVar(decl: VarDecl, variableName: List<String>) {
|
private fun assignInitialValueToVar(decl: VarDecl, variableName: List<String>) {
|
||||||
val variable = IdentifierReference(variableName, decl.position)
|
val asmName = asmVariableName(variableName)
|
||||||
variable.linkParents(decl.parent)
|
|
||||||
val asgn = AsmAssignment(
|
val asgn = AsmAssignment(
|
||||||
AsmAssignSource.fromAstSource(decl.value!!, program),
|
AsmAssignSource.fromAstSource(decl.value!!, program, this),
|
||||||
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, decl.datatype, variable = variable),
|
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, decl.datatype, decl.definingSubroutine(), variableAsmName = asmName),
|
||||||
false, decl.position)
|
false, decl.position)
|
||||||
assignmentAsmGen.translateNormalAssignment(asgn)
|
assignmentAsmGen.translateNormalAssignment(asgn)
|
||||||
}
|
}
|
||||||
@ -496,8 +500,14 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun asmSymbolName(name: String) = fixNameSymbols(name)
|
||||||
|
internal fun asmVariableName(name: String) = fixNameSymbols(name)
|
||||||
|
internal fun asmSymbolName(name: Iterable<String>) = fixNameSymbols(name.joinToString("."))
|
||||||
|
internal fun asmVariableName(name: Iterable<String>) = fixNameSymbols(name.joinToString("."))
|
||||||
|
|
||||||
|
|
||||||
internal fun loadByteFromPointerIntoA(pointervar: IdentifierReference): Pair<Boolean, String> {
|
internal fun loadByteFromPointerIntoA(pointervar: IdentifierReference): Pair<Boolean, String> {
|
||||||
// returns if the pointer is already on the ZP itself or not (in which case SCRATCH_W1 is used as intermediary)
|
// returns if the pointer is already on the ZP itself or not (in the latter case SCRATCH_W1 is used as intermediary)
|
||||||
val sourceName = asmVariableName(pointervar)
|
val sourceName = asmVariableName(pointervar)
|
||||||
val vardecl = pointervar.targetVarDecl(program.namespace)!!
|
val vardecl = pointervar.targetVarDecl(program.namespace)!!
|
||||||
val scopedName = vardecl.makeScopedName(vardecl.name)
|
val scopedName = vardecl.makeScopedName(vardecl.name)
|
||||||
@ -538,35 +548,70 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
|
private fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
|
||||||
|
|
||||||
|
internal fun saveRegister(register: CpuRegister, dontUseStack: Boolean, scope: Subroutine?) {
|
||||||
private val saveRegisterLabels = Stack<String>();
|
if(dontUseStack) {
|
||||||
|
when (register) {
|
||||||
internal fun saveRegister(register: CpuRegister) {
|
CpuRegister.A -> {
|
||||||
when(register) {
|
out(" sta _prog8_regsaveA")
|
||||||
CpuRegister.A -> out(" pha")
|
if (scope != null)
|
||||||
CpuRegister.X -> {
|
scope.asmGenInfo.usedRegsaveA = true
|
||||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phx")
|
}
|
||||||
else out(" stx _prog8_regsave${register.name}")
|
CpuRegister.X -> {
|
||||||
|
out(" stx _prog8_regsaveX")
|
||||||
|
if (scope != null)
|
||||||
|
scope.asmGenInfo.usedRegsaveX = true
|
||||||
|
}
|
||||||
|
CpuRegister.Y -> {
|
||||||
|
out(" sty _prog8_regsaveY")
|
||||||
|
if (scope != null)
|
||||||
|
scope.asmGenInfo.usedRegsaveY = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
CpuRegister.Y -> {
|
|
||||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phy")
|
} else {
|
||||||
else out(" sty _prog8_regsave${register.name}")
|
when (register) {
|
||||||
|
CpuRegister.A -> out(" pha")
|
||||||
|
CpuRegister.X -> {
|
||||||
|
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phx")
|
||||||
|
else {
|
||||||
|
out(" stx _prog8_regsaveX")
|
||||||
|
if (scope != null)
|
||||||
|
scope.asmGenInfo.usedRegsaveX = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CpuRegister.Y -> {
|
||||||
|
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phy")
|
||||||
|
else {
|
||||||
|
out(" sty _prog8_regsaveY")
|
||||||
|
if (scope != null)
|
||||||
|
scope.asmGenInfo.usedRegsaveY = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun restoreRegister(register: CpuRegister) {
|
internal fun restoreRegister(register: CpuRegister, dontUseStack: Boolean) {
|
||||||
when(register) {
|
if(dontUseStack) {
|
||||||
CpuRegister.A -> out(" pla")
|
when (register) {
|
||||||
CpuRegister.X -> {
|
CpuRegister.A -> out(" sta _prog8_regsaveA")
|
||||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" plx")
|
CpuRegister.X -> out(" ldx _prog8_regsaveX")
|
||||||
else out(" ldx _prog8_regsave${register.name}")
|
CpuRegister.Y -> out(" ldy _prog8_regsaveY")
|
||||||
}
|
}
|
||||||
CpuRegister.Y -> {
|
|
||||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" ply")
|
} else {
|
||||||
else out(" ldy _prog8_regsave${register.name}")
|
when (register) {
|
||||||
|
CpuRegister.A -> out(" pla")
|
||||||
|
CpuRegister.X -> {
|
||||||
|
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" plx")
|
||||||
|
else out(" ldx _prog8_regsaveX")
|
||||||
|
}
|
||||||
|
CpuRegister.Y -> {
|
||||||
|
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" ply")
|
||||||
|
else out(" ldy _prog8_regsaveY")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -588,9 +633,10 @@ internal class AsmGen(private val program: Program,
|
|||||||
if (builtinFunc != null) {
|
if (builtinFunc != null) {
|
||||||
builtinFunctionsAsmGen.translateFunctioncallStatement(stmt, builtinFunc)
|
builtinFunctionsAsmGen.translateFunctioncallStatement(stmt, builtinFunc)
|
||||||
} else {
|
} else {
|
||||||
functioncallAsmGen.translateFunctionCall(stmt)
|
|
||||||
// discard any results from the stack:
|
|
||||||
val sub = stmt.target.targetSubroutine(program.namespace)!!
|
val sub = stmt.target.targetSubroutine(program.namespace)!!
|
||||||
|
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any {it.statusflag!=null}
|
||||||
|
functioncallAsmGen.translateFunctionCall(stmt, preserveStatusRegisterAfterCall)
|
||||||
|
// discard any results from the stack:
|
||||||
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
||||||
for ((t, reg) in returns) {
|
for ((t, reg) in returns) {
|
||||||
if (reg.stack) {
|
if (reg.stack) {
|
||||||
@ -598,6 +644,8 @@ internal class AsmGen(private val program: Program,
|
|||||||
else if (t == DataType.FLOAT) out(" inx | inx | inx")
|
else if (t == DataType.FLOAT) out(" inx | inx | inx")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(preserveStatusRegisterAfterCall)
|
||||||
|
out(" plp\t; restore status flags from call")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Assignment -> assignmentAsmGen.translate(stmt)
|
is Assignment -> assignmentAsmGen.translate(stmt)
|
||||||
@ -628,131 +676,75 @@ internal class AsmGen(private val program: Program,
|
|||||||
register: CpuRegister,
|
register: CpuRegister,
|
||||||
addOneExtra: Boolean=false) {
|
addOneExtra: Boolean=false) {
|
||||||
val reg = register.toString().toLowerCase()
|
val reg = register.toString().toLowerCase()
|
||||||
val index = expr.arrayspec.index
|
val indexnum = expr.indexer.constIndex()
|
||||||
if(index is NumericLiteralValue) {
|
if(indexnum!=null) {
|
||||||
val indexValue = index.number.toInt() * elementDt.memorySize() + if(addOneExtra) 1 else 0
|
val indexValue = indexnum * elementDt.memorySize() + if(addOneExtra) 1 else 0
|
||||||
out(" ld$reg #$indexValue")
|
out(" ld$reg #$indexValue")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val indexName = asmVariableName(expr.indexer.indexVar!!)
|
||||||
if(addOneExtra) {
|
if(addOneExtra) {
|
||||||
// add 1 to the result
|
// add 1 to the result
|
||||||
if (index is IdentifierReference) {
|
when(elementDt) {
|
||||||
val indexName = asmVariableName(index)
|
in ByteDatatypes -> {
|
||||||
when(elementDt) {
|
out(" ldy $indexName | iny")
|
||||||
in ByteDatatypes -> {
|
when(register) {
|
||||||
out(" ldy $indexName | iny")
|
CpuRegister.A -> out(" tya")
|
||||||
when(register) {
|
CpuRegister.X -> out(" tyx")
|
||||||
CpuRegister.A -> out(" tya")
|
CpuRegister.Y -> {}
|
||||||
CpuRegister.X -> out(" tyx")
|
|
||||||
CpuRegister.Y -> {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
|
||||||
out(" lda $indexName | sec | rol a")
|
|
||||||
when(register) {
|
|
||||||
CpuRegister.A -> {}
|
|
||||||
CpuRegister.X -> out(" tax")
|
|
||||||
CpuRegister.Y -> out(" tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
require(DataType.FLOAT.memorySize()==5)
|
|
||||||
out("""
|
|
||||||
lda $indexName
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
sec
|
|
||||||
adc $indexName""")
|
|
||||||
when(register) {
|
|
||||||
CpuRegister.A -> {}
|
|
||||||
CpuRegister.X -> out(" tax")
|
|
||||||
CpuRegister.Y -> out(" tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird dt")
|
|
||||||
}
|
}
|
||||||
}
|
in WordDatatypes -> {
|
||||||
else {
|
out(" lda $indexName | sec | rol a")
|
||||||
expressionsAsmGen.translateExpression(index)
|
when(register) {
|
||||||
out("""
|
CpuRegister.A -> {}
|
||||||
inc P8ESTACK_LO,x
|
CpuRegister.X -> out(" tax")
|
||||||
bne +
|
CpuRegister.Y -> out(" tay")
|
||||||
inc P8ESTACK_HI,x
|
}
|
||||||
+""")
|
|
||||||
when(register) {
|
|
||||||
CpuRegister.A -> out(" inx | lda P8ESTACK_LO,x")
|
|
||||||
CpuRegister.X -> out(" inx | lda P8ESTACK_LO,x | tax")
|
|
||||||
CpuRegister.Y -> out(" inx | ldy P8ESTACK_LO,x")
|
|
||||||
}
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
require(DataType.FLOAT.memorySize()==5)
|
||||||
|
out("""
|
||||||
|
lda $indexName
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
sec
|
||||||
|
adc $indexName""")
|
||||||
|
when(register) {
|
||||||
|
CpuRegister.A -> {}
|
||||||
|
CpuRegister.X -> out(" tax")
|
||||||
|
CpuRegister.Y -> out(" tay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (index is IdentifierReference) {
|
when(elementDt) {
|
||||||
val indexName = asmVariableName(index)
|
in ByteDatatypes -> out(" ld$reg $indexName")
|
||||||
when(elementDt) {
|
in WordDatatypes -> {
|
||||||
in ByteDatatypes -> out(" ld$reg $indexName")
|
out(" lda $indexName | asl a")
|
||||||
in WordDatatypes -> {
|
when(register) {
|
||||||
out(" lda $indexName | asl a")
|
CpuRegister.A -> {}
|
||||||
when(register) {
|
CpuRegister.X -> out(" tax")
|
||||||
CpuRegister.A -> {}
|
CpuRegister.Y -> out(" tay")
|
||||||
CpuRegister.X -> out(" tax")
|
|
||||||
CpuRegister.Y -> out(" tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
require(DataType.FLOAT.memorySize()==5)
|
|
||||||
out("""
|
|
||||||
lda $indexName
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc $indexName""")
|
|
||||||
when(register) {
|
|
||||||
CpuRegister.A -> {}
|
|
||||||
CpuRegister.X -> out(" tax")
|
|
||||||
CpuRegister.Y -> out(" tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird dt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
expressionsAsmGen.translateExpression(index)
|
|
||||||
when(elementDt) {
|
|
||||||
in ByteDatatypes -> {
|
|
||||||
when (register) {
|
|
||||||
CpuRegister.A -> out(" inx | lda P8ESTACK_LO,x")
|
|
||||||
CpuRegister.X -> out(" inx | lda P8ESTACK_LO,x | tax")
|
|
||||||
CpuRegister.Y -> out(" inx | ldy P8ESTACK_LO,x")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in WordDatatypes -> {
|
|
||||||
out("""
|
|
||||||
inx
|
|
||||||
lda P8ESTACK_LO,x
|
|
||||||
asl a""")
|
|
||||||
when (register) {
|
|
||||||
CpuRegister.A -> {}
|
|
||||||
CpuRegister.X -> out(" tax")
|
|
||||||
CpuRegister.Y -> out(" tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
require(DataType.FLOAT.memorySize()==5)
|
|
||||||
out("""
|
|
||||||
inx
|
|
||||||
lda P8ESTACK_LO,x
|
|
||||||
asl a
|
|
||||||
asl a
|
|
||||||
clc
|
|
||||||
adc P8ESTACK_LO,x""")
|
|
||||||
when (register) {
|
|
||||||
CpuRegister.A -> {}
|
|
||||||
CpuRegister.X -> out(" tax")
|
|
||||||
CpuRegister.Y -> out(" tay")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
require(DataType.FLOAT.memorySize()==5)
|
||||||
|
out("""
|
||||||
|
lda $indexName
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc $indexName""")
|
||||||
|
when(register) {
|
||||||
|
CpuRegister.A -> {}
|
||||||
|
CpuRegister.X -> out(" tax")
|
||||||
|
CpuRegister.Y -> out(" tay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -760,11 +752,14 @@ internal class AsmGen(private val program: Program,
|
|||||||
internal fun translateExpression(expression: Expression) =
|
internal fun translateExpression(expression: Expression) =
|
||||||
expressionsAsmGen.translateExpression(expression)
|
expressionsAsmGen.translateExpression(expression)
|
||||||
|
|
||||||
|
internal fun translateExpression(indexer: ArrayIndex) =
|
||||||
|
expressionsAsmGen.translateExpression(indexer)
|
||||||
|
|
||||||
internal fun translateFunctioncallExpression(functionCall: FunctionCall, signature: FSignature) =
|
internal fun translateFunctioncallExpression(functionCall: FunctionCall, signature: FSignature) =
|
||||||
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature)
|
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature)
|
||||||
|
|
||||||
internal fun translateFunctionCall(functionCall: FunctionCall) =
|
internal fun translateFunctionCall(functionCall: FunctionCall, preserveStatusRegisterAfterCall: Boolean) =
|
||||||
functioncallAsmGen.translateFunctionCall(functionCall)
|
functioncallAsmGen.translateFunctionCall(functionCall, preserveStatusRegisterAfterCall)
|
||||||
|
|
||||||
internal fun translateNormalAssignment(assign: AsmAssignment) =
|
internal fun translateNormalAssignment(assign: AsmAssignment) =
|
||||||
assignmentAsmGen.translateNormalAssignment(assign)
|
assignmentAsmGen.translateNormalAssignment(assign)
|
||||||
@ -806,10 +801,13 @@ internal class AsmGen(private val program: Program,
|
|||||||
out("; statements")
|
out("; statements")
|
||||||
sub.statements.forEach{ translate(it) }
|
sub.statements.forEach{ translate(it) }
|
||||||
out("; variables")
|
out("; variables")
|
||||||
out("""
|
out("; register saves")
|
||||||
; register saves
|
if(sub.asmGenInfo.usedRegsaveA)
|
||||||
_prog8_regsaveX .byte 0
|
out("_prog8_regsaveA .byte 0")
|
||||||
_prog8_regsaveY .byte 0""") // TODO only generate these bytes if they're actually used by saveRegister()
|
if(sub.asmGenInfo.usedRegsaveX)
|
||||||
|
out("_prog8_regsaveX .byte 0")
|
||||||
|
if(sub.asmGenInfo.usedRegsaveY)
|
||||||
|
out("_prog8_regsaveY .byte 0")
|
||||||
vardecls2asm(sub.statements)
|
vardecls2asm(sub.statements)
|
||||||
out(" .pend\n")
|
out(" .pend\n")
|
||||||
}
|
}
|
||||||
@ -934,6 +932,8 @@ _prog8_regsaveY .byte 0""") // TODO only generate these bytes if the
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun repeatWordCountInAY(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
private fun repeatWordCountInAY(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
||||||
|
if(constIterations==0)
|
||||||
|
return
|
||||||
// note: A/Y must have been loaded with the number of iterations already!
|
// note: A/Y must have been loaded with the number of iterations already!
|
||||||
val counterVar = makeLabel("repeatcounter")
|
val counterVar = makeLabel("repeatcounter")
|
||||||
out("""
|
out("""
|
||||||
@ -963,8 +963,12 @@ $counterVar .word 0""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun repeatByteCountInA(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
private fun repeatByteCountInA(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
||||||
|
if(constIterations==0)
|
||||||
|
return
|
||||||
// note: A must have been loaded with the number of iterations already!
|
// note: A must have been loaded with the number of iterations already!
|
||||||
val counterVar = makeLabel("repeatcounter")
|
val counterVar = makeLabel("repeatcounter")
|
||||||
|
if(constIterations==null)
|
||||||
|
out(" beq $endLabel")
|
||||||
out("""
|
out("""
|
||||||
sta $counterVar
|
sta $counterVar
|
||||||
$repeatLabel""")
|
$repeatLabel""")
|
||||||
@ -1169,4 +1173,40 @@ $counterVar .byte 0""")
|
|||||||
val assembly = asm.assembly.trimEnd().trimStart('\n')
|
val assembly = asm.assembly.trimEnd().trimStart('\n')
|
||||||
assemblyLines.add(assembly)
|
assemblyLines.add(assembly)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun signExtendStackLsb(valueDt: DataType) {
|
||||||
|
// sign extend signed byte on stack to signed word
|
||||||
|
when(valueDt) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
out("""
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
ora #$7f
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
+ sta P8ESTACK_HI+1,x""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("need byte type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun signExtendVariableLsb(asmvar: String, valueDt: DataType) {
|
||||||
|
// sign extend signed byte in a word variable
|
||||||
|
when(valueDt) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
out(" lda #0 | sta $asmvar+1")
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
out("""
|
||||||
|
lda $asmvar+1
|
||||||
|
ora #$7f
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
+ sta $asmvar+1""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("need byte type")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,8 +196,8 @@ private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>)
|
|||||||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
||||||
(first.startsWith("stx ") && second.startsWith("ldx "))
|
(first.startsWith("stx ") && second.startsWith("ldx "))
|
||||||
) {
|
) {
|
||||||
val firstLoc = first.substring(4)
|
val firstLoc = first.substring(4).trimStart()
|
||||||
val secondLoc = second.substring(4)
|
val secondLoc = second.substring(4).trimStart()
|
||||||
if (firstLoc == secondLoc) {
|
if (firstLoc == secondLoc) {
|
||||||
mods.add(Modification(pair[1].index, true, null))
|
mods.add(Modification(pair[1].index, true, null))
|
||||||
}
|
}
|
||||||
|
@ -159,8 +159,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
asmgen.translateExpression(what.identifier)
|
asmgen.translateExpression(what.arrayvar)
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
asmgen.translateExpression(what.indexer)
|
||||||
asmgen.out(" jsr prog8_lib.ror2_array_ub")
|
asmgen.out(" jsr prog8_lib.ror2_array_ub")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
@ -182,8 +182,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
asmgen.translateExpression(what.identifier)
|
asmgen.translateExpression(what.arrayvar)
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
asmgen.translateExpression(what.indexer)
|
||||||
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
@ -204,8 +204,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
asmgen.translateExpression(what.identifier)
|
asmgen.translateExpression(what.arrayvar)
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
asmgen.translateExpression(what.indexer)
|
||||||
asmgen.out(" jsr prog8_lib.ror_array_ub")
|
asmgen.out(" jsr prog8_lib.ror_array_ub")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
@ -234,8 +234,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
asmgen.translateExpression(what.identifier)
|
asmgen.translateExpression(what.arrayvar)
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
asmgen.translateExpression(what.indexer)
|
||||||
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
@ -256,8 +256,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
asmgen.translateExpression(what.identifier)
|
asmgen.translateExpression(what.arrayvar)
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
asmgen.translateExpression(what.indexer)
|
||||||
asmgen.out(" jsr prog8_lib.rol2_array_ub")
|
asmgen.out(" jsr prog8_lib.rol2_array_ub")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
@ -279,8 +279,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
asmgen.translateExpression(what.identifier)
|
asmgen.translateExpression(what.arrayvar)
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
asmgen.translateExpression(what.indexer)
|
||||||
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
@ -301,8 +301,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
asmgen.translateExpression(what.identifier)
|
asmgen.translateExpression(what.arrayvar)
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
asmgen.translateExpression(what.indexer)
|
||||||
asmgen.out(" jsr prog8_lib.rol_array_ub")
|
asmgen.out(" jsr prog8_lib.rol_array_ub")
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
@ -331,8 +331,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
asmgen.translateExpression(what.identifier)
|
asmgen.translateExpression(what.arrayvar)
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
asmgen.translateExpression(what.indexer)
|
||||||
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
@ -390,12 +390,22 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
private fun funcStrlen(fcall: IFunctionCall) {
|
private fun funcStrlen(fcall: IFunctionCall) {
|
||||||
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
|
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
|
||||||
asmgen.out("""
|
val type = fcall.args[0].inferType(program)
|
||||||
lda #<$name
|
when {
|
||||||
ldy #>$name
|
type.istype(DataType.STR) -> asmgen.out("""
|
||||||
jsr prog8_lib.strlen
|
lda #<$name
|
||||||
sta P8ESTACK_LO,x
|
ldy #>$name
|
||||||
dex""")
|
jsr prog8_lib.strlen
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
type.istype(DataType.UWORD) -> asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
ldy $name+1
|
||||||
|
jsr prog8_lib.strlen
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
else -> throw AssemblyError("strlen requires str or uword arg")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSwap(fcall: IFunctionCall) {
|
private fun funcSwap(fcall: IFunctionCall) {
|
||||||
@ -468,19 +478,26 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(first is ArrayIndexedExpression && second is ArrayIndexedExpression) {
|
if(first is ArrayIndexedExpression && second is ArrayIndexedExpression) {
|
||||||
val indexValue1 = first.arrayspec.index as? NumericLiteralValue
|
val arrayVarName1 = asmgen.asmVariableName(first.arrayvar)
|
||||||
val indexName1 = first.arrayspec.index as? IdentifierReference
|
val arrayVarName2 = asmgen.asmVariableName(second.arrayvar)
|
||||||
val indexValue2 = second.arrayspec.index as? NumericLiteralValue
|
|
||||||
val indexName2 = second.arrayspec.index as? IdentifierReference
|
|
||||||
val arrayVarName1 = asmgen.asmVariableName(first.identifier)
|
|
||||||
val arrayVarName2 = asmgen.asmVariableName(second.identifier)
|
|
||||||
val elementDt = first.inferType(program).typeOrElse(DataType.STRUCT)
|
val elementDt = first.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
|
||||||
if(indexValue1!=null && indexValue2!=null) {
|
val firstNum = first.indexer.indexNum
|
||||||
swapArrayValues(elementDt, arrayVarName1, indexValue1, arrayVarName2, indexValue2)
|
val firstVar = first.indexer.indexVar
|
||||||
|
val secondNum = second.indexer.indexNum
|
||||||
|
val secondVar = second.indexer.indexVar
|
||||||
|
|
||||||
|
if(firstNum!=null && secondNum!=null) {
|
||||||
|
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondNum)
|
||||||
return
|
return
|
||||||
} else if(indexName1!=null && indexName2!=null) {
|
} else if(firstVar!=null && secondVar!=null) {
|
||||||
swapArrayValues(elementDt, arrayVarName1, indexName1, arrayVarName2, indexName2)
|
swapArrayValues(elementDt, arrayVarName1, firstVar, arrayVarName2, secondVar)
|
||||||
|
return
|
||||||
|
} else if(firstNum!=null && secondVar!=null) {
|
||||||
|
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondVar)
|
||||||
|
return
|
||||||
|
} else if(firstVar!=null && secondNum!=null) {
|
||||||
|
swapArrayValues(elementDt, arrayVarName1, firstVar, arrayVarName2, secondNum)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -488,9 +505,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
// all other types of swap() calls are done via the evaluation stack
|
// all other types of swap() calls are done via the evaluation stack
|
||||||
fun targetFromExpr(expr: Expression, datatype: DataType): AsmAssignTarget {
|
fun targetFromExpr(expr: Expression, datatype: DataType): AsmAssignTarget {
|
||||||
return when (expr) {
|
return when (expr) {
|
||||||
is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, variable=expr)
|
is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, expr.definingSubroutine(), variableAsmName = asmgen.asmVariableName(expr))
|
||||||
is ArrayIndexedExpression -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, datatype, array = expr)
|
is ArrayIndexedExpression -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, datatype, expr.definingSubroutine(), array = expr)
|
||||||
is DirectMemoryRead -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, datatype, memory = DirectMemoryWrite(expr.addressExpression, expr.position))
|
is DirectMemoryRead -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, datatype, expr.definingSubroutine(), memory = DirectMemoryWrite(expr.addressExpression, expr.position))
|
||||||
else -> throw AssemblyError("invalid expression object $expr")
|
else -> throw AssemblyError("invalid expression object $expr")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -499,12 +516,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.translateExpression(second)
|
asmgen.translateExpression(second)
|
||||||
val datatype = first.inferType(program).typeOrElse(DataType.STRUCT)
|
val datatype = first.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
val assignFirst = AsmAssignment(
|
val assignFirst = AsmAssignment(
|
||||||
AsmAssignSource(SourceStorageKind.STACK, program, datatype),
|
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, datatype),
|
||||||
targetFromExpr(first, datatype),
|
targetFromExpr(first, datatype),
|
||||||
false, first.position
|
false, first.position
|
||||||
)
|
)
|
||||||
val assignSecond = AsmAssignment(
|
val assignSecond = AsmAssignment(
|
||||||
AsmAssignSource(SourceStorageKind.STACK, program, datatype),
|
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, datatype),
|
||||||
targetFromExpr(second, datatype),
|
targetFromExpr(second, datatype),
|
||||||
false, second.position
|
false, second.position
|
||||||
)
|
)
|
||||||
@ -591,7 +608,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
lda $arrayVarName2+1,y
|
lda $arrayVarName2+1,y
|
||||||
sta $arrayVarName1+1,x
|
sta $arrayVarName1+1,x
|
||||||
pla
|
pla
|
||||||
sta $arrayVarName2+1,y
|
sta $arrayVarName2+1,y
|
||||||
ldx P8ZP_SCRATCH_REG
|
ldx P8ZP_SCRATCH_REG
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
@ -626,6 +643,122 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexName2: IdentifierReference) {
|
||||||
|
val index1 = indexValue1.number.toInt() * elementDt.memorySize()
|
||||||
|
val idxAsmName2 = asmgen.asmVariableName(indexName2)
|
||||||
|
when(elementDt) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $arrayVarName1 + $index1
|
||||||
|
pha
|
||||||
|
ldy $idxAsmName2
|
||||||
|
lda $arrayVarName2,y
|
||||||
|
sta $arrayVarName1 + $index1
|
||||||
|
pla
|
||||||
|
sta $arrayVarName2,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $arrayVarName1 + $index1
|
||||||
|
pha
|
||||||
|
lda $idxAsmName2
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda $arrayVarName2,y
|
||||||
|
sta $arrayVarName1 + $index1
|
||||||
|
pla
|
||||||
|
sta $arrayVarName2,y
|
||||||
|
lda $arrayVarName1 + $index1+1
|
||||||
|
pha
|
||||||
|
lda $arrayVarName2+1,y
|
||||||
|
sta $arrayVarName1 + $index1+1
|
||||||
|
pla
|
||||||
|
sta $arrayVarName2+1,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<(${arrayVarName1}+$index1)
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda #>(${arrayVarName1}+$index1)
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda #>$arrayVarName1
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda $idxAsmName2
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc $idxAsmName2
|
||||||
|
adc #<$arrayVarName1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
bcc +
|
||||||
|
inc P8ZP_SCRATCH_W1+1
|
||||||
|
+ jsr floats.swap_floats
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid aray elt type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexValue2: NumericLiteralValue) {
|
||||||
|
val idxAsmName1 = asmgen.asmVariableName(indexName1)
|
||||||
|
val index2 = indexValue2.number.toInt() * elementDt.memorySize()
|
||||||
|
when(elementDt) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $arrayVarName2 + $index2
|
||||||
|
pha
|
||||||
|
ldy $idxAsmName1
|
||||||
|
lda $arrayVarName1,y
|
||||||
|
sta $arrayVarName2 + $index2
|
||||||
|
pla
|
||||||
|
sta $arrayVarName1,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $arrayVarName2 + $index2
|
||||||
|
pha
|
||||||
|
lda $idxAsmName1
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda $arrayVarName1,y
|
||||||
|
sta $arrayVarName2 + $index2
|
||||||
|
pla
|
||||||
|
sta $arrayVarName1,y
|
||||||
|
lda $arrayVarName2 + $index2+1
|
||||||
|
pha
|
||||||
|
lda $arrayVarName1+1,y
|
||||||
|
sta $arrayVarName2 + $index2+1
|
||||||
|
pla
|
||||||
|
sta $arrayVarName1+1,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #>$arrayVarName1
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda $idxAsmName1
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc $idxAsmName1
|
||||||
|
adc #<$arrayVarName1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
bcc +
|
||||||
|
inc P8ZP_SCRATCH_W1+1
|
||||||
|
+ lda #<(${arrayVarName2}+$index2)
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda #>(${arrayVarName2}+$index2)
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
jsr floats.swap_floats
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid aray elt type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
|
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
|
||||||
translateFunctionArguments(fcall.args, func)
|
translateFunctionArguments(fcall.args, func)
|
||||||
val dt = fcall.args.single().inferType(program)
|
val dt = fcall.args.single().inferType(program)
|
||||||
|
@ -3,6 +3,7 @@ package prog8.compiler.target.c64.codegen
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.ArrayIndex
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.CpuType
|
import prog8.compiler.target.CpuType
|
||||||
@ -22,7 +23,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
is DirectMemoryRead -> translateDirectMemReadExpression(expression, true)
|
is DirectMemoryRead -> translateDirectMemReadExpression(expression, true)
|
||||||
is NumericLiteralValue -> translateExpression(expression)
|
is NumericLiteralValue -> translateExpression(expression)
|
||||||
is IdentifierReference -> translateExpression(expression)
|
is IdentifierReference -> translateExpression(expression)
|
||||||
is FunctionCall -> translateExpression(expression)
|
is FunctionCall -> translateFunctionCallResultOntoStack(expression)
|
||||||
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
||||||
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
||||||
}
|
}
|
||||||
@ -90,6 +91,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
in ByteDatatypes -> translateByteEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
in ByteDatatypes -> translateByteEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
||||||
in WordDatatypes -> translateWordEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
in WordDatatypes -> translateWordEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
||||||
DataType.FLOAT -> translateFloatEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
DataType.FLOAT -> translateFloatEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
||||||
|
DataType.STR -> translateStringEquals(left as IdentifierReference, right as IdentifierReference, jumpIfFalseLabel)
|
||||||
else -> throw AssemblyError("weird operand datatype")
|
else -> throw AssemblyError("weird operand datatype")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,6 +133,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
in ByteDatatypes -> translateByteNotEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
in ByteDatatypes -> translateByteNotEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
||||||
in WordDatatypes -> translateWordNotEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
in WordDatatypes -> translateWordNotEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
||||||
DataType.FLOAT -> translateFloatNotEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
DataType.FLOAT -> translateFloatNotEquals(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
||||||
|
DataType.STR -> translateStringNotEquals(left as IdentifierReference, right as IdentifierReference, jumpIfFalseLabel)
|
||||||
else -> throw AssemblyError("weird operand datatype")
|
else -> throw AssemblyError("weird operand datatype")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,6 +148,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
translateExpression(right)
|
translateExpression(right)
|
||||||
asmgen.out(" jsr floats.less_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
asmgen.out(" jsr floats.less_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
||||||
}
|
}
|
||||||
|
DataType.STR -> translateStringLess(left as IdentifierReference, right as IdentifierReference, jumpIfFalseLabel)
|
||||||
else -> throw AssemblyError("weird operand datatype")
|
else -> throw AssemblyError("weird operand datatype")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,6 +163,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
translateExpression(right)
|
translateExpression(right)
|
||||||
asmgen.out(" jsr floats.lesseq_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
asmgen.out(" jsr floats.lesseq_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
||||||
}
|
}
|
||||||
|
DataType.STR -> translateStringLessOrEqual(left as IdentifierReference, right as IdentifierReference, jumpIfFalseLabel)
|
||||||
else -> throw AssemblyError("weird operand datatype")
|
else -> throw AssemblyError("weird operand datatype")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,6 +178,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
translateExpression(right)
|
translateExpression(right)
|
||||||
asmgen.out(" jsr floats.greater_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
asmgen.out(" jsr floats.greater_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
||||||
}
|
}
|
||||||
|
DataType.STR -> translateStringGreater(left as IdentifierReference, right as IdentifierReference, jumpIfFalseLabel)
|
||||||
else -> throw AssemblyError("weird operand datatype")
|
else -> throw AssemblyError("weird operand datatype")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,6 +193,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
translateExpression(right)
|
translateExpression(right)
|
||||||
asmgen.out(" jsr floats.greatereq_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
asmgen.out(" jsr floats.greatereq_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
||||||
}
|
}
|
||||||
|
DataType.STR -> translateStringGreaterOrEqual(left as IdentifierReference, right as IdentifierReference, jumpIfFalseLabel)
|
||||||
else -> throw AssemblyError("weird operand datatype")
|
else -> throw AssemblyError("weird operand datatype")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -942,14 +949,106 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
asmgen.out(" jsr floats.notequal_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
asmgen.out(" jsr floats.notequal_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expression: FunctionCall) {
|
private fun translateStringEquals(left: IdentifierReference, right: IdentifierReference, jumpIfFalseLabel: String) {
|
||||||
|
val leftNam = asmgen.asmVariableName(left)
|
||||||
|
val rightNam = asmgen.asmVariableName(right)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$rightNam
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda #>$rightNam
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
lda #<$leftNam
|
||||||
|
ldy #>$leftNam
|
||||||
|
jsr prog8_lib.strcmp_mem
|
||||||
|
cmp #0
|
||||||
|
bne $jumpIfFalseLabel""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateStringNotEquals(left: IdentifierReference, right: IdentifierReference, jumpIfFalseLabel: String) {
|
||||||
|
val leftNam = asmgen.asmVariableName(left)
|
||||||
|
val rightNam = asmgen.asmVariableName(right)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$rightNam
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda #>$rightNam
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
lda #<$leftNam
|
||||||
|
ldy #>$leftNam
|
||||||
|
jsr prog8_lib.strcmp_mem
|
||||||
|
cmp #0
|
||||||
|
beq $jumpIfFalseLabel""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateStringLess(left: IdentifierReference, right: IdentifierReference, jumpIfFalseLabel: String) {
|
||||||
|
val leftNam = asmgen.asmVariableName(left)
|
||||||
|
val rightNam = asmgen.asmVariableName(right)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$rightNam
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda #>$rightNam
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
lda #<$leftNam
|
||||||
|
ldy #>$leftNam
|
||||||
|
jsr prog8_lib.strcmp_mem
|
||||||
|
bpl $jumpIfFalseLabel""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateStringGreater(left: IdentifierReference, right: IdentifierReference, jumpIfFalseLabel: String) {
|
||||||
|
val leftNam = asmgen.asmVariableName(left)
|
||||||
|
val rightNam = asmgen.asmVariableName(right)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$rightNam
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda #>$rightNam
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
lda #<$leftNam
|
||||||
|
ldy #>$leftNam
|
||||||
|
jsr prog8_lib.strcmp_mem
|
||||||
|
beq $jumpIfFalseLabel
|
||||||
|
bmi $jumpIfFalseLabel""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateStringLessOrEqual(left: IdentifierReference, right: IdentifierReference, jumpIfFalseLabel: String) {
|
||||||
|
val leftNam = asmgen.asmVariableName(left)
|
||||||
|
val rightNam = asmgen.asmVariableName(right)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$rightNam
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda #>$rightNam
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
lda #<$leftNam
|
||||||
|
ldy #>$leftNam
|
||||||
|
jsr prog8_lib.strcmp_mem
|
||||||
|
beq +
|
||||||
|
bpl $jumpIfFalseLabel
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateStringGreaterOrEqual(left: IdentifierReference, right: IdentifierReference, jumpIfFalseLabel: String) {
|
||||||
|
val leftNam = asmgen.asmVariableName(left)
|
||||||
|
val rightNam = asmgen.asmVariableName(right)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$rightNam
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda #>$rightNam
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
lda #<$leftNam
|
||||||
|
ldy #>$leftNam
|
||||||
|
jsr prog8_lib.strcmp_mem
|
||||||
|
beq +
|
||||||
|
bmi $jumpIfFalseLabel
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateFunctionCallResultOntoStack(expression: FunctionCall) {
|
||||||
val functionName = expression.target.nameInSource.last()
|
val functionName = expression.target.nameInSource.last()
|
||||||
val builtinFunc = BuiltinFunctions[functionName]
|
val builtinFunc = BuiltinFunctions[functionName]
|
||||||
if (builtinFunc != null) {
|
if (builtinFunc != null) {
|
||||||
asmgen.translateFunctioncallExpression(expression, builtinFunc)
|
asmgen.translateFunctioncallExpression(expression, builtinFunc)
|
||||||
} else {
|
} else {
|
||||||
val sub = expression.target.targetSubroutine(program.namespace)!!
|
val sub = expression.target.targetSubroutine(program.namespace)!!
|
||||||
asmgen.translateFunctionCall(expression)
|
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any {it.statusflag!=null}
|
||||||
|
asmgen.translateFunctionCall(expression, preserveStatusRegisterAfterCall)
|
||||||
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
||||||
for ((_, reg) in returns) {
|
for ((_, reg) in returns) {
|
||||||
if (!reg.stack) {
|
if (!reg.stack) {
|
||||||
@ -992,11 +1091,11 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: TypecastExpression) {
|
private fun translateExpression(typecast: TypecastExpression) {
|
||||||
translateExpression(expr.expression)
|
translateExpression(typecast.expression)
|
||||||
when(expr.expression.inferType(program).typeOrElse(DataType.STRUCT)) {
|
when(typecast.expression.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when(expr.type) {
|
when(typecast.type) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {}
|
DataType.UBYTE, DataType.BYTE -> {}
|
||||||
DataType.UWORD, DataType.WORD -> {
|
DataType.UWORD, DataType.WORD -> {
|
||||||
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
|
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
|
||||||
@ -1010,24 +1109,16 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
when(expr.type) {
|
when(typecast.type) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {}
|
DataType.UBYTE, DataType.BYTE -> {}
|
||||||
DataType.UWORD, DataType.WORD -> {
|
DataType.UWORD, DataType.WORD -> asmgen.signExtendStackLsb(DataType.BYTE)
|
||||||
// sign extend
|
|
||||||
asmgen.out("""
|
|
||||||
lda P8ESTACK_LO+1,x
|
|
||||||
ora #$7f
|
|
||||||
bmi +
|
|
||||||
lda #0
|
|
||||||
+ sta P8ESTACK_HI+1,x""")
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_b2float")
|
DataType.FLOAT -> asmgen.out(" jsr floats.stack_b2float")
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
when(expr.type) {
|
when(typecast.type) {
|
||||||
DataType.BYTE, DataType.UBYTE -> {}
|
DataType.BYTE, DataType.UBYTE -> {}
|
||||||
DataType.WORD, DataType.UWORD -> {}
|
DataType.WORD, DataType.UWORD -> {}
|
||||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_uw2float")
|
DataType.FLOAT -> asmgen.out(" jsr floats.stack_uw2float")
|
||||||
@ -1036,7 +1127,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
when(expr.type) {
|
when(typecast.type) {
|
||||||
DataType.BYTE, DataType.UBYTE -> {}
|
DataType.BYTE, DataType.UBYTE -> {}
|
||||||
DataType.WORD, DataType.UWORD -> {}
|
DataType.WORD, DataType.UWORD -> {}
|
||||||
DataType.FLOAT -> asmgen.out(" jsr floats.stack_w2float")
|
DataType.FLOAT -> asmgen.out(" jsr floats.stack_w2float")
|
||||||
@ -1045,7 +1136,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
when(expr.type) {
|
when(typecast.type) {
|
||||||
DataType.UBYTE -> asmgen.out(" jsr floats.stack_float2uw")
|
DataType.UBYTE -> asmgen.out(" jsr floats.stack_float2uw")
|
||||||
DataType.BYTE -> asmgen.out(" jsr floats.stack_float2w")
|
DataType.BYTE -> asmgen.out(" jsr floats.stack_float2w")
|
||||||
DataType.UWORD -> asmgen.out(" jsr floats.stack_float2uw")
|
DataType.UWORD -> asmgen.out(" jsr floats.stack_float2uw")
|
||||||
@ -1055,6 +1146,10 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DataType.STR -> {
|
||||||
|
if (typecast.type != DataType.UWORD && typecast.type == DataType.STR)
|
||||||
|
throw AssemblyError("cannot typecast a string into another incompatitble type")
|
||||||
|
}
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast pass-by-reference value into another type")
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast pass-by-reference value into another type")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -1128,6 +1223,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: BinaryExpression) {
|
private fun translateExpression(expr: BinaryExpression) {
|
||||||
|
// TODO needs to use optimized assembly generation like the assignment instructions. But avoid code duplication.... rewrite all expressions into assignment form?
|
||||||
val leftIDt = expr.left.inferType(program)
|
val leftIDt = expr.left.inferType(program)
|
||||||
val rightIDt = expr.right.inferType(program)
|
val rightIDt = expr.right.inferType(program)
|
||||||
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
||||||
@ -1137,6 +1233,106 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
||||||
// see if we can apply some optimized routines
|
// see if we can apply some optimized routines
|
||||||
when(expr.operator) {
|
when(expr.operator) {
|
||||||
|
"+" -> {
|
||||||
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
|
val leftVal = expr.left.constValue(program)?.number?.toInt()
|
||||||
|
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||||
|
if (leftVal!=null && leftVal in -4..4) {
|
||||||
|
translateExpression(expr.right)
|
||||||
|
if(rightDt in ByteDatatypes) {
|
||||||
|
val incdec = if(leftVal<0) "dec" else "inc"
|
||||||
|
repeat(leftVal.absoluteValue) {
|
||||||
|
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// word
|
||||||
|
if(leftVal<0) {
|
||||||
|
repeat(leftVal.absoluteValue) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
dec P8ESTACK_HI+1,x
|
||||||
|
+ dec P8ESTACK_LO+1,x""")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
repeat(leftVal) {
|
||||||
|
asmgen.out("""
|
||||||
|
inc P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
inc P8ESTACK_HI+1,x
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else if (rightVal!=null && rightVal in -4..4)
|
||||||
|
{
|
||||||
|
translateExpression(expr.left)
|
||||||
|
if(leftDt in ByteDatatypes) {
|
||||||
|
val incdec = if(rightVal<0) "dec" else "inc"
|
||||||
|
repeat(rightVal.absoluteValue) {
|
||||||
|
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// word
|
||||||
|
if(rightVal<0) {
|
||||||
|
repeat(rightVal.absoluteValue) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
dec P8ESTACK_HI+1,x
|
||||||
|
+ dec P8ESTACK_LO+1,x""")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
repeat(rightVal) {
|
||||||
|
asmgen.out("""
|
||||||
|
inc P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
inc P8ESTACK_HI+1,x
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"-" -> {
|
||||||
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
|
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||||
|
if (rightVal!=null && rightVal in -4..4)
|
||||||
|
{
|
||||||
|
translateExpression(expr.left)
|
||||||
|
if(leftDt in ByteDatatypes) {
|
||||||
|
val incdec = if(rightVal<0) "inc" else "dec"
|
||||||
|
repeat(rightVal.absoluteValue) {
|
||||||
|
asmgen.out(" $incdec P8ESTACK_LO+1,x")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// word
|
||||||
|
if(rightVal>0) {
|
||||||
|
repeat(rightVal.absoluteValue) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
dec P8ESTACK_HI+1,x
|
||||||
|
+ dec P8ESTACK_LO+1,x""")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
repeat(rightVal) {
|
||||||
|
asmgen.out("""
|
||||||
|
inc P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
inc P8ESTACK_HI+1,x
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
">>" -> {
|
">>" -> {
|
||||||
translateExpression(expr.left)
|
translateExpression(expr.left)
|
||||||
val amount = expr.right.constValue(program)?.number?.toInt()
|
val amount = expr.right.constValue(program)?.number?.toInt()
|
||||||
@ -1218,6 +1414,16 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
if(value!=null) {
|
if(value!=null) {
|
||||||
if(rightDt in IntegerDatatypes) {
|
if(rightDt in IntegerDatatypes) {
|
||||||
val amount = value.number.toInt()
|
val amount = value.number.toInt()
|
||||||
|
if(amount==2) {
|
||||||
|
// optimize x*2 common case
|
||||||
|
translateExpression(expr.left)
|
||||||
|
if(leftDt in ByteDatatypes) {
|
||||||
|
asmgen.out(" asl P8ESTACK_LO+1,x")
|
||||||
|
} else {
|
||||||
|
asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
when(rightDt) {
|
when(rightDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(amount in asmgen.optimizedByteMultiplications) {
|
if(amount in asmgen.optimizedByteMultiplications) {
|
||||||
@ -1262,20 +1468,41 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"/" -> {
|
||||||
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
|
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||||
|
if(rightVal!=null && rightVal==2) {
|
||||||
|
translateExpression(expr.left)
|
||||||
|
when(leftDt) {
|
||||||
|
DataType.UBYTE -> asmgen.out(" lsr P8ESTACK_LO+1,x")
|
||||||
|
DataType.BYTE -> asmgen.out(" asl P8ESTACK_LO+1,x | ror P8ESTACK_LO+1,x")
|
||||||
|
DataType.UWORD -> asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
|
||||||
|
DataType.WORD -> asmgen.out(" asl P8ESTACK_HI+1,x | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x")
|
||||||
|
else -> throw AssemblyError("wrong dt")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the general, non-optimized cases
|
|
||||||
translateExpression(expr.left)
|
|
||||||
translateExpression(expr.right)
|
|
||||||
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|
if((leftDt in ByteDatatypes && rightDt !in ByteDatatypes)
|
||||||
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
|
|| (leftDt in WordDatatypes && rightDt !in WordDatatypes))
|
||||||
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
|
throw AssemblyError("binary operator ${expr.operator} left/right dt not identical")
|
||||||
|
|
||||||
when (leftDt) {
|
// the general, non-optimized cases TODO optimize more cases....
|
||||||
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
|
translateExpression(expr.left)
|
||||||
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
|
translateExpression(expr.right)
|
||||||
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
|
if(leftDt==DataType.STR && rightDt==DataType.STR && expr.operator in comparisonOperators) {
|
||||||
else -> throw AssemblyError("non-numerical datatype")
|
translateCompareStrings(expr.operator)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
when (leftDt) {
|
||||||
|
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
|
||||||
|
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
|
||||||
|
DataType.FLOAT -> translateBinaryOperatorFloats(expr.operator)
|
||||||
|
else -> throw AssemblyError("non-numerical datatype")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1316,11 +1543,10 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(arrayExpr: ArrayIndexedExpression) {
|
private fun translateExpression(arrayExpr: ArrayIndexedExpression) {
|
||||||
val index = arrayExpr.arrayspec.index
|
|
||||||
val elementDt = arrayExpr.inferType(program).typeOrElse(DataType.STRUCT)
|
val elementDt = arrayExpr.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
val arrayVarName = asmgen.asmVariableName(arrayExpr.identifier)
|
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
|
||||||
if(index is NumericLiteralValue) {
|
if(arrayExpr.indexer.indexNum!=null) {
|
||||||
val indexValue = index.number.toInt() * elementDt.memorySize()
|
val indexValue = arrayExpr.indexer.constIndex()!! * elementDt.memorySize()
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
|
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
|
||||||
@ -1359,6 +1585,14 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun translateExpression(indexer: ArrayIndex) {
|
||||||
|
// it is either a number, or a variable
|
||||||
|
val indexNum = indexer.indexNum
|
||||||
|
val indexVar = indexer.indexVar
|
||||||
|
indexNum?.let { asmgen.translateExpression(indexNum) }
|
||||||
|
indexVar?.let { asmgen.translateExpression(indexVar) }
|
||||||
|
}
|
||||||
|
|
||||||
private fun translateBinaryOperatorBytes(operator: String, types: DataType) {
|
private fun translateBinaryOperatorBytes(operator: String, types: DataType) {
|
||||||
when(operator) {
|
when(operator) {
|
||||||
"**" -> throw AssemblyError("** operator requires floats")
|
"**" -> throw AssemblyError("** operator requires floats")
|
||||||
@ -1447,4 +1681,36 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
else -> throw AssemblyError("invalid operator $operator")
|
else -> throw AssemblyError("invalid operator $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun translateCompareStrings(operator: String) {
|
||||||
|
asmgen.out(" jsr prog8_lib.func_strcmp | lda P8ESTACK_LO+1,x") // result of compare in A
|
||||||
|
when(operator) {
|
||||||
|
"==" -> asmgen.out(" and #1 | eor #1 | sta P8ESTACK_LO+1,x")
|
||||||
|
"!=" -> asmgen.out(" and #1 | sta P8ESTACK_LO+1,x")
|
||||||
|
"<=" -> asmgen.out("""
|
||||||
|
bpl +
|
||||||
|
lda #1
|
||||||
|
bne ++
|
||||||
|
+ lda #0
|
||||||
|
+ sta P8ESTACK_LO+1,x""")
|
||||||
|
">=" -> asmgen.out("""
|
||||||
|
bmi +
|
||||||
|
lda #1
|
||||||
|
bne ++
|
||||||
|
+ lda #0
|
||||||
|
+ sta P8ESTACK_LO+1,x""")
|
||||||
|
"<" -> asmgen.out("""
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+ sta P8ESTACK_LO+1,x""")
|
||||||
|
">" -> asmgen.out("""
|
||||||
|
bpl +
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+ sta P8ESTACK_LO+1,x""")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -610,8 +610,8 @@ $endLabel""")
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) {
|
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) {
|
||||||
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), variable=stmt.loopVar)
|
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), stmt.definingSubroutine(), variableAsmName=asmgen.asmVariableName(stmt.loopVar))
|
||||||
val src = AsmAssignSource.fromAstSource(range.from, program).adjustDataTypeToTarget(target)
|
val src = AsmAssignSource.fromAstSource(range.from, program, asmgen).adjustSignedUnsigned(target)
|
||||||
val assign = AsmAssignment(src, target, false, range.position)
|
val assign = AsmAssignment(src, target, false, range.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
package prog8.compiler.target.c64.codegen
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
@ -13,13 +14,13 @@ import prog8.compiler.target.c64.codegen.assignment.*
|
|||||||
|
|
||||||
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
internal fun translateFunctionCall(stmt: IFunctionCall) {
|
internal fun translateFunctionCall(stmt: IFunctionCall, preserveStatusRegisterAfterCall: Boolean) {
|
||||||
// output the code to setup the parameters and perform the actual call
|
// output the code to setup the parameters and perform the actual call
|
||||||
// does NOT output the code to deal with the result values!
|
// does NOT output the code to deal with the result values!
|
||||||
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||||
val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult() || sub.regXasParam()
|
val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult() || sub.regXasParam()
|
||||||
if(saveX)
|
if(saveX)
|
||||||
asmgen.saveRegister(CpuRegister.X)
|
asmgen.saveRegister(CpuRegister.X, preserveStatusRegisterAfterCall, (stmt as Node).definingSubroutine())
|
||||||
|
|
||||||
val subName = asmgen.asmSymbolName(stmt.target)
|
val subName = asmgen.asmSymbolName(stmt.target)
|
||||||
if(stmt.args.isNotEmpty()) {
|
if(stmt.args.isNotEmpty()) {
|
||||||
@ -57,8 +58,14 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
asmgen.out(" jsr $subName")
|
asmgen.out(" jsr $subName")
|
||||||
|
|
||||||
|
if(preserveStatusRegisterAfterCall) {
|
||||||
|
asmgen.out(" php\t; save status flags from call")
|
||||||
|
// note: the containing statement (such as the FunctionCallStatement or the Assignment or the Expression)
|
||||||
|
// must take care of popping this value again at the end!
|
||||||
|
}
|
||||||
|
|
||||||
if(saveX)
|
if(saveX)
|
||||||
asmgen.restoreRegister(CpuRegister.X)
|
asmgen.restoreRegister(CpuRegister.X, preserveStatusRegisterAfterCall)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||||
@ -148,11 +155,9 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
throw AssemblyError("argument type incompatible")
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
val scopedParamVar = (sub.scopedname+"."+parameter.value.name).split(".")
|
val varName = asmgen.asmVariableName(sub.scopedname+"."+parameter.value.name)
|
||||||
val identifier = IdentifierReference(scopedParamVar, sub.position)
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, parameter.value.type, sub, variableAsmName = varName)
|
||||||
identifier.linkParents(value.parent)
|
val source = AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(tgt)
|
||||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, parameter.value.type, variable = identifier)
|
|
||||||
val source = AsmAssignSource.fromAstSource(value, program).adjustDataTypeToTarget(tgt)
|
|
||||||
val asgn = AsmAssignment(source, tgt, false, Position.DUMMY)
|
val asgn = AsmAssignment(source, tgt, false, Position.DUMMY)
|
||||||
asmgen.translateNormalAssignment(asgn)
|
asmgen.translateNormalAssignment(asgn)
|
||||||
}
|
}
|
||||||
@ -170,13 +175,22 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
val statusflag = paramRegister.statusflag
|
val statusflag = paramRegister.statusflag
|
||||||
val register = paramRegister.registerOrPair
|
val register = paramRegister.registerOrPair
|
||||||
val stack = paramRegister.stack
|
val stack = paramRegister.stack
|
||||||
|
val requiredDt = parameter.value.type
|
||||||
|
if(requiredDt!=valueDt) {
|
||||||
|
if(valueDt largerThan requiredDt)
|
||||||
|
throw AssemblyError("can only convert byte values to word param types")
|
||||||
|
}
|
||||||
when {
|
when {
|
||||||
stack -> {
|
stack -> {
|
||||||
// push arg onto the stack
|
// push arg onto the stack
|
||||||
// note: argument order is reversed (first argument will be deepest on the stack)
|
// note: argument order is reversed (first argument will be deepest on the stack)
|
||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
|
if(requiredDt!=valueDt)
|
||||||
|
asmgen.signExtendStackLsb(valueDt)
|
||||||
}
|
}
|
||||||
statusflag!=null -> {
|
statusflag!=null -> {
|
||||||
|
if(requiredDt!=valueDt)
|
||||||
|
throw AssemblyError("for statusflag, byte value is required")
|
||||||
if (statusflag == Statusflag.Pc) {
|
if (statusflag == Statusflag.Pc) {
|
||||||
// this param needs to be set last, right before the jsr
|
// this param needs to be set last, right before the jsr
|
||||||
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
// for now, this is already enforced on the subroutine definition by the Ast Checker
|
||||||
@ -216,15 +230,30 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// via register or register pair
|
// via register or register pair
|
||||||
val target = AsmAssignTarget.fromRegisters(register!!, program, asmgen)
|
val target = AsmAssignTarget.fromRegisters(register!!, sub, program, asmgen)
|
||||||
val src = if(valueDt in PassByReferenceDatatypes) {
|
if(requiredDt largerThan valueDt) {
|
||||||
val addr = AddressOf(value as IdentifierReference, Position.DUMMY)
|
// we need to sign extend the source, do this via temporary word variable
|
||||||
AsmAssignSource.fromAstSource(addr, program).adjustDataTypeToTarget(target)
|
val scratchVar = asmgen.asmVariableName("P8ZP_SCRATCH_W1")
|
||||||
} else {
|
val scratchTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UBYTE, sub, scratchVar)
|
||||||
AsmAssignSource.fromAstSource(value, program).adjustDataTypeToTarget(target)
|
val source = AsmAssignSource.fromAstSource(value, program, asmgen)
|
||||||
|
asmgen.translateNormalAssignment(AsmAssignment(source, scratchTarget, false, value.position))
|
||||||
|
asmgen.signExtendVariableLsb(scratchVar, valueDt)
|
||||||
|
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, DataType.UWORD, scratchVar)
|
||||||
|
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val src = if(valueDt in PassByReferenceDatatypes) {
|
||||||
|
if(value is IdentifierReference) {
|
||||||
|
val addr = AddressOf(value, Position.DUMMY)
|
||||||
|
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
||||||
|
} else {
|
||||||
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
|
}
|
||||||
|
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY))
|
||||||
}
|
}
|
||||||
|
|
||||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
val targetIdent = stmt.target.identifier
|
val targetIdent = stmt.target.identifier
|
||||||
val targetMemory = stmt.target.memoryAddress
|
val targetMemory = stmt.target.memoryAddress
|
||||||
val targetArrayIdx = stmt.target.arrayindexed
|
val targetArrayIdx = stmt.target.arrayindexed
|
||||||
|
val scope = stmt.definingSubroutine()
|
||||||
when {
|
when {
|
||||||
targetIdent!=null -> {
|
targetIdent!=null -> {
|
||||||
val what = asmgen.asmVariableName(targetIdent)
|
val what = asmgen.asmVariableName(targetIdent)
|
||||||
@ -69,67 +70,64 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
targetArrayIdx!=null -> {
|
targetArrayIdx!=null -> {
|
||||||
val index = targetArrayIdx.arrayspec.index
|
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar)
|
||||||
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.identifier)
|
|
||||||
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT)
|
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
when(index) {
|
if(targetArrayIdx.indexer.indexNum!=null) {
|
||||||
is NumericLiteralValue -> {
|
val indexValue = targetArrayIdx.indexer.constIndex()!! * elementDt.memorySize()
|
||||||
val indexValue = index.number.toInt() * elementDt.memorySize()
|
when(elementDt) {
|
||||||
when(elementDt) {
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
in WordDatatypes -> {
|
||||||
in WordDatatypes -> {
|
if(incr)
|
||||||
if(incr)
|
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
|
||||||
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
|
else
|
||||||
else
|
asmgen.out("""
|
||||||
asmgen.out("""
|
lda $asmArrayvarname+$indexValue
|
||||||
lda $asmArrayvarname+$indexValue
|
bne +
|
||||||
bne +
|
dec $asmArrayvarname+$indexValue+1
|
||||||
dec $asmArrayvarname+$indexValue+1
|
|
||||||
+ dec $asmArrayvarname+$indexValue
|
+ dec $asmArrayvarname+$indexValue
|
||||||
""")
|
""")
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
asmgen.out(" lda #<$asmArrayvarname+$indexValue | ldy #>$asmArrayvarname+$indexValue")
|
|
||||||
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("need numeric type")
|
|
||||||
}
|
}
|
||||||
}
|
DataType.FLOAT -> {
|
||||||
else -> {
|
asmgen.out(" lda #<$asmArrayvarname+$indexValue | ldy #>$asmArrayvarname+$indexValue")
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
|
asmgen.out(if(incr) " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
|
||||||
asmgen.saveRegister(CpuRegister.X)
|
|
||||||
asmgen.out(" tax")
|
|
||||||
when(elementDt) {
|
|
||||||
in ByteDatatypes -> {
|
|
||||||
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
|
||||||
}
|
|
||||||
in WordDatatypes -> {
|
|
||||||
if(incr)
|
|
||||||
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
|
|
||||||
else
|
|
||||||
asmgen.out("""
|
|
||||||
lda $asmArrayvarname,x
|
|
||||||
bne +
|
|
||||||
dec $asmArrayvarname+1,x
|
|
||||||
+ dec $asmArrayvarname
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
asmgen.out("""
|
|
||||||
ldy #>$asmArrayvarname
|
|
||||||
clc
|
|
||||||
adc #<$asmArrayvarname
|
|
||||||
bcc +
|
|
||||||
iny
|
|
||||||
+ jsr floats.inc_var_f""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird array elt dt")
|
|
||||||
}
|
}
|
||||||
asmgen.restoreRegister(CpuRegister.X)
|
else -> throw AssemblyError("need numeric type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
|
||||||
|
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||||
|
asmgen.out(" tax")
|
||||||
|
when(elementDt) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
if(incr)
|
||||||
|
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
lda $asmArrayvarname,x
|
||||||
|
bne +
|
||||||
|
dec $asmArrayvarname+1,x
|
||||||
|
+ dec $asmArrayvarname
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #>$asmArrayvarname
|
||||||
|
clc
|
||||||
|
adc #<$asmArrayvarname
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jsr floats.inc_var_f""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird array elt dt")
|
||||||
|
}
|
||||||
|
asmgen.restoreRegister(CpuRegister.X, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird target type ${stmt.target}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import prog8.ast.statements.Assignment
|
import prog8.ast.statements.Assignment
|
||||||
import prog8.ast.statements.DirectMemoryWrite
|
import prog8.ast.statements.DirectMemoryWrite
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
import prog8.compiler.target.c64.codegen.AsmGen
|
||||||
|
|
||||||
@ -29,10 +30,11 @@ internal enum class SourceStorageKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class AsmAssignTarget(val kind: TargetStorageKind,
|
internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||||
program: Program,
|
private val program: Program,
|
||||||
asmgen: AsmGen,
|
private val asmgen: AsmGen,
|
||||||
val datatype: DataType,
|
val datatype: DataType,
|
||||||
val variable: IdentifierReference? = null,
|
val scope: Subroutine?,
|
||||||
|
private val variableAsmName: String? = null,
|
||||||
val array: ArrayIndexedExpression? = null,
|
val array: ArrayIndexedExpression? = null,
|
||||||
val memory: DirectMemoryWrite? = null,
|
val memory: DirectMemoryWrite? = null,
|
||||||
val register: RegisterOrPair? = null,
|
val register: RegisterOrPair? = null,
|
||||||
@ -40,20 +42,16 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
||||||
val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() }
|
val constArrayIndexValue by lazy { array?.indexer?.constIndex() }
|
||||||
val vardecl by lazy { variable!!.targetVarDecl(program.namespace)!! }
|
val asmVarname: String
|
||||||
val asmVarname by lazy {
|
get() = if(array==null)
|
||||||
if(variable!=null)
|
variableAsmName!!
|
||||||
asmgen.asmVariableName(variable)
|
|
||||||
else
|
else
|
||||||
asmgen.asmVariableName(array!!.identifier)
|
asmgen.asmVariableName(array.arrayvar)
|
||||||
}
|
|
||||||
|
|
||||||
lateinit var origAssign: AsmAssignment
|
lateinit var origAssign: AsmAssignment
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if(variable!=null && vardecl.type == VarDeclType.CONST)
|
|
||||||
throw AssemblyError("can't assign to a constant")
|
|
||||||
if(register!=null && datatype !in IntegerDatatypes)
|
if(register!=null && datatype !in IntegerDatatypes)
|
||||||
throw AssemblyError("register must be integer type")
|
throw AssemblyError("register must be integer type")
|
||||||
}
|
}
|
||||||
@ -62,29 +60,30 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget = with(assign.target) {
|
fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget = with(assign.target) {
|
||||||
val dt = inferType(program, assign).typeOrElse(DataType.STRUCT)
|
val dt = inferType(program, assign).typeOrElse(DataType.STRUCT)
|
||||||
when {
|
when {
|
||||||
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, variable=identifier, origAstTarget = this)
|
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
||||||
arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, array = arrayindexed, origAstTarget = this)
|
arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine(), array = arrayindexed, origAstTarget = this)
|
||||||
memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, memory = memoryAddress, origAstTarget = this)
|
memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, assign.definingSubroutine(), memory = memoryAddress, origAstTarget = this)
|
||||||
else -> throw AssemblyError("weird target")
|
else -> throw AssemblyError("weird target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromRegisters(registers: RegisterOrPair, program: Program, asmgen: AsmGen): AsmAssignTarget =
|
fun fromRegisters(registers: RegisterOrPair, scope: Subroutine?, program: Program, asmgen: AsmGen): AsmAssignTarget =
|
||||||
when(registers) {
|
when(registers) {
|
||||||
RegisterOrPair.A,
|
RegisterOrPair.A,
|
||||||
RegisterOrPair.X,
|
RegisterOrPair.X,
|
||||||
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UBYTE, register = registers)
|
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UBYTE, scope, register = registers)
|
||||||
RegisterOrPair.AX,
|
RegisterOrPair.AX,
|
||||||
RegisterOrPair.AY,
|
RegisterOrPair.AY,
|
||||||
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UWORD, register = registers)
|
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UWORD, scope, register = registers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class AsmAssignSource(val kind: SourceStorageKind,
|
internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||||
private val program: Program,
|
private val program: Program,
|
||||||
|
private val asmgen: AsmGen,
|
||||||
val datatype: DataType,
|
val datatype: DataType,
|
||||||
val variable: IdentifierReference? = null,
|
private val variableAsmName: String? = null,
|
||||||
val array: ArrayIndexedExpression? = null,
|
val array: ArrayIndexedExpression? = null,
|
||||||
val memory: DirectMemoryRead? = null,
|
val memory: DirectMemoryRead? = null,
|
||||||
val register: CpuRegister? = null,
|
val register: CpuRegister? = null,
|
||||||
@ -93,53 +92,73 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
||||||
val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() }
|
val constArrayIndexValue by lazy { array?.indexer?.constIndex() }
|
||||||
val vardecl by lazy { variable?.targetVarDecl(program.namespace)!! }
|
|
||||||
|
val asmVarname: String
|
||||||
|
get() = if(array==null)
|
||||||
|
variableAsmName!!
|
||||||
|
else
|
||||||
|
asmgen.asmVariableName(array.arrayvar)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromAstSource(value: Expression, program: Program): AsmAssignSource {
|
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
|
||||||
val cv = value.constValue(program)
|
val cv = value.constValue(program)
|
||||||
if(cv!=null)
|
if(cv!=null)
|
||||||
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, cv.type, number = cv)
|
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
|
||||||
|
|
||||||
return when(value) {
|
return when(value) {
|
||||||
is NumericLiteralValue -> AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, value.type, number = cv)
|
is NumericLiteralValue -> AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, value.type, number = cv)
|
||||||
is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
||||||
is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, dt, variable = value)
|
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = asmgen.asmVariableName(value))
|
||||||
}
|
}
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
AsmAssignSource(SourceStorageKind.MEMORY, program, DataType.UBYTE, memory = value)
|
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||||
}
|
}
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
AsmAssignSource(SourceStorageKind.ARRAY, program, dt, array = value)
|
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
if(value is FunctionCall) {
|
||||||
|
// functioncall.
|
||||||
|
val asmSub = value.target.targetStatement(program.namespace)
|
||||||
|
if(asmSub is Subroutine && asmSub.isAsmSubroutine) {
|
||||||
|
when (asmSub.asmReturnvaluesRegisters.count { rr -> rr.registerOrPair!=null }) {
|
||||||
|
0 -> throw AssemblyError("can't translate zero return values in assignment")
|
||||||
|
1 -> {
|
||||||
|
// assignment generation itself must make sure the status register is correct after the subroutine call, if status register is involved!
|
||||||
|
val reg = asmSub.asmReturnvaluesRegisters.single { rr->rr.registerOrPair!=null }.registerOrPair!!
|
||||||
|
val dt = when(reg) {
|
||||||
|
RegisterOrPair.A,
|
||||||
|
RegisterOrPair.X,
|
||||||
|
RegisterOrPair.Y -> DataType.UBYTE
|
||||||
|
RegisterOrPair.AX,
|
||||||
|
RegisterOrPair.AY,
|
||||||
|
RegisterOrPair.XY -> DataType.UWORD
|
||||||
|
}
|
||||||
|
return AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt, expression = value)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("can't translate multiple return values in assignment")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, dt, expression = value)
|
return AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt, expression = value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAstValue(): Expression = when(kind) {
|
fun adjustSignedUnsigned(target: AsmAssignTarget): AsmAssignSource {
|
||||||
SourceStorageKind.LITERALNUMBER -> number!!
|
|
||||||
SourceStorageKind.VARIABLE -> variable!!
|
|
||||||
SourceStorageKind.ARRAY -> array!!
|
|
||||||
SourceStorageKind.MEMORY -> memory!!
|
|
||||||
SourceStorageKind.EXPRESSION -> expression!!
|
|
||||||
SourceStorageKind.REGISTER -> throw AssemblyError("cannot get a register source as Ast node")
|
|
||||||
SourceStorageKind.STACK -> throw AssemblyError("cannot get a stack source as Ast node")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun withAdjustedDt(newType: DataType) =
|
|
||||||
AsmAssignSource(kind, program, newType, variable, array, memory, register, number, expression)
|
|
||||||
|
|
||||||
fun adjustDataTypeToTarget(target: AsmAssignTarget): AsmAssignSource {
|
|
||||||
// allow some signed/unsigned relaxations
|
// allow some signed/unsigned relaxations
|
||||||
|
|
||||||
|
fun withAdjustedDt(newType: DataType) =
|
||||||
|
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, expression)
|
||||||
|
|
||||||
if(target.datatype!=datatype) {
|
if(target.datatype!=datatype) {
|
||||||
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {
|
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {
|
||||||
return withAdjustedDt(target.datatype)
|
return withAdjustedDt(target.datatype)
|
||||||
@ -160,6 +179,9 @@ internal class AsmAssignment(val source: AsmAssignSource,
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
if(target.register !in setOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
if(target.register !in setOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||||
require(source.datatype.memorySize() == target.datatype.memorySize()) { "source and target datatype must be same storage class" }
|
require(source.datatype != DataType.STRUCT) { "must not be placeholder datatype" }
|
||||||
|
require(source.datatype.memorySize() <= target.datatype.memorySize()) {
|
||||||
|
"source storage size must be less or equal to target datatype storage size"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
|
|
||||||
fun translate(assignment: Assignment) {
|
fun translate(assignment: Assignment) {
|
||||||
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
|
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
|
||||||
val source = AsmAssignSource.fromAstSource(assignment.value, program).adjustDataTypeToTarget(target)
|
val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target)
|
||||||
|
|
||||||
val assign = AsmAssignment(source, target, assignment.isAugmentable, assignment.position)
|
val assign = AsmAssignment(source, target, assignment.isAugmentable, assignment.position)
|
||||||
target.origAssign = assign
|
target.origAssign = assign
|
||||||
@ -34,7 +34,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
SourceStorageKind.LITERALNUMBER -> {
|
SourceStorageKind.LITERALNUMBER -> {
|
||||||
// simple case: assign a constant number
|
// simple case: assign a constant number
|
||||||
val num = assign.source.number!!.number
|
val num = assign.source.number!!.number
|
||||||
when (assign.source.datatype) {
|
when (assign.target.datatype) {
|
||||||
DataType.UBYTE, DataType.BYTE -> assignConstantByte(assign.target, num.toShort())
|
DataType.UBYTE, DataType.BYTE -> assignConstantByte(assign.target, num.toShort())
|
||||||
DataType.UWORD, DataType.WORD -> assignConstantWord(assign.target, num.toInt())
|
DataType.UWORD, DataType.WORD -> assignConstantWord(assign.target, num.toInt())
|
||||||
DataType.FLOAT -> assignConstantFloat(assign.target, num.toDouble())
|
DataType.FLOAT -> assignConstantFloat(assign.target, num.toDouble())
|
||||||
@ -43,23 +43,22 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
SourceStorageKind.VARIABLE -> {
|
SourceStorageKind.VARIABLE -> {
|
||||||
// simple case: assign from another variable
|
// simple case: assign from another variable
|
||||||
val variable = assign.source.variable!!
|
val variable = assign.source.asmVarname
|
||||||
when (assign.source.datatype) {
|
when (assign.target.datatype) {
|
||||||
DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable)
|
DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable)
|
||||||
DataType.UWORD, DataType.WORD -> assignVariableWord(assign.target, variable)
|
DataType.UWORD, DataType.WORD -> assignVariableWord(assign.target, variable)
|
||||||
DataType.FLOAT -> assignVariableFloat(assign.target, variable)
|
DataType.FLOAT -> assignVariableFloat(assign.target, variable)
|
||||||
in PassByReferenceDatatypes -> assignAddressOf(assign.target, variable)
|
DataType.STR -> assignVariableString(assign.target, variable)
|
||||||
else -> throw AssemblyError("unsupported assignment target type ${assign.target.datatype}")
|
else -> throw AssemblyError("unsupported assignment target type ${assign.target.datatype}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SourceStorageKind.ARRAY -> {
|
SourceStorageKind.ARRAY -> {
|
||||||
val value = assign.source.array!!
|
val value = assign.source.array!!
|
||||||
val elementDt = assign.source.datatype
|
val elementDt = assign.source.datatype
|
||||||
val index = value.arrayspec.index
|
val arrayVarName = asmgen.asmVariableName(value.arrayvar)
|
||||||
val arrayVarName = asmgen.asmVariableName(value.identifier)
|
if (value.indexer.indexNum!=null) {
|
||||||
if (index is NumericLiteralValue) {
|
|
||||||
// constant array index value
|
// constant array index value
|
||||||
val indexValue = index.number.toInt() * elementDt.memorySize()
|
val indexValue = value.indexer.constIndex()!! * elementDt.memorySize()
|
||||||
when (elementDt) {
|
when (elementDt) {
|
||||||
in ByteDatatypes ->
|
in ByteDatatypes ->
|
||||||
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
|
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
|
||||||
@ -114,31 +113,46 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SourceStorageKind.EXPRESSION -> {
|
SourceStorageKind.EXPRESSION -> {
|
||||||
val value = assign.source.expression!!
|
when(val value = assign.source.expression!!) {
|
||||||
when(value) {
|
is AddressOf -> {
|
||||||
is AddressOf -> assignAddressOf(assign.target, value.identifier)
|
val sourceName = value.identifier.firstStructVarName(program.namespace) ?: asmgen.asmVariableName(value.identifier)
|
||||||
|
assignAddressOf(assign.target, sourceName)
|
||||||
|
}
|
||||||
is NumericLiteralValue -> throw AssemblyError("source kind should have been literalnumber")
|
is NumericLiteralValue -> throw AssemblyError("source kind should have been literalnumber")
|
||||||
is IdentifierReference -> throw AssemblyError("source kind should have been variable")
|
is IdentifierReference -> throw AssemblyError("source kind should have been variable")
|
||||||
is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array")
|
is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array")
|
||||||
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory")
|
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory")
|
||||||
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, assign)
|
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, assign)
|
||||||
// is FunctionCall -> {
|
is FunctionCall -> {
|
||||||
// if (assign.target.kind == TargetStorageKind.STACK) {
|
if(value.target.targetSubroutine(program.namespace)?.isAsmSubroutine==true) {
|
||||||
// asmgen.translateExpression(value)
|
// handle asmsub functioncalls specifically, without shoving stuff on the estack
|
||||||
// assignStackValue(assign.target)
|
val sub = value.target.targetSubroutine(program.namespace)!!
|
||||||
// } else {
|
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any { it.statusflag != null }
|
||||||
// val functionName = value.target.nameInSource.last()
|
asmgen.translateFunctionCall(value, preserveStatusRegisterAfterCall)
|
||||||
// val builtinFunc = BuiltinFunctions[functionName]
|
when((sub.asmReturnvaluesRegisters.single { it.registerOrPair!=null }).registerOrPair) {
|
||||||
// if (builtinFunc != null) {
|
RegisterOrPair.A -> assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
// println("!!!!BUILTIN-FUNCCALL target=${assign.target.kind} $value") // TODO optimize certain functions?
|
RegisterOrPair.X -> assignRegisterByte(assign.target, CpuRegister.X)
|
||||||
// }
|
RegisterOrPair.Y -> assignRegisterByte(assign.target, CpuRegister.Y)
|
||||||
// asmgen.translateExpression(value)
|
RegisterOrPair.AX -> assignRegisterpairWord(assign.target, RegisterOrPair.AX)
|
||||||
// assignStackValue(assign.target)
|
RegisterOrPair.AY -> assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||||
// }
|
RegisterOrPair.XY -> assignRegisterpairWord(assign.target, RegisterOrPair.XY)
|
||||||
// }
|
else -> throw AssemblyError("should be just one register byte result value")
|
||||||
|
}
|
||||||
|
if(preserveStatusRegisterAfterCall)
|
||||||
|
asmgen.out(" plp\t; restore status flags from call")
|
||||||
|
} else {
|
||||||
|
// regular subroutine, return values are (for now) always done via the stack... TODO optimize this
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
if(assign.target.datatype in WordDatatypes && assign.source.datatype in ByteDatatypes)
|
||||||
|
asmgen.signExtendStackLsb(assign.source.datatype)
|
||||||
|
assignStackValue(assign.target)
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// everything else just evaluate via the stack.
|
// everything else just evaluate via the stack.
|
||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
|
if(assign.target.datatype in WordDatatypes && assign.source.datatype in ByteDatatypes)
|
||||||
|
asmgen.signExtendStackLsb(assign.source.datatype)
|
||||||
assignStackValue(assign.target)
|
assignStackValue(assign.target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,9 +170,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
val valueDt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
val valueDt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
when(value) {
|
when(value) {
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
if (valueDt == DataType.UBYTE || valueDt == DataType.BYTE) {
|
if(targetDt in WordDatatypes) {
|
||||||
if(targetDt in WordDatatypes) {
|
if(valueDt==DataType.UBYTE) {
|
||||||
assignVariableByteIntoWord(target, value, valueDt)
|
assignVariableUByteIntoWord(target, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(valueDt==DataType.BYTE) {
|
||||||
|
assignVariableByteIntoWord(target, value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,7 +207,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
is FunctionCall -> {}
|
is FunctionCall -> {}
|
||||||
else -> {
|
else -> {
|
||||||
// TODO optimize the others further?
|
// TODO optimize the others further?
|
||||||
println("warning: slow stack evaluation used for typecast: into $targetDt at ${value.position}")
|
if(this.asmgen.options.slowCodegenWarnings)
|
||||||
|
println("warning: slow stack evaluation used for typecast: into $targetDt at ${value.position}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,6 +240,18 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
jsr floats.pop_float
|
jsr floats.pop_float
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
|
DataType.STR -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${target.asmVarname}
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda #>${target.asmVarname}
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
inx
|
||||||
|
lda P8ESTACK_HI,x
|
||||||
|
tay
|
||||||
|
lda P8ESTACK_LO,x
|
||||||
|
jsr prog8_lib.strcpy""")
|
||||||
|
}
|
||||||
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,66 +260,60 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
storeByteViaRegisterAInMemoryAddress("P8ESTACK_LO,x", target.memory!!)
|
storeByteViaRegisterAInMemoryAddress("P8ESTACK_LO,x", target.memory!!)
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
val index = target.array!!.arrayspec.index
|
if(target.constArrayIndexValue!=null) {
|
||||||
when {
|
val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize()
|
||||||
target.constArrayIndexValue!=null -> {
|
when(target.datatype) {
|
||||||
val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize()
|
in ByteDatatypes -> {
|
||||||
when(target.datatype) {
|
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx")
|
||||||
in ByteDatatypes -> {
|
|
||||||
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx")
|
|
||||||
}
|
|
||||||
in WordDatatypes -> {
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda P8ESTACK_LO,x
|
|
||||||
sta ${target.asmVarname}+$scaledIdx
|
|
||||||
lda P8ESTACK_HI,x
|
|
||||||
sta ${target.asmVarname}+$scaledIdx+1
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<${target.asmVarname}+$scaledIdx
|
|
||||||
ldy #>${target.asmVarname}+$scaledIdx
|
|
||||||
jsr floats.pop_float
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
|
||||||
}
|
}
|
||||||
}
|
in WordDatatypes -> {
|
||||||
index is IdentifierReference -> {
|
asmgen.out("""
|
||||||
when(target.datatype) {
|
inx
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
lda P8ESTACK_LO,x
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
sta ${target.asmVarname}+$scaledIdx
|
||||||
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname},y")
|
lda P8ESTACK_HI,x
|
||||||
}
|
sta ${target.asmVarname}+$scaledIdx+1
|
||||||
DataType.UWORD, DataType.WORD -> {
|
""")
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda P8ESTACK_LO,x
|
|
||||||
sta ${target.asmVarname},y
|
|
||||||
lda P8ESTACK_HI,x
|
|
||||||
sta ${target.asmVarname}+1,y
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A)
|
|
||||||
asmgen.out("""
|
|
||||||
ldy #>${target.asmVarname}
|
|
||||||
clc
|
|
||||||
adc #<${target.asmVarname}
|
|
||||||
bcc +
|
|
||||||
iny
|
|
||||||
+ jsr floats.pop_float""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird dt")
|
|
||||||
}
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${target.asmVarname}+$scaledIdx
|
||||||
|
ldy #>${target.asmVarname}+$scaledIdx
|
||||||
|
jsr floats.pop_float
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
||||||
}
|
}
|
||||||
else -> {
|
}
|
||||||
asmgen.translateExpression(index)
|
else
|
||||||
asmgen.out(" inx | lda P8ESTACK_LO,x")
|
{
|
||||||
popAndWriteArrayvalueWithUnscaledIndexA(target.datatype, target.asmVarname)
|
target.array!!
|
||||||
|
when(target.datatype) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
||||||
|
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname},y")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda P8ESTACK_LO,x
|
||||||
|
sta ${target.asmVarname},y
|
||||||
|
lda P8ESTACK_HI,x
|
||||||
|
sta ${target.asmVarname}+1,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A)
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #>${target.asmVarname}
|
||||||
|
clc
|
||||||
|
adc #<${target.asmVarname}
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jsr floats.pop_float""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -319,9 +344,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignAddressOf(target: AsmAssignTarget, name: IdentifierReference) {
|
private fun assignAddressOf(target: AsmAssignTarget, sourceName: String) {
|
||||||
val sourceName = name.firstStructVarName(program.namespace) ?: asmgen.fixNameSymbols(name.nameInSource.joinToString("."))
|
|
||||||
|
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -346,19 +369,54 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
val srcname = asmgen.asmVariableName(name)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$srcname
|
lda #<$sourceName
|
||||||
sta P8ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
lda #>$srcname
|
lda #>$sourceName
|
||||||
sta P8ESTACK_HI,x
|
sta P8ESTACK_HI,x
|
||||||
dex""")
|
dex""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignVariableWord(target: AsmAssignTarget, variable: IdentifierReference) {
|
private fun assignVariableString(target: AsmAssignTarget, sourceName: String) {
|
||||||
val sourceName = asmgen.asmVariableName(variable)
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
when(target.datatype) {
|
||||||
|
DataType.UWORD -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$sourceName
|
||||||
|
sta ${target.asmVarname}
|
||||||
|
lda #>$sourceName
|
||||||
|
sta ${target.asmVarname}+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${target.asmVarname}
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda #>${target.asmVarname}
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda #<$sourceName
|
||||||
|
ldy #>$sourceName
|
||||||
|
jsr prog8_lib.strcpy""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("assign string to incompatible variable type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$sourceName
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
lda #>$sourceName+1
|
||||||
|
sta P8ESTACK_HI,x
|
||||||
|
dex""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("string-assign to weird target")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignVariableWord(target: AsmAssignTarget, sourceName: String) {
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -372,73 +430,66 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
throw AssemblyError("no asm gen for assign wordvar $sourceName to memory ${target.memory}")
|
throw AssemblyError("no asm gen for assign wordvar $sourceName to memory ${target.memory}")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
val index = target.array!!.arrayspec.index
|
target.array!!
|
||||||
when {
|
if(target.constArrayIndexValue!=null) {
|
||||||
target.constArrayIndexValue!=null -> {
|
val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize()
|
||||||
val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize()
|
when(target.datatype) {
|
||||||
when(target.datatype) {
|
in ByteDatatypes -> {
|
||||||
in ByteDatatypes -> {
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
|
||||||
}
|
|
||||||
in WordDatatypes -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $sourceName
|
|
||||||
sta ${target.asmVarname}+$scaledIdx
|
|
||||||
lda $sourceName+1
|
|
||||||
sta ${target.asmVarname}+$scaledIdx+1
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$sourceName
|
|
||||||
ldy #>$sourceName
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
sty P8ZP_SCRATCH_W1+1
|
|
||||||
lda #<${target.asmVarname}+$scaledIdx
|
|
||||||
ldy #>${target.asmVarname}+$scaledIdx
|
|
||||||
jsr floats.copy_float
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
|
||||||
}
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta ${target.asmVarname}+$scaledIdx
|
||||||
|
lda $sourceName+1
|
||||||
|
sta ${target.asmVarname}+$scaledIdx+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$sourceName
|
||||||
|
ldy #>$sourceName
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda #<${target.asmVarname}+$scaledIdx
|
||||||
|
ldy #>${target.asmVarname}+$scaledIdx
|
||||||
|
jsr floats.copy_float
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
||||||
}
|
}
|
||||||
index is IdentifierReference -> {
|
}
|
||||||
when(target.datatype) {
|
else
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
{
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
when(target.datatype) {
|
||||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
}
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
||||||
DataType.UWORD, DataType.WORD -> {
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
}
|
||||||
asmgen.out("""
|
DataType.UWORD, DataType.WORD -> {
|
||||||
lda $sourceName
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
||||||
sta ${target.asmVarname},y
|
asmgen.out("""
|
||||||
lda $sourceName+1
|
lda $sourceName
|
||||||
sta ${target.asmVarname}+1,y
|
sta ${target.asmVarname},y
|
||||||
""")
|
lda $sourceName+1
|
||||||
}
|
sta ${target.asmVarname}+1,y
|
||||||
DataType.FLOAT -> {
|
""")
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A)
|
}
|
||||||
asmgen.out("""
|
DataType.FLOAT -> {
|
||||||
ldy #<$sourceName
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A)
|
||||||
sty P8ZP_SCRATCH_W1
|
asmgen.out("""
|
||||||
ldy #>$sourceName
|
ldy #<$sourceName
|
||||||
sty P8ZP_SCRATCH_W1+1
|
sty P8ZP_SCRATCH_W1
|
||||||
ldy #>${target.asmVarname}
|
ldy #>$sourceName
|
||||||
clc
|
sty P8ZP_SCRATCH_W1+1
|
||||||
adc #<${target.asmVarname}
|
ldy #>${target.asmVarname}
|
||||||
bcc +
|
clc
|
||||||
iny
|
adc #<${target.asmVarname}
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
+ jsr floats.copy_float""")
|
+ jsr floats.copy_float""")
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird dt")
|
|
||||||
}
|
}
|
||||||
}
|
else -> throw AssemblyError("weird dt")
|
||||||
else -> {
|
|
||||||
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | lda $sourceName+1 | sta P8ESTACK_HI,x | dex")
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.out(" inx | lda P8ESTACK_LO,x")
|
|
||||||
popAndWriteArrayvalueWithUnscaledIndexA(target.datatype, target.asmVarname)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -461,8 +512,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignVariableFloat(target: AsmAssignTarget, variable: IdentifierReference) {
|
private fun assignVariableFloat(target: AsmAssignTarget, sourceName: String) {
|
||||||
val sourceName = asmgen.asmVariableName(variable)
|
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -479,16 +529,23 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
// TODO optimize this, but the situation doesn't occur very often
|
asmgen.out("""
|
||||||
// if(target.constArrayIndexValue!=null) {
|
lda #<$sourceName
|
||||||
// TODO("const index ${target.constArrayIndexValue}")
|
ldy #>$sourceName
|
||||||
// } else if(target.array!!.arrayspec.index is IdentifierReference) {
|
sta P8ZP_SCRATCH_W1
|
||||||
// TODO("array[var] ${target.constArrayIndexValue}")
|
sty P8ZP_SCRATCH_W1+1
|
||||||
// }
|
lda #<${target.asmVarname}
|
||||||
val index = target.array!!.arrayspec.index
|
ldy #>${target.asmVarname}
|
||||||
asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr floats.push_float")
|
sta P8ZP_SCRATCH_W2
|
||||||
asmgen.translateExpression(index)
|
sty P8ZP_SCRATCH_W2+1""")
|
||||||
asmgen.out(" lda #<${target.asmVarname} | ldy #>${target.asmVarname} | jsr floats.pop_float_to_indexed_var")
|
if(target.array!!.indexer.indexNum!=null) {
|
||||||
|
val index = target.array.indexer.constIndex()!!
|
||||||
|
asmgen.out(" lda #$index")
|
||||||
|
} else {
|
||||||
|
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
|
||||||
|
asmgen.out(" lda $asmvarname")
|
||||||
|
}
|
||||||
|
asmgen.out(" jsr floats.set_array_float")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
|
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
|
||||||
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
|
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
|
||||||
@ -496,8 +553,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignVariableByte(target: AsmAssignTarget, variable: IdentifierReference) {
|
private fun assignVariableByte(target: AsmAssignTarget, sourceName: String) {
|
||||||
val sourceName = asmgen.asmVariableName(variable)
|
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -509,22 +565,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
storeByteViaRegisterAInMemoryAddress(sourceName, target.memory!!)
|
storeByteViaRegisterAInMemoryAddress(sourceName, target.memory!!)
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
val index = target.array!!.arrayspec.index
|
if (target.constArrayIndexValue!=null) {
|
||||||
when {
|
val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize()
|
||||||
target.constArrayIndexValue!=null -> {
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||||
val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize()
|
}
|
||||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
else {
|
||||||
}
|
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, target.datatype, CpuRegister.Y)
|
||||||
index is IdentifierReference -> {
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
|
||||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex")
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.out(" inx | lda P8ESTACK_LO,x")
|
|
||||||
popAndWriteArrayvalueWithUnscaledIndexA(target.datatype, target.asmVarname)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> {
|
||||||
@ -546,10 +593,69 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignVariableByteIntoWord(wordtarget: AsmAssignTarget, bytevar: IdentifierReference, valueDt: DataType) {
|
private fun assignVariableByteIntoWord(wordtarget: AsmAssignTarget, bytevar: IdentifierReference) {
|
||||||
if(valueDt == DataType.BYTE)
|
val sourceName = asmgen.asmVariableName(bytevar)
|
||||||
TODO("sign extend byte to word")
|
when (wordtarget.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta ${wordtarget.asmVarname}
|
||||||
|
ora #$7f
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
+ sta ${wordtarget.asmVarname}+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
// TODO optimize slow stack evaluation for this case, see assignVariableUByteIntoWord
|
||||||
|
if(this.asmgen.options.slowCodegenWarnings)
|
||||||
|
println("warning: slow stack evaluation used for sign-extend byte typecast at ${bytevar.position}")
|
||||||
|
asmgen.translateExpression(wordtarget.origAssign.source.expression!!)
|
||||||
|
assignStackValue(wordtarget)
|
||||||
|
}
|
||||||
|
TargetStorageKind.REGISTER -> {
|
||||||
|
when(wordtarget.register!!) {
|
||||||
|
RegisterOrPair.AX -> asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
pha
|
||||||
|
ora #$7f
|
||||||
|
bmi +
|
||||||
|
ldx #0
|
||||||
|
+ tax
|
||||||
|
pla""")
|
||||||
|
RegisterOrPair.AY -> asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
pha
|
||||||
|
ora #$7f
|
||||||
|
bmi +
|
||||||
|
ldy #0
|
||||||
|
+ tay
|
||||||
|
pla""")
|
||||||
|
RegisterOrPair.XY -> asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
tax
|
||||||
|
ora #$7f
|
||||||
|
bmi +
|
||||||
|
ldy #0
|
||||||
|
+ tay""")
|
||||||
|
else -> throw AssemblyError("only reg pairs are words")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
ora #$7f
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
+ sta P8ESTACK_HI,x
|
||||||
|
dex""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("target type isn't word")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignVariableUByteIntoWord(wordtarget: AsmAssignTarget, bytevar: IdentifierReference) {
|
||||||
val sourceName = asmgen.asmVariableName(bytevar)
|
val sourceName = asmgen.asmVariableName(bytevar)
|
||||||
when(wordtarget.kind) {
|
when(wordtarget.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
@ -561,22 +667,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
val index = wordtarget.array!!.arrayspec.index
|
if (wordtarget.constArrayIndexValue!=null) {
|
||||||
when {
|
val scaledIdx = wordtarget.constArrayIndexValue!! * 2
|
||||||
wordtarget.constArrayIndexValue!=null -> {
|
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx | lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
|
||||||
val scaledIdx = wordtarget.constArrayIndexValue!! * 2
|
}
|
||||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx | lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
|
else {
|
||||||
}
|
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array!!, wordtarget.datatype, CpuRegister.Y)
|
||||||
index is IdentifierReference -> {
|
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname},y | lda #0 | iny | sta ${wordtarget.asmVarname},y")
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array, wordtarget.datatype, CpuRegister.Y)
|
|
||||||
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname},y | lda #0 | iny | sta ${wordtarget.asmVarname},y")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | lda #0 | sta P8ESTACK_HI,x | dex")
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.out(" inx | lda P8ESTACK_LO,x")
|
|
||||||
popAndWriteArrayvalueWithUnscaledIndexA(wordtarget.datatype, wordtarget.asmVarname)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> {
|
TargetStorageKind.REGISTER -> {
|
||||||
@ -589,13 +686,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #$sourceName
|
lda $sourceName
|
||||||
sta P8ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
lda #0
|
lda #0
|
||||||
sta P8ESTACK_HI,x
|
sta P8ESTACK_HI,x
|
||||||
dex""")
|
dex""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("other types aren't word")
|
else -> throw AssemblyError("target type isn't word")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -609,40 +706,21 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
storeRegisterInMemoryAddress(register, target.memory!!)
|
storeRegisterInMemoryAddress(register, target.memory!!)
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
val index = target.array!!.arrayspec.index
|
when {
|
||||||
when (index) {
|
target.constArrayIndexValue!=null -> {
|
||||||
is NumericLiteralValue -> {
|
|
||||||
val memindex = index.number.toInt()
|
|
||||||
when (register) {
|
when (register) {
|
||||||
CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+$memindex")
|
CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||||
CpuRegister.X -> asmgen.out(" stx ${target.asmVarname}+$memindex")
|
CpuRegister.X -> asmgen.out(" stx ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||||
CpuRegister.Y -> asmgen.out(" sty ${target.asmVarname}+$memindex")
|
CpuRegister.Y -> asmgen.out(" sty ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
else -> {
|
||||||
when (register) {
|
when (register) {
|
||||||
CpuRegister.A -> {}
|
CpuRegister.A -> {}
|
||||||
CpuRegister.X -> asmgen.out(" txa")
|
CpuRegister.X -> asmgen.out(" txa")
|
||||||
CpuRegister.Y -> asmgen.out(" tya")
|
CpuRegister.Y -> asmgen.out(" tya")
|
||||||
}
|
}
|
||||||
asmgen.out(" ldy ${asmgen.asmVariableName(index)} | sta ${target.asmVarname},y")
|
asmgen.out(" ldy ${asmgen.asmVariableName(target.array!!.indexer.indexVar!!)} | sta ${target.asmVarname},y")
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.saveRegister(register)
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.restoreRegister(register)
|
|
||||||
when (register) {
|
|
||||||
CpuRegister.A -> asmgen.out(" sta P8ZP_SCRATCH_B1")
|
|
||||||
CpuRegister.X -> asmgen.out(" stx P8ZP_SCRATCH_B1")
|
|
||||||
CpuRegister.Y -> asmgen.out(" sty P8ZP_SCRATCH_B1")
|
|
||||||
}
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda P8ESTACK_LO,x
|
|
||||||
tay
|
|
||||||
lda P8ZP_SCRATCH_B1
|
|
||||||
sta ${target.asmVarname},y
|
|
||||||
""")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -684,6 +762,54 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun assignRegisterpairWord(target: AsmAssignTarget, regs: RegisterOrPair) {
|
||||||
|
require(target.datatype in WordDatatypes)
|
||||||
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
when(regs) {
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname} | stx ${target.asmVarname}+1")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname} | sty ${target.asmVarname}+1")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname} | sty ${target.asmVarname}+1")
|
||||||
|
else -> throw AssemblyError("expected reg pair")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
TODO("store register pair $regs into word-array ${target.array}")
|
||||||
|
}
|
||||||
|
TargetStorageKind.REGISTER -> {
|
||||||
|
when(regs) {
|
||||||
|
RegisterOrPair.AX -> when(target.register!!) {
|
||||||
|
RegisterOrPair.AY -> { asmgen.out(" stx P8ZP_SCRATCH_REG | ldy P8ZP_SCRATCH_REG") }
|
||||||
|
RegisterOrPair.AX -> { }
|
||||||
|
RegisterOrPair.XY -> { asmgen.out(" stx P8ZP_SCRATCH_REG | ldy P8ZP_SCRATCH_REG | tax") }
|
||||||
|
else -> throw AssemblyError("expected reg pair")
|
||||||
|
}
|
||||||
|
RegisterOrPair.AY -> when(target.register!!) {
|
||||||
|
RegisterOrPair.AY -> { }
|
||||||
|
RegisterOrPair.AX -> { asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG") }
|
||||||
|
RegisterOrPair.XY -> { asmgen.out(" tax") }
|
||||||
|
else -> throw AssemblyError("expected reg pair")
|
||||||
|
}
|
||||||
|
RegisterOrPair.XY -> when(target.register!!) {
|
||||||
|
RegisterOrPair.AY -> { asmgen.out(" txa") }
|
||||||
|
RegisterOrPair.AX -> { asmgen.out(" txa | sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG") }
|
||||||
|
RegisterOrPair.XY -> { }
|
||||||
|
else -> throw AssemblyError("expected reg pair")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("expected reg pair")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> {
|
||||||
|
when(regs) {
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
|
||||||
|
RegisterOrPair.AX, RegisterOrPair.XY -> throw AssemblyError("can't use X here")
|
||||||
|
else -> throw AssemblyError("expected reg pair")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> throw AssemblyError("can't store word into memory byte")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun assignConstantWord(target: AsmAssignTarget, word: Int) {
|
private fun assignConstantWord(target: AsmAssignTarget, word: Int) {
|
||||||
when(target.kind) {
|
when(target.kind) {
|
||||||
TargetStorageKind.VARIABLE -> {
|
TargetStorageKind.VARIABLE -> {
|
||||||
@ -707,19 +833,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
throw AssemblyError("no asm gen for assign word $word to memory ${target.memory}")
|
throw AssemblyError("no asm gen for assign word $word to memory ${target.memory}")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
// TODO optimize this, but the situation doesn't occur very often
|
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y)
|
||||||
// if(target.constArrayIndexValue!=null) {
|
|
||||||
// TODO("const index ${target.constArrayIndexValue}")
|
|
||||||
// } else if(target.array!!.arrayspec.index is IdentifierReference) {
|
|
||||||
// TODO("array[var] ${target.constArrayIndexValue}")
|
|
||||||
// }
|
|
||||||
val index = target.array!!.arrayspec.index
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
|
||||||
lda P8ESTACK_LO,x
|
|
||||||
asl a
|
|
||||||
tay
|
|
||||||
lda #<${word.toHex()}
|
lda #<${word.toHex()}
|
||||||
sta ${target.asmVarname},y
|
sta ${target.asmVarname},y
|
||||||
lda #>${word.toHex()}
|
lda #>${word.toHex()}
|
||||||
@ -754,25 +869,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
storeByteViaRegisterAInMemoryAddress("#${byte.toHex()}", target.memory!!)
|
storeByteViaRegisterAInMemoryAddress("#${byte.toHex()}", target.memory!!)
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
val index = target.array!!.arrayspec.index
|
if (target.constArrayIndexValue!=null) {
|
||||||
when {
|
val indexValue = target.constArrayIndexValue!!
|
||||||
target.constArrayIndexValue!=null -> {
|
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue")
|
||||||
val indexValue = target.constArrayIndexValue!!
|
}
|
||||||
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue")
|
else {
|
||||||
}
|
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
|
||||||
index is IdentifierReference -> {
|
asmgen.out(" lda #<${byte.toHex()} | sta ${target.asmVarname},y")
|
||||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
|
|
||||||
asmgen.out(" lda #<${byte.toHex()} | sta ${target.asmVarname},y")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
ldy P8ESTACK_LO,x
|
|
||||||
lda #${byte.toHex()}
|
|
||||||
sta ${target.asmVarname},y
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> when(target.register!!) {
|
TargetStorageKind.REGISTER -> when(target.register!!) {
|
||||||
@ -816,15 +919,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
// TODO optimize this, but the situation doesn't occur very often
|
if (target.array!!.indexer.indexNum!=null) {
|
||||||
// if(target.constArrayIndexValue!=null) {
|
val indexValue = target.array.indexer.constIndex()!! * DataType.FLOAT.memorySize()
|
||||||
// TODO("const index ${target.constArrayIndexValue}")
|
|
||||||
// } else if(target.array!!.arrayspec.index is IdentifierReference) {
|
|
||||||
// TODO("array[var] ${target.constArrayIndexValue}")
|
|
||||||
// }
|
|
||||||
val index = target.array!!.arrayspec.index
|
|
||||||
if (index is NumericLiteralValue) {
|
|
||||||
val indexValue = index.number.toInt() * DataType.FLOAT.memorySize()
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #0
|
lda #0
|
||||||
sta ${target.asmVarname}+$indexValue
|
sta ${target.asmVarname}+$indexValue
|
||||||
@ -834,12 +930,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
sta ${target.asmVarname}+$indexValue+4
|
sta ${target.asmVarname}+$indexValue+4
|
||||||
""")
|
""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.translateExpression(index)
|
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<${target.asmVarname}
|
lda #<${target.asmVarname}
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
lda #>${target.asmVarname}
|
lda #>${target.asmVarname}
|
||||||
sta P8ZP_SCRATCH_W1+1
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda $asmvarname
|
||||||
jsr floats.set_0_array_float
|
jsr floats.set_0_array_float
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
@ -870,16 +967,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
// TODO optimize this, but the situation doesn't occur very often
|
|
||||||
// if(target.constArrayIndexValue!=null) {
|
|
||||||
// TODO("const index ${target.constArrayIndexValue}")
|
|
||||||
// } else if(target.array!!.arrayspec.index is IdentifierReference) {
|
|
||||||
// TODO("array[var] ${target.constArrayIndexValue}")
|
|
||||||
// }
|
|
||||||
val index = target.array!!.arrayspec.index
|
|
||||||
val arrayVarName = target.asmVarname
|
val arrayVarName = target.asmVarname
|
||||||
if (index is NumericLiteralValue) {
|
if (target.array!!.indexer.indexNum!=null) {
|
||||||
val indexValue = index.number.toInt() * DataType.FLOAT.memorySize()
|
val indexValue = target.array.indexer.constIndex()!! * DataType.FLOAT.memorySize()
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $constFloat
|
lda $constFloat
|
||||||
sta $arrayVarName+$indexValue
|
sta $arrayVarName+$indexValue
|
||||||
@ -893,7 +983,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
sta $arrayVarName+$indexValue+4
|
sta $arrayVarName+$indexValue+4
|
||||||
""")
|
""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.translateExpression(index)
|
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<${constFloat}
|
lda #<${constFloat}
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
@ -903,6 +993,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
lda #>${arrayVarName}
|
lda #>${arrayVarName}
|
||||||
sta P8ZP_SCRATCH_W2+1
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
lda $asmvarname
|
||||||
jsr floats.set_array_float
|
jsr floats.set_array_float
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
@ -1047,6 +1138,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.storeByteIntoPointer(addressExpr, ldaInstructionArg)
|
asmgen.storeByteIntoPointer(addressExpr, ldaInstructionArg)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
asmgen.out(" lda $ldaInstructionArg | pha")
|
||||||
asmgen.translateExpression(addressExpr)
|
asmgen.translateExpression(addressExpr)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
@ -1054,8 +1146,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
sta P8ZP_SCRATCH_W2
|
sta P8ZP_SCRATCH_W2
|
||||||
lda P8ESTACK_HI,x
|
lda P8ESTACK_HI,x
|
||||||
sta P8ZP_SCRATCH_W2+1
|
sta P8ZP_SCRATCH_W2+1
|
||||||
lda $ldaInstructionArg
|
|
||||||
ldy #0
|
ldy #0
|
||||||
|
pla
|
||||||
sta (P8ZP_SCRATCH_W2),y""")
|
sta (P8ZP_SCRATCH_W2),y""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1079,9 +1171,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.storeByteIntoPointer(addressExpr, null)
|
asmgen.storeByteIntoPointer(addressExpr, null)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.saveRegister(register)
|
asmgen.saveRegister(register, false, memoryAddress.definingSubroutine())
|
||||||
asmgen.translateExpression(addressExpr)
|
asmgen.translateExpression(addressExpr)
|
||||||
asmgen.restoreRegister(CpuRegister.A)
|
asmgen.restoreRegister(CpuRegister.A, false)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
ldy P8ESTACK_LO,x
|
ldy P8ESTACK_LO,x
|
||||||
@ -1093,24 +1185,4 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun popAndWriteArrayvalueWithUnscaledIndexA(elementDt: DataType, asmArrayvarname: String) {
|
|
||||||
when (elementDt) {
|
|
||||||
in ByteDatatypes ->
|
|
||||||
asmgen.out(" tay | inx | lda P8ESTACK_LO,x | sta $asmArrayvarname,y")
|
|
||||||
in WordDatatypes ->
|
|
||||||
asmgen.out(" asl a | tay | inx | lda P8ESTACK_LO,x | sta $asmArrayvarname,y | lda P8ESTACK_HI,x | sta $asmArrayvarname+1,y")
|
|
||||||
DataType.FLOAT ->
|
|
||||||
// scaling * 5 is done in the subroutine that's called
|
|
||||||
asmgen.out("""
|
|
||||||
sta P8ESTACK_LO,x
|
|
||||||
dex
|
|
||||||
lda #<$asmArrayvarname
|
|
||||||
ldy #>$asmArrayvarname
|
|
||||||
jsr floats.pop_float_to_indexed_var
|
|
||||||
""")
|
|
||||||
else ->
|
|
||||||
throw AssemblyError("weird array type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@ package prog8.compiler.target.c64.codegen.assignment
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.CpuType
|
import prog8.compiler.target.CpuType
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
import prog8.compiler.target.c64.codegen.AsmGen
|
||||||
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
|
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
import kotlin.math.absoluteValue
|
|
||||||
|
|
||||||
internal class AugmentableAssignmentAsmGen(private val program: Program,
|
internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||||
private val assignmentAsmGen: AssignmentAsmGen,
|
private val assignmentAsmGen: AssignmentAsmGen,
|
||||||
@ -19,8 +19,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
require(assign.isAugmentable)
|
require(assign.isAugmentable)
|
||||||
require(assign.source.kind== SourceStorageKind.EXPRESSION)
|
require(assign.source.kind== SourceStorageKind.EXPRESSION)
|
||||||
|
|
||||||
val value = assign.source.expression!!
|
when (val value = assign.source.expression!!) {
|
||||||
when (value) {
|
|
||||||
is PrefixExpression -> {
|
is PrefixExpression -> {
|
||||||
// A = -A , A = +A, A = ~A, A = not A
|
// A = -A , A = +A, A = ~A, A = not A
|
||||||
val type = value.inferType(program).typeOrElse(DataType.STRUCT)
|
val type = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
@ -137,13 +136,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
when {
|
when {
|
||||||
valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble())
|
valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble(), target.scope)
|
||||||
ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident)
|
ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident, target.scope)
|
||||||
value is TypecastExpression -> {
|
value is TypecastExpression -> {
|
||||||
if (tryRemoveRedundantCast(value, target, operator)) return
|
if (tryRemoveRedundantCast(value, target, operator)) return
|
||||||
inplaceModification_float_value_to_variable(target.asmVarname, operator, value)
|
inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope)
|
||||||
}
|
}
|
||||||
else -> inplaceModification_float_value_to_variable(target.asmVarname, operator, value)
|
else -> inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
|
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
|
||||||
@ -180,7 +179,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
println("warning: slow stack evaluation used (1): ${memory.addressExpression::class.simpleName} at ${memory.addressExpression.position}") // TODO optimize...
|
if(asmgen.options.slowCodegenWarnings)
|
||||||
|
println("warning: slow stack evaluation used (1): ${memory.addressExpression::class.simpleName} at ${memory.addressExpression.position}") // TODO optimize...
|
||||||
asmgen.translateExpression(memory.addressExpression)
|
asmgen.translateExpression(memory.addressExpression)
|
||||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
|
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
|
||||||
val zp = CompilationTarget.instance.machine.zeropage
|
val zp = CompilationTarget.instance.machine.zeropage
|
||||||
@ -199,7 +199,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> {
|
TargetStorageKind.ARRAY -> {
|
||||||
println("*** TODO optimize simple inplace array assignment ${target.array} $operator= $value")
|
if(asmgen.options.slowCodegenWarnings)
|
||||||
|
println("*** TODO optimize simple inplace array assignment ${target.array} $operator= $value")
|
||||||
assignmentAsmGen.translateNormalAssignment(target.origAssign) // TODO get rid of this fallback for the most common cases here
|
assignmentAsmGen.translateNormalAssignment(target.origAssign) // TODO get rid of this fallback for the most common cases here
|
||||||
}
|
}
|
||||||
TargetStorageKind.REGISTER -> TODO("reg in-place modification")
|
TargetStorageKind.REGISTER -> TODO("reg in-place modification")
|
||||||
@ -220,125 +221,95 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_byte_value_to_memory(pointervar: IdentifierReference, operator: String, value: Expression) {
|
private fun inplaceModification_byte_value_to_memory(pointervar: IdentifierReference, operator: String, value: Expression) {
|
||||||
println("warning: slow stack evaluation used (3): @(${pointervar.nameInSource.last()}) $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
if(asmgen.options.slowCodegenWarnings)
|
||||||
|
println("warning: slow stack evaluation used (3): @(${pointervar.nameInSource.last()}) $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
|
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
// note: ** (power) operator requires floats.
|
// note: ** (power) operator requires floats.
|
||||||
"+" -> {
|
"+" -> asmgen.out(" clc | adc P8ESTACK_LO+1,x")
|
||||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
"-" -> asmgen.out(" sec | sbc P8ESTACK_LO+1,x")
|
||||||
asmgen.out(" clc | adc P8ESTACK_LO+1,x")
|
"*" -> asmgen.out(" pha | lda P8ESTACK_LO+1,x | tay | pla | jsr math.multiply_bytes | ldy #0")
|
||||||
if(ptrOnZp)
|
"/" -> asmgen.out(" pha | lda P8ESTACK_LO+1,x | tay | pla | jsr math.divmod_ub_asm | tya | ldy #0")
|
||||||
asmgen.out(" sta ($sourceName),y")
|
"%" -> asmgen.out(" pha | lda P8ESTACK_LO+1,x | tay | pla | jsr math.divmod_ub_asm | ldy #0")
|
||||||
else
|
"<<" -> {
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
asmgen.out("""
|
||||||
|
pha
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
bne +
|
||||||
|
pla
|
||||||
|
rts
|
||||||
|
+ tay
|
||||||
|
pla
|
||||||
|
- asl a
|
||||||
|
dey
|
||||||
|
bne -
|
||||||
|
+""")
|
||||||
}
|
}
|
||||||
"-" -> {
|
">>" -> {
|
||||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
asmgen.out("""
|
||||||
asmgen.out(" sec | sbc P8ESTACK_LO+1,x")
|
pha
|
||||||
if(ptrOnZp)
|
lda P8ESTACK_LO+1,x
|
||||||
asmgen.out(" sta ($sourceName),y")
|
bne +
|
||||||
else
|
pla
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
rts
|
||||||
}
|
+ tay
|
||||||
"*" -> {
|
pla
|
||||||
TODO("mul mem byte")// asmgen.out(" jsr prog8_lib.mul_byte")
|
- lsr a
|
||||||
}
|
dey
|
||||||
"/" -> TODO("div mem byte")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
|
bne -
|
||||||
"%" -> {
|
+""")
|
||||||
TODO("mem byte remainder")
|
|
||||||
// if(types==DataType.BYTE)
|
|
||||||
// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
|
||||||
// asmgen.out(" jsr prog8_lib.remainder_ub")
|
|
||||||
}
|
|
||||||
"<<" -> TODO("mem ubyte asl")
|
|
||||||
">>" -> TODO("mem ubyte lsr")
|
|
||||||
"&" -> {
|
|
||||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
|
||||||
asmgen.out(" and P8ESTACK_LO+1,x")
|
|
||||||
if(ptrOnZp)
|
|
||||||
asmgen.out(" sta ($sourceName),y")
|
|
||||||
else
|
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
|
||||||
}
|
|
||||||
"^" -> {
|
|
||||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
|
||||||
asmgen.out(" eor P8ESTACK_LO+1,x")
|
|
||||||
if(ptrOnZp)
|
|
||||||
asmgen.out(" sta ($sourceName),y")
|
|
||||||
else
|
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
|
||||||
}
|
|
||||||
"|" -> {
|
|
||||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
|
||||||
asmgen.out(" ora P8ESTACK_LO+1,x")
|
|
||||||
if(ptrOnZp)
|
|
||||||
asmgen.out(" sta ($sourceName),y")
|
|
||||||
else
|
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
|
||||||
}
|
}
|
||||||
|
"&" -> asmgen.out(" and P8ESTACK_LO+1,x")
|
||||||
|
"^" -> asmgen.out(" eor P8ESTACK_LO+1,x")
|
||||||
|
"|" -> asmgen.out(" ora P8ESTACK_LO+1,x")
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
|
if(ptrOnZp)
|
||||||
|
asmgen.out(" sta ($sourceName),y")
|
||||||
|
else
|
||||||
|
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||||
asmgen.out(" inx")
|
asmgen.out(" inx")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_byte_variable_to_memory(pointervar: IdentifierReference, operator: String, value: IdentifierReference) {
|
private fun inplaceModification_byte_variable_to_memory(pointervar: IdentifierReference, operator: String, value: IdentifierReference) {
|
||||||
val otherName = asmgen.asmVariableName(value)
|
val otherName = asmgen.asmVariableName(value)
|
||||||
|
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
|
|
||||||
when (operator) {
|
when (operator) {
|
||||||
// note: ** (power) operator requires floats.
|
// note: ** (power) operator requires floats.
|
||||||
"+" -> {
|
"+" -> asmgen.out(" clc | adc $otherName")
|
||||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
"-" -> asmgen.out(" sec | sbc $otherName")
|
||||||
asmgen.out(" clc | adc $otherName")
|
"*" -> asmgen.out(" ldy $otherName | jsr math.multiply_bytes | ldy #0")
|
||||||
if(ptrOnZp)
|
"/" -> asmgen.out(" ldy $otherName | jsr math.divmod_ub_asm | tya | ldy #0")
|
||||||
asmgen.out(" sta ($sourceName),y")
|
"%" -> asmgen.out(" ldy $otherName | jsr math.divmod_ub_asm | ldy #0")
|
||||||
else
|
"<<" -> {
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
asmgen.out("""
|
||||||
|
ldy $otherName
|
||||||
|
beq +
|
||||||
|
- asl a
|
||||||
|
dey
|
||||||
|
bne -
|
||||||
|
+""")
|
||||||
}
|
}
|
||||||
"-" -> {
|
">>" -> {
|
||||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
asmgen.out("""
|
||||||
asmgen.out(" sec | sbc $otherName")
|
ldy $otherName
|
||||||
if(ptrOnZp)
|
beq +
|
||||||
asmgen.out(" sta ($sourceName),y")
|
- lsr a
|
||||||
else
|
dey
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
bne -
|
||||||
}
|
+""")
|
||||||
"*" -> {
|
|
||||||
TODO("mem mul")// asmgen.out(" jsr prog8_lib.mul_byte")
|
|
||||||
}
|
|
||||||
"/" -> TODO("mem div")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
|
|
||||||
"%" -> {
|
|
||||||
TODO("mem byte remainder")
|
|
||||||
// if(types==DataType.BYTE)
|
|
||||||
// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
|
||||||
// asmgen.out(" jsr prog8_lib.remainder_ub")
|
|
||||||
}
|
|
||||||
"<<" -> TODO("mem ubyte asl")
|
|
||||||
">>" -> TODO("mem ubyte lsr")
|
|
||||||
"&" -> {
|
|
||||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
|
||||||
asmgen.out(" and $otherName")
|
|
||||||
if(ptrOnZp)
|
|
||||||
asmgen.out(" sta ($sourceName),y")
|
|
||||||
else
|
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
|
||||||
}
|
|
||||||
"^" -> {
|
|
||||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
|
||||||
asmgen.out(" eor $otherName")
|
|
||||||
if(ptrOnZp)
|
|
||||||
asmgen.out(" sta ($sourceName),y")
|
|
||||||
else
|
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
|
||||||
}
|
|
||||||
"|" -> {
|
|
||||||
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
|
||||||
asmgen.out(" ora $otherName")
|
|
||||||
if(ptrOnZp)
|
|
||||||
asmgen.out(" sta ($sourceName),y")
|
|
||||||
else
|
|
||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
|
||||||
}
|
}
|
||||||
|
"&" -> asmgen.out(" and $otherName")
|
||||||
|
"^" -> asmgen.out(" eor $otherName")
|
||||||
|
"|" -> asmgen.out(" ora $otherName")
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
|
if(ptrOnZp)
|
||||||
|
asmgen.out(" sta ($sourceName),y")
|
||||||
|
else
|
||||||
|
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_byte_litval_to_memory(pointervar: IdentifierReference, operator: String, value: Int) {
|
private fun inplaceModification_byte_litval_to_memory(pointervar: IdentifierReference, operator: String, value: Int) {
|
||||||
@ -361,26 +332,35 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
if(value in asmgen.optimizedByteMultiplications) {
|
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
TODO("optimized mem mul ubyte litval $value")
|
if(value in asmgen.optimizedByteMultiplications)
|
||||||
} else {
|
asmgen.out(" jsr math.mul_byte_${value}")
|
||||||
TODO("mem mul ubyte litval $value")
|
else
|
||||||
// asmgen.out(" jsr prog8_lib.mul_byte")
|
asmgen.out(" ldy #$value | jsr math.multiply_bytes | ldy #0")
|
||||||
}
|
if(ptrOnZp)
|
||||||
|
asmgen.out(" sta ($sourceName),y")
|
||||||
|
else
|
||||||
|
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||||
}
|
}
|
||||||
"/" -> {
|
"/" -> {
|
||||||
|
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
if(value==0)
|
if(value==0)
|
||||||
throw AssemblyError("division by zero")
|
throw AssemblyError("division by zero")
|
||||||
TODO("mem div byte litval")
|
asmgen.out(" ldy #$value | jsr math.divmod_ub_asm | tya | ldy #0")
|
||||||
// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
|
if(ptrOnZp)
|
||||||
|
asmgen.out(" sta ($sourceName),y")
|
||||||
|
else
|
||||||
|
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||||
}
|
}
|
||||||
"%" -> {
|
"%" -> {
|
||||||
|
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||||
if(value==0)
|
if(value==0)
|
||||||
throw AssemblyError("division by zero")
|
throw AssemblyError("division by zero")
|
||||||
TODO("mem byte remainder litval")
|
asmgen.out(" ldy #$value | jsr math.divmod_ub_asm | ldy #0")
|
||||||
// if(types==DataType.BYTE)
|
if(ptrOnZp)
|
||||||
// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
asmgen.out(" sta ($sourceName),y")
|
||||||
// asmgen.out(" jsr prog8_lib.remainder_ub")
|
else
|
||||||
|
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||||
}
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
if (value > 0) {
|
if (value > 0) {
|
||||||
@ -433,25 +413,24 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
private fun inplaceModification_byte_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) {
|
private fun inplaceModification_byte_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) {
|
||||||
// this should be the last resort for code generation for this,
|
// this should be the last resort for code generation for this,
|
||||||
// because the value is evaluated onto the eval stack (=slow).
|
// because the value is evaluated onto the eval stack (=slow).
|
||||||
println("warning: slow stack evaluation used (5): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
if(asmgen.options.slowCodegenWarnings)
|
||||||
|
println("warning: slow stack evaluation used (5): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
// note: ** (power) operator requires floats.
|
// note: ** (power) operator requires floats.
|
||||||
"+" -> asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name")
|
"+" -> asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name")
|
||||||
"-" -> asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name")
|
"-" -> asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name")
|
||||||
"*" -> {
|
"*" -> asmgen.out(" lda P8ESTACK_LO+1,x | ldy $name | jsr math.multiply_bytes | sta $name")
|
||||||
TODO("var mul byte expr")
|
|
||||||
// check optimizedByteMultiplications
|
|
||||||
// asmgen.out(" jsr prog8_lib.mul_byte")
|
|
||||||
}
|
|
||||||
"/" -> {
|
"/" -> {
|
||||||
TODO("var div byte expr")// asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.idiv_ub" else " jsr prog8_lib.idiv_b")
|
if(dt==DataType.UBYTE)
|
||||||
|
asmgen.out(" lda P8ESTACK_LO+1,x | tay | lda $name | jsr math.divmod_ub_asm | sty $name")
|
||||||
|
else
|
||||||
|
asmgen.out(" lda P8ESTACK_LO+1,x | tay | lda $name | jsr math.divmod_b_asm | sty $name")
|
||||||
}
|
}
|
||||||
"%" -> {
|
"%" -> {
|
||||||
TODO("var byte remainder expr")
|
if(dt==DataType.BYTE)
|
||||||
// if(types==DataType.BYTE)
|
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||||
// throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
asmgen.out(" lda P8ESTACK_LO+1,x | tay | lda $name | jsr math.divmod_ub_asm | sta $name")
|
||||||
// asmgen.out(" jsr prog8_lib.remainder_ub")
|
|
||||||
}
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
@ -519,25 +498,31 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"<<" -> {
|
"<<" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $otherName
|
ldy $otherName
|
||||||
|
beq +
|
||||||
- asl $name
|
- asl $name
|
||||||
dey
|
dey
|
||||||
bne -""")
|
bne -
|
||||||
|
+""")
|
||||||
}
|
}
|
||||||
">>" -> {
|
">>" -> {
|
||||||
if(dt==DataType.UBYTE) {
|
if(dt==DataType.UBYTE) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $otherName
|
ldy $otherName
|
||||||
|
beq +
|
||||||
- lsr $name
|
- lsr $name
|
||||||
dey
|
dey
|
||||||
bne -""")
|
bne -
|
||||||
|
+""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $otherName
|
ldy $otherName
|
||||||
|
beq +
|
||||||
- lda $name
|
- lda $name
|
||||||
asl a
|
asl a
|
||||||
ror $name
|
ror $name
|
||||||
dey
|
dey
|
||||||
bne -""")
|
bne -
|
||||||
|
+""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&" -> asmgen.out(" lda $name | and $otherName | sta $name")
|
"&" -> asmgen.out(" lda $name | and $otherName | sta $name")
|
||||||
@ -571,33 +556,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"+" -> asmgen.out(" lda $name | clc | adc #$value | sta $name")
|
"+" -> asmgen.out(" lda $name | clc | adc #$value | sta $name")
|
||||||
"-" -> asmgen.out(" lda $name | sec | sbc #$value | sta $name")
|
"-" -> asmgen.out(" lda $name | sec | sbc #$value | sta $name")
|
||||||
"*" -> {
|
"*" -> {
|
||||||
if(dt == DataType.UBYTE) {
|
if(value in asmgen.optimizedByteMultiplications)
|
||||||
if(value in asmgen.optimizedByteMultiplications) {
|
asmgen.out(" lda $name | jsr math.mul_byte_$value | sta $name")
|
||||||
asmgen.out(" lda $name | jsr math.mul_byte_$value | sta $name")
|
else
|
||||||
} else {
|
asmgen.out(" lda $name | ldy #$value | jsr math.multiply_bytes | sta $name")
|
||||||
TODO("var mul ubyte litval $value")
|
|
||||||
// asmgen.out(" jsr prog8_lib.mul_byte")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(value.absoluteValue in asmgen.optimizedByteMultiplications) {
|
|
||||||
asmgen.out(" lda $name | jsr math.mul_byte_$value | sta $name")
|
|
||||||
} else {
|
|
||||||
TODO("var mul sbyte litval $value")
|
|
||||||
// asmgen.out(" jsr prog8_lib.mul_byte")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"/" -> {
|
"/" -> {
|
||||||
if (dt == DataType.UBYTE) {
|
if (dt == DataType.UBYTE)
|
||||||
asmgen.out("""
|
asmgen.out(" lda $name | ldy #$value | jsr math.divmod_ub_asm | sty $name")
|
||||||
lda $name
|
else
|
||||||
ldy #$value
|
asmgen.out(" lda $name | ldy #$value | jsr math.divmod_b_asm | sty $name")
|
||||||
jsr math.divmod_ub_asm
|
|
||||||
sty $name
|
|
||||||
""")
|
|
||||||
} else {
|
|
||||||
TODO("var BYTE div litval")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"%" -> {
|
"%" -> {
|
||||||
if(dt==DataType.BYTE)
|
if(dt==DataType.BYTE)
|
||||||
@ -609,14 +577,30 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta $name""")
|
sta $name""")
|
||||||
}
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
repeat(value) { asmgen.out(" asl $name") }
|
if(value>=8) asmgen.out(" lda #0 | sta $name")
|
||||||
|
else repeat(value) { asmgen.out(" asl $name") }
|
||||||
}
|
}
|
||||||
">>" -> {
|
">>" -> {
|
||||||
if(value>0) {
|
if(value>0) {
|
||||||
if (dt == DataType.UBYTE) {
|
if (dt == DataType.UBYTE) {
|
||||||
repeat(value) { asmgen.out(" lsr $name") }
|
if(value>=8) asmgen.out(" lda #0 | sta $name")
|
||||||
|
else repeat(value) { asmgen.out(" lsr $name") }
|
||||||
} else {
|
} else {
|
||||||
repeat(value) { asmgen.out(" lda $name | asl a | ror $name") }
|
when {
|
||||||
|
value>=8 -> asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #-1
|
||||||
|
+ sta $name""")
|
||||||
|
value>3 -> asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
ldy #$value
|
||||||
|
jsr math.lsr_byte_A
|
||||||
|
sta $name""")
|
||||||
|
else -> repeat(value) { asmgen.out(" lda $name | asl a | ror $name") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -644,10 +628,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sec
|
sec
|
||||||
sbc P8ZP_SCRATCH_B1
|
sbc P8ZP_SCRATCH_B1
|
||||||
sta $name""")
|
sta $name""")
|
||||||
// TODO: more operators
|
// TODO: tuned code for more operators
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
inplaceModification_byte_value_to_variable(name, dt, operator, memread);
|
inplaceModification_byte_value_to_variable(name, dt, operator, memread)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -675,10 +659,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
bcc +
|
bcc +
|
||||||
dec $name+1
|
dec $name+1
|
||||||
+""")
|
+""")
|
||||||
// TODO: more operators
|
// TODO: tuned code for more operators
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
inplaceModification_word_value_to_variable(name, dt, operator, memread);
|
inplaceModification_word_value_to_variable(name, dt, operator, memread)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -686,7 +670,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
private fun inplaceModification_word_litval_to_variable(name: String, dt: DataType, operator: String, value: Int) {
|
private fun inplaceModification_word_litval_to_variable(name: String, dt: DataType, operator: String, value: Int) {
|
||||||
when (operator) {
|
when (operator) {
|
||||||
// note: ** (power) operator requires floats.
|
// note: ** (power) operator requires floats.
|
||||||
// TODO use these + and - optimizations in the expressionAsmGenerator as well.
|
|
||||||
"+" -> {
|
"+" -> {
|
||||||
when {
|
when {
|
||||||
value==0 -> {}
|
value==0 -> {}
|
||||||
@ -701,6 +684,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
value==0x0100 -> asmgen.out(" inc $name+1")
|
value==0x0100 -> asmgen.out(" inc $name+1")
|
||||||
value==0x0200 -> asmgen.out(" inc $name+1 | inc $name+1")
|
value==0x0200 -> asmgen.out(" inc $name+1 | inc $name+1")
|
||||||
value==0x0300 -> asmgen.out(" inc $name+1 | inc $name+1 | inc $name+1")
|
value==0x0300 -> asmgen.out(" inc $name+1 | inc $name+1 | inc $name+1")
|
||||||
|
value==0x0400 -> asmgen.out(" inc $name+1 | inc $name+1 | inc $name+1 | inc $name+1")
|
||||||
value and 255==0 -> asmgen.out(" lda $name+1 | clc | adc #>$value | sta $name+1")
|
value and 255==0 -> asmgen.out(" lda $name+1 | clc | adc #>$value | sta $name+1")
|
||||||
else -> asmgen.out("""
|
else -> asmgen.out("""
|
||||||
lda $name
|
lda $name
|
||||||
@ -726,6 +710,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
value==0x0100 -> asmgen.out(" dec $name+1")
|
value==0x0100 -> asmgen.out(" dec $name+1")
|
||||||
value==0x0200 -> asmgen.out(" dec $name+1 | dec $name+1")
|
value==0x0200 -> asmgen.out(" dec $name+1 | dec $name+1")
|
||||||
value==0x0300 -> asmgen.out(" dec $name+1 | dec $name+1 | dec $name+1")
|
value==0x0300 -> asmgen.out(" dec $name+1 | dec $name+1 | dec $name+1")
|
||||||
|
value==0x0400 -> asmgen.out(" dec $name+1 | dec $name+1 | dec $name+1 | dec $name+1")
|
||||||
value and 255==0 -> asmgen.out(" lda $name+1 | sec | sbc #>$value | sta $name+1")
|
value and 255==0 -> asmgen.out(" lda $name+1 | sec | sbc #>$value | sta $name+1")
|
||||||
else -> asmgen.out("""
|
else -> asmgen.out("""
|
||||||
lda $name
|
lda $name
|
||||||
@ -738,41 +723,22 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
if(dt == DataType.UWORD){
|
// the mul code works for both signed and unsigned
|
||||||
if(value in asmgen.optimizedWordMultiplications) {
|
if(value in asmgen.optimizedWordMultiplications) {
|
||||||
asmgen.out(" lda $name | ldy $name+1 | jsr math.mul_word_$value | sta $name | sty $name+1")
|
asmgen.out(" lda $name | ldy $name+1 | jsr math.mul_word_$value | sta $name | sty $name+1")
|
||||||
} else {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $name
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
lda $name+1
|
|
||||||
sta P8ZP_SCRATCH_W1+1
|
|
||||||
lda #<$value
|
|
||||||
ldy #>$value
|
|
||||||
jsr math.multiply_words
|
|
||||||
lda math.multiply_words.result
|
|
||||||
sta $name
|
|
||||||
lda math.multiply_words.result+1
|
|
||||||
sta $name+1""")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if(value.absoluteValue in asmgen.optimizedWordMultiplications) {
|
asmgen.out("""
|
||||||
asmgen.out(" lda $name | ldy $name+1 | jsr math.mul_word_$value | sta $name | sty $name+1")
|
lda $name
|
||||||
} else {
|
sta P8ZP_SCRATCH_W1
|
||||||
// TODO does this work for signed words? if so the uword/word distinction can be removed altogether
|
lda $name+1
|
||||||
asmgen.out("""
|
sta P8ZP_SCRATCH_W1+1
|
||||||
lda $name
|
lda #<$value
|
||||||
sta P8ZP_SCRATCH_W1
|
ldy #>$value
|
||||||
lda $name+1
|
jsr math.multiply_words
|
||||||
sta P8ZP_SCRATCH_W1+1
|
lda math.multiply_words.result
|
||||||
lda #<$value
|
sta $name
|
||||||
ldy #>$value
|
lda math.multiply_words.result+1
|
||||||
jsr math.multiply_words
|
sta $name+1""")
|
||||||
lda math.multiply_words.result
|
|
||||||
sta $name
|
|
||||||
lda math.multiply_words.result+1
|
|
||||||
sta $name+1""")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"/" -> {
|
"/" -> {
|
||||||
@ -825,14 +791,63 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
repeat(value) { asmgen.out(" asl $name | rol $name+1") }
|
when {
|
||||||
|
value>=16 -> asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||||
|
value==8 -> asmgen.out(" lda $name | sta $name+1 | lda #0 | sta $name")
|
||||||
|
value>2 -> asmgen.out("""
|
||||||
|
ldy #$value
|
||||||
|
- asl $name
|
||||||
|
rol $name+1
|
||||||
|
dey
|
||||||
|
bne -
|
||||||
|
""")
|
||||||
|
else -> repeat(value) { asmgen.out(" asl $name | rol $name+1") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
">>" -> {
|
">>" -> {
|
||||||
if (value > 0) {
|
if (value > 0) {
|
||||||
if(dt==DataType.UWORD) {
|
if(dt==DataType.UWORD) {
|
||||||
repeat(value) { asmgen.out(" lsr $name+1 | ror $name")}
|
when {
|
||||||
|
value>=16 -> asmgen.out(" lda #0 | sta $name | sta $name+1")
|
||||||
|
value==8 -> asmgen.out(" lda $name+1 | sta $name | lda #0 | sta $name+1")
|
||||||
|
value>2 -> asmgen.out("""
|
||||||
|
ldy #$value
|
||||||
|
- lsr $name+1
|
||||||
|
ror $name
|
||||||
|
dey
|
||||||
|
bne -""")
|
||||||
|
else -> repeat(value) { asmgen.out(" lsr $name+1 | ror $name")}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
repeat(value) { asmgen.out(" lda $name+1 | asl a | ror $name+1 | ror $name") }
|
when {
|
||||||
|
value>=16 -> asmgen.out("""
|
||||||
|
lda $name+1
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
beq ++
|
||||||
|
+ lda #-1
|
||||||
|
+ sta $name
|
||||||
|
sta $name+1""")
|
||||||
|
value==8 -> asmgen.out("""
|
||||||
|
lda $name+1
|
||||||
|
sta $name
|
||||||
|
bmi +
|
||||||
|
lda #0
|
||||||
|
- sta $name+1
|
||||||
|
beq ++
|
||||||
|
+ lda #-1
|
||||||
|
sta $name+1
|
||||||
|
+""")
|
||||||
|
value>2 -> asmgen.out("""
|
||||||
|
ldy #$value
|
||||||
|
- lda $name+1
|
||||||
|
asl a
|
||||||
|
ror $name+1
|
||||||
|
ror $name
|
||||||
|
dey
|
||||||
|
bne -""")
|
||||||
|
else -> repeat(value) { asmgen.out(" lda $name+1 | asl a | ror $name+1 | ror $name") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -956,33 +971,43 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
"<<" -> {
|
"<<" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $otherName
|
ldy $otherName
|
||||||
|
beq +
|
||||||
- asl $name
|
- asl $name
|
||||||
rol $name+1
|
rol $name+1
|
||||||
dey
|
dey
|
||||||
bne -""")
|
bne -
|
||||||
|
+""")
|
||||||
}
|
}
|
||||||
">>" -> {
|
">>" -> {
|
||||||
if(dt==DataType.UWORD) {
|
if(dt==DataType.UWORD) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $otherName
|
ldy $otherName
|
||||||
|
beq +
|
||||||
- lsr $name+1
|
- lsr $name+1
|
||||||
ror $name
|
ror $name
|
||||||
dey
|
dey
|
||||||
bne -""")
|
bne -
|
||||||
|
+""")
|
||||||
} else {
|
} else {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy $otherName
|
ldy $otherName
|
||||||
|
beq +
|
||||||
- lda $name+1
|
- lda $name+1
|
||||||
asl a
|
asl a
|
||||||
ror $name+1
|
ror $name+1
|
||||||
ror $name
|
ror $name
|
||||||
dey
|
dey
|
||||||
bne -""")
|
bne -
|
||||||
|
+""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&" -> TODO("bitand (u)wordvar bytevar")
|
"&" -> {
|
||||||
"^" -> TODO("bitxor (u)wordvar bytevar")
|
asmgen.out(" lda $otherName | and $name | sta $name")
|
||||||
"|" -> TODO("bitor (u)wordvar bytevar")
|
if(dt in WordDatatypes)
|
||||||
|
asmgen.out(" lda #0 | sta $name+1")
|
||||||
|
}
|
||||||
|
"^" -> asmgen.out(" lda $otherName | eor $name | sta $name")
|
||||||
|
"|" -> asmgen.out(" lda $otherName | ora $name | sta $name")
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1068,10 +1093,73 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) {
|
private fun inplaceModification_word_value_to_variable(name: String, dt: DataType, operator: String, value: Expression) {
|
||||||
// this should be the last resort for code generation for this,
|
// this should be the last resort for code generation for this,
|
||||||
// because the value is evaluated onto the eval stack (=slow).
|
// because the value is evaluated onto the eval stack (=slow).
|
||||||
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
if(asmgen.options.slowCodegenWarnings)
|
||||||
|
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
val valueDt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
val valueDt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
|
||||||
|
fun multiplyWord() {
|
||||||
|
asmgen.out("""
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
ldy P8ESTACK_HI+1,x
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda $name
|
||||||
|
ldy $name+1
|
||||||
|
jsr math.multiply_words
|
||||||
|
lda math.multiply_words.result
|
||||||
|
sta $name
|
||||||
|
lda math.multiply_words.result+1
|
||||||
|
sta $name+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun divideWord() {
|
||||||
|
if (dt == DataType.WORD) {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
ldy $name+1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
ldy P8ESTACK_HI+1,x
|
||||||
|
jsr math.divmod_w_asm
|
||||||
|
sta $name
|
||||||
|
sty $name+1
|
||||||
|
""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
ldy $name+1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
ldy P8ESTACK_HI+1,x
|
||||||
|
jsr math.divmod_uw_asm
|
||||||
|
sta $name
|
||||||
|
sty $name+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remainderWord() {
|
||||||
|
if(dt==DataType.WORD)
|
||||||
|
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||||
|
asmgen.out("""
|
||||||
|
lda $name
|
||||||
|
ldy $name+1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda P8ESTACK_LO+1,x
|
||||||
|
ldy P8ESTACK_HI+1,x
|
||||||
|
jsr math.divmod_uw_asm
|
||||||
|
lda P8ZP_SCRATCH_W2
|
||||||
|
sta $name
|
||||||
|
lda P8ZP_SCRATCH_W2+1
|
||||||
|
sta $name+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
when(valueDt) {
|
when(valueDt) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
// the other variable is a BYTE type so optimize for that
|
// the other variable is a BYTE type so optimize for that
|
||||||
@ -1125,27 +1213,35 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sbc P8ZP_SCRATCH_B1
|
sbc P8ZP_SCRATCH_B1
|
||||||
sta $name+1""")
|
sta $name+1""")
|
||||||
}
|
}
|
||||||
"*" -> TODO("mul (u)word (u)byte")
|
"*" -> {
|
||||||
"/" -> TODO("div (u)word (u)byte")
|
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
|
||||||
"%" -> TODO("(u)word remainder (u)byte")
|
asmgen.signExtendStackLsb(valueDt)
|
||||||
|
multiplyWord()
|
||||||
|
}
|
||||||
|
"/" -> {
|
||||||
|
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
|
||||||
|
asmgen.signExtendStackLsb(valueDt)
|
||||||
|
divideWord()
|
||||||
|
}
|
||||||
|
"%" -> {
|
||||||
|
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
|
||||||
|
asmgen.signExtendStackLsb(valueDt)
|
||||||
|
remainderWord()
|
||||||
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
asmgen.translateExpression(value)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
ldy P8ESTACK_LO+1,x
|
||||||
ldy P8ESTACK_LO,x
|
beq +
|
||||||
beq +
|
- asl $name
|
||||||
- asl $name
|
rol $name+1
|
||||||
rol $name+1
|
dey
|
||||||
dey
|
bne -
|
||||||
bne -
|
|
||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
">>" -> {
|
">>" -> {
|
||||||
asmgen.translateExpression(value)
|
|
||||||
if(dt==DataType.UWORD) {
|
if(dt==DataType.UWORD) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
ldy P8ESTACK_LO+1,x
|
||||||
ldy P8ESTACK_LO,x
|
|
||||||
beq +
|
beq +
|
||||||
- lsr $name+1
|
- lsr $name+1
|
||||||
ror $name
|
ror $name
|
||||||
@ -1154,8 +1250,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
+""") }
|
+""") }
|
||||||
else {
|
else {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
ldy P8ESTACK_LO+1,x
|
||||||
ldy P8ESTACK_LO,x
|
|
||||||
beq +
|
beq +
|
||||||
- lda $name+1
|
- lda $name+1
|
||||||
asl a
|
asl a
|
||||||
@ -1166,9 +1261,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
+""")
|
+""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"&" -> TODO("bitand (u)word (u)byte")
|
"&" -> {
|
||||||
"^" -> TODO("bitxor (u)word (u)byte")
|
asmgen.out(" lda P8ESTACK_LO+1,x | and $name | sta $name")
|
||||||
"|" -> TODO("bitor (u)word (u)byte")
|
if(dt in WordDatatypes)
|
||||||
|
asmgen.out(" lda #0 | sta $name+1")
|
||||||
|
}
|
||||||
|
"^" -> asmgen.out(" lda P8ESTACK_LO+1,x | eor $name | sta $name")
|
||||||
|
"|" -> asmgen.out(" lda P8ESTACK_LO+1,x | ora $name | sta $name")
|
||||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1178,65 +1277,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
// note: ** (power) operator requires floats.
|
// note: ** (power) operator requires floats.
|
||||||
"+" -> asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name | lda $name+1 | adc P8ESTACK_HI+1,x | sta $name+1")
|
"+" -> asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name | lda $name+1 | adc P8ESTACK_HI+1,x | sta $name+1")
|
||||||
"-" -> asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name | lda $name+1 | sbc P8ESTACK_HI+1,x | sta $name+1")
|
"-" -> asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name | lda $name+1 | sbc P8ESTACK_HI+1,x | sta $name+1")
|
||||||
"*" -> {
|
"*" -> multiplyWord()
|
||||||
asmgen.out("""
|
"/" -> divideWord()
|
||||||
lda P8ESTACK_LO+1,x
|
"%" -> remainderWord()
|
||||||
ldy P8ESTACK_HI+1,x
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
sty P8ZP_SCRATCH_W1+1
|
|
||||||
lda $name
|
|
||||||
ldy $name+1
|
|
||||||
jsr math.multiply_words
|
|
||||||
lda math.multiply_words.result
|
|
||||||
sta $name
|
|
||||||
lda math.multiply_words.result+1
|
|
||||||
sta $name+1
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
"/" -> {
|
|
||||||
if (dt == DataType.WORD) {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $name
|
|
||||||
ldy $name+1
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
sty P8ZP_SCRATCH_W1+1
|
|
||||||
lda P8ESTACK_LO+1,x
|
|
||||||
ldy P8ESTACK_HI+1,x
|
|
||||||
jsr math.divmod_w_asm
|
|
||||||
sta $name
|
|
||||||
sty $name+1
|
|
||||||
""")
|
|
||||||
} else {
|
|
||||||
asmgen.out("""
|
|
||||||
lda $name
|
|
||||||
ldy $name+1
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
sty P8ZP_SCRATCH_W1+1
|
|
||||||
lda P8ESTACK_LO+1,x
|
|
||||||
ldy P8ESTACK_HI+1,x
|
|
||||||
jsr math.divmod_uw_asm
|
|
||||||
sta $name
|
|
||||||
sty $name+1
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"%" -> {
|
|
||||||
if(dt==DataType.WORD)
|
|
||||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
|
||||||
asmgen.out("""
|
|
||||||
lda $name
|
|
||||||
ldy $name+1
|
|
||||||
sta P8ZP_SCRATCH_W1
|
|
||||||
sty P8ZP_SCRATCH_W1+1
|
|
||||||
lda P8ESTACK_LO+1,x
|
|
||||||
ldy P8ESTACK_HI+1,x
|
|
||||||
jsr math.divmod_uw_asm
|
|
||||||
lda P8ZP_SCRATCH_W2
|
|
||||||
sta $name
|
|
||||||
lda P8ZP_SCRATCH_W2+1
|
|
||||||
sta $name+1
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
|
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
|
||||||
"&" -> asmgen.out(" lda $name | and P8ESTACK_LO+1,x | sta $name | lda $name+1 | and P8ESTACK_HI+1,x | sta $name+1")
|
"&" -> asmgen.out(" lda $name | and P8ESTACK_LO+1,x | sta $name | lda $name+1 | and P8ESTACK_HI+1,x | sta $name+1")
|
||||||
"^" -> asmgen.out(" lda $name | eor P8ESTACK_LO+1,x | sta $name | lda $name+1 | eor P8ESTACK_HI+1,x | sta $name+1")
|
"^" -> asmgen.out(" lda $name | eor P8ESTACK_LO+1,x | sta $name | lda $name+1 | eor P8ESTACK_HI+1,x | sta $name+1")
|
||||||
@ -1252,13 +1295,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out(" inx")
|
asmgen.out(" inx")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: Expression) {
|
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: Expression, scope: Subroutine?) {
|
||||||
// this should be the last resort for code generation for this,
|
// this should be the last resort for code generation for this,
|
||||||
// because the value is evaluated onto the eval stack (=slow).
|
// because the value is evaluated onto the eval stack (=slow).
|
||||||
println("warning: slow stack evaluation used (2): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
if(asmgen.options.slowCodegenWarnings)
|
||||||
|
println("warning: slow stack evaluation used (2): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
|
||||||
asmgen.translateExpression(value)
|
asmgen.translateExpression(value)
|
||||||
asmgen.out(" jsr floats.pop_float_fac1")
|
asmgen.out(" jsr floats.pop_float_fac1")
|
||||||
asmgen.saveRegister(CpuRegister.X)
|
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"**" -> {
|
"**" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1303,16 +1347,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr floats.MOVMF
|
jsr floats.MOVMF
|
||||||
""")
|
""")
|
||||||
asmgen.restoreRegister(CpuRegister.X)
|
asmgen.restoreRegister(CpuRegister.X, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference) {
|
private fun inplaceModification_float_variable_to_variable(name: String, operator: String, ident: IdentifierReference, scope: Subroutine?) {
|
||||||
val valueDt = ident.targetVarDecl(program.namespace)!!.datatype
|
val valueDt = ident.targetVarDecl(program.namespace)!!.datatype
|
||||||
if(valueDt != DataType.FLOAT)
|
if(valueDt != DataType.FLOAT)
|
||||||
throw AssemblyError("float variable expected")
|
throw AssemblyError("float variable expected")
|
||||||
|
|
||||||
val otherName = asmgen.asmVariableName(ident)
|
val otherName = asmgen.asmVariableName(ident)
|
||||||
asmgen.saveRegister(CpuRegister.X)
|
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"**" -> {
|
"**" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1372,12 +1416,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr floats.MOVMF
|
jsr floats.MOVMF
|
||||||
""")
|
""")
|
||||||
asmgen.restoreRegister(CpuRegister.X)
|
asmgen.restoreRegister(CpuRegister.X, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double) {
|
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: Subroutine?) {
|
||||||
val constValueName = asmgen.getFloatAsmConst(value)
|
val constValueName = asmgen.getFloatAsmConst(value)
|
||||||
asmgen.saveRegister(CpuRegister.X)
|
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"**" -> {
|
"**" -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -1444,7 +1488,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
ldy #>$name
|
ldy #>$name
|
||||||
jsr floats.MOVMF
|
jsr floats.MOVMF
|
||||||
""")
|
""")
|
||||||
asmgen.restoreRegister(CpuRegister.X)
|
asmgen.restoreRegister(CpuRegister.X, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceCast(target: AsmAssignTarget, cast: TypecastExpression, position: Position) {
|
private fun inplaceCast(target: AsmAssignTarget, cast: TypecastExpression, position: Position) {
|
||||||
@ -1537,7 +1581,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
println("warning: slow stack evaluation used (6): ${mem.addressExpression::class.simpleName} at ${mem.addressExpression.position}") // TODO
|
if(asmgen.options.slowCodegenWarnings)
|
||||||
|
println("warning: slow stack evaluation used (6): ${mem.addressExpression::class.simpleName} at ${mem.addressExpression.position}") // TODO
|
||||||
asmgen.translateExpression(mem.addressExpression)
|
asmgen.translateExpression(mem.addressExpression)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jsr prog8_lib.read_byte_from_address_on_stack
|
jsr prog8_lib.read_byte_from_address_on_stack
|
||||||
@ -1606,7 +1651,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
println("warning: slow stack evaluation used (7): ${memory.addressExpression::class.simpleName} at ${memory.addressExpression.position}") // TODO
|
if(asmgen.options.slowCodegenWarnings)
|
||||||
|
println("warning: slow stack evaluation used (7): ${memory.addressExpression::class.simpleName} at ${memory.addressExpression.position}") // TODO
|
||||||
asmgen.translateExpression(memory.addressExpression)
|
asmgen.translateExpression(memory.addressExpression)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
jsr prog8_lib.read_byte_from_address_on_stack
|
jsr prog8_lib.read_byte_from_address_on_stack
|
||||||
|
@ -101,7 +101,8 @@ val BuiltinFunctions = mapOf(
|
|||||||
"rightstr" to FSignature(false, listOf(
|
"rightstr" to FSignature(false, listOf(
|
||||||
FParam("source", IterableDatatypes + DataType.UWORD),
|
FParam("source", IterableDatatypes + DataType.UWORD),
|
||||||
FParam("target", IterableDatatypes + DataType.UWORD),
|
FParam("target", IterableDatatypes + DataType.UWORD),
|
||||||
FParam("length", setOf(DataType.UBYTE))), null)
|
FParam("length", setOf(DataType.UBYTE))), null),
|
||||||
|
"strcmp" to FSignature(false, listOf(FParam("s1", IterableDatatypes + DataType.UWORD), FParam("s2", IterableDatatypes + DataType.UWORD)), DataType.BYTE, null)
|
||||||
)
|
)
|
||||||
|
|
||||||
fun builtinMax(array: List<Number>): Number = array.maxByOrNull { it.toDouble() }!!
|
fun builtinMax(array: List<Number>): Number = array.maxByOrNull { it.toDouble() }!!
|
||||||
@ -285,9 +286,9 @@ private fun builtinStrlen(args: List<Expression>, position: Position, program: P
|
|||||||
return NumericLiteralValue.optimalInteger(argument.value.length, argument.position)
|
return NumericLiteralValue.optimalInteger(argument.value.length, argument.position)
|
||||||
val vardecl = (argument as IdentifierReference).targetVarDecl(program.namespace)
|
val vardecl = (argument as IdentifierReference).targetVarDecl(program.namespace)
|
||||||
if(vardecl!=null) {
|
if(vardecl!=null) {
|
||||||
if(vardecl.datatype!=DataType.STR)
|
if(vardecl.datatype!=DataType.STR && vardecl.datatype!=DataType.UWORD)
|
||||||
throw SyntaxError("strlen must have string argument", position)
|
throw SyntaxError("strlen must have string argument", position)
|
||||||
if(vardecl.autogeneratedDontRemove) {
|
if(vardecl.autogeneratedDontRemove && vardecl.value!=null) {
|
||||||
return NumericLiteralValue.optimalInteger((vardecl.value as StringLiteralValue).value.length, argument.position)
|
return NumericLiteralValue.optimalInteger((vardecl.value as StringLiteralValue).value.length, argument.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
80
compiler/src/prog8/optimizer/BinExprSplitter.kt
Normal file
80
compiler/src/prog8/optimizer/BinExprSplitter.kt
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package prog8.optimizer
|
||||||
|
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.processing.AstWalker
|
||||||
|
import prog8.ast.processing.IAstModification
|
||||||
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
|
||||||
|
|
||||||
|
internal class BinExprSplitter(private val program: Program) : AstWalker() {
|
||||||
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
|
// override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
// TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...:
|
||||||
|
// if(decl.type==VarDeclType.VAR ) {
|
||||||
|
// val binExpr = decl.value as? BinaryExpression
|
||||||
|
// if (binExpr != null && binExpr.operator in augmentAssignmentOperators) {
|
||||||
|
// // split into a vardecl with just the left expression, and an aug. assignment with the right expression.
|
||||||
|
// val augExpr = BinaryExpression(IdentifierReference(listOf(decl.name), decl.position), binExpr.operator, binExpr.right, binExpr.position)
|
||||||
|
// val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||||
|
// val assign = Assignment(target, augExpr, binExpr.position)
|
||||||
|
// println("SPLIT VARDECL $decl")
|
||||||
|
// return listOf(
|
||||||
|
// IAstModification.SetExpression({ decl.value = it }, binExpr.left, decl),
|
||||||
|
// IAstModification.InsertAfter(decl, assign, parent)
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return noModifications
|
||||||
|
// }
|
||||||
|
|
||||||
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
|
if (binExpr != null) {
|
||||||
|
/*
|
||||||
|
|
||||||
|
reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
|
||||||
|
by attempting to splitting it up into individual simple steps:
|
||||||
|
|
||||||
|
|
||||||
|
X = BinExpr X = LeftExpr
|
||||||
|
<operator> followed by
|
||||||
|
/ \ IF 'X' not used X = BinExpr
|
||||||
|
/ \ IN LEFTEXPR ==> <operator>
|
||||||
|
/ \ / \
|
||||||
|
LeftExpr. RightExpr. / \
|
||||||
|
/ \ / \ X RightExpr.
|
||||||
|
.. .. .. ..
|
||||||
|
|
||||||
|
*/
|
||||||
|
if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target, program.namespace)) {
|
||||||
|
if (!assignment.isAugmentable) {
|
||||||
|
val firstAssign = Assignment(assignment.target, binExpr.left, binExpr.left.position)
|
||||||
|
val targetExpr = assignment.target.toExpression()
|
||||||
|
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
|
||||||
|
return listOf(
|
||||||
|
IAstModification.InsertBefore(assignment, firstAssign, assignment.definingScope()),
|
||||||
|
IAstModification.ReplaceNode(assignment.value, augExpr, assignment))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO further unraveling of binary expression trees into flat statements.
|
||||||
|
// however this should probably be done in a more generic way to also service
|
||||||
|
// the expressiontrees that are not used in an assignment statement...
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSimpleTarget(target: AssignTarget, namespace: INameScope) =
|
||||||
|
if (target.identifier!=null || target.memoryAddress!=null || target.arrayindexed!=null)
|
||||||
|
target.isInRegularRAM(namespace)
|
||||||
|
else
|
||||||
|
false
|
||||||
|
|
||||||
|
}
|
@ -1,11 +1,10 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.*
|
||||||
import prog8.ast.Module
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.ErrorReporter
|
||||||
import prog8.ast.base.ParentSentinel
|
import prog8.ast.base.ParentSentinel
|
||||||
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.FunctionCall
|
import prog8.ast.expressions.FunctionCall
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
@ -25,7 +24,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
|
|
||||||
val imports = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val imports = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
val importedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
val importedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||||
val calls = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
val calls = mutableMapOf<Subroutine, List<Subroutine>>().withDefault { mutableListOf() }
|
||||||
val calledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
val calledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
||||||
|
|
||||||
// TODO add dataflow graph: what statements use what variables - can be used to eliminate unused vars
|
// TODO add dataflow graph: what statements use what variables - can be used to eliminate unused vars
|
||||||
@ -79,8 +78,10 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
importedBy[importedModule] = importedBy.getValue(importedModule).plus(thisModule)
|
importedBy[importedModule] = importedBy.getValue(importedModule).plus(thisModule)
|
||||||
} else if (directive.directive == "%asminclude") {
|
} else if (directive.directive == "%asminclude") {
|
||||||
val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source)
|
val asm = loadAsmIncludeFile(directive.args[0].str!!, thisModule.source)
|
||||||
val scope = directive.definingScope()
|
val scope = directive.definingSubroutine()
|
||||||
scanAssemblyCode(asm, directive, scope)
|
if(scope!=null) {
|
||||||
|
scanAssemblyCode(asm, directive, scope)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.visit(directive)
|
super.visit(directive)
|
||||||
@ -167,12 +168,12 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
|
|
||||||
override fun visit(inlineAssembly: InlineAssembly) {
|
override fun visit(inlineAssembly: InlineAssembly) {
|
||||||
// parse inline asm for subroutine calls (jmp, jsr)
|
// parse inline asm for subroutine calls (jmp, jsr)
|
||||||
val scope = inlineAssembly.definingScope()
|
val scope = inlineAssembly.definingSubroutine()
|
||||||
scanAssemblyCode(inlineAssembly.assembly, inlineAssembly, scope)
|
scanAssemblyCode(inlineAssembly.assembly, inlineAssembly, scope)
|
||||||
super.visit(inlineAssembly)
|
super.visit(inlineAssembly)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scanAssemblyCode(asm: String, context: Statement, scope: INameScope) {
|
private fun scanAssemblyCode(asm: String, context: Statement, scope: Subroutine?) {
|
||||||
asm.lines().forEach { line ->
|
asm.lines().forEach { line ->
|
||||||
val matches = asmJumpRx.matchEntire(line)
|
val matches = asmJumpRx.matchEntire(line)
|
||||||
if (matches != null) {
|
if (matches != null) {
|
||||||
@ -180,13 +181,15 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
if (jumptarget != null && (jumptarget[0].isLetter() || jumptarget[0] == '_')) {
|
if (jumptarget != null && (jumptarget[0].isLetter() || jumptarget[0] == '_')) {
|
||||||
val node = program.namespace.lookup(jumptarget.split('.'), context)
|
val node = program.namespace.lookup(jumptarget.split('.'), context)
|
||||||
if (node is Subroutine) {
|
if (node is Subroutine) {
|
||||||
calls[scope] = calls.getValue(scope).plus(node)
|
if(scope!=null)
|
||||||
|
calls[scope] = calls.getValue(scope).plus(node)
|
||||||
calledBy[node] = calledBy.getValue(node).plus(context)
|
calledBy[node] = calledBy.getValue(node).plus(context)
|
||||||
} else if (jumptarget.contains('.')) {
|
} else if (jumptarget.contains('.')) {
|
||||||
// maybe only the first part already refers to a subroutine
|
// maybe only the first part already refers to a subroutine
|
||||||
val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context)
|
val node2 = program.namespace.lookup(listOf(jumptarget.substringBefore('.')), context)
|
||||||
if (node2 is Subroutine) {
|
if (node2 is Subroutine) {
|
||||||
calls[scope] = calls.getValue(scope).plus(node2)
|
if(scope!=null)
|
||||||
|
calls[scope] = calls.getValue(scope).plus(node2)
|
||||||
calledBy[node2] = calledBy.getValue(node2).plus(context)
|
calledBy[node2] = calledBy.getValue(node2).plus(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,7 +202,8 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
if (target.contains('.')) {
|
if (target.contains('.')) {
|
||||||
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
|
val node = program.namespace.lookup(listOf(target.substringBefore('.')), context)
|
||||||
if (node is Subroutine) {
|
if (node is Subroutine) {
|
||||||
calls[scope] = calls.getValue(scope).plus(node)
|
if(scope!=null)
|
||||||
|
calls[scope] = calls.getValue(scope).plus(node)
|
||||||
calledBy[node] = calledBy.getValue(node).plus(context)
|
calledBy[node] = calledBy.getValue(node).plus(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -208,4 +212,55 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun checkRecursiveCalls(errors: ErrorReporter) {
|
||||||
|
val cycles = recursionCycles()
|
||||||
|
if(cycles.any()) {
|
||||||
|
errors.warn("Program contains recursive subroutine calls. These only works in very specific limited scenarios!", Position.DUMMY)
|
||||||
|
val printed = mutableSetOf<Subroutine>()
|
||||||
|
for(chain in cycles) {
|
||||||
|
if(chain[0] !in printed) {
|
||||||
|
val chainStr = chain.joinToString(" <-- ") { "${it.name} at ${it.position}" }
|
||||||
|
errors.warn("Cycle in (a subroutine call in) $chainStr", Position.DUMMY)
|
||||||
|
printed.add(chain[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recursionCycles(): List<List<Subroutine>> {
|
||||||
|
val chains = mutableListOf<MutableList<Subroutine>>()
|
||||||
|
for(caller in calls.keys) {
|
||||||
|
val visited = calls.keys.associateWith { false }.toMutableMap()
|
||||||
|
val recStack = calls.keys.associateWith { false }.toMutableMap()
|
||||||
|
val chain = mutableListOf<Subroutine>()
|
||||||
|
if(hasCycle(caller, visited, recStack, chain))
|
||||||
|
chains.add(chain)
|
||||||
|
}
|
||||||
|
return chains
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasCycle(sub: Subroutine, visited: MutableMap<Subroutine, Boolean>, recStack: MutableMap<Subroutine, Boolean>, chain: MutableList<Subroutine>): Boolean {
|
||||||
|
// mark current node as visited and add to recursion stack
|
||||||
|
if(recStack[sub]==true)
|
||||||
|
return true
|
||||||
|
if(visited[sub]==true)
|
||||||
|
return false
|
||||||
|
|
||||||
|
// mark visited and add to recursion stack
|
||||||
|
visited[sub] = true
|
||||||
|
recStack[sub] = true
|
||||||
|
|
||||||
|
// recurse for all neighbours
|
||||||
|
for(called in calls.getValue(sub)) {
|
||||||
|
if(hasCycle(called, visited, recStack, chain)) {
|
||||||
|
chain.add(called)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pop from recursion stack
|
||||||
|
recStack[sub] = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import prog8.ast.statements.VarDecl
|
|||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
|
|
||||||
// Fix up the literal value's type to match that of the vardecl
|
// Fix up the literal value's type to match that of the vardecl
|
||||||
internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
|
internal class VarConstantValueTypeAdjuster(private val program: Program) : AstWalker() {
|
||||||
private val noModifications = emptyList<IAstModification>()
|
private val noModifications = emptyList<IAstModification>()
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
@ -58,15 +58,16 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
// TODO: use call graph for this?
|
// TODO: use call graph for this?
|
||||||
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.index?.referencesIdentifier(decl.name) == true) {
|
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexVar?.referencesIdentifier(decl.name) == true) {
|
||||||
errors.err("recursive var declaration", decl.position)
|
errors.err("recursive var declaration", decl.position)
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
if(decl.type== VarDeclType.CONST || decl.type== VarDeclType.VAR) {
|
if(decl.type== VarDeclType.CONST || decl.type== VarDeclType.VAR) {
|
||||||
if(decl.isArray){
|
if(decl.isArray){
|
||||||
if(decl.arraysize==null) {
|
val arraysize = decl.arraysize
|
||||||
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
|
if(arraysize==null) {
|
||||||
|
// for arrays that have no size specifier attempt to deduce the size
|
||||||
val arrayval = decl.value as? ArrayLiteralValue
|
val arrayval = decl.value as? ArrayLiteralValue
|
||||||
if(arrayval!=null) {
|
if(arrayval!=null) {
|
||||||
return listOf(IAstModification.SetExpression(
|
return listOf(IAstModification.SetExpression(
|
||||||
@ -75,14 +76,13 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
decl
|
decl
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
} else if(arraysize.constIndex()==null) {
|
||||||
else if(decl.arraysize?.constIndex()==null) {
|
// see if we can calculate the size from other fields
|
||||||
val size = decl.arraysize!!.index.constValue(program)
|
val cval = arraysize.indexVar?.constValue(program) ?: arraysize.origExpression?.constValue(program)
|
||||||
if(size!=null) {
|
if(cval!=null) {
|
||||||
return listOf(IAstModification.SetExpression(
|
arraysize.indexVar = null
|
||||||
{ decl.arraysize = ArrayIndex(it, decl.position) },
|
arraysize.origExpression = null
|
||||||
size, decl
|
arraysize.indexNum = cval
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.AstWalker
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.processing.IAstModification
|
import prog8.ast.processing.IAstModification
|
||||||
import prog8.ast.statements.AssignTarget
|
|
||||||
import prog8.ast.statements.Assignment
|
|
||||||
import prog8.ast.statements.VarDecl
|
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.log2
|
import kotlin.math.log2
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
@ -279,80 +275,6 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
|
||||||
// TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...:
|
|
||||||
// if(decl.type==VarDeclType.VAR ) {
|
|
||||||
// val binExpr = decl.value as? BinaryExpression
|
|
||||||
// if (binExpr != null && binExpr.operator in augmentAssignmentOperators) {
|
|
||||||
// // split into a vardecl with just the left expression, and an aug. assignment with the right expression.
|
|
||||||
// val augExpr = BinaryExpression(IdentifierReference(listOf(decl.name), decl.position), binExpr.operator, binExpr.right, binExpr.position)
|
|
||||||
// val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
|
||||||
// val assign = Assignment(target, augExpr, binExpr.position)
|
|
||||||
// println("SPLIT VARDECL $decl")
|
|
||||||
// return listOf(
|
|
||||||
// IAstModification.SetExpression({ decl.value = it }, binExpr.left, decl),
|
|
||||||
// IAstModification.InsertAfter(decl, assign, parent)
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return noModifications
|
|
||||||
// }
|
|
||||||
|
|
||||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
|
||||||
|
|
||||||
val binExpr = assignment.value as? BinaryExpression
|
|
||||||
if (binExpr != null) {
|
|
||||||
/*
|
|
||||||
|
|
||||||
reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
|
|
||||||
by attempting to splitting it up into individual simple steps:
|
|
||||||
|
|
||||||
|
|
||||||
X = BinExpr X = LeftExpr
|
|
||||||
<operator> followed by
|
|
||||||
/ \ IF 'X' not used X = BinExpr
|
|
||||||
/ \ IN LEFTEXPR ==> <operator>
|
|
||||||
/ \ / \
|
|
||||||
LeftExpr. RightExpr. / \
|
|
||||||
/ \ / \ X RightExpr.
|
|
||||||
.. .. .. ..
|
|
||||||
|
|
||||||
*/
|
|
||||||
if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target, program.namespace)) {
|
|
||||||
if (!assignment.isAugmentable) {
|
|
||||||
val firstAssign = Assignment(assignment.target, binExpr.left, binExpr.left.position)
|
|
||||||
val targetExpr = assignment.target.toExpression()
|
|
||||||
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
|
|
||||||
return listOf(
|
|
||||||
IAstModification.InsertBefore(assignment, firstAssign, parent),
|
|
||||||
IAstModification.ReplaceNode(assignment.value, augExpr, assignment))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO further unraveling of binary expression trees into flat statements.
|
|
||||||
// however this should probably be done in a more generic way to also service
|
|
||||||
// the expressiontrees that are not used in an assignment statement...
|
|
||||||
}
|
|
||||||
|
|
||||||
return noModifications
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isSimpleTarget(target: AssignTarget, namespace: INameScope): Boolean {
|
|
||||||
return when {
|
|
||||||
target.identifier!=null -> target.isInRegularRAM(namespace)
|
|
||||||
target.memoryAddress!=null -> target.isInRegularRAM(namespace)
|
|
||||||
target.arrayindexed!=null -> {
|
|
||||||
val index = target.arrayindexed!!.arrayspec.index
|
|
||||||
if(index is NumericLiteralValue)
|
|
||||||
target.isInRegularRAM(namespace)
|
|
||||||
else
|
|
||||||
false
|
|
||||||
}
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||||
if(functionCall.target.nameInSource == listOf("lsb")) {
|
if(functionCall.target.nameInSource == listOf("lsb")) {
|
||||||
val arg = functionCall.args[0]
|
val arg = functionCall.args[0]
|
||||||
@ -429,6 +351,13 @@ X = BinExpr X = LeftExpr
|
|||||||
}
|
}
|
||||||
// no need to check for left val constant (because of associativity)
|
// no need to check for left val constant (because of associativity)
|
||||||
|
|
||||||
|
val rnum = rightVal?.number?.toDouble()
|
||||||
|
if(rnum!=null && rnum<0.0) {
|
||||||
|
expr.operator = "-"
|
||||||
|
expr.right = NumericLiteralValue(rightVal.type, -rnum, rightVal.position)
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,12 +372,16 @@ X = BinExpr X = LeftExpr
|
|||||||
|
|
||||||
if (rightVal != null) {
|
if (rightVal != null) {
|
||||||
// right value is a constant, see if we can optimize
|
// right value is a constant, see if we can optimize
|
||||||
val rightConst: NumericLiteralValue = rightVal
|
val rnum = rightVal.number.toDouble()
|
||||||
when (rightConst.number.toDouble()) {
|
if (rnum == 0.0) {
|
||||||
0.0 -> {
|
// left
|
||||||
// left
|
return expr.left
|
||||||
return expr.left
|
}
|
||||||
}
|
|
||||||
|
if(rnum<0.0) {
|
||||||
|
expr.operator = "+"
|
||||||
|
expr.right = NumericLiteralValue(rightVal.type, -rnum, rightVal.position)
|
||||||
|
return expr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (leftVal != null) {
|
if (leftVal != null) {
|
||||||
@ -461,6 +394,7 @@ X = BinExpr X = LeftExpr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -683,6 +617,7 @@ X = BinExpr X = LeftExpr
|
|||||||
if (amount >= 16) {
|
if (amount >= 16) {
|
||||||
return NumericLiteralValue(targetDt, 0, expr.position)
|
return NumericLiteralValue(targetDt, 0, expr.position)
|
||||||
} else if (amount >= 8) {
|
} else if (amount >= 8) {
|
||||||
|
// TODO is this correct???
|
||||||
val lsb = TypecastExpression(expr.left, DataType.UBYTE, true, expr.position)
|
val lsb = TypecastExpression(expr.left, DataType.UBYTE, true, expr.position)
|
||||||
if (amount == 8) {
|
if (amount == 8) {
|
||||||
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteralValue.optimalInteger(0, expr.position)), expr.position)
|
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteralValue.optimalInteger(0, expr.position)), expr.position)
|
||||||
@ -722,8 +657,9 @@ X = BinExpr X = LeftExpr
|
|||||||
return NumericLiteralValue.optimalInteger(0, expr.position)
|
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||||
} else if (amount >= 8) {
|
} else if (amount >= 8) {
|
||||||
val msb = FunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
val msb = FunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||||
if (amount == 8)
|
if (amount == 8) {
|
||||||
return msb
|
return TypecastExpression(msb, DataType.UWORD, true, expr.position)
|
||||||
|
}
|
||||||
return BinaryExpression(msb, ">>", NumericLiteralValue.optimalInteger(amount - 8, expr.position), expr.position)
|
return BinaryExpression(msb, ">>", NumericLiteralValue.optimalInteger(amount - 8, expr.position), expr.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -731,14 +667,6 @@ X = BinExpr X = LeftExpr
|
|||||||
if (amount > 16) {
|
if (amount > 16) {
|
||||||
expr.right = NumericLiteralValue.optimalInteger(16, expr.right.position)
|
expr.right = NumericLiteralValue.optimalInteger(16, expr.right.position)
|
||||||
return null
|
return null
|
||||||
} else if (amount >= 8) {
|
|
||||||
val msbAsByte = TypecastExpression(
|
|
||||||
FunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position),
|
|
||||||
DataType.BYTE,
|
|
||||||
true, expr.position)
|
|
||||||
if (amount == 8)
|
|
||||||
return msbAsByte
|
|
||||||
return BinaryExpression(msbAsByte, ">>", NumericLiteralValue.optimalInteger(amount - 8, expr.position), expr.position)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -5,7 +5,7 @@ import prog8.ast.base.ErrorReporter
|
|||||||
|
|
||||||
|
|
||||||
internal fun Program.constantFold(errors: ErrorReporter) {
|
internal fun Program.constantFold(errors: ErrorReporter) {
|
||||||
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
|
val valuetypefixer = VarConstantValueTypeAdjuster(this)
|
||||||
valuetypefixer.visit(this)
|
valuetypefixer.visit(this)
|
||||||
if(errors.isEmpty()) {
|
if(errors.isEmpty()) {
|
||||||
valuetypefixer.applyModifications()
|
valuetypefixer.applyModifications()
|
||||||
@ -53,3 +53,9 @@ internal fun Program.simplifyExpressions() : Int {
|
|||||||
opti.visit(this)
|
opti.visit(this)
|
||||||
return opti.applyModifications()
|
return opti.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun Program.splitBinaryExpressions() : Int {
|
||||||
|
val opti = BinExprSplitter(this)
|
||||||
|
opti.visit(this)
|
||||||
|
return opti.applyModifications()
|
||||||
|
}
|
||||||
|
@ -25,12 +25,12 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
if("force_output" !in block.options()) {
|
if("force_output" !in block.options()) {
|
||||||
if (block.containsNoCodeNorVars()) {
|
if (block.containsNoCodeNorVars()) {
|
||||||
errors.warn("removing empty block '${block.name}'", block.position)
|
errors.warn("removing empty block '${block.name}'", block.position)
|
||||||
return listOf(IAstModification.Remove(block, parent))
|
return listOf(IAstModification.Remove(block, parent as INameScope))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block !in callgraph.usedSymbols) {
|
if (block !in callgraph.usedSymbols) {
|
||||||
errors.warn("removing unused block '${block.name}'", block.position)
|
errors.warn("removing unused block '${block.name}'", block.position)
|
||||||
return listOf(IAstModification.Remove(block, parent))
|
return listOf(IAstModification.Remove(block, parent as INameScope))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
@ -42,16 +42,16 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
if(subroutine.containsNoCodeNorVars()) {
|
if(subroutine.containsNoCodeNorVars()) {
|
||||||
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||||
val removals = callgraph.calledBy.getValue(subroutine).map {
|
val removals = callgraph.calledBy.getValue(subroutine).map {
|
||||||
IAstModification.Remove(it, it.parent)
|
IAstModification.Remove(it, it.definingScope())
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
removals += IAstModification.Remove(subroutine, parent)
|
removals += IAstModification.Remove(subroutine, subroutine.definingScope())
|
||||||
return removals
|
return removals
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
||||||
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||||
return listOf(IAstModification.Remove(subroutine, parent))
|
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
@ -63,7 +63,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
if(decl.type == VarDeclType.VAR)
|
if(decl.type == VarDeclType.VAR)
|
||||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||||
|
|
||||||
return listOf(IAstModification.Remove(decl, parent))
|
return listOf(IAstModification.Remove(decl, decl.definingScope()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
@ -74,7 +74,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
val functionName = functionCallStatement.target.nameInSource[0]
|
val functionName = functionCallStatement.target.nameInSource[0]
|
||||||
if (functionName in pureBuiltinFunctions) {
|
if (functionName in pureBuiltinFunctions) {
|
||||||
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
||||||
return listOf(IAstModification.Remove(functionCallStatement, parent))
|
return listOf(IAstModification.Remove(functionCallStatement, functionCallStatement.definingScope()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
if(subroutine!=null) {
|
if(subroutine!=null) {
|
||||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||||
if(first is Return)
|
if(first is Return)
|
||||||
return listOf(IAstModification.Remove(functionCallStatement, parent))
|
return listOf(IAstModification.Remove(functionCallStatement, functionCallStatement.definingScope()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
@ -150,7 +150,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
|
||||||
// remove empty if statements
|
// remove empty if statements
|
||||||
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars())
|
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars())
|
||||||
return listOf(IAstModification.Remove(ifStatement, parent))
|
return listOf(IAstModification.Remove(ifStatement, ifStatement.definingScope()))
|
||||||
|
|
||||||
// empty true part? switch with the else part
|
// empty true part? switch with the else part
|
||||||
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsCodeOrVars()) {
|
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsCodeOrVars()) {
|
||||||
@ -183,12 +183,12 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||||
if(forLoop.body.containsNoCodeNorVars()) {
|
if(forLoop.body.containsNoCodeNorVars()) {
|
||||||
errors.warn("removing empty for loop", forLoop.position)
|
errors.warn("removing empty for loop", forLoop.position)
|
||||||
return listOf(IAstModification.Remove(forLoop, parent))
|
return listOf(IAstModification.Remove(forLoop, forLoop.definingScope()))
|
||||||
} else if(forLoop.body.statements.size==1) {
|
} else if(forLoop.body.statements.size==1) {
|
||||||
val loopvar = forLoop.body.statements[0] as? VarDecl
|
val loopvar = forLoop.body.statements[0] as? VarDecl
|
||||||
if(loopvar!=null && loopvar.name==forLoop.loopVar.nameInSource.singleOrNull()) {
|
if(loopvar!=null && loopvar.name==forLoop.loopVar.nameInSource.singleOrNull()) {
|
||||||
// remove empty for loop (only loopvar decl in it)
|
// remove empty for loop (only loopvar decl in it)
|
||||||
return listOf(IAstModification.Remove(forLoop, parent))
|
return listOf(IAstModification.Remove(forLoop, forLoop.definingScope()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +265,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
} else {
|
} else {
|
||||||
// always false -> remove the while statement altogether
|
// always false -> remove the while statement altogether
|
||||||
errors.warn("condition is always false", whileLoop.condition.position)
|
errors.warn("condition is always false", whileLoop.condition.position)
|
||||||
listOf(IAstModification.Remove(whileLoop, parent))
|
listOf(IAstModification.Remove(whileLoop, whileLoop.definingScope()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
@ -276,12 +276,12 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
if(iter!=null) {
|
if(iter!=null) {
|
||||||
if(repeatLoop.body.containsNoCodeNorVars()) {
|
if(repeatLoop.body.containsNoCodeNorVars()) {
|
||||||
errors.warn("empty loop removed", repeatLoop.position)
|
errors.warn("empty loop removed", repeatLoop.position)
|
||||||
return listOf(IAstModification.Remove(repeatLoop, parent))
|
return listOf(IAstModification.Remove(repeatLoop, repeatLoop.definingScope()))
|
||||||
}
|
}
|
||||||
val iterations = iter.constValue(program)?.number?.toInt()
|
val iterations = iter.constValue(program)?.number?.toInt()
|
||||||
if (iterations == 0) {
|
if (iterations == 0) {
|
||||||
errors.warn("iterations is always 0, removed loop", iter.position)
|
errors.warn("iterations is always 0, removed loop", iter.position)
|
||||||
return listOf(IAstModification.Remove(repeatLoop, parent))
|
return listOf(IAstModification.Remove(repeatLoop, repeatLoop.definingScope()))
|
||||||
}
|
}
|
||||||
if (iterations == 1) {
|
if (iterations == 1) {
|
||||||
errors.warn("iterations is always 1", iter.position)
|
errors.warn("iterations is always 1", iter.position)
|
||||||
@ -308,7 +308,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
val scope = jump.definingScope()
|
val scope = jump.definingScope()
|
||||||
val label = jump.identifier?.targetStatement(scope)
|
val label = jump.identifier?.targetStatement(scope)
|
||||||
if(label!=null && scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1)
|
if(label!=null && scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1)
|
||||||
return listOf(IAstModification.Remove(jump, parent))
|
return listOf(IAstModification.Remove(jump, jump.definingScope()))
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
@ -341,7 +341,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
)
|
)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
||||||
IAstModification.InsertAfter(assignment, addConstant, parent))
|
IAstModification.InsertAfter(assignment, addConstant, assignment.definingScope()))
|
||||||
} else if (op2 == "-") {
|
} else if (op2 == "-") {
|
||||||
// A = A +/- B - N
|
// A = A +/- B - N
|
||||||
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
|
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
|
||||||
@ -352,7 +352,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
)
|
)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
||||||
IAstModification.InsertAfter(assignment, subConstant, parent))
|
IAstModification.InsertAfter(assignment, subConstant, assignment.definingScope()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -374,7 +374,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
if(assignment.target isSameAs assignment.value) {
|
if(assignment.target isSameAs assignment.value) {
|
||||||
// remove assignment to self
|
// remove assignment to self
|
||||||
return listOf(IAstModification.Remove(assignment, parent))
|
return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
||||||
}
|
}
|
||||||
|
|
||||||
val targetIDt = assignment.target.inferType(program, assignment)
|
val targetIDt = assignment.target.inferType(program, assignment)
|
||||||
@ -394,7 +394,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
when (bexpr.operator) {
|
when (bexpr.operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
if (rightCv == 0.0) {
|
if (rightCv == 0.0) {
|
||||||
return listOf(IAstModification.Remove(assignment, parent))
|
return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
||||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
||||||
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
||||||
@ -408,7 +408,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
if (rightCv == 0.0) {
|
if (rightCv == 0.0) {
|
||||||
return listOf(IAstModification.Remove(assignment, parent))
|
return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
||||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
||||||
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
||||||
@ -420,18 +420,18 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"*" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent))
|
"*" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
||||||
"/" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent))
|
"/" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
||||||
"**" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent))
|
"**" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
||||||
"|" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, parent))
|
"|" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
||||||
"^" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, parent))
|
"^" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
if (rightCv == 0.0)
|
if (rightCv == 0.0)
|
||||||
return listOf(IAstModification.Remove(assignment, parent))
|
return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
||||||
}
|
}
|
||||||
">>" -> {
|
">>" -> {
|
||||||
if (rightCv == 0.0)
|
if (rightCv == 0.0)
|
||||||
return listOf(IAstModification.Remove(assignment, parent))
|
return listOf(IAstModification.Remove(assignment, assignment.definingScope()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,20 +20,20 @@ internal class UnusedCodeRemover(private val program: Program, private val error
|
|||||||
program.modules.forEach {
|
program.modules.forEach {
|
||||||
callgraph.forAllSubroutines(it) { sub ->
|
callgraph.forAllSubroutines(it) { sub ->
|
||||||
if (sub !== entrypoint && !sub.isAsmSubroutine && (callgraph.calledBy[sub].isNullOrEmpty() || sub.containsNoCodeNorVars())) {
|
if (sub !== entrypoint && !sub.isAsmSubroutine && (callgraph.calledBy[sub].isNullOrEmpty() || sub.containsNoCodeNorVars())) {
|
||||||
removals.add(IAstModification.Remove(sub, sub.definingScope() as Node))
|
removals.add(IAstModification.Remove(sub, sub.definingScope()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
program.modules.flatMap { it.statements }.filterIsInstance<Block>().forEach { block ->
|
program.modules.flatMap { it.statements }.filterIsInstance<Block>().forEach { block ->
|
||||||
if (block.containsNoCodeNorVars() && "force_output" !in block.options())
|
if (block.containsNoCodeNorVars() && "force_output" !in block.options())
|
||||||
removals.add(IAstModification.Remove(block, block.definingScope() as Node))
|
removals.add(IAstModification.Remove(block, block.definingScope()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove modules that are not imported, or are empty (unless it's a library modules)
|
// remove modules that are not imported, or are empty (unless it's a library modules)
|
||||||
program.modules.forEach {
|
program.modules.forEach {
|
||||||
if (!it.isLibraryModule && (it.importedBy.isEmpty() || it.containsNoCodeNorVars()))
|
if (!it.isLibraryModule && (it.importedBy.isEmpty() || it.containsNoCodeNorVars()))
|
||||||
removals.add(IAstModification.Remove(it, it.parent))
|
removals.add(IAstModification.Remove(it, it.definingScope()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return removals
|
return removals
|
||||||
@ -91,8 +91,10 @@ internal class UnusedCodeRemover(private val program: Program, private val error
|
|||||||
val assign1 = stmtPairs[0] as? Assignment
|
val assign1 = stmtPairs[0] as? Assignment
|
||||||
val assign2 = stmtPairs[1] as? Assignment
|
val assign2 = stmtPairs[1] as? Assignment
|
||||||
if (assign1 != null && assign2 != null && !assign2.isAugmentable) {
|
if (assign1 != null && assign2 != null && !assign2.isAugmentable) {
|
||||||
if (assign1.target.isSameAs(assign2.target, program) && assign1.target.isInRegularRAM(program.namespace))
|
if (assign1.target.isSameAs(assign2.target, program) && assign1.target.isInRegularRAM(program.namespace)) {
|
||||||
linesToRemove.add(assign1)
|
if(assign2.target.identifier==null || !assign2.value.referencesIdentifier(*(assign2.target.identifier!!.nameInSource.toTypedArray())))
|
||||||
|
linesToRemove.add(assign1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ class TestC64Zeropage {
|
|||||||
@Test
|
@Test
|
||||||
fun testFreeSpaces() {
|
fun testFreeSpaces() {
|
||||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
||||||
assertEquals(16, zp1.available())
|
assertEquals(18, zp1.available())
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false))
|
||||||
assertEquals(89, zp2.available())
|
assertEquals(89, zp2.available())
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false))
|
||||||
@ -220,7 +220,7 @@ class TestC64Zeropage {
|
|||||||
@Test
|
@Test
|
||||||
fun testBasicsafeAllocation() {
|
fun testBasicsafeAllocation() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
||||||
assertEquals(16, zp.available())
|
assertEquals(18, zp.available())
|
||||||
|
|
||||||
assertFailsWith<ZeropageDepletedError> {
|
assertFailsWith<ZeropageDepletedError> {
|
||||||
// in regular zp there aren't 5 sequential bytes free
|
// in regular zp there aren't 5 sequential bytes free
|
||||||
@ -273,16 +273,18 @@ class TestC64Zeropage {
|
|||||||
@Test
|
@Test
|
||||||
fun testEfficientAllocation() {
|
fun testEfficientAllocation() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false))
|
||||||
assertEquals(16, zp.available())
|
assertEquals(18, zp.available())
|
||||||
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
|
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
|
||||||
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
|
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
|
||||||
assertEquals(0x0a, zp.allocate("", DataType.UBYTE, null, errors))
|
assertEquals(0x0a, zp.allocate("", DataType.UBYTE, null, errors))
|
||||||
assertEquals(0x94, zp.allocate("", DataType.UWORD, null, errors))
|
assertEquals(0x9b, zp.allocate("", DataType.UWORD, null, errors))
|
||||||
assertEquals(0xa7, zp.allocate("", DataType.UWORD, null, errors))
|
assertEquals(0x9e, zp.allocate("", DataType.UWORD, null, errors))
|
||||||
assertEquals(0xa9, zp.allocate("", DataType.UWORD, null, errors))
|
assertEquals(0xa5, zp.allocate("", DataType.UWORD, null, errors))
|
||||||
assertEquals(0xb5, zp.allocate("", DataType.UWORD, null, errors))
|
assertEquals(0xb0, zp.allocate("", DataType.UWORD, null, errors))
|
||||||
assertEquals(0xf7, zp.allocate("", DataType.UWORD, null, errors))
|
assertEquals(0xbe, zp.allocate("", DataType.UWORD, null, errors))
|
||||||
assertEquals(0x0e, zp.allocate("", DataType.UBYTE, null, errors))
|
assertEquals(0x0e, zp.allocate("", DataType.UBYTE, null, errors))
|
||||||
|
assertEquals(0x92, zp.allocate("", DataType.UBYTE, null, errors))
|
||||||
|
assertEquals(0x96, zp.allocate("", DataType.UBYTE, null, errors))
|
||||||
assertEquals(0xf9, zp.allocate("", DataType.UBYTE, null, errors))
|
assertEquals(0xf9, zp.allocate("", DataType.UBYTE, null, errors))
|
||||||
assertEquals(0, zp.available())
|
assertEquals(0, zp.available())
|
||||||
}
|
}
|
||||||
|
BIN
docs/source/_static/primes_cx16.png
Normal file
BIN
docs/source/_static/primes_cx16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@ -41,7 +41,7 @@ of that build task, you can start the compiler with:
|
|||||||
|
|
||||||
(You should probably make an alias...)
|
(You should probably make an alias...)
|
||||||
|
|
||||||
.. note::
|
.. hint::
|
||||||
Development and testing is done on Linux, but the compiler should run on most
|
Development and testing is done on Linux, but the compiler should run on most
|
||||||
operating systems. If you do have trouble building or running
|
operating systems. If you do have trouble building or running
|
||||||
the compiler on another operating system, please let me know!
|
the compiler on another operating system, please let me know!
|
||||||
|
@ -96,6 +96,12 @@ when compiled an ran on a C-64 you get this:
|
|||||||
:align: center
|
:align: center
|
||||||
:alt: result when run on C-64
|
:alt: result when run on C-64
|
||||||
|
|
||||||
|
when the exact same program is compiled for the Commander X16 target, and run on the emulator, you get this:
|
||||||
|
|
||||||
|
.. image:: _static/primes_cx16.png
|
||||||
|
:align: center
|
||||||
|
:alt: result when run on CX16 emulator
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Design principles and features
|
Design principles and features
|
||||||
@ -165,6 +171,7 @@ If you're targeting the CommanderX16, there's the `x16emu <https://github.com/co
|
|||||||
building.rst
|
building.rst
|
||||||
programming.rst
|
programming.rst
|
||||||
syntaxreference.rst
|
syntaxreference.rst
|
||||||
|
libraries.rst
|
||||||
todo.rst
|
todo.rst
|
||||||
|
|
||||||
|
|
||||||
|
94
docs/source/libraries.rst
Normal file
94
docs/source/libraries.rst
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
************************
|
||||||
|
Compiler library modules
|
||||||
|
************************
|
||||||
|
|
||||||
|
The compiler provides several "built-in" library modules with useful subroutine and variables.
|
||||||
|
|
||||||
|
Some of these may be specific for a certain compilation target, or work slightly different,
|
||||||
|
but some effort is put into making them available across compilation targets.
|
||||||
|
|
||||||
|
This means that as long as your program is only using the subroutines from these
|
||||||
|
libraries and not using hardware- and/or system dependent code, and isn't hardcoding certain
|
||||||
|
assumptions like the screen size, the exact same source program can
|
||||||
|
be compiled for multiple different target platforms. Many of the example programs that come
|
||||||
|
with Prog8 are written like this.
|
||||||
|
|
||||||
|
You can ``%import`` and use these modules explicitly, but the compiler may also import one or more
|
||||||
|
of these library modules automatically as required.
|
||||||
|
|
||||||
|
.. caution::
|
||||||
|
The resulting compiled binary program *only works on the target machine it was compiled for*.
|
||||||
|
You must recompile the program for every target you want to run it on.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
syslib
|
||||||
|
------
|
||||||
|
The "system library" for your target machine. It contains many system-specific definitions such
|
||||||
|
as ROM/kernal subroutine definitions, memory location constants, and utility subroutines.
|
||||||
|
|
||||||
|
Many of these definitions overlap for the C64 and Commander X16 targets so it is still possible
|
||||||
|
to write programs that work on both targets without modifications.
|
||||||
|
|
||||||
|
conv
|
||||||
|
----
|
||||||
|
Routines to convert strings to numbers or vice versa.
|
||||||
|
|
||||||
|
- numbers to strings, in various formats (binary, hex, decimal)
|
||||||
|
- strings in decimal, hex and binary format into numbers
|
||||||
|
|
||||||
|
|
||||||
|
textio (txt.*)
|
||||||
|
--------------
|
||||||
|
This will probably be the most used library module. It contains a whole lot of routines
|
||||||
|
dealing with text-based input and output (to the screen). Such as
|
||||||
|
|
||||||
|
- printing strings and numbers
|
||||||
|
- reading text input from the user via the keyboard
|
||||||
|
- filling or clearing the screen and colors
|
||||||
|
- scrolling the text on the screen
|
||||||
|
- placing individual characters on the screen
|
||||||
|
|
||||||
|
|
||||||
|
diskio
|
||||||
|
------
|
||||||
|
Provides several routines that deal with disk drive I/O, such as:
|
||||||
|
|
||||||
|
- show directory
|
||||||
|
- display disk drive status
|
||||||
|
- load and save data from and to the disk
|
||||||
|
- delete and rename files on the disk
|
||||||
|
|
||||||
|
|
||||||
|
floats
|
||||||
|
------
|
||||||
|
Provides definitions for the ROM/kernel subroutines and utility routines dealing with floating
|
||||||
|
point variables. This includes ``print_f``, the routine used to print floating point numbers.
|
||||||
|
|
||||||
|
|
||||||
|
graphics
|
||||||
|
--------
|
||||||
|
High-res monochrome bitmap graphics routines:
|
||||||
|
|
||||||
|
- clearing the screen
|
||||||
|
- drawing lines
|
||||||
|
- drawing circles and discs (filled circles)
|
||||||
|
- plotting individual pixels
|
||||||
|
|
||||||
|
|
||||||
|
math
|
||||||
|
----
|
||||||
|
Low level math routines. You should not normally have to bother with this directly.
|
||||||
|
The compiler needs it to implement most of the math operations in your programs.
|
||||||
|
|
||||||
|
|
||||||
|
cx16logo
|
||||||
|
--------
|
||||||
|
A 'fun' module that contains the Commander X16 logo and that allows you
|
||||||
|
to print it anywhere on the screen.
|
||||||
|
|
||||||
|
|
||||||
|
prog8_lib
|
||||||
|
---------
|
||||||
|
Low level language support. You should not normally have to bother with this directly.
|
||||||
|
The compiler needs it for verious built-in system routines.
|
@ -260,6 +260,12 @@ Note that the various keywords for the data type and variable type (``byte``, ``
|
|||||||
can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
|
can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
|
||||||
for instance.
|
for instance.
|
||||||
|
|
||||||
|
|
||||||
|
It's possible to assign a new array to another array, this will overwrite all elements in the original
|
||||||
|
array with those in the value array. The number and types of elements have to match.
|
||||||
|
For large arrays this is a slow operation because every element is copied over. It should probably be avoided.
|
||||||
|
|
||||||
|
|
||||||
**Arrays at a specific memory location:**
|
**Arrays at a specific memory location:**
|
||||||
Using the memory-mapped syntax it is possible to define an array to be located at a specific memory location.
|
Using the memory-mapped syntax it is possible to define an array to be located at a specific memory location.
|
||||||
For instance to reference the first 5 rows of the Commodore 64's screen matrix as an array, you can define::
|
For instance to reference the first 5 rows of the Commodore 64's screen matrix as an array, you can define::
|
||||||
@ -287,7 +293,7 @@ This @-prefix can also be used for character byte values.
|
|||||||
You can concatenate two string literals using '+' (not very useful though) or repeat
|
You can concatenate two string literals using '+' (not very useful though) or repeat
|
||||||
a string literal a given number of times using '*'. You can also assign a new string
|
a string literal a given number of times using '*'. You can also assign a new string
|
||||||
value to another string. No bounds check is done so be sure the destination string is
|
value to another string. No bounds check is done so be sure the destination string is
|
||||||
large enough to contain the new value::
|
large enough to contain the new value (it is overwritten in memory)::
|
||||||
|
|
||||||
str string1 = "first part" + "second part"
|
str string1 = "first part" + "second part"
|
||||||
str string2 = "hello!" * 10
|
str string2 = "hello!" * 10
|
||||||
@ -296,7 +302,13 @@ large enough to contain the new value::
|
|||||||
string1 = "new value"
|
string1 = "new value"
|
||||||
|
|
||||||
|
|
||||||
.. info::
|
There are several 'escape sequences' to help you put special characters into strings, such
|
||||||
|
as newlines, quote characters themselves, and so on. The ones used most often are
|
||||||
|
``\\``, ``\"``, ``\n``, ``\r``. For a detailed description of all of them and what they mean,
|
||||||
|
read the syntax reference on strings.
|
||||||
|
|
||||||
|
|
||||||
|
.. hint::
|
||||||
Strings and uwords (=memory address) can often be interchanged.
|
Strings and uwords (=memory address) can often be interchanged.
|
||||||
An array of strings is actually an array of uwords where every element is the memory
|
An array of strings is actually an array of uwords where every element is the memory
|
||||||
address of the string. You can pass a memory address to assembly functions
|
address of the string. You can pass a memory address to assembly functions
|
||||||
@ -361,13 +373,6 @@ address you specified, and setting the varible will directly modify that memory
|
|||||||
&word SCREENCOLORS = $d020 ; a 16-bit word at the addres $d020-$d021
|
&word SCREENCOLORS = $d020 ; a 16-bit word at the addres $d020-$d021
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
Directly accessing random memory locations is not yet supported without the
|
|
||||||
intermediate step of declaring a memory-mapped variable for the memory location.
|
|
||||||
The advantages of this however, is that it's clearer what the memory location
|
|
||||||
stands for, and the compiler also knows the data type.
|
|
||||||
|
|
||||||
|
|
||||||
Converting types into other types
|
Converting types into other types
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -645,23 +650,20 @@ There's a set of predefined functions in the language. These are fixed and can't
|
|||||||
You can use them in expressions and the compiler will evaluate them at compile-time if possible.
|
You can use them in expressions and the compiler will evaluate them at compile-time if possible.
|
||||||
|
|
||||||
|
|
||||||
sin(x)
|
Math
|
||||||
Sine. (floating point version)
|
^^^^
|
||||||
|
|
||||||
|
abs(x)
|
||||||
|
Absolute value.
|
||||||
|
|
||||||
|
atan(x)
|
||||||
|
Arctangent.
|
||||||
|
|
||||||
|
ceil(x)
|
||||||
|
Rounds the floating point up to an integer towards positive infinity.
|
||||||
|
|
||||||
cos(x)
|
cos(x)
|
||||||
Cosine. (floating point version)
|
Cosine. (floating point version)
|
||||||
|
|
||||||
sin8u(x)
|
|
||||||
Fast 8-bit ubyte sine of angle 0..255, result is in range 0..255
|
|
||||||
|
|
||||||
sin8(x)
|
|
||||||
Fast 8-bit byte sine of angle 0..255, result is in range -127..127
|
|
||||||
|
|
||||||
sin16u(x)
|
|
||||||
Fast 16-bit uword sine of angle 0..255, result is in range 0..65535
|
|
||||||
|
|
||||||
sin16(x)
|
|
||||||
Fast 16-bit word sine of angle 0..255, result is in range -32767..32767
|
|
||||||
|
|
||||||
cos8u(x)
|
cos8u(x)
|
||||||
Fast 8-bit ubyte cosine of angle 0..255, result is in range 0..255
|
Fast 8-bit ubyte cosine of angle 0..255, result is in range 0..255
|
||||||
@ -675,14 +677,11 @@ cos16u(x)
|
|||||||
cos16(x)
|
cos16(x)
|
||||||
Fast 16-bit word cosine of angle 0..255, result is in range -32767..32767
|
Fast 16-bit word cosine of angle 0..255, result is in range -32767..32767
|
||||||
|
|
||||||
abs(x)
|
deg(x)
|
||||||
Absolute value.
|
Radians to degrees.
|
||||||
|
|
||||||
tan(x)
|
floor (x)
|
||||||
Tangent.
|
Rounds the floating point down to an integer towards minus infinity.
|
||||||
|
|
||||||
atan(x)
|
|
||||||
Arctangent.
|
|
||||||
|
|
||||||
ln(x)
|
ln(x)
|
||||||
Natural logarithm (base e).
|
Natural logarithm (base e).
|
||||||
@ -690,45 +689,48 @@ ln(x)
|
|||||||
log2(x)
|
log2(x)
|
||||||
Base 2 logarithm.
|
Base 2 logarithm.
|
||||||
|
|
||||||
|
rad(x)
|
||||||
|
Degrees to radians.
|
||||||
|
|
||||||
|
round(x)
|
||||||
|
Rounds the floating point to the closest integer.
|
||||||
|
|
||||||
|
sin(x)
|
||||||
|
Sine. (floating point version)
|
||||||
|
|
||||||
|
sgn(x)
|
||||||
|
Get the sign of the value. Result is -1, 0 or 1 (negative, zero, positive).
|
||||||
|
|
||||||
|
sin8u(x)
|
||||||
|
Fast 8-bit ubyte sine of angle 0..255, result is in range 0..255
|
||||||
|
|
||||||
|
sin8(x)
|
||||||
|
Fast 8-bit byte sine of angle 0..255, result is in range -127..127
|
||||||
|
|
||||||
|
sin16u(x)
|
||||||
|
Fast 16-bit uword sine of angle 0..255, result is in range 0..65535
|
||||||
|
|
||||||
|
sin16(x)
|
||||||
|
Fast 16-bit word sine of angle 0..255, result is in range -32767..32767
|
||||||
|
|
||||||
sqrt16(w)
|
sqrt16(w)
|
||||||
16 bit unsigned integer Square root. Result is unsigned byte.
|
16 bit unsigned integer Square root. Result is unsigned byte.
|
||||||
|
|
||||||
sqrt(x)
|
sqrt(x)
|
||||||
Floating point Square root.
|
Floating point Square root.
|
||||||
|
|
||||||
round(x)
|
tan(x)
|
||||||
Rounds the floating point to the closest integer.
|
Tangent.
|
||||||
|
|
||||||
floor (x)
|
|
||||||
Rounds the floating point down to an integer towards minus infinity.
|
|
||||||
|
|
||||||
ceil(x)
|
Array operations
|
||||||
Rounds the floating point up to an integer towards positive infinity.
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
rad(x)
|
any(x)
|
||||||
Degrees to radians.
|
1 ('true') if any of the values in the array value x is 'true' (not zero), else 0 ('false')
|
||||||
|
|
||||||
deg(x)
|
all(x)
|
||||||
Radians to degrees.
|
1 ('true') if all of the values in the array value x are 'true' (not zero), else 0 ('false')
|
||||||
|
|
||||||
max(x)
|
|
||||||
Maximum of the values in the array value x
|
|
||||||
|
|
||||||
min(x)
|
|
||||||
Minimum of the values in the array value x
|
|
||||||
|
|
||||||
sum(x)
|
|
||||||
Sum of the values in the array value x
|
|
||||||
|
|
||||||
sort(array)
|
|
||||||
Sort the array in ascending order (in-place)
|
|
||||||
Note: sorting a floating-point array is not supported right now, as a general sorting routine for this will
|
|
||||||
be extremely slow. Either build one yourself or find another solution that doesn't require sorting
|
|
||||||
floating point values.
|
|
||||||
|
|
||||||
reverse(array)
|
|
||||||
Reverse the values in the array (in-place).
|
|
||||||
Can be used after sort() to sort an array in descending order.
|
|
||||||
|
|
||||||
len(x)
|
len(x)
|
||||||
Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte).
|
Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte).
|
||||||
@ -737,15 +739,80 @@ len(x)
|
|||||||
length of the string during execution, the value of len(string) may no longer be correct!
|
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)
|
(use strlen function if you want to dynamically determine the length)
|
||||||
|
|
||||||
sizeof(name)
|
max(x)
|
||||||
Number of bytes that the object 'name' occupies in memory. This is a constant determined by the data type of
|
Maximum of the values in the array value x
|
||||||
the object. For instance, for a variable of type uword, the sizeof is 2.
|
|
||||||
For an 10 element array of floats, it is 50 (on the C-64, where a float is 5 bytes).
|
min(x)
|
||||||
Note: usually you will be interested in the number of elements in an array, use len() for that.
|
Minimum of the values in the array value x
|
||||||
|
|
||||||
|
reverse(array)
|
||||||
|
Reverse the values in the array (in-place).
|
||||||
|
Can be used after sort() to sort an array in descending order.
|
||||||
|
|
||||||
|
sum(x)
|
||||||
|
Sum of the values in the array value x
|
||||||
|
|
||||||
|
sort(array)
|
||||||
|
Sort the array in ascending order (in-place)
|
||||||
|
Supported are arrays of bytes or word values.
|
||||||
|
Sorting a floating-point array is not supported right now, as a general sorting routine for this will
|
||||||
|
be extremely slow. Either build one yourself or find another solution that doesn't require sorting.
|
||||||
|
Finally, note that sorting an array with strings in it will not do what you might think;
|
||||||
|
it considers the array as just an array of integer words and sorts the string *pointers* accordingly.
|
||||||
|
Sorting strings alphabetically has to be programmed yourself if you need it.
|
||||||
|
|
||||||
|
|
||||||
|
Strings and memory blocks
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
memcopy(from, to, numbytes)
|
||||||
|
Efficiently copy a number of bytes (1 - 256) from a memory location to another.
|
||||||
|
NOTE: 'to' must NOT overlap with 'from', unless it is *before* 'from'.
|
||||||
|
Because this function imposes some overhead to handle the parameters,
|
||||||
|
it is only faster if the number of bytes is larger than a certain threshold.
|
||||||
|
Compare the generated code to see if it was beneficial or not.
|
||||||
|
The most efficient will always be to write a specialized copy routine in assembly yourself!
|
||||||
|
|
||||||
|
memset(address, numbytes, bytevalue)
|
||||||
|
Efficiently set a part of memory to the given (u)byte value.
|
||||||
|
But the most efficient will always be to write a specialized fill routine in assembly yourself!
|
||||||
|
Note that for clearing the character screen, very fast specialized subroutines are
|
||||||
|
available in the ``txt`` block (part of the ``textio`` module)
|
||||||
|
|
||||||
|
memsetw(address, numwords, wordvalue)
|
||||||
|
Efficiently set a part of memory to the given (u)word value.
|
||||||
|
But the most efficient will always be to write a specialized fill routine in assembly yourself!
|
||||||
|
|
||||||
|
leftstr(source, target, length)
|
||||||
|
Copies the left side of the source string of the given length to target string.
|
||||||
|
It is assumed the target string buffer is large enough to contain the result.
|
||||||
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
|
rightstr(source, target, length)
|
||||||
|
Copies the right side of the source string of the given length to target string.
|
||||||
|
It is assumed the target string buffer is large enough to contain the result.
|
||||||
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
strlen(str)
|
strlen(str)
|
||||||
Number of bytes in the string. This value is determined during runtime and counts upto
|
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.
|
the first terminating 0 byte in the string, regardless of the size of the string during compilation time.
|
||||||
|
Don't confuse this with ``len`` and ``sizeof``
|
||||||
|
|
||||||
|
strcmp(string1, string2)
|
||||||
|
Returns -1, 0 or 1 depeding on wether string1 sorts before, equal or after string2.
|
||||||
|
Note that you can also directly compare strings and string values with eachother
|
||||||
|
using ``==``, ``<`` etcetera (it will use strcmp for you under water automatically).
|
||||||
|
|
||||||
|
substr(source, target, start, length)
|
||||||
|
Copies a segment from the source string, starting at the given index,
|
||||||
|
and of the given length to target string.
|
||||||
|
It is assumed the target string buffer is large enough to contain the result.
|
||||||
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
|
Miscellaneous
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
exit(returncode)
|
||||||
|
Immediately stops the program and exits it, with the returncode in the A register.
|
||||||
|
Note: custom interrupt handlers remain active unless manually cleared first!
|
||||||
|
|
||||||
lsb(x)
|
lsb(x)
|
||||||
Get the least significant byte of the word x. Equivalent to the cast "x as ubyte".
|
Get the least significant byte of the word x. Equivalent to the cast "x as ubyte".
|
||||||
@ -753,19 +820,10 @@ lsb(x)
|
|||||||
msb(x)
|
msb(x)
|
||||||
Get the most significant byte of the word x.
|
Get the most significant byte of the word x.
|
||||||
|
|
||||||
sgn(x)
|
|
||||||
Get the sign of the value. Result is -1, 0 or 1 (negative, zero, positive).
|
|
||||||
|
|
||||||
mkword(msb, lsb)
|
mkword(msb, lsb)
|
||||||
Efficiently create a word value from two bytes (the msb and the lsb). Avoids multiplication and shifting.
|
Efficiently create a word value from two bytes (the msb and the lsb). Avoids multiplication and shifting.
|
||||||
So mkword($80, $22) results in $8022.
|
So mkword($80, $22) results in $8022.
|
||||||
|
|
||||||
any(x)
|
|
||||||
1 ('true') if any of the values in the array value x is 'true' (not zero), else 0 ('false')
|
|
||||||
|
|
||||||
all(x)
|
|
||||||
1 ('true') if all of the values in the array value x are 'true' (not zero), else 0 ('false')
|
|
||||||
|
|
||||||
rnd()
|
rnd()
|
||||||
returns a pseudo-random byte from 0..255
|
returns a pseudo-random byte from 0..255
|
||||||
|
|
||||||
@ -799,51 +857,6 @@ ror2(x)
|
|||||||
It uses some extra logic to not consider the carry flag as extra rotation bit.
|
It uses some extra logic to not consider the carry flag as extra rotation bit.
|
||||||
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||||
|
|
||||||
memcopy(from, to, numbytes)
|
|
||||||
Efficiently copy a number of bytes (1 - 256) from a memory location to another.
|
|
||||||
NOTE: 'to' must NOT overlap with 'from', unless it is *before* 'from'.
|
|
||||||
Because this function imposes some overhead to handle the parameters,
|
|
||||||
it is only faster if the number of bytes is larger than a certain threshold.
|
|
||||||
Compare the generated code to see if it was beneficial or not.
|
|
||||||
The most efficient will always be to write a specialized copy routine in assembly yourself!
|
|
||||||
|
|
||||||
memset(address, numbytes, bytevalue)
|
|
||||||
Efficiently set a part of memory to the given (u)byte value.
|
|
||||||
But the most efficient will always be to write a specialized fill routine in assembly yourself!
|
|
||||||
Note that for clearing the character screen, very fast specialized subroutines are
|
|
||||||
available in the ``txt`` block (part of the ``textio`` module)
|
|
||||||
|
|
||||||
memsetw(address, numwords, wordvalue)
|
|
||||||
Efficiently set a part of memory to the given (u)word value.
|
|
||||||
But the most efficient will always be to write a specialized fill routine in assembly yourself!
|
|
||||||
|
|
||||||
leftstr(source, target, length)
|
|
||||||
Copies the left side of the source string of the given length to target string.
|
|
||||||
It is assumed the target string buffer is large enough to contain the result.
|
|
||||||
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
|
||||||
|
|
||||||
rightstr(source, target, length)
|
|
||||||
Copies the right side of the source string of the given length to target string.
|
|
||||||
It is assumed the target string buffer is large enough to contain the result.
|
|
||||||
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
|
||||||
|
|
||||||
substr(source, target, start, length)
|
|
||||||
Copies a segment from the source string, starting at the given index,
|
|
||||||
and of the given length to target string.
|
|
||||||
It is assumed the target string buffer is large enough to contain the result.
|
|
||||||
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
|
||||||
|
|
||||||
swap(x, y)
|
|
||||||
Swap the values of numerical variables (or memory locations) x and y in a fast way.
|
|
||||||
|
|
||||||
set_carry() / clear_carry()
|
|
||||||
Set (or clear) the CPU status register Carry flag. No result value.
|
|
||||||
(translated into ``SEC`` or ``CLC`` cpu instruction)
|
|
||||||
|
|
||||||
set_irqd() / clear_irqd()
|
|
||||||
Set (or clear) the CPU status register Interrupt Disable flag. No result value.
|
|
||||||
(translated into ``SEI`` or ``CLI`` cpu instruction)
|
|
||||||
|
|
||||||
rsave()
|
rsave()
|
||||||
Saves the CPU registers and the status flags.
|
Saves the CPU registers and the status flags.
|
||||||
You can now more or less 'safely' use the registers directly, until you
|
You can now more or less 'safely' use the registers directly, until you
|
||||||
@ -858,10 +871,22 @@ rrestore()
|
|||||||
read_flags()
|
read_flags()
|
||||||
Returns the current value of the CPU status register.
|
Returns the current value of the CPU status register.
|
||||||
|
|
||||||
exit(returncode)
|
sizeof(name)
|
||||||
Immediately stops the program and exits it, with the returncode in the A register.
|
Number of bytes that the object 'name' occupies in memory. This is a constant determined by the data type of
|
||||||
Note: custom interrupt handlers remain active unless manually cleared first!
|
the object. For instance, for a variable of type uword, the sizeof is 2.
|
||||||
|
For an 10 element array of floats, it is 50 (on the C-64, where a float is 5 bytes).
|
||||||
|
Note: usually you will be interested in the number of elements in an array, use len() for that.
|
||||||
|
|
||||||
|
set_carry() / clear_carry()
|
||||||
|
Set (or clear) the CPU status register Carry flag. No result value.
|
||||||
|
(translated into ``SEC`` or ``CLC`` cpu instruction)
|
||||||
|
|
||||||
|
set_irqd() / clear_irqd()
|
||||||
|
Set (or clear) the CPU status register Interrupt Disable flag. No result value.
|
||||||
|
(translated into ``SEI`` or ``CLI`` cpu instruction)
|
||||||
|
|
||||||
|
swap(x, y)
|
||||||
|
Swap the values of numerical variables (or memory locations) x and y in a fast way.
|
||||||
|
|
||||||
|
|
||||||
Library routines
|
Library routines
|
||||||
|
@ -78,7 +78,7 @@ Directives
|
|||||||
- style ``full`` -- claim the whole ZP for variables for the program, overwriting everything,
|
- style ``full`` -- claim the whole ZP for variables for the program, overwriting everything,
|
||||||
except the few addresses mentioned above that are used by the system's IRQ routine.
|
except the few addresses mentioned above that are used by the system's IRQ routine.
|
||||||
Even though the default IRQ routine is still active, it is impossible to use most BASIC and KERNAL ROM routines.
|
Even though the default IRQ routine is still active, it is impossible to use most BASIC and KERNAL ROM routines.
|
||||||
This includes many floating point operations and several utility routines that do I/O, such as ``print_string``.
|
This includes many floating point operations and several utility routines that do I/O, such as ``print``.
|
||||||
This option makes programs smaller and faster because even more variables can
|
This option makes programs smaller and faster because even more variables can
|
||||||
be stored in the ZP (which allows for more efficient assembly code).
|
be stored in the ZP (which allows for more efficient assembly code).
|
||||||
It's not possible to return cleanly to BASIC when the program exits. The only choice is
|
It's not possible to return cleanly to BASIC when the program exits. The only choice is
|
||||||
@ -402,6 +402,24 @@ Struct variables can be assigned a struct literal value (also in their declarati
|
|||||||
Color rgb = [255, 100, 0] ; note that the value is an array
|
Color rgb = [255, 100, 0] ; note that the value is an array
|
||||||
|
|
||||||
|
|
||||||
|
String
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
``"hello"`` is a string translated into the default character encoding (PETSCII)
|
||||||
|
|
||||||
|
``@"hello"`` is a string translated into the alternate character encoding (Screencodes/pokes)
|
||||||
|
|
||||||
|
There are several escape sequences available to put special characters into your string value:
|
||||||
|
|
||||||
|
- ``\\`` - the backslash itself, has to be escaped because it is the escape symbol by itself
|
||||||
|
- ``\n`` - newline character (move cursor down and to beginning of next line)
|
||||||
|
- ``\r`` - carriage return character (more or less the same as newline if printing to the screen)
|
||||||
|
- ``\"`` - quote character (otherwise it would terminate the string)
|
||||||
|
- ``\'`` - apostrophe character (has to be escaped in character literals, is okay inside a string)
|
||||||
|
- ``\uHHHH`` - a unicode codepoint \u0000 - \uffff (16-bit hexadecimal)
|
||||||
|
- ``\xHH`` - 8-bit hex value that will be copied verbatim *without encoding*
|
||||||
|
|
||||||
|
|
||||||
Operators
|
Operators
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@ -486,6 +504,8 @@ takes no parameters. If the subroutine returns a value, usually you assign it t
|
|||||||
If you're not interested in the return value, prefix the function call with the ``void`` keyword.
|
If you're not interested in the return value, prefix the function call with the ``void`` keyword.
|
||||||
Otherwise the compiler will warn you about discarding the result of the call.
|
Otherwise the compiler will warn you about discarding the result of the call.
|
||||||
|
|
||||||
|
Multiple return values
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
Normal subroutines can only return zero or one return values.
|
Normal subroutines can only return zero or one return values.
|
||||||
However, the special ``asmsub`` routines (implemented in assembly code) or ``romsub`` routines
|
However, the special ``asmsub`` routines (implemented in assembly code) or ``romsub`` routines
|
||||||
(referencing a routine in kernel ROM) can return more than one return value.
|
(referencing a routine in kernel ROM) can return more than one return value.
|
||||||
@ -493,9 +513,16 @@ For example a status in the carry bit and a number in A, or a 16-bit value in A/
|
|||||||
It is not possible to process the results of a call to these kind of routines
|
It is not possible to process the results of a call to these kind of routines
|
||||||
directly from the language, because only single value assignments are possible.
|
directly from the language, because only single value assignments are possible.
|
||||||
You can still call the subroutine and not store the results.
|
You can still call the subroutine and not store the results.
|
||||||
But if you want to do something with the values it returns, you'll have to write
|
|
||||||
a small block of custom inline assembly that does the call and stores the values
|
**There is an exception:** if there's just one return value in a register, and one or more others that are returned
|
||||||
appropriately. Don't forget to save/restore the registers if required.
|
as bits in the status register (such as the Carry bit), the compiler allows you to call the subroutine.
|
||||||
|
It will then store the result value in a variable if required, and *keep the status register untouched
|
||||||
|
after the call* so you can use a conditional branch statement for that.
|
||||||
|
Note that this makes no sense inside an expression, so the compiler will still give an error for that.
|
||||||
|
|
||||||
|
If there really are multiple return values (other than a combined 16 bit return value in 2 registers),
|
||||||
|
you'll have to write a small block of custom inline assembly that does the call and stores the values
|
||||||
|
appropriately. Don't forget to save/restore any registers that are modified.
|
||||||
|
|
||||||
|
|
||||||
Subroutine definitions
|
Subroutine definitions
|
||||||
|
@ -16,8 +16,9 @@ Currently there are two machines that are supported as compiler target (selectab
|
|||||||
|
|
||||||
This chapter explains the relevant system details of these machines.
|
This chapter explains the relevant system details of these machines.
|
||||||
|
|
||||||
.. note::
|
.. hint::
|
||||||
If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
If you only use standard kernel and prog8 library routines,
|
||||||
|
it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
|
||||||
|
|
||||||
|
|
||||||
Memory Model
|
Memory Model
|
||||||
|
@ -3,7 +3,6 @@ TODO
|
|||||||
====
|
====
|
||||||
|
|
||||||
- get rid of all other TODO's in the code ;-)
|
- get rid of all other TODO's in the code ;-)
|
||||||
- make it possible for array literals to not only contain compile time constants?
|
|
||||||
- implement @stack for asmsub parameters
|
- implement @stack for asmsub parameters
|
||||||
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
|
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
|
||||||
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
|
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
|
||||||
@ -19,7 +18,7 @@ Add more compiler optimizations to the existing ones.
|
|||||||
|
|
||||||
- further optimize assignment codegeneration, such as the following:
|
- further optimize assignment codegeneration, such as the following:
|
||||||
- binexpr splitting (beware self-referencing expressions and asm code ballooning though)
|
- binexpr splitting (beware self-referencing expressions and asm code ballooning though)
|
||||||
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise.
|
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise. Especially for built-in functions!
|
||||||
- can such parameter passing to subroutines be optimized to avoid copying?
|
- can such parameter passing to subroutines be optimized to avoid copying?
|
||||||
- more optimizations on the language AST level
|
- more optimizations on the language AST level
|
||||||
- more optimizations on the final assembly source level
|
- more optimizations on the final assembly source level
|
||||||
|
Binary file not shown.
Binary file not shown.
BIN
examples/compiled/textelite.prg
Normal file
BIN
examples/compiled/textelite.prg
Normal file
Binary file not shown.
@ -166,7 +166,7 @@ main {
|
|||||||
else
|
else
|
||||||
c64.SPRPTR[i] = $2000/64 ; small ball
|
c64.SPRPTR[i] = $2000/64 ; small ball
|
||||||
|
|
||||||
c64.SPCOL[i] = spritecolors[(zc>>13) as byte + 4] ; further away=darker color
|
c64.SPCOL[i] = spritecolors[(zc>>13) as ubyte + 4] ; further away=darker color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,14 @@
|
|||||||
%target cx16
|
%target cx16
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
void cx16.screen_set_mode($80)
|
void cx16.screen_set_mode($80)
|
||||||
cx16.r0=0
|
cx16.r0=0
|
||||||
|
void cx16.screen_set_mode(0)
|
||||||
|
|
||||||
cx16.FB_init()
|
cx16.FB_init()
|
||||||
cx16.GRAPH_init()
|
cx16.GRAPH_init()
|
||||||
|
17
examples/cxlogo.p8
Normal file
17
examples/cxlogo.p8
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
%import textio
|
||||||
|
%import cx16logo
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
repeat {
|
||||||
|
ubyte col = rnd() % (txt.DEFAULT_WIDTH-12) + 3
|
||||||
|
ubyte row = rnd() % (txt.DEFAULT_HEIGHT-7)
|
||||||
|
cx16logo.logo_at(col, row)
|
||||||
|
txt.plot(col-3, row+7 )
|
||||||
|
txt.print("commander x16")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,47 +0,0 @@
|
|||||||
%target c64
|
|
||||||
%import textio
|
|
||||||
%import syslib
|
|
||||||
%zeropage basicsafe
|
|
||||||
%option no_sysinit
|
|
||||||
%launcher none
|
|
||||||
%address 50000
|
|
||||||
|
|
||||||
; This example shows the directory contents of disk drive 8.
|
|
||||||
; You load it with LOAD "diskdir-sys50000",8,1
|
|
||||||
; and then call it with SYS 50000.
|
|
||||||
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
txt.print("directory of disk drive #8:\n\n")
|
|
||||||
diskdir(8)
|
|
||||||
}
|
|
||||||
|
|
||||||
sub diskdir(ubyte drivenumber) {
|
|
||||||
c64.SETNAM(1, "$")
|
|
||||||
c64.SETLFS(1, drivenumber, 0)
|
|
||||||
c64.OPEN() ; open 1,8,0,"$"
|
|
||||||
c64.CHKIN(1) ; use #1 as input channel
|
|
||||||
|
|
||||||
repeat 4 {
|
|
||||||
void c64.CHRIN() ; skip the 4 prologue bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
; while not key pressed / EOF encountered, read data.
|
|
||||||
while not (@($c6) | c64.STATUS) {
|
|
||||||
txt.print_uw(mkword(c64.CHRIN(), c64.CHRIN()))
|
|
||||||
txt.chrout(' ')
|
|
||||||
ubyte @zp char
|
|
||||||
do {
|
|
||||||
char = c64.CHRIN()
|
|
||||||
txt.chrout(char)
|
|
||||||
} until char==0
|
|
||||||
txt.chrout('\n')
|
|
||||||
repeat 2 {
|
|
||||||
void c64.CHRIN() ; skip 2 bytes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c64.CLOSE(1)
|
|
||||||
c64.CLRCHN() ; restore default i/o devices
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
%target c64
|
|
||||||
%import textio
|
|
||||||
%import syslib
|
|
||||||
%option no_sysinit
|
|
||||||
%zeropage basicsafe
|
|
||||||
|
|
||||||
; This example shows the directory contents of disk drive 8.
|
|
||||||
|
|
||||||
main {
|
|
||||||
sub start() {
|
|
||||||
txt.print("directory of disk drive #8:\n\n")
|
|
||||||
diskdir(8)
|
|
||||||
}
|
|
||||||
|
|
||||||
sub diskdir(ubyte drivenumber) {
|
|
||||||
c64.SETNAM(1, "$")
|
|
||||||
c64.SETLFS(1, drivenumber, 0)
|
|
||||||
c64.OPEN() ; open 1,8,0,"$"
|
|
||||||
c64.CHKIN(1) ; use #1 as input channel
|
|
||||||
|
|
||||||
repeat 4 {
|
|
||||||
void c64.CHRIN() ; skip the 4 prologue bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
; while not key pressed / EOF encountered, read data.
|
|
||||||
while not (@($c6) | c64.STATUS) {
|
|
||||||
txt.print_uw(mkword(c64.CHRIN(), c64.CHRIN()))
|
|
||||||
txt.chrout(' ')
|
|
||||||
ubyte @zp char
|
|
||||||
do {
|
|
||||||
char = c64.CHRIN()
|
|
||||||
txt.chrout(char)
|
|
||||||
} until char==0
|
|
||||||
txt.chrout('\n')
|
|
||||||
repeat 2 {
|
|
||||||
void c64.CHRIN() ; skip 2 bytes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c64.CLOSE(1)
|
|
||||||
c64.CLRCHN() ; restore default i/o devices
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,14 +9,14 @@
|
|||||||
; Note: this program is compatible with C64 and CX16.
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
main {
|
main {
|
||||||
const ubyte width = 255
|
const uword width = 320
|
||||||
const ubyte height = 200
|
const ubyte height = 200
|
||||||
const ubyte max_iter = 16
|
const ubyte max_iter = 16
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
graphics.enable_bitmap_mode()
|
graphics.enable_bitmap_mode()
|
||||||
|
|
||||||
ubyte pixelx
|
uword pixelx
|
||||||
ubyte pixely
|
ubyte pixely
|
||||||
|
|
||||||
for pixely in 0 to height-1 {
|
for pixely in 0 to height-1 {
|
||||||
|
@ -30,7 +30,7 @@ main {
|
|||||||
txt.print("es")
|
txt.print("es")
|
||||||
txt.print(" left.\nWhat is your next guess? ")
|
txt.print(" left.\nWhat is your next guess? ")
|
||||||
void txt.input_chars(input)
|
void txt.input_chars(input)
|
||||||
ubyte guess = lsb(conv.str2uword(input))
|
ubyte guess = conv.str2ubyte(input)
|
||||||
|
|
||||||
if guess==secretnumber {
|
if guess==secretnumber {
|
||||||
ending(true)
|
ending(true)
|
||||||
|
@ -218,7 +218,7 @@ waitkey:
|
|||||||
if linepos and blocklogic.isLineFull(linepos)
|
if linepos and blocklogic.isLineFull(linepos)
|
||||||
blocklogic.collapse(linepos)
|
blocklogic.collapse(linepos)
|
||||||
lines += num_lines
|
lines += num_lines
|
||||||
uword[] scores = [10, 25, 50, 100] ; can never clear more than 4 lines
|
uword[] scores = [10, 25, 50, 100] ; can never clear more than 4 lines at once
|
||||||
score += scores[num_lines-1]
|
score += scores[num_lines-1]
|
||||||
speedlevel = 1+lsb(lines/10)
|
speedlevel = 1+lsb(lines/10)
|
||||||
drawScore()
|
drawScore()
|
||||||
@ -249,8 +249,10 @@ waitkey:
|
|||||||
txt.print("────────────────────────")
|
txt.print("────────────────────────")
|
||||||
c64.CHROUT('K')
|
c64.CHROUT('K')
|
||||||
|
|
||||||
while c64.GETIN()!=133 {
|
ubyte key = 0
|
||||||
|
while key!=133 {
|
||||||
; endless loop until user presses F1 to restart the game
|
; endless loop until user presses F1 to restart the game
|
||||||
|
key = c64.GETIN()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,41 +1,23 @@
|
|||||||
%import textio
|
%import textio
|
||||||
%import syslib
|
%import conv
|
||||||
%import floats
|
%import floats
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
struct Color {
|
|
||||||
ubyte red
|
|
||||||
ubyte green
|
|
||||||
ubyte blue
|
|
||||||
}
|
|
||||||
|
|
||||||
Color c1 = [11,22,33]
|
|
||||||
Color c2 = [11,22,33]
|
|
||||||
Color c3 = [11,22,33]
|
|
||||||
uword[] colors = [ c1, c2, c3]
|
|
||||||
|
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
txt.print_ub(c1.red)
|
uword[] array = [1, 2, 3]
|
||||||
txt.chrout('\n')
|
ubyte ii = 0
|
||||||
txt.print_ub(c1.green)
|
ubyte ii2 = ii+2
|
||||||
txt.chrout('\n')
|
array[ii+1] = array[ii2] ; TODO fix overwriting the single array index autovar
|
||||||
txt.print_ub(c1.blue)
|
|
||||||
txt.chrout('\n')
|
|
||||||
txt.chrout('\n')
|
|
||||||
|
|
||||||
c1 = [99,88,77]
|
uword xx
|
||||||
|
for xx in array {
|
||||||
|
txt.print_uw(xx)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
|
||||||
txt.print_ub(c1.red)
|
|
||||||
txt.chrout('\n')
|
|
||||||
txt.print_ub(c1.green)
|
|
||||||
txt.chrout('\n')
|
|
||||||
txt.print_ub(c1.blue)
|
|
||||||
txt.chrout('\n')
|
|
||||||
testX()
|
testX()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,5 +35,4 @@ main {
|
|||||||
_saveX .byte 0
|
_saveX .byte 0
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ main {
|
|||||||
|
|
||||||
&str ms1 = $c000
|
&str ms1 = $c000
|
||||||
|
|
||||||
|
|
||||||
byte[4] barray
|
byte[4] barray
|
||||||
ubyte[4] ubarray
|
ubyte[4] ubarray
|
||||||
word[4] warray
|
word[4] warray
|
||||||
@ -84,20 +83,20 @@ main {
|
|||||||
uw=muwarray[bb]
|
uw=muwarray[bb]
|
||||||
fl=mflarray[bb]
|
fl=mflarray[bb]
|
||||||
|
|
||||||
A=s1[bb*3]
|
; A=s1[bb*3]
|
||||||
ub=s1[bb*3]
|
; ub=s1[bb*3]
|
||||||
bb=barray[bb*3]
|
; bb=barray[bb*3]
|
||||||
ub=ubarray[bb*3]
|
; ub=ubarray[bb*3]
|
||||||
ww=warray[bb*3]
|
; ww=warray[bb*3]
|
||||||
uw=uwarray[bb*3]
|
; uw=uwarray[bb*3]
|
||||||
fl=flarray[bb*3]
|
; fl=flarray[bb*3]
|
||||||
A=ms1[bb*3]
|
; A=ms1[bb*3]
|
||||||
ub=ms1[bb*3]
|
; ub=ms1[bb*3]
|
||||||
bb=mbarray[bb*3]
|
; bb=mbarray[bb*3]
|
||||||
ub=mubarray[bb*3]
|
; ub=mubarray[bb*3]
|
||||||
ww=mwarray[bb*3]
|
; ww=mwarray[bb*3]
|
||||||
uw=muwarray[bb*3]
|
; uw=muwarray[bb*3]
|
||||||
fl=mflarray[bb*3]
|
; fl=mflarray[bb*3]
|
||||||
|
|
||||||
; write array
|
; write array
|
||||||
barray[2]++
|
barray[2]++
|
||||||
@ -133,11 +132,11 @@ main {
|
|||||||
uwarray[bb] = uw
|
uwarray[bb] = uw
|
||||||
flarray[bb] = fl
|
flarray[bb] = fl
|
||||||
|
|
||||||
s1[bb*3] = ub
|
; s1[bb*3] = ub
|
||||||
barray[bb*3] = bb
|
; barray[bb*3] = bb
|
||||||
ubarray[bb*3] = ub
|
; ubarray[bb*3] = ub
|
||||||
warray[bb*3] = ww
|
; warray[bb*3] = ww
|
||||||
uwarray[bb*3] = uw
|
; uwarray[bb*3] = uw
|
||||||
flarray[bb*3] = fl
|
; flarray[bb*3] = fl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
999
examples/textelite.p8
Normal file
999
examples/textelite.p8
Normal file
@ -0,0 +1,999 @@
|
|||||||
|
%import textio
|
||||||
|
%import conv
|
||||||
|
%import diskio
|
||||||
|
%option no_sysinit
|
||||||
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
; Prog8 adaptation of the Text-Elite galaxy system trading simulation engine.
|
||||||
|
; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm
|
||||||
|
|
||||||
|
; Note: this program is compatible with C64 and CX16.
|
||||||
|
|
||||||
|
main {
|
||||||
|
|
||||||
|
const ubyte numforLave = 7 ; Lave is 7th generated planet in galaxy one
|
||||||
|
const ubyte numforZaonce = 129
|
||||||
|
const ubyte numforDiso = 147
|
||||||
|
const ubyte numforRiedquat = 46
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
txt.lowercase()
|
||||||
|
txt.print("\u000c\n --- TextElite v1.1 ---\n")
|
||||||
|
|
||||||
|
galaxy.travel_to(1, numforLave)
|
||||||
|
market.init(0) ; Lave's market is seeded with 0
|
||||||
|
ship.init()
|
||||||
|
planet.display(false)
|
||||||
|
|
||||||
|
repeat {
|
||||||
|
str input = "????????"
|
||||||
|
txt.print("\nCash: ")
|
||||||
|
util.print_10s(ship.cash)
|
||||||
|
txt.print("\nCommand (?=help): ")
|
||||||
|
ubyte num_chars = txt.input_chars(input)
|
||||||
|
txt.chrout('\n')
|
||||||
|
if num_chars {
|
||||||
|
when input[0] {
|
||||||
|
'?' -> {
|
||||||
|
txt.print("\nCommands are:\n"+
|
||||||
|
"buy jump info cash >=save\n"+
|
||||||
|
"sell teleport market hold <=load\n"+
|
||||||
|
"fuel galhyp local quit\n")
|
||||||
|
}
|
||||||
|
'q' -> break
|
||||||
|
'b' -> trader.do_buy()
|
||||||
|
's' -> trader.do_sell()
|
||||||
|
'f' -> trader.do_fuel()
|
||||||
|
'j' -> trader.do_jump()
|
||||||
|
't' -> trader.do_teleport()
|
||||||
|
'g' -> trader.do_next_galaxy()
|
||||||
|
'i' -> trader.do_info()
|
||||||
|
'm' -> trader.do_show_market()
|
||||||
|
'l' -> trader.do_local()
|
||||||
|
'c' -> trader.do_cash()
|
||||||
|
'h' -> trader.do_hold()
|
||||||
|
'<' -> trader.do_load()
|
||||||
|
'>' -> trader.do_save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trader {
|
||||||
|
str Savegame = "↑commander.save"
|
||||||
|
str input = "??????????"
|
||||||
|
ubyte num_chars
|
||||||
|
|
||||||
|
struct SaveData {
|
||||||
|
ubyte galaxy
|
||||||
|
ubyte planet
|
||||||
|
ubyte cargo0
|
||||||
|
ubyte cargo1
|
||||||
|
ubyte cargo2
|
||||||
|
ubyte cargo3
|
||||||
|
ubyte cargo4
|
||||||
|
ubyte cargo5
|
||||||
|
ubyte cargo6
|
||||||
|
ubyte cargo7
|
||||||
|
ubyte cargo8
|
||||||
|
ubyte cargo9
|
||||||
|
ubyte cargo10
|
||||||
|
ubyte cargo11
|
||||||
|
ubyte cargo12
|
||||||
|
ubyte cargo13
|
||||||
|
ubyte cargo14
|
||||||
|
ubyte cargo15
|
||||||
|
ubyte cargo16
|
||||||
|
uword cash
|
||||||
|
ubyte max_cargo
|
||||||
|
ubyte fuel
|
||||||
|
}
|
||||||
|
SaveData savedata
|
||||||
|
|
||||||
|
sub do_load() {
|
||||||
|
txt.print("\nLoading universe...")
|
||||||
|
if diskio.load(8, Savegame, &savedata) {
|
||||||
|
txt.print("ok\n")
|
||||||
|
} else {
|
||||||
|
txt.print("\ni/o error: ")
|
||||||
|
diskio.status(8)
|
||||||
|
txt.chrout('\n')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ship.cash = savedata.cash
|
||||||
|
ship.Max_cargo = savedata.max_cargo
|
||||||
|
ship.fuel = savedata.fuel
|
||||||
|
memcopy(&savedata.cargo0, ship.cargohold, len(ship.cargohold))
|
||||||
|
galaxy.travel_to(savedata.galaxy, savedata.planet)
|
||||||
|
|
||||||
|
planet.display(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_save() {
|
||||||
|
savedata.galaxy = galaxy.number
|
||||||
|
savedata.planet = planet.number
|
||||||
|
savedata.cash = ship.cash
|
||||||
|
savedata.max_cargo = ship.Max_cargo
|
||||||
|
savedata.fuel = ship.fuel
|
||||||
|
memcopy(ship.cargohold, &savedata.cargo0, len(ship.cargohold))
|
||||||
|
|
||||||
|
txt.print("\nSaving universe...")
|
||||||
|
diskio.delete(8, Savegame)
|
||||||
|
if diskio.save(8, Savegame, &savedata, sizeof(savedata)) {
|
||||||
|
txt.print("ok\n")
|
||||||
|
} else {
|
||||||
|
txt.print("\ni/o error: ")
|
||||||
|
diskio.status(8)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_jump() {
|
||||||
|
txt.print("\nJump to what system? ")
|
||||||
|
jump_to_system()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_teleport() {
|
||||||
|
txt.print("\nCheat! Teleport to what system? ")
|
||||||
|
ubyte fuel = ship.fuel
|
||||||
|
ship.fuel = 255
|
||||||
|
jump_to_system()
|
||||||
|
ship.fuel = fuel
|
||||||
|
}
|
||||||
|
|
||||||
|
sub jump_to_system() {
|
||||||
|
void txt.input_chars(input)
|
||||||
|
ubyte current_planet = planet.number
|
||||||
|
ubyte x = planet.x
|
||||||
|
ubyte y = planet.y
|
||||||
|
if galaxy.search_closest_planet(input) {
|
||||||
|
ubyte distance = planet.distance(x, y)
|
||||||
|
if distance <= ship.fuel {
|
||||||
|
galaxy.init_market_for_planet()
|
||||||
|
ship.fuel -= distance
|
||||||
|
txt.print("\n\nHyperspace jump! Arrived at:\n")
|
||||||
|
planet.display(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
txt.print("Insufficient fuel\n")
|
||||||
|
} else {
|
||||||
|
txt.print(" Not found!\n")
|
||||||
|
}
|
||||||
|
galaxy.travel_to(galaxy.number, current_planet)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_buy() {
|
||||||
|
txt.print("\nBuy what commodity? ")
|
||||||
|
str commodity = "???????????????"
|
||||||
|
void txt.input_chars(commodity)
|
||||||
|
ubyte ci = market.match(commodity)
|
||||||
|
if ci & 128 {
|
||||||
|
txt.print("Unknown\n")
|
||||||
|
} else {
|
||||||
|
txt.print("\nHow much? ")
|
||||||
|
void txt.input_chars(input)
|
||||||
|
ubyte amount = conv.str2ubyte(input)
|
||||||
|
if market.current_quantity[ci] < amount {
|
||||||
|
txt.print(" Insufficient supply!\n")
|
||||||
|
} else {
|
||||||
|
uword price = market.current_price[ci] * amount
|
||||||
|
txt.print(" Total price: ")
|
||||||
|
util.print_10s(price)
|
||||||
|
if price > ship.cash {
|
||||||
|
txt.print(" Not enough cash!\n")
|
||||||
|
} else {
|
||||||
|
ship.cash -= price
|
||||||
|
ship.cargohold[ci] += amount
|
||||||
|
market.current_quantity[ci] -= amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_sell() {
|
||||||
|
txt.print("\nSell what commodity? ")
|
||||||
|
str commodity = "???????????????"
|
||||||
|
void txt.input_chars(commodity)
|
||||||
|
ubyte ci = market.match(commodity)
|
||||||
|
if ci & 128 {
|
||||||
|
txt.print("Unknown\n")
|
||||||
|
} else {
|
||||||
|
txt.print("\nHow much? ")
|
||||||
|
void txt.input_chars(input)
|
||||||
|
ubyte amount = conv.str2ubyte(input)
|
||||||
|
if ship.cargohold[ci] < amount {
|
||||||
|
txt.print(" Insufficient supply!\n")
|
||||||
|
} else {
|
||||||
|
uword price = market.current_price[ci] * amount
|
||||||
|
txt.print(" Total price: ")
|
||||||
|
util.print_10s(price)
|
||||||
|
ship.cash += price
|
||||||
|
ship.cargohold[ci] -= amount
|
||||||
|
market.current_quantity[ci] += amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_fuel() {
|
||||||
|
txt.print("\nBuy fuel. Amount? ")
|
||||||
|
void txt.input_chars(input)
|
||||||
|
ubyte buy_fuel = 10*conv.str2ubyte(input)
|
||||||
|
ubyte max_fuel = ship.Max_fuel - ship.fuel
|
||||||
|
if buy_fuel > max_fuel
|
||||||
|
buy_fuel = max_fuel
|
||||||
|
uword price = buy_fuel as uword * ship.Fuel_cost
|
||||||
|
if price > ship.cash {
|
||||||
|
txt.print("Not enough cash!\n")
|
||||||
|
} else {
|
||||||
|
ship.cash -= price
|
||||||
|
ship.fuel += buy_fuel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_cash() {
|
||||||
|
txt.print("\nCheat! Set cash amount: ")
|
||||||
|
void txt.input_chars(input)
|
||||||
|
ship.cash = conv.str2uword(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_hold() {
|
||||||
|
txt.print("\nCheat! Set cargohold size: ")
|
||||||
|
void txt.input_chars(input)
|
||||||
|
ship.Max_cargo = conv.str2ubyte(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_next_galaxy() {
|
||||||
|
galaxy.travel_to(galaxy.number+1, planet.number)
|
||||||
|
planet.display(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_info() {
|
||||||
|
txt.print("\nSystem name (empty=current): ")
|
||||||
|
num_chars = txt.input_chars(input)
|
||||||
|
if num_chars {
|
||||||
|
ubyte current_planet = planet.number
|
||||||
|
if galaxy.search_closest_planet(input) {
|
||||||
|
planet.display(false)
|
||||||
|
} else {
|
||||||
|
txt.print(" Not found!")
|
||||||
|
}
|
||||||
|
galaxy.travel_to(galaxy.number, current_planet)
|
||||||
|
} else {
|
||||||
|
planet.display(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_local() {
|
||||||
|
galaxy.local_area()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_show_market() {
|
||||||
|
market.display()
|
||||||
|
txt.print("\nFuel: ")
|
||||||
|
util.print_10s(ship.fuel)
|
||||||
|
txt.print(" Cargohold space: ")
|
||||||
|
txt.print_ub(ship.cargo_free())
|
||||||
|
txt.print("t\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ship {
|
||||||
|
const ubyte Max_fuel = 70
|
||||||
|
const ubyte Fuel_cost = 2
|
||||||
|
ubyte Max_cargo = 20
|
||||||
|
|
||||||
|
ubyte fuel = Max_fuel
|
||||||
|
uword cash = 1000 ; actually has to be 4 bytes for the ultra rich....
|
||||||
|
ubyte[17] cargohold = 0
|
||||||
|
|
||||||
|
sub init() {
|
||||||
|
memset(cargohold, len(cargohold), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cargo_free() -> ubyte {
|
||||||
|
ubyte ci
|
||||||
|
ubyte total = 0
|
||||||
|
for ci in 0 to len(cargohold)-1 {
|
||||||
|
if market.units[ci]==0 ; tonnes only
|
||||||
|
total += cargohold[ci]
|
||||||
|
}
|
||||||
|
return Max_cargo - total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
market {
|
||||||
|
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
|
||||||
|
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
|
||||||
|
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
|
||||||
|
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
|
||||||
|
ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0]
|
||||||
|
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
|
||||||
|
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
|
||||||
|
|
||||||
|
ubyte[17] current_quantity = 0
|
||||||
|
uword[17] current_price = 0
|
||||||
|
|
||||||
|
sub init(ubyte fluct) {
|
||||||
|
; Prices and availabilities are influenced by the planet's economy type
|
||||||
|
; (0-7) and a random "fluctuation" byte that was kept within the saved
|
||||||
|
; commander position to keep the market prices constant over gamesaves.
|
||||||
|
; Availabilities must be saved with the game since the player alters them
|
||||||
|
; by buying (and selling(?))
|
||||||
|
;
|
||||||
|
; Almost all operations are one byte only and overflow "errors" are
|
||||||
|
; extremely frequent and exploited.
|
||||||
|
;
|
||||||
|
; Trade Item prices are held internally in a single byte=true value/4.
|
||||||
|
; The decimal point in prices is introduced only when printing them.
|
||||||
|
; Internally, all prices are integers.
|
||||||
|
; The player's cash is held in four bytes.
|
||||||
|
ubyte ci
|
||||||
|
for ci in 0 to len(names)-1 {
|
||||||
|
word product
|
||||||
|
byte changing
|
||||||
|
product = planet.economy as word * gradients[ci]
|
||||||
|
changing = fluct & maskbytes[ci] as byte
|
||||||
|
ubyte q = (basequants[ci] as word + changing - product) as ubyte
|
||||||
|
if q & $80
|
||||||
|
q = 0 ; clip to positive 8-bit
|
||||||
|
current_quantity[ci] = q & $3f
|
||||||
|
q = (baseprices[ci] + changing + product) as ubyte
|
||||||
|
current_price[ci] = q * $0004
|
||||||
|
}
|
||||||
|
current_quantity[16] = 0 ; force nonavailability of Alien Items
|
||||||
|
}
|
||||||
|
|
||||||
|
sub display() {
|
||||||
|
ubyte ci
|
||||||
|
txt.chrout('\n')
|
||||||
|
planet.print_name_uppercase()
|
||||||
|
txt.print(" trade market:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
|
||||||
|
for ci in 0 to len(names)-1 {
|
||||||
|
util.print_right(13, names[ci])
|
||||||
|
txt.print(" ")
|
||||||
|
util.print_10s(current_price[ci])
|
||||||
|
txt.print(" ")
|
||||||
|
txt.print_ub(current_quantity[ci])
|
||||||
|
when units[ci] {
|
||||||
|
0 -> txt.chrout('t')
|
||||||
|
1 -> txt.print("kg")
|
||||||
|
2 -> txt.chrout('g')
|
||||||
|
}
|
||||||
|
txt.print(" ")
|
||||||
|
txt.print_ub(ship.cargohold[ci])
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub match(uword nameptr) -> ubyte {
|
||||||
|
ubyte ci
|
||||||
|
for ci in 0 to len(names)-1 {
|
||||||
|
if util.prefix_matches(nameptr, names[ci])
|
||||||
|
return ci
|
||||||
|
}
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
galaxy {
|
||||||
|
const uword GALSIZE = 256
|
||||||
|
const uword base0 = $5A4A ; seeds for the first galaxy
|
||||||
|
const uword base1 = $0248
|
||||||
|
const uword base2 = $B753
|
||||||
|
|
||||||
|
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
|
||||||
|
|
||||||
|
ubyte number
|
||||||
|
|
||||||
|
uword[3] seed
|
||||||
|
|
||||||
|
sub init(ubyte galaxynum) {
|
||||||
|
number = 1
|
||||||
|
planet.number = 255
|
||||||
|
seed = [base0, base1, base2]
|
||||||
|
repeat galaxynum-1 {
|
||||||
|
nextgalaxy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub nextgalaxy() {
|
||||||
|
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
|
||||||
|
number++
|
||||||
|
if number==9
|
||||||
|
number = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
sub travel_to(ubyte galaxynum, ubyte system) {
|
||||||
|
init(galaxynum)
|
||||||
|
generate_next_planet() ; always at least planet 0 (separate to avoid repeat ubyte overflow)
|
||||||
|
repeat system {
|
||||||
|
generate_next_planet()
|
||||||
|
}
|
||||||
|
planet.name = make_current_planet_name()
|
||||||
|
init_market_for_planet()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub init_market_for_planet() {
|
||||||
|
market.init(lsb(seed[0])+msb(seed[2]))
|
||||||
|
}
|
||||||
|
|
||||||
|
sub search_closest_planet(uword nameptr) -> ubyte {
|
||||||
|
ubyte x = planet.x
|
||||||
|
ubyte y = planet.y
|
||||||
|
ubyte current_planet_num = planet.number
|
||||||
|
|
||||||
|
init(number)
|
||||||
|
ubyte found = false
|
||||||
|
ubyte current_closest_pi
|
||||||
|
ubyte current_distance = 127
|
||||||
|
ubyte pi
|
||||||
|
for pi in 0 to 255 {
|
||||||
|
generate_next_planet()
|
||||||
|
planet.name = make_current_planet_name()
|
||||||
|
if util.prefix_matches(nameptr, planet.name) {
|
||||||
|
ubyte distance = planet.distance(x, y)
|
||||||
|
if distance < current_distance {
|
||||||
|
current_distance = distance
|
||||||
|
current_closest_pi = pi
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found
|
||||||
|
travel_to(number, current_closest_pi)
|
||||||
|
else
|
||||||
|
travel_to(number, current_planet_num)
|
||||||
|
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
sub local_area() {
|
||||||
|
ubyte current_planet = planet.number
|
||||||
|
ubyte px = planet.x
|
||||||
|
ubyte py = planet.y
|
||||||
|
ubyte pn = 0
|
||||||
|
|
||||||
|
init(number)
|
||||||
|
txt.print("\nGalaxy #")
|
||||||
|
txt.print_ub(number)
|
||||||
|
txt.print(" - systems in vicinity:\n")
|
||||||
|
do {
|
||||||
|
generate_next_planet()
|
||||||
|
ubyte distance = planet.distance(px, py)
|
||||||
|
if distance <= ship.Max_fuel {
|
||||||
|
if distance <= ship.fuel
|
||||||
|
txt.chrout('*')
|
||||||
|
else
|
||||||
|
txt.chrout('-')
|
||||||
|
txt.chrout(' ')
|
||||||
|
planet.name = make_current_planet_name()
|
||||||
|
planet.display(true)
|
||||||
|
txt.print(" (")
|
||||||
|
util.print_10s(distance)
|
||||||
|
txt.print(" LY)\n")
|
||||||
|
}
|
||||||
|
pn++
|
||||||
|
} until pn==0
|
||||||
|
|
||||||
|
travel_to(number, current_planet)
|
||||||
|
}
|
||||||
|
|
||||||
|
ubyte pn_pair1
|
||||||
|
ubyte pn_pair2
|
||||||
|
ubyte pn_pair3
|
||||||
|
ubyte pn_pair4
|
||||||
|
ubyte longname
|
||||||
|
|
||||||
|
sub generate_next_planet() {
|
||||||
|
determine_planet_properties()
|
||||||
|
longname = lsb(seed[0]) & 64
|
||||||
|
|
||||||
|
; Always four iterations of random number
|
||||||
|
pn_pair1 = (msb(seed[2]) & 31) * 2
|
||||||
|
tweakseed()
|
||||||
|
pn_pair2 = (msb(seed[2]) & 31) * 2
|
||||||
|
tweakseed()
|
||||||
|
pn_pair3 = (msb(seed[2]) & 31) * 2
|
||||||
|
tweakseed()
|
||||||
|
pn_pair4 = (msb(seed[2]) & 31) * 2
|
||||||
|
tweakseed()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub make_current_planet_name() -> str {
|
||||||
|
ubyte ni = 0
|
||||||
|
str name = " " ; max 8
|
||||||
|
|
||||||
|
if pn_pairs[pn_pair1] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair1]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
if pn_pairs[pn_pair1+1] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair1+1]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
if pn_pairs[pn_pair2] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair2]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
if pn_pairs[pn_pair2+1] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair2+1]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
if pn_pairs[pn_pair3] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair3]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
if pn_pairs[pn_pair3+1] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair3+1]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
|
||||||
|
if longname {
|
||||||
|
if pn_pairs[pn_pair4] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair4]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
if pn_pairs[pn_pair4+1] != '.' {
|
||||||
|
name[ni] = pn_pairs[pn_pair4+1]
|
||||||
|
ni++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name[ni] = 0
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
sub determine_planet_properties() {
|
||||||
|
; create the planet's characteristics
|
||||||
|
planet.number++
|
||||||
|
planet.x = msb(seed[1])
|
||||||
|
planet.y = msb(seed[0])
|
||||||
|
planet.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
|
||||||
|
planet.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
|
||||||
|
if planet.govtype <= 1
|
||||||
|
planet.economy = (planet.economy | 2)
|
||||||
|
planet.techlevel = (msb(seed[1]) & 3) + (planet.economy ^ 7)
|
||||||
|
planet.techlevel += planet.govtype >> 1
|
||||||
|
if planet.govtype & 1
|
||||||
|
planet.techlevel++
|
||||||
|
planet.population = 4 * planet.techlevel + planet.economy
|
||||||
|
planet.population += planet.govtype + 1
|
||||||
|
planet.productivity = ((planet.economy ^ 7) + 3) * (planet.govtype + 4)
|
||||||
|
planet.productivity *= planet.population * 8
|
||||||
|
ubyte seed2_msb = msb(seed[2])
|
||||||
|
planet.radius = mkword((seed2_msb & 15) + 11, planet.x)
|
||||||
|
planet.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo
|
||||||
|
if planet.species_is_alien {
|
||||||
|
planet.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
|
||||||
|
planet.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
|
||||||
|
planet.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||||
|
planet.species_kind = (planet.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
|
||||||
|
}
|
||||||
|
|
||||||
|
planet.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
|
||||||
|
}
|
||||||
|
|
||||||
|
sub tweakseed() {
|
||||||
|
uword temp = seed[0] + seed[1] + seed[2]
|
||||||
|
seed[0] = seed[1]
|
||||||
|
seed[1] = seed[2]
|
||||||
|
seed[2] = temp
|
||||||
|
}
|
||||||
|
|
||||||
|
sub twist(uword x) -> uword {
|
||||||
|
ubyte xh = msb(x)
|
||||||
|
ubyte xl = lsb(x)
|
||||||
|
rol(xh)
|
||||||
|
rol(xl)
|
||||||
|
return mkword(xh, xl)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub debug_seed() {
|
||||||
|
txt.print("\ngalaxy #")
|
||||||
|
txt.print_ub(number)
|
||||||
|
txt.print("\ngalaxy seed0=")
|
||||||
|
txt.print_uwhex(galaxy.seed[0], true)
|
||||||
|
txt.print("\ngalaxy seed1=")
|
||||||
|
txt.print_uwhex(galaxy.seed[1], true)
|
||||||
|
txt.print("\ngalaxy seed2=")
|
||||||
|
txt.print_uwhex(galaxy.seed[2], true)
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
planet {
|
||||||
|
%option force_output
|
||||||
|
|
||||||
|
str[] species_sizes = ["Large", "Fierce", "Small"]
|
||||||
|
str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"]
|
||||||
|
str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"]
|
||||||
|
str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"]
|
||||||
|
str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship", "Communist", "Confederacy", "Democracy", "Corporate State"]
|
||||||
|
str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial",
|
||||||
|
"Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"]
|
||||||
|
|
||||||
|
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"]
|
||||||
|
str[] words82 = ["very", "mildly", "most", "reasonably", ""]
|
||||||
|
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"]
|
||||||
|
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
|
||||||
|
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
|
||||||
|
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
|
||||||
|
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
|
||||||
|
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
|
||||||
|
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
|
||||||
|
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
|
||||||
|
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
|
||||||
|
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
|
||||||
|
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
|
||||||
|
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
|
||||||
|
str[] words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"]
|
||||||
|
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
|
||||||
|
str[] words91 = ["planet", "world", "place", "little planet", "dump"]
|
||||||
|
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
|
||||||
|
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
|
||||||
|
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
|
||||||
|
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
|
||||||
|
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
|
||||||
|
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
|
||||||
|
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
|
||||||
|
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
|
||||||
|
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
|
||||||
|
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
|
||||||
|
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
|
||||||
|
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
|
||||||
|
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
|
||||||
|
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"]
|
||||||
|
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
|
||||||
|
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
|
||||||
|
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
|
||||||
|
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
|
||||||
|
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
|
||||||
|
|
||||||
|
uword[] wordlists = [
|
||||||
|
words81, words82, words83, words84, words85, words86, words87, words88,
|
||||||
|
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
|
||||||
|
words91, words92, words93, words94, words95, words96, words97, words98,
|
||||||
|
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
|
||||||
|
wordsA1, wordsA2, wordsA3, wordsA4]
|
||||||
|
|
||||||
|
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
|
||||||
|
|
||||||
|
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
|
||||||
|
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
|
||||||
|
|
||||||
|
str name = " " ; 8 max
|
||||||
|
ubyte number ; starts at 0 in new galaxy, then increases by 1 for each generated planet
|
||||||
|
ubyte x
|
||||||
|
ubyte y
|
||||||
|
ubyte economy
|
||||||
|
ubyte govtype
|
||||||
|
ubyte techlevel
|
||||||
|
ubyte population
|
||||||
|
uword productivity
|
||||||
|
uword radius
|
||||||
|
ubyte species_is_alien ; otherwise "Human Colonials"
|
||||||
|
ubyte species_size
|
||||||
|
ubyte species_color
|
||||||
|
ubyte species_look
|
||||||
|
ubyte species_kind
|
||||||
|
|
||||||
|
sub set_seed(uword s1, uword s2) {
|
||||||
|
goatsoup_seed[0] = lsb(s1)
|
||||||
|
goatsoup_seed[1] = msb(s1)
|
||||||
|
goatsoup_seed[2] = lsb(s2)
|
||||||
|
goatsoup_seed[3] = msb(s2)
|
||||||
|
reset_rnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
sub reset_rnd() {
|
||||||
|
goatsoup_rnd[0] = goatsoup_seed[0]
|
||||||
|
goatsoup_rnd[1] = goatsoup_seed[1]
|
||||||
|
goatsoup_rnd[2] = goatsoup_seed[2]
|
||||||
|
goatsoup_rnd[3] = goatsoup_seed[3]
|
||||||
|
}
|
||||||
|
|
||||||
|
sub random_name() -> str {
|
||||||
|
ubyte ii
|
||||||
|
str name = " " ; 8 chars max
|
||||||
|
ubyte nx = 0
|
||||||
|
for ii in 0 to goatsoup_rnd_number() & 3 {
|
||||||
|
ubyte x = goatsoup_rnd_number() & $3e
|
||||||
|
if pairs0[x] != '.' {
|
||||||
|
name[nx] = pairs0[x]
|
||||||
|
nx++
|
||||||
|
}
|
||||||
|
x++
|
||||||
|
if pairs0[x] != '.' {
|
||||||
|
name[nx] = pairs0[x]
|
||||||
|
nx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name[nx] = 0
|
||||||
|
name[0] |= 32 ; uppercase first letter
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
sub goatsoup_rnd_number() -> ubyte {
|
||||||
|
ubyte x = goatsoup_rnd[0] * 2
|
||||||
|
uword a = x as uword + goatsoup_rnd[2]
|
||||||
|
if goatsoup_rnd[0] > 127
|
||||||
|
a ++
|
||||||
|
goatsoup_rnd[0] = lsb(a)
|
||||||
|
goatsoup_rnd[2] = x
|
||||||
|
x = goatsoup_rnd[1]
|
||||||
|
ubyte ac = x + goatsoup_rnd[3] + msb(a)
|
||||||
|
goatsoup_rnd[1] = ac
|
||||||
|
goatsoup_rnd[3] = x
|
||||||
|
return ac
|
||||||
|
}
|
||||||
|
|
||||||
|
sub distance(ubyte px, ubyte py) -> ubyte {
|
||||||
|
uword ax
|
||||||
|
uword ay
|
||||||
|
if px>x
|
||||||
|
ax=px-x
|
||||||
|
else
|
||||||
|
ax=x-px
|
||||||
|
if py>y
|
||||||
|
ay=py-y
|
||||||
|
else
|
||||||
|
ay=y-py
|
||||||
|
ay /= 2
|
||||||
|
ubyte d = sqrt16(ax*ax + ay*ay)
|
||||||
|
if d>63
|
||||||
|
return 255
|
||||||
|
return d*4
|
||||||
|
}
|
||||||
|
|
||||||
|
sub soup() -> str {
|
||||||
|
str planet_result = " " * 160
|
||||||
|
uword[6] source_stack
|
||||||
|
ubyte stack_ptr = 0
|
||||||
|
str start_source = "\x8F is \x97."
|
||||||
|
uword source_ptr = &start_source
|
||||||
|
uword result_ptr = &planet_result
|
||||||
|
|
||||||
|
reset_rnd()
|
||||||
|
recursive_soup()
|
||||||
|
return planet_result
|
||||||
|
|
||||||
|
sub recursive_soup() {
|
||||||
|
repeat {
|
||||||
|
ubyte c = @(source_ptr)
|
||||||
|
source_ptr++
|
||||||
|
if c == $00 {
|
||||||
|
@(result_ptr) = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else if c <= $80 {
|
||||||
|
@(result_ptr) = c
|
||||||
|
result_ptr++
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if c <= $a4 {
|
||||||
|
ubyte rnr = goatsoup_rnd_number()
|
||||||
|
ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC)
|
||||||
|
source_stack[stack_ptr] = source_ptr
|
||||||
|
stack_ptr++
|
||||||
|
source_ptr = getword(c, wordNr)
|
||||||
|
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
|
||||||
|
stack_ptr--
|
||||||
|
source_ptr = source_stack[stack_ptr]
|
||||||
|
} else {
|
||||||
|
if c == $b0 {
|
||||||
|
@(result_ptr) = name[0] | 32
|
||||||
|
result_ptr++
|
||||||
|
concat_string(&name + 1)
|
||||||
|
}
|
||||||
|
else if c == $b1 {
|
||||||
|
@(result_ptr) = name[0] | 32
|
||||||
|
result_ptr++
|
||||||
|
ubyte ni
|
||||||
|
for ni in 1 to len(name) {
|
||||||
|
ubyte cc = name[ni]
|
||||||
|
if cc=='e' or cc=='o' or cc==0
|
||||||
|
break
|
||||||
|
else {
|
||||||
|
@(result_ptr) = cc
|
||||||
|
result_ptr++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@(result_ptr) = 'i'
|
||||||
|
result_ptr++
|
||||||
|
@(result_ptr) = 'a'
|
||||||
|
result_ptr++
|
||||||
|
@(result_ptr) = 'n'
|
||||||
|
result_ptr++
|
||||||
|
}
|
||||||
|
else if c == $b2 {
|
||||||
|
concat_string(random_name())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
@(result_ptr) = c
|
||||||
|
result_ptr++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub concat_string(uword str_ptr) {
|
||||||
|
repeat {
|
||||||
|
ubyte c = @(str_ptr)
|
||||||
|
if c==0
|
||||||
|
break
|
||||||
|
else {
|
||||||
|
@(result_ptr) = c
|
||||||
|
str_ptr++
|
||||||
|
result_ptr++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub display(ubyte compressed) {
|
||||||
|
if compressed {
|
||||||
|
print_name_uppercase()
|
||||||
|
txt.print(" TL:")
|
||||||
|
txt.print_ub(techlevel+1)
|
||||||
|
txt.chrout(' ')
|
||||||
|
txt.print(econnames[economy])
|
||||||
|
txt.chrout(' ')
|
||||||
|
txt.print(govnames[govtype])
|
||||||
|
} else {
|
||||||
|
txt.print("\n\nSystem: ")
|
||||||
|
print_name_uppercase()
|
||||||
|
txt.print("\nPosition: ")
|
||||||
|
txt.print_ub(x)
|
||||||
|
txt.chrout('\'')
|
||||||
|
txt.print_ub(y)
|
||||||
|
txt.chrout(' ')
|
||||||
|
txt.chrout('#')
|
||||||
|
txt.print_ub(number)
|
||||||
|
txt.print("\nEconomy: ")
|
||||||
|
txt.print(econnames[economy])
|
||||||
|
txt.print("\nGovernment: ")
|
||||||
|
txt.print(govnames[govtype])
|
||||||
|
txt.print("\nTech Level: ")
|
||||||
|
txt.print_ub(techlevel+1)
|
||||||
|
txt.print("\nTurnover: ")
|
||||||
|
txt.print_uw(productivity)
|
||||||
|
txt.print("\nRadius: ")
|
||||||
|
txt.print_uw(radius)
|
||||||
|
txt.print("\nPopulation: ")
|
||||||
|
txt.print_ub(population >> 3)
|
||||||
|
txt.print(" Billion\nSpecies: ")
|
||||||
|
if species_is_alien {
|
||||||
|
if species_size < len(species_sizes) {
|
||||||
|
txt.print(species_sizes[species_size])
|
||||||
|
txt.chrout(' ')
|
||||||
|
}
|
||||||
|
if species_color < len(species_colors) {
|
||||||
|
txt.print(species_colors[species_color])
|
||||||
|
txt.chrout(' ')
|
||||||
|
}
|
||||||
|
if species_look < len(species_looks) {
|
||||||
|
txt.print(species_looks[species_look])
|
||||||
|
txt.chrout(' ')
|
||||||
|
}
|
||||||
|
if species_kind < len(species_kinds) {
|
||||||
|
txt.print(species_kinds[species_kind])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
txt.print("Human Colonials")
|
||||||
|
}
|
||||||
|
txt.chrout('\n')
|
||||||
|
txt.print(soup())
|
||||||
|
txt.chrout('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub print_name_uppercase() {
|
||||||
|
ubyte c
|
||||||
|
for c in name
|
||||||
|
txt.chrout(c | 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY {
|
||||||
|
%asm {{
|
||||||
|
sty P8ZP_SCRATCH_REG
|
||||||
|
sec
|
||||||
|
sbc #$81
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda wordlists,y
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda wordlists+1,y
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda P8ZP_SCRATCH_REG
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
|
pha
|
||||||
|
iny
|
||||||
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
util {
|
||||||
|
sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte {
|
||||||
|
ubyte ix=0
|
||||||
|
repeat {
|
||||||
|
ubyte pc = @(prefixptr)
|
||||||
|
ubyte sc = @(stringptr)
|
||||||
|
if pc == 0
|
||||||
|
return true
|
||||||
|
; to lowercase for case insensitive compare:
|
||||||
|
pc &= 127
|
||||||
|
sc &= 127
|
||||||
|
if pc != sc
|
||||||
|
return false
|
||||||
|
prefixptr++
|
||||||
|
stringptr++
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sub print_right(ubyte width, uword string) {
|
||||||
|
repeat width - strlen(string) {
|
||||||
|
txt.chrout(' ')
|
||||||
|
}
|
||||||
|
txt.print(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub print_10s(uword value @AY) clobbers(A, X, Y) {
|
||||||
|
%asm {{
|
||||||
|
jsr conv.uword2decimal
|
||||||
|
lda conv.uword2decimal.decTenThousands
|
||||||
|
ldy #0 ; have we started printing?
|
||||||
|
cmp #'0'
|
||||||
|
beq +
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
+ lda conv.uword2decimal.decThousands
|
||||||
|
cmp #'0'
|
||||||
|
bne +
|
||||||
|
cpy #0
|
||||||
|
beq ++
|
||||||
|
+ jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
+ lda conv.uword2decimal.decHundreds
|
||||||
|
cmp #'0'
|
||||||
|
bne +
|
||||||
|
cpy #0
|
||||||
|
beq ++
|
||||||
|
+ jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
+ lda conv.uword2decimal.decTens
|
||||||
|
jsr c64.CHROUT
|
||||||
|
lda #'.'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
lda conv.uword2decimal.decOnes
|
||||||
|
jsr c64.CHROUT
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub testX() {
|
||||||
|
%asm {{
|
||||||
|
stx _saveX
|
||||||
|
lda #13
|
||||||
|
jsr txt.chrout
|
||||||
|
lda _saveX
|
||||||
|
jsr txt.print_ub
|
||||||
|
lda #13
|
||||||
|
jsr txt.chrout
|
||||||
|
ldx _saveX
|
||||||
|
rts
|
||||||
|
_saveX .byte 0
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
%import graphics
|
%import graphics
|
||||||
%zeropage floatsafe
|
%zeropage floatsafe
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
@ -172,10 +172,10 @@ expression :
|
|||||||
| left = expression EOL? bop = ('+' | '-' ) EOL? right = expression
|
| left = expression EOL? bop = ('+' | '-' ) EOL? right = expression
|
||||||
| left = expression EOL? bop = ('<<' | '>>' ) EOL? right = expression
|
| left = expression EOL? bop = ('<<' | '>>' ) EOL? right = expression
|
||||||
| left = expression EOL? bop = ('<' | '>' | '<=' | '>=') EOL? right = expression
|
| left = expression EOL? bop = ('<' | '>' | '<=' | '>=') EOL? right = expression
|
||||||
| left = expression EOL? bop = ('==' | '!=') EOL? right = expression
|
|
||||||
| left = expression EOL? bop = '&' EOL? right = expression
|
| left = expression EOL? bop = '&' EOL? right = expression
|
||||||
| left = expression EOL? bop = '^' EOL? right = expression
|
| left = expression EOL? bop = '^' EOL? right = expression
|
||||||
| left = expression EOL? bop = '|' EOL? right = expression
|
| left = expression EOL? bop = '|' EOL? right = expression
|
||||||
|
| left = expression EOL? bop = ('==' | '!=') EOL? right = expression
|
||||||
| rangefrom = expression rto = ('to'|'downto') rangeto = expression ('step' rangestep = expression)? // can't create separate rule due to mutual left-recursion
|
| rangefrom = expression rto = ('to'|'downto') rangeto = expression ('step' rangestep = expression)? // can't create separate rule due to mutual left-recursion
|
||||||
| left = expression EOL? bop = 'and' EOL? right = expression
|
| left = expression EOL? bop = 'and' EOL? right = expression
|
||||||
| left = expression EOL? bop = 'or' EOL? right = expression
|
| left = expression EOL? bop = 'or' EOL? right = expression
|
||||||
|
Reference in New Issue
Block a user