Compare commits

...

289 Commits

Author SHA1 Message Date
Irmen de Jong
231b50dacb optimize certain word multiplication with bit shifting if it's a power of 2 2025-09-18 20:57:26 +02:00
Irmen de Jong
a71895cbe8 optimize pointer.field += 1 into pointer.field INC/DEC 2025-09-18 19:27:36 +02:00
Irmen de Jong
a8bede17b2 fix defer() with the arena allocator ("return values are evaluated before the defer is executed") 2025-09-17 23:59:32 +02:00
Irmen de Jong
f6c8e693a5 add offsetof() 2025-09-17 23:30:15 +02:00
Irmen de Jong
9461e4088c fixed the hashtable example and workarounds for misbehaving defer in allocators 2025-09-17 22:53:01 +02:00
Irmen de Jong
efd73fd10d implement some more in place pointer operators 2025-09-17 18:19:03 +02:00
Irmen de Jong
ddf6e84a1a fix type checks for wrong pointer types in pointer array initalizer and assignment 2025-09-16 21:18:29 +02:00
Irmen de Jong
633d6c34e2 add support for struct pointers and short-form initializers in arrays and assignments 2025-09-16 20:37:50 +02:00
Irmen de Jong
6e7fbc6683 fix crash when invalid struct name is used in pointer decl 2025-09-15 20:39:21 +02:00
Irmen de Jong
124ea1230b fix vm to understand struct instances in arrays 2025-09-14 19:04:01 +02:00
Irmen de Jong
8b48a295b6 allow struct initializers to occur in array literals 2025-09-14 18:16:46 +02:00
Irmen de Jong
d285d37fdb fix clobbers syntax in symboldumper 2025-09-13 21:47:52 +02:00
Irmen de Jong
8bb927b483 fix compiler crash with on..call statement in nested scope 2025-09-13 06:44:49 +02:00
Irmen de Jong
1af4cd0d63 fix struct initializer error checking 2025-09-12 19:55:12 +02:00
Irmen de Jong
db2f28c4cd add struct and pointer benchmark to benchmark program (btree, subscore=654, total 7420)
fix nullpointer in array initalizer
2025-09-12 17:09:44 +02:00
Irmen de Jong
5d9fbd2ccc fix doc build 2025-09-11 22:53:57 +02:00
Irmen de Jong
7efc709538 release 12.0 BETA 1 2025-09-11 22:50:34 +02:00
Irmen de Jong
79419a98d0 add if-expression versions for the conditionals if_cc, if_cs, if_vc etc 2025-09-11 01:57:30 +02:00
Irmen de Jong
1c77d5d5e7 avoid creating inferredtype instances all the time 2025-09-10 20:43:15 +02:00
Irmen de Jong
c6854e22a3 fix wrong operand order for in place pointer subtraction 2025-09-10 17:19:03 +02:00
Irmen de Jong
83acc2f285 fix pointer variable overwriting when used in expression 2025-09-10 16:40:45 +02:00
Irmen de Jong
6de95f7a3b new static struct initializer syntax ^^Node : [1,2,3]
avoids confusion with function calls
2025-09-10 13:47:12 +02:00
Irmen de Jong
82839a2d82 fix alias error and byte to word assignment error 2025-09-09 15:05:02 +02:00
Irmen de Jong
e5fc9b3132 fix 6502 pointer arithmetic optimization that led to increased code size 2025-09-09 14:06:41 +02:00
Irmen de Jong
ced4c5944a fix broken optimization for wordvar - value expressions 2025-09-09 08:05:51 +02:00
Irmen de Jong
d4c460072b don't place library and subroutine parameter pointers in ZP automatically, to save ZP space 2025-09-09 06:25:40 +02:00
Irmen de Jong
0b9384b556 fix typecasted byte-to-word pointer assignment, some more asm optimizations 2025-09-09 05:56:55 +02:00
Irmen de Jong
6c3277e3e3 fix more alias bugs 2025-09-09 02:28:05 +02:00
Irmen de Jong
d9ff1eb38a fix internal error in on..call 2025-09-08 19:30:32 +02:00
Irmen de Jong
e178097735 support more struct instance assignments via memcopy() 2025-09-08 13:18:50 +02:00
Irmen de Jong
a1ab8ed208 fixing alias bugs 2025-09-07 22:52:13 +02:00
Irmen de Jong
6ababbf8f4 support more struct instance assignments via memcopy() 2025-09-07 00:29:18 +02:00
Irmen de Jong
79629befc1 fix pointer arithmetic error in struct instance assignments (that get turned into a memcopy call) 2025-09-06 00:28:59 +02:00
Irmen de Jong
8022c0772a oops 2025-09-05 20:37:19 +02:00
Irmen de Jong
8ad2b4638b fix and optimize nodes[i].field (it could clobber the actual temporary pointer value in a scratch register) 2025-09-05 20:02:37 +02:00
Irmen de Jong
3a0392df8a there's a problem with the struct initializer syntax 2025-09-05 00:18:42 +02:00
Irmen de Jong
beb28b061d fix non-struct pointer deref code bug
fix gradle 9 compatibility issue (fixes #175)
2025-09-04 21:30:20 +02:00
Irmen de Jong
c39acc5031 fix mistakes 2025-09-04 00:30:00 +02:00
Irmen de Jong
f54a29c415 small 6502 pointer arithmetic expression rewrite that produces smaller code 2025-09-03 23:54:10 +02:00
Irmen de Jong
783b111059 pointer to uword casts should not be removed
optimized addUnsignedByteOrWordToAY a tiny bit (but needs more)
2025-09-03 23:09:11 +02:00
Irmen de Jong
fb286f8b54 optimize operatorDereference() to not always use a zp scratch pointer 2025-09-03 20:30:10 +02:00
Irmen de Jong
5999110e3f avoid using temporary pointer to access struct fields if a simple Y indexed cpu instruction can be used 2025-09-03 18:37:56 +02:00
Irmen de Jong
52a757ea78 todo 2025-09-02 00:42:46 +02:00
Irmen de Jong
28df08eea8 introduce P8ZP_SCRATCH_PTR temporary zp pointer to avoid clobbering W1,W2 2025-09-01 23:56:31 +02:00
Irmen de Jong
79505308ba optimize indexed pointer code a bit more 2025-09-01 22:18:17 +02:00
Irmen de Jong
a7e9d8e14b fix struct field offset calculations 2025-09-01 18:57:02 +02:00
Irmen de Jong
08f3abe5bf prefer to put pointer variables into zeropage (add implicit @zp) 2025-08-31 17:06:14 +02:00
Irmen de Jong
3ef09d7d9a use LOADFIELD instruction more instead of an extra explicit ADD
add implicit @zp to pointer variables if they don't have a preference
2025-08-31 15:53:44 +02:00
Irmen de Jong
a9142b9ce5 slightly optimize certain pointer indexing calculation, fix invalid deref optimization 2025-08-31 13:40:28 +02:00
Irmen de Jong
13e6f64d3b implement missing struct pointer indexing codegen 2025-08-31 12:20:38 +02:00
Irmen de Jong
4a1256c772 optimizing some pointer codegen for indexed derefs 2025-08-30 21:20:14 +02:00
Irmen de Jong
ff9bec90ec fix struct pointer array indexing field dereferencing
the pointers/sorting example now actually works on the 6502 too
2025-08-30 12:24:30 +02:00
Irmen de Jong
a6fee1e510 fixing deref() 2025-08-30 07:04:01 +02:00
Irmen de Jong
80538f101e Merge branch 'master' into structs6502 2025-08-29 21:42:03 +02:00
Irmen de Jong
aee53b14c7 avoid costly multiplications to get indexed pointer address 2025-08-29 20:18:26 +02:00
Irmen de Jong
5eb2fc8d86 fix a bad pointer arithmetic optimization 2025-08-29 19:36:57 +02:00
Irmen de Jong
98f91bbf88 another attempt to get correct docs chapters 2025-08-29 02:50:09 +02:00
Irmen de Jong
b48b36ef18 another attempt to get correct docs chapters 2025-08-29 00:52:29 +02:00
Irmen de Jong
221a093e5f optimize some pointer arithmetic, fix pointer arithmetic for ptr-value 2025-08-28 23:08:06 +02:00
Irmen de Jong
2ca1820d4e Merge branch 'master' into structs6502 2025-08-28 21:55:46 +02:00
Irmen de Jong
d58737d5be use RST chapter notation to attempt to fix PDF chapters 2025-08-28 21:46:09 +02:00
Irmen de Jong
b52cee3154 use RST chapter notation to attempt to fix PDF chapters 2025-08-28 21:19:16 +02:00
Irmen de Jong
9a76941e10 fix invalid optimization for ptr-value 2025-08-28 20:13:02 +02:00
Irmen de Jong
0285a4cce1 avoid needless pointer arithmetic multiplication by struct size 1 2025-08-27 21:50:09 +02:00
Irmen de Jong
5a3aa1bd25 optimized IR to return a constant value: use RETURNI 2025-08-26 22:54:11 +02:00
Irmen de Jong
0f79351de9 support word size indexing on typed pointers 2025-08-25 22:58:48 +02:00
Irmen de Jong
3cdb25ce8e fix invalid string comparisons being generated for regular pointer value comparison 2025-08-24 16:19:42 +02:00
Irmen de Jong
b7193bd0c6 update examples to use typed pointers where appropriate
(found 2 regressions that still need to be handled)
2025-08-24 15:17:47 +02:00
Irmen de Jong
10ff6a0095 Merge branch 'master' into structs6502 2025-08-24 14:34:15 +02:00
Irmen de Jong
d30f58004e converting untyped to typed pointers in custom target libraries 2025-08-24 14:27:25 +02:00
Irmen de Jong
17bdb22e4f converting untyped to typed pointers in c64,c128,pet32 libraries 2025-08-24 14:21:51 +02:00
Irmen de Jong
a68209be37 converting untyped to typed pointers in cx16 libraries 2025-08-24 14:13:02 +02:00
Irmen de Jong
7b40eade44 added txt.rvs_on() and txt.rvs_off(), added txt.color() support for virtual target 2025-08-24 13:29:29 +02:00
Irmen de Jong
8a717c74b9 sorting lib symboldumps 2025-08-24 13:07:10 +02:00
Irmen de Jong
aa72ded21e add sorting module to symboldump, moved shared cbm textio routines to shared module 2025-08-24 13:05:38 +02:00
Irmen de Jong
8e53c83844 optimize certain ptr+value expression on 6502 2025-08-24 05:46:49 +02:00
Irmen de Jong
e2a2db1256 converting untyped to typed pointers in libraries 2025-08-22 22:47:19 +02:00
markjreed
2afd2d4dae Make PET PLOT routine more efficient (#174)
* feat: take advantage of KERNAL vars to implement PLOT

* fix: tabs

* fix: update TBLX too, so cursor change sticks after output

* fix: formatting
2025-08-22 21:28:21 +02:00
Irmen de Jong
fad17cc094 support a few other simple cases of struct instance assignment 2025-08-21 00:04:30 +02:00
Irmen de Jong
f93d957999 proper warning for struct instance assignment (not yet supported) 2025-08-20 20:35:29 +02:00
Irmen de Jong
8db0344cee Merge branch 'master' into structs6502
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt
2025-08-19 23:10:15 +02:00
Irmen de Jong
32e531f951 test 2025-08-19 23:09:33 +02:00
Irmen de Jong
a4769702f9 todo 2025-08-19 23:06:04 +02:00
Irmen de Jong
cf19fb8df1 fix 6502 struct type and field name prefixing and datatype references to stale structs from stale Symboltable 2025-08-19 20:47:37 +02:00
Irmen de Jong
79b8bb5c9f Merge branch 'master' into structs6502 2025-08-19 01:12:50 +02:00
Irmen de Jong
fc5889ec0b kotlin 2.2.10, kotest 2025-08-19 01:04:24 +02:00
Irmen de Jong
369303c46d fix register clobbering in pointer deref 2025-08-18 23:36:35 +02:00
Irmen de Jong
d65670cc7b fix 6502 address-of for array-indexed pointers (pointer arithmetic) 2025-08-18 20:24:39 +02:00
Irmen de Jong
f74eeaee0f fix IR address-of for array-indexed pointers (pointer arithmetic) 2025-08-18 03:24:32 +02:00
Irmen de Jong
826fb3e9c2 implemented assign indexed ptr byte 2025-08-17 13:21:31 +02:00
Irmen de Jong
a3d7b8a899 fix detection of string comparisons (make it aware of new pointer types equivalent to STR) 2025-08-16 17:04:59 +02:00
Irmen de Jong
0cc36ed6e4 pointer TODO's all as stub methods in the PointerAssignmentsGen class 2025-08-16 16:21:06 +02:00
Irmen de Jong
976bd52972 docs dark mode 2025-08-16 11:32:24 +02:00
Irmen de Jong
4a8d5def84 code cleanups, pointer TODOs, docs dark mode 2025-08-16 11:25:18 +02:00
Irmen de Jong
2f60716082 6502 struct allocation to asm file, struct name and field prefixing (maybe unneeded...) 2025-08-16 01:49:22 +02:00
Irmen de Jong
729efb04e1 fix code size regressions 2025-08-14 23:59:59 +02:00
Irmen de Jong
4ea8b4d445 fix unknown field test and redundant errors 2025-08-14 21:01:25 +02:00
Irmen de Jong
e800c165f9 fix 6502 inplace pointer variable assignment 2025-08-14 20:38:18 +02:00
Irmen de Jong
fd9bd23449 6502 statementreorderer: str -> ^^ubyte 2025-08-13 18:45:29 +02:00
Irmen de Jong
8880ed1393 fix address-of struct fields 2025-08-08 23:08:47 +02:00
Irmen de Jong
f7fde070ca fix boolean pointer condition in if expression 2025-08-08 22:15:58 +02:00
Irmen de Jong
5ada80779d Merge branch 'refs/heads/master' into structs6502
# Conflicts:
#	examples/test.p8
2025-08-07 21:25:07 +02:00
Irmen de Jong
8972235a0e fix missing peekbool() and pokebool() code gen 2025-08-07 21:22:48 +02:00
Irmen de Jong
e56f533e38 more basic pointer inplace operations 2025-08-07 00:36:47 +02:00
Irmen de Jong
324fb7dbf7 more basic pointer inplace operations (float)
basic pointers unit test now passes
2025-08-06 22:26:23 +02:00
Irmen de Jong
44285b9b5d more basic pointer inplace operations 2025-08-06 00:32:15 +02:00
Irmen de Jong
a68f477d61 Merge branch 'master' into structs6502
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2025-08-05 23:29:58 +02:00
Irmen de Jong
ae9f99448e fix pointer subtract arithmetic 2025-08-05 23:23:29 +02:00
Irmen de Jong
7c0fb10197 fix bug: VM MULR float error, another pointer dependency checker error 2025-08-05 22:00:05 +02:00
Irmen de Jong
9e85571a7b fix pointer variable usage detection in other block 2025-08-05 17:39:22 +02:00
Irmen de Jong
9e10c15e2e working on 6502 pointer inplace assignments 2025-08-04 23:32:17 +02:00
Irmen de Jong
6bd7752bac working on 6502 pointer dereferencing 2025-08-04 20:22:13 +02:00
Irmen de Jong
83ec437e8a testpointers unit test now also for 6502 targets
implementing first simple pointer operations
pointer vars also allocated in ZP for dontcare
2025-08-03 22:12:03 +02:00
Irmen de Jong
4a1d05dd46 first 6502 codegen results 2025-08-03 16:10:00 +02:00
Irmen de Jong
aa324e355a remove 6502 pointer check, TODOs for pointer assignments 2025-08-03 13:28:39 +02:00
Irmen de Jong
5cb8bcead7 todo 2025-08-03 00:54:43 +02:00
Irmen de Jong
bbd06c0c99 implement peekbool/pokebool on 6502, fix float assignment register error 2025-08-02 21:38:29 +02:00
Irmen de Jong
651830ea82 update syntax files 2025-08-02 21:00:16 +02:00
Irmen de Jong
c70146f1dc Merge branch 'master' into structs
# Conflicts:
#	docs/source/todo.rst
2025-08-02 19:26:54 +02:00
Irmen de Jong
d4e83b28bb error messages and trying to improve support for struct allocs in arrays
added sorting example
2025-08-02 19:22:58 +02:00
Irmen de Jong
bc58a25765 allow sizeof(^^type) to return the size of a pointer 2025-08-02 11:33:15 +02:00
Irmen de Jong
38645022c9 actually disallow ^^str 2025-08-02 05:49:12 +02:00
Irmen de Jong
647cd0fbe1 fix pointer[i].field compiler crash 2025-08-02 05:29:16 +02:00
Irmen de Jong
ea8935a346 doc 2025-08-02 00:02:29 +02:00
Irmen de Jong
7ea80babfc todo 2025-08-01 23:38:49 +02:00
Irmen de Jong
dee761a99e fix compiler crash on certain pointer assignments 2025-08-01 22:22:43 +02:00
Irmen de Jong
88ee7a8187 fix expected outcome of function call arg type pointer test 2025-08-01 00:23:10 +02:00
Irmen de Jong
eb8b408b82 fix countries[2]^^ = 0 compiler crash 2025-07-31 02:03:18 +02:00
Irmen de Jong
3d10882f57 fix ast printing 2025-07-30 23:42:45 +02:00
Irmen de Jong
1988496512 correct commit hash 2025-07-30 01:08:22 +02:00
Irmen de Jong
88b074c208 pointer types should just be uwords in IR 2025-07-30 01:07:37 +02:00
Irmen de Jong
c4c5636a81 fixing array indexing on pointers 2025-07-29 23:41:38 +02:00
Irmen de Jong
c39d570b72 make more use of ISubType interface itself rather than casting it to StructDecl all the time 2025-07-29 22:59:31 +02:00
Irmen de Jong
4ccd7f9f3a improve docs about recursion 2025-07-29 22:49:24 +02:00
Irmen de Jong
1c9c5aeef7 todos 2025-07-28 23:29:18 +02:00
Irmen de Jong
23ad540aa5 fix IR codegen type error on array pointer dereference 2025-07-28 20:25:56 +02:00
Irmen de Jong
08810c2749 proper error message for unsupported &&subroutine 2025-07-27 23:44:24 +02:00
Irmen de Jong
a52966f327 rollback implicit casts to boolean in struct initializers and function call arguments 2025-07-27 03:27:26 +02:00
Irmen de Jong
624220e9a3 fix boolean struct field as if conditional 2025-07-27 01:58:18 +02:00
Irmen de Jong
842b11ed9e struct docs 2025-07-27 00:00:40 +02:00
Irmen de Jong
82267b3f56 Merge branch 'master' into structs
# Conflicts:
#	compiler/test/codegeneration/TestVariables.kt
#	docs/source/_static/symboldumps/skeletons-c128.txt
#	docs/source/_static/symboldumps/skeletons-c64.txt
#	docs/source/_static/symboldumps/skeletons-cx16.txt
#	docs/source/_static/symboldumps/skeletons-pet32.txt
#	docs/source/_static/symboldumps/skeletons-virtual.txt
#	docs/source/todo.rst
#	examples/test.p8
2025-07-26 12:43:13 +02:00
Irmen de Jong
67fb45a55b don't produce invalid boolean initalization error. Fixes #173 2025-07-26 12:35:23 +02:00
Irmen de Jong
11186f1dbe make sure that the virtual target -emu (vm) only runs the actual .p8ir file (fixes #172)
added emudbg.console_nl()
2025-07-26 11:19:01 +02:00
Irmen de Jong
817f4f8e7c Merge branch 'master' into structs
# Conflicts:
#	compiler/src/prog8/compiler/Compiler.kt
#	compiler/src/prog8/compiler/astprocessing/AstExtensions.kt
#	compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt
#	docs/source/todo.rst
#	examples/test.p8
2025-07-24 22:27:29 +02:00
Irmen de Jong
28e351daab new library routine skeletons 2025-07-22 18:18:15 +02:00
Irmen de Jong
893e16d814 replace str or ubyte[] param and returnvalue type into ^^ubyte rather than uword 2025-07-21 22:59:14 +02:00
Irmen de Jong
33470c47fc last changes to virtual diskio to make untyped pointers typed 2025-07-21 22:13:36 +02:00
Irmen de Jong
63f7b87572 Merge branch 'master' into structs 2025-07-21 22:11:34 +02:00
Irmen de Jong
ce8006992a changing virtual diskio to use typed pointers 2025-07-21 22:10:04 +02:00
Irmen de Jong
0b5413ad83 Merge branch 'master' into structs
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2025-07-21 20:53:06 +02:00
Irmen de Jong
23058b51a1 started changing libs to typed pointers 2025-07-21 20:50:33 +02:00
Irmen de Jong
c3be7ab4b3 fix if expression type problems with pointers 2025-07-18 23:44:55 +02:00
Irmen de Jong
a9b8fbc6c6 Merge branch 'master' into structs
# Conflicts:
#	examples/test.p8
2025-07-18 22:37:36 +02:00
Irmen de Jong
b0981a5fae pointers no longer implicitly converted to boolean in expressions, to be consistent with how integers are handled in conditionals
adding particles fountain examples
2025-07-18 01:43:35 +02:00
Irmen de Jong
dc434d034a fix address-of identifier alias replacement stripping array index away 2025-07-14 23:54:26 +02:00
Irmen de Jong
3f34b83e0d sdks 2025-07-14 21:54:40 +02:00
Irmen de Jong
9c63ef39c7 fix pointer test 2025-07-07 23:09:45 +02:00
Irmen de Jong
9f6106452e revert & to untyped pointer, added && for typed pointer address-of 2025-07-07 16:17:07 +02:00
Irmen de Jong
f9fbfe30e3 fix &x +/- offset pointer arithmetic expression 2025-07-07 13:11:57 +02:00
Irmen de Jong
9a9bf170c6 Merge branch 'master' into structs 2025-07-06 23:17:00 +02:00
Irmen de Jong
b6c0bac96f identified problems with pointer array as parameter 2025-07-06 20:33:20 +02:00
Irmen de Jong
8ede098154 fix pointer array initialization 2025-07-06 19:42:54 +02:00
Irmen de Jong
2a4a3b786e cleanup error message for currently unsupported deref'd pointer assignments 2025-07-06 14:59:42 +02:00
Irmen de Jong
b4e0a2019e fixed assignment to a[i]^^ 2025-07-06 13:38:22 +02:00
Irmen de Jong
e14c3f8b59 code cleanups 2025-07-06 00:52:37 +02:00
Irmen de Jong
c81f76226d Merge branch 'master' into structs 2025-07-06 00:37:58 +02:00
Irmen de Jong
dcce519c69 Merge branch 'master' into structs
# Conflicts:
#	.idea/libraries/KotlinJavaRuntime.xml
#	build.gradle.kts
#	gradle.properties
2025-07-06 00:07:16 +02:00
Irmen de Jong
54d41b7f6f fixed a[i]^^ 2025-07-05 23:54:08 +02:00
Irmen de Jong
0541b84d09 Merge branch 'master' into structs 2025-06-29 16:34:32 +02:00
Irmen de Jong
2119817e4a Merge branch 'master' into structs 2025-06-24 21:14:53 +02:00
Irmen de Jong
a68cf3c812 fix animalgame node reuse 2025-06-18 23:34:47 +02:00
Irmen de Jong
c2bf9024f8 start writing docs about structs and pointers, update syntax files with ^^ 2025-06-18 19:00:18 +02:00
Irmen de Jong
bd72eaad4c Merge branch 'refs/heads/master' into structs
# Conflicts:
#	examples/test.p8
2025-06-18 17:53:20 +02:00
Irmen de Jong
1d306e5cdc moved new animalgame 2025-06-17 23:21:54 +02:00
Irmen de Jong
b137164fe6 allow str assigned to ^^ubyte without an explicit cast 2025-06-17 18:29:48 +02:00
Irmen de Jong
67d4ad50e1 add new animals example (that uses a pointer tree) 2025-06-17 01:08:36 +02:00
Irmen de Jong
c71066af4c fixing name lookup issue 2025-06-16 22:15:51 +02:00
Irmen de Jong
6f0a0981bd fixing name lookup issue 2025-06-16 00:21:54 +02:00
Irmen de Jong
49a4d9ba37 allow str as struct field type (^^ubyte) and strings in struct initializers 2025-06-15 00:29:59 +02:00
Irmen de Jong
fcdfa741b9 Merge branch 'master' into structs
# Conflicts:
#	compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt
2025-06-14 21:07:23 +02:00
Irmen de Jong
3bab177d50 working on pointers/binarytree example 2025-06-13 23:20:15 +02:00
Irmen de Jong
12abafb917 Merge branch 'master' into structs
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
#	gradle.properties
2025-06-12 00:11:58 +02:00
Irmen de Jong
0be90dedf2 check for split word array as argument 2025-06-11 21:35:36 +02:00
Irmen de Jong
e6bab3ceeb IR: adding LOADFIELD and STOREFIELD instructions 2025-06-09 01:41:45 +02:00
Irmen de Jong
59387b2ae8 Merge branch 'master' into structs
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2025-06-07 20:28:23 +02:00
Irmen de Jong
f8f20440d3 allow pointer to be treated as uword value in augmented assignments 2025-06-03 21:33:35 +02:00
Irmen de Jong
f8722faa4e Merge branch 'master' into structs
# Conflicts:
#	examples/test.p8
2025-06-03 21:15:19 +02:00
Irmen de Jong
26fbbf48a4 adapt new antlr visitor to the pointer/struct additions 2025-06-03 01:28:58 +02:00
Irmen de Jong
d5cc414221 Merge branch 'master' into structs
# Conflicts:
#	codeOptimizers/src/prog8/optimizer/StatementOptimizer.kt
#	compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt
#	compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt
#	examples/test.p8
2025-06-02 20:52:50 +02:00
Irmen de Jong
d28f154f1c Merge branch 'master' into structs
# Conflicts:
#	examples/test.p8
#	parser/src/main/antlr/Prog8ANTLR.g4
2025-06-01 17:53:41 +02:00
Irmen de Jong
1c02179c5c refactor loadIndexReg() 2025-05-30 21:39:17 +02:00
Irmen de Jong
77584493fd support a.b.ptr[i]^^.value as expression (RHS) 2025-05-30 20:28:10 +02:00
Irmen de Jong
a36709e638 Merge branch 'master' into structs
# Conflicts:
#	compiler/src/prog8/compiler/Compiler.kt
2025-05-30 12:39:23 +02:00
Irmen de Jong
8d63cce749 working on deref after array indexing 2025-05-30 11:30:49 +02:00
Irmen de Jong
8e7bbcdbe0 clear syntax error for yet unsupported deref after array indexing 2025-05-29 21:14:38 +02:00
Irmen de Jong
37ecdc47b3 allow ptr1^^ = ptr^^ (replaces it with memcopy) 2025-05-29 16:34:47 +02:00
Irmen de Jong
112ca3cc53 allow sizeof(&thing), add sys.SIZEOF_POINTER 2025-05-29 15:58:29 +02:00
Irmen de Jong
33b3a1664c replace sizeof(list^^) with sizeof(List) to allow it to compile. Same with simple pointers. 2025-05-29 14:13:42 +02:00
Irmen de Jong
8a0c02e264 Merge branch 'refs/heads/master' into structs
# Conflicts:
#	codeCore/src/prog8/code/target/NormalMemSizer.kt
#	codeCore/src/prog8/code/target/VMTarget.kt
#	compiler/src/prog8/compiler/BuiltinFunctions.kt
#	compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt
#	examples/test.p8
2025-05-29 13:42:16 +02:00
Irmen de Jong
e40ace9dea todo 2025-05-29 01:35:53 +02:00
Irmen de Jong
4c0e6e2640 fix split/nosplit pointer arrays, replace ubyteptr^^ with @(ubyteptr), fix double dereference 2025-05-29 00:36:55 +02:00
Irmen de Jong
08b314c37d IR: fix various register type mismatches 2025-05-28 22:15:07 +02:00
Irmen de Jong
86da9d3c7e assigning to plain pointer with array indexing 2025-05-28 18:08:53 +02:00
Irmen de Jong
4e61e25c02 Merge branch 'master' into structs
# Conflicts:
#	compiler/test/TestTypecasts.kt
2025-05-27 23:52:59 +02:00
Irmen de Jong
5097d52d99 IR codegen for pointer indexing expressions, -assignment 2025-05-27 23:41:08 +02:00
Irmen de Jong
09d2185bb1 PtArrayIndexer variable is now nullable (because it could be a ptr deref instead) 2025-05-25 23:04:32 +02:00
Irmen de Jong
5c02e2bd71 fix a ptr indexing case, fix address-of fields 2025-05-25 21:32:31 +02:00
Irmen de Jong
fb01389b3d cleaning up pointer deref 2025-05-25 18:33:37 +02:00
Irmen de Jong
aaa81210ce cleaning up pointer indexing 2025-05-25 02:56:32 +02:00
Irmen de Jong
51269257ea fix a.b.c.d desugaring into pointer deref chain 2025-05-24 14:48:02 +02:00
Irmen de Jong
23a853db1e Merge branch 'master' into structs
# Conflicts:
#	compiler/src/prog8/compiler/astprocessing/AstChecker.kt
#	examples/test.p8
2025-05-23 19:00:59 +02:00
Irmen de Jong
db6c887795 Merge branch 'master' into structs
# Conflicts:
#	compiler/test/ast/TestVariousCompilerAst.kt
2025-05-21 00:27:45 +02:00
Irmen de Jong
61fe55168a Merge branch 'master' into structs
# Conflicts:
#	codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt
#	codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
#	examples/test.p8
2025-05-20 22:35:04 +02:00
Irmen de Jong
1c4999ec87 adding ptr unit tests 2025-05-20 20:57:05 +02:00
Irmen de Jong
c726d3f937 fix ptr errors 2025-05-19 22:49:07 +02:00
Irmen de Jong
f70341df1b fix ptr errors 2025-05-19 20:06:31 +02:00
Irmen de Jong
f0b791452e fix many ptr deref errors 2025-05-19 01:33:57 +02:00
Irmen de Jong
adf5600a9b simplify 2025-05-18 18:37:19 +02:00
Irmen de Jong
6d4ccc5feb fix pointer variable not getting marked as used in some cases 2025-05-18 13:23:56 +02:00
Irmen de Jong
5f3829d5cc partly fix weird errors for ptr indexed expressions 2025-05-17 22:56:41 +02:00
Irmen de Jong
770ebdcd4a party fix weird errors for ptr indexed assignment 2025-05-17 20:54:02 +02:00
Irmen de Jong
96f690e749 fix a ptr indexing error 2025-05-17 20:27:38 +02:00
Irmen de Jong
eabdd3a8f3 fix the ptr.uword[index] assignment target error 2025-05-17 14:57:24 +02:00
Irmen de Jong
50650b966b repeat countervars again in zeropage if possible, fix pointer arithmetic error 2025-05-17 14:18:56 +02:00
Irmen de Jong
65e34d4989 stricter types for & operator (address-of), and fix invalid recursive subroutine flagging related to struct definition 2025-05-17 11:32:54 +02:00
Irmen de Jong
05dad5ab5f cleanup 6502 pointer usage checks 2025-05-16 21:44:28 +02:00
Irmen de Jong
1a69a2f1bc fix some ptr vs uword type checks 2025-05-16 20:31:15 +02:00
Irmen de Jong
435faafaad fix split-word storage (lsb/msb) of arrays of pointers 2025-05-16 17:53:15 +02:00
Irmen de Jong
686b32dc29 replace ^^str by ^^ubyte and allow returning ubyte/uword when pointer is expected 2025-05-16 17:53:15 +02:00
Irmen de Jong
0e64a22910 tweak address-of types 2025-05-16 17:53:15 +02:00
Irmen de Jong
4f0839f27e rewrite pointer[0] into @(pointer) if its ^^ubyte 2025-05-16 17:53:15 +02:00
Irmen de Jong
bb1953267d Merge branch 'master' into structs
# Conflicts:
#	examples/test.p8
2025-05-15 23:09:30 +02:00
Irmen de Jong
cd8aae4681 allow @(..) to take a ^^ubyte address pointer, not only uwords. 2025-05-15 22:12:42 +02:00
Irmen de Jong
11456496bd Merge branch 'master' into structs
# Conflicts:
#	compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt
#	parser/src/main/antlr/Prog8ANTLR.g4
2025-05-15 21:35:31 +02:00
Irmen de Jong
86eef7039f @(..) now also accepts pointer to ubyte address 2025-05-15 20:07:02 +02:00
Irmen de Jong
f4b2264fcf fix struct type checks for subroutine call arguments 2025-05-14 23:33:55 +02:00
Irmen de Jong
9b36ae2277 implement inplace boolean short-circuit operators on pointer dereferenced booleans 2025-05-14 21:29:50 +02:00
Irmen de Jong
913ab03963 get rid of invalid ARRAY_STRUCT data type (arrays of struct instance are not yet supported) 2025-05-14 20:43:00 +02:00
Irmen de Jong
38448e471c Merge branch 'master' into structs
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/ForLoopsAsmGen.kt
#	codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt
#	examples/test.p8
2025-05-13 23:42:13 +02:00
Irmen de Jong
d138a7a567 add struct and pointers to IDEA syntax 2025-05-11 23:35:53 +02:00
Irmen de Jong
ea27d732ab Merge branch 'refs/heads/master' into structs 2025-05-11 23:35:13 +02:00
Irmen de Jong
924e28e9b3 Merge branch 'master' into structs
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt
#	compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt
#	compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt
#	compilerAst/src/prog8/ast/AstToSourceTextConverter.kt
#	compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt
#	compilerAst/src/prog8/ast/walk/AstWalker.kt
#	compilerAst/src/prog8/ast/walk/IAstVisitor.kt
#	docs/source/todo.rst
#	examples/test.p8
#	parser/src/main/antlr/Prog8ANTLR.g4
2025-05-11 23:23:06 +02:00
Irmen de Jong
60a73248cd todo 2025-05-11 03:16:48 +02:00
Irmen de Jong
abbb7d7ba3 fix struct pointers in subroutine parameters and return values 2025-05-11 02:08:59 +02:00
Irmen de Jong
59c378089e fix some struct type and symbol lookup errors 2025-05-11 00:52:35 +02:00
Irmen de Jong
0b789b5f0b added most inplace operators for pointer deref 2025-05-10 20:58:01 +02:00
Irmen de Jong
4382b96a9a tweaking pointer deref in IR 2025-05-10 19:52:06 +02:00
Irmen de Jong
246e4f35a6 Merge branch 'master' into structs
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt
#	codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt
#	compiler/test/ast/TestConst.kt
#	docs/source/todo.rst
#	examples/test.p8
2025-05-10 16:36:16 +02:00
Irmen de Jong
506062c6b6 start implementing ptr deref augmented assigns 2025-05-09 23:05:27 +02:00
Irmen de Jong
8353c689ca start making '&' (address-of) return a typed pointer, fixes some errors 2025-05-08 23:40:41 +02:00
Irmen de Jong
e98e6f70ac Merge branch 'master' into structs
# Conflicts:
#	compiler/src/prog8/compiler/astprocessing/AstChecker.kt
#	compiler/test/TestTypecasts.kt
#	docs/source/todo.rst
#	examples/test.p8
#	gradle.properties
2025-05-07 23:26:21 +02:00
Irmen de Jong
134352ed7c Merge branch 'master' into structs
# Conflicts:
#	compiler/test/TestTypecasts.kt
#	docs/source/todo.rst
#	examples/test.p8
#	virtualmachine/src/prog8/vm/VmProgramLoader.kt
2025-05-07 22:32:41 +02:00
Irmen de Jong
5de626aab8 support comparison operators on pointers 2025-05-06 22:26:27 +02:00
Irmen de Jong
7aad5d486e Merge pull request #168 from adiee5/master
Update `nano` syntax highlighting
2025-05-06 18:17:58 +02:00
Irmen de Jong
701f155951 Merge branch 'master' into structs
# Conflicts:
#	codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt
#	compiler/test/TestSymbolTable.kt
#	docs/source/todo.rst
#	examples/test.p8
#	intermediate/src/prog8/intermediate/IRFileReader.kt
#	intermediate/src/prog8/intermediate/IRFileWriter.kt
#	intermediate/src/prog8/intermediate/IRSymbolTable.kt
#	simpleAst/src/prog8/code/SymbolTable.kt
#	simpleAst/src/prog8/code/SymbolTableMaker.kt
#	virtualmachine/src/prog8/vm/VmProgramLoader.kt
2025-05-06 17:59:36 +02:00
Irmen de Jong
8c324d7514 tweak error 2025-05-06 17:50:53 +02:00
adiee5
97390db5f5 Update prog8.nanorc 2025-05-05 21:06:56 +02:00
Irmen de Jong
af920d1427 pointer arithmetic for '-', fixed '+' 2025-05-05 21:06:49 +02:00
Irmen de Jong
779ebc0537 pointer arithmetic for '+' 2025-05-05 18:09:19 +02:00
Irmen de Jong
38949b82c3 type check tuning 2025-05-05 15:41:32 +02:00
Irmen de Jong
d11386ef26 type check tuning 2025-05-04 23:23:21 +02:00
Irmen de Jong
0e0377d1f0 IR/VM implemented struct allocations and initialization 2025-05-04 14:04:44 +02:00
Irmen de Jong
55e0dbab27 preparing for statically allocating struct instances 2025-05-03 23:44:29 +02:00
Irmen de Jong
4dc82f2c83 preparing for statically allocating struct instances 2025-05-03 19:00:27 +02:00
Irmen de Jong
1ba5587404 allow syntax for declaring variables with struct instance type 2025-05-03 16:14:22 +02:00
Irmen de Jong
835c4b6da3 allow multi-field declarations in structs, get rid of . -> ^^ rewrite 2025-05-03 12:32:29 +02:00
Irmen de Jong
dbd955b61e tweak typecheck and better code for constant indexes in pointer derefs 2025-05-02 23:44:26 +02:00
Irmen de Jong
d20e2fd88c structs will be a new major version of the compiler 2025-05-02 22:25:26 +02:00
Irmen de Jong
e0dea89477 added support for ptr[x].field 2025-05-02 22:16:20 +02:00
Irmen de Jong
6fc2902895 fixing ptr traversal typecheck issues 2025-05-02 00:41:42 +02:00
Irmen de Jong
c96e4b40d4 building syntax support for ptr[x].field
attempting to do this by making '.' an expression operator
2025-05-02 00:41:42 +02:00
Irmen de Jong
37da3e2170 parser 2025-05-02 00:41:42 +02:00
Irmen de Jong
2661d3c489 allow array syntax on pointers 2025-05-02 00:41:42 +02:00
Irmen de Jong
b89bbb9281 allow pointers in subroutines params and return values 2025-05-02 00:41:42 +02:00
Irmen de Jong
696bf636ed better parsing of directive names results in better error messages when an invalid one is found 2025-05-02 00:41:42 +02:00
Irmen de Jong
40952a788a PtSub: params and returns now as children (in PtSignature node) for easier Ast walking
PtPointerDeref: same but for its start identifier
2025-05-02 00:41:42 +02:00
Irmen de Jong
0162e7a0c1 fix the scoping problems on subtypes 2025-05-02 00:41:42 +02:00
Irmen de Jong
6ce099f176 IR: fix ptr type checks and struct field assignment errors 2025-05-02 00:41:42 +02:00
Irmen de Jong
476a4bac8e IR: LOADI allows r1 and r2 to be the same for pointer chain dereference optimalization 2025-05-02 00:41:42 +02:00
Irmen de Jong
63a410a6df implicit cast to bool for numeric or pointers as condition arguments (to if, while, until) 2025-05-02 00:41:42 +02:00
Irmen de Jong
cca27faa3b fix pointer value assignment (not dereferencing the actual pointer) 2025-05-02 00:41:42 +02:00
Irmen de Jong
803e6bd81a fix uword vs pointer type errors and casts 2025-05-02 00:41:42 +02:00
Irmen de Jong
88269628a2 had to turn ^type syntax into ^^type to avoid confusion with the eor operator once again 2025-05-02 00:41:42 +02:00
Irmen de Jong
b920d553a0 make address-of dereference work 2025-05-02 00:41:42 +02:00
Irmen de Jong
5e2d0d0dfc fix param order of AssignTarget 2025-05-02 00:41:42 +02:00
Irmen de Jong
2ae3bd68eb more pointer dereferencing for chains 2025-05-02 00:41:42 +02:00
Irmen de Jong
9c183f27eb pointer dereferencing for chains 2025-05-02 00:41:42 +02:00
Irmen de Jong
8046023e82 pointer dereferencing for simple types (read and write) 2025-05-02 00:41:42 +02:00
Irmen de Jong
e328520588 initial struct and typed pointer support 2025-05-02 00:41:40 +02:00
252 changed files with 16014 additions and 3808 deletions

3
.gitignore vendored
View File

@@ -7,10 +7,13 @@ build/
dist/ dist/
output/ output/
out/ out/
out-new/
out-old/
.*cache/ .*cache/
*.directory *.directory
*.prg *.prg
*.bin *.bin
*.p8ir
*.labels.txt *.labels.txt
*.vm.txt *.vm.txt
*.vice-mon-list *.vice-mon-list

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +1,23 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="KotlinJavaRuntime" type="repository"> <library name="KotlinJavaRuntime" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.0" /> <properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.0/kotlin-stdlib-jdk8-2.2.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.0/kotlin-stdlib-jdk7-2.2.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.0/kotlin-stdlib-jdk8-2.2.0-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.0/kotlin-stdlib-jdk7-2.2.0-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.0/kotlin-stdlib-jdk8-2.2.0-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.0/kotlin-stdlib-jdk7-2.2.0-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
</component> </component>

View File

@@ -1,20 +0,0 @@
<component name="libraryTable">
<library name="io.kotest.assertions.core.jvm" type="repository">
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.9.1" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.9.1/kotest-assertions-core-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@@ -1,19 +1,19 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="michael.bull.kotlin.result.jvm" type="repository"> <library name="michael.bull.kotlin.result.jvm" type="repository">
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1" /> <properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.1.0/kotlin-result-jvm-2.1.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>

3
.idea/misc.xml generated
View File

@@ -26,4 +26,7 @@
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
<component name="PythonCompatibilityInspectionAdvertiser">
<option name="version" value="3" />
</component>
</project> </project>

View File

@@ -61,6 +61,7 @@ What does Prog8 provide?
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS - code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
- modularity, symbol scoping, subroutines. No need for forward declarations. - modularity, symbol scoping, subroutines. No need for forward declarations.
- various data types other than just bytes (16-bit words, floats, strings) - various data types other than just bytes (16-bit words, floats, strings)
- Structs and typed pointers
- floating point math is supported on certain targets - floating point math is supported on certain targets
- access to most Kernal ROM routines as external subroutine definitions you can call normally - access to most Kernal ROM routines as external subroutine definitions you can call normally
- tight control over Zeropage usage - tight control over Zeropage usage

View File

@@ -0,0 +1,234 @@
; Binary Search Tree.
; It's a simple implementation for test/demonstration purposes of the pointer support;
; no balancing is done and memory is not freed when elements are removed.
%import textio
btree {
sub benchmark(uword max_time) -> uword {
txt.nl()
cbm.SETTIM(0,0,0)
uword score
while cbm.RDTIM16() < max_time {
bench_operations()
txt.chrout('.')
score++
}
txt.nl()
return score
}
sub bench_operations() {
arena.freeall()
btree.root = 0
for cx16.r0 in [321, 719, 194, 550, 187, 203, 520, 562, 221, 676, 97, 852, 273, 326, 589, 606, 275, 794, 63, 716]
btree.add(cx16.r0)
cx16.r0L = btree.size()
btree.process_tree_inorder()
btree.process_tree_preorder()
void btree.contains(203)
void btree.contains(204)
void btree.contains(605)
void btree.contains(606)
btree.remove(9999)
btree.remove(97)
btree.remove(187)
btree.remove(203)
btree.remove(275)
btree.remove(321)
btree.remove(520)
btree.remove(562)
btree.remove(606)
btree.remove(719)
btree.remove(794)
cx16.r0L = btree.size()
btree.process_tree_inorder()
btree.process_tree_preorder()
}
struct Node {
^^Node left
^^Node right
uword value
}
^^Node root = 0
sub add(uword value) {
^^Node node = arena.alloc(sizeof(Node))
node.value = value
node.left = node.right = 0
if root==0
root=node
else {
^^Node parent = root
repeat {
if parent.value >= value {
if parent.left!=0
parent = parent.left
else {
parent.left = node
return
}
} else {
if parent.right!=0
parent = parent.right
else {
parent.right = node
return
}
}
}
}
}
sub contains(uword value) -> bool {
^^Node r = root
while r!=0 {
if r.value==value
return true
if r.value>value
r = r.left
else
r = r.right
}
return false
}
sub size() -> ubyte {
ubyte count
if root!=0
count_node(root)
return count
sub count_node(^^Node r) {
count++
if r.left!=0 {
sys.pushw(r)
count_node(r.left)
r = sys.popw()
}
if r.right!=0 {
sys.pushw(r)
count_node(r.right)
r = sys.popw()
}
}
}
sub remove(uword value) {
; note: we don't deallocate the memory from the node, for simplicity sake
^^Node n = root
^^Node parent = 0
while n!=0 {
if n.value==value {
if n.left==0
replacechild(parent, n, n.right)
else if n.right==0
replacechild(parent, n, n.left)
else {
; Both left & right subtrees are present.
; N = node to delete.
; Find N's successor S. (N's right subtree's minimum element)
; Attach N's left subtree to S.left (S doesn't have a left child)
; Attach N's right subtree to Parent in place of N.
^^Node successor = find_successor(n)
successor.left = n.left
replacechild(parent, n, n.right)
}
return
}
parent = n
if n.value>value
n = n.left
else
n = n.right
}
sub find_successor(^^Node p) -> ^^Node {
^^Node succ = p
p = p.right
while p!=0 {
succ = p
p = p.left
}
return succ
}
sub replacechild(^^Node p, ^^Node child, ^^Node newchild) {
if p.left==child
p.left = newchild
else
p.right = newchild
}
}
sub process_tree_inorder() {
if root!=0
process_tree(root)
sub process_tree(^^Node r) {
if r.left!=0 {
sys.pushw(r)
process_tree(r.left)
r = sys.popw()
}
cx16.r0 = r.value
if r.right!=0 {
sys.pushw(r)
process_tree(r.right)
r = sys.popw()
}
}
}
sub process_tree_preorder() {
if root!=0
process_tree(root,0)
sub process_tree(^^Node r, ubyte depth) {
cx16.r0 = r.value
if r.left!=0 {
sys.pushw(r)
sys.push(depth)
process_tree(r.left, depth+1)
depth = sys.pop()
r = sys.popw()
}
if r.right!=0 {
sys.pushw(r)
sys.push(depth)
process_tree(r.right, depth+1)
depth = sys.pop()
r = sys.popw()
}
}
}
}
arena {
; extremely trivial arena allocator (that never frees)
uword buffer = memory("arena", 2000, 0)
uword next = buffer
sub alloc(ubyte size) -> uword {
defer next += size
return next
}
sub freeall() {
next = buffer
}
}

View File

@@ -16,6 +16,7 @@
%import b_textelite %import b_textelite
%import b_maze %import b_maze
%import b_sprites %import b_sprites
%import b_btree
%zeropage basicsafe %zeropage basicsafe
%option no_sysinit %option no_sysinit
@@ -67,10 +68,6 @@ main {
benchmark_score[benchmark_number] = circles.draw(false, 300) benchmark_score[benchmark_number] = circles.draw(false, 300)
benchmark_number++ benchmark_number++
; announce_benchmark("circles with kernal")
; benchmark_score[benchmark_number] = circles.draw(true, 300)
; benchmark_number++
announce_benchmark("text-elite") announce_benchmark("text-elite")
benchmark_score[benchmark_number] = textelite.bench(120) benchmark_score[benchmark_number] = textelite.bench(120)
benchmark_number++ benchmark_number++
@@ -79,13 +76,17 @@ main {
benchmark_score[benchmark_number] = animsprites.benchmark(300) benchmark_score[benchmark_number] = animsprites.benchmark(300)
benchmark_number++ benchmark_number++
announce_benchmark("btree-struct-pointers")
benchmark_score[benchmark_number] = btree.benchmark(200)
benchmark_number++
benchmark_names[benchmark_number] = 0 benchmark_names[benchmark_number] = 0
benchmark_score[benchmark_number] = 0 benchmark_score[benchmark_number] = 0
cx16.set_screen_mode(3) cx16.set_screen_mode(3)
txt.uppercase() txt.uppercase()
txt.color2(1, 6) txt.color2(1, 6)
uword final_score uword total_score
benchmark_number = 0 benchmark_number = 0
txt.print("\nscore benchmark\n\n") txt.print("\nscore benchmark\n\n")
do { do {
@@ -93,14 +94,14 @@ main {
txt.print_uw(benchmark_score[benchmark_number]) txt.print_uw(benchmark_score[benchmark_number])
txt.column(6) txt.column(6)
txt.print(benchmark_names[benchmark_number]) txt.print(benchmark_names[benchmark_number])
final_score += benchmark_score[benchmark_number] total_score += benchmark_score[benchmark_number]
txt.nl() txt.nl()
benchmark_number++ benchmark_number++
} until benchmark_names[benchmark_number]==0 } until benchmark_names[benchmark_number]==0
txt.print("\n\nfinal score : ") txt.print("\n\ntotal score : ")
txt.print_uw(final_score) txt.print_uw(total_score)
txt.nl() txt.print(" (higher=better)\n")
sub announce_benchmark(str name) { sub announce_benchmark(str name) {
benchmark_names[benchmark_number] = name benchmark_names[benchmark_number] = name

View File

@@ -1,10 +1,11 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
plugins { plugins {
kotlin("jvm") version "2.2.0" kotlin("jvm") version "2.2.20"
} }
allprojects { allprojects {
@@ -20,6 +21,7 @@ allprojects {
freeCompilerArgs = listOf("-Xwhen-guards") freeCompilerArgs = listOf("-Xwhen-guards")
jvmTarget = JvmTarget.JVM_11 jvmTarget = JvmTarget.JVM_11
jvmDefault = JvmDefaultMode.NO_COMPATIBILITY jvmDefault = JvmDefaultMode.NO_COMPATIBILITY
// languageVersion.set(KotlinVersion.KOTLIN_2_3)
} }
sourceSets.all { sourceSets.all {
languageSettings { languageSettings {

View File

@@ -1,5 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
kotlin("jvm") kotlin("jvm")
} }
@@ -7,7 +5,7 @@ plugins {
dependencies { dependencies {
// should have no dependencies to other modules // should have no dependencies to other modules
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1") implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
} }
sourceSets { sourceSets {

View File

@@ -5,9 +5,10 @@ import java.nio.file.Path
import kotlin.io.path.absolute import kotlin.io.path.absolute
// the automatically generated module where all string literals are interned to:
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings" const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
val PROG8_CONTAINER_MODULES = arrayOf(INTERNED_STRINGS_MODULENAME) // option to add more if needed one day
// all automatically generated labels everywhere need to have the same label name prefix: // all automatically generated labels everywhere need to have the same label name prefix:
const val GENERATED_LABEL_PREFIX = "p8_label_gen_" const val GENERATED_LABEL_PREFIX = "p8_label_gen_"

View File

@@ -96,12 +96,14 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)), "prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)), "prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)), "prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
"prog8_lib_structalloc" to FSignature(true, BaseDataType.UWORD),
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)), "abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)), "abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)), "abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)), "abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)), "len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *BaseDataType.entries.toTypedArray())), "sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())),
"offsetof" to FSignature(true, BaseDataType.UBYTE, FParam("field", BaseDataType.UBYTE)),
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)), "sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)), "sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)), "sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
@@ -131,10 +133,15 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)), "max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)), "max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)), "peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
"peekbool" to FSignature(true, BaseDataType.BOOL, FParam("address", BaseDataType.UWORD)),
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)), "peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
"peekl" to FSignature(true, BaseDataType.LONG, FParam("address", BaseDataType.UWORD)),
"peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)), "peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)), "poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
"pokebool" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
"pokebowl" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.BOOL)),
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)), "pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
"pokel" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.LONG)),
"pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)), "pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
"pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)), "pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
"rsave" to FSignature(false, null), "rsave" to FSignature(false, null),

View File

@@ -13,6 +13,9 @@ enum class BaseDataType {
STR, // pass by reference STR, // pass by reference
ARRAY, // pass by reference, subtype is the element type ARRAY, // pass by reference, subtype is the element type
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types) ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
POINTER, // typed pointer, subtype is whatever type is pointed to
STRUCT_INSTANCE, // the actual instance of a struct (not directly supported in the language yet, but we need its type)
ARRAY_POINTER, // array of pointers (uwords), subtype is whatever type each element points to
UNDEFINED; UNDEFINED;
@@ -26,6 +29,7 @@ enum class BaseDataType {
this.isArray && other.isArray -> false this.isArray && other.isArray -> false
this.isArray -> other != FLOAT this.isArray -> other != FLOAT
this == STR -> other != FLOAT this == STR -> other != FLOAT
this.isPointer -> other.isByteOrBool
else -> true else -> true
} }
@@ -34,7 +38,8 @@ enum class BaseDataType {
this == other -> true this == other -> true
this.isArray && other.isArray -> true this.isArray && other.isArray -> true
this.isByteOrBool -> other.isByteOrBool this.isByteOrBool -> other.isByteOrBool
this.isWord -> other.isWord this.isWord -> other.isWord || other.isPointer
this.isPointer -> other.isWord
this == STR && other== UWORD || this== UWORD && other== STR -> true this == STR && other== UWORD || this== UWORD && other== STR -> true
this == STR && other.isArray -> true this == STR && other.isArray -> true
this.isArray && other == STR -> true this.isArray && other == STR -> true
@@ -50,84 +55,163 @@ val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, Bas
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT) val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW val BaseDataType.isPointer get() = this == BaseDataType.POINTER
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) val BaseDataType.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE
val BaseDataType.isPassByRef get() = this.isIterable val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER
val BaseDataType.isPassByValue get() = !this.isIterable val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER // pointer arrays are also always stored as split uwords
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER)
val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer
val BaseDataType.isPassByValue get() = !this.isIterable || this.isPointer
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?) { interface ISubType {
val scopedNameString: String
fun memsize(sizer: IMemSizer): Int
fun sameas(other: ISubType): Boolean
fun getFieldType(name: String): DataType?
}
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?, var subType: ISubType?, var subTypeFromAntlr: List<String>?=null) {
init { init {
if(base.isArray) { when {
require(sub != null) base.isPointerArray -> {
if(base.isSplitWordArray) require(sub!=null || subType!=null || subTypeFromAntlr!=null)
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD) }
base.isArray -> {
require(sub != null && subType==null && subTypeFromAntlr==null)
if(base.isSplitWordArray)
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
}
base==BaseDataType.STR -> require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
base!=BaseDataType.POINTER -> require(sub == null) { "only string, array and pointer base types can have a subtype"}
else -> {
require(sub == null || (subType == null && subTypeFromAntlr == null)) {
"sub and subtype can't both be set"
}
}
} }
else if(base==BaseDataType.STR)
require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
else
require(sub == null) { "only string and array base types can have a subtype"}
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is DataType) return false if (other !is DataType) return false
return base == other.base && sub == other.sub return base == other.base && sub == other.sub && (subType==other.subType || subType!!.sameas(other.subType!!))
} }
override fun hashCode(): Int = Objects.hash(base, sub) override fun hashCode(): Int = Objects.hash(base, sub, subType)
fun setActualSubType(actualSubType: ISubType) {
subType = actualSubType
subTypeFromAntlr = null
}
companion object { companion object {
val UBYTE = DataType(BaseDataType.UBYTE, null) val UBYTE = DataType(BaseDataType.UBYTE, null, null)
val BYTE = DataType(BaseDataType.BYTE, null) val BYTE = DataType(BaseDataType.BYTE, null, null)
val UWORD = DataType(BaseDataType.UWORD, null) val UWORD = DataType(BaseDataType.UWORD, null, null)
val WORD = DataType(BaseDataType.WORD, null) val WORD = DataType(BaseDataType.WORD, null, null)
val LONG = DataType(BaseDataType.LONG, null) val LONG = DataType(BaseDataType.LONG, null, null)
val FLOAT = DataType(BaseDataType.FLOAT, null) val FLOAT = DataType(BaseDataType.FLOAT, null, null)
val BOOL = DataType(BaseDataType.BOOL, null) val BOOL = DataType(BaseDataType.BOOL, null, null)
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE) val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE, null)
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null) val UNDEFINED = DataType(BaseDataType.UNDEFINED, null, null)
private val simpletypes = mapOf( private val simpletypes = mapOf(
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null), BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null, null),
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null), BaseDataType.BYTE to DataType(BaseDataType.BYTE, null, null),
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null), BaseDataType.UWORD to DataType(BaseDataType.UWORD, null, null),
BaseDataType.WORD to DataType(BaseDataType.WORD, null), BaseDataType.WORD to DataType(BaseDataType.WORD, null, null),
BaseDataType.LONG to DataType(BaseDataType.LONG, null), BaseDataType.LONG to DataType(BaseDataType.LONG, null, null),
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null), BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null, null),
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null), BaseDataType.BOOL to DataType(BaseDataType.BOOL, null, null),
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE), BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE, null),
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null) BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null, null)
) )
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt) fun forDt(dt: BaseDataType): DataType {
if(dt.isStructInstance)
TODO("cannot use struct instance as a data type (yet) - use a pointer instead")
return simpletypes.getValue(dt)
}
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType { fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
require(!elementDt.isPointer) { "use other array constructor for arrays of pointers" }
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
return if(splitwordarray && actualElementDt.isWord) return if(splitwordarray && actualElementDt.isWord)
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt) DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null)
else { else {
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG) if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
DataType(BaseDataType.ARRAY, actualElementDt) DataType(BaseDataType.ARRAY, actualElementDt, null)
else else
throw NoSuchElementException("invalid element dt $elementDt") throw NoSuchElementException("invalid basic element dt $elementDt")
} }
} }
fun arrayOfPointersTo(sub: BaseDataType): DataType = DataType(BaseDataType.ARRAY_POINTER, sub, null)
fun arrayOfPointersTo(structType: ISubType?): DataType = DataType(BaseDataType.ARRAY_POINTER, null, structType)
fun arrayOfPointersFromAntlrTo(sub: BaseDataType?, identifier: List<String>?): DataType =
DataType(BaseDataType.ARRAY_POINTER, sub, null, identifier)
fun pointer(base: BaseDataType): DataType = DataType(BaseDataType.POINTER, base, null)
fun pointer(dt: DataType): DataType = if(dt.isBasic)
DataType(BaseDataType.POINTER, dt.base, null)
else
DataType(BaseDataType.POINTER, null, dt.subType, dt.subTypeFromAntlr)
fun pointer(structType: ISubType): DataType = DataType(BaseDataType.POINTER, null, structType)
fun pointerFromAntlr(identifier: List<String>): DataType = DataType(BaseDataType.POINTER, null, null, identifier)
fun structInstance(type: ISubType?): DataType = DataType(BaseDataType.STRUCT_INSTANCE, sub=null, type)
fun structInstanceFromAntlr(struct: List<String>): DataType = DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr = struct)
} }
fun elementToArray(splitwords: Boolean = true): DataType { fun elementToArray(splitwords: Boolean = true): DataType {
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords) return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
else arrayFor(base, false) else arrayFor(base, false)
} }
fun elementType(): DataType = fun elementType(): DataType =
if(base.isArray || base==BaseDataType.STR) when {
forDt(sub!!) isPointerArray -> DataType(BaseDataType.POINTER, sub, subType)
else base.isArray || base==BaseDataType.STR -> forDt(sub!!)
throw IllegalArgumentException("not an array") else -> throw IllegalArgumentException("not an array")
}
fun typeForAddressOf(msb: Boolean): DataType {
if (isUndefined)
return if(msb) pointer(BaseDataType.UBYTE) else UWORD
else {
if (isBasic)
return pointer(base)
if (isString)
return pointer(BaseDataType.UBYTE)
if (isPointer)
return UWORD
if (isArray) {
if (msb || isSplitWordArray)
return pointer(BaseDataType.UBYTE)
val elementDt = elementType()
require(elementDt.isBasic)
return pointer(elementDt)
}
if (subType != null)
return pointer(this)
return UWORD
}
}
fun dereference(): DataType {
require(isPointer || isUnsignedWord)
return when {
isUnsignedWord -> forDt(BaseDataType.UBYTE)
sub!=null -> forDt(sub)
subType!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, subType)
subTypeFromAntlr!=null -> DataType(BaseDataType.STRUCT_INSTANCE, null, null, subTypeFromAntlr)
else -> throw IllegalArgumentException("cannot dereference this pointer type")
}
}
override fun toString(): String = when(base) { override fun toString(): String = when(base) {
BaseDataType.ARRAY -> { BaseDataType.ARRAY -> {
@@ -148,6 +232,15 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
else -> throw IllegalArgumentException("invalid sub type") else -> throw IllegalArgumentException("invalid sub type")
} }
} }
BaseDataType.POINTER -> {
if(sub!=null) "^^${sub.name.lowercase()}" else if(subType!=null) "^^${subType!!.scopedNameString}" else "^^${subTypeFromAntlr}"
}
BaseDataType.ARRAY_POINTER -> {
if(sub!=null) "^^${sub.name.lowercase()}[] (split)" else if (subType!=null) "^^${subType!!.scopedNameString}[] (split)" else "^^${subTypeFromAntlr}[] (split)"
}
BaseDataType.STRUCT_INSTANCE -> {
sub?.name?.lowercase() ?: if (subType!=null) subType!!.scopedNameString else "$subTypeFromAntlr"
}
else -> base.name.lowercase() else -> base.name.lowercase()
} }
@@ -160,6 +253,30 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
BaseDataType.LONG -> "long" BaseDataType.LONG -> "long"
BaseDataType.FLOAT -> "float" BaseDataType.FLOAT -> "float"
BaseDataType.STR -> "str" BaseDataType.STR -> "str"
BaseDataType.POINTER -> {
when {
sub!=null -> "^^${sub.name.lowercase()}"
subType!=null -> "^^${subType!!.scopedNameString}"
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}"
else -> "?????"
}
}
BaseDataType.STRUCT_INSTANCE -> {
when {
sub!=null -> sub.name.lowercase()
subType!=null -> subType!!.scopedNameString
subTypeFromAntlr!=null -> subTypeFromAntlr!!.joinToString(".")
else -> "?????"
}
}
BaseDataType.ARRAY_POINTER -> {
when {
sub!=null -> "^^${sub.name.lowercase()}["
subType!=null -> "^^${subType!!.scopedNameString}["
subTypeFromAntlr!=null -> "^^${subTypeFromAntlr!!.joinToString(".")}["
else -> "????? ["
}
}
BaseDataType.ARRAY -> { BaseDataType.ARRAY -> {
when(sub) { when(sub) {
BaseDataType.UBYTE -> "ubyte[" BaseDataType.UBYTE -> "ubyte["
@@ -187,18 +304,37 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT) BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT) BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT) BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT, BaseDataType.POINTER, BaseDataType.ARRAY_POINTER)
BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT) BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT) BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT) BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD) BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD) || (targetType.isPointer && targetType.sub==BaseDataType.UBYTE)
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
BaseDataType.POINTER -> {
when {
targetType.base == BaseDataType.UWORD || targetType.base == BaseDataType.LONG -> true
targetType.isPointer -> this.isUnsignedWord || this == targetType
else -> false
}
}
BaseDataType.STRUCT_INSTANCE -> false // we cannot deal with actual struct instances yet in any shape or form (only getting fields from it)
BaseDataType.ARRAY_POINTER -> false
BaseDataType.UNDEFINED -> false BaseDataType.UNDEFINED -> false
} }
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base) fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base)
fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base) fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base)
// note: for pointer types, size() doesn't return the size of the pointer itself but the size of the thing it points to
fun size(memsizer: IMemSizer): Int = if(sub!=null) {
memsizer.memorySize(sub)
} else if(subType!=null) {
subType!!.memsize(memsizer)
} else {
memsizer.memorySize(base)
}
val isBasic = sub==null && subType==null && subTypeFromAntlr==null
val isUndefined = base == BaseDataType.UNDEFINED val isUndefined = base == BaseDataType.UNDEFINED
val isByte = base.isByte val isByte = base.isByte
val isUnsignedByte = base == BaseDataType.UBYTE val isUnsignedByte = base == BaseDataType.UBYTE
@@ -214,22 +350,27 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
val isSigned = base.isSigned val isSigned = base.isSigned
val isUnsigned = !base.isSigned val isUnsigned = !base.isSigned
val isArray = base.isArray val isArray = base.isArray
val isBoolArray = base.isArray && sub == BaseDataType.BOOL val isPointer = base.isPointer
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE) val isPointerToByte = base.isPointer && sub?.isByteOrBool==true
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE val isPointerToWord = base.isPointer && sub?.isWord==true
val isSignedByteArray = base.isArray && sub == BaseDataType.BYTE val isStructInstance = base.isStructInstance
val isWordArray = base.isArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD) val isPointerArray = base.isPointerArray
val isUnsignedWordArray = base.isArray && sub == BaseDataType.UWORD val isBoolArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BOOL
val isSignedWordArray = base.isArray && sub == BaseDataType.WORD val isByteArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
val isFloatArray = base.isArray && sub == BaseDataType.FLOAT val isUnsignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UBYTE
val isSignedByteArray = base.isArray && !base.isPointerArray && sub == BaseDataType.BYTE
val isWordArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
val isUnsignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UWORD
val isSignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.WORD
val isFloatArray = base.isArray && !base.isPointerArray && sub == BaseDataType.FLOAT
val isString = base == BaseDataType.STR val isString = base == BaseDataType.STR
val isBool = base == BaseDataType.BOOL val isBool = base == BaseDataType.BOOL
val isFloat = base == BaseDataType.FLOAT val isFloat = base == BaseDataType.FLOAT
val isLong = base == BaseDataType.LONG val isLong = base == BaseDataType.LONG
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)) val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE))
val isSplitWordArray = base.isSplitWordArray val isSplitWordArray = base.isSplitWordArray
val isSplitUnsignedWordArray = base.isSplitWordArray && sub == BaseDataType.UWORD val isSplitUnsignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.UWORD
val isSplitSignedWordArray = base.isSplitWordArray && sub == BaseDataType.WORD val isSplitSignedWordArray = base.isSplitWordArray && !base.isPointerArray && sub == BaseDataType.WORD
val isIterable = base.isIterable val isIterable = base.isIterable
val isPassByRef = base.isPassByRef val isPassByRef = base.isPassByRef
val isPassByValue = base.isPassByValue val isPassByValue = base.isPassByValue
@@ -280,10 +421,13 @@ enum class RegisterOrPair {
BaseDataType.BYTE -> "sL" BaseDataType.BYTE -> "sL"
BaseDataType.WORD -> "s" BaseDataType.WORD -> "s"
BaseDataType.UWORD, null -> "" BaseDataType.UWORD, null -> ""
else -> throw kotlin.IllegalArgumentException("invalid register param type") else -> throw IllegalArgumentException("invalid register param type")
} }
return listOf("cx16", name.lowercase()+suffix) return listOf("cx16", name.lowercase()+suffix)
} }
fun isWord() = this==AX || this == AY || this==XY || this in Cx16VirtualRegisters
} // only used in parameter and return value specs in asm subroutines } // only used in parameter and return value specs in asm subroutines
enum class Statusflag { enum class Statusflag {

View File

@@ -23,8 +23,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
abstract val SCRATCH_B1 : UInt // temp storage for a single byte abstract val SCRATCH_B1 : UInt // temp storage for a single byte
abstract val SCRATCH_REG : UInt // temp storage for a register byte, must be B1+1 abstract val SCRATCH_REG : UInt // temp storage for a register byte, must be B1+1
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc abstract val SCRATCH_W1 : UInt // temp storage 1 for a word
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc abstract val SCRATCH_W2 : UInt // temp storage 2 for a word
abstract val SCRATCH_PTR : UInt // temp storage for a pointer
// the variables allocated into Zeropage. // the variables allocated into Zeropage.
@@ -38,7 +39,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
for (reserved in options.zpReserved) for (reserved in options.zpReserved)
reserve(reserved) reserve(reserved)
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u)) free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u, SCRATCH_PTR, SCRATCH_PTR+1u))
} }
} }
@@ -72,6 +73,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
val size: Int = val size: Int =
when { when {
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null) datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
datatype.isPointer -> options.compTarget.memorySize(datatype, null)
datatype.isString || datatype.isArray -> { datatype.isString || datatype.isArray -> {
val memsize = options.compTarget.memorySize(datatype, numElements!!) val memsize = options.compTarget.memorySize(datatype, numElements!!)
if(position!=null) if(position!=null)
@@ -122,6 +124,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
datatype.isNumericOrBool -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments datatype.isNumericOrBool -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
datatype.isString -> VarAllocation(address, datatype, size) datatype.isString -> VarAllocation(address, datatype, size)
datatype.isArray -> VarAllocation(address, datatype, size) datatype.isArray -> VarAllocation(address, datatype, size)
datatype.isPointer -> VarAllocation(address, datatype, size)
else -> throw AssemblyError("invalid dt") else -> throw AssemblyError("invalid dt")
} }
} }

View File

@@ -31,10 +31,10 @@ class C128Target: ICompilationTarget,
override val PROGRAM_LOAD_ADDRESS = 0x1c01u override val PROGRAM_LOAD_ADDRESS = 0x1c01u
override val PROGRAM_MEMTOP_ADDRESS = 0xc000u override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
override val BSSHIGHRAM_START = 0u // TODO override val BSSHIGHRAM_START = 0u // TODO address?
override val BSSHIGHRAM_END = 0u // TODO override val BSSHIGHRAM_END = 0u // TODO address?
override val BSSGOLDENRAM_START = 0u // TODO override val BSSGOLDENRAM_START = 0u // TODO address?
override val BSSGOLDENRAM_END = 0u // TODO override val BSSGOLDENRAM_END = 0u // TODO address?
override lateinit var zeropage: Zeropage override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam override lateinit var golden: GoldenRam

View File

@@ -9,7 +9,7 @@ import java.nio.file.Path
class C64Target: ICompilationTarget, class C64Target: ICompilationTarget,
IStringEncoding by Encoder(true), IStringEncoding by Encoder(true),
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) { IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
override val name = NAME override val name = NAME
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII

View File

@@ -33,6 +33,7 @@ class ConfigFileTarget(
val zpScratchReg: UInt, val zpScratchReg: UInt,
val zpScratchW1: UInt, val zpScratchW1: UInt,
val zpScratchW2: UInt, val zpScratchW2: UInt,
val zpScratchPtr: UInt,
val virtualregistersStart: UInt, val virtualregistersStart: UInt,
val zpFullsafe: List<UIntRange>, val zpFullsafe: List<UIntRange>,
val zpKernalsafe: List<UIntRange>, val zpKernalsafe: List<UIntRange>,
@@ -132,6 +133,7 @@ class ConfigFileTarget(
props.getInteger("zp_scratch_reg"), props.getInteger("zp_scratch_reg"),
props.getInteger("zp_scratch_w1"), props.getInteger("zp_scratch_w1"),
props.getInteger("zp_scratch_w2"), props.getInteger("zp_scratch_w2"),
props.getInteger("zp_scratch_ptr"),
props.getInteger("virtual_registers"), props.getInteger("virtual_registers"),
zpFullsafe, zpFullsafe,
zpKernalsafe, zpKernalsafe,
@@ -159,7 +161,7 @@ class ConfigFileTarget(
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) { override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = ConfigurableZeropage( zeropage = ConfigurableZeropage(
zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2, zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2, zpScratchPtr,
virtualregistersStart, virtualregistersStart,
zpBasicsafe, zpBasicsafe,
zpKernalsafe, zpKernalsafe,

View File

@@ -8,7 +8,7 @@ import java.nio.file.Path
class Cx16Target: ICompilationTarget, class Cx16Target: ICompilationTarget,
IStringEncoding by Encoder(true), IStringEncoding by Encoder(true),
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) { IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
override val name = NAME override val name = NAME
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII

View File

@@ -18,7 +18,7 @@ data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, va
// and https://en.wikipedia.org/wiki/IEEE_754-1985 // and https://en.wikipedia.org/wiki/IEEE_754-1985
val flt = num.toDouble() val flt = num.toDouble()
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE) if (flt !in FLOAT_MAX_NEGATIVE..FLOAT_MAX_POSITIVE)
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this") throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
if (flt == 0.0) if (flt == 0.0)
return zero return zero

View File

@@ -7,7 +7,9 @@ import prog8.code.core.IMemSizer
internal class NormalMemSizer(val floatsize: Int): IMemSizer { internal class NormalMemSizer(val floatsize: Int): IMemSizer {
override fun memorySize(dt: DataType, numElements: Int?): Int { override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray) { if(dt.isPointerArray)
return 2 * numElements!! // array of pointers is just array of uwords
else if(dt.isArray) {
if(numElements==null) return 2 // treat it as a pointer size if(numElements==null) return 2 // treat it as a pointer size
return when(dt.sub) { return when(dt.sub) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
@@ -26,6 +28,8 @@ internal class NormalMemSizer(val floatsize: Int): IMemSizer {
dt.isByteOrBool -> 1 * (numElements ?: 1) dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> floatsize * (numElements ?: 1) dt.isFloat -> floatsize * (numElements ?: 1)
dt.isLong -> 4 * (numElements ?: 1) dt.isLong -> 4 * (numElements ?: 1)
dt.isPointer -> 2 // pointer is just a uword
dt.isStructInstance -> dt.subType!!.memsize(this)
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size") dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
else -> 2 * (numElements ?: 1) else -> 2 * (numElements ?: 1)
} }

View File

@@ -8,7 +8,7 @@ import java.nio.file.Path
class PETTarget: ICompilationTarget, class PETTarget: ICompilationTarget,
IStringEncoding by Encoder(true), IStringEncoding by Encoder(true),
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) { IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
override val name = NAME override val name = NAME
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII

View File

@@ -3,6 +3,7 @@ package prog8.code.target
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.encodings.Encoder import prog8.code.target.encodings.Encoder
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.extension
import kotlin.io.path.isReadable import kotlin.io.path.isReadable
import kotlin.io.path.name import kotlin.io.path.name
import kotlin.io.path.readText import kotlin.io.path.readText
@@ -73,16 +74,11 @@ class VMTarget: ICompilationTarget,
// to not have external module dependencies in our own module, we launch the virtual machine via reflection // to not have external module dependencies in our own module, we launch the virtual machine via reflection
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
val filename = programNameWithPath.name val withExt = if(programNameWithPath.extension=="p8ir") programNameWithPath else programNameWithPath.resolveSibling("${programNameWithPath.name}.p8ir")
if(programNameWithPath.isReadable()) { if(withExt.isReadable())
vm.runProgram(programNameWithPath.readText(), quiet) vm.runProgram(withExt.readText(), quiet)
} else { else
val withExt = programNameWithPath.resolveSibling("$filename.p8ir") throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
if(withExt.isReadable())
vm.runProgram(withExt.readText(), quiet)
else
throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
}
} }
override fun isIOAddress(address: UInt): Boolean = false override fun isIOAddress(address: UInt): Boolean = false
@@ -108,4 +104,6 @@ private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_W2: UInt override val SCRATCH_W2: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable") get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_PTR: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
} }

View File

@@ -14,6 +14,7 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_REG = 0x75u // temp storage for a register byte, must be B1+1 override val SCRATCH_REG = 0x75u // temp storage for a register byte, must be B1+1
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
override val SCRATCH_PTR = 0x0bu // temp storage for a pointer $0b+$0c
init { init {
if (options.floats && options.zeropage !in arrayOf( if (options.floats && options.zeropage !in arrayOf(

View File

@@ -9,6 +9,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_REG = 0x03u // temp storage for a register byte, must be B1+1 override val SCRATCH_REG = 0x03u // temp storage for a register byte, must be B1+1
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
override val SCRATCH_PTR = 0x9bu // temp storage for a pointer $9b+$9c
init { init {
@@ -21,7 +22,6 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
if (options.zeropage == ZeropageType.FULL) { if (options.zeropage == ZeropageType.FULL) {
free.addAll(0x02u..0xffu) free.addAll(0x02u..0xffu)
free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
} else { } else {
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) { if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {

View File

@@ -9,6 +9,7 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_REG = 0x7bu // temp storage for a register byte, must be B1+1 override val SCRATCH_REG = 0x7bu // temp storage for a register byte, must be B1+1
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
override val SCRATCH_PTR = 0x22u // temp storage for a pointer $22+$23
init { init {

View File

@@ -10,6 +10,8 @@ class ConfigurableZeropage(
override val SCRATCH_REG: UInt, // temp storage for a register byte, must be B1+1 override val SCRATCH_REG: UInt, // temp storage for a register byte, must be B1+1
override val SCRATCH_W1: UInt, // temp storage 1 for a word override val SCRATCH_W1: UInt, // temp storage 1 for a word
override val SCRATCH_W2: UInt, // temp storage 2 for a word override val SCRATCH_W2: UInt, // temp storage 2 for a word
override val SCRATCH_PTR: UInt, // temp storage for a pointer
val virtualRegistersStart: UInt, // location of 32 bytes for the r0-r15 virtual registers val virtualRegistersStart: UInt, // location of 32 bytes for the r0-r15 virtual registers
basicsafe: List<UIntRange>, basicsafe: List<UIntRange>,
kernalsafe: List<UIntRange>, kernalsafe: List<UIntRange>,

View File

@@ -14,6 +14,7 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_REG = 0xb4u // temp storage for a register byte, must be B1+1 override val SCRATCH_REG = 0xb4u // temp storage for a register byte, must be B1+1
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
override val SCRATCH_PTR = 0xb1u // temp storage for a pointer $b1+$b2
init { init {
if (options.floats && options.zeropage !in arrayOf( if (options.floats && options.zeropage !in arrayOf(
@@ -34,7 +35,7 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
} }
ZeropageType.FLOATSAFE, ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE -> { ZeropageType.BASICSAFE -> {
free.addAll(0xb3u..0xbau) // TODO more? free.addAll(0xb1u..0xbau) // TODO more?
} }
ZeropageType.DONTUSE -> { ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all free.clear() // don't use zeropage at all

View File

@@ -1,5 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
kotlin("jvm") kotlin("jvm")
} }
@@ -9,12 +7,10 @@ dependencies {
implementation(project(":simpleAst")) implementation(project(":simpleAst"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1") implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1") testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
testImplementation("io.kotest:kotest-framework-datatest:5.9.1") testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
} }
sourceSets { sourceSets {

View File

@@ -13,7 +13,6 @@
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="simpleAst" /> <orderEntry type="module" module-name="simpleAst" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" /> <orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" /> <orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" /> <orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
</component> </component>

View File

@@ -30,14 +30,17 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
private fun prefixSymbols(program: PtProgram, options: CompilationOptions, st: SymbolTable): SymbolTable { private fun prefixSymbols(program: PtProgram, options: CompilationOptions, st: SymbolTable): SymbolTable {
val nodesToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index val nodesToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index
val functionCallsToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index val functionCallsToPrefix = mutableListOf<Pair<PtNode, Int>>() // parent + index
val expressionsToFixSubtype = mutableListOf<PtExpression>()
val variablesToFixSubtype = mutableListOf<IPtVariable>()
fun prefixNamedNode(node: PtNamedNode) { fun prefixNamedNode(node: PtNamedNode) {
when(node) { when(node) {
is PtAsmSub, is PtSub -> node.name = "p8s_${node.name}" is PtAsmSub, is PtSub -> node.name = "p8s_${node.name}"
is PtBlock -> node.name = "p8b_${node.name}" is PtBlock -> node.name = "p8b_${node.name}"
is PtLabel -> if(!node.name.startsWith(GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // don't prefix autogenerated labels is PtLabel -> if(!node.name.startsWith(GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // only prefix user defined labels
is PtConstant -> node.name = "p8c_${node.name}" is PtConstant -> node.name = "p8c_${node.name}"
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> node.name = "p8v_${node.name}" is PtVariable, is PtMemMapped, is PtSubroutineParameter -> node.name = "p8v_${node.name}"
is PtStructDecl -> node.name = "p8t_${node.name}"
} }
} }
@@ -50,10 +53,7 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
node.address!!.varbank = node.address!!.varbank!!.prefix(node, st) node.address!!.varbank = node.address!!.varbank!!.prefix(node, st)
} }
} }
is PtSub -> { is PtSub -> prefixNamedNode(node)
prefixNamedNode(node)
node.parameters.forEach { prefixNamedNode(it) }
}
is PtFunctionCall -> { is PtFunctionCall -> {
val stNode = st.lookup(node.name)!! val stNode = st.lookup(node.name)!!
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) { if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
@@ -62,11 +62,28 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
} }
} }
is PtIdentifier -> { is PtIdentifier -> {
// check if the identifier is part of a pointer dereference (which means you cannot look it up in the symboltable because it's a struct field)
val pexpr = node.parent as? PtBinaryExpression
if(pexpr?.operator==".") {
if(pexpr.left is PtArrayIndexer) {
val arrayvar = (pexpr.left as PtArrayIndexer).variable!!
if(arrayvar.type.subType!=null) {
// don't prefix a struct field name here, we take care of that at asm generation time, which was a lot easier
//nodesToPrefix += node.parent to node.parent.children.indexOf(node)
return
} else
throw AssemblyError("can only use deref expression on struct type")
} else {
TODO("handle other left operand ${pexpr.left }in dereferencing of struct fields ${node.position}")
}
}
// normal (non pointer deref) expression
var lookupName = node.name var lookupName = node.name
if(node.type.isSplitWordArray && (lookupName.endsWith("_lsb") || lookupName.endsWith("_msb"))) { if(node.type.isSplitWordArray && (lookupName.endsWith("_lsb") || lookupName.endsWith("_msb"))) {
lookupName = lookupName.dropLast(4) lookupName = lookupName.dropLast(4)
} }
val stNode = st.lookup(lookupName) ?: throw AssemblyError("unknown identifier $node") val stNode = st.lookup(lookupName) ?:
throw AssemblyError("unknown identifier $node")
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) { if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node) val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index nodesToPrefix += node.parent to index
@@ -81,8 +98,28 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
val index = node.parent.children.indexOf(node) val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index nodesToPrefix += node.parent to index
} }
is PtStructDecl -> prefixNamedNode(node) // note: field names are not prefixed here, we take care of that at asm generation time, which was a lot easier
is PtBuiltinFunctionCall -> {
// could be a struct instance creation
if(node.name=="prog8_lib_structalloc") {
val struct = node.type.subType!!
if(struct is StStruct) {
// update link to active symboltable node
node.type.subType = st.lookup(struct.scopedNameString) as StStruct
}
}
}
else -> { } else -> { }
} }
if(node is IPtVariable) {
if(node.type.subType!=null)
variablesToFixSubtype.add(node)
} else if(node is PtExpression) {
if(node.type.subType!=null)
expressionsToFixSubtype.add(node)
}
node.children.forEach { prefixSymbols(it) } node.children.forEach { prefixSymbols(it) }
} }
@@ -139,8 +176,40 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
} }
} }
return SymbolTableMaker(program, options).make()
val updatedSt = SymbolTableMaker(program, options).make()
fun findSubtypeReplacement(sub: ISubType): StStruct? {
if(updatedSt.lookup(sub.scopedNameString)!=null)
return null
val old = st.lookup(sub.scopedNameString)
if(old==null)
throw AssemblyError("old subtype not found: ${sub.scopedNameString}")
val prefixed = ArrayDeque<String>()
var node: StNode = old
while(node.type!= StNodeType.GLOBAL) {
val typeChar = typePrefixChar(node.type)
prefixed.addFirst("p8${typeChar}_${node.name}")
node=node.parent
}
return updatedSt.lookup(prefixed.joinToString(".")) as StStruct
}
expressionsToFixSubtype.forEach { node ->
findSubtypeReplacement(node.type.subType!!)?.let {
node.type.subType = it
}
}
variablesToFixSubtype.forEach { node ->
findSubtypeReplacement(node.type.subType!!)?.let {
node.type.subType = it
}
}
return updatedSt
} }
} }
private fun prefixScopedName(name: String, type: Char): String { private fun prefixScopedName(name: String, type: Char): String {
@@ -175,14 +244,22 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
if(elt.definingBlock()?.options?.noSymbolPrefixing==true) if(elt.definingBlock()?.options?.noSymbolPrefixing==true)
newValue.add(elt) newValue.add(elt)
else { else {
val newAddr = PtAddressOf(elt.position) val newAddr = PtAddressOf(elt.type, false, elt.position)
newAddr.children.add(elt.identifier.prefix(newAddr, st)) newAddr.add(elt.identifier!!.prefix(newAddr, st))
if(elt.arrayIndexExpr!=null) if (elt.arrayIndexExpr != null)
newAddr.children.add(elt.arrayIndexExpr!!) newAddr.add(elt.arrayIndexExpr!!)
newAddr.parent = arrayValue newAddr.parent = arrayValue
newValue.add(newAddr) newValue.add(newAddr)
} }
} }
is PtBuiltinFunctionCall -> {
// could be a struct instance or memory slab "allocation"
if (elt.name != "prog8_lib_structalloc" && elt.name != "memory")
throw AssemblyError("weird array value element $elt")
else {
newValue.add(elt)
}
}
else -> throw AssemblyError("weird array value element $elt") else -> throw AssemblyError("weird array value element $elt")
} }
} }
@@ -207,19 +284,34 @@ private fun PtFunctionCall.withNewName(name: String): PtFunctionCall {
} }
private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier { private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
var target = st.lookup(name) val targetNt: StNodeType
val target = st.lookup(name)
if(target?.astNode?.definingBlock()?.options?.noSymbolPrefixing==true) if(target?.astNode?.definingBlock()?.options?.noSymbolPrefixing==true)
return this return this
if(target==null) { if(target==null) {
if(name.endsWith("_lsb") || name.endsWith("_msb")) { if(name.endsWith("_lsb") || name.endsWith("_msb")) {
target = st.lookup(name.dropLast(4)) val target2 = st.lookup(name.dropLast(4))
if(target?.astNode?.definingBlock()?.options?.noSymbolPrefixing==true) if(target2?.astNode?.definingBlock()?.options?.noSymbolPrefixing==true)
return this return this
targetNt = target2!!.type
} else {
// if no target found, assume that the identifier is a struct field
targetNt = StNodeType.STATICVAR
} }
} else {
targetNt = target.type
} }
val prefixType = when(target!!.type) { val prefixType = typePrefixChar(targetNt)
val newName = prefixScopedName(name, prefixType)
val node = PtIdentifier(newName, type, position)
node.parent = parent
return node
}
private fun typePrefixChar(targetNt: StNodeType): Char {
return when(targetNt) {
StNodeType.BLOCK -> 'b' StNodeType.BLOCK -> 'b'
StNodeType.SUBROUTINE, StNodeType.EXTSUB -> 's' StNodeType.SUBROUTINE, StNodeType.EXTSUB -> 's'
StNodeType.LABEL -> 'l' StNodeType.LABEL -> 'l'
@@ -227,12 +319,10 @@ private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
StNodeType.CONSTANT -> 'c' StNodeType.CONSTANT -> 'c'
StNodeType.BUILTINFUNC -> 's' StNodeType.BUILTINFUNC -> 's'
StNodeType.MEMORYSLAB -> 'v' StNodeType.MEMORYSLAB -> 'v'
StNodeType.STRUCT -> 't'
StNodeType.STRUCTINSTANCE -> 'i'
else -> '?' else -> '?'
} }
val newName = prefixScopedName(name, prefixType)
val node = PtIdentifier(newName, type, position)
node.parent = parent
return node
} }
@@ -255,10 +345,17 @@ class AsmGen6502Internal (
private val functioncallAsmGen = FunctionCallAsmGen(program, this) private val functioncallAsmGen = FunctionCallAsmGen(program, this)
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage) private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
private val anyExprGen = AnyExprAsmGen(this) private val anyExprGen = AnyExprAsmGen(this)
private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator) private val pointerGen = PointerAssignmentsGen(this, allocator)
private val assignmentAsmGen = AssignmentAsmGen(program, this, pointerGen, anyExprGen, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen) private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, assignmentAsmGen, errors) private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, pointerGen, assignmentAsmGen, errors)
private val ifExpressionAsmgen = IfExpressionAsmGen(this, assignmentAsmGen, errors) private val ifExpressionAsmgen = IfExpressionAsmGen(this, assignmentAsmGen, errors)
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, assignmentAsmGen, this, pointerGen, allocator)
init {
assignmentAsmGen.augmentableAsmGen = augmentableAsmGen
pointerGen.augmentableAsmGen = augmentableAsmGen
}
fun compileToAssembly(): IAssemblyProgram? { fun compileToAssembly(): IAssemblyProgram? {
@@ -316,9 +413,9 @@ class AsmGen6502Internal (
if(symbolTable.allVariables.isNotEmpty()) { if(symbolTable.allVariables.isNotEmpty()) {
println("Static variables (not in ZeroPage):") println("Static variables (not in ZeroPage):")
symbolTable.allVariables symbolTable.allVariables
.filterNot { allocator.isZpVar(it.scopedName) } .filterNot { allocator.isZpVar(it.scopedNameString) }
.sortedBy { it.scopedName }.forEach { .sortedBy { it.scopedNameString }.forEach {
println(" ${it.dt}\t${it.scopedName}\t") println(" ${it.dt}\t${it.scopedNameString}\t")
} }
} }
if(allocator.globalFloatConsts.isNotEmpty()) { if(allocator.globalFloatConsts.isNotEmpty()) {
@@ -330,9 +427,9 @@ class AsmGen6502Internal (
if(symbolTable.allMemMappedVariables.isNotEmpty()) { if(symbolTable.allMemMappedVariables.isNotEmpty()) {
println("Memory mapped:") println("Memory mapped:")
symbolTable.allMemMappedVariables symbolTable.allMemMappedVariables
.sortedWith( compareBy( {it.address}, {it.scopedName} )) .sortedWith( compareBy( {it.address}, {it.scopedNameString} ))
.forEach { mvar -> .forEach { mvar ->
println(" $${mvar.address.toString(16).padStart(4, '0')}\t${mvar.dt}\t${mvar.scopedName}") println(" $${mvar.address.toString(16).padStart(4, '0')}\t${mvar.dt}\t${mvar.scopedNameString}")
} }
} }
if(symbolTable.allMemorySlabs.isNotEmpty()) { if(symbolTable.allMemorySlabs.isNotEmpty()) {
@@ -420,7 +517,7 @@ class AsmGen6502Internal (
} }
fun asmVariableName(st: StNode, scope: IPtSubroutine?): String { fun asmVariableName(st: StNode, scope: IPtSubroutine?): String {
val name = asmVariableName(st.scopedName) val name = asmVariableName(st.scopedNameString)
if(scope==null) if(scope==null)
return name return name
// remove the whole prefix and just make the variable name locally scoped (64tass scopes it to the proper .proc block) // remove the whole prefix and just make the variable name locally scoped (64tass scopes it to the proper .proc block)
@@ -434,7 +531,7 @@ class AsmGen6502Internal (
internal fun loadByteFromPointerIntoA(pointervar: PtIdentifier): String { internal fun loadByteFromPointerIntoA(pointervar: PtIdentifier): String {
// returns the source name of the zero page pointervar if it's already in the ZP, // returns the source name of the zero page pointervar if it's already in the ZP,
// otherwise returns "P8ZP_SCRATCH_W1" which is the intermediary // otherwise returns "P8ZP_SCRATCH_PTR" which is the intermediary
val symbol = symbolTable.lookup(pointervar.name) val symbol = symbolTable.lookup(pointervar.name)
when (val target = symbol!!.astNode) { when (val target = symbol!!.astNode) {
is PtLabel -> { is PtLabel -> {
@@ -453,10 +550,10 @@ class AsmGen6502Internal (
out(""" out("""
lda $sourceName lda $sourceName
ldy $sourceName+1 ldy $sourceName+1
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_PTR
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_PTR+1
lda (P8ZP_SCRATCH_W1)""") lda (P8ZP_SCRATCH_PTR)""")
"P8ZP_SCRATCH_W1" "P8ZP_SCRATCH_PTR"
} }
} else { } else {
return if (allocator.isZpVar((target as PtNamedNode).scopedName)) { return if (allocator.isZpVar((target as PtNamedNode).scopedName)) {
@@ -467,11 +564,11 @@ class AsmGen6502Internal (
out(""" out("""
lda $sourceName lda $sourceName
ldy $sourceName+1 ldy $sourceName+1
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_PTR
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_PTR+1
ldy #0 ldy #0
lda (P8ZP_SCRATCH_W1),y""") lda (P8ZP_SCRATCH_PTR),y""")
"P8ZP_SCRATCH_W1" "P8ZP_SCRATCH_PTR"
} }
} }
} }
@@ -488,10 +585,10 @@ class AsmGen6502Internal (
} else { } else {
out(""" out("""
ldy $sourceName ldy $sourceName
sty P8ZP_SCRATCH_W2 sty P8ZP_SCRATCH_PTR
ldy $sourceName+1 ldy $sourceName+1
sty P8ZP_SCRATCH_W2+1 sty P8ZP_SCRATCH_PTR+1
sta (P8ZP_SCRATCH_W2)""") sta (P8ZP_SCRATCH_PTR)""")
} }
} else { } else {
if (allocator.isZpVar(pointervar.name)) { if (allocator.isZpVar(pointervar.name)) {
@@ -500,11 +597,11 @@ class AsmGen6502Internal (
} else { } else {
out(""" out("""
ldy $sourceName ldy $sourceName
sty P8ZP_SCRATCH_W2 sty P8ZP_SCRATCH_PTR
ldy $sourceName+1 ldy $sourceName+1
sty P8ZP_SCRATCH_W2+1 sty P8ZP_SCRATCH_PTR+1
ldy #0 ldy #0
sta (P8ZP_SCRATCH_W2),y""") sta (P8ZP_SCRATCH_PTR),y""")
} }
} }
} }
@@ -621,7 +718,7 @@ class AsmGen6502Internal (
is PtDefer -> throw AssemblyError("defer should have been transformed") is PtDefer -> throw AssemblyError("defer should have been transformed")
is PtNodeGroup -> stmt.children.forEach { translate(it) } is PtNodeGroup -> stmt.children.forEach { translate(it) }
is PtJmpTable -> translate(stmt) is PtJmpTable -> translate(stmt)
is PtNop -> {} is PtNop, is PtStructDecl, is PtSubSignature -> {}
else -> throw AssemblyError("missing asm translation for $stmt") else -> throw AssemblyError("missing asm translation for $stmt")
} }
} }
@@ -630,6 +727,8 @@ class AsmGen6502Internal (
val reg = register.toString().lowercase() val reg = register.toString().lowercase()
val indexnum = expr.index.asConstInteger() val indexnum = expr.index.asConstInteger()
if (indexnum != null) { if (indexnum != null) {
if(indexnum > 255)
throw AssemblyError("array index $indexnum is larger than a byte ${expr.position}")
val indexValue = if(expr.splitWords) val indexValue = if(expr.splitWords)
indexnum indexnum
else else
@@ -638,6 +737,9 @@ class AsmGen6502Internal (
return return
} }
if(!expr.index.type.isByte)
throw AssemblyError("array index $indexnum is larger than a byte ${expr.position}")
if(expr.splitWords) { if(expr.splitWords) {
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register)) assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register))
return return
@@ -717,15 +819,15 @@ class AsmGen6502Internal (
when(target.kind) { when(target.kind) {
TargetStorageKind.VARIABLE -> { TargetStorageKind.VARIABLE -> {
if (isTargetCpu(CpuType.CPU6502)) if (isTargetCpu(CpuType.CPU6502))
out("lda #0 | sta ${target.asmVarname}") out(" lda #0 | sta ${target.asmVarname}")
else else
out("stz ${target.asmVarname}") out(" stz ${target.asmVarname}")
} }
TargetStorageKind.MEMORY -> { TargetStorageKind.MEMORY -> {
val address = target.memory!!.address.asConstInteger() val address = target.memory!!.address.asConstInteger()
if(address!=null) { if(address!=null) {
if (isTargetCpu(CpuType.CPU6502)) if (isTargetCpu(CpuType.CPU6502))
out("lda #0 | sta ${address.toHex()}") out(" lda #0 | sta ${address.toHex()}")
else else
out(" stz ${address.toHex()}") out(" stz ${address.toHex()}")
return return
@@ -737,6 +839,10 @@ class AsmGen6502Internal (
assignExpressionToRegister(zero, target.register!!) assignExpressionToRegister(zero, target.register!!)
return return
} }
TargetStorageKind.POINTER -> {
TODO("assign to pointer ${target.position}")
return
}
else -> { } else -> { }
} }
} }
@@ -744,6 +850,7 @@ class AsmGen6502Internal (
assignExpressionToRegister(value, RegisterOrPair.A) assignExpressionToRegister(value, RegisterOrPair.A)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, target.datatype.isSigned, false) assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, target.datatype.isSigned, false)
} }
target.datatype.isPointer -> TODO("assign expression to pointer ${target.position}")
target.datatype.isWord || target.datatype.isPassByRef -> { target.datatype.isWord || target.datatype.isPassByRef -> {
assignExpressionToRegister(value, RegisterOrPair.AY) assignExpressionToRegister(value, RegisterOrPair.AY)
translateNormalAssignment( translateNormalAssignment(
@@ -761,7 +868,7 @@ class AsmGen6502Internal (
} }
} }
private fun branchInstruction(condition: BranchCondition, complement: Boolean) = internal fun branchInstruction(condition: BranchCondition, complement: Boolean) =
if(complement) { if(complement) {
when (condition) { when (condition) {
BranchCondition.CS -> "bcc" BranchCondition.CS -> "bcc"
@@ -796,7 +903,7 @@ class AsmGen6502Internal (
when { when {
iterations == 0 -> {} iterations == 0 -> {}
iterations == 1 -> translate(stmt.statements) iterations == 1 -> translate(stmt.statements)
iterations<0 || iterations>65536 -> throw AssemblyError("invalid number of iterations") iterations !in 0..65536 -> throw AssemblyError("invalid number of iterations")
iterations <= 256 -> repeatByteCount(iterations, stmt) iterations <= 256 -> repeatByteCount(iterations, stmt)
else -> repeatWordCount(iterations, stmt) else -> repeatWordCount(iterations, stmt)
} }
@@ -838,7 +945,7 @@ class AsmGen6502Internal (
private fun repeatWordCount(iterations: Int, stmt: PtRepeatLoop) { private fun repeatWordCount(iterations: Int, stmt: PtRepeatLoop) {
require(iterations in 257..65536) { "invalid repeat count ${stmt.position}" } require(iterations in 257..65536) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
val counterVar = createTempVarReused(BaseDataType.UWORD, isTargetCpu(CpuType.CPU65C02), stmt) val counterVar = createTempVarReused(BaseDataType.UWORD, true, stmt)
val loopcount = if(iterations==65536) 0 else if(iterations and 0x00ff == 0) iterations else iterations + 0x0100 // so that the loop can simply use a double-dec val loopcount = if(iterations==65536) 0 else if(iterations and 0x00ff == 0) iterations else iterations + 0x0100 // so that the loop can simply use a double-dec
out(""" out("""
ldy #>$loopcount ldy #>$loopcount
@@ -858,7 +965,7 @@ $repeatLabel""")
// note: A/Y must have been loaded with the number of iterations! // note: A/Y must have been loaded with the number of iterations!
// the iny + double dec is microoptimization of the 16 bit loop // the iny + double dec is microoptimization of the 16 bit loop
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
val counterVar = createTempVarReused(BaseDataType.UWORD, false, stmt) val counterVar = createTempVarReused(BaseDataType.UWORD, true, stmt)
out(""" out("""
cmp #0 cmp #0
beq + beq +
@@ -1046,17 +1153,18 @@ $repeatLabel""")
if(evaluateAddressExpression) { if(evaluateAddressExpression) {
val arrayIdx = jump.target as? PtArrayIndexer val arrayIdx = jump.target as? PtArrayIndexer
if (arrayIdx!=null) { if (arrayIdx!=null) {
val arrayVariable = arrayIdx.variable ?: TODO("support for ptr indexing ${arrayIdx.position}")
if (isTargetCpu(CpuType.CPU65C02)) { if (isTargetCpu(CpuType.CPU65C02)) {
if (!arrayIdx.splitWords) { if (!arrayIdx.splitWords) {
// if the jump target is an address in a non-split array (like a jump table of only pointers), // if the jump target is an address in a non-split array (like a jump table of only pointers),
// on the 65c02, more optimal assembly can be generated using JMP (address,X) // on the 65c02, more optimal assembly can be generated using JMP (address,X)
assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A) assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
out(" asl a | tax") out(" asl a | tax")
return JumpTarget(asmSymbolName(arrayIdx.variable), true, true, false) return JumpTarget(asmSymbolName(arrayVariable), true, true, false)
} else { } else {
// print a message when more optimal code is possible for 65C02 cpu // print a message when more optimal code is possible for 65C02 cpu
val variable = symbolTable.lookup(arrayIdx.variable.name)!! val variable = symbolTable.lookup(arrayVariable.name)!!
if(variable is StStaticVariable && variable.length!!<=128) if(variable is StStaticVariable && variable.length!!<=128u)
errors.info("the jump address array is @split, but @nosplit would create more efficient code here", jump.position) errors.info("the jump address array is @split, but @nosplit would create more efficient code here", jump.position)
} }
} else { } else {
@@ -1075,18 +1183,20 @@ $repeatLabel""")
} }
private fun translate(ret: PtReturn) { private fun translate(ret: PtReturn) {
val returnvalue = ret.children.singleOrNull() val returnvalue = ret.children.singleOrNull() as? PtExpression
val sub = ret.definingSub()!! val sub = ret.definingSub()!!
val returnRegs = sub.returnsWhatWhere() val returnRegs = sub.returnsWhatWhere()
if(returnvalue!=null) { if(returnvalue!=null) {
if (sub.returns.single().isNumericOrBool) { val returnDt = sub.signature.returns.single()
assignExpressionToRegister(returnvalue as PtExpression, returnRegs.single().first.registerOrPair!!) if (returnDt.isNumericOrBool || returnDt.isPointer) {
assignExpressionToRegister(returnvalue, returnRegs.single().first.registerOrPair!!)
} }
else { else {
// all else take its address and assign that also to AY register pair // all else take its address and assign that also to AY register pair
val addrofValue = PtAddressOf(returnvalue.position) val addrOfDt = returnvalue.type.typeForAddressOf(false)
addrofValue.add(returnvalue as PtIdentifier) val addrofValue = PtAddressOf(addrOfDt, false, returnvalue.position)
addrofValue.add(returnvalue)
addrofValue.parent = ret.parent addrofValue.parent = ret.parent
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnRegs.single().first.registerOrPair!!, false) assignmentAsmGen.assignExpressionToRegister(addrofValue, returnRegs.single().first.registerOrPair!!, false)
} }
@@ -1213,11 +1323,11 @@ $repeatLabel""")
return null return null
val leftDt = left.type val leftDt = left.type
val rightDt = right.type val rightDt = right.type
if(leftDt.isUnsignedWord && rightDt.isUnsignedByte) if((leftDt.isUnsignedWord || leftDt.isPointer) && rightDt.isUnsignedByte)
return Pair(left, right) return Pair(left, right)
if(leftDt.isUnsignedByte && rightDt.isUnsignedWord) if(leftDt.isUnsignedByte && rightDt.isUnsignedWord)
return Pair(right, left) return Pair(right, left)
if(leftDt.isUnsignedWord && rightDt.isUnsignedWord) { if((leftDt.isUnsignedWord || leftDt.isPointer) && rightDt.isUnsignedWord) {
// could be that the index was a constant numeric byte but converted to word, check that // could be that the index was a constant numeric byte but converted to word, check that
val constIdx = right as? PtNumber val constIdx = right as? PtNumber
if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) { if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) {
@@ -1253,9 +1363,7 @@ $repeatLabel""")
} }
if(addressExpr.operator=="+") { if(addressExpr.operator=="+") {
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr) val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr) ?: return false
if (ptrAndIndex == null) return false
if(write) { if(write) {
// WRITING TO pointer + offset // WRITING TO pointer + offset
@@ -1265,8 +1373,10 @@ $repeatLabel""")
if(addrOf!=null && constOffset!=null) { if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) { if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf") TODO("address-of array element $addrOf")
} else if(addrOf.dereference!=null) {
throw AssemblyError("write &dereference, makes no sense at ${addrOf.position}")
} else { } else {
out(" sta ${asmSymbolName(addrOf.identifier)}+${constOffset}") out(" sta ${asmSymbolName(addrOf.identifier!!)}+${constOffset}")
return true return true
} }
} }
@@ -1305,8 +1415,10 @@ $repeatLabel""")
if(addrOf!=null && constOffset!=null) { if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) { if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf") TODO("address-of array element $addrOf")
} else if(addrOf.dereference!=null) {
TODO("read &dereference")
} else { } else {
out(" lda ${asmSymbolName(addrOf.identifier)}+${constOffset}") out(" lda ${asmSymbolName(addrOf.identifier!!)}+${constOffset}")
return true return true
} }
} }
@@ -1343,9 +1455,7 @@ $repeatLabel""")
} }
else if(addressExpr.operator=="-") { else if(addressExpr.operator=="-") {
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, true) val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, true) ?: return false
if (ptrAndIndex == null) return false
if(write) { if(write) {
// WRITING TO pointer - offset // WRITING TO pointer - offset
@@ -1355,8 +1465,10 @@ $repeatLabel""")
if(addrOf!=null && constOffset!=null) { if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) { if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf") TODO("address-of array element $addrOf")
} else if(addrOf.dereference!=null) {
throw AssemblyError("write &dereference, makes no sense at ${addrOf.position}")
} else { } else {
out(" sta ${asmSymbolName(addrOf.identifier)}-${constOffset}") out(" sta ${asmSymbolName(addrOf.identifier!!)}-${constOffset}")
return true return true
} }
} }
@@ -1386,8 +1498,10 @@ $repeatLabel""")
if(addrOf!=null && constOffset!=null) { if(addrOf!=null && constOffset!=null) {
if(addrOf.isFromArrayElement) { if(addrOf.isFromArrayElement) {
TODO("address-of array element $addrOf") TODO("address-of array element $addrOf")
} else if(addrOf.dereference!=null) {
TODO("read &dereference")
} else { } else {
out(" lda ${asmSymbolName(addrOf.identifier)}-${constOffset}") out(" lda ${asmSymbolName(addrOf.identifier!!)}-${constOffset}")
return true return true
} }
} }
@@ -1418,7 +1532,15 @@ $repeatLabel""")
val node = stScope.astNode val node = stScope.astNode
if(node is PtSubroutineParameter) if(node is PtSubroutineParameter)
return node return node
return node!!.definingSub()?.parameters?.singleOrNull { it.name===name } val params = node!!.definingSub()?.signature?.children
if(params!=null) {
for(param in params) {
param as PtSubroutineParameter
if(param.scopedName==name)
return param
}
}
return null
} }
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) { internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
@@ -1584,6 +1706,10 @@ $repeatLabel""")
ifExpressionAsmgen.assignIfExpression(target, value) ifExpressionAsmgen.assignIfExpression(target, value)
} }
internal fun assignBranchCondExpression(target: AsmAssignTarget, value: PtBranchCondExpression) {
ifExpressionAsmgen.assignBranchCondExpression(target, value)
}
internal fun cmpAwithByteValue(value: PtExpression, useSbc: Boolean) { internal fun cmpAwithByteValue(value: PtExpression, useSbc: Boolean) {
val compare = if(useSbc) "sec | sbc" else "cmp" val compare = if(useSbc) "sec | sbc" else "cmp"
fun cmpViaScratch() { fun cmpViaScratch() {
@@ -1604,7 +1730,9 @@ $repeatLabel""")
if(constIndex!=null) { if(constIndex!=null) {
val offset = program.memsizer.memorySize(value.type, constIndex) val offset = program.memsizer.memorySize(value.type, constIndex)
if(offset<256) { if(offset<256) {
return out(" ldy #$offset | $compare ${asmVariableName(value.variable)},y") if(value.variable==null)
TODO("support for ptr indexing ${value.position}")
return out(" ldy #$offset | $compare ${asmVariableName(value.variable!!)},y")
} }
} }
cmpViaScratch() cmpViaScratch()
@@ -1775,7 +1903,372 @@ $repeatLabel""")
return null return null
} }
fun romableError(problem: String, pos: Position, assemblerShouldFail: Boolean = true) { internal fun loadIndirectByte(zpPtrVar: String, offset: UByte) {
// loads byte pointed to by the ptrvar into A
if (offset > 0u) {
out(" ldy #$offset | lda ($zpPtrVar),y")
} else {
if(isTargetCpu(CpuType.CPU65C02))
out(" lda ($zpPtrVar)")
else
out(" ldy #0 | lda ($zpPtrVar),y")
}
}
internal fun loadIndirectFloat(zpPtrVar: String, offset: UByte) {
// loads float pointed to by the ptrvar into FAC1
if (offset > 0u) {
out("""
lda $zpPtrVar
ldy $zpPtrVar+1
clc
adc #$offset
bcc +
iny
+ jsr floats.MOVFM""")
return
}
out("""
lda $zpPtrVar
ldy $zpPtrVar+1
jsr floats.MOVFM""")
}
internal fun loadIndirectWord(zpPtrVar: String, offset: UByte) {
// loads word pointed to by the ptr var into AY
if (offset > 0u) {
out("""
ldy #$offset
lda ($zpPtrVar),y
tax
iny
lda ($zpPtrVar),y
tay
txa""")
} else {
if(isTargetCpu(CpuType.CPU65C02))
out("""
ldy #1
lda ($zpPtrVar),y
tay
lda ($zpPtrVar)""")
else
out("""
ldy #0
lda ($zpPtrVar),y
tax
iny
lda ($zpPtrVar),y
tay
txa""")
}
}
internal fun storeIndirectByte(byte: Int, zpPtrVar: String, offset: UByte) {
if (offset > 0u) {
out(" lda #$byte | ldy #$offset | sta ($zpPtrVar),y")
} else {
if(isTargetCpu(CpuType.CPU65C02)) {
out(" lda #$byte | sta ($zpPtrVar)")
} else {
if (byte == 0)
out(" lda #0 | tay | sta ($zpPtrVar),y")
else
out(" lda #$byte | ldy #0 | sta ($zpPtrVar),y")
}
}
}
internal fun storeIndirectByteVar(varname: String, zpPtrVar: String, offset: UByte) {
if (offset > 0u) {
out(" lda $varname | ldy #$offset | sta ($zpPtrVar),y")
} else {
if(isTargetCpu(CpuType.CPU65C02))
out(" lda $varname | sta ($zpPtrVar)")
else
out(" lda $varname | ldy #0 | sta ($zpPtrVar),y")
}
}
internal fun storeIndirectWord(word: Int, zpPtrVar: String, offset: UByte) {
if (offset > 0u) {
out("""
lda #<$word
ldy #$offset
sta ($zpPtrVar),y
lda #>$word
iny
sta ($zpPtrVar),y""")
} else {
if(word==0) {
out("""
lda #0
tay
sta ($zpPtrVar),y
iny
sta ($zpPtrVar),y""")
} else {
out("""
lda #<$word
ldy #0
sta ($zpPtrVar),y
lda #>$word
iny
sta ($zpPtrVar),y""")
}
}
}
internal fun storeIndirectByteReg(
register: CpuRegister,
zpPtrVar: String,
offset: UByte,
signed: Boolean,
extendWord: Boolean
) {
if(extendWord) {
when(register) {
CpuRegister.A -> {}
CpuRegister.X -> out(" txa")
CpuRegister.Y -> out(" tya")
}
signExtendAXlsb(if(signed) BaseDataType.BYTE else BaseDataType.UBYTE)
out("""
ldy #$offset
sta ($zpPtrVar),y
iny
txa
sta ($zpPtrVar),y""")
return
}
if(offset > 0u) {
when(register) {
CpuRegister.A -> out(" ldy #$offset | sta ($zpPtrVar),y")
CpuRegister.X -> out(" txa | ldy #$offset | sta ($zpPtrVar),y")
CpuRegister.Y -> out(" tya | ldy #$offset | sta ($zpPtrVar),y")
}
return
}
when(register) {
CpuRegister.A -> {
if(isTargetCpu(CpuType.CPU65C02)) out(" sta ($zpPtrVar)") else out(" ldy #0 | sta ($zpPtrVar),y")
}
CpuRegister.X -> {
out(" txa")
if(isTargetCpu(CpuType.CPU65C02)) out(" sta ($zpPtrVar)") else out(" ldy #0 | sta ($zpPtrVar),y")
}
CpuRegister.Y -> {
out(" tya")
if(isTargetCpu(CpuType.CPU65C02)) out(" sta ($zpPtrVar)") else out(" ldy #0 | sta ($zpPtrVar),y")
}
}
}
internal fun storeIndirectWordReg(regs: RegisterOrPair, zpPtrVar: String, offset: UByte) {
if (offset > 0u) {
when(regs) {
RegisterOrPair.AX -> {
out("""
ldy #$offset
sta ($zpPtrVar),y
txa
iny
sta ($zpPtrVar),y""")
}
RegisterOrPair.AY -> {
out("""
sty P8ZP_SCRATCH_REG
ldy #$offset
sta ($zpPtrVar),y
lda P8ZP_SCRATCH_REG
iny
sta ($zpPtrVar),y""")
}
RegisterOrPair.XY -> {
out("""
sty P8ZP_SCRATCH_REG
txa
ldy #$offset
sta ($zpPtrVar),y
lda P8ZP_SCRATCH_REG
iny
sta ($zpPtrVar),y""")
}
in Cx16VirtualRegisters -> {
val regname = regs.asScopedNameVirtualReg(DataType.UWORD)
out("""
lda $regname
ldy #$offset
sta ($zpPtrVar),y
lda $regname+1
iny
sta ($zpPtrVar),y""")
}
else -> throw AssemblyError("wrong word reg")
}
} else {
when(regs) {
RegisterOrPair.AX -> {
if(isTargetCpu(CpuType.CPU65C02))
out("""
sta ($zpPtrVar)
txa
ldy #1
sta ($zpPtrVar),y""")
else
out("""
ldy #0
sta ($zpPtrVar),y
txa
iny
sta ($zpPtrVar),y""")
}
RegisterOrPair.AY -> {
if(isTargetCpu(CpuType.CPU65C02))
out("""
sta ($zpPtrVar)
tya
ldy #1
sta ($zpPtrVar),y""")
else
out("""
sty P8ZP_SCRATCH_REG
ldy #0
sta ($zpPtrVar),y
lda P8ZP_SCRATCH_REG
iny
sta ($zpPtrVar),y""")
}
RegisterOrPair.XY -> {
if(isTargetCpu(CpuType.CPU65C02))
out("""
txa
sta ($zpPtrVar)
tya
ldy #1
sta ($zpPtrVar),y""")
else
out("""
sty P8ZP_SCRATCH_REG
txa
ldy #0
sta ($zpPtrVar),y
lda P8ZP_SCRATCH_REG
iny
sta ($zpPtrVar),y""")
}
in Cx16VirtualRegisters -> {
val regname = regs.asScopedNameVirtualReg(DataType.UWORD)
out("""
lda $regname
ldy #0
sta ($zpPtrVar),y
lda $regname+1
iny
sta ($zpPtrVar),y""")
}
else -> throw AssemblyError("wrong word reg")
}
}
}
internal fun storeIndirectWordVar(varname: String, sourceDt: DataType, zpPtrVar: String, offset: UByte) {
if(sourceDt.isByteOrBool) TODO("implement byte/bool to word pointer assignment")
if (offset > 0u) {
out("""
lda $varname
ldy #$offset
sta ($zpPtrVar),y
lda $varname+1
iny
sta ($zpPtrVar),y""")
} else {
if(isTargetCpu(CpuType.CPU65C02))
out("""
lda $varname
sta ($zpPtrVar)
lda $varname+1
ldy #1
sta ($zpPtrVar),y""")
else
out("""
lda $varname
ldy #0
sta ($zpPtrVar),y
lda $varname+1
iny
sta ($zpPtrVar),y""")
}
}
internal fun storeIndirectFloat(float: Double, zpPtrVar: String, offset: UByte) {
val floatConst = allocator.getFloatAsmConst(float)
if (offset > 0u) {
out("""
lda #<$floatConst
ldy #>$floatConst
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda $zpPtrVar
ldy $zpPtrVar+1
clc
adc #$offset
bcc +
iny
+ jsr floats.copy_float2""")
return
}
out("""
lda #<$floatConst
ldy #>$floatConst
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda $zpPtrVar
ldy $zpPtrVar+1
jsr floats.copy_float2""")
}
internal fun storeIndirectFloatVar(varname: String, zpPtrVar: String, offset: UByte) {
if (offset > 0u) {
out("""
lda #<$varname
ldy #>$varname+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda $zpPtrVar
ldy $zpPtrVar+1
clc
adc #$offset
bcc +
iny
+ jsr floats.copy_float""")
return
}
out("""
lda #<$varname
ldy #>$varname+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda $zpPtrVar
ldy $zpPtrVar+1
jsr floats.copy_float""")
}
internal fun romableError(problem: String, pos: Position, assemblerShouldFail: Boolean = true) {
if(options.romable) { if(options.romable) {
// until the code generation can provide an alternative, we have to report about code generated that is incompatible with ROMable code mode... // until the code generation can provide an alternative, we have to report about code generated that is incompatible with ROMable code mode...
errors.warn("problem for ROMable code: $problem", pos) errors.warn("problem for ROMable code: $problem", pos)

View File

@@ -73,6 +73,13 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationT
numberOfOptimizations++ numberOfOptimizations++
} }
mods = optimizeAddWordToSameVariableOrExtraRegisterLoadInWordStore(linesByFourteen)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFourteen = getLinesBy(lines, 14)
numberOfOptimizations++
}
return numberOfOptimizations return numberOfOptimizations
} }
@@ -408,7 +415,7 @@ private fun optimizeStoreLoadSame(
// a branch instruction follows, we can only remove the load instruction if // a branch instruction follows, we can only remove the load instruction if
// another load instruction of the same register precedes the store instruction // another load instruction of the same register precedes the store instruction
// (otherwise wrong cpu flags are used) // (otherwise wrong cpu flags are used)
val loadinstruction = second.substring(0, 3) val loadinstruction = second.take(3)
lines[0].value.trimStart().startsWith(loadinstruction) lines[0].value.trimStart().startsWith(loadinstruction)
} }
else { else {
@@ -446,15 +453,26 @@ private fun optimizeStoreLoadSame(
// lda X + sta X, ldy X + sty X, ldx X + stx X -> the second instruction can be eliminated // lda X + sta X, ldy X + sty X, ldx X + stx X -> the second instruction can be eliminated
if ((first.startsWith("lda ") && second.startsWith("sta ")) || if (first.startsWith("lda ") && second.startsWith("sta ") ||
(first.startsWith("ldx ") && second.startsWith("stx ")) || first.startsWith("ldx ") && second.startsWith("stx ") ||
(first.startsWith("ldy ") && second.startsWith("sty ")) first.startsWith("ldy ") && second.startsWith("sty ")
) { ) {
val firstLoc = first.substring(4).trimStart() val firstLoc = first.substring(4).trimStart()
val secondLoc = second.substring(4).trimStart() val secondLoc = second.substring(4).trimStart()
if (firstLoc == secondLoc) if (firstLoc == secondLoc)
mods.add(Modification(lines[2].index, true, null)) mods.add(Modification(lines[2].index, true, null))
} }
// all 3 registers: lda VALUE + sta SOMEWHERE + lda VALUE -> last load can be eliminated
if (first.startsWith("lda ") && second.startsWith("sta ") && third.startsWith("lda ") ||
first.startsWith("ldx ") && second.startsWith("stx ") && third.startsWith("ldx ") ||
first.startsWith("ldy ") && second.startsWith("sty ") && third.startsWith("ldy ")
) {
val firstVal = first.substring(4).trimStart()
val thirdVal = third.substring(4).trimStart()
if (firstVal == thirdVal)
mods.add(Modification(lines[3].index, true, null))
}
} }
return mods return mods
} }
@@ -694,23 +712,32 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
optimize('x', lines) optimize('x', lines)
optimize('y', lines) optimize('y', lines)
val first = lines[1].value.trimStart() val first = lines[0].value.trimStart()
val second = lines[2].value.trimStart() val second = lines[1].value.trimStart()
val third = lines[3].value.trimStart() val third = lines[2].value.trimStart()
val fourth = lines[3].value.trimStart()
// phy + ldy + pla -> tya + ldy // phy + ldy + pla -> tya + ldy
// phx + ldx + pla -> txa + ldx // phx + ldx + pla -> txa + ldx
// pha + lda + pla -> nop // pha + lda + pla -> nop
// pha + tya + tay + pla -> nop
// pha + txa + tax + pla -> nop
when (first) { when (first) {
"phy" if second.startsWith("ldy ") && third=="pla" -> { "phy" if second.startsWith("ldy ") && third=="pla" -> {
mods.add(Modification(lines[3].index, true, null)) mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[1].index, false, " tya")) mods.add(Modification(lines[0].index, false, " tya"))
} }
"phx" if second.startsWith("ldx ") && third=="pla" -> { "phx" if second.startsWith("ldx ") && third=="pla" -> {
mods.add(Modification(lines[3].index, true, null)) mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[1].index, false, " txa")) mods.add(Modification(lines[0].index, false, " txa"))
} }
"pha" if second.startsWith("lda ") && third=="pla" -> { "pha" if second.startsWith("lda ") && third=="pla" -> {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, true, null))
}
"pha" if ((second=="tya" && third=="tay") || (second=="txa" && third=="tax")) && fourth=="pla" -> {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[1].index, true, null)) mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, true, null)) mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[3].index, true, null)) mods.add(Modification(lines[3].index, true, null))
@@ -722,7 +749,6 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
return mods return mods
} }
private fun optimizeTSBtoRegularOr(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> { private fun optimizeTSBtoRegularOr(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
// Asm peephole: lda var2 / tsb var1 / lda var1 Replace this with this to save 1 cycle: lda var1 / ora var2 / sta var1 // Asm peephole: lda var2 / tsb var1 / lda var1 Replace this with this to save 1 cycle: lda var1 / ora var2 / sta var1
val mods = mutableListOf<Modification>() val mods = mutableListOf<Modification>()
@@ -767,3 +793,120 @@ private fun optimizeUnneededTempvarInAdd(linesByFour: Sequence<List<IndexedValue
return mods return mods
} }
private fun optimizeAddWordToSameVariableOrExtraRegisterLoadInWordStore(linesByFourteen: Sequence<List<IndexedValue<String>>>): List<Modification> {
/*
; FIRST SEQUYENCE: P8ZP_SCRATCH_PTR += AY :
clc
adc P8ZP_SCRATCH_PTR
pha
tya
adc P8ZP_SCRATCH_PTR+1
tay
pla
sta P8ZP_SCRATCH_PTR
sty P8ZP_SCRATCH_PTR+1
->
clc
adc P8ZP_SCRATCH_PTR
sta P8ZP_SCRATCH_PTR
tya
adc P8ZP_SCRATCH_PTR+1
sta P8ZP_SCRATCH_PTR+1
also SECOND SEQUENCE:
ldx VALUE/ ldy VALUE
sta SOMEWHERE_WITHOUT_,x_OR_,y
txa / tya
ldy #1
sta SOMEWHERE
-->
sta SOMEWHERE_WITHOUT_,x_OR_,y
lda VALUE
ldy #1
sta SOMEWHERE
also THIRD SEQUENCE:
ldx VALUE
ldy #0
sta SOMEWHERE_WITHOUT_,x
txa
iny
sta SOMEWHERE
-->
ldy #0
sta SOMEWHERE_WITHOUT_,x
lda VALUE
iny
sta SOMEWHERE
*/
val mods = mutableListOf<Modification>()
for (lines in linesByFourteen) {
val first = lines[0].value.trimStart()
val second = lines[1].value.trimStart()
val third = lines[2].value.trimStart()
val fourth = lines[3].value.trimStart()
val fifth = lines[4].value.trimStart()
val sixth = lines[5].value.trimStart()
val seventh = lines[6].value.trimStart()
val eight = lines[7].value.trimStart()
val ninth = lines[8].value.trimStart()
// FIRST SEQUENCE
if(first=="clc" && second.startsWith("adc") && third=="pha" && fourth=="tya" &&
fifth.startsWith("adc") && sixth=="tay" && seventh=="pla" && eight.startsWith("sta") && ninth.startsWith("sty")) {
val var2 = second.substring(4)
val var5 = fifth.substring(4).substringBefore('+')
val var8 = eight.substring(4)
val var9 = ninth.substring(4).substringBefore('+')
if(var2==var5 && var2==var8 && var2==var9) {
if(fifth.endsWith("$var5+1") && ninth.endsWith("$var9+1")) {
mods.add(Modification(lines[2].index, false, " sta $var2"))
mods.add(Modification(lines[5].index, false, " sta $var2+1"))
mods.add(Modification(lines[6].index, true, null))
mods.add(Modification(lines[7].index, true, null))
mods.add(Modification(lines[8].index, true, null))
}
}
}
// SECOND SEQUENCE
if(first.startsWith("ldx ") && second.startsWith("sta ") &&
third=="txa" && fourth.startsWith("ldy ") && fifth.startsWith("sta ")
) {
if(",x" !in second) {
val value = first.substring(4)
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[2].index, false, " lda $value"))
}
}
if(first.startsWith("ldy ") && second.startsWith("sta ") &&
third=="tya" && fourth.startsWith("ldy ") && fifth.startsWith("sta ")
) {
if(",y" !in second) {
val value = first.substring(4)
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[2].index, false, " lda $value"))
}
}
// THIRD SEQUENCE
if(first.startsWith("ldx ") && second.startsWith("ldy ") && third.startsWith("sta ") &&
fourth=="txa" && fifth=="iny" && sixth.startsWith("sta ")
) {
if(",x" !in third) {
val value = first.substring(4)
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[3].index, false, " lda $value"))
}
}
}
return mods
}

View File

@@ -1,5 +1,8 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.code.StMemorySlabBlockName
import prog8.code.StStructInstanceBlockName
import prog8.code.SymbolTable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.* import prog8.codegen.cpu6502.assignment.*
@@ -46,6 +49,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"memory" -> funcMemory(fcall, discardResult, resultRegister) "memory" -> funcMemory(fcall, discardResult, resultRegister)
"peekw" -> funcPeekW(fcall, resultRegister) "peekw" -> funcPeekW(fcall, resultRegister)
"peekf" -> funcPeekF(fcall, resultRegister) "peekf" -> funcPeekF(fcall, resultRegister)
"peekbool" -> funcPeekBool(fcall, resultRegister)
"peek" -> throw AssemblyError("peek() should have been replaced by @()") "peek" -> throw AssemblyError("peek() should have been replaced by @()")
"pokew" -> funcPokeW(fcall) "pokew" -> funcPokeW(fcall)
"pokef" -> funcPokeF(fcall) "pokef" -> funcPokeF(fcall)
@@ -60,12 +64,14 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" pla") asmgen.out(" pla")
} }
"poke" -> throw AssemblyError("poke() should have been replaced by @()") "poke" -> throw AssemblyError("poke() should have been replaced by @()")
"pokebool" -> funcPokeBool(fcall)
"rsave" -> funcRsave() "rsave" -> funcRsave()
"rrestore" -> funcRrestore() "rrestore" -> funcRrestore()
"cmp" -> funcCmp(fcall) "cmp" -> funcCmp(fcall)
"callfar" -> funcCallFar(fcall, resultRegister) "callfar" -> funcCallFar(fcall, resultRegister)
"callfar2" -> funcCallFar2(fcall, resultRegister) "callfar2" -> funcCallFar2(fcall, resultRegister)
"call" -> funcCall(fcall) "call" -> funcCall(fcall)
"prog8_lib_structalloc" -> funcStructAlloc(fcall, discardResult, resultRegister)
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister) "prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
"prog8_lib_square_byte" -> funcSquare(fcall, BaseDataType.UBYTE, resultRegister) "prog8_lib_square_byte" -> funcSquare(fcall, BaseDataType.UBYTE, resultRegister)
"prog8_lib_square_word" -> funcSquare(fcall, BaseDataType.UWORD, resultRegister) "prog8_lib_square_word" -> funcSquare(fcall, BaseDataType.UWORD, resultRegister)
@@ -380,8 +386,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
throw AssemblyError("should not discard result of memory allocation at $fcall") throw AssemblyError("should not discard result of memory allocation at $fcall")
val name = (fcall.args[0] as PtString).value val name = (fcall.args[0] as PtString).value
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"} require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
val addressOf = PtAddressOf(fcall.position) val slabname = PtIdentifier("$StMemorySlabBlockName.memory_$name", DataType.UWORD, fcall.position)
val addressOf = PtAddressOf(DataType.pointer(BaseDataType.UBYTE), false, fcall.position)
addressOf.add(slabname) addressOf.add(slabname)
addressOf.parent = fcall addressOf.parent = fcall
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf) val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
@@ -390,6 +397,22 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.translateNormalAssignment(assign, fcall.definingISub()) asmgen.translateNormalAssignment(assign, fcall.definingISub())
} }
private fun funcStructAlloc(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?) {
if(discardResult)
throw AssemblyError("should not discard result of struct allocation at $fcall")
// ... don't need to pay attention to args here because struct instance is put together elsewhere we just have to get a pointer to it
val prefix = if(fcall.args.isEmpty()) "${StStructInstanceBlockName}_bss" else StStructInstanceBlockName
val labelname = PtIdentifier("$prefix.${SymbolTable.labelnameForStructInstance(fcall)}", fcall.type, fcall.position)
val addressOf = PtAddressOf(fcall.type, true, fcall.position)
addressOf.add(labelname)
addressOf.parent = fcall
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, fcall.type, expression = addressOf)
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position)
asmgen.translateNormalAssignment(assign, fcall.definingISub())
}
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) { private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope) translateArguments(fcall, scope)
when(fcall.args[0].type.base) { when(fcall.args[0].type.base) {
@@ -415,8 +438,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
BaseDataType.UBYTE -> { BaseDataType.UBYTE -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
val varname = asmgen.asmVariableName(what.variable) val varname = asmgen.asmVariableName(what.variable!!)
asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #$80 |+ | sta ${varname},x") asmgen.out(" lda ${varname},x | lsr a | bcc + | ora #$80 |+ | sta ${varname},x")
} }
is PtMemoryByte -> { is PtMemoryByte -> {
@@ -438,8 +464,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
BaseDataType.UWORD -> { BaseDataType.UWORD -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
val varname = asmgen.asmVariableName(what.variable) val varname = asmgen.asmVariableName(what.variable!!)
if(what.splitWords) if(what.splitWords)
asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #$80 | sta ${varname}_msb,x |+ ") asmgen.out(" lsr ${varname}_msb,x | ror ${varname}_lsb,x | bcc + | lda ${varname}_msb,x | ora #$80 | sta ${varname}_msb,x |+ ")
else else
@@ -465,7 +493,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(!what.index.isSimple()) asmgen.out(" php") // save Carry if(!what.index.isSimple()) asmgen.out(" php") // save Carry
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
if(!what.index.isSimple()) asmgen.out(" plp") if(!what.index.isSimple()) asmgen.out(" plp")
val varname = asmgen.asmVariableName(what.variable)
if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
val varname = asmgen.asmVariableName(what.variable!!)
asmgen.out(" ror ${varname},x") asmgen.out(" ror ${varname},x")
} }
is PtMemoryByte -> { is PtMemoryByte -> {
@@ -498,7 +529,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(!what.index.isSimple()) asmgen.out(" php") // save Carry if(!what.index.isSimple()) asmgen.out(" php") // save Carry
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
if(!what.index.isSimple()) asmgen.out(" plp") if(!what.index.isSimple()) asmgen.out(" plp")
val varname = asmgen.asmVariableName(what.variable) if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
val varname = asmgen.asmVariableName(what.variable!!)
if(what.splitWords) if(what.splitWords)
asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x") asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x")
else else
@@ -521,8 +554,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
BaseDataType.UBYTE -> { BaseDataType.UBYTE -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
val varname = asmgen.asmVariableName(what.variable) val varname = asmgen.asmVariableName(what.variable!!)
asmgen.out(" lda ${varname},x | cmp #$80 | rol a | sta ${varname},x") asmgen.out(" lda ${varname},x | cmp #$80 | rol a | sta ${varname},x")
} }
is PtMemoryByte -> { is PtMemoryByte -> {
@@ -545,7 +580,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
val varname = asmgen.asmVariableName(what.variable) if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
val varname = asmgen.asmVariableName(what.variable!!)
if(what.splitWords) if(what.splitWords)
asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb,x |+") asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb,x |+")
else else
@@ -571,7 +608,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(!what.index.isSimple()) asmgen.out(" php") // save Carry if(!what.index.isSimple()) asmgen.out(" php") // save Carry
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
if(!what.index.isSimple()) asmgen.out(" plp") if(!what.index.isSimple()) asmgen.out(" plp")
val varname = asmgen.asmVariableName(what.variable) if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
val varname = asmgen.asmVariableName(what.variable!!)
asmgen.out(" rol ${varname},x") asmgen.out(" rol ${varname},x")
} }
is PtMemoryByte -> { is PtMemoryByte -> {
@@ -604,7 +643,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(!what.index.isSimple()) asmgen.out(" php") // save Carry if(!what.index.isSimple()) asmgen.out(" php") // save Carry
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
if(!what.index.isSimple()) asmgen.out(" plp") if(!what.index.isSimple()) asmgen.out(" plp")
val varname = asmgen.asmVariableName(what.variable) if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
val varname = asmgen.asmVariableName(what.variable!!)
if(what.splitWords) if(what.splitWords)
asmgen.out(" rol ${varname}_lsb,x | rol ${varname}_msb,x") asmgen.out(" rol ${varname}_lsb,x | rol ${varname}_msb,x")
else else
@@ -633,7 +674,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val elementSize: Int val elementSize: Int
val msbAdd: Int val msbAdd: Int
if(indexer.splitWords) { if(indexer.splitWords) {
val arrayVariable = indexer.variable val arrayVariable = indexer.variable ?: TODO("support for ptr indexing ${indexer.position}")
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.arrayFor(BaseDataType.UBYTE, false), arrayVariable.position) indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.arrayFor(BaseDataType.UBYTE, false), arrayVariable.position)
indexer.children[0].parent = indexer indexer.children[0].parent = indexer
elementSize = 1 elementSize = 1
@@ -754,6 +795,52 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
} }
private fun funcPokeBool(fcall: PtBuiltinFunctionCall) {
when(val addrExpr = fcall.args[0]) {
is PtNumber -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
val addr = addrExpr.number.toHex()
asmgen.out(" sta $addr")
return
}
is PtIdentifier -> {
val varname = asmgen.asmVariableName(addrExpr)
if(asmgen.isZpVar(addrExpr)) {
// pointervar is already in the zero page, no need to copy
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
asmgen.storeIndirectByteReg(CpuRegister.A, varname, 0u, false, false)
return
}
}
is PtBinaryExpression -> {
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
val pointer = result?.first as? PtIdentifier
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
// can do ZP,Y indexing
val varname = asmgen.asmVariableName(pointer)
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
asmgen.restoreRegisterStack(CpuRegister.Y, true)
asmgen.out(" sta ($varname),y")
return
}
}
else -> { /* fall through */ }
}
// fall through method:
if(fcall.args[1].isSimple()) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
} else {
asmgen.pushCpuStack(BaseDataType.UBYTE, fcall.args[1])
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
asmgen.restoreRegisterStack(CpuRegister.A, false)
}
asmgen.storeIndirectByteReg(CpuRegister.A, "P8ZP_SCRATCH_W1", 0u, false, false)
}
private fun funcPokeW(fcall: PtBuiltinFunctionCall) { private fun funcPokeW(fcall: PtBuiltinFunctionCall) {
when(val addrExpr = fcall.args[0]) { when(val addrExpr = fcall.args[0]) {
is PtNumber -> { is PtNumber -> {
@@ -767,20 +854,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
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
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
if (asmgen.isTargetCpu(CpuType.CPU65C02)) { asmgen.storeIndirectWordReg(RegisterOrPair.AX, varname, 0u)
asmgen.out("""
sta ($varname)
txa
ldy #1
sta ($varname),y""")
} else {
asmgen.out("""
ldy #0
sta ($varname),y
txa
iny
sta ($varname),y""")
}
return return
} }
} }
@@ -815,7 +889,46 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" jsr floats.MOVFM") asmgen.out(" jsr floats.MOVFM")
if(resultRegister!=null) { if(resultRegister!=null) {
assignAsmGen.assignFAC1float( assignAsmGen.assignFAC1float(
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position)) AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), register=resultRegister, position=fcall.position))
}
}
private fun funcPeekBool(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
fun fallback() {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.func_peek")
}
when(val addrExpr = fcall.args[0]) {
is PtNumber -> {
val addr = addrExpr.number.toHex()
asmgen.out(" lda $addr")
}
is PtIdentifier -> {
val varname = asmgen.asmVariableName(addrExpr)
if(asmgen.isZpVar(addrExpr))
asmgen.loadIndirectByte(varname, 0u)
else
fallback()
}
is PtBinaryExpression -> {
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
val pointer = result?.first as? PtIdentifier
if(result!=null && pointer!=null && asmgen.isZpVar(pointer)) {
// can do ZP,Y indexing
val varname = asmgen.asmVariableName(pointer)
asmgen.assignExpressionToRegister(result.second, RegisterOrPair.Y)
asmgen.out(" lda ($varname),y")
} else fallback()
}
else -> fallback()
}
when(resultRegister ?: RegisterOrPair.A) {
RegisterOrPair.A -> {}
RegisterOrPair.X -> asmgen.out(" tax")
RegisterOrPair.Y -> asmgen.out(" tay")
in Cx16VirtualRegisters -> asmgen.out(" sta cx16.${resultRegister.toString().lowercase()}")
else -> throw AssemblyError("invalid reg")
} }
} }
@@ -831,25 +944,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
is PtIdentifier -> { is PtIdentifier -> {
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 asmgen.loadIndirectWord(varname, 0u)
if (asmgen.isTargetCpu(CpuType.CPU65C02)) { else
asmgen.out(""" fallback()
ldy #1
lda ($varname),y
tay
lda ($varname)""")
} else {
asmgen.out("""
ldy #0
lda ($varname),y
tax
iny
lda ($varname),y
tay
txa""")
}
} else fallback()
} }
is PtBinaryExpression -> { is PtBinaryExpression -> {
val result = asmgen.pointerViaIndexRegisterPossible(addrExpr) val result = asmgen.pointerViaIndexRegisterPossible(addrExpr)
@@ -1112,7 +1210,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) { if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
// just read the msb byte out of the word array // just read the msb byte out of the word array
if(arg.splitWords) { if(arg.splitWords) {
val arrayVar = asmgen.asmVariableName(arg.variable)+"_msb" if(arg.variable==null)
TODO("support for ptr indexing ${arg.position}")
val arrayVar = asmgen.asmVariableName(arg.variable!!)+"_msb"
when(resultRegister) { when(resultRegister) {
null, RegisterOrPair.A -> { null, RegisterOrPair.A -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
@@ -1129,7 +1229,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("invalid reg") else -> throw AssemblyError("invalid reg")
} }
} else { } else {
val arrayVar = asmgen.asmVariableName(arg.variable) if(arg.variable==null)
TODO("support for ptr indexing ${arg.position}")
val arrayVar = asmgen.asmVariableName(arg.variable!!)
when(resultRegister) { when(resultRegister) {
null, RegisterOrPair.A -> { null, RegisterOrPair.A -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
@@ -1211,7 +1313,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else { } else {
if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) { if(arg is PtArrayIndexer && resultRegister in arrayOf(null, RegisterOrPair.A, RegisterOrPair.Y, RegisterOrPair.X)) {
// just read the lsb byte out of the word array // just read the lsb byte out of the word array
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable)+"_lsb" else asmgen.asmVariableName(arg.variable) if(arg.variable==null)
TODO("support for ptr indexing ${arg.position}")
val arrayVar = if(arg.splitWords) asmgen.asmVariableName(arg.variable!!)+"_lsb" else asmgen.asmVariableName(arg.variable!!)
when(resultRegister) { when(resultRegister) {
null, RegisterOrPair.A -> { null, RegisterOrPair.A -> {
asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(arg, CpuRegister.Y)
@@ -1276,7 +1381,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
fun getSourceForFloat(value: PtExpression): AsmAssignSource { fun getSourceForFloat(value: PtExpression): AsmAssignSource {
return when (value) { return when (value) {
is PtIdentifier -> { is PtIdentifier -> {
val addr = PtAddressOf(value.position) val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), false, value.position)
addr.add(value) addr.add(value)
addr.parent = call addr.parent = call
AsmAssignSource.fromAstSource(addr, program, asmgen) AsmAssignSource.fromAstSource(addr, program, asmgen)
@@ -1290,7 +1395,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position) val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
val addr = PtAddressOf(value.position) val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), false, value.position)
addr.add(variable) addr.add(variable)
addr.parent = call addr.parent = call
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT) asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
@@ -1310,7 +1415,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value) conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
conv.dt.isPassByRef -> { conv.dt.isPassByRef -> {
// put the address of the argument in AY // put the address of the argument in AY
val addr = PtAddressOf(value.position) val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false, value.position)
addr.add(value) addr.add(value)
addr.parent = call addr.parent = call
AsmAssignSource.fromAstSource(addr, program, asmgen) AsmAssignSource.fromAstSource(addr, program, asmgen)
@@ -1328,7 +1433,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value) conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
conv.dt.isPassByRef -> { conv.dt.isPassByRef -> {
// put the address of the argument in AY // put the address of the argument in AY
val addr = PtAddressOf(value.position) val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false,value.position)
addr.add(value) addr.add(value)
addr.parent = call addr.parent = call
AsmAssignSource.fromAstSource(addr, program, asmgen) AsmAssignSource.fromAstSource(addr, program, asmgen)

View File

@@ -500,10 +500,10 @@ $endLabel""")
val endLabel = asmgen.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.add(endLabel) asmgen.loopEndLabels.add(endLabel)
val iterableName = asmgen.asmVariableName(ident) val iterableName = asmgen.asmVariableName(ident)
val numElements = when(val symbol = asmgen.symbolTable.lookup(ident.name)) { val numElements: UInt = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
is StStaticVariable -> symbol.length!! is StStaticVariable -> symbol.length!!
is StMemVar -> symbol.length!! is StMemVar -> symbol.length!!
else -> 0 else -> 0u
} }
when { when {
iterableDt.isString -> { iterableDt.isString -> {
@@ -549,7 +549,7 @@ $loopLabel sty $indexVar
lda $iterableName,y lda $iterableName,y
sta ${asmgen.asmVariableName(stmt.variable)}""") sta ${asmgen.asmVariableName(stmt.variable)}""")
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
if(numElements<=255) { if(numElements<=255u) {
asmgen.out(""" asmgen.out("""
ldy $indexVar ldy $indexVar
iny iny
@@ -565,7 +565,7 @@ $loopLabel sty $indexVar
beq $endLabel""") beq $endLabel""")
} }
if(!asmgen.options.romable) { if(!asmgen.options.romable) {
if(numElements>=16) { if(numElements>=16u) {
// allocate index var on ZP if possible, otherwise inline // allocate index var on ZP if possible, otherwise inline
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors) val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold( result.fold(
@@ -592,7 +592,7 @@ $loopLabel sty $indexVar
lda ${iterableName}_msb,y lda ${iterableName}_msb,y
sta $loopvarName+1""") sta $loopvarName+1""")
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
if(numElements<=255) { if(numElements<=255u) {
asmgen.out(""" asmgen.out("""
ldy $indexVar ldy $indexVar
iny iny
@@ -608,7 +608,7 @@ $loopLabel sty $indexVar
beq $endLabel""") beq $endLabel""")
} }
if(!asmgen.options.romable) { if(!asmgen.options.romable) {
if(numElements>=16) { if(numElements>=16u) {
// allocate index var on ZP if possible, otherwise inline // allocate index var on ZP if possible, otherwise inline
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors) val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold( result.fold(
@@ -635,12 +635,12 @@ $loopLabel sty $indexVar
lda $iterableName+1,y lda $iterableName+1,y
sta $loopvarName+1""") sta $loopvarName+1""")
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
if(numElements<=127) { if(numElements<=127u) {
asmgen.out(""" asmgen.out("""
ldy $indexVar ldy $indexVar
iny iny
iny iny
cpy #${numElements*2} cpy #${numElements*2u}
beq $endLabel beq $endLabel
bne $loopLabel""") bne $loopLabel""")
} else { } else {
@@ -653,7 +653,7 @@ $loopLabel sty $indexVar
beq $endLabel""") beq $endLabel""")
} }
if(!asmgen.options.romable) { if(!asmgen.options.romable) {
if(numElements>=16) { if(numElements>=16u) {
// allocate index var on ZP if possible, otherwise inline // allocate index var on ZP if possible, otherwise inline
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors) val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold( result.fold(

View File

@@ -21,7 +21,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// we consider them NOT to be optimized into (possibly different) CPU registers. // we consider them NOT to be optimized into (possibly different) CPU registers.
// Just load them in whatever the register spec says. // Just load them in whatever the register spec says.
return when (params.size) { return when (params.size) {
1 -> params[0].type.isIntegerOrBool && params[0].register == null 1 -> params[0].register == null && (params[0].type.isIntegerOrBool || params[0].type.isPointer)
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null 2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null
else -> false else -> false
} }
@@ -36,7 +36,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
val symbol = asmgen.symbolTable.lookup(call.name)!! val symbol = asmgen.symbolTable.lookup(call.name)!!
if(symbol.type == StNodeType.LABEL) { if(symbol.type == StNodeType.LABEL) {
require(call.void) require(call.void)
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedName)}") asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedNameString)}")
return return
} }
@@ -139,15 +139,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} }
} }
else if(sub is PtSub) { else if(sub is PtSub) {
if(optimizeIntArgsViaCpuRegisters(sub.parameters)) { val parameters = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
if(optimizeIntArgsViaCpuRegisters(parameters)) {
// Note that if the args fit into cpu registers, we don't concern ourselves here // Note that if the args fit into cpu registers, we don't concern ourselves here
// if they should be put into regular subroutine parameter variables, or the R0-R15 register variables. // if they should be put into regular subroutine parameter variables, or the R0-R15 register variables.
// That is now up to the subroutine itself. // That is now up to the subroutine itself.
useCpuRegistersForArgs(call.args, sub) useCpuRegistersForArgs(call.args, sub)
} else { } else {
// arguments via variables // arguments via variables
val paramValues = sub.parameters.zip(call.args) val paramValues = parameters.zip(call.args)
val (normalParams, registerParams) = paramValues.partition { it.first.register == null } val (normalParams, registerParams) = paramValues.partition { (it.first.register == null) }
if (normalParams.isNotEmpty()) { if (normalParams.isNotEmpty()) {
for (p in normalParams) for (p in normalParams)
argumentViaVariable(sub, p.first, p.second) argumentViaVariable(sub, p.first, p.second)
@@ -166,7 +167,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} }
private fun useCpuRegistersForArgs(args: List<PtExpression>, sub: PtSub) { private fun useCpuRegistersForArgs(args: List<PtExpression>, sub: PtSub) {
val params = sub.parameters val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
when(params.size) { when(params.size) {
1 -> { 1 -> {
val register = if (params[0].type.isByteOrBool) RegisterOrPair.A else RegisterOrPair.AY val register = if (params[0].type.isByteOrBool) RegisterOrPair.A else RegisterOrPair.AY
@@ -332,14 +333,14 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} else { } else {
val scope = value.definingISub() val scope = value.definingISub()
val target: AsmAssignTarget = val target: AsmAssignTarget =
if(parameter.value.type.isByte && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters)) if(parameter.value.type.isByte && register.isWord())
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register) AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
else { else {
AsmAssignTarget.fromRegisters(register, parameter.value.type.isSigned, value.position, scope, asmgen) AsmAssignTarget.fromRegisters(register, parameter.value.type.isSigned, value.position, scope, asmgen)
} }
val src = if(value.type.isPassByRef) { val src = if(value.type.isPassByRef) {
if(value is PtIdentifier) { if(value is PtIdentifier) {
val addr = PtAddressOf(Position.DUMMY) val addr = PtAddressOf(value.type.typeForAddressOf(false), false, Position.DUMMY)
addr.add(value) addr.add(value)
addr.parent = scope as PtNode addr.parent = scope as PtNode
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target) AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)

View File

@@ -6,11 +6,13 @@ import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.AsmAssignTarget import prog8.codegen.cpu6502.assignment.AsmAssignTarget
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
import prog8.codegen.cpu6502.assignment.PointerAssignmentsGen
import prog8.codegen.cpu6502.assignment.TargetStorageKind import prog8.codegen.cpu6502.assignment.TargetStorageKind
internal class IfElseAsmGen(private val program: PtProgram, internal class IfElseAsmGen(private val program: PtProgram,
private val st: SymbolTable, private val st: SymbolTable,
private val asmgen: AsmGen6502Internal, private val asmgen: AsmGen6502Internal,
private val pointergen: PointerAssignmentsGen,
private val assignmentAsmGen: AssignmentAsmGen, private val assignmentAsmGen: AssignmentAsmGen,
private val errors: IErrorReporter) { private val errors: IErrorReporter) {
@@ -43,7 +45,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
val rightDt = compareCond.right.type val rightDt = compareCond.right.type
return when { return when {
rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf) rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf)
rightDt.isWord -> translateIfWord(stmt, compareCond, jumpAfterIf) rightDt.isWord || rightDt.isPointer -> translateIfWord(stmt, compareCond, jumpAfterIf)
rightDt.isFloat -> translateIfFloat(stmt, compareCond, jumpAfterIf) rightDt.isFloat -> translateIfFloat(stmt, compareCond, jumpAfterIf)
else -> throw AssemblyError("weird dt") else -> throw AssemblyError("weird dt")
} }
@@ -63,6 +65,16 @@ internal class IfElseAsmGen(private val program: PtProgram,
} }
} }
val dereference = stmt.condition as? PtPointerDeref
if(dereference!=null) {
val (zpPtrVar, offset) = pointergen.deref(dereference)
asmgen.loadIndirectByte(zpPtrVar, offset)
return if (jumpAfterIf != null)
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("beq", stmt)
}
throw AssemblyError("weird non-boolean condition node type ${stmt.condition} at ${stmt.condition.position}") throw AssemblyError("weird non-boolean condition node type ${stmt.condition} at ${stmt.condition.position}")
} }
@@ -885,7 +897,9 @@ _jump jmp (${target.asmLabel})
private fun loadAndCmp0MSB(value: PtExpression) { private fun loadAndCmp0MSB(value: PtExpression) {
when(value) { when(value) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
val varname = asmgen.asmVariableName(value.variable) if(value.variable==null)
TODO("support for ptr indexing ${value.position}")
val varname = asmgen.asmVariableName(value.variable!!)
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
if(value.splitWords) if(value.splitWords)
asmgen.out(" lda ${varname}_msb,y") asmgen.out(" lda ${varname}_msb,y")
@@ -901,8 +915,8 @@ _jump jmp (${target.asmLabel})
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true) asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
asmgen.out(" cpy #0") asmgen.out(" cpy #0")
} else { } else {
var varname = asmgen.asmVariableName(value.identifier) var varname = asmgen.asmVariableName(value.identifier!!)
if(value.identifier.type.isSplitWordArray) { if(value.identifier!!.type.isSplitWordArray) {
varname += if(value.isMsbForSplitArray) "_msb" else "_lsb" varname += if(value.isMsbForSplitArray) "_msb" else "_lsb"
} }
asmgen.out(" lda #>$varname") asmgen.out(" lda #>$varname")
@@ -980,8 +994,10 @@ _jump jmp (${target.asmLabel})
if(value is PtIdentifier) if(value is PtIdentifier)
return compareLsbMsb(value.name, value.name+"+1") return compareLsbMsb(value.name, value.name+"+1")
if(value is PtArrayIndexer) { if(value is PtArrayIndexer) {
if(value.variable==null)
TODO("support for ptr indexing ${value.position}")
val constIndex = value.index.asConstInteger() val constIndex = value.index.asConstInteger()
val varname = asmgen.asmVariableName(value.variable) val varname = asmgen.asmVariableName(value.variable!!)
if(constIndex!=null) { if(constIndex!=null) {
if(value.splitWords) { if(value.splitWords) {
return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex") return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
@@ -1127,8 +1143,10 @@ _jump jmp (${target.asmLabel})
if(value is PtIdentifier) if(value is PtIdentifier)
return compareLsbMsb(value.name, value.name+"+1") return compareLsbMsb(value.name, value.name+"+1")
if(value is PtArrayIndexer) { if(value is PtArrayIndexer) {
if(value.variable==null)
TODO("support for ptr indexing ${value.position}")
val constIndex = value.index.asConstInteger() val constIndex = value.index.asConstInteger()
val varname = asmgen.asmVariableName(value.variable) val varname = asmgen.asmVariableName(value.variable!!)
if(constIndex!=null) { if(constIndex!=null) {
if(value.splitWords) { if(value.splitWords) {
return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex") return compareLsbMsb("${varname}_lsb+$constIndex", "${varname}_msb+$constIndex")
@@ -1241,7 +1259,9 @@ _jump jmp (${target.asmLabel})
is PtArrayIndexer -> { is PtArrayIndexer -> {
val constIndex = value.index.asConstInteger() val constIndex = value.index.asConstInteger()
if(constIndex!=null) { if(constIndex!=null) {
val varName = asmgen.asmVariableName(value.variable) if(value.variable==null)
TODO("support for ptr indexing ${value.position}")
val varName = asmgen.asmVariableName(value.variable!!)
if(value.splitWords) { if(value.splitWords) {
return translateLoadFromVarSplitw(varName, constIndex, "bne", "beq") return translateLoadFromVarSplitw(varName, constIndex, "bne", "beq")
} }
@@ -1262,7 +1282,9 @@ _jump jmp (${target.asmLabel})
is PtArrayIndexer -> { is PtArrayIndexer -> {
val constIndex = value.index.asConstInteger() val constIndex = value.index.asConstInteger()
if (constIndex != null) { if (constIndex != null) {
val varName = asmgen.asmVariableName(value.variable) if(value.variable==null)
TODO("support for ptr indexing ${value.position}")
val varName = asmgen.asmVariableName(value.variable!!)
if(value.splitWords) { if(value.splitWords) {
return translateLoadFromVarSplitw(varName, constIndex, "beq", "bne") return translateLoadFromVarSplitw(varName, constIndex, "beq", "bne")
} }
@@ -1643,8 +1665,10 @@ _jump jmp (${target.asmLabel})
fun translateEqualsArray(left: PtArrayIndexer, right: PtExpression) { fun translateEqualsArray(left: PtArrayIndexer, right: PtExpression) {
val constIndex = left.index.asConstInteger() val constIndex = left.index.asConstInteger()
if(constIndex!=null) { if(constIndex!=null) {
if(left.variable==null)
TODO("support for ptr indexing ${left.position}")
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed) asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
val varName = asmgen.asmVariableName(left.variable) val varName = asmgen.asmVariableName(left.variable!!)
if(left.splitWords) { if(left.splitWords) {
return if(notEquals) return if(notEquals)
translateAYNotEquals("${varName}_lsb+$constIndex", "${varName}_msb+$constIndex") translateAYNotEquals("${varName}_lsb+$constIndex", "${varName}_msb+$constIndex")
@@ -1707,13 +1731,13 @@ _jump jmp (${target.asmLabel})
} }
} }
is PtAddressOf -> { is PtAddressOf -> {
if(left.isFromArrayElement) if(left.isFromArrayElement) {
fallbackTranslateForSimpleCondition(stmt) fallbackTranslateForSimpleCondition(stmt)
else { } else {
val varname = if(left.identifier.type.isSplitWordArray) { val varname = if(left.identifier!!.type.isSplitWordArray) {
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb" if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
} else { } else {
left.identifier.name left.identifier!!.name
} }
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed) asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
translateAYNotEquals("#<$varname", "#>$varname") translateAYNotEquals("#<$varname", "#>$varname")
@@ -1759,13 +1783,13 @@ _jump jmp (${target.asmLabel})
} }
} }
is PtAddressOf -> { is PtAddressOf -> {
if(left.isFromArrayElement) if(left.isFromArrayElement) {
fallbackTranslateForSimpleCondition(stmt) fallbackTranslateForSimpleCondition(stmt)
else { } else {
val varname = if(left.identifier.type.isSplitWordArray) { val varname = if(left.identifier!!.type.isSplitWordArray) {
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb" if(left.isMsbForSplitArray) left.identifier!!.name+"_msb" else left.identifier!!.name+"_lsb"
} else { } else {
left.identifier.name left.identifier!!.name
} }
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed) asmgen.assignExpressionToRegister(right, RegisterOrPair.AY, signed)
translateAYEquals("#<$varname", "#>$varname") translateAYEquals("#<$varname", "#>$varname")

View File

@@ -9,7 +9,9 @@ import prog8.codegen.cpu6502.assignment.TargetStorageKind
internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) { internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) {
internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) { internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) {
require(target.datatype==expr.type || target.datatype.isUnsignedWord && expr.type.isString) require(target.datatype==expr.type ||
target.datatype.isUnsignedWord && (expr.type.isString || expr.type.isPointer) ||
target.datatype.isPointer && (expr.type.isUnsignedWord || expr.type.isPointer || expr.type.isString))
val falseLabel = asmgen.makeLabel("ifexpr_false") val falseLabel = asmgen.makeLabel("ifexpr_false")
val endLabel = asmgen.makeLabel("ifexpr_end") val endLabel = asmgen.makeLabel("ifexpr_end")
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel) evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
@@ -42,6 +44,69 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
} }
} }
internal fun assignBranchCondExpression(target: AsmAssignTarget, expr: PtBranchCondExpression) {
require(target.datatype==expr.type ||
target.datatype.isUnsignedWord && (expr.type.isString || expr.type.isPointer) ||
target.datatype.isPointer && (expr.type.isUnsignedWord || expr.type.isPointer || expr.type.isString))
if(target.kind==TargetStorageKind.REGISTER && target.datatype.isUnsignedByte && target.register==RegisterOrPair.A) {
if(expr.condition==BranchCondition.CC) {
if(expr.truevalue.asConstInteger()==0 && expr.falsevalue.asConstInteger()==1) {
asmgen.out(" lda #0 | rol a")
return
}
else if(expr.truevalue.asConstInteger()==1 && expr.falsevalue.asConstInteger()==0) {
asmgen.out(" lda #0 | rol a | eor #1")
return
}
}
else if(expr.condition==BranchCondition.CS) {
if(expr.truevalue.asConstInteger()==0 && expr.falsevalue.asConstInteger()==1) {
asmgen.out(" lda #0 | rol a | eor #1")
return
}
else if(expr.truevalue.asConstInteger()==1 && expr.falsevalue.asConstInteger()==0) {
asmgen.out(" lda #0 | rol a")
return
}
}
}
val trueLabel = asmgen.makeLabel("branchexpr_true")
val endLabel = asmgen.makeLabel("branchexpr_end")
val branch = asmgen.branchInstruction(expr.condition, false)
asmgen.out(" $branch $trueLabel")
when {
expr.type.isByteOrBool -> {
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
asmgen.jmp(endLabel)
asmgen.out(trueLabel)
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
}
expr.type.isWord || expr.type.isString -> {
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
asmgen.jmp(endLabel)
asmgen.out(trueLabel)
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
}
expr.type.isFloat -> {
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
asmgen.jmp(endLabel)
asmgen.out(trueLabel)
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
asmgen.out(endLabel)
asmgen.assignRegister(RegisterOrPair.FAC1, target)
}
else -> throw AssemblyError("weird dt")
}
}
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) { private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
when (condition) { when (condition) {
is PtBinaryExpression -> { is PtBinaryExpression -> {

View File

@@ -44,6 +44,7 @@ internal class ProgramAndVarsGen(
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue") asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
} }
structInstances2asm()
memorySlabs() memorySlabs()
footer() footer()
} }
@@ -70,6 +71,7 @@ internal class ProgramAndVarsGen(
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}") asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word") asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word") asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
asmgen.out("P8ZP_SCRATCH_PTR = ${zp.SCRATCH_PTR} ; word")
if(compTarget.name=="c64") { if(compTarget.name=="c64") {
if(options.floats) if(options.floats)
asmgen.out("PROG8_C64_BANK_CONFIG=31 ; basic+IO+kernal") asmgen.out("PROG8_C64_BANK_CONFIG=31 ; basic+IO+kernal")
@@ -207,18 +209,20 @@ internal class ProgramAndVarsGen(
private fun memorySlabs() { private fun memorySlabs() {
if(symboltable.allMemorySlabs.isNotEmpty()) { if(symboltable.allMemorySlabs.isNotEmpty()) {
asmgen.out("; memory slabs\n .section slabs_BSS") asmgen.out("; memory slabs\n .section BSS_SLABS")
asmgen.out("prog8_slabs\t.block") asmgen.out("$StMemorySlabBlockName\t.block")
for (slab in symboltable.allMemorySlabs) { for (slab in symboltable.allMemorySlabs) {
if (slab.align > 1u) if (slab.align > 1u)
asmgen.out("\t.align ${slab.align.toHex()}") asmgen.out("\t.align ${slab.align.toHex()}")
asmgen.out("${slab.name}\t.fill ${slab.size}") asmgen.out("${slab.name}\t.fill ${slab.size}")
} }
asmgen.out("\t.bend\n .send slabs_BSS") asmgen.out("\t.bend\n .send BSS_SLABS")
} }
} }
private fun footer() { private fun footer() {
asmgen.out(" .dsection STRUCTINSTANCES\n")
var relocateBssVars = false var relocateBssVars = false
var relocateBssSlabs = false var relocateBssSlabs = false
var relocatedBssStart = 0u var relocatedBssStart = 0u
@@ -274,14 +278,14 @@ internal class ProgramAndVarsGen(
asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}") asmgen.out("PROG8_VARSHIGH_RAMBANK = ${options.varsHighBank ?: 1}")
if(relocateBssVars) { if(relocateBssVars) {
if(!relocateBssSlabs) if(!relocateBssSlabs)
asmgen.out(" .dsection slabs_BSS") asmgen.out(" .dsection BSS_SLABS")
asmgen.out("prog8_program_end\t; end of program label for progend()") asmgen.out("prog8_program_end\t; end of program label for progend()")
asmgen.out(" * = ${relocatedBssStart.toHex()}") asmgen.out(" * = ${relocatedBssStart.toHex()}")
asmgen.out(" .dsection BSS_NOCLEAR") asmgen.out(" .dsection BSS_NOCLEAR")
asmgen.out("prog8_bss_section_start") asmgen.out("prog8_bss_section_start")
asmgen.out(" .dsection BSS") asmgen.out(" .dsection BSS")
if(relocateBssSlabs) if(relocateBssSlabs)
asmgen.out(" .dsection slabs_BSS") asmgen.out(" .dsection BSS_SLABS")
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"") asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many variables/data for BSS section\"")
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start") asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
} else { } else {
@@ -290,12 +294,12 @@ internal class ProgramAndVarsGen(
asmgen.out(" .dsection BSS") asmgen.out(" .dsection BSS")
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start") asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
if(!relocateBssSlabs) if(!relocateBssSlabs)
asmgen.out(" .dsection slabs_BSS") asmgen.out(" .dsection BSS_SLABS")
asmgen.out("prog8_program_end\t; end of program label for progend()") asmgen.out("prog8_program_end\t; end of program label for progend()")
if(relocateBssSlabs) { if(relocateBssSlabs) {
asmgen.out(" * = ${relocatedBssStart.toHex()}") asmgen.out(" * = ${relocatedBssStart.toHex()}")
asmgen.out(" .dsection slabs_BSS") asmgen.out(" .dsection BSS_SLABS")
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"") asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for BSS_SLABS section\"")
} }
} }
@@ -348,7 +352,7 @@ internal class ProgramAndVarsGen(
val varsInBlock = getVars(scope) val varsInBlock = getVars(scope)
// Zeropage Variables // Zeropage Variables
val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet() val varnames = varsInBlock.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
zeropagevars2asm(varnames) zeropagevars2asm(varnames)
// MemDefs and Consts // MemDefs and Consts
@@ -362,11 +366,100 @@ internal class ProgramAndVarsGen(
// normal statically allocated variables // normal statically allocated variables
val variables = varsInBlock val variables = varsInBlock
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) } .filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
.map { it.value as StStaticVariable } .map { it.value as StStaticVariable }
nonZpVariables2asm(variables) nonZpVariables2asm(variables)
} }
private fun asmTypeString(dt: DataType): String {
return when {
dt.isBool || dt.isUnsignedByte -> ".byte"
dt.isSignedByte -> ".char"
dt.isUnsignedWord || dt.isPointer -> ".word"
dt.isSignedWord -> ".sint"
dt.isFloat -> ".byte"
else -> {
throw AssemblyError("weird dt")
}
}
}
private fun structInstances2asm() {
fun initValues(instance: StStructInstance): List<String> {
val structtype: StStruct = symboltable.lookup(instance.structName) as StStruct
return structtype.fields.zip(instance.initialValues).map { (field, value) ->
if(field.first.isFloat) {
"["+compTarget.getFloatAsmBytes(value.number!!)+"]"
} else {
when {
value.number!=null -> {
if(field.first.isPointer)
"$"+value.number!!.toInt().toString(16)
else if(field.first.isInteger)
value.number!!.toInt().toString()
else
value.number.toString()
}
value.addressOfSymbol!=null -> value.addressOfSymbol!!
value.boolean!=null -> if(value.boolean==true) "1" else "0"
else -> throw AssemblyError("weird struct initial value $value")
}
}
}
}
asmgen.out("; struct types")
symboltable.allStructInstances.distinctBy { it.structName }.forEach {
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
val structargs = structtype.fields.withIndex().joinToString(",") { field -> "f${field.index}" }
asmgen.out("${it.structName} .struct $structargs\n")
structtype.fields.withIndex().forEach { (index, field) ->
val dt = field.first
val varname = "f${index}"
val type = when {
dt.isBool || dt.isUnsignedByte -> ".byte"
dt.isSignedByte -> ".char"
dt.isUnsignedWord || dt.isPointer -> ".word"
dt.isSignedWord -> ".sint"
dt.isFloat -> ".byte" // TODO check that float bytes are passed as an array parameter
else -> throw AssemblyError("weird dt")
}
asmgen.out("p8v_${field.second} $type \\$varname") // note: struct field symbol prefixing done here because that is a lot simpler than fixing up all expressions in the AST
}
asmgen.out(" .endstruct\n")
}
val (instancesNoInit, instances) = symboltable.allStructInstances.partition { it.initialValues.isEmpty() }
asmgen.out("; struct instances without initialization values, as BSS zeroed at startup\n")
asmgen.out(" .section BSS\n")
asmgen.out("${StStructInstanceBlockName}_bss .block\n")
instancesNoInit.forEach {
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
val zerovalues = structtype.fields.map { field ->
if(field.first.isFloat) {
val floatbytes = List(compTarget.memorySize(BaseDataType.FLOAT)) { "?" }
"[${floatbytes.joinToString(",")}]"
}
else "?"
}
asmgen.out("${it.name} .dstruct ${it.structName}, ${zerovalues.joinToString(",")}\n")
}
asmgen.out(" .endblock\n")
asmgen.out(" .send BSS\n")
asmgen.out("; struct instances with initialization values\n")
asmgen.out(" .section STRUCTINSTANCES\n")
asmgen.out("$StStructInstanceBlockName .block\n")
instances.forEach {
val instancename = it.name.substringAfter('.')
asmgen.out("$instancename .dstruct ${it.structName}, ${initValues(it).joinToString(",")}\n")
}
asmgen.out(" .endblock\n")
asmgen.out(" .send STRUCTINSTANCES\n")
}
internal fun translateAsmSubroutine(sub: PtAsmSub) { internal fun translateAsmSubroutine(sub: PtAsmSub) {
if(sub.inline) { if(sub.inline) {
return // subroutine gets inlined at call site. return // subroutine gets inlined at call site.
@@ -416,7 +509,7 @@ internal class ProgramAndVarsGen(
val varsInSubroutine = getVars(scope) val varsInSubroutine = getVars(scope)
// Zeropage Variables // Zeropage Variables
val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedName }.toSet() val varnames = varsInSubroutine.filter { it.value.type==StNodeType.STATICVAR }.map { it.value.scopedNameString }.toSet()
zeropagevars2asm(varnames) zeropagevars2asm(varnames)
// MemDefs and Consts // MemDefs and Consts
@@ -434,7 +527,7 @@ internal class ProgramAndVarsGen(
if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main")) if((sub.name=="start" || sub.name=="p8s_start") && (sub.definingBlock()!!.name=="main" || sub.definingBlock()!!.name=="p8b_main"))
entrypointInitialization() entrypointInitialization()
val params = sub.parameters val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
if(functioncallAsmGen.optimizeIntArgsViaCpuRegisters(params)) { if(functioncallAsmGen.optimizeIntArgsViaCpuRegisters(params)) {
asmgen.out("; simple int arg(s) passed via cpu register(s)") asmgen.out("; simple int arg(s) passed via cpu register(s)")
@@ -490,7 +583,7 @@ internal class ProgramAndVarsGen(
// normal statically allocated variables // normal statically allocated variables
val variables = varsInSubroutine val variables = varsInSubroutine
.filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedName) } .filter { it.value.type==StNodeType.STATICVAR && !allocator.isZpVar(it.value.scopedNameString) }
.map { it.value as StStaticVariable } .map { it.value as StStaticVariable }
nonZpVariables2asm(variables) nonZpVariables2asm(variables)
@@ -545,12 +638,12 @@ internal class ProgramAndVarsGen(
stringVarsWithInitInZp.forEach { stringVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.name)+"_init_value" val varname = asmgen.asmVariableName(it.name)+"_init_value"
outputStringvar(varname, 0, it.value.second, it.value.first) outputStringvar(varname, 0u, it.value.second, it.value.first)
} }
arrayVarsWithInitInZp.forEach { arrayVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.name)+"_init_value" val varname = asmgen.asmVariableName(it.name)+"_init_value"
arrayVariable2asm(varname, it.alloc.dt, 0, it.value, null) arrayVariable2asm(varname, it.alloc.dt, 0u, it.value, null)
} }
asmgen.out("+") asmgen.out("+")
@@ -618,7 +711,7 @@ internal class ProgramAndVarsGen(
fun generate(section: String, variables: List<StStaticVariable>) { fun generate(section: String, variables: List<StStaticVariable>) {
asmgen.out(" .section $section") asmgen.out(" .section $section")
val (notAligned, aligned) = variables.partition { it.align == 0 } val (notAligned, aligned) = variables.partition { it.align == 0u }
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach { notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
uninitializedVariable2asm(it) uninitializedVariable2asm(it)
} }
@@ -643,8 +736,8 @@ internal class ProgramAndVarsGen(
if(varsWithInit.isNotEmpty()) { if(varsWithInit.isNotEmpty()) {
asmgen.out("; non-zeropage variables with init value") asmgen.out("; non-zeropage variables with init value")
val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt.isString } val (stringvars, othervars) = varsWithInit.sortedBy { it.name }.partition { it.dt.isString }
val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0 } val (notAlignedStrings, alignedStrings) = stringvars.partition { it.align==0u }
val (notAlignedOther, alignedOther) = othervars.partition { it.align==0 } val (notAlignedOther, alignedOther) = othervars.partition { it.align==0u }
notAlignedStrings.forEach { notAlignedStrings.forEach {
outputStringvar( outputStringvar(
it.name, it.name,
@@ -691,23 +784,27 @@ internal class ProgramAndVarsGen(
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.FLOAT_MEM_SIZE}") dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.FLOAT_MEM_SIZE}")
dt.isSplitWordArray -> { dt.isSplitWordArray -> {
alignVar(variable.align) alignVar(variable.align)
val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!) / 2 val numbytesPerHalf = compTarget.memorySize(variable.dt, variable.length!!.toInt()) / 2
asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf") asmgen.out("${variable.name}_lsb\t.fill $numbytesPerHalf")
asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf") asmgen.out("${variable.name}_msb\t.fill $numbytesPerHalf")
} }
dt.isArray -> { dt.isArray -> {
alignVar(variable.align) alignVar(variable.align)
val numbytes = compTarget.memorySize(variable.dt, variable.length!!) val numbytes = compTarget.memorySize(variable.dt, variable.length!!.toInt())
asmgen.out("${variable.name}\t.fill $numbytes") asmgen.out("${variable.name}\t.fill $numbytes")
} }
dt.isPointer -> asmgen.out("${variable.name}\t.word ?") // a pointer is just an uword address
dt.isPointerArray -> {
TODO("pointers are not supported yet")
}
else -> { else -> {
throw AssemblyError("weird dt") throw AssemblyError("weird dt")
} }
} }
} }
private fun alignVar(align: Int) { private fun alignVar(align: UInt) {
if(align > 1) if(align > 1u)
asmgen.out(" .align ${align.toHex()}") asmgen.out(" .align ${align.toHex()}")
} }
@@ -744,7 +841,7 @@ internal class ProgramAndVarsGen(
throw AssemblyError("all string vars should have been interned into prog") throw AssemblyError("all string vars should have been interned into prog")
} }
dt.isArray -> { dt.isArray -> {
arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length) arrayVariable2asm(variable.name, variable.dt, variable.align, variable.initializationArrayValue, variable.length?.toInt())
} }
else -> { else -> {
throw AssemblyError("weird dt") throw AssemblyError("weird dt")
@@ -752,7 +849,7 @@ internal class ProgramAndVarsGen(
} }
} }
private fun arrayVariable2asm(varname: String, dt: DataType, align: Int, value: StArray?, orNumberOfZeros: Int?) { private fun arrayVariable2asm(varname: String, dt: DataType, align: UInt, value: StArray?, orNumberOfZeros: Int?) {
alignVar(align) alignVar(align)
when { when {
dt.isUnsignedByteArray || dt.isBoolArray -> { dt.isUnsignedByteArray || dt.isBoolArray -> {
@@ -776,7 +873,7 @@ internal class ProgramAndVarsGen(
} }
} }
dt.isSplitWordArray -> { dt.isSplitWordArray -> {
if(dt.elementType().isUnsignedWord) { if(dt.elementType().isUnsignedWord || dt.elementType().isPointer) {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros) val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}") asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname") asmgen.out("${varname}_lsb\t.byte <_array_$varname")
@@ -824,7 +921,7 @@ internal class ProgramAndVarsGen(
private fun zeroFilledArray(numElts: Int): StArray { private fun zeroFilledArray(numElts: Int): StArray {
val values = mutableListOf<StArrayElement>() val values = mutableListOf<StArrayElement>()
repeat(numElts) { repeat(numElts) {
values.add(StArrayElement(0.0, null, null)) values.add(StArrayElement(0.0, null, null,null,null))
} }
return values return values
} }
@@ -854,7 +951,7 @@ internal class ProgramAndVarsGen(
} }
} }
private fun outputStringvar(varname: String, align: Int, encoding: Encoding, value: String) { private fun outputStringvar(varname: String, align: UInt, encoding: Encoding, value: String) {
alignVar(align) alignVar(align)
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false) asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte()) val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
@@ -882,7 +979,7 @@ internal class ProgramAndVarsGen(
val number = it.number!!.toInt() val number = it.number!!.toInt()
"$"+number.toString(16).padStart(2, '0') "$"+number.toString(16).padStart(2, '0')
} }
dt.isArray && dt.elementType().isUnsignedWord -> array.map { dt.isArray && (dt.elementType().isUnsignedWord || dt.elementType().isPointer) -> array.map {
if(it.number!=null) { if(it.number!=null) {
"$" + it.number!!.toInt().toString(16).padStart(4, '0') "$" + it.number!!.toInt().toString(16).padStart(4, '0')
} }
@@ -894,8 +991,15 @@ internal class ProgramAndVarsGen(
else else
asmgen.asmSymbolName(addrOfSymbol) asmgen.asmSymbolName(addrOfSymbol)
} }
else else if(it.structInstance!=null) {
asmgen.asmSymbolName("${StStructInstanceBlockName}.${it.structInstance!!}")
}
else if(it.structInstanceUninitialized!=null) {
asmgen.asmSymbolName("${StStructInstanceBlockName}_bss.${it.structInstanceUninitialized!!}")
}
else {
throw AssemblyError("weird array elt") throw AssemblyError("weird array elt")
}
} }
else -> throw AssemblyError("invalid dt") else -> throw AssemblyError("invalid dt")
} }
@@ -919,7 +1023,7 @@ internal class ProgramAndVarsGen(
else else
"-$$hexnum" "-$$hexnum"
} }
dt.isArray && dt.elementType().isUnsignedWord -> array.map { dt.isArray && (dt.elementType().isUnsignedWord || dt.elementType().isPointer) -> array.map {
val number = it.number!!.toInt() val number = it.number!!.toInt()
"$" + number.toString(16).padStart(4, '0') "$" + number.toString(16).padStart(4, '0')
} }

View File

@@ -52,7 +52,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE } val varsRequiringZp = allVariables.filter { it.zpwish == ZeropageWish.REQUIRE_ZEROPAGE }
val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE } val varsPreferringZp = allVariables.filter { it.zpwish == ZeropageWish.PREFER_ZEROPAGE }
val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE } val varsNotZp = allVariables.filter { it.zpwish == ZeropageWish.NOT_IN_ZEROPAGE }
val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0 } val (varsDontCareWithoutAlignment, varsDontCareWithAlignment) = allVariables.filter { it.zpwish == ZeropageWish.DONTCARE }.partition { it.align == 0u }
require(varsDontCareWithAlignment.size + varsDontCareWithoutAlignment.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables) require(varsDontCareWithAlignment.size + varsDontCareWithoutAlignment.size + varsRequiringZp.size + varsPreferringZp.size + varsNotZp.size == numberOfAllocatableVariables)
var numVariablesAllocatedInZP = 0 var numVariablesAllocatedInZP = 0
@@ -60,9 +60,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
varsRequiringZp.forEach { variable -> varsRequiringZp.forEach { variable ->
val result = zeropage.allocate( val result = zeropage.allocate(
variable.scopedName, variable.scopedNameString,
variable.dt, variable.dt,
variable.length, variable.length?.toInt(),
variable.astNode?.position ?: Position.DUMMY, variable.astNode?.position ?: Position.DUMMY,
errors errors
) )
@@ -79,9 +79,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
if(errors.noErrors()) { if(errors.noErrors()) {
varsPreferringZp.forEach { variable -> varsPreferringZp.forEach { variable ->
val result = zeropage.allocate( val result = zeropage.allocate(
variable.scopedName, variable.scopedNameString,
variable.dt, variable.dt,
variable.length, variable.length?.toInt(),
variable.astNode?.position ?: Position.DUMMY, variable.astNode?.position ?: Position.DUMMY,
errors errors
) )
@@ -92,16 +92,16 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
// try to allocate the "don't care" interger variables into the zeropage until it is full. // try to allocate the "don't care" interger variables into the zeropage until it is full.
// TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...? // TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...?
if(errors.noErrors()) { if(errors.noErrors()) {
val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedName } val sortedList = varsDontCareWithoutAlignment.sortedByDescending { it.scopedNameString }
for (variable in sortedList) { for (variable in sortedList) {
if(variable.dt.isIntegerOrBool) { if(variable.dt.isIntegerOrBool || variable.dt.isPointer) {
if(zeropage.free.isEmpty()) { if(zeropage.free.isEmpty()) {
break break
} else { } else {
val result = zeropage.allocate( val result = zeropage.allocate(
variable.scopedName, variable.scopedNameString,
variable.dt, variable.dt,
variable.length, variable.length?.toInt(),
variable.astNode?.position ?: Position.DUMMY, variable.astNode?.position ?: Position.DUMMY,
errors errors
) )

View File

@@ -20,6 +20,8 @@ internal class AnyExprAsmGen(
private val asmgen: AsmGen6502Internal private val asmgen: AsmGen6502Internal
) { ) {
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
if(expr.operator==".")
throw AssemblyError("pointer deref expression should have been handled elsewhere ${expr.position}")
when { when {
expr.type.isByteOrBool -> { expr.type.isByteOrBool -> {
if(expr.left.type.isByteOrBool && expr.right.type.isByteOrBool) if(expr.left.type.isByteOrBool && expr.right.type.isByteOrBool)
@@ -46,6 +48,12 @@ internal class AnyExprAsmGen(
} }
return assignFloatBinExpr(expr, assign) return assignFloatBinExpr(expr, assign)
} }
expr.type.isPointer -> {
require((expr.left.type.isPointer || expr.left.type.isUnsignedWord) && (expr.right.type.isPointer || expr.right.type.isUnsignedWord)) {
"both operands must be pointers or uwords"
}
throw AssemblyError("expression should have been handled otherwise: pointer ${expr.operator} at ${expr.position}")
}
else -> throw AssemblyError("weird expression type in assignment") else -> throw AssemblyError("weird expression type in assignment")
} }
} }

View File

@@ -6,11 +6,12 @@ import prog8.codegen.cpu6502.AsmGen6502Internal
internal enum class TargetStorageKind { internal enum class TargetStorageKind {
VARIABLE, VARIABLE, // non-pointer variable
ARRAY, ARRAY,
MEMORY, MEMORY,
REGISTER, REGISTER,
VOID // assign nothing - used in multi-value assigns for void placeholders POINTER, // wherever the pointer variable points to
VOID // assign nothing - used in multi-value assigns for void placeholders
} }
internal enum class SourceStorageKind { internal enum class SourceStorageKind {
@@ -32,6 +33,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
val array: PtArrayIndexer? = null, val array: PtArrayIndexer? = null,
val memory: PtMemoryByte? = null, val memory: PtMemoryByte? = null,
val register: RegisterOrPair? = null, val register: RegisterOrPair? = null,
val pointer: PtPointerDeref? = null,
val origAstTarget: PtAssignTarget? = null val origAstTarget: PtAssignTarget? = null
) )
{ {
@@ -39,13 +41,24 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
val asmVarname: String by lazy { val asmVarname: String by lazy {
if (array == null) if (array == null)
variableAsmName!! variableAsmName!!
else else {
asmgen.asmVariableName(array.variable) if(array.variable==null)
TODO("asmVarname for array with pointer")
asmgen.asmVariableName(array.variable!!)
}
} }
init { init {
if(register!=null && !datatype.isNumericOrBool) if(register!=null && !datatype.isNumericOrBool)
throw AssemblyError("must be numeric type") throw AssemblyError("must be numeric type")
if(kind==TargetStorageKind.REGISTER)
require(register!=null)
else
require(register==null)
if(kind==TargetStorageKind.POINTER)
require(pointer!=null)
if(pointer!=null)
require(kind==TargetStorageKind.POINTER)
} }
companion object { companion object {
@@ -77,6 +90,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
} }
array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, target.position, array = array, origAstTarget = this) array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, target.position, array = array, origAstTarget = this)
memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, target.position, memory = memory, origAstTarget = this) memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, target.position, memory = memory, origAstTarget = this)
pointerDeref != null -> return AsmAssignTarget(TargetStorageKind.POINTER, asmgen, type, definingSub, target.position, pointer = pointerDeref, origAstTarget = this)
else -> throw AssemblyError("weird target") else -> throw AssemblyError("weird target")
} }
} }
@@ -134,11 +148,14 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
left is PtIdentifier && left.name==scopedName left is PtIdentifier && left.name==scopedName
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords left is PtArrayIndexer && left isSameAs array!! && left.splitWords==array.splitWords && (left.pointerderef==null && array.pointerderef==null || left.pointerderef!! isSameAs array.pointerderef!!)
} }
TargetStorageKind.MEMORY -> { TargetStorageKind.MEMORY -> {
left isSameAs memory!! left isSameAs memory!!
} }
TargetStorageKind.POINTER -> {
TODO("is pointer deref target same as expression? ${this.position}")
}
TargetStorageKind.REGISTER -> false TargetStorageKind.REGISTER -> false
TargetStorageKind.VOID -> false TargetStorageKind.VOID -> false
} }
@@ -160,8 +177,11 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
val asmVarname: String val asmVarname: String
get() = if(array==null) get() = if(array==null)
variableAsmName!! variableAsmName!!
else else {
asmgen.asmVariableName(array.variable) if(array.variable==null)
TODO("asmVarname for array with pointer")
asmgen.asmVariableName(array.variable!!)
}
companion object { companion object {
fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen6502Internal): AsmAssignSource { fun fromAstSource(value: PtExpression, program: PtProgram, asmgen: AsmGen6502Internal): AsmAssignSource {
@@ -203,7 +223,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}") val symbol = asmgen.symbolTable.lookup(value.name) ?: throw AssemblyError("lookup error ${value.name}")
val sub = symbol.astNode as IPtSubroutine val sub = symbol.astNode as IPtSubroutine
val returnType = val returnType =
if(sub is PtSub && sub.returns.size>1) if(sub is PtSub && sub.signature.returns.size>1)
DataType.UNDEFINED // TODO list of types instead? DataType.UNDEFINED // TODO list of types instead?
else else
sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second

View File

@@ -9,6 +9,7 @@ import prog8.codegen.cpu6502.VariableAllocator
internal class AugmentableAssignmentAsmGen(private val program: PtProgram, internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
private val assignmentAsmGen: AssignmentAsmGen, private val assignmentAsmGen: AssignmentAsmGen,
private val asmgen: AsmGen6502Internal, private val asmgen: AsmGen6502Internal,
private val ptrgen: PointerAssignmentsGen,
private val allocator: VariableAllocator private val allocator: VariableAllocator
) { ) {
fun translate(assign: AsmAugmentedAssignment, scope: IPtSubroutine?) { fun translate(assign: AsmAugmentedAssignment, scope: IPtSubroutine?) {
@@ -84,22 +85,23 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
} }
} }
target.datatype.isWord -> { target.datatype.isWord || target.datatype.isPointer -> {
val block = target.origAstTarget?.definingBlock() val block = target.origAstTarget?.definingBlock()
val targetDt = if(target.datatype.isWord) target.datatype else DataType.UWORD // pointers themselves that get a new value are just treated as UWORD variables
when(value.kind) { when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationWordWithLiteralval(target.asmVarname, target.datatype, operator, value.boolean!!.asInt(), block) SourceStorageKind.LITERALBOOLEAN -> inplacemodificationWordWithLiteralval(target.asmVarname, targetDt, operator, value.boolean!!.asInt(), block)
SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(target.asmVarname, target.datatype, operator, value.number!!.number.toInt(), block) SourceStorageKind.LITERALNUMBER -> inplacemodificationWordWithLiteralval(target.asmVarname, targetDt, operator, value.number!!.number.toInt(), block)
SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, value.asmVarname, value.datatype, block) SourceStorageKind.VARIABLE -> inplacemodificationWordWithVariable(target.asmVarname, targetDt, operator, value.asmVarname, value.datatype, block)
SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(target.asmVarname, target.datatype, operator, regName(value), value.datatype, block) SourceStorageKind.REGISTER -> inplacemodificationWordWithVariable(target.asmVarname, targetDt, operator, regName(value), value.datatype, block)
SourceStorageKind.MEMORY -> inplacemodificationWordWithMemread(target.asmVarname, target.datatype, operator, value.memory!!) SourceStorageKind.MEMORY -> inplacemodificationWordWithMemread(target.asmVarname, targetDt, operator, value.memory!!)
SourceStorageKind.ARRAY -> inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.array!!, block) SourceStorageKind.ARRAY -> inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.array!!, block)
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
if(value.expression is PtTypeCast) { if(value.expression is PtTypeCast) {
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression, block) inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.expression, block)
} }
else { else {
inplacemodificationWordWithValue(target.asmVarname, target.datatype, operator, value.expression!!, block) inplacemodificationWordWithValue(target.asmVarname, targetDt, operator, value.expression!!, block)
} }
} }
} }
@@ -224,7 +226,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
val indexNum = target.array!!.index as? PtNumber val deref = target.array!!.pointerderef
if(deref!=null) {
TODO("inplace modification array indexed pointer deref ${target.position}")
return
}
val targetArrayVar = target.array.variable!!
val indexNum = target.array.index as? PtNumber
if (indexNum!=null) { if (indexNum!=null) {
val index = indexNum.number.toInt() val index = indexNum.number.toInt()
if(target.array.splitWords) { if(target.array.splitWords) {
@@ -301,7 +309,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
} }
} }
target.datatype.isPointer -> TODO("inplace modification of pointer array ${target.position}")
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}") else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
} }
} }
@@ -320,7 +328,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
return return
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
asmgen.saveRegisterStack(CpuRegister.Y, false) asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.out(" lda ${target.array.variable.name},y") asmgen.out(" lda ${targetArrayVar.name},y")
when(value.kind) { when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> { SourceStorageKind.LITERALBOOLEAN -> {
inplacemodificationRegisterAwithVariable(operator, "#${value.boolean!!.asInt()}", target.datatype.isSigned) inplacemodificationRegisterAwithVariable(operator, "#${value.boolean!!.asInt()}", target.datatype.isSigned)
@@ -366,7 +374,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(" lda $tempVar") asmgen.out(" lda $tempVar")
} }
} }
asmgen.out(" sta ${target.array.variable.name},y") asmgen.out(" sta ${targetArrayVar.name},y")
} }
target.datatype.isWord -> { target.datatype.isWord -> {
@@ -377,11 +385,11 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
asmgen.saveRegisterStack(CpuRegister.Y, false) asmgen.saveRegisterStack(CpuRegister.Y, false)
if(target.array.splitWords) { if(target.array.splitWords) {
asmgen.out(" lda ${target.array.variable.name}_lsb,y") asmgen.out(" lda ${targetArrayVar.name}_lsb,y")
asmgen.out(" ldx ${target.array.variable.name}_msb,y") asmgen.out(" ldx ${targetArrayVar.name}_msb,y")
} else { } else {
asmgen.out(" lda ${target.array.variable.name},y") asmgen.out(" lda ${targetArrayVar.name},y")
asmgen.out(" ldx ${target.array.variable.name}+1,y") asmgen.out(" ldx ${targetArrayVar.name}+1,y")
} }
val block = target.origAstTarget?.definingBlock() val block = target.origAstTarget?.definingBlock()
when(value.kind) { when(value.kind) {
@@ -450,9 +458,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
asmgen.restoreRegisterStack(CpuRegister.Y, true) asmgen.restoreRegisterStack(CpuRegister.Y, true)
if(target.array.splitWords) if(target.array.splitWords)
asmgen.out(" sta ${target.array.variable.name}_lsb,y | txa | sta ${target.array.variable.name}_msb,y") asmgen.out(" sta ${targetArrayVar.name}_lsb,y | txa | sta ${targetArrayVar.name}_msb,y")
else else
asmgen.out(" sta ${target.array.variable.name},y | txa | sta ${target.array.variable.name}+1,y") asmgen.out(" sta ${targetArrayVar.name},y | txa | sta ${targetArrayVar.name}+1,y")
} }
target.datatype.isFloat -> { target.datatype.isFloat -> {
@@ -504,18 +512,24 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
pla ; restore array ptr lsb pla ; restore array ptr lsb
jsr floats.copy_float""") jsr floats.copy_float""")
} }
target.datatype.isPointer -> ptrgen.inplaceModification(PtrTarget(target), operator, value)
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}") else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
} }
} }
} }
TargetStorageKind.POINTER -> ptrgen.inplaceModification(PtrTarget(target), operator, value)
TargetStorageKind.REGISTER -> throw AssemblyError("no asm gen for reg in-place modification") TargetStorageKind.REGISTER -> throw AssemblyError("no asm gen for reg in-place modification")
TargetStorageKind.VOID -> { /* do nothing */ } TargetStorageKind.VOID -> { /* do nothing */ }
} }
} }
private fun tryIndexedIncDec(array: PtArrayIndexer, operator: String): Boolean { private fun tryIndexedIncDec(array: PtArrayIndexer, operator: String): Boolean {
val arrayvar = asmgen.asmVariableName(array.variable) val arrayVar = array.variable
if(arrayVar==null) {
TODO("indexed inc/dec on pointer ${array.position}")
return false
}
val arrayvar = asmgen.asmVariableName(arrayVar)
when { when {
array.type.isByte -> { array.type.isByte -> {
asmgen.loadScaledArrayIndexIntoRegister(array, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(array, CpuRegister.X)
@@ -571,6 +585,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
asmgen.out(if(operator=="+") " jsr floats.inc_var_f" else " jsr floats.dec_var_f") asmgen.out(if(operator=="+") " jsr floats.inc_var_f" else " jsr floats.dec_var_f")
return true return true
} }
array.type.isPointer -> TODO("indexed inc/dec on pointer ${array.position}")
else -> return false else -> return false
} }
} }
@@ -884,7 +899,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
private fun tryInplaceModifyWithRemovedRedundantCast(value: PtTypeCast, target: AsmAssignTarget, operator: String): Boolean { private fun tryInplaceModifyWithRemovedRedundantCast(value: PtTypeCast, target: AsmAssignTarget, operator: String): Boolean {
if (target.datatype == value.type) { if (target.datatype == value.type || (target.datatype.isPointer && value.type.isWord)) {
val childDt = value.value.type val childDt = value.value.type
if (!value.type.isFloat && (value.type.equalsSize(childDt) || value.type.largerSizeThan(childDt))) { if (!value.type.isFloat && (value.type.equalsSize(childDt) || value.type.largerSizeThan(childDt))) {
// this typecast is redundant here; the rest of the code knows how to deal with the uncasted value. // this typecast is redundant here; the rest of the code knows how to deal with the uncasted value.
@@ -972,7 +987,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} else { } else {
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY) asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2") asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.out("+\tinc ${'$'}ffff\t; modified") asmgen.out($$"+\tinc $ffff\t; modified")
} }
} else { } else {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar) val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
@@ -992,7 +1007,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} else { } else {
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY) asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2") asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.out("+\tdec ${'$'}ffff\t; modified") asmgen.out($$"+\tdec $ffff\t; modified")
} }
} else { } else {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar) val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
@@ -1125,11 +1140,18 @@ $shortcutLabel:""")
} }
if(value is PtArrayIndexer && value.isSimple()) { if(value is PtArrayIndexer && value.isSimple()) {
val valueVar = value.variable
if(valueVar==null) {
TODO("inplace modification on pointer ${value.position}")
return
}
// use the already existing optimized codegen for regular assignments x += array[index] // use the already existing optimized codegen for regular assignments x += array[index]
val binexpr = PtBinaryExpression(operator, dt, value.position) val binexpr = PtBinaryExpression(operator, dt, value.position)
binexpr.add(PtIdentifier(name, dt, value.position)) binexpr.add(PtIdentifier(name, dt, value.position))
val arrayValue = PtArrayIndexer(value.type, value.position) val arrayValue = PtArrayIndexer(value.type, value.position)
arrayValue.add(value.variable) arrayValue.add(valueVar)
arrayValue.add(value.index) arrayValue.add(value.index)
binexpr.add(arrayValue) binexpr.add(arrayValue)
binexpr.parent = value binexpr.parent = value
@@ -2256,7 +2278,7 @@ $shortcutLabel:""")
private fun inplacemodificationWordWithVariable(name: String, dt: DataType, operator: String, otherName: String, valueDt: DataType, block: PtBlock?) { private fun inplacemodificationWordWithVariable(name: String, dt: DataType, operator: String, otherName: String, valueDt: DataType, block: PtBlock?) {
require(dt.isWord) require(dt.isWord)
require(valueDt.isInteger) require(valueDt.isInteger || valueDt.isPointer)
when { when {
valueDt.isByte -> { valueDt.isByte -> {
// the other variable is a BYTE type so optimize for that // the other variable is a BYTE type so optimize for that
@@ -2466,7 +2488,7 @@ $shortcutLabel:""")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
valueDt.isWord -> { valueDt.isWord || valueDt.isPointer -> {
// the value is a proper 16-bit word, so use both bytes of it. // the value is a proper 16-bit word, so use both bytes of it.
when (operator) { when (operator) {
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1") "+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
@@ -2888,7 +2910,7 @@ $shortcutLabel:""")
else -> throw AssemblyError("invalid operator for in-place modification $operator") else -> throw AssemblyError("invalid operator for in-place modification $operator")
} }
} }
valueDt.isWord -> { valueDt.isWord || valueDt.isPointer -> {
// the value is a proper 16-bit word, so use both bytes of it. // the value is a proper 16-bit word, so use both bytes of it.
if(value is PtArrayIndexer && value.isSimple()) { if(value is PtArrayIndexer && value.isSimple()) {
@@ -2903,7 +2925,12 @@ $shortcutLabel:""")
"-" -> { "-" -> {
if(value.index.type.isByte) { if(value.index.type.isByte) {
// it's an array indexed by a byte so we can use sbc array,y // it's an array indexed by a byte so we can use sbc array,y
val arrayname = value.variable.name val valueVar = value.variable
if(valueVar==null) {
TODO("inplace modification on pointer ${value.position}")
return
}
val arrayname = valueVar.name
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y) asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
if(value.splitWords) { if(value.splitWords) {
asmgen.out(""" asmgen.out("""

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,9 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer { internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType, numElements: Int?): Int { override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray) { if(dt.isPointerArray)
return 2 * numElements!!
else if(dt.isArray) {
require(numElements != null) require(numElements != null)
return when(dt.sub) { return when(dt.sub) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements

View File

@@ -1,5 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
kotlin("jvm") kotlin("jvm")
} }
@@ -11,7 +9,7 @@ dependencies {
implementation(project(":codeGenIntermediate")) implementation(project(":codeGenIntermediate"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1") implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
} }
sourceSets { sourceSets {

View File

@@ -1,5 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
kotlin("jvm") kotlin("jvm")
} }
@@ -11,12 +9,10 @@ dependencies {
implementation(project(":intermediate")) implementation(project(":intermediate"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1") implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1") testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
testImplementation("io.kotest:kotest-framework-datatest:5.9.1") testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
} }
sourceSets { sourceSets {

View File

@@ -13,9 +13,8 @@
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="simpleAst" /> <orderEntry type="module" module-name="simpleAst" />
<orderEntry type="module" module-name="intermediate" /> <orderEntry type="module" module-name="intermediate" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" /> <orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" /> <orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
</component> </component>
</module> </module>

View File

@@ -124,34 +124,152 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val target = augAssign.target val target = augAssign.target
val targetDt = irType(target.type) val targetDt = irType(target.type)
val value = augAssign.value
val memTarget = target.memory val memTarget = target.memory
val constAddress = (memTarget?.address as? PtNumber)?.number?.toInt() val constAddress = (memTarget?.address as? PtNumber)?.number?.toInt()
val symbol = target.identifier?.name val symbol = target.identifier?.name
val array = target.array val array = target.array
val value = augAssign.value
val signed = target.type.isSigned val signed = target.type.isSigned
val pointerDeref = target.pointerDeref
val chunks: IRCodeChunks
val chunks = when (augAssign.operator) { if(pointerDeref!=null) {
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value) val inplaceInstrs = mutableListOf<IRCodeChunkBase>()
"-=" -> operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value) val (addressReg, fieldOffset) = codeGen.evaluatePointerAddressIntoReg(inplaceInstrs, pointerDeref)
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value, signed) val oldvalueReg = codeGen.registers.next(targetDt)
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value) if((augAssign.operator=="+=" || augAssign.operator=="-=") && value.asConstInteger()==1 || value.asConstInteger()==2) {
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value) // INC/DEC optimization instead of ADD/SUB
"&=" -> operatorAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
"and=" -> operatorLogicalAndInplace(symbol, array, constAddress, memTarget, targetDt, value) loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
"^=", "xor=" -> operatorXorInplace(symbol, array, constAddress, memTarget, targetDt, value) val instr = if(augAssign.operator=="+=") Opcode.INC else Opcode.DEC
"<<=" -> operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value) repeat(value.asConstInteger()!!) {
">>=" -> operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed) addInstr(inplaceInstrs, IRInstruction(instr, targetDt, reg1 = oldvalueReg), null)
"%=" -> operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value) }
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
} else {
var operandTr = ExpressionCodeResult(emptyList(), IRDataType.BYTE, -1, -1)
if(augAssign.operator!="or=" && augAssign.operator!="and=") {
// for everything except the shortcircuit boolean operators, we can evaluate the value here unconditionally
operandTr = expressionEval.translateExpression(value)
// note: the instructions to load the value will be placed after the LOADFIELD instruction so that later optimizations about what modification is actually done, are easier
}
if(targetDt== IRDataType.FLOAT) {
loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
inplaceInstrs += operandTr.chunks
when(augAssign.operator) {
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVSR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
"+" -> { /* inplace + is a no-op */ }
else -> throw AssemblyError("invalid augmented assign operator for floats ${augAssign.operator}")
}
} else {
loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
inplaceInstrs += operandTr.chunks
when(augAssign.operator) {
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"|=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"&=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"^=", "xor=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"<<=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSLN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
">>=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSRN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
"or=" -> {
val shortcutLabel = codeGen.createLabelName()
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)
val valueTr = expressionEval.translateExpression(value)
inplaceInstrs += valueTr.chunks
addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
}
"and=" -> {
val shortcutLabel = codeGen.createLabelName()
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTEQ, labelSymbol = shortcutLabel), null)
val valueTr = expressionEval.translateExpression(value)
inplaceInstrs += valueTr.chunks
addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
}
"-" -> addInstr(inplaceInstrs, IRInstruction(Opcode.NEG, targetDt, reg1 = oldvalueReg), null)
"~" -> addInstr(inplaceInstrs, IRInstruction(Opcode.INV, targetDt, reg1 = oldvalueReg), null)
"not" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XOR, targetDt, reg1 = oldvalueReg, immediate = 1), null)
"+" -> { /* inplace + is a no-op */ }
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
}
}
}
codeGen.storeValueAtPointersLocation(inplaceInstrs, addressReg, fieldOffset, pointerDeref.type, false, oldvalueReg)
chunks = inplaceInstrs
} else {
chunks = when (augAssign.operator) {
"+=" -> operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
"-=" -> operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
"*=" -> operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"/=" -> operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"|=" -> operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
"or=" -> operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
"&=" -> operatorAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
"and=" -> operatorLogicalAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
"^=", "xor=" -> operatorXorInplace(symbol, array, constAddress, memTarget, targetDt, value)
"<<=" -> operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value)
">>=" -> operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
"%=" -> operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value)
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
} ?: fallbackAssign(augAssign)
}
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
} ?: fallbackAssign(augAssign)
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(augAssign.position) chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(augAssign.position)
return chunks return chunks
} }
private fun loadfield(
inplaceInstrs: MutableList<IRCodeChunkBase>,
addressReg: Int,
fieldOffset: UByte,
targetDt: IRDataType,
oldvalueReg: Int
) {
if (targetDt == IRDataType.FLOAT) {
if (fieldOffset > 0u)
addInstr(
inplaceInstrs,
IRInstruction(Opcode.LOADFIELD, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg, immediate = fieldOffset.toInt()),
null
)
else
addInstr(
inplaceInstrs,
IRInstruction(Opcode.LOADI, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg),
null
)
} else {
if (fieldOffset > 0u)
addInstr(
inplaceInstrs,
IRInstruction(Opcode.LOADFIELD, targetDt, reg1 = oldvalueReg, reg2 = addressReg, immediate = fieldOffset.toInt()),
null
)
else
addInstr(
inplaceInstrs,
IRInstruction(Opcode.LOADI, targetDt, reg1 = oldvalueReg, reg2 = addressReg),
null
)
}
}
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks { private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
val value: PtExpression val value: PtExpression
if(origAssign.operator in PrefixOperators) { if(origAssign.operator in PrefixOperators) {
@@ -240,45 +358,47 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
when(operator) { when(operator) {
"+" -> { } "+" -> { }
"-" -> { "-" -> {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
val skipCarryLabel = codeGen.createLabelName() val skipCarryLabel = codeGen.createLabelName()
if(constIndex!=null) { if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null) addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex), null)
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel), null) addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel), null)
addInstr(result, IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null) addInstr(result, IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex), null)
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), skipCarryLabel) addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex), skipCarryLabel)
} else { } else {
val indexReg = loadIndex() val indexReg = loadIndex()
val registerLsb = codeGen.registers.next(IRDataType.BYTE) val registerLsb = codeGen.registers.next(IRDataType.BYTE)
val registerMsb = codeGen.registers.next(IRDataType.BYTE) val registerMsb = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb") it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerLsb) it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerLsb)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb") it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb") it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerMsb) it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerMsb)
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = registerLsb, immediate = 0) it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = registerLsb, immediate = 0)
it += IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel) it += IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel)
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1 = registerMsb) it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1 = registerMsb)
} }
result += IRCodeChunk(skipCarryLabel, null).also { result += IRCodeChunk(skipCarryLabel, null).also {
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb") it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
} }
} }
} }
"~" -> { "~" -> {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
if(constIndex!=null) { if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null) addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex), null)
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null) addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex), null)
} else { } else {
val indexReg = loadIndex() val indexReg = loadIndex()
val register = codeGen.registers.next(IRDataType.BYTE) val register = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb") it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register) it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb") it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_lsb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb") it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register) it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb") it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName+"_msb")
} }
} }
} }
@@ -292,44 +412,47 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
when(operator) { when(operator) {
"+" -> { } "+" -> { }
"-" -> { "-" -> {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
if(constIndex!=null) { if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null) addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize), null)
} else { } else {
val indexReg = loadIndex() val indexReg = loadIndex()
val register = codeGen.registers.next(vmDt) val register = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name) it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
it += IRInstruction(Opcode.NEG, vmDt, reg1 = register) it += IRInstruction(Opcode.NEG, vmDt, reg1 = register)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name) it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
} }
} }
} }
"~" -> { "~" -> {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
if(constIndex!=null) { if(constIndex!=null) {
addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null) addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize), null)
} else { } else {
val indexReg = loadIndex() val indexReg = loadIndex()
val register = codeGen.registers.next(vmDt) val register = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name) it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
it += IRInstruction(Opcode.INV, vmDt, reg1 = register) it += IRInstruction(Opcode.INV, vmDt, reg1 = register)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name) it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
} }
} }
} }
"not" -> { "not" -> {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
val register = codeGen.registers.next(vmDt) val register = codeGen.registers.next(vmDt)
if(constIndex!=null) { if(constIndex!=null) {
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=register, immediate = 1) it += IRInstruction(Opcode.LOAD, vmDt, reg1=register, immediate = 1)
it += IRInstruction(Opcode.XORM, vmDt, reg1=register, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) it += IRInstruction(Opcode.XORM, vmDt, reg1=register, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
} }
} else { } else {
val indexReg = loadIndex() val indexReg = loadIndex()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name) it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
it += IRInstruction(Opcode.XOR, vmDt, reg1 = register, immediate = 1) it += IRInstruction(Opcode.XOR, vmDt, reg1 = register, immediate = 1)
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name) it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = arrayVariableName)
} }
} }
} }
@@ -343,6 +466,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val targetIdent = assignment.target.identifier val targetIdent = assignment.target.identifier
val targetMemory = assignment.target.memory val targetMemory = assignment.target.memory
val targetArray = assignment.target.array val targetArray = assignment.target.array
val targetPointerDeref = assignment.target.pointerDeref
val valueDt = irType(assignment.value.type) val valueDt = irType(assignment.value.type)
val targetDt = irType(assignment.target.type) val targetDt = irType(assignment.target.type)
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
@@ -400,80 +524,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return result return result
} }
else if(targetArray!=null) { else if(targetArray!=null) {
val variable = targetArray.variable.name val eltSize = codeGen.program.memsizer.memorySize(targetArray.type, null)
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type, null) val variable = targetArray.variable
if(variable==null)
val fixedIndex = targetArray.index.asConstInteger() translateRegularAssignPointerIndexed(result, targetArray.pointerderef!!, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name) else if(variable.type.isPointer)
if(zero) { assignToIndexedSimplePointer(result, variable, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
if(fixedIndex!=null) { else
val chunk = IRCodeChunk(null, null).also { translateRegularAssignArrayIndexed(result, variable.name, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
else
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_lsb")
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, immediate = arrayLength, labelSymbol = variable+"_msb")
}
else
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
}
}
} else {
if(targetDt== IRDataType.FLOAT) {
if(fixedIndex!=null) {
val offset = fixedIndex*itemsize
val chunk = IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = variable, symbolOffset = offset)
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable)
}
}
} else {
if(fixedIndex!=null) {
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
else
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
}
result += chunk
} else {
val (code, indexReg) = loadIndexReg(targetArray, itemsize)
result += code
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
else
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
}
}
}
}
return result return result
} }
else if(targetMemory!=null) { else if(targetMemory!=null) {
@@ -497,31 +555,33 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return result return result
} }
val ptrWithOffset = targetMemory.address as? PtBinaryExpression val ptrWithOffset = targetMemory.address as? PtBinaryExpression
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) { if(ptrWithOffset!=null) {
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) { if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
// STOREIX only works with byte index. val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
val ptrName = (ptrWithOffset.left as PtIdentifier).name if(constOffset in 0..255) {
val offsetReg = codeGen.registers.next(IRDataType.BYTE) val ptrName = (ptrWithOffset.left as PtIdentifier).name
result += IRCodeChunk(null, null).also { val pointerReg = codeGen.registers.next(IRDataType.WORD)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger()) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=offsetReg, labelSymbol = ptrName) it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1=pointerReg, labelSymbol = ptrName)
it += IRInstruction(Opcode.STOREFIELD, IRDataType.BYTE, reg1=valueRegister, reg2=pointerReg, immediate = constOffset)
}
return result
} }
}
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// STOREIX only works with byte index.
val tr = if(offsetTypecast?.value?.type?.isByte==true)
expressionEval.translateExpression(offsetTypecast.value)
else
expressionEval.translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
return result return result
} }
} }
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// STOREIX only works with byte index.
val tr = if(offsetTypecast?.value?.type?.isByte==true)
expressionEval.translateExpression(offsetTypecast.value)
else
expressionEval.translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
return result
}
val tr = expressionEval.translateExpression(targetMemory.address) val tr = expressionEval.translateExpression(targetMemory.address)
val addressReg = tr.resultReg val addressReg = tr.resultReg
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
@@ -531,29 +591,188 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return result return result
} }
else if(targetPointerDeref!=null) {
val (addressReg, offset) = codeGen.evaluatePointerAddressIntoReg(result, targetPointerDeref)
val actualValueReg = if(targetPointerDeref.type.isFloat) valueFpRegister else valueRegister
codeGen.storeValueAtPointersLocation(result, addressReg, offset, targetPointerDeref.type, zero, actualValueReg)
return result
}
else else
throw AssemblyError("weird assigntarget") throw AssemblyError("weird assigntarget")
} }
private fun loadIndexReg(array: PtArrayIndexer, itemsize: Int): Pair<IRCodeChunks, Int> { private fun assignToIndexedSimplePointer(
// returns the code to load the Index into the register, which is also returned. result: MutableList<IRCodeChunkBase>,
targetIdent: PtIdentifier,
eltSize: Int,
targetArray: PtArrayIndexer,
zeroValue: Boolean,
targetDt: IRDataType,
valueRegister: Int,
valueFpRegister: Int
) {
val pointerTr = expressionEval.translateExpression(targetIdent)
result += pointerTr.chunks
val pointerReg = pointerTr.resultReg
val result = mutableListOf<IRCodeChunkBase>() val constIndex = targetArray.index.asConstInteger()
if(itemsize==1 || array.splitWords) { if(zeroValue) {
val tr = expressionEval.translateExpression(array.index) if(constIndex!=null) {
addToResult(result, tr, tr.resultReg, -1) val offset = eltSize * constIndex
return Pair(result, tr.resultReg) addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=pointerReg, immediate = offset), null)
} else {
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, true, targetArray.splitWords)
result += code
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexReg), null)
}
codeGen.storeValueAtPointersLocation(result, pointerReg, 0u, targetIdent.type.dereference(), true, -1)
} else {
if(constIndex!=null) {
val offset = eltSize * constIndex
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=pointerReg, immediate = offset), null)
} else {
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, true, targetArray.splitWords)
result += code
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexReg), null)
}
val realValueReg = if(targetDt == IRDataType.FLOAT) valueFpRegister else valueRegister
codeGen.storeValueAtPointersLocation(result, pointerReg, 0u, targetIdent.type.dereference(), false, realValueReg)
}
}
private fun translateRegularAssignArrayIndexed(
result: MutableList<IRCodeChunkBase>,
variable: String,
eltSize: Int,
targetArray: PtArrayIndexer,
zero: Boolean,
targetDt: IRDataType,
valueRegister: Int,
valueFpRegister: Int
) {
val fixedIndex = targetArray.index.asConstInteger()
val arrayLength = codeGen.symbolTable.getLength(variable)
if(zero) {
if(fixedIndex!=null) {
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
else
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = variable, symbolOffset = fixedIndex*eltSize)
}
result += chunk
} else {
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, false, targetArray.splitWords)
result += code
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, labelSymbol = variable+"_lsb")
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1 = indexReg, labelSymbol = variable+"_msb")
}
else
it += IRInstruction(Opcode.STOREZX, targetDt, reg1=indexReg, labelSymbol = variable)
}
}
} else {
if(targetDt== IRDataType.FLOAT) {
if(fixedIndex!=null) {
val offset = fixedIndex*eltSize
val chunk = IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = variable, symbolOffset = offset)
}
result += chunk
} else {
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, false, targetArray.splitWords)
result += code
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = indexReg, fpReg1 = valueFpRegister, labelSymbol = variable)
}
}
} else {
if(fixedIndex!=null) {
val chunk = IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = lsbmsbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
}
else
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = variable, symbolOffset = fixedIndex*eltSize)
}
result += chunk
} else {
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, false, targetArray.splitWords)
result += code
result += IRCodeChunk(null, null).also {
if(targetArray.splitWords) {
val lsbmsbReg = codeGen.registers.next(IRDataType.BYTE)
it += IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = lsbmsbReg, reg2 = valueRegister)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = lsbmsbReg, reg2=indexReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
}
else
it += IRInstruction(Opcode.STOREX, targetDt, reg1 = valueRegister, reg2=indexReg, labelSymbol = variable)
}
}
}
}
}
private fun translateRegularAssignPointerIndexed(
result: MutableList<IRCodeChunkBase>,
pointerderef: PtPointerDeref,
eltSize: Int,
targetArray: PtArrayIndexer,
zero: Boolean,
targetDt: IRDataType,
valueRegister: Int,
valueFpRegister: Int
) {
val pointerTr = expressionEval.translateExpression(pointerderef)
result += pointerTr.chunks
val pointerReg = pointerTr.resultReg
val fixedIndex = targetArray.index.asConstInteger()
if(fixedIndex!=null) {
val offset = fixedIndex*eltSize
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = offset), null)
if(zero) {
addInstr(result, IRInstruction(Opcode.STOREZI, targetDt, reg1 = pointerReg), null)
} else {
addInstr(
result,
if (targetDt == IRDataType.FLOAT)
IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = valueFpRegister, reg1 = pointerReg)
else
IRInstruction(Opcode.STOREI, targetDt, reg1 = valueRegister, reg2 = pointerReg), null
)
}
} else {
// index is an expression
val (code, indexReg) = codeGen.loadIndexReg(targetArray.index, eltSize, true, targetArray.splitWords)
result += code
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg), null)
if(zero) {
addInstr(result, IRInstruction(Opcode.STOREZI, targetDt, reg1 = pointerReg), null)
} else {
addInstr(result,
if(targetDt== IRDataType.FLOAT)
IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 =valueFpRegister, reg1 = pointerReg)
else
IRInstruction(Opcode.STOREI, targetDt, reg1 = valueRegister, reg2 = pointerReg)
, null)
}
} }
val mult: PtExpression = PtBinaryExpression("*", DataType.UBYTE, array.position)
mult.children += array.index
mult.children += PtNumber(BaseDataType.UBYTE, itemsize.toDouble(), array.position)
val tr = expressionEval.translateExpression(mult)
addToResult(result, tr, tr.resultReg, -1)
return Pair(result, tr.resultReg)
} }
private fun operatorAndInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? { private fun operatorAndInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) { if(array!=null) {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger() val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger() val constValue = operand.asConstInteger()
@@ -565,14 +784,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8)
it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex) it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex) it += IRInstruction(Opcode.ANDM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
} }
} else { } else {
val valueReg = codeGen.registers.next(vmDt) val valueReg = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue) it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
} }
} }
return result return result
@@ -594,6 +813,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun operatorLogicalAndInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? { private fun operatorLogicalAndInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) { if(array!=null) {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger() val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger() val constValue = operand.asConstInteger()
@@ -605,7 +825,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val valueReg = codeGen.registers.next(vmDt) val valueReg = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue) it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) it += IRInstruction(Opcode.ANDM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
} }
} }
return result return result
@@ -652,20 +872,21 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val constValue = operand.asConstInteger() val constValue = operand.asConstInteger()
val eltSize = codeGen.program.memsizer.memorySize(array.type, null) val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constIndex!=null && constValue!=null) { if(constIndex!=null && constValue!=null) {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
if(array.splitWords) { if(array.splitWords) {
val valueRegLsb = codeGen.registers.next(IRDataType.BYTE) val valueRegLsb = codeGen.registers.next(IRDataType.BYTE)
val valueRegMsb = codeGen.registers.next(IRDataType.BYTE) val valueRegMsb = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8)
it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex) it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex) it += IRInstruction(Opcode.ORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
} }
} else { } else {
val valueReg = codeGen.registers.next(vmDt) val valueReg = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue) it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
} }
} }
return result return result
@@ -695,10 +916,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(array.splitWords) { if(array.splitWords) {
throw AssemblyError("logical or on (split) word array should not happen") throw AssemblyError("logical or on (split) word array should not happen")
} else { } else {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
val valueReg = codeGen.registers.next(vmDt) val valueReg = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue) it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) it += IRInstruction(Opcode.ORM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
} }
} }
return result return result
@@ -804,11 +1026,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val constValue = operand.asConstInteger() val constValue = operand.asConstInteger()
if(constIndex!=null && constValue!=null) { if(constIndex!=null && constValue!=null) {
if(constValue!=1) { if(constValue!=1) {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
val valueReg=codeGen.registers.next(eltDt) val valueReg=codeGen.registers.next(eltDt)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue) it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
val opcode = if(signed) Opcode.MULSM else Opcode.MULM val opcode = if(signed) Opcode.MULSM else Opcode.MULM
it += IRInstruction(opcode, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) it += IRInstruction(opcode, eltDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
} }
} }
return result return result
@@ -861,13 +1084,15 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val constIndex = array.index.asConstInteger() val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger() val constValue = operand.asConstInteger()
if(constIndex!=null && constValue!=null) { if(constIndex!=null && constValue!=null) {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
if(constValue==1) { if(constValue==1) {
addInstr(result, IRInstruction(Opcode.DECM, eltDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null) addInstr(result, IRInstruction(Opcode.DECM, eltDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize), null)
} else { } else {
val valueReg=codeGen.registers.next(eltDt) val valueReg=codeGen.registers.next(eltDt)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue) it += IRInstruction(Opcode.LOAD, eltDt, reg1=valueReg, immediate = constValue)
it += IRInstruction(Opcode.SUBM, eltDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) it += IRInstruction(Opcode.SUBM, eltDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
} }
} }
return result return result
@@ -927,14 +1152,16 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(constIndex!=null) { if(constIndex!=null) {
val skip = codeGen.createLabelName() val skip = codeGen.createLabelName()
if(constValue==1) { if(constValue==1) {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
val lsbReg = codeGen.registers.next(IRDataType.BYTE) val lsbReg = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lsbReg, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lsbReg, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.BSTNE, labelSymbol = skip) it += IRInstruction(Opcode.BSTNE, labelSymbol = skip)
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex) it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
} }
result += IRCodeChunk(skip, null).also { result += IRCodeChunk(skip, null).also {
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex) it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
} }
return result return result
} else { } else {
@@ -944,7 +1171,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return null // fallback to slow method // TODO("inplace split word array -") return null // fallback to slow method // TODO("inplace split word array -")
} }
private fun operatorPlusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? { private fun operatorPlusInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?,
vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) { if(array!=null) {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
if(array.splitWords) if(array.splitWords)
@@ -954,13 +1182,15 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val constIndex = array.index.asConstInteger() val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger() val constValue = operand.asConstInteger()
if(constIndex!=null && constValue!=null) { if(constIndex!=null && constValue!=null) {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
if(constValue==1) { if(constValue==1) {
addInstr(result, IRInstruction(Opcode.INCM, elementDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null) addInstr(result, IRInstruction(Opcode.INCM, elementDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize), null)
} else { } else {
val valueReg=codeGen.registers.next(elementDt) val valueReg=codeGen.registers.next(elementDt)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, elementDt, reg1=valueReg, immediate = constValue) it += IRInstruction(Opcode.LOAD, elementDt, reg1=valueReg, immediate = constValue)
it += IRInstruction(Opcode.ADDM, elementDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) it += IRInstruction(Opcode.ADDM, elementDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
} }
} }
return result return result
@@ -971,39 +1201,36 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
return null // TODO("optimized memory in-place +"") return null // TODO("optimized memory in-place +"")
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
if((operand as? PtNumber)?.number==1.0) { if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(constAddress!=null) addInstr(result, if (constAddress != null)
IRInstruction(Opcode.INCM, vmDt, address = constAddress) IRInstruction(Opcode.INCM, vmDt, address = constAddress)
else else
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) , null)
, null)
} }
else { else {
val tr = expressionEval.translateExpression(operand) val tr = expressionEval.translateExpression(operand)
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, if(constAddress!=null) addInstr(result, if (constAddress != null)
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, address = constAddress) IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, address = constAddress)
else else
IRInstruction(Opcode.ADDM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol) IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol) , null)
, null)
} }
} else { } else {
if((operand as? PtNumber)?.number==1.0) { if((operand as? PtNumber)?.number==1.0) {
addInstr(result, if(constAddress!=null) addInstr(result, if (constAddress != null)
IRInstruction(Opcode.INCM, vmDt, address = constAddress) IRInstruction(Opcode.INCM, vmDt, address = constAddress)
else else
IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) IRInstruction(Opcode.INCM, vmDt, labelSymbol = symbol) , null)
, null)
} }
else { else {
val tr = expressionEval.translateExpression(operand) val tr = expressionEval.translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, if(constAddress!=null) if (constAddress != null)
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, address = constAddress) addInstr(result, IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, address = constAddress), null)
else else
IRInstruction(Opcode.ADDM, vmDt, reg1=tr.resultReg, labelSymbol = symbol) addInstr(result, IRInstruction(Opcode.ADDM, vmDt, reg1 = tr.resultReg, labelSymbol = symbol) , null)
, null)
} }
} }
return result return result
@@ -1016,10 +1243,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
if(constIndex!=null) { if(constIndex!=null) {
val skip = codeGen.createLabelName() val skip = codeGen.createLabelName()
if(constValue==1) { if(constValue==1) {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex) it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.BSTNE, labelSymbol = skip) it += IRInstruction(Opcode.BSTNE, labelSymbol = skip)
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex) it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
} }
result += IRCodeChunk(skip, null) result += IRCodeChunk(skip, null)
return result return result
@@ -1037,24 +1266,26 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val constValue = operand.asConstInteger() val constValue = operand.asConstInteger()
if(constIndex!=null && constValue!=null) { if(constIndex!=null && constValue!=null) {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
if(array.splitWords) { if(array.splitWords) {
repeat(constValue) { repeat(constValue) {
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LSRM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex) it += IRInstruction(Opcode.LSRM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ROXRM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex) it += IRInstruction(Opcode.ROXRM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
} }
} }
} else { } else {
val eltSize = codeGen.program.memsizer.memorySize(array.type, null) val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constValue==1) { if(constValue==1) {
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LSRM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) it += IRInstruction(Opcode.LSRM, vmDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
} }
} else { } else {
val valueReg = codeGen.registers.next(IRDataType.BYTE) val valueReg = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueReg, immediate=constValue and 255) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueReg, immediate=constValue and 255)
it += IRInstruction(Opcode.LSRNM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) it += IRInstruction(Opcode.LSRNM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
} }
} }
} }
@@ -1074,13 +1305,18 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
IRInstruction(opc, vmDt, labelSymbol = symbol) IRInstruction(opc, vmDt, labelSymbol = symbol)
addInstr(result, ins, null) addInstr(result, ins, null)
} else { } else {
val tr = expressionEval.translateExpression(operand) val shiftTr = expressionEval.translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, shiftTr, shiftTr.resultReg, -1)
var shiftReg = shiftTr.resultReg
if(vmDt==IRDataType.WORD && shiftTr.dt==IRDataType.BYTE) {
shiftReg = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=shiftReg, reg2=shiftTr.resultReg), null)
}
val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM val opc = if (signed) Opcode.ASRNM else Opcode.LSRNM
val ins = if(constAddress!=null) val ins = if(constAddress!=null)
IRInstruction(opc, vmDt, reg1 = tr.resultReg, address = constAddress) IRInstruction(opc, vmDt, reg1 = shiftReg, address = constAddress)
else else
IRInstruction(opc, vmDt, reg1 = tr.resultReg, labelSymbol = symbol) IRInstruction(opc, vmDt, reg1 = shiftReg, labelSymbol = symbol)
addInstr(result, ins, null) addInstr(result, ins, null)
} }
return result return result
@@ -1093,24 +1329,26 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val constValue = operand.asConstInteger() val constValue = operand.asConstInteger()
if(constIndex!=null && constValue!=null) { if(constIndex!=null && constValue!=null) {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
if(array.splitWords) { if(array.splitWords) {
repeat(constValue) { repeat(constValue) {
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LSLM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex) it += IRInstruction(Opcode.LSLM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.ROXLM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex) it += IRInstruction(Opcode.ROXLM, IRDataType.BYTE, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
} }
} }
} else { } else {
val eltSize = codeGen.program.memsizer.memorySize(array.type, null) val eltSize = codeGen.program.memsizer.memorySize(array.type, null)
if(constValue==1) { if(constValue==1) {
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LSLM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) it += IRInstruction(Opcode.LSLM, vmDt, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
} }
} else { } else {
val valueReg = codeGen.registers.next(IRDataType.BYTE) val valueReg = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueReg, immediate=constValue and 255) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueReg, immediate=constValue and 255)
it += IRInstruction(Opcode.LSLNM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) it += IRInstruction(Opcode.LSLNM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
} }
} }
} }
@@ -1129,12 +1367,17 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol) IRInstruction(Opcode.LSLM, vmDt, labelSymbol = symbol)
, null) , null)
} else { } else {
val tr = expressionEval.translateExpression(operand) val shiftTr = expressionEval.translateExpression(operand)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, shiftTr, shiftTr.resultReg, -1)
var shiftReg = shiftTr.resultReg
if(vmDt==IRDataType.WORD && shiftTr.dt==IRDataType.BYTE) {
shiftReg = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=shiftReg, reg2=shiftTr.resultReg), null)
}
addInstr(result, if(constAddress!=null) addInstr(result, if(constAddress!=null)
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, address = constAddress) IRInstruction(Opcode.LSLNM, vmDt, reg1=shiftReg, address = constAddress)
else else
IRInstruction(Opcode.LSLNM, vmDt, reg1=tr.resultReg, labelSymbol = symbol) IRInstruction(Opcode.LSLNM, vmDt, reg1=shiftReg, labelSymbol = symbol)
,null) ,null)
} }
return result return result
@@ -1142,6 +1385,8 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
private fun operatorXorInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? { private fun operatorXorInplace(symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType, operand: PtExpression): IRCodeChunks? {
if(array!=null) { if(array!=null) {
val arrayVariableName = array.variable?.name ?: TODO("support for ptr indexing ${array.position}")
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val constIndex = array.index.asConstInteger() val constIndex = array.index.asConstInteger()
val constValue = operand.asConstInteger() val constValue = operand.asConstInteger()
@@ -1153,14 +1398,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegLsb, immediate=constValue and 255)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=valueRegMsb, immediate=constValue shr 8)
it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex) it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegLsb, labelSymbol = arrayVariableName+"_lsb", symbolOffset = constIndex)
it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex) it += IRInstruction(Opcode.XORM, IRDataType.BYTE, reg1=valueRegMsb, labelSymbol = arrayVariableName+"_msb", symbolOffset = constIndex)
} }
} else { } else {
val valueReg = codeGen.registers.next(vmDt) val valueReg = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue) it += IRInstruction(Opcode.LOAD, vmDt, reg1=valueReg, immediate=constValue)
it += IRInstruction(Opcode.XORM, vmDt, reg1=valueReg, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize) it += IRInstruction(Opcode.XORM, vmDt, reg1=valueReg, labelSymbol = arrayVariableName, symbolOffset = constIndex*eltSize)
} }
} }
return result return result

View File

@@ -1,5 +1,8 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.StMemorySlabBlockName
import prog8.code.StStructInstanceBlockName
import prog8.code.SymbolTable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.AssemblyError import prog8.code.core.AssemblyError
import prog8.code.core.BaseDataType import prog8.code.core.BaseDataType
@@ -27,9 +30,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"lsb" -> funcLsb(call) "lsb" -> funcLsb(call)
"memory" -> funcMemory(call) "memory" -> funcMemory(call)
"peek" -> funcPeek(call, IRDataType.BYTE) "peek" -> funcPeek(call, IRDataType.BYTE)
"peekbool" -> funcPeek(call, IRDataType.BYTE)
"peekw" -> funcPeek(call, IRDataType.WORD) "peekw" -> funcPeek(call, IRDataType.WORD)
"peekf" -> funcPeek(call, IRDataType.FLOAT) "peekf" -> funcPeek(call, IRDataType.FLOAT)
"poke" -> funcPoke(call, IRDataType.BYTE) "poke" -> funcPoke(call, IRDataType.BYTE)
"pokebool" -> funcPoke(call, IRDataType.BYTE)
"pokebowl" -> funcPoke(call, IRDataType.BYTE)
"pokew" -> funcPoke(call, IRDataType.WORD) "pokew" -> funcPoke(call, IRDataType.WORD)
"pokef" -> funcPoke(call, IRDataType.FLOAT) "pokef" -> funcPoke(call, IRDataType.FLOAT)
"pokemon" -> funcPokemon(call) "pokemon" -> funcPokemon(call)
@@ -46,6 +52,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"prog8_lib_stringcompare" -> funcStringCompare(call) "prog8_lib_stringcompare" -> funcStringCompare(call)
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE) "prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD) "prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
"prog8_lib_structalloc" -> funcStructAlloc(call)
"sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
"offsetof" -> throw AssemblyError("offsetof must have been replaced with a constant")
else -> throw AssemblyError("missing builtinfunc for ${call.name}") else -> throw AssemblyError("missing builtinfunc for ${call.name}")
} }
} }
@@ -494,8 +503,16 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val name = (call.args[0] as PtString).value val name = (call.args[0] as PtString).value
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
val resultReg = codeGen.registers.next(IRDataType.WORD) val resultReg = codeGen.registers.next(IRDataType.WORD)
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "prog8_slabs.prog8_memoryslab_$name") code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "$StMemorySlabBlockName.memory_$name")
return ExpressionCodeResult(code, IRDataType.BYTE, resultReg, -1) return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
}
private fun funcStructAlloc(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val code = IRCodeChunk(null, null)
val resultReg = codeGen.registers.next(IRDataType.WORD)
val labelname = SymbolTable.labelnameForStructInstance(call)
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "${StStructInstanceBlockName}.$labelname")
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
} }
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@@ -545,7 +562,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val arr = (arg as? PtArrayIndexer) val arr = (arg as? PtArrayIndexer)
val index = arr?.index?.asConstInteger() val index = arr?.index?.asConstInteger()
if(arr!=null && index!=null) { if(arr!=null && index!=null) {
val variable = arr.variable.name if(arr.variable==null)
TODO("support for ptr indexing ${arr.position}")
val variable = arr.variable!!.name
if(arr.splitWords) { if(arr.splitWords) {
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
when(opcodeMemAndReg.first) { when(opcodeMemAndReg.first) {
@@ -614,7 +633,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
if(target.splitWords) { if(target.splitWords) {
// lsb/msb in split arrays, element index 'size' is always 1 // lsb/msb in split arrays, element index 'size' is always 1
val constIndex = target.index.asConstInteger() val constIndex = target.index.asConstInteger()
val varName = target.variable.name + if(msb) "_msb" else "_lsb" if(target.variable==null)
TODO("support for ptr indexing ${target.position}")
val varName = target.variable!!.name + if(msb) "_msb" else "_lsb"
if(isConstZeroValue) { if(isConstZeroValue) {
if(constIndex!=null) { if(constIndex!=null) {
val offsetReg = codeGen.registers.next(IRDataType.BYTE) val offsetReg = codeGen.registers.next(IRDataType.BYTE)
@@ -648,6 +669,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} }
} }
else { else {
val targetVariable = target.variable ?: TODO("support for ptr indexing ${target.position}")
val eltSize = codeGen.program.memsizer.memorySize(target.type, null) val eltSize = codeGen.program.memsizer.memorySize(target.type, null)
val constIndex = target.index.asConstInteger() val constIndex = target.index.asConstInteger()
if(isConstZeroValue) { if(isConstZeroValue) {
@@ -656,7 +679,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val offset = eltSize*constIndex + if(msb) 1 else 0 val offset = eltSize*constIndex + if(msb) 1 else 0
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = target.variable.name) it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = targetVariable.name)
} }
} else { } else {
val indexTr = exprGen.translateExpression(target.index) val indexTr = exprGen.translateExpression(target.index)
@@ -666,7 +689,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize) it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
if(msb) if(msb)
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg) it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = target.variable.name) it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = targetVariable.name)
} }
} }
} else { } else {
@@ -677,7 +700,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val offset = eltSize*constIndex + if(msb) 1 else 0 val offset = eltSize*constIndex + if(msb) 1 else 0
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = target.variable.name) it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = targetVariable.name)
} }
} else { } else {
val indexTr = exprGen.translateExpression(target.index) val indexTr = exprGen.translateExpression(target.index)
@@ -687,7 +710,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize) it += codeGen.multiplyByConst(DataType.UBYTE, indexTr.resultReg, eltSize)
if(msb) if(msb)
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg) it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name) it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = targetVariable.name)
} }
} }
} }

View File

@@ -83,15 +83,111 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
is PtArrayIndexer -> translate(expr) is PtArrayIndexer -> translate(expr)
is PtBinaryExpression -> translate(expr) is PtBinaryExpression -> translate(expr)
is PtIfExpression -> translate(expr) is PtIfExpression -> translate(expr)
is PtBranchCondExpression -> translate(expr)
is PtBuiltinFunctionCall -> codeGen.translateBuiltinFunc(expr) is PtBuiltinFunctionCall -> codeGen.translateBuiltinFunc(expr)
is PtFunctionCall -> translate(expr) is PtFunctionCall -> translate(expr)
is PtContainmentCheck -> translate(expr) is PtContainmentCheck -> translate(expr)
is PtPointerDeref -> translate(expr)
is PtRange, is PtRange,
is PtArray, is PtArray,
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression") is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
} }
} }
private fun translate(deref: PtPointerDeref): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
var pointerReg: Int
if(deref.startpointer.type.isStructInstance) {
TODO("translate structinstance deref???")
/*
val arrayIndexer = deref.startpointer as? PtArrayIndexer
if(arrayIndexer==null)
throw AssemblyError("when start pointer is struct instance, array indexer is expected PTR[x]")
// first evaluate the adress of POINTER[x].a
// which is: value in pointer + x*sizeof(struct) + offsetof(a)
// then use traverseDerefChainToCalculateFinalAddress on b.c.d.field
val struct = deref.startpointer.type.subType as StStruct
val chain = ArrayDeque(deref.chain)
val firstField = struct.getField(chain.removeFirst(), codeGen.program.memsizer)
val pointerTr = translateExpression(arrayIndexer.variable)
result += pointerTr.chunks
pointerReg = pointerTr.resultReg
val constIndex = arrayIndexer.index.asConstInteger()
if(constIndex!=null) {
val offset = constIndex * struct.size.toInt()
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = offset), null)
} else {
val indexTr = translateExpression(arrayIndexer.index)
result += indexTr.chunks
// multiply the index by the size of the struct and add that to the pointer, then add the offset of the field,
// and retrieve the pointer value that is stored there, or the actual value if it's a pointer to simple type
result += IRCodeChunk(null, null).also {
val indexReg: Int
if(arrayIndexer.index.type.isByte) {
// extend array index to word
indexReg = codeGen.registers.next(IRDataType.WORD)
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, indexReg, indexTr.resultReg)
} else {
indexReg = indexTr.resultReg
}
it += codeGen.multiplyByConst(DataType.UWORD, indexReg, struct.size.toInt())
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg)
}
}
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = firstField.second.toInt())
if (firstField.first.isPointer) {
// get the address stored in the pointer and use that for the rest of the chain
// LOADI has an exception to allo reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg)
} else {
require(chain.isEmpty())
// it's a pointer to a simple value, so keep the pointer as-is
}
}
// now use traverseDerefChainToCalculateFinalAddress on b.c.d and finally field or on b.c.d.field if field isn't a field.
val derefField = if(deref.type.isPointer) null else chain.removeLastOrNull()
actualDeref = PtPointerDeref(deref.type, chain, derefField, deref.position)
actualDeref.add(arrayIndexer.variable)
*/
} else {
val tr = translateExpression(deref.startpointer)
result += tr.chunks
pointerReg = tr.resultReg
}
val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerReg)
result += instructions
if(offset<=0u) {
val irdt = irType(deref.type)
return if(deref.type.isFloat) {
val resultReg = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg), null)
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
} else {
val resultReg = codeGen.registers.next(irdt)
addInstr(result, IRInstruction(Opcode.LOADI, irdt, reg1 = resultReg, reg2 = pointerReg), null)
ExpressionCodeResult(result, irdt, resultReg, -1)
}
}
// load field with offset
return if(deref.type.isFloat) {
val resultReg = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOADFIELD, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg, immediate = offset.toInt()), null)
ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
} else {
val irdt = irType(deref.type)
val resultReg = codeGen.registers.next(irdt)
addInstr(result, IRInstruction(Opcode.LOADFIELD, irdt, reg1 = resultReg, reg2 = pointerReg, immediate = offset.toInt()), null)
ExpressionCodeResult(result, irdt, resultReg, -1)
}
}
private fun translate(ifExpr: PtIfExpression): ExpressionCodeResult { private fun translate(ifExpr: PtIfExpression): ExpressionCodeResult {
if((ifExpr.condition as? PtPrefix)?.operator=="not") if((ifExpr.condition as? PtPrefix)?.operator=="not")
@@ -158,21 +254,83 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
} }
private fun translate(branchExpr: PtBranchCondExpression): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
val trueTr = translateExpression(branchExpr.truevalue)
val falseTr = translateExpression(branchExpr.falsevalue)
val trueLabel = codeGen.createLabelName()
val endLabel = codeGen.createLabelName()
val irDt = irType(branchExpr.type)
if(branchExpr.condition==BranchCondition.CC && irDt==IRDataType.BYTE) {
if(branchExpr.truevalue.asConstInteger()==0 && branchExpr.falsevalue.asConstInteger()==1) {
result.add(IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
})
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
}
else if(branchExpr.truevalue.asConstInteger()==1 && branchExpr.falsevalue.asConstInteger()==0) {
result.add(IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
it += IRInstruction(Opcode.XOR, irDt, reg1=trueTr.resultReg, immediate = 1)
})
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
}
}
else if(branchExpr.condition==BranchCondition.CS) {
if(branchExpr.truevalue.asConstInteger()==0 && branchExpr.falsevalue.asConstInteger()==1) {
result.add(IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
it += IRInstruction(Opcode.XOR, irDt, reg1=trueTr.resultReg, immediate = 1)
})
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
}
else if(branchExpr.truevalue.asConstInteger()==1 && branchExpr.falsevalue.asConstInteger()==0) {
result.add(IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, irDt, reg1=trueTr.resultReg, immediate = 0)
it += IRInstruction(Opcode.ROXL, irDt, reg1=trueTr.resultReg)
})
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
}
}
val branchInstr = codeGen.IRBranchInstr(branchExpr.condition, trueLabel)
addInstr(result, branchInstr, null)
if (irDt != IRDataType.FLOAT) {
addToResult(result, falseTr, trueTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
result += IRCodeChunk(trueLabel, null)
addToResult(result, trueTr, trueTr.resultReg, -1)
result += IRCodeChunk(endLabel, null)
return ExpressionCodeResult(result, irDt, trueTr.resultReg, -1)
} else {
addToResult(result, falseTr, -1, trueTr.resultFpReg)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
result += IRCodeChunk(trueLabel, null)
addToResult(result, trueTr, -1, trueTr.resultFpReg)
result += IRCodeChunk(endLabel, null)
return ExpressionCodeResult(result, irDt, -1, trueTr.resultFpReg)
}
}
private fun translate(expr: PtAddressOf): ExpressionCodeResult { private fun translate(expr: PtAddressOf): ExpressionCodeResult {
val vmDt = irType(expr.type) val vmDt = irType(expr.type)
val symbol = expr.identifier.name
// note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location // note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val resultRegister = codeGen.registers.next(vmDt) val identifier = expr.identifier
fun loadAddressOfArrayLabel(reg: Int) { fun loadAddressOfArrayLabel(reg: Int) {
if (expr.isMsbForSplitArray) { if (expr.isMsbForSplitArray) {
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_msb"), null) addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier!!.name + "_msb"), null)
} else if (expr.identifier.type.isSplitWordArray) { } else if (identifier!!.type.isSplitWordArray) {
// the _lsb split array comes first in memory // the _lsb split array comes first in memory
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_lsb"), null) addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name + "_lsb"), null)
} else } else
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol), null) addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name), null)
} }
if(expr.isFromArrayElement) { if(expr.isFromArrayElement) {
@@ -183,30 +341,55 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=ixWord, reg2=indexTr.resultReg), null) addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=ixWord, reg2=indexTr.resultReg), null)
ixWord ixWord
} else indexTr.resultReg } else indexTr.resultReg
if(expr.identifier.type.isUnsignedWord) { val resultRegister = codeGen.registers.next(vmDt)
if(identifier!!.type.isUnsignedWord) {
require(!expr.isMsbForSplitArray) require(!expr.isMsbForSplitArray)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
val ptr = codeGen.symbolTable.lookup(expr.identifier.name) val ptr = codeGen.symbolTable.lookup(identifier.name)
it += if(ptr is StConstant) it += if(ptr is StConstant)
IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, immediate = ptr.value.toInt()) IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, immediate = ptr.value.toInt())
else else
IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol) IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = identifier.name)
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg) it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
} }
} else if(identifier.type.isPointer) {
// apply pointer arithmetic for the array indexing
val eltSize = if(identifier.type.sub!=null)
codeGen.program.memsizer.memorySize(identifier.type.sub!!)
else
identifier.type.subType!!.memsize(codeGen.program.memsizer)
result += IRCodeChunk(null, null).also {
addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = identifier.name), null)
if (eltSize > 1) {
it += codeGen.multiplyByConst(DataType.UWORD, indexWordReg, eltSize)
}
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = resultRegister, reg2 = indexWordReg)
}
} else { } else {
val eltSize = codeGen.program.memsizer.memorySize(expr.identifier.type, 1) // regular array indexing
val eltSize = codeGen.program.memsizer.memorySize(identifier.type, 1)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
loadAddressOfArrayLabel(resultRegister) loadAddressOfArrayLabel(resultRegister)
if(eltSize>1 && !expr.identifier.type.isSplitWordArray) { if (eltSize > 1 && !identifier.type.isSplitWordArray) {
it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1=indexWordReg, immediate = eltSize) it += codeGen.multiplyByConst(DataType.UWORD, indexWordReg, eltSize)
} }
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg) it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = resultRegister, reg2 = indexWordReg)
} }
} }
} else { return ExpressionCodeResult(result, vmDt, resultRegister, -1)
} else if(expr.identifier!=null ) {
val resultRegister = codeGen.registers.next(vmDt)
loadAddressOfArrayLabel(resultRegister) loadAddressOfArrayLabel(resultRegister)
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
} else {
require(vmDt==IRDataType.WORD)
val pointerTr = translateExpression(expr.dereference!!.startpointer)
result += pointerTr.chunks
val (instructions, offset) = traverseRestOfDerefChainToCalculateFinalAddress(expr.dereference!!, pointerTr.resultReg)
result += instructions
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerTr.resultReg, immediate = offset.toInt()), null)
return ExpressionCodeResult(result, vmDt, pointerTr.resultReg, -1)
} }
return ExpressionCodeResult(result, vmDt, resultRegister, -1)
} }
private fun translate(mem: PtMemoryByte): ExpressionCodeResult { private fun translate(mem: PtMemoryByte): ExpressionCodeResult {
@@ -220,31 +403,32 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
val ptrWithOffset = mem.address as? PtBinaryExpression val ptrWithOffset = mem.address as? PtBinaryExpression
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) { if(ptrWithOffset!=null) {
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) { if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
// LOADIX only works with byte index. val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
val ptrName = (ptrWithOffset.left as PtIdentifier).name if(constOffset in 0..255) {
val offsetReg = codeGen.registers.next(IRDataType.BYTE) val ptrName = (ptrWithOffset.left as PtIdentifier).name
result += IRCodeChunk(null, null).also { val pointerReg = codeGen.registers.next(IRDataType.WORD)
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger()) result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=offsetReg, labelSymbol = ptrName) it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1 = pointerReg, labelSymbol = ptrName)
it += IRInstruction(Opcode.LOADFIELD, IRDataType.BYTE, reg1=resultRegister, reg2=pointerReg, immediate = constOffset)
}
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} }
}
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// LOADIX only works with byte index.
val tr = if(offsetTypecast?.value?.type?.isByte==true)
translateExpression(offsetTypecast.value)
else
translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
} }
} }
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
&& (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
// LOADIX only works with byte index.
val tr = if(offsetTypecast?.value?.type?.isByte==true)
translateExpression(offsetTypecast.value)
else
translateExpression(ptrWithOffset.right)
addToResult(result, tr, tr.resultReg, -1)
val ptrName = (ptrWithOffset.left as PtIdentifier).name
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
}
val tr = translateExpression(mem.address) val tr = translateExpression(mem.address)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
@@ -343,11 +527,30 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
private fun translate(arrayIx: PtArrayIndexer): ExpressionCodeResult { private fun translate(arrayIx: PtArrayIndexer): ExpressionCodeResult {
if(arrayIx.type.isStructInstance)
throw AssemblyError("cannot translate POINTER[x] resulting in a struct instance; this is likely part of a larger expression POINTER[x].field and that has to be translated earlier as a whole")
val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type, null) val eltSize = codeGen.program.memsizer.memorySize(arrayIx.type, null)
val vmDt = irType(arrayIx.type) val vmDt = irType(arrayIx.type)
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val arrayVarSymbol = arrayIx.variable.name val arrayVar = arrayIx.variable
if(arrayVar==null) {
val pointerTr = translateExpression(arrayIx.pointerderef!!)
result += pointerTr.chunks
val pointerReg = pointerTr.resultReg
return translatePointerIndexing(result, pointerReg, arrayIx.index, eltSize, vmDt)
}
if(arrayVar.type.isPointer) {
val pointerTr = translateExpression(arrayVar)
result += pointerTr.chunks
val pointerReg = pointerTr.resultReg
return translatePointerIndexing(result, pointerReg, arrayIx.index, eltSize, vmDt)
}
require(!arrayVar.type.isPointer) { "only regular array indexing here ${arrayIx.position}" }
var resultRegister = -1 var resultRegister = -1
var resultFpRegister = -1
val arrayVarSymbol = arrayVar.name
if(arrayIx.splitWords) { if(arrayIx.splitWords) {
require(vmDt==IRDataType.WORD) require(vmDt==IRDataType.WORD)
@@ -374,9 +577,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, vmDt, finalResultReg, -1) return ExpressionCodeResult(result, vmDt, finalResultReg, -1)
} }
var resultFpRegister = -1 fun indexByNumber(index: Int) {
if(arrayIx.index is PtNumber) { val memOffset = index * eltSize
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize)
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT) resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null) addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
@@ -385,23 +587,61 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
resultRegister = codeGen.registers.next(vmDt) resultRegister = codeGen.registers.next(vmDt)
addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null) addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
} }
} else { }
val tr = translateExpression(arrayIx.index)
addToResult(result, tr, tr.resultReg, -1) fun indexByExpression() {
if(eltSize>1) val (code, indexByteReg) = codeGen.loadIndexReg(arrayIx.index, eltSize, false, arrayIx.splitWords)
result += codeGen.multiplyByConst(DataType.UBYTE, tr.resultReg, eltSize) result += code
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT) resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=tr.resultReg, labelSymbol = arrayVarSymbol), null) addInstr(result, IRInstruction(Opcode.LOADX, IRDataType.FLOAT, fpReg1 = resultFpRegister, reg1=indexByteReg, labelSymbol = arrayVarSymbol), null)
} }
else { else {
resultRegister = codeGen.registers.next(vmDt) resultRegister = codeGen.registers.next(vmDt)
addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = arrayVarSymbol), null) addInstr(result, IRInstruction(Opcode.LOADX, vmDt, reg1=resultRegister, reg2=indexByteReg, labelSymbol = arrayVarSymbol), null)
} }
} }
if(arrayIx.index is PtNumber)
indexByNumber((arrayIx.index as PtNumber).number.toInt())
else
indexByExpression()
return ExpressionCodeResult(result, vmDt, resultRegister, resultFpRegister) return ExpressionCodeResult(result, vmDt, resultRegister, resultFpRegister)
} }
private fun translatePointerIndexing(
result: MutableList<IRCodeChunkBase>,
pointerReg: Int,
index: PtExpression,
eltSize: Int,
resultDt: IRDataType
): ExpressionCodeResult {
var resultRegister = -1
var resultFpRegister = -1
if(index is PtNumber) {
val memOffset = eltSize * index.number.toInt()
if(memOffset>0)
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1=pointerReg, immediate = memOffset), null)
}
else {
val (code, indexWordReg) = codeGen.loadIndexReg(index, eltSize, true, false)
result += code
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=pointerReg, reg2=indexWordReg), null)
}
if(resultDt==IRDataType.FLOAT) {
resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1=resultFpRegister, reg1=pointerReg), null)
}
else {
resultRegister = codeGen.registers.next(resultDt)
addInstr(result, IRInstruction(Opcode.LOADI, resultDt, reg1=resultRegister, reg2=pointerReg), null)
}
return ExpressionCodeResult(result, resultDt, resultRegister, resultFpRegister)
}
private fun translate(expr: PtPrefix): ExpressionCodeResult { private fun translate(expr: PtPrefix): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = translateExpression(expr.value) val tr = translateExpression(expr.value)
@@ -442,9 +682,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=tr.resultReg, immediate = 0), null) addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=tr.resultReg, immediate = 0), null)
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result) actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
} }
valueDt.isWord -> { valueDt.isWord || valueDt.isPointer -> {
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.WORD, reg1=tr.resultReg, immediate = 0), null) addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.WORD, reg1=tr.resultReg, immediate = 0), null)
actualResultReg2 =loadStatusAsBooleanResult(Opcode.BSTNE, result) actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
} }
valueDt.isFloat -> { valueDt.isFloat -> {
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE) actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
@@ -453,7 +693,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=actualResultReg2, immediate = 1) it += IRInstruction(Opcode.AND, IRDataType.BYTE, reg1=actualResultReg2, immediate = 1)
} }
} }
else -> throw AssemblyError("weird cast value type") else -> throw AssemblyError("weird cast value type ${cast.position}")
} }
} }
BaseDataType.UBYTE -> { BaseDataType.UBYTE -> {
@@ -469,7 +709,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE) actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.FTOUB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null) addInstr(result, IRInstruction(Opcode.FTOUB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
} }
else -> throw AssemblyError("weird cast value type") else -> throw AssemblyError("weird cast value type ${cast.position}")
} }
} }
BaseDataType.BYTE -> { BaseDataType.BYTE -> {
@@ -485,7 +725,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE) actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.FTOSB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null) addInstr(result, IRInstruction(Opcode.FTOSB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
} }
else -> throw AssemblyError("weird cast value type") else -> throw AssemblyError("weird cast value type ${cast.position}")
} }
} }
BaseDataType.UWORD -> { BaseDataType.UWORD -> {
@@ -507,7 +747,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
actualResultReg2 = codeGen.registers.next(IRDataType.WORD) actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null) addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
} }
else -> throw AssemblyError("weird cast value type") BaseDataType.POINTER -> {
actualResultReg2 = tr.resultReg
}
else -> throw AssemblyError("weird cast value type ${cast.position}")
} }
} }
BaseDataType.WORD -> { BaseDataType.WORD -> {
@@ -529,7 +772,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
actualResultReg2 = codeGen.registers.next(IRDataType.WORD) actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.FTOSW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null) addInstr(result, IRInstruction(Opcode.FTOSW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
} }
else -> throw AssemblyError("weird cast value type") else -> throw AssemblyError("weird cast value type ${cast.position}")
} }
} }
BaseDataType.FLOAT -> { BaseDataType.FLOAT -> {
@@ -547,49 +790,61 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
BaseDataType.WORD -> { BaseDataType.WORD -> {
addInstr(result, IRInstruction(Opcode.FFROMSW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null) addInstr(result, IRInstruction(Opcode.FFROMSW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
} }
else -> throw AssemblyError("weird cast value type") else -> throw AssemblyError("weird cast value type ${cast.position}")
} }
} }
else -> throw AssemblyError("weird cast type") BaseDataType.POINTER -> {
require(valueDt.isUnsignedWord || valueDt.isPointer)
actualResultReg2 = tr.resultReg
// no further conversion required, pointers are all just uwords
}
BaseDataType.ARRAY_POINTER -> {
TODO("typecast to array of pointers $valueDt -> ${cast.type}")
}
else -> throw AssemblyError("weird cast value type ${cast.position}")
} }
return ExpressionCodeResult(result, irType(cast.type), actualResultReg2, actualResultFpReg2) return ExpressionCodeResult(result, irType(cast.type), actualResultReg2, actualResultFpReg2)
} }
private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult { private fun translate(binExpr: PtBinaryExpression): ExpressionCodeResult {
val vmDt = irType(binExpr.left.type)
val signed = binExpr.left.type.isSigned val signed = binExpr.left.type.isSigned
return when(binExpr.operator) { if(binExpr.operator==".") {
"+" -> operatorPlus(binExpr, vmDt) return operatorDereference(binExpr) // eww, nasty, would rather not have any such expressions anymore
"-" -> operatorMinus(binExpr, vmDt) } else {
"*" -> operatorMultiply(binExpr, binExpr.left.type) val vmDt = irType(binExpr.left.type)
"/" -> operatorDivide(binExpr, binExpr.left.type) return when (binExpr.operator) {
"%" -> operatorModulo(binExpr, vmDt) "+" -> operatorPlus(binExpr, vmDt)
"|" -> operatorOr(binExpr, vmDt, true) "-" -> operatorMinus(binExpr, vmDt)
"&" -> operatorAnd(binExpr, vmDt, true) "*" -> operatorMultiply(binExpr, binExpr.left.type)
"^", "xor" -> operatorXor(binExpr, vmDt) "/" -> operatorDivide(binExpr, binExpr.left.type)
"or" -> operatorOr(binExpr, vmDt, false) "%" -> operatorModulo(binExpr, vmDt)
"and" -> operatorAnd(binExpr, vmDt, false) "|" -> operatorOr(binExpr, vmDt, true)
"<<" -> operatorShiftLeft(binExpr, vmDt) "&" -> operatorAnd(binExpr, vmDt, true)
">>" -> operatorShiftRight(binExpr, vmDt, signed) "^", "xor" -> operatorXor(binExpr, vmDt)
"==" -> operatorEquals(binExpr, vmDt, false) "or" -> operatorOr(binExpr, vmDt, false)
"!=" -> operatorEquals(binExpr, vmDt, true) "and" -> operatorAnd(binExpr, vmDt, false)
"<" -> operatorLessThan(binExpr, vmDt, signed, false) "<<" -> operatorShiftLeft(binExpr, vmDt)
">" -> operatorGreaterThan(binExpr, vmDt, signed, false) ">>" -> operatorShiftRight(binExpr, vmDt, signed)
"<=" -> operatorLessThan(binExpr, vmDt, signed, true) "==" -> operatorEquals(binExpr, vmDt, false)
">=" -> operatorGreaterThan(binExpr, vmDt, signed, true) "!=" -> operatorEquals(binExpr, vmDt, true)
else -> throw AssemblyError("weird operator ${binExpr.operator}") "<" -> operatorLessThan(binExpr, vmDt, signed, false)
">" -> operatorGreaterThan(binExpr, vmDt, signed, false)
"<=" -> operatorLessThan(binExpr, vmDt, signed, true)
">=" -> operatorGreaterThan(binExpr, vmDt, signed, true)
else -> throw AssemblyError("weird operator ${binExpr.operator}")
}
} }
} }
internal fun translate(fcall: PtFunctionCall): ExpressionCodeResult { internal fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
val callTarget = codeGen.symbolTable.lookup(fcall.name)!! val callTarget = codeGen.symbolTable.lookup(fcall.name)!!
if(callTarget.scopedName in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) { if(callTarget.scopedNameString in listOf("sys.push", "sys.pushw", "sys.pop", "sys.popw", "floats.push", "floats.pop")) {
// special case, these should be inlined, or even use specialized instructions. Instead of doing a normal subroutine call. // special case, these should be inlined, or even use specialized instructions. Instead of doing a normal subroutine call.
return translateStackFunctions(fcall, callTarget) return translateStackFunctions(fcall, callTarget)
} }
when(callTarget.scopedName) { when(callTarget.scopedNameString) {
"sys.clear_carry" -> { "sys.clear_carry" -> {
val chunk = mutableListOf<IRCodeChunkBase>() val chunk = mutableListOf<IRCodeChunkBase>()
addInstr(chunk, IRInstruction(Opcode.CLC), null) addInstr(chunk, IRInstruction(Opcode.CLC), null)
@@ -638,7 +893,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
} }
// return value(s) // return value(s)
// TODO: for current implemenation of the call convention in case of multiple return values, // TODO: for current implementation of the call convention in case of multiple return values,
// a list of Ir virtual registers to hold the results is NOT correct (they're loaded into AY + R15..R0 instead!) // a list of Ir virtual registers to hold the results is NOT correct (they're loaded into AY + R15..R0 instead!)
// So we use an empty list to avoid confusion here. This may change in a future version. // So we use an empty list to avoid confusion here. This may change in a future version.
val returnRegSpecs = if(fcall.void || callTarget.returns.size>1) emptyList() else { val returnRegSpecs = if(fcall.void || callTarget.returns.size>1) emptyList() else {
@@ -769,6 +1024,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else else
ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1) ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1)
} }
is StStruct -> {
throw AssemblyError("stray struct constructor should have been removed (normally it can only occur as initialization expression for a pointer variable)")
}
else -> { else -> {
if(callTarget.type == StNodeType.LABEL) { if(callTarget.type == StNodeType.LABEL) {
require(fcall.void) require(fcall.void)
@@ -778,7 +1036,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} }
else { else {
throw AssemblyError("invalid node type") throw AssemblyError("invalid node type ${callTarget.type} at ${callTarget.astNode?.position}")
} }
} }
} }
@@ -811,7 +1069,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
private fun translateStackFunctions(fcall: PtFunctionCall, callTarget: StNode): ExpressionCodeResult { private fun translateStackFunctions(fcall: PtFunctionCall, callTarget: StNode): ExpressionCodeResult {
val chunk = mutableListOf<IRCodeChunkBase>() val chunk = mutableListOf<IRCodeChunkBase>()
when(callTarget.scopedName) { when(callTarget.scopedNameString) {
"sys.push" -> { "sys.push" -> {
// push byte // push byte
val tr = translateExpression(fcall.args.single()) val tr = translateExpression(fcall.args.single())
@@ -1238,10 +1496,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val rightTr = translateExpression(binExpr.right) val rightTr = translateExpression(binExpr.right)
addToResult(result, rightTr, -1, rightTr.resultFpReg) addToResult(result, rightTr, -1, rightTr.resultFpReg)
addInstr(result, if(dt.isSigned) addInstr(result, if(dt.isSigned)
IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg) IRInstruction(Opcode.DIVSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
else else
IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg) IRInstruction(Opcode.DIVR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2=rightTr.resultFpReg)
, null) , null)
return ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg) return ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
} }
} else { } else {
@@ -1256,10 +1514,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, leftTr.resultReg, -1) addToResult(result, leftTr, leftTr.resultReg, -1)
addInstr(result, if (dt.isSigned) addInstr(result, if (dt.isSigned)
IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()) IRInstruction(Opcode.DIVS, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
else else
IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt()) IRInstruction(Opcode.DIV, vmDt, reg1 = leftTr.resultReg, immediate = (binExpr.right as PtNumber).number.toInt())
, null) , null)
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1) ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
@@ -1267,10 +1525,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val rightTr = translateExpression(binExpr.right) val rightTr = translateExpression(binExpr.right)
addToResult(result, rightTr, rightTr.resultReg, -1) addToResult(result, rightTr, rightTr.resultReg, -1)
addInstr(result, if (dt.isSigned) addInstr(result, if (dt.isSigned)
IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg) IRInstruction(Opcode.DIVSR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
else else
IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg) IRInstruction(Opcode.DIVR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg)
, null) , null)
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1) ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
} }
} }
@@ -1438,6 +1696,136 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
} }
private fun operatorDereference(binExpr: PtBinaryExpression): ExpressionCodeResult {
// the only case we support here is: a.b.c[i] . value
val left = binExpr.left as? PtArrayIndexer
val right = binExpr.right as? PtIdentifier
require(binExpr.operator=="." && left!=null && right!=null) {"invalid dereference expression ${binExpr.position}"}
val result = mutableListOf<IRCodeChunkBase>()
val field: Pair<DataType, UByte>
val pointerReg: Int
var extraFieldOffset = 0
if(left.type.isStructInstance) {
// indexing on a pointer directly
// fetch pointer address, determine struct and field, add index * structsize
if(left.variable!=null) {
val pointerTr = translateExpression(left.variable!!)
result += pointerTr.chunks
pointerReg = pointerTr.resultReg
} else if(left.pointerderef!=null) {
TODO("get pointer from deref $left")
} else {
throw AssemblyError("weird arrayindexer $left")
}
val struct = left.type.subType!! as StStruct
val constindex = left.index as? PtNumber
if(constindex!=null) {
extraFieldOffset = struct.size.toInt() * constindex.number.toInt()
} else {
val (chunks, indexReg) = codeGen.loadIndexReg(left.index, struct.size.toInt(), true, false)
result += chunks
addInstr(result, IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg), null)
}
field = struct.getField(right.name, codeGen.program.memsizer)
} else {
// indexing on an array with pointers
// fetch the pointer from the array, determine the struct & field
val indexedTr = translateExpression(left)
result += indexedTr.chunks
pointerReg = indexedTr.resultReg
val struct = left.type.dereference().subType as? StStruct
require(indexedTr.dt == IRDataType.WORD && struct != null)
field = struct.getField(right.name, codeGen.program.memsizer)
}
// add field offset to pointer and load the value into the result register
val fieldVmDt = irType(field.first)
val fieldOffset = field.second.toInt() + extraFieldOffset
var resultFpReg = -1
var resultReg = -1
if (fieldVmDt == IRDataType.FLOAT)
resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
else
resultReg = codeGen.registers.next(fieldVmDt)
if(fieldOffset==0) {
// no offset, can use current pointer directly
result += IRCodeChunk(null, null).also {
it += if (fieldVmDt == IRDataType.FLOAT)
IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultFpReg, reg1 = pointerReg)
else
IRInstruction(Opcode.LOADI, fieldVmDt, reg1 = resultReg, reg2 = pointerReg)
}
} else {
// actually have to add an offset
if(fieldOffset<=255) {
if(fieldVmDt==IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOADFIELD, fieldVmDt, fpReg1 = resultFpReg, reg1 = pointerReg, immediate = fieldOffset), null)
else
addInstr(result, IRInstruction(Opcode.LOADFIELD, fieldVmDt, reg1 = resultReg, reg2 = pointerReg, immediate = fieldOffset), null)
} else {
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldOffset)
it += if (fieldVmDt == IRDataType.FLOAT)
IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultFpReg, reg1 = pointerReg)
else
IRInstruction(Opcode.LOADI, fieldVmDt, reg1 = resultReg, reg2 = pointerReg)
}
}
}
return ExpressionCodeResult(result, fieldVmDt, resultReg, resultFpReg)
}
internal fun traverseRestOfDerefChainToCalculateFinalAddress(targetPointerDeref: PtPointerDeref, pointerReg: Int): Pair<IRCodeChunks, UByte> {
// returns instructions to calculate the pointer address, and the offset into the struct it points to
// so that LOADFIELD and STOREFIELD opcodes can be used instead of having an explicit extra ADD
val result = mutableListOf<IRCodeChunkBase>()
if(targetPointerDeref.chain.isEmpty())
return result to 0u // nothing to do; there's no deref chain
var struct: StStruct? = null
if(targetPointerDeref.startpointer.type.subType!=null)
struct = targetPointerDeref.startpointer.type.subType as StStruct
if(targetPointerDeref.chain.isNotEmpty()) {
// traverse deref chain
for(deref in targetPointerDeref.chain.dropLast(1)) {
val fieldinfo = struct!!.getField(deref, codeGen.program.memsizer)
struct = fieldinfo.first.subType as StStruct
// get new pointer from field
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt())
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
it += IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg)
}
}
}
val field = targetPointerDeref.chain.last()
val fieldinfo = struct!!.getField(field, codeGen.program.memsizer)
if(fieldinfo.second>0u) {
if(targetPointerDeref.derefLast) {
require(fieldinfo.first.isPointer)
// add the field offset
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = fieldinfo.second.toInt()), null)
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
return result to 0u
} else {
return result to fieldinfo.second
}
}
if(targetPointerDeref.derefLast) {
require(fieldinfo.first.isPointer)
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
}
return result to 0u
}
} }

View File

@@ -56,6 +56,8 @@ class IRCodeGen(
return irProg return irProg
} }
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes()
private fun changeGlobalVarInits(symbolTable: SymbolTable) { private fun changeGlobalVarInits(symbolTable: SymbolTable) {
// Normally, block level (global) variables that have a numeric initialization value // Normally, block level (global) variables that have a numeric initialization value
// are initialized via an assignment statement. // are initialized via an assignment statement.
@@ -65,17 +67,16 @@ class IRCodeGen(
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) { if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
val block = variable.parent.astNode as PtBlock val block = variable.parent.astNode as PtBlock
val initialization = (block.children.firstOrNull { val initialization = (block.children.firstOrNull {
it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedName it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedNameString
} as PtAssignment?) } as PtAssignment?)
val initValue = initialization?.value when(val initValue = initialization?.value){
when(initValue){
is PtBool -> { is PtBool -> {
require(initValue.asInt()!=0) { "boolean var should not be initialized with false, it wil be set to false as part of BSS clear, initializer=$initialization" } require(initValue.asInt()!=0 || variable.zpwish!=ZeropageWish.NOT_IN_ZEROPAGE) { "non-zp variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
variable.setOnetimeInitNumeric(initValue.asInt().toDouble()) variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
initsToRemove += block to initialization initsToRemove += block to initialization
} }
is PtNumber -> { is PtNumber -> {
require(initValue.number!=0.0 || variable.zpwish!=ZeropageWish.NOT_IN_ZEROPAGE) {"non-zp variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" } require(initValue.number!=0.0 || variable.zpwish!=ZeropageWish.NOT_IN_ZEROPAGE) { "non-zp variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
variable.setOnetimeInitNumeric(initValue.number) variable.setOnetimeInitNumeric(initValue.number)
initsToRemove += block to initialization initsToRemove += block to initialization
} }
@@ -104,6 +105,15 @@ class IRCodeGen(
is PtVariable -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" } is PtVariable -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
is PtProgram -> require('.' !in node.name) { "program name should not be scoped: ${node.name}" } is PtProgram -> require('.' !in node.name) { "program name should not be scoped: ${node.name}" }
is PtSubroutineParameter -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" } is PtSubroutineParameter -> require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
is PtPointerDeref -> require('.' in node.startpointer.name) { "node $node name is not scoped: ${node.startpointer.name}" }
is PtIdentifier -> {
if('.' !in node.name) {
// there is 1 case where the identifier is not scoped: if it's the value field name after a pointer array indexing.
val expr = node.parent as? PtBinaryExpression
if (expr?.operator != "." || expr.right !== node || expr.left !is PtArrayIndexer || (!expr.left.type.isPointer && !expr.left.type.isStructInstance))
require('.' in node.name) { "node $node name is not scoped: ${node.name}" }
}
}
else -> { /* node has no name or is ok to have no dots in the name */ } else -> { /* node has no name or is ok to have no dots in the name */ }
} }
node.children.forEach { verifyPtNode(it) } node.children.forEach { verifyPtNode(it) }
@@ -257,8 +267,10 @@ class IRCodeGen(
is PtArray, is PtArray,
is PtBlock, is PtBlock,
is PtDefer -> throw AssemblyError("defer should have been transformed") is PtDefer -> throw AssemblyError("defer should have been transformed")
is PtString -> throw AssemblyError("string should not occur as separate statement node ${node.position}") is PtString -> throw AssemblyError("string should not occur as separate statement node $node")
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}") is PtSub -> throw AssemblyError("nested subroutines should have been flattened $node")
is PtStructDecl -> emptyList()
is PtSubSignature -> emptyList()
else -> TODO("missing codegen for $node") else -> TODO("missing codegen for $node")
} }
@@ -324,7 +336,7 @@ class IRCodeGen(
return result return result
} }
private fun IRBranchInstr(condition: BranchCondition, label: String?=null, address: Int?=null): IRInstruction { internal fun IRBranchInstr(condition: BranchCondition, label: String?=null, address: Int?=null): IRInstruction {
if(label!=null) if(label!=null)
return when(condition) { return when(condition) {
BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label) BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label)
@@ -477,7 +489,7 @@ class IRCodeGen(
translateForInNonConstantRange(forLoop, loopvar) translateForInNonConstantRange(forLoop, loopvar)
} }
is PtIdentifier -> { is PtIdentifier -> {
require(forLoop.variable.name == loopvar.scopedName) require(forLoop.variable.name == loopvar.scopedNameString)
val elementDt = irType(iterable.type.elementType()) val elementDt = irType(iterable.type.elementType())
val iterableLength = symbolTable.getLength(iterable.name) val iterableLength = symbolTable.getLength(iterable.name)
val loopvarSymbol = forLoop.variable.name val loopvarSymbol = forLoop.variable.name
@@ -501,7 +513,7 @@ class IRCodeGen(
result += jumpChunk result += jumpChunk
result += IRCodeChunk(endLabel, null) result += IRCodeChunk(endLabel, null)
} }
iterable.type.isSplitWordArray -> { iterable.type.isSplitWordArray || iterable.type.isPointerArray -> {
// iterate over lsb/msb split word array // iterate over lsb/msb split word array
if(elementDt!=IRDataType.WORD) if(elementDt!=IRDataType.WORD)
throw AssemblyError("weird dt") throw AssemblyError("weird dt")
@@ -557,7 +569,7 @@ class IRCodeGen(
val step = iterable.step.number.toInt() val step = iterable.step.number.toInt()
if (step==0) if (step==0)
throw AssemblyError("step 0") throw AssemblyError("step 0")
require(forLoop.variable.name == loopvar.scopedName) require(forLoop.variable.name == loopvar.scopedNameString)
val loopvarSymbol = forLoop.variable.name val loopvarSymbol = forLoop.variable.name
val loopvarDt = when(loopvar) { val loopvarDt = when(loopvar) {
is StMemVar -> loopvar.dt is StMemVar -> loopvar.dt
@@ -643,7 +655,7 @@ class IRCodeGen(
private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StNode): IRCodeChunks { private fun translateForInConstantRange(forLoop: PtForLoop, loopvar: StNode): IRCodeChunks {
val loopLabel = createLabelName() val loopLabel = createLabelName()
require(forLoop.variable.name == loopvar.scopedName) require(forLoop.variable.name == loopvar.scopedNameString)
val loopvarSymbol = forLoop.variable.name val loopvarSymbol = forLoop.variable.name
val loopvarDt = when(loopvar) { val loopvarDt = when(loopvar) {
is StMemVar -> loopvar.dt is StMemVar -> loopvar.dt
@@ -1665,7 +1677,7 @@ class IRCodeGen(
translateSimple(cond, Opcode.BSTEQ, false) translateSimple(cond, Opcode.BSTEQ, false)
} }
is PtTypeCast -> { is PtTypeCast -> {
require(cond.type.isBool && cond.value.type.isNumeric) require(cond.type.isBool && (cond.value.type.isNumeric || cond.value.type.isPointer))
translateSimple(cond, Opcode.BSTEQ, false) translateSimple(cond, Opcode.BSTEQ, false)
} }
is PtIdentifier, is PtArrayIndexer, is PtContainmentCheck -> { is PtIdentifier, is PtArrayIndexer, is PtContainmentCheck -> {
@@ -1681,6 +1693,9 @@ class IRCodeGen(
is PtBinaryExpression -> { is PtBinaryExpression -> {
translateBinExpr(cond) translateBinExpr(cond)
} }
is PtPointerDeref -> {
translateSimple(cond, Opcode.BSTEQ, false)
}
else -> throw AssemblyError("weird if condition ${ifElse.condition}") else -> throw AssemblyError("weird if condition ${ifElse.condition}")
} }
return result return result
@@ -1755,9 +1770,7 @@ class IRCodeGen(
private fun isIndirectJump(jump: PtJump): Boolean { private fun isIndirectJump(jump: PtJump): Boolean {
if(jump.target.asConstInteger()!=null) if(jump.target.asConstInteger()!=null)
return false return false
val identifier = jump.target as? PtIdentifier val identifier = jump.target as? PtIdentifier ?: return true
if(identifier==null)
return true
val symbol = symbolTable.lookup(identifier.name) val symbol = symbolTable.lookup(identifier.name)
return symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR return symbol?.type==StNodeType.MEMVAR || symbol?.type==StNodeType.STATICVAR
} }
@@ -1848,7 +1861,7 @@ class IRCodeGen(
is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ } is PtVariable, is PtConstant, is PtMemMapped -> { /* vars should be looked up via symbol table */ }
is PtAlign -> TODO("ir support for inline %align") is PtAlign -> TODO("ir support for inline %align")
is PtSub -> { is PtSub -> {
val sub = IRSubroutine(child.name, translate(child.parameters), child.returns, child.position) val sub = IRSubroutine(child.name, translateParameters(child.signature.children), child.signature.returns, child.position)
for (subchild in child.children) { for (subchild in child.children) {
translateNode(subchild).forEach { sub += it } translateNode(subchild).forEach { sub += it }
} }
@@ -1897,21 +1910,22 @@ class IRCodeGen(
} }
} }
} }
is PtStructDecl -> { /* do nothing, should be found in the symbol table */ }
else -> TODO("weird block child node $child") else -> TODO("weird block child node $child")
} }
} }
return irBlock return irBlock
} }
private fun translate(parameters: List<PtSubroutineParameter>): List<IRSubroutine.IRParam> { private fun translateParameters(parameters: List<PtNode>): List<IRSubroutine.IRParam> {
val result = mutableListOf<IRSubroutine.IRParam>() val result = mutableListOf<IRSubroutine.IRParam>()
parameters.forEach { parameters.forEach {
it as PtSubroutineParameter
if(it.register==null) { if(it.register==null) {
val flattenedName = it.definingISub()!!.name + "." + it.name require('.' in it.name) { "even parameter names should have been made fully scoped by now" }
if (symbolTable.lookup(flattenedName) == null) val orig = symbolTable.lookup(it.name) as? StStaticVariable
TODO("fix missing lookup for: $flattenedName parameter") ?: TODO("fix missing lookup for: ${it.name} parameter")
val orig = symbolTable.lookup(flattenedName) as StStaticVariable result += IRSubroutine.IRParam(it.name, orig.dt)
result += IRSubroutine.IRParam(flattenedName, orig.dt)
} else { } else {
val reg = it.register val reg = it.register
require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" } require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
@@ -1936,7 +1950,7 @@ class IRCodeGen(
internal fun isOne(expression: PtExpression): Boolean = (expression as? PtNumber)?.number==1.0 || (expression as? PtBool)?.value==true internal fun isOne(expression: PtExpression): Boolean = (expression as? PtNumber)?.number==1.0 || (expression as? PtBool)?.value==true
fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk { internal fun makeSyscall(syscall: IMSyscall, params: List<Pair<IRDataType, Int>>, returns: Pair<IRDataType, Int>?, label: String?=null): IRCodeChunk {
return IRCodeChunk(label, null).also { return IRCodeChunk(label, null).also {
val args = params.map { (dt, reg)-> val args = params.map { (dt, reg)->
FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(dt, reg, null)) FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(dt, reg, null))
@@ -1947,9 +1961,7 @@ class IRCodeGen(
} }
} }
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes() internal fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
val chunk = IRCodeChunk(null, null) val chunk = IRCodeChunk(null, null)
when(registerOrFlag.registerOrPair) { when(registerOrFlag.registerOrPair) {
RegisterOrPair.A -> chunk += IRInstruction(Opcode.STOREHA, IRDataType.BYTE, reg1=resultReg) RegisterOrPair.A -> chunk += IRInstruction(Opcode.STOREHA, IRDataType.BYTE, reg1=resultReg)
@@ -1972,4 +1984,77 @@ class IRCodeGen(
} }
return chunk return chunk
} }
internal fun evaluatePointerAddressIntoReg(result: MutableList<IRCodeChunkBase>, deref: PtPointerDeref): Pair<Int, UByte> {
// calculates the pointer address and returns the register it's in + remaining offset into the struct (so that LOADFIELD/STOREFIELD instructions can be used)
val pointerTr = expressionEval.translateExpression(deref.startpointer)
result += pointerTr.chunks
val (instructions, offset) = expressionEval.traverseRestOfDerefChainToCalculateFinalAddress(deref, pointerTr.resultReg)
result += instructions
return pointerTr.resultReg to offset
}
internal fun storeValueAtPointersLocation(result: MutableList<IRCodeChunkBase>, addressReg: Int, offset: UByte, type: DataType, valueIsZero: Boolean, existingValueRegister: Int) {
if(offset<=0u) {
val irdt = irType(type)
val instr = if(type.isFloat) {
if (valueIsZero) IRInstruction(Opcode.STOREZI, IRDataType.FLOAT, reg1 = addressReg)
else IRInstruction(Opcode.STOREI, IRDataType.FLOAT, fpReg1 = existingValueRegister, reg1 = addressReg)
} else {
if (valueIsZero) IRInstruction(Opcode.STOREZI, irdt, reg1 = addressReg)
else IRInstruction(Opcode.STOREI, irdt, reg1 = existingValueRegister, reg2 = addressReg)
}
addInstr(result, instr, null)
return
}
// store with field offset
var valueRegister = existingValueRegister
val irdt = irType(type)
if(valueIsZero && valueRegister<0) {
if(type.isFloat) {
valueRegister = registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = valueRegister, immediateFp = 0.0), null)
} else {
valueRegister = registers.next(irdt)
addInstr(result, IRInstruction(Opcode.LOAD, irdt, reg1 = valueRegister, immediate = 0), null)
}
}
val instr = if (type.isFloat)
IRInstruction(Opcode.STOREFIELD, IRDataType.FLOAT, fpReg1 = valueRegister, reg1 = addressReg, immediate = offset.toInt())
else
IRInstruction(Opcode.STOREFIELD, irdt, reg1 = valueRegister, reg2 = addressReg, immediate = offset.toInt())
addInstr(result, instr, null)
}
internal fun loadIndexReg(index: PtExpression, itemsize: Int, wordIndex: Boolean, arrayIsSplitWords: Boolean): Pair<IRCodeChunks, Int> {
// returns the code to load the Index into the register, which is also returned.
require(index !is PtNumber) { "index should not be a constant number here, calling code should handle that in a more efficient way" }
val result = mutableListOf<IRCodeChunkBase>()
if(wordIndex) {
val tr = expressionEval.translateExpression(index)
addToResult(result, tr, tr.resultReg, -1)
var indexReg = tr.resultReg
if(tr.dt==IRDataType.BYTE) {
indexReg = registers.next(IRDataType.WORD)
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexReg, reg2=tr.resultReg), null)
}
result += multiplyByConst(DataType.UWORD, indexReg, itemsize)
return Pair(result, indexReg)
}
// regular byte size index value.
val byteIndexTr = expressionEval.translateExpression(index)
addToResult(result, byteIndexTr, byteIndexTr.resultReg, -1)
if(itemsize==1 || arrayIsSplitWords)
return Pair(result, byteIndexTr.resultReg)
result += multiplyByConst(DataType.UBYTE, byteIndexTr.resultReg, itemsize)
return Pair(result, byteIndexTr.resultReg)
}
} }

View File

@@ -53,6 +53,8 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions) || removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|| removeDoubleSecClc(chunk1, indexedInstructions) || removeDoubleSecClc(chunk1, indexedInstructions)
|| cleanupPushPop(chunk1, indexedInstructions) || cleanupPushPop(chunk1, indexedInstructions)
|| simplifyConstantReturns(chunk1, indexedInstructions)
|| removeNeedlessLoads(chunk1, indexedInstructions)
} while (changed) } while (changed)
} }
} }
@@ -352,6 +354,29 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed return changed
} }
private fun removeNeedlessLoads(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
/*
load.b r2,#2
loadr.b r1,r2
jump p8_label_gen_2
*/
var changed=false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(idx>=2 && ins.opcode in OpcodesThatJump) {
val previous = indexedInstructions[idx-1].value
val previous2 = indexedInstructions[idx-2].value
if(previous.opcode==Opcode.LOADR && previous2.opcode in OpcodesThatLoad) {
if(previous.reg2==previous2.reg1) {
chunk.instructions[idx-2] = previous2.copy(reg1=previous.reg1)
chunk.instructions.removeAt(idx-1)
changed=true
}
}
}
}
return changed
}
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean { private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first // note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
var changed = false var changed = false
@@ -455,4 +480,23 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
return changed return changed
} }
private fun simplifyConstantReturns(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// use a RETURNI when a RETURNR is just returning a constant that was loaded into a register just before
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(ins.opcode==Opcode.RETURNR) {
if(idx>0) {
val insBefore = chunk.instructions[idx-1]
if(insBefore.opcode == Opcode.LOAD && insBefore.immediate!=null) {
val constvalue = insBefore.immediate!!
chunk.instructions[idx] = IRInstruction(Opcode.RETURNI, ins.type, immediate = constvalue)
chunk.instructions.removeAt(idx-1)
changed = true
}
}
}
}
return changed
}
} }

View File

@@ -27,6 +27,9 @@ class IRUnusedCodeRemover(
// we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction or variable initializer value // we could clean up the SymbolTable as well, but ONLY if these symbols aren't referenced somewhere still in an instruction or variable initializer value
val prefix = "$blockLabel." val prefix = "$blockLabel."
val blockVars = irprog.st.allVariables().filter { it.name.startsWith(prefix) } val blockVars = irprog.st.allVariables().filter { it.name.startsWith(prefix) }
// check if there are symbols referenced elsewhere that we should not prune (even though the rest of the block is empty)
blockVars.forEach { stVar -> blockVars.forEach { stVar ->
irprog.allSubs().flatMap { it.chunks }.forEach { chunk -> irprog.allSubs().flatMap { it.chunks }.forEach { chunk ->
chunk.instructions.forEach { ins -> chunk.instructions.forEach { ins ->
@@ -47,6 +50,19 @@ class IRUnusedCodeRemover(
} }
} }
val blockStructs = irprog.st.allStructDefs().filter { it.name.startsWith(prefix) }
blockStructs.forEach { struct ->
irprog.st.allStructInstances().forEach { instance ->
if(instance.structName == struct.name)
return // a struct instance is declared using this struct type
}
irprog.st.allVariables().forEach { variable ->
if(variable.dt.isPointer || variable.dt.isStructInstance)
if(struct.name == variable.dt.subType!!.scopedNameString)
return // a variable exists with the struct as (pointer) type
}
}
irprog.st.removeTree(blockLabel) irprog.st.removeTree(blockLabel)
removeBlockInits(irprog, blockLabel) removeBlockInits(irprog, blockLabel)
} }

View File

@@ -14,6 +14,12 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
StNodeType.MEMVAR -> st.add(convert(it.value as StMemVar)) StNodeType.MEMVAR -> st.add(convert(it.value as StMemVar))
StNodeType.CONSTANT -> st.add(convert(it.value as StConstant)) StNodeType.CONSTANT -> st.add(convert(it.value as StConstant))
StNodeType.MEMORYSLAB -> st.add(convert(it.value as StMemorySlab)) StNodeType.MEMORYSLAB -> st.add(convert(it.value as StMemorySlab))
StNodeType.STRUCTINSTANCE -> {
val instance = it.value as StStructInstance
val struct = sourceSt.lookup(instance.structName) as StStruct
st.add(convert(instance, struct.fields))
}
StNodeType.STRUCT -> st.add(convert(it.value as StStruct))
else -> { } else -> { }
} }
} }
@@ -37,12 +43,23 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
} }
private fun convert(variable: StStaticVariable): IRStStaticVariable { private fun convert(struct: StStruct): IRStStructDef =
IRStStructDef(struct.scopedNameString, struct.fields, struct.size)
fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol) private fun convertArrayElt(elt: StArrayElement): IRStArrayElement {
else return if (elt.boolean != null)
IRStArrayElement(null, elt.number, elt.addressOfSymbol) IRStArrayElement(elt.boolean, null, null)
else if(elt.number!=null)
IRStArrayElement(null, elt.number, null)
else {
val symbol = elt.addressOfSymbol ?: (StStructInstanceBlockName + "." + (elt.structInstance ?: elt.structInstanceUninitialized))
IRStArrayElement(null, null, symbol)
}
}
private fun convert(variable: StStaticVariable): IRStStaticVariable {
if('.' in variable.name) { if('.' in variable.name) {
return IRStStaticVariable(variable.name, return IRStStaticVariable(variable.name,
@@ -61,15 +78,16 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
val newArray = mutableListOf<IRStArrayElement>() val newArray = mutableListOf<IRStArrayElement>()
array.forEach { array.forEach {
if(it.addressOfSymbol!=null) { if(it.addressOfSymbol!=null) {
val target = variable.lookup(it.addressOfSymbol!!) ?: throw NoSuchElementException("can't find variable ${it.addressOfSymbol}") val target = variable.lookup(it.addressOfSymbol!!) ?:
newArray.add(IRStArrayElement(null, null, target.scopedName)) throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
newArray.add(IRStArrayElement(null, null, target.scopedNameString))
} else { } else {
newArray.add(convertArrayElt(it)) newArray.add(convertArrayElt(it))
} }
} }
return newArray return newArray
} }
val scopedName = variable.scopedName val scopedName = variable.scopedNameString
return IRStStaticVariable(scopedName, return IRStStaticVariable(scopedName,
variable.dt, variable.dt,
variable.initializationNumericValue, variable.initializationNumericValue,
@@ -94,7 +112,7 @@ private fun convert(variable: StMemVar): IRStMemVar {
) )
} else { } else {
val scopedName = try { val scopedName = try {
variable.scopedName variable.scopedNameString
} catch (_: UninitializedPropertyAccessException) { } catch (_: UninitializedPropertyAccessException) {
variable.name variable.name
} }
@@ -109,7 +127,7 @@ private fun convert(constant: StConstant): IRStConstant {
constant.name constant.name
} else { } else {
try { try {
constant.scopedName constant.scopedNameString
} catch (_: UninitializedPropertyAccessException) { } catch (_: UninitializedPropertyAccessException) {
constant.name constant.name
} }
@@ -118,16 +136,21 @@ private fun convert(constant: StConstant): IRStConstant {
} }
private fun convert(variable: StMemorySlab): IRStMemorySlab { private fun convert(mem: StMemorySlab): IRStMemorySlab {
return if('.' in variable.name) return if('.' in mem.name)
IRStMemorySlab(variable.name, variable.size, variable.align) IRStMemorySlab(mem.name, mem.size, mem.align)
else else
IRStMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align) IRStMemorySlab("$StMemorySlabBlockName.${mem.name}", mem.size, mem.align)
} }
/*
private fun convert(instance: StStructInstance, fields: Iterable<Pair<DataType, String>>): IRStStructInstance {
val values = fields.zip(instance.initialValues).map { (field, value) ->
val elt = convertArrayElt(value)
*/ IRStructInitValue(field.first.base, elt)
}
return if('.' in instance.name)
IRStStructInstance(instance.name, instance.structName, values, instance.size)
else
IRStStructInstance("${StStructInstanceBlockName}.${instance.name}", instance.structName, values, instance.size)
}

View File

@@ -3,7 +3,9 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer { internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType, numElements: Int?): Int { override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray || dt.isSplitWordArray) { if(dt.isPointerArray)
return 2 * numElements!!
else if(dt.isArray || dt.isSplitWordArray) {
require(numElements!=null) require(numElements!=null)
return when(dt.sub) { return when(dt.sub) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements

View File

@@ -1,5 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
kotlin("jvm") kotlin("jvm")
} }
@@ -8,7 +6,7 @@ dependencies {
implementation(project(":codeCore")) implementation(project(":codeCore"))
implementation(project(":compilerAst")) implementation(project(":compilerAst"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1") implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
// implementation "org.jetbrains.kotlin:kotlin-reflect" // implementation "org.jetbrains.kotlin:kotlin-reflect"
} }

View File

@@ -27,8 +27,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
// @( &thing ) --> thing (but only if thing is a byte type!) // @( &thing ) --> thing (but only if thing is a byte type!)
val addrOf = memread.addressExpression as? AddressOf val addrOf = memread.addressExpression as? AddressOf
if(addrOf!=null) { if(addrOf!=null) {
if(addrOf.identifier.inferType(program).isBytes) if(addrOf.identifier?.inferType(program)?.isBytes==true)
return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier, parent)) return listOf(IAstModification.ReplaceNode(memread, addrOf.identifier!!, parent))
} }
return noModifications return noModifications
} }
@@ -332,15 +332,19 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val constIndex = arrayIndexedExpression.indexer.constIndex() val constIndex = arrayIndexedExpression.indexer.constIndex()
if (constIndex != null) { if (constIndex != null) {
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl() if(arrayIndexedExpression.plainarrayvar!=null) {
if(arrayVar!=null) { val arrayVar = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
val array =arrayVar.value as? ArrayLiteral if(arrayVar!=null) {
if(array!=null) { val array =arrayVar.value as? ArrayLiteral
val value = array.value[constIndex].constValue(program) if(array!=null) {
if(value!=null) { val value = array.value[constIndex].constValue(program)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent)) if(value!=null) {
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent))
}
} }
} }
} else if(arrayIndexedExpression.pointerderef!=null) {
TODO("constant fold pointer[i]")
} }
} }
} }
@@ -390,9 +394,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val loopvar = forLoop.loopVar.targetVarDecl() ?: return noModifications val loopvar = forLoop.loopVar.targetVarDecl() ?: return noModifications
val stepLiteral = iterableRange.step as? NumericLiteral val stepLiteral = iterableRange.step as? NumericLiteral
require(loopvar.datatype.sub == null) require(loopvar.datatype.isBasic)
val loopvarSimpleDt = loopvar.datatype.base when(val loopvarSimpleDt = loopvar.datatype.base) {
when(loopvarSimpleDt) {
BaseDataType.UBYTE -> { BaseDataType.UBYTE -> {
if(rangeFrom.type != BaseDataType.UBYTE) { if(rangeFrom.type != BaseDataType.UBYTE) {
// attempt to translate the iterable into ubyte values // attempt to translate the iterable into ubyte values

View File

@@ -26,7 +26,7 @@ class VarConstantValueTypeAdjuster(
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> { override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.parent is AnonymousScope) if(decl.parent is AnonymousScope)
throw FatalAstException("vardecl may no longer occur in anonymousscope") throw FatalAstException("vardecl may no longer occur in anonymousscope ${decl.position}")
try { try {
val declConstValue = decl.value?.constValue(program) val declConstValue = decl.value?.constValue(program)
@@ -323,7 +323,7 @@ internal class ConstantIdentifierReplacer(
val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position) val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
return if(arrayIdx.parent is AssignTarget) { return if(arrayIdx.parent is AssignTarget) {
val memwrite = DirectMemoryWrite(add, identifier.position) val memwrite = DirectMemoryWrite(add, identifier.position)
val assignTarget = AssignTarget(null, null, memwrite, null, false, identifier.position) val assignTarget = AssignTarget(null, null, memwrite, null, false, position = identifier.position)
listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent)) listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
} else { } else {
val memread = DirectMemoryRead(add, identifier.position) val memread = DirectMemoryRead(add, identifier.position)

View File

@@ -18,8 +18,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
// try to statically convert a literal value into one of the desired type // try to statically convert a literal value into one of the desired type
val literal = typecast.expression as? NumericLiteral val literal = typecast.expression as? NumericLiteral
if (literal != null) { if (literal != null && typecast.type.isBasic) {
val newLiteral = literal.cast(typecast.type, typecast.implicit) val newLiteral = literal.cast(typecast.type.base, typecast.implicit)
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) { if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent) mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
} }
@@ -33,7 +33,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast) mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
} }
} else { } else {
if (typecast.expression.inferType(program) issimpletype typecast.type) { if (typecast.expression.inferType(program) istype typecast.type) {
// remove duplicate cast // remove duplicate cast
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent) mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
} }
@@ -242,14 +242,16 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
} }
} }
if (rightVal?.number == 1.0) { if (rightVal?.number == 1.0) {
if (rightDt != leftDt) { if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position) val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr)) return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
} }
} }
else if (rightVal?.number == 0.0) { else if (rightVal?.number == 0.0) {
if (rightDt != leftDt) { if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position) val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr)) return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
} }
} }
@@ -262,14 +264,16 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
} }
} }
if (rightVal?.number == 1.0) { if (rightVal?.number == 1.0) {
if(rightDt!=leftDt) { if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position) val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr)) return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
} }
} }
else if (rightVal?.number == 0.0) { else if (rightVal?.number == 0.0) {
if(rightDt!=leftDt) { if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
val right = NumericLiteral(leftDt.base, rightVal.number, rightVal.position) val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
return listOf(IAstModification.ReplaceNode(expr.right, right, expr)) return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
} }
} }
@@ -423,6 +427,31 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return noModifications return noModifications
} }
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
if(arrayIndexedExpression.indexer.constIndex()==0) {
if(arrayIndexedExpression.plainarrayvar!=null) {
if((arrayIndexedExpression.parent as? BinaryExpression)?.operator !=".") {
val dt = arrayIndexedExpression.plainarrayvar!!.inferType(program).getOrUndef()
if(dt.isPointer) {
// pointer[0] --> pointer^^
val deref = PtrDereference(arrayIndexedExpression.plainarrayvar!!.nameInSource, true, arrayIndexedExpression.plainarrayvar!!.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression,deref, parent))
}
}
}
val ptrDeref = arrayIndexedExpression.pointerderef
if(ptrDeref!=null) {
val dt = ptrDeref.inferType(program).getOrUndef()
if(dt.isPointer) {
// ptr1.ptr2[0] --> ptr1.ptr2^^
val deref = PtrDereference(ptrDeref.chain, true, ptrDeref.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, deref, parent))
}
}
}
return noModifications
}
private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? { private fun applyAbsorptionLaws(expr: BinaryExpression): Expression? {
// NOTE: only when the terms are not function calls!!! // NOTE: only when the terms are not function calls!!!
if(expr.left is IFunctionCall || expr.right is IFunctionCall) if(expr.left is IFunctionCall || expr.right is IFunctionCall)
@@ -499,7 +528,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
} }
else if (valueDt issimpletype BaseDataType.BYTE) { else if (valueDt issimpletype BaseDataType.BYTE) {
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now. // useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
val cast = TypecastExpression(arg.expression, BaseDataType.UBYTE, true, arg.position) val cast = TypecastExpression(arg.expression, DataType.UBYTE, true, arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent)) return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
} }
} else { } else {
@@ -516,7 +545,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
} }
else if (argDt issimpletype BaseDataType.BYTE) { else if (argDt issimpletype BaseDataType.BYTE) {
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now. // useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
val cast = TypecastExpression(arg, BaseDataType.UBYTE, true, arg.position) val cast = TypecastExpression(arg, DataType.UBYTE, true, arg.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent)) return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
} }
} }
@@ -555,7 +584,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
if(functionCallExpr.target.nameInSource == listOf("mkword")) { if(functionCallExpr.target.nameInSource == listOf("mkword")) {
if(functionCallExpr.args[0].constValue(program)?.number==0.0) { if(functionCallExpr.args[0].constValue(program)?.number==0.0) {
// just cast the lsb to uword // just cast the lsb to uword
val cast = TypecastExpression(functionCallExpr.args[1], BaseDataType.UWORD, true, functionCallExpr.position) val cast = TypecastExpression(functionCallExpr.args[1], DataType.UWORD, true, functionCallExpr.position)
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent)) return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
} }
} }
@@ -711,9 +740,9 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
// just use: msb(value) as type // just use: msb(value) as type
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position) val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return if(leftDt.isSignedWord) return if(leftDt.isSignedWord)
TypecastExpression(msb, BaseDataType.BYTE, true, expr.position) TypecastExpression(msb, DataType.BYTE, true, expr.position)
else else
TypecastExpression(msb, BaseDataType.UWORD, true, expr.position) TypecastExpression(msb, DataType.UWORD, true, expr.position)
} }
else -> return null else -> return null
} }
@@ -839,14 +868,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
// shift left by 8 bits is just a byte operation: mkword(lsb(X), 0) // shift left by 8 bits is just a byte operation: mkword(lsb(X), 0)
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position) val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)), expr.position) val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)), expr.position)
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position) return TypecastExpression(mkword, DataType.WORD, true, expr.position)
} }
else if (amount > 8) { else if (amount > 8) {
// same as above but with residual shifts. // same as above but with residual shifts.
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position) val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position) val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position) val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position) return TypecastExpression(mkword, DataType.WORD, true, expr.position)
} }
} }
else -> { else -> {
@@ -887,12 +916,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
else if(amount==8) { else if(amount==8) {
// shift right by 8 bits is just a byte operation: msb(X) as uword // shift right by 8 bits is just a byte operation: msb(X) as uword
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position) val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(msb, BaseDataType.UWORD, true, expr.position) return TypecastExpression(msb, DataType.UWORD, true, expr.position)
} }
else if (amount > 8) { else if (amount > 8) {
// same as above but with residual shifts. // same as above but with residual shifts.
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position) val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), BaseDataType.UWORD, true, expr.position) return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
} }
} }
BaseDataType.WORD -> { BaseDataType.WORD -> {
@@ -903,12 +932,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
else if(amount == 8) { else if(amount == 8) {
// shift right by 8 bits is just a byte operation: msb(X) as byte (will get converted to word later) // shift right by 8 bits is just a byte operation: msb(X) as byte (will get converted to word later)
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position) val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
return TypecastExpression(msb, BaseDataType.BYTE, true, expr.position) return TypecastExpression(msb, DataType.BYTE, true, expr.position)
} }
else if(amount > 8) { else if(amount > 8) {
// same as above but with residual shifts. Take care to do signed shift. // same as above but with residual shifts. Take care to do signed shift.
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position) val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
val signed = TypecastExpression(msb, BaseDataType.BYTE, true, expr.position) val signed = TypecastExpression(msb, DataType.BYTE, true, expr.position)
return BinaryExpression(signed, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position) return BinaryExpression(signed, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
} }
} }

View File

@@ -113,7 +113,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
} }
private fun makeFullyScoped(identifier: IdentifierReference) { private fun makeFullyScoped(identifier: IdentifierReference) {
identifier.targetStatement(program)?.let { target -> identifier.targetStatement()?.let { target ->
val scoped = (target as INamedStatement).scopedName val scoped = (target as INamedStatement).scopedName
val scopedIdent = IdentifierReference(scoped, identifier.position) val scopedIdent = IdentifierReference(scoped, identifier.position)
modifications += IAstModification.ReplaceNode(identifier, scopedIdent, identifier.parent) modifications += IAstModification.ReplaceNode(identifier, scopedIdent, identifier.parent)
@@ -149,7 +149,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
when (it) { when (it) {
is NumericLiteral -> it.copy() is NumericLiteral -> it.copy()
is IdentifierReference -> { is IdentifierReference -> {
val target = it.targetStatement(program) ?: return emptyList() val target = it.targetStatement() ?: return emptyList()
val scoped = (target as INamedStatement).scopedName val scoped = (target as INamedStatement).scopedName
IdentifierReference(scoped, it.position) IdentifierReference(scoped, it.position)
} }
@@ -205,7 +205,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
} }
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> { override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine val sub = functionCallStatement.target.targetStatement(program.builtinFunctions) as? Subroutine
return if(sub==null || !canInline(sub, functionCallStatement)) return if(sub==null || !canInline(sub, functionCallStatement))
noModifications noModifications
else else
@@ -213,7 +213,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
} }
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> { override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine val sub = functionCallExpr.target.targetStatement(program.builtinFunctions) as? Subroutine
if(sub!=null && sub.inline && sub.parameters.isEmpty() && canInline(sub, functionCallExpr)) { if(sub!=null && sub.inline && sub.parameters.isEmpty() && canInline(sub, functionCallExpr)) {
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) { require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1]))) {
"invalid inline sub at ${sub.position}" "invalid inline sub at ${sub.position}"
@@ -243,13 +243,13 @@ class Inliner(private val program: Program, private val options: CompilationOpti
} }
private fun canInline(sub: Subroutine, fcall: IFunctionCall): Boolean { private fun canInline(sub: Subroutine, fcall: IFunctionCall): Boolean {
if(!sub.inline) if (!sub.inline)
return false return false
if(options.compTarget.name!=VMTarget.NAME) { if (options.compTarget.name != VMTarget.NAME) {
val stmt = sub.statements.single() val stmt = sub.statements.single()
if (stmt is IFunctionCall) { if (stmt is IFunctionCall) {
val existing = (fcall as Node).definingScope.lookup(stmt.target.nameInSource.take(1)) val existing = (fcall as Node).definingScope.lookup(stmt.target.nameInSource.take(1))
return existing !is VarDecl return existing !is VarDecl && existing !is StructFieldRef
} }
} }
return true return true

View File

@@ -29,7 +29,14 @@ class StatementOptimizer(private val program: Program,
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) { if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
val arg = functionCallStatement.args.single() val arg = functionCallStatement.args.single()
val stringVar: IdentifierReference? = if(arg is AddressOf) { val stringVar: IdentifierReference? = if(arg is AddressOf) {
if(arg.arrayIndex==null) arg.identifier else null if(arg.arrayIndex==null) {
if(arg.identifier!=null)
arg.identifier
else
null // struct can't have string fields so nothing to look at here
} else {
null
}
} else { } else {
arg as? IdentifierReference arg as? IdentifierReference
} }
@@ -146,7 +153,7 @@ class StatementOptimizer(private val program: Program,
// for loop over a (constant) range of just a single value-- optimize the loop away // for loop over a (constant) range of just a single value-- optimize the loop away
// loopvar/reg = range value , follow by block // loopvar/reg = range value , follow by block
val scope = AnonymousScope.empty(forLoop.position) val scope = AnonymousScope.empty(forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position)) scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), range.from, AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
} }
@@ -161,7 +168,7 @@ class StatementOptimizer(private val program: Program,
val character = options.compTarget.encodeString(sv.value, sv.encoding)[0] val character = options.compTarget.encodeString(sv.value, sv.encoding)[0]
val byte = NumericLiteral(BaseDataType.UBYTE, character.toDouble(), iterable.position) val byte = NumericLiteral(BaseDataType.UBYTE, character.toDouble(), iterable.position)
val scope = AnonymousScope.empty(forLoop.position) val scope = AnonymousScope.empty(forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position)) scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, null, false, position=forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
} }
@@ -174,7 +181,7 @@ class StatementOptimizer(private val program: Program,
if(av!=null) { if(av!=null) {
val scope = AnonymousScope.empty(forLoop.position) val scope = AnonymousScope.empty(forLoop.position)
scope.statements.add(Assignment( scope.statements.add(Assignment(
AssignTarget(forLoop.loopVar, null, null, null, false, forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position), AssignTarget(forLoop.loopVar, null, null, null, false, position = forLoop.position), NumericLiteral.optimalInteger(av.toInt(), iterable.position),
AssignmentOrigin.OPTIMIZER, forLoop.position)) AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements) scope.statements.addAll(forLoop.body.statements)
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent)) return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
@@ -341,6 +348,39 @@ class StatementOptimizer(private val program: Program,
} }
} }
// pointer arithmetic for 6502 target
if (options.compTarget.cpu != CpuType.VIRTUAL) {
if(bexpr.operator=="+" && !bexpr.right.isSimple && !assignment.isAugmentable) {
if(targetIDt.isUnsignedWord || targetIDt.getOrUndef().isPointerToByte) {
val leftDt = bexpr.left.inferType(program).getOrUndef()
val rightDt = bexpr.right.inferType(program).getOrUndef()
fun setSizedValue(a: Assignment, value: Expression, size: Int) {
val sized = BinaryExpression(value, "*", NumericLiteral.optimalInteger(size, value.position), value.position)
a.value = sized
sized.linkParents(a)
}
if (leftDt.isPointer && !leftDt.isPointerToByte) {
// uword x = pointer + value --> x=value * sizeof , x += pointer
val size = leftDt.size(options.compTarget)
setSizedValue(assignment, bexpr.right, size)
val pointerAdd = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.left, bexpr.position)
val a2 = Assignment(assignment.target.copy(), pointerAdd, assignment.origin, assignment.position)
return listOf(IAstModification.InsertAfter(assignment, a2, parent as IStatementContainer))
} else if (rightDt.isPointer && !rightDt.isPointerToByte) {
// uword x = value + pointer --> x=value * sizeof, x += pointer
val size = rightDt.size(options.compTarget)
setSizedValue(assignment, bexpr.left, size)
assignment.linkParents(parent)
val pointerAdd = BinaryExpression(assignment.target.toExpression(), bexpr.operator, bexpr.right, bexpr.position)
val a2 = Assignment(assignment.target.copy(), pointerAdd, assignment.origin, assignment.position)
return listOf(IAstModification.InsertAfter(assignment, a2, parent as IStatementContainer))
}
}
}
}
} }
// word = lsb(word) // word = lsb(word)

View File

@@ -8,7 +8,7 @@ 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.code.INTERNED_STRINGS_MODULENAME import prog8.code.PROG8_CONTAINER_MODULES
import prog8.code.core.ICompilationTarget import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter import prog8.code.core.IErrorReporter
import prog8.compiler.CallGraph import prog8.compiler.CallGraph
@@ -93,7 +93,7 @@ class UnusedCodeRemover(private val program: Program,
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) {
if (block.name != INTERNED_STRINGS_MODULENAME && "ignore_unused" !in block.options()) { if (block.name !in PROG8_CONTAINER_MODULES && "ignore_unused" !in block.options()) {
if (!block.statements.any { it is Subroutine && it.hasBeenInlined }) if (!block.statements.any { it is Subroutine && it.hasBeenInlined })
errors.info("removing unused block '${block.name}'", block.position) errors.info("removing unused block '${block.name}'", block.position)
} }
@@ -181,14 +181,20 @@ class UnusedCodeRemover(private val program: Program,
val declIndex = (parent as IStatementContainer).statements.indexOf(decl) val declIndex = (parent as IStatementContainer).statements.indexOf(decl)
val singleUseIndex = (parent as IStatementContainer).statements.indexOf(singleUse.parent) val singleUseIndex = (parent as IStatementContainer).statements.indexOf(singleUse.parent)
if(declIndex==singleUseIndex-1) { if(declIndex==singleUseIndex-1) {
if("ignore_unused" !in decl.definingBlock.options()) val callStruct = (assignment.value as IFunctionCall).target.targetStructDecl()
errors.info("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position) if(callStruct!=null) {
val fcall = assignment.value as IFunctionCall // don't turn a struct instance allocation call to a void call, instead, remove everything
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position) return listOf(IAstModification.Remove(assignment, assignment.parent as IStatementContainer))
return listOf( } else {
IAstModification.ReplaceNode(decl, voidCall, parent), if("ignore_unused" !in decl.definingBlock.options())
IAstModification.Remove(assignment, assignment.parent as IStatementContainer) errors.info("replaced unused variable '${decl.name}' with void call, maybe this can be removed altogether", decl.position)
) val fcall = assignment.value as IFunctionCall
val voidCall = FunctionCallStatement(fcall.target, fcall.args, true, fcall.position)
return listOf(
IAstModification.ReplaceNode(decl, voidCall, parent),
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
)
}
} }
} }
} else { } else {

View File

@@ -1,5 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
id("application") id("application")
kotlin("jvm") kotlin("jvm")
@@ -18,18 +16,15 @@ dependencies {
implementation(project(":codeGenExperimental")) implementation(project(":codeGenExperimental"))
implementation(project(":virtualmachine")) implementation(project(":virtualmachine"))
// implementation(project(":beanshell")) // implementation(project(":beanshell"))
implementation("org.antlr:antlr4-runtime:4.13.2")
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation("org.jetbrains.kotlin:kotlin-reflect") // implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.6") implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.6")
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1") implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.1.0")
testImplementation(project(":codeCore")) testImplementation(project(":codeCore"))
testImplementation(project(":intermediate")) testImplementation(project(":intermediate"))
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1") testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
testImplementation("io.kotest:kotest-framework-datatest:5.9.1") testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
} }
configurations.all { configurations.all {
@@ -83,6 +78,8 @@ gversion {
classPackage = "prog8.buildversion" classPackage = "prog8.buildversion"
className = "Version" className = "Version"
language = "kotlin" language = "kotlin"
debug = false
annotate = true
} }
tasks.build { tasks.build {

View File

@@ -13,9 +13,7 @@
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" /> <orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" /> <orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" /> <orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="simpleAst" /> <orderEntry type="module" module-name="simpleAst" />
<orderEntry type="module" module-name="compilerAst" /> <orderEntry type="module" module-name="compilerAst" />
@@ -25,6 +23,6 @@
<orderEntry type="module" module-name="codeGenIntermediate" /> <orderEntry type="module" module-name="codeGenIntermediate" />
<orderEntry type="module" module-name="virtualmachine" /> <orderEntry type="module" module-name="virtualmachine" />
<orderEntry type="module" module-name="intermediate" /> <orderEntry type="module" module-name="intermediate" />
<orderEntry type="library" name="antlr.antlr4" level="project" /> <orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
</component> </component>
</module> </module>

View File

@@ -440,6 +440,7 @@ sys {
const ubyte SIZEOF_WORD = sizeof(word) const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = sizeof(uword) const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_LONG = sizeof(long) const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
const ubyte SIZEOF_FLOAT = sizeof(float) const ubyte SIZEOF_FLOAT = sizeof(float)
const byte MIN_BYTE = -128 const byte MIN_BYTE = -128
const byte MAX_BYTE = 127 const byte MAX_BYTE = 127
@@ -679,7 +680,7 @@ _loop lda P8ZP_SCRATCH_W1
}} }}
} }
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) { asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
; Called when the compiler wants to assign a string value to another string. ; Called when the compiler wants to assign a string value to another string.
%asm {{ %asm {{
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1

View File

@@ -16,26 +16,6 @@ const ubyte DEFAULT_HEIGHT = 25
extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character. extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
sub clear_screen() {
chrout(147)
}
sub cls() {
chrout(147)
}
sub home() {
chrout(19)
}
sub nl() {
chrout('\n')
}
sub spc() {
chrout(' ')
}
sub bell() { sub bell() {
chrout(7) chrout(7)
} }

View File

@@ -149,6 +149,29 @@ copy_float .proc
rts rts
.pend .pend
copy_float2 .proc
; -- copies the 5 bytes of the mflt value pointed to by P8ZP_SCRATCH_W2,
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
lda (P8ZP_SCRATCH_W2),y
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W2),y
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W2),y
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W2),y
sta (P8ZP_SCRATCH_W1),y
iny
lda (P8ZP_SCRATCH_W2),y
sta (P8ZP_SCRATCH_W1),y
rts
.pend
inc_var_f .proc inc_var_f .proc
; -- add 1 to float pointed to by A/Y ; -- add 1 to float pointed to by A/Y
; clobbers X ; clobbers X

View File

@@ -57,7 +57,7 @@ cbm {
; ---- CBM ROM kernal routines (C64 addresses) ---- ; ---- CBM ROM kernal routines (C64 addresses) ----
extsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead) extsub $AB1E = STROUT(str strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use txt.print instead)
extsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen extsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
extsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen extsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
extsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine extsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
@@ -445,6 +445,7 @@ sys {
const ubyte SIZEOF_WORD = sizeof(word) const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = sizeof(uword) const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_LONG = sizeof(long) const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
const ubyte SIZEOF_FLOAT = sizeof(float) const ubyte SIZEOF_FLOAT = sizeof(float)
const byte MIN_BYTE = -128 const byte MIN_BYTE = -128
const byte MAX_BYTE = 127 const byte MAX_BYTE = 127
@@ -686,7 +687,7 @@ _loop lda P8ZP_SCRATCH_W1
}} }}
} }
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) { asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
; Called when the compiler wants to assign a string value to another string. ; Called when the compiler wants to assign a string value to another string.
%asm {{ %asm {{
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1

View File

@@ -15,25 +15,6 @@ const ubyte DEFAULT_HEIGHT = 25
extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character. extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
sub clear_screen() {
chrout(147)
}
sub cls() {
chrout(147)
}
sub home() {
chrout(19)
}
sub nl() {
chrout('\n')
}
sub spc() {
chrout(' ')
}
sub bell() { sub bell() {
; beep ; beep

View File

@@ -19,9 +19,9 @@ bmx {
uword palette_entries ; 1-256 uword palette_entries ; 1-256
ubyte palette_start ubyte palette_start
ubyte compression ubyte compression
uword @shared palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram ^^ubyte @shared palette_buffer_ptr = 0 ; should you want to load or save the palette into main memory instead of directly into vram
uword error_message ; pointer to error message, or 0 if all ok ^^ubyte error_message ; pointer to error message, or 0 if all ok
ubyte old_drivenumber ubyte old_drivenumber
sub open(ubyte drivenumber, str filename) -> bool { sub open(ubyte drivenumber, str filename) -> bool {

View File

@@ -121,7 +121,7 @@ io_error:
goto diskio.directory.internal_dir goto diskio.directory.internal_dir
} }
sub diskname() -> uword { sub diskname() -> str {
; -- Returns pointer to disk name string or 0 if failure. ; -- Returns pointer to disk name string or 0 if failure.
cbm.SETNAM(1, "$") cbm.SETNAM(1, "$")
@@ -165,7 +165,7 @@ io_error:
; internal variables for the iterative file lister / loader ; internal variables for the iterative file lister / loader
bool list_skip_disk_name bool list_skip_disk_name
uword list_pattern ^^ubyte list_pattern
uword list_blocks uword list_blocks
bool iteration_in_progress = false bool iteration_in_progress = false
bool write_iteration_in_progress = false bool write_iteration_in_progress = false
@@ -174,7 +174,7 @@ io_error:
; ----- get a list of files (uses iteration functions internally) ----- ; ----- get a list of files (uses iteration functions internally) -----
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte { sub list_filenames(str pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
; -- fill the provided buffer with the names of the files on the disk (until buffer is full). ; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
; Files in the buffer are separated by a 0 byte. You can provide an optional pattern to match against. ; Files in the buffer are separated by a 0 byte. You can provide an optional pattern to match against.
; After the last filename one additional 0 byte is placed to indicate the end of the list. ; After the last filename one additional 0 byte is placed to indicate the end of the list.
@@ -209,7 +209,7 @@ io_error:
; ----- iterative file lister functions (uses the read io channel) ----- ; ----- iterative file lister functions (uses the read io channel) -----
sub lf_start_list(uword pattern_ptr) -> bool { sub lf_start_list(str pattern_ptr) -> bool {
; -- start an iterative file listing with optional pattern matching. ; -- start an iterative file listing with optional pattern matching.
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
cbm.SETNAM(1, "$") cbm.SETNAM(1, "$")
@@ -239,7 +239,7 @@ io_error:
return false return false
} }
sub lf_start_list_dirs(uword pattern_ptr) -> bool { sub lf_start_list_dirs(str pattern_ptr) -> bool {
; -- start an iterative directory contents listing with optional pattern matching. ; -- start an iterative directory contents listing with optional pattern matching.
; this version it only returns directory entries! ; this version it only returns directory entries!
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
@@ -247,7 +247,7 @@ io_error:
goto diskio.lf_start_list.start_list_internal goto diskio.lf_start_list.start_list_internal
} }
sub lf_start_list_files(uword pattern_ptr) -> bool { sub lf_start_list_files(str pattern_ptr) -> bool {
; -- start an iterative directory contents listing with optional pattern matching. ; -- start an iterative directory contents listing with optional pattern matching.
; this version only returns actual file entries! ; this version only returns actual file entries!
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
@@ -267,7 +267,7 @@ io_error:
repeat { repeat {
reset_read_channel() ; use the input io channel again reset_read_channel() ; use the input io channel again
uword nameptr = &list_filename ^^ubyte nameptr = &list_filename
ubyte blocks_lsb = cbm.CHRIN() ubyte blocks_lsb = cbm.CHRIN()
ubyte blocks_msb = cbm.CHRIN() ubyte blocks_msb = cbm.CHRIN()
@@ -460,7 +460,7 @@ byte_read_loop: ; fallback if MACPTR isn't supported on the device
return total_read return total_read
} }
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y, ubyte @A { asmsub f_readline(^^ubyte bufptr @AY) clobbers(X) -> ubyte @Y, ubyte @A {
; Routine to read text lines from a text file. Lines must be less than 255 characters. ; Routine to read text lines from a text file. Lines must be less than 255 characters.
; Reads characters from the input file UNTIL a newline or return character, or 0 byte (likely EOF). ; Reads characters from the input file UNTIL a newline or return character, or 0 byte (likely EOF).
; The line read will be 0-terminated in the buffer (and not contain the end of line character). ; The line read will be 0-terminated in the buffer (and not contain the end of line character).
@@ -590,7 +590,7 @@ return_status:
; ---- other functions ---- ; ---- other functions ----
sub status() -> uword { sub status() -> str {
; -- retrieve the disk drive's current status message ; -- retrieve the disk drive's current status message
; TODO this doesn't seem to work reliably, sometimes READST returns 128 when the drive is just fine ; TODO this doesn't seem to work reliably, sometimes READST returns 128 when the drive is just fine
@@ -601,7 +601,7 @@ return_status:
; return device_not_present_error ; return device_not_present_error
; } ; }
uword messageptr = &list_filename ^^ubyte messageptr = &list_filename
cbm.SETNAM(0, list_filename) cbm.SETNAM(0, list_filename)
cbm.SETLFS(15, drivenumber, 15) cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN() ; open 15,8,15 void cbm.OPEN() ; open 15,8,15
@@ -663,16 +663,16 @@ io_error:
; saves a block of memory to disk, including the default 2 byte prg header. ; saves a block of memory to disk, including the default 2 byte prg header.
sub save(uword filenameptr, uword startaddress, uword savesize) -> bool { sub save(str filenameptr, uword startaddress, uword savesize) -> bool {
return internal_save_routine(filenameptr, startaddress, savesize, false) return internal_save_routine(filenameptr, startaddress, savesize, false)
} }
; like save() but omits the 2 byte prg header. ; like save() but omits the 2 byte prg header.
sub save_raw(uword filenameptr, uword startaddress, uword savesize) -> bool { sub save_raw(str filenameptr, uword startaddress, uword savesize) -> bool {
return internal_save_routine(filenameptr, startaddress, savesize, true) return internal_save_routine(filenameptr, startaddress, savesize, true)
} }
sub internal_save_routine(uword filenameptr, uword startaddress, uword savesize, bool headerless) -> bool { sub internal_save_routine(str filenameptr, uword startaddress, uword savesize, bool headerless) -> bool {
cbm.SETNAM(strings.length(filenameptr), filenameptr) cbm.SETNAM(strings.length(filenameptr), filenameptr)
cbm.SETLFS(1, drivenumber, 0) cbm.SETLFS(1, drivenumber, 0)
uword @shared end_address = startaddress + savesize uword @shared end_address = startaddress + savesize
@@ -719,25 +719,25 @@ io_error:
; the return address will still be one higher. Which means, because the Kernal ; the return address will still be one higher. Which means, because the Kernal
; load routine is bank-aware, it will return $a000 and will have switched to the next hiram bank! ; load routine is bank-aware, it will return $a000 and will have switched to the next hiram bank!
; So you'll have to reset the ram bank with cx16.rambank() to switch back to the bank that the data was put in. ; So you'll have to reset the ram bank with cx16.rambank() to switch back to the bank that the data was put in.
sub load(uword filenameptr, uword address_override) -> uword { sub load(str filenameptr, uword address_override) -> uword {
return internal_load_routine(filenameptr, address_override, false) return internal_load_routine(filenameptr, address_override, false)
} }
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file. ; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
; No program header is assumed in the file. Everything is loaded. ; No program header is assumed in the file. Everything is loaded.
; See comments on load() for more details. Including the banking behavior on the X16. ; See comments on load() for more details. Including the banking behavior on the X16.
sub load_raw(uword filenameptr, uword startaddress) -> uword { sub load_raw(str filenameptr, uword startaddress) -> uword {
return internal_load_routine(filenameptr, startaddress, true) return internal_load_routine(filenameptr, startaddress, true)
} }
; Load a prog8 compiled library binary blob at the given location into memory. ; Load a prog8 compiled library binary blob at the given location into memory.
sub loadlib(uword libnameptr, uword libaddress) -> uword { sub loadlib(str libnameptr, uword libaddress) -> uword {
return internal_load_routine(libnameptr, libaddress, true) return internal_load_routine(libnameptr, libaddress, true)
} }
sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword { sub internal_load_routine(str filenameptr, uword address_override, bool headerless) -> uword {
cbm.SETNAM(strings.length(filenameptr), filenameptr) cbm.SETNAM(strings.length(filenameptr), filenameptr)
ubyte secondary = 1 ubyte secondary = 1
cx16.r1 = 0 cx16.r1 = 0
@@ -760,7 +760,7 @@ io_error:
return cx16.r1 return cx16.r1
} }
sub delete(uword filenameptr) { sub delete(str filenameptr) {
; -- delete a file on the drive ; -- delete a file on the drive
list_filename[0] = 's' list_filename[0] = 's'
list_filename[1] = ':' list_filename[1] = ':'
@@ -772,7 +772,7 @@ io_error:
cbm.CLOSE(1) cbm.CLOSE(1)
} }
sub rename(uword oldfileptr, uword newfileptr) { sub rename(str oldfileptr, str newfileptr) {
; -- rename a file on the drive ; -- rename a file on the drive
list_filename[0] = 'r' list_filename[0] = 'r'
list_filename[1] = ':' list_filename[1] = ':'
@@ -786,7 +786,7 @@ io_error:
cbm.CLOSE(1) cbm.CLOSE(1)
} }
sub send_command(uword commandptr) { sub send_command(str commandptr) {
; -- send a dos command to the drive (don't read any response) ; -- send a dos command to the drive (don't read any response)
cbm.SETNAM(strings.length(commandptr), commandptr) cbm.SETNAM(strings.length(commandptr), commandptr)
cbm.SETLFS(15, drivenumber, 15) cbm.SETLFS(15, drivenumber, 15)
@@ -906,7 +906,7 @@ internal_vload:
send_command(list_filename) send_command(list_filename)
} }
sub curdir() -> uword { sub curdir() -> str {
; return current directory name or 0 if error ; return current directory name or 0 if error
; special X16 dos command to only return the current path in the entry list (R42+) ; special X16 dos command to only return the current path in the entry list (R42+)
const ubyte MAX_PATH_LEN=80 const ubyte MAX_PATH_LEN=80

View File

@@ -47,6 +47,12 @@ emudbg {
} }
} }
sub console_nl() {
; write a newline to the debug output console.
; because '\n' gets encoded in the x16 value for it (13) which is different than what the shell expects (10).
console_chrout(10)
}
sub console_chrout(ubyte char) { sub console_chrout(ubyte char) {
; note: make sure the character is in Iso encoding. ; note: make sure the character is in Iso encoding.
if is_emulator() if is_emulator()

View File

@@ -21,8 +21,8 @@ extsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 'facmo'
; (tip: use GIVAYFAY to use A/Y input; lo/hi switched to normal order) ; (tip: use GIVAYFAY to use A/Y input; lo/hi switched to normal order)
extsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y) extsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
extsub $fe06 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY extsub $fe06 = FOUT() clobbers(X) -> str @ AY ; fac1 -> string, address returned in AY
extsub $fe09 = VAL_1(uword string @XY, ubyte length @A) clobbers(A,X,Y) -> float @FAC1 ; convert ASCII string in XY and length in A, to floating point in FAC1. WARNING: only implemented in ROM 47+. Safer to use floats.parse() instead. extsub $fe09 = VAL_1(str string @XY, ubyte length @A) clobbers(A,X,Y) -> float @FAC1 ; convert ASCII string in XY and length in A, to floating point in FAC1. WARNING: only implemented in ROM 47+. Safer to use floats.parse() instead.
; GETADR: fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15) ; GETADR: fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order) ; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order)

View File

@@ -825,7 +825,7 @@ set_byte:
cx16.screen_set_charset(charset, 0) cx16.screen_set_charset(charset, 0)
} }
sub text(uword @zp xx, uword yy, ubyte color, uword sctextptr) { sub text(uword @zp xx, uword yy, ubyte color, str sctextptr) {
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!). ; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
; You must also have called text_charset() first to select and prepare the character set to use. ; You must also have called text_charset() first to select and prepare the character set to use.
uword chardataptr uword chardataptr

View File

@@ -743,7 +743,7 @@ skip:
const ubyte charset_bank = $1 const ubyte charset_bank = $1
const uword charset_addr = $f000 ; in bank 1, so $1f000 const uword charset_addr = $f000 ; in bank 1, so $1f000
sub text(uword @zp xx, uword yy, ubyte color, uword textptr) { sub text(uword @zp xx, uword yy, ubyte color, str textptr) {
; -- Write some text at the given pixel position. The text string must be in an encoding approprite for the charset. ; -- Write some text at the given pixel position. The text string must be in an encoding approprite for the charset.
; You must also have called text_charset() first to select and prepare the character set to use. ; You must also have called text_charset() first to select and prepare the character set to use.
uword chardataptr uword chardataptr

View File

@@ -89,18 +89,18 @@ sprites {
sprite_reg = VERA_SPRITEREGS + 2 + first_spritenum*$0008 sprite_reg = VERA_SPRITEREGS + 2 + first_spritenum*$0008
cx16.vaddr_autoincr(1, sprite_reg, 0, 8) cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8) cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
repeat num_sprites { tovera(xpositions_ptr)
cx16.VERA_DATA0 = @(xpositions_ptr)
cx16.VERA_DATA1 = @(xpositions_ptr+num_sprites)
xpositions_ptr ++
}
sprite_reg += 2 sprite_reg += 2
cx16.vaddr_autoincr(1, sprite_reg, 0, 8) cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8) cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
repeat num_sprites { tovera(ypositions_ptr)
cx16.VERA_DATA0 = @(ypositions_ptr)
cx16.VERA_DATA1 = @(ypositions_ptr+num_sprites) sub tovera(uword positions @R0) {
ypositions_ptr ++ repeat num_sprites {
cx16.VERA_DATA0 = @(cx16.r0)
cx16.VERA_DATA1 = @(cx16.r0+num_sprites)
cx16.r0 ++
}
} }
} }
@@ -109,20 +109,19 @@ sprites {
sprite_reg = VERA_SPRITEREGS + 2 + first_spritenum*$0008 sprite_reg = VERA_SPRITEREGS + 2 + first_spritenum*$0008
cx16.vaddr_autoincr(1, sprite_reg, 0, 8) cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8) cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
repeat num_sprites { tovera(xpositions_ptr)
cx16.VERA_DATA0 = @(xpositions_ptr)
xpositions_ptr ++
cx16.VERA_DATA1 = @(xpositions_ptr)
xpositions_ptr ++
}
sprite_reg += 2 sprite_reg += 2
cx16.vaddr_autoincr(1, sprite_reg, 0, 8) cx16.vaddr_autoincr(1, sprite_reg, 0, 8)
cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8) cx16.vaddr_autoincr(1, sprite_reg+1, 1, 8)
repeat num_sprites { tovera(ypositions_ptr)
cx16.VERA_DATA0 = @(ypositions_ptr)
ypositions_ptr ++ sub tovera(uword positions @R0) {
cx16.VERA_DATA1 = @(ypositions_ptr) repeat num_sprites {
ypositions_ptr ++ cx16.VERA_DATA0 = @(cx16.r0)
cx16.r0 ++
cx16.VERA_DATA1 = @(cx16.r0)
cx16.r0 ++
}
} }
} }

View File

@@ -1470,7 +1470,7 @@ sub search_x16edit() -> ubyte {
}} }}
} }
sub set_program_args(uword args_ptr, ubyte args_size) { sub set_program_args(str args_ptr, ubyte args_size) {
; -- Set the inter-program arguments. ; -- Set the inter-program arguments.
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff. ; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
; see https://github.com/X16Community/x16-docs/blob/101759f3bfa5e6cce4e8c5a0b67cb0f2f1c6341e/X16%20Reference%20-%2008%20-%20Memory%20Map.md#bank-0 ; see https://github.com/X16Community/x16-docs/blob/101759f3bfa5e6cce4e8c5a0b67cb0f2f1c6341e/X16%20Reference%20-%2008%20-%20Memory%20Map.md#bank-0
@@ -1482,7 +1482,7 @@ sub search_x16edit() -> ubyte {
rambank(sys.pop()) rambank(sys.pop())
} }
asmsub get_program_args(uword buffer @R0, ubyte buf_size @R1, bool binary @Pc) { asmsub get_program_args(^^ubyte buffer @R0, ubyte buf_size @R1, bool binary @Pc) {
; -- Retrieve the inter-program arguments. If binary=false, it treats them as a string (stops copying at first zero). ; -- Retrieve the inter-program arguments. If binary=false, it treats them as a string (stops copying at first zero).
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff. ; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
; see https://github.com/X16Community/x16-docs/blob/101759f3bfa5e6cce4e8c5a0b67cb0f2f1c6341e/X16%20Reference%20-%2008%20-%20Memory%20Map.md#bank-0 ; see https://github.com/X16Community/x16-docs/blob/101759f3bfa5e6cce4e8c5a0b67cb0f2f1c6341e/X16%20Reference%20-%2008%20-%20Memory%20Map.md#bank-0
@@ -1560,6 +1560,7 @@ sys {
const ubyte SIZEOF_WORD = sizeof(word) const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = sizeof(uword) const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_LONG = sizeof(long) const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
const ubyte SIZEOF_FLOAT = sizeof(float) const ubyte SIZEOF_FLOAT = sizeof(float)
const byte MIN_BYTE = -128 const byte MIN_BYTE = -128
const byte MAX_BYTE = 127 const byte MAX_BYTE = 127
@@ -1758,7 +1759,7 @@ _loop lda P8ZP_SCRATCH_W1
}} }}
} }
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) { asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
; Called when the compiler wants to assign a string value to another string. ; Called when the compiler wants to assign a string value to another string.
%asm {{ %asm {{
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1

View File

@@ -17,26 +17,6 @@ const long VERA_TEXTMATRIX = $1b000
extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character. extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
sub clear_screen() {
chrout(147)
}
sub cls() {
chrout(147)
}
sub home() {
chrout(19)
}
sub nl() {
chrout('\n')
}
sub spc() {
chrout(' ')
}
sub bell() { sub bell() {
chrout(7) chrout(7)
} }

View File

@@ -119,6 +119,7 @@ verafx {
; Returns the 16 bits unsigned result of R0*R1 in AY. ; Returns the 16 bits unsigned result of R0*R1 in AY.
; Note: only the lower 16 bits! (the upper 16 bits are not valid for unsigned word multiplications, only for signed) ; Note: only the lower 16 bits! (the upper 16 bits are not valid for unsigned word multiplications, only for signed)
; Verafx doesn't support unsigned values like this for full 32 bit result. ; Verafx doesn't support unsigned values like this for full 32 bit result.
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
%asm {{ %asm {{
lda cx16.r0 lda cx16.r0
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
@@ -136,6 +137,7 @@ verafx {
asmsub muls(word value1 @R0, word value2 @R1) clobbers(X) -> word @AY, word @R0 { asmsub muls(word value1 @R0, word value2 @R1) clobbers(X) -> word @AY, word @R0 {
; Returns the 32 bits signed result in AY and R0 (lower word, upper word). ; Returns the 32 bits signed result in AY and R0 (lower word, upper word).
; Vera Fx multiplication support only works on signed values! ; Vera Fx multiplication support only works on signed values!
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
%asm {{ %asm {{
lda #(2 << 1) lda #(2 << 1)
sta cx16.VERA_CTRL ; $9F25 sta cx16.VERA_CTRL ; $9F25

View File

@@ -6,7 +6,7 @@ cx16logo {
%option ignore_unused %option ignore_unused
sub logo_at(ubyte column, ubyte row) { sub logo_at(ubyte column, ubyte row) {
uword strptr ^^ubyte strptr
for strptr in logo_lines { for strptr in logo_lines {
txt.plot(column, row) txt.plot(column, row)
txt.print(strptr) txt.print(strptr)
@@ -15,7 +15,7 @@ cx16logo {
} }
sub logo() { sub logo() {
uword strptr ^^ubyte strptr
for strptr in logo_lines { for strptr in logo_lines {
txt.print(strptr) txt.print(strptr)
txt.nl() txt.nl()

View File

@@ -547,7 +547,7 @@ log2_tab
}} }}
} }
sub crc16(uword data, uword length) -> uword { sub crc16(^^ubyte data, uword length) -> uword {
; calculates the CRC16 (XMODEM) checksum of the buffer. ; calculates the CRC16 (XMODEM) checksum of the buffer.
; There are also "streaming" crc16_start/update/end routines below, that allow you to calculate crc16 for data that doesn't fit in a single memory block. ; There are also "streaming" crc16_start/update/end routines below, that allow you to calculate crc16 for data that doesn't fit in a single memory block.
crc16_start() crc16_start()
@@ -602,7 +602,7 @@ log2_tab
return cx16.r15 return cx16.r15
} }
sub crc32(uword data, uword length) { sub crc32(^^ubyte data, uword length) {
; Calculates the CRC-32 (ISO-HDLC/PKZIP) checksum of the buffer. ; Calculates the CRC-32 (ISO-HDLC/PKZIP) checksum of the buffer.
; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words. ; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words.
; result stored in cx16.r14 (low word) and cx16.r15 (high word) ; result stored in cx16.r14 (low word) and cx16.r15 (high word)

View File

@@ -104,6 +104,7 @@ sys {
const ubyte SIZEOF_WORD = sizeof(word) const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = sizeof(uword) const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_LONG = sizeof(long) const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
const ubyte SIZEOF_FLOAT = sizeof(float) const ubyte SIZEOF_FLOAT = sizeof(float)
const byte MIN_BYTE = -128 const byte MIN_BYTE = -128
const byte MAX_BYTE = 127 const byte MAX_BYTE = 127
@@ -162,7 +163,7 @@ _loop lda P8ZP_SCRATCH_W1
}} }}
} }
asmsub internal_stringcopy(uword source @R0, uword target @AY) clobbers (A,Y) { asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
; Called when the compiler wants to assign a string value to another string. ; Called when the compiler wants to assign a string value to another string.
%asm {{ %asm {{
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1

View File

@@ -15,26 +15,6 @@ const ubyte DEFAULT_HEIGHT = 25
extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character. extsub $FFD2 = chrout(ubyte character @ A) ; for consistency. You can also use cbm.CHROUT directly ofcourse. Note: takes a PETSCII encoded character.
sub clear_screen() {
chrout(147)
}
sub cls() {
chrout(147)
}
sub home() {
chrout(19)
}
sub nl() {
chrout('\n')
}
sub spc() {
chrout(' ')
}
sub bell() { sub bell() {
chrout(7) chrout(7)
} }
@@ -225,20 +205,16 @@ sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored) {
asmsub plot (ubyte col @ Y, ubyte row @ X) { asmsub plot (ubyte col @ Y, ubyte row @ X) {
%asm {{ %asm {{
jsr home stx $d8
cpy #0 txa
beq + asl a
- lda #29 tax
jsr chrout lda setchr._screenrows,x
dey sta $c4
bne - lda setchr._screenrows+1,x
+ cpx #0 sta $c5
beq + sty $c6
- lda #17 rts
jsr chrout
dex
bne -
+ rts
}} }}
} }

View File

@@ -401,8 +401,17 @@ _loop_hi ldy _index_first
.pend .pend
func_peek .proc
; -- read the byte value on the address in AY, into A
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
lda (P8ZP_SCRATCH_W1),y
rts
.pend
func_peekw .proc func_peekw .proc
; -- read the word value on the address in AY ; -- read the word value on the address in AY, into AY
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1 sty P8ZP_SCRATCH_W1+1
ldy #0 ldy #0

View File

@@ -101,7 +101,7 @@ io_error:
} }
sub diskname() -> uword { sub diskname() -> str {
; -- Returns pointer to disk name string or 0 if failure. ; -- Returns pointer to disk name string or 0 if failure.
cbm.SETNAM(1, "$") cbm.SETNAM(1, "$")
@@ -145,7 +145,7 @@ io_error:
; internal variables for the iterative file lister / loader ; internal variables for the iterative file lister / loader
bool list_skip_disk_name bool list_skip_disk_name
uword list_pattern ^^ubyte list_pattern
uword list_blocks uword list_blocks
bool iteration_in_progress = false bool iteration_in_progress = false
bool write_iteration_in_progress = false bool write_iteration_in_progress = false
@@ -154,7 +154,7 @@ io_error:
; ----- get a list of files (uses iteration functions internally) ----- ; ----- get a list of files (uses iteration functions internally) -----
sub list_filenames(uword pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte { sub list_filenames(str pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte {
; -- fill the provided buffer with the names of the files on the disk (until buffer is full). ; -- fill the provided buffer with the names of the files on the disk (until buffer is full).
; Files in the buffer are separated by a 0 byte. You can provide an optional pattern to match against. ; Files in the buffer are separated by a 0 byte. You can provide an optional pattern to match against.
; After the last filename one additional 0 byte is placed to indicate the end of the list. ; After the last filename one additional 0 byte is placed to indicate the end of the list.
@@ -163,7 +163,7 @@ io_error:
; Note that no list of pointers of some form is returned, the names are just squashed together. ; Note that no list of pointers of some form is returned, the names are just squashed together.
; If you really need a list of pointers to the names, that is pretty straightforward to construct by iterating over the names ; If you really need a list of pointers to the names, that is pretty straightforward to construct by iterating over the names
; and registering when the next one starts after the 0-byte separator. ; and registering when the next one starts after the 0-byte separator.
uword buffer_start = filenames_buffer uword buffer_start = filenames_buffer
ubyte files_found = 0 ubyte files_found = 0
if lf_start_list(pattern_ptr) { if lf_start_list(pattern_ptr) {
while lf_next_entry() { while lf_next_entry() {
@@ -187,7 +187,7 @@ io_error:
; ----- iterative file lister functions (uses the read io channel) ----- ; ----- iterative file lister functions (uses the read io channel) -----
sub lf_start_list(uword pattern_ptr) -> bool { sub lf_start_list(str pattern_ptr) -> bool {
; -- start an iterative file listing with optional pattern matching. ; -- start an iterative file listing with optional pattern matching.
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
cbm.SETNAM(1, "$") cbm.SETNAM(1, "$")
@@ -217,7 +217,7 @@ io_error:
return false return false
} }
sub lf_start_list_dirs(uword pattern_ptr) -> bool { sub lf_start_list_dirs(str pattern_ptr) -> bool {
; -- start an iterative directory contents listing with optional pattern matching. ; -- start an iterative directory contents listing with optional pattern matching.
; this version it only returns directory entries! ; this version it only returns directory entries!
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
@@ -225,7 +225,7 @@ io_error:
goto diskio.lf_start_list.start_list_internal goto diskio.lf_start_list.start_list_internal
} }
sub lf_start_list_files(uword pattern_ptr) -> bool { sub lf_start_list_files(str pattern_ptr) -> bool {
; -- start an iterative directory contents listing with optional pattern matching. ; -- start an iterative directory contents listing with optional pattern matching.
; this version only returns actual file entries! ; this version only returns actual file entries!
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
@@ -244,7 +244,7 @@ io_error:
repeat { repeat {
reset_read_channel() ; use the input io channel again reset_read_channel() ; use the input io channel again
uword nameptr = &list_filename ^^ubyte nameptr = &list_filename
ubyte blocks_lsb = cbm.CHRIN() ubyte blocks_lsb = cbm.CHRIN()
ubyte blocks_msb = cbm.CHRIN() ubyte blocks_msb = cbm.CHRIN()
@@ -311,7 +311,7 @@ close_end:
; ----- iterative file loader functions (uses the input io channel) ----- ; ----- iterative file loader functions (uses the input io channel) -----
sub f_open(uword filenameptr) -> bool { sub f_open(str filenameptr) -> bool {
; -- open a file for iterative reading with f_read ; -- open a file for iterative reading with f_read
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
; Returns true if the file is successfully opened and readable. ; Returns true if the file is successfully opened and readable.
@@ -413,8 +413,8 @@ close_end:
return total_read return total_read
} }
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y, ubyte @A { asmsub f_readline(^^ubyte bufptr @AY) clobbers(X) -> ubyte @Y, ubyte @A {
; Routine to read text lines from a text file. Lines must be less than 255 characters. ; Routine to read a text line from a text file. Lines must be less than 255 characters.
; Reads characters from the input file UNTIL a newline or return character, or 0 byte (likely EOF). ; Reads characters from the input file UNTIL a newline or return character, or 0 byte (likely EOF).
; The line read will be 0-terminated in the buffer (and not contain the end of line character). ; The line read will be 0-terminated in the buffer (and not contain the end of line character).
; The length of the line is returned in Y. Note that an empty line is okay and is length 0! ; The length of the line is returned in Y. Note that an empty line is okay and is length 0!
@@ -461,7 +461,7 @@ _end jsr cbm.READST
; ----- iterative file writing functions (uses write io channel) ----- ; ----- iterative file writing functions (uses write io channel) -----
sub f_open_w(uword filenameptr) -> bool { sub f_open_w(str filenameptr) -> bool {
; -- open a file for iterative writing with f_write ; -- open a file for iterative writing with f_write
; WARNING: returns true if the open command was received by the device, ; WARNING: returns true if the open command was received by the device,
; but this can still mean the file wasn't successfully opened for writing! ; but this can still mean the file wasn't successfully opened for writing!
@@ -516,7 +516,7 @@ _end jsr cbm.READST
; ---- other functions ---- ; ---- other functions ----
sub status() -> uword { sub status() -> str {
; -- retrieve the disk drive's current status message ; -- retrieve the disk drive's current status message
; TODO this doesn't seem to work reliably, sometimes READST returns 128 when the drive is just fine ; TODO this doesn't seem to work reliably, sometimes READST returns 128 when the drive is just fine
@@ -527,7 +527,7 @@ _end jsr cbm.READST
; return device_not_present_error ; return device_not_present_error
; } ; }
uword messageptr = &list_filename ^^ubyte messageptr = &list_filename
cbm.SETNAM(0, list_filename) cbm.SETNAM(0, list_filename)
cbm.SETLFS(15, drivenumber, 15) cbm.SETLFS(15, drivenumber, 15)
void cbm.OPEN() ; open 15,8,15 void cbm.OPEN() ; open 15,8,15
@@ -586,7 +586,7 @@ io_error:
return 255 return 255
} }
sub save(uword filenameptr, uword start_address, uword savesize) -> bool { sub save(str filenameptr, uword start_address, uword savesize) -> bool {
cbm.SETNAM(strings.length(filenameptr), filenameptr) cbm.SETNAM(strings.length(filenameptr), filenameptr)
cbm.SETLFS(1, drivenumber, 0) cbm.SETLFS(1, drivenumber, 0)
uword @shared end_address = start_address + savesize uword @shared end_address = start_address + savesize
@@ -617,7 +617,7 @@ io_error:
; If you specify a custom address_override, the first 2 bytes in the file are ignored ; If you specify a custom address_override, the first 2 bytes in the file are ignored
; and the rest is loaded at the given location in memory. ; and the rest is loaded at the given location in memory.
; Returns the end load address+1 if successful or 0 if a load error occurred. ; Returns the end load address+1 if successful or 0 if a load error occurred.
sub load(uword filenameptr, uword address_override) -> uword { sub load(str filenameptr, uword address_override) -> uword {
cbm.SETNAM(strings.length(filenameptr), filenameptr) cbm.SETNAM(strings.length(filenameptr), filenameptr)
ubyte secondary = 1 ubyte secondary = 1
cx16.r1 = 0 cx16.r1 = 0
@@ -641,7 +641,7 @@ io_error:
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file. ; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
; No program header is assumed in the file. Everything is loaded. ; No program header is assumed in the file. Everything is loaded.
; See comments on load() for more details. ; See comments on load() for more details.
sub load_raw(uword filenameptr, uword start_address) -> uword { sub load_raw(str filenameptr, uword start_address) -> uword {
; read the 2 header bytes separately to skip them ; read the 2 header bytes separately to skip them
if not f_open(filenameptr) if not f_open(filenameptr)
return 0 return 0
@@ -654,12 +654,12 @@ io_error:
} }
; Load a prog8 compiled library binary blob at the given location into memory. ; Load a prog8 compiled library binary blob at the given location into memory.
sub loadlib(uword libnameptr, uword libaddress) -> uword { sub loadlib(str libnameptr, uword libaddress) -> uword {
return load(libnameptr, libaddress) return load(libnameptr, libaddress)
} }
sub delete(uword filenameptr) { sub delete(str filenameptr) {
; -- delete a file on the drive ; -- delete a file on the drive
list_filename[0] = 's' list_filename[0] = 's'
list_filename[1] = ':' list_filename[1] = ':'
@@ -671,7 +671,7 @@ io_error:
cbm.CLOSE(1) cbm.CLOSE(1)
} }
sub rename(uword oldfileptr, uword newfileptr) { sub rename(str oldfileptr, str newfileptr) {
; -- rename a file on the drive ; -- rename a file on the drive
list_filename[0] = 'r' list_filename[0] = 'r'
list_filename[1] = ':' list_filename[1] = ':'
@@ -696,7 +696,7 @@ io_error:
return false return false
} }
sub send_command(uword commandptr) { sub send_command(str commandptr) {
; -- send a dos command to the drive ; -- send a dos command to the drive
cbm.SETNAM(strings.length(commandptr), commandptr) cbm.SETNAM(strings.length(commandptr), commandptr)
cbm.SETLFS(15, drivenumber, 15) cbm.SETLFS(15, drivenumber, 15)

View File

@@ -3,6 +3,35 @@ txt {
%option merge, no_symbol_prefixing, ignore_unused %option merge, no_symbol_prefixing, ignore_unused
sub clear_screen() {
chrout(147)
}
sub cls() {
chrout(147)
}
sub home() {
chrout(19)
}
sub nl() {
chrout('\n')
}
sub spc() {
chrout(' ')
}
sub rvs_on() {
txt.chrout(18)
}
sub rvs_off() {
txt.chrout(146)
}
asmsub print (str text @ AY) clobbers(A,Y) { asmsub print (str text @ AY) clobbers(A,Y) {
; ---- print zero terminated string, in PETSCII encoding, from A/Y ; ---- print zero terminated string, in PETSCII encoding, from A/Y
; note: the compiler contains an optimization that will replace ; note: the compiler contains an optimization that will replace
@@ -136,7 +165,7 @@ _allzero lda #'0'
}} }}
} }
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y { asmsub input_chars (^^ubyte buffer @ AY) clobbers(A) -> ubyte @ Y {
; ---- Input a string (max. 80 chars) from the keyboard, in PETSCII encoding. ; ---- Input a string (max. 80 chars) from the keyboard, in PETSCII encoding.
; Returns length in Y. (string is terminated with a 0 byte as well) ; Returns length in Y. (string is terminated with a 0 byte as well)
; It assumes the keyboard is selected as I/O channel! ; It assumes the keyboard is selected as I/O channel!

View File

@@ -108,7 +108,7 @@ strings {
repeat cx16.r2L { repeat cx16.r2L {
if startswith(cx16.r3, needle) { if startswith(cx16.r3, needle) {
sys.set_carry() sys.set_carry()
return cx16.r3-haystack as ubyte return cx16.r3-(haystack as uword) as ubyte
} }
cx16.r3++ cx16.r3++
} }

View File

@@ -10,7 +10,7 @@ sorting {
; NOTE: sorting is done in ascending order!!! ; NOTE: sorting is done in ascending order!!!
; Note: could be made slightly faster by using modifying dcode for the CPY after _loop but that compromises romability ; Note: could be made slightly faster by using modifying dcode for the CPY after _loop but that compromises romability
asmsub gnomesort_ub(uword bytearray @AY, ubyte num_elements @X) { asmsub gnomesort_ub(^^ubyte bytearray @AY, ubyte num_elements @X) {
%asm {{ %asm {{
stx P8ZP_SCRATCH_REG stx P8ZP_SCRATCH_REG
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
@@ -43,21 +43,21 @@ _done
}} }}
} }
sub gnomesort_by_ub(uword @requirezp uw_keys, uword values, ubyte num_elements) { sub gnomesort_by_ub(^^ubyte @requirezp ub_keys, ^^uword wordvalues, ubyte num_elements) {
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse). ; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
ubyte @zp pos=1 ubyte @zp pos=1
while pos != num_elements { while pos != num_elements {
if uw_keys[pos]>=uw_keys[pos-1] if ub_keys[pos]>=ub_keys[pos-1]
pos++ pos++
else { else {
; swap elements ; swap elements
cx16.r0L = uw_keys[pos-1] cx16.r0L = ub_keys[pos-1]
uw_keys[pos-1] = uw_keys[pos] ub_keys[pos-1] = ub_keys[pos]
uw_keys[pos] = cx16.r0L ub_keys[pos] = cx16.r0L
uword @requirezp vptr = values + pos*$0002 -2 ^^uword @requirezp vptr = wordvalues + (pos-1)
cx16.r0 = peekw(vptr) cx16.r0 = peekw(vptr)
pokew(vptr, peekw(vptr+2)) pokew(vptr, peekw(vptr+1))
pokew(vptr+2, cx16.r0) pokew(vptr+1, cx16.r0)
pos-- pos--
if_z if_z
@@ -66,20 +66,20 @@ _done
} }
} }
sub gnomesort_uw(uword @requirezp values, ubyte num_elements) { sub gnomesort_uw(uword @requirezp wordvalues, ubyte num_elements) {
; Sorts the values array (no-split unsigned words). ; Sorts the wordvalues array (no-split unsigned words).
; Max number of elements is 128. Clobbers R0 and R1. ; Max number of elements is 128 to keep indexing code size small and fast. Clobbers R0 and R1.
ubyte @zp pos=2 ubyte @zp pos=2
num_elements *= 2 num_elements *= 2
while pos != num_elements { while pos != num_elements {
cx16.r1L = pos-2 cx16.r1L = pos-2
if peekw(values+pos) >= peekw(values + cx16.r1L) if peekw(wordvalues+pos) >= peekw(wordvalues + cx16.r1L)
pos += 2 pos += 2
else { else {
; swap elements ; swap elements
cx16.r0 = peekw(values + cx16.r1L) cx16.r0 = peekw(wordvalues + cx16.r1L)
pokew(values + cx16.r1L, peekw(values + pos)) pokew(wordvalues + cx16.r1L, peekw(wordvalues + pos))
pokew(values + pos, cx16.r0) pokew(wordvalues + pos, cx16.r0)
pos-=2 pos-=2
if_z if_z
pos+=2 pos+=2
@@ -90,7 +90,7 @@ _done
sub gnomesort_by_uw(uword @requirezp uw_keys, uword wordvalues, ubyte num_elements) { sub gnomesort_by_uw(uword @requirezp uw_keys, uword wordvalues, ubyte num_elements) {
; Sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse). ; Sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
; both arrays should be no-split array of words. uw_keys are unsigned. ; both arrays should be no-split array of words. uw_keys are unsigned.
; Max number of elements is 128. Clobbers R0 and R1. ; Max number of elements is 128 to keep indexing code size small and fast. Clobbers R0 and R1.
ubyte @zp pos=2 ubyte @zp pos=2
num_elements *= 2 num_elements *= 2
while pos != num_elements { while pos != num_elements {
@@ -115,7 +115,7 @@ _done
; gnomesort_pointers is not worth it over shellshort_pointers. ; gnomesort_pointers is not worth it over shellshort_pointers.
sub shellsort_ub(uword @requirezp values, ubyte num_elements) { sub shellsort_ub(^^ubyte @requirezp values, ubyte num_elements) {
; sorts the values array (unsigned bytes). ; sorts the values array (unsigned bytes).
num_elements-- num_elements--
ubyte @zp gap ubyte @zp gap
@@ -138,6 +138,7 @@ _done
} }
} }
; TODO convert to ^^uword once code size regression is fixed
sub shellsort_uw(uword @requirezp values, ubyte num_elements) { sub shellsort_uw(uword @requirezp values, ubyte num_elements) {
; sorts the values array (no-split unsigned words). ; sorts the values array (no-split unsigned words).
num_elements-- num_elements--
@@ -160,7 +161,8 @@ _done
} }
} }
sub shellsort_by_ub(uword @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) { ; TODO convert to ^^uword once code size regression is fixed
sub shellsort_by_ub(^^ubyte @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) {
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse). ; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
num_elements-- num_elements--
ubyte @zp gap ubyte @zp gap
@@ -186,6 +188,7 @@ _done
} }
} }
; TODO convert to ^^uword once code size regression is fixed
sub shellsort_by_uw(uword @requirezp uw_keys, uword @requirezp wordvalues, ubyte num_elements) { sub shellsort_by_uw(uword @requirezp uw_keys, uword @requirezp wordvalues, ubyte num_elements) {
; sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse). ; sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
; both arrays should be no-split array of words. uw_keys are unsigned. ; both arrays should be no-split array of words. uw_keys are unsigned.
@@ -236,7 +239,7 @@ _done
} }
} }
asmsub string_comparator(uword string1 @R0, uword string2 @R1) -> bool @Pc { asmsub string_comparator(str string1 @R0, str string2 @R1) -> bool @Pc {
; R0 and R1 are the two strings, must return Carry=1 when R0<=R1, else Carry=0 ; R0 and R1 are the two strings, must return Carry=1 when R0<=R1, else Carry=0
%asm {{ %asm {{
lda cx16.r1L lda cx16.r1L

View File

@@ -5,7 +5,7 @@
strings { strings {
%option no_symbol_prefixing, ignore_unused %option no_symbol_prefixing, ignore_unused
asmsub length(uword string @AY) clobbers(A) -> ubyte @Y { asmsub length(str string @AY) clobbers(A) -> ubyte @Y {
; Returns the number of bytes in the string. ; Returns the number of bytes in the string.
; This value is determined during runtime and counts upto the first terminating 0 byte in the string, ; This value is determined during runtime and counts upto the first terminating 0 byte in the string,
; regardless of the size of the string during compilation time. Dont confuse this with len and sizeof! ; regardless of the size of the string during compilation time. Dont confuse this with len and sizeof!
@@ -23,7 +23,7 @@ strings {
}} }}
} }
asmsub left(uword source @AX, ubyte length @Y, uword target @R1) clobbers(A, Y) { asmsub left(str source @AX, ubyte length @Y, str target @R1) clobbers(A, Y) {
; Copies the left side of the source string of the given length to target string. ; Copies the left side of the source string of the given length to target string.
; It is assumed the target string buffer is large enough to contain the result. ; It is assumed the target string buffer is large enough to contain the result.
; Also, you have to make sure yourself that length is smaller or equal to the length of the source string. ; Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
@@ -50,7 +50,7 @@ _loop dey
}} }}
} }
asmsub right(uword source @AY, ubyte length @X, uword target @R1) clobbers(A,Y) { asmsub right(str source @AY, ubyte length @X, str target @R1) clobbers(A,Y) {
; Copies the right side of the source string of the given length to target string. ; Copies the right side of the source string of the given length to target string.
; It is assumed the target string buffer is large enough to contain the result. ; It is assumed the target string buffer is large enough to contain the result.
; Also, you have to make sure yourself that length is smaller or equal to the length of the source string. ; Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
@@ -89,7 +89,7 @@ _loop dey
}} }}
} }
asmsub slice(uword source @R0, ubyte start @A, ubyte length @Y, uword target @R1) clobbers(A, Y) { asmsub slice(str source @R0, ubyte start @A, ubyte length @Y, str target @R1) clobbers(A, Y) {
; Copies a segment from the source string, starting at the given index, ; Copies a segment from the source string, starting at the given index,
; and of the given length to target string. ; and of the given length to target string.
; It is assumed the target string buffer is large enough to contain the result. ; It is assumed the target string buffer is large enough to contain the result.
@@ -127,7 +127,7 @@ _startloop dey
}} }}
} }
asmsub find(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc { asmsub find(str string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
; Locates the first position of the given character in the string, ; Locates the first position of the given character in the string,
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index). ; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
%asm {{ %asm {{
@@ -152,7 +152,7 @@ _found tya
} }
asmsub find_eol(uword string @AY) -> ubyte @A, bool @Pc { asmsub find_eol(str string @AY) -> ubyte @A, bool @Pc {
; Locates the position of the first End Of Line character in the string. ; Locates the position of the first End Of Line character in the string.
; This is a convenience function that looks for both a CR or LF (byte 13 or byte 10) as being a possible Line Ending. ; This is a convenience function that looks for both a CR or LF (byte 13 or byte 10) as being a possible Line Ending.
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index). ; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
@@ -178,7 +178,7 @@ _found tya
}} }}
} }
asmsub rfind(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc { asmsub rfind(str string @AY, ubyte character @X) -> ubyte @A, bool @Pc {
; Locates the first position of the given character in the string, starting from the right. ; Locates the first position of the given character in the string, starting from the right.
; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index). ; returns Carry set if found + index in A, or Carry clear if not found (and A will be 255, an invalid index).
%asm {{ %asm {{
@@ -203,14 +203,14 @@ _found tya
}} }}
} }
asmsub contains(uword string @AY, ubyte character @X) -> bool @Pc { asmsub contains(str string @AY, ubyte character @X) -> bool @Pc {
; Just return true/false if the character is in the given string or not. ; Just return true/false if the character is in the given string or not.
%asm {{ %asm {{
jmp find jmp find
}} }}
} }
asmsub copy(uword source @R0, uword target @AY) clobbers(A) -> ubyte @Y { asmsub copy(str source @R0, str target @AY) clobbers(A) -> ubyte @Y {
; Copy a string to another, overwriting that one. ; Copy a string to another, overwriting that one.
; Returns the length of the string that was copied. ; Returns the length of the string that was copied.
; Often you dont have to call this explicitly and can just write string1 = string2 ; Often you dont have to call this explicitly and can just write string1 = string2
@@ -224,7 +224,7 @@ _found tya
}} }}
} }
asmsub append(uword target @R0, uword suffix @R1) clobbers(Y) -> ubyte @A { asmsub append(str target @R0, str suffix @R1) clobbers(Y) -> ubyte @A {
; Append the suffix string to the target. (make sure the buffer is large enough!) ; Append the suffix string to the target. (make sure the buffer is large enough!)
; Returns the length of the resulting string. ; Returns the length of the resulting string.
%asm {{ %asm {{
@@ -249,7 +249,7 @@ _found tya
}} }}
} }
asmsub compare(uword string1 @R0, uword string2 @AY) clobbers(Y) -> byte @A { asmsub compare(str string1 @R0, str string2 @AY) clobbers(Y) -> byte @A {
; Compares two strings for sorting. ; Compares two strings for sorting.
; Returns -1 (255), 0 or 1, meaning: string1 sorts before, equal or after string2. ; Returns -1 (255), 0 or 1, meaning: string1 sorts before, equal or after string2.
; Note that you can also directly compare strings and string values with eachother using ; Note that you can also directly compare strings and string values with eachother using
@@ -263,7 +263,7 @@ _found tya
}} }}
} }
asmsub ncompare(uword string1 @R0, uword string2 @AY, ubyte length @X) clobbers(X, Y) -> byte @A { asmsub ncompare(str string1 @R0, str string2 @AY, ubyte length @X) clobbers(X, Y) -> byte @A {
; Compares two strings for sorting. ; Compares two strings for sorting.
; Returns -1 (255), 0 or 1, meaning: string1 sorts before, equal or after string2. ; Returns -1 (255), 0 or 1, meaning: string1 sorts before, equal or after string2.
; Only compares the strings from index 0 up to the length argument. ; Only compares the strings from index 0 up to the length argument.
@@ -276,7 +276,7 @@ _found tya
}} }}
} }
asmsub lower(uword st @AY) -> ubyte @Y { asmsub lower(str st @AY) -> ubyte @Y {
; Lowercases the petscii string in-place. Returns length of the string. ; Lowercases the petscii string in-place. Returns length of the string.
; (for efficiency, non-letter characters > 128 will also not be left intact, ; (for efficiency, non-letter characters > 128 will also not be left intact,
; but regular text doesn't usually contain those characters anyway.) ; but regular text doesn't usually contain those characters anyway.)
@@ -299,7 +299,7 @@ _done rts
}} }}
} }
asmsub upper(uword st @AY) -> ubyte @Y { asmsub upper(str st @AY) -> ubyte @Y {
; Uppercases the petscii string in-place. Returns length of the string. ; Uppercases the petscii string in-place. Returns length of the string.
%asm {{ %asm {{
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1

View File

@@ -1,6 +1,6 @@
compression { compression {
sub decode_rle(uword @zp compressed, uword @zp target, uword maxsize) -> uword { sub decode_rle(^^ubyte @zp compressed, ^^ubyte @zp target, uword maxsize) -> uword {
cx16.r0 = target ; original target cx16.r0 = target ; original target
cx16.r1 = target+maxsize ; decompression limit cx16.r1 = target+maxsize ; decompression limit
@@ -30,7 +30,7 @@ compression {
return target-cx16.r0 return target-cx16.r0
} }
sub encode_rle(uword data, uword size, uword target, bool is_last_block) -> uword { sub encode_rle(^^ubyte data, uword size, ^^ubyte target, bool is_last_block) -> uword {
; -- Compress the given data block using ByteRun1 aka PackBits RLE encoding. ; -- Compress the given data block using ByteRun1 aka PackBits RLE encoding.
; Returns the size of the compressed RLE data. Worst case result storage size needed = (size + (size+126) / 127) + 1. ; Returns the size of the compressed RLE data. Worst case result storage size needed = (size + (size+126) / 127) + 1.
; is_last_block = usually true, but you can set it to false if you want to concatenate multiple ; is_last_block = usually true, but you can set it to false if you want to concatenate multiple
@@ -39,7 +39,7 @@ compression {
uword idx = 0 uword idx = 0
uword literals_start_idx = 0 uword literals_start_idx = 0
ubyte literals_length = 0 ubyte literals_length = 0
uword orig_target = target ^^ubyte orig_target = target
sub next_same_span() { sub next_same_span() {
; returns length in cx16.r1L, and the byte value in cx16.r1H ; returns length in cx16.r1L, and the byte value in cx16.r1H
@@ -54,7 +54,7 @@ compression {
sub output_literals() { sub output_literals() {
@(target) = literals_length-1 @(target) = literals_length-1
target++ target++
uword dataptr = data + literals_start_idx ^^ubyte dataptr = data + literals_start_idx
ubyte i ubyte i
for i in 0 to literals_length-1 { for i in 0 to literals_length-1 {
@(target) = @(dataptr) @(target) = @(dataptr)

View File

@@ -29,7 +29,7 @@ sub str_ub(ubyte value) -> str {
sub str_b(byte value) -> str { sub str_b(byte value) -> str {
; ---- convert the byte in A in decimal string form, without left padding 0s ; ---- convert the byte in A in decimal string form, without left padding 0s
uword out_ptr = &string_out ^^ubyte out_ptr = &string_out
if value<0 { if value<0 {
@(out_ptr) = '-' @(out_ptr) = '-'
out_ptr++ out_ptr++
@@ -39,7 +39,7 @@ sub str_b(byte value) -> str {
return string_out return string_out
} }
sub internal_str_ub(ubyte value, uword out_ptr) { sub internal_str_ub(ubyte value, str out_ptr) {
ubyte hundreds = value / 100 ubyte hundreds = value / 100
value -= hundreds*100 value -= hundreds*100
ubyte tens = value / 10 ubyte tens = value / 10
@@ -73,7 +73,7 @@ sub str_ubhex (ubyte value) -> str {
sub str_ubbin (ubyte value) -> str { sub str_ubbin (ubyte value) -> str {
; ---- convert the ubyte in A in binary string form ; ---- convert the ubyte in A in binary string form
uword out_ptr = &string_out ^^ubyte out_ptr = &string_out
repeat 8 { repeat 8 {
rol(value) rol(value)
if_cc if_cc
@@ -88,7 +88,7 @@ sub str_ubbin (ubyte value) -> str {
sub str_uwbin (uword value) -> str { sub str_uwbin (uword value) -> str {
; ---- convert the uword in A/Y in binary string form ; ---- convert the uword in A/Y in binary string form
uword out_ptr = &string_out ^^ubyte out_ptr = &string_out
repeat 16 { repeat 16 {
rol(value) rol(value)
if_cc if_cc
@@ -142,7 +142,7 @@ sub str_uw (uword value) -> str {
sub str_w (word value) -> str { sub str_w (word value) -> str {
; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's ; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's
uword out_ptr = &string_out ^^ubyte out_ptr = &string_out
if value<0 { if value<0 {
@(out_ptr) = '-' @(out_ptr) = '-'
out_ptr++ out_ptr++
@@ -152,7 +152,7 @@ sub str_w (word value) -> str {
return string_out return string_out
} }
sub internal_str_uw(uword value, uword out_ptr) { sub internal_str_uw(uword value, str out_ptr) {
uword value2 = value/10 uword value2 = value/10
ubyte digits = value-value2*10 as ubyte ubyte digits = value-value2*10 as ubyte
uword value3 = value2/10 uword value3 = value2/10

View File

@@ -24,7 +24,7 @@ diskio {
; ----- iterative file loader functions (uses the input io channel) ----- ; ----- iterative file loader functions (uses the input io channel) -----
sub f_open(uword filenameptr) -> bool { sub f_open(str filenameptr) -> bool {
; -- open a file for iterative reading with f_read ; -- open a file for iterative reading with f_read
; note: only a single iteration loop can be active at a time! ; note: only a single iteration loop can be active at a time!
; Returns true if the file is successfully opened and readable. ; Returns true if the file is successfully opened and readable.
@@ -39,7 +39,7 @@ diskio {
}} }}
} }
sub f_read(uword bufferpointer, uword num_bytes) -> uword { sub f_read(str bufferpointer, uword num_bytes) -> uword {
; -- read from the currently open file, up to the given number of bytes. ; -- read from the currently open file, up to the given number of bytes.
; returns the actual number of bytes read. (checks for End-of-file and error conditions) ; returns the actual number of bytes read. (checks for End-of-file and error conditions)
uword actual uword actual
@@ -57,7 +57,7 @@ diskio {
return actual return actual
} }
sub f_read_all(uword bufferpointer) -> uword { sub f_read_all(str bufferpointer) -> uword {
; -- read the full rest of the file, returns number of bytes read. ; -- read the full rest of the file, returns number of bytes read.
; It is assumed the file size is less than 64 K. ; It is assumed the file size is less than 64 K.
; Usually you will just be using load() / load_raw() to read entire files! ; Usually you will just be using load() / load_raw() to read entire files!
@@ -75,7 +75,7 @@ diskio {
} }
} }
sub f_readline(uword bufptr) -> ubyte { sub f_readline(str bufptr) -> ubyte {
; Routine to read text lines from a text file. Lines must be less than 255 characters. ; Routine to read text lines from a text file. Lines must be less than 255 characters.
; Reads characters from the input file UNTIL a newline or return character, or 0 byte (likely EOF). ; Reads characters from the input file UNTIL a newline or return character, or 0 byte (likely EOF).
; The line read will be 0-terminated in the buffer (and not contain the end of line character). ; The line read will be 0-terminated in the buffer (and not contain the end of line character).
@@ -119,7 +119,7 @@ diskio {
; ----- iterative file writing functions (uses write io channel) ----- ; ----- iterative file writing functions (uses write io channel) -----
sub f_open_w(uword filenameptr) -> bool { sub f_open_w(str filenameptr) -> bool {
; -- open a file for iterative writing with f_write ; -- open a file for iterative writing with f_write
; WARNING: returns true if the open command was received by the device, ; WARNING: returns true if the open command was received by the device,
; but this can still mean the file wasn't successfully opened for writing! ; but this can still mean the file wasn't successfully opened for writing!
@@ -133,7 +133,7 @@ diskio {
}} }}
} }
sub f_write(uword bufferpointer, uword num_bytes) -> bool { sub f_write(str bufferpointer, uword num_bytes) -> bool {
; -- write the given number of bytes to the currently open file ; -- write the given number of bytes to the currently open file
; you can call this multiple times to append more data ; you can call this multiple times to append more data
repeat num_bytes { repeat num_bytes {
@@ -185,7 +185,7 @@ diskio {
}} }}
} }
sub curdir() -> uword { sub curdir() -> str {
; return current directory name or 0 if error ; return current directory name or 0 if error
%ir {{ %ir {{
syscall 48 (): r99000.w syscall 48 (): r99000.w
@@ -204,7 +204,7 @@ diskio {
} }
sub save(uword filenameptr, uword start_address, uword savesize) -> bool { sub save(str filenameptr, uword start_address, uword savesize) -> bool {
%ir {{ %ir {{
load.b r99100,0 load.b r99100,0
loadm.w r99000,diskio.save.filenameptr loadm.w r99000,diskio.save.filenameptr
@@ -216,7 +216,7 @@ diskio {
} }
; like save() but omits the 2 byte prg header. ; like save() but omits the 2 byte prg header.
sub save_raw(uword filenameptr, uword start_address, uword savesize) -> bool { sub save_raw(str filenameptr, uword start_address, uword savesize) -> bool {
%ir {{ %ir {{
load.b r99100,1 load.b r99100,1
loadm.w r99000,diskio.save_raw.filenameptr loadm.w r99000,diskio.save_raw.filenameptr
@@ -233,7 +233,7 @@ diskio {
; If you specify a custom address_override, the first 2 bytes in the file are ignored ; If you specify a custom address_override, the first 2 bytes in the file are ignored
; and the rest is loaded at the given location in memory. ; and the rest is loaded at the given location in memory.
; Returns the end load address+1 if successful or 0 if a load error occurred. ; Returns the end load address+1 if successful or 0 if a load error occurred.
sub load(uword filenameptr, uword address_override) -> uword { sub load(str filenameptr, uword address_override) -> uword {
%ir {{ %ir {{
loadm.w r99000,diskio.load.filenameptr loadm.w r99000,diskio.load.filenameptr
loadm.w r99001,diskio.load.address_override loadm.w r99001,diskio.load.address_override
@@ -245,7 +245,7 @@ diskio {
; Identical to load(), but DOES INCLUDE the first 2 bytes in the file. ; Identical to load(), but DOES INCLUDE the first 2 bytes in the file.
; No program header is assumed in the file. Everything is loaded. ; No program header is assumed in the file. Everything is loaded.
; See comments on load() for more details. ; See comments on load() for more details.
sub load_raw(uword filenameptr, uword start_address) -> uword { sub load_raw(str filenameptr, uword start_address) -> uword {
%ir {{ %ir {{
loadm.w r99000,diskio.load_raw.filenameptr loadm.w r99000,diskio.load_raw.filenameptr
loadm.w r99001,diskio.load_raw.start_address loadm.w r99001,diskio.load_raw.start_address
@@ -254,7 +254,7 @@ diskio {
}} }}
} }
sub delete(uword filenameptr) { sub delete(str filenameptr) {
; -- delete a file on the drive ; -- delete a file on the drive
%ir {{ %ir {{
loadm.w r99000,diskio.delete.filenameptr loadm.w r99000,diskio.delete.filenameptr
@@ -262,7 +262,7 @@ diskio {
}} }}
} }
sub rename(uword oldfileptr, uword newfileptr) { sub rename(str oldfileptr, str newfileptr) {
; -- rename a file on the drive ; -- rename a file on the drive
%ir {{ %ir {{
loadm.w r99000,diskio.rename.oldfileptr loadm.w r99000,diskio.rename.oldfileptr

View File

@@ -316,7 +316,7 @@ math {
return w2-w1 return w2-w1
} }
sub crc16(uword data, uword length) -> uword { sub crc16(^^ubyte data, uword length) -> uword {
; calculates the CRC16 (XMODEM) checksum of the buffer. ; calculates the CRC16 (XMODEM) checksum of the buffer.
; There are also "streaming" crc16_start/update/end routines below, that allow you to calculate crc32 for data that doesn't fit in a single memory block. ; There are also "streaming" crc16_start/update/end routines below, that allow you to calculate crc32 for data that doesn't fit in a single memory block.
crc16_start() crc16_start()
@@ -353,7 +353,7 @@ math {
return cx16.r15 return cx16.r15
} }
sub crc32(uword data, uword length) { sub crc32(^^ubyte data, uword length) {
; Calculates the CRC-32 (ISO-HDLC/PKZIP) checksum of the buffer. ; Calculates the CRC-32 (ISO-HDLC/PKZIP) checksum of the buffer.
; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words. ; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words.
; result stored in cx16.r14 (low word) and cx16.r15 (high word) ; result stored in cx16.r14 (low word) and cx16.r15 (high word)

View File

@@ -5,7 +5,7 @@
sorting { sorting {
%option ignore_unused %option ignore_unused
sub shellsort_ub(uword @requirezp values, ubyte num_elements) { sub shellsort_ub(^^ubyte @requirezp values, ubyte num_elements) {
num_elements-- num_elements--
ubyte @zp gap ubyte @zp gap
for gap in [132, 57, 23, 10, 4, 1] { for gap in [132, 57, 23, 10, 4, 1] {
@@ -27,29 +27,29 @@ sorting {
} }
} }
sub shellsort_uw(uword @requirezp values, ubyte num_elements) { sub shellsort_uw(^^uword @requirezp values, ubyte num_elements) {
num_elements-- num_elements--
ubyte gap ubyte gap
for gap in [132, 57, 23, 10, 4, 1] { for gap in [132, 57, 23, 10, 4, 1] {
ubyte i ubyte i
for i in gap to num_elements { for i in gap to num_elements {
uword @zp temp = peekw(values+i*$0002) uword @zp temp = values[i]
ubyte @zp j = i ubyte @zp j = i
ubyte @zp k = j-gap ubyte @zp k = j-gap
while j>=gap { while j>=gap {
uword @zp v = peekw(values+k*2) uword @zp v = values[k]
if v <= temp break if v <= temp break
pokew(values+j*2, v) values[j] = v
j = k j = k
k -= gap k -= gap
} }
pokew(values+j*2, temp) values[j] = temp
} }
} }
} }
sub shellsort_by_ub(uword @requirezp ub_keys, uword @requirezp wordvalues, ubyte num_elements) { sub shellsort_by_ub(^^ubyte @requirezp ub_keys, ^^uword @requirezp wordvalues, ubyte num_elements) {
; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse). ; sorts the 'wordvalues' array (no-split array of words) according to the 'ub_keys' array (which also gets sorted ofcourse).
num_elements-- num_elements--
ubyte @zp gap ubyte @zp gap
@@ -57,7 +57,7 @@ sorting {
ubyte i ubyte i
for i in gap to num_elements { for i in gap to num_elements {
ubyte @zp temp = ub_keys[i] ubyte @zp temp = ub_keys[i]
uword temp_wv = peekw(wordvalues + i*$0002) uword temp_wv = wordvalues[i]
ubyte @zp j = i ubyte @zp j = i
ubyte @zp k = j-gap ubyte @zp k = j-gap
repeat { repeat {
@@ -65,17 +65,17 @@ sorting {
if v <= temp break if v <= temp break
if j < gap break if j < gap break
ub_keys[j] = v ub_keys[j] = v
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002)) wordvalues[j] = wordvalues[k]
j = k j = k
k -= gap k -= gap
} }
ub_keys[j] = temp ub_keys[j] = temp
pokew(wordvalues + j*$0002, temp_wv) wordvalues[j] = temp_wv
} }
} }
} }
sub shellsort_by_uw(uword @requirezp uw_keys, uword @requirezp wordvalues, ubyte num_elements) { sub shellsort_by_uw(^^uword @requirezp uw_keys, ^^uword @requirezp wordvalues, ubyte num_elements) {
; sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse). ; sorts the 'wordvalues' array according to the 'uw_keys' array (which also gets sorted ofcourse).
; both arrays should be no-split array of words. uw_keys are unsigned. ; both arrays should be no-split array of words. uw_keys are unsigned.
num_elements-- num_elements--
@@ -83,20 +83,20 @@ sorting {
for gap in [132, 57, 23, 10, 4, 1] { for gap in [132, 57, 23, 10, 4, 1] {
ubyte i ubyte i
for i in gap to num_elements { for i in gap to num_elements {
uword @zp temp = peekw(uw_keys+i*$0002) uword @zp temp = uw_keys[i]
uword temp_wv = peekw(wordvalues + i*$0002) uword temp_wv = wordvalues[i]
ubyte @zp j = i ubyte @zp j = i
ubyte @zp k = j-gap ubyte @zp k = j-gap
while j>=gap { while j>=gap {
uword @zp v = peekw(uw_keys+k*2) uword @zp v = uw_keys[k]
if v <= temp break if v <= temp break
pokew(uw_keys+j*2, v) uw_keys[j] = v
pokew(wordvalues + j*$0002, peekw(wordvalues + k*$0002)) wordvalues[j] = wordvalues[k]
j = k j = k
k -= gap k -= gap
} }
pokew(uw_keys+j*2, temp) uw_keys[j] = temp
pokew(wordvalues + j*$0002, temp_wv) wordvalues[j] = temp_wv
} }
} }
} }

View File

@@ -82,7 +82,7 @@ strings {
return 255, false return 255, false
} }
sub rfind(uword stringptr, ubyte character) -> ubyte, bool { sub rfind(str stringptr, ubyte character) -> ubyte, bool {
; Locates the first position of the given character in the string, starting from the right. ; Locates the first position of the given character in the string, starting from the right.
; returns index in A, and boolean if found or not. (when false A will also be 255, an invalid index). ; returns index in A, and boolean if found or not. (when false A will also be 255, an invalid index).
ubyte ix ubyte ix

View File

@@ -13,6 +13,7 @@ sys {
const ubyte SIZEOF_WORD = sizeof(word) const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = sizeof(uword) const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_LONG = sizeof(long) const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
const ubyte SIZEOF_FLOAT = sizeof(float) const ubyte SIZEOF_FLOAT = sizeof(float)
const byte MIN_BYTE = -128 const byte MIN_BYTE = -128
const byte MAX_BYTE = 127 const byte MAX_BYTE = 127
@@ -47,7 +48,7 @@ sys {
}} }}
} }
sub internal_stringcopy(uword source, uword tgt) { sub internal_stringcopy(str source, str tgt) {
; Called when the compiler wants to assign a string value to another string. ; Called when the compiler wants to assign a string value to another string.
%ir {{ %ir {{
loadm.w r99000,sys.internal_stringcopy.source loadm.w r99000,sys.internal_stringcopy.source

View File

@@ -53,6 +53,21 @@ sub uppercase() {
; not supported ; not supported
} }
sub rvs_on() {
print("\x1b[7m")
}
sub rvs_off() {
print("\x1b[0m")
}
sub color (ubyte txtcol) {
print("\x1b[3")
chrout('0' + txtcol)
chrout('m')
}
sub chrout(ubyte char) { sub chrout(ubyte char) {
%ir {{ %ir {{
loadm.b r99100,txt.chrout.char loadm.b r99100,txt.chrout.char
@@ -136,7 +151,7 @@ sub print_w (word value) {
print(conv.str_w(value)) print(conv.str_w(value))
} }
sub input_chars (uword buffer) -> ubyte { sub input_chars (str buffer) -> ubyte {
; ---- Input a string (max. 80 chars) from the keyboard. Returns length of input. (string is terminated with a 0 byte as well) ; ---- Input a string (max. 80 chars) from the keyboard. Returns length of input. (string is terminated with a 0 byte as well)
; It assumes the keyboard is selected as I/O channel! ; It assumes the keyboard is selected as I/O channel!
%ir {{ %ir {{

View File

@@ -5,6 +5,7 @@ import prog8.ast.FatalAstException
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.SyntaxError import prog8.ast.SyntaxError
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.StructDecl
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.code.core.* import prog8.code.core.*
import kotlin.math.* import kotlin.math.*
@@ -15,6 +16,7 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
"abs" to ::builtinAbs, "abs" to ::builtinAbs,
"len" to ::builtinLen, "len" to ::builtinLen,
"sizeof" to ::builtinSizeof, "sizeof" to ::builtinSizeof,
"offsetof" to ::builtinOffsetof,
"sgn" to ::builtinSgn, "sgn" to ::builtinSgn,
"sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } }, "sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
"sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } }, "sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
@@ -90,20 +92,39 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
else throw SyntaxError("abs requires one integer argument", position) else throw SyntaxError("abs requires one integer argument", position)
} }
private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program): NumericLiteral {
// 1 arg, "Struct.field"
if(args.size!=1)
throw SyntaxError("offsetof requires one argument", position)
val identifier = (args[0] as? IdentifierReference)?.nameInSource
if(identifier==null || identifier.size<2)
throw CannotEvaluateException("offsetof","argument should be an identifier of the form Struct.field")
val structname = identifier.dropLast(1)
val fieldname = identifier.last()
val struct = args[0].definingScope.lookup(structname) as? StructDecl
if(struct==null)
throw SyntaxError("cannot find struct '$structname'", args[0].position)
val offset = struct.offsetof(fieldname, program.memsizer)
if(offset==null)
throw SyntaxError("no such field '${identifier.joinToString(".")}'", args[0].position)
return NumericLiteral.optimalInteger(offset.toInt(), position)
}
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteral { private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteral {
// 1 arg, type = anything, result type = ubyte or uword // 1 arg, type = anything, result type = ubyte or uword
if(args.size!=1) if(args.size!=1)
throw SyntaxError("sizeof requires one argument", position) throw SyntaxError("sizeof requires one argument", position)
if(args[0] !is IdentifierReference && args[0] !is NumericLiteral) if(args[0] !is IdentifierReference && args[0] !is NumericLiteral && args[0] !is AddressOf)
throw SyntaxError("sizeof argument should be an identifier or number", position) throw CannotEvaluateException("sizeof","argument should be an identifier, number, or type name")
val dt = args[0].inferType(program) val dt = args[0].inferType(program)
if(dt.isKnown) { if(dt.isKnown) {
if(args[0] is NumericLiteral) if(args[0] is NumericLiteral || args[0] is AddressOf)
return NumericLiteral.optimalInteger(program.memsizer.memorySize(dt.getOrUndef(), null), position) return NumericLiteral.optimalInteger(program.memsizer.memorySize(dt.getOrUndef(), null), position)
val target = (args[0] as IdentifierReference).targetStatement(program) val target = (args[0] as? IdentifierReference)?.targetStatement()
?: throw CannotEvaluateException("sizeof", "no target") ?: throw SyntaxError("wrong argument type", position)
return when { return when {
dt.isArray -> { dt.isArray -> {
@@ -112,7 +133,7 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
NumericLiteral.optimalInteger(program.memsizer.memorySize(elementDt, length), position) NumericLiteral.optimalInteger(program.memsizer.memorySize(elementDt, length), position)
} }
dt.isString -> throw SyntaxError("sizeof(str) is undefined, did you mean len, or perhaps strings.length?", position) dt.isString -> throw SyntaxError("sizeof(str) is undefined, did you mean len, or perhaps strings.length?", position)
else -> NumericLiteral(BaseDataType.UBYTE, program.memsizer.memorySize(dt.getOrUndef(), null).toDouble(), position) else -> NumericLiteral.optimalInteger( program.memsizer.memorySize(dt.getOrUndef(), null), position)
} }
} else { } else {
val identifier = args[0] as? IdentifierReference val identifier = args[0] as? IdentifierReference
@@ -128,11 +149,17 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
} }
} }
throw SyntaxError("sizeof invalid argument type", position) // the argument could refer to a struct declaration
val struct = (args[0] as? IdentifierReference)?.targetStructDecl()
if(struct!=null) {
val size = struct.memsize(program.memsizer)
return NumericLiteral(BaseDataType.UBYTE, size.toDouble(), position)
}
throw SyntaxError("sizeof argument should be an identifier, number, or type name", position)
} }
} }
@Suppress("unused")
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteral { private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteral {
// 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)

View File

@@ -5,6 +5,7 @@ import prog8.ast.*
import prog8.ast.expressions.Expression import prog8.ast.expressions.Expression
import prog8.ast.expressions.NumericLiteral import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Directive import prog8.ast.statements.Directive
import prog8.code.SymbolTable
import prog8.code.SymbolTableMaker import prog8.code.SymbolTableMaker
import prog8.code.ast.PtProgram import prog8.code.ast.PtProgram
import prog8.code.ast.printAst import prog8.code.ast.printAst
@@ -176,17 +177,21 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
println("*********** COMPILER AST END *************\n") println("*********** COMPILER AST END *************\n")
} }
var symbolTable: SymbolTable
val (intermediateAst, simplifiedAstDuration2) = measureTimedValue { val (intermediateAst, simplifiedAstDuration2) = measureTimedValue {
val intermediateAst = SimplifiedAstMaker(program, args.errors).transform() val intermediateAst = SimplifiedAstMaker(program, args.errors).transform()
val stMaker = SymbolTableMaker(intermediateAst, compilationOptions) val stMaker = SymbolTableMaker(intermediateAst, compilationOptions)
val symbolTable = stMaker.make() symbolTable = stMaker.make()
postprocessSimplifiedAst(intermediateAst, symbolTable, args.errors) postprocessSimplifiedAst(intermediateAst, symbolTable, compilationOptions, args.errors)
args.errors.report() args.errors.report()
symbolTable = stMaker.make() // need an updated ST because the postprocessing changes stuff
if (compilationOptions.optimize) { if (compilationOptions.optimize) {
optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors) optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors)
args.errors.report() args.errors.report()
symbolTable = stMaker.make() // need an updated ST because the optimization changes stuff
} }
if (args.printAst2) { if (args.printAst2) {
@@ -199,11 +204,12 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
args.errors.report() args.errors.report()
intermediateAst intermediateAst
} }
simplifiedAstDuration =simplifiedAstDuration2 simplifiedAstDuration = simplifiedAstDuration2
createAssemblyDuration = measureTime { createAssemblyDuration = measureTime {
if (!createAssemblyAndAssemble( if (!createAssemblyAndAssemble(
intermediateAst, intermediateAst,
symbolTable,
args.errors, args.errors,
compilationOptions, compilationOptions,
program.generatedLabelSequenceNumber program.generatedLabelSequenceNumber
@@ -299,7 +305,7 @@ internal fun determineProgramLoadAddress(program: Program, options: CompilationO
if(loadAddress>options.memtopAddress) { if(loadAddress>options.memtopAddress) {
errors.warn("program load address ${loadAddress.toHex()} is beyond default memtop address ${options.memtopAddress.toHex()}. " + errors.warn("program load address ${loadAddress.toHex()} is beyond default memtop address ${options.memtopAddress.toHex()}. " +
"Memtop has been adjusted to ${'$'}ffff to avoid assembler error. Set a valid %memtop yourself to get rid of this warning.", program.toplevelModule.position) $$"Memtop has been adjusted to $ffff to avoid assembler error. Set a valid %memtop yourself to get rid of this warning.", program.toplevelModule.position)
options.memtopAddress = 0xffffu options.memtopAddress = 0xffffu
} }
} }
@@ -456,7 +462,6 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) { private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
program.preprocessAst(errors, compilerOptions) program.preprocessAst(errors, compilerOptions)
if(errors.noErrors() && compilerOptions.dumpSymbols) { if(errors.noErrors() && compilerOptions.dumpSymbols) {
printSymbols(program) printSymbols(program)
exitProcess(0) exitProcess(0)
@@ -559,6 +564,7 @@ private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOpt
} }
private fun createAssemblyAndAssemble(program: PtProgram, private fun createAssemblyAndAssemble(program: PtProgram,
symbolTable: SymbolTable,
errors: IErrorReporter, errors: IErrorReporter,
compilerOptions: CompilationOptions, compilerOptions: CompilationOptions,
lastGeneratedLabelSequenceNr: Int lastGeneratedLabelSequenceNr: Int
@@ -573,10 +579,6 @@ private fun createAssemblyAndAssemble(program: PtProgram,
else else
throw NotImplementedError("no code generator for cpu ${compilerOptions.compTarget.cpu}") throw NotImplementedError("no code generator for cpu ${compilerOptions.compTarget.cpu}")
// need to make a new symboltable here to capture possible changes made by optimization steps performed earlier!
val stMaker = SymbolTableMaker(program, compilerOptions)
val symbolTable = stMaker.make()
val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors) val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors)
errors.report() errors.report()

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