Compare commits

...

133 Commits
v6.1 ... v6.4

Author SHA1 Message Date
4cae2c56ec implemented last remaining codegen for word-byte division and remainders. 2021-03-25 22:03:36 +01:00
d840975054 remove unreached error checks 2021-03-25 21:47:05 +01:00
1b14da6c03 compiler warning instead of crash when attempting to assign invalid array value to other array 2021-03-24 22:01:22 +01:00
292640b17a asmgen: string values cannot be typecasted 2021-03-24 21:49:33 +01:00
112a7b09f2 added codegen for expression that needs the status-flag register result as a value on the stack 2021-03-24 21:42:27 +01:00
863ec9ce8a Merge pull request #26 from Elektron72/vim-syntax
Add support for built-in functions to Vim syntax file (and other fixes)
2021-03-24 20:50:53 +01:00
2eb346a205 Add support for built-ins to Vim syntax file
This commit adds support for highlighting built-in functions and
variables to the Vim syntax file.
2021-03-23 19:53:20 -04:00
8092355acb Add syntax sync to Vim syntax file
This will make the highlighting slightly slower, but will fix issues
with assembly not being highlighted properly.
2021-03-23 19:41:34 -04:00
e7ef2ed31b todo 2021-03-23 23:48:53 +01:00
af4de6d2fc replacing complex array indexer expressions moved to BeforeAsmGeneration + use cx16 virtualregister instead of adding temp variables for this 2021-03-23 23:44:14 +01:00
69f73dd779 Add void operator to Vim syntax file 2021-03-23 18:12:52 -04:00
9706b46012 credits 2021-03-23 02:50:16 +01:00
6d75dd3bb8 Merge pull request #25 from Elektron72/vim-syntax
Add Vim syntax highlighting file
2021-03-23 01:29:57 +01:00
bd295ffc99 array indexer complexity is now dealt with in the asm-generator only 2021-03-22 19:40:57 +01:00
07ce3e3c9d Add Vim syntax highlighting file
The readme file in syntax-files/Vim/ was also modified to give simple
installation instructions.
2021-03-22 12:13:20 -04:00
cbc3e37a89 stuff 2021-03-22 02:29:59 +01:00
3626828ceb decided 2021-03-22 01:45:19 +01:00
24b77fb5a5 comments. 2021-03-21 21:10:29 +01:00
1505fe686a updated vtui example 2021-03-21 20:40:35 +01:00
0991131fa8 don't stript unused asmsub definitions 2021-03-21 19:55:21 +01:00
2e928bd3c2 fix compiler crash for certain str argument to asm functions 2021-03-21 18:39:39 +01:00
ca868ae19e added cx16.vload() (like the VLOAD basic instruction) 2021-03-20 02:39:53 +01:00
3e286dd14c move test 2021-03-18 19:34:54 +01:00
11247d52b1 fix bugs in word <= and >= comparisons 2021-03-18 19:20:48 +01:00
1dbc902513 fix bugs in uword <= and >= comparisons 2021-03-18 18:41:41 +01:00
330e691b78 wip 2021-03-18 02:43:08 +01:00
6780d4f562 fix bug in uword > comparison 2021-03-18 02:21:21 +01:00
b30b8b7368 fix bug in float < and > comparisons 2021-03-18 01:41:54 +01:00
3df182b8c3 created extensive comparison test suite 2021-03-18 00:50:13 +01:00
7f21d89fea moved test programs to test folder in compiler module 2021-03-17 20:15:16 +01:00
2b267b4ba1 IDE syntax 2021-03-17 19:36:37 +01:00
ef64881528 busy creating extensive comparison test suite 2021-03-17 19:35:22 +01:00
9a6bd760bd fixed issues in uword '>' 2021-03-16 23:40:32 +01:00
00b9766aea fixed issues in word '>' 2021-03-16 23:22:58 +01:00
6381d2b6ac improve word '<', word (u)word '<=' , uword '>=' codegen 2021-03-16 18:15:47 +01:00
d2ab5f230d example TODOs 2021-03-16 01:09:25 +01:00
824b41d457 improve word '>' and '>=' codegen 2021-03-16 00:48:03 +01:00
b5523c7077 don't optimize with inlining too aggressively (code bloat) 2021-03-16 00:33:15 +01:00
eb3594b18c revert to just using comparison expressions in graphics code (we're optimizing these now!) 2021-03-16 00:11:55 +01:00
852d85d010 improve uword '<' and '>' codegen 2021-03-16 00:03:51 +01:00
5e0aef04fe improve (u)byte '>=' codegen 2021-03-15 23:20:16 +01:00
a00c693f93 improve (u)byte '<=' codegen 2021-03-15 23:17:04 +01:00
c943da1448 improve ubyte '<' and '>' codegen 2021-03-15 23:12:52 +01:00
b630fae580 refactor byte '==', '!=', '<' and '>' codegen 2 2021-03-15 23:08:30 +01:00
38e40084f1 refactor byte '==', '!=', '<' and '>' codegen 2021-03-15 22:47:18 +01:00
bf23ad78e6 improve byte '<' and '>' codegen 2021-03-15 22:26:00 +01:00
ded1d19737 improve '==' and '!=' codegen 2021-03-15 19:29:32 +01:00
496a3b0d2c todo 2021-03-15 18:56:25 +01:00
6922333755 add a cmp(x,y) function that returns no value but only sets the status bits based off the comparison (can be used with a conditional jump afterwards) 2021-03-13 15:11:22 +01:00
a00c39e9cf compiler error instead of crash when using functioncall without returnvalue 2021-03-12 19:31:04 +01:00
1c1da8e38e additional optimization to the bresenham line routines 2021-03-10 18:49:40 +01:00
50a306f492 line drawing fixes 2021-03-09 22:11:30 +01:00
6995ee2d17 fix cx16 bresenham line inaccuracy 2021-03-09 22:04:19 +01:00
6c60ea9cac allocate even more c64 zeropage locations for floats 2021-03-09 21:47:36 +01:00
2431ed811a don't remove typecasts in asmsub argument lists 2021-03-09 21:29:48 +01:00
6bd205c02a fix c64 bresenham line inaccuracy 2021-03-09 21:07:55 +01:00
62ec77e148 ver 2021-03-08 23:35:52 +01:00
9120e1de88 fix ubyte/uword to float conversion crashes on Commander X16 2021-03-08 23:21:52 +01:00
60e169bd87 added optimized integer square (x*x) routine 2021-03-08 23:08:47 +01:00
e4bca5fe47 version 2021-03-06 23:07:30 +01:00
a1729b65ab fix min(), max(), sum(), abs() 2021-03-06 22:57:22 +01:00
2950d26c8e array and struct value assignments now via memcopy instead of assignment per element 2021-03-06 22:10:03 +01:00
4f8d4a9585 use memcopy to assign arrays 2021-03-06 19:01:16 +01:00
d787795759 simplified 2021-03-06 15:43:23 +01:00
cf74e73e27 IDEA syntax colors 2021-03-06 15:23:58 +01:00
2770254fd9 removed inline assembly from bobs demo 2021-03-06 14:31:26 +01:00
de04bd8cfa added more convenient number-to-string functions to conv library 2021-03-06 13:47:27 +01:00
076a547f91 added more convenient number-to-string functions to conv library 2021-03-06 13:34:57 +01:00
dffd0a2706 added fastrnd8() with the old rnd() generator code in it, new code for rnd() uses the much better rndw() generator now. 2021-03-05 22:49:14 +01:00
6c66f86103 todo 2021-03-05 21:07:35 +01:00
26502c949a add unlimited bobs example 2021-03-05 19:05:13 +01:00
8dfe510883 avoid compiler crash when evaluating const expressions fails due to things like integer out of bounds 2021-03-04 01:32:02 +01:00
96ba9f5902 spelling correction 2021-03-04 01:31:29 +01:00
3a6ba0ab71 added 'kefrenbars' example 2021-03-03 01:09:18 +01:00
32d894d6b6 optimized repeat loop for word counts 2021-02-28 21:22:46 +01:00
543efa4299 attempt 2 at optimizing repeats 2021-02-28 21:02:17 +01:00
eba0708099 Revert "optimized repeat loop for word counts"
This reverts commit 51e6bf0d
2021-02-28 20:29:28 +01:00
51e6bf0d45 optimized repeat loop for word counts 2021-02-28 17:34:18 +01:00
07b5c44a54 preparing to optimize 16 bit repeat loop 2021-02-28 17:13:15 +01:00
9fe32c1c34 codegen uses 'bra' on 65c02 instead of 'jmp' 2021-02-28 16:46:08 +01:00
0e0278c84a for loops now use 'bra' if available 2021-02-28 16:35:59 +01:00
dea775a9cd package refactor 2021-02-28 16:29:15 +01:00
7e3e18a5c7 deal with 'bra' better on 65c02 2021-02-28 16:20:03 +01:00
8e3ebc84f0 readme 2021-02-28 15:40:04 +01:00
e6079dfd71 don't always use pha/pla in pointer expression code 2021-02-27 16:21:46 +01:00
2b435fe6a5 vtui example updated to vtui 0.6 2021-02-27 03:30:21 +01:00
4e640b11fd added kernal bank switch trick to rasterbars 2021-02-26 01:16:06 +01:00
8b1e1e68fa switch to Kotlin's new JVM IR compilation 2021-02-26 01:10:00 +01:00
fd11927708 optimized highres 4c position calc a bit 2021-02-26 00:43:51 +01:00
cd500fee8c wording 2021-02-25 00:52:27 +01:00
1bd32c0f19 added animal guessing game example 2021-02-24 22:58:16 +01:00
7aefca3de0 target 2021-02-24 00:17:52 +01:00
f275ed96ea optimized palette.set_color() 2021-02-24 00:01:27 +01:00
d14dac3872 got rid of final traces of heapid, fixed compiler warnings 2021-02-24 00:01:04 +01:00
b0213b0565 vtui lib 2021-02-23 23:31:32 +01:00
c677f0a875 fixed string interning to also consider the alt-encoding 2021-02-23 23:27:44 +01:00
6e65cb2c0a added sounds to cx16 tehtriz 2021-02-23 01:29:45 +01:00
e65c5402d7 added cx16 rasterbars example 2021-02-22 02:11:44 +01:00
334f86480a added irq routines for cx16 2021-02-22 00:48:41 +01:00
0e62f5b759 don't remove subroutines in a block marked with "force_output" 2021-02-21 23:25:26 +01:00
edf9a500d3 kernel -> kernal 2021-02-21 22:48:06 +01:00
001d01fdaf slight tweak to 64tass .cpu to enable wdc65c02 variant on cx16 with its extra opcodes 2021-02-21 22:45:23 +01:00
a95677564e changed system irq/rasterirq setting routines 2021-02-21 22:23:50 +01:00
4aca8bb8df also track subroutines in the callgraph that only get their address taken 2021-02-21 22:09:49 +01:00
5540482888 compiler error for duplicate when choice labels 2021-02-21 21:26:15 +01:00
00d735249b fix pointer write outside zeropage 2021-02-21 16:22:44 +01:00
b5289511ba don't remove empty when choice from the list of choices! 2021-02-21 15:11:19 +01:00
b6ded8501f added 'align_word' and 'align_page' block options to control block start address alignment in the assembler 2021-02-21 01:24:44 +01:00
781915d2cf reducing dependencies 2021-02-20 17:54:33 +01:00
f4cef3eaf2 reducing dependencies 2021-02-20 17:19:54 +01:00
d23c2eed86 test 2021-02-20 16:58:24 +01:00
15695a304e start address of blocks without explicit memory address, is now word-aligned in memory 2021-02-20 03:06:00 +01:00
6319269976 underscore '_' is now also mapped to petscii, to the graphical symbol 2021-02-20 02:55:06 +01:00
0ed3d951a7 don't require carry parameter Pc to asmsubs to be last 2021-02-20 02:27:57 +01:00
2aa39757b4 reduce dependencies on global compilationtarget 2021-02-19 19:02:29 +01:00
39d32a3600 refactor cpuCheck 2021-02-19 18:48:12 +01:00
219d17de34 reduce dependencies on global compilaiontarget 2021-02-19 18:33:54 +01:00
9bb5b454e4 reduce dependencies on global compilaiontarget 2021-02-18 23:44:26 +01:00
2412f8c531 added cx16 vtui example 2021-02-18 23:16:38 +01:00
8701d684e6 added cx16 vtui example 2021-02-18 03:45:06 +01:00
b543cc34cd no longer warn about removing unused asmsubs 2021-02-18 01:52:56 +01:00
791dbbab9b fixed block label itself not getting the correct memory address in the assembly
fixed %asmbinary relative path issues
2021-02-18 01:28:33 +01:00
ac0b1da3fc machinedefinition doesn't import system libs itself anymore 2021-02-18 00:43:32 +01:00
2f97aedc3c fixed invalid removal of string tag from memory() 2021-02-16 23:58:31 +01:00
ab544ee965 improved string constant interning; no longer output duplicate strings in the Ast 2021-02-16 23:43:38 +01:00
fa527f8624 restored optimization of txt.print() with strings of lengths 1 or 2 2021-02-16 23:37:11 +01:00
92ee0aefee docs: replaced old invalid c64scr names with txt 2021-02-16 23:28:35 +01:00
99759ae853 enhanced tehtriz blocks to have light edges 2021-02-15 17:48:10 +01:00
81930312ff added textio.setcc2() on commanderX16 to enable setting fg+bg colors. 2021-02-15 17:47:48 +01:00
194fbcdd91 todos 2021-02-15 04:41:33 +01:00
1e3930aae2 fix bug in evaluating logical expressions if one of the operands was not boolean 1 or 0 2021-02-14 18:29:05 +01:00
62dda4d891 fix asm bug in conv.any2uword 2021-02-14 17:13:56 +01:00
2b870fb9f7 get rid of compiled examples. Just compile them yourself... 2021-02-14 17:13:29 +01:00
162 changed files with 5564 additions and 4800 deletions

2
.idea/kotlinc.xml generated
View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="Kotlin2JvmCompilerArguments"> <component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="1.8" /> <option name="jvmTarget" value="11" />
</component> </component>
</project> </project>

View File

@ -23,13 +23,14 @@ https://prog8.readthedocs.io/
What does Prog8 provide? What does Prog8 provide?
------------------------ ------------------------
- big reduction of source code length over raw assembly - reduction of source code length over raw assembly
- modularity, symbol scoping, subroutines - modularity, symbol scoping, subroutines
- various data types other than just bytes (16-bit words, floats, strings) - various data types other than just bytes (16-bit words, floats, strings)
- automatic variable allocations, automatic string and array variables and string sharing - automatic variable allocations, automatic string and array variables and string sharing
- subroutines with an input- and output parameter signature - subroutines with input parameters and result values
- no stack frame allocations because parameters and local variables are automatically allocated statically - high-level program optimizations
- constant folding in expressions and other high-level program optimizations - small program boilerplate/compilersupport overhead
- sane variable initialization, programs can be restarted again just fine after exiting to basic
- conditional branches - conditional branches
- floating point operations (requires the C64 Basic ROM routines for this) - floating point operations (requires the C64 Basic ROM routines for this)
- 'when' statement to provide a concise jump table alternative to if/elseif chains - 'when' statement to provide a concise jump table alternative to if/elseif chains
@ -38,8 +39,10 @@ What does Prog8 provide?
- various powerful built-in libraries to do I/O, number conversions, graphics and more - various powerful built-in libraries to do I/O, number conversions, graphics and more
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses - convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
- fast execution speed due to compilation to native assembly code - fast execution speed due to compilation to native assembly code
- variables are allocated statically
- inline assembly allows you to have full control when every cycle or byte matters - inline assembly allows you to have full control when every cycle or byte matters
- supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, and provides them also on the C64. - supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, and provides them also on the C64.
- encode strings and characters into petscii or screencodes as desired (C64/Cx16)
*Rapid edit-compile-run-debug cycle:* *Rapid edit-compile-run-debug cycle:*
@ -52,7 +55,7 @@ What does Prog8 provide?
- "c64": Commodore-64 (6510 CPU = almost a 6502) - "c64": Commodore-64 (6510 CPU = almost a 6502)
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) - "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)! - If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!

View File

@ -34,6 +34,7 @@ dependencies {
compileKotlin { compileKotlin {
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = "11"
useIR = true
// verbose = true // verbose = true
// freeCompilerArgs += "-XXLanguage:+NewInference" // freeCompilerArgs += "-XXLanguage:+NewInference"
} }
@ -42,6 +43,7 @@ compileKotlin {
compileTestKotlin { compileTestKotlin {
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = "11"
useIR = true
} }
} }

View File

@ -1,5 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4"> <module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="Python" name="Python">
<configuration sdkName="Python 3.9" />
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true"> <component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
@ -14,5 +19,6 @@
<orderEntry type="library" name="unittest-libs" level="project" /> <orderEntry type="library" name="unittest-libs" level="project" />
<orderEntry type="library" name="kotlinx-cli-jvm" level="project" /> <orderEntry type="library" name="kotlinx-cli-jvm" level="project" />
<orderEntry type="module" module-name="compilerAst" /> <orderEntry type="module" module-name="compilerAst" />
<orderEntry type="library" name="Python 3.9 interpreter library" level="application" />
</component> </component>
</module> </module>

View File

@ -428,7 +428,9 @@ var_fac1_greater_f .proc
cmp #1 cmp #1
beq + beq +
lda #0 lda #0
+ rts rts
+ lda #1
rts
.pend .pend
var_fac1_greatereq_f .proc var_fac1_greatereq_f .proc

View File

@ -2,7 +2,7 @@
%import textio %import textio
; bitmap pixel graphics module for the C64 ; bitmap pixel graphics module for the C64
; only black/white monchrome 320x200 for now ; only black/white monochrome 320x200 for now
; assumes bitmap screen memory is $2000-$3fff ; assumes bitmap screen memory is $2000-$3fff
graphics { graphics {
@ -34,36 +34,33 @@ graphics {
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) { sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
; Bresenham algorithm. ; Bresenham algorithm.
; This code special-cases various quadrant loops to allow simple ++ and -- operations. ; This code special-cases various quadrant loops to allow simple ++ and -- operations.
; TODO there are some slight errors at the first/last pixels in certain slopes...??
if y1>y2 { if y1>y2 {
; make sure dy is always positive to have only 4 instead of 8 special cases ; make sure dy is always positive to have only 4 instead of 8 special cases
swap(x1, x2) swap(x1, x2)
swap(y1, y2) swap(y1, y2)
} }
word @zp dx = x2-x1 as word word @zp dx = (x2 as word)-x1
word @zp dy = y2-y1 word @zp dy = (y2 as word)-y1
if dx==0 { if dx==0 {
vertical_line(x1, y1, abs(dy)+1 as ubyte) vertical_line(x1, y1, abs(dy) as ubyte +1)
return return
} }
if dy==0 { if dy==0 {
if x1>x2 if x1>x2
x1=x2 x1=x2
horizontal_line(x1, y1, abs(dx)+1 as uword) horizontal_line(x1, y1, abs(dx) as uword +1)
return return
} }
; TODO rewrite the rest in optimized assembly
word @zp d = 0 word @zp d = 0
ubyte positive_ix = true ubyte positive_ix = true
if dx < 0 { if dx < 0 {
dx = -dx dx = -dx
positive_ix = false positive_ix = false
} }
dx *= 2 word @zp dx2 = dx*2
dy *= 2 word @zp dy2 = dy*2
internal_plotx = x1 internal_plotx = x1
if dx >= dy { if dx >= dy {
@ -73,10 +70,10 @@ graphics {
if internal_plotx==x2 if internal_plotx==x2
return return
internal_plotx++ internal_plotx++
d += dy d += dy2
if d > dx { if d > dx {
y1++ y1++
d -= dx d -= dx2
} }
} }
} else { } else {
@ -85,10 +82,10 @@ graphics {
if internal_plotx==x2 if internal_plotx==x2
return return
internal_plotx-- internal_plotx--
d += dy d += dy2
if d > dx { if d > dx {
y1++ y1++
d -= dx d -= dx2
} }
} }
} }
@ -100,10 +97,10 @@ graphics {
if y1 == y2 if y1 == y2
return return
y1++ y1++
d += dx d += dx2
if d > dy { if d > dy {
internal_plotx++ internal_plotx++
d -= dy d -= dy2
} }
} }
} else { } else {
@ -112,10 +109,10 @@ graphics {
if y1 == y2 if y1 == y2
return return
y1++ y1++
d += dx d += dx2
if d > dy { if d > dy {
internal_plotx-- internal_plotx--
d -= dy d -= dy2
} }
} }
} }

View File

@ -11,7 +11,7 @@ c64 {
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte &ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
&ubyte TIME_MID = $a1 ; .. mid byte &ubyte TIME_MID = $a1 ; .. mid byte
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec &ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
&ubyte STATUS = $90 ; kernel status variable for I/O &ubyte STATUS = $90 ; kernal status variable for I/O
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ) &ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ) &ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
@ -178,7 +178,7 @@ c64 {
; ---- C64 ROM kernal routines ---- ; ---- C64 ROM kernal routines ----
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use c64scr.print instead) romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
@ -202,7 +202,7 @@ romsub $FFAE = UNLSN() clobbers(A) ; command serial
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 secondary @ 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(X,Y) -> ubyte @Pc, ubyte @A ; (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
@ -211,8 +211,8 @@ romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320
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(X, 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, uword @ XY ; (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(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A) romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
@ -294,6 +294,12 @@ asmsub init_system() {
}} }}
} }
asmsub init_system_phase2() {
%asm {{
rts ; no phase 2 steps on the C64
}}
}
asmsub disable_runstop_and_charsetswitch() clobbers(A) { asmsub disable_runstop_and_charsetswitch() clobbers(A) {
%asm {{ %asm {{
lda #$80 lda #$80
@ -304,27 +310,13 @@ asmsub disable_runstop_and_charsetswitch() clobbers(A) {
}} }}
} }
asmsub set_irqvec_excl() clobbers(A) { asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
%asm {{
sei
lda #<_irq_handler
sta c64.CINV
lda #>_irq_handler
sta c64.CINV+1
cli
rts
_irq_handler jsr set_irqvec._irq_handler_init
jsr irq.irq
jsr set_irqvec._irq_handler_end
lda #$ff
sta c64.VICIRQ ; acknowledge raster irq
lda c64.CIA1ICR ; acknowledge CIA1 interrupt
jmp c64.IRQDFEND ; end irq processing - don't call kernel
}}
}
asmsub set_irqvec() clobbers(A) {
%asm {{ %asm {{
sta _modified+1
sty _modified+2
lda #0
adc #0
sta _use_kernal
sei sei
lda #<_irq_handler lda #<_irq_handler
sta c64.CINV sta c64.CINV
@ -333,9 +325,23 @@ asmsub set_irqvec() clobbers(A) {
cli cli
rts rts
_irq_handler jsr _irq_handler_init _irq_handler jsr _irq_handler_init
jsr irq.irq _modified jsr $ffff ; modified
jsr _irq_handler_end jsr _irq_handler_end
jmp c64.IRQDFRT ; continue with normal kernel irq routine lda _use_kernal
bne +
lda #$ff
sta c64.VICIRQ ; acknowledge raster irq
lda c64.CIA1ICR ; acknowledge CIA1 interrupt
; end irq processing - don't use kernal's irq handling
pla
tay
pla
tax
pla
rti
+ jmp c64.IRQDFRT ; continue with normal kernal irq routine
_use_kernal .byte 0
_irq_handler_init _irq_handler_init
; save all zp scratch registers and the X register as these might be clobbered by the irq routine ; save all zp scratch registers and the X register as these might be clobbered by the irq routine
@ -388,7 +394,7 @@ IRQ_SCRATCH_ZPWORD2 .word 0
}} }}
} }
asmsub restore_irqvec() clobbers(A) { asmsub restore_irq() clobbers(A) {
%asm {{ %asm {{
sei sei
lda #<c64.IRQDFRT lda #<c64.IRQDFRT
@ -404,8 +410,15 @@ asmsub restore_irqvec() clobbers(A) {
}} }}
} }
asmsub set_rasterirq(uword rasterpos @ AY) clobbers(A) { asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0, ubyte useKernal @Pc) clobbers(A) {
%asm {{ %asm {{
sta _modified+1
sty _modified+2
lda #0
adc #0
sta set_irq._use_kernal
lda cx16.r0
ldy cx16.r0+1
sei sei
jsr _setup_raster_irq jsr _setup_raster_irq
lda #<_raster_irq_handler lda #<_raster_irq_handler
@ -416,12 +429,21 @@ asmsub set_rasterirq(uword rasterpos @ AY) clobbers(A) {
rts rts
_raster_irq_handler _raster_irq_handler
jsr set_irqvec._irq_handler_init jsr set_irq._irq_handler_init
jsr irq.irq _modified jsr $ffff ; modified
jsr set_irqvec._irq_handler_end jsr set_irq._irq_handler_end
lda #$ff lda #$ff
sta c64.VICIRQ ; acknowledge raster irq sta c64.VICIRQ ; acknowledge raster irq
jmp c64.IRQDFRT lda set_irq._use_kernal
bne +
; end irq processing - don't use kernal's irq handling
pla
tay
pla
tax
pla
rti
+ jmp c64.IRQDFRT ; continue with kernal irq routine
_setup_raster_irq _setup_raster_irq
pha pha
@ -445,28 +467,6 @@ _setup_raster_irq
}} }}
} }
asmsub set_rasterirq_excl(uword rasterpos @ AY) clobbers(A) {
%asm {{
sei
jsr set_rasterirq._setup_raster_irq
lda #<_raster_irq_handler
sta c64.CINV
lda #>_raster_irq_handler
sta c64.CINV+1
cli
rts
_raster_irq_handler
jsr set_irqvec._irq_handler_init
jsr irq.irq
jsr set_irqvec._irq_handler_end
lda #$ff
sta c64.VICIRQ ; acknowledge raster irq
jmp c64.IRQDFEND ; end irq processing - don't call kernel
}}
}
; ---- end of C64 specific system utility routines ---- ; ---- end of C64 specific system utility routines ----
} }

View File

@ -586,7 +586,7 @@ _colormod sta $ffff ; modified
} }
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) { asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
; ---- safe wrapper around PLOT kernel routine, to save the X register. ; ---- safe wrapper around PLOT kernal routine, to save the X register.
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
tax tax

View File

@ -7,239 +7,213 @@ conv {
; ----- number conversions to decimal strings ---- ; ----- number conversions to decimal strings ----
asmsub ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @A, ubyte @X { str string_out = "????????????????" ; result buffer for the string conversion routines
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
asmsub str_ub0 (ubyte value @ A) clobbers(A,Y) {
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
%asm {{ %asm {{
ldy #uword2decimal.ASCII_0_OFFSET phx
bne uword2decimal.hex_try200 jsr conv.ubyte2decimal
rts sty string_out
sta string_out+1
stx string_out+2
lda #0
sta string_out+3
plx
rts
}} }}
} }
asmsub uword2decimal (uword value @AY) -> ubyte @Y, ubyte @A, ubyte @X { asmsub str_ub (ubyte value @ A) clobbers(A,Y) {
; ---- convert 16 bit uword in A/Y to decimal ; ---- convert the ubyte in A in decimal string form, without left padding 0s
; output in uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes
; (these are terminated by a zero byte so they can be easily printed)
; also returns Y = 100's, A = 10's, X = 1's
%asm {{ %asm {{
phx
;Convert 16 bit Hex to Decimal (0-65535) Rev 2 ldy #0
;By Omegamatrix Further optimizations by tepples sty P8ZP_SCRATCH_B1
; routine from http://forums.nesdev.com/viewtopic.php?f=2&t=11341&start=15 jsr conv.ubyte2decimal
_output_byte_digits
;HexToDec99 ; hundreds?
; start in A cpy #'0'
; end with A = 10's, decOnes (also in X) beq +
;HexToDec255
; start in A
; end with Y = 100's, A = 10's, decOnes (also in X)
;HexToDec999
; start with A = high byte, Y = low byte
; end with Y = 100's, A = 10's, decOnes (also in X)
; requires 1 extra temp register on top of decOnes, could combine
; these two if HexToDec65535 was eliminated...
;HexToDec65535
; start with A/Y (low/high) as 16 bit value
; end with decTenThousand, decThousand, Y = 100's, A = 10's, decOnes (also in X)
; (irmen: I store Y and A in decHundreds and decTens too, so all of it can be easily printed)
ASCII_0_OFFSET = $30
temp = P8ZP_SCRATCH_B1 ; byte in zeropage
hexHigh = P8ZP_SCRATCH_W1 ; byte in zeropage
hexLow = P8ZP_SCRATCH_W1+1 ; byte in zeropage
HexToDec65535; SUBROUTINE
sty hexHigh ;3 @9
sta hexLow ;3 @12
tya
tax ;2 @14
lsr a ;2 @16
lsr a ;2 @18 integer divide 1024 (result 0-63)
cpx #$A7 ;2 @20 account for overflow of multiplying 24 from 43,000 ($A7F8) onward,
adc #1 ;2 @22 we can just round it to $A700, and the divide by 1024 is fine...
;at this point we have a number 1-65 that we have to times by 24,
;add to original sum, and Mod 1024 to get a remainder 0-999
sta temp ;3 @25
asl a ;2 @27
adc temp ;3 @30 x3
tay ;2 @32
lsr a ;2 @34
lsr a ;2 @36
lsr a ;2 @38
lsr a ;2 @40
lsr a ;2 @42
tax ;2 @44
tya ;2 @46
asl a ;2 @48
asl a ;2 @50
asl a ;2 @52
clc ;2 @54
adc hexLow ;3 @57
sta hexLow ;3 @60
txa ;2 @62
adc hexHigh ;3 @65
sta hexHigh ;3 @68
ror a ;2 @70
lsr a ;2 @72
tay ;2 @74 integer divide 1,000 (result 0-65)
lsr a ;2 @76 split the 1,000 and 10,000 digit
tax ;2 @78
lda ShiftedBcdTab,x ;4 @82
tax ;2 @84
rol a ;2 @86
and #$0F ;2 @88
ora #ASCII_0_OFFSET
sta decThousands ;3 @91
txa ;2 @93
lsr a ;2 @95
lsr a ;2 @97
lsr a ;2 @99
ora #ASCII_0_OFFSET
sta decTenThousands ;3 @102
lda hexLow ;3 @105
cpy temp ;3 @108
bmi _doSubtract ;2³ @110/111
beq _useZero ;2³ @112/113
adc #23 + 24 ;2 @114
_doSubtract
sbc #23 ;2 @116
sta hexLow ;3 @119
_useZero
lda hexHigh ;3 @122
sbc #0 ;2 @124
Start100s
and #$03 ;2 @126
tax ;2 @128 0,1,2,3
cmp #2 ;2 @130
rol a ;2 @132 0,2,5,7
ora #ASCII_0_OFFSET
tay ;2 @134 Y = Hundreds digit
lda hexLow ;3 @137
adc Mod100Tab,x ;4 @141 adding remainder of 256, 512, and 256+512 (all mod 100)
bcs hex_doSub200 ;2³ @143/144
hex_try200
cmp #200 ;2 @145
bcc hex_try100 ;2³ @147/148
hex_doSub200
iny ;2 @149
iny ;2 @151
sbc #200 ;2 @153
hex_try100
cmp #100 ;2 @155
bcc HexToDec99 ;2³ @157/158
iny ;2 @159
sbc #100 ;2 @161
HexToDec99; SUBROUTINE
lsr a ;2 @163
tax ;2 @165
lda ShiftedBcdTab,x ;4 @169
tax ;2 @171
rol a ;2 @173
and #$0F ;2 @175
ora #ASCII_0_OFFSET
sta decOnes ;3 @178
txa ;2 @180
lsr a ;2 @182
lsr a ;2 @184
lsr a ;2 @186
ora #ASCII_0_OFFSET
; irmen: load X with ones, and store Y and A too, for easy printing afterwards
sty decHundreds
sta decTens
ldx decOnes
rts ;6 @192 Y=hundreds, A = tens digit, X=ones digit
HexToDec999; SUBROUTINE
sty hexLow ;3 @9
jmp Start100s ;3 @12
Mod100Tab
.byte 0,56,12,56+12
ShiftedBcdTab
.byte $00,$01,$02,$03,$04,$08,$09,$0A,$0B,$0C
.byte $10,$11,$12,$13,$14,$18,$19,$1A,$1B,$1C
.byte $20,$21,$22,$23,$24,$28,$29,$2A,$2B,$2C
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
decTenThousands .byte 0
decThousands .byte 0
decHundreds .byte 0
decTens .byte 0
decOnes .byte 0
.byte 0 ; zero-terminate the decimal output string
}}
}
asmsub byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- A (signed byte) to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
; note: if the number is negative, you have to deal with the '-' yourself!
%asm {{
cmp #0
bpl +
eor #255
clc
adc #1
+ jmp ubyte2decimal
}}
}
asmsub ubyte2hex (ubyte value @A) -> ubyte @A, ubyte @Y {
; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y)
%asm {{
stx P8ZP_SCRATCH_REG
pha pha
and #$0f tya
tax ldy P8ZP_SCRATCH_B1
ldy _hex_digits,x sta string_out,y
pla pla
lsr a inc P8ZP_SCRATCH_B1
lsr a ; tens?
lsr a + ldy P8ZP_SCRATCH_B1
lsr a cmp #'0'
tax beq +
lda _hex_digits,x sta string_out,y
ldx P8ZP_SCRATCH_REG iny
rts + ; ones.
txa
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well sta string_out,y
iny
lda #0
sta string_out,y
plx
rts
}} }}
} }
asmsub uword2hex (uword value @AY) clobbers(A,Y) { asmsub str_b (byte value @ A) clobbers(A,Y) {
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated) ; ---- convert the byte in A in decimal string form, without left padding 0s
%asm {{ %asm {{
sta P8ZP_SCRATCH_REG phx
tya ldy #0
jsr ubyte2hex sty P8ZP_SCRATCH_B1
sta output cmp #0
sty output+1 bpl +
lda P8ZP_SCRATCH_REG pha
jsr ubyte2hex lda #'-'
sta output+2 sta string_out
sty output+3 inc P8ZP_SCRATCH_B1
rts pla
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier) + jsr conv.byte2decimal
bra str_ub._output_byte_digits
}}
}
asmsub str_ubhex (ubyte value @ A) clobbers(A,Y) {
; ---- convert the ubyte in A in hex string form
%asm {{
jsr conv.ubyte2hex
sta string_out
sty string_out+1
lda #0
sta string_out+2
rts
}}
}
asmsub str_ubbin (ubyte value @ A) clobbers(A,Y) {
; ---- convert the ubyte in A in binary string form
%asm {{
sta P8ZP_SCRATCH_B1
ldy #0
sty string_out+8
ldy #7
- lsr P8ZP_SCRATCH_B1
bcc +
lda #'1'
bne _digit
+ lda #'0'
_digit sta string_out,y
dey
bpl -
rts
}}
}
asmsub str_uwbin (uword value @ AY) clobbers(A,Y) {
; ---- convert the uword in A/Y in binary string form
%asm {{
sta P8ZP_SCRATCH_REG
tya
jsr str_ubbin
ldy #0
sty string_out+16
ldy #7
- lsr P8ZP_SCRATCH_REG
bcc +
lda #'1'
bne _digit
+ lda #'0'
_digit sta string_out+8,y
dey
bpl -
rts
}}
}
asmsub str_uwhex (uword value @ AY) clobbers(A,Y) {
; ---- convert the uword in A/Y in hexadecimal string form (4 digits)
%asm {{
pha
tya
jsr conv.ubyte2hex
sta string_out
sty string_out+1
pla
jsr conv.ubyte2hex
sta string_out+2
sty string_out+3
lda #0
sta string_out+4
rts
}}
}
asmsub str_uw0 (uword value @ AY) clobbers(A,Y) {
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
%asm {{
phx
jsr conv.uword2decimal
ldy #0
- lda conv.uword2decimal.decTenThousands,y
sta string_out,y
beq +
iny
bne -
+ plx
rts
}}
}
asmsub str_uw (uword value @ AY) clobbers(A,Y) {
; ---- convert the uword in A/Y in decimal string form, without left padding 0s
%asm {{
phx
jsr conv.uword2decimal
ldx #0
_output_digits
ldy #0
- lda conv.uword2decimal.decTenThousands,y
beq _allzero
cmp #'0'
bne _gotdigit
iny
bne -
_gotdigit sta string_out,x
inx
iny
lda conv.uword2decimal.decTenThousands,y
bne _gotdigit
_end lda #0
sta string_out,x
plx
rts
_allzero lda #'0'
sta string_out,x
inx
bne _end
}}
}
asmsub str_w (word value @ AY) clobbers(A,Y) {
; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's
%asm {{
cpy #0
bpl str_uw
phx
pha
lda #'-'
sta string_out
tya
eor #255
tay
pla
eor #255
clc
adc #1
bcc +
iny
+ jsr conv.uword2decimal
ldx #1
bne str_uw._output_digits
}} }}
} }
@ -257,7 +231,7 @@ asmsub any2uword(str string @AY) clobbers(Y) -> ubyte @A {
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
ldy #0 ldy #0
lda (P8ZP_SCRATCH_W1) lda (P8ZP_SCRATCH_W1),y
ldy P8ZP_SCRATCH_W1+1 ldy P8ZP_SCRATCH_W1+1
cmp #'$' cmp #'$'
beq _hex beq _hex
@ -520,4 +494,243 @@ _stop
}} }}
} }
; ----- low level number conversions to decimal strings ----
asmsub ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
%asm {{
ldy #uword2decimal.ASCII_0_OFFSET
bne uword2decimal.hex_try200
rts
}}
}
asmsub uword2decimal (uword value @AY) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- convert 16 bit uword in A/Y to decimal
; output in uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes
; (these are terminated by a zero byte so they can be easily printed)
; also returns Y = 100's, A = 10's, X = 1's
%asm {{
;Convert 16 bit Hex to Decimal (0-65535) Rev 2
;By Omegamatrix Further optimizations by tepples
; routine from http://forums.nesdev.com/viewtopic.php?f=2&t=11341&start=15
;HexToDec99
; start in A
; end with A = 10's, decOnes (also in X)
;HexToDec255
; start in A
; end with Y = 100's, A = 10's, decOnes (also in X)
;HexToDec999
; start with A = high byte, Y = low byte
; end with Y = 100's, A = 10's, decOnes (also in X)
; requires 1 extra temp register on top of decOnes, could combine
; these two if HexToDec65535 was eliminated...
;HexToDec65535
; start with A/Y (low/high) as 16 bit value
; end with decTenThousand, decThousand, Y = 100's, A = 10's, decOnes (also in X)
; (irmen: I store Y and A in decHundreds and decTens too, so all of it can be easily printed)
ASCII_0_OFFSET = $30
temp = P8ZP_SCRATCH_B1 ; byte in zeropage
hexHigh = P8ZP_SCRATCH_W1 ; byte in zeropage
hexLow = P8ZP_SCRATCH_W1+1 ; byte in zeropage
HexToDec65535; SUBROUTINE
sty hexHigh ;3 @9
sta hexLow ;3 @12
tya
tax ;2 @14
lsr a ;2 @16
lsr a ;2 @18 integer divide 1024 (result 0-63)
cpx #$A7 ;2 @20 account for overflow of multiplying 24 from 43,000 ($A7F8) onward,
adc #1 ;2 @22 we can just round it to $A700, and the divide by 1024 is fine...
;at this point we have a number 1-65 that we have to times by 24,
;add to original sum, and Mod 1024 to get a remainder 0-999
sta temp ;3 @25
asl a ;2 @27
adc temp ;3 @30 x3
tay ;2 @32
lsr a ;2 @34
lsr a ;2 @36
lsr a ;2 @38
lsr a ;2 @40
lsr a ;2 @42
tax ;2 @44
tya ;2 @46
asl a ;2 @48
asl a ;2 @50
asl a ;2 @52
clc ;2 @54
adc hexLow ;3 @57
sta hexLow ;3 @60
txa ;2 @62
adc hexHigh ;3 @65
sta hexHigh ;3 @68
ror a ;2 @70
lsr a ;2 @72
tay ;2 @74 integer divide 1,000 (result 0-65)
lsr a ;2 @76 split the 1,000 and 10,000 digit
tax ;2 @78
lda ShiftedBcdTab,x ;4 @82
tax ;2 @84
rol a ;2 @86
and #$0F ;2 @88
ora #ASCII_0_OFFSET
sta decThousands ;3 @91
txa ;2 @93
lsr a ;2 @95
lsr a ;2 @97
lsr a ;2 @99
ora #ASCII_0_OFFSET
sta decTenThousands ;3 @102
lda hexLow ;3 @105
cpy temp ;3 @108
bmi _doSubtract ;2³ @110/111
beq _useZero ;2³ @112/113
adc #23 + 24 ;2 @114
_doSubtract
sbc #23 ;2 @116
sta hexLow ;3 @119
_useZero
lda hexHigh ;3 @122
sbc #0 ;2 @124
Start100s
and #$03 ;2 @126
tax ;2 @128 0,1,2,3
cmp #2 ;2 @130
rol a ;2 @132 0,2,5,7
ora #ASCII_0_OFFSET
tay ;2 @134 Y = Hundreds digit
lda hexLow ;3 @137
adc Mod100Tab,x ;4 @141 adding remainder of 256, 512, and 256+512 (all mod 100)
bcs hex_doSub200 ;2³ @143/144
hex_try200
cmp #200 ;2 @145
bcc hex_try100 ;2³ @147/148
hex_doSub200
iny ;2 @149
iny ;2 @151
sbc #200 ;2 @153
hex_try100
cmp #100 ;2 @155
bcc HexToDec99 ;2³ @157/158
iny ;2 @159
sbc #100 ;2 @161
HexToDec99; SUBROUTINE
lsr a ;2 @163
tax ;2 @165
lda ShiftedBcdTab,x ;4 @169
tax ;2 @171
rol a ;2 @173
and #$0F ;2 @175
ora #ASCII_0_OFFSET
sta decOnes ;3 @178
txa ;2 @180
lsr a ;2 @182
lsr a ;2 @184
lsr a ;2 @186
ora #ASCII_0_OFFSET
; irmen: load X with ones, and store Y and A too, for easy printing afterwards
sty decHundreds
sta decTens
ldx decOnes
rts ;6 @192 Y=hundreds, A = tens digit, X=ones digit
HexToDec999; SUBROUTINE
sty hexLow ;3 @9
jmp Start100s ;3 @12
Mod100Tab
.byte 0,56,12,56+12
ShiftedBcdTab
.byte $00,$01,$02,$03,$04,$08,$09,$0A,$0B,$0C
.byte $10,$11,$12,$13,$14,$18,$19,$1A,$1B,$1C
.byte $20,$21,$22,$23,$24,$28,$29,$2A,$2B,$2C
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
decTenThousands .byte 0
decThousands .byte 0
decHundreds .byte 0
decTens .byte 0
decOnes .byte 0
.byte 0 ; zero-terminate the decimal output string
}}
}
asmsub byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- A (signed byte) to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
; note: if the number is negative, you have to deal with the '-' yourself!
%asm {{
cmp #0
bpl +
eor #255
clc
adc #1
+ jmp ubyte2decimal
}}
}
asmsub ubyte2hex (ubyte value @A) -> ubyte @A, ubyte @Y {
; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y)
%asm {{
stx P8ZP_SCRATCH_REG
pha
and #$0f
tax
ldy _hex_digits,x
pla
lsr a
lsr a
lsr a
lsr a
tax
lda _hex_digits,x
ldx P8ZP_SCRATCH_REG
rts
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
}}
}
asmsub uword2hex (uword value @AY) clobbers(A,Y) {
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
%asm {{
sta P8ZP_SCRATCH_REG
tya
jsr ubyte2hex
sta output
sty output+1
lda P8ZP_SCRATCH_REG
jsr ubyte2hex
sta output+2
sty output+3
rts
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
}}
}
} }

View File

@ -79,10 +79,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_W2 sta _tmp
sty P8ZP_SCRATCH_B1 sty P8ZP_SCRATCH_B1
tya tya
ldy P8ZP_SCRATCH_W2 ldy _tmp
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 +
@ -91,6 +91,7 @@ asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
jsr FADD jsr FADD
+ plx + plx
rts rts
_tmp .byte 0
_flt65536 .byte 145,0,0,0,0 ; 65536.0 _flt65536 .byte 145,0,0,0,0 ; 65536.0
}} }}
} }
@ -128,6 +129,14 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
}} }}
} }
asmsub FREADUY (ubyte value @Y) {
; -- 8 bit unsigned Y -> float in fac1
%asm {{
lda #0
jmp GIVAYF
}}
}
sub print_f (float value) { sub print_f (float value) {
; ---- prints the floating point value (without a newline). ; ---- prints the floating point value (without a newline).
%asm {{ %asm {{

View File

@ -2,7 +2,7 @@
; Bitmap pixel graphics routines for the CommanderX16 ; Bitmap pixel graphics routines for the CommanderX16
; Custom routines to use the full-screen 640x480 and 320x240 screen modes. ; Custom routines to use the full-screen 640x480 and 320x240 screen modes.
; (These modes are not supported by the documented GRAPH_xxxx kernel routines) ; (These modes are not supported by the documented GRAPH_xxxx kernal routines)
; ;
; No text layer is currently shown, text can be drawn as part of the bitmap itself. ; No text layer is currently shown, text can be drawn as part of the bitmap itself.
; Note: for similar graphics routines that also work on the C-64, use the "graphics" module instead. ; Note: for similar graphics routines that also work on the C-64, use the "graphics" module instead.
@ -15,13 +15,12 @@
; SCREEN MODE LIST: ; SCREEN MODE LIST:
; mode 0 = reset back to default text mode ; mode 0 = reset back to default text mode
; mode 1 = bitmap 320 x 240 monochrome ; mode 1 = bitmap 320 x 240 monochrome
; mode 2 = bitmap 320 x 240 x 4c (unsupported TODO not yet implemented) ; mode 2 = bitmap 320 x 240 x 4c (TODO not yet implemented)
; mode 3 = bitmap 320 x 240 x 16c (unsupported TODO not yet implemented) ; mode 3 = bitmap 320 x 240 x 16c (TODO not yet implemented)
; mode 4 = bitmap 320 x 240 x 256c ; mode 4 = bitmap 320 x 240 x 256c
; mode 5 = bitmap 640 x 480 monochrome ; mode 5 = bitmap 640 x 480 monochrome
; mode 6 = bitmap 640 x 480 x 4c (unsupported TODO being implemented) ; mode 6 = bitmap 640 x 480 x 4c
; mode 7 = bitmap 640 x 480 x 16c (unsupported due to lack of VRAM) ; higher color dephts in highres are not supported due to lack of VRAM
; mode 8 = bitmap 640 x 480 x 256c (unsupported due to lack of VRAM)
; TODO can we make a FB vector table and emulation routines for the Cx16s' GRAPH_init() call? to replace the builtin 320x200 fb driver? ; TODO can we make a FB vector table and emulation routines for the Cx16s' GRAPH_init() call? to replace the builtin 320x200 fb driver?
@ -37,7 +36,7 @@ gfx2 {
sub screen_mode(ubyte mode) { sub screen_mode(ubyte mode) {
when mode { when mode {
1 -> { 1 -> {
; lores monchrome ; lores monochrome
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1 cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
cx16.VERA_DC_HSCALE = 64 cx16.VERA_DC_HSCALE = 64
cx16.VERA_DC_VSCALE = 64 cx16.VERA_DC_VSCALE = 64
@ -85,7 +84,6 @@ gfx2 {
height = 480 height = 480
bpp = 2 bpp = 2
} }
; modes 7 and 8 not supported due to lack of VRAM
else -> { else -> {
; back to default text mode and colors ; back to default text mode and colors
cx16.VERA_CTRL = %10000000 ; reset VERA and palette cx16.VERA_CTRL = %10000000 ; reset VERA and palette
@ -168,7 +166,7 @@ gfx2 {
if separate_pixels as uword > length if separate_pixels as uword > length
separate_pixels = lsb(length) separate_pixels = lsb(length)
repeat separate_pixels { repeat separate_pixels {
; this could be optimized by setting this byte in 1 go but probably not worth it due to code size ; TODO optimize this by writing a masked byte in 1 go
plot(x, y, color) plot(x, y, color)
x++ x++
} }
@ -210,7 +208,7 @@ _loop lda length
_done _done
}} }}
repeat separate_pixels { repeat separate_pixels {
; this could be optimized by setting this byte in 1 go but probably not worth it due to code size ; TODO optimize this by writing a masked byte in 1 go
plot(x, y, color) plot(x, y, color)
x++ x++
} }
@ -299,6 +297,7 @@ _done
1, 5 -> { 1, 5 -> {
; monochrome, either resolution ; monochrome, either resolution
; note for the 1 bpp modes we can't use vera's auto increment mode because we have to 'or' the pixel data in place. ; note for the 1 bpp modes we can't use vera's auto increment mode because we have to 'or' the pixel data in place.
; TODO use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment IS possible
cx16.VERA_ADDR_H &= %00000111 ; no auto advance cx16.VERA_ADDR_H &= %00000111 ; no auto advance
cx16.r15 = gfx2.plot.bits[x as ubyte & 7] ; bitmask cx16.r15 = gfx2.plot.bits[x as ubyte & 7] ; bitmask
if active_mode>=5 if active_mode>=5
@ -326,7 +325,7 @@ _done
} }
} else { } else {
; stippling. ; stippling.
height = (height+1)/2 height = (height+1)/2 ; TODO is the line sometimes 1 pixel too long now because of rounding?
%asm {{ %asm {{
lda x lda x
eor y eor y
@ -375,7 +374,8 @@ _done
lda cx16.VERA_ADDR_M lda cx16.VERA_ADDR_M
adc #0 adc #0
sta cx16.VERA_ADDR_M sta cx16.VERA_ADDR_M
; lda cx16.VERA_ADDR_H ; the bitmap size is small enough to not have to deal with the _H part. ; the bitmap size is small enough to not have to deal with the _H part:
; lda cx16.VERA_ADDR_H
; adc #0 ; adc #0
; sta cx16.VERA_ADDR_H ; sta cx16.VERA_ADDR_H
}} }}
@ -399,11 +399,12 @@ _done
6 -> { 6 -> {
; highres 4c ; highres 4c
; note for this mode we can't use vera's auto increment mode because we have to 'or' the pixel data in place. ; note for this mode we can't use vera's auto increment mode because we have to 'or' the pixel data in place.
; TODO use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment IS possible
cx16.VERA_ADDR_H &= %00000111 ; no auto advance cx16.VERA_ADDR_H &= %00000111 ; no auto advance
; TODO also mostly usable for lores 4c? ; TODO also mostly usable for lores 4c?
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte) void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
; TODO optimize the loop in pure assembly ; TODO optimize this vertical line loop in pure assembly
color &= 3 color &= 3
color <<= gfx2.plot.shift4c[lsb(x) & 3] color <<= gfx2.plot.shift4c[lsb(x) & 3]
ubyte mask = gfx2.plot.mask4c[lsb(x) & 3] ubyte mask = gfx2.plot.mask4c[lsb(x) & 3]
@ -432,48 +433,46 @@ _done
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, ubyte color) { sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, ubyte color) {
; Bresenham algorithm. ; Bresenham algorithm.
; This code special-cases various quadrant loops to allow simple ++ and -- operations. ; This code special-cases various quadrant loops to allow simple ++ and -- operations.
; TODO there are some slight errors at the first/last pixels in certain slopes...
if y1>y2 { if y1>y2 {
; make sure dy is always positive to have only 4 instead of 8 special cases ; make sure dy is always positive to have only 4 instead of 8 special cases
swap(x1, x2) swap(x1, x2)
swap(y1, y2) swap(y1, y2)
} }
word @zp dx = x2-x1 as word word @zp dx = (x2 as word)-x1
word @zp dy = y2-y1 as word word @zp dy = (y2 as word)-y1
if dx==0 { if dx==0 {
vertical_line(x1, y1, abs(dy)+1 as uword, color) vertical_line(x1, y1, abs(dy) as uword +1, color)
return return
} }
if dy==0 { if dy==0 {
if x1>x2 if x1>x2
x1=x2 x1=x2
horizontal_line(x1, y1, abs(dx)+1 as uword, color) horizontal_line(x1, y1, abs(dx) as uword +1, color)
return return
} }
; TODO rewrite the rest in optimized assembly (or reuse GRAPH_draw_line if we can get the FB replacement vector layer working)
word @zp d = 0 word @zp d = 0
ubyte positive_ix = true cx16.r13 = true ; 'positive_ix'
if dx < 0 { if dx < 0 {
dx = -dx dx = -dx
positive_ix = false cx16.r13 = false
} }
dx *= 2 word @zp dx2 = dx*2
dy *= 2 word @zp dy2 = dy*2
cx16.r14 = x1 ; internal plot X cx16.r14 = x1 ; internal plot X
if dx >= dy { if dx >= dy {
if positive_ix { if cx16.r13 {
repeat { repeat {
plot(cx16.r14, y1, color) plot(cx16.r14, y1, color)
if cx16.r14==x2 if cx16.r14==x2
return return
cx16.r14++ cx16.r14++
d += dy d += dy2
if d > dx { if d > dx {
y1++ y1++
d -= dx d -= dx2
} }
} }
} else { } else {
@ -482,25 +481,25 @@ _done
if cx16.r14==x2 if cx16.r14==x2
return return
cx16.r14-- cx16.r14--
d += dy d += dy2
if d > dx { if d > dx {
y1++ y1++
d -= dx d -= dx2
} }
} }
} }
} }
else { else {
if positive_ix { if cx16.r13 {
repeat { repeat {
plot(cx16.r14, y1, color) plot(cx16.r14, y1, color)
if y1 == y2 if y1 == y2
return return
y1++ y1++
d += dx d += dx2
if d > dy { if d > dy {
cx16.r14++ cx16.r14++
d -= dy d -= dy2
} }
} }
} else { } else {
@ -509,10 +508,10 @@ _done
if y1 == y2 if y1 == y2
return return
y1++ y1++
d += dx d += dx2
if d > dy { if d > dy {
cx16.r14-- cx16.r14--
d -= dy d -= dy2
} }
} }
} }
@ -812,6 +811,7 @@ _done
chardataptr = charset_addr + (@(sctextptr) as uword)*8 chardataptr = charset_addr + (@(sctextptr) as uword)*8
cx16.vaddr(charset_bank, chardataptr, 1, 1) cx16.vaddr(charset_bank, chardataptr, 1, 1)
repeat 8 { repeat 8 {
; TODO rewrite this inner loop fully in assembly
position(x,y) position(x,y)
y++ y++
%asm {{ %asm {{
@ -840,7 +840,7 @@ _done
while @(sctextptr) { while @(sctextptr) {
chardataptr = charset_addr + (@(sctextptr) as uword)*8 chardataptr = charset_addr + (@(sctextptr) as uword)*8
repeat 8 { repeat 8 {
; TODO rewrite this inner loop in assembly ; TODO rewrite this inner loop fully in assembly
ubyte charbits = cx16.vpeek(charset_bank, chardataptr) ubyte charbits = cx16.vpeek(charset_bank, chardataptr)
repeat 8 { repeat 8 {
charbits <<= 1 charbits <<= 1
@ -877,15 +877,31 @@ _done
}} }}
} }
sub addr_mul_24_for_highres_4c(uword yy, uword xx) { asmsub addr_mul_24_for_highres_4c(uword yy @R2, uword xx @R3) clobbers(A, Y) -> uword @R0, uword @R1 {
; TODO turn into asmsub ; yy * 160 + xx/4 (24 bits calculation)
; 24 bits result is in r0 and r1L (highest byte) ; 24 bits result is in r0 and r1L (highest byte)
cx16.r0 = yy*128
cx16.r2 = yy*32
xx >>= 2
%asm {{ %asm {{
; add r2 and xx to r0 (24-bits) ldy #5
- asl cx16.r2
rol cx16.r2+1
dey
bne -
lda cx16.r2
sta cx16.r0
lda cx16.r2+1
sta cx16.r0+1
asl cx16.r0
rol cx16.r0+1
asl cx16.r0
rol cx16.r0+1
; xx >>= 2 (xx=R3)
lsr cx16.r3+1
ror cx16.r3
lsr cx16.r3+1
ror cx16.r3
; add r2 and xx (r3) to r0 (24-bits)
stz cx16.r1 stz cx16.r1
clc clc
lda cx16.r0 lda cx16.r0
@ -898,60 +914,61 @@ _done
inc cx16.r1 inc cx16.r1
+ clc + clc
lda cx16.r0 lda cx16.r0
adc xx adc cx16.r3
sta cx16.r0 sta cx16.r0
lda cx16.r0+1 lda cx16.r0+1
adc xx+1 adc cx16.r3+1
sta cx16.r0+1 sta cx16.r0+1
bcc + bcc +
inc cx16.r1 inc cx16.r1
+ +
rts
}} }}
} }
asmsub addr_mul_24_for_lores_256c(uword yy @R0, uword xx @AY) clobbers(A) -> uword @R0, ubyte @R1 { asmsub addr_mul_24_for_lores_256c(uword yy @R0, uword xx @AY) clobbers(A) -> uword @R0, ubyte @R1 {
; yy * 320 + xx (24 bits calculation) ; yy * 320 + xx (24 bits calculation)
%asm {{ %asm {{
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
lda cx16.r0 lda cx16.r0
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
lda cx16.r0+1 lda cx16.r0+1
sta cx16.r1 sta cx16.r1
sta P8ZP_SCRATCH_REG sta P8ZP_SCRATCH_REG
lda cx16.r0 lda cx16.r0
asl a asl a
rol P8ZP_SCRATCH_REG rol P8ZP_SCRATCH_REG
asl a asl a
rol P8ZP_SCRATCH_REG rol P8ZP_SCRATCH_REG
asl a asl a
rol P8ZP_SCRATCH_REG rol P8ZP_SCRATCH_REG
asl a asl a
rol P8ZP_SCRATCH_REG rol P8ZP_SCRATCH_REG
asl a asl a
rol P8ZP_SCRATCH_REG rol P8ZP_SCRATCH_REG
asl a asl a
rol P8ZP_SCRATCH_REG rol P8ZP_SCRATCH_REG
sta cx16.r0 sta cx16.r0
lda P8ZP_SCRATCH_B1 lda P8ZP_SCRATCH_B1
clc clc
adc P8ZP_SCRATCH_REG adc P8ZP_SCRATCH_REG
sta cx16.r0+1 sta cx16.r0+1
bcc + bcc +
inc cx16.r1 inc cx16.r1
+ ; now add the value to this 24-bits number + ; now add the value to this 24-bits number
lda cx16.r0 lda cx16.r0
clc clc
adc P8ZP_SCRATCH_W1 adc P8ZP_SCRATCH_W1
sta cx16.r0 sta cx16.r0
lda cx16.r0+1 lda cx16.r0+1
adc P8ZP_SCRATCH_W1+1 adc P8ZP_SCRATCH_W1+1
sta cx16.r0+1 sta cx16.r0+1
bcc + bcc +
inc cx16.r1 inc cx16.r1
+ lda cx16.r1 + lda cx16.r1
rts rts
}} }}
} }
} }

View File

@ -4,7 +4,7 @@
; Bitmap pixel graphics module for the CommanderX16 ; Bitmap pixel graphics module for the CommanderX16
; wraps the graphics functions that are in ROM. ; wraps the graphics functions that are in ROM.
; only black/white monchrome 320x200 for now. (i.e. truncated at the bottom) ; only black/white monochrome 320x200 for now. (i.e. truncated at the bottom)
; For full-screen 640x480 or 320x240 graphics, use the "gfx2" module instead. (but that is Cx16-specific) ; For full-screen 640x480 or 320x240 graphics, use the "gfx2" module instead. (but that is Cx16-specific)
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module. ; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.

View File

@ -9,8 +9,10 @@ palette {
ubyte c ubyte c
sub set_color(ubyte index, uword color) { sub set_color(ubyte index, uword color) {
cx16.vpoke(1, $fa00+index*2, lsb(color)) vera_palette_ptr = $fa00+index*2
cx16.vpoke(1, $fa01+index*2, msb(color)) cx16.vpoke(1, vera_palette_ptr, lsb(color))
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, msb(color))
} }
sub set_rgb4(uword palette_bytes_ptr, uword num_colors) { sub set_rgb4(uword palette_bytes_ptr, uword num_colors) {
@ -98,7 +100,7 @@ palette {
$666, ; 12 = medium grey $666, ; 12 = medium grey
$9D8, ; 13 = light green $9D8, ; 13 = light green
$65B, ; 14 = light blue $65B, ; 14 = light blue
$999 ; 15 = light grey $999 ; 15 = light grey
] ]
uword[] C64_colorpalette_pepto = [ ; # this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/ uword[] C64_colorpalette_pepto = [ ; # this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
@ -117,7 +119,7 @@ palette {
$777, ; 12 = medium grey $777, ; 12 = medium grey
$af9, ; 13 = light green $af9, ; 13 = light green
$76e, ; 14 = light blue $76e, ; 14 = light blue
$bbb ; 15 = light grey $bbb ; 15 = light grey
] ]
uword[] C64_colorpalette_light = [ ; this is a lighter palette uword[] C64_colorpalette_light = [ ; this is a lighter palette

View File

@ -35,7 +35,7 @@ romsub $FFAE = UNLSN() clobbers(A) ; command serial
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 secondary @ 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(X,Y) -> ubyte @Pc, ubyte @A ; (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
@ -44,8 +44,8 @@ romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320
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(X, 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, uword @ XY ; (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(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A) romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
@ -102,10 +102,11 @@ asmsub MEMTOP2() -> ubyte @A {
cx16 { cx16 {
; 65c02 hardware vectors: ; irq and hardware vectors:
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in &uword CINV = $0314 ; IRQ vector (in ram)
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in &uword NMI_VEC = $FFFA ; 65c02 nmi vector, determined by the kernal if banked in
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in &uword RESET_VEC = $FFFC ; 65c02 reset vector, determined by the kernal if banked in
&uword IRQ_VEC = $FFFE ; 65c02 interrupt vector, determined by the kernal if banked in
; the sixteen virtual 16-bit registers ; the sixteen virtual 16-bit registers
@ -349,75 +350,115 @@ asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrO
} }
asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) { asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
; -- write a single byte to VERA's video memory ; -- write a single byte to VERA's video memory
; note: inefficient when writing multiple sequential bytes! ; note: inefficient when writing multiple sequential bytes!
%asm {{ %asm {{
stz cx16.VERA_CTRL stz cx16.VERA_CTRL
and #1 and #1
sta cx16.VERA_ADDR_H sta cx16.VERA_ADDR_H
lda cx16.r0 lda cx16.r0
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda cx16.r0+1 lda cx16.r0+1
sta cx16.VERA_ADDR_M sta cx16.VERA_ADDR_M
sty cx16.VERA_DATA0 sty cx16.VERA_DATA0
rts rts
}} }}
} }
asmsub vpoke_or(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) { asmsub vpoke_or(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
; -- or a single byte to the value already in the VERA's video memory at that location ; -- or a single byte to the value already in the VERA's video memory at that location
; note: inefficient when writing multiple sequential bytes! ; note: inefficient when writing multiple sequential bytes!
%asm {{ %asm {{
stz cx16.VERA_CTRL stz cx16.VERA_CTRL
and #1 and #1
sta cx16.VERA_ADDR_H sta cx16.VERA_ADDR_H
lda cx16.r0 lda cx16.r0
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda cx16.r0+1 lda cx16.r0+1
sta cx16.VERA_ADDR_M sta cx16.VERA_ADDR_M
tya tya
ora cx16.VERA_DATA0 ora cx16.VERA_DATA0
sta cx16.VERA_DATA0 sta cx16.VERA_DATA0
rts rts
}} }}
} }
asmsub vpoke_and(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) { asmsub vpoke_and(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
; -- and a single byte to the value already in the VERA's video memory at that location ; -- and a single byte to the value already in the VERA's video memory at that location
; note: inefficient when writing multiple sequential bytes! ; note: inefficient when writing multiple sequential bytes!
%asm {{ %asm {{
stz cx16.VERA_CTRL stz cx16.VERA_CTRL
and #1 and #1
sta cx16.VERA_ADDR_H sta cx16.VERA_ADDR_H
lda cx16.r0 lda cx16.r0
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda cx16.r0+1 lda cx16.r0+1
sta cx16.VERA_ADDR_M sta cx16.VERA_ADDR_M
tya tya
and cx16.VERA_DATA0 and cx16.VERA_DATA0
sta cx16.VERA_DATA0 sta cx16.VERA_DATA0
rts rts
}} }}
} }
asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) { asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
; -- xor a single byte to the value already in the VERA's video memory at that location ; -- xor a single byte to the value already in the VERA's video memory at that location
; note: inefficient when writing multiple sequential bytes! ; note: inefficient when writing multiple sequential bytes!
%asm {{ %asm {{
stz cx16.VERA_CTRL stz cx16.VERA_CTRL
and #1 and #1
sta cx16.VERA_ADDR_H sta cx16.VERA_ADDR_H
lda cx16.r0 lda cx16.r0
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda cx16.r0+1 lda cx16.r0+1
sta cx16.VERA_ADDR_M sta cx16.VERA_ADDR_M
tya tya
eor cx16.VERA_DATA0 eor cx16.VERA_DATA0
sta cx16.VERA_DATA0 sta cx16.VERA_DATA0
rts rts
}} }}
} }
asmsub vload(str name @R0, ubyte device @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
; -- like the basic command VLOAD "filename",device,bank,address
; loads a file into video memory in the given bank:address, returns success in A
; !! NOTE !! the V38 ROMs contain a bug in the LOAD code that makes the load address not work correctly,
; it works fine when loading from local filesystem
%asm {{
; -- load a file into video ram
phx
pha
tya
tax
lda #1
ldy #0
jsr c64.SETLFS
lda cx16.r0
ldy cx16.r0+1
jsr prog8_lib.strlen
tya
ldx cx16.r0
ldy cx16.r0+1
jsr c64.SETNAM
pla
clc
adc #2
ldx cx16.r1
ldy cx16.r1+1
stz P8ZP_SCRATCH_B1
jsr c64.LOAD
bcs +
inc P8ZP_SCRATCH_B1
+ jsr c64.CLRCHN
lda #1
jsr c64.CLOSE
plx
lda P8ZP_SCRATCH_B1
rts
}}
}
sub FB_set_pixels_from_buf(uword buffer, uword count) { sub FB_set_pixels_from_buf(uword buffer, uword count) {
%asm {{ %asm {{
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM ; -- This is replacement code for the normal FB_set_pixels subroutine in ROM
@ -453,7 +494,7 @@ _loop ldy #0
} }
; ---- system stuff ----- ; ---- system stuff -----
asmsub init_system() { asmsub init_system() {
; Initializes the machine to a sane starting state. ; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic. ; Called automatically by the loader program logic.
%asm {{ %asm {{
@ -461,7 +502,7 @@ asmsub init_system() {
cld cld
;stz $00 ;stz $00
;stz $01 ;stz $01
;stz d1prb ; select rom bank 0 ;stz d1prb ; select rom bank 0 (enable kernal)
lda #$80 lda #$80
sta VERA_CTRL sta VERA_CTRL
jsr c64.IOINIT jsr c64.IOINIT
@ -485,8 +526,177 @@ asmsub init_system() {
}} }}
} }
asmsub init_system_phase2() {
%asm {{
sei
lda cx16.CINV
sta restore_irq._orig_irqvec
lda cx16.CINV+1
sta restore_irq._orig_irqvec+1
cli
rts
}}
} }
asmsub set_irq(uword handler @AY, ubyte useKernal @Pc) clobbers(A) {
%asm {{
sta _modified+1
sty _modified+2
lda #0
adc #0
sta _use_kernal
sei
lda #<_irq_handler
sta cx16.CINV
lda #>_irq_handler
sta cx16.CINV+1
lda cx16.VERA_IEN
ora #%00000001 ; enable the vsync irq
sta cx16.VERA_IEN
cli
rts
_irq_handler jsr _irq_handler_init
_modified jsr $ffff ; modified
jsr _irq_handler_end
lda _use_kernal
bne +
; end irq processing - don't use kernal's irq handling
lda cx16.VERA_ISR
ora #1
sta cx16.VERA_ISR ; clear Vera Vsync irq status
ply
plx
pla
rti
+ jmp (restore_irq._orig_irqvec) ; continue with normal kernal irq routine
_use_kernal .byte 0
_irq_handler_init
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
stx IRQ_X_REG
lda P8ZP_SCRATCH_B1
sta IRQ_SCRATCH_ZPB1
lda P8ZP_SCRATCH_REG
sta IRQ_SCRATCH_ZPREG
lda P8ZP_SCRATCH_W1
sta IRQ_SCRATCH_ZPWORD1
lda P8ZP_SCRATCH_W1+1
sta IRQ_SCRATCH_ZPWORD1+1
lda P8ZP_SCRATCH_W2
sta IRQ_SCRATCH_ZPWORD2
lda P8ZP_SCRATCH_W2+1
sta IRQ_SCRATCH_ZPWORD2+1
; stack protector; make sure we don't clobber the top of the evaluation stack
dex
dex
dex
dex
dex
dex
cld
rts
_irq_handler_end
; restore all zp scratch registers and the X register
lda IRQ_SCRATCH_ZPB1
sta P8ZP_SCRATCH_B1
lda IRQ_SCRATCH_ZPREG
sta P8ZP_SCRATCH_REG
lda IRQ_SCRATCH_ZPWORD1
sta P8ZP_SCRATCH_W1
lda IRQ_SCRATCH_ZPWORD1+1
sta P8ZP_SCRATCH_W1+1
lda IRQ_SCRATCH_ZPWORD2
sta P8ZP_SCRATCH_W2
lda IRQ_SCRATCH_ZPWORD2+1
sta P8ZP_SCRATCH_W2+1
ldx IRQ_X_REG
rts
IRQ_X_REG .byte 0
IRQ_SCRATCH_ZPB1 .byte 0
IRQ_SCRATCH_ZPREG .byte 0
IRQ_SCRATCH_ZPWORD1 .word 0
IRQ_SCRATCH_ZPWORD2 .word 0
}}
}
asmsub restore_irq() clobbers(A) {
%asm {{
sei
lda _orig_irqvec
sta cx16.CINV
lda _orig_irqvec+1
sta cx16.CINV+1
lda cx16.VERA_IEN
and #%11110000 ; disable all Vera IRQs
ora #%00000001 ; enable only the vsync Irq
sta cx16.VERA_IEN
cli
rts
_orig_irqvec .word 0
}}
}
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
%asm {{
sta _modified+1
sty _modified+2
lda cx16.r0
ldy cx16.r0+1
sei
lda cx16.VERA_IEN
and #%11110000 ; clear other IRQs
ora #%00000010 ; enable the line (raster) irq
sta cx16.VERA_IEN
lda cx16.r0
ldy cx16.r0+1
jsr set_rasterline
lda #<_raster_irq_handler
sta cx16.CINV
lda #>_raster_irq_handler
sta cx16.CINV+1
cli
rts
_raster_irq_handler
jsr set_irq._irq_handler_init
_modified jsr $ffff ; modified
jsr set_irq._irq_handler_end
; end irq processing - don't use kernal's irq handling
lda cx16.VERA_ISR
ora #%00000010
sta cx16.VERA_ISR ; clear Vera line irq status
ply
plx
pla
rti
}}
}
asmsub set_rasterline(uword line @AY) {
%asm {{
sta cx16.VERA_IRQ_LINE_L
lda cx16.VERA_IEN
and #%01111111
sta cx16.VERA_IEN
tya
lsr a
ror a
and #%10000000
ora cx16.VERA_IEN
sta cx16.VERA_IEN
rts
}}
}
}
sys { sys {
; ------- lowlevel system routines -------- ; ------- lowlevel system routines --------

View File

@ -420,7 +420,7 @@ _print_byte_digits
jsr c64.CHROUT jsr c64.CHROUT
pla pla
jsr c64.CHROUT jsr c64.CHROUT
jmp _ones bra _ones
+ pla + pla
cmp #'0' cmp #'0'
beq _ones beq _ones
@ -443,7 +443,7 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
jsr c64.CHROUT jsr c64.CHROUT
+ pla + pla
jsr conv.byte2decimal jsr conv.byte2decimal
jmp print_ub._print_byte_digits bra print_ub._print_byte_digits
}} }}
} }
@ -494,7 +494,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
jsr print_ubbin jsr print_ubbin
pla pla
clc clc
jmp print_ubbin bra print_ubbin
}} }}
} }
@ -507,7 +507,7 @@ asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
jsr print_ubhex jsr print_ubhex
pla pla
clc clc
jmp print_ubhex bra print_ubhex
}} }}
} }
@ -570,7 +570,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
adc #1 adc #1
bcc + bcc +
iny iny
+ jmp print_uw + bra print_uw
}} }}
} }
@ -626,6 +626,8 @@ asmsub getchr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A) { asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A) {
; ---- set the color in A on the screen matrix at the given position ; ---- set the color in A on the screen matrix at the given position
; note: on the CommanderX16 this allows you to set both Fg and Bg colors;
; use the high nybble in A to set the Bg color!
%asm {{ %asm {{
pha pha
txa txa
@ -657,6 +659,8 @@ asmsub getclr (ubyte col @A, ubyte row @Y) -> ubyte @ A {
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) { sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
; ---- set char+color at the given position on the screen ; ---- set char+color at the given position on the screen
; note: color handling is the same as on the C64: it only sets the foreground color.
; use setcc2 if you want Cx-16 specific feature of setting both Bg+Fg colors.
%asm {{ %asm {{
phx phx
lda column lda column
@ -685,8 +689,35 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
}} }}
} }
sub setcc2 (ubyte column, ubyte row, ubyte char, ubyte colors) {
; ---- set char+color at the given position on the screen
; note: on the CommanderX16 this allows you to set both Fg and Bg colors;
; use the high nybble in A to set the Bg color!
%asm {{
phx
lda column
asl a
tax
ldy row
stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
stx cx16.VERA_ADDR_L
sty cx16.VERA_ADDR_M
lda char
sta cx16.VERA_DATA0
inx
stz cx16.VERA_ADDR_H
stx cx16.VERA_ADDR_L
sty cx16.VERA_ADDR_M
lda colors
sta cx16.VERA_DATA0
plx
rts
}}
}
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) { asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
; ---- safe wrapper around PLOT kernel routine, to save the X register. ; ---- safe wrapper around PLOT kernal routine, to save the X register.
%asm {{ %asm {{
phx phx
tax tax

View File

@ -244,8 +244,8 @@ randseed .proc
.pend .pend
randbyte .proc fast_randbyte .proc
; -- 8-bit pseudo random number generator into A ; -- fast but bad 8-bit pseudo random number generator into A
lda _seed lda _seed
beq _eor beq _eor
asl a asl a
@ -263,6 +263,10 @@ _seed .byte $3a
.pend .pend
randbyte .proc
; -- 8 bit pseudo random number generator into A (by just reusing randword)
jmp randword
.pend
randword .proc randword .proc
; -- 16 bit pseudo random number generator into AY ; -- 16 bit pseudo random number generator into AY
@ -1537,3 +1541,71 @@ _negative lsr a
rts rts
.pend .pend
square .proc
; -- calculate square root of signed word in AY, result in AY
; routine by Lee Davsion, source: http://6502.org/source/integers/square.htm
; using this routine is about twice as fast as doing a regular multiplication.
;
; Calculates the 16 bit unsigned integer square of the signed 16 bit integer in
; Numberl/Numberh. The result is always in the range 0 to 65025 and is held in
; Squarel/Squareh
;
; The maximum input range is only +/-255 and no checking is done to ensure that
; this is so.
;
; This routine is useful if you are trying to draw circles as for any circle
;
; x^2+y^2=r^2 where x and y are the co-ordinates of any point on the circle and
; r is the circle radius
numberl = P8ZP_SCRATCH_W1 ; number to square low byte
numberh = P8ZP_SCRATCH_W1+1 ; number to square high byte
squarel = P8ZP_SCRATCH_W2 ; square low byte
squareh = P8ZP_SCRATCH_W2+1 ; square high byte
tempsq = P8ZP_SCRATCH_B1 ; temp byte for intermediate result
sta numberl
sty numberh
stx P8ZP_SCRATCH_REG
lda #$00 ; clear a
sta squarel ; clear square low byte
; (no need to clear the high byte, it gets shifted out)
lda numberl ; get number low byte
ldx numberh ; get number high byte
bpl _nonneg ; if +ve don't negate it
; else do a two's complement
eor #$ff ; invert
sec ; +1
adc #$00 ; and add it
_nonneg:
sta tempsq ; save abs(number)
ldx #$08 ; set bit count
_nextr2bit:
asl squarel ; low byte *2
rol squareh ; high byte *2+carry from low
asl a ; shift number byte
bcc _nosqadd ; don't do add if c = 0
tay ; save a
clc ; clear carry for add
lda tempsq ; get number
adc squarel ; add number^2 low byte
sta squarel ; save number^2 low byte
lda #$00 ; clear a
adc squareh ; add number^2 high byte
sta squareh ; save number^2 high byte
tya ; get a back
_nosqadd:
dex ; decrement bit count
bne _nextr2bit ; go do next bit
lda squarel
ldy squareh
ldx P8ZP_SCRATCH_REG
rts
.pend

View File

@ -387,6 +387,14 @@ func_sqrt16_into_A .proc
rts rts
.pend .pend
func_fastrnd8_stack .proc
; -- put a random ubyte on the estack (using fast but bad RNG)
jsr math.fast_randbyte
sta P8ESTACK_LO,x
dex
rts
.pend
func_rnd_stack .proc func_rnd_stack .proc
; -- put a random ubyte on the estack ; -- put a random ubyte on the estack
jsr math.randbyte jsr math.randbyte
@ -432,6 +440,7 @@ func_min_ub_stack .proc
func_min_b_into_A .proc func_min_b_into_A .proc
; -- min(barray) -> A. (array in P8ZP_SCRATCH_W1, num elements in A) ; -- min(barray) -> A. (array in P8ZP_SCRATCH_W1, num elements in A)
tay tay
dey
lda #127 lda #127
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
- lda (P8ZP_SCRATCH_W1),y - lda (P8ZP_SCRATCH_W1),y
@ -548,6 +557,7 @@ func_min_w_stack .proc
func_max_ub_into_A .proc func_max_ub_into_A .proc
; -- max(ubarray) -> A (array in P8ZP_SCRATCH_W1, num elements in A) ; -- max(ubarray) -> A (array in P8ZP_SCRATCH_W1, num elements in A)
tay tay
dey
lda #0 lda #0
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
- lda (P8ZP_SCRATCH_W1),y - lda (P8ZP_SCRATCH_W1),y

View File

@ -1072,3 +1072,14 @@ sign_extend_AY_byte .proc
rts rts
.pend .pend
strlen .proc
; -- returns the number of bytes in the string in AY, in Y.
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
- lda (P8ZP_SCRATCH_W1),y
beq +
iny
bne -
+ rts
.pend

View File

@ -1 +1 @@
6.1 6.4

View File

@ -8,7 +8,6 @@ import prog8.ast.base.AstException
import prog8.compiler.CompilationResult import prog8.compiler.CompilationResult
import prog8.compiler.compileProgram import prog8.compiler.compileProgram
import prog8.compiler.target.C64Target import prog8.compiler.target.C64Target
import prog8.compiler.target.ICompilationTarget
import prog8.compiler.target.Cx16Target import prog8.compiler.target.Cx16Target
import prog8.parser.ParsingFailedError import prog8.parser.ParsingFailedError
import java.nio.file.FileSystems import java.nio.file.FileSystems
@ -117,7 +116,7 @@ private fun compileMain(args: Array<String>) {
if (compilationResult.programName.isEmpty()) if (compilationResult.programName.isEmpty())
println("\nCan't start emulator because no program was assembled.") println("\nCan't start emulator because no program was assembled.")
else { else {
ICompilationTarget.instance.machine.launchEmulator(compilationResult.programName) compilationResult.compTarget.machine.launchEmulator(compilationResult.programName)
} }
} }
} }

View File

@ -8,10 +8,11 @@ import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.ast.walk.IAstVisitor
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: ErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() { internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
private val noModifications = emptyList<IAstModification>() private val noModifications = emptyList<IAstModification>()
@ -107,7 +108,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
} }
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine. // add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
// and if an assembly block doesn't contain a rts/rti, and some other situations. // and if an assembly block doesn't contain a rts/rti, and some other situations.
val mods = mutableListOf<IAstModification>() val mods = mutableListOf<IAstModification>()
val returnStmt = Return(null, subroutine.position) val returnStmt = Return(null, subroutine.position)
@ -154,16 +155,6 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
// The only place for now where we can do this is for: // The only place for now where we can do this is for:
// asmsub register pair parameter. // asmsub register pair parameter.
if(typecast.type in WordDatatypes) {
val fcall = typecast.parent as? IFunctionCall
if (fcall != null) {
val sub = fcall.target.targetStatement(program) as? Subroutine
if (sub != null && sub.isAsmSubroutine) {
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
}
}
}
if(sourceDt in PassByReferenceDatatypes) { if(sourceDt in PassByReferenceDatatypes) {
if(typecast.type==DataType.UWORD) { if(typecast.type==DataType.UWORD) {
if(typecast.expression is IdentifierReference) { if(typecast.expression is IdentifierReference) {
@ -216,4 +207,93 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
} }
return noModifications return noModifications
} }
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
if(functionCallStatement.target.nameInSource==listOf("cmp")) {
// if the datatype of the arguments of cmp() are different, cast the byte one to word.
val arg1 = functionCallStatement.args[0]
val arg2 = functionCallStatement.args[1]
val dt1 = arg1.inferType(program).typeOrElse(DataType.STRUCT)
val dt2 = arg2.inferType(program).typeOrElse(DataType.STRUCT)
if(dt1 in ByteDatatypes) {
if(dt2 in ByteDatatypes)
return noModifications
val cast1 = TypecastExpression(arg1, if(dt1==DataType.UBYTE) DataType.UWORD else DataType.WORD, true, functionCallStatement.position)
return listOf(IAstModification.ReplaceNode(arg1, cast1, functionCallStatement))
} else {
if(dt2 in WordDatatypes)
return noModifications
val cast2 = TypecastExpression(arg2, if(dt2==DataType.UBYTE) DataType.UWORD else DataType.WORD, true, functionCallStatement.position)
return listOf(IAstModification.ReplaceNode(arg2, cast2, functionCallStatement))
}
}
return noModifications
}
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
val containingStatement = getContainingStatement(arrayIndexedExpression)
if(getComplexArrayIndexedExpressions(containingStatement).size > 1) {
errors.err("it's not possible to use more than one complex array indexing expression in a single statement; break it up via a temporary variable for instance", containingStatement.position)
return noModifications
}
val index = arrayIndexedExpression.indexer.indexExpr
if(index !is NumericLiteralValue && index !is IdentifierReference) {
// replace complex indexing expression with a temp variable to hold the computed index first
return getAutoIndexerVarFor(arrayIndexedExpression)
}
return noModifications
}
private fun getComplexArrayIndexedExpressions(stmt: Statement): List<ArrayIndexedExpression> {
class Searcher : IAstVisitor {
val complexArrayIndexedExpressions = mutableListOf<ArrayIndexedExpression>()
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
val ix = arrayIndexedExpression.indexer.indexExpr
if(ix !is NumericLiteralValue && ix !is IdentifierReference)
complexArrayIndexedExpressions.add(arrayIndexedExpression)
}
override fun visit(branchStatement: BranchStatement) {}
override fun visit(forLoop: ForLoop) {}
override fun visit(ifStatement: IfStatement) {
ifStatement.condition.accept(this)
}
override fun visit(untilLoop: UntilLoop) {
untilLoop.condition.accept(this)
}
}
val searcher = Searcher()
stmt.accept(searcher)
return searcher.complexArrayIndexedExpressions
}
private fun getContainingStatement(expression: Expression): Statement {
var node: Node = expression
while(node !is Statement)
node = node.parent
return node
}
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
val modifications = mutableListOf<IAstModification>()
val statement = expr.containingStatement()
// replace the indexer with just the variable (simply use a cx16 virtual register r9, that we HOPE is not used for other things in the expression...)
// assign the indexing expression to the helper variable, but only if that hasn't been done already
val target = AssignTarget(IdentifierReference(listOf("cx16", "r9"), expr.indexer.position), null, null, expr.indexer.position)
val assign = Assignment(target, expr.indexer.indexExpr, expr.indexer.position)
modifications.add(IAstModification.InsertBefore(statement, assign, statement.definingScope()))
modifications.add(IAstModification.ReplaceNode(expr.indexer.indexExpr, target.identifier!!.copy(), expr.indexer))
return modifications
}
} }

View File

@ -2,6 +2,7 @@ package prog8.compiler
import prog8.ast.AstToSourceCode import prog8.ast.AstToSourceCode
import prog8.ast.IBuiltinFunctions import prog8.ast.IBuiltinFunctions
import prog8.ast.IMemSizer
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.AstException import prog8.ast.base.AstException
import prog8.ast.base.Position import prog8.ast.base.Position
@ -48,7 +49,8 @@ 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,
val compTarget: ICompilationTarget) {
var slowCodegenWarnings = false var slowCodegenWarnings = false
var optimize = false var optimize = false
} }
@ -59,6 +61,7 @@ class CompilerException(message: String?) : Exception(message)
class CompilationResult(val success: Boolean, class CompilationResult(val success: Boolean,
val programAst: Program, val programAst: Program,
val programName: String, val programName: String,
val compTarget: ICompilationTarget,
val importedFiles: List<Path>) val importedFiles: List<Path>)
@ -73,27 +76,28 @@ fun compileProgram(filepath: Path,
lateinit var importedFiles: List<Path> lateinit var importedFiles: List<Path>
val errors = ErrorReporter() val errors = ErrorReporter()
when(compilationTarget) { val compTarget =
C64Target.name -> ICompilationTarget.instance = C64Target when(compilationTarget) {
Cx16Target.name -> ICompilationTarget.instance = Cx16Target C64Target.name -> C64Target
else -> { Cx16Target.name -> Cx16Target
System.err.println("invalid compilation target") else -> {
exitProcess(1) System.err.println("invalid compilation target")
exitProcess(1)
}
} }
}
try { try {
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, compTarget)
compilationOptions.slowCodegenWarnings = slowCodegenWarnings compilationOptions.slowCodegenWarnings = slowCodegenWarnings
compilationOptions.optimize = optimize compilationOptions.optimize = optimize
programAst = ast programAst = ast
importedFiles = imported importedFiles = imported
processAst(programAst, errors, compilationOptions, ICompilationTarget.instance) processAst(programAst, errors, compilationOptions)
if (compilationOptions.optimize) if (compilationOptions.optimize)
optimizeAst(programAst, errors, BuiltinFunctionsFacade(BuiltinFunctions)) optimizeAst(programAst, errors, BuiltinFunctionsFacade(BuiltinFunctions), compTarget)
postprocessAst(programAst, errors, compilationOptions, ICompilationTarget.instance) postprocessAst(programAst, errors, compilationOptions)
// printAst(programAst) // printAst(programAst)
@ -103,7 +107,7 @@ fun compileProgram(filepath: Path,
System.out.flush() System.out.flush()
System.err.flush() System.err.flush()
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.") println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
return CompilationResult(true, programAst, programName, importedFiles) return CompilationResult(true, programAst, programName, compTarget, importedFiles)
} catch (px: ParsingFailedError) { } catch (px: ParsingFailedError) {
System.err.print("\u001b[91m") // bright red System.err.print("\u001b[91m") // bright red
@ -127,8 +131,8 @@ fun compileProgram(filepath: Path,
throw x throw x
} }
val failedProgram = Program("failed", mutableListOf(), BuiltinFunctionsFacade(BuiltinFunctions)) val failedProgram = Program("failed", mutableListOf(), BuiltinFunctionsFacade(BuiltinFunctions), compTarget)
return CompilationResult(false, failedProgram, programName, emptyList()) return CompilationResult(false, failedProgram, programName, compTarget, emptyList())
} }
private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuiltinFunctions { private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuiltinFunctions {
@ -137,13 +141,13 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
override val names = functions.keys override val names = functions.keys
override val purefunctionNames = functions.filter { it.value.pure }.map { it.key }.toSet() override val purefunctionNames = functions.filter { it.value.pure }.map { it.key }.toSet()
override fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue? { override fun constValue(name: String, args: List<Expression>, position: Position, memsizer: IMemSizer): NumericLiteralValue? {
val func = BuiltinFunctions[name] val func = BuiltinFunctions[name]
if(func!=null) { if(func!=null) {
val exprfunc = func.constExpressionFunc val exprfunc = func.constExpressionFunc
if(exprfunc!=null) { if(exprfunc!=null) {
return try { return try {
exprfunc(args, position, program) exprfunc(args, position, program, memsizer)
} catch(x: NotConstArgumentException) { } catch(x: NotConstArgumentException) {
// const-evaluating the builtin function call failed. // const-evaluating the builtin function call failed.
null null
@ -153,7 +157,7 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
} }
} }
else if(func.known_returntype==null) else if(func.known_returntype==null)
throw IllegalArgumentException("builtin function $name can't be used here because it doesn't return a value") return null // builtin function $name can't be used here because it doesn't return a value
} }
return null return null
} }
@ -161,34 +165,34 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
builtinFunctionReturnType(name, args, program) builtinFunctionReturnType(name, args, program)
} }
private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program, CompilationOptions, List<Path>> { private fun parseImports(filepath: Path, errors: IErrorReporter, compTarget: ICompilationTarget): Triple<Program, CompilationOptions, List<Path>> {
val compilationTargetName = ICompilationTarget.instance.name val compilationTargetName = compTarget.name
println("Compiler target: $compilationTargetName. Parsing...") println("Compiler target: $compilationTargetName. Parsing...")
val importer = ModuleImporter() val importer = ModuleImporter()
val bf = BuiltinFunctionsFacade(BuiltinFunctions) val bf = BuiltinFunctionsFacade(BuiltinFunctions)
val programAst = Program(moduleName(filepath.fileName), mutableListOf(), bf) val programAst = Program(moduleName(filepath.fileName), mutableListOf(), bf, compTarget)
bf.program = programAst bf.program = programAst
importer.importModule(programAst, filepath, ICompilationTarget.instance, compilationTargetName) importer.importModule(programAst, filepath, compTarget, compilationTargetName)
errors.handle() errors.report()
val importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map { it.source } val importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map { it.source }
val compilerOptions = determineCompilationOptions(programAst, compTarget)
val compilerOptions = determineCompilationOptions(programAst)
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG) if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.") throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
// 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
ICompilationTarget.instance.machine.importLibs(compilerOptions, importer, programAst, ICompilationTarget.instance, compilationTargetName) for(lib in compTarget.machine.importLibs(compilerOptions, compilationTargetName))
importer.importLibraryModule(programAst, lib, compTarget, compilationTargetName)
// always import prog8_lib and math // always import prog8_lib and math
importer.importLibraryModule(programAst, "math", ICompilationTarget.instance, compilationTargetName) importer.importLibraryModule(programAst, "math", compTarget, compilationTargetName)
importer.importLibraryModule(programAst, "prog8_lib", ICompilationTarget.instance, compilationTargetName) importer.importLibraryModule(programAst, "prog8_lib", compTarget, compilationTargetName)
errors.handle() errors.report()
return Triple(programAst, compilerOptions, importedFiles) return Triple(programAst, compilerOptions, importedFiles)
} }
private fun determineCompilationOptions(program: Program): CompilationOptions { private fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
val mainModule = program.modules.first() val mainModule = program.mainModule
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" } val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
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" }
@ -209,7 +213,7 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
// error will be printed by the astchecker // error will be printed by the astchecker
} }
if (zpType==ZeropageType.FLOATSAFE && ICompilationTarget.instance.name == Cx16Target.name) { if (zpType==ZeropageType.FLOATSAFE && compTarget.name == Cx16Target.name) {
System.err.println("Warning: Cx16 target must use zp option basicsafe instead of floatsafe") System.err.println("Warning: Cx16 target must use zp option basicsafe instead of floatsafe")
zpType = ZeropageType.BASICSAFE zpType = ZeropageType.BASICSAFE
} }
@ -233,78 +237,81 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
return CompilationOptions( return CompilationOptions(
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType), if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType), if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
zpType, zpReserved, floatsEnabled, noSysInit zpType, zpReserved, floatsEnabled, noSysInit,
compTarget
) )
} }
private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions, compTarget: ICompilationTarget) { private fun processAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
// perform initial syntax checks and processings // perform initial syntax checks and processings
println("Processing for target ${compTarget.name}...") println("Processing for target ${compilerOptions.compTarget.name}...")
programAst.checkIdentifiers(errors, compTarget) programAst.checkIdentifiers(errors, compilerOptions.compTarget)
errors.handle() errors.report()
programAst.constantFold(errors) programAst.constantFold(errors, compilerOptions.compTarget)
errors.handle() errors.report()
programAst.reorderStatements(errors) programAst.reorderStatements(errors)
errors.handle() errors.report()
programAst.addTypecasts(errors) programAst.addTypecasts(errors)
errors.handle() errors.report()
programAst.variousCleanups() programAst.variousCleanups()
programAst.checkValid(compilerOptions, errors, compTarget) programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget)
errors.handle() errors.report()
programAst.checkIdentifiers(errors, compTarget) programAst.checkIdentifiers(errors, compilerOptions.compTarget)
errors.handle() errors.report()
} }
private fun optimizeAst(programAst: Program, errors: ErrorReporter, functions: IBuiltinFunctions) { private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget) {
// optimize the parse tree // optimize the parse tree
println("Optimizing...") println("Optimizing...")
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.splitBinaryExpressions() val optsDone2 = programAst.splitBinaryExpressions(compTarget)
val optsDone3 = programAst.optimizeStatements(errors, functions) val optsDone3 = programAst.optimizeStatements(errors, functions, compTarget, ::loadAsmIncludeFile)
programAst.constantFold(errors) // because simplified statements and expressions can result in more constants that can be folded away programAst.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
errors.handle() errors.report()
if (optsDone1 + optsDone2 + optsDone3 == 0) if (optsDone1 + optsDone2 + optsDone3 == 0)
break break
} }
val remover = UnusedCodeRemover(programAst, errors) val remover = UnusedCodeRemover(programAst, errors, compTarget, ::loadAsmIncludeFile)
remover.visit(programAst) remover.visit(programAst)
remover.applyModifications() remover.applyModifications()
errors.handle() errors.report()
} }
private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions, compTarget: ICompilationTarget) { private fun postprocessAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
programAst.addTypecasts(errors) programAst.addTypecasts(errors)
errors.handle() errors.report()
programAst.variousCleanups() programAst.variousCleanups()
programAst.checkValid(compilerOptions, errors, compTarget) // check if final tree is still valid programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget) // check if final tree is still valid
errors.handle() errors.report()
val callGraph = CallGraph(programAst) val callGraph = CallGraph(programAst, ::loadAsmIncludeFile)
callGraph.checkRecursiveCalls(errors) callGraph.checkRecursiveCalls(errors)
errors.handle() errors.report()
programAst.verifyFunctionArgTypes() programAst.verifyFunctionArgTypes()
programAst.moveMainAndStartToFirst() programAst.moveMainAndStartToFirst()
} }
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path, private fun writeAssembly(programAst: Program,
errors: IErrorReporter,
outputDir: Path,
compilerOptions: CompilationOptions): String { compilerOptions: CompilationOptions): String {
// asm generation directly from the Ast, // asm generation directly from the Ast
programAst.processAstBeforeAsmGeneration(errors, ICompilationTarget.instance) programAst.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
errors.handle() errors.report()
// printAst(programAst) // printAst(programAst)
ICompilationTarget.instance.machine.initializeZeropage(compilerOptions) compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
val assembly = asmGeneratorFor(ICompilationTarget.instance, val assembly = asmGeneratorFor(compilerOptions.compTarget,
programAst, programAst,
errors, errors,
ICompilationTarget.instance.machine.zeropage, compilerOptions.compTarget.machine.zeropage,
compilerOptions, compilerOptions,
outputDir).compileToAssembly() outputDir).compileToAssembly()
assembly.assemble(compilerOptions) assembly.assemble(compilerOptions)
errors.handle() errors.report()
return assembly.name return assembly.name
} }

View File

@ -3,7 +3,16 @@ package prog8.compiler
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.parser.ParsingFailedError import prog8.parser.ParsingFailedError
class ErrorReporter {
interface IErrorReporter {
fun err(msg: String, position: Position)
fun warn(msg: String, position: Position)
fun isEmpty(): Boolean
fun report()
}
internal class ErrorReporter: IErrorReporter {
private enum class MessageSeverity { private enum class MessageSeverity {
WARNING, WARNING,
ERROR ERROR
@ -13,10 +22,14 @@ class ErrorReporter {
private val messages = mutableListOf<CompilerMessage>() private val messages = mutableListOf<CompilerMessage>()
private val alreadyReportedMessages = mutableSetOf<String>() private val alreadyReportedMessages = mutableSetOf<String>()
fun err(msg: String, position: Position) = messages.add(CompilerMessage(MessageSeverity.ERROR, msg, position)) override fun err(msg: String, position: Position) {
fun warn(msg: String, position: Position) = messages.add(CompilerMessage(MessageSeverity.WARNING, msg, position)) messages.add(CompilerMessage(MessageSeverity.ERROR, msg, position))
}
override fun warn(msg: String, position: Position) {
messages.add(CompilerMessage(MessageSeverity.WARNING, msg, position))
}
fun handle() { override fun report() {
var numErrors = 0 var numErrors = 0
var numWarnings = 0 var numWarnings = 0
messages.forEach { messages.forEach {
@ -40,5 +53,5 @@ class ErrorReporter {
throw ParsingFailedError("There are $numErrors errors and $numWarnings warnings.") throw ParsingFailedError("There are $numErrors errors and $numWarnings warnings.")
} }
fun isEmpty() = messages.isEmpty() override fun isEmpty() = messages.isEmpty()
} }

View File

@ -21,7 +21,7 @@ abstract class Zeropage(protected val options: CompilationOptions) {
fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: ErrorReporter): Int { fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: IErrorReporter): Int {
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"scopedname can't be allocated twice"} assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"scopedname can't be allocated twice"}
if(options.zeropage==ZeropageType.DONTUSE) if(options.zeropage==ZeropageType.DONTUSE)

View File

@ -8,7 +8,7 @@ import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.compiler.CompilationOptions import prog8.compiler.CompilationOptions
import prog8.compiler.ErrorReporter import prog8.compiler.IErrorReporter
import prog8.compiler.functions.BuiltinFunctions import prog8.compiler.functions.BuiltinFunctions
import prog8.compiler.functions.builtinFunctionReturnType import prog8.compiler.functions.builtinFunctionReturnType
import prog8.compiler.target.C64Target import prog8.compiler.target.C64Target
@ -18,7 +18,7 @@ import java.io.File
internal class AstChecker(private val program: Program, internal class AstChecker(private val program: Program,
private val compilerOptions: CompilationOptions, private val compilerOptions: CompilationOptions,
private val errors: ErrorReporter, private val errors: IErrorReporter,
private val compTarget: ICompilationTarget private val compTarget: ICompilationTarget
) : IAstVisitor { ) : IAstVisitor {
@ -41,19 +41,6 @@ internal class AstChecker(private val program: Program,
} }
} }
// there can be an optional single 'irq' block with a 'irq' subroutine in it,
// which will be used as the 60hz irq routine in the vm if it's present
val irqBlocks = program.modules.flatMap { it.statements }.filter { it is Block && it.name=="irq" }.map { it as Block }
if(irqBlocks.size>1)
errors.err("more than one 'irq' block", irqBlocks[0].position)
for(irqBlock in irqBlocks) {
val irqSub = irqBlock.subScope("irq") as? Subroutine
if (irqSub != null) {
if (irqSub.parameters.isNotEmpty() || irqSub.returntypes.isNotEmpty())
errors.err("irq entrypoint subroutine can't have parameters and/or return values", irqSub.position)
}
}
super.visit(program) super.visit(program)
} }
@ -187,7 +174,7 @@ internal class AstChecker(private val program: Program,
else -> false else -> false
} }
if (!ok) { if (!ok) {
errors.err("statement occurs in a block, where it will never be executed. Use it in a subroutine instead.", statement.position) errors.err("non-declarative statement occurs in block scope, where it will never be executed. Move it to a subroutine instead.", statement.position)
break break
} }
} }
@ -231,13 +218,13 @@ internal class AstChecker(private val program: Program,
err("subroutines can only have one return value") err("subroutines can only have one return value")
// subroutine must contain at least one 'return' or 'goto' // subroutine must contain at least one 'return' or 'goto'
// (or if it has an asm block, that must contain a 'rts' or 'jmp') // (or if it has an asm block, that must contain a 'rts' or 'jmp' or 'bra')
if(subroutine.statements.count { it is Return || it is Jump } == 0) { if(subroutine.statements.count { it is Return || it is Jump } == 0) {
if (subroutine.amountOfRtsInAsm() == 0) { if (subroutine.amountOfRtsInAsm() == 0) {
if (subroutine.returntypes.isNotEmpty()) { if (subroutine.returntypes.isNotEmpty()) {
// for asm subroutines with an address, no statement check is possible. // for asm subroutines with an address, no statement check is possible.
if (subroutine.asmAddress == null && !subroutine.inline) if (subroutine.asmAddress == null && !subroutine.inline)
err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or 'rts' / 'jmp' in case of %asm)") err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or rts/jmp/bra in case of %asm)")
} }
} }
} }
@ -351,10 +338,6 @@ internal class AstChecker(private val program: Program,
if(statusFlagsNoCarry.isNotEmpty()) if(statusFlagsNoCarry.isNotEmpty())
err("can only use Carry as status flag parameter") err("can only use Carry as status flag parameter")
val carryParameter = subroutine.asmParameterRegisters.singleOrNull { it.statusflag==Statusflag.Pc }
if(carryParameter!=null && carryParameter !== subroutine.asmParameterRegisters.last())
err("carry parameter has to come last")
} else { } else {
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly // Pass-by-reference datatypes can not occur as parameters to a subroutine directly
// Instead, their reference (address) should be passed (as an UWORD). // Instead, their reference (address) should be passed (as an UWORD).
@ -391,7 +374,7 @@ internal class AstChecker(private val program: Program,
if(!idt.isKnown) { if(!idt.isKnown) {
errors.err("return type mismatch", assignment.value.position) errors.err("return type mismatch", assignment.value.position)
} }
if(stmt.returntypes.size <= 1 && stmt.returntypes.single() isNotAssignableTo idt.typeOrElse(DataType.BYTE)) { if(stmt.returntypes.isEmpty() || (stmt.returntypes.size == 1 && stmt.returntypes.single() isNotAssignableTo idt.typeOrElse(DataType.BYTE))) {
errors.err("return type mismatch", assignment.value.position) errors.err("return type mismatch", assignment.value.position)
} }
} }
@ -510,7 +493,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?.indexVar?.referencesIdentifier(decl.name) == true) if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexExpr?.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)
@ -756,7 +739,7 @@ internal class AstChecker(private val program: Program,
err("this directive may only occur in a block or at module level") err("this directive may only occur in a block or at module level")
if(directive.args.isEmpty()) if(directive.args.isEmpty())
err("missing option directive argument(s)") err("missing option directive argument(s)")
else if(directive.args.map{it.name in setOf("enable_floats", "force_output", "no_sysinit")}.any { !it }) else if(directive.args.map{it.name in setOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page")}.any { !it })
err("invalid option directive argument(s)") err("invalid option directive argument(s)")
} }
"%target" -> { "%target" -> {
@ -970,6 +953,20 @@ internal class AstChecker(private val program: Program,
} }
} }
// functions that don't return a value, can't be used in an expression or assignment
if(targetStatement is Subroutine) {
if(targetStatement.returntypes.isEmpty()) {
if(functionCall.parent is Expression || functionCall.parent is Assignment)
errors.err("subroutine doesn't return a value", functionCall.position)
}
}
else if(targetStatement is BuiltinFunctionStatementPlaceholder) {
if(builtinFunctionReturnType(targetStatement.name, functionCall.args, program).isUnknown) {
if(functionCall.parent is Expression || functionCall.parent is Assignment)
errors.err("function doesn't return a value", functionCall.position)
}
}
super.visit(functionCall) super.visit(functionCall)
} }
@ -1128,15 +1125,9 @@ internal class AstChecker(private val program: Program,
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 dtxNum = arrayIndexedExpression.indexer.indexNum?.inferType(program)?.typeOrElse(DataType.STRUCT) val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program)
if(dtxNum!=null && dtxNum != DataType.UBYTE && dtxNum != DataType.BYTE) if(!dtxNum.istype(DataType.UBYTE) && !dtxNum.istype(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)
} }
@ -1145,12 +1136,18 @@ internal class AstChecker(private val program: Program,
val conditionType = whenStatement.condition.inferType(program).typeOrElse(DataType.STRUCT) val conditionType = whenStatement.condition.inferType(program).typeOrElse(DataType.STRUCT)
if(conditionType !in IntegerDatatypes) if(conditionType !in IntegerDatatypes)
errors.err("when condition must be an integer value", whenStatement.position) errors.err("when condition must be an integer value", whenStatement.position)
val choiceValues = whenStatement.choiceValues(program) val tally = mutableSetOf<Int>()
val occurringValues = choiceValues.map {it.first} for((choices, choiceNode) in whenStatement.choiceValues(program)) {
val tally = choiceValues.associate { it.second to occurringValues.count { ov->it.first==ov} } if(choices!=null) {
tally.filter { it.value>1 }.forEach { for (c in choices) {
errors.err("choice value occurs multiple times", it.key.position) if(c in tally)
errors.err("choice value already occurs earlier", choiceNode.position)
else
tally.add(c)
}
}
} }
if(whenStatement.choices.isEmpty()) if(whenStatement.choices.isEmpty())
errors.err("empty when statement", whenStatement.position) errors.err("empty when statement", whenStatement.position)
@ -1361,7 +1358,7 @@ internal class AstChecker(private val program: Program,
val array = value.value.map { val array = value.value.map {
when (it) { when (it) {
is NumericLiteralValue -> it.number.toInt() is NumericLiteralValue -> it.number.toInt()
is AddressOf -> it.identifier.heapId(program.namespace) is AddressOf -> it.identifier.hashCode() and 0xffff
is TypecastExpression -> { is TypecastExpression -> {
val constVal = it.expression.constValue(program) val constVal = it.expression.constValue(program)
val cast = constVal?.cast(it.type) val cast = constVal?.cast(it.type)
@ -1420,7 +1417,10 @@ internal class AstChecker(private val program: Program,
} }
false false
} }
else -> errors.err("cannot assign new value to variable of type $targetDatatype", position) else -> {
errors.err("cannot assign new value to variable of type $targetDatatype", position)
false
}
} }
if(result) if(result)

View File

@ -1,32 +1,32 @@
package prog8.compiler.astprocessing package prog8.compiler.astprocessing
import prog8.ast.Program import prog8.ast.Program
import prog8.compiler.ErrorReporter
import prog8.ast.base.FatalAstException import prog8.ast.base.FatalAstException
import prog8.ast.statements.Directive import prog8.ast.statements.Directive
import prog8.compiler.BeforeAsmGenerationAstChanger import prog8.compiler.BeforeAsmGenerationAstChanger
import prog8.compiler.CompilationOptions import prog8.compiler.CompilationOptions
import prog8.compiler.IErrorReporter
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter, compTarget: ICompilationTarget) { internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: IErrorReporter, compTarget: ICompilationTarget) {
val checker = AstChecker(this, compilerOptions, errors, compTarget) val checker = AstChecker(this, compilerOptions, errors, compTarget)
checker.visit(this) checker.visit(this)
} }
internal fun Program.processAstBeforeAsmGeneration(errors: ErrorReporter, compTarget: ICompilationTarget) { internal fun Program.processAstBeforeAsmGeneration(errors: IErrorReporter, compTarget: ICompilationTarget) {
val fixer = BeforeAsmGenerationAstChanger(this, errors, compTarget) val fixer = BeforeAsmGenerationAstChanger(this, errors, compTarget)
fixer.visit(this) fixer.visit(this)
fixer.applyModifications() fixer.applyModifications()
} }
internal fun Program.reorderStatements(errors: ErrorReporter) { internal fun Program.reorderStatements(errors: IErrorReporter) {
val reorder = StatementReorderer(this, errors) val reorder = StatementReorderer(this, errors)
reorder.visit(this) reorder.visit(this)
reorder.applyModifications() reorder.applyModifications()
} }
internal fun Program.addTypecasts(errors: ErrorReporter) { internal fun Program.addTypecasts(errors: IErrorReporter) {
val caster = TypecastsAdder(this, errors) val caster = TypecastsAdder(this, errors)
caster.visit(this) caster.visit(this)
caster.applyModifications() caster.applyModifications()
@ -37,7 +37,7 @@ internal fun Program.verifyFunctionArgTypes() {
fixer.visit(this) fixer.visit(this)
} }
internal fun Program.checkIdentifiers(errors: ErrorReporter, compTarget: ICompilationTarget) { internal fun Program.checkIdentifiers(errors: IErrorReporter, compTarget: ICompilationTarget) {
val checker2 = AstIdentifiersChecker(this, errors, compTarget) val checker2 = AstIdentifiersChecker(this, errors, compTarget)
checker2.visit(this) checker2.visit(this)

View File

@ -3,7 +3,6 @@ package prog8.compiler.astprocessing
import prog8.ast.Module import prog8.ast.Module
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.compiler.ErrorReporter
import prog8.ast.base.NumericDatatypes import prog8.ast.base.NumericDatatypes
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.expressions.ArrayLiteralValue import prog8.ast.expressions.ArrayLiteralValue
@ -11,10 +10,11 @@ import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.StringLiteralValue import prog8.ast.expressions.StringLiteralValue
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.compiler.IErrorReporter
import prog8.compiler.functions.BuiltinFunctions import prog8.compiler.functions.BuiltinFunctions
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
internal class AstIdentifiersChecker(private val program: Program, private val errors: ErrorReporter, private val compTarget: ICompilationTarget) : IAstVisitor { internal class AstIdentifiersChecker(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : IAstVisitor {
private var blocks = mutableMapOf<String, Block>() private var blocks = mutableMapOf<String, Block>()
private fun nameError(name: String, position: Position, existing: Statement) { private fun nameError(name: String, position: Position, existing: Statement) {

View File

@ -32,7 +32,7 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
} }
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> { override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
// For non-kernel subroutines and non-asm parameters: // For non-kernal subroutines and non-asm parameters:
// inject subroutine params as local variables (if they're not there yet). // inject subroutine params as local variables (if they're not there yet).
val symbolsInSub = subroutine.allDefinedSymbols() val symbolsInSub = subroutine.allDefinedSymbols()
val namesInSub = symbolsInSub.map{ it.first }.toSet() val namesInSub = symbolsInSub.map{ it.first }.toSet()

View File

@ -17,13 +17,10 @@ internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> { override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
if(string.parent !is VarDecl && string.parent !is WhenChoice) { if(string.parent !is VarDecl && string.parent !is WhenChoice) {
// replace the literal string by a identifier reference to a new local vardecl // replace the literal string by a identifier reference to the interned string
val vardecl = VarDecl.createAuto(string) val scopedName = program.internString(string)
val identifier = IdentifierReference(listOf(vardecl.name), vardecl.position) val identifier = IdentifierReference(scopedName, string.position)
return listOf( return listOf(IAstModification.ReplaceNode(string, identifier, parent))
IAstModification.ReplaceNode(string, identifier, parent),
IAstModification.InsertFirst(vardecl, string.definingScope())
)
} }
return noModifications return noModifications
} }

View File

@ -1,16 +1,19 @@
package prog8.compiler.astprocessing package prog8.compiler.astprocessing
import prog8.ast.* import prog8.ast.IFunctionCall
import prog8.ast.Module
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.compiler.ErrorReporter import prog8.compiler.IErrorReporter
import prog8.compiler.functions.BuiltinFunctions import prog8.compiler.functions.BuiltinFunctions
internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() { internal class StatementReorderer(val program: Program, val errors: IErrorReporter) : 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.
@ -89,8 +92,7 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) { if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
// rewrite pointervar[index] into @(pointervar+index) // rewrite pointervar[index] into @(pointervar+index)
val indexer = arrayIndexedExpression.indexer val indexer = arrayIndexedExpression.indexer
val index = (indexer.indexNum ?: indexer.indexVar)!! val add = BinaryExpression(arrayIndexedExpression.arrayvar, "+", indexer.indexExpr, arrayIndexedExpression.position)
val add = BinaryExpression(arrayIndexedExpression.arrayvar, "+", index, arrayIndexedExpression.position)
return if(parent is AssignTarget) { return if(parent is AssignTarget) {
// we're part of the target of an assignment, we have to actually change the assign target itself // we're part of the target of an assignment, we have to actually change the assign target itself
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position) val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
@ -102,23 +104,7 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
} }
} }
when (val expr2 = arrayIndexedExpression.indexer.origExpression) { return noModifications
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
}
} }
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> { override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
@ -167,41 +153,41 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
else -> return noModifications else -> return noModifications
} }
} }
else if(expr.operator in logicalOperators) {
// make sure that logical expressions like "var and other-logical-expression
// is rewritten as "var!=0 and other-logical-expression", to avoid bitwise boolean and
// generating the wrong results later
fun wrapped(expr: Expression): Expression =
BinaryExpression(expr, "!=", NumericLiteralValue(DataType.UBYTE, 0, expr.position), expr.position)
fun isLogicalExpr(expr: Expression?): Boolean {
if(expr is BinaryExpression && expr.operator in (logicalOperators + comparisonOperators))
return true
if(expr is PrefixExpression && expr.operator in logicalOperators)
return true
return false
}
return if(isLogicalExpr(expr.left)) {
if(isLogicalExpr(expr.right))
noModifications
else
listOf(IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr))
} else {
if(isLogicalExpr(expr.right))
listOf(IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr))
else {
listOf(
IAstModification.ReplaceNode(expr.left, wrapped(expr.left), expr),
IAstModification.ReplaceNode(expr.right, wrapped(expr.right), expr)
)
}
}
}
return noModifications return noModifications
} }
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
val modifications = mutableListOf<IAstModification>()
val subroutine = expr.definingSubroutine()!!
val statement = expr.containingStatement()
val indexerVarPrefix = "prog8_autovar_index_"
val repo = subroutine.asmGenInfo.usedAutoArrayIndexerForStatements
// TODO make this a bit smarter so it can reuse indexer variables. BUT BEWARE of scoping+initialization problems then
// add another loop index var to be used for this expression
val indexerVarName = "$indexerVarPrefix${expr.indexer.hashCode()}"
val indexerVar = AsmGenInfo.ArrayIndexerInfo(indexerVarName, expr.indexer)
repo.add(indexerVar)
// create the indexer var at block level scope
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE,
null, indexerVarName, null, null, isArray = false, autogeneratedDontRemove = true, position = expr.position)
modifications.add(IAstModification.InsertFirst(vardecl, subroutine))
// replace the indexer with just the variable
// assign the indexing expression to the helper variable, but only if that hasn't been done already
val indexerExpression = expr.indexer.origExpression!!
val target = AssignTarget(IdentifierReference(listOf(indexerVar.name), indexerExpression.position), null, null, indexerExpression.position)
val assign = Assignment(target, indexerExpression, indexerExpression.position)
modifications.add(IAstModification.InsertBefore(statement, assign, statement.definingScope()))
modifications.add(IAstModification.SetExpression( {
expr.indexer.indexVar = it as IdentifierReference
expr.indexer.indexNum = null
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 {
it.first?.first() ?: Int.MAX_VALUE it.first?.first() ?: Int.MAX_VALUE
@ -236,32 +222,23 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
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) val targetType = assignment.target.inferType(program)
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 )) {
assignments = if (assignment.value is ArrayLiteralValue) { if (assignment.value is ArrayLiteralValue) {
flattenStructAssignmentFromStructLiteral(assignment) // 'structvar = [ ..... ] ' errors.err("cannot assign non-const array value, use separate assignment per field", assignment.position)
} else { } else {
flattenStructAssignmentFromIdentifier(assignment) // 'structvar1 = structvar2' return copyStructValue(assignment)
} }
} }
if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) { if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) {
assignments = if (assignment.value is ArrayLiteralValue) { if (assignment.value is ArrayLiteralValue) {
flattenArrayAssignmentFromArrayLiteral(assignment) // 'arrayvar = [ ..... ] ' errors.err("cannot assign non-const array value, use separate assignment per element", assignment.position)
} else { } else {
flattenArrayAssignmentFromIdentifier(assignment) // 'arrayvar1 = arrayvar2' return copyArrayValue(assignment)
} }
} }
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
} }
@ -314,113 +291,98 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
return noModifications return noModifications
} }
private fun flattenArrayAssignmentFromArrayLiteral(assign: Assignment): List<Assignment> { private fun copyArrayValue(assign: Assignment): List<IAstModification> {
val identifier = assign.target.identifier!! val identifier = assign.target.identifier!!
val targetVar = identifier.targetVarDecl(program)!! val targetVar = identifier.targetVarDecl(program)!!
val alv = assign.value as? ArrayLiteralValue
return flattenArrayAssign(targetVar, alv, identifier, assign.position)
}
private fun flattenArrayAssignmentFromIdentifier(assign: Assignment): List<Assignment> { if(targetVar.arraysize==null)
val identifier = assign.target.identifier!! errors.err("array has no defined size", assign.position)
val targetVar = identifier.targetVarDecl(program)!!
if(assign.value !is IdentifierReference) {
errors.err("invalid array value to assign to other array", assign.value.position)
return noModifications
}
val sourceIdent = assign.value as IdentifierReference val sourceIdent = assign.value as IdentifierReference
val sourceVar = sourceIdent.targetVarDecl(program)!! val sourceVar = sourceIdent.targetVarDecl(program)!!
if(!sourceVar.isArray) { if(!sourceVar.isArray) {
errors.err("value must be an array", sourceIdent.position) errors.err("value must be an array", sourceIdent.position)
return emptyList() } else {
if (sourceVar.arraysize!!.constIndex() != targetVar.arraysize!!.constIndex())
errors.err("element count mismatch", assign.position)
if (sourceVar.datatype != targetVar.datatype)
errors.err("element type mismatch", assign.position)
} }
val alv = sourceVar.value as? ArrayLiteralValue
return flattenArrayAssign(targetVar, alv, identifier, assign.position) if(!errors.isEmpty())
return noModifications
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), assign.position),
mutableListOf(
AddressOf(sourceIdent, assign.position),
AddressOf(identifier, assign.position),
NumericLiteralValue.optimalInteger(targetVar.arraysize!!.constIndex()!!, assign.position)
),
true,
assign.position
)
return listOf(IAstModification.ReplaceNode(assign, memcopy, assign.parent))
} }
private fun flattenArrayAssign(targetVar: VarDecl, alv: ArrayLiteralValue?, identifier: IdentifierReference, position: Position): List<Assignment> { private fun copyStructValue(structAssignment: Assignment): List<IAstModification> {
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.mapIndexed { 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 targetVar = identifier.targetVarDecl(program)!!
val struct = targetVar.struct!!
val slv = structAssignment.value as? ArrayLiteralValue
if(slv==null || slv.value.size != struct.numberOfElements) {
errors.err("element count mismatch", structAssignment.position)
return emptyList()
}
return struct.statements.zip(slv.value).map { (targetDecl, sourceValue) ->
targetDecl as VarDecl
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
sourceValue, sourceValue.position)
assign.linkParents(structAssignment)
assign
}
}
private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment): List<Assignment> {
// TODO use memcopy beyond a certain number of elements
val identifier = structAssignment.target.identifier!!
val identifierName = identifier.nameInSource.single()
val targetVar = identifier.targetVarDecl(program)!! val targetVar = identifier.targetVarDecl(program)!!
val struct = targetVar.struct!! val struct = targetVar.struct!!
when (structAssignment.value) { when (structAssignment.value) {
is IdentifierReference -> { is IdentifierReference -> {
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program)!! val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program)!!
val memsize = struct.memsize(program.memsizer)
when { when {
sourceVar.struct!=null -> { sourceVar.struct!=null -> {
// struct memberwise copy // struct memberwise copy
val sourceStruct = sourceVar.struct!! val sourceStruct = sourceVar.struct!!
if(sourceStruct!==targetVar.struct) { if(sourceStruct!==targetVar.struct) {
// structs are not the same in assignment errors.err("struct type mismatch", structAssignment.position)
return listOf() // error will be printed elsewhere return listOf()
} }
if(struct.statements.size!=sourceStruct.statements.size) if(struct.statements.size!=sourceStruct.statements.size) {
return listOf() // error will be printed elsewhere errors.err("struct element count mismatch", structAssignment.position)
return struct.statements.zip(sourceStruct.statements).map { member -> return listOf()
val targetDecl = member.first as VarDecl
val sourceDecl = member.second as VarDecl
if(targetDecl.name != sourceDecl.name)
throw FatalAstException("struct member mismatch")
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), sourceIdref, member.second.position)
assign.linkParents(structAssignment)
assign
} }
if(memsize!=sourceStruct.memsize(program.memsizer)) {
errors.err("memory size mismatch", structAssignment.position)
return listOf()
}
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), structAssignment.position),
mutableListOf(
AddressOf(structAssignment.value as IdentifierReference, structAssignment.position),
AddressOf(identifier, structAssignment.position),
NumericLiteralValue.optimalInteger(memsize, structAssignment.position)
),
true,
structAssignment.position
)
return listOf(IAstModification.ReplaceNode(structAssignment, memcopy, structAssignment.parent))
} }
sourceVar.isArray -> { sourceVar.isArray -> {
val array = (sourceVar.value as ArrayLiteralValue).value val array = sourceVar.value as ArrayLiteralValue
if(struct.statements.size!=array.size) if(struct.statements.size!=array.value.size) {
return listOf() // error will be printed elsewhere errors.err("struct element count mismatch", structAssignment.position)
return struct.statements.zip(array).map { return listOf()
val decl = it.first as VarDecl
val mangled = mangledStructMemberName(identifierName, decl.name)
val targetName = IdentifierReference(listOf(mangled), structAssignment.position)
val target = AssignTarget(targetName, null, null, structAssignment.position)
val assign = Assignment(target, it.second, structAssignment.position)
assign.linkParents(structAssignment)
assign
} }
if(memsize!=array.memsize(program.memsizer)) {
errors.err("memory size mismatch", structAssignment.position)
return listOf()
}
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), structAssignment.position),
mutableListOf(
AddressOf(structAssignment.value as IdentifierReference, structAssignment.position),
AddressOf(identifier, structAssignment.position),
NumericLiteralValue.optimalInteger(memsize, structAssignment.position)
),
true,
structAssignment.position
)
return listOf(IAstModification.ReplaceNode(structAssignment, memcopy, structAssignment.parent))
} }
else -> { else -> {
throw FatalAstException("can only assign arrays or structs to structs") throw FatalAstException("can only assign arrays or structs to structs")
@ -428,7 +390,7 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
} }
} }
is ArrayLiteralValue -> { is ArrayLiteralValue -> {
throw IllegalArgumentException("not going to flatten a structLv assignment here") throw IllegalArgumentException("not going to do a structLv assignment here")
} }
else -> throw FatalAstException("strange struct value") else -> throw FatalAstException("strange struct value")
} }

View File

@ -8,11 +8,11 @@ import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.compiler.ErrorReporter import prog8.compiler.IErrorReporter
import prog8.compiler.functions.BuiltinFunctions import prog8.compiler.functions.BuiltinFunctions
class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalker() { class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalker() {
/* /*
* Make sure any value assignments get the proper type casts if needed to cast them into the target variable's type. * Make sure any value assignments get the proper type casts if needed to cast them into the target variable's type.
* (this includes function call arguments) * (this includes function call arguments)

View File

@ -1,19 +1,19 @@
package prog8.compiler.functions package prog8.compiler.functions
import prog8.ast.IMemSizer
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.StructDecl import prog8.ast.statements.StructDecl
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.compiler.CompilerException import prog8.compiler.CompilerException
import prog8.compiler.target.ICompilationTarget
import kotlin.math.* import kotlin.math.*
class FParam(val name: String, val possibleDatatypes: Set<DataType>) class FParam(val name: String, val possibleDatatypes: Set<DataType>)
typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteralValue typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer) -> NumericLiteralValue
class ReturnConvention(val dt: DataType, val reg: RegisterOrPair?, val floatFac1: Boolean) class ReturnConvention(val dt: DataType, val reg: RegisterOrPair?, val floatFac1: Boolean)
@ -88,6 +88,7 @@ class FSignature(val name: String,
} }
} }
@Suppress("UNUSED_ANONYMOUS_PARAMETER")
private val functionSignatures: List<FSignature> = listOf( private val functionSignatures: List<FSignature> = listOf(
// this set of function have no return value and operate in-place: // this set of function have no return value and operate in-place:
FSignature("rol" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null), FSignature("rol" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
@ -96,46 +97,48 @@ private val functionSignatures: List<FSignature> = listOf(
FSignature("ror2" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null), FSignature("ror2" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null), FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null),
FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null), FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null),
FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null),
// these few have a return value depending on the argument(s): // these few have a return value depending on the argument(s):
FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args
FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args
FSignature("sum" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args FSignature("sum" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
FSignature("sizeof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof), FSignature("sizeof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof),
FSignature("offsetof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinOffsetof), FSignature("offsetof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinOffsetof),
// normal functions follow: // normal functions follow:
FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ), FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
FSignature("sin" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) }, FSignature("sin" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::sin) },
FSignature("sin8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ), FSignature("sin8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
FSignature("sin8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ), FSignature("sin8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
FSignature("sin16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ), FSignature("sin16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
FSignature("sin16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ), FSignature("sin16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
FSignature("cos" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) }, FSignature("cos" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::cos) },
FSignature("cos8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ), FSignature("cos8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
FSignature("cos8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ), FSignature("cos8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
FSignature("cos16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ), FSignature("cos16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
FSignature("cos16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ), FSignature("cos16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
FSignature("tan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) }, FSignature("tan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::tan) },
FSignature("atan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) }, FSignature("atan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::atan) },
FSignature("ln" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) }, FSignature("ln" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::log) },
FSignature("log2" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) }, FSignature("log2" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, ::log2) },
FSignature("sqrt16" , true, listOf(FParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } }, FSignature("sqrt16" , true, listOf(FParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
FSignature("sqrt" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) }, FSignature("sqrt" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::sqrt) },
FSignature("rad" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) }, FSignature("rad" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::toRadians) },
FSignature("deg" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) }, FSignature("deg" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::toDegrees) },
FSignature("round" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::round) }, FSignature("round" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
FSignature("floor" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) }, FSignature("floor" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
FSignature("ceil" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) }, FSignature("ceil" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) }, FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinAny) },
FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) }, FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinAll) },
FSignature("lsb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 } }, FSignature("lsb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 } },
FSignature("msb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255} }, FSignature("msb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255} },
FSignature("mkword" , true, listOf(FParam("msb", setOf(DataType.UBYTE)), FParam("lsb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword), FSignature("mkword" , true, listOf(FParam("msb", setOf(DataType.UBYTE)), FParam("lsb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
FSignature("peek" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UBYTE), FSignature("peek" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UBYTE),
FSignature("peekw" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UWORD), FSignature("peekw" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UWORD),
FSignature("poke" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UBYTE))), null), FSignature("poke" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UBYTE))), null),
FSignature("pokew" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UWORD))), null), FSignature("pokew" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UWORD))), null),
FSignature("fastrnd8" , false, emptyList(), DataType.UBYTE),
FSignature("rnd" , false, emptyList(), DataType.UBYTE), FSignature("rnd" , false, emptyList(), DataType.UBYTE),
FSignature("rndw" , false, emptyList(), DataType.UWORD), FSignature("rndw" , false, emptyList(), DataType.UWORD),
FSignature("rndf" , false, emptyList(), DataType.FLOAT), FSignature("rndf" , false, emptyList(), DataType.FLOAT),
@ -271,7 +274,8 @@ private fun collectionArg(args: List<Expression>, position: Position, program: P
return NumericLiteralValue.optimalNumeric(function(constElements.mapNotNull { it }), args[0].position) return NumericLiteralValue.optimalNumeric(function(constElements.mapNotNull { it }), args[0].position)
} }
private fun builtinAbs(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { @Suppress("UNUSED_PARAMETER")
private fun builtinAbs(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
// 1 arg, type = float or int, result type= isSameAs as argument type // 1 arg, type = float or int, result type= isSameAs as argument type
if(args.size!=1) if(args.size!=1)
throw SyntaxError("abs requires one numeric argument", position) throw SyntaxError("abs requires one numeric argument", position)
@ -284,7 +288,7 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
} }
} }
private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
// 1 arg, type = anything, result type = ubyte // 1 arg, type = anything, result type = ubyte
if(args.size!=1) if(args.size!=1)
throw SyntaxError("offsetof requires one argument", position) throw SyntaxError("offsetof requires one argument", position)
@ -301,12 +305,12 @@ private fun builtinOffsetof(args: List<Expression>, position: Position, program:
for(member in struct.statements) { for(member in struct.statements) {
if((member as VarDecl).name == membername) if((member as VarDecl).name == membername)
return NumericLiteralValue(DataType.UBYTE, offset, position) return NumericLiteralValue(DataType.UBYTE, offset, position)
offset += ICompilationTarget.instance.memorySize(member.datatype) offset += memsizer.memorySize(member.datatype)
} }
throw SyntaxError("undefined struct member", position) throw SyntaxError("undefined struct member", position)
} }
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { private fun builtinSizeof(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
// 1 arg, type = anything, result type = ubyte // 1 arg, type = anything, result type = ubyte
if(args.size!=1) if(args.size!=1)
throw SyntaxError("sizeof requires one argument", position) throw SyntaxError("sizeof requires one argument", position)
@ -318,14 +322,13 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
val target = (args[0] as IdentifierReference).targetStatement(program) val target = (args[0] as IdentifierReference).targetStatement(program)
?: throw CannotEvaluateException("sizeof", "no target") ?: throw CannotEvaluateException("sizeof", "no target")
fun structSize(target: StructDecl) = fun structSize(target: StructDecl) = NumericLiteralValue(DataType.UBYTE, target.memsize(memsizer), position)
NumericLiteralValue(DataType.UBYTE, target.statements.map { ICompilationTarget.instance.memorySize((it as VarDecl).datatype) }.sum(), position)
return when { return when {
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> { dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {
val length = (target as VarDecl).arraysize!!.constIndex() ?: throw CannotEvaluateException("sizeof", "unknown array size") val length = (target as VarDecl).arraysize!!.constIndex() ?: throw CannotEvaluateException("sizeof", "unknown array size")
val elementDt = ArrayElementTypes.getValue(dt.typeOrElse(DataType.STRUCT)) val elementDt = ArrayElementTypes.getValue(dt.typeOrElse(DataType.STRUCT))
numericLiteral(ICompilationTarget.instance.memorySize(elementDt) * length, position) numericLiteral(memsizer.memorySize(elementDt) * length, position)
} }
dt.istype(DataType.STRUCT) -> { dt.istype(DataType.STRUCT) -> {
when (target) { when (target) {
@ -335,14 +338,15 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
} }
} }
dt.istype(DataType.STR) -> throw SyntaxError("sizeof str is undefined, did you mean len?", position) dt.istype(DataType.STR) -> throw SyntaxError("sizeof str is undefined, did you mean len?", position)
else -> NumericLiteralValue(DataType.UBYTE, ICompilationTarget.instance.memorySize(dt.typeOrElse(DataType.STRUCT)), position) else -> NumericLiteralValue(DataType.UBYTE, memsizer.memorySize(dt.typeOrElse(DataType.STRUCT)), position)
} }
} else { } else {
throw SyntaxError("sizeof invalid argument type", position) throw SyntaxError("sizeof invalid argument type", position)
} }
} }
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { @Suppress("UNUSED_PARAMETER")
private fun builtinLen(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE. // note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
if(args.size!=1) if(args.size!=1)
throw SyntaxError("len requires one argument", position) throw SyntaxError("len requires one argument", position)
@ -366,7 +370,7 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
NumericLiteralValue.optimalInteger(arraySize, args[0].position) NumericLiteralValue.optimalInteger(arraySize, args[0].position)
} }
DataType.STR -> { DataType.STR -> {
val refLv = target.value as StringLiteralValue val refLv = target.value as? StringLiteralValue ?: throw CannotEvaluateException("len", "stringsize unknown")
NumericLiteralValue.optimalInteger(refLv.value.length, args[0].position) NumericLiteralValue.optimalInteger(refLv.value.length, args[0].position)
} }
DataType.STRUCT -> throw SyntaxError("cannot use len on struct, did you mean sizeof?", args[0].position) DataType.STRUCT -> throw SyntaxError("cannot use len on struct, did you mean sizeof?", args[0].position)
@ -376,7 +380,8 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
} }
private fun builtinMkword(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { @Suppress("UNUSED_PARAMETER")
private fun builtinMkword(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
if (args.size != 2) if (args.size != 2)
throw SyntaxError("mkword requires msb and lsb arguments", position) throw SyntaxError("mkword requires msb and lsb arguments", position)
val constMsb = args[0].constValue(program) ?: throw NotConstArgumentException() val constMsb = args[0].constValue(program) ?: throw NotConstArgumentException()
@ -385,7 +390,8 @@ private fun builtinMkword(args: List<Expression>, position: Position, program: P
return NumericLiteralValue(DataType.UWORD, result, position) return NumericLiteralValue(DataType.UWORD, result, position)
} }
private fun builtinSin8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { @Suppress("UNUSED_PARAMETER")
private fun builtinSin8(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
if (args.size != 1) if (args.size != 1)
throw SyntaxError("sin8 requires one argument", position) throw SyntaxError("sin8 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException() val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
@ -393,7 +399,8 @@ private fun builtinSin8(args: List<Expression>, position: Position, program: Pro
return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toInt().toShort(), position) return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toInt().toShort(), position)
} }
private fun builtinSin8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { @Suppress("UNUSED_PARAMETER")
private fun builtinSin8u(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
if (args.size != 1) if (args.size != 1)
throw SyntaxError("sin8u requires one argument", position) throw SyntaxError("sin8u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException() val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
@ -401,7 +408,8 @@ private fun builtinSin8u(args: List<Expression>, position: Position, program: Pr
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toInt().toShort(), position) return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toInt().toShort(), position)
} }
private fun builtinCos8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { @Suppress("UNUSED_PARAMETER")
private fun builtinCos8(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
if (args.size != 1) if (args.size != 1)
throw SyntaxError("cos8 requires one argument", position) throw SyntaxError("cos8 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException() val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
@ -409,7 +417,8 @@ private fun builtinCos8(args: List<Expression>, position: Position, program: Pro
return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toInt().toShort(), position) return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toInt().toShort(), position)
} }
private fun builtinCos8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { @Suppress("UNUSED_PARAMETER")
private fun builtinCos8u(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
if (args.size != 1) if (args.size != 1)
throw SyntaxError("cos8u requires one argument", position) throw SyntaxError("cos8u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException() val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
@ -417,7 +426,8 @@ private fun builtinCos8u(args: List<Expression>, position: Position, program: Pr
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toInt().toShort(), position) return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toInt().toShort(), position)
} }
private fun builtinSin16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { @Suppress("UNUSED_PARAMETER")
private fun builtinSin16(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
if (args.size != 1) if (args.size != 1)
throw SyntaxError("sin16 requires one argument", position) throw SyntaxError("sin16 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException() val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
@ -425,7 +435,8 @@ private fun builtinSin16(args: List<Expression>, position: Position, program: Pr
return NumericLiteralValue(DataType.WORD, (32767.0 * sin(rad)).toInt(), position) return NumericLiteralValue(DataType.WORD, (32767.0 * sin(rad)).toInt(), position)
} }
private fun builtinSin16u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { @Suppress("UNUSED_PARAMETER")
private fun builtinSin16u(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
if (args.size != 1) if (args.size != 1)
throw SyntaxError("sin16u requires one argument", position) throw SyntaxError("sin16u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException() val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
@ -433,7 +444,8 @@ private fun builtinSin16u(args: List<Expression>, position: Position, program: P
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * sin(rad)).toInt(), position) return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * sin(rad)).toInt(), position)
} }
private fun builtinCos16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { @Suppress("UNUSED_PARAMETER")
private fun builtinCos16(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
if (args.size != 1) if (args.size != 1)
throw SyntaxError("cos16 requires one argument", position) throw SyntaxError("cos16 requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException() val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
@ -441,7 +453,8 @@ private fun builtinCos16(args: List<Expression>, position: Position, program: Pr
return NumericLiteralValue(DataType.WORD, (32767.0 * cos(rad)).toInt(), position) return NumericLiteralValue(DataType.WORD, (32767.0 * cos(rad)).toInt(), position)
} }
private fun builtinCos16u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { @Suppress("UNUSED_PARAMETER")
private fun builtinCos16u(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
if (args.size != 1) if (args.size != 1)
throw SyntaxError("cos16u requires one argument", position) throw SyntaxError("cos16u requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException() val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
@ -449,7 +462,8 @@ private fun builtinCos16u(args: List<Expression>, position: Position, program: P
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * cos(rad)).toInt(), position) return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * cos(rad)).toInt(), position)
} }
private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteralValue { @Suppress("UNUSED_PARAMETER")
private fun builtinSgn(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
if (args.size != 1) if (args.size != 1)
throw SyntaxError("sgn requires one argument", position) throw SyntaxError("sgn requires one argument", position)
val constval = args[0].constValue(program) ?: throw NotConstArgumentException() val constval = args[0].constValue(program) ?: throw NotConstArgumentException()

View File

@ -1,5 +1,6 @@
package prog8.compiler.target package prog8.compiler.target
import prog8.ast.IMemSizer
import prog8.ast.IStringEncoding import prog8.ast.IStringEncoding
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
@ -7,25 +8,20 @@ import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.statements.AssignTarget import prog8.ast.statements.AssignTarget
import prog8.compiler.CompilationOptions import prog8.compiler.CompilationOptions
import prog8.compiler.ErrorReporter import prog8.compiler.IErrorReporter
import prog8.compiler.Zeropage import prog8.compiler.Zeropage
import prog8.compiler.target.c64.C64MachineDefinition import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.c64.Petscii import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.c64.codegen.AsmGen import prog8.compiler.target.cpu6502.codegen.AsmGen
import prog8.compiler.target.cx16.CX16MachineDefinition import prog8.compiler.target.cx16.CX16MachineDefinition
import java.nio.file.Path import java.nio.file.Path
internal interface ICompilationTarget: IStringEncoding { interface ICompilationTarget: IStringEncoding, IMemSizer {
val name: String val name: String
val machine: IMachineDefinition val machine: IMachineDefinition
override fun encodeString(str: String, altEncoding: Boolean): List<Short> override fun encodeString(str: String, altEncoding: Boolean): List<Short>
override fun decodeString(bytes: List<Short>, altEncoding: Boolean): String override fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
fun memorySize(dt: DataType): Int
companion object {
lateinit var instance: ICompilationTarget // TODO reduce dependency on this by just passing the instance as a parameter
}
fun isInRegularRAM(target: AssignTarget, program: Program): Boolean { fun isInRegularRAM(target: AssignTarget, program: Program): Boolean {
val memAddr = target.memoryAddress val memAddr = target.memoryAddress
@ -67,7 +63,6 @@ internal interface ICompilationTarget: IStringEncoding {
else -> return true else -> return true
} }
} }
} }
@ -113,11 +108,12 @@ internal object Cx16Target: ICompilationTarget {
internal fun asmGeneratorFor( internal fun asmGeneratorFor(
compTarget: ICompilationTarget, compTarget: ICompilationTarget,
program: Program, program: Program,
errors: ErrorReporter, errors: IErrorReporter,
zp: Zeropage, zp: Zeropage,
options: CompilationOptions, options: CompilationOptions,
outputDir: Path outputDir: Path
): IAssemblyGenerator ): IAssemblyGenerator
{ {
// at the moment we only have one code generation backend (for 6502 and 65c02)
return AsmGen(program, errors, zp, options, compTarget, outputDir) return AsmGen(program, errors, zp, options, compTarget, outputDir)
} }

View File

@ -1,23 +1,20 @@
package prog8.compiler.target package prog8.compiler.target
import prog8.ast.IStringEncoding
import prog8.ast.Program
import prog8.compiler.CompilationOptions import prog8.compiler.CompilationOptions
import prog8.compiler.Zeropage import prog8.compiler.Zeropage
import prog8.parser.ModuleImporter
internal interface IMachineFloat { interface IMachineFloat {
fun toDouble(): Double fun toDouble(): Double
fun makeFloatFillAsm(): String fun makeFloatFillAsm(): String
} }
internal enum class CpuType { enum class CpuType {
CPU6502, CPU6502,
CPU65c02 CPU65c02
} }
internal interface IMachineDefinition { interface IMachineDefinition {
val FLOAT_MAX_NEGATIVE: Double val FLOAT_MAX_NEGATIVE: Double
val FLOAT_MAX_POSITIVE: Double val FLOAT_MAX_POSITIVE: Double
val FLOAT_MEM_SIZE: Int val FLOAT_MEM_SIZE: Int
@ -34,10 +31,7 @@ internal interface IMachineDefinition {
fun initializeZeropage(compilerOptions: CompilationOptions) fun initializeZeropage(compilerOptions: CompilationOptions)
fun getFloat(num: Number): IMachineFloat fun getFloat(num: Number): IMachineFloat
// TODO don't do the importing here, just return a list of modules to import...: fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String>
fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program,
encoder: IStringEncoding, compilationTargetName: String)
fun launchEmulator(programName: String) fun launchEmulator(programName: String)
fun isRegularRAMaddress(address: Int): Boolean fun isRegularRAMaddress(address: Int): Boolean
} }

View File

@ -1,12 +1,9 @@
package prog8.compiler.target.c64 package prog8.compiler.target.c64
import prog8.ast.IStringEncoding
import prog8.ast.Program
import prog8.compiler.* import prog8.compiler.*
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
import prog8.compiler.target.IMachineDefinition import prog8.compiler.target.IMachineDefinition
import prog8.compiler.target.IMachineFloat import prog8.compiler.target.IMachineFloat
import prog8.parser.ModuleImporter
import java.io.IOException import java.io.IOException
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.math.pow import kotlin.math.pow
@ -31,15 +28,11 @@ internal object C64MachineDefinition: IMachineDefinition {
override fun getFloat(num: Number) = Mflpt5.fromNumber(num) override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
override fun importLibs( override fun importLibs(compilerOptions: CompilationOptions,compilationTargetName: String): List<String> {
compilerOptions: CompilationOptions, return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
importer: ModuleImporter, listOf("syslib")
program: Program, else
encoder: IStringEncoding, emptyList()
compilationTargetName: String)
{
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
importer.importLibraryModule(program, "syslib", encoder, compilationTargetName)
} }
override fun launchEmulator(programName: String) { override fun launchEmulator(programName: String) {
@ -109,13 +102,14 @@ internal object C64MachineDefinition: IMachineDefinition {
0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c,
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0xff
// 0x90-0xfa is 'kernel work storage area' // 0x90-0xfa is 'kernal work storage area'
)) ))
} }
if (options.zeropage == ZeropageType.FLOATSAFE) { if (options.zeropage == ZeropageType.FLOATSAFE) {
// remove the zero page locations used for floating point operations from the free list // remove the zero page locations used for floating point operations from the free list
free.removeAll(listOf( free.removeAll(listOf(
0x22, 0x23, 0x24, 0x25,
0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
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,

View File

@ -172,7 +172,7 @@ object Petscii {
'\u258c', // ▌ 0xA1 -> LEFT HALF BLOCK '\u258c', // ▌ 0xA1 -> LEFT HALF BLOCK
'\u2584', // ▄ 0xA2 -> LOWER HALF BLOCK '\u2584', // ▄ 0xA2 -> LOWER HALF BLOCK
'\u2594', // ▔ 0xA3 -> UPPER ONE EIGHTH BLOCK '\u2594', // ▔ 0xA3 -> UPPER ONE EIGHTH BLOCK
'\u2581', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK '_', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
'\u258f', // ▏ 0xA5 -> LEFT ONE EIGHTH BLOCK '\u258f', // ▏ 0xA5 -> LEFT ONE EIGHTH BLOCK
'\u2592', // ▒ 0xA6 -> MEDIUM SHADE '\u2592', // ▒ 0xA6 -> MEDIUM SHADE
'\u2595', // ▕ 0xA7 -> RIGHT ONE EIGHTH BLOCK '\u2595', // ▕ 0xA7 -> RIGHT ONE EIGHTH BLOCK
@ -236,7 +236,7 @@ object Petscii {
'\u258c', // ▌ 0xE1 -> LEFT HALF BLOCK '\u258c', // ▌ 0xE1 -> LEFT HALF BLOCK
'\u2584', // ▄ 0xE2 -> LOWER HALF BLOCK '\u2584', // ▄ 0xE2 -> LOWER HALF BLOCK
'\u2594', // ▔ 0xE3 -> UPPER ONE EIGHTH BLOCK '\u2594', // ▔ 0xE3 -> UPPER ONE EIGHTH BLOCK
'\u2581', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK '_', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
'\u258f', // ▏ 0xE5 -> LEFT ONE EIGHTH BLOCK '\u258f', // ▏ 0xE5 -> LEFT ONE EIGHTH BLOCK
'\u2592', // ▒ 0xE6 -> MEDIUM SHADE '\u2592', // ▒ 0xE6 -> MEDIUM SHADE
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK '\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
@ -431,7 +431,7 @@ object Petscii {
'\u258c', // ▌ 0xA1 -> LEFT HALF BLOCK '\u258c', // ▌ 0xA1 -> LEFT HALF BLOCK
'\u2584', // ▄ 0xA2 -> LOWER HALF BLOCK '\u2584', // ▄ 0xA2 -> LOWER HALF BLOCK
'\u2594', // ▔ 0xA3 -> UPPER ONE EIGHTH BLOCK '\u2594', // ▔ 0xA3 -> UPPER ONE EIGHTH BLOCK
'\u2581', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK '_', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
'\u258f', // ▏ 0xA5 -> LEFT ONE EIGHTH BLOCK '\u258f', // ▏ 0xA5 -> LEFT ONE EIGHTH BLOCK
'\u2592', // ▒ 0xA6 -> MEDIUM SHADE '\u2592', // ▒ 0xA6 -> MEDIUM SHADE
'\u2595', // ▕ 0xA7 -> RIGHT ONE EIGHTH BLOCK '\u2595', // ▕ 0xA7 -> RIGHT ONE EIGHTH BLOCK
@ -495,7 +495,7 @@ object Petscii {
'\u258c', // ▌ 0xE1 -> LEFT HALF BLOCK '\u258c', // ▌ 0xE1 -> LEFT HALF BLOCK
'\u2584', // ▄ 0xE2 -> LOWER HALF BLOCK '\u2584', // ▄ 0xE2 -> LOWER HALF BLOCK
'\u2594', // ▔ 0xE3 -> UPPER ONE EIGHTH BLOCK '\u2594', // ▔ 0xE3 -> UPPER ONE EIGHTH BLOCK
'\u2581', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK '_', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
'\u258f', // ▏ 0xE5 -> LEFT ONE EIGHTH BLOCK '\u258f', // ▏ 0xE5 -> LEFT ONE EIGHTH BLOCK
'\u2592', // ▒ 0xE6 -> MEDIUM SHADE '\u2592', // ▒ 0xE6 -> MEDIUM SHADE
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK '\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
@ -626,7 +626,7 @@ object Petscii {
'\u258c', // ▌ 0x61 -> LEFT HALF BLOCK '\u258c', // ▌ 0x61 -> LEFT HALF BLOCK
'\u2584', // ▄ 0x62 -> LOWER HALF BLOCK '\u2584', // ▄ 0x62 -> LOWER HALF BLOCK
'\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK '\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK
'\u2581', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK '_', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
'\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK '\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK
'\u2592', // ▒ 0x66 -> MEDIUM SHADE '\u2592', // ▒ 0x66 -> MEDIUM SHADE
'\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK '\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK
@ -885,7 +885,7 @@ object Petscii {
'\u258c', // ▌ 0x61 -> LEFT HALF BLOCK '\u258c', // ▌ 0x61 -> LEFT HALF BLOCK
'\u2584', // ▄ 0x62 -> LOWER HALF BLOCK '\u2584', // ▄ 0x62 -> LOWER HALF BLOCK
'\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK '\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK
'\u2581', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK '_', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
'\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK '\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK
'\u2592', // ▒ 0x66 -> MEDIUM SHADE '\u2592', // ▒ 0x66 -> MEDIUM SHADE
'\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK '\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen package prog8.compiler.target.cpu6502.codegen
import prog8.ast.* import prog8.ast.*
import prog8.ast.antlr.escape import prog8.ast.antlr.escape
@ -11,10 +11,11 @@ import prog8.compiler.functions.FSignature
import prog8.compiler.target.* import prog8.compiler.target.*
import prog8.compiler.target.c64.AssemblyProgram import prog8.compiler.target.c64.AssemblyProgram
import prog8.compiler.target.c64.Petscii import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
import prog8.compiler.target.c64.codegen.assignment.AssignmentAsmGen import prog8.compiler.target.cpu6502.codegen.assignment.AssignmentAsmGen
import java.io.CharConversionException import java.io.CharConversionException
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
import java.util.* import java.util.*
@ -22,10 +23,10 @@ import kotlin.math.absoluteValue
internal class AsmGen(private val program: Program, internal class AsmGen(private val program: Program,
val errors: ErrorReporter, val errors: IErrorReporter,
val zeropage: Zeropage, val zeropage: Zeropage,
val options: CompilationOptions, val options: CompilationOptions,
val compTarget: ICompilationTarget, private val compTarget: ICompilationTarget,
private val outputDir: Path): IAssemblyGenerator { private val outputDir: Path): IAssemblyGenerator {
// for expressions and augmented assignments: // for expressions and augmented assignments:
@ -88,12 +89,14 @@ internal class AsmGen(private val program: Program,
return AssemblyProgram(program.name, outputDir, compTarget.name) return AssemblyProgram(program.name, outputDir, compTarget.name)
} }
internal fun isTargetCpu(cpu: CpuType) = compTarget.machine.cpu == cpu
internal fun haveFPWR() = compTarget is Cx16Target
private fun header() { private fun header() {
val ourName = this.javaClass.name val ourName = this.javaClass.name
val cpu = when(compTarget.machine.cpu) { val cpu = when(compTarget.machine.cpu) {
CpuType.CPU6502 -> "6502" CpuType.CPU6502 -> "6502"
CpuType.CPU65c02 -> "65c02" CpuType.CPU65c02 -> "w65c02"
else -> "unsupported" else -> "unsupported"
} }
@ -130,12 +133,14 @@ internal class AsmGen(private val program: Program,
out("_prog8_entrypoint\t; assembly code starts here\n") out("_prog8_entrypoint\t; assembly code starts here\n")
if(!options.noSysInit) if(!options.noSysInit)
out(" jsr ${compTarget.name}.init_system") out(" jsr ${compTarget.name}.init_system")
out(" jsr ${compTarget.name}.init_system_phase2")
} }
options.output == OutputType.PRG -> { options.output == OutputType.PRG -> {
out("; ---- program without basic sys call ----") out("; ---- program without basic sys call ----")
out("* = ${program.actualLoadAddress.toHex()}\n") out("* = ${program.actualLoadAddress.toHex()}\n")
if(!options.noSysInit) if(!options.noSysInit)
out(" jsr ${compTarget.name}.init_system") out(" jsr ${compTarget.name}.init_system")
out(" jsr ${compTarget.name}.init_system_phase2")
} }
options.output == OutputType.RAW -> { options.output == OutputType.RAW -> {
out("; ---- raw assembler program ----") out("; ---- raw assembler program ----")
@ -152,7 +157,7 @@ internal class AsmGen(private val program: Program,
pha""") pha""")
} }
out(" jmp main.start ; start program / force start proc to be included") jmp("main.start")
} }
private fun slaballocations() { private fun slaballocations() {
@ -176,14 +181,17 @@ internal class AsmGen(private val program: Program,
private fun block2asm(block: Block) { private fun block2asm(block: Block) {
out("\n\n; ---- block: '${block.name}' ----") out("\n\n; ---- block: '${block.name}' ----")
out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n")) if(block.address!=null)
out("* = ${block.address!!.toHex()}")
val addr = block.address else {
if(addr!=null) { if("align_word" in block.options())
out(".cerror * > ${addr.toHex()}, 'block address overlaps by ', *-${addr.toHex()},' bytes'") out("\t.align 2")
out("* = ${addr.toHex()}") else if("align_page" in block.options())
out("\t.align $100")
} }
out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
outputSourceLine(block) outputSourceLine(block)
zeropagevars2asm(block.statements) zeropagevars2asm(block.statements)
memdefs2asm(block.statements) memdefs2asm(block.statements)
@ -262,7 +270,7 @@ internal class AsmGen(private val program: Program,
try { try {
val errors = ErrorReporter() val errors = ErrorReporter()
val address = zeropage.allocate(fullName, variable.datatype, null, errors) val address = zeropage.allocate(fullName, variable.datatype, null, errors)
errors.handle() errors.report()
out("${variable.name} = $address\t; auto zp ${variable.datatype}") out("${variable.name} = $address\t; auto zp ${variable.datatype}")
// make sure we add the var to the set of zpvars for this block // make sure we add the var to the set of zpvars for this block
allocatedZeropageVariables[fullName] = address to variable.datatype allocatedZeropageVariables[fullName] = address to variable.datatype
@ -348,7 +356,7 @@ internal class AsmGen(private val program: Program,
} }
private fun memdefs2asm(statements: List<Statement>) { private fun memdefs2asm(statements: List<Statement>) {
out("\n; memdefs and kernel subroutines") out("\n; memdefs and kernal subroutines")
val memvars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST } val memvars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.MEMORY || it.type==VarDeclType.CONST }
for(m in memvars) { for(m in memvars) {
if(m.value is NumericLiteralValue) if(m.value is NumericLiteralValue)
@ -361,7 +369,7 @@ internal class AsmGen(private val program: Program,
val addr = sub.asmAddress val addr = sub.asmAddress
if(addr!=null) { if(addr!=null) {
if(sub.statements.isNotEmpty()) if(sub.statements.isNotEmpty())
throw AssemblyError("kernel subroutine cannot have statements") throw AssemblyError("kernal subroutine cannot have statements")
out(" ${sub.name} = ${addr.toHex()}") out(" ${sub.name} = ${addr.toHex()}")
} }
} }
@ -399,8 +407,9 @@ internal class AsmGen(private val program: Program,
} }
private fun outputStringvar(lastvar: VarDecl, encoded: List<Short>) { private fun outputStringvar(lastvar: VarDecl, encoded: List<Short>) {
val string = (lastvar.value as StringLiteralValue).value val sv = lastvar.value as StringLiteralValue
out("${lastvar.name}\t; ${lastvar.datatype} \"${escape(string).replace("\u0000", "<NULL>")}\"") val altEncoding = if(sv.altEncoding) "@" else ""
out("${lastvar.name}\t; ${lastvar.datatype} $altEncoding\"${escape(sv.value).replace("\u0000", "<NULL>")}\"")
val outputBytes = encoded.map { "$" + it.toString(16).padStart(2, '0') } val outputBytes = encoded.map { "$" + it.toString(16).padStart(2, '0') }
for (chunk in outputBytes.chunked(16)) for (chunk in outputBytes.chunked(16))
out(" .byte " + chunk.joinToString()) out(" .byte " + chunk.joinToString())
@ -527,7 +536,7 @@ internal class AsmGen(private val program: Program,
val sourceName = asmVariableName(pointervar) val sourceName = asmVariableName(pointervar)
val vardecl = pointervar.targetVarDecl(program)!! val vardecl = pointervar.targetVarDecl(program)!!
val scopedName = vardecl.makeScopedName(vardecl.name) val scopedName = vardecl.makeScopedName(vardecl.name)
if (compTarget.machine.cpu == CpuType.CPU65c02) { if (isTargetCpu(CpuType.CPU65c02)) {
return if (isZpVar(scopedName)) { return if (isZpVar(scopedName)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
out(" lda ($sourceName)") out(" lda ($sourceName)")
@ -562,7 +571,7 @@ internal class AsmGen(private val program: Program,
private 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 saveRegisterLocal(register: CpuRegister, scope: Subroutine) { internal fun saveRegisterLocal(register: CpuRegister, scope: Subroutine) {
if (compTarget.machine.cpu == CpuType.CPU65c02) { if (isTargetCpu(CpuType.CPU65c02)) {
// just use the cpu's stack for all registers, shorter code // just use the cpu's stack for all registers, shorter code
when (register) { when (register) {
CpuRegister.A -> out(" pha") CpuRegister.A -> out(" pha")
@ -591,7 +600,7 @@ internal class AsmGen(private val program: Program,
when (register) { when (register) {
CpuRegister.A -> out(" pha") CpuRegister.A -> out(" pha")
CpuRegister.X -> { CpuRegister.X -> {
if (compTarget.machine.cpu == CpuType.CPU65c02) out(" phx") if (isTargetCpu(CpuType.CPU65c02)) out(" phx")
else { else {
if(keepA) if(keepA)
out(" sta P8ZP_SCRATCH_REG | txa | pha | lda P8ZP_SCRATCH_REG") out(" sta P8ZP_SCRATCH_REG | txa | pha | lda P8ZP_SCRATCH_REG")
@ -600,7 +609,7 @@ internal class AsmGen(private val program: Program,
} }
} }
CpuRegister.Y -> { CpuRegister.Y -> {
if (compTarget.machine.cpu == CpuType.CPU65c02) out(" phy") if (isTargetCpu(CpuType.CPU65c02)) out(" phy")
else { else {
if(keepA) if(keepA)
out(" sta P8ZP_SCRATCH_REG | tya | pha | lda P8ZP_SCRATCH_REG") out(" sta P8ZP_SCRATCH_REG | tya | pha | lda P8ZP_SCRATCH_REG")
@ -612,7 +621,7 @@ internal class AsmGen(private val program: Program,
} }
internal fun restoreRegisterLocal(register: CpuRegister) { internal fun restoreRegisterLocal(register: CpuRegister) {
if (compTarget.machine.cpu == CpuType.CPU65c02) { if (isTargetCpu(CpuType.CPU65c02)) {
when (register) { when (register) {
// this just used the stack, for all registers. Shorter code. // this just used the stack, for all registers. Shorter code.
CpuRegister.A -> out(" pla") CpuRegister.A -> out(" pla")
@ -637,7 +646,7 @@ internal class AsmGen(private val program: Program,
out(" pla") out(" pla")
} }
CpuRegister.X -> { CpuRegister.X -> {
if (compTarget.machine.cpu == CpuType.CPU65c02) out(" plx") if (isTargetCpu(CpuType.CPU65c02)) out(" plx")
else { else {
if(keepA) if(keepA)
out(" sta P8ZP_SCRATCH_REG | pla | tax | lda P8ZP_SCRATCH_REG") out(" sta P8ZP_SCRATCH_REG | pla | tax | lda P8ZP_SCRATCH_REG")
@ -646,7 +655,7 @@ internal class AsmGen(private val program: Program,
} }
} }
CpuRegister.Y -> { CpuRegister.Y -> {
if (compTarget.machine.cpu == CpuType.CPU65c02) out(" ply") if (isTargetCpu(CpuType.CPU65c02)) out(" ply")
else { else {
if(keepA) if(keepA)
out(" sta P8ZP_SCRATCH_REG | pla | tay | lda P8ZP_SCRATCH_REG") out(" sta P8ZP_SCRATCH_REG | pla | tay | lda P8ZP_SCRATCH_REG")
@ -686,7 +695,7 @@ internal class AsmGen(private val program: Program,
is Break -> { is Break -> {
if(loopEndLabels.isEmpty()) if(loopEndLabels.isEmpty())
throw AssemblyError("break statement out of context ${stmt.position}") throw AssemblyError("break statement out of context ${stmt.position}")
out(" jmp ${loopEndLabels.peek()}") jmp(loopEndLabels.peek())
} }
is WhileLoop -> translate(stmt) is WhileLoop -> translate(stmt)
is RepeatLoop -> translate(stmt) is RepeatLoop -> translate(stmt)
@ -711,7 +720,10 @@ internal class AsmGen(private val program: Program,
return return
} }
val indexName = asmVariableName(expr.indexer.indexVar!!) val indexVar = expr.indexer.indexExpr as? IdentifierReference
?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.indexer.position}")
val indexName = asmVariableName(indexVar)
if(addOneExtra) { if(addOneExtra) {
// add 1 to the result // add 1 to the result
when(elementDt) { when(elementDt) {
@ -921,7 +933,7 @@ internal class AsmGen(private val program: Program,
val endLabel = makeLabel("if_end") val endLabel = makeLabel("if_end")
expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, elseLabel) expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, elseLabel)
translate(stmt.truepart) translate(stmt.truepart)
out(" jmp $endLabel") jmp(endLabel)
out(elseLabel) out(elseLabel)
translate(stmt.elsepart) translate(stmt.elsepart)
out(endLabel) out(endLabel)
@ -943,7 +955,7 @@ internal class AsmGen(private val program: Program,
// endless loop // endless loop
out(repeatLabel) out(repeatLabel)
translate(stmt.body) translate(stmt.body)
out(" jmp $repeatLabel") jmp(repeatLabel)
out(endLabel) out(endLabel)
} }
is NumericLiteralValue -> { is NumericLiteralValue -> {
@ -967,10 +979,12 @@ internal class AsmGen(private val program: Program,
val name = asmVariableName(stmt.iterations as IdentifierReference) val name = asmVariableName(stmt.iterations as IdentifierReference)
when(vardecl.datatype) { when(vardecl.datatype) {
DataType.UBYTE, DataType.BYTE -> { DataType.UBYTE, DataType.BYTE -> {
repeatByteCountVar(name, repeatLabel, endLabel, stmt.body) assignVariableToRegister(name, RegisterOrPair.A)
repeatByteCountInA(null, repeatLabel, endLabel, stmt.body)
} }
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
repeatWordCountVar(name, repeatLabel, endLabel, stmt.body) assignVariableToRegister(name, RegisterOrPair.AY)
repeatWordCountInAY(null, repeatLabel, endLabel, stmt.body)
} }
else -> throw AssemblyError("invalid loop variable datatype $vardecl") else -> throw AssemblyError("invalid loop variable datatype $vardecl")
} }
@ -997,10 +1011,11 @@ internal class AsmGen(private val program: Program,
} }
private fun repeatWordCountInAY(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) { private fun repeatWordCountInAY(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
// note: A/Y must have been loaded with the number of iterations!
if(constIterations==0) if(constIterations==0)
return return
// note: A/Y must have been loaded with the number of iterations already! // no need to explicitly test for 0 iterations as this is done in the count down logic below
// TODO can be even more optimized by iterating over pages
val counterVar = makeLabel("repeatcounter") val counterVar = makeLabel("repeatcounter")
out(""" out("""
sta $counterVar sta $counterVar
@ -1009,80 +1024,48 @@ $repeatLabel lda $counterVar
bne + bne +
lda $counterVar+1 lda $counterVar+1
beq $endLabel beq $endLabel
+ lda $counterVar lda $counterVar
bne + bne +
dec $counterVar+1 dec $counterVar+1
+ dec $counterVar + dec $counterVar
""") """)
translate(body) translate(body)
out(" jmp $repeatLabel") jmp(repeatLabel)
if(constIterations!=null && constIterations>=16 && zeropage.available() > 1) {
// allocate count var on ZP
val zpAddr = zeropage.allocate(counterVar, DataType.UWORD, body.position, errors)
out("""$counterVar = $zpAddr ; auto zp UWORD""")
} else {
out("""
$counterVar .word 0""")
}
out(endLabel)
if(constIterations!=null && constIterations>=16 && zeropage.available() > 1) {
// allocate count var on ZP TODO can be shared with countervars from other subroutines
val zpAddr = zeropage.allocate(counterVar, DataType.UWORD, body.position, errors)
out("$counterVar = $zpAddr ; auto zp UWORD")
} else {
out("$counterVar .word 0")
}
out(endLabel)
} }
private fun repeatByteCountInA(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) { private fun repeatByteCountInA(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
// note: A must have been loaded with the number of iterations!
if(constIterations==0) if(constIterations==0)
return return
// note: A must have been loaded with the number of iterations already!
val counterVar = makeLabel("repeatcounter")
if(constIterations==null) if(constIterations==null)
out(" beq $endLabel") out(" beq $endLabel ; skip loop if zero iters")
val counterVar = makeLabel("repeatcounter")
out(" sta $counterVar") out(" sta $counterVar")
out(repeatLabel) out(repeatLabel)
translate(body) translate(body)
out(""" out("""
dec $counterVar dec $counterVar
bne $repeatLabel bne $repeatLabel
beq $endLabel beq $endLabel""")
$counterVar .byte 0""")
out(endLabel)
}
private fun repeatByteCountVar(repeatCountVar: String, repeatLabel: String, endLabel: String, body: AnonymousScope) { if(constIterations!=null && constIterations>=16 && zeropage.available() > 0) {
// note: cannot use original counter variable because it should retain its original value // allocate count var on ZP TODO can be shared with countervars from other subroutines
val counterVar = makeLabel("repeatcounter") val zpAddr = zeropage.allocate(counterVar, DataType.UBYTE, body.position, errors)
out(" lda $repeatCountVar | beq $endLabel | sta $counterVar") out("$counterVar = $zpAddr ; auto zp UBYTE")
out(repeatLabel) } else {
translate(body) out("$counterVar .byte 0")
out(" dec $counterVar | bne $repeatLabel") }
// inline countervar:
out("""
beq $endLabel
$counterVar .byte 0""")
out(endLabel)
}
private fun repeatWordCountVar(repeatCountVar: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
// TODO can be even more optimized by iterating over pages
// note: cannot use original counter variable because it should retain its original value
val counterVar = makeLabel("repeatcounter")
out("""
lda $repeatCountVar
sta $counterVar
ora $repeatCountVar+1
beq $endLabel
lda $repeatCountVar+1
sta $counterVar+1""")
out(repeatLabel)
translate(body)
out("""
lda $counterVar
bne +
dec $counterVar+1
+ dec $counterVar
lda $counterVar
ora $counterVar+1
bne $repeatLabel
beq $endLabel
$counterVar .word 0""")
out(endLabel) out(endLabel)
} }
@ -1095,7 +1078,7 @@ $counterVar .word 0""")
out(whileLabel) out(whileLabel)
expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, endLabel) expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, endLabel)
translate(stmt.body) translate(stmt.body)
out(" jmp $whileLabel") jmp(whileLabel)
out(endLabel) out(endLabel)
loopEndLabels.pop() loopEndLabels.pop()
} }
@ -1129,7 +1112,7 @@ $counterVar .word 0""")
if(choice.values==null) { if(choice.values==null) {
// the else choice // the else choice
translate(choice.statements) translate(choice.statements)
out(" jmp $endLabel") jmp(endLabel)
} else { } else {
choiceBlocks.add(choiceLabel to choice.statements) choiceBlocks.add(choiceLabel to choice.statements)
for (cv in choice.values!!) { for (cv in choice.values!!) {
@ -1148,11 +1131,11 @@ $counterVar .word 0""")
} }
} }
} }
out(" jmp $endLabel") jmp(endLabel)
for(choiceBlock in choiceBlocks) { for(choiceBlock in choiceBlocks) {
out(choiceBlock.first) out(choiceBlock.first)
translate(choiceBlock.second) translate(choiceBlock.second)
out(" jmp $endLabel") jmp(endLabel)
} }
out(endLabel) out(endLabel)
} }
@ -1211,7 +1194,7 @@ $counterVar .word 0""")
val endLabel = makeLabel("branch_end") val endLabel = makeLabel("branch_end")
out(" $instruction $elseLabel") out(" $instruction $elseLabel")
translate(stmt.truepart) translate(stmt.truepart)
out(" jmp $endLabel") jmp(endLabel)
out(elseLabel) out(elseLabel)
translate(stmt.elsepart) translate(stmt.elsepart)
out(endLabel) out(endLabel)
@ -1254,7 +1237,9 @@ $counterVar .word 0""")
"%asmbinary" -> { "%asmbinary" -> {
val offset = if(stmt.args.size>1) ", ${stmt.args[1].int}" else "" val offset = if(stmt.args.size>1) ", ${stmt.args[1].int}" else ""
val length = if(stmt.args.size>2) ", ${stmt.args[2].int}" else "" val length = if(stmt.args.size>2) ", ${stmt.args[2].int}" else ""
out(" .binary \"${stmt.args[0].str}\" $offset $length") val includedSourcePath = stmt.definingModule().source.resolveSibling(stmt.args[0].str)
val relPath = Paths.get("").relativize(includedSourcePath)
out(" .binary \"./$relPath\" $offset $length")
} }
"%breakpoint" -> { "%breakpoint" -> {
val label = "_prog8_breakpoint_${breakpointLabels.size+1}" val label = "_prog8_breakpoint_${breakpointLabels.size+1}"
@ -1266,14 +1251,12 @@ $label nop""")
} }
} }
private fun translate(jmp: Jump) { private fun translate(jump: Jump) = jmp(getJumpTarget(jump))
out(" jmp ${getJumpTarget(jmp)}")
}
private fun getJumpTarget(jmp: Jump): String { private fun getJumpTarget(jump: Jump): String {
val ident = jmp.identifier val ident = jump.identifier
val label = jmp.generatedLabel val label = jump.generatedLabel
val addr = jmp.address val addr = jump.address
return when { return when {
ident!=null -> { ident!=null -> {
val target = ident.targetStatement(program) val target = ident.targetStatement(program)
@ -1329,7 +1312,7 @@ $label nop""")
// sign extend signed byte on stack to signed word on stack // sign extend signed byte on stack to signed word on stack
when(valueDt) { when(valueDt) {
DataType.UBYTE -> { DataType.UBYTE -> {
if(compTarget.machine.cpu == CpuType.CPU65c02) if(isTargetCpu(CpuType.CPU65c02))
out(" stz P8ESTACK_HI+1,x") out(" stz P8ESTACK_HI+1,x")
else else
out(" lda #0 | sta P8ESTACK_HI+1,x") out(" lda #0 | sta P8ESTACK_HI+1,x")
@ -1343,7 +1326,7 @@ $label nop""")
// sign extend signed byte in a var to a full word in that variable // sign extend signed byte in a var to a full word in that variable
when(valueDt) { when(valueDt) {
DataType.UBYTE -> { DataType.UBYTE -> {
if(compTarget.machine.cpu == CpuType.CPU65c02) if(isTargetCpu(CpuType.CPU65c02))
out(" stz $asmvar+1") out(" stz $asmvar+1")
else else
out(" lda #0 | sta $asmvar+1") out(" lda #0 | sta $asmvar+1")
@ -1367,6 +1350,13 @@ $label nop""")
return vardecl.makeScopedName(vardecl.name) in allocatedZeropageVariables return vardecl.makeScopedName(vardecl.name) in allocatedZeropageVariables
} }
internal fun jmp(asmLabel: String) {
if(isTargetCpu(CpuType.CPU65c02))
out(" bra $asmLabel") // note: 64tass will convert this automatically to a jmp if the relative distance is too large
else
out(" jmp $asmLabel")
}
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: Expression): Pair<Expression, Expression>? { internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: Expression): Pair<Expression, Expression>? {
if(pointerOffsetExpr is BinaryExpression && pointerOffsetExpr.operator=="+") { if(pointerOffsetExpr is BinaryExpression && pointerOffsetExpr.operator=="+") {
val leftDt = pointerOffsetExpr.left.inferType(program) val leftDt = pointerOffsetExpr.left.inferType(program)
@ -1396,39 +1386,44 @@ $label nop""")
internal fun tryOptimizedPointerAccessWithA(expr: BinaryExpression, write: Boolean): Boolean { internal fun tryOptimizedPointerAccessWithA(expr: BinaryExpression, write: Boolean): Boolean {
// optimize pointer,indexregister if possible // optimize pointer,indexregister if possible
fun evalBytevalueWillClobberA(expr: Expression): Boolean {
val dt = expr.inferType(program)
if(!dt.istype(DataType.UBYTE) && !dt.istype(DataType.BYTE))
return true
return when(expr) {
is IdentifierReference -> false
is NumericLiteralValue -> false
is DirectMemoryRead -> expr.addressExpression !is IdentifierReference && expr.addressExpression !is NumericLiteralValue
is TypecastExpression -> evalBytevalueWillClobberA(expr.expression)
else -> true
}
}
if(expr.operator=="+") { if(expr.operator=="+") {
val ptrAndIndex = pointerViaIndexRegisterPossible(expr) val ptrAndIndex = pointerViaIndexRegisterPossible(expr)
if(ptrAndIndex!=null) { if(ptrAndIndex!=null) {
val pointervar = ptrAndIndex.first as? IdentifierReference val pointervar = ptrAndIndex.first as? IdentifierReference
if(write) { if(write) {
when(ptrAndIndex.second) { if(pointervar!=null && isZpVar(pointervar)) {
is NumericLiteralValue, is IdentifierReference -> { val saveA = evalBytevalueWillClobberA(ptrAndIndex.second)
if(pointervar!=null && isZpVar(pointervar)) { if(saveA)
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) out(" pha")
out(" sta (${asmSymbolName(pointervar)}),y") assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
} else { if(saveA)
// copy the pointer var to zp first out(" pla")
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) out(" sta (${asmSymbolName(pointervar)}),y")
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) } else {
out(" sta (P8ZP_SCRATCH_W2),y") // copy the pointer var to zp first
} val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second)
} if(saveA)
else -> { out(" pha")
// same as above but we need to save the A register assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if(pointervar!=null && isZpVar(pointervar)) { assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" pha") if(saveA)
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) out(" pla")
out(" pla") out(" sta (P8ZP_SCRATCH_W2),y")
out(" sta (${asmSymbolName(pointervar)}),y")
} else {
// copy the pointer var to zp first
assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
out(" pha")
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" pla")
out(" sta (P8ZP_SCRATCH_W2),y")
}
}
} }
} else { } else {
if(pointervar!=null && isZpVar(pointervar)) { if(pointervar!=null && isZpVar(pointervar)) {

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen package prog8.compiler.target.cpu6502.codegen
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations // note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations

View File

@ -1,16 +1,19 @@
package prog8.compiler.target.c64.codegen package prog8.compiler.target.cpu6502.codegen
import prog8.ast.IFunctionCall import prog8.ast.IFunctionCall
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.statements.* import prog8.ast.statements.ArrayIndex
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.statements.Subroutine
import prog8.ast.toHex import prog8.ast.toHex
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.functions.FSignature import prog8.compiler.functions.FSignature
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.assignment.* import prog8.compiler.target.cpu6502.codegen.assignment.*
import prog8.compiler.target.subroutineFloatEvalResultVar2 import prog8.compiler.target.subroutineFloatEvalResultVar2
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val assignAsmGen: AssignmentAsmGen) { internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val assignAsmGen: AssignmentAsmGen) {
@ -48,7 +51,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
"ln", "log2", "sqrt", "rad", "ln", "log2", "sqrt", "rad",
"deg", "round", "floor", "ceil", "deg", "round", "floor", "ceil",
"rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, resultRegister, sscope) "rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, resultRegister, sscope)
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope) "fastrnd8", "rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope) "sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
"rol" -> funcRol(fcall) "rol" -> funcRol(fcall)
"rol2" -> funcRol2(fcall) "rol2" -> funcRol2(fcall)
@ -61,14 +64,83 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
"peek" -> throw AssemblyError("peek() should have been replaced by @()") "peek" -> throw AssemblyError("peek() should have been replaced by @()")
"pokew" -> funcPokeW(fcall) "pokew" -> funcPokeW(fcall)
"poke" -> throw AssemblyError("poke() should have been replaced by @()") "poke" -> throw AssemblyError("poke() should have been replaced by @()")
else -> TODO("missing asmgen for builtin func ${func.name}") "cmp" -> funcCmp(fcall)
else -> throw AssemblyError("missing asmgen for builtin func ${func.name}")
}
}
private fun funcCmp(fcall: IFunctionCall) {
val arg1 = fcall.args[0]
val arg2 = fcall.args[1]
val dt1 = arg1.inferType(program).typeOrElse(DataType.STRUCT)
val dt2 = arg2.inferType(program).typeOrElse(DataType.STRUCT)
if(dt1 in ByteDatatypes) {
if(dt2 in ByteDatatypes) {
when (arg2) {
is IdentifierReference -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp ${asmgen.asmVariableName(arg2)}")
}
is NumericLiteralValue -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp #${arg2.number}")
}
is DirectMemoryRead -> {
if(arg2.addressExpression is NumericLiteralValue) {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp ${arg2.addressExpression.constValue(program)!!.number.toHex()}")
} else {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1")
}
}
else -> {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1")
}
}
} else
throw AssemblyError("args for cmp() should have same dt")
} else {
// dt1 is a word
if(dt2 in WordDatatypes) {
when (arg2) {
is IdentifierReference -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy ${asmgen.asmVariableName(arg2)}+1
bne +
cmp ${asmgen.asmVariableName(arg2)}
+""")
}
is NumericLiteralValue -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy #>${arg2.number}
bne +
cmp #<${arg2.number}
+""")
}
else -> {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, (fcall as Node).definingSubroutine())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy P8ZP_SCRATCH_W1+1
bne +
cmp P8ZP_SCRATCH_W1
+""")
}
}
} else
throw AssemblyError("args for cmp() should have same dt")
} }
} }
private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) { private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
if(discardResult || fcall !is FunctionCall) if(discardResult || fcall !is FunctionCall)
throw AssemblyError("should not discard result of memory allocation at $fcall") throw AssemblyError("should not discard result of memory allocation at $fcall")
val scope = fcall.definingScope()
val nameRef = fcall.args[0] as IdentifierReference val nameRef = fcall.args[0] as IdentifierReference
val name = (nameRef.targetVarDecl(program)!!.value as StringLiteralValue).value val name = (nameRef.targetVarDecl(program)!!.value as StringLiteralValue).value
val size = (fcall.args[1] as NumericLiteralValue).number.toInt() val size = (fcall.args[1] as NumericLiteralValue).number.toInt()
@ -85,12 +157,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null) AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null)
else else
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, null, program, asmgen) AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, null, program, asmgen)
val assign = AsmAssignment(src, target, false, fcall.position) val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position)
asmgen.translateNormalAssignment(assign) asmgen.translateNormalAssignment(assign)
// remove the variable for the name, it's not used as a variable only as a tag for the assembler.
val nameDecl = scope.statements.single { it is VarDecl && it.name==nameRef.nameInSource.single() }
asmgen.removals.add(Pair(nameDecl, scope))
asmgen.slabs[name] = size asmgen.slabs[name] = size
} }
@ -396,8 +464,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) { private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) {
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null) asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
val indexerExpr = if(indexer.indexVar!=null) indexer.indexVar!! else indexer.indexNum!! asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
asmgen.assignExpressionToVariable(indexerExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
} }
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
@ -480,11 +547,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
} }
DataType.ARRAY_UW -> { DataType.ARRAY_UW -> {
asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY") asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY) assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
} }
DataType.ARRAY_W -> { DataType.ARRAY_W -> {
asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY") asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY) assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
} }
DataType.ARRAY_F -> { DataType.ARRAY_F -> {
asmgen.out(" jsr floats.func_${function.name}_f_fac1") asmgen.out(" jsr floats.func_${function.name}_f_fac1")
@ -511,19 +578,19 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
when (dt.typeOrElse(DataType.STRUCT)) { when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_UB, DataType.STR -> { DataType.ARRAY_UB, DataType.STR -> {
asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY") asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY) assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
} }
DataType.ARRAY_B -> { DataType.ARRAY_B -> {
asmgen.out(" jsr prog8_lib.func_sum_b_into_AY") asmgen.out(" jsr prog8_lib.func_sum_b_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY) assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
} }
DataType.ARRAY_UW -> { DataType.ARRAY_UW -> {
asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY") asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY) assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
} }
DataType.ARRAY_W -> { DataType.ARRAY_W -> {
asmgen.out(" jsr prog8_lib.func_sum_w_into_AY") asmgen.out(" jsr prog8_lib.func_sum_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY) assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
} }
DataType.ARRAY_F -> { DataType.ARRAY_F -> {
asmgen.out(" jsr floats.func_sum_f_fac1") asmgen.out(" jsr floats.func_sum_f_fac1")
@ -612,10 +679,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
throw AssemblyError("unknown dt") throw AssemblyError("unknown dt")
val elementDt = elementIDt.typeOrElse(DataType.STRUCT) val elementDt = elementIDt.typeOrElse(DataType.STRUCT)
val firstNum = first.indexer.indexNum val firstNum = first.indexer.indexExpr as? NumericLiteralValue
val firstVar = first.indexer.indexVar val firstVar = first.indexer.indexExpr as? IdentifierReference
val secondNum = second.indexer.indexNum val secondNum = second.indexer.indexExpr as? NumericLiteralValue
val secondVar = second.indexer.indexVar val secondVar = second.indexer.indexExpr as? IdentifierReference
if(firstNum!=null && secondNum!=null) { if(firstNum!=null && secondNum!=null) {
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondNum) swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondNum)
@ -651,12 +718,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val assignFirst = AsmAssignment( val assignFirst = AsmAssignment(
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W2"), AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W2"),
targetFromExpr(first, datatype), targetFromExpr(first, datatype),
false, first.position false, program.memsizer, first.position
) )
val assignSecond = AsmAssignment( val assignSecond = AsmAssignment(
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W1"), AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W1"),
targetFromExpr(second, datatype), targetFromExpr(second, datatype),
false, second.position false, program.memsizer, second.position
) )
asmgen.translateNormalAssignment(assignFirst) asmgen.translateNormalAssignment(assignFirst)
asmgen.translateNormalAssignment(assignSecond) asmgen.translateNormalAssignment(assignSecond)
@ -668,12 +735,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val assignFirst = AsmAssignment( val assignFirst = AsmAssignment(
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT), AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT),
targetFromExpr(first, datatype), targetFromExpr(first, datatype),
false, first.position false, program.memsizer, first.position
) )
val assignSecond = AsmAssignment( val assignSecond = AsmAssignment(
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT), AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT),
targetFromExpr(second, datatype), targetFromExpr(second, datatype),
false, second.position false, program.memsizer, second.position
) )
asmgen.translateNormalAssignment(assignFirst) asmgen.translateNormalAssignment(assignFirst)
asmgen.translateNormalAssignment(assignSecond) asmgen.translateNormalAssignment(assignSecond)
@ -683,8 +750,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
} }
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexValue2: NumericLiteralValue) { private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexValue2: NumericLiteralValue) {
val index1 = indexValue1.number.toInt() * asmgen.compTarget.memorySize(elementDt) val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
val index2 = indexValue2.number.toInt() * asmgen.compTarget.memorySize(elementDt) val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
when(elementDt) { when(elementDt) {
DataType.UBYTE, DataType.BYTE -> { DataType.UBYTE, DataType.BYTE -> {
asmgen.out(""" asmgen.out("""
@ -797,7 +864,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
} }
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexName2: IdentifierReference) { private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexName2: IdentifierReference) {
val index1 = indexValue1.number.toInt() * asmgen.compTarget.memorySize(elementDt) val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
val idxAsmName2 = asmgen.asmVariableName(indexName2) val idxAsmName2 = asmgen.asmVariableName(indexName2)
when(elementDt) { when(elementDt) {
DataType.UBYTE, DataType.BYTE -> { DataType.UBYTE, DataType.BYTE -> {
@ -856,7 +923,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexValue2: NumericLiteralValue) { private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexValue2: NumericLiteralValue) {
val idxAsmName1 = asmgen.asmVariableName(indexName1) val idxAsmName1 = asmgen.asmVariableName(indexName1)
val index2 = indexValue2.number.toInt() * asmgen.compTarget.memorySize(elementDt) val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
when(elementDt) { when(elementDt) {
DataType.UBYTE, DataType.BYTE -> { DataType.UBYTE, DataType.BYTE -> {
asmgen.out(""" asmgen.out("""
@ -926,15 +993,15 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
when (dt) { when (dt) {
in ByteDatatypes -> { in ByteDatatypes -> {
asmgen.out(" jsr prog8_lib.abs_b_into_A") asmgen.out(" jsr prog8_lib.abs_b_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), CpuRegister.A) assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
} }
in WordDatatypes -> { in WordDatatypes -> {
asmgen.out(" jsr prog8_lib.abs_w_into_AY") asmgen.out(" jsr prog8_lib.abs_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), RegisterOrPair.AY) assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
} }
DataType.FLOAT -> { DataType.FLOAT -> {
asmgen.out(" jsr floats.abs_f_fac1") asmgen.out(" jsr floats.abs_f_fac1")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), RegisterOrPair.FAC1) assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen))
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
@ -943,6 +1010,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) { private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
when(func.name) { when(func.name) {
"fastrnd8" -> {
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_fastrnd8_stack")
else {
asmgen.out(" jsr math.fast_randbyte")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
}
}
"rnd" -> { "rnd" -> {
if(resultToStack) if(resultToStack)
asmgen.out(" jsr prog8_lib.func_rnd_stack") asmgen.out(" jsr prog8_lib.func_rnd_stack")
@ -977,7 +1052,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine()!!) asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine()!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) { if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
asmgen.out(""" asmgen.out("""
sta ($varname) sta ($varname)
txa txa
@ -1028,7 +1103,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val varname = asmgen.asmVariableName(addrExpr) val varname = asmgen.asmVariableName(addrExpr)
if(asmgen.isZpVar(addrExpr)) { if(asmgen.isZpVar(addrExpr)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) { if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
asmgen.out(""" asmgen.out("""
ldy #1 ldy #1
lda ($varname),y lda ($varname),y
@ -1292,7 +1367,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
} }
} }
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, conv.dt, null, variableAsmName = varname) val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, conv.dt, null, variableAsmName = varname)
val assign = AsmAssignment(src, tgt, false, value.position) val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
asmgen.translateNormalAssignment(assign) asmgen.translateNormalAssignment(assign)
} }
conv.reg != null -> { conv.reg != null -> {
@ -1308,7 +1383,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
} }
} }
val tgt = AsmAssignTarget.fromRegisters(conv.reg, null, program, asmgen) val tgt = AsmAssignTarget.fromRegisters(conv.reg, null, program, asmgen)
val assign = AsmAssignment(src, tgt, false, value.position) val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
asmgen.translateNormalAssignment(assign) asmgen.translateNormalAssignment(assign)
} }
else -> throw AssemblyError("callconv") else -> throw AssemblyError("callconv")

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen package prog8.compiler.target.cpu6502.codegen
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.ArrayElementTypes import prog8.ast.base.ArrayElementTypes
@ -57,9 +57,9 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
lda $varname lda $varname
$modifiedLabel cmp #0 ; modified $modifiedLabel cmp #0 ; modified
beq $endLabel beq $endLabel
$incdec $varname $incdec $varname""")
jmp $loopLabel asmgen.jmp(loopLabel)
$endLabel""") asmgen.out(endLabel)
} else { } else {
@ -117,16 +117,15 @@ $modifiedLabel2 cmp #0 ; modified
asmgen.out(""" asmgen.out("""
+ inc $varname + inc $varname
bne $loopLabel bne $loopLabel
inc $varname+1 inc $varname+1""")
jmp $loopLabel asmgen.jmp(loopLabel)
""")
} else { } else {
asmgen.out(""" asmgen.out("""
+ lda $varname + lda $varname
bne + bne +
dec $varname+1 dec $varname+1
+ dec $varname + dec $varname""")
jmp $loopLabel""") asmgen.jmp(loopLabel)
} }
asmgen.out(endLabel) asmgen.out(endLabel)
} }
@ -386,23 +385,25 @@ $loopLabel""")
} }
-2 -> { -2 -> {
when (range.last) { when (range.last) {
0 -> asmgen.out(""" 0 -> {
lda $varname asmgen.out("""
beq $endLabel lda $varname
dec $varname beq $endLabel
dec $varname dec $varname
jmp $loopLabel""") dec $varname""")
asmgen.jmp(loopLabel)
}
1 -> asmgen.out(""" 1 -> asmgen.out("""
dec $varname dec $varname
beq $endLabel beq $endLabel
dec $varname dec $varname
bne $loopLabel""") bne $loopLabel""")
else -> asmgen.out(""" else -> asmgen.out("""
dec $varname dec $varname
dec $varname dec $varname
lda $varname lda $varname
cmp #${range.last-2} cmp #${range.last-2}
bne $loopLabel""") bne $loopLabel""")
} }
} }
else -> { else -> {
@ -413,8 +414,8 @@ $loopLabel""")
beq $endLabel beq $endLabel
clc clc
adc #${range.step} adc #${range.step}
sta $varname sta $varname""")
jmp $loopLabel""") asmgen.jmp(loopLabel)
} }
} }
asmgen.out(endLabel) asmgen.out(endLabel)
@ -450,9 +451,9 @@ $loopLabel""")
sta $varname sta $varname
lda $varname+1 lda $varname+1
adc #>${range.step} adc #>${range.step}
sta $varname+1 sta $varname+1""")
jmp $loopLabel asmgen.jmp(loopLabel)
$endLabel""") asmgen.out(endLabel)
} }
} }
} }
@ -502,9 +503,9 @@ $loopLabel""")
asmgen.out(""" asmgen.out("""
lda $varname lda $varname
beq $endLabel beq $endLabel
dec $varname dec $varname""")
jmp $loopLabel asmgen.jmp(loopLabel)
$endLabel""") asmgen.out(endLabel)
} }
1 -> { 1 -> {
asmgen.out(""" asmgen.out("""
@ -545,9 +546,9 @@ $loopLabel""")
beq $endLabel beq $endLabel
+ inc $varname + inc $varname
bne $loopLabel bne $loopLabel
inc $varname+1 inc $varname+1""")
jmp $loopLabel asmgen.jmp(loopLabel)
$endLabel""") asmgen.out(endLabel)
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }
@ -573,9 +574,9 @@ $loopLabel""")
+ lda $varname + lda $varname
bne + bne +
dec $varname+1 dec $varname+1
+ dec $varname + dec $varname""")
jmp $loopLabel asmgen.jmp(loopLabel)
$endLabel""") asmgen.out(endLabel)
asmgen.loopEndLabels.pop() asmgen.loopEndLabels.pop()
} }

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen package prog8.compiler.target.cpu6502.codegen
import prog8.ast.IFunctionCall import prog8.ast.IFunctionCall
import prog8.ast.Node import prog8.ast.Node
@ -8,10 +8,10 @@ import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignSource
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignTarget
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind import prog8.compiler.target.cpu6502.codegen.assignment.TargetStorageKind
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) { internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
@ -91,11 +91,15 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
when { when {
stmt.args.all {isNoClobberRisk(it)} -> { stmt.args.all {isNoClobberRisk(it)} -> {
// There's no risk of clobbering for these simple argument types. Optimize the register loading directly from these values. // There's no risk of clobbering for these simple argument types. Optimize the register loading directly from these values.
// register assignment order: 1) cx16 virtual word registers, 2) actual CPU registers, 3) CPU Carry status flag.
val argsInfo = sub.parameters.withIndex().zip(stmt.args).zip(sub.asmParameterRegisters) val argsInfo = sub.parameters.withIndex().zip(stmt.args).zip(sub.asmParameterRegisters)
val (cx16virtualRegsArgsInfo, otherRegsArgsInfo) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters } val (cx16virtualRegs, args2) = argsInfo.partition { it.second.registerOrPair in Cx16VirtualRegisters }
for(arg in cx16virtualRegsArgsInfo) val (cpuRegs, statusRegs) = args2.partition { it.second.registerOrPair!=null }
for(arg in cx16virtualRegs)
argumentViaRegister(sub, arg.first.first, arg.first.second) argumentViaRegister(sub, arg.first.first, arg.first.second)
for(arg in otherRegsArgsInfo) for(arg in cpuRegs)
argumentViaRegister(sub, arg.first.first, arg.first.second)
for(arg in statusRegs)
argumentViaRegister(sub, arg.first.first, arg.first.second) argumentViaRegister(sub, arg.first.first, arg.first.second)
} }
else -> { else -> {
@ -168,12 +172,12 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
lda P8ESTACK_LO$plusIdxStr,x lda P8ESTACK_LO$plusIdxStr,x
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()} sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
""") """)
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if (asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1") asmgen.out(" stz cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
else else
asmgen.out(" lda #0 | sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1") asmgen.out(" lda #0 | sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
} }
in WordDatatypes -> in WordDatatypes, in IterableDatatypes ->
asmgen.out(""" asmgen.out("""
lda P8ESTACK_LO$plusIdxStr,x lda P8ESTACK_LO$plusIdxStr,x
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()} sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
@ -318,7 +322,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
} else { } else {
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target) AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
} }
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY)) asmgen.translateNormalAssignment(AsmAssignment(src, target, false, program.memsizer, Position.DUMMY))
} }
} }
} }

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen package prog8.compiler.target.cpu6502.codegen
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
@ -66,8 +66,9 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
targetArrayIdx!=null -> { targetArrayIdx!=null -> {
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar) val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar)
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT) val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT)
if(targetArrayIdx.indexer.indexNum!=null) { val constIndex = targetArrayIdx.indexer.constIndex()
val indexValue = targetArrayIdx.indexer.constIndex()!! * asmgen.compTarget.memorySize(elementDt) if(constIndex!=null) {
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
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 -> {

View File

@ -1,12 +1,12 @@
package prog8.compiler.target.c64.codegen.assignment package prog8.compiler.target.cpu6502.codegen.assignment
import prog8.ast.IMemSizer
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.* import prog8.ast.statements.*
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.cpu6502.codegen.AsmGen
import prog8.compiler.target.c64.codegen.AsmGen
internal enum class TargetStorageKind { internal enum class TargetStorageKind {
@ -120,13 +120,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
asmgen.asmVariableName(array.arrayvar) asmgen.asmVariableName(array.arrayvar)
companion object { companion object {
fun fromAstSource(indexer: ArrayIndex, program: Program, asmgen: AsmGen): AsmAssignSource { fun fromAstSource(indexer: ArrayIndex, program: Program, asmgen: AsmGen): AsmAssignSource = fromAstSource(indexer.indexExpr, program, asmgen)
return when {
indexer.indexNum!=null -> fromAstSource(indexer.indexNum!!, program, asmgen)
indexer.indexVar!=null -> fromAstSource(indexer.indexVar!!, program, asmgen)
else -> throw AssemblyError("weird indexer")
}
}
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource { fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
val cv = value.constValue(program) val cv = value.constValue(program)
@ -207,12 +201,13 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
internal class AsmAssignment(val source: AsmAssignSource, internal class AsmAssignment(val source: AsmAssignSource,
val target: AsmAssignTarget, val target: AsmAssignTarget,
val isAugmentable: Boolean, val isAugmentable: Boolean,
memsizer: IMemSizer,
val position: Position) { val position: Position) {
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 != DataType.STRUCT) { "must not be placeholder datatype" } require(source.datatype != DataType.STRUCT) { "must not be placeholder datatype" }
require(ICompilationTarget.instance.memorySize(source.datatype) <= ICompilationTarget.instance.memorySize(target.datatype)) { require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
"source storage size must be less or equal to target datatype storage size" "source storage size must be less or equal to target datatype storage size"
} }
} }

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen.assignment package prog8.compiler.target.cpu6502.codegen.assignment
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
@ -9,12 +9,13 @@ import prog8.compiler.AssemblyError
import prog8.compiler.functions.BuiltinFunctions import prog8.compiler.functions.BuiltinFunctions
import prog8.compiler.functions.builtinFunctionReturnType import prog8.compiler.functions.builtinFunctionReturnType
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.AsmGen import prog8.compiler.target.cpu6502.codegen.AsmGen
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen import prog8.compiler.target.cpu6502.codegen.ExpressionsAsmGen
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen, internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen,
private val exprAsmgen: ExpressionsAsmGen) { private val exprAsmgen: ExpressionsAsmGen
) {
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, exprAsmgen, asmgen) private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, exprAsmgen, asmgen)
@ -22,7 +23,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen) val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(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, program.memsizer, assignment.position)
target.origAssign = assign target.origAssign = assign
if(assign.isAugmentable) if(assign.isAugmentable)
@ -64,9 +65,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val value = assign.source.array!! val value = assign.source.array!!
val elementDt = assign.source.datatype val elementDt = assign.source.datatype
val arrayVarName = asmgen.asmVariableName(value.arrayvar) val arrayVarName = asmgen.asmVariableName(value.arrayvar)
if (value.indexer.indexNum!=null) { val constIndex = value.indexer.constIndex()
if (constIndex!=null) {
// constant array index value // constant array index value
val indexValue = value.indexer.constIndex()!! * asmgen.compTarget.memorySize(elementDt) val indexValue = constIndex * program.memsizer.memorySize(elementDt)
when (elementDt) { when (elementDt) {
in ByteDatatypes -> { in ByteDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue") asmgen.out(" lda $arrayVarName+$indexValue")
@ -114,7 +116,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
SourceStorageKind.MEMORY -> { SourceStorageKind.MEMORY -> {
fun assignViaExprEval(expression: Expression) { fun assignViaExprEval(expression: Expression) {
assignExpressionToVariable(expression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope) assignExpressionToVariable(expression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope)
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if (asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" lda (P8ZP_SCRATCH_W2)") asmgen.out(" lda (P8ZP_SCRATCH_W2)")
else else
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y") asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
@ -319,7 +321,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
fun assignViaExprEval(addressExpression: Expression) { fun assignViaExprEval(addressExpression: Expression) {
asmgen.assignExpressionToVariable(addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) asmgen.assignExpressionToVariable(addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if (asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" lda (P8ZP_SCRATCH_W2)") asmgen.out(" lda (P8ZP_SCRATCH_W2)")
else else
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y") asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
@ -356,7 +358,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
// special case optimizations // special case optimizations
if(target.kind==TargetStorageKind.VARIABLE) { if(target.kind== TargetStorageKind.VARIABLE) {
if(value is IdentifierReference && valueDt != DataType.STRUCT) if(value is IdentifierReference && valueDt != DataType.STRUCT)
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt) return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
@ -441,7 +443,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val lsb = FunctionCall(IdentifierReference(listOf("lsb"), value.position), mutableListOf(value), value.position) val lsb = FunctionCall(IdentifierReference(listOf("lsb"), value.position), mutableListOf(value), value.position)
lsb.linkParents(value.parent) lsb.linkParents(value.parent)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb) val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
val assign = AsmAssignment(src, target, false, value.position) val assign = AsmAssignment(src, target, false, program.memsizer, value.position)
translateNormalAssignment(assign) translateNormalAssignment(assign)
} }
@ -473,7 +475,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName") asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
} }
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | stz $targetAsmVarName+1") asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | stz $targetAsmVarName+1")
else else
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1") asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
@ -496,7 +498,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName") asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
} }
DataType.UWORD -> { DataType.UWORD -> {
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | stz $targetAsmVarName+1") asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | stz $targetAsmVarName+1")
else else
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1") asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
@ -569,11 +571,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} }
DataType.STR -> { DataType.STR -> throw AssemblyError("cannot typecast a string value")
if (targetDt != DataType.UWORD && targetDt == DataType.STR)
throw AssemblyError("cannot typecast a string into another incompatitble type")
TODO("assign typecasted string into target var")
}
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} }
@ -592,7 +590,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName") asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
} }
DataType.UWORD, DataType.WORD -> { DataType.UWORD, DataType.WORD -> {
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1") asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
else else
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1") asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
@ -620,7 +618,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName") asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
} }
DataType.UWORD -> { DataType.UWORD -> {
if(asmgen.compTarget.machine.cpu==CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1") asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
else else
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1") asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
@ -708,11 +706,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} }
DataType.STR -> { DataType.STR -> throw AssemblyError("cannot typecast a string value")
if (targetDt != DataType.UWORD && targetDt == DataType.STR)
throw AssemblyError("cannot typecast a string into another incompatitble type")
TODO("assign typecasted string into target var")
}
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} }
@ -762,7 +756,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
if(target.constArrayIndexValue!=null) { if(target.constArrayIndexValue!=null) {
val scaledIdx = target.constArrayIndexValue!! * asmgen.compTarget.memorySize(target.datatype) val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype)
when(target.datatype) { when(target.datatype) {
in ByteDatatypes -> { in ByteDatatypes -> {
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx") asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx")
@ -968,7 +962,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
target.array!! target.array!!
if(target.constArrayIndexValue!=null) { if(target.constArrayIndexValue!=null) {
val scaledIdx = target.constArrayIndexValue!! * asmgen.compTarget.memorySize(target.datatype) val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype)
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")
@ -1073,11 +1067,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
ldy #>${target.asmVarname} ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1""") sty P8ZP_SCRATCH_W1+1""")
if(target.array!!.indexer.indexNum!=null) { val constIndex = target.array!!.indexer.constIndex()
val index = target.array.indexer.constIndex()!! if(constIndex!=null) {
asmgen.out(" lda #$index") asmgen.out(" lda #$constIndex")
} else { } else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!) val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
asmgen.out(" lda $asmvarname") asmgen.out(" lda $asmvarname")
} }
asmgen.out(" jsr floats.set_array_float_from_fac1") asmgen.out(" jsr floats.set_array_float_from_fac1")
@ -1109,11 +1103,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
ldy #>${target.asmVarname} ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1""") sty P8ZP_SCRATCH_W2+1""")
if(target.array!!.indexer.indexNum!=null) { val constIndex = target.array!!.indexer.constIndex()
val index = target.array.indexer.constIndex()!! if(constIndex!=null) {
asmgen.out(" lda #$index") asmgen.out(" lda #$constIndex")
} else { } else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!) val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
asmgen.out(" lda $asmvarname") asmgen.out(" lda $asmvarname")
} }
asmgen.out(" jsr floats.set_array_float") asmgen.out(" jsr floats.set_array_float")
@ -1156,11 +1150,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
ldy #>${target.asmVarname} ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1""") sty P8ZP_SCRATCH_W2+1""")
if(target.array!!.indexer.indexNum!=null) { val constIndex = target.array!!.indexer.constIndex()
val index = target.array.indexer.constIndex()!! if(constIndex!=null) {
asmgen.out(" lda #$index") asmgen.out(" lda #$constIndex")
} else { } else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!) val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
asmgen.out(" lda $asmvarname") asmgen.out(" lda $asmvarname")
} }
asmgen.out(" jsr floats.set_array_float") asmgen.out(" jsr floats.set_array_float")
@ -1191,7 +1185,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
if (target.constArrayIndexValue!=null) { if (target.constArrayIndexValue!=null) {
val scaledIdx = target.constArrayIndexValue!! * asmgen.compTarget.memorySize(target.datatype) val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype)
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx") asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
} }
else { else {
@ -1295,7 +1289,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
when(wordtarget.kind) { when(wordtarget.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}") asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}")
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz ${wordtarget.asmVarname}+1") asmgen.out(" stz ${wordtarget.asmVarname}+1")
else else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1") asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
@ -1304,7 +1298,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
if (wordtarget.constArrayIndexValue!=null) { if (wordtarget.constArrayIndexValue!=null) {
val scaledIdx = wordtarget.constArrayIndexValue!! * 2 val scaledIdx = wordtarget.constArrayIndexValue!! * 2
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx") asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx")
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1") asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1")
else else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1") asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
@ -1329,7 +1323,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
TargetStorageKind.STACK -> { TargetStorageKind.STACK -> {
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x") asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x")
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ESTACK_HI,x | dex") asmgen.out(" stz P8ESTACK_HI,x | dex")
else else
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex") asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
@ -1339,8 +1333,16 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) { internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
if(target.register !in Cx16VirtualRegisters) // we make an exception in the type check for assigning something to a cx16 virtual register
require(target.datatype in ByteDatatypes) if(target.register !in Cx16VirtualRegisters) {
if(target.kind==TargetStorageKind.VARIABLE) {
val parts = target.asmVarname.split('.')
if (parts.size != 2 || parts[0] != "cx16")
require(target.datatype in ByteDatatypes)
} else {
require(target.datatype in ByteDatatypes)
}
}
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
@ -1368,7 +1370,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
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(target.array!!.indexer.indexVar!!)} | sta ${target.asmVarname},y") val indexVar = target.array!!.indexer.indexExpr as IdentifierReference
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y")
} }
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
@ -1571,7 +1574,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
private fun assignConstantWord(target: AsmAssignTarget, word: Int) { private fun assignConstantWord(target: AsmAssignTarget, word: Int) {
if(word==0 && asmgen.compTarget.machine.cpu == CpuType.CPU65c02) { if(word==0 && asmgen.isTargetCpu(CpuType.CPU65c02)) {
// optimize setting zero value for this processor // optimize setting zero value for this processor
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
@ -1666,7 +1669,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
private fun assignConstantByte(target: AsmAssignTarget, byte: Short) { private fun assignConstantByte(target: AsmAssignTarget, byte: Short) {
if(byte==0.toShort() && asmgen.compTarget.machine.cpu == CpuType.CPU65c02) { if(byte==0.toShort() && asmgen.isTargetCpu(CpuType.CPU65c02)) {
// optimize setting zero value for this cpu // optimize setting zero value for this cpu
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
@ -1736,7 +1739,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float") RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
in Cx16VirtualRegisters -> { in Cx16VirtualRegisters -> {
asmgen.out(" lda #${byte.toHex()} | sta cx16.${target.register.toString().toLowerCase()}") asmgen.out(" lda #${byte.toHex()} | sta cx16.${target.register.toString().toLowerCase()}")
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz cx16.${target.register.toString().toLowerCase()}+1\n") asmgen.out(" stz cx16.${target.register.toString().toLowerCase()}+1\n")
else else
asmgen.out(" lda #0 | sta cx16.${target.register.toString().toLowerCase()}+1\n") asmgen.out(" lda #0 | sta cx16.${target.register.toString().toLowerCase()}+1\n")
@ -1757,7 +1760,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
// optimized case for float zero // optimized case for float zero
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(""" asmgen.out("""
stz ${target.asmVarname} stz ${target.asmVarname}
stz ${target.asmVarname}+1 stz ${target.asmVarname}+1
@ -1776,9 +1779,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
""") """)
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
if (target.array!!.indexer.indexNum!=null) { val constIndex = target.array!!.indexer.constIndex()
val indexValue = target.array.indexer.constIndex()!! * asmgen.compTarget.memorySize(DataType.FLOAT) if (constIndex!=null) {
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT)
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(""" asmgen.out("""
stz ${target.asmVarname}+$indexValue stz ${target.asmVarname}+$indexValue
stz ${target.asmVarname}+$indexValue+1 stz ${target.asmVarname}+$indexValue+1
@ -1796,7 +1800,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
sta ${target.asmVarname}+$indexValue+4 sta ${target.asmVarname}+$indexValue+4
""") """)
} else { } else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!) val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
asmgen.out(""" asmgen.out("""
lda #<${target.asmVarname} lda #<${target.asmVarname}
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
@ -1841,8 +1845,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
val arrayVarName = target.asmVarname val arrayVarName = target.asmVarname
if (target.array!!.indexer.indexNum!=null) { val constIndex = target.array!!.indexer.constIndex()
val indexValue = target.array.indexer.constIndex()!! * asmgen.compTarget.memorySize(DataType.FLOAT) if (constIndex!=null) {
val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT)
asmgen.out(""" asmgen.out("""
lda $constFloat lda $constFloat
sta $arrayVarName+$indexValue sta $arrayVarName+$indexValue
@ -1856,7 +1861,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
sta $arrayVarName+$indexValue+4 sta $arrayVarName+$indexValue+4
""") """)
} else { } else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!) val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
asmgen.out(""" asmgen.out("""
lda #<${constFloat} lda #<${constFloat}
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
@ -1975,7 +1980,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
when(wordtarget.kind) { when(wordtarget.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
asmgen.out(" lda ${address.toHex()} | sta ${wordtarget.asmVarname}") asmgen.out(" lda ${address.toHex()} | sta ${wordtarget.asmVarname}")
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz ${wordtarget.asmVarname}+1") asmgen.out(" stz ${wordtarget.asmVarname}+1")
else else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1") asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
@ -1991,7 +1996,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
} }
TargetStorageKind.STACK -> { TargetStorageKind.STACK -> {
asmgen.out(" lda ${address.toHex()} | sta P8ESTACK_LO,x") asmgen.out(" lda ${address.toHex()} | sta P8ESTACK_LO,x")
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ESTACK_HI,x | dex") asmgen.out(" stz P8ESTACK_HI,x | dex")
else else
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex") asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
@ -2003,7 +2008,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
asmgen.loadByteFromPointerIntoA(identifier) asmgen.loadByteFromPointerIntoA(identifier)
asmgen.out(" sta ${wordtarget.asmVarname}") asmgen.out(" sta ${wordtarget.asmVarname}")
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz ${wordtarget.asmVarname}+1") asmgen.out(" stz ${wordtarget.asmVarname}+1")
else else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1") asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
@ -2023,7 +2028,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
TargetStorageKind.STACK -> { TargetStorageKind.STACK -> {
asmgen.loadByteFromPointerIntoA(identifier) asmgen.loadByteFromPointerIntoA(identifier)
asmgen.out(" sta P8ESTACK_LO,x") asmgen.out(" sta P8ESTACK_LO,x")
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ESTACK_HI,x | dex") asmgen.out(" stz P8ESTACK_HI,x | dex")
else else
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex") asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
@ -2041,7 +2046,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
when(addressExpr) { when(addressExpr) {
is NumericLiteralValue, is IdentifierReference -> { is NumericLiteralValue, is IdentifierReference -> {
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if (asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" sta (P8ZP_SCRATCH_W2)") asmgen.out(" sta (P8ZP_SCRATCH_W2)")
else else
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y") asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
@ -2051,7 +2056,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" pha") asmgen.out(" pha")
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.out(" pla") asmgen.out(" pla")
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if (asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" sta (P8ZP_SCRATCH_W2)") asmgen.out(" sta (P8ZP_SCRATCH_W2)")
else else
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y") asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
@ -2063,7 +2068,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val sourceName = asmgen.asmVariableName(pointervar) val sourceName = asmgen.asmVariableName(pointervar)
val vardecl = pointervar.targetVarDecl(program)!! val vardecl = pointervar.targetVarDecl(program)!!
val scopedName = vardecl.makeScopedName(vardecl.name) val scopedName = vardecl.makeScopedName(vardecl.name)
if (asmgen.compTarget.machine.cpu == CpuType.CPU65c02) { if (asmgen.isTargetCpu(CpuType.CPU65c02)) {
if (asmgen.isZpVar(scopedName)) { if (asmgen.isZpVar(scopedName)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
asmgen.out(" sta ($sourceName)") asmgen.out(" sta ($sourceName)")
@ -2109,21 +2114,21 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair) { internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair) {
val src = AsmAssignSource.fromAstSource(expr, program, asmgen) val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen) val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen)
val assign = AsmAssignment(src, tgt, false, expr.position) val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
translateNormalAssignment(assign) translateNormalAssignment(assign)
} }
internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) { internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) {
val src = AsmAssignSource.fromAstSource(expr, program, asmgen) val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, scope, variableAsmName = asmVarName) val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, scope, variableAsmName = asmVarName)
val assign = AsmAssignment(src, tgt, false, expr.position) val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
translateNormalAssignment(assign) translateNormalAssignment(assign)
} }
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair) { internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair) {
val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen) val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen)
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName) val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName)
val assign = AsmAssignment(src, tgt, false, Position.DUMMY) val assign = AsmAssignment(src, tgt, false, program.memsizer, Position.DUMMY)
translateNormalAssignment(assign) translateNormalAssignment(assign)
} }
} }

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen.assignment package prog8.compiler.target.cpu6502.codegen.assignment
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.* import prog8.ast.base.*
@ -7,14 +7,14 @@ import prog8.ast.statements.Subroutine
import prog8.ast.toHex import prog8.ast.toHex
import prog8.compiler.AssemblyError import prog8.compiler.AssemblyError
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
import prog8.compiler.target.Cx16Target import prog8.compiler.target.cpu6502.codegen.AsmGen
import prog8.compiler.target.c64.codegen.AsmGen import prog8.compiler.target.cpu6502.codegen.ExpressionsAsmGen
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
internal class AugmentableAssignmentAsmGen(private val program: Program, internal class AugmentableAssignmentAsmGen(private val program: Program,
private val assignmentAsmGen: AssignmentAsmGen, private val assignmentAsmGen: AssignmentAsmGen,
private val exprAsmGen: ExpressionsAsmGen, private val exprAsmGen: ExpressionsAsmGen,
private val asmgen: AsmGen) { private val asmgen: AsmGen
) {
fun translate(assign: AsmAssignment) { fun translate(assign: AsmAssignment) {
require(assign.isAugmentable) require(assign.isAugmentable)
require(assign.source.kind== SourceStorageKind.EXPRESSION) require(assign.source.kind== SourceStorageKind.EXPRESSION)
@ -199,9 +199,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
with(target.array!!.indexer) { with(target.array!!.indexer) {
val indexNum = indexExpr as? NumericLiteralValue
val indexVar = indexExpr as? IdentifierReference
when { when {
indexNum!=null -> { indexNum!=null -> {
val targetVarName = "${target.asmVarname} + ${indexNum!!.number.toInt()*asmgen.compTarget.memorySize(target.datatype)}" val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
when(target.datatype) { when(target.datatype) {
in ByteDatatypes -> { in ByteDatatypes -> {
when { when {
@ -245,19 +247,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when(target.datatype) { when(target.datatype) {
in ByteDatatypes -> { in ByteDatatypes -> {
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.A, null, program, asmgen) val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.A, null, program, asmgen)
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position) val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
assignmentAsmGen.translateNormalAssignment(assign) assignmentAsmGen.translateNormalAssignment(assign)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A) assignmentAsmGen.assignRegisterByte(target, CpuRegister.A)
} }
in WordDatatypes -> { in WordDatatypes -> {
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen) val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen)
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position) val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
assignmentAsmGen.translateNormalAssignment(assign) assignmentAsmGen.translateNormalAssignment(assign)
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY) assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
} }
DataType.FLOAT -> { DataType.FLOAT -> {
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.FAC1, null, program, asmgen) val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.FAC1, null, program, asmgen)
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position) val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
assignmentAsmGen.translateNormalAssignment(assign) assignmentAsmGen.translateNormalAssignment(assign)
assignmentAsmGen.assignFAC1float(target) assignmentAsmGen.assignFAC1float(target)
} }
@ -268,8 +270,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
} }
TargetStorageKind.REGISTER -> TODO("reg in-place modification") TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg in-place modification")
TargetStorageKind.STACK -> TODO("stack in-place modification") TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack in-place modification")
} }
} }
@ -320,7 +322,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1") "&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1") "|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1") "^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
if(ptrOnZp) if(ptrOnZp)
@ -361,7 +362,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&", "and" -> asmgen.out(" and $otherName") "&", "and" -> asmgen.out(" and $otherName")
"|", "or" -> asmgen.out(" ora $otherName") "|", "or" -> asmgen.out(" ora $otherName")
"^", "xor" -> asmgen.out(" eor $otherName") "^", "xor" -> asmgen.out(" eor $otherName")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
if(ptrOnZp) if(ptrOnZp)
@ -464,7 +464,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y") asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
} }
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -540,7 +539,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name") asmgen.out(" eor $name | sta $name")
} }
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -598,7 +596,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name") "&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name")
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name") "|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name")
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name") "^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -631,7 +628,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
"<<" -> { "<<" -> {
if(value>=8) { if(value>=8) {
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name") asmgen.out(" stz $name")
else else
asmgen.out(" lda #0 | sta $name") asmgen.out(" lda #0 | sta $name")
@ -642,7 +639,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
if(value>0) { if(value>0) {
if (dt == DataType.UBYTE) { if (dt == DataType.UBYTE) {
if(value>=8) { if(value>=8) {
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name") asmgen.out(" stz $name")
else else
asmgen.out(" lda #0 | sta $name") asmgen.out(" lda #0 | sta $name")
@ -670,7 +667,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name") "&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name") "|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name") "^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -857,14 +853,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"<<" -> { "<<" -> {
when { when {
value>=16 -> { value>=16 -> {
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name | stz $name+1") asmgen.out(" stz $name | stz $name+1")
else else
asmgen.out(" lda #0 | sta $name | sta $name+1") asmgen.out(" lda #0 | sta $name | sta $name+1")
} }
value==8 -> { value==8 -> {
asmgen.out(" lda $name | sta $name+1") asmgen.out(" lda $name | sta $name+1")
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name") asmgen.out(" stz $name")
else else
asmgen.out(" lda #0 | sta $name") asmgen.out(" lda #0 | sta $name")
@ -884,14 +880,14 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
if(dt==DataType.UWORD) { if(dt==DataType.UWORD) {
when { when {
value>=16 -> { value>=16 -> {
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name | stz $name+1") asmgen.out(" stz $name | stz $name+1")
else else
asmgen.out(" lda #0 | sta $name | sta $name+1") asmgen.out(" lda #0 | sta $name | sta $name+1")
} }
value==8 -> { value==8 -> {
asmgen.out(" lda $name+1 | sta $name") asmgen.out(" lda $name+1 | sta $name")
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name+1") asmgen.out(" stz $name+1")
else else
asmgen.out(" lda #0 | sta $name+1") asmgen.out(" lda #0 | sta $name+1")
@ -940,13 +936,13 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&", "and" -> { "&", "and" -> {
when { when {
value == 0 -> { value == 0 -> {
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name | stz $name+1") asmgen.out(" stz $name | stz $name+1")
else else
asmgen.out(" lda #0 | sta $name | sta $name+1") asmgen.out(" lda #0 | sta $name | sta $name+1")
} }
value and 255 == 0 -> { value and 255 == 0 -> {
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name") asmgen.out(" stz $name")
else else
asmgen.out(" lda #0 | sta $name") asmgen.out(" lda #0 | sta $name")
@ -954,7 +950,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
value < 0x0100 -> { value < 0x0100 -> {
asmgen.out(" lda $name | and #$value | sta $name") asmgen.out(" lda $name | and #$value | sta $name")
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name+1") asmgen.out(" stz $name+1")
else else
asmgen.out(" lda #0 | sta $name+1") asmgen.out(" lda #0 | sta $name+1")
@ -978,7 +974,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1") else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1")
} }
} }
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -1041,7 +1036,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
"*" -> { "*" -> {
asmgen.out(" lda $otherName | sta P8ZP_SCRATCH_W1") asmgen.out(" lda $otherName | sta P8ZP_SCRATCH_W1")
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ZP_SCRATCH_W1+1") asmgen.out(" stz P8ZP_SCRATCH_W1+1")
else else
asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1") asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1")
@ -1054,8 +1049,49 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
lda math.multiply_words.result+1 lda math.multiply_words.result+1
sta $name+1""") sta $name+1""")
} }
"/" -> TODO("div (u)wordvar/bytevar") "/" -> {
"%" -> TODO("(u)word remainder bytevar") if(dt==DataType.UWORD) {
asmgen.out("""
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda $otherName
ldy #0
jsr math.divmod_uw_asm
sta $name
sty $name+1
""")
} else {
asmgen.out("""
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda $otherName
ldy #0
jsr math.divmod_w_asm
sta $name
sty $name+1
""")
}
}
"%" -> {
if(valueDt!=DataType.UBYTE || dt!=DataType.UWORD)
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 $otherName
ldy #0
jsr math.divmod_uw_asm
lda P8ZP_SCRATCH_W2
sta $name
lda P8ZP_SCRATCH_W2+1
sta $name+1
""") }
"<<" -> { "<<" -> {
asmgen.out(""" asmgen.out("""
ldy $otherName ldy $otherName
@ -1092,7 +1128,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&", "and" -> { "&", "and" -> {
asmgen.out(" lda $otherName | and $name | sta $name") asmgen.out(" lda $otherName | and $name | sta $name")
if(dt in WordDatatypes) { if(dt in WordDatatypes) {
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name+1") asmgen.out(" stz $name+1")
else else
asmgen.out(" lda #0 | sta $name+1") asmgen.out(" lda #0 | sta $name+1")
@ -1100,7 +1136,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
"|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name") "|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name")
"^", "xor" -> asmgen.out(" lda $otherName | eor $name | sta $name") "^", "xor" -> asmgen.out(" lda $otherName | eor $name | sta $name")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -1174,7 +1209,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1") "&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1") "|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1") "^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -1351,7 +1385,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name") asmgen.out(" and $name | sta $name")
if(dt in WordDatatypes) { if(dt in WordDatatypes) {
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name+1") asmgen.out(" stz $name+1")
else else
asmgen.out(" lda #0 | sta $name+1") asmgen.out(" lda #0 | sta $name+1")
@ -1365,7 +1399,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.assignExpressionToRegister(value, RegisterOrPair.A) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name") asmgen.out(" eor $name | sta $name")
} }
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -1406,7 +1439,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1") asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
} }
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
@ -1454,7 +1486,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.FDIV jsr floats.FDIV
""") """)
} }
in comparisonOperators -> TODO("in-place float modification for $operator")
else -> throw AssemblyError("invalid operator for in-place float modification $operator") else -> throw AssemblyError("invalid operator for in-place float modification $operator")
} }
asmgen.out(""" asmgen.out("""
@ -1474,7 +1505,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.saveRegisterLocal(CpuRegister.X, scope) asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) { when (operator) {
"**" -> { "**" -> {
if(asmgen.compTarget is Cx16Target) { if(asmgen.haveFPWR()) {
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$otherName
ldy #>$otherName
jsr floats.FPWR
""")
} else
// cx16 doesn't have FPWR() only FPWRT() // cx16 doesn't have FPWR() only FPWRT()
asmgen.out(""" asmgen.out("""
lda #<$name lda #<$name
@ -1485,15 +1525,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.MOVFM jsr floats.MOVFM
jsr floats.FPWRT jsr floats.FPWRT
""") """)
} else
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$otherName
ldy #>$otherName
jsr floats.FPWR
""")
} }
"+" -> { "+" -> {
asmgen.out(""" asmgen.out("""
@ -1535,7 +1566,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.FDIV jsr floats.FDIV
""") """)
} }
in comparisonOperators -> TODO("in-place float modification for $operator")
else -> throw AssemblyError("invalid operator for in-place float modification $operator") else -> throw AssemblyError("invalid operator for in-place float modification $operator")
} }
// store Fac1 back into memory // store Fac1 back into memory
@ -1552,7 +1582,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.saveRegisterLocal(CpuRegister.X, scope) asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) { when (operator) {
"**" -> { "**" -> {
if(asmgen.compTarget is Cx16Target) { if(asmgen.haveFPWR()) {
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$constValueName
ldy #>$constValueName
jsr floats.FPWR
""")
} else
// cx16 doesn't have FPWR() only FPWRT() // cx16 doesn't have FPWR() only FPWRT()
asmgen.out(""" asmgen.out("""
lda #<$name lda #<$name
@ -1563,15 +1602,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.MOVFM jsr floats.MOVFM
jsr floats.FPWRT jsr floats.FPWRT
""") """)
} else
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$constValueName
ldy #>$constValueName
jsr floats.FPWR
""")
} }
"+" -> { "+" -> {
if (value == 0.0) if (value == 0.0)
@ -1620,7 +1650,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.FDIV jsr floats.FDIV
""") """)
} }
in comparisonOperators -> TODO("in-place float modification for $operator")
else -> throw AssemblyError("invalid operator for in-place float modification $operator") else -> throw AssemblyError("invalid operator for in-place float modification $operator")
} }
// store Fac1 back into memory // store Fac1 back into memory
@ -1644,7 +1673,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
DataType.UBYTE, DataType.BYTE -> { DataType.UBYTE, DataType.BYTE -> {
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz ${target.asmVarname}+1") asmgen.out(" stz ${target.asmVarname}+1")
else else
asmgen.out(" lda #0 | sta ${target.asmVarname}+1") asmgen.out(" lda #0 | sta ${target.asmVarname}+1")
@ -1654,7 +1683,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" lda #0 | sta ${target.asmVarname},y") asmgen.out(" lda #0 | sta ${target.asmVarname},y")
} }
TargetStorageKind.STACK -> { TargetStorageKind.STACK -> {
if(asmgen.compTarget.machine.cpu == CpuType.CPU65c02) if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ESTACK_HI+1,x") asmgen.out(" stz P8ESTACK_HI+1,x")
else else
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x") asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
@ -1730,9 +1759,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
} }
TargetStorageKind.ARRAY -> TODO("in-place not of ubyte array") TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place not of ubyte array")
TargetStorageKind.REGISTER -> TODO("reg not") TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg not")
TargetStorageKind.STACK -> TODO("stack not") TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack not")
} }
} }
DataType.UWORD -> { DataType.UWORD -> {
@ -1749,9 +1778,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta ${target.asmVarname}+1""") sta ${target.asmVarname}+1""")
} }
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory not") TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory not")
TargetStorageKind.ARRAY -> TODO("in-place not of uword array") TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place not of uword array")
TargetStorageKind.REGISTER -> TODO("reg not") TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg not")
TargetStorageKind.STACK -> TODO("stack not") TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack not")
} }
} }
else -> throw AssemblyError("boolean-not of invalid type") else -> throw AssemblyError("boolean-not of invalid type")
@ -1796,9 +1825,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
} }
} }
} }
TargetStorageKind.ARRAY -> TODO("in-place invert ubyte array") TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place invert ubyte array")
TargetStorageKind.REGISTER -> TODO("reg invert") TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg invert")
TargetStorageKind.STACK -> TODO("stack invert") TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack invert")
} }
} }
DataType.UWORD -> { DataType.UWORD -> {
@ -1813,9 +1842,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta ${target.asmVarname}+1""") sta ${target.asmVarname}+1""")
} }
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory invert") TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory invert")
TargetStorageKind.ARRAY -> TODO("in-place invert uword array") TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place invert uword array")
TargetStorageKind.REGISTER -> TODO("reg invert") TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg invert")
TargetStorageKind.STACK -> TODO("stack invert") TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack invert")
} }
} }
else -> throw AssemblyError("invert of invalid type") else -> throw AssemblyError("invert of invalid type")
@ -1834,9 +1863,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta ${target.asmVarname}""") sta ${target.asmVarname}""")
} }
TargetStorageKind.MEMORY -> throw AssemblyError("can't in-place negate memory ubyte") TargetStorageKind.MEMORY -> throw AssemblyError("can't in-place negate memory ubyte")
TargetStorageKind.ARRAY -> TODO("in-place negate byte array") TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate byte array")
TargetStorageKind.REGISTER -> TODO("reg negate") TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg negate")
TargetStorageKind.STACK -> TODO("stack negate") TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack negate")
} }
} }
DataType.WORD -> { DataType.WORD -> {
@ -1851,10 +1880,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sbc ${target.asmVarname}+1 sbc ${target.asmVarname}+1
sta ${target.asmVarname}+1""") sta ${target.asmVarname}+1""")
} }
TargetStorageKind.ARRAY -> TODO("in-place negate word array") TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate word array")
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for word memory negate") TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for word memory negate")
TargetStorageKind.REGISTER -> TODO("reg negate") TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg negate")
TargetStorageKind.STACK -> TODO("stack negate") TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack negate")
} }
} }
DataType.FLOAT -> { DataType.FLOAT -> {
@ -1867,8 +1896,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta ${target.asmVarname}+1 sta ${target.asmVarname}+1
""") """)
} }
TargetStorageKind.ARRAY -> TODO("in-place negate float array") TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate float array")
TargetStorageKind.STACK -> TODO("stack float negate") TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack float negate")
else -> throw AssemblyError("weird target kind for float") else -> throw AssemblyError("weird target kind for float")
} }
} }

View File

@ -1,12 +1,9 @@
package prog8.compiler.target.cx16 package prog8.compiler.target.cx16
import prog8.ast.IStringEncoding
import prog8.ast.Program
import prog8.compiler.* import prog8.compiler.*
import prog8.compiler.target.CpuType import prog8.compiler.target.CpuType
import prog8.compiler.target.IMachineDefinition import prog8.compiler.target.IMachineDefinition
import prog8.compiler.target.c64.C64MachineDefinition import prog8.compiler.target.c64.C64MachineDefinition
import prog8.parser.ModuleImporter
import java.io.IOException import java.io.IOException
internal object CX16MachineDefinition: IMachineDefinition { internal object CX16MachineDefinition: IMachineDefinition {
@ -28,16 +25,11 @@ internal object CX16MachineDefinition: IMachineDefinition {
override lateinit var zeropage: Zeropage override lateinit var zeropage: Zeropage
override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num) override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num)
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
override fun importLibs( return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
compilerOptions: CompilationOptions, listOf("syslib")
importer: ModuleImporter, else
program: Program, emptyList()
encoder: IStringEncoding,
compilationTargetName: String)
{
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
importer.importLibraryModule(program, "syslib", encoder, compilationTargetName)
} }
override fun launchEmulator(programName: String) { override fun launchEmulator(programName: String) {

View File

@ -10,7 +10,7 @@ import prog8.ast.walk.IAstModification
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
internal class BinExprSplitter(private val program: Program) : AstWalker() { internal class BinExprSplitter(private val program: Program, private val compTarget: ICompilationTarget) : 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> {
@ -80,7 +80,7 @@ X = BinExpr X = LeftExpr
private fun isSimpleTarget(target: AssignTarget, program: Program) = private fun isSimpleTarget(target: AssignTarget, program: Program) =
if (target.identifier!=null || target.memoryAddress!=null) if (target.identifier!=null || target.memoryAddress!=null)
ICompilationTarget.instance.isInRegularRAM(target, program) compTarget.isInRegularRAM(target, program)
else else
false false

View File

@ -5,25 +5,25 @@ import prog8.ast.Module
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.DataType import prog8.ast.base.DataType
import prog8.compiler.ErrorReporter
import prog8.ast.base.ParentSentinel import prog8.ast.base.ParentSentinel
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.FunctionCall import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.IdentifierReference
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.compiler.loadAsmIncludeFile import prog8.compiler.IErrorReporter
import java.nio.file.Path
private val alwaysKeepSubroutines = setOf( private val alwaysKeepSubroutines = setOf(
Pair("main", "start"), Pair("main", "start")
Pair("irq", "irq")
) )
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE) private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr|bra)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE) private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
class CallGraph(private val program: Program) : IAstVisitor { class CallGraph(private val program: Program, private val asmFileLoader: (filename: String, source: Path)->String) : 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() }
@ -80,7 +80,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
imports[thisModule] = imports.getValue(thisModule).plus(importedModule) imports[thisModule] = imports.getValue(thisModule).plus(importedModule)
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 = asmFileLoader(directive.args[0].str!!, thisModule.source)
val scope = directive.definingSubroutine() val scope = directive.definingSubroutine()
if(scope!=null) { if(scope!=null) {
scanAssemblyCode(asm, directive, scope) scanAssemblyCode(asm, directive, scope)
@ -150,6 +150,17 @@ class CallGraph(private val program: Program) : IAstVisitor {
super.visit(functionCallStatement) super.visit(functionCallStatement)
} }
override fun visit(addressOf: AddressOf) {
val otherSub = addressOf.identifier.targetSubroutine(program)
if(otherSub!=null) {
addressOf.definingSubroutine()?.let { thisSub ->
calls[thisSub] = calls.getValue(thisSub).plus(otherSub)
calledBy[otherSub] = calledBy.getValue(otherSub).plus(thisSub)
}
}
super.visit(addressOf)
}
override fun visit(jump: Jump) { override fun visit(jump: Jump) {
val otherSub = jump.identifier?.targetSubroutine(program) val otherSub = jump.identifier?.targetSubroutine(program)
if (otherSub != null) { if (otherSub != null) {
@ -167,7 +178,7 @@ 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, bra)
val scope = inlineAssembly.definingSubroutine() val scope = inlineAssembly.definingSubroutine()
scanAssemblyCode(inlineAssembly.assembly, inlineAssembly, scope) scanAssemblyCode(inlineAssembly.assembly, inlineAssembly, scope)
super.visit(inlineAssembly) super.visit(inlineAssembly)
@ -213,7 +224,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
} }
} }
fun checkRecursiveCalls(errors: ErrorReporter) { fun checkRecursiveCalls(errors: IErrorReporter) {
val cycles = recursionCycles() val cycles = recursionCycles()
if(cycles.any()) { if(cycles.any()) {
errors.warn("Program contains recursive subroutine calls. These only works in very specific limited scenarios!", Position.DUMMY) errors.warn("Program contains recursive subroutine calls. These only works in very specific limited scenarios!", Position.DUMMY)

View File

@ -9,28 +9,32 @@ import kotlin.math.pow
class ConstExprEvaluator { class ConstExprEvaluator {
fun evaluate(left: NumericLiteralValue, operator: String, right: NumericLiteralValue): Expression { fun evaluate(left: NumericLiteralValue, operator: String, right: NumericLiteralValue): Expression {
return when(operator) { try {
"+" -> plus(left, right) return when(operator) {
"-" -> minus(left, right) "+" -> plus(left, right)
"*" -> multiply(left, right) "-" -> minus(left, right)
"/" -> divide(left, right) "*" -> multiply(left, right)
"%" -> remainder(left, right) "/" -> divide(left, right)
"**" -> power(left, right) "%" -> remainder(left, right)
"&" -> bitwiseand(left, right) "**" -> power(left, right)
"|" -> bitwiseor(left, right) "&" -> bitwiseand(left, right)
"^" -> bitwisexor(left, right) "|" -> bitwiseor(left, right)
"and" -> logicaland(left, right) "^" -> bitwisexor(left, right)
"or" -> logicalor(left, right) "and" -> logicaland(left, right)
"xor" -> logicalxor(left, right) "or" -> logicalor(left, right)
"<" -> NumericLiteralValue.fromBoolean(left < right, left.position) "xor" -> logicalxor(left, right)
">" -> NumericLiteralValue.fromBoolean(left > right, left.position) "<" -> NumericLiteralValue.fromBoolean(left < right, left.position)
"<=" -> NumericLiteralValue.fromBoolean(left <= right, left.position) ">" -> NumericLiteralValue.fromBoolean(left > right, left.position)
">=" -> NumericLiteralValue.fromBoolean(left >= right, left.position) "<=" -> NumericLiteralValue.fromBoolean(left <= right, left.position)
"==" -> NumericLiteralValue.fromBoolean(left == right, left.position) ">=" -> NumericLiteralValue.fromBoolean(left >= right, left.position)
"!=" -> NumericLiteralValue.fromBoolean(left != right, left.position) "==" -> NumericLiteralValue.fromBoolean(left == right, left.position)
"<<" -> shiftedleft(left, right) "!=" -> NumericLiteralValue.fromBoolean(left != right, left.position)
">>" -> shiftedright(left, right) "<<" -> shiftedleft(left, right)
else -> throw FatalAstException("const evaluation for invalid operator $operator") ">>" -> shiftedright(left, right)
else -> throw FatalAstException("const evaluation for invalid operator $operator")
}
} catch (ax: FatalAstException) {
throw ExpressionError(ax.message, left.position)
} }
} }

View File

@ -13,7 +13,7 @@ import prog8.compiler.target.ICompilationTarget
import kotlin.math.pow import kotlin.math.pow
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() { internal class ConstantFoldingOptimizer(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() {
private val noModifications = emptyList<IAstModification>() private val noModifications = emptyList<IAstModification>()
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> { override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
@ -224,7 +224,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
range.step range.step
} }
return RangeExpr(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, ICompilationTarget.instance, range.position) return RangeExpr(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, compTarget, range.position)
} }
// adjust the datatype of a range expression in for loops to the loop variable. // adjust the datatype of a range expression in for loops to the loop variable.

View File

@ -10,11 +10,11 @@ import prog8.ast.statements.ForLoop
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.compiler.ErrorReporter import prog8.compiler.IErrorReporter
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
// 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, private val errors: IErrorReporter) : 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> {
@ -39,7 +39,7 @@ internal class VarConstantValueTypeAdjuster(private val program: Program, privat
// Replace all constant identifiers with their actual value, // Replace all constant identifiers with their actual value,
// and the array var initializer values and sizes. // and the array var initializer values and sizes.
// This is needed because further constant optimizations depend on those. // This is needed because further constant optimizations depend on those.
internal class ConstantIdentifierReplacer(private val program: Program, private val errors: ErrorReporter) : AstWalker() { internal class ConstantIdentifierReplacer(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
private val noModifications = emptyList<IAstModification>() private val noModifications = emptyList<IAstModification>()
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> { override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
@ -75,7 +75,7 @@ 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?.indexVar?.referencesIdentifier(decl.name) == true) { if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexExpr?.referencesIdentifier(decl.name) == true) {
errors.err("recursive var declaration", decl.position) errors.err("recursive var declaration", decl.position)
return noModifications return noModifications
} }
@ -93,19 +93,6 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
decl decl
)) ))
} }
} else if(arraysize.constIndex()==null) {
// see if we can calculate the size from other fields
try {
val cval = arraysize.indexVar?.constValue(program) ?: arraysize.origExpression?.constValue(program)
if (cval != null) {
arraysize.indexVar = null
arraysize.origExpression = null
arraysize.indexNum = cval
}
} catch (x: UndefinedSymbolError) {
errors.err(x.message, x.position)
return noModifications
}
} }
} }
@ -192,7 +179,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
if(rangeExpr==null && litval!=null) { if(rangeExpr==null && litval!=null) {
// arraysize initializer is a single int, and we know the size. // arraysize initializer is a single int, and we know the size.
val fillvalue = litval.number.toDouble() val fillvalue = litval.number.toDouble()
if (fillvalue < ICompilationTarget.instance.machine.FLOAT_MAX_NEGATIVE || fillvalue > ICompilationTarget.instance.machine.FLOAT_MAX_POSITIVE) if (fillvalue < compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > compTarget.machine.FLOAT_MAX_POSITIVE)
errors.err("float value overflow", litval.position) errors.err("float value overflow", litval.position)
else { else {
// create the array itself, filled with the fillvalue. // create the array itself, filled with the fillvalue.

View File

@ -2,16 +2,18 @@ package prog8.optimizer
import prog8.ast.IBuiltinFunctions import prog8.ast.IBuiltinFunctions
import prog8.ast.Program import prog8.ast.Program
import prog8.compiler.ErrorReporter import prog8.compiler.IErrorReporter
import prog8.compiler.target.ICompilationTarget
import java.nio.file.Path
internal fun Program.constantFold(errors: ErrorReporter) { internal fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) {
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors) val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
valuetypefixer.visit(this) valuetypefixer.visit(this)
if(errors.isEmpty()) { if(errors.isEmpty()) {
valuetypefixer.applyModifications() valuetypefixer.applyModifications()
val replacer = ConstantIdentifierReplacer(this, errors) val replacer = ConstantIdentifierReplacer(this, errors, compTarget)
replacer.visit(this) replacer.visit(this)
if (errors.isEmpty()) { if (errors.isEmpty()) {
replacer.applyModifications() replacer.applyModifications()
@ -20,7 +22,7 @@ internal fun Program.constantFold(errors: ErrorReporter) {
if(errors.isEmpty()) { if(errors.isEmpty()) {
valuetypefixer.applyModifications() valuetypefixer.applyModifications()
val optimizer = ConstantFoldingOptimizer(this) val optimizer = ConstantFoldingOptimizer(this, compTarget)
optimizer.visit(this) optimizer.visit(this)
while (errors.isEmpty() && optimizer.applyModifications() > 0) { while (errors.isEmpty() && optimizer.applyModifications() > 0) {
optimizer.visit(this) optimizer.visit(this)
@ -39,8 +41,11 @@ internal fun Program.constantFold(errors: ErrorReporter) {
} }
internal fun Program.optimizeStatements(errors: ErrorReporter, functions: IBuiltinFunctions): Int { internal fun Program.optimizeStatements(errors: IErrorReporter,
val optimizer = StatementOptimizer(this, errors, functions) functions: IBuiltinFunctions,
compTarget: ICompilationTarget,
asmFileLoader: (filename: String, source: Path)->String): Int {
val optimizer = StatementOptimizer(this, errors, functions, compTarget, asmFileLoader)
optimizer.visit(this) optimizer.visit(this)
val optimizationCount = optimizer.applyModifications() val optimizationCount = optimizer.applyModifications()
@ -55,8 +60,8 @@ internal fun Program.simplifyExpressions() : Int {
return opti.applyModifications() return opti.applyModifications()
} }
internal fun Program.splitBinaryExpressions() : Int { internal fun Program.splitBinaryExpressions(compTarget: ICompilationTarget) : Int {
val opti = BinExprSplitter(this) val opti = BinExprSplitter(this, compTarget)
opti.visit(this) opti.visit(this)
return opti.applyModifications() return opti.applyModifications()
} }

View File

@ -10,23 +10,27 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.compiler.ErrorReporter import prog8.compiler.IErrorReporter
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
import java.nio.file.Path
import kotlin.math.floor import kotlin.math.floor
internal class StatementOptimizer(private val program: Program, internal class StatementOptimizer(private val program: Program,
private val errors: ErrorReporter, private val errors: IErrorReporter,
private val functions: IBuiltinFunctions private val functions: IBuiltinFunctions,
private val compTarget: ICompilationTarget,
asmFileLoader: (filename: String, source: Path)->String
) : AstWalker() { ) : AstWalker() {
private val noModifications = emptyList<IAstModification>() private val noModifications = emptyList<IAstModification>()
private val callgraph = CallGraph(program) private val callgraph = CallGraph(program, asmFileLoader)
override fun after(block: Block, parent: Node): Iterable<IAstModification> { override fun after(block: Block, parent: Node): Iterable<IAstModification> {
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) if(block.name != program.internedStringsModuleName)
errors.warn("removing empty block '${block.name}'", block.position)
return listOf(IAstModification.Remove(block, parent as INameScope)) return listOf(IAstModification.Remove(block, parent as INameScope))
} }
@ -52,8 +56,10 @@ internal class StatementOptimizer(private val program: Program,
} }
if(subroutine !in callgraph.usedSymbols && !forceOutput) { if(subroutine !in callgraph.usedSymbols && !forceOutput) {
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position) if(!subroutine.isAsmSubroutine) {
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope())) errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
}
} }
return noModifications return noModifications
@ -81,11 +87,9 @@ internal class StatementOptimizer(private val program: Program,
} }
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters // printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
// this is a C-64 specific optimization if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print")) {
val arg = functionCallStatement.args.single() val arg = functionCallStatement.args.single()
val stringVar: IdentifierReference? val stringVar: IdentifierReference? = if(arg is AddressOf) {
stringVar = if(arg is AddressOf) {
arg.identifier arg.identifier
} else { } else {
arg as? IdentifierReference arg as? IdentifierReference
@ -96,29 +100,29 @@ internal class StatementOptimizer(private val program: Program,
if(string!=null) { if(string!=null) {
val pos = functionCallStatement.position val pos = functionCallStatement.position
if (string.value.length == 1) { if (string.value.length == 1) {
val firstCharEncoded = ICompilationTarget.instance.encodeString(string.value, string.altEncoding)[0] val firstCharEncoded = compTarget.encodeString(string.value, string.altEncoding)[0]
val chrout = FunctionCallStatement( val chrout = FunctionCallStatement(
IdentifierReference(listOf("c64", "CHROUT"), pos), IdentifierReference(listOf("txt", "chrout"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)), mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
functionCallStatement.void, pos functionCallStatement.void, pos
) )
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent)) return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
} else if (string.value.length == 2) { } else if (string.value.length == 2) {
val firstTwoCharsEncoded = ICompilationTarget.instance.encodeString(string.value.take(2), string.altEncoding) val firstTwoCharsEncoded = compTarget.encodeString(string.value.take(2), string.altEncoding)
val chrout1 = FunctionCallStatement( val chrout1 = FunctionCallStatement(
IdentifierReference(listOf("c64", "CHROUT"), pos), IdentifierReference(listOf("txt", "chrout"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)), mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
functionCallStatement.void, pos functionCallStatement.void, pos
) )
val chrout2 = FunctionCallStatement( val chrout2 = FunctionCallStatement(
IdentifierReference(listOf("c64", "CHROUT"), pos), IdentifierReference(listOf("txt", "chrout"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)), mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
functionCallStatement.void, pos functionCallStatement.void, pos
) )
val anonscope = AnonymousScope(mutableListOf(), pos) return listOf(
anonscope.statements.add(chrout1) IAstModification.InsertBefore(functionCallStatement, chrout1, parent as INameScope),
anonscope.statements.add(chrout2) IAstModification.ReplaceNode(functionCallStatement, chrout2, parent)
return listOf(IAstModification.ReplaceNode(functionCallStatement, anonscope, parent)) )
} }
} }
} }
@ -212,7 +216,7 @@ internal class StatementOptimizer(private val program: Program,
val size = sv.value.length val size = sv.value.length
if(size==1) { if(size==1) {
// loop over string of length 1 -> just assign the single character // loop over string of length 1 -> just assign the single character
val character = ICompilationTarget.instance.encodeString(sv.value, sv.altEncoding)[0] val character = compTarget.encodeString(sv.value, sv.altEncoding)[0]
val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position) val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position)
val scope = AnonymousScope(mutableListOf(), forLoop.position) val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, forLoop.position)) scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, forLoop.position))
@ -293,18 +297,6 @@ internal class StatementOptimizer(private val program: Program,
return noModifications return noModifications
} }
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
// remove empty choices
class ChoiceRemover(val choice: WhenChoice) : IAstModification {
override fun perform() {
whenStatement.choices.remove(choice)
}
}
return whenStatement.choices
.filter { !it.statements.containsCodeOrVars() }
.map { ChoiceRemover(it) }
}
override fun after(jump: Jump, parent: Node): Iterable<IAstModification> { override fun after(jump: Jump, parent: Node): Iterable<IAstModification> {
// if the jump is to the next statement, remove the jump // if the jump is to the next statement, remove the jump
val scope = jump.definingScope() val scope = jump.definingScope()

View File

@ -3,7 +3,6 @@ package prog8.optimizer
import prog8.ast.INameScope import prog8.ast.INameScope
import prog8.ast.Node import prog8.ast.Node
import prog8.ast.Program import prog8.ast.Program
import prog8.compiler.ErrorReporter
import prog8.ast.expressions.BinaryExpression import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.FunctionCall import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.PrefixExpression import prog8.ast.expressions.PrefixExpression
@ -11,20 +10,27 @@ import prog8.ast.expressions.TypecastExpression
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.compiler.IErrorReporter
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
import java.nio.file.Path
internal class UnusedCodeRemover(private val program: Program, private val errors: ErrorReporter): AstWalker() { internal class UnusedCodeRemover(private val program: Program,
private val errors: IErrorReporter,
private val compTarget: ICompilationTarget,
private val asmFileLoader: (filename: String, source: Path)->String): AstWalker() {
override fun before(program: Program, parent: Node): Iterable<IAstModification> { override fun before(program: Program, parent: Node): Iterable<IAstModification> {
val callgraph = CallGraph(program) val callgraph = CallGraph(program, asmFileLoader)
val removals = mutableListOf<IAstModification>() val removals = mutableListOf<IAstModification>()
// remove all subroutines that aren't called, or are empty // remove all subroutines that aren't called, or are empty
// NOTE: part of this is also done already in the StatementOptimizer
val entrypoint = program.entrypoint() val entrypoint = program.entrypoint()
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())) { val forceOutput = "force_output" in sub.definingBlock().options()
if (sub !== entrypoint && !forceOutput && !sub.isAsmSubroutine && (callgraph.calledBy[sub].isNullOrEmpty() || sub.containsNoCodeNorVars())) {
removals.add(IAstModification.Remove(sub, sub.definingScope())) removals.add(IAstModification.Remove(sub, sub.definingScope()))
} }
} }
@ -96,7 +102,7 @@ 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) && ICompilationTarget.instance.isInRegularRAM(assign1.target, program)) { if (assign1.target.isSameAs(assign2.target, program) && compTarget.isInRegularRAM(assign1.target, program)) {
if(assign2.target.identifier==null || !assign2.value.referencesIdentifier(*(assign2.target.identifier!!.nameInSource.toTypedArray()))) if(assign2.target.identifier==null || !assign2.value.referencesIdentifier(*(assign2.target.identifier!!.nameInSource.toTypedArray())))
// only remove the second assignment if its value is a simple expression! // only remove the second assignment if its value is a simple expression!
when(assign2.value) { when(assign2.value) {

View File

@ -5,21 +5,22 @@ import org.hamcrest.Matchers.closeTo
import org.hamcrest.Matchers.equalTo import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
import prog8.ast.IBuiltinFunctions import prog8.ast.*
import prog8.ast.Module import prog8.ast.base.DataType
import prog8.ast.Program import prog8.ast.base.ParentSentinel
import prog8.ast.base.* import prog8.ast.base.Position
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.toHex
import prog8.compiler.* import prog8.compiler.*
import prog8.compiler.target.C64Target import prog8.compiler.target.C64Target
import prog8.compiler.target.Cx16Target
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5 import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
import prog8.compiler.target.c64.Petscii import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.cx16.CX16MachineDefinition import prog8.compiler.target.cx16.CX16MachineDefinition.CX16Zeropage
import java.io.CharConversionException import java.io.CharConversionException
import java.nio.file.Path import java.nio.file.Path
import kotlin.test.* import kotlin.test.*
@ -134,7 +135,7 @@ class TestC64Zeropage {
@Test @Test
fun testNames() { fun testNames() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
zp.allocate("", DataType.UBYTE, null, errors) zp.allocate("", DataType.UBYTE, null, errors)
zp.allocate("", DataType.UBYTE, null, errors) zp.allocate("", DataType.UBYTE, null, errors)
@ -147,37 +148,37 @@ class TestC64Zeropage {
@Test @Test
fun testZpFloatEnable() { fun testZpFloatEnable() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
assertFailsWith<CompilerException> { assertFailsWith<CompilerException> {
zp.allocate("", DataType.FLOAT, null, errors) zp.allocate("", DataType.FLOAT, null, errors)
} }
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false)) val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, C64Target))
assertFailsWith<CompilerException> { assertFailsWith<CompilerException> {
zp2.allocate("", DataType.FLOAT, null, errors) zp2.allocate("", DataType.FLOAT, null, errors)
} }
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false)) val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
zp3.allocate("", DataType.FLOAT, null, errors) zp3.allocate("", DataType.FLOAT, null, errors)
} }
@Test @Test
fun testZpModesWithFloats() { fun testZpModesWithFloats() {
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
assertFailsWith<CompilerException> { assertFailsWith<CompilerException> {
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false, C64Target))
} }
assertFailsWith<CompilerException> { assertFailsWith<CompilerException> {
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false)) C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false, C64Target))
} }
} }
@Test @Test
fun testZpDontuse() { fun testZpDontuse() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target))
println(zp.free) println(zp.free)
assertEquals(0, zp.available()) assertEquals(0, zp.available())
assertFailsWith<CompilerException> { assertFailsWith<CompilerException> {
@ -187,19 +188,19 @@ 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, C64Target))
assertEquals(18, 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, C64Target))
assertEquals(89, zp2.available()) assertEquals(85, 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, C64Target))
assertEquals(125, zp3.available()) assertEquals(125, zp3.available())
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false)) val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
assertEquals(238, zp4.available()) assertEquals(238, zp4.available())
} }
@Test @Test
fun testReservedSpace() { fun testReservedSpace() {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false)) val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
assertEquals(238, zp1.available()) assertEquals(238, zp1.available())
assertTrue(50 in zp1.free) assertTrue(50 in zp1.free)
assertTrue(100 in zp1.free) assertTrue(100 in zp1.free)
@ -208,7 +209,7 @@ class TestC64Zeropage {
assertTrue(200 in zp1.free) assertTrue(200 in zp1.free)
assertTrue(255 in zp1.free) assertTrue(255 in zp1.free)
assertTrue(199 in zp1.free) assertTrue(199 in zp1.free)
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false)) val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false, C64Target))
assertEquals(139, zp2.available()) assertEquals(139, zp2.available())
assertFalse(50 in zp2.free) assertFalse(50 in zp2.free)
assertFalse(100 in zp2.free) assertFalse(100 in zp2.free)
@ -221,7 +222,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, C64Target))
assertEquals(18, zp.available()) assertEquals(18, zp.available())
assertFailsWith<ZeropageDepletedError> { assertFailsWith<ZeropageDepletedError> {
@ -244,7 +245,7 @@ class TestC64Zeropage {
@Test @Test
fun testFullAllocation() { fun testFullAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
assertEquals(238, zp.available()) assertEquals(238, zp.available())
val loc = zp.allocate("", DataType.UWORD, null, errors) val loc = zp.allocate("", DataType.UWORD, null, errors)
assertTrue(loc > 3) assertTrue(loc > 3)
@ -274,7 +275,7 @@ 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, C64Target))
assertEquals(18, 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))
@ -293,7 +294,7 @@ class TestC64Zeropage {
@Test @Test
fun testReservedLocations() { fun testReservedLocations() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false)) val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word") assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word")
} }
} }
@ -303,9 +304,30 @@ class TestC64Zeropage {
class TestCx16Zeropage { class TestCx16Zeropage {
@Test @Test
fun testReservedLocations() { fun testReservedLocations() {
val zp = CX16MachineDefinition.CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false)) val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, Cx16Target))
assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word") assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word")
} }
@Test
fun testFreeSpaces() {
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, Cx16Target))
assertEquals(88, zp1.available())
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
assertEquals(175, zp3.available())
val zp4 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
assertEquals(216, zp4.available())
}
@Test
fun testReservedSpace() {
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
assertEquals(216, zp1.available())
assertTrue(0x22 in zp1.free)
assertTrue(0x80 in zp1.free)
assertTrue(0xff in zp1.free)
assertFalse(0x02 in zp1.free)
assertFalse(0x21 in zp1.free)
}
} }
@ -408,16 +430,20 @@ class TestMemory {
private class DummyFunctions: IBuiltinFunctions { private class DummyFunctions: IBuiltinFunctions {
override val names: Set<String> = emptySet() override val names: Set<String> = emptySet()
override val purefunctionNames: Set<String> = emptySet() override val purefunctionNames: Set<String> = emptySet()
override fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue? = null override fun constValue(name: String, args: List<Expression>, position: Position, memsizer: IMemSizer): NumericLiteralValue? = null
override fun returnType(name: String, args: MutableList<Expression>) = InferredTypes.InferredType.unknown() override fun returnType(name: String, args: MutableList<Expression>) = InferredTypes.InferredType.unknown()
} }
private class DummyMemsizer: IMemSizer {
override fun memorySize(dt: DataType): Int = 0
}
@Test @Test
fun testInValidRamC64_memory_addresses() { fun testInValidRamC64_memory_addresses() {
var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY) var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val program = Program("test", mutableListOf(), DummyFunctions()) val program = Program("test", mutableListOf(), DummyFunctions(), DummyMemsizer())
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(C64Target.isInRegularRAM(target, program))
memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY)
@ -442,7 +468,7 @@ class TestMemory {
var memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY) var memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val program = Program("test", mutableListOf(), DummyFunctions()) val program = Program("test", mutableListOf(), DummyFunctions(), DummyMemsizer())
assertFalse(C64Target.isInRegularRAM(target, program)) assertFalse(C64Target.isInRegularRAM(target, program))
memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY) memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY)
@ -461,7 +487,7 @@ class TestMemory {
@Test @Test
fun testInValidRamC64_memory_identifiers() { fun testInValidRamC64_memory_identifiers() {
var target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.VAR) var target = createTestProgramForMemoryRefViaVar(0x1000, VarDeclType.VAR)
val program = Program("test", mutableListOf(), DummyFunctions()) val program = Program("test", mutableListOf(), DummyFunctions(), DummyMemsizer())
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(C64Target.isInRegularRAM(target, program))
target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.VAR) target = createTestProgramForMemoryRefViaVar(0xd020, VarDeclType.VAR)
@ -490,7 +516,7 @@ class TestMemory {
fun testInValidRamC64_memory_expression() { fun testInValidRamC64_memory_expression() {
val memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY) val memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY) val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val program = Program("test", mutableListOf(), DummyFunctions()) val program = Program("test", mutableListOf(), DummyFunctions(), DummyMemsizer())
assertFalse(C64Target.isInRegularRAM(target, program)) assertFalse(C64Target.isInRegularRAM(target, program))
} }
@ -501,7 +527,7 @@ class TestMemory {
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of("")) val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
val program = Program("test", mutableListOf(module), DummyFunctions()) val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
module.linkParents(ParentSentinel) module.linkParents(ParentSentinel)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(C64Target.isInRegularRAM(target, program))
} }
@ -514,7 +540,7 @@ class TestMemory {
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of("")) val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
val program = Program("test", mutableListOf(module), DummyFunctions()) val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
module.linkParents(ParentSentinel) module.linkParents(ParentSentinel)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(C64Target.isInRegularRAM(target, program))
} }
@ -527,7 +553,7 @@ class TestMemory {
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of("")) val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
val program = Program("test", mutableListOf(module), DummyFunctions()) val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
module.linkParents(ParentSentinel) module.linkParents(ParentSentinel)
assertFalse(C64Target.isInRegularRAM(target, program)) assertFalse(C64Target.isInRegularRAM(target, program))
} }
@ -540,7 +566,7 @@ class TestMemory {
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of("")) val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
val program = Program("test", mutableListOf(module), DummyFunctions()) val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
module.linkParents(ParentSentinel) module.linkParents(ParentSentinel)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(C64Target.isInRegularRAM(target, program))
} }
@ -554,7 +580,7 @@ class TestMemory {
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of("")) val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
val program = Program("test", mutableListOf(module), DummyFunctions()) val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
module.linkParents(ParentSentinel) module.linkParents(ParentSentinel)
assertTrue(C64Target.isInRegularRAM(target, program)) assertTrue(C64Target.isInRegularRAM(target, program))
} }
@ -568,7 +594,7 @@ class TestMemory {
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY) val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY) val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of("")) val module = Module("test", mutableListOf(subroutine), Position.DUMMY, false, Path.of(""))
val program = Program("test", mutableListOf(module), DummyFunctions()) val program = Program("test", mutableListOf(module), DummyFunctions(), DummyMemsizer())
module.linkParents(ParentSentinel) module.linkParents(ParentSentinel)
assertFalse(C64Target.isInRegularRAM(target, program)) assertFalse(C64Target.isInRegularRAM(target, program))
} }

View File

@ -0,0 +1,13 @@
.PHONY: all clean test
all: test
clean:
rm -f *.prg *.asm *.vice-*
test: clean
p8compile -target cx16 *.p8 >/dev/null
for program in *.prg; do \
echo "RUNNING:" $$program ; \
x16emu -run -prg $$program >/dev/null ; \
done

View File

@ -0,0 +1,18 @@
.PHONY: all clean test
all: test
clean:
rm -f *.prg *.asm *.vice-* test_*.p8
test: clean generate test_prgs
generate:
python make_tests.py
p8compile -noopt -target cx16 *.p8 >/dev/null
test_prgs:
for program in *.prg; do \
echo "RUNNING:" $$program ; \
x16emu -run -prg $$program >/dev/null ; \
done

View File

@ -0,0 +1,508 @@
# generates various Prog8 files with a huge amount of number comparion tests,
# for all supported datatypes and all comparison operators.
import sys
index = 0
def minmaxvalues(dt):
if dt == "ubyte":
return 0, 255
elif dt == "uword":
return 0, 65535
elif dt == "byte":
return -128, 127
elif dt == "word":
return -32768, 32767
elif dt == "float":
return -99999999, 99999999
else:
raise ValueError(dt)
def gen_test(dt, comparison, left, right, expected):
global index
etxt = f"{left} {comparison} {right}"
if eval(etxt) != expected:
raise ValueError("invalid comparison: "+etxt+" for "+dt)
if expected:
stmt_ok = lambda ix: "num_successes++"
stmt_else = lambda ix: f"error({ix})"
else:
stmt_ok = lambda ix: f"error({ix})"
stmt_else = lambda ix: "num_successes++"
def c(number):
if dt not in ("byte", "ubyte"):
return f"({number} as {dt})"
return str(number)
print(
f""" left = {c(left)}
right = {c(right)}
"""
)
# const <op> const
index += 1
print(
f""" ; test #{index}
if {c(left)} {comparison} {c(right)} {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# const <op> var
index += 1
print(
f""" ; test #{index}
if {c(left)} {comparison} right {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# const <op> expr
index += 1
print(
f""" ; test #{index}
if {c(left)} {comparison} right+zero {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# var <op> const
index += 1
print(
f""" ; test #{index}
if left {comparison} {c(right)} {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# var <op> var
index += 1
print(
f""" ; test #{index}
if left {comparison} right {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# var <op> expr
index += 1
print(
f""" ; test #{index}
if left {comparison} right+zero {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# expr <op> const
index += 1
print(
f""" ; test #{index}
if left+zero {comparison} {c(right)} {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# expr <op> var
index += 1
print(
f""" ; test #{index}
if left+zero {comparison} right {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# expr <op> expr
index += 1
print(
f""" ; test #{index}
if left+zero {comparison} right+zero {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
def gen_comp_header(dt, operator):
print(" ; tests: ", dt, operator)
print(" comparison = \""+operator+"\"")
print(" txt.print(datatype)")
print(" txt.spc()")
print(" txt.print(comparison)")
print(" txt.nl()")
def gen_comp_equal(dt):
minval, maxval = minmaxvalues(dt)
gen_comp_header(dt, "==")
gen_test(dt, "==", 0, 0, True)
gen_test(dt, "==", 0, 1, False)
gen_test(dt, "==", 100, 100, True)
gen_test(dt, "==", 100, 101, False)
if maxval >= 200:
gen_test(dt, "==", 200, 200, True)
gen_test(dt, "==", 200, 201, False)
if maxval >= 9999:
gen_test(dt, "==", 9999, 9999, True)
gen_test(dt, "==", 9999, 10000, False)
gen_test(dt, "==", 0x5000, 0x5000, True)
gen_test(dt, "==", 0x5000, 0x5001, False)
gen_test(dt, "==", 0x5000, 0x4fff, False)
if maxval >= 30000:
gen_test(dt, "==", 30000, 30000, True)
gen_test(dt, "==", 30000, 30001, False)
if maxval >= 40000:
gen_test(dt, "==", 0xf000, 0xf000, True)
gen_test(dt, "==", 0xf000, 0xf001, False)
gen_test(dt, "==", 0xf000, 0xffff, False)
if minval < 0:
gen_test(dt, "==", 0, -1, False)
gen_test(dt, "==", -100, -100, True)
if minval < -200:
gen_test(dt, "==", -200, -200, True)
gen_test(dt, "==", -200, -201, False)
if minval < -9999:
gen_test(dt, "==", -0x5000, -0x5000, True)
gen_test(dt, "==", -0x5000, -0x5001, False)
gen_test(dt, "==", -0x5000, -0x4fff, False)
gen_test(dt, "==", -9999, -9999, True)
gen_test(dt, "==", -9999, -10000, False)
gen_test(dt, "==", minval, minval, True)
gen_test(dt, "==", minval, minval+1, False)
gen_test(dt, "==", maxval, maxval, True)
gen_test(dt, "==", maxval, maxval-1, False)
def gen_comp_notequal(dt):
minval, maxval = minmaxvalues(dt)
gen_comp_header(dt, "!=")
gen_test(dt, "!=", 0, 0, False)
gen_test(dt, "!=", 0, 1, True)
gen_test(dt, "!=", 100, 100, False)
gen_test(dt, "!=", 100, 101, True)
if maxval >= 200:
gen_test(dt, "!=", 200, 200, False)
gen_test(dt, "!=", 200, 201, True)
if maxval >= 9999:
gen_test(dt, "!=", 9999, 9999, False)
gen_test(dt, "!=", 9999, 10000, True)
gen_test(dt, "!=", 0x5000, 0x5000, False)
gen_test(dt, "!=", 0x5000, 0x5001, True)
gen_test(dt, "!=", 0x5000, 0x4fff, True)
if maxval >= 30000:
gen_test(dt, "!=", 30000, 30000, False)
gen_test(dt, "!=", 30000, 30001, True)
if maxval >= 40000:
gen_test(dt, "!=", 0xf000, 0xf000, False)
gen_test(dt, "!=", 0xf000, 0xf001, True)
gen_test(dt, "!=", 0xf000, 0xffff, True)
if minval < 0:
gen_test(dt, "!=", 0, -1, True)
gen_test(dt, "!=", -100, -100, False)
if minval < -200:
gen_test(dt, "!=", -200, -200, False)
gen_test(dt, "!=", -200, -201, True)
if minval < -9999:
gen_test(dt, "!=", -0x5000, -0x5000, False)
gen_test(dt, "!=", -0x5000, -0x5001, True)
gen_test(dt, "!=", -0x5000, -0x4fff, True)
gen_test(dt, "!=", -9999, -9999, False)
gen_test(dt, "!=", -9999, -10000, True)
gen_test(dt, "!=", minval, minval, False)
gen_test(dt, "!=", minval, minval+1, True)
gen_test(dt, "!=", maxval, maxval, False)
gen_test(dt, "!=", maxval, maxval-1, True)
def gen_comp_less(dt):
minval, maxval = minmaxvalues(dt)
gen_comp_header(dt, "<")
gen_test(dt, "<", 0, 0, False)
gen_test(dt, "<", 0, 1, True)
gen_test(dt, "<", 100, 100, False)
gen_test(dt, "<", 100, 101, True)
gen_test(dt, "<", 100, 99, False)
if maxval >= 200:
gen_test(dt, "<", 200, 200, False)
gen_test(dt, "<", 200, 201, True)
gen_test(dt, "<", 200, 199, False)
if maxval >= 9999:
gen_test(dt, "<", 9999, 9999, False)
gen_test(dt, "<", 9999, 10000, True)
gen_test(dt, "<", 9999, 9998, False)
gen_test(dt, "<", 0x5000, 0x5000, False)
gen_test(dt, "<", 0x5000, 0x5001, True)
gen_test(dt, "<", 0x5000, 0x4fff, False)
if maxval >= 30000:
gen_test(dt, "<", 30000, 30000, False)
gen_test(dt, "<", 30000, 30001, True)
gen_test(dt, "<", 30000, 29999, False)
if maxval >= 40000:
gen_test(dt, "<", 0xf000, 0xf000, False)
gen_test(dt, "<", 0xf000, 0xf001, True)
gen_test(dt, "<", 0xf000, 0xefff, False)
if minval < 0:
gen_test(dt, "<", 0, -1, False)
gen_test(dt, "<", -100, -100, False)
gen_test(dt, "<", -100, -101, False)
gen_test(dt, "<", -100, -99, True)
if minval < -200:
gen_test(dt, "<", -200, -200, False)
gen_test(dt, "<", -200, -201, False)
gen_test(dt, "<", -200, -199, True)
if minval < -9999:
gen_test(dt, "<", -0x5000, -0x5000, False)
gen_test(dt, "<", -0x5000, -0x5001, False)
gen_test(dt, "<", -0x5000, -0x4fff, True)
gen_test(dt, "<", -9999, -9999, False)
gen_test(dt, "<", -9999, -10000, False)
gen_test(dt, "<", -9999, -9998, True)
def gen_comp_greater(dt):
minval, maxval = minmaxvalues(dt)
gen_comp_header(dt, ">")
gen_test(dt, ">", 0, 0, False)
gen_test(dt, ">", 0, 1, False)
gen_test(dt, ">", 100, 100, False)
gen_test(dt, ">", 100, 101, False)
gen_test(dt, ">", 100, 99, True)
if maxval >= 200:
gen_test(dt, ">", 200, 200, False)
gen_test(dt, ">", 200, 201, False)
gen_test(dt, ">", 200, 199, True)
if maxval >= 9999:
gen_test(dt, ">", 9999, 9999, False)
gen_test(dt, ">", 9999, 10000, False)
gen_test(dt, ">", 9999, 9998, True)
gen_test(dt, ">", 0x5000, 0x5000, False)
gen_test(dt, ">", 0x5000, 0x5001, False)
gen_test(dt, ">", 0x5000, 0x4fff, True)
if maxval >= 30000:
gen_test(dt, ">", 30000, 30000, False)
gen_test(dt, ">", 30000, 30001, False)
gen_test(dt, ">", 30000, 29999, True)
if maxval >= 40000:
gen_test(dt, ">", 0xf000, 0xf000, False)
gen_test(dt, ">", 0xf000, 0xf001, False)
gen_test(dt, ">", 0xf000, 0xefff, True)
if minval < 0:
gen_test(dt, ">", 0, -1, True)
gen_test(dt, ">", -100, -100, False)
gen_test(dt, ">", -100, -101, True)
gen_test(dt, ">", -100, -99, False)
if minval < -200:
gen_test(dt, ">", -200, -200, False)
gen_test(dt, ">", -200, -201, True)
gen_test(dt, ">", -200, -199, False)
if minval < -9999:
gen_test(dt, ">", -0x5000, -0x5000, False)
gen_test(dt, ">", -0x5000, -0x5001, True)
gen_test(dt, ">", -0x5000, -0x4fff, False)
gen_test(dt, ">", -9999, -9999, False)
gen_test(dt, ">", -9999, -10000, True)
gen_test(dt, ">", -9999, -9998, False)
def gen_comp_lessequal(dt):
minval, maxval = minmaxvalues(dt)
gen_comp_header(dt, "<=")
gen_test(dt, "<=", 0, 0, True)
gen_test(dt, "<=", 0, 1, True)
gen_test(dt, "<=", 100, 100, True)
gen_test(dt, "<=", 100, 101, True)
gen_test(dt, "<=", 100, 99, False)
if maxval >= 200:
gen_test(dt, "<=", 200, 200, True)
gen_test(dt, "<=", 200, 201, True)
gen_test(dt, "<=", 200, 199, False)
if maxval >= 9999:
gen_test(dt, "<=", 9999, 9999, True)
gen_test(dt, "<=", 9999, 10000, True)
gen_test(dt, "<=", 9999, 9998, False)
gen_test(dt, "<=", 0x5000, 0x5000, True)
gen_test(dt, "<=", 0x5000, 0x5001, True)
gen_test(dt, "<=", 0x5000, 0x4fff, False)
if maxval >= 30000:
gen_test(dt, "<=", 30000, 30000, True)
gen_test(dt, "<=", 30000, 30001, True)
gen_test(dt, "<=", 30000, 29999, False)
if maxval >= 40000:
gen_test(dt, "<=", 0xf000, 0xf000, True)
gen_test(dt, "<=", 0xf000, 0xf001, True)
gen_test(dt, "<=", 0xf000, 0xefff, False)
if minval < 0:
gen_test(dt, "<=", 0, -1, False)
gen_test(dt, "<=", -100, -100, True)
gen_test(dt, "<=", -100, -101, False)
gen_test(dt, "<=", -100, -99, True)
if minval < -200:
gen_test(dt, "<=", -200, -200, True)
gen_test(dt, "<=", -200, -201, False)
gen_test(dt, "<=", -200, -199, True)
if minval < -9999:
gen_test(dt, "<=", -0x5000, -0x5000, True)
gen_test(dt, "<=", -0x5000, -0x5001, False)
gen_test(dt, "<=", -0x5000, -0x4fff, True)
gen_test(dt, "<=", -9999, -9999, True)
gen_test(dt, "<=", -9999, -10000, False)
gen_test(dt, "<=", -9999, -9998, True)
def gen_comp_greaterequal(dt):
minval, maxval = minmaxvalues(dt)
gen_comp_header(dt, ">=")
gen_test(dt, ">=", 0, 0, True)
gen_test(dt, ">=", 0, 1, False)
gen_test(dt, ">=", 100, 100, True)
gen_test(dt, ">=", 100, 101, False)
gen_test(dt, ">=", 100, 99, True)
if maxval >= 200:
gen_test(dt, ">=", 200, 200, True)
gen_test(dt, ">=", 200, 201, False)
gen_test(dt, ">=", 200, 199, True)
if maxval >= 9999:
gen_test(dt, ">=", 9999, 9999, True)
gen_test(dt, ">=", 9999, 10000, False)
gen_test(dt, ">=", 9999, 9998, True)
gen_test(dt, ">=", 0x5000, 0x5000, True)
gen_test(dt, ">=", 0x5000, 0x5001, False)
gen_test(dt, ">=", 0x5000, 0x4fff, True)
if maxval >= 30000:
gen_test(dt, ">=", 30000, 30000, True)
gen_test(dt, ">=", 30000, 30001, False)
gen_test(dt, ">=", 30000, 29999, True)
if maxval >= 40000:
gen_test(dt, ">=", 0xf000, 0xf000, True)
gen_test(dt, ">=", 0xf000, 0xf001, False)
gen_test(dt, ">=", 0xf000, 0xefff, True)
if minval < 0:
gen_test(dt, ">=", 0, -1, True)
gen_test(dt, ">=", -100, -100, True)
gen_test(dt, ">=", -100, -101, True)
gen_test(dt, ">=", -100, -99, False)
if minval < -200:
gen_test(dt, ">=", -200, -200, True)
gen_test(dt, ">=", -200, -201, True)
gen_test(dt, ">=", -200, -199, False)
if minval < -9999:
gen_test(dt, ">=", -0x5000, -0x5000, True)
gen_test(dt, ">=", -0x5000, -0x5001, True)
gen_test(dt, ">=", -0x5000, -0x4fff, False)
gen_test(dt, ">=", -9999, -9999, True)
gen_test(dt, ">=", -9999, -10000, True)
gen_test(dt, ">=", -9999, -9998, False)
def generate_test_routine_equalsnotequals(dt):
print(f"""
sub test_comparisons() {{
{dt} left
{dt} right
{dt} zero = 0
""")
gen_comp_equal(dt)
gen_comp_notequal(dt)
print(" }")
def generate_test_routine_lessgreater(dt):
print(f"""
sub test_comparisons() {{
{dt} left
{dt} right
{dt} zero = 0
""")
gen_comp_less(dt)
gen_comp_greater(dt)
print(" }")
def generate_test_routine_lessequalsgreaterequals(dt):
print(f"""
sub test_comparisons() {{
{dt} left
{dt} right
{dt} zero = 0
""")
gen_comp_lessequal(dt)
gen_comp_greaterequal(dt)
print(" }")
def generate(dt, operators):
global index
index = 0
print(f"""
%import textio
%import floats
%import test_stack
%zeropage basicsafe
main {{
uword num_errors = 0
uword num_successes = 0
str datatype = "{dt}"
uword comparison
sub start() {{
test_comparisons()
print_results()
test_stack.test()
}}
sub error(uword index) {{
txt.print(" ! error in test ")
txt.print_uw(index)
txt.chrout(' ')
txt.print(datatype)
txt.chrout(' ')
txt.print(comparison)
txt.nl()
num_errors++
}}
""")
if operators=="eq":
generate_test_routine_equalsnotequals(dt)
elif operators=="lt":
generate_test_routine_lessgreater(dt)
elif operators=="lteq":
generate_test_routine_lessequalsgreaterequals(dt)
else:
raise ValueError(operators)
print(f"""
sub print_results() {{
txt.nl()
txt.print("total {index}: ")
txt.print_uw(num_successes)
txt.print(" good, ")
txt.print_uw(num_errors)
txt.print(" errors!\\n")
}}
}}
""")
if __name__ == '__main__':
for dt in ["ubyte", "uword", "byte", "word", "float"]:
sys.stdout = open(f"test_{dt}_eq.p8", "wt")
generate(dt, "eq")
sys.stdout = open(f"test_{dt}_lt.p8", "wt")
generate(dt, "lt")
sys.stdout = open(f"test_{dt}_lteq.p8", "wt")
generate(dt, "lteq")

View File

@ -396,14 +396,18 @@ galaxy10 {
sub init(ubyte galaxy10num) { sub init(ubyte galaxy10num) {
number = 1 number = 1
planet10.number = 255 planet10.number = 255
seed = [base0, base1, base2] seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy10num-1 { repeat galaxy10num-1 {
nextgalaxy10() nextgalaxy10()
} }
} }
sub nextgalaxy10() { sub nextgalaxy10() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++ number++
if number==9 if number==9
number = 1 number = 1
@ -576,8 +580,10 @@ galaxy10 {
planet10.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi) planet10.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
planet10.species_kind = (planet10.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 planet10.species_kind = (planet10.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
} }
planet10.goatsoup_seed[0] = lsb(seed[1])
planet10.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] planet10.goatsoup_seed[1] = msb(seed[1])
planet10.goatsoup_seed[2] = lsb(seed[2])
planet10.goatsoup_seed[3] = seed2_msb
} }
sub tweakseed() { sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy2 {
sub init(ubyte galaxy2num) { sub init(ubyte galaxy2num) {
number = 1 number = 1
planet2.number = 255 planet2.number = 255
seed = [base0, base1, base2] seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy2num-1 { repeat galaxy2num-1 {
nextgalaxy2() nextgalaxy2()
} }
} }
sub nextgalaxy2() { sub nextgalaxy2() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++ number++
if number==9 if number==9
number = 1 number = 1
@ -577,7 +581,10 @@ galaxy2 {
planet2.species_kind = (planet2.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 planet2.species_kind = (planet2.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
} }
planet2.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] planet2.goatsoup_seed[0] = lsb(seed[1])
planet2.goatsoup_seed[1] = msb(seed[1])
planet2.goatsoup_seed[2] = lsb(seed[2])
planet2.goatsoup_seed[3] = seed2_msb
} }
sub tweakseed() { sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy3 {
sub init(ubyte galaxy3num) { sub init(ubyte galaxy3num) {
number = 1 number = 1
planet3.number = 255 planet3.number = 255
seed = [base0, base1, base2] seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy3num-1 { repeat galaxy3num-1 {
nextgalaxy3() nextgalaxy3()
} }
} }
sub nextgalaxy3() { sub nextgalaxy3() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++ number++
if number==9 if number==9
number = 1 number = 1
@ -577,7 +581,10 @@ galaxy3 {
planet3.species_kind = (planet3.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 planet3.species_kind = (planet3.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
} }
planet3.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] planet3.goatsoup_seed[0] = lsb(seed[1])
planet3.goatsoup_seed[1] = msb(seed[1])
planet3.goatsoup_seed[2] = lsb(seed[2])
planet3.goatsoup_seed[3] = seed2_msb
} }
sub tweakseed() { sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy4 {
sub init(ubyte galaxy4num) { sub init(ubyte galaxy4num) {
number = 1 number = 1
planet4.number = 255 planet4.number = 255
seed = [base0, base1, base2] seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy4num-1 { repeat galaxy4num-1 {
nextgalaxy4() nextgalaxy4()
} }
} }
sub nextgalaxy4() { sub nextgalaxy4() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++ number++
if number==9 if number==9
number = 1 number = 1
@ -577,7 +581,10 @@ galaxy4 {
planet4.species_kind = (planet4.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 planet4.species_kind = (planet4.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
} }
planet4.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] planet4.goatsoup_seed[0] = lsb(seed[1])
planet4.goatsoup_seed[1] = msb(seed[1])
planet4.goatsoup_seed[2] = lsb(seed[2])
planet4.goatsoup_seed[3] = seed2_msb
} }
sub tweakseed() { sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy5 {
sub init(ubyte galaxy5num) { sub init(ubyte galaxy5num) {
number = 1 number = 1
planet5.number = 255 planet5.number = 255
seed = [base0, base1, base2] seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy5num-1 { repeat galaxy5num-1 {
nextgalaxy5() nextgalaxy5()
} }
} }
sub nextgalaxy5() { sub nextgalaxy5() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++ number++
if number==9 if number==9
number = 1 number = 1
@ -577,7 +581,10 @@ galaxy5 {
planet5.species_kind = (planet5.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 planet5.species_kind = (planet5.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
} }
planet5.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] planet5.goatsoup_seed[0] = lsb(seed[1])
planet5.goatsoup_seed[1] = msb(seed[1])
planet5.goatsoup_seed[2] = lsb(seed[2])
planet5.goatsoup_seed[3] = seed2_msb
} }
sub tweakseed() { sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy6 {
sub init(ubyte galaxy6num) { sub init(ubyte galaxy6num) {
number = 1 number = 1
planet6.number = 255 planet6.number = 255
seed = [base0, base1, base2] seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy6num-1 { repeat galaxy6num-1 {
nextgalaxy6() nextgalaxy6()
} }
} }
sub nextgalaxy6() { sub nextgalaxy6() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++ number++
if number==9 if number==9
number = 1 number = 1
@ -577,7 +581,10 @@ galaxy6 {
planet6.species_kind = (planet6.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 planet6.species_kind = (planet6.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
} }
planet6.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] planet6.goatsoup_seed[0] = lsb(seed[1])
planet6.goatsoup_seed[1] = msb(seed[1])
planet6.goatsoup_seed[2] = lsb(seed[2])
planet6.goatsoup_seed[3] = seed2_msb
} }
sub tweakseed() { sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy7 {
sub init(ubyte galaxy7num) { sub init(ubyte galaxy7num) {
number = 1 number = 1
planet7.number = 255 planet7.number = 255
seed = [base0, base1, base2] seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy7num-1 { repeat galaxy7num-1 {
nextgalaxy7() nextgalaxy7()
} }
} }
sub nextgalaxy7() { sub nextgalaxy7() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++ number++
if number==9 if number==9
number = 1 number = 1
@ -577,7 +581,10 @@ galaxy7 {
planet7.species_kind = (planet7.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 planet7.species_kind = (planet7.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
} }
planet7.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] planet7.goatsoup_seed[0] = lsb(seed[1])
planet7.goatsoup_seed[1] = msb(seed[1])
planet7.goatsoup_seed[2] = lsb(seed[2])
planet7.goatsoup_seed[3] = seed2_msb
} }
sub tweakseed() { sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy8 {
sub init(ubyte galaxy8num) { sub init(ubyte galaxy8num) {
number = 1 number = 1
planet8.number = 255 planet8.number = 255
seed = [base0, base1, base2] seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy8num-1 { repeat galaxy8num-1 {
nextgalaxy8() nextgalaxy8()
} }
} }
sub nextgalaxy8() { sub nextgalaxy8() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++ number++
if number==9 if number==9
number = 1 number = 1
@ -577,7 +581,10 @@ galaxy8 {
planet8.species_kind = (planet8.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 planet8.species_kind = (planet8.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
} }
planet8.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] planet8.goatsoup_seed[0] = lsb(seed[1])
planet8.goatsoup_seed[1] = msb(seed[1])
planet8.goatsoup_seed[2] = lsb(seed[2])
planet8.goatsoup_seed[3] = seed2_msb
} }
sub tweakseed() { sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy9 {
sub init(ubyte galaxy9num) { sub init(ubyte galaxy9num) {
number = 1 number = 1
planet9.number = 255 planet9.number = 255
seed = [base0, base1, base2] seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy9num-1 { repeat galaxy9num-1 {
nextgalaxy9() nextgalaxy9()
} }
} }
sub nextgalaxy9() { sub nextgalaxy9() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++ number++
if number==9 if number==9
number = 1 number = 1
@ -576,8 +580,10 @@ galaxy9 {
planet9.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi) planet9.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
planet9.species_kind = (planet9.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 planet9.species_kind = (planet9.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
} }
planet9.goatsoup_seed[0] = lsb(seed[1])
planet9.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb] planet9.goatsoup_seed[1] = msb(seed[1])
planet9.goatsoup_seed[2] = lsb(seed[2])
planet9.goatsoup_seed[3] = seed2_msb
} }
sub tweakseed() { sub tweakseed() {

View File

@ -406,14 +406,18 @@ galaxy {
sub init(ubyte galaxynum) { sub init(ubyte galaxynum) {
number = 1 number = 1
planet.number = 255 planet.number = 255
seed = [base0, base1, base2] seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxynum-1 { repeat galaxynum-1 {
nextgalaxy() nextgalaxy()
} }
} }
sub nextgalaxy() { sub nextgalaxy() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])] seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++ number++
if number==9 if number==9
number = 1 number = 1
@ -587,7 +591,10 @@ galaxy {
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.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] planet.goatsoup_seed[0] = lsb(seed[1])
planet.goatsoup_seed[1] = msb(seed[1])
planet.goatsoup_seed[2] = lsb(seed[2])
planet.goatsoup_seed[3] = seed2_msb
} }
sub tweakseed() { sub tweakseed() {

View File

@ -33,6 +33,7 @@ dependencies {
compileKotlin { compileKotlin {
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = "11"
useIR = true
// verbose = true // verbose = true
// freeCompilerArgs += "-XXLanguage:+NewInference" // freeCompilerArgs += "-XXLanguage:+NewInference"
} }
@ -41,6 +42,7 @@ compileKotlin {
compileTestKotlin { compileTestKotlin {
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = "11"
useIR = true
} }
} }

View File

@ -121,8 +121,7 @@ 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!!.indexNum?.accept(this) decl.arraysize!!.indexExpr.accept(this)
decl.arraysize!!.indexVar?.accept(this)
} }
if(decl.isArray) if(decl.isArray)
output("]") output("]")
@ -365,8 +364,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) { override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
arrayIndexedExpression.arrayvar.accept(this) arrayIndexedExpression.arrayvar.accept(this)
output("[") output("[")
arrayIndexedExpression.indexer.indexNum?.accept(this) arrayIndexedExpression.indexer.indexExpr.accept(this)
arrayIndexedExpression.indexer.indexVar?.accept(this)
output("]") output("]")
} }

View File

@ -1,10 +1,7 @@
package prog8.ast package prog8.ast
import prog8.ast.base.* import prog8.ast.base.*
import prog8.ast.expressions.Expression import prog8.ast.expressions.*
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.InferredTypes
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
@ -244,10 +241,14 @@ interface IAssignable {
// just a tag for now // just a tag for now
} }
interface IMemSizer {
fun memorySize(dt: DataType): Int
}
interface IBuiltinFunctions { interface IBuiltinFunctions {
val names: Set<String> val names: Set<String>
val purefunctionNames: Set<String> val purefunctionNames: Set<String>
fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue? fun constValue(name: String, args: List<Expression>, position: Position, memsizer: IMemSizer): NumericLiteralValue?
fun returnType(name: String, args: MutableList<Expression>): InferredTypes.InferredType fun returnType(name: String, args: MutableList<Expression>): InferredTypes.InferredType
} }
@ -255,13 +256,32 @@ interface IBuiltinFunctions {
class Program(val name: String, val modules: MutableList<Module>, val builtinFunctions: IBuiltinFunctions): Node { class Program(val name: String,
val modules: MutableList<Module>,
val builtinFunctions: IBuiltinFunctions,
val memsizer: IMemSizer): Node {
val namespace = GlobalNamespace(modules, builtinFunctions.names) val namespace = GlobalNamespace(modules, builtinFunctions.names)
val mainModule: Module
get() = modules.first { it.name!=internedStringsModuleName }
val definedLoadAddress: Int val definedLoadAddress: Int
get() = modules.first().loadAddress get() = mainModule.loadAddress
var actualLoadAddress: Int = 0 var actualLoadAddress: Int = 0
private val internedStrings = mutableMapOf<Pair<String, Boolean>, List<String>>()
val internedStringsModuleName = "prog8_interned_strings"
init {
// insert a container module for all interned strings later
if(modules.firstOrNull()?.name != internedStringsModuleName) {
val internedStringsModule = Module(internedStringsModuleName, mutableListOf(), Position.DUMMY, false, Path.of(""))
modules.add(0, internedStringsModule)
val block = Block(internedStringsModuleName, null, mutableListOf(), false, Position.DUMMY)
internedStringsModule.statements.add(block)
internedStringsModule.linkParents(this)
internedStringsModule.program = this
}
}
fun entrypoint(): Subroutine? { fun entrypoint(): Subroutine? {
val mainBlocks = allBlocks().filter { it.name=="main" } val mainBlocks = allBlocks().filter { it.name=="main" }
@ -274,6 +294,22 @@ class Program(val name: String, val modules: MutableList<Module>, val builtinFun
} }
} }
fun internString(string: StringLiteralValue): List<String> {
val key = Pair(string.value, string.altEncoding)
val existing = internedStrings[key]
if(existing!=null)
return existing
val decl = VarDecl(VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, "string_${internedStrings.size}", null, string,
isArray = false, autogeneratedDontRemove = true, position = string.position)
val internedStringsBlock = modules.first { it.name==internedStringsModuleName }.statements.first { it is Block && it.name == internedStringsModuleName}
(internedStringsBlock as Block).statements.add(decl)
decl.linkParents(internedStringsBlock)
val scopedName = listOf(internedStringsModuleName, decl.name)
internedStrings[key] = scopedName
return scopedName
}
fun allBlocks(): List<Block> = modules.flatMap { it.statements.filterIsInstance<Block>() } fun allBlocks(): List<Block> = modules.flatMap { it.statements.filterIsInstance<Block>() }
override val position: Position = Position.DUMMY override val position: Position = Position.DUMMY

View File

@ -60,7 +60,13 @@ enum class DataType {
enum class CpuRegister { enum class CpuRegister {
A, A,
X, X,
Y Y;
fun asRegisterOrPair(): RegisterOrPair = when(this) {
A -> RegisterOrPair.A
X -> RegisterOrPair.X
Y -> RegisterOrPair.Y
}
} }
enum class RegisterOrPair { enum class RegisterOrPair {

View File

@ -6,13 +6,15 @@ import prog8.ast.base.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import java.util.* import java.util.Objects
import kotlin.math.abs import kotlin.math.abs
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=") val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
val comparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=") val comparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val augmentAssignmentOperators = setOf("+", "-", "/", "*", "**", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor") val augmentAssignmentOperators = setOf("+", "-", "/", "*", "**", "&", "|", "^", "<<", ">>", "%", "and", "or", "xor")
val logicalOperators = setOf("and", "or", "xor", "not")
sealed class Expression: Node { sealed class Expression: Node {
abstract fun constValue(program: Program): NumericLiteralValue? abstract fun constValue(program: Program): NumericLiteralValue?
@ -478,15 +480,11 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
} }
} }
private var heapIdSequence = 0 // unique ids for strings and arrays "on the heap"
class StringLiteralValue(val value: String, class StringLiteralValue(val value: String,
val altEncoding: Boolean, // such as: screencodes instead of Petscii for the C64 val altEncoding: Boolean, // such as: screencodes instead of Petscii for the C64
override val position: Position) : Expression() { override val position: Position) : Expression() {
override lateinit var parent: Node override lateinit var parent: Node
val heapId = ++heapIdSequence
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
} }
@ -516,8 +514,6 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
override val position: Position) : Expression() { override val position: Position) : Expression() {
override lateinit var parent: Node override lateinit var parent: Node
val heapId = ++heapIdSequence
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
value.forEach {it.linkParents(this)} value.forEach {it.linkParents(this)}
@ -546,6 +542,14 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
return type==other.type && value.contentEquals(other.value) return type==other.type && value.contentEquals(other.value)
} }
fun memsize(memsizer: IMemSizer): Int {
if(type.isKnown) {
val eltType = ArrayElementTypes.getValue(type.typeOrElse(DataType.STRUCT))
return memsizer.memorySize(eltType) * value.size
}
else throw IllegalArgumentException("array datatype is not yet known")
}
fun guessDatatype(program: Program): InferredTypes.InferredType { fun guessDatatype(program: Program): InferredTypes.InferredType {
// Educated guess of the desired array literal's datatype. // Educated guess of the desired array literal's datatype.
// If it's inside a for loop, assume the data type of the loop variable is what we want. // If it's inside a for loop, assume the data type of the loop variable is what we want.
@ -763,17 +767,6 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
fun memberOfStruct(program: Program) = this.targetVarDecl(program)?.struct fun memberOfStruct(program: Program) = this.targetVarDecl(program)?.struct
fun heapId(namespace: INameScope): Int {
val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
val value = (node as? VarDecl)?.value ?: throw FatalAstException("requires a reference value")
return when (value) {
is IdentifierReference -> value.heapId(namespace)
is StringLiteralValue -> value.heapId
is ArrayLiteralValue -> value.heapId
else -> throw FatalAstException("requires a reference value")
}
}
fun firstStructVarName(program: Program): String? { fun firstStructVarName(program: Program): String? {
// take the name of the first struct member of the structvariable instead // take the name of the first struct member of the structvariable instead
// if it's just a regular variable, return null. // if it's just a regular variable, return null.
@ -818,7 +811,7 @@ class FunctionCall(override var target: IdentifierReference,
// lenghts of arrays and strings are constants that are determined at compile time! // lenghts of arrays and strings are constants that are determined at compile time!
if(target.nameInSource.size>1) if(target.nameInSource.size>1)
return null return null
val resultValue: NumericLiteralValue? = program.builtinFunctions.constValue(target.nameInSource[0], args, position) val resultValue: NumericLiteralValue? = program.builtinFunctions.constValue(target.nameInSource[0], args, position, program.memsizer)
if(withDatatypeCheck) { if(withDatatypeCheck) {
val resultDt = this.inferType(program) val resultDt = this.inferType(program)
if(resultValue==null || resultDt istype resultValue.type) if(resultValue==null || resultDt istype resultValue.type)

View File

@ -176,12 +176,6 @@ open class VarDecl(val type: VarDeclType,
companion object { companion object {
private var autoHeapValueSequenceNumber = 0 private var autoHeapValueSequenceNumber = 0
fun createAuto(string: StringLiteralValue): VarDecl {
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
return VarDecl(VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, autoVarName, null, string,
isArray = false, autogeneratedDontRemove = true, position = string.position)
}
fun createAuto(array: ArrayLiteralValue): VarDecl { fun createAuto(array: ArrayLiteralValue): VarDecl {
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}" val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
val arrayDt = val arrayDt =
@ -227,14 +221,20 @@ open class VarDecl(val type: VarDeclType,
value?.linkParents(this) value?.linkParents(this)
if(structName!=null) { if(structName!=null) {
val structStmt = definingScope().lookup(listOf(structName), this) val structStmt = definingScope().lookup(listOf(structName), this)
if(structStmt!=null) if(structStmt!=null) {
struct = definingScope().lookup(listOf(structName), this) as StructDecl val node = definingScope().lookup(listOf(structName), this)
if(node is StructDecl)
struct = node
else
datatypeErrors.add(SyntaxError("invalid datatype declaration", position))
}
} }
} }
override fun replaceChildNode(node: Node, replacement: Node) { override fun replaceChildNode(node: Node, replacement: Node) {
// 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 && node===value)
require(replacement is Expression) // NOTE: ideally you also want to check that node===value but this sometimes crashes the optimizer when queueing multiple node replacements
// just accept the risk of having the wrong node specified in the IAstModification object...
value = replacement value = replacement
replacement.parent = this replacement.parent = this
} }
@ -280,46 +280,19 @@ 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 origExpression: Expression?, // will be replaced later by either the number or the identifier class ArrayIndex(var indexExpr: Expression,
override val position: Position) : Node { 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
origExpression?.linkParents(this) indexExpr.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) require(replacement is Expression)
when { if (node===indexExpr) indexExpr = replacement
node===origExpression -> origExpression = replacement else throw FatalAstException("invalid replace")
node===indexVar -> {
when (replacement) {
is NumericLiteralValue -> {
indexVar = null
indexNum = replacement
}
is IdentifierReference -> {
indexVar = replacement
indexNum = null
}
else -> {
throw FatalAstException("invalid replace")
}
}
}
else -> throw FatalAstException("invalid replace")
}
} }
companion object { companion object {
@ -329,29 +302,16 @@ class ArrayIndex(var origExpression: Expression?, // will be replaced
} }
} }
fun accept(visitor: IAstVisitor) { fun accept(visitor: IAstVisitor) = indexExpr.accept(visitor)
origExpression?.accept(visitor) fun accept(visitor: AstWalker, parent: Node) = indexExpr.accept(visitor, this)
indexNum?.accept(visitor)
indexVar?.accept(visitor)
}
fun accept(visitor: AstWalker, parent: Node) {
origExpression?.accept(visitor, this)
indexNum?.accept(visitor, this)
indexVar?.accept(visitor, this)
}
override fun toString(): String { override fun toString(): String {
return("ArrayIndex($indexNum, $indexVar, pos=$position)") return("ArrayIndex($indexExpr, pos=$position)")
} }
fun constIndex() = indexNum?.number?.toInt() fun constIndex() = (indexExpr as? NumericLiteralValue)?.number?.toInt()
infix fun isSameAs(other: ArrayIndex): Boolean { infix fun isSameAs(other: ArrayIndex): Boolean = indexExpr isSameAs other.indexExpr
return if(indexNum!=null || indexVar!=null)
indexNum==other.indexNum && indexVar == other.indexVar
else
other.origExpression!=null && origExpression!! isSameAs other.origExpression!!
}
} }
open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() { open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() {
@ -651,14 +611,11 @@ class AsmGenInfo {
// Conceptually it should be part of any INameScope. // Conceptually it should be part of any INameScope.
// But because the resulting code only creates "real" scopes on a subroutine level, // But because the resulting code only creates "real" scopes on a subroutine level,
// it's more consistent to only define these attributes on a Subroutine node. // it's more consistent to only define these attributes on a Subroutine node.
var usedAutoArrayIndexerForStatements = mutableListOf<ArrayIndexerInfo>()
var usedRegsaveA = false var usedRegsaveA = false
var usedRegsaveX = false var usedRegsaveX = false
var usedRegsaveY = false var usedRegsaveY = false
var usedFloatEvalResultVar1 = false var usedFloatEvalResultVar1 = false
var usedFloatEvalResultVar2 = false var usedFloatEvalResultVar2 = false
class ArrayIndexerInfo(val name: String, val replaces: ArrayIndex)
} }
// the subroutine class covers both the normal user-defined subroutines, // the subroutine class covers both the normal user-defined subroutines,
@ -734,7 +691,7 @@ class Subroutine(override val name: String,
.asSequence() .asSequence()
.filter { it is InlineAssembly } .filter { it is InlineAssembly }
.map { (it as InlineAssembly).assembly } .map { (it as InlineAssembly).assembly }
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it } .count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it || " bra" in it || "\tbra" in it}
} }
@ -1006,6 +963,9 @@ class StructDecl(override val name: String,
val numberOfElements: Int val numberOfElements: Int
get() = this.statements.size get() = this.statements.size
fun memsize(memsizer: IMemSizer) =
statements.map { memsizer.memorySize((it as VarDecl).datatype) }.sum()
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)

View File

@ -56,7 +56,7 @@ interface IAstModification {
} }
} }
class ReplaceNode(private val node: Node, private val replacement: Node, private val parent: Node) : class ReplaceNode(val node: Node, private val replacement: Node, private val parent: Node) :
IAstModification { IAstModification {
override fun perform() { override fun perform() {
parent.replaceChildNode(node, replacement) parent.replaceChildNode(node, replacement)
@ -158,9 +158,19 @@ abstract class AstWalker {
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = emptyList() open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = emptyList()
private val modifications = mutableListOf<Triple<IAstModification, Node, Node>>() private val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
// private val modificationsReplacedNodes = mutableSetOf<Pair<Node, Position>>()
private fun track(mods: Iterable<IAstModification>, node: Node, parent: Node) { private fun track(mods: Iterable<IAstModification>, node: Node, parent: Node) {
for (it in mods) modifications += Triple(it, node, parent) for (it in mods) {
// if(it is IAstModification.ReplaceNode) {
// val replaceKey = Pair(it.node, it.node.position)
// if(replaceKey in modificationsReplacedNodes)
// throw FatalAstException("there already is a node replacement for $replaceKey - optimizer can't deal with multiple replacements for same node yet. Split the ast modification?")
// else
// modificationsReplacedNodes.add(replaceKey)
// }
modifications += Triple(it, node, parent)
}
} }
fun applyModifications(): Int { fun applyModifications(): Int {
@ -169,6 +179,7 @@ abstract class AstWalker {
} }
val amount = modifications.size val amount = modifications.size
modifications.clear() modifications.clear()
// modificationsReplacedNodes.clear()
return amount return amount
} }

View File

@ -5,7 +5,7 @@
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/build" />
</content> </content>
<orderEntry type="jdk" jdkName="Python 3.8 virtualenv" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3.9" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
</module> </module>

View File

@ -20,7 +20,7 @@ import os
# -- Project information ----------------------------------------------------- # -- Project information -----------------------------------------------------
project = 'Prog8' project = 'Prog8'
copyright = '2019, Irmen de Jong' copyright = '2021, Irmen de Jong'
author = 'Irmen de Jong' author = 'Irmen de Jong'

View File

@ -50,14 +50,15 @@ Language features
- Provide high level programming constructs but at the same time stay close to the metal; - Provide high level programming constructs but at the same time stay close to the metal;
still able to directly use memory addresses and ROM subroutines, still able to directly use memory addresses and ROM subroutines,
and inline assembly to have full control when every register, cycle or byte matters and inline assembly to have full control when every register, cycle or byte matters
- Arbitrary number of subroutine parameters, Complex nested expressions are possible - Subroutines with parameters and return values
- No stack frame allocations because parameters and local variables are automatically allocated statically - complex nested expressions are possible
- Variables are allocated statically
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters - Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
- Variable data types include signed and unsigned bytes and words, arrays, strings and floats. - Variable data types include signed and unsigned bytes and words, arrays, strings and floats.
- High-level code optimizations, such as const-folding, expression and statement simplifications/rewriting. - High-level code optimizations, such as const-folding, expression and statement simplifications/rewriting.
- Many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse`` - Many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
- Supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, also on the C64. - Supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, also on the C64.
- 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 kernal and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
Code example Code example

View File

@ -105,7 +105,7 @@ conv
Routines to convert strings to numbers or vice versa. Routines to convert strings to numbers or vice versa.
- numbers to strings, in various formats (binary, hex, decimal) - numbers to strings, in various formats (binary, hex, decimal)
- strings in decimal, hex and binary format into numbers - strings in decimal, hex and binary format into numbers (bytes, words)
textio (txt.*) textio (txt.*)
@ -182,7 +182,7 @@ Provides string manipulation routines.
floats floats
------ ------
Provides definitions for the ROM/kernel subroutines and utility routines dealing with floating Provides definitions for the ROM/kernal subroutines and utility routines dealing with floating
point variables. This includes ``print_f``, the routine used to print floating point numbers. point variables. This includes ``print_f``, the routine used to print floating point numbers.

View File

@ -522,12 +522,12 @@ Use a ``when`` statement if you have a set of fixed choices that each should res
action. It is possible to combine several choices to result in the same action:: action. It is possible to combine several choices to result in the same action::
when value { when value {
4 -> c64scr.print("four") 4 -> txt.print("four")
5 -> c64scr.print("five") 5 -> txt.print("five")
10,20,30 -> { 10,20,30 -> {
c64scr.print("ten or twenty or thirty") txt.print("ten or twenty or thirty")
} }
else -> c64scr.print("don't know") else -> txt.print("don't know")
} }
The when-*value* can be any expression but the choice values have to evaluate to The when-*value* can be any expression but the choice values have to evaluate to
@ -739,9 +739,11 @@ sin16(x)
sqrt16(w) sqrt16(w)
16 bit unsigned integer Square root. Result is unsigned byte. 16 bit unsigned integer Square root. Result is unsigned byte.
To do the reverse, squaring an integer, just write ``x*x``.
sqrt(x) sqrt(x)
Floating point Square root. Floating point Square root.
To do the reverse, squaring a floating point number, just write ``x*x`` or ``x**2``.
tan(x) tan(x)
Tangent. Tangent.
@ -757,11 +759,12 @@ all(x)
1 ('true') if all of the values in the array value x are 'true' (not zero), else 0 ('false') 1 ('true') if all of the values in the array value x are 'true' (not zero), else 0 ('false')
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 0-byte).
Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte. See sizeof(). Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte. See sizeof().
Note: lengths of strings and arrays are determined at compile-time! If your program modifies the actual Note: lengths of strings and arrays are determined at compile-time! If your program modifies the actual
length of the string during execution, the value of len(string) may no longer be correct! length of the string during execution, the value of len(s) may no longer be correct!
(use strlen function if you want to dynamically determine the length) (use the ``string.length`` routine if you want to dynamically determine the length by counting to the
first 0-byte)
max(x) max(x)
Maximum of the values in the array value x Maximum of the values in the array value x
@ -788,6 +791,12 @@ sort(array)
Miscellaneous Miscellaneous
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
cmp(x,y)
Compare the integer value x to integer value y. Doesn't return a value or boolean result, only sets the processor's status bits!
You can use a conditional jumps (``if_cc`` etcetera) to act on this.
Normally you should just use a comparison expression (``x < y``)
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".
@ -824,6 +833,9 @@ rndw()
rndf() rndf()
returns a pseudo-random float between 0.0 and 1.0 returns a pseudo-random float between 0.0 and 1.0
fastrnd8()
returns a pseudo-random byte from 0..255 (using a fast but not very good rng)
rol(x) rol(x)
Rotate the bits in x (byte or word) one position to the left. Rotate the bits in x (byte or word) one position to the left.
This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag, This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag,

View File

@ -70,7 +70,7 @@ Directives
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
to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this) to perform a system reset. (A ``system_reset`` subroutine is available in the syslib to help you do this)
- style ``floatsafe`` -- like the previous one but also reserves the addresses that - style ``floatsafe`` -- like the previous one but also reserves the addresses that
are required to perform floating point operations (from the BASIC kernel). No clean exit is possible. are required to perform floating point operations (from the BASIC kernal). No clean exit is possible.
- style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't - style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't
touch change anything else. This allows full use of BASIC and KERNAL ROM routines including default IRQs touch change anything else. This allows full use of BASIC and KERNAL ROM routines including default IRQs
during normal system operation. during normal system operation.
@ -120,17 +120,18 @@ Directives
Level: module, block. Level: module, block.
Sets special compiler options. Sets special compiler options.
- For a module option, there is ``enable_floats``, which will tell the compiler - ``enable_floats`` (module level) tells the compiler
to deal with floating point numbers (by using various subroutines from the Commodore-64 kernal). to deal with floating point numbers (by using various subroutines from the Commodore-64 kernal).
Otherwise, floating point support is not enabled. Normally you don't have to use this yourself as Otherwise, floating point support is not enabled. Normally you don't have to use this yourself as
importing the ``floats`` library is required anyway and that will enable it for you automatically. importing the ``floats`` library is required anyway and that will enable it for you automatically.
- There's also ``no_sysinit`` which cause the resulting program to *not* include - ``no_sysinit`` (module level) which cause the resulting program to *not* include
the system re-initialization logic of clearing the screen, resetting I/O config etc. You'll have to the system re-initialization logic of clearing the screen, resetting I/O config etc. You'll have to
take care of that yourself. The program will just start running from whatever state the machine is in when the take care of that yourself. The program will just start running from whatever state the machine is in when the
program was launched. program was launched.
- When used in a block with the ``force_output`` option, it will force the block to be outputted - ``force_output`` (in a block) will force the block to be outputted in the final program.
in the final program. Can be useful to make sure some Can be useful to make sure some data is generated that would otherwise be discarded because it's not referenced (such as sprite data).
data is generated that would otherwise be discarded because it's not referenced (such as sprite data). - ``align_word`` (in a block) will make the assembler align the start address of this block on a word boundary in memory (so, an even memory address).
- ``align_page`` (in a block) will make the assembler align the start address of this block on a page boundary in memory (so, the LSB of the address is 0).
.. data:: %asmbinary "<filename>" [, <offset>[, <length>]] .. data:: %asmbinary "<filename>" [, <offset>[, <length>]]
@ -513,7 +514,7 @@ 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 kernal ROM) can return more than one return value.
For example a status in the carry bit and a number in A, or a 16-bit value in A/Y registers. For example a status in the carry bit and a number in A, or a 16-bit value in A/Y registers.
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.
@ -591,7 +592,7 @@ flag such as Carry (Pc).
Asmsubs can also be tagged as ``inline asmsub`` to make trivial pieces of assembly inserted Asmsubs can also be tagged as ``inline asmsub`` to make trivial pieces of assembly inserted
directly instead of a call to them. Note that it is literal copy-paste of code that is done, directly instead of a call to them. Note that it is literal copy-paste of code that is done,
so make sure the assembly is actually written to behave like such - which probably means you so make sure the assembly is actually written to behave like such - which probably means you
don't want a ``rts`` or ``jmp`` in it! don't want a ``rts`` or ``jmp`` or ``bra`` in it!
.. note:: .. note::
@ -772,11 +773,11 @@ Choices can result in a single statement or a block of multiple statements in w
case you have to use { } to enclose them:: case you have to use { } to enclose them::
when value { when value {
4 -> c64scr.print("four") 4 -> txt.print("four")
5 -> c64scr.print("five") 5 -> txt.print("five")
10,20,30 -> { 10,20,30 -> {
c64scr.print("ten or twenty or thirty") txt.print("ten or twenty or thirty")
} }
else -> c64scr.print("don't know") else -> txt.print("don't know")
} }

View File

@ -17,7 +17,7 @@ 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.
.. hint:: .. hint::
If you only use standard kernel and prog8 library routines, If you only use standard kernal and prog8 library routines,
it is possible to compile the *exact same program* for both machines (just change the compiler target flag)! it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
@ -139,26 +139,17 @@ IRQ Handling
============ ============
Normally, the system's default IRQ handling is not interfered with. Normally, the system's default IRQ handling is not interfered with.
You can however install your own IRQ handler. You can however install your own IRQ handler (for clean separation, it is advised to define it inside its own block).
This is possible ofcourse by doing it all using customized inline assembly, There are a few library routines available to make setting up C-64 60hz IRQs and Raster IRQs a lot easier (no assembly code required).
but there are a few library routines available to make setting up C-64 IRQs and raster IRQs a lot easier (no assembly code required).
For the C64 these routines are:: For the C64 these routines are::
c64.set_irqvec() c64.set_irq(uword handler_address, boolean useKernal)
c64.set_irqvec_excl() c64.set_rasterirq(uword handler_address, uword rasterline, boolean useKernal)
c64.restore_irq() ; set everything back to the systems default irq handler
c64.set_rasterirq( <raster line> ) And for the Commander X16:
c64.set_rasterirq_excl( <raster line> )
c64.restore_irqvec() ; set it back to the systems default irq handler
If you activate an IRQ handler with one of these, it expects the handler to be defined
as a subroutine ``irq`` in the module ``irq`` so like this::
irq {
sub irq() {
; ... irq handling here ...
}
}
cx16.set_irq(uword handler_address, boolean useKernal) ; vsync irq
cx16.set_rasterirq(uword handler_address, uword rasterline) ; note: disables kernal irq handler! sys.wait() won't work anymore
cx16.restore_irq() ; set everything back to the systems default irq handler

View File

@ -50,7 +50,7 @@ Calling the routine is just a simple JSR instruction, but the other two work lik
``asmsub`` routines ``asmsub`` routines
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
These are usually declarations of kernel (ROM) routines or low-level assembly only routines, These are usually declarations of kernal (ROM) routines or low-level assembly only routines,
that have their arguments solely passed into specific registers. that have their arguments solely passed into specific registers.
Sometimes even via a processor status flag such as the Carry flag. Sometimes even via a processor status flag such as the Carry flag.
Return values also via designated registers. Return values also via designated registers.

View File

@ -2,21 +2,19 @@
TODO TODO
==== ====
- optimize several inner loops in gfx2
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
- optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
- add a flood fill routine to gfx2?
- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color) ?
- add a f_seek() routine for the Cx16 that uses its seek dos api?
- refactor the asmgen into their own submodule? - refactor the asmgen into their own submodule?
- refactor the compiler optimizers into their own submodule? - refactor the compiler optimizers into their own submodule?
- optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
- optimize several inner loops in gfx2 (highres 4 color mode)
- use the 65c02 bit clear/set/test instructions for single-bit operations
- try to fix the bresenham line routines in graphics and gfx2 (sometimes they're a pixel 'off')
- add a flood fill routine to gfx2?
- add sound to the cx16 tehtriz
- add a f_seek() routine for the Cx16 that uses its seek dos api?
- optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation - optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation
- add a compiler option to not remove unused subroutines. this allows for building library programs - add a compiler option to not remove unused subroutines. this allows for building library programs. But this won't work with 64tass's .proc ...
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine) - make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``v_``
- 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)
- c64: make the graphics.BITMAP_ADDRESS configurable - c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
- some support for recursive subroutines? - some support for recursive subroutines?
- via %option recursive?: allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters - via %option recursive?: allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters
- Or via a special recursive call operation that copies the current values of all local vars (including arguments) to the stack, replaces the arguments, jsr subroutine, and after returning copy the stack back to the local variables - Or via a special recursive call operation that copies the current values of all local vars (including arguments) to the stack, replaces the arguments, jsr subroutine, and after returning copy the stack back to the local variables
@ -28,30 +26,12 @@ More optimizations
Add more compiler optimizations to the existing ones. Add more compiler optimizations to the existing ones.
- further optimize assignment codegeneration, such as the following: - further optimize assignment codegeneration, such as the following:
- rewrite expression code generator to not use eval stack but a fixed number of predetermined value 'variables' (1 per nesting level?)
- binexpr splitting (beware self-referencing expressions and asm code ballooning though) - binexpr splitting (beware self-referencing expressions and asm code ballooning though)
- 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
Eval stack redesign? (lot of work)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The eval stack is now a split lsb/msb stack using X as the stackpointer.
Is it easier/faster to just use a single page unsplit stack?
It could then even be moved into the zeropage to reduce code size and slowness.
Or just move the LSB portion into a slab of the zeropage.
Allocate a fixed word in ZP that is the Top Of Stack value so we can always operate on TOS directly
without having to index with X into the eval stack all the time?
This could GREATLY improve code size and speed for operations that work on just a single value.
Bug Fixing
^^^^^^^^^^
Ofcourse there are always bugs to fix ;)
Misc Misc
^^^^ ^^^^

144
examples/animals.p8 Normal file
View File

@ -0,0 +1,144 @@
%import textio
%import string
; Animal guessing game where the computer gets smarter every time.
; Note: this program is compatible with C64 and CX16.
main {
const ubyte database_size = 100
uword animal_names_buf
uword questions_buf
uword animal_names_ptr
uword questions_ptr
uword[database_size] animals
uword[database_size] questions
uword[database_size] answers_questions
uword[database_size] answers_animals
ubyte new_animal_number
ubyte new_question_number
str userinput = "x"*80
sub start() {
; initialize the database
animal_names_buf = memory("animalnames", 500)
questions_buf = memory("questions", 2000)
animal_names_ptr = animal_names_buf
questions_ptr = questions_buf
animals[0] = 0
animals[1] = "dolphin"
animals[2] = "eagle"
animals[3] = "horse"
new_animal_number = 4
questions[0] = 0
questions[1] = "does it swim"
questions[2] = "can it fly"
new_question_number = 3
answers_questions[0] = mkword(0, 0)
answers_questions[1] = mkword(0, 2)
answers_questions[2] = mkword(0, 0)
answers_animals[0] = mkword(0, 0)
answers_animals[1] = mkword(1, 0)
answers_animals[2] = mkword(2, 3)
; play the game
game()
}
sub game() {
repeat {
ubyte current_question = 1
txt.print("\n\nanimal guessing game!\nthink of an animal.\n")
ubyte guessed = false
while not guessed {
txt.print(questions[current_question])
txt.print("? ")
if txt.input_chars(userinput) {
txt.nl()
ubyte animal_number
if userinput[0]=='y' {
animal_number = msb(answers_animals[current_question])
if animal_number {
guess(current_question, true, animal_number)
guessed = true
} else {
current_question = msb(answers_questions[current_question])
}
}
else if userinput[0]=='n' {
animal_number = lsb(answers_animals[current_question])
if animal_number {
guess(current_question, false, animal_number)
guessed = true
} else {
current_question = lsb(answers_questions[current_question])
}
}
else {
txt.print("answer (y)es or (n)o please.\n")
}
} else
txt.nl()
}
}
}
sub guess(ubyte question_number, ubyte given_answer_yesno, ubyte animal_number) {
txt.print("is it a ")
txt.print(animals[animal_number])
txt.print("? ")
txt.input_chars(userinput)
if userinput[0] == 'y' {
txt.print("\n\nsee, i knew it!\n")
return
}
str name = "x"*30
txt.print("\n\ni give up. what is it? ")
txt.input_chars(name)
txt.print("\nwhat yes-no question would best articulate the difference\nbetween a ")
txt.print(animals[animal_number])
txt.print(" and a ")
txt.print(name)
txt.print("? ")
txt.input_chars(userinput)
txt.print("\nfor a ")
txt.print(name)
txt.print(", what is the answer to that; yes or no? ")
str answer = "x"*10
txt.input_chars(answer)
animals[new_animal_number] = animal_names_ptr
questions[new_question_number] = questions_ptr
animal_names_ptr += string.copy(name, animal_names_ptr)+1 ; store animal name in buffer
questions_ptr += string.copy(userinput, questions_ptr)+1 ; store question in buffer
answers_questions[new_question_number] = mkword(0, 0)
if answer[0]=='y'
answers_animals[new_question_number] = mkword(new_animal_number, animal_number)
else
answers_animals[new_question_number] = mkword(animal_number, new_animal_number)
uword previous_animals = answers_animals[question_number]
uword previous_questions = answers_questions[question_number]
if given_answer_yesno {
answers_animals[question_number] = mkword(0, lsb(previous_animals))
answers_questions[question_number] = mkword(new_question_number, lsb(previous_questions))
} else {
answers_animals[question_number] = mkword(msb(previous_animals), 0)
answers_questions[question_number] = mkword(msb(previous_questions), new_question_number)
}
new_animal_number++
new_question_number++
txt.print("\n\nthanks, i know more animals now! let's try again.\n")
}
}

View File

@ -23,7 +23,7 @@ main {
c64.SCROLX &= %11110111 ; 38 column mode c64.SCROLX &= %11110111 ; 38 column mode
c64.set_rasterirq(200) ; enable animation c64.set_rasterirq(&irq.irq, 200, false) ; enable animation via raster interrupt
ubyte target_height = 10 ubyte target_height = 10
ubyte active_height = 24 ubyte active_height = 24
@ -42,7 +42,7 @@ main {
active_height-- active_height--
upwards = false upwards = false
} else { } else {
target_height = 8 + rnd() % 16 target_height = 8 + fastrnd8() % 16
if upwards if upwards
mountain = 233 mountain = 233
else else
@ -57,7 +57,7 @@ main {
txt.scroll_left(true) txt.scroll_left(true)
; float the balloon ; float the balloon
if rnd() & %10000 if fastrnd8() & %10000
c64.SPXY[1] ++ c64.SPXY[1] ++
else else
c64.SPXY[1] -- c64.SPXY[1] --
@ -71,10 +71,10 @@ main {
txt.setcc(39, yy, 160, 8) ; draw mountain txt.setcc(39, yy, 160, 8) ; draw mountain
} }
yy = rnd() yy = fastrnd8()
if yy > 100 { if yy > 100 {
; draw a star ; draw a star
txt.setcc(39, yy % (active_height-1), '.', rnd()) txt.setcc(39, yy % (active_height-1), '.', fastrnd8())
} }
if yy > 200 { if yy > 200 {
@ -85,7 +85,7 @@ main {
tree = 88 tree = 88
else if yy & %00100000 != 0 else if yy & %00100000 != 0
tree = 65 tree = 65
if rnd() > 130 if fastrnd8() > 130
treecolor = 13 treecolor = 13
txt.setcc(39, active_height, tree, treecolor) txt.setcc(39, active_height, tree, treecolor)
} }

View File

@ -24,12 +24,12 @@ main {
; Setup Starting Ball Positions ; Setup Starting Ball Positions
ubyte lp ubyte lp
for lp in 0 to ballCount-1 { for lp in 0 to ballCount-1 {
BX[lp] = rnd() % txt.DEFAULT_WIDTH BX[lp] = fastrnd8() % txt.DEFAULT_WIDTH
BY[lp] = rnd() % txt.DEFAULT_HEIGHT BY[lp] = fastrnd8() % txt.DEFAULT_HEIGHT
BC[lp] = rnd() & 15 BC[lp] = fastrnd8() & 15
DX[lp] = rnd() & 1 DX[lp] = fastrnd8() & 1
DY[lp] = rnd() & 1 DY[lp] = fastrnd8() & 1
void rnd() void fastrnd8()
} }
; start clock ; start clock

Some files were not shown because too many files have changed in this diff Show More