Compare commits

...

287 Commits

Author SHA1 Message Date
c71066af4c fixing name lookup issue 2025-06-16 22:15:51 +02:00
6f0a0981bd fixing name lookup issue 2025-06-16 00:21:54 +02:00
49a4d9ba37 allow str as struct field type (^^ubyte) and strings in struct initializers 2025-06-15 00:29:59 +02:00
fcdfa741b9 Merge branch 'master' into structs
# Conflicts:
#	compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt
2025-06-14 21:07:23 +02:00
e3e395836d fix splitting of array decl and initializer for non numeric types 2025-06-13 23:31:56 +02:00
3bab177d50 working on pointers/binarytree example 2025-06-13 23:20:15 +02:00
12abafb917 Merge branch 'master' into structs
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
#	gradle.properties
2025-06-12 00:11:58 +02:00
8dc2e47507 fix partial unused code removal in vm target 2025-06-11 23:31:29 +02:00
0be90dedf2 check for split word array as argument 2025-06-11 21:35:36 +02:00
daf7c3357c better detection of missing return statement
preparing 11.4
2025-06-09 16:01:56 +02:00
e6bab3ceeb IR: adding LOADFIELD and STOREFIELD instructions 2025-06-09 01:41:45 +02:00
59387b2ae8 Merge branch 'master' into structs
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2025-06-07 20:28:23 +02:00
e8795859c5 added sorting library for target virtual
added sorting routines that sort a values array together with the keys array
optimized gnomesort a little
2025-06-07 19:42:40 +02:00
bebe60b687 fix compiler crash on for x in wordvar, add sys.get_as_returnaddress() 2025-06-05 16:10:40 +02:00
ddceec364e optimized coroutines library 2025-06-04 21:34:32 +02:00
f8f20440d3 allow pointer to be treated as uword value in augmented assignments 2025-06-03 21:33:35 +02:00
f8722faa4e Merge branch 'master' into structs
# Conflicts:
#	examples/test.p8
2025-06-03 21:15:19 +02:00
d067fa4b73 added strings.find_eol() 2025-06-03 21:09:44 +02:00
26fbbf48a4 adapt new antlr visitor to the pointer/struct additions 2025-06-03 01:28:58 +02:00
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
b5e51ab937 cleaner timings output 2025-06-02 19:30:25 +02:00
552e55c29f fix missing cmp #0 when asmsub call is part of a boolean expression 2025-06-02 19:22:00 +02:00
a228908c1a fix wrong address calculation for &wordarray[i] where i is a variable 2025-06-02 03:13:23 +02:00
15fc3b6c04 replace old antlr2kotlin code with the new visitor-based translator 2025-06-02 01:56:07 +02:00
0456badd02 creating on a new visitor-based antlr to kotlin translator 2025-06-02 01:18:07 +02:00
d28f154f1c Merge branch 'master' into structs
# Conflicts:
#	examples/test.p8
#	parser/src/main/antlr/Prog8ANTLR.g4
2025-06-01 17:53:41 +02:00
399cf5118d we will get a 11.4 version first before structs will land 2025-06-01 17:47:06 +02:00
a87f2640d3 fixed signed byte comparisons in case of overflowing values 2025-06-01 14:01:25 +02:00
a90ef274d7 fix word*128 codegen.
added cx16/landscape.p8 example that draws procedurally generated landscapes.
found bug in signed byte comparisons with overflow.
2025-05-31 05:27:19 +02:00
1c02179c5c refactor loadIndexReg() 2025-05-30 21:39:17 +02:00
77584493fd support a.b.ptr[i]^^.value as expression (RHS) 2025-05-30 20:28:10 +02:00
a36709e638 Merge branch 'master' into structs
# Conflicts:
#	compiler/src/prog8/compiler/Compiler.kt
2025-05-30 12:39:23 +02:00
341778ba67 added -timings flag 2025-05-30 12:38:16 +02:00
8d63cce749 working on deref after array indexing 2025-05-30 11:30:49 +02:00
ec50b5a007 homebrew info 2025-05-30 03:43:03 +02:00
8e7bbcdbe0 clear syntax error for yet unsupported deref after array indexing 2025-05-29 21:14:38 +02:00
37ecdc47b3 allow ptr1^^ = ptr^^ (replaces it with memcopy) 2025-05-29 16:34:47 +02:00
112ca3cc53 allow sizeof(&thing), add sys.SIZEOF_POINTER 2025-05-29 15:58:29 +02:00
33b3a1664c replace sizeof(list^^) with sizeof(List) to allow it to compile. Same with simple pointers. 2025-05-29 14:13:42 +02:00
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
31d84c8921 doc 2025-05-29 13:35:20 +02:00
34bedbeef1 optimize byte modulus (%) routine with repeated subtraction instead of using full division 2025-05-29 13:26:04 +02:00
3b1b0985c1 make sizeof(float) work, so you don't have to use sys.SIZEOF_FLOAT anymore etc.
define sys.SIZEOF_FLOAT in terms of sizeof(float)
2025-05-29 12:38:03 +02:00
e40ace9dea todo 2025-05-29 01:35:53 +02:00
4c0e6e2640 fix split/nosplit pointer arrays, replace ubyteptr^^ with @(ubyteptr), fix double dereference 2025-05-29 00:36:55 +02:00
08b314c37d IR: fix various register type mismatches 2025-05-28 22:15:07 +02:00
86da9d3c7e assigning to plain pointer with array indexing 2025-05-28 18:08:53 +02:00
4e61e25c02 Merge branch 'master' into structs
# Conflicts:
#	compiler/test/TestTypecasts.kt
2025-05-27 23:52:59 +02:00
5097d52d99 IR codegen for pointer indexing expressions, -assignment 2025-05-27 23:41:08 +02:00
368387e1a7 allow floats to be (explicitly) cast to integers 2025-05-26 21:39:48 +02:00
09d2185bb1 PtArrayIndexer variable is now nullable (because it could be a ptr deref instead) 2025-05-25 23:04:32 +02:00
5c02e2bd71 fix a ptr indexing case, fix address-of fields 2025-05-25 21:32:31 +02:00
fb01389b3d cleaning up pointer deref 2025-05-25 18:33:37 +02:00
aaa81210ce cleaning up pointer indexing 2025-05-25 02:56:32 +02:00
51269257ea fix a.b.c.d desugaring into pointer deref chain 2025-05-24 14:48:02 +02:00
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
9da430ffeb vm: more complete V-flag handling. somd doc and todo updates. 2025-05-23 18:58:14 +02:00
cc063124cf add joystick control to cx16 fileselector.
fix fileselector Basic exasmple.
fixed too aggressive asm peephole optimization that destroyed %jumptable in libraries for example.
2025-05-23 17:50:11 +02:00
3b37b89951 added cx16.joysticks_detect() and cx16.joysticks_getall() 2025-05-23 02:26:21 +02:00
844b537d1e cobramk3 example now draws with new monogfx doublebuffering 2025-05-22 23:29:49 +02:00
caf1d4a22a fix monogfx INVERT draw mode 2025-05-22 21:29:23 +02:00
d8e244df99 fix monogfx example 2025-05-22 00:37:20 +02:00
548e421e27 added doublebuffering to monogfx (in both lores and hires mode) 2025-05-22 00:10:03 +02:00
322fa7ea69 slightly optimize monogfx plot() 2025-05-21 01:16:43 +02:00
db6c887795 Merge branch 'master' into structs
# Conflicts:
#	compiler/test/ast/TestVariousCompilerAst.kt
2025-05-21 00:27:45 +02:00
cf7bea0985 cleanup RTS insertion and ast postprocessing before assembly generation 2025-05-21 00:19:50 +02:00
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
25d7f8808f IR: added signed multiplication opcodes 2025-05-20 21:36:05 +02:00
1c4999ec87 adding ptr unit tests 2025-05-20 20:57:05 +02:00
c726d3f937 fix ptr errors 2025-05-19 22:49:07 +02:00
f70341df1b fix ptr errors 2025-05-19 20:06:31 +02:00
f0b791452e fix many ptr deref errors 2025-05-19 01:33:57 +02:00
adf5600a9b simplify 2025-05-18 18:37:19 +02:00
6d4ccc5feb fix pointer variable not getting marked as used in some cases 2025-05-18 13:23:56 +02:00
5f3829d5cc partly fix weird errors for ptr indexed expressions 2025-05-17 22:56:41 +02:00
770ebdcd4a party fix weird errors for ptr indexed assignment 2025-05-17 20:54:02 +02:00
96f690e749 fix a ptr indexing error 2025-05-17 20:27:38 +02:00
eabdd3a8f3 fix the ptr.uword[index] assignment target error 2025-05-17 14:57:24 +02:00
50650b966b repeat countervars again in zeropage if possible, fix pointer arithmetic error 2025-05-17 14:18:56 +02:00
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
05dad5ab5f cleanup 6502 pointer usage checks 2025-05-16 21:44:28 +02:00
1a69a2f1bc fix some ptr vs uword type checks 2025-05-16 20:31:15 +02:00
435faafaad fix split-word storage (lsb/msb) of arrays of pointers 2025-05-16 17:53:15 +02:00
686b32dc29 replace ^^str by ^^ubyte and allow returning ubyte/uword when pointer is expected 2025-05-16 17:53:15 +02:00
0e64a22910 tweak address-of types 2025-05-16 17:53:15 +02:00
4f0839f27e rewrite pointer[0] into @(pointer) if its ^^ubyte 2025-05-16 17:53:15 +02:00
bb1953267d Merge branch 'master' into structs
# Conflicts:
#	examples/test.p8
2025-05-15 23:09:30 +02:00
acc630972a make keyboardhandler example restartable 2025-05-15 23:07:54 +02:00
6a33be3fd8 IR: allow returning boolean in Pc/Pv cpu status register (Pz and Pn are not yet possible) 2025-05-15 22:56:45 +02:00
cd8aae4681 allow @(..) to take a ^^ubyte address pointer, not only uwords. 2025-05-15 22:12:42 +02:00
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
f5fc4e345c fix build error on case-insensitive filesystems 2025-05-15 21:11:34 +02:00
86eef7039f @(..) now also accepts pointer to ubyte address 2025-05-15 20:07:02 +02:00
f4b2264fcf fix struct type checks for subroutine call arguments 2025-05-14 23:33:55 +02:00
9b36ae2277 implement inplace boolean short-circuit operators on pointer dereferenced booleans 2025-05-14 21:29:50 +02:00
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
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
67231af623 fix forloop codegen over non-split word arrays of length >= 64 elements 2025-05-13 23:32:26 +02:00
e31ef6f06f IR: fix temp register type in for x in array 2025-05-13 22:23:04 +02:00
09d188106a different temp var mechanism for certain array expression, remove old tmpvar mechanism. 2025-05-13 21:12:10 +02:00
d8e2116481 different temp var mechanism for for loops, and pokef() 2025-05-13 21:06:33 +02:00
435dfbb932 optimize: rewrite suitable when into on..goto 2025-05-13 01:12:58 +02:00
ba93966474 optimize codegen: shortcut redundant jumps in when statement 2025-05-13 00:35:22 +02:00
ea8d17cdb2 optimized the cx16 multi-irq dispatcher used in cx16.enable_irq_handlers() 2025-05-12 23:26:54 +02:00
082265fb25 todo 2025-05-12 00:24:57 +02:00
d138a7a567 add struct and pointers to IDEA syntax 2025-05-11 23:35:53 +02:00
ea27d732ab Merge branch 'refs/heads/master' into structs 2025-05-11 23:35:13 +02:00
9e557ce8ac add keyword 'on' to IDEA syntax 2025-05-11 23:32:54 +02:00
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
e5d9af75de remove double bra/jmp 2025-05-11 23:01:13 +02:00
31c1bf8bc5 added on..goto/call statement 2025-05-11 21:37:44 +02:00
37d4055036 translate newline '\n' to char code 13 in various encodings such as ISO (used to be 10)
This means that when printed, such newlines will now properly go to the next line in these encodings too (ISO variants, KATAKANA).
2025-05-11 19:45:24 +02:00
78b1076110 some more software links 2025-05-11 19:39:20 +02:00
0a3c748e41 fix code gen bug for certain goto array[idx] 2025-05-11 17:18:20 +02:00
ebf79ef9e2 release 11.3.2 2025-05-11 12:30:36 +02:00
60a73248cd todo 2025-05-11 03:16:48 +02:00
abbb7d7ba3 fix struct pointers in subroutine parameters and return values 2025-05-11 02:08:59 +02:00
59c378089e fix some struct type and symbol lookup errors 2025-05-11 00:52:35 +02:00
0b789b5f0b added most inplace operators for pointer deref 2025-05-10 20:58:01 +02:00
4382b96a9a tweaking pointer deref in IR 2025-05-10 19:52:06 +02:00
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
99b9370178 fix various bugs around word-indexing combined with address-of: &buffer[2000] 2025-05-10 16:22:05 +02:00
506062c6b6 start implementing ptr deref augmented assigns 2025-05-09 23:05:27 +02:00
d634061cd9 Merge pull request #169 from Frosty-J/crc
Specify X16 CRC type
2025-05-08 23:47:17 +02:00
8353c689ca start making '&' (address-of) return a typed pointer, fixes some errors 2025-05-08 23:40:41 +02:00
d59d8ff1fe Specify X16 CRC type 2025-05-08 05:57:30 +01:00
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
53e442d509 fix regression in 11.3.1: allow bitwise operation between different types as long as they're the same size. 2025-05-07 23:06:45 +02:00
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
f7cbfdff06 skip this test for now 2025-05-07 21:14:52 +02:00
b28ee0819f revert behavior change of @dirty variables, instead document the initialization behavior correctly in the docs
they get zeroed at program startup (like other uninitialized BSS variables), just not on entry in the subroutine.
2025-05-07 21:07:12 +02:00
5de626aab8 support comparison operators on pointers 2025-05-06 22:26:27 +02:00
7aad5d486e Merge pull request #168 from adiee5/master
Update `nano` syntax highlighting
2025-05-06 18:17:58 +02:00
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
8c324d7514 tweak error 2025-05-06 17:50:53 +02:00
522958e0e9 @dirty variables now actually end up in the uninitialized BSS_NOCLEAR section 2025-05-06 01:41:34 +02:00
97390db5f5 Update prog8.nanorc 2025-05-05 21:06:56 +02:00
af920d1427 pointer arithmetic for '-', fixed '+' 2025-05-05 21:06:49 +02:00
779ebc0537 pointer arithmetic for '+' 2025-05-05 18:09:19 +02:00
38949b82c3 type check tuning 2025-05-05 15:41:32 +02:00
d11386ef26 type check tuning 2025-05-04 23:23:21 +02:00
0e0377d1f0 IR/VM implemented struct allocations and initialization 2025-05-04 14:04:44 +02:00
55e0dbab27 preparing for statically allocating struct instances 2025-05-03 23:44:29 +02:00
4dc82f2c83 preparing for statically allocating struct instances 2025-05-03 19:00:27 +02:00
1ba5587404 allow syntax for declaring variables with struct instance type 2025-05-03 16:14:22 +02:00
835c4b6da3 allow multi-field declarations in structs, get rid of . -> ^^ rewrite 2025-05-03 12:32:29 +02:00
dbd955b61e tweak typecheck and better code for constant indexes in pointer derefs 2025-05-02 23:44:26 +02:00
d20e2fd88c structs will be a new major version of the compiler 2025-05-02 22:25:26 +02:00
e0dea89477 added support for ptr[x].field 2025-05-02 22:16:20 +02:00
ccc6b56e35 added link to prog8reu library 2025-05-02 19:38:59 +02:00
6fc2902895 fixing ptr traversal typecheck issues 2025-05-02 00:41:42 +02:00
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
37da3e2170 parser 2025-05-02 00:41:42 +02:00
2661d3c489 allow array syntax on pointers 2025-05-02 00:41:42 +02:00
b89bbb9281 allow pointers in subroutines params and return values 2025-05-02 00:41:42 +02:00
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
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
0162e7a0c1 fix the scoping problems on subtypes 2025-05-02 00:41:42 +02:00
6ce099f176 IR: fix ptr type checks and struct field assignment errors 2025-05-02 00:41:42 +02:00
476a4bac8e IR: LOADI allows r1 and r2 to be the same for pointer chain dereference optimalization 2025-05-02 00:41:42 +02:00
63a410a6df implicit cast to bool for numeric or pointers as condition arguments (to if, while, until) 2025-05-02 00:41:42 +02:00
cca27faa3b fix pointer value assignment (not dereferencing the actual pointer) 2025-05-02 00:41:42 +02:00
803e6bd81a fix uword vs pointer type errors and casts 2025-05-02 00:41:42 +02:00
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
b920d553a0 make address-of dereference work 2025-05-02 00:41:42 +02:00
5e2d0d0dfc fix param order of AssignTarget 2025-05-02 00:41:42 +02:00
2ae3bd68eb more pointer dereferencing for chains 2025-05-02 00:41:42 +02:00
9c183f27eb pointer dereferencing for chains 2025-05-02 00:41:42 +02:00
8046023e82 pointer dereferencing for simple types (read and write) 2025-05-02 00:41:42 +02:00
e328520588 initial struct and typed pointer support 2025-05-02 00:41:40 +02:00
7eb079050c fix strings.hash() on 6502 CPU's. NOTE: now takes string pointer in AY instead of R0 on all platforms 2025-05-01 22:47:43 +02:00
2fdd5543b2 release 11.3.1 2025-04-30 22:40:54 +02:00
d04164c0a6 fix const evaluation of bitwise logical expressions (&, |, ^, <<, >>) of signed operands 2025-04-30 22:27:31 +02:00
b047731f82 removed some redundant arguments 2025-04-25 23:27:06 +02:00
4d91f92a2e clearer error message 2025-04-25 19:38:45 +02:00
98505d27b1 IR: fix invalid removal of label chunks and include asmsubs when looking for labels 2025-04-24 23:30:51 +02:00
cd63a58ad9 IR: get rid of problematic PREPARECALL pseudo instruction 2025-04-24 22:32:40 +02:00
170f8dd092 IR: new CALLFAR instructions to support callfar into another bank 2025-04-24 21:17:48 +02:00
619dcb6a84 fix error in IR register typing 2025-04-24 20:50:19 +02:00
99ae8ea52e code cleanup 2025-04-24 18:51:03 +02:00
dc031c30eb add -version option to just print the compiler version and then exit 2025-04-24 18:13:35 +02:00
1e702439b7 avoid JDK 21+/Kotlin method conflict of removeLast()/removeFirst()
background: see https://www.reddit.com/r/androiddev/comments/1gspjrs/dont_use_kotlins_removefirst_and_removelast_when/
https://youtrack.jetbrains.com/issue/KT-71375/Prevent-Kotlins-removeFirst-and-removeLast-from-causing-crashes-on-Android-14-and-below-after-upgrading-to-Android-API-Level-35

it's about Android but the problem also occurs on desktop JDKs for example when running a Java21 compiled prog8 with Java17
2025-04-24 18:11:42 +02:00
8debc42381 slightly stricter assignment type compatibility checking 2025-04-24 15:05:35 +02:00
532d719089 more optimal math.randrange() routines 2025-04-24 04:04:14 +02:00
b40860aca4 get rid of RTS after JMP 2025-04-23 17:08:16 +02:00
2cbe6b5f7f info message when more optimal goto array[idx] is possible 2025-04-23 16:59:18 +02:00
d2cc7ccdfa remove redundant variable=0 initializations (BSS clear takes care of them) 2025-04-23 14:45:38 +02:00
2cb183c6d8 fix regression for goto array[idx] on 6502 cpu which doesn't have jmp (ptr,x) 2025-04-23 02:56:10 +02:00
84026b105f smaller code for goto nosplitptrarray[index] 2025-04-22 20:01:16 +02:00
a4d0589f10 fix errors like parse error still printed in color when -plaintext is set 2025-04-22 19:26:11 +02:00
e375f6afce fix diskio.f_read() returning 1 less than the actual size read.
fix diskio.f_read_all() more robust error status end of loop checking.
2025-04-21 05:02:16 +02:00
5a7bc04816 update docs about library jump table 2025-04-19 13:49:03 +02:00
bd1894580e allow floating point value as part of a multi-value return 2025-04-18 22:45:05 +02:00
9e694c0337 doc 2025-04-18 21:11:27 +02:00
c82586db28 print copyright message better 2025-04-17 23:09:28 +02:00
dd2d466350 code cleanups 2025-04-17 22:58:02 +02:00
830da8de0a memorymapped vars in ZP are now treated as ZP-variables by prog8 itself too 2025-04-17 22:19:01 +02:00
4e5ee333c8 preparing release 11.3 2025-04-17 21:16:52 +02:00
9df899eb63 document romable option and that strings+initialized arrays become read-only 2025-04-17 21:05:46 +02:00
ca7491a702 cx16: sys.enable_irq_handlers() and associated functions are now romable 2025-04-17 00:25:25 +02:00
1a07129865 c64: graphics.horizontal_line() is romable 2025-04-17 00:02:17 +02:00
4fbd67ff99 txt.setcc() is romable 2025-04-16 23:44:50 +02:00
5bc6c50f42 txt.setclr() and getclr() are romable 2025-04-16 23:38:16 +02:00
063de3801d txt.getchr() is romable 2025-04-16 23:32:18 +02:00
ae65266a4a txt.setchr() is romable 2025-04-16 23:27:23 +02:00
8ed2401e0b cx16: txt.scroll_left(), right, up and down are now romable 2025-04-16 23:11:27 +02:00
d2e8ee8269 cx16: txt.fill_screen, txt.clear_screenchars, clear_screencolors are now romable 2025-04-16 22:59:36 +02:00
1f996e3b8b sorting.gnomesort_ub(), compression.decode_rle() and decode_rle_srcfunc() are now romable 2025-04-16 22:47:34 +02:00
7108b74105 string.rfind() is now romable and now works correctly on empty strings.
added some comments to strings.pattern_match
2025-04-16 21:55:55 +02:00
801fe1b604 c64: callfar() is now romable 2025-04-16 21:13:59 +02:00
fb44c87597 make diskio.f_read() ROM-compatible 2025-04-15 00:23:23 +02:00
6b9cdbd482 remove unused arraycopy routines 2025-04-14 22:10:54 +02:00
0ab98033b5 add rom-compatible random number routines 2025-04-14 22:04:11 +02:00
14a2b96609 scanned libraries for self-modifying code/inline vars (romable problem) 2025-04-14 21:15:32 +02:00
f829b689db update zsmkit example to zsmkit release 2.6 2025-04-14 19:28:12 +02:00
dfda8b7ed5 remove problematic rewriting of X=value-X 2025-04-14 02:35:37 +02:00
4388466451 romable comments 2025-04-10 22:21:27 +02:00
5c2f509a52 also hide emulator process output when using -quiet 2025-04-10 21:26:48 +02:00
59582f5210 added -quiet flag to suppres all compiler and assembler messages 2025-04-10 21:16:26 +02:00
e2a8bdbdfb romable comments 2025-04-09 22:35:23 +02:00
0916b943da sys.exit(), exit2(), exit3() now romable 2025-04-09 22:13:52 +02:00
9c7ebc883c fixed: memsetw() invalid asm, c128: sys.restore_irq() 2025-04-09 21:29:48 +02:00
0ee42b9aa0 output_type is now part of custom target config and atari again defaults to .xex 2025-04-09 20:43:44 +02:00
37b3868ca3 symboldump now also includes aliased symbols (like palette.set_default) 2025-04-08 21:40:28 +02:00
a6835ce3f0 fix signed word value range check error, fix rol2() on array element 2025-04-08 21:05:50 +02:00
69c96ad99b cx16: added cx16.push_rambank/rombank and cx16.pop_rambank/rombank for easy temporary bank switching 2025-04-06 15:33:35 +02:00
b72877d59d cx16: added routines to get and set the default palette (new rom 49+ extapi) 2025-04-06 15:14:04 +02:00
05eb15d4f7 add cx16.memory_decompress_from_func() 2025-04-06 13:48:08 +02:00
f1fec37c79 rename examples/vm to examples/virtual 2025-04-04 20:02:45 +02:00
73f6880ff8 fix irq related crash by no longer zeroing out stored vector 2025-04-02 22:22:21 +02:00
8a53742f31 fix notreached error 2025-04-01 23:12:08 +02:00
9be40e85ff Improve romability of low-level libs (#160)
* Improve romability of low-level libs

* Improve ROMability of targeted syslibs

Previous commit just did that for multitarget libs, now it's also syslibs of built-in targets that get this treatment too.
2025-04-01 23:00:14 +02:00
61079c1eb7 errormessage for assignment to str/arrays in ROM 2025-03-31 23:57:04 +02:00
1075ee8fc3 errormessage for non romable extsub bank declaration 2025-03-31 23:05:58 +02:00
a28b265197 fix diskio merge issue on c64 and c128. Fixes #167 2025-03-31 22:06:49 +02:00
20e534c468 fix memtop limit when using -varshigh option 2025-03-27 23:31:05 +01:00
da7aa5dc49 forloop temp index variables no longer inline when romable 2025-03-26 22:38:50 +01:00
8f2a43ca0a temp vars for for loops indices in romable mode, are no longer allocated from the precious Zeropage 2025-03-26 22:12:22 +01:00
d0909d7810 added diskio.loadlib() convenience function to load library blobs 2025-03-24 22:35:34 +01:00
1641999d20 upgrade to kotlin 2.1.20 2025-03-22 23:06:44 +01:00
e16452037c no more self modifying code for loops over non-const word range 2025-03-22 22:51:30 +01:00
344d79684a get rid of more modifying code in forloops (byte ranges with step 1/-1)
fix invalid stack corrupting loops
2025-03-22 20:45:28 +01:00
573a1d9b7b refactor for loop over byte ranges 2025-03-22 16:07:46 +01:00
25ab57580c for loop over string no longer requires self-modifying code 2025-03-22 13:38:49 +01:00
a332e0e3d1 remove self-modifying code from @(ptr)++ and @(ptr)--
romable errors for string and array variables turned into read-only warnings

remove self-modifying code from @(ptr)++ and @(ptr)--
2025-03-21 23:54:19 +01:00
376f1cb139 remove self-modifying code from rol(ptr) and ror(ptr) 2025-03-21 23:16:40 +01:00
90f80558d7 add more 'modified' comments to self-modifying code lines 2025-03-21 17:59:49 +01:00
e281994898 intellij syntax def file works for all jetbrains ides 2025-03-21 01:32:24 +01:00
29fac122e1 fix: ignore EOF when looking for error status after reading first byte of newly-opened file (#166)
* fix: ignore EOF when looking for error status after reading first byte of newly-opened file

* fix: replace hard-coded $40 and $BF with STATUS_EOF and ~STATUS_EOF
2025-03-19 23:00:55 +01:00
1dc412eb90 simplify 2025-03-18 23:50:51 +01:00
3770a4fe0c easier datatype notation by just using the type objects directly 2025-03-18 23:33:04 +01:00
79cda544c8 allow integer range as when choice value 2025-03-17 22:26:27 +01:00
f04b97d890 support symlinks in paths (triggered by symlinked tmp on mac os) 2025-03-15 14:33:33 +01:00
3e9b4ccc45 getting rid of needless absolute pathings 2025-03-14 20:39:18 +01:00
2c3d838dd8 get rid of deprecated usage of thread id to create temporary test output file names 2025-03-14 01:23:01 +01:00
7668a3c660 use kotest tempdir instead of hardcoded output directory 2025-03-14 01:18:33 +01:00
5dd45b714a psg module: the envelope handler no longer writes to vera PSG voices that haven't been enabled 2025-03-12 23:07:13 +01:00
8b08895d0f also add strings.ncompare() to virtual lib 2025-03-09 14:33:31 +01:00
8f8d99e3ed updated symboldumps 2025-03-07 23:58:06 +01:00
23474360ec fix ncompare calls 2025-03-07 23:56:13 +01:00
81c255c450 Add strings.ncompare (and backing strncmp_mem) to compare up to n characters of a string. (#164)
* Add strings.ncompare (and backing strncmp_mem) to compare up to n characters of a string.

* Document strings.ncompare.
2025-03-07 23:53:56 +01:00
ef23d52ed7 better error if module name clash occurs caused by case-insensitive names 2025-03-07 21:35:35 +01:00
220ab773aa fix asmgen error when assigning address of split word array without explicit adressof syntax 2025-03-06 23:20:42 +01:00
e3e5bff7bb add missing sprites benchmark module 2025-03-06 19:39:55 +01:00
7b9a841b2a fix C64 graphics.plot() it was broken since 10.5 2025-03-06 00:42:17 +01:00
40423911ef added footgun warning when calling labels as subroutine 2025-03-04 22:14:21 +01:00
582a70b046 fix calling label as subroutine (JSR label) 2025-03-04 21:53:53 +01:00
5b63590ebf fix symbol prefixing bug triggered by certain usage of %option no_symbol_prefixing 2025-03-03 22:26:19 +01:00
2b6510dc19 improve error message for non-numeric when choice values 2025-03-03 19:48:45 +01:00
9d49589d73 fix codegen for @(ptr-offset)=value writing bogus values 2025-03-02 23:40:14 +01:00
125b66c929 fix crash in asmgen for boolean comparison with false, when not using optimizations 2025-03-01 23:57:55 +01:00
5255f1c052 print offending source position in more of the TODO errors to help diagnosing 2025-03-01 23:16:34 +01:00
a6ba05d60c compile time evaluation of constant rangecheck 2025-03-01 13:45:11 +01:00
41e963b04b memory() name argument should be string literal, nice error message 2025-03-01 12:38:42 +01:00
6ff75bef29 fix verafx register address typo VERA_FX_POLY_FILL_H 2025-02-27 21:59:04 +01:00
72c16d0d32 avoid doing useless jsr for reboot/poweroff calls 2025-02-26 20:42:38 +01:00
94653e5c8c possible workaround for SMC issue that could make sys.reset_system() and sys.poweroff_system() not work properly 2025-02-24 23:07:51 +01:00
3e2b2a698d Separate simple Ast and Symboltable from codeCore into new simpleAst module. VirtualMachine and Intermediate do not need them, just codeCore. 2025-02-24 22:06:52 +01:00
ae04f5aee8 %option romable now disables floating point support
because if it's your code that is running in the rom bank you can't have the floating point routines available there
2025-02-23 10:21:55 +01:00
5c56267662 Also romable warning for inline variables. Added TODO: Romable in library files where applicable 2025-02-21 21:58:31 +01:00
e55ce5504e added %option romable to enable romable mode, but only generate a bunch of warnings for problematic codegeneration atm 2025-02-20 23:40:44 +01:00
fb1e89d9ef update docs about call convention for multi-value results (first is in A or AY, then R15...R0)
added sprites+coroutines+defer part to benchmark program
2025-02-19 22:10:12 +01:00
bc550a4549 fix optimized multi-value call result assignment 2025-02-19 20:19:20 +01:00
ebdea9cf76 optimized call convention for multi-value return and assign on regular asmsubs (6502) 2025-02-19 20:19:20 +01:00
09ec508f82 avoid unnecessary word extension when assigning a register byte to a byte target 2025-02-19 20:19:20 +01:00
d06e9ea7f6 allow comparisons against constant values with different type 2025-02-19 20:19:20 +01:00
a36bdc54fd Add Notepad++ syntax highlighting file for dark mode (#161) 2025-02-19 18:33:31 +01:00
289 changed files with 16609 additions and 6420 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.1.10" /> <properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.21" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.21/kotlin-stdlib-jdk8-2.1.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.21/kotlin-stdlib-2.1.21.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.1.10/kotlin-stdlib-jdk7-2.1.10.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.21/kotlin-stdlib-jdk7-2.1.21.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.21/kotlin-stdlib-jdk8-2.1.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.21/kotlin-stdlib-2.1.21-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.1.10/kotlin-stdlib-jdk7-2.1.10-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.21/kotlin-stdlib-jdk7-2.1.21-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.10/kotlin-stdlib-jdk8-2.1.10-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.21/kotlin-stdlib-jdk8-2.1.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.10/kotlin-stdlib-2.1.10-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.21/kotlin-stdlib-2.1.21-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.1.10/kotlin-stdlib-jdk7-2.1.10-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.21/kotlin-stdlib-jdk7-2.1.21-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
</component> </component>

View File

@ -1,11 +1,11 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="eclipse.lsp4j" type="repository"> <library name="eclipse.lsp4j" type="repository">
<properties maven-id="org.eclipse.lsp4j:org.eclipse.lsp4j:0.23.1" /> <properties maven-id="org.eclipse.lsp4j:org.eclipse.lsp4j:0.24.0" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.23.1/org.eclipse.lsp4j-0.23.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j/0.24.0/org.eclipse.lsp4j-0.24.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.23.1/org.eclipse.lsp4j.jsonrpc-0.23.1.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/eclipse/lsp4j/org.eclipse.lsp4j.jsonrpc/0.24.0/org.eclipse.lsp4j.jsonrpc-0.24.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.11.0/gson-2.11.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.12.1/gson-2.12.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.27.0/error_prone_annotations-2.27.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.36.0/error_prone_annotations-2.36.0.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC /> <JAVADOC />
<SOURCES /> <SOURCES />

3
.idea/misc.xml generated
View File

@ -12,6 +12,7 @@
<option name="pkg" value="" /> <option name="pkg" value="" />
<option name="language" value="Java" /> <option name="language" value="Java" />
<option name="generateListener" value="false" /> <option name="generateListener" value="false" />
<option name="generateVisitor" value="true" />
</PerGrammarGenerationSettings> </PerGrammarGenerationSettings>
</list> </list>
</option> </option>
@ -22,7 +23,7 @@
<component name="FrameworkDetectionExcludesConfiguration"> <component name="FrameworkDetectionExcludesConfiguration">
<type id="Python" /> <type id="Python" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="openjdk-11" 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>
</project> </project>

1
.idea/modules.xml generated
View File

@ -17,6 +17,7 @@
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" /> <module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" /> <module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
<module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" /> <module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
</modules> </modules>
</component> </component>

View File

@ -63,12 +63,15 @@ What does Prog8 provide?
- various data types other than just bytes (16-bit words, floats, strings) - various data types other than just bytes (16-bit words, floats, strings)
- 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
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
- programs can be configured to execute in ROM
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents. - strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
- automatic static variable allocations, automatic string and array variables and string sharing - automatic static variable allocations, automatic string and array variables and string sharing
- high-level program optimizations - high-level program optimizations
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
- conditional branches that map 1:1 to cpu status flags - conditional branches that map 1:1 to cpu status flags
- ``when`` statement to provide a concise jump table alternative to if/elseif chains - ``when`` statement to provide a concise jump table alternative to if/elseif chains
- ``on .. goto`` statement for fast jump tables
- ``in`` expression for concise and efficient multi-value/containment check - ``in`` expression for concise and efficient multi-value/containment check
- ``defer`` statement to help write concise and robust subroutine cleanup logic - ``defer`` statement to help write concise and robust subroutine cleanup logic
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror`` - several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
@ -93,8 +96,7 @@ What does Prog8 provide?
- "c64": Commodore-64 (6502 like CPU) - "c64": Commodore-64 (6502 like CPU)
- "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported) - "c128": Commodore-128 (6502 like CPU - the Z80 cpu mode is not supported)
- "pet32": Commodore PET (limited support) - "pet32": Commodore PET (limited support)
- "atari": Atari 8 bit such as 800XL (experimental) - via external configurable targets: Atari 800 XL, Neo6502, NES, C64OS, ...
- "neo": Neo6502 (experimental)
- If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag) - If you only use standard kernal and prog8 library routines, it is possible to compile the *exact same program* for different machines (just change the compiler target flag)

View File

@ -12,7 +12,7 @@ circles {
sub draw(bool use_kernal, uword max_time) -> uword { sub draw(bool use_kernal, uword max_time) -> uword {
if use_kernal if use_kernal
void cx16.set_screen_mode(128) cx16.set_screen_mode(128)
else else
gfx_lores.graphics_mode() gfx_lores.graphics_mode()
@ -33,7 +33,7 @@ circles {
} }
if use_kernal if use_kernal
void cx16.set_screen_mode(3) cx16.set_screen_mode(3)
else { else {
gfx_lores.text_mode() gfx_lores.text_mode()
} }

View File

@ -0,0 +1,69 @@
%import sprites
%import coroutines
%import math
animsprites {
uword num_iterations
ubyte[64] sx
ubyte[64] sy
ubyte[64] sc
ubyte[64] dx
ubyte[64] dy
uword maximum_duration
sub benchmark(uword max_duration) -> uword {
maximum_duration = max_duration
math.rndseed(1122,9876)
cx16.set_screen_mode(3)
cx16.mouse_config2(1)
sprites.set_mousepointer_hand()
repeat 64
void coroutines.add(animsprite, 0)
cx16.mouse_config2(0)
cbm.SETTIM(0,0,0)
coroutines.run(supervisor)
sprites.reset(0, 64)
return num_iterations
}
sub supervisor() -> bool {
if cbm.RDTIM16() >= maximum_duration {
coroutines.killall()
return false
}
return true
}
sub animsprite() {
num_iterations++
; set up the sprite
ubyte sprnum = coroutines.current()
cx16.r6L, cx16.r7 = sprites.get_data_ptr(0)
sprites.init(sprnum, cx16.r6L, cx16.r7, sprites.SIZE_16, sprites.SIZE_16, sprites.COLORS_256, 0)
sx[sprnum] = math.rnd()
sy[sprnum] = math.rnd()
sc[sprnum] = math.rnd()
dx[sprnum] = if math.rnd()&1 == 1 1 else 255
dy[sprnum] = if math.rnd()&1 == 1 1 else 255
; move the sprite around
while sc[sprnum]!=0 {
animate(sprnum)
void coroutines.yield()
sprnum = coroutines.current()
}
sub animate(ubyte spr) {
defer sc[spr]--
sprites.pos(spr, sx[spr], sy[spr])
sx[spr] += dx[spr]
sy[spr] += dy[spr]
}
; end the task but replace it with a fresh animated sprite task
void coroutines.add(animsprite, 0)
}
}

View File

@ -15,6 +15,7 @@
%import b_queens %import b_queens
%import b_textelite %import b_textelite
%import b_maze %import b_maze
%import b_sprites
%zeropage basicsafe %zeropage basicsafe
%option no_sysinit %option no_sysinit
@ -29,7 +30,7 @@ main {
sub start() { sub start() {
ubyte benchmark_number ubyte benchmark_number
void cx16.set_screen_mode(3) cx16.set_screen_mode(3)
txt.color2(1, 6) txt.color2(1, 6)
txt.clear_screen() txt.clear_screen()
@ -74,10 +75,14 @@ main {
benchmark_score[benchmark_number] = textelite.bench(120) benchmark_score[benchmark_number] = textelite.bench(120)
benchmark_number++ benchmark_number++
announce_benchmark("sprites-coroutines-defer")
benchmark_score[benchmark_number] = animsprites.benchmark(300)
benchmark_number++
benchmark_names[benchmark_number] = 0 benchmark_names[benchmark_number] = 0
benchmark_score[benchmark_number] = 0 benchmark_score[benchmark_number] = 0
void 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 final_score
@ -99,7 +104,7 @@ main {
sub announce_benchmark(str name) { sub announce_benchmark(str name) {
benchmark_names[benchmark_number] = name benchmark_names[benchmark_number] = name
void cx16.set_screen_mode(3) cx16.set_screen_mode(3)
txt.uppercase() txt.uppercase()
txt.color2(1, 6) txt.color2(1, 6)
txt.clear_screen() txt.clear_screen()

View File

@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
plugins { plugins {
kotlin("jvm") version "2.1.10" kotlin("jvm") version "2.1.21"
} }
allprojects { allprojects {

View File

@ -0,0 +1,31 @@
package prog8.code
import java.io.IOException
import java.nio.file.Path
import kotlin.io.path.absolute
// the automatically generated module where all string literals are interned to:
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
// all automatically generated labels everywhere need to have the same label name prefix:
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
/**
* Returns the absolute path of the given path,
* where links are replaced by the actual directories,
* and containing no redundant path elements.
* If the path doesn't refer to an existing directory or file on the file system,
* it is returned unchanged.
*/
fun Path.sanitize(): Path {
return try {
this.toRealPath().normalize()
} catch (_: java.nio.file.NoSuchFileException) {
this.absolute().normalize()
//throw NoSuchFileException(this.toFile(), null, nx.reason).also { it.initCause(nx) }
} catch (iox: IOException) {
throw FileSystemException(this.toFile()).also { it.initCause(iox) }
}
}

View File

@ -1,16 +0,0 @@
package prog8.code.ast
import prog8.code.SymbolTable
import prog8.code.core.*
fun verifyFinalAstBeforeAsmGen(program: PtProgram, options: CompilationOptions, st: SymbolTable, errors: IErrorReporter) {
/*
walkAst(program) { node, _ ->
if(node is PtVariable) {
if(node.value!=null) {
// require(node.type in ArrayDatatypes || node.type==DataType.STR) { "final check before asmgen: only string and array variables can still have an init value ${node.name} ${node.type} ${node.position}"}
}
}
}
*/
}

View File

@ -101,7 +101,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"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())),
"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)),

View File

@ -11,6 +11,7 @@ class CompilationOptions(val output: OutputType,
val zpAllowed: List<UIntRange>, val zpAllowed: List<UIntRange>,
val floats: Boolean, val floats: Boolean,
val noSysInit: Boolean, val noSysInit: Boolean,
val romable: Boolean,
val compTarget: ICompilationTarget, val compTarget: ICompilationTarget,
// these are set later, based on command line arguments or options in the source code: // these are set later, based on command line arguments or options in the source code:
var loadAddress: UInt, var loadAddress: UInt,
@ -31,6 +32,7 @@ class CompilationOptions(val output: OutputType,
var breakpointCpuInstruction: String? = null, var breakpointCpuInstruction: String? = null,
var ignoreFootguns: Boolean = false, var ignoreFootguns: Boolean = false,
var outputDir: Path = Path(""), var outputDir: Path = Path(""),
var quiet: Boolean = false,
var symbolDefs: Map<String, String> = emptyMap() var symbolDefs: Map<String, String> = emptyMap()
) { ) {
init { init {

View File

@ -1,6 +1,6 @@
package prog8.code.core package prog8.code.core
import java.util.Objects import java.util.*
enum class BaseDataType { enum class BaseDataType {
UBYTE, // pass by value 8 bits unsigned UBYTE, // pass by value 8 bits unsigned
@ -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,73 +55,164 @@ 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
}
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, null)
val BYTE = DataType(BaseDataType.BYTE, null, null)
val UWORD = DataType(BaseDataType.UWORD, null, null)
val WORD = DataType(BaseDataType.WORD, null, null)
val LONG = DataType(BaseDataType.LONG, null, null)
val FLOAT = DataType(BaseDataType.FLOAT, null, null)
val BOOL = DataType(BaseDataType.BOOL, null, null)
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE, 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)
return DataType(BaseDataType.POINTER, dt.base, null)
else
return DataType(BaseDataType.POINTER, null, dt.subType, dt.subTypeFromAntlr)
}
fun pointer(structType: ISubType): DataType = DataType(BaseDataType.POINTER, null, structType, null)
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 -> {
@ -137,6 +233,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()
} }
@ -149,6 +254,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["
@ -176,18 +305,36 @@ 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 -> TODO("check assignability of array of pointers")
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)
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
@ -203,6 +350,9 @@ 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 isPointer = base.isPointer
val isStructInstance = base.isStructInstance
val isPointerArray = base.isPointerArray
val isBoolArray = base.isArray && sub == BaseDataType.BOOL val isBoolArray = base.isArray && sub == BaseDataType.BOOL
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE) val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
@ -269,7 +419,7 @@ 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)
} }

View File

@ -28,6 +28,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
val libraryPath: Path? val libraryPath: Path?
val customLauncher: List<String> val customLauncher: List<String>
val additionalAssemblerOptions: String? val additionalAssemblerOptions: String?
val defaultOutputType: OutputType
fun initializeMemoryAreas(compilerOptions: CompilationOptions) fun initializeMemoryAreas(compilerOptions: CompilationOptions)
fun getFloatAsmBytes(num: Number): String fun getFloatAsmBytes(num: Number): String
@ -35,7 +36,7 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
fun convertFloatToBytes(num: Double): List<UByte> fun convertFloatToBytes(num: Double): List<UByte>
fun convertBytesToFloat(bytes: List<UByte>): Double fun convertBytesToFloat(bytes: List<UByte>): Double
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean)
fun isIOAddress(address: UInt): Boolean fun isIOAddress(address: UInt): Boolean
override fun encodeString(str: String, encoding: Encoding): List<UByte> override fun encodeString(str: String, encoding: Encoding): List<UByte>

View File

@ -13,4 +13,6 @@ interface IErrorReporter {
} }
fun noErrorForLine(position: Position): Boolean fun noErrorForLine(position: Position): Boolean
fun printSingleError(errormessage: String)
} }

View File

@ -5,7 +5,7 @@ interface IMemSizer {
fun memorySize(dt: BaseDataType): Int { fun memorySize(dt: BaseDataType): Int {
if(dt.isPassByRef) if(dt.isPassByRef)
return memorySize(DataType.forDt(BaseDataType.UWORD), null) // a pointer size return memorySize(DataType.UWORD, null) // a pointer size
try { try {
return memorySize(DataType.forDt(dt), null) return memorySize(DataType.forDt(dt), null)
} catch (x: NoSuchElementException) { } catch (x: NoSuchElementException) {

View File

@ -82,7 +82,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
} }
datatype.isFloat -> { datatype.isFloat -> {
if (options.floats) { if (options.floats) {
val memsize = options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null) val memsize = options.compTarget.memorySize(DataType.FLOAT, null)
if(position!=null) if(position!=null)
errors.warn("allocating a large value in zeropage; float $memsize bytes", position) errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
else else
@ -154,7 +154,7 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!) datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
datatype.isFloat -> { datatype.isFloat -> {
if (options.floats) { if (options.floats) {
options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null) options.compTarget.memorySize(DataType.FLOAT, null)
} else return Err(MemAllocationError("floating point option not enabled")) } else return Err(MemAllocationError("floating point option not enabled"))
} }
else -> throw MemAllocationError("weird dt") else -> throw MemAllocationError("weird dt")

View File

@ -1,9 +1,9 @@
package prog8.code.core package prog8.code.core
import prog8.code.sanitize
import prog8.code.source.SourceCode import prog8.code.source.SourceCode
import java.nio.file.InvalidPathException import java.nio.file.InvalidPathException
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.io.path.absolute
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) { data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]" override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
@ -13,7 +13,7 @@ data class Position(val file: String, val line: Int, val startCol: Int, val endC
if(SourceCode.isLibraryResource(file)) if(SourceCode.isLibraryResource(file))
return "$file:$line:$startCol:" return "$file:$line:$startCol:"
return try { return try {
val path = Path(file).absolute().normalize().toString() val path = Path(file).sanitize().toString()
"file://$path:$line:$startCol:" "file://$path:$line:$startCol:"
} catch(_: InvalidPathException) { } catch(_: InvalidPathException) {
// this can occur on Windows when the source origin contains "invalid" characters such as ':' // this can occur on Windows when the source origin contains "invalid" characters such as ':'

View File

@ -1,10 +1,10 @@
package prog8.code.source package prog8.code.source
import prog8.code.core.Position import prog8.code.core.Position
import prog8.code.sanitize
import java.nio.file.Path import java.nio.file.Path
import java.util.TreeMap import java.util.*
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.io.path.absolute
// Resource caching "filesystem". // Resource caching "filesystem".
@ -22,7 +22,7 @@ object ImportFileSystem {
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString())) fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
fun getFile(path: Path, isLibrary: Boolean=false): SourceCode { fun getFile(path: Path, isLibrary: Boolean=false): SourceCode {
val normalized = path.absolute().normalize() val normalized = path.sanitize()
val cached = cache[normalized.toString()] val cached = cache[normalized.toString()]
if (cached != null) if (cached != null)
return cached return cached
@ -48,7 +48,7 @@ object ImportFileSystem {
val cached = cache[position.file] val cached = cache[position.file]
if(cached != null) if(cached != null)
return getLine(cached, position.line) return getLine(cached, position.line)
val path = Path(position.file).absolute().normalize() val path = Path(position.file).sanitize()
val cached2 = cache[path.toString()] val cached2 = cache[path.toString()]
if(cached2 != null) if(cached2 != null)
return getLine(cached2, position.line) return getLine(cached2, position.line)

View File

@ -1,5 +1,6 @@
package prog8.code.source package prog8.code.source
import prog8.code.sanitize
import java.io.IOException import java.io.IOException
import java.nio.file.Path import java.nio.file.Path
import java.text.Normalizer import java.text.Normalizer
@ -59,7 +60,7 @@ sealed class SourceCode {
private const val LIBRARYFILEPREFIX = "library:" private const val LIBRARYFILEPREFIX = "library:"
private const val STRINGSOURCEPREFIX = "string:" private const val STRINGSOURCEPREFIX = "string:"
val curdir: Path = Path(".").absolute() val curdir: Path = Path(".").absolute()
fun relative(path: Path): Path = curdir.relativize(path.absolute()) fun relative(path: Path): Path = curdir.relativize(path.sanitize())
fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString) fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX) fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX) fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)

View File

@ -6,12 +6,16 @@ import prog8.code.target.zp.C128Zeropage
import java.nio.file.Path import java.nio.file.Path
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) { class C128Target: ICompilationTarget,
IStringEncoding by Encoder(true),
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
override val libraryPath = null override val libraryPath = null
override val customLauncher: List<String> = emptyList() override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object { companion object {
const val NAME = "c128" const val NAME = "c128"
@ -27,10 +31,10 @@ class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
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
@ -48,17 +52,21 @@ class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
return m5.toDouble() return m5.toDouble()
} }
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
if(selectedEmulator!=1) { if(selectedEmulator!=1) {
System.err.println("The c128 target only supports the main emulator (Vice).") System.err.println("The c128 target only supports the main emulator (Vice).")
return return
} }
println("\nStarting C-128 emulator x128...") if(!quiet)
println("\nStarting C-128 emulator x128...")
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString()) val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist, val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO() val processb = ProcessBuilder(cmdline)
if(!quiet)
processb.inheritIO()
val process: Process = processb.start() val process: Process = processb.start()
process.waitFor() process.waitFor()
} }

View File

@ -7,12 +7,16 @@ import java.io.IOException
import java.nio.file.Path import java.nio.file.Path
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) { class C64Target: ICompilationTarget,
IStringEncoding by Encoder(true),
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
override val name = NAME override val name = NAME
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII
override val libraryPath = null override val libraryPath = null
override val customLauncher: List<String> = emptyList() override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object { companion object {
const val NAME = "c64" const val NAME = "c64"
@ -52,18 +56,22 @@ class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
return m5.toDouble() return m5.toDouble()
} }
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
if(selectedEmulator!=1) { if(selectedEmulator!=1) {
System.err.println("The c64 target only supports the main emulator (Vice).") System.err.println("The c64 target only supports the main emulator (Vice).")
return return
} }
for(emulator in listOf("x64sc", "x64")) { for(emulator in listOf("x64sc", "x64")) {
println("\nStarting C-64 emulator $emulator...") if(!quiet)
println("\nStarting C-64 emulator $emulator...")
val viceMonlist = viceMonListName(programNameWithPath.toString()) val viceMonlist = viceMonListName(programNameWithPath.toString())
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist, val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO() val processb = ProcessBuilder(cmdline)
if(!quiet)
processb.inheritIO()
val process: Process val process: Process
try { try {
process=processb.start() process=processb.start()

View File

@ -24,6 +24,7 @@ class ConfigFileTarget(
override val BSSHIGHRAM_END: UInt, override val BSSHIGHRAM_END: UInt,
override val BSSGOLDENRAM_START: UInt, override val BSSGOLDENRAM_START: UInt,
override val BSSGOLDENRAM_END: UInt, override val BSSGOLDENRAM_END: UInt,
override val defaultOutputType: OutputType,
override val libraryPath: Path, override val libraryPath: Path,
override val customLauncher: List<String>, override val customLauncher: List<String>,
override val additionalAssemblerOptions: String?, override val additionalAssemblerOptions: String?,
@ -36,7 +37,7 @@ class ConfigFileTarget(
val zpFullsafe: List<UIntRange>, val zpFullsafe: List<UIntRange>,
val zpKernalsafe: List<UIntRange>, val zpKernalsafe: List<UIntRange>,
val zpBasicsafe: List<UIntRange> val zpBasicsafe: List<UIntRange>
): ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(8) { ): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) {
companion object { companion object {
@ -108,7 +109,10 @@ class ConfigFileTarget(
(customLauncherStr+"\n").lines().map { it.trimEnd() } (customLauncherStr+"\n").lines().map { it.trimEnd() }
else emptyList() else emptyList()
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim() val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
val assemblerOptions = if(assemblerOptionsStr.isBlank()) null else assemblerOptionsStr val assemblerOptions = assemblerOptionsStr.ifBlank { null }
val outputTypeString = props.getProperty("output_type", "PRG")
val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
return ConfigFileTarget( return ConfigFileTarget(
configfile.nameWithoutExtension, configfile.nameWithoutExtension,
@ -121,6 +125,7 @@ class ConfigFileTarget(
props.getInteger("bss_highram_end"), props.getInteger("bss_highram_end"),
props.getInteger("bss_goldenram_start"), props.getInteger("bss_goldenram_start"),
props.getInteger("bss_goldenram_end"), props.getInteger("bss_goldenram_end"),
defaultOutputType,
libraryPath, libraryPath,
customLauncher, customLauncher,
assemblerOptions, assemblerOptions,
@ -148,7 +153,7 @@ class ConfigFileTarget(
override fun getFloatAsmBytes(num: Number) = TODO("floats") override fun getFloatAsmBytes(num: Number) = TODO("floats")
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats") override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats")
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats") override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats")
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.") throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.")
} }

View File

@ -6,12 +6,16 @@ import prog8.code.target.zp.CX16Zeropage
import java.nio.file.Path import java.nio.file.Path
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) { class Cx16Target: ICompilationTarget,
IStringEncoding by Encoder(true),
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
override val name = NAME override val name = NAME
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII
override val libraryPath = null override val libraryPath = null
override val customLauncher: List<String> = emptyList() override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object { companion object {
const val NAME = "cx16" const val NAME = "cx16"
@ -48,7 +52,7 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
return m5.toDouble() return m5.toDouble()
} }
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
val emulator: String val emulator: String
val extraArgs: List<String> val extraArgs: List<String>
@ -67,9 +71,13 @@ class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
} }
} }
println("\nStarting Commander X16 emulator $emulator...") if(!quiet)
println("\nStarting Commander X16 emulator $emulator...")
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
val processb = ProcessBuilder(cmdline).inheritIO() val processb = ProcessBuilder(cmdline)
if(!quiet)
processb.inheritIO()
processb.environment()["PULSE_LATENCY_MSEC"] = "10" processb.environment()["PULSE_LATENCY_MSEC"] = "10"
val process: Process = processb.start() val process: Process = processb.start()
process.waitFor() process.waitFor()

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
@ -18,14 +20,16 @@ internal class NormalMemSizer(val floatsize: Int): IMemSizer {
} }
} }
else if (dt.isString) { else if (dt.isString) {
if(numElements!=null) return numElements // treat it as the size of the given string with the length return numElements // treat it as the size of the given string with the length
else return 2 // treat it as the size to store a string pointer ?: 2 // treat it as the size to store a string pointer
} }
return when { return when {
dt.isByteOrBool -> 1 * (numElements ?: 1) dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> floatsize * (numElements ?: 1) dt.isFloat -> floatsize * (numElements ?: 1)
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory") 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

@ -6,12 +6,16 @@ import prog8.code.target.zp.PETZeropage
import java.nio.file.Path import java.nio.file.Path
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) { class PETTarget: ICompilationTarget,
IStringEncoding by Encoder(true),
IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
override val name = NAME override val name = NAME
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII
override val libraryPath = null override val libraryPath = null
override val customLauncher: List<String> = emptyList() override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object { companion object {
const val NAME = "pet32" const val NAME = "pet32"
@ -47,17 +51,21 @@ class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by No
return m5.toDouble() return m5.toDouble()
} }
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
if(selectedEmulator!=1) { if(selectedEmulator!=1) {
System.err.println("The pet target only supports the main emulator (Vice).") System.err.println("The pet target only supports the main emulator (Vice).")
return return
} }
println("\nStarting PET emulator...") if(!quiet)
println("\nStarting PET emulator...")
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString()) val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist, val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg") "-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO() val processb = ProcessBuilder(cmdline)
if(!quiet)
processb.inheritIO()
val process=processb.start() val process=processb.start()
process.waitFor() process.waitFor()
} }

View File

@ -7,12 +7,16 @@ 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
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) { class VMTarget: ICompilationTarget,
IStringEncoding by Encoder(false),
IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
override val name = NAME override val name = NAME
override val defaultEncoding = Encoding.ISO override val defaultEncoding = Encoding.ISO
override val libraryPath = null override val libraryPath = null
override val customLauncher: List<String> = emptyList() override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object { companion object {
const val NAME = "virtual" const val NAME = "virtual"
@ -21,8 +25,8 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
override val cpu = CpuType.VIRTUAL override val cpu = CpuType.VIRTUAL
override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE.toDouble() override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE
override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE.toDouble() override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE
override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
@ -39,7 +43,7 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
// little endian binary representation // little endian binary representation
val bits = num.toDouble().toBits().toULong() val bits = num.toDouble().toBits().toULong()
val hexStr = bits.toString(16).padStart(16, '0') val hexStr = bits.toString(16).padStart(16, '0')
val parts = hexStr.chunked(2).map { "\$" + it } val parts = hexStr.chunked(2).map { "$$it" }
return parts.joinToString(", ") return parts.joinToString(", ")
} }
@ -63,19 +67,21 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7) return Double.fromBits(b0 or b1 or b2 or b3 or b4 or b5 or b6 or b7)
} }
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) { override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
println("\nStarting Virtual Machine...") if(!quiet)
println("\nStarting Virtual Machine...")
// 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 filename = programNameWithPath.name
if(programNameWithPath.isReadable()) { if(programNameWithPath.isReadable()) {
vm.runProgram(programNameWithPath.readText()) vm.runProgram(programNameWithPath.readText(), quiet)
} else { } else {
val withExt = programNameWithPath.resolveSibling("$filename.p8ir") val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
if(withExt.isReadable()) if(withExt.isReadable())
vm.runProgram(withExt.readText()) vm.runProgram(withExt.readText(), quiet)
else else
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file") throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
} }
} }
@ -85,37 +91,12 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
zeropage = VirtualZeropage(compilerOptions) zeropage = VirtualZeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
} }
override fun memorySize(dt: DataType, numElements: Int?): Int {
if(dt.isArray) {
if(numElements==null) return 2 // treat it as a pointer size
return when(dt.sub) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
BaseDataType.FLOAT-> numElements * FLOAT_MEM_SIZE
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
else -> throw IllegalArgumentException("invalid sub type")
}
}
else if (dt.isString) {
if(numElements!=null) return numElements // treat it as the size of the given string with the length
else return 2 // treat it as the size to store a string pointer
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> FLOAT_MEM_SIZE * (numElements ?: 1)
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
else -> 2 * (numElements ?: 1)
}
}
} }
interface IVirtualMachineRunner { interface IVirtualMachineRunner {
fun runProgram(irSource: String) fun runProgram(irSource: String, quiet: Boolean)
} }
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) { private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {

View File

@ -26,7 +26,7 @@ object AtasciiEncoding {
'▖', '▖',
// $10 // $10
'♣', '♣',
'┌', '┌',
'─', '─',
'┼', '┼',
@ -62,7 +62,7 @@ object AtasciiEncoding {
'/', '/',
// $30 // $30
'0', '0',
'1', '1',
'2', '2',
'3', '3',
@ -80,7 +80,7 @@ object AtasciiEncoding {
'?', '?',
// $40 // $40
'@', '@',
'A', 'A',
'B', 'B',
'C', 'C',
@ -197,6 +197,7 @@ object AtasciiEncoding {
fun encode(str: String): Result<List<UByte>, CharConversionException> { fun encode(str: String): Result<List<UByte>, CharConversionException> {
val mapped = str.map { chr -> val mapped = str.map { chr ->
when (chr) { when (chr) {
'\r' -> 0x9bu
'\u0000' -> 0u '\u0000' -> 0u
in '\u8000'..'\u80ff' -> { in '\u8000'..'\u80ff' -> {
// special case: take the lower 8 bit hex value directly // special case: take the lower 8 bit hex value directly

View File

@ -285,6 +285,7 @@ object C64osEncoding {
val screencode = encodingC64os[chr] val screencode = encodingC64os[chr]
return screencode?.toUByte() ?: when (chr) { return screencode?.toUByte() ?: when (chr) {
'\u0000' -> 0u '\u0000' -> 0u
'\n' -> 13u
in '\u8000'..'\u80ff' -> { in '\u8000'..'\u80ff' -> {
// special case: take the lower 8 bit hex value directly // special case: take the lower 8 bit hex value directly
(chr.code - 0x8000).toUByte() (chr.code - 0x8000).toUByte()

View File

@ -5,19 +5,19 @@ import prog8.code.core.Encoding
import prog8.code.core.IStringEncoding import prog8.code.core.IStringEncoding
import prog8.code.core.InternalCompilerException import prog8.code.core.InternalCompilerException
object Encoder: IStringEncoding { class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
override val defaultEncoding: Encoding = Encoding.ISO override val defaultEncoding: Encoding = Encoding.ISO
override fun encodeString(str: String, encoding: Encoding): List<UByte> { override fun encodeString(str: String, encoding: Encoding): List<UByte> {
val coded = when(encoding) { val coded = when(encoding) {
Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true) Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true) Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
Encoding.ISO -> IsoEncoding.encode(str) Encoding.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn)
Encoding.ATASCII -> AtasciiEncoding.encode(str) Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn)
Encoding.ISO5 -> IsoCyrillicEncoding.encode(str) Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn)
Encoding.ISO16 -> IsoEasternEncoding.encode(str)
Encoding.CP437 -> Cp437Encoding.encode(str) Encoding.CP437 -> Cp437Encoding.encode(str)
Encoding.KATAKANA -> KatakanaEncoding.encode(str) Encoding.KATAKANA -> KatakanaEncoding.encode(str, newlineToCarriageReturn)
Encoding.ATASCII -> AtasciiEncoding.encode(str)
Encoding.C64OS -> C64osEncoding.encode(str) Encoding.C64OS -> C64osEncoding.encode(str)
else -> throw InternalCompilerException("unsupported encoding $encoding") else -> throw InternalCompilerException("unsupported encoding $encoding")
} }
@ -30,12 +30,12 @@ object Encoder: IStringEncoding {
val decoded = when(encoding) { val decoded = when(encoding) {
Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true) Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true) Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
Encoding.ISO -> IsoEncoding.decode(bytes) Encoding.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn)
Encoding.ATASCII -> AtasciiEncoding.decode(bytes) Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn)
Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes) Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn)
Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
Encoding.CP437 -> Cp437Encoding.decode(bytes) Encoding.CP437 -> Cp437Encoding.decode(bytes)
Encoding.KATAKANA -> KatakanaEncoding.decode(bytes) Encoding.KATAKANA -> KatakanaEncoding.decode(bytes, newlineToCarriageReturn)
Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
Encoding.C64OS -> C64osEncoding.decode(bytes) Encoding.C64OS -> C64osEncoding.decode(bytes)
else -> throw InternalCompilerException("unsupported encoding $encoding") else -> throw InternalCompilerException("unsupported encoding $encoding")
} }

View File

@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result
import java.io.CharConversionException import java.io.CharConversionException
import java.nio.charset.Charset import java.nio.charset.Charset
open class IsoEncodingBase(charsetName: String) { open class IsoEncodingBase(charsetName: String) {
val charset: Charset = Charset.forName(charsetName) val charset: Charset = Charset.forName(charsetName)
fun encode(str: String): Result<List<UByte>, CharConversionException> { fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
return try { return try {
val mapped = str.map { chr -> val mapped = str.map { chr ->
when (chr) { when (chr) {
'\u0000' -> 0u '\u0000' -> 0u
'\n' -> if(newlineToCarriageReturn) 13u else 10u
in '\u8000'..'\u80ff' -> { in '\u8000'..'\u80ff' -> {
// special case: take the lower 8 bit hex value directly // special case: take the lower 8 bit hex value directly
(chr.code - 0x8000).toUByte() (chr.code - 0x8000).toUByte()
@ -27,9 +29,14 @@ open class IsoEncodingBase(charsetName: String) {
} }
} }
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> { fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
return try { return try {
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset)) Ok(String(bytes.map {
when(it) {
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
else -> it.toByte()
}
}.toByteArray(), charset))
} catch (ce: CharConversionException) { } catch (ce: CharConversionException) {
Err(ce) Err(ce)
} }

View File

@ -64,10 +64,11 @@ object JapaneseCharacterConverter {
object KatakanaEncoding { object KatakanaEncoding {
val charset: Charset = Charset.forName("JIS_X0201") val charset: Charset = Charset.forName("JIS_X0201")
fun encode(str: String): Result<List<UByte>, CharConversionException> { fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
return try { return try {
val mapped = str.map { chr -> val mapped = str.map { chr ->
when (chr) { when (chr) {
'\n' -> if(newlineToCarriageReturn) 13u else 10u
'\u0000' -> 0u '\u0000' -> 0u
'\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves '\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
@ -112,9 +113,14 @@ object KatakanaEncoding {
} }
} }
fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> { fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
return try { return try {
Ok(String(bytes.map { it.toByte() }.toByteArray(), charset)) Ok(String(bytes.map {
when(it) {
13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
else -> it.toByte()
}
}.toByteArray(), charset))
} catch (ce: CharConversionException) { } catch (ce: CharConversionException) {
Err(ce) Err(ce)
} }

View File

@ -21,7 +21,7 @@ object PetsciiEncoding {
'\ufffe', // 0x07 -> UNDEFINED '\ufffe', // 0x07 -> UNDEFINED
'\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS) '\uf118', // 0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
'\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS) '\uf119', // 0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
'\ufffe', // 0x0A -> UNDEFINED '\n', // 0x0A -> LINE FEED (RETURN)
'\ufffe', // 0x0B -> UNDEFINED '\ufffe', // 0x0B -> UNDEFINED
'\ufffe', // 0x0C -> UNDEFINED '\ufffe', // 0x0C -> UNDEFINED
'\n' , // 0x0D -> LINE FEED (RETURN) '\n' , // 0x0D -> LINE FEED (RETURN)
@ -1117,6 +1117,8 @@ object PetsciiEncoding {
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr] val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
return screencode?.toUByte() ?: when (chr) { return screencode?.toUByte() ?: when (chr) {
'\u0000' -> 0u '\u0000' -> 0u
'\n' -> 141u
'\r' -> 141u
in '\u8000'..'\u80ff' -> { in '\u8000'..'\u80ff' -> {
// special case: take the lower 8 bit hex value directly // special case: take the lower 8 bit hex value directly
(chr.code - 0x8000).toUByte() (chr.code - 0x8000).toUByte()

View File

@ -16,12 +16,6 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
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
init { init {
if (options.floats) {
throw InternalCompilerException("C128 target doesn't yet support floating point routines")
// note: in git commit labeled 'c128: remove floats module' the floats.p8 and floats.asm files are removed,
// they could be retrieved again at a later time if the compiler somehow *does* store the fp variables in bank1.
}
if (options.floats && options.zeropage !in arrayOf( if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE, ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE, ZeropageType.BASICSAFE,

View File

@ -85,12 +85,12 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer) // This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02). // The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
for(reg in 0..15) { for(reg in 0..15) {
allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15 allocatedVariables["cx16.r${reg}"] = VarAllocation((4+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s allocatedVariables["cx16.r${reg}s"] = VarAllocation((4+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L allocatedVariables["cx16.r${reg}L"] = VarAllocation((4+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H allocatedVariables["cx16.r${reg}H"] = VarAllocation((5+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL allocatedVariables["cx16.r${reg}sL"] = VarAllocation((4+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH allocatedVariables["cx16.r${reg}sH"] = VarAllocation((5+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
free.remove((4+reg*2).toUInt()) free.remove((4+reg*2).toUInt())
free.remove((5+reg*2).toUInt()) free.remove((5+reg*2).toUInt())
} }

View File

@ -57,12 +57,12 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well. // However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer) // This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
for(reg in 0..15) { for(reg in 0..15) {
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15 allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15
allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s allocatedVariables["cx16.r${reg}s"] = VarAllocation((2+reg*2).toUInt(), DataType.WORD, 2) // cx16.r0s .. cx16.r15s
allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L allocatedVariables["cx16.r${reg}L"] = VarAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H allocatedVariables["cx16.r${reg}H"] = VarAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL allocatedVariables["cx16.r${reg}sL"] = VarAllocation((2+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH allocatedVariables["cx16.r${reg}sH"] = VarAllocation((3+reg*2).toUInt(), DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
} }
} }
} }

View File

@ -1,6 +1,9 @@
package prog8.code.target.zp package prog8.code.target.zp
import prog8.code.core.* import prog8.code.core.CompilationOptions
import prog8.code.core.DataType
import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
class ConfigurableZeropage( class ConfigurableZeropage(
override val SCRATCH_B1: UInt, // temp storage for a single byte override val SCRATCH_B1: UInt, // temp storage for a single byte
@ -16,7 +19,7 @@ class ConfigurableZeropage(
init { init {
if (options.floats) { if (options.floats) {
TODO("floats") TODO("floats in configurable target zp")
} }
if(SCRATCH_REG!=SCRATCH_B1+1u) if(SCRATCH_REG!=SCRATCH_B1+1u)
@ -27,7 +30,7 @@ class ConfigurableZeropage(
ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) } ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) } ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
ZeropageType.KERNALSAFE -> kernalsafe.forEach { free.addAll(it) } ZeropageType.KERNALSAFE -> kernalsafe.forEach { free.addAll(it) }
ZeropageType.FLOATSAFE -> TODO("floatsafe") ZeropageType.FLOATSAFE -> TODO("floatsafe in configurable target zp")
} }
val distinctFree = free.distinct() val distinctFree = free.distinct()
@ -46,12 +49,12 @@ class ConfigurableZeropage(
for(reg in 0..15) { for(reg in 0..15) {
val address = virtualRegistersStart + (2*reg).toUInt() val address = virtualRegistersStart + (2*reg).toUInt()
if(address<=0xffu) { if(address<=0xffu) {
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.forDt(BaseDataType.UWORD), 2) // cx16.r0 .. cx16.r15 allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.UWORD, 2) // cx16.r0 .. cx16.r15
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.forDt(BaseDataType.WORD), 2) // cx16.r0s .. cx16.r15s allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.WORD, 2) // cx16.r0s .. cx16.r15s
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0L .. cx16.r15L allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.forDt(BaseDataType.UBYTE), 1) // cx16.r0H .. cx16.r15H allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sL .. cx16.r15sL allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.forDt(BaseDataType.BYTE), 1) // cx16.r0sH .. cx16.r15sH allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
} }
} }
} }

View File

@ -16,10 +16,6 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
init { init {
if (options.floats) {
throw InternalCompilerException("PET target doesn't yet support floating point routines")
}
if (options.floats && options.zeropage !in arrayOf( if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE, ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE, ZeropageType.BASICSAFE,

View File

@ -6,6 +6,7 @@ plugins {
dependencies { dependencies {
implementation(project(":codeCore")) implementation(project(":codeCore"))
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.0.1")

View File

@ -11,6 +11,7 @@
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<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.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" />

View File

@ -1,10 +1,7 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold import com.github.michaelbull.result.fold
import prog8.code.StNode import prog8.code.*
import prog8.code.StNodeType
import prog8.code.SymbolTable
import prog8.code.SymbolTableMaker
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.code.source.ImportFileSystem import prog8.code.source.ImportFileSystem
@ -12,7 +9,6 @@ import prog8.code.source.SourceCode
import prog8.code.target.Cx16Target import prog8.code.target.Cx16Target
import prog8.codegen.cpu6502.assignment.* import prog8.codegen.cpu6502.assignment.*
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.io.path.absolute
import kotlin.io.path.writeLines import kotlin.io.path.writeLines
@ -39,9 +35,12 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
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(PtLabel.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}" // don't prefix autogenerated 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 -> { /* do nothing */ }
} }
} }
@ -54,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) {
@ -90,9 +86,35 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
node.children.forEach { prefixSymbols(it) } node.children.forEach { prefixSymbols(it) }
} }
fun maybePrefixFunctionCallsAndIdentifierReferences(node: PtNode) {
if(node is PtFunctionCall) {
// function calls to subroutines defined in a block that does NOT have NoSymbolPrefixing, still have to be prefixed at the call site
val stNode = st.lookup(node.name)!!
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
functionCallsToPrefix += node.parent to index
}
}
else if (node is PtIdentifier) {
// identifier references to things defined in a block that does NOT have NoSymbolPrefixing, still have to be prefixed at the referencing point
var lookupName = node.name
if(node.type.isSplitWordArray && (lookupName.endsWith("_lsb") || lookupName.endsWith("_msb"))) {
lookupName = lookupName.dropLast(4)
}
val stNode = st.lookup(lookupName) ?: throw AssemblyError("unknown identifier $node")
if(stNode.astNode!!.definingBlock()?.options?.noSymbolPrefixing!=true) {
val index = node.parent.children.indexOf(node)
nodesToPrefix += node.parent to index
}
}
node.children.forEach { maybePrefixFunctionCallsAndIdentifierReferences(it) }
}
program.allBlocks().forEach { block -> program.allBlocks().forEach { block ->
if (!block.options.noSymbolPrefixing) { if (!block.options.noSymbolPrefixing) {
prefixSymbols(block) prefixSymbols(block)
} else {
maybePrefixFunctionCallsAndIdentifierReferences(block)
} }
} }
@ -109,7 +131,9 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
functionCallsToPrefix.reversed().forEach { (parent, index) -> functionCallsToPrefix.reversed().forEach { (parent, index) ->
val node = parent.children[index] val node = parent.children[index]
if(node is PtFunctionCall) { if(node is PtFunctionCall) {
parent.children[index] = node.prefix(parent) val prefixedName = PtIdentifier(node.name, DataType.UNDEFINED, Position.DUMMY).prefix(parent, st)
val prefixedNode = node.withNewName(prefixedName.name)
parent.children[index] = prefixedNode
} else { } else {
throw AssemblyError("expected PtFunctionCall") throw AssemblyError("expected PtFunctionCall")
} }
@ -121,14 +145,14 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
private fun prefixScopedName(name: String, type: Char): String { private fun prefixScopedName(name: String, type: Char): String {
if('.' !in name) { if('.' !in name) {
if(name.startsWith(PtLabel.GENERATED_LABEL_PREFIX)) if(name.startsWith(GENERATED_LABEL_PREFIX))
return name return name
return "p8${type}_$name" return "p8${type}_$name"
} }
val parts = name.split('.') val parts = name.split('.')
val firstPrefixed = "p8b_${parts[0]}" val firstPrefixed = "p8b_${parts[0]}"
val lastPart = parts.last() val lastPart = parts.last()
val lastPrefixed = if(lastPart.startsWith(PtLabel.GENERATED_LABEL_PREFIX)) lastPart else "p8${type}_$lastPart" val lastPrefixed = if(lastPart.startsWith(GENERATED_LABEL_PREFIX)) lastPart else "p8${type}_$lastPart"
// the parts in between are assumed to be subroutine scopes. // the parts in between are assumed to be subroutine scopes.
val inbetweenPrefixed = parts.drop(1).dropLast(1).map{ "p8s_$it" } val inbetweenPrefixed = parts.drop(1).dropLast(1).map{ "p8s_$it" }
val prefixed = listOf(firstPrefixed) + inbetweenPrefixed + listOf(lastPrefixed) val prefixed = listOf(firstPrefixed) + inbetweenPrefixed + listOf(lastPrefixed)
@ -151,10 +175,10 @@ 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, 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)
} }
@ -162,16 +186,20 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
else -> throw AssemblyError("weird array value element $elt") else -> throw AssemblyError("weird array value element $elt")
} }
} }
val result = PtVariable(name, type, zeropage, align, newValue, arraySize, position) val result = PtVariable(name, type, zeropage, align, dirty, newValue, arraySize, position)
result.parent = parent result.parent = parent
result result
} }
else this else this
} }
private fun PtFunctionCall.prefix(parent: PtNode): PtFunctionCall { //private fun PtFunctionCall.prefix(targetType: Char): PtFunctionCall {
val newName = prefixScopedName(name, 's') // val newName = prefixScopedName(name, targetType)
val call = PtFunctionCall(newName, void, type, position) // return this.withNewName(newName)
//}
private fun PtFunctionCall.withNewName(name: String): PtFunctionCall {
val call = PtFunctionCall(name, void, type, position)
call.children.addAll(children) call.children.addAll(children)
call.children.forEach { it.parent = call } call.children.forEach { it.parent = call }
call.parent = parent call.parent = parent
@ -199,6 +227,7 @@ 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.STRUCTINSTANCE -> 'i'
else -> '?' else -> '?'
} }
val newName = prefixScopedName(name, prefixType) val newName = prefixScopedName(name, prefixType)
@ -237,7 +266,9 @@ class AsmGen6502Internal (
assembly.clear() assembly.clear()
loopEndLabels.clear() loopEndLabels.clear()
println("Generating assembly code... ") if(!options.quiet)
println("Generating assembly code... ")
programGen.generate() programGen.generate()
if(errors.noErrors()) { if(errors.noErrors()) {
@ -286,9 +317,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()) {
@ -300,9 +331,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()) {
@ -390,7 +421,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)
@ -402,22 +433,6 @@ class AsmGen6502Internal (
} }
internal val tempVarsCounters = mutableMapOf(
BaseDataType.BOOL to 0,
BaseDataType.BYTE to 0,
BaseDataType.UBYTE to 0,
BaseDataType.WORD to 0,
BaseDataType.UWORD to 0,
BaseDataType.FLOAT to 0
)
internal fun buildTempVarName(dt: BaseDataType, counter: Int): String = "prog8_tmpvar_${dt.toString().lowercase()}_$counter"
internal fun getTempVarName(dt: BaseDataType): String {
tempVarsCounters[dt] = tempVarsCounters.getValue(dt)+1
return buildTempVarName(dt, tempVarsCounters.getValue(dt))
}
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_W1" which is the intermediary
@ -592,7 +607,7 @@ class AsmGen6502Internal (
is PtJump -> { is PtJump -> {
val target = getJumpTarget(stmt) val target = getJumpTarget(stmt)
require(!target.needsExpressionEvaluation) require(!target.needsExpressionEvaluation)
jmp(target.asmLabel, target.indirect) jmp(target.asmLabel, target.indirect, target.indexedX)
} }
is PtLabel -> translate(stmt) is PtLabel -> translate(stmt)
is PtConditionalBranch -> translate(stmt) is PtConditionalBranch -> translate(stmt)
@ -607,7 +622,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")
} }
} }
@ -625,16 +640,16 @@ class AsmGen6502Internal (
} }
if(expr.splitWords) { if(expr.splitWords) {
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false) assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register))
return return
} }
when { when {
expr.type.isByteOrBool -> { expr.type.isByteOrBool -> {
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false) assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register))
} }
expr.type.isWord -> { expr.type.isWord -> {
assignExpressionToRegister(expr.index, RegisterOrPair.A, false) assignExpressionToRegister(expr.index, RegisterOrPair.A)
out(" asl a") out(" asl a")
when (register) { when (register) {
CpuRegister.A -> {} CpuRegister.A -> {}
@ -643,8 +658,9 @@ class AsmGen6502Internal (
} }
} }
expr.type.isFloat -> { expr.type.isFloat -> {
require(options.compTarget.FLOAT_MEM_SIZE == 5) {"invalid float size ${expr.position}"} if(options.compTarget.FLOAT_MEM_SIZE != 5)
assignExpressionToRegister(expr.index, RegisterOrPair.A, false) TODO("support float size other than 5 ${expr.position}")
assignExpressionToRegister(expr.index, RegisterOrPair.A)
out(""" out("""
sta P8ZP_SCRATCH_REG sta P8ZP_SCRATCH_REG
asl a asl a
@ -687,8 +703,8 @@ class AsmGen6502Internal (
RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister(), target.datatype.isSigned, true) RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister(), target.datatype.isSigned, true)
RegisterOrPair.AX, RegisterOrPair.AX,
RegisterOrPair.AY, RegisterOrPair.AY,
RegisterOrPair.XY, RegisterOrPair.XY -> assignmentAsmGen.assignRegisterpairWord(target, reg)
in Cx16VirtualRegisters -> assignmentAsmGen.assignRegisterpairWord(target, reg) in Cx16VirtualRegisters -> assignmentAsmGen.assignVirtualRegister(target, reg)
RegisterOrPair.FAC1 -> assignmentAsmGen.assignFAC1float(target) RegisterOrPair.FAC1 -> assignmentAsmGen.assignFAC1float(target)
RegisterOrPair.FAC2 -> assignmentAsmGen.assignFAC2float(target) RegisterOrPair.FAC2 -> assignmentAsmGen.assignFAC2float(target)
else -> throw AssemblyError("invalid register") else -> throw AssemblyError("invalid register")
@ -719,7 +735,7 @@ class AsmGen6502Internal (
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> {
val zero = PtNumber(BaseDataType.UBYTE, 0.0, value.position) val zero = PtNumber(BaseDataType.UBYTE, 0.0, value.position)
zero.parent = value zero.parent = value
assignExpressionToRegister(zero, target.register!!, false) assignExpressionToRegister(zero, target.register!!)
return return
} }
else -> { } else -> { }
@ -727,7 +743,7 @@ class AsmGen6502Internal (
} }
assignExpressionToRegister(value, RegisterOrPair.A) assignExpressionToRegister(value, RegisterOrPair.A)
assignRegister(RegisterOrPair.A, target) assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, target.datatype.isSigned, false)
} }
target.datatype.isWord || target.datatype.isPassByRef -> { target.datatype.isWord || target.datatype.isPassByRef -> {
assignExpressionToRegister(value, RegisterOrPair.AY) assignExpressionToRegister(value, RegisterOrPair.AY)
@ -823,7 +839,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 = createRepeatCounterVar(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
@ -843,7 +859,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 = createRepeatCounterVar(BaseDataType.UWORD, false, stmt) val counterVar = createTempVarReused(BaseDataType.UWORD, true, stmt)
out(""" out("""
cmp #0 cmp #0
beq + beq +
@ -866,13 +882,13 @@ $repeatLabel""")
require(count in 2..256) { "invalid repeat count ${stmt.position}" } require(count in 2..256) { "invalid repeat count ${stmt.position}" }
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
if(isTargetCpu(CpuType.CPU65C02)) { if(isTargetCpu(CpuType.CPU65C02)) {
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, true, stmt) val counterVar = createTempVarReused(BaseDataType.UBYTE, true, stmt)
out(" lda #${count and 255} | sta $counterVar") out(" lda #${count and 255} | sta $counterVar")
out(repeatLabel) out(repeatLabel)
translate(stmt.statements) translate(stmt.statements)
out(" dec $counterVar | bne $repeatLabel") out(" dec $counterVar | bne $repeatLabel")
} else { } else {
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, false, stmt) val counterVar = createTempVarReused(BaseDataType.UBYTE, false, stmt)
out(" lda #${count and 255} | sta $counterVar") out(" lda #${count and 255} | sta $counterVar")
out(repeatLabel) out(repeatLabel)
translate(stmt.statements) translate(stmt.statements)
@ -884,13 +900,13 @@ $repeatLabel""")
val repeatLabel = makeLabel("repeat") val repeatLabel = makeLabel("repeat")
out(" cpy #0") out(" cpy #0")
if(isTargetCpu(CpuType.CPU65C02)) { if(isTargetCpu(CpuType.CPU65C02)) {
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, true, stmt) val counterVar = createTempVarReused(BaseDataType.UBYTE, true, stmt)
out(" beq $endLabel | sty $counterVar") out(" beq $endLabel | sty $counterVar")
out(repeatLabel) out(repeatLabel)
translate(stmt.statements) translate(stmt.statements)
out(" dec $counterVar | bne $repeatLabel") out(" dec $counterVar | bne $repeatLabel")
} else { } else {
val counterVar = createRepeatCounterVar(BaseDataType.UBYTE, false, stmt) val counterVar = createTempVarReused(BaseDataType.UBYTE, false, stmt)
out(" beq $endLabel | sty $counterVar") out(" beq $endLabel | sty $counterVar")
out(repeatLabel) out(repeatLabel)
translate(stmt.statements) translate(stmt.statements)
@ -899,43 +915,9 @@ $repeatLabel""")
out(endLabel) out(endLabel)
} }
private fun createRepeatCounterVar(dt: BaseDataType, preferZeropage: Boolean, stmt: PtRepeatLoop): String {
val scope = stmt.definingISub()!!
val asmInfo = subroutineExtra(scope)
var parent = stmt.parent
while(parent !is PtProgram) {
if(parent is PtRepeatLoop)
break
parent = parent.parent
}
val isNested = parent is PtRepeatLoop
if(!isNested) {
// we can re-use a counter var from the subroutine if it already has one for that datatype
val existingVar = asmInfo.extraVars.firstOrNull { it.first==dt && it.second.endsWith("counter") }
if(existingVar!=null) {
if(!preferZeropage || existingVar.third!=null)
return existingVar.second
}
}
val counterVar = makeLabel("counter")
when(dt) {
BaseDataType.UBYTE, BaseDataType.UWORD -> {
val result = zeropage.allocate(counterVar, DataType.forDt(dt), null, stmt.position, errors)
result.fold(
success = { (address, _, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally
)
return counterVar
}
else -> throw AssemblyError("invalidt dt")
}
}
private fun translate(stmt: PtWhen) { private fun translate(stmt: PtWhen) {
val endLabel = makeLabel("when_end") val endLabel = makeLabel("when_end")
val choiceBlocks = mutableListOf<Pair<String, PtNodeGroup>>() val choiceBlocks = mutableListOf<Pair<String, PtWhenChoice>>()
val conditionDt = stmt.value.type val conditionDt = stmt.value.type
if(conditionDt.isByte) if(conditionDt.isByte)
assignExpressionToRegister(stmt.value, RegisterOrPair.A) assignExpressionToRegister(stmt.value, RegisterOrPair.A)
@ -946,13 +928,20 @@ $repeatLabel""")
val choice = choiceNode as PtWhenChoice val choice = choiceNode as PtWhenChoice
var choiceLabel = makeLabel("choice") var choiceLabel = makeLabel("choice")
if(choice.isElse) { if(choice.isElse) {
require(choice.parent.children.last() === choice)
translate(choice.statements) translate(choice.statements)
// is always the last node so can fall through
} else { } else {
if(choice.statements.children.isEmpty()) { if(choice.statements.children.isEmpty()) {
// no statements for this choice value, jump to the end immediately // no statements for this choice value, jump to the end immediately
choiceLabel = endLabel choiceLabel = endLabel
} else { } else {
choiceBlocks.add(choiceLabel to choice.statements) val onlyJumpLabel = ((choice.statements.children.singleOrNull() as? PtJump)?.target as? PtIdentifier)?.name
if(onlyJumpLabel==null) {
choiceBlocks.add(choiceLabel to choice)
} else {
choiceLabel = onlyJumpLabel
}
} }
for (cv in choice.values.children) { for (cv in choice.values.children) {
val value = (cv as PtNumber).number.toInt() val value = (cv as PtNumber).number.toInt()
@ -969,11 +958,14 @@ $repeatLabel""")
} }
} }
} }
jmp(endLabel)
if(choiceBlocks.isNotEmpty())
jmp(endLabel)
for(choiceBlock in choiceBlocks.withIndex()) { for(choiceBlock in choiceBlocks.withIndex()) {
out(choiceBlock.value.first) out(choiceBlock.value.first)
translate(choiceBlock.value.second) translate(choiceBlock.value.second.statements)
if (choiceBlock.index < choiceBlocks.size - 1) if (choiceBlock.index < choiceBlocks.size - 1 && !choiceBlock.value.second.isOnlyGotoOrReturn())
jmp(endLabel) jmp(endLabel)
} }
out(endLabel) out(endLabel)
@ -1002,6 +994,7 @@ $repeatLabel""")
val target = getJumpTarget(jump) val target = getJumpTarget(jump)
require(!target.needsExpressionEvaluation) require(!target.needsExpressionEvaluation)
if(target.indirect) { if(target.indirect) {
require(!target.indexedX)
val complementedInstruction = branchInstruction(stmt.condition, true) val complementedInstruction = branchInstruction(stmt.condition, true)
out(""" out("""
$complementedInstruction + $complementedInstruction +
@ -1035,7 +1028,7 @@ $repeatLabel""")
} }
} }
class JumpTarget(val asmLabel: String, val indirect: Boolean, val needsExpressionEvaluation: Boolean) class JumpTarget(val asmLabel: String, val indirect: Boolean, val indexedX: Boolean, val needsExpressionEvaluation: Boolean)
internal fun getJumpTarget(jump: PtJump, evaluateAddressExpression: Boolean = true): JumpTarget { internal fun getJumpTarget(jump: PtJump, evaluateAddressExpression: Boolean = true): JumpTarget {
val ident = jump.target as? PtIdentifier val ident = jump.target as? PtIdentifier
@ -1043,45 +1036,78 @@ $repeatLabel""")
// can be a label, or a pointer variable // can be a label, or a pointer variable
val symbol = symbolTable.lookup(ident.name) val symbol = symbolTable.lookup(ident.name)
return if(symbol?.type in arrayOf(StNodeType.STATICVAR, StNodeType.MEMVAR, StNodeType.CONSTANT)) return if(symbol?.type in arrayOf(StNodeType.STATICVAR, StNodeType.MEMVAR, StNodeType.CONSTANT))
JumpTarget(asmSymbolName(ident), true, false) // indirect jump if the jump symbol is a variable JumpTarget(asmSymbolName(ident), true, false,false) // indirect jump if the jump symbol is a variable
else else
JumpTarget(asmSymbolName(ident), false, false) JumpTarget(asmSymbolName(ident), false, false,false)
} }
val addr = jump.target.asConstInteger() val addr = jump.target.asConstInteger()
if(addr!=null) if(addr!=null)
return JumpTarget(addr.toHex(), false, false) return JumpTarget(addr.toHex(), false, false,false)
else { else {
if(evaluateAddressExpression) { if(evaluateAddressExpression) {
val arrayIdx = jump.target as? PtArrayIndexer
if (arrayIdx!=null) {
val arrayVariable = arrayIdx.variable
if(arrayVariable==null)
TODO("support for ptr indexing ${arrayIdx.position}")
if (isTargetCpu(CpuType.CPU65C02)) {
if (!arrayIdx.splitWords) {
// 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)
assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
out(" asl a | tax")
return JumpTarget(asmSymbolName(arrayVariable), true, true, false)
} else {
// print a message when more optimal code is possible for 65C02 cpu
val variable = symbolTable.lookup(arrayVariable.name)!!
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)
}
} else {
// print a message when more optimal code is possible for 6502 cpu
if(!arrayIdx.splitWords)
errors.info("the jump address array is @nosplit, but @split would create more efficient code here", jump.position)
}
}
// we can do the address evaluation right now and just use a temporary pointer variable // we can do the address evaluation right now and just use a temporary pointer variable
assignExpressionToVariable(jump.target, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UWORD)) assignExpressionToVariable(jump.target, "P8ZP_SCRATCH_W1", DataType.UWORD)
return JumpTarget("P8ZP_SCRATCH_W1", true, false) return JumpTarget("P8ZP_SCRATCH_W1", true, false,false)
} else { } else {
return JumpTarget("PROG8_JUMP_TARGET_IS_UNEVALUATED_ADDRESS_EXPRESSION", true, true) return JumpTarget("PROG8_JUMP_TARGET_IS_UNEVALUATED_ADDRESS_EXPRESSION", true, false,true)
} }
} }
} }
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 returnRegs = sub.returnsWhatWhere()
if(returnvalue!=null) { if(returnvalue!=null) {
val sub = ret.definingSub()!! if (sub.signature.returns.single().isNumericOrBool) {
val returnReg = sub.returnsWhatWhere().single() assignExpressionToRegister(returnvalue, returnRegs.single().first.registerOrPair!!)
if (sub.returns.single().isNumericOrBool==true) {
assignExpressionToRegister(returnvalue as PtExpression, returnReg.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)
val addrofValue = PtAddressOf(addrOfDt, returnvalue.position)
addrofValue.add(returnvalue as PtIdentifier) addrofValue.add(returnvalue as PtIdentifier)
addrofValue.parent = ret.parent addrofValue.parent = ret.parent
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnReg.first.registerOrPair!!, false) assignmentAsmGen.assignExpressionToRegister(addrofValue, returnRegs.single().first.registerOrPair!!, false)
} }
} }
else if(ret.children.size>1) { else if(ret.children.size>1) {
// multi-value returns are passed throug cx16.R15 down to R0 (allows unencumbered use of many Rx registers if you don't return that many values) // note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
val registersReverseOrder = Cx16VirtualRegisters.reversed() // (this allows unencumbered use of many Rx registers if you don't return that many values)
ret.children.zip(registersReverseOrder).forEach { (value, register) -> // to avoid register clobbering, assign the first return value last in row.
assignExpressionToRegister(value as PtExpression, register) val assigns = ret.children.zip(returnRegs).map { it.first to it.second }
assigns.drop(1).forEach {
val tgt = AsmAssignTarget(TargetStorageKind.REGISTER, this, it.second.second, null, it.first.position, register = it.second.first.registerOrPair!!)
assignExpressionTo(it.first as PtExpression, tgt)
}
assigns.first().also {
assignExpressionToRegister(it.first as PtExpression, it.second.first.registerOrPair!!)
} }
} }
out(" rts") out(" rts")
@ -1102,8 +1128,8 @@ $repeatLabel""")
val sourcePath = Path(incbin.definingBlock()!!.source.origin) val sourcePath = Path(incbin.definingBlock()!!.source.origin)
val includedPath = sourcePath.resolveSibling(incbin.file) val includedPath = sourcePath.resolveSibling(incbin.file)
val pathForAssembler = options.outputDir // #54: 64tass needs the path *relative to the .asm file* val pathForAssembler = options.outputDir // #54: 64tass needs the path *relative to the .asm file*
.absolute() .sanitize()
.relativize(includedPath.absolute()) .relativize(includedPath.sanitize())
.normalize() // avoid assembler warnings (-Wportable; only some, not all) .normalize() // avoid assembler warnings (-Wportable; only some, not all)
.toString().replace('\\', '/') .toString().replace('\\', '/')
out(" .binary \"$pathForAssembler\" $offset $length") out(" .binary \"$pathForAssembler\" $offset $length")
@ -1169,10 +1195,14 @@ $repeatLabel""")
internal fun isZpVar(variable: PtIdentifier): Boolean = allocator.isZpVar(variable.name) internal fun isZpVar(variable: PtIdentifier): Boolean = allocator.isZpVar(variable.name)
internal fun jmp(asmLabel: String, indirect: Boolean=false) { internal fun jmp(asmLabel: String, indirect: Boolean=false, indexedX: Boolean=false) {
if(indirect) { if(indirect) {
out(" jmp ($asmLabel)") if(indexedX)
out(" jmp ($asmLabel,x)")
else
out(" jmp ($asmLabel)")
} else { } else {
require(!indexedX) { "indexedX only allowed for indirect jumps" }
if (isTargetCpu(CpuType.CPU65C02)) if (isTargetCpu(CpuType.CPU65C02))
out(" bra $asmLabel") // note: 64tass will convert this automatically to a jmp if the relative distance is too large out(" bra $asmLabel") // note: 64tass will convert this automatically to a jmp if the relative distance is too large
else else
@ -1229,7 +1259,7 @@ $repeatLabel""")
} }
if(addressExpr.operator=="+") { if(addressExpr.operator=="+") {
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, false) val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr)
if (ptrAndIndex == null) return false if (ptrAndIndex == null) return false
if(write) { if(write) {
@ -1241,8 +1271,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
} }
} }
@ -1259,13 +1291,13 @@ $repeatLabel""")
val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second) val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second)
if(saveA) out(" pha") if(saveA) out(" pha")
if(ptrAndIndex.second.isSimple()) { if(ptrAndIndex.second.isSimple()) {
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
if(saveA) out(" pla") if(saveA) out(" pla")
out(" sta (P8ZP_SCRATCH_W2),y") out(" sta (P8ZP_SCRATCH_W2),y")
} else { } else {
pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second) pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, true) restoreRegisterStack(CpuRegister.Y, true)
if(saveA) out(" pla") if(saveA) out(" pla")
out(" sta (P8ZP_SCRATCH_W2),y") out(" sta (P8ZP_SCRATCH_W2),y")
@ -1281,8 +1313,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
} }
} }
@ -1302,12 +1336,12 @@ $repeatLabel""")
} else { } else {
// copy the pointer var to zp first // copy the pointer var to zp first
if(ptrAndIndex.second.isSimple()) { if(ptrAndIndex.second.isSimple()) {
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" lda (P8ZP_SCRATCH_W2),y") out(" lda (P8ZP_SCRATCH_W2),y")
} else { } else {
pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second) pushCpuStack(BaseDataType.UBYTE, ptrAndIndex.second)
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false) restoreRegisterStack(CpuRegister.Y, false)
out(" lda (P8ZP_SCRATCH_W2),y") out(" lda (P8ZP_SCRATCH_W2),y")
} }
@ -1331,8 +1365,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
} }
} }
@ -1342,13 +1378,14 @@ $repeatLabel""")
if(pointervar!=null && isZpVar(pointervar)) { if(pointervar!=null && isZpVar(pointervar)) {
val varname = asmSymbolName(pointervar) val varname = asmSymbolName(pointervar)
out(" ldy #${256-constOffset} ; negative offset $constOffset") out(" ldy #${256-constOffset} ; negative offset $constOffset")
out(" dec $varname+1 | sta ($varname),y | inc $varname+1") // temporarily make MSB 1 less out(" dec $varname+1 | sta ($varname),y | inc $varname+1") // temporarily make MSB 1 less to be able to use the negative Y offset
return true return true
} else { } else {
// copy the pointer var to zp first // copy the pointer var to zp first
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) out(" pha")
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
out(" ldy #${256-constOffset} ; negative offset $constOffset") out(" ldy #${256-constOffset} ; negative offset $constOffset")
out(" dec P8ZP_SCRATCH_W2+1 | sta (P8ZP_SCRATCH_W2),y | inc P8ZP_SCRATCH_W2+1") // temporarily make MSB 1 less out(" dec P8ZP_SCRATCH_W2+1 | pla | sta (P8ZP_SCRATCH_W2),y") // temporarily make MSB 1 less to be able to use the negative Y offset
return true return true
} }
} }
@ -1361,8 +1398,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
} }
} }
@ -1372,13 +1411,13 @@ $repeatLabel""")
if(pointervar!=null && isZpVar(pointervar)) { if(pointervar!=null && isZpVar(pointervar)) {
val varname = asmSymbolName(pointervar) val varname = asmSymbolName(pointervar)
out(" ldy #${256-constOffset} ; negative offset $constOffset") out(" ldy #${256-constOffset} ; negative offset $constOffset")
out(" dec $varname+1 | lda ($varname),y | inc $varname+1") // temporarily make MSB 1 less out(" dec $varname+1 | lda ($varname),y | inc $varname+1") // temporarily make MSB 1 less to be able to use the negative Y offset
return true return true
} else { } else {
// copy the pointer var to zp first // copy the pointer var to zp first
assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD)
out(" ldy #${256-constOffset} ; negative offset $constOffset") out(" ldy #${256-constOffset} ; negative offset $constOffset")
out(" dec P8ZP_SCRATCH_W2+1 | lda (P8ZP_SCRATCH_W2),y | inc P8ZP_SCRATCH_W2+1") // temporarily make MSB 1 less out(" dec P8ZP_SCRATCH_W2+1 | lda (P8ZP_SCRATCH_W2),y") // temporarily make MSB 1 less to be able to use the negative Y offset
return true return true
} }
} }
@ -1393,27 +1432,35 @@ $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.name==name)
return param
}
}
return null
} }
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) { internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
if(left.isSimple()) { if(left.isSimple()) {
assignExpressionToVariable(right, rightVarName, DataType.forDt(BaseDataType.UBYTE)) assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
assignExpressionToRegister(left, RegisterOrPair.A) assignExpressionToRegister(left, RegisterOrPair.A)
} else { } else {
pushCpuStack(BaseDataType.UBYTE, left) pushCpuStack(BaseDataType.UBYTE, left)
assignExpressionToVariable(right, rightVarName, DataType.forDt(BaseDataType.UBYTE)) assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
out(" pla") out(" pla")
} }
} }
internal fun assignWordOperandsToAYAndVar(left: PtExpression, right: PtExpression, rightVarname: String) { internal fun assignWordOperandsToAYAndVar(left: PtExpression, right: PtExpression, rightVarname: String) {
if(left.isSimple()) { if(left.isSimple()) {
assignExpressionToVariable(right, rightVarname, DataType.forDt(BaseDataType.UWORD)) assignExpressionToVariable(right, rightVarname, DataType.UWORD)
assignExpressionToRegister(left, RegisterOrPair.AY) assignExpressionToRegister(left, RegisterOrPair.AY)
} else { } else {
pushCpuStack(BaseDataType.UWORD, left) pushCpuStack(BaseDataType.UWORD, left)
assignExpressionToVariable(right, rightVarname, DataType.forDt(BaseDataType.UWORD)) assignExpressionToVariable(right, rightVarname, DataType.UWORD)
restoreRegisterStack(CpuRegister.Y, false) restoreRegisterStack(CpuRegister.Y, false)
restoreRegisterStack(CpuRegister.A, false) restoreRegisterStack(CpuRegister.A, false)
} }
@ -1422,7 +1469,7 @@ $repeatLabel""")
internal fun translateDirectMemReadExpressionToRegA(expr: PtMemoryByte) { internal fun translateDirectMemReadExpressionToRegA(expr: PtMemoryByte) {
fun assignViaExprEval() { fun assignViaExprEval() {
assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) assignExpressionToVariable(expr.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
if (isTargetCpu(CpuType.CPU65C02)) { if (isTargetCpu(CpuType.CPU65C02)) {
out(" lda (P8ZP_SCRATCH_W2)") out(" lda (P8ZP_SCRATCH_W2)")
} else { } else {
@ -1495,7 +1542,52 @@ $repeatLabel""")
internal fun makeLabel(postfix: String): String { internal fun makeLabel(postfix: String): String {
generatedLabelSequenceNumber++ generatedLabelSequenceNumber++
return "${PtLabel.GENERATED_LABEL_PREFIX}${generatedLabelSequenceNumber}_$postfix" return "$GENERATED_LABEL_PREFIX${generatedLabelSequenceNumber}_$postfix"
}
internal fun createTempVarReused(dt: BaseDataType, preferZeropage: Boolean, stmt: PtNode): String {
val scope = stmt.definingISub()!!
val asmInfo = subroutineExtra(scope)
var parent = stmt.parent
while(parent !is PtProgram) {
if(parent is PtRepeatLoop || parent is PtForLoop)
break
parent = parent.parent
}
val isNested = parent is PtRepeatLoop || parent is PtForLoop
if(!isNested) {
// we can re-use a counter var from the subroutine if it already has one for that datatype
val existingVar = asmInfo.extraVars.firstOrNull { it.first==dt && it.second.endsWith("tempv") }
if(existingVar!=null) {
if(!preferZeropage || existingVar.third!=null) {
// println("reuse temp counter var: $dt ${existingVar.second} @${stmt.position}")
return existingVar.second
}
}
}
val counterVar = makeLabel("tempv")
// println("new temp counter var: $dt $counterVar @${stmt.position}")
when {
dt.isIntegerOrBool -> {
if(preferZeropage) {
val result = zeropage.allocate(counterVar, DataType.forDt(dt), null, stmt.position, errors)
result.fold(
success = { (address, _, _) -> asmInfo.extraVars.add(Triple(dt, counterVar, address)) },
failure = { asmInfo.extraVars.add(Triple(dt, counterVar, null)) } // allocate normally
)
} else {
asmInfo.extraVars.add(Triple(dt, counterVar, null)) // allocate normally
}
return counterVar
}
dt == BaseDataType.FLOAT -> {
asmInfo.extraVars.add(Triple(dt, counterVar, null)) // allocate normally, floats never on zeropage
return counterVar
}
else -> throw AssemblyError("invalid dt")
}
} }
internal fun assignConstFloatToPointerAY(number: PtNumber) { internal fun assignConstFloatToPointerAY(number: PtNumber) {
@ -1518,7 +1610,7 @@ $repeatLabel""")
val compare = if(useSbc) "sec | sbc" else "cmp" val compare = if(useSbc) "sec | sbc" else "cmp"
fun cmpViaScratch() { fun cmpViaScratch() {
if(assignmentAsmGen.directIntoY(value)) { if(assignmentAsmGen.directIntoY(value)) {
assignExpressionToRegister(value, RegisterOrPair.Y, false) assignExpressionToRegister(value, RegisterOrPair.Y)
out(" sty P8ZP_SCRATCH_REG") out(" sty P8ZP_SCRATCH_REG")
} else { } else {
out(" pha") out(" pha")
@ -1534,7 +1626,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()
@ -1577,7 +1671,7 @@ $repeatLabel""")
} }
internal fun assignConditionValueToRegisterAndTest(condition: PtExpression) { internal fun assignConditionValueToRegisterAndTest(condition: PtExpression) {
assignExpressionToRegister(condition, RegisterOrPair.A, false) assignExpressionToRegister(condition, RegisterOrPair.A)
when(condition) { when(condition) {
is PtNumber, is PtNumber,
is PtBool, is PtBool,
@ -1618,7 +1712,7 @@ $repeatLabel""")
is PtIdentifier -> equalf(asmVariableName(left), asmVariableName(right)) is PtIdentifier -> equalf(asmVariableName(left), asmVariableName(right))
is PtNumber -> equalf(asmVariableName(left), allocator.getFloatAsmConst(right.number)) is PtNumber -> equalf(asmVariableName(left), allocator.getFloatAsmConst(right.number))
else -> { else -> {
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT)) assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
equalf(asmVariableName(left), subroutineFloatEvalResultVar1) equalf(asmVariableName(left), subroutineFloatEvalResultVar1)
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
} }
@ -1628,7 +1722,7 @@ $repeatLabel""")
is PtIdentifier -> equalf(left, asmVariableName(right)) is PtIdentifier -> equalf(left, asmVariableName(right))
is PtNumber -> equalf(left, allocator.getFloatAsmConst(right.number)) is PtNumber -> equalf(left, allocator.getFloatAsmConst(right.number))
else -> { else -> {
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT)) assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
equalf(left, subroutineFloatEvalResultVar1) equalf(left, subroutineFloatEvalResultVar1)
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
} }
@ -1663,7 +1757,7 @@ $repeatLabel""")
is PtIdentifier -> lessf(asmVariableName(left), asmVariableName(right)) is PtIdentifier -> lessf(asmVariableName(left), asmVariableName(right))
is PtNumber -> lessf(asmVariableName(left), allocator.getFloatAsmConst(right.number)) is PtNumber -> lessf(asmVariableName(left), allocator.getFloatAsmConst(right.number))
else -> { else -> {
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT)) assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
lessf(asmVariableName(left), subroutineFloatEvalResultVar1) lessf(asmVariableName(left), subroutineFloatEvalResultVar1)
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
} }
@ -1673,7 +1767,7 @@ $repeatLabel""")
is PtIdentifier -> lessf(left, asmVariableName(right)) is PtIdentifier -> lessf(left, asmVariableName(right))
is PtNumber -> lessf(left, allocator.getFloatAsmConst(right.number)) is PtNumber -> lessf(left, allocator.getFloatAsmConst(right.number))
else -> { else -> {
assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.forDt(BaseDataType.FLOAT)) assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT)
lessf(left, subroutineFloatEvalResultVar1) lessf(left, subroutineFloatEvalResultVar1)
subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true subroutineExtra(left.definingISub()!!).usedFloatEvalResultVar1 = true
} }
@ -1704,6 +1798,16 @@ $repeatLabel""")
} }
return null return null
} }
fun romableError(problem: String, pos: Position, assemblerShouldFail: Boolean = true) {
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...
errors.warn("problem for ROMable code: $problem", pos)
if(assemblerShouldFail) {
out(" .error \"ROMable code selected but incompatible code was generated: $problem $pos\"")
}
}
}
} }
/** /**

View File

@ -1,9 +1,9 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.code.GENERATED_LABEL_PREFIX
import prog8.code.StConstant import prog8.code.StConstant
import prog8.code.StMemVar import prog8.code.StMemVar
import prog8.code.SymbolTable import prog8.code.SymbolTable
import prog8.code.ast.PtLabel
import prog8.code.core.ICompilationTarget import prog8.code.core.ICompilationTarget
@ -362,7 +362,7 @@ or *_afterif labels.
This gets generated after certain if conditions, and only the branch instruction is needed in these cases. This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
*/ */
val autoLabelPrefix = PtLabel.GENERATED_LABEL_PREFIX val autoLabelPrefix = GENERATED_LABEL_PREFIX
if(first=="beq +" && second=="lda #1" && third=="+") { if(first=="beq +" && second=="lda #1" && third=="+") {
if((fourth.startsWith("beq $autoLabelPrefix") || fourth.startsWith("bne $autoLabelPrefix")) && if((fourth.startsWith("beq $autoLabelPrefix") || fourth.startsWith("bne $autoLabelPrefix")) &&
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) { (fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {
@ -459,7 +459,7 @@ private fun optimizeStoreLoadSame(
return mods return mods
} }
private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_\.$]*)""") private val identifierRegex = Regex("""^([a-zA-Z_$][a-zA-Z\d_.$]*)""")
private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? { private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
// try to get the constant value address, could return null if it's a symbol instead // try to get the constant value address, could return null if it's a symbol instead
@ -508,9 +508,11 @@ private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): L
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> { private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
// jsr Sub + rts -> jmp Sub // jsr Sub + rts -> jmp Sub
// jmp Sub + rts -> jmp Sub
// rts + jmp -> remove jmp // rts + jmp -> remove jmp
// rts + bxx -> remove bxx // rts + bxx -> remove bxx
// lda + cmp #0 -> remove cmp, same for cpy and cpx. // lda + cmp #0 -> remove cmp, same for cpy and cpx.
// bra/jmp + bra/jmp -> remove second bra/jmp (bra bra / jmp jmp are not removed because this is likely a jump table!)
// and some other optimizations. // and some other optimizations.
val mods = mutableListOf<Modification>() val mods = mutableListOf<Modification>()
@ -520,7 +522,10 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
val third = lines[2].value val third = lines[2].value
if(!haslabel(second)) { if(!haslabel(second)) {
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) { if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) {
mods += Modification(lines[1].index, true, null)
}
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
if("floats.pushFAC" !in first && "floats.popFAC" !in first) { // these 2 routines depend on being called with JSR!! if("floats.pushFAC" !in first && "floats.popFAC" !in first) { // these 2 routines depend on being called with JSR!!
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp")) mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
mods += Modification(lines[1].index, true, null) mods += Modification(lines[1].index, true, null)
@ -563,6 +568,15 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
} }
} }
} }
// only remove bra followed by jmp or jmp followed by bra
// bra bra or jmp jmp is likely part of a jump table, which should keep all entries!
if((" bra" in first || "\tbra" in first) && (" jmp" in second || "\tjmp" in second)) {
mods.add(Modification(lines[1].index, true, null))
}
if((" jmp" in first || "\tjmp" in first) && (" bra" in second || "\tbra" in second)) {
mods.add(Modification(lines[1].index, true, null))
}
} }
/* /*
@ -687,18 +701,20 @@ private fun optimizeUselessPushPopStack(linesByFour: Sequence<List<IndexedValue<
// 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
if(first=="phy" && second.startsWith("ldy ") && third=="pla") { when (first) {
mods.add(Modification(lines[3].index, true, null)) "phy" if second.startsWith("ldy ") && third=="pla" -> {
mods.add(Modification(lines[1].index, false, " tya")) mods.add(Modification(lines[3].index, true, null))
} mods.add(Modification(lines[1].index, false, " tya"))
else if(first=="phx" && second.startsWith("ldx ") && third=="pla") { }
mods.add(Modification(lines[3].index, true, null)) "phx" if second.startsWith("ldx ") && third=="pla" -> {
mods.add(Modification(lines[1].index, false, " txa")) mods.add(Modification(lines[3].index, true, null))
} mods.add(Modification(lines[1].index, false, " txa"))
else if(first=="pha" && second.startsWith("lda ") && third=="pla") { }
mods.add(Modification(lines[1].index, true, null)) "pha" if second.startsWith("lda ") && third=="pla" -> {
mods.add(Modification(lines[2].index, true, null)) mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[3].index, true, null)) mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[3].index, true, null))
}
} }
} }

View File

@ -1,7 +1,11 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.code.ast.PtLabel import prog8.code.GENERATED_LABEL_PREFIX
import prog8.code.core.* import prog8.code.IAssemblyProgram
import prog8.code.core.CompilationOptions
import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
import prog8.code.core.OutputType
import prog8.code.target.C128Target import prog8.code.target.C128Target
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.code.target.PETTarget import prog8.code.target.PETTarget
@ -45,7 +49,8 @@ internal class AssemblyProgram(
command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString())) command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
assemblerCommand = command assemblerCommand = command
println("\nCreating prg for target ${compTarget.name}.") if(!options.quiet)
println("\nCreating prg for target ${compTarget.name}.")
} }
OutputType.XEX -> { OutputType.XEX -> {
// Atari800XL .xex generation. // Atari800XL .xex generation.
@ -66,7 +71,8 @@ internal class AssemblyProgram(
command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString())) command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
assemblerCommand = command assemblerCommand = command
println("\nCreating xex for target ${compTarget.name}.") if(!options.quiet)
println("\nCreating xex for target ${compTarget.name}.")
} }
OutputType.RAW -> { OutputType.RAW -> {
// Neo6502/headerless raw program generation. // Neo6502/headerless raw program generation.
@ -86,7 +92,8 @@ internal class AssemblyProgram(
command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString())) command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
assemblerCommand = command assemblerCommand = command
println("\nCreating raw binary for target ${compTarget.name}.") if(!options.quiet)
println("\nCreating raw binary for target ${compTarget.name}.")
} }
OutputType.LIBRARY -> { OutputType.LIBRARY -> {
// CBM machines library (.bin) generation (with or without 2 byte load address header depending on the compilation target machine) // CBM machines library (.bin) generation (with or without 2 byte load address header depending on the compilation target machine)
@ -106,10 +113,12 @@ internal class AssemblyProgram(
command.add("--list=$listFile") command.add("--list=$listFile")
if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) { if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) {
println("\nCreating binary library file with header for target ${compTarget.name}.") if(!options.quiet)
println("\nCreating binary library file with header for target ${compTarget.name}.")
command.add("--cbm-prg") command.add("--cbm-prg")
} else { } else {
println("\nCreating binary library file without header for target ${compTarget.name}.") if(!options.quiet)
println("\nCreating binary library file without header for target ${compTarget.name}.")
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
} }
@ -121,8 +130,11 @@ internal class AssemblyProgram(
if(options.compTarget.additionalAssemblerOptions!=null) if(options.compTarget.additionalAssemblerOptions!=null)
assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!) assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
val proc = ProcessBuilder(assemblerCommand).inheritIO().start() val proc = ProcessBuilder(assemblerCommand)
val result = proc.waitFor() if(!options.quiet)
proc.inheritIO()
val process = proc.start()
val result = process.waitFor()
if (result == 0) { if (result == 0) {
removeGeneratedLabelsFromMonlist() removeGeneratedLabelsFromMonlist()
generateBreakpointList() generateBreakpointList()
@ -131,7 +143,7 @@ internal class AssemblyProgram(
} }
private fun removeGeneratedLabelsFromMonlist() { private fun removeGeneratedLabelsFromMonlist() {
val pattern = Regex("""al (\w+) \S+${PtLabel.GENERATED_LABEL_PREFIX}.+?""") val pattern = Regex("""al (\w+) \S+$GENERATED_LABEL_PREFIX.+?""")
val lines = viceMonListFile.toFile().readLines() val lines = viceMonListFile.toFile().readLines()
viceMonListFile.toFile().outputStream().bufferedWriter().use { viceMonListFile.toFile().outputStream().bufferedWriter().use {
for (line in lines) { for (line in lines) {
@ -148,7 +160,7 @@ internal class AssemblyProgram(
for (line in viceMonListFile.toFile().readLines()) { for (line in viceMonListFile.toFile().readLines()) {
val match = pattern.matchEntire(line) val match = pattern.matchEntire(line)
if (match != null) if (match != null)
breakpoints.add("break \$" + match.groupValues[1]) breakpoints.add("break $" + match.groupValues[1])
} }
val num = breakpoints.size val num = breakpoints.size
breakpoints.add(0, "; breakpoint list now follows") breakpoints.add(0, "; breakpoint list now follows")

View File

@ -44,6 +44,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"setlsb" -> funcSetLsbMsb(fcall, false) "setlsb" -> funcSetLsbMsb(fcall, false)
"setmsb" -> funcSetLsbMsb(fcall, true) "setmsb" -> funcSetLsbMsb(fcall, true)
"memory" -> funcMemory(fcall, discardResult, resultRegister) "memory" -> funcMemory(fcall, discardResult, resultRegister)
"structalloc" -> funcStructAlloc(fcall, discardResult, resultRegister)
"peekw" -> funcPeekW(fcall, resultRegister) "peekw" -> funcPeekW(fcall, resultRegister)
"peekf" -> funcPeekF(fcall, resultRegister) "peekf" -> funcPeekF(fcall, resultRegister)
"peek" -> throw AssemblyError("peek() should have been replaced by @()") "peek" -> throw AssemblyError("peek() should have been replaced by @()")
@ -53,9 +54,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val memread = PtMemoryByte(fcall.position) val memread = PtMemoryByte(fcall.position)
memread.add(fcall.args[0]) memread.add(fcall.args[0])
memread.parent = fcall memread.parent = fcall
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(memread, RegisterOrPair.A)
asmgen.out(" pha") asmgen.out(" pha")
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.position, memory=memread) val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.position, memory=memread)
asmgen.assignExpressionTo(fcall.args[1], memtarget) asmgen.assignExpressionTo(fcall.args[1], memtarget)
asmgen.out(" pla") asmgen.out(" pla")
} }
@ -107,8 +108,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" jsr prog8_math.divmod_ub_asm") asmgen.out(" jsr prog8_math.divmod_ub_asm")
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier) val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
val var3name = asmgen.asmVariableName(fcall.args[3] as PtIdentifier) val var3name = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[2].position, var2name) val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[3].position, var3name) val remainderTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[3].position, var3name)
assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false, false) assignAsmGen.assignRegisterByte(remainderTarget, CpuRegister.A, false, false)
assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false, false) assignAsmGen.assignRegisterByte(divisionTarget, CpuRegister.Y, false, false)
} }
@ -120,7 +121,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
// output: P8ZP_SCRATCH_W2 in ZP: 16-bit remainder, A/Y: 16 bit division result // output: P8ZP_SCRATCH_W2 in ZP: 16-bit remainder, A/Y: 16 bit division result
asmgen.out(" jsr prog8_math.divmod_uw_asm") asmgen.out(" jsr prog8_math.divmod_uw_asm")
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier) val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingISub(), fcall.args[2].position, var2name) val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
val remainderVar = asmgen.asmVariableName(fcall.args[3] as PtIdentifier) val remainderVar = asmgen.asmVariableName(fcall.args[3] as PtIdentifier)
assignAsmGen.assignRegisterpairWord(divisionTarget, RegisterOrPair.AY) assignAsmGen.assignRegisterpairWord(divisionTarget, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
@ -196,7 +197,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
return return
} }
asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.forDt(BaseDataType.UWORD)) // jump address asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD) // jump address
asmgen.out(""" asmgen.out("""
; push a return address so the jmp becomes indirect jsr ; push a return address so the jmp becomes indirect jsr
lda #>((+)-1) lda #>((+)-1)
@ -227,6 +228,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
.word ${constAddress.toHex()} .word ${constAddress.toHex()}
.byte $constBank""") .byte $constBank""")
} else { } else {
if(asmgen.options.romable)
TODO("no code for non-const callfar (jsrfar) yet that's usable in ROM ${fcall.position}")
// self-modifying code: set jsrfar arguments
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
asmgen.out(" sta (++)+0") asmgen.out(" sta (++)+0")
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
@ -283,6 +287,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
.word ${constAddress.toHex()} .word ${constAddress.toHex()}
.byte $constBank""") .byte $constBank""")
} else { } else {
if(asmgen.options.romable)
TODO("no code for non-const callfar2 (jsrfar) yet that's usable in ROM ${fcall.position}")
// self-modifying code: set jsrfar arguments
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank
asmgen.out(" sta (++)+0") asmgen.out(" sta (++)+0")
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address
@ -346,7 +353,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
cmp ${asmgen.asmVariableName(arg2)} cmp ${asmgen.asmVariableName(arg2)}
+""") +""")
} }
is PtBool -> TODO("word compare against bool") is PtBool -> TODO("word compare against bool ${arg2.position}")
is PtNumber -> { is PtNumber -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
@ -374,16 +381,27 @@ 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.forDt(BaseDataType.UWORD), fcall.position)
val addressOf = PtAddressOf(fcall.position) val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
val addressOf = PtAddressOf(DataType.pointer(BaseDataType.UBYTE), fcall.position)
addressOf.add(slabname) addressOf.add(slabname)
addressOf.parent = fcall addressOf.parent = fcall
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.forDt(BaseDataType.UWORD), expression = addressOf) val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UWORD, expression = addressOf)
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen) val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position) val assign = AsmAssignment(src, listOf(target), program.memsizer, fcall.position)
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")
if(fcall.args.isEmpty())
TODO("struct alloc in BSS")
else
TODO("static struct alloc with values")
}
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) {
@ -409,14 +427,17 @@ 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 -> {
if (what.address is PtNumber) { if (what.address is PtNumber) {
val number = (what.address as PtNumber).number val number = (what.address as PtNumber).number
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}") asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #$80 |+ | sta ${number.toHex()}")
} else { } else {
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY) asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.ror2_mem_ub") asmgen.out(" jsr prog8_lib.ror2_mem_ub")
@ -424,7 +445,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
is PtIdentifier -> { is PtIdentifier -> {
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable") asmgen.out(" lda $variable | lsr a | bcc + | ora #$80 |+ | sta $variable")
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
@ -432,16 +453,18 @@ 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
asmgen.out(" lsr ${varname}+1,x | ror ${varname},x | bcc + | lda ${varname}+1,x | ora #\$80 | sta ${varname}+1,x |+ ") asmgen.out(" lsr ${varname}+1,x | ror ${varname},x | bcc + | lda ${varname}+1,x | ora #$80 | sta ${varname}+1,x |+ ")
} }
is PtIdentifier -> { is PtIdentifier -> {
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ") asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #$80 | sta $variable+1 |+ ")
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
@ -459,33 +482,26 @@ 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 -> {
if (what.address is PtNumber) { when {
val number = (what.address as PtNumber).number what.address is PtNumber -> {
asmgen.out(" ror ${number.toHex()}") val number = (what.address as PtNumber).number
} else { asmgen.out(" ror ${number.toHex()}")
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address) }
if(ptrAndIndex!=null) { what.address is PtIdentifier -> {
asmgen.out(" php") asmgen.out(" php") // save Carry
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A) val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
asmgen.saveRegisterStack(CpuRegister.A, true) asmgen.out(" plp | ror a")
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY) asmgen.storeAIntoZpPointerVar(sourceName, false)
asmgen.out(" sta (+) + 1 | sty (+) + 2") }
asmgen.restoreRegisterStack(CpuRegister.X, false) else -> {
asmgen.out(""" TODO("ror ptr-expression ${what.position}")
plp
+ ror ${'$'}ffff,x ; modified""")
} else {
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
if(!what.address.isSimple()) asmgen.out(" plp")
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ ror ${'$'}ffff ; modified""")
} }
} }
} }
@ -502,7 +518,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
@ -525,14 +543,16 @@ 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 -> {
if (what.address is PtNumber) { if (what.address is PtNumber) {
val number = (what.address as PtNumber).number val number = (what.address as PtNumber).number
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}") asmgen.out(" lda ${number.toHex()} | cmp #$80 | rol a | sta ${number.toHex()}")
} else { } else {
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY) asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.rol2_mem_ub") asmgen.out(" jsr prog8_lib.rol2_mem_ub")
@ -540,7 +560,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
is PtIdentifier -> { is PtIdentifier -> {
val variable = asmgen.asmVariableName(what) val variable = asmgen.asmVariableName(what)
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable") asmgen.out(" lda $variable | cmp #$80 | rol a | sta $variable")
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
@ -549,9 +569,11 @@ 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 |+") asmgen.out(" asl ${varname}_lsb,x | rol ${varname}_msb,x | bcc + | inc ${varname}_lsb,x |+")
else else
asmgen.out(" asl ${varname},x | rol ${varname}+1,x | bcc + | inc ${varname},x |+ ") asmgen.out(" asl ${varname},x | rol ${varname}+1,x | bcc + | inc ${varname},x |+ ")
} }
@ -575,33 +597,25 @@ 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 -> {
if (what.address is PtNumber) { when {
val number = (what.address as PtNumber).number what.address is PtNumber -> {
asmgen.out(" rol ${number.toHex()}") val number = (what.address as PtNumber).number
} else { asmgen.out(" rol ${number.toHex()}")
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address) }
if(ptrAndIndex!=null) { what.address is PtIdentifier -> {
asmgen.out(" php") asmgen.out(" php") // save Carry
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A) val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
asmgen.saveRegisterStack(CpuRegister.A, true) asmgen.out(" plp | rol a")
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY) asmgen.storeAIntoZpPointerVar(sourceName, false)
asmgen.out(" sta (+) + 1 | sty (+) + 2") }
asmgen.restoreRegisterStack(CpuRegister.X, false) else -> {
asmgen.out(""" TODO("rol ptr-expression ${what.position}")
plp
+ rol ${'$'}ffff,x ; modified""")
} else {
if(!what.address.isSimple()) asmgen.out(" php") // save Carry
asmgen.assignExpressionToRegister(what.address, RegisterOrPair.AY)
if(!what.address.isSimple()) asmgen.out(" plp")
asmgen.out("""
sta (+) + 1
sty (+) + 2
+ rol ${'$'}ffff ; modified""")
} }
} }
} }
@ -618,7 +632,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
@ -640,7 +656,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
when(fcall.args[0]) { when(fcall.args[0]) {
is PtIdentifier -> { is PtIdentifier -> {
val varname = asmgen.asmVariableName(fcall.args[0] as PtIdentifier) + if(msb) "+1" else "" val varname = asmgen.asmVariableName(fcall.args[0] as PtIdentifier) + if(msb) "+1" else ""
target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, variableAsmName = varname) target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, variableAsmName = varname)
} }
is PtArrayIndexer -> { is PtArrayIndexer -> {
val indexer = fcall.args[0] as PtArrayIndexer val indexer = fcall.args[0] as PtArrayIndexer
@ -648,6 +664,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val msbAdd: Int val msbAdd: Int
if(indexer.splitWords) { if(indexer.splitWords) {
val arrayVariable = indexer.variable val arrayVariable = indexer.variable
if(arrayVariable==null)
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
@ -682,7 +700,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
multipliedIndex.parent=indexer multipliedIndex.parent=indexer
} }
} }
target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.forDt(BaseDataType.UBYTE), fcall.definingSub(), fcall.position, array = indexer) target = AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, array = indexer)
} }
else -> throw AssemblyError("setlsb/setmsb on weird target ${fcall.args[0]}") else -> throw AssemblyError("setlsb/setmsb on weird target ${fcall.args[0]}")
} }
@ -690,7 +708,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(fcall.args[1].asConstInteger() == 0) { if(fcall.args[1].asConstInteger() == 0) {
assignAsmGen.assignConstantByte(target, 0) assignAsmGen.assignConstantByte(target, 0)
} else { } else {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A, false) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
assignAsmGen.assignRegisterByte(target, CpuRegister.A, false, false) assignAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
} }
} }
@ -753,8 +771,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.assignConstFloatToPointerAY(number) asmgen.assignConstFloatToPointerAY(number)
} }
else -> { else -> {
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT) val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, fcall)
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.forDt(BaseDataType.FLOAT)) asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY) asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
pha pha
@ -829,7 +847,7 @@ 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.forDt(BaseDataType.FLOAT), fcall.definingISub(), fcall.position, null, null, null, resultRegister, null)) AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position))
} }
} }
@ -1126,7 +1144,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)
@ -1143,7 +1163,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)
@ -1225,7 +1247,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)
@ -1290,7 +1315,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), value.position)
addr.add(value) addr.add(value)
addr.parent = call addr.parent = call
AsmAssignSource.fromAstSource(addr, program, asmgen) AsmAssignSource.fromAstSource(addr, program, asmgen)
@ -1303,11 +1328,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
throw AssemblyError("cannot use float arguments outside of a subroutine scope") throw AssemblyError("cannot use float arguments outside of a subroutine scope")
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.forDt(BaseDataType.FLOAT), value.position) val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
val addr = PtAddressOf(value.position) val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), value.position)
addr.add(variable) addr.add(variable)
addr.parent = call addr.parent = call
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.forDt(BaseDataType.FLOAT)) asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
AsmAssignSource.fromAstSource(addr, program, asmgen) AsmAssignSource.fromAstSource(addr, program, asmgen)
} }
} }
@ -1324,7 +1349,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), value.position)
addr.add(value) addr.add(value)
addr.parent = call addr.parent = call
AsmAssignSource.fromAstSource(addr, program, asmgen) AsmAssignSource.fromAstSource(addr, program, asmgen)
@ -1342,7 +1367,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),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

@ -1,39 +0,0 @@
package prog8.codegen.cpu6502
import prog8.code.ast.IPtSubroutine
import prog8.code.ast.PtAsmSub
import prog8.code.ast.PtSub
import prog8.code.core.AssemblyError
import prog8.code.core.DataType
import prog8.code.core.RegisterOrPair
import prog8.code.core.RegisterOrStatusflag
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<RegisterOrStatusflag, DataType>> {
when(this) {
is PtAsmSub -> {
return returns
}
is PtSub -> {
// for non-asm subroutines, determine the return registers based on the type of the return values
when(returns.size) {
0 -> return emptyList()
1 -> {
val returntype = returns.single()
val register = when {
returntype.isByteOrBool -> RegisterOrStatusflag(RegisterOrPair.A, null)
returntype.isWord -> RegisterOrStatusflag(RegisterOrPair.AY, null)
returntype.isFloat -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
}
return listOf(Pair(register, returntype))
}
else -> {
// TODO for multi-value results, put the first one in register(s) and only the rest in the virtual registers?
throw AssemblyError("multi-value returns from a normal subroutine are not put into registers, this routine shouldn't have been called in this scenario")
}
}
}
}
}

View File

@ -33,224 +33,351 @@ internal class ForLoopsAsmGen(
} }
private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) { private fun translateForOverNonconstRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
val loopLabel = asmgen.makeLabel("for_loop") if(range.step.asConstInteger()!! < -1) {
val endLabel = asmgen.makeLabel("for_end")
val modifiedLabel = asmgen.makeLabel("for_modified")
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
asmgen.loopEndLabels.add(endLabel)
val stepsize=range.step.asConstInteger()!!
if(stepsize < -1) {
val limit = range.to.asConstInteger() val limit = range.to.asConstInteger()
if(limit==0) if(limit==0)
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping") throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
} }
when { when {
iterableDt.isByteArray -> { iterableDt.isByteArray -> forOverNonconstByteRange(stmt, iterableDt, range)
val varname = asmgen.asmVariableName(stmt.variable) iterableDt.isWordArray && !iterableDt.isSplitWordArray -> forOverNonconstWordRange(stmt, iterableDt, range)
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType()) else -> throw AssemblyError("range expression can only be byte or word")
if (stepsize==-1 && range.to.asConstInteger()==0) { }
// simple loop downto 0 step -1
asmgen.out(loopLabel) asmgen.loopEndLabels.removeLast()
asmgen.translate(stmt.statements) }
private fun forOverNonconstByteRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
val stepsize = range.step.asConstInteger()!!
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.add(endLabel)
val varname = asmgen.asmVariableName(stmt.variable)
asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
when (stepsize) {
-1 if range.to.asConstInteger()==0 -> {
// simple loop downto 0 step -1
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
asmgen.out("""
dec $varname
lda $varname
cmp #255
bne $loopLabel""")
}
-1 if range.to.asConstInteger()==1 -> {
// simple loop downto 1 step -1
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
asmgen.out("""
dec $varname
bne $loopLabel""")
}
1, -1 -> forOverBytesRangeStepOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
else -> forOverBytesRangeStepGreaterOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
}
}
private fun forOverBytesRangeStepOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
// bytes range, step 1 or -1
val stepsize = range.step.asConstInteger()!!
val incdec = if(stepsize==1) "inc" else "dec"
if(asmgen.options.romable) {
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
// so we need to store the loop end value in a newly allocated temporary variable
val toValueVar = asmgen.createTempVarReused(iterableDt.elementType().base, false, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
asmgen.out(" sta $toValueVar")
// pre-check for end already reached
if(iterableDt.isSignedByteArray) {
if(stepsize<0) {
asmgen.out(""" asmgen.out("""
dec $varname clc
lda $varname sbc $varname
cmp #255 bvc +
bne $loopLabel""") eor #$80
+ bpl $endLabel""")
} }
else if (stepsize==-1 && range.to.asConstInteger()==1) { else {
// simple loop downto 1 step -1
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
asmgen.out(""" asmgen.out("""
dec $varname sec
bne $loopLabel""") sbc $varname
bvc +
eor #$80
+ bmi $endLabel""")
} }
else if (stepsize==1 || stepsize==-1) { } else {
// bytes array, step 1 or -1 if(stepsize<0) {
val incdec = if(stepsize==1) "inc" else "dec"
// loop over byte range via loopvar
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
// pre-check for end already reached
if(iterableDt.isSignedByteArray) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0) {
asmgen.out("""
clc
sbc $varname
bvc +
eor #${'$'}80
+ bpl $endLabel""")
}
else
asmgen.out("""
sec
sbc $varname
bvc +
eor #${'$'}80
+ bmi $endLabel""")
} else {
if(stepsize<0) {
asmgen.out("""
cmp $varname
beq +
bcs $endLabel
+""")
}
else
asmgen.out(" cmp $varname | bcc $endLabel")
asmgen.out(" sta $modifiedLabel+1")
}
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
asmgen.out(""" asmgen.out("""
lda $varname cmp $varname
$modifiedLabel cmp #0 ; modified beq +
beq $endLabel bcs $endLabel
$incdec $varname""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
} else {
// bytes, step >= 2 or <= -2
// loop over byte range via loopvar
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
// pre-check for end already reached
if(iterableDt.isSignedByteArray) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0)
asmgen.out("""
clc
sbc $varname
bvc +
eor #${'$'}80
+ bpl $endLabel""")
else
asmgen.out("""
sec
sbc $varname
bvc +
eor #${'$'}80
+ bmi $endLabel""")
} else {
if(stepsize<0)
asmgen.out("""
cmp $varname
beq +
bcs $endLabel
+""") +""")
else }
asmgen.out(" cmp $varname | bcc $endLabel") else {
asmgen.out(" sta $modifiedLabel+1") asmgen.out(" cmp $varname | bcc $endLabel")
}
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
if(stepsize>0) {
asmgen.out("""
lda $varname
clc
adc #$stepsize
sta $varname
$modifiedLabel cmp #0 ; modified
bmi $loopLabel
beq $loopLabel""")
} else {
asmgen.out("""
lda $varname
sec
sbc #${stepsize.absoluteValue}
sta $varname
$modifiedLabel cmp #0 ; modified
bpl $loopLabel""")
}
asmgen.out(endLabel)
} }
} }
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> { asmgen.out(loopLabel)
val varname = asmgen.asmVariableName(stmt.variable) asmgen.translate(forloop.statements)
assignLoopvarWord(stmt, range) asmgen.out("""
if(stepsize==-1 && range.to.asConstInteger()==0) { lda $varname
// simple loop downto 0 step -1 (words) cmp $toValueVar
asmgen.out(loopLabel) beq $endLabel
asmgen.translate(stmt.statements) $incdec $varname""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
} else {
// use self-modifying code to store the loop end comparison value
val modifiedLabel = asmgen.makeLabel("for_modified")
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
// pre-check for end already reached
if(iterableDt.isSignedByteArray) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0) {
asmgen.out(""" asmgen.out("""
lda $varname clc
bne ++ sbc $varname
lda $varname+1 bvc +
beq $endLabel eor #$80
+ lda $varname + bpl $endLabel""")
bne +
dec $varname+1
+ dec $varname""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
} }
else if (stepsize==-1 && range.to.asConstInteger()==1) { else
// simple loop downto 1 step -1 (words)
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
asmgen.out(""" asmgen.out("""
lda $varname sec
cmp #1 sbc $varname
bne + bvc +
lda $varname+1 eor #$80
beq $endLabel + bmi $endLabel""")
+ lda $varname } else {
bne + if(stepsize<0) {
dec $varname+1 asmgen.out("""
+ dec $varname""") cmp $varname
asmgen.jmp(loopLabel) beq +
asmgen.out(endLabel) bcs $endLabel
+""")
} }
else if (stepsize == 1 || stepsize == -1) { else {
// words, step 1 or -1 asmgen.out(" cmp $varname | bcc $endLabel")
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) }
precheckFromToWord(iterableDt, stepsize, varname, endLabel) asmgen.out(" sta $modifiedLabel+1")
asmgen.out(""" }
asmgen.out(loopLabel)
asmgen.translate(forloop.statements)
asmgen.out("""
lda $varname
$modifiedLabel cmp #0 ; modified
beq $endLabel
$incdec $varname""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
}
}
private fun forOverBytesRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
// bytes range, step >= 2 or <= -2
val stepsize = range.step.asConstInteger()!!
val modifiedLabel = asmgen.makeLabel("for_modified")
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
// pre-check for end already reached
if(iterableDt.isSignedByteArray) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0)
asmgen.out("""
clc
sbc $varname
bvc +
eor #$80
+ bpl $endLabel""")
else
asmgen.out("""
sec
sbc $varname
bvc +
eor #$80
+ bmi $endLabel""")
} else {
if(stepsize<0)
asmgen.out("""
cmp $varname
beq +
bcs $endLabel
+""")
else {
asmgen.out(" cmp $varname | bcc $endLabel")
}
asmgen.out(" sta $modifiedLabel+1")
}
asmgen.out(loopLabel)
asmgen.translate(forloop.statements)
asmgen.romableError("self-modifying code (forloop over bytes range)", forloop.position) // TODO fix romable; there is self-modifying code below
if(stepsize>0) {
asmgen.out("""
lda $varname
clc
adc #$stepsize
sta $varname
$modifiedLabel cmp #0 ; modified
bmi $loopLabel
beq $loopLabel""")
} else {
asmgen.out("""
lda $varname
sec
sbc #${stepsize.absoluteValue}
sta $varname
$modifiedLabel cmp #0 ; modified
bpl $loopLabel""")
}
asmgen.out(endLabel)
}
private fun forOverNonconstWordRange(stmt: PtForLoop, iterableDt: DataType, range: PtRange) {
val stepsize = range.step.asConstInteger()!!
val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.add(endLabel)
val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvarWord(stmt, range)
if(stepsize==-1 && range.to.asConstInteger()==0) {
// simple loop downto 0 step -1 (words)
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
asmgen.out("""
lda $varname
bne ++
lda $varname+1
beq $endLabel
+ lda $varname
bne +
dec $varname+1
+ dec $varname""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
}
else if (stepsize==-1 && range.to.asConstInteger()==1) {
// simple loop downto 1 step -1 (words)
asmgen.out(loopLabel)
asmgen.translate(stmt.statements)
asmgen.out("""
lda $varname
cmp #1
bne +
lda $varname+1
beq $endLabel
+ lda $varname
bne +
dec $varname+1
+ dec $varname""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
}
else if (stepsize == 1 || stepsize == -1)
forOverWordsRangeStepOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
else if (stepsize > 0)
forOverWordsRangeStepGreaterOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
else
forOverWordsRangeStepGreaterOneDescending(range, varname, iterableDt, loopLabel, endLabel, stmt)
}
private fun forOverWordsRangeStepOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, forloop: PtForLoop) {
// words range, step 1 or -1
val stepsize = range.step.asConstInteger()!!
if(asmgen.options.romable) {
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
// so we need to store the loop end value in a newly allocated temporary variable
val toValueVar = asmgen.createTempVarReused(iterableDt.elementType().base, false, range)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out(" sta $toValueVar")
asmgen.out(" sty $toValueVar+1")
asmgen.out(loopLabel)
asmgen.translate(forloop.statements)
asmgen.out("""
lda $varname+1
cmp $toValueVar+1
bne +
lda $varname
cmp $toValueVar
beq $endLabel""")
if(stepsize==1) {
asmgen.out("""
+ inc $varname
bne $loopLabel
inc $varname+1""")
asmgen.jmp(loopLabel)
} else {
asmgen.out("""
+ lda $varname
bne +
dec $varname+1
+ dec $varname""")
asmgen.jmp(loopLabel)
}
asmgen.out(endLabel)
} else {
val modifiedLabel = asmgen.makeLabel("for_modified")
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out("""
sty $modifiedLabel+1 sty $modifiedLabel+1
sta $modifiedLabel2+1 sta $modifiedLabel2+1
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.statements) asmgen.translate(forloop.statements)
asmgen.out(""" asmgen.out("""
lda $varname+1 lda $varname+1
$modifiedLabel cmp #0 ; modified $modifiedLabel cmp #0 ; modified
bne + bne +
lda $varname lda $varname
$modifiedLabel2 cmp #0 ; modified $modifiedLabel2 cmp #0 ; modified
beq $endLabel""") beq $endLabel""")
if(stepsize==1) { if(stepsize==1) {
asmgen.out(""" asmgen.out("""
+ inc $varname + inc $varname
bne $loopLabel bne $loopLabel
inc $varname+1""") inc $varname+1""")
asmgen.jmp(loopLabel) asmgen.jmp(loopLabel)
} else { } else {
asmgen.out(""" asmgen.out("""
+ lda $varname + lda $varname
bne + bne +
dec $varname+1 dec $varname+1
+ dec $varname""") + dec $varname""")
asmgen.jmp(loopLabel) asmgen.jmp(loopLabel)
} }
asmgen.out(endLabel) asmgen.out(endLabel)
} }
else if (stepsize > 0) { }
// (u)words, step >= 2
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) private fun forOverWordsRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
precheckFromToWord(iterableDt, stepsize, varname, endLabel) // (u)words, step >= 2
asmgen.out(""" val stepsize = range.step.asConstInteger()!!
val modifiedLabel = asmgen.makeLabel("for_modified")
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out("""
sty $modifiedLabel+1 sty $modifiedLabel+1
sta $modifiedLabel2+1 sta $modifiedLabel2+1
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
if (iterableDt.isUnsignedWordArray) { asmgen.romableError("self-modifying code (forloop over word range)", stmt.position) // TODO fix romable; there is self-modifying code below
asmgen.out(""" if (iterableDt.isUnsignedWordArray) {
asmgen.out("""
lda $varname lda $varname
clc clc
adc #<$stepsize adc #<$stepsize
@ -266,8 +393,8 @@ $modifiedLabel2 lda #0 ; modified
bcc $endLabel bcc $endLabel
bcs $loopLabel bcs $loopLabel
$endLabel""") $endLabel""")
} else { } else {
asmgen.out(""" asmgen.out("""
lda $varname lda $varname
clc clc
adc #<$stepsize adc #<$stepsize
@ -283,20 +410,23 @@ $modifiedLabel lda #0 ; modified
eor #$80 eor #$80
+ bpl $loopLabel + bpl $loopLabel
$endLabel""") $endLabel""")
} }
} }
else {
// (u)words, step <= -2 private fun forOverWordsRangeStepGreaterOneDescending(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) // (u)words, step <= -2
precheckFromToWord(iterableDt, stepsize, varname, endLabel) val stepsize = range.step.asConstInteger()!!
asmgen.out(""" val modifiedLabel = asmgen.makeLabel("for_modified")
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.romableError("self-modifying code (forloop over words range)", stmt.position) // TODO fix romable; there is self-modifying code below
asmgen.out("""
sty $modifiedLabel+1 sty $modifiedLabel+1
sta $modifiedLabel2+1 sta $modifiedLabel2+1
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
asmgen.out("""
asmgen.out("""
lda $varname lda $varname
sec sec
sbc #<${stepsize.absoluteValue} sbc #<${stepsize.absoluteValue}
@ -313,12 +443,6 @@ $modifiedLabel sbc #0 ; modified
eor #$80 eor #$80
+ bpl $loopLabel + bpl $loopLabel
$endLabel""") $endLabel""")
}
}
else -> throw AssemblyError("range expression can only be byte or word")
}
asmgen.loopEndLabels.removeLast()
} }
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) { private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
@ -330,23 +454,23 @@ $endLabel""")
sta P8ZP_SCRATCH_W2 ; to sta P8ZP_SCRATCH_W2 ; to
sty P8ZP_SCRATCH_W2+1 ; to sty P8ZP_SCRATCH_W2+1 ; to
lda $fromVar lda $fromVar
cmp P8ZP_SCRATCH_W2 cmp P8ZP_SCRATCH_W2
lda $fromVar+1 lda $fromVar+1
sbc P8ZP_SCRATCH_W2+1 sbc P8ZP_SCRATCH_W2+1
bvc + bvc +
eor #${'$'}80 eor #$80
+ bmi $endLabel + bmi $endLabel
lda P8ZP_SCRATCH_W2 lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1""") ldy P8ZP_SCRATCH_W2+1""")
else else
asmgen.out(""" asmgen.out("""
sta P8ZP_SCRATCH_REG sta P8ZP_SCRATCH_REG
cmp $fromVar cmp $fromVar
tya tya
sbc $fromVar+1 sbc $fromVar+1
bvc + bvc +
eor #${'$'}80 eor #$80
+ bmi $endLabel + bmi $endLabel
lda P8ZP_SCRATCH_REG""") lda P8ZP_SCRATCH_REG""")
} else { } else {
if(stepsize<0) if(stepsize<0)
@ -362,11 +486,11 @@ $endLabel""")
+""") +""")
else else
asmgen.out(""" asmgen.out("""
cpy $fromVar+1 cpy $fromVar+1
bcc $endLabel bcc $endLabel
bne + bne +
cmp $fromVar cmp $fromVar
bcc $endLabel bcc $endLabel
+""") +""")
} }
} }
@ -376,38 +500,56 @@ $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 -> {
asmgen.out(""" if(asmgen.options.romable) {
lda #<$iterableName val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
ldy #>$iterableName asmgen.out("""
sta $loopLabel+1 ldy #0
sty $loopLabel+2 sty $indexVar
$loopLabel lda ${65535.toHex()} ; modified $loopLabel lda $iterableName,y
beq $endLabel beq $endLabel
sta ${asmgen.asmVariableName(stmt.variable)}""") sta ${asmgen.asmVariableName(stmt.variable)}""")
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
asmgen.out(""" asmgen.out("""
inc $loopLabel+1 inc $indexVar
bne $loopLabel ldy $indexVar
inc $loopLabel+2 bne $loopLabel
bne $loopLabel
$endLabel""") $endLabel""")
} else {
val indexVar = asmgen.makeLabel("for_index")
asmgen.out("""
ldy #0
sty $indexVar
$loopLabel lda $iterableName,y
beq $endLabel
sta ${asmgen.asmVariableName(stmt.variable)}""")
asmgen.translate(stmt.statements)
asmgen.out("""
inc $indexVar
ldy $indexVar
bne $loopLabel
$indexVar .byte 0
$endLabel""")
}
} }
iterableDt.isByteArray || iterableDt.isBoolArray -> { iterableDt.isByteArray || iterableDt.isBoolArray -> {
val indexVar = asmgen.makeLabel("for_index") val indexVar = if(asmgen.options.romable)
asmgen.createTempVarReused(iterableDt.elementType().base, false, stmt)
else
asmgen.makeLabel("for_index")
asmgen.out(""" asmgen.out("""
ldy #0 ldy #0
$loopLabel sty $indexVar $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
@ -422,20 +564,25 @@ $loopLabel sty $indexVar
bne $loopLabel bne $loopLabel
beq $endLabel""") beq $endLabel""")
} }
if(numElements>=16) { if(!asmgen.options.romable) {
// allocate index var on ZP if possible if(numElements>=16u) {
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors) // allocate index var on ZP if possible, otherwise inline
result.fold( val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") }, result.fold(
failure = { asmgen.out("$indexVar .byte 0") } success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
) failure = { asmgen.out("$indexVar .byte 0") }
} else { )
asmgen.out("$indexVar .byte 0") } else {
asmgen.out("$indexVar .byte 0")
}
} }
asmgen.out(endLabel) asmgen.out(endLabel)
} }
iterableDt.isSplitWordArray -> { iterableDt.isSplitWordArray -> {
val indexVar = asmgen.makeLabel("for_index") val indexVar = if(asmgen.options.romable)
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
else
asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable) val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out(""" asmgen.out("""
ldy #0 ldy #0
@ -445,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
@ -460,21 +607,25 @@ $loopLabel sty $indexVar
bne $loopLabel bne $loopLabel
beq $endLabel""") beq $endLabel""")
} }
if(numElements>=16) { if(!asmgen.options.romable) {
// allocate index var on ZP if possible if(numElements>=16u) {
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors) // allocate index var on ZP if possible, otherwise inline
result.fold( val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") }, result.fold(
failure = { asmgen.out("$indexVar .byte 0") } success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
) failure = { asmgen.out("$indexVar .byte 0") }
} else { )
asmgen.out("$indexVar .byte 0") } else {
asmgen.out("$indexVar .byte 0")
}
} }
asmgen.out(endLabel) asmgen.out(endLabel)
} }
iterableDt.isWordArray -> { iterableDt.isWordArray -> {
val length = numElements * 2 val indexVar = if(asmgen.options.romable)
val indexVar = asmgen.makeLabel("for_index") asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
else
asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable) val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out(""" asmgen.out("""
ldy #0 ldy #0
@ -484,16 +635,16 @@ $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(length<=127) { if(numElements<=127u) {
asmgen.out(""" asmgen.out("""
ldy $indexVar ldy $indexVar
iny iny
iny iny
cpy #$length cpy #${numElements*2u}
beq $endLabel beq $endLabel
bne $loopLabel""") bne $loopLabel""")
} else { } else {
// length is 128 words, 256 bytes // array size is 128 words, 256 bytes
asmgen.out(""" asmgen.out("""
ldy $indexVar ldy $indexVar
iny iny
@ -501,15 +652,17 @@ $loopLabel sty $indexVar
bne $loopLabel bne $loopLabel
beq $endLabel""") beq $endLabel""")
} }
if(length>=16) { if(!asmgen.options.romable) {
// allocate index var on ZP if possible if(numElements>=16u) {
val result = zeropage.allocate(indexVar, DataType.forDt(BaseDataType.UBYTE), null, stmt.position, asmgen.errors) // allocate index var on ZP if possible, otherwise inline
result.fold( val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") }, result.fold(
failure = { asmgen.out("$indexVar .byte 0") } success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
) failure = { asmgen.out("$indexVar .byte 0") }
} else { )
asmgen.out("$indexVar .byte 0") } else {
asmgen.out("$indexVar .byte 0")
}
} }
asmgen.out(endLabel) asmgen.out(endLabel)
} }

View File

@ -1,5 +1,6 @@
package prog8.codegen.cpu6502 package prog8.codegen.cpu6502
import prog8.code.StNodeType
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.AsmAssignSource import prog8.codegen.cpu6502.assignment.AsmAssignSource
@ -32,8 +33,14 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!! // NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this) // (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
val symbol = asmgen.symbolTable.lookup(call.name) val symbol = asmgen.symbolTable.lookup(call.name)!!
val sub = symbol?.astNode as IPtSubroutine if(symbol.type == StNodeType.LABEL) {
require(call.void)
asmgen.out(" jsr ${asmgen.asmSymbolName(symbol.scopedNameString)}")
return
}
val sub = symbol.astNode as IPtSubroutine
val subAsmName = asmgen.asmSymbolName(call.name) val subAsmName = asmgen.asmSymbolName(call.name)
if(sub is PtAsmSub) { if(sub is PtAsmSub) {
@ -51,6 +58,10 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
if(bank==null) { if(bank==null) {
val varbank = if(sub.address?.varbank==null) null else asmgen.asmVariableName(sub.address!!.varbank!!) val varbank = if(sub.address?.varbank==null) null else asmgen.asmVariableName(sub.address!!.varbank!!)
if(varbank!=null) { if(varbank!=null) {
if(asmgen.options.romable)
TODO("no codegen yet for non-const bank in subroutine call that's usable in ROM ${call.position}")
// self-modifying code: set jsrfar bank argument
when(asmgen.options.compTarget.name) { when(asmgen.options.compTarget.name) {
"cx16" -> { "cx16" -> {
// JSRFAR can jump to a banked RAM address as well! // JSRFAR can jump to a banked RAM address as well!
@ -128,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)
@ -155,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
@ -223,9 +235,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
val param = sub.parameters[it] val param = sub.parameters[it]
val arg = call.args[it] val arg = call.args[it]
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) { registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
if(!registersUsed.any{it.statusflag!=null || it.registerOrPair in CpuRegisters}) if(!registersUsed.any{r -> r.statusflag!=null || r.registerOrPair in CpuRegisters})
argumentViaRegister(sub, IndexedValue(it, param.second), arg) argumentViaRegister(sub, IndexedValue(it, param.second), arg)
else if(registersUsed.any {it.statusflag!=null}) { else if(registersUsed.any { r-> r.statusflag!=null }) {
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}") throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
} }
else { else {
@ -315,7 +327,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
register!! register!!
if(requiredDt.largerSizeThan(value.type)) { if(requiredDt.largerSizeThan(value.type)) {
// we need to sign extend the source, do this via temporary word variable // we need to sign extend the source, do this via temporary word variable
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UBYTE)) asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type.base) asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type.base)
asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY) asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register, null, Position.DUMMY)
} else { } else {
@ -328,7 +340,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} }
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),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

@ -38,7 +38,6 @@ internal class IfElseAsmGen(private val program: PtProgram,
// use a BIT instruction to test for bit 7 or 6 set/clear // use a BIT instruction to test for bit 7 or 6 set/clear
val (testBitSet, variable, bitmask) = useBIT val (testBitSet, variable, bitmask) = useBIT
return translateIfBIT(stmt, jumpAfterIf, testBitSet, variable, bitmask) return translateIfBIT(stmt, jumpAfterIf, testBitSet, variable, bitmask)
return
} }
val rightDt = compareCond.right.type val rightDt = compareCond.right.type
@ -87,6 +86,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
if(testForBitSet) { if(testForBitSet) {
if(jumpAfterIf!=null) { if(jumpAfterIf!=null) {
val target = asmgen.getJumpTarget(jumpAfterIf) val target = asmgen.getJumpTarget(jumpAfterIf)
require(!target.indexedX)
branch("bmi", target) branch("bmi", target)
} }
else else
@ -94,6 +94,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
} else { } else {
if(jumpAfterIf!=null) { if(jumpAfterIf!=null) {
val target = asmgen.getJumpTarget(jumpAfterIf) val target = asmgen.getJumpTarget(jumpAfterIf)
require(!target.indexedX)
branch("bpl", target) branch("bpl", target)
} }
else else
@ -107,6 +108,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
if(testForBitSet) { if(testForBitSet) {
if(jumpAfterIf!=null) { if(jumpAfterIf!=null) {
val target = asmgen.getJumpTarget(jumpAfterIf) val target = asmgen.getJumpTarget(jumpAfterIf)
require(!target.indexedX)
branch("bvs", target) branch("bvs", target)
} }
else else
@ -114,6 +116,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
} else { } else {
if(jumpAfterIf!=null) { if(jumpAfterIf!=null) {
val target = asmgen.getJumpTarget(jumpAfterIf) val target = asmgen.getJumpTarget(jumpAfterIf)
require(!target.indexedX)
branch("bvc", target) branch("bvc", target)
} }
else else
@ -146,6 +149,93 @@ internal class IfElseAsmGen(private val program: PtProgram,
translateIfElseBodies("beq", ifElse) translateIfElseBodies("beq", ifElse)
} }
private fun translateIfElseBodiesSignedByte(elseConditional: String, value: PtExpression, stmt: PtIfElse) {
fun branchElse(label: String) {
when (elseConditional) {
"<" -> {
asmgen.out("""
bvc +
eor #$80
+ bpl $label""")
}
">=" -> {
asmgen.out("""
bvc +
eor #$80
+ bmi $label""")
}
else -> throw AssemblyError("wrong conditional $elseConditional")
}
}
val afterIfLabel = asmgen.makeLabel("afterif")
asmgen.cmpAwithByteValue(value, true)
if(stmt.hasElse()) {
// if and else blocks
val elseLabel = asmgen.makeLabel("else")
branchElse(elseLabel)
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
// no else block
branchElse(afterIfLabel)
asmgen.translate(stmt.ifScope)
}
asmgen.out(afterIfLabel)
}
private fun translateJumpElseBodiesSignedByte(elseConditional: String, value: PtExpression, jump: PtJump, elseBlock: PtNodeGroup) {
fun branchTarget(label: String) {
when (elseConditional) {
"<" -> {
asmgen.out("""
bvc +
eor #$80
+ bmi $label""")
}
">=" -> {
asmgen.out("""
bvc +
eor #$80
+ bpl $label""")
}
else -> throw AssemblyError("wrong conditional $elseConditional")
}
}
fun branchElse(label: String) {
when (elseConditional) {
"<" -> {
asmgen.out("""
bvc +
eor #$80
+ bpl $label""")
}
">=" -> {
asmgen.out("""
bvc +
eor #$80
+ bmi $label""")
}
else -> throw AssemblyError("wrong conditional $elseConditional")
}
}
var target = asmgen.getJumpTarget(jump, false)
asmgen.cmpAwithByteValue(value, true)
if(target.indirect) {
branchElse("+")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump)
asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
asmgen.out("+")
} else {
require(!target.needsExpressionEvaluation)
branchTarget(target.asmLabel)
}
asmgen.translate(elseBlock)
}
private fun translateIfElseBodies(elseBranchInstr: String, stmt: PtIfElse) { private fun translateIfElseBodies(elseBranchInstr: String, stmt: PtIfElse) {
// comparison value is already in A // comparison value is already in A
val afterIfLabel = asmgen.makeLabel("afterif") val afterIfLabel = asmgen.makeLabel("afterif")
@ -154,7 +244,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
val elseLabel = asmgen.makeLabel("else") val elseLabel = asmgen.makeLabel("else")
asmgen.out(" $elseBranchInstr $elseLabel") asmgen.out(" $elseBranchInstr $elseLabel")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -171,10 +261,9 @@ internal class IfElseAsmGen(private val program: PtProgram,
if(target.indirect) { if(target.indirect) {
asmgen.out(" $falseBranch +") asmgen.out(" $falseBranch +")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
asmgen.out(""" asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
jmp (${target.asmLabel}) asmgen.out("+")
+""")
} else { } else {
require(!target.needsExpressionEvaluation) require(!target.needsExpressionEvaluation)
asmgen.out(" $branchInstr ${target.asmLabel}") asmgen.out(" $branchInstr ${target.asmLabel}")
@ -210,40 +299,11 @@ internal class IfElseAsmGen(private val program: PtProgram,
translateIfElseBodies("beq", stmt) translateIfElseBodies("beq", stmt)
} }
"<" -> translateByteLess(stmt, signed, jumpAfterIf) "<" -> translateByteLess(stmt, signed, jumpAfterIf)
"<=" -> { "<=" -> translateByteLessEqual(stmt, signed, jumpAfterIf)
// X<=Y -> Y>=X (reverse of >=)
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, signed)
asmgen.cmpAwithByteValue(condition.left, false)
return if(signed) {
if(jumpAfterIf!=null)
translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("bmi", stmt)
} else {
if(jumpAfterIf!=null)
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("bcc", stmt)
}
}
">" -> translateByteGreater(stmt, signed, jumpAfterIf) ">" -> translateByteGreater(stmt, signed, jumpAfterIf)
">=" -> { ">=" -> translateByteGreaterEqual(stmt, signed, jumpAfterIf)
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
asmgen.cmpAwithByteValue(condition.right, false)
return if(signed) {
if(jumpAfterIf!=null)
translateJumpElseBodies("bpl", "bmi", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("bmi", stmt)
} else {
if(jumpAfterIf!=null)
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("bcc", stmt)
}
}
in LogicalOperators -> { in LogicalOperators -> {
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), stmt.definingISub(), condition.position, register=RegisterOrPair.A) val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, stmt.definingISub(), condition.position, register=RegisterOrPair.A)
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) { if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
if (jumpAfterIf != null) if (jumpAfterIf != null)
translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope) translateJumpElseBodies("bne", "beq", jumpAfterIf, stmt.elseScope)
@ -286,7 +346,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
if(target.indirect) { if(target.indirect) {
asmgen.out(" bmi + | beq +") asmgen.out(" bmi + | beq +")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jumpAfterIf, true) target = asmgen.getJumpTarget(jumpAfterIf)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
jmp (${target.asmLabel}) jmp (${target.asmLabel})
+""") +""")
@ -306,7 +367,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
val elseLabel = asmgen.makeLabel("else") val elseLabel = asmgen.makeLabel("else")
asmgen.out(" bmi $elseLabel | beq $elseLabel") asmgen.out(" bmi $elseLabel | beq $elseLabel")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -353,7 +414,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
if(target.indirect) { if(target.indirect) {
asmgen.out(" bmi + | bne ++") asmgen.out(" bmi + | bne ++")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jumpAfterIf, true) target = asmgen.getJumpTarget(jumpAfterIf)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
+ jmp (${target.asmLabel}) + jmp (${target.asmLabel})
+""") +""")
@ -375,7 +437,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
bpl $elseLabel bpl $elseLabel
+""") +""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -402,13 +464,13 @@ internal class IfElseAsmGen(private val program: PtProgram,
private fun translateByteLess(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) { private fun translateByteLess(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
val condition = stmt.condition as PtBinaryExpression val condition = stmt.condition as PtBinaryExpression
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed) asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
asmgen.cmpAwithByteValue(condition.right, false)
if(signed) { if(signed) {
if(jumpAfterIf!=null) if(jumpAfterIf!=null)
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope) translateJumpElseBodiesSignedByte("<", condition.right, jumpAfterIf, stmt.elseScope)
else else
translateIfElseBodies("bpl", stmt) translateIfElseBodiesSignedByte("<", condition.right, stmt)
} else { } else {
asmgen.cmpAwithByteValue(condition.right, false)
if(jumpAfterIf!=null) if(jumpAfterIf!=null)
translateJumpElseBodies("bcc", "bcs", jumpAfterIf, stmt.elseScope) translateJumpElseBodies("bcc", "bcs", jumpAfterIf, stmt.elseScope)
else else
@ -416,25 +478,43 @@ internal class IfElseAsmGen(private val program: PtProgram,
} }
} }
private fun translateByteLessEqual(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
// X<=Y -> Y>=X (reverse of >=)
val condition = stmt.condition as PtBinaryExpression
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, signed)
return if(signed) {
if(jumpAfterIf!=null)
translateJumpElseBodiesSignedByte(">=", condition.left, jumpAfterIf, stmt.elseScope)
else
translateIfElseBodiesSignedByte(">=", condition.left, stmt)
} else {
asmgen.cmpAwithByteValue(condition.left, false)
if(jumpAfterIf!=null)
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("bcc", stmt)
}
}
private fun translateByteGreater(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) { private fun translateByteGreater(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
val condition = stmt.condition as PtBinaryExpression val condition = stmt.condition as PtBinaryExpression
if(signed) { if(signed) {
// X>Y --> Y<X // X>Y --> Y<X
asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, true) asmgen.assignExpressionToRegister(condition.right, RegisterOrPair.A, true)
asmgen.cmpAwithByteValue(condition.left, true)
if (jumpAfterIf != null) if (jumpAfterIf != null)
translateJumpElseBodies("bmi", "bpl", jumpAfterIf, stmt.elseScope) translateJumpElseBodiesSignedByte("<", condition.left, jumpAfterIf, stmt.elseScope)
else else
translateIfElseBodies("bpl", stmt) translateIfElseBodiesSignedByte("<", condition.left, stmt)
} else { } else {
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A)
asmgen.cmpAwithByteValue(condition.right, false) asmgen.cmpAwithByteValue(condition.right, false)
if(jumpAfterIf!=null) { if(jumpAfterIf!=null) {
var target = asmgen.getJumpTarget(jumpAfterIf, false) var target = asmgen.getJumpTarget(jumpAfterIf, false)
if(target.indirect) { if(target.indirect) {
asmgen.out(" bcc + | beq +") asmgen.out(" bcc + | beq +")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jumpAfterIf, true) target = asmgen.getJumpTarget(jumpAfterIf)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
jmp (${target.asmLabel}) jmp (${target.asmLabel})
+""") +""")
@ -453,7 +533,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
val elseLabel = asmgen.makeLabel("else") val elseLabel = asmgen.makeLabel("else")
asmgen.out(" bcc $elseLabel | beq $elseLabel") asmgen.out(" bcc $elseLabel | beq $elseLabel")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -466,6 +546,23 @@ internal class IfElseAsmGen(private val program: PtProgram,
} }
} }
private fun translateByteGreaterEqual(stmt: PtIfElse, signed: Boolean, jumpAfterIf: PtJump?) {
val condition = stmt.condition as PtBinaryExpression
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
return if(signed) {
if(jumpAfterIf!=null)
translateJumpElseBodiesSignedByte(">=", condition.right, jumpAfterIf, stmt.elseScope)
else
translateIfElseBodiesSignedByte(">=", condition.right, stmt)
} else {
asmgen.cmpAwithByteValue(condition.right, false)
if(jumpAfterIf!=null)
translateJumpElseBodies("bcs", "bcc", jumpAfterIf, stmt.elseScope)
else
translateIfElseBodies("bcc", stmt)
}
}
private fun translateIfWord(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) { private fun translateIfWord(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
val signed = condition.left.type.isSigned val signed = condition.left.type.isSigned
val constValue = condition.right.asConstInteger() val constValue = condition.right.asConstInteger()
@ -535,7 +632,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
eor #128 eor #128
+ bpl +""") + bpl +""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
jmp (${target.asmLabel}) jmp (${target.asmLabel})
+""") +""")
@ -562,7 +660,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
eor #128 eor #128
+ bmi $elseLabel""") + bmi $elseLabel""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -590,7 +688,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
cmp $valueLsb cmp $valueLsb
bcs +""") bcs +""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
_jump jmp (${target.asmLabel}) _jump jmp (${target.asmLabel})
+""") +""")
@ -615,7 +714,7 @@ _jump jmp (${target.asmLabel})
sbc $valueMsb sbc $valueMsb
bcs $elseLabel""") bcs $elseLabel""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -667,7 +766,8 @@ _jump jmp (${target.asmLabel})
eor #128 eor #128
+ bpl +""") + bpl +""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
jmp (${target.asmLabel}) jmp (${target.asmLabel})
+""") +""")
@ -694,7 +794,7 @@ _jump jmp (${target.asmLabel})
eor #128 eor #128
+ bmi $elseLabel""") + bmi $elseLabel""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -721,7 +821,8 @@ _jump jmp (${target.asmLabel})
sbc $valueMsb sbc $valueMsb
bcc +""") bcc +""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
jmp (${target.asmLabel}) jmp (${target.asmLabel})
+""") +""")
@ -744,7 +845,7 @@ _jump jmp (${target.asmLabel})
sbc $valueMsb sbc $valueMsb
bcc $elseLabel""") bcc $elseLabel""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -784,7 +885,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")
@ -800,8 +903,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")
@ -829,7 +932,8 @@ _jump jmp (${target.asmLabel})
lda $valueLsb lda $valueLsb
bne ++""") bne ++""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
+ jmp (${target.asmLabel}) + jmp (${target.asmLabel})
+""") +""")
@ -857,7 +961,7 @@ _jump jmp (${target.asmLabel})
bne $elseLabel bne $elseLabel
+""") +""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -878,8 +982,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")
@ -908,7 +1014,8 @@ _jump jmp (${target.asmLabel})
cmp #0 cmp #0
bne ++""") bne ++""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
+ jmp (${target.asmLabel}) + jmp (${target.asmLabel})
+""") +""")
@ -936,7 +1043,7 @@ _jump jmp (${target.asmLabel})
bne $elseLabel bne $elseLabel
+""") +""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -973,7 +1080,8 @@ _jump jmp (${target.asmLabel})
lda $valueLsb lda $valueLsb
beq ++""") beq ++""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
+ jmp (${target.asmLabel}) + jmp (${target.asmLabel})
+""") +""")
@ -1001,7 +1109,7 @@ _jump jmp (${target.asmLabel})
beq $elseLabel beq $elseLabel
+""") +""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -1023,8 +1131,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")
@ -1053,7 +1163,8 @@ _jump jmp (${target.asmLabel})
cmp #0 cmp #0
beq ++""") beq ++""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
+ jmp (${target.asmLabel}) + jmp (${target.asmLabel})
+""") +""")
@ -1081,7 +1192,7 @@ _jump jmp (${target.asmLabel})
beq $elseLabel beq $elseLabel
+""") +""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -1136,7 +1247,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")
} }
@ -1157,7 +1270,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")
} }
@ -1196,7 +1311,8 @@ _jump jmp (${target.asmLabel})
cpy $valueMsb cpy $valueMsb
beq ++""") beq ++""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
+ jmp (${target.asmLabel}) + jmp (${target.asmLabel})
+""") +""")
@ -1221,7 +1337,7 @@ _jump jmp (${target.asmLabel})
beq $elseLabel beq $elseLabel
+""") +""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -1248,7 +1364,8 @@ _jump jmp (${target.asmLabel})
cpy $valueMsb cpy $valueMsb
bne +""") bne +""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
jmp (${target.asmLabel}) jmp (${target.asmLabel})
+""") +""")
@ -1273,7 +1390,7 @@ _jump jmp (${target.asmLabel})
cpy $valueMsb cpy $valueMsb
bne $elseLabel""") bne $elseLabel""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -1302,7 +1419,8 @@ _jump jmp (${target.asmLabel})
cmp ${right.name}+1 cmp ${right.name}+1
beq ++""") beq ++""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
+ jmp (${target.asmLabel}) + jmp (${target.asmLabel})
+""") +""")
@ -1331,7 +1449,7 @@ _jump jmp (${target.asmLabel})
beq $elseLabel beq $elseLabel
+""") +""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -1361,7 +1479,8 @@ _jump jmp (${target.asmLabel})
cmp ${right.name}+1 cmp ${right.name}+1
bne +""") bne +""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
jmp (${target.asmLabel}) jmp (${target.asmLabel})
+""") +""")
@ -1390,7 +1509,7 @@ _jump jmp (${target.asmLabel})
cmp ${right.name}+1 cmp ${right.name}+1
bne $elseLabel""") bne $elseLabel""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -1423,7 +1542,8 @@ _jump jmp (${target.asmLabel})
cmp #>$value cmp #>$value
beq ++""") beq ++""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
+ jmp (${target.asmLabel}) + jmp (${target.asmLabel})
+""") +""")
@ -1452,7 +1572,7 @@ _jump jmp (${target.asmLabel})
beq $elseLabel beq $elseLabel
+""") +""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -1481,7 +1601,8 @@ _jump jmp (${target.asmLabel})
cmp #>$value cmp #>$value
bne +""") bne +""")
if(target.needsExpressionEvaluation) if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true) target = asmgen.getJumpTarget(jump)
require(!target.indexedX)
asmgen.out(""" asmgen.out("""
jmp (${target.asmLabel}) jmp (${target.asmLabel})
+""") +""")
@ -1510,7 +1631,7 @@ _jump jmp (${target.asmLabel})
cmp #>$value cmp #>$value
bne $elseLabel""") bne $elseLabel""")
asmgen.translate(stmt.ifScope) asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false) asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel) asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope) asmgen.translate(stmt.elseScope)
} else { } else {
@ -1532,8 +1653,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")
@ -1596,13 +1719,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")
@ -1648,13 +1771,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

@ -15,18 +15,18 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel) evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
when { when {
expr.type.isByteOrBool -> { expr.type.isByteOrBool -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
asmgen.jmp(endLabel) asmgen.jmp(endLabel)
asmgen.out(falseLabel) asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
asmgen.out(endLabel) asmgen.out(endLabel)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false) assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
} }
expr.type.isWord || expr.type.isString -> { expr.type.isWord || expr.type.isString -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false) asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
asmgen.jmp(endLabel) asmgen.jmp(endLabel)
asmgen.out(falseLabel) asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false) asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
asmgen.out(endLabel) asmgen.out(endLabel)
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY) assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
} }
@ -43,21 +43,26 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
} }
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) { private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
if (condition is PtBinaryExpression) { when (condition) {
val rightDt = condition.right.type is PtBinaryExpression -> {
return when { val rightDt = condition.right.type
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel) return when {
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel) rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel) rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
else -> throw AssemblyError("weird dt") rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
else -> throw AssemblyError("weird dt")
}
}
is PtPrefix if condition.operator=="not" -> {
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
}
else -> {
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
asmgen.assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
} }
}
else if(condition is PtPrefix && condition.operator=="not") {
throw AssemblyError("not prefix in ifexpression should have been replaced by swapped values")
} else {
// the condition is "simple" enough to just assign its 0/1 value to a register and branch on that
asmgen.assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
} }
} }
@ -82,7 +87,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
asmgen.out(" beq $falseLabel") asmgen.out(" beq $falseLabel")
} }
in LogicalOperators -> { in LogicalOperators -> {
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.BOOL), condition.definingISub(), condition.position, register=RegisterOrPair.A) val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, condition.definingISub(), condition.position, register=RegisterOrPair.A)
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) { if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
asmgen.out(" beq $falseLabel") asmgen.out(" beq $falseLabel")
} else { } else {
@ -190,7 +195,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
beq $falseLabel beq $falseLabel
+""") +""")
} else { } else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
cmp $varRight cmp $varRight
bne + bne +
@ -214,7 +219,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
cmp $varRight+1 cmp $varRight+1
bne $falseLabel""") bne $falseLabel""")
} else { } else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
cmp $varRight cmp $varRight
bne $falseLabel bne $falseLabel
@ -237,7 +242,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
beq $falseLabel beq $falseLabel
+""") +""")
} else { } else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out(""" asmgen.out("""
cmp #<$number cmp #<$number
bne + bne +
@ -260,7 +265,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
cmp #>$number cmp #>$number
bne $falseLabel""") bne $falseLabel""")
} else { } else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out( """ asmgen.out( """
cmp #<$number cmp #<$number
bne $falseLabel bne $falseLabel
@ -279,7 +284,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
ora $varname+1 ora $varname+1
beq $falseLabel""") beq $falseLabel""")
} else { } else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel") asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
} }
} }
@ -294,7 +299,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
ora $varname+1 ora $varname+1
bne $falseLabel""") bne $falseLabel""")
} else { } else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false) asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel") asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
} }
} }

View File

@ -45,7 +45,6 @@ internal class ProgramAndVarsGen(
} }
memorySlabs() memorySlabs()
tempVars()
footer() footer()
} }
} }
@ -219,27 +218,6 @@ internal class ProgramAndVarsGen(
} }
} }
private fun tempVars() {
asmgen.out("; expression temp vars\n .section BSS")
for((dt, count) in asmgen.tempVarsCounters) {
if(count>0) {
for(num in 1..count) {
val name = asmgen.buildTempVarName(dt, num)
when (dt) {
BaseDataType.BOOL -> asmgen.out("$name .byte ?")
BaseDataType.BYTE -> asmgen.out("$name .char ?")
BaseDataType.UBYTE -> asmgen.out("$name .byte ?")
BaseDataType.WORD -> asmgen.out("$name .sint ?")
BaseDataType.UWORD -> asmgen.out("$name .word ?")
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
else -> throw AssemblyError("weird dt for extravar $dt")
}
}
}
}
asmgen.out(" .send BSS")
}
private fun footer() { private fun footer() {
var relocateBssVars = false var relocateBssVars = false
var relocateBssSlabs = false var relocateBssSlabs = false
@ -299,6 +277,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" .dsection slabs_BSS") asmgen.out(" .dsection slabs_BSS")
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("prog8_bss_section_start") asmgen.out("prog8_bss_section_start")
asmgen.out(" .dsection BSS") asmgen.out(" .dsection BSS")
if(relocateBssSlabs) if(relocateBssSlabs)
@ -306,6 +285,7 @@ internal class ProgramAndVarsGen(
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 {
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")
asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start") asmgen.out("prog8_bss_section_size = * - prog8_bss_section_start")
@ -318,6 +298,10 @@ internal class ProgramAndVarsGen(
asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"") asmgen.out(" .cerror * > ${relocatedBssEnd.toHex()}, \"too many data for slabs_BSS section\"")
} }
} }
if(relocatedBssEnd >= options.memtopAddress)
options.memtopAddress = relocatedBssEnd+1u
asmgen.out(" ; memtop check") asmgen.out(" ; memtop check")
asmgen.out(" .cerror * >= ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${(options.memtopAddress-1u).toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"") asmgen.out(" .cerror * >= ${options.memtopAddress.toHex()}, \"Program too long by \", * - ${(options.memtopAddress-1u).toHex()}, \" bytes, memtop=${options.memtopAddress.toHex()}\"")
} }
@ -346,7 +330,9 @@ internal class ProgramAndVarsGen(
initializers.forEach { assign -> initializers.forEach { assign ->
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name)) if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
asmgen.translate(assign) asmgen.translate(assign)
// the other variables that should be set to zero are done so as part of the BSS section. else
throw AssemblyError("non-zp variable should not be initialized to zero; it will be zeroed as part of BSS clear")
// the other variables that should be set to zero are done so as part of the BSS section clear.
} }
asmgen.out(" rts\n .bend") asmgen.out(" rts\n .bend")
} }
@ -363,7 +349,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
@ -377,7 +363,7 @@ 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)
} }
@ -431,7 +417,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
@ -449,7 +435,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)")
@ -485,14 +471,14 @@ internal class ProgramAndVarsGen(
sub.children.forEach { asmgen.translate(it) } sub.children.forEach { asmgen.translate(it) }
asmgen.out("; variables") asmgen.out("; variables")
asmgen.out(" .section BSS") asmgen.out(" .section BSS_NOCLEAR") // these extra vars are initialized before use
val asmGenInfo = asmgen.subroutineExtra(sub) val asmGenInfo = asmgen.subroutineExtra(sub)
for((dt, name, addr) in asmGenInfo.extraVars) { for((dt, name, addr) in asmGenInfo.extraVars) {
if(addr!=null) if(addr!=null)
asmgen.out("$name = $addr") asmgen.out("$name = $addr")
else when(dt) { else when(dt) {
BaseDataType.UBYTE -> asmgen.out("$name .byte ?") BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL -> asmgen.out("$name .byte ?")
BaseDataType.UWORD -> asmgen.out("$name .word ?") BaseDataType.UWORD, BaseDataType.WORD -> asmgen.out("$name .word ?")
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}") BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
else -> throw AssemblyError("weird dt for extravar $dt") else -> throw AssemblyError("weird dt for extravar $dt")
} }
@ -501,11 +487,11 @@ internal class ProgramAndVarsGen(
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.FLOAT_MEM_SIZE}") asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
if(asmGenInfo.usedFloatEvalResultVar2) if(asmGenInfo.usedFloatEvalResultVar2)
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.FLOAT_MEM_SIZE}") asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
asmgen.out(" .send BSS") asmgen.out(" .send BSS_NOCLEAR")
// 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)
@ -560,12 +546,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("+")
@ -629,22 +615,37 @@ internal class ProgramAndVarsGen(
val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized } val (varsNoInit, varsWithInit) = variables.partition { it.uninitialized }
if(varsNoInit.isNotEmpty()) { if(varsNoInit.isNotEmpty()) {
asmgen.out("; non-zeropage variables") asmgen.out("; non-zeropage variables")
asmgen.out(" .section BSS") val (dirty, clean) = varsNoInit.partition { it.dirty }
val (notAligned, aligned) = varsNoInit.partition { it.align==0 }
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach { fun generate(section: String, variables: List<StStaticVariable>) {
uninitializedVariable2asm(it) asmgen.out(" .section $section")
val (notAligned, aligned) = variables.partition { it.align == 0u }
notAligned.sortedWith(compareBy<StStaticVariable> { it.name }.thenBy { it.dt.base }).forEach {
uninitializedVariable2asm(it)
}
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base })
.forEach { uninitializedVariable2asm(it) }
asmgen.out(" .send $section")
} }
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base }).forEach {
uninitializedVariable2asm(it) if(clean.isNotEmpty()) {
// clean vars end up in BSS so they're at least cleared to 0 at startup
generate("BSS", clean)
}
if(dirty.isNotEmpty()) {
// Dirty vars actually are ALSO are put into BSS so they're cleared to 0 at program startup,
// but NOT at each entry of the subroutine they're declared in.
// This saves the STZ's instructions in the subroutine, while still having deterministic start state.
// So there is no actual difference here when compared to the way non-dirty variables are allocated.
generate("BSS", dirty)
} }
asmgen.out(" .send BSS")
} }
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,
@ -652,6 +653,7 @@ internal class ProgramAndVarsGen(
it.initializationStringValue!!.second, it.initializationStringValue!!.second,
it.initializationStringValue!!.first it.initializationStringValue!!.first
) )
asmgen.romableError("string (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
} }
alignedStrings.sortedBy { it.align }.forEach { alignedStrings.sortedBy { it.align }.forEach {
outputStringvar( outputStringvar(
@ -660,13 +662,22 @@ internal class ProgramAndVarsGen(
it.initializationStringValue!!.second, it.initializationStringValue!!.second,
it.initializationStringValue!!.first it.initializationStringValue!!.first
) )
asmgen.romableError("string (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
} }
notAlignedOther.sortedBy { it.type }.forEach { notAlignedOther.sortedBy { it.type }.forEach {
staticVariable2asm(it) staticVariable2asm(it)
if(it.dt.isArray)
asmgen.romableError("array (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
else
asmgen.romableError("inlined variable (${it.dt} ${it.name})", Position.DUMMY) // TODO print warning with position of the var
} }
alignedOther.sortedBy { it.align }.sortedBy { it.type }.forEach { alignedOther.sortedBy { it.align }.sortedBy { it.type }.forEach {
staticVariable2asm(it) staticVariable2asm(it)
if(it.dt.isArray)
asmgen.romableError("array (${it.dt} ${it.name}) can only be used as read-only in ROMable code", Position.DUMMY, false) // TODO print warning with position of the var
else
asmgen.romableError("inlined variable (${it.dt} ${it.name})", Position.DUMMY) // TODO print warning with position of the var
} }
} }
} }
@ -681,23 +692,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()}")
} }
@ -734,7 +749,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")
@ -742,7 +757,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 -> {
@ -844,7 +859,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())

View File

@ -2,10 +2,7 @@ package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold import com.github.michaelbull.result.fold
import com.github.michaelbull.result.onSuccess import com.github.michaelbull.result.onSuccess
import prog8.code.StNode import prog8.code.*
import prog8.code.StNodeType
import prog8.code.StStaticVariable
import prog8.code.SymbolTable
import prog8.code.core.* import prog8.code.core.*
@ -23,7 +20,13 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
zeropageVars = zeropage.allocatedVariables zeropageVars = zeropage.allocatedVariables
} }
internal fun isZpVar(scopedName: String) = scopedName in zeropageVars internal fun isZpVar(scopedName: String): Boolean {
if(scopedName in zeropageVars)
return true
val v = symboltable.lookup(scopedName)
return if(v is StMemVar) v.address <= 255u else false
}
internal fun getFloatAsmConst(number: Double): String { internal fun getFloatAsmConst(number: Double): String {
val asmName = globalFloatConsts[number] val asmName = globalFloatConsts[number]
@ -49,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
@ -57,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
) )
@ -76,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
) )
@ -89,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) {
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

@ -2,7 +2,10 @@ package prog8.codegen.cpu6502.assignment
import prog8.code.ast.PtBinaryExpression import prog8.code.ast.PtBinaryExpression
import prog8.code.ast.PtExpression import prog8.code.ast.PtExpression
import prog8.code.core.* import prog8.code.core.AssemblyError
import prog8.code.core.ComparisonOperators
import prog8.code.core.DataType
import prog8.code.core.RegisterOrPair
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.code.target.Cx16Target import prog8.code.target.Cx16Target
import prog8.codegen.cpu6502.AsmGen6502Internal import prog8.codegen.cpu6502.AsmGen6502Internal
@ -43,6 +46,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")
} }
} }
@ -50,17 +59,17 @@ internal class AnyExprAsmGen(
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.operator) { when(expr.operator) {
"+" -> { "+" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out(" pha") asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE)) asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1") asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target) asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true return true
} }
"-" -> { "-" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out(" pha") asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE)) asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1") asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target) asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true return true
@ -73,25 +82,25 @@ internal class AnyExprAsmGen(
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}") "and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}") "or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
"&" -> { "&" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out(" pha") asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE)) asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | and P8ZP_SCRATCH_B1") asmgen.out(" pla | and P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target) asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true return true
} }
"|" -> { "|" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out(" pha") asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE)) asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | ora P8ZP_SCRATCH_B1") asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target) asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true return true
} }
"^", "xor" -> { "^", "xor" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out(" pha") asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE)) asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | eor P8ZP_SCRATCH_B1") asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
asmgen.assignRegister(RegisterOrPair.A, assign.target) asmgen.assignRegister(RegisterOrPair.A, assign.target)
return true return true
@ -190,14 +199,14 @@ internal class AnyExprAsmGen(
asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true) asmgen.assignExpressionToRegister(right, RegisterOrPair.FAC2, true)
if (!right.isSimple()) asmgen.popFAC1() if (!right.isSimple()) asmgen.popFAC1()
} }
else -> TODO("don't know how to evaluate float expression for selected compilation target") else -> TODO("don't know how to evaluate float expression for selected compilation target ${left.position}")
} }
} }
private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) { private fun setupFloatComparisonFAC1vsVarAY(expr: PtBinaryExpression) {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true) asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.FAC1, true)
if(!expr.right.isSimple()) asmgen.pushFAC1() if(!expr.right.isSimple()) asmgen.pushFAC1()
asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.forDt(BaseDataType.FLOAT)) asmgen.assignExpressionToVariable(expr.right, "floats.floats_temp_var", DataType.FLOAT)
if(!expr.right.isSimple()) asmgen.popFAC1() if(!expr.right.isSimple()) asmgen.popFAC1()
asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var") asmgen.out(" lda #<floats.floats_temp_var | ldy #>floats.floats_temp_var")
} }

View File

@ -3,7 +3,6 @@ package prog8.codegen.cpu6502.assignment
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.returnsWhatWhere
internal enum class TargetStorageKind { internal enum class TargetStorageKind {
@ -40,8 +39,11 @@ 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 {
@ -53,7 +55,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
fun fromAstAssignmentMulti(targets: List<PtAssignTarget>, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): List<AsmAssignTarget> { fun fromAstAssignmentMulti(targets: List<PtAssignTarget>, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): List<AsmAssignTarget> {
return targets.map { return targets.map {
if(it.void) if(it.void)
AsmAssignTarget(TargetStorageKind.VOID, asmgen, DataType.forDt(BaseDataType.UNDEFINED), null, it.position) AsmAssignTarget(TargetStorageKind.VOID, asmgen, DataType.UNDEFINED, null, it.position)
else else
fromAstAssignment(it, definingSub, asmgen) fromAstAssignment(it, definingSub, asmgen)
} }
@ -88,18 +90,18 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
RegisterOrPair.A, RegisterOrPair.A,
RegisterOrPair.X, RegisterOrPair.X,
RegisterOrPair.Y -> { RegisterOrPair.Y -> {
val dt = DataType.forDt(if(signed) BaseDataType.BYTE else BaseDataType.UBYTE) val dt = if(signed) DataType.BYTE else DataType.UBYTE
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers) AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
} }
RegisterOrPair.AX, RegisterOrPair.AX,
RegisterOrPair.AY, RegisterOrPair.AY,
RegisterOrPair.XY -> { RegisterOrPair.XY -> {
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD) val dt = if(signed) DataType.WORD else DataType.UWORD
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers) AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
} }
RegisterOrPair.FAC1, RegisterOrPair.FAC1,
RegisterOrPair.FAC2 -> { RegisterOrPair.FAC2 -> {
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.FLOAT), scope, pos, register = registers) AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
} }
RegisterOrPair.R0, RegisterOrPair.R0,
RegisterOrPair.R1, RegisterOrPair.R1,
@ -117,7 +119,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
RegisterOrPair.R13, RegisterOrPair.R13,
RegisterOrPair.R14, RegisterOrPair.R14,
RegisterOrPair.R15 -> { RegisterOrPair.R15 -> {
val dt = DataType.forDt(if(signed) BaseDataType.WORD else BaseDataType.UWORD) val dt = if(signed) DataType.WORD else DataType.UWORD
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers) AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
} }
} }
@ -161,8 +163,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 {
@ -171,7 +176,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv) return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
val bv = value as? PtBool val bv = value as? PtBool
if(bv!=null) if(bv!=null)
return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.forDt(BaseDataType.BOOL), boolean = bv) return AsmAssignSource(SourceStorageKind.LITERALBOOLEAN, program, asmgen, DataType.BOOL, boolean = bv)
return when(value) { return when(value) {
// checked above: is PtNumber -> throw AssemblyError("should have been constant value") // checked above: is PtNumber -> throw AssemblyError("should have been constant value")
@ -192,7 +197,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
} }
} }
is PtMemoryByte -> { is PtMemoryByte -> {
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.forDt(BaseDataType.UBYTE), memory = value) AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
} }
is PtArrayIndexer -> { is PtArrayIndexer -> {
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value) AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, value.type, array = value)
@ -204,8 +209,8 @@ 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.forDt(BaseDataType.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
?: throw AssemblyError("can't translate zero return values in assignment") ?: throw AssemblyError("can't translate zero return values in assignment")

View File

@ -1,15 +1,10 @@
package prog8.codegen.cpu6502.assignment package prog8.codegen.cpu6502.assignment
import prog8.code.StMemVar import prog8.code.*
import prog8.code.StExtSub
import prog8.code.StExtSubParameter
import prog8.code.StStaticVariable
import prog8.code.StSub
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.returnsWhatWhere
internal class AssignmentAsmGen( internal class AssignmentAsmGen(
@ -44,7 +39,7 @@ internal class AssignmentAsmGen(
if(extsub!=null) { if(extsub!=null) {
require(extsub.returns.size>=2) require(extsub.returns.size>=2)
if(extsub.returns.any { it.type.isFloat }) if(extsub.returns.any { it.type.isFloat })
TODO("deal with (multiple?) FP return registers") TODO("deal with (multiple?) FP return registers ${assignment.position}")
asmgen.translate(values) asmgen.translate(values)
@ -154,7 +149,7 @@ internal class AssignmentAsmGen(
else -> throw AssemblyError("weird dt") else -> throw AssemblyError("weird dt")
} }
} }
else TODO("array target for multi-value assignment") // Not done yet due to result register clobbering complexity else TODO("array target for multi-value assignment ${target.position}") // Not done yet due to result register clobbering complexity
} }
} }
} }
@ -242,7 +237,7 @@ internal class AssignmentAsmGen(
targetDt.isSignedWord -> assignVariableWord(assign.target, variable, assign.source.datatype) targetDt.isSignedWord -> assignVariableWord(assign.target, variable, assign.source.datatype)
targetDt.isUnsignedWord -> { targetDt.isUnsignedWord -> {
if(assign.source.datatype.isPassByRef) if(assign.source.datatype.isPassByRef)
assignAddressOf(assign.target, variable, false, null, null) assignAddressOf(assign.target, variable, false, assign.source.datatype, assign.source.array ?: PtNumber(BaseDataType.UBYTE, 0.0, assign.position))
else else
assignVariableWord(assign.target, variable, assign.source.datatype) assignVariableWord(assign.target, variable, assign.source.datatype)
} }
@ -254,7 +249,12 @@ internal class AssignmentAsmGen(
SourceStorageKind.ARRAY -> { SourceStorageKind.ARRAY -> {
val value = assign.source.array!! val value = assign.source.array!!
val elementDt = assign.source.datatype val elementDt = assign.source.datatype
val arrayVarName = asmgen.asmVariableName(value.variable) val valueVar = value.variable
if(valueVar==null) {
TODO("translate assignmenton pointer ${value.position}")
return
}
val arrayVarName = asmgen.asmVariableName(valueVar)
val constIndex = value.index.asConstInteger() val constIndex = value.index.asConstInteger()
if(value.splitWords) { if(value.splitWords) {
@ -383,7 +383,7 @@ internal class AssignmentAsmGen(
} }
// fallback assignmen through temporary pointer var // fallback assignmen through temporary pointer var
assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.UWORD)
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false) asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false)
assignRegisterByte(target, CpuRegister.A, false, true) assignRegisterByte(target, CpuRegister.A, false, true)
} }
@ -434,7 +434,7 @@ internal class AssignmentAsmGen(
// } // }
} }
if(saveA) asmgen.out(" pha") if(saveA) asmgen.out(" pha")
assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.UWORD)
if(saveA) asmgen.out(" pla") if(saveA) asmgen.out(" pla")
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2", false) asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2", false)
} }
@ -443,15 +443,21 @@ internal class AssignmentAsmGen(
private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) { private fun assignExpression(assign: AsmAssignment, scope: IPtSubroutine?) {
when(val value = assign.source.expression!!) { when(val value = assign.source.expression!!) {
is PtAddressOf -> { is PtAddressOf -> {
val arrayDt = value.identifier.type val identifier = value.identifier
val sourceName = if(identifier==null) TODO("read &dereference")
if(value.isMsbForSplitArray) else {
asmgen.asmSymbolName(value.identifier) + "_msb" val source = asmgen.symbolTable.lookup(identifier.name)
else if(arrayDt.isSplitWordArray) require(source !is StConstant) { "addressOf of a constant should have been rewritten to a simple addition expression" }
asmgen.asmSymbolName(value.identifier) + "_lsb" // the _lsb split array comes first in memory val arrayDt = identifier.type
else val sourceName =
asmgen.asmSymbolName(value.identifier) if (value.isMsbForSplitArray)
assignAddressOf(assign.target, sourceName, value.isMsbForSplitArray, arrayDt, value.arrayIndexExpr) asmgen.asmSymbolName(identifier) + "_msb"
else if (arrayDt.isSplitWordArray)
asmgen.asmSymbolName(identifier) + "_lsb" // the _lsb split array comes first in memory
else
asmgen.asmSymbolName(identifier)
assignAddressOf(assign.target, sourceName, value.isMsbForSplitArray, arrayDt, value.arrayIndexExpr)
}
} }
is PtBool -> throw AssemblyError("source kind should have been literalboolean") is PtBool -> throw AssemblyError("source kind should have been literalboolean")
is PtNumber -> throw AssemblyError("source kind should have been literalnumber") is PtNumber -> throw AssemblyError("source kind should have been literalnumber")
@ -609,12 +615,14 @@ internal class AssignmentAsmGen(
val symbol = asmgen.symbolTable.lookup(value.name) val symbol = asmgen.symbolTable.lookup(value.name)
val sub = symbol!!.astNode as IPtSubroutine val sub = symbol!!.astNode as IPtSubroutine
asmgen.translateFunctionCall(value) asmgen.translateFunctionCall(value)
if(sub is PtSub && sub.returns.size>1) { if(sub is PtSub && sub.signature.returns.size>1) {
// multi-value returns are passed throug cx16.R15 down to R0 (allows unencumbered use of many Rx registers if you don't return that many values) // note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
val registersReverseOrder = Cx16VirtualRegisters.reversed() // (this allows unencumbered use of many Rx registers if you don't return that many values)
assign.targets.zip(registersReverseOrder).forEach { (target, register) -> val returnRegs = sub.returnsWhatWhere()
if(target.kind!=TargetStorageKind.VOID) assign.targets.zip(returnRegs).forEach { target ->
assignVirtualRegister(target, register) if(target.first.kind != TargetStorageKind.VOID) {
asmgen.assignRegister(target.second.first.registerOrPair!!, target.first)
}
} }
} else { } else {
val target = assign.target val target = assign.target
@ -700,13 +708,16 @@ internal class AssignmentAsmGen(
} }
} }
private fun assignVirtualRegister(target: AsmAssignTarget, register: RegisterOrPair) { internal fun assignVirtualRegister(target: AsmAssignTarget, register: RegisterOrPair) {
// Note: while the virtual register R0-R15 can hold a word value,
// the actual datatype that gets assigned is determined by the assignment target.
// This can be a single byte!
when { when {
target.datatype.isByteOrBool -> { target.datatype.isByteOrBool -> {
if(register in Cx16VirtualRegisters) { if(register in Cx16VirtualRegisters) {
asmgen.out(" lda cx16.${register.toString().lowercase()}L") asmgen.out(" lda cx16.${register.toString().lowercase()}L")
} else { } else {
TODO("LDA byte from $register") TODO("LDA byte from $register ${target.position}")
} }
assignRegisterByte(target, CpuRegister.A, false, false) assignRegisterByte(target, CpuRegister.A, false, false)
} }
@ -850,7 +861,7 @@ internal class AssignmentAsmGen(
RegisterOrPair.A -> "a" RegisterOrPair.A -> "a"
RegisterOrPair.X -> "x" RegisterOrPair.X -> "x"
RegisterOrPair.Y -> "y" RegisterOrPair.Y -> "y"
else -> TODO("comparison to word register") else -> TODO("comparison to word register ${expr.position}")
} }
val assignTrue = PtInlineAssembly("\tld${reg} #1", false, assign.target.position) val assignTrue = PtInlineAssembly("\tld${reg} #1", false, assign.target.position)
val assignFalse = PtInlineAssembly("\tld${reg} #0", false, assign.target.position) val assignFalse = PtInlineAssembly("\tld${reg} #0", false, assign.target.position)
@ -920,12 +931,18 @@ internal class AssignmentAsmGen(
val tgt = PtAssignTarget(false, assign.target.position) val tgt = PtAssignTarget(false, assign.target.position)
val targetarray = assign.target.array!! val targetarray = assign.target.array!!
val array = PtArrayIndexer(assign.target.datatype, targetarray.position) val array = PtArrayIndexer(assign.target.datatype, targetarray.position)
array.add(targetarray.variable)
array.add(targetarray.index) val targetArrayVar = targetarray.variable
tgt.add(array) if(targetArrayVar==null) {
assignTrue = PtAssignment(assign.position) TODO("optimized comparison on pointer ${targetarray.position}")
assignTrue.add(tgt) } else {
assignTrue.add(PtNumber.fromBoolean(true, assign.position)) array.add(targetArrayVar)
array.add(targetarray.index)
tgt.add(array)
assignTrue = PtAssignment(assign.position)
assignTrue.add(tgt)
assignTrue.add(PtNumber.fromBoolean(true, assign.position))
}
} }
TargetStorageKind.REGISTER -> { /* handled earlier */ return true } TargetStorageKind.REGISTER -> { /* handled earlier */ return true }
TargetStorageKind.VOID -> { /* do nothing */ return true } TargetStorageKind.VOID -> { /* do nothing */ return true }
@ -971,7 +988,7 @@ internal class AssignmentAsmGen(
if(!directIntoY(expr.right)) asmgen.out(" pha") if(!directIntoY(expr.right)) asmgen.out(" pha")
assignExpressionToRegister(expr.right, RegisterOrPair.Y, false) assignExpressionToRegister(expr.right, RegisterOrPair.Y, false)
if(!directIntoY(expr.right)) asmgen.out(" pla") if(!directIntoY(expr.right)) asmgen.out(" pla")
asmgen.out(" jsr prog8_math.divmod_ub_asm") asmgen.out(" jsr prog8_math.remainder_ub_asm")
if(target.register==RegisterOrPair.A) if(target.register==RegisterOrPair.A)
asmgen.out(" cmp #0") // fix the status register asmgen.out(" cmp #0") // fix the status register
else else
@ -981,7 +998,7 @@ internal class AssignmentAsmGen(
expr.type.isUnsignedWord -> { expr.type.isUnsignedWord -> {
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1") asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
asmgen.out(" jsr prog8_math.divmod_uw_asm") asmgen.out(" jsr prog8_math.divmod_uw_asm")
assignVariableWord(target, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) assignVariableWord(target, "P8ZP_SCRATCH_W2", DataType.UWORD)
return true return true
} }
else -> return false else -> return false
@ -1186,19 +1203,18 @@ internal class AssignmentAsmGen(
} }
} else if(dt.isWord) { } else if(dt.isWord) {
if(shifts==7 && expr.operator == "<<") { if(shifts==7 && expr.operator == "<<") {
// optimized shift left 7 (*128) by first swapping the lsb/msb and then doing just one final shift // optimized shift left 7 (*128) by swapping the lsb/msb and then doing just one final shift
assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed) assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed)
asmgen.out(""" asmgen.out("""
; shift left 7 ; shift left 7
sty P8ZP_SCRATCH_REG ; msb sty P8ZP_SCRATCH_REG
lsr P8ZP_SCRATCH_REG lsr P8ZP_SCRATCH_REG
php ; save carry ror a
sta P8ZP_SCRATCH_REG sta P8ZP_SCRATCH_REG
lda #0 lda #0
plp ; restore carry
ror P8ZP_SCRATCH_REG
ror a ror a
ldy P8ZP_SCRATCH_REG""") ldy P8ZP_SCRATCH_REG""")
assignRegisterpairWord(target, RegisterOrPair.AY)
return true return true
} }
@ -1311,25 +1327,35 @@ internal class AssignmentAsmGen(
val rightArrayIndexer = expr.right as? PtArrayIndexer val rightArrayIndexer = expr.right as? PtArrayIndexer
if(expr.operator=="+" && leftArrayIndexer!=null && leftArrayIndexer.type.isByte && right.type.isByte) { if(expr.operator=="+" && leftArrayIndexer!=null && leftArrayIndexer.type.isByte && right.type.isByte) {
// special optimization for bytearray[y] + bytevalue : no need to use a tempvar, just use adc array,y // special optimization for bytearray[y] + bytevalue : no need to use a tempvar, just use adc array,y
assignExpressionToRegister(right, RegisterOrPair.A, right.type.isSigned) val leftArrayVar = leftArrayIndexer.variable
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pha") if(leftArrayVar==null) {
asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y, false) TODO("optimized plusmin pointer ${leftArrayIndexer.position}")
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pla") } else {
val arrayvarname = asmgen.asmSymbolName(leftArrayIndexer.variable) assignExpressionToRegister(right, RegisterOrPair.A, right.type.isSigned)
asmgen.out(" clc | adc $arrayvarname,y") if (!leftArrayIndexer.index.isSimple()) asmgen.out(" pha")
assignRegisterByte(target, CpuRegister.A, dt.isSigned, true) asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y)
if (!leftArrayIndexer.index.isSimple()) asmgen.out(" pla")
val arrayvarname = asmgen.asmSymbolName(leftArrayVar)
asmgen.out(" clc | adc $arrayvarname,y")
assignRegisterByte(target, CpuRegister.A, dt.isSigned, true)
}
} else if(rightArrayIndexer!=null && rightArrayIndexer.type.isByte && left.type.isByte) { } else if(rightArrayIndexer!=null && rightArrayIndexer.type.isByte && left.type.isByte) {
// special optimization for bytevalue +/- bytearray[y] : no need to use a tempvar, just use adc array,y or sbc array,y // special optimization for bytevalue +/- bytearray[y] : no need to use a tempvar, just use adc array,y or sbc array,y
assignExpressionToRegister(left, RegisterOrPair.A, left.type.isSigned) val rightArrayVar = rightArrayIndexer.variable
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pha") if(rightArrayVar==null) {
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y, false) TODO("optimized plusmin pointer ${rightArrayIndexer.position}")
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pla") } else {
val arrayvarname = asmgen.asmSymbolName(rightArrayIndexer.variable) assignExpressionToRegister(left, RegisterOrPair.A, left.type.isSigned)
if (expr.operator == "+") if (!rightArrayIndexer.index.isSimple()) asmgen.out(" pha")
asmgen.out(" clc | adc $arrayvarname,y") asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y)
else if (!rightArrayIndexer.index.isSimple()) asmgen.out(" pla")
asmgen.out(" sec | sbc $arrayvarname,y") val arrayvarname = asmgen.asmSymbolName(rightArrayVar)
assignRegisterByte(target, CpuRegister.A, dt.isSigned, true) if (expr.operator == "+")
asmgen.out(" clc | adc $arrayvarname,y")
else
asmgen.out(" sec | sbc $arrayvarname,y")
assignRegisterByte(target, CpuRegister.A, dt.isSigned, true)
}
} else if(expr.operator=="+" && leftMemByte!=null && right.type.isByte && optimizedPointerIndexPlusMinusByteIntoA(right, "+", leftMemByte)) { } else if(expr.operator=="+" && leftMemByte!=null && right.type.isByte && optimizedPointerIndexPlusMinusByteIntoA(right, "+", leftMemByte)) {
assignRegisterByte(target, CpuRegister.A, dt.isSigned, true) assignRegisterByte(target, CpuRegister.A, dt.isSigned, true)
return true return true
@ -1355,7 +1381,7 @@ internal class AssignmentAsmGen(
return true return true
} }
} }
} else if(dt.isWord) { } else if(dt.isWord || dt.isPointer) {
fun doAddOrSubWordExpr() { fun doAddOrSubWordExpr() {
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1") asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
@ -1382,11 +1408,13 @@ internal class AssignmentAsmGen(
when (right) { when (right) {
is PtAddressOf -> { is PtAddressOf -> {
var symbol = asmgen.asmVariableName(right.identifier)
if(right.isFromArrayElement) { if(right.isFromArrayElement) {
TODO("address-of array element $symbol at ${right.position}") TODO("address-of array element at ${right.position}")
} else if(right.dereference!=null) {
TODO("read &dereference")
} else { } else {
if(right.identifier.type.isSplitWordArray) { var symbol = asmgen.asmVariableName(right.identifier!!)
if(right.identifier!!.type.isSplitWordArray) {
symbol = if(right.isMsbForSplitArray) symbol+"_msb" else symbol+"_lsb" symbol = if(right.isMsbForSplitArray) symbol+"_msb" else symbol+"_lsb"
} }
assignExpressionToRegister(left, RegisterOrPair.AY, dt.isSigned) assignExpressionToRegister(left, RegisterOrPair.AY, dt.isSigned)
@ -1497,7 +1525,7 @@ internal class AssignmentAsmGen(
if(right.type.isWord && castedValue.type.isByte && castedValue is PtIdentifier) { if(right.type.isWord && castedValue.type.isByte && castedValue is PtIdentifier) {
if(right.type.isSigned) { if(right.type.isSigned) {
// we need to sign extend, do this via temporary word variable // we need to sign extend, do this via temporary word variable
asmgen.assignExpressionToVariable(right, "P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.WORD)) asmgen.assignExpressionToVariable(right, "P8ZP_SCRATCH_W1", DataType.WORD)
assignExpressionToRegister(left, RegisterOrPair.AY, dt.isSigned) assignExpressionToRegister(left, RegisterOrPair.AY, dt.isSigned)
if(expr.operator=="+") { if(expr.operator=="+") {
asmgen.out(""" asmgen.out("""
@ -1567,8 +1595,8 @@ internal class AssignmentAsmGen(
if(ptrVar!=null && asmgen.isZpVar(ptrVar)) { if(ptrVar!=null && asmgen.isZpVar(ptrVar)) {
assignExpressionToRegister(value, RegisterOrPair.A, false) assignExpressionToRegister(value, RegisterOrPair.A, false)
val pointername = asmgen.asmVariableName(ptrVar) val pointername = asmgen.asmVariableName(ptrVar)
if (constOffset != null && constOffset < 256) { if (constOffset != null) {
// we have value + @(zpptr + 255), or value - @(zpptr+255) // we have value + @(zpptr + 255), or value - @(zpptr+255). the offset is always <256.
asmgen.out(" ldy #$constOffset") asmgen.out(" ldy #$constOffset")
if (operator == "+") if (operator == "+")
asmgen.out(" clc | adc ($pointername),y") asmgen.out(" clc | adc ($pointername),y")
@ -1613,10 +1641,16 @@ internal class AssignmentAsmGen(
} }
val rightArray = expr.right as? PtArrayIndexer val rightArray = expr.right as? PtArrayIndexer
if(rightArray!=null) { if(rightArray!=null) {
val rightArrayVar = rightArray.variable
if(rightArrayVar==null) {
TODO("optimized bitwise pointer ${rightArray.position}")
return false
}
val constIndex = rightArray.index.asConstInteger() val constIndex = rightArray.index.asConstInteger()
if(constIndex!=null) { if(constIndex!=null) {
assignExpressionToRegister(expr.left, RegisterOrPair.A, false) assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
val valueVarname = "${asmgen.asmSymbolName(rightArray.variable)} + ${program.memsizer.memorySize(rightArray.type, constIndex)}" val valueVarname = "${asmgen.asmSymbolName(rightArrayVar)} + ${program.memsizer.memorySize(rightArray.type, constIndex)}"
when(expr.operator) { when(expr.operator) {
"&" -> asmgen.out(" and $valueVarname") "&" -> asmgen.out(" and $valueVarname")
"|" -> asmgen.out(" ora $valueVarname") "|" -> asmgen.out(" ora $valueVarname")
@ -1634,7 +1668,7 @@ internal class AssignmentAsmGen(
asmgen.out(" sty P8ZP_SCRATCH_B1") asmgen.out(" sty P8ZP_SCRATCH_B1")
} else { } else {
asmgen.out(" pha") asmgen.out(" pha")
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE)) assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla") asmgen.out(" pla")
} }
when (expr.operator) { when (expr.operator) {
@ -1687,7 +1721,7 @@ internal class AssignmentAsmGen(
asmgen.out(" sty P8ZP_SCRATCH_B1") asmgen.out(" sty P8ZP_SCRATCH_B1")
} else { } else {
asmgen.out(" pha") asmgen.out(" pha")
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE)) assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla") asmgen.out(" pla")
} }
when (operator) { when (operator) {
@ -1725,12 +1759,17 @@ internal class AssignmentAsmGen(
is PtArrayIndexer -> { is PtArrayIndexer -> {
val constIndex = right.index.asConstInteger() val constIndex = right.index.asConstInteger()
if(constIndex!=null) { if(constIndex!=null) {
val valueVarname = "${asmgen.asmSymbolName(right.variable)} + ${program.memsizer.memorySize(right.type, constIndex)}" val rightArrayVar = right.variable
when(operator) { if(rightArrayVar==null) {
"and" -> asmgen.out(" and $valueVarname") TODO("assign result into A pointer ${right.position}")
"or" -> asmgen.out(" ora $valueVarname") } else {
"xor" -> asmgen.out(" eor $valueVarname") val valueVarname = "${asmgen.asmSymbolName(rightArrayVar)} + ${program.memsizer.memorySize(right.type, constIndex)}"
else -> throw AssemblyError("invalid logical operator") when(operator) {
"and" -> asmgen.out(" and $valueVarname")
"or" -> asmgen.out(" ora $valueVarname")
"xor" -> asmgen.out(" eor $valueVarname")
else -> throw AssemblyError("invalid logical operator")
}
} }
} }
else assignViaScratch() else assignViaScratch()
@ -1739,6 +1778,17 @@ internal class AssignmentAsmGen(
} }
} }
fun requiresCmp(expr: PtExpression) =
when (expr) {
is PtFunctionCall -> {
val function = asmgen.symbolTable.lookup(expr.name)
function is StExtSub // don't assume the extsub/asmsub has set the cpu flags correctly on exit, add an explicit cmp
}
is PtBuiltinFunctionCall -> true
is PtIfExpression -> true
else -> false
}
if(!expr.right.isSimple() && expr.operator!="xor") { if(!expr.right.isSimple() && expr.operator!="xor") {
// shortcircuit evaluation into A // shortcircuit evaluation into A
val shortcutLabel = asmgen.makeLabel("shortcut") val shortcutLabel = asmgen.makeLabel("shortcut")
@ -1746,15 +1796,23 @@ internal class AssignmentAsmGen(
"and" -> { "and" -> {
// short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT) // short-circuit LEFT and RIGHT --> if LEFT then RIGHT else LEFT (== if !LEFT then LEFT else RIGHT)
assignExpressionToRegister(expr.left, RegisterOrPair.A, false) assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
if(requiresCmp(expr.left))
asmgen.out(" cmp #0")
asmgen.out(" beq $shortcutLabel") asmgen.out(" beq $shortcutLabel")
assignExpressionToRegister(expr.right, RegisterOrPair.A, false) assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
if(requiresCmp(expr.right))
asmgen.out(" cmp #0")
asmgen.out(shortcutLabel) asmgen.out(shortcutLabel)
} }
"or" -> { "or" -> {
// short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT // short-circuit LEFT or RIGHT --> if LEFT then LEFT else RIGHT
assignExpressionToRegister(expr.left, RegisterOrPair.A, false) assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
if(requiresCmp(expr.left))
asmgen.out(" cmp #0")
asmgen.out(" bne $shortcutLabel") asmgen.out(" bne $shortcutLabel")
assignExpressionToRegister(expr.right, RegisterOrPair.A, false) assignExpressionToRegister(expr.right, RegisterOrPair.A, false)
if(requiresCmp(expr.right))
asmgen.out(" cmp #0")
asmgen.out(shortcutLabel) asmgen.out(shortcutLabel)
} }
else -> throw AssemblyError("invalid logical operator") else -> throw AssemblyError("invalid logical operator")
@ -1841,8 +1899,7 @@ internal class AssignmentAsmGen(
"==" -> { "==" -> {
val dt = expr.left.type val dt = expr.left.type
when { when {
dt.isBool -> TODO("compare bool to 0") dt.isBool || dt.isByte -> {
dt.isByte -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt.isSigned) assignExpressionToRegister(expr.left, RegisterOrPair.A, dt.isSigned)
asmgen.out(""" asmgen.out("""
cmp #0 cmp #0
@ -1877,8 +1934,7 @@ internal class AssignmentAsmGen(
"!=" -> { "!=" -> {
val dt = expr.left.type val dt = expr.left.type
when { when {
dt.isBool -> TODO("compare bool to 0") dt.isBool || dt.isByte -> {
dt.isByte -> {
assignExpressionToRegister(expr.left, RegisterOrPair.A, dt.isSigned) assignExpressionToRegister(expr.left, RegisterOrPair.A, dt.isSigned)
asmgen.out(" beq + | lda #1") asmgen.out(" beq + | lda #1")
asmgen.out("+") asmgen.out("+")
@ -1962,27 +2018,27 @@ $endLabel""")
val (dt, numElements) = when(symbol) { val (dt, numElements) = when(symbol) {
is StStaticVariable -> symbol.dt to symbol.length!! is StStaticVariable -> symbol.dt to symbol.length!!
is StMemVar -> symbol.dt to symbol.length!! is StMemVar -> symbol.dt to symbol.length!!
else -> DataType.forDt(BaseDataType.UNDEFINED) to 0 else -> DataType.UNDEFINED to 0u
} }
when { when {
dt.isString -> { dt.isString -> {
assignExpressionToRegister(containment.needle, RegisterOrPair.A, elementDt.isSigned) assignExpressionToRegister(containment.needle, RegisterOrPair.A, elementDt.isSigned)
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position,"P8ZP_SCRATCH_W1"), symbolName, false, null, null) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position,"P8ZP_SCRATCH_W1"), symbolName, false, null, null)
asmgen.out(" pla") asmgen.out(" pla")
asmgen.out(" ldy #${numElements-1}") asmgen.out(" ldy #${numElements-1u}")
asmgen.out(" jsr prog8_lib.containment_bytearray") asmgen.out(" jsr prog8_lib.containment_bytearray")
} }
dt.isFloatArray -> { dt.isFloatArray -> {
assignExpressionToRegister(containment.needle, RegisterOrPair.FAC1, true) assignExpressionToRegister(containment.needle, RegisterOrPair.FAC1, true)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, false, null, null) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, false, null, null)
asmgen.out(" ldy #$numElements") asmgen.out(" ldy #$numElements")
asmgen.out(" jsr floats.containment_floatarray") asmgen.out(" jsr floats.containment_floatarray")
} }
dt.isByteArray -> { dt.isByteArray -> {
assignExpressionToRegister(containment.needle, RegisterOrPair.A, elementDt.isSigned) assignExpressionToRegister(containment.needle, RegisterOrPair.A, elementDt.isSigned)
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, false, null, null) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, false, null, null)
asmgen.out(" pla") asmgen.out(" pla")
asmgen.out(" ldy #$numElements") asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_bytearray") asmgen.out(" jsr prog8_lib.containment_bytearray")
@ -1990,11 +2046,11 @@ $endLabel""")
dt.isWordArray -> { dt.isWordArray -> {
assignExpressionToVariable(containment.needle, "P8ZP_SCRATCH_W1", elementDt) assignExpressionToVariable(containment.needle, "P8ZP_SCRATCH_W1", elementDt)
if(dt.isSplitWordArray) { if(dt.isSplitWordArray) {
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName+"_lsb", false, null, null) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName+"_lsb", false, null, null)
asmgen.out(" ldy #$numElements") asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_splitwordarray") asmgen.out(" jsr prog8_lib.containment_splitwordarray")
} else { } else {
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(BaseDataType.UWORD), containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName, false, null, null) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName, false, null, null)
asmgen.out(" ldy #$numElements") asmgen.out(" ldy #$numElements")
asmgen.out(" jsr prog8_lib.containment_linearwordarray") asmgen.out(" jsr prog8_lib.containment_linearwordarray")
} }
@ -2058,7 +2114,7 @@ $endLabel""")
if(targetDt.isWord) { if(targetDt.isWord) {
fun assignViaExprEval(addressExpression: PtExpression) { fun assignViaExprEval(addressExpression: PtExpression) {
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD)
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false) asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false)
asmgen.out(" ldy #0") asmgen.out(" ldy #0")
assignRegisterpairWord(target, RegisterOrPair.AY) assignRegisterpairWord(target, RegisterOrPair.AY)
@ -2125,7 +2181,7 @@ $endLabel""")
assignExpressionToRegister(value, RegisterOrPair.A, valueDt.isSigned) assignExpressionToRegister(value, RegisterOrPair.A, valueDt.isSigned)
assignTypeCastedRegisters(target.asmVarname, targetDt.base, RegisterOrPair.A, valueDt.base) assignTypeCastedRegisters(target.asmVarname, targetDt.base, RegisterOrPair.A, valueDt.base)
} }
valueDt.isWord -> { valueDt.isWord || valueDt.isPointer -> {
assignExpressionToRegister(value, RegisterOrPair.AY, valueDt.isSigned) assignExpressionToRegister(value, RegisterOrPair.AY, valueDt.isSigned)
assignTypeCastedRegisters(target.asmVarname, targetDt.base, RegisterOrPair.AY, valueDt.base) assignTypeCastedRegisters(target.asmVarname, targetDt.base, RegisterOrPair.AY, valueDt.base)
} }
@ -2278,6 +2334,12 @@ $endLabel""")
return return
} }
if(targetDt.isUnsignedWord && valueDt.isPointer) {
assignExpressionToRegister(value, RegisterOrPair.AY, false)
assignRegisterpairWord(target, RegisterOrPair.AY)
return
}
// No more special optimized cases yet. Do the rest via more complex evaluation // No more special optimized cases yet. Do the rest via more complex evaluation
// note: cannot use assignTypeCastedValue because that is ourselves :P // note: cannot use assignTypeCastedValue because that is ourselves :P
// NOTE: THIS MAY TURN INTO A STACK OVERFLOW ERROR IF IT CAN'T SIMPLIFY THE TYPECAST..... :-/ // NOTE: THIS MAY TURN INTO A STACK OVERFLOW ERROR IF IT CAN'T SIMPLIFY THE TYPECAST..... :-/
@ -2285,10 +2347,10 @@ $endLabel""")
} }
private fun assignCastViaLsbFunc(value: PtExpression, target: AsmAssignTarget) { private fun assignCastViaLsbFunc(value: PtExpression, target: AsmAssignTarget) {
val lsb = PtBuiltinFunctionCall("lsb", false, true, DataType.forDt(BaseDataType.UBYTE), value.position) val lsb = PtBuiltinFunctionCall("lsb", false, true, DataType.UBYTE, value.position)
lsb.parent = value.parent lsb.parent = value.parent
lsb.add(value) lsb.add(value)
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.forDt(BaseDataType.UBYTE), expression = lsb) val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, DataType.UBYTE, expression = lsb)
val assign = AsmAssignment(src, listOf(target), program.memsizer, value.position) val assign = AsmAssignment(src, listOf(target), program.memsizer, value.position)
translateNormalAssignment(assign, value.definingISub()) translateNormalAssignment(assign, value.definingISub())
} }
@ -2603,6 +2665,18 @@ $endLabel""")
} }
} }
BaseDataType.STR -> throw AssemblyError("cannot typecast a string value") BaseDataType.STR -> throw AssemblyError("cannot typecast a string value")
BaseDataType.POINTER -> {
if(targetDt.isWord || targetDt.isPointer) {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
RegisterOrPair.XY -> asmgen.out(" stx $targetAsmVarName | sty $targetAsmVarName+1")
else -> throw AssemblyError("non-word regs")
}
} else {
throw AssemblyError("cannot assign pointer to $targetDt")
}
}
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} }
@ -2611,20 +2685,31 @@ $endLabel""")
if(arrayIndexExpr!=null) { if(arrayIndexExpr!=null) {
val constIndex = arrayIndexExpr.asConstInteger() val constIndex = arrayIndexExpr.asConstInteger()
if(constIndex!=null) { if(constIndex!=null) {
if (arrayDt?.isUnsignedWord==true) { if (arrayDt!!.isUnsignedWord) {
// using a UWORD pointer with array indexing, always bytes
require(!msb) require(!msb)
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position) assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
if(constIndex>0) if(constIndex in 1..255)
asmgen.out(""" asmgen.out("""
clc clc
adc #$constIndex adc #$constIndex
bcc + bcc +
iny iny
+""") +""")
else if(constIndex>=256) {
asmgen.out("""
clc
adc #<$constIndex
pha
tya
adc #>$constIndex
tay
pla""")
}
} }
else { else {
if(constIndex>0) { if(constIndex>0) {
val offset = if(arrayDt!!.isSplitWordArray) constIndex else program.memsizer.memorySize(arrayDt, constIndex) // add arrayIndexExpr * elementsize to the address of the array variable. val offset = if(arrayDt.isSplitWordArray) constIndex else program.memsizer.memorySize(arrayDt, constIndex) // add arrayIndexExpr * elementsize to the address of the array variable.
asmgen.out(" lda #<($sourceName + $offset) | ldy #>($sourceName + $offset)") asmgen.out(" lda #<($sourceName + $offset) | ldy #>($sourceName + $offset)")
} else { } else {
asmgen.out(" lda #<$sourceName | ldy #>$sourceName") asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
@ -2633,23 +2718,61 @@ $endLabel""")
assignRegisterpairWord(target, RegisterOrPair.AY) assignRegisterpairWord(target, RegisterOrPair.AY)
return return
} else { } else {
if (arrayDt?.isUnsignedWord==true) { if (arrayDt!!.isUnsignedWord) {
// using a UWORD pointer with array indexing, always bytes
require(!msb) require(!msb)
assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position) assignVariableToRegister(sourceName, RegisterOrPair.AY, false, arrayIndexExpr.definingISub(), arrayIndexExpr.position)
asmgen.saveRegisterStack(CpuRegister.A, false) asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false) asmgen.saveRegisterStack(CpuRegister.Y, false)
assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_REG", DataType.forDt(BaseDataType.UBYTE)) if(arrayIndexExpr.type.isWord) {
asmgen.restoreRegisterStack(CpuRegister.Y, false) assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.AY, false)
asmgen.restoreRegisterStack(CpuRegister.A, false) asmgen.out("""
asmgen.out(""" sta P8ZP_SCRATCH_W1
clc sty P8ZP_SCRATCH_W1+1
adc P8ZP_SCRATCH_REG pla
bcc + tay
iny pla
clc
adc P8ZP_SCRATCH_W1
pha
tya
adc P8ZP_SCRATCH_W1+1
tay
pla""")
}
else {
assignExpressionToVariable(arrayIndexExpr, "P8ZP_SCRATCH_REG", DataType.UBYTE)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out("""
clc
adc P8ZP_SCRATCH_REG
bcc +
iny
+""") +""")
}
} }
else { else {
assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.A, false) assignExpressionToRegister(arrayIndexExpr, RegisterOrPair.A, false)
val subtype = arrayDt.sub!!
if(subtype.isByteOrBool) {
// elt size 1, we're good
} else if(subtype.isWord) {
if(!arrayDt.isSplitWordArray) {
// elt size 2
asmgen.out(" asl a")
}
} else if(subtype==BaseDataType.FLOAT) {
if(asmgen.options.compTarget.FLOAT_MEM_SIZE != 5)
TODO("support float size other than 5 ${arrayIndexExpr.position}")
asmgen.out("""
sta P8ZP_SCRATCH_REG
asl a
asl a
clc
adc P8ZP_SCRATCH_REG"""
)
} else throw AssemblyError("weird type $subtype")
asmgen.out(""" asmgen.out("""
ldy #>$sourceName ldy #>$sourceName
clc clc
@ -2856,7 +2979,7 @@ $endLabel""")
jsr floats.MOVMF""") jsr floats.MOVMF""")
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
asmgen.out(""" asmgen.out("""
ldy #<${target.asmVarname} ldy #<${target.asmVarname}
sty P8ZP_SCRATCH_W1 sty P8ZP_SCRATCH_W1
@ -2888,7 +3011,7 @@ $endLabel""")
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
asmgen.out(" pha") asmgen.out(" pha")
asmgen.saveRegisterStack(CpuRegister.Y, false) asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
asmgen.restoreRegisterStack(CpuRegister.Y, false) asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.out(" pla") asmgen.out(" pla")
asmgen.out(""" asmgen.out("""
@ -2925,7 +3048,7 @@ $endLabel""")
jsr floats.copy_float""") jsr floats.copy_float""")
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
asmgen.out(""" asmgen.out("""
ldy #<$sourceName ldy #<$sourceName
sty P8ZP_SCRATCH_W1 sty P8ZP_SCRATCH_W1
@ -3222,7 +3345,7 @@ $endLabel""")
if(signed) if(signed)
asmgen.out(""" asmgen.out("""
ldy #0 ldy #0
cmp #${'$'}80 cmp #$80
bcc + bcc +
dey dey
+""") +""")
@ -3234,7 +3357,7 @@ $endLabel""")
if(signed) if(signed)
asmgen.out(""" asmgen.out("""
ldx #0 ldx #0
cmp #${'$'}80 cmp #$80
bcc + bcc +
dex dex
+""") +""")
@ -3247,7 +3370,7 @@ $endLabel""")
asmgen.out(""" asmgen.out("""
tax tax
ldy #0 ldy #0
cpx #${'$'}80 cpx #$80
bcc + bcc +
dey dey
+""") +""")
@ -3273,7 +3396,7 @@ $endLabel""")
asmgen.out(""" asmgen.out("""
txa txa
ldy #0 ldy #0
cmp #${'$'}80 cmp #$80
bcc + bcc +
dey dey
+""") +""")
@ -3286,7 +3409,7 @@ $endLabel""")
asmgen.out(""" asmgen.out("""
txa txa
ldx #0 ldx #0
cmp #${'$'}80 cmp #$80
bcc + bcc +
dex dex
+""") +""")
@ -3298,7 +3421,7 @@ $endLabel""")
if(signed) if(signed)
asmgen.out(""" asmgen.out("""
ldy #0 ldy #0
cpx #${'$'}80 cpx #$80
bcc + bcc +
dey dey
+""") +""")
@ -3324,7 +3447,7 @@ $endLabel""")
asmgen.out(""" asmgen.out("""
tya tya
ldy #0 ldy #0
cmp #${'$'}80 cmp #$80
bcc + bcc +
dey dey
+""") +""")
@ -3337,7 +3460,7 @@ $endLabel""")
asmgen.out(""" asmgen.out("""
tya tya
ldx #0 ldx #0
cmp #${'$'}80 cmp #$80
bcc + bcc +
dex dex
+""") +""")
@ -3351,7 +3474,7 @@ $endLabel""")
tya tya
tax tax
ldy #0 ldy #0
cpx #${'$'}80 cpx #$80
bcc + bcc +
dey dey
+""") +""")
@ -3398,7 +3521,7 @@ $endLabel""")
} else { } else {
require(target.array.index.type.isByteOrBool) require(target.array.index.type.isByteOrBool)
asmgen.saveRegisterStack(register, false) asmgen.saveRegisterStack(register, false)
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false) asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y)
asmgen.out(" pla | sta ${target.asmVarname},y") asmgen.out(" pla | sta ${target.asmVarname},y")
} }
} }
@ -3713,7 +3836,7 @@ $endLabel""")
asmgen.out(" stz ${target.asmVarname}+$indexValue") asmgen.out(" stz ${target.asmVarname}+$indexValue")
} }
else { else {
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.X, false) asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.X)
asmgen.out(" stz ${target.asmVarname},x") asmgen.out(" stz ${target.asmVarname},x")
} }
} }
@ -3803,7 +3926,7 @@ $endLabel""")
sta ${target.asmVarname}+4""") sta ${target.asmVarname}+4""")
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
asmgen.out(""" asmgen.out("""
ldy #<${target.asmVarname} ldy #<${target.asmVarname}
sty P8ZP_SCRATCH_W1 sty P8ZP_SCRATCH_W1
@ -3837,7 +3960,7 @@ $endLabel""")
jsr floats.copy_float""") jsr floats.copy_float""")
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
asmgen.out(""" asmgen.out("""
ldy #<${constFloat} ldy #<${constFloat}
sty P8ZP_SCRATCH_W1 sty P8ZP_SCRATCH_W1
@ -4020,8 +4143,10 @@ $endLabel""")
addressOf != null -> { addressOf != null -> {
if(addressOf.isFromArrayElement) { if(addressOf.isFromArrayElement) {
TODO("address-of array element $addressOf") TODO("address-of array element $addressOf")
} else if(addressOf.dereference!=null) {
throw AssemblyError("write &dereference, makes no sense at ${addressOf.position}")
} else { } else {
asmgen.out(" sta ${asmgen.asmSymbolName(addressOf.identifier)}") asmgen.out(" sta ${asmgen.asmSymbolName(addressOf.identifier!!)}")
} }
} }
addressExpr is PtIdentifier -> { addressExpr is PtIdentifier -> {
@ -4089,7 +4214,7 @@ $endLabel""")
asmgen.storeAIntoPointerVar(memory.address as PtIdentifier) asmgen.storeAIntoPointerVar(memory.address as PtIdentifier)
} }
else -> { else -> {
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD)) asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
if(asmgen.isTargetCpu(CpuType.CPU65C02)) { if(asmgen.isTargetCpu(CpuType.CPU65C02)) {
asmgen.out(""" asmgen.out("""
lda (P8ZP_SCRATCH_W2) lda (P8ZP_SCRATCH_W2)
@ -4151,8 +4276,8 @@ $endLabel""")
val target = assign.target val target = assign.target
val datatype = if(ignoreDatatype) { val datatype = if(ignoreDatatype) {
when { when {
target.datatype.isByte -> DataType.forDt(BaseDataType.BYTE) target.datatype.isByte -> DataType.BYTE
target.datatype.isWord -> DataType.forDt(BaseDataType.WORD) target.datatype.isWord -> DataType.WORD
else -> target.datatype else -> target.datatype
} }
} else target.datatype } else target.datatype

View File

@ -131,18 +131,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
is PtNumber -> { is PtNumber -> {
val addr = (memory.address as PtNumber).number.toInt() val addr = (memory.address as PtNumber).number.toInt()
when(value.kind) { when(value.kind) {
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.boolean!!.asInt()) SourceStorageKind.LITERALBOOLEAN -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.UBYTE, operator, value.boolean!!.asInt())
SourceStorageKind.LITERALNUMBER -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.number!!.number.toInt()) SourceStorageKind.LITERALNUMBER -> inplacemodificationByteWithLiteralval(addr.toHex(), DataType.UBYTE, operator, value.number!!.number.toInt())
SourceStorageKind.VARIABLE -> inplacemodificationByteWithVariable(addr.toHex(), false, operator, value.asmVarname) SourceStorageKind.VARIABLE -> inplacemodificationByteWithVariable(addr.toHex(), false, operator, value.asmVarname)
SourceStorageKind.REGISTER -> inplacemodificationByteWithVariable(addr.toHex(), false, operator, regName(value)) SourceStorageKind.REGISTER -> inplacemodificationByteWithVariable(addr.toHex(), false, operator, regName(value))
SourceStorageKind.MEMORY -> inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.memory!!) SourceStorageKind.MEMORY -> inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.memory!!)
SourceStorageKind.ARRAY -> inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.array!!) SourceStorageKind.ARRAY -> inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.array!!)
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
inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.expression) inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.expression)
} else { } else {
inplacemodificationByteWithValue(addr.toHex(), DataType.forDt(BaseDataType.UBYTE), operator, value.expression!!) inplacemodificationByteWithValue(addr.toHex(), DataType.UBYTE, operator, value.expression!!)
} }
} }
} }
@ -170,7 +170,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
if(memory.address is PtBinaryExpression && tryOptimizedMemoryInplace(memory.address as PtBinaryExpression, operator, value)) if(memory.address is PtBinaryExpression && tryOptimizedMemoryInplace(memory.address as PtBinaryExpression, operator, value))
return return
// slower method to calculate and use the pointer to access the memory with: // slower method to calculate and use the pointer to access the memory with:
asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY, false) asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY)
asmgen.saveRegisterStack(CpuRegister.A, true) asmgen.saveRegisterStack(CpuRegister.A, true)
asmgen.saveRegisterStack(CpuRegister.Y, true) asmgen.saveRegisterStack(CpuRegister.Y, true)
if(asmgen.isTargetCpu(CpuType.CPU65C02)) if(asmgen.isTargetCpu(CpuType.CPU65C02))
@ -196,21 +196,21 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
SourceStorageKind.MEMORY -> { SourceStorageKind.MEMORY -> {
asmgen.out(" sta P8ZP_SCRATCH_B1") asmgen.out(" sta P8ZP_SCRATCH_B1")
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), operator, value.memory!!) inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.memory!!)
asmgen.out(" ldx P8ZP_SCRATCH_B1") asmgen.out(" ldx P8ZP_SCRATCH_B1")
} }
SourceStorageKind.ARRAY -> { SourceStorageKind.ARRAY -> {
asmgen.out(" sta P8ZP_SCRATCH_B1") asmgen.out(" sta P8ZP_SCRATCH_B1")
inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), operator, value.array!!) inplacemodificationByteWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.array!!)
asmgen.out(" ldx P8ZP_SCRATCH_B1") asmgen.out(" ldx P8ZP_SCRATCH_B1")
} }
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE) val tempVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, memory)
asmgen.out(" sta $tempVar") asmgen.out(" sta $tempVar")
if(value.expression is PtTypeCast) if(value.expression is PtTypeCast)
inplacemodificationByteWithValue(tempVar, DataType.forDt(BaseDataType.UBYTE), operator, value.expression) inplacemodificationByteWithValue(tempVar, DataType.UBYTE, operator, value.expression)
else else
inplacemodificationByteWithValue(tempVar, DataType.forDt(BaseDataType.UBYTE), operator, value.expression!!) inplacemodificationByteWithValue(tempVar, DataType.UBYTE, operator, value.expression!!)
asmgen.out(" ldx $tempVar") asmgen.out(" ldx $tempVar")
} }
} }
@ -224,7 +224,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
} }
TargetStorageKind.ARRAY -> { TargetStorageKind.ARRAY -> {
val indexNum = target.array!!.index as? PtNumber val targetArrayVar = target.array!!.variable
if(targetArrayVar==null) {
TODO("array indexing on pointer ${target.position}")
return
}
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) {
@ -320,7 +325,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)
@ -356,7 +361,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
val tempVar = asmgen.getTempVarName(BaseDataType.UBYTE) val tempVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, target.array)
asmgen.out(" sta $tempVar") asmgen.out(" sta $tempVar")
if(value.expression is PtTypeCast) if(value.expression is PtTypeCast)
inplacemodificationByteWithValue(tempVar, target.datatype, operator, value.expression) inplacemodificationByteWithValue(tempVar, target.datatype, operator, value.expression)
@ -366,7 +371,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 +382,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) {
@ -439,7 +444,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
SourceStorageKind.EXPRESSION -> { SourceStorageKind.EXPRESSION -> {
val tempVar = asmgen.getTempVarName(BaseDataType.UWORD) val tempVar = asmgen.createTempVarReused(BaseDataType.UWORD, false, target.array)
asmgen.out(" sta $tempVar | stx $tempVar+1") asmgen.out(" sta $tempVar | stx $tempVar+1")
if(value.expression is PtTypeCast) if(value.expression is PtTypeCast)
inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression, block) inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression, block)
@ -450,14 +455,14 @@ 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 -> {
// copy array value into tempvar // copy array value into tempvar
val tempvar = asmgen.getTempVarName(BaseDataType.FLOAT) val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, target.array)
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.A) asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.A)
asmgen.out(""" asmgen.out("""
ldy #>${target.asmVarname} ldy #>${target.asmVarname}
@ -515,7 +520,12 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
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)
@ -583,7 +593,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
if(address.left is PtIdentifier && asmgen.isZpVar(address.left as PtIdentifier)) { if(address.left is PtIdentifier && asmgen.isZpVar(address.left as PtIdentifier)) {
return (address.left as PtIdentifier).name return (address.left as PtIdentifier).name
} else { } else {
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false) asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY)
asmgen.out(" sta P8ZP_SCRATCH_W2 | sty P8ZP_SCRATCH_W2+1") asmgen.out(" sta P8ZP_SCRATCH_W2 | sty P8ZP_SCRATCH_W2+1")
return "P8ZP_SCRATCH_W2" return "P8ZP_SCRATCH_W2"
} }
@ -592,7 +602,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
fun assignValueToA() { fun assignValueToA() {
val assignValue = AsmAssignment(value, val assignValue = AsmAssignment(value,
listOf( listOf(
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.forDt(BaseDataType.UBYTE), AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE,
address.definingISub(), Position.DUMMY, register = RegisterOrPair.A) address.definingISub(), Position.DUMMY, register = RegisterOrPair.A)
), ),
program.memsizer, Position.DUMMY) program.memsizer, Position.DUMMY)
@ -635,9 +645,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
return true return true
} }
if(rightTc!=null) if(rightTc!=null)
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A)
else else
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A)
asmgen.out(" pha") // offset on stack asmgen.out(" pha") // offset on stack
val zpPointerVarName = addrIntoZpPointer() val zpPointerVarName = addrIntoZpPointer()
assignValueToA() assignValueToA()
@ -898,7 +908,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
private fun inplacemodificationBytePointerWithValue(pointervar: PtIdentifier, operator: String, value: PtExpression) { private fun inplacemodificationBytePointerWithValue(pointervar: PtIdentifier, operator: String, value: PtExpression) {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE)) asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE)
inplacemodificationBytePointerWithVariable(pointervar, operator, "P8ZP_SCRATCH_B1") inplacemodificationBytePointerWithVariable(pointervar, operator, "P8ZP_SCRATCH_B1")
} }
@ -910,7 +920,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"-" -> asmgen.out(" sec | sbc $otherName") "-" -> asmgen.out(" sec | sbc $otherName")
"*" -> asmgen.out(" ldy $otherName | jsr prog8_math.multiply_bytes") "*" -> asmgen.out(" ldy $otherName | jsr prog8_math.multiply_bytes")
"/" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm | tya") "/" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm | tya")
"%" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm") "%" -> asmgen.out(" ldy $otherName | jsr prog8_math.remainder_ub_asm")
"<<" -> { "<<" -> {
asmgen.out(""" asmgen.out("""
ldy $otherName ldy $otherName
@ -962,9 +972,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
when (operator) { when (operator) {
"+" -> { "+" -> {
if(value==1) { if(value==1) {
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY) if(asmgen.options.romable) {
asmgen.out(" sta (+) + 1 | sty (+) + 2") val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out("+\tinc ${'$'}ffff\t; modified") if(asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out(" inc a")
else
asmgen.out(" clc | adc #1")
asmgen.storeAIntoZpPointerVar(sourceName, false)
} else {
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.out("+\tinc ${'$'}ffff\t; modified")
}
} else { } else {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar) val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" clc | adc #$value") asmgen.out(" clc | adc #$value")
@ -973,9 +992,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
} }
"-" -> { "-" -> {
if(value==1) { if(value==1) {
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY) if(asmgen.options.romable) {
asmgen.out(" sta (+) + 1 | sty (+) + 2") val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out("+\tdec ${'$'}ffff\t; modified") if(asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out(" dec a")
else
asmgen.out(" sec | sbc #1")
asmgen.storeAIntoZpPointerVar(sourceName, false)
} else {
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
asmgen.out(" sta (+) + 1 | sty (+) + 2")
asmgen.out("+\tdec ${'$'}ffff\t; modified")
}
} else { } else {
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar) val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" sec | sbc #$value") asmgen.out(" sec | sbc #$value")
@ -1001,7 +1029,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar) val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
if(value==0) if(value==0)
throw AssemblyError("division by zero") throw AssemblyError("division by zero")
asmgen.out(" ldy #$value | jsr prog8_math.divmod_ub_asm") asmgen.out(" ldy #$value | jsr prog8_math.remainder_ub_asm")
asmgen.storeAIntoZpPointerVar(sourceName, false) asmgen.storeAIntoZpPointerVar(sourceName, false)
} }
"<<" -> { "<<" -> {
@ -1107,11 +1135,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
@ -1172,7 +1207,7 @@ $shortcutLabel:""")
"%" -> { "%" -> {
if(signed) if(signed)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out(" ldy $variable | jsr prog8_math.divmod_ub_asm") asmgen.out(" ldy $variable | jsr prog8_math.remainder_ub_asm")
} }
"<<" -> { "<<" -> {
asmgen.out(""" asmgen.out("""
@ -1345,7 +1380,7 @@ $shortcutLabel:""")
"%" -> { "%" -> {
if(signed) if(signed)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead") throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out(" tay | lda $variable | jsr prog8_math.divmod_ub_asm") asmgen.out(" tay | lda $variable | jsr prog8_math.remainder_ub_asm")
} }
"<<" -> { "<<" -> {
asmgen.out(""" asmgen.out("""
@ -1516,7 +1551,7 @@ $shortcutLabel:""")
asmgen.out(""" asmgen.out("""
lda $name lda $name
ldy #$value ldy #$value
jsr prog8_math.divmod_ub_asm jsr prog8_math.remainder_ub_asm
sta $name""") sta $name""")
} }
"<<" -> { "<<" -> {
@ -1903,18 +1938,16 @@ $shortcutLabel:""")
asmgen.out(" lda #0 | sta $lsb") asmgen.out(" lda #0 | sta $lsb")
} }
value==7 -> { value==7 -> {
// optimized shift left 7 (*128) by first swapping the lsb/msb and then doing just one final shift // optimized shift left 7 (*128) by swapping the lsb/msb and then doing just one final shift
asmgen.out(""" asmgen.out("""
; shift left 7 ; shift left 7
lsr $msb lsr $msb
php ; save carry
lda $lsb lda $lsb
ror a
sta $msb sta $msb
lda #0 lda #0
sta $lsb ror a
plp ; restore carry sta $lsb""")
ror $msb
ror $lsb""")
} }
value>3 -> asmgen.out(""" value>3 -> asmgen.out("""
ldy #$value ldy #$value
@ -2450,7 +2483,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")
@ -2722,7 +2755,7 @@ $shortcutLabel:""")
"+" -> { "+" -> {
// name += byteexpression // name += byteexpression
if(valueDt.isUnsignedByte) { if(valueDt.isUnsignedByte) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, false) asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(""" asmgen.out("""
clc clc
adc $name adc $name
@ -2872,7 +2905,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()) {
@ -2887,7 +2920,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("""

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
@ -68,6 +70,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
override fun noErrors(): Boolean = errors.isEmpty() override fun noErrors(): Boolean = errors.isEmpty()
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it } override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
override fun report() { override fun report() {
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") } infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }

View File

@ -5,12 +5,15 @@ import io.kotest.assertions.withClue
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import prog8.code.StMemVar
import prog8.code.SymbolTable
import prog8.code.SymbolTableMaker import prog8.code.SymbolTableMaker
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.code.source.SourceCode import prog8.code.source.SourceCode
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.codegen.cpu6502.AsmGen6502 import prog8.codegen.cpu6502.AsmGen6502
import prog8.codegen.cpu6502.VariableAllocator
import java.nio.file.Files import java.nio.file.Files
import kotlin.io.path.Path import kotlin.io.path.Path
@ -26,6 +29,7 @@ class TestCodegen: FunSpec({
zpAllowed = CompilationOptions.AllZeropageAllowed, zpAllowed = CompilationOptions.AllZeropageAllowed,
floats = true, floats = true,
noSysInit = false, noSysInit = false,
romable = false,
compTarget = target, compTarget = target,
loadAddress = target.PROGRAM_LOAD_ADDRESS, loadAddress = target.PROGRAM_LOAD_ADDRESS,
memtopAddress = 0xffffu memtopAddress = 0xffffu
@ -51,9 +55,10 @@ class TestCodegen: FunSpec({
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY) val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
sub.add(PtVariable( sub.add(PtVariable(
"pi", "pi",
DataType.forDt(BaseDataType.UBYTE), DataType.UBYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@ -63,6 +68,7 @@ class TestCodegen: FunSpec({
DataType.arrayFor(BaseDataType.UBYTE), DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
3u, 3u,
Position.DUMMY Position.DUMMY
@ -72,15 +78,17 @@ class TestCodegen: FunSpec({
DataType.arrayFor(BaseDataType.UBYTE), DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
3u, 3u,
Position.DUMMY Position.DUMMY
)) ))
sub.add(PtVariable( sub.add(PtVariable(
"xx", "xx",
DataType.forDt(BaseDataType.WORD), DataType.WORD,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@ -88,13 +96,13 @@ class TestCodegen: FunSpec({
val assign = PtAugmentedAssign("+=", Position.DUMMY) val assign = PtAugmentedAssign("+=", Position.DUMMY)
val target = PtAssignTarget(false, Position.DUMMY).also { val target = PtAssignTarget(false, Position.DUMMY).also {
val targetIdx = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY).also { idx -> val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY)) idx.add(PtIdentifier("main.start.particleX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
idx.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY)) idx.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
} }
it.add(targetIdx) it.add(targetIdx)
} }
val value = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY) val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY)) value.add(PtIdentifier("main.start.particleDX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY)) value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
assign.add(target) assign.add(target)
@ -103,15 +111,15 @@ class TestCodegen: FunSpec({
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY) val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also { val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
prefixAssign.add(prefixTarget) prefixAssign.add(prefixTarget)
prefixAssign.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY)) prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
sub.add(prefixAssign) sub.add(prefixAssign)
val numberAssign = PtAugmentedAssign("-=", Position.DUMMY) val numberAssign = PtAugmentedAssign("-=", Position.DUMMY)
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also { val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
numberAssign.add(numberAssignTarget) numberAssign.add(numberAssignTarget)
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY)) numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
@ -119,10 +127,10 @@ class TestCodegen: FunSpec({
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY) val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also { val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
cxregAssign.add(cxregAssignTarget) cxregAssign.add(cxregAssignTarget)
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY)) cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
sub.add(cxregAssign) sub.add(cxregAssign)
block.add(sub) block.add(sub)
@ -130,7 +138,7 @@ class TestCodegen: FunSpec({
// define the "cx16.r0" virtual register // define the "cx16.r0" virtual register
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY) val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
cx16block.add(PtMemMapped("r0", DataType.forDt(BaseDataType.UWORD), 100u, null, Position.DUMMY)) cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
program.add(cx16block) program.add(cx16block)
val options = getTestOptions() val options = getTestOptions()
@ -159,5 +167,15 @@ class TestCodegen: FunSpec({
} }
} }
} }
test("memory mapped zp var is correctly considered to be zp var") {
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val st = SymbolTable(program)
st.add(StMemVar("zpmemvar", DataType.WORD, 0x20u, null, null))
st.add(StMemVar("normalmemvar", DataType.WORD, 0x9000u, null, null))
val allocator = VariableAllocator(st, getTestOptions(), ErrorReporterForTests())
allocator.isZpVar("zpmemvar") shouldBe true
allocator.isZpVar("normalmemvar") shouldBe false
}
}) })

View File

@ -6,6 +6,7 @@ plugins {
dependencies { dependencies {
implementation(project(":codeCore")) implementation(project(":codeCore"))
implementation(project(":simpleAst"))
implementation(project(":intermediate")) implementation(project(":intermediate"))
implementation(project(":codeGenIntermediate")) implementation(project(":codeGenIntermediate"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") // implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

View File

@ -13,5 +13,6 @@
<orderEntry type="module" module-name="codeGenIntermediate" /> <orderEntry type="module" module-name="codeGenIntermediate" />
<orderEntry type="module" module-name="intermediate" /> <orderEntry type="module" module-name="intermediate" />
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="simpleAst" />
</component> </component>
</module> </module>

View File

@ -1,10 +1,10 @@
package prog8.codegen.experimental package prog8.codegen.experimental
import prog8.code.IAssemblyProgram
import prog8.code.ICodeGeneratorBackend
import prog8.code.SymbolTable import prog8.code.SymbolTable
import prog8.code.ast.PtProgram import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.code.core.ICodeGeneratorBackend
import prog8.code.core.IErrorReporter import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter import prog8.intermediate.IRFileWriter
@ -26,7 +26,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
// this stub only writes the IR program to disk but doesn't generate anything else. // this stub only writes the IR program to disk but doesn't generate anything else.
IRFileWriter(irProgram, null).write() IRFileWriter(irProgram, null).write()
println("** experimental codegen stub: no assembly generated **") if(!options.quiet)
println("** experimental codegen stub: no assembly generated **")
return EmptyProgram return EmptyProgram
} }
} }
@ -34,7 +35,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
private object EmptyProgram : IAssemblyProgram { private object EmptyProgram : IAssemblyProgram {
override val name = "<Empty Program>" override val name = "<Empty Program>"
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean { override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
println("** nothing assembled **") if(!options.quiet)
println("** nothing assembled **")
return true return true
} }

View File

@ -7,6 +7,7 @@ plugins {
dependencies { dependencies {
implementation(project(":codeCore")) implementation(project(":codeCore"))
implementation(project(":simpleAst"))
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"

View File

@ -11,6 +11,7 @@
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<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.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" />

View File

@ -1,8 +1,10 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
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
import prog8.code.core.DataType
import prog8.intermediate.* import prog8.intermediate.*
@ -45,6 +47,8 @@ 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)
"structalloc" -> funcStructAlloc(call)
"sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
else -> throw AssemblyError("missing builtinfunc for ${call.name}") else -> throw AssemblyError("missing builtinfunc for ${call.name}")
} }
} }
@ -78,7 +82,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val bankTr = exprGen.translateExpression(call.args[0]) val bankTr = exprGen.translateExpression(call.args[0])
val addressTr = exprGen.translateExpression(call.args[1]) val addressTr = exprGen.translateExpression(call.args[1])
val argumentwordTr = exprGen.translateExpression(call.args[2]) val argumentwordTr = exprGen.translateExpression(call.args[2])
@ -91,7 +94,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val bankTr = exprGen.translateExpression(call.args[0]) val bankTr = exprGen.translateExpression(call.args[0])
val addressTr = exprGen.translateExpression(call.args[1]) val addressTr = exprGen.translateExpression(call.args[1])
val argumentA = exprGen.translateExpression(call.args[2]) val argumentA = exprGen.translateExpression(call.args[2])
@ -143,7 +145,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val left = exprGen.translateExpression(call.args[0]) val left = exprGen.translateExpression(call.args[0])
val right = exprGen.translateExpression(call.args[1]) val right = exprGen.translateExpression(call.args[1])
addToResult(result, left, left.resultReg, -1) addToResult(result, left, left.resultReg, -1)
@ -240,11 +241,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} }
BaseDataType.UWORD -> { BaseDataType.UWORD -> {
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.next(IRDataType.WORD) val resultReg = codeGen.registers.next(IRDataType.BYTE) // sqrt of a word still produces just a byte result
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg) it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
} }
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
} }
BaseDataType.FLOAT -> { BaseDataType.FLOAT -> {
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
@ -278,7 +279,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val type = irType(call.type) val type = irType(call.type)
val valueTr = exprGen.translateExpression(call.args[0]) val valueTr = exprGen.translateExpression(call.args[0])
val minimumTr = exprGen.translateExpression(call.args[1]) val minimumTr = exprGen.translateExpression(call.args[1])
@ -497,8 +497,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 = "$StMemorySlabPrefix.prog8_memoryslab_$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 = labelname)
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
} }
private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@ -548,7 +556,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) {
@ -617,7 +627,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)
@ -651,6 +663,10 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} }
} }
else { else {
val targetVariable = target.variable
if(targetVariable==null)
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) {
@ -659,17 +675,17 @@ 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)
addToResult(result, indexTr, indexTr.resultReg, -1) addToResult(result, indexTr, indexTr.resultReg, -1)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
if(eltSize>1) if(eltSize>1)
it += codeGen.multiplyByConst(IRDataType.BYTE, 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 {
@ -680,17 +696,17 @@ 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)
addToResult(result, indexTr, indexTr.resultReg, -1) addToResult(result, indexTr, indexTr.resultReg, -1)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
if(eltSize>1) if(eltSize>1)
it += codeGen.multiplyByConst(IRDataType.BYTE, 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

@ -1,8 +1,6 @@
package prog8.codegen.intermediate package prog8.codegen.intermediate
import prog8.code.StNode import prog8.code.*
import prog8.code.StExtSub
import prog8.code.StSub
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* import prog8.code.core.*
import prog8.intermediate.* import prog8.intermediate.*
@ -29,7 +27,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
fun translateExpression(expr: PtExpression): ExpressionCodeResult { fun translateExpression(expr: PtExpression): ExpressionCodeResult {
return when (expr) { return when (expr) {
is PtIrRegister -> { is PtIrRegister -> {
ExpressionCodeResult(emptyList(), irType(expr.type), expr.register, -1) if(expr.type.isFloat)
ExpressionCodeResult(emptyList(), IRDataType.FLOAT, -1, expr.register)
else
ExpressionCodeResult(emptyList(), irType(expr.type), expr.register, -1)
} }
is PtBool -> { is PtBool -> {
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
@ -85,12 +86,107 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
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")
@ -159,46 +255,63 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
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) {
val indexTr = translateExpression(expr.arrayIndexExpr!!) val indexTr = translateExpression(expr.arrayIndexExpr!!)
addToResult(result, indexTr, indexTr.resultReg, -1) addToResult(result, indexTr, indexTr.resultReg, -1)
val indexWordReg = codeGen.registers.next(IRDataType.WORD) val indexWordReg = if(indexTr.dt==IRDataType.BYTE) {
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexWordReg, reg2=indexTr.resultReg), null) val ixWord = codeGen.registers.next(IRDataType.WORD)
if(expr.identifier.type.isUnsignedWord) { addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=ixWord, reg2=indexTr.resultReg), null)
ixWord
} else indexTr.resultReg
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 {
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol) val ptr = codeGen.symbolTable.lookup(identifier.name)
it += if(ptr is StConstant)
IRInstruction(Opcode.LOAD, vmDt, reg1 = resultRegister, immediate = ptr.value.toInt())
else
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 { } else {
val eltSize = codeGen.program.memsizer.memorySize(expr.identifier.type, 1) 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 += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1 = indexWordReg, immediate = 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 {
@ -212,31 +325,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)
@ -282,7 +396,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val haystackVar = check.haystackHeapVar!! val haystackVar = check.haystackHeapVar!!
when { when {
haystackVar.type.isString -> { haystackVar.type.isString -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val elementTr = translateExpression(check.needle) val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, elementTr.resultReg, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(haystackVar) val iterableTr = translateExpression(haystackVar)
@ -293,7 +406,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
} }
haystackVar.type.isByteArray -> { haystackVar.type.isByteArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.needle) val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, elementTr.resultReg, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(haystackVar) val iterableTr = translateExpression(haystackVar)
@ -307,7 +419,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
} }
haystackVar.type.isWordArray -> { haystackVar.type.isWordArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.needle) val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, elementTr.resultReg, -1) addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(haystackVar) val iterableTr = translateExpression(haystackVar)
@ -321,7 +432,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
} }
haystackVar.type.isFloatArray -> { haystackVar.type.isFloatArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.needle) val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, -1, elementTr.resultFpReg) addToResult(result, elementTr, -1, elementTr.resultFpReg)
val iterableTr = translateExpression(haystackVar) val iterableTr = translateExpression(haystackVar)
@ -339,11 +449,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)
@ -370,9 +499,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)
@ -381,23 +509,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(IRDataType.BYTE, 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)
@ -438,9 +604,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)
@ -503,6 +669,9 @@ 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)
} }
BaseDataType.POINTER -> {
actualResultReg2 = tr.resultReg
}
else -> throw AssemblyError("weird cast value type") else -> throw AssemblyError("weird cast value type")
} }
} }
@ -546,6 +715,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else -> throw AssemblyError("weird cast value type") else -> throw AssemblyError("weird cast value 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 type") else -> throw AssemblyError("weird cast type")
} }
@ -558,8 +735,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return when(binExpr.operator) { return when(binExpr.operator) {
"+" -> operatorPlus(binExpr, vmDt) "+" -> operatorPlus(binExpr, vmDt)
"-" -> operatorMinus(binExpr, vmDt) "-" -> operatorMinus(binExpr, vmDt)
"*" -> operatorMultiply(binExpr, vmDt) "*" -> operatorMultiply(binExpr, binExpr.left.type)
"/" -> operatorDivide(binExpr, vmDt, signed) "/" -> operatorDivide(binExpr, binExpr.left.type)
"%" -> operatorModulo(binExpr, vmDt) "%" -> operatorModulo(binExpr, vmDt)
"|" -> operatorOr(binExpr, vmDt, true) "|" -> operatorOr(binExpr, vmDt, true)
"&" -> operatorAnd(binExpr, vmDt, true) "&" -> operatorAnd(binExpr, vmDt, true)
@ -574,18 +751,19 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
">" -> operatorGreaterThan(binExpr, vmDt, signed, false) ">" -> operatorGreaterThan(binExpr, vmDt, signed, false)
"<=" -> operatorLessThan(binExpr, vmDt, signed, true) "<=" -> operatorLessThan(binExpr, vmDt, signed, true)
">=" -> operatorGreaterThan(binExpr, vmDt, signed, true) ">=" -> operatorGreaterThan(binExpr, vmDt, signed, true)
"." -> operatorDereference(binExpr, vmDt) // eww, nasty, would rather not have any such expressions anymore
else -> throw AssemblyError("weird operator ${binExpr.operator}") else -> throw AssemblyError("weird operator ${binExpr.operator}")
} }
} }
internal fun translate(fcall: PtFunctionCall): ExpressionCodeResult { internal fun translate(fcall: PtFunctionCall): ExpressionCodeResult {
val callTarget = codeGen.symbolTable.flat.getValue(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)
@ -611,7 +789,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when (callTarget) { when (callTarget) {
is StSub -> { is StSub -> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
// assign the arguments // assign the arguments
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>() val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) { for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
@ -635,7 +812,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
} }
// return value(s) // return value(s)
val returnRegSpecs = if(fcall.void) emptyList() else { // 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!)
// 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 {
callTarget.returns.map { callTarget.returns.map {
val returnIrType = irType(it) val returnIrType = irType(it)
FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.next(returnIrType), null) FunctionCallArgs.RegSpec(returnIrType, codeGen.registers.next(returnIrType), null)
@ -653,14 +833,13 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else else
ExpressionCodeResult(result, returnRegSpec.dt, returnRegSpec.registerNum, -1) ExpressionCodeResult(result, returnRegSpec.dt, returnRegSpec.registerNum, -1)
} else { } else {
// multi-value returns are passed throug cx16.R15 down to R0 (allows unencumbered use of many Rx registers if you don't return that many values) // note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
// so the actual result of the expression here is 'void' (doesn't use IR virtual registers at all) // so the actual result of the expression here is 'void' (doesn't use IR virtual registers at all)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
} }
} }
is StExtSub -> { is StExtSub -> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
// assign the arguments // assign the arguments
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>() val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) { for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
@ -671,25 +850,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else else
argRegisters.add(FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, parameter.register))) argRegisters.add(FunctionCallArgs.ArgumentSpec("", null, FunctionCallArgs.RegSpec(paramDt, tr.resultReg, parameter.register)))
result += tr.chunks result += tr.chunks
when(parameter.register.registerOrPair) { result += codeGen.setCpuRegister(parameter.register, paramDt, tr.resultReg, tr.resultFpReg)
RegisterOrPair.A -> addInstr(result, IRInstruction(Opcode.STOREHA, IRDataType.BYTE, reg1=tr.resultReg), null)
RegisterOrPair.X -> addInstr(result, IRInstruction(Opcode.STOREHX, IRDataType.BYTE, reg1=tr.resultReg), null)
RegisterOrPair.Y -> addInstr(result, IRInstruction(Opcode.STOREHY, IRDataType.BYTE, reg1=tr.resultReg), null)
RegisterOrPair.AX -> addInstr(result, IRInstruction(Opcode.STOREHAX, IRDataType.WORD, reg1=tr.resultReg), null)
RegisterOrPair.AY -> addInstr(result, IRInstruction(Opcode.STOREHAY, IRDataType.WORD, reg1=tr.resultReg), null)
RegisterOrPair.XY -> addInstr(result, IRInstruction(Opcode.STOREHXY, IRDataType.WORD, reg1=tr.resultReg), null)
RegisterOrPair.FAC1 -> addInstr(result, IRInstruction(Opcode.STOREHFACZERO, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null)
RegisterOrPair.FAC2 -> addInstr(result, IRInstruction(Opcode.STOREHFACONE, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null)
in Cx16VirtualRegisters -> {
addInstr(result, IRInstruction(Opcode.STOREM, paramDt, reg1=tr.resultReg, labelSymbol = "cx16.${parameter.register.registerOrPair.toString().lowercase()}"), null)
}
null -> when(parameter.register.statusflag) {
// TODO: do the statusflag argument as last
Statusflag.Pc -> addInstr(result, IRInstruction(Opcode.LSR, paramDt, reg1=tr.resultReg), null)
else -> throw AssemblyError("weird statusflag as param")
}
else -> throw AssemblyError("unsupported register arg")
}
} }
if(callTarget.returns.size>1) if(callTarget.returns.size>1)
@ -717,9 +878,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
Opcode.CALL, Opcode.CALL,
address = address.address.toInt(), address = address.address.toInt(),
fcallArgs = FunctionCallArgs(argRegisters, returnRegs)) fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
} } else if(address.constbank!=null) {
else { IRInstruction(
TODO("callfar into another bank is not implemented for the selected compilation target") Opcode.CALLFAR,
address = address.address.toInt(),
immediate = address.constbank!!.toInt()
)
} else {
val tr = translateExpression(address.varbank!!)
require(tr.dt==IRDataType.BYTE)
result += tr.chunks
IRInstruction(
Opcode.CALLFARVB,
address = address.address.toInt(),
reg1 = tr.resultReg
)
} }
} }
addInstr(result, call, null) addInstr(result, call, null)
@ -770,7 +943,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
else else
ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1) ExpressionCodeResult(result, returnRegSpec!!.dt, finalReturnRegister, -1)
} }
else -> throw AssemblyError("invalid node type") is StStruct -> {
throw AssemblyError("stray struct constructor should have been removed (normally it can only occur as initialization expression for a pointer variable)")
}
else -> {
if(callTarget.type == StNodeType.LABEL) {
require(fcall.void)
val result = mutableListOf<IRCodeChunkBase>()
val args = FunctionCallArgs(emptyList(), emptyList())
addInstr(result, IRInstruction(Opcode.CALL, labelSymbol = fcall.name, fcallArgs = args), null)
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
}
else {
throw AssemblyError("invalid node type ${callTarget.type} at ${callTarget.astNode?.position}")
}
}
} }
} }
@ -801,7 +988,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())
@ -1036,7 +1223,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
private fun loadStatusAsBooleanResult(branchForTrue: Opcode, result: MutableList<IRCodeChunkBase>): Int { private fun loadStatusAsBooleanResult(branchForTrue: Opcode, result: MutableList<IRCodeChunkBase>): Int {
// TODO this used to be a single instruction like SCC, SCS, SZ etc but those were problematic // TODO this used to be a single instruction like SCC, SCS, SZ etc
val other = codeGen.createLabelName() val other = codeGen.createLabelName()
val after = codeGen.createLabelName() val after = codeGen.createLabelName()
val resultReg = codeGen.registers.next(IRDataType.BYTE) val resultReg = codeGen.registers.next(IRDataType.BYTE)
@ -1051,7 +1238,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
private fun compareRegisterAsBooleanResult(branchForTrue: Opcode, dt: IRDataType, reg1: Int, reg2: Int, result: MutableList<IRCodeChunkBase>): Int { private fun compareRegisterAsBooleanResult(branchForTrue: Opcode, dt: IRDataType, reg1: Int, reg2: Int, result: MutableList<IRCodeChunkBase>): Int {
// TODO this used to be a single instruction like SCC, SCS, SZ etc but those were problematic // TODO this used to be a single instruction like SCC, SCS, SZ etc
val other = codeGen.createLabelName() val other = codeGen.createLabelName()
val after = codeGen.createLabelName() val after = codeGen.createLabelName()
val resultReg = codeGen.registers.next(IRDataType.BYTE) val resultReg = codeGen.registers.next(IRDataType.BYTE)
@ -1211,7 +1398,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
} }
private fun operatorDivide(binExpr: PtBinaryExpression, vmDt: IRDataType, signed: Boolean): ExpressionCodeResult { private fun operatorDivide(binExpr: PtBinaryExpression, dt: DataType): ExpressionCodeResult {
val vmDt = irType(dt)
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val constFactorRight = binExpr.right as? PtNumber val constFactorRight = binExpr.right as? PtNumber
if(vmDt==IRDataType.FLOAT) { if(vmDt==IRDataType.FLOAT) {
@ -1226,11 +1414,11 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addToResult(result, leftTr, -1, leftTr.resultFpReg) addToResult(result, leftTr, -1, leftTr.resultFpReg)
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(signed) 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 {
@ -1238,35 +1426,36 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val factor = constFactorRight.number.toInt() val factor = constFactorRight.number.toInt()
result += codeGen.divideByConst(vmDt, tr.resultReg, factor, signed) result += codeGen.divideByConst(vmDt, tr.resultReg, factor, dt.isSigned)
ExpressionCodeResult(result, vmDt, tr.resultReg, -1) ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else { } else {
if(binExpr.right is PtNumber) { if(binExpr.right is PtNumber) {
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 (signed) 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)
addToResult(result, leftTr, leftTr.resultReg, -1) addToResult(result, leftTr, leftTr.resultReg, -1)
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 (signed) 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)
} }
} }
} }
} }
private fun operatorMultiply(binExpr: PtBinaryExpression, vmDt: IRDataType): ExpressionCodeResult { private fun operatorMultiply(binExpr: PtBinaryExpression, dt: DataType): ExpressionCodeResult {
val vmDt = irType(dt)
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val constFactorLeft = binExpr.left as? PtNumber val constFactorLeft = binExpr.left as? PtNumber
val constFactorRight = binExpr.right as? PtNumber val constFactorRight = binExpr.right as? PtNumber
@ -1288,7 +1477,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addToResult(result, leftTr, -1, leftTr.resultFpReg) addToResult(result, leftTr, -1, leftTr.resultFpReg)
val rightTr = translateExpression(binExpr.right) val rightTr = translateExpression(binExpr.right)
addToResult(result, rightTr, -1, rightTr.resultFpReg) addToResult(result, rightTr, -1, rightTr.resultFpReg)
addInstr(result, IRInstruction(Opcode.MULR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null) addInstr(result, IRInstruction(Opcode.MULSR, vmDt, fpReg1 = leftTr.resultFpReg, fpReg2 = rightTr.resultFpReg), null)
ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg) ExpressionCodeResult(result, vmDt, -1, leftTr.resultFpReg)
} }
} else { } else {
@ -1296,20 +1485,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val tr = translateExpression(binExpr.right) val tr = translateExpression(binExpr.right)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val factor = constFactorLeft.number.toInt() val factor = constFactorLeft.number.toInt()
result += codeGen.multiplyByConst(vmDt, tr.resultReg, factor) result += codeGen.multiplyByConst(dt, tr.resultReg, factor)
ExpressionCodeResult(result, vmDt, tr.resultReg, -1) ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else if(constFactorRight!=null && !constFactorRight.type.isFloat) { } else if(constFactorRight!=null && !constFactorRight.type.isFloat) {
val tr = translateExpression(binExpr.left) val tr = translateExpression(binExpr.left)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val factor = constFactorRight.number.toInt() val factor = constFactorRight.number.toInt()
result += codeGen.multiplyByConst(vmDt, tr.resultReg, factor) result += codeGen.multiplyByConst(dt, tr.resultReg, factor)
ExpressionCodeResult(result, vmDt, tr.resultReg, -1) ExpressionCodeResult(result, vmDt, tr.resultReg, -1)
} else { } else {
val leftTr = translateExpression(binExpr.left) val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, leftTr.resultReg, -1) addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = translateExpression(binExpr.right) val rightTr = translateExpression(binExpr.right)
addToResult(result, rightTr, rightTr.resultReg, -1) addToResult(result, rightTr, rightTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.MULR, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null) val opcode = if(dt.isSigned) Opcode.MULSR else Opcode.MULR
addInstr(result, IRInstruction(opcode, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1) ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)
} }
} }
@ -1425,6 +1615,83 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
} }
} }
private fun operatorDereference(binExpr: PtBinaryExpression, vmDt: IRDataType): 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 indexedTr = translateExpression(left)
result += indexedTr.chunks
val pointerReg = indexedTr.resultReg
val struct = left.type.dereference().subType as? StStruct
require(indexedTr.dt== IRDataType.WORD && struct!=null)
val field = struct.getField(right.name, this.codeGen.program.memsizer)
var resultFpReg = -1
var resultReg = -1
if(vmDt==IRDataType.FLOAT)
resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
else
resultReg = codeGen.registers.next(vmDt)
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = field.second.toInt())
if(vmDt==IRDataType.FLOAT)
it += IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultFpReg, reg1 = pointerReg)
else
it += IRInstruction(Opcode.LOADI, vmDt, reg1 = resultReg, reg2 = pointerReg)
}
return ExpressionCodeResult(result, vmDt, resultReg, resultFpReg)
}
internal fun traverseRestOfDerefChainToCalculateFinalAddress(targetPointerDeref: PtPointerDeref, pointerReg: Int): Pair<IRCodeChunks, UInt> {
// 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

@ -25,7 +25,7 @@ class IRCodeGen(
verifyNameScoping(program, symbolTable) verifyNameScoping(program, symbolTable)
changeGlobalVarInits(symbolTable) changeGlobalVarInits(symbolTable)
val irSymbolTable = IRSymbolTable.fromAstSymboltable(symbolTable) val irSymbolTable = convertStToIRSt(symbolTable)
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding) val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
// collect global variables initializers // collect global variables initializers
@ -48,6 +48,7 @@ class IRCodeGen(
irProg.linkChunks() irProg.linkChunks()
irProg.convertAsmChunks() irProg.convertAsmChunks()
// the optimizer also does 1 essential step regardless of optimizations: joining adjacent chunks.
val optimizer = IRPeepholeOptimizer(irProg) val optimizer = IRPeepholeOptimizer(irProg)
optimizer.optimize(options.optimize, errors) optimizer.optimize(options.optimize, errors)
irProg.validate() irProg.validate()
@ -55,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.
@ -64,15 +67,17 @@ 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.target.identifier?.name==variable.scopedName it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedNameString
} as PtAssignment?) } as PtAssignment?)
val initValue = initialization?.value val initValue = initialization?.value
when(initValue){ 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" }
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 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
} }
@ -101,6 +106,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)
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) }
@ -194,10 +208,7 @@ class IRCodeGen(
old.fpReg1, old.fpReg1,
old.fpReg2, old.fpReg2,
immediate = immediateValue, immediate = immediateValue,
null, address = addressValue
address = addressValue,
null,
null
) )
} }
} }
@ -256,9 +267,11 @@ class IRCodeGen(
is PtBool, is PtBool,
is PtArray, is PtArray,
is PtBlock, is PtBlock,
is PtDefer -> throw AssemblyError("should have been transformed") is PtDefer -> throw AssemblyError("defer should have been transformed")
is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}") is PtString -> throw AssemblyError("string should not occur as separate statement node ${node.position}")
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}") is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
is PtStructDecl -> emptyList()
is PtSubSignature -> emptyList()
else -> TODO("missing codegen for $node") else -> TODO("missing codegen for $node")
} }
@ -420,35 +433,44 @@ class IRCodeGen(
whenStmt.choices.children.forEach { whenStmt.choices.children.forEach {
val choice = it as PtWhenChoice val choice = it as PtWhenChoice
if(choice.isElse) { if(choice.isElse) {
require(choice.parent.children.last() === choice)
result += translateNode(choice.statements) result += translateNode(choice.statements)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null) // is always the last node so can fall through
} else { } else {
if(choice.statements.children.isEmpty()) { if(choice.statements.children.isEmpty()) {
// no statements for this choice value, jump to the end immediately // no statements for this choice value, jump to the end immediately
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value -> choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also { chunk ->
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt()) chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel) chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
} }
} }
} else { } else {
val choiceLabel = createLabelName() val choiceLabel = createLabelName()
choices.add(choiceLabel to choice) val onlyJumpLabel = ((choice.statements.children.singleOrNull() as? PtJump)?.target as? PtIdentifier)?.name
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value -> val branchLabel: String
result += IRCodeChunk(null, null).also { if(onlyJumpLabel==null) {
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt()) choices.add(choiceLabel to choice)
it += IRInstruction(Opcode.BSTEQ, labelSymbol = choiceLabel) branchLabel = choiceLabel
} else {
branchLabel = onlyJumpLabel
}
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
result += IRCodeChunk(null, null).also { chunk ->
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = branchLabel)
} }
} }
} }
} }
} }
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
if(choices.isNotEmpty())
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
choices.forEach { (label, choice) -> choices.forEach { (label, choice) ->
result += labelFirstChunk(translateNode(choice.statements), label) result += labelFirstChunk(translateNode(choice.statements), label)
val lastStatement = choice.statements.children.last() if(!choice.isOnlyGotoOrReturn())
if(lastStatement !is PtReturn && lastStatement !is PtJump)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null) addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
} }
@ -468,11 +490,12 @@ 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 iterableLength = symbolTable.getLength(iterable.name) val iterableLength = symbolTable.getLength(iterable.name)
val loopvarSymbol = forLoop.variable.name val loopvarSymbol = forLoop.variable.name
val indexReg = registers.next(IRDataType.BYTE) val indexReg = registers.next(IRDataType.BYTE)
val tmpReg = registers.next(IRDataType.BYTE) val tmpReg = registers.next(elementDt)
val loopLabel = createLabelName() val loopLabel = createLabelName()
val endLabel = createLabelName() val endLabel = createLabelName()
when { when {
@ -480,9 +503,9 @@ class IRCodeGen(
// iterate over a zero-terminated string // iterate over a zero-terminated string
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also { result += IRCodeChunk(loopLabel, null).also {
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name) it += IRInstruction(Opcode.LOADX, elementDt, reg1 = tmpReg, reg2 = indexReg, labelSymbol = iterable.name)
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel) it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tmpReg, labelSymbol = loopvarSymbol) it += IRInstruction(Opcode.STOREM, elementDt, reg1 = tmpReg, labelSymbol = loopvarSymbol)
} }
result += translateNode(forLoop.statements) result += translateNode(forLoop.statements)
val jumpChunk = IRCodeChunk(null, null) val jumpChunk = IRCodeChunk(null, null)
@ -491,10 +514,9 @@ 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
val elementDt = iterable.type.elementType() if(elementDt!=IRDataType.WORD)
if(!elementDt.isWord)
throw AssemblyError("weird dt") throw AssemblyError("weird dt")
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=indexReg, immediate = 0), null)
result += IRCodeChunk(loopLabel, null).also { result += IRCodeChunk(loopLabel, null).also {
@ -504,7 +526,7 @@ class IRCodeGen(
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, labelSymbol=iterable.name+"_msb") it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, labelSymbol=iterable.name+"_msb")
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, labelSymbol=iterable.name+"_lsb") it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, labelSymbol=iterable.name+"_lsb")
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=concatReg, reg2=tmpRegMsb, reg3=tmpRegLsb) it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=concatReg, reg2=tmpRegMsb, reg3=tmpRegLsb)
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=concatReg, labelSymbol = loopvarSymbol) it += IRInstruction(Opcode.STOREM, elementDt, reg1=concatReg, labelSymbol = loopvarSymbol)
} }
result += translateNode(forLoop.statements) result += translateNode(forLoop.statements)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
@ -548,7 +570,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
@ -634,7 +656,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
@ -771,7 +793,7 @@ class IRCodeGen(
code += if(factor==0.0) { code += if(factor==0.0) {
IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0.0) IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = 0.0)
} else { } else {
IRInstruction(Opcode.MUL, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor) IRInstruction(Opcode.MULS, IRDataType.FLOAT, fpReg1 = fpReg, immediateFp = factor)
} }
return code return code
} }
@ -789,38 +811,40 @@ class IRCodeGen(
val factorReg = registers.next(IRDataType.FLOAT) val factorReg = registers.next(IRDataType.FLOAT)
code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor) code += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1=factorReg, immediateFp = factor)
code += if(knownAddress!=null) code += if(knownAddress!=null)
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress) IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, address = knownAddress)
else else
IRInstruction(Opcode.MULM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol) IRInstruction(Opcode.MULSM, IRDataType.FLOAT, fpReg1 = factorReg, labelSymbol = symbol)
} }
return code return code
} }
internal fun multiplyByConst(dt: IRDataType, reg: Int, factor: Int): IRCodeChunk { internal fun multiplyByConst(dt: DataType, reg: Int, factor: Int): IRCodeChunk {
val irdt = irType(dt)
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
if(factor==1) if(factor==1)
return code return code
val pow2 = powersOfTwoInt.indexOf(factor) val pow2 = powersOfTwoInt.indexOf(factor)
if(pow2==1) { if(pow2==1) {
// just shift 1 bit // just shift 1 bit
code += IRInstruction(Opcode.LSL, dt, reg1=reg) code += IRInstruction(Opcode.LSL, irdt, reg1=reg)
} }
else if(pow2>=1) { else if(pow2>=1) {
// just shift multiple bits // just shift multiple bits
val pow2reg = registers.next(IRDataType.BYTE) val pow2reg = registers.next(IRDataType.BYTE)
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=pow2reg, immediate = pow2) code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=pow2reg, immediate = pow2)
code += IRInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg) code += IRInstruction(Opcode.LSLN, irdt, reg1=reg, reg2=pow2reg)
} else { } else {
code += if (factor == 0) { code += if (factor == 0) {
IRInstruction(Opcode.LOAD, dt, reg1=reg, immediate = 0) IRInstruction(Opcode.LOAD, irdt, reg1=reg, immediate = 0)
} else { } else {
IRInstruction(Opcode.MUL, dt, reg1=reg, immediate = factor) val opcode = if(dt.isSigned) Opcode.MULS else Opcode.MUL
IRInstruction(opcode, irdt, reg1=reg, immediate = factor)
} }
} }
return code return code
} }
internal fun multiplyByConstInplace(dt: IRDataType, knownAddress: Int?, symbol: String?, factor: Int): IRCodeChunk { internal fun multiplyByConstInplace(dt: IRDataType, signed: Boolean, knownAddress: Int?, symbol: String?, factor: Int): IRCodeChunk {
val code = IRCodeChunk(null, null) val code = IRCodeChunk(null, null)
if(factor==1) if(factor==1)
return code return code
@ -850,10 +874,11 @@ class IRCodeGen(
else { else {
val factorReg = registers.next(dt) val factorReg = registers.next(dt)
code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor) code += IRInstruction(Opcode.LOAD, dt, reg1=factorReg, immediate = factor)
val opcode = if(signed) Opcode.MULSM else Opcode.MULM
code += if(knownAddress!=null) code += if(knownAddress!=null)
IRInstruction(Opcode.MULM, dt, reg1=factorReg, address = knownAddress) IRInstruction(opcode, dt, reg1=factorReg, address = knownAddress)
else else
IRInstruction(Opcode.MULM, dt, reg1=factorReg, labelSymbol = symbol) IRInstruction(opcode, dt, reg1=factorReg, labelSymbol = symbol)
} }
} }
return code return code
@ -1070,7 +1095,7 @@ class IRCodeGen(
} }
// evaluate jump address expression into a register and jump indirectly to it // evaluate jump address expression into a register and jump indirectly to it
val tr = expressionEval.translateExpression(goto.target) val tr = expressionEval.translateExpression(goto.target)
for(i in tr.chunks.flatMap { it.instructions }) { for(i in tr.chunks.flatMap { c -> c.instructions }) {
it += i it += i
} }
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg) it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
@ -1653,7 +1678,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 -> {
@ -1759,12 +1784,35 @@ class IRCodeGen(
private fun translate(ret: PtReturn): IRCodeChunks { private fun translate(ret: PtReturn): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
if(ret.children.size>1) { if(ret.children.size>1) {
// multi-value returns are passed throug cx16.R15 down to R0 (allows unencumbered use of many Rx registers if you don't return that many values) // note: multi-value returns are passed throug A or AY (for the first value) then cx16.R15 down to R0
val registersReverseOrder = Cx16VirtualRegisters.reversed() // (this allows unencumbered use of many Rx registers if you don't return that many values)
for ((value, register) in ret.children.zip(registersReverseOrder)) { // a floating point value is passed via FAC (just one fp value is possible)
val returnRegs = ret.definingISub()!!.returnsWhatWhere()
val values = ret.children.zip(returnRegs)
// first all but the first return values
for ((value, register) in values.drop(1)) {
val tr = expressionEval.translateExpression(value as PtExpression) val tr = expressionEval.translateExpression(value as PtExpression)
addToResult(result, tr, tr.resultReg, -1) if(register.second.isFloat) {
addInstr(result, IRInstruction(Opcode.STOREM, tr.dt, reg1=tr.resultReg, labelSymbol = "cx16.${register.toString().lowercase()}"), null) addToResult(result, tr, -1, tr.resultFpReg)
result += setCpuRegister(register.first, IRDataType.FLOAT, -1, tr.resultFpReg)
}
else {
addToResult(result, tr, tr.resultReg, -1)
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
}
}
// finally do the first of the return values (this avoids clobbering of its value in AY)
values.first().also { (value, register) ->
val tr = expressionEval.translateExpression(value as PtExpression)
if(register.second.isFloat) {
addToResult(result, tr, -1, tr.resultFpReg)
result += setCpuRegister(register.first, IRDataType.FLOAT, -1, tr.resultFpReg)
}
else {
addToResult(result, tr, tr.resultReg, -1)
result += setCpuRegister(register.first, irType(register.second), tr.resultReg, -1)
}
} }
addInstr(result, IRInstruction(Opcode.RETURN), null) addInstr(result, IRInstruction(Opcode.RETURN), null)
return result return result
@ -1813,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 }
} }
@ -1862,21 +1910,23 @@ 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") if (orig == null)
val orig = symbolTable.lookup(flattenedName) as StStaticVariable TODO("fix missing lookup for: ${it.name} parameter")
result += IRSubroutine.IRParam(flattenedName, orig.dt) result += IRSubroutine.IRParam(it.name, 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" }
@ -1891,7 +1941,7 @@ class IRCodeGen(
private var labelSequenceNumber = 0 private var labelSequenceNumber = 0
internal fun createLabelName(): String { internal fun createLabelName(): String {
labelSequenceNumber++ labelSequenceNumber++
return "${PtLabel.GENERATED_LABEL_PREFIX}$labelSequenceNumber" return "${GENERATED_LABEL_PREFIX}$labelSequenceNumber"
} }
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult
@ -1901,7 +1951,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))
@ -1912,5 +1962,100 @@ class IRCodeGen(
} }
} }
fun registerTypes(): Map<Int, IRDataType> = registers.getTypes() internal fun setCpuRegister(registerOrFlag: RegisterOrStatusflag, paramDt: IRDataType, resultReg: Int, resultFpReg: Int): IRCodeChunk {
val chunk = IRCodeChunk(null, null)
when(registerOrFlag.registerOrPair) {
RegisterOrPair.A -> chunk += IRInstruction(Opcode.STOREHA, IRDataType.BYTE, reg1=resultReg)
RegisterOrPair.X -> chunk += IRInstruction(Opcode.STOREHX, IRDataType.BYTE, reg1=resultReg)
RegisterOrPair.Y -> chunk += IRInstruction(Opcode.STOREHY, IRDataType.BYTE, reg1=resultReg)
RegisterOrPair.AX -> chunk += IRInstruction(Opcode.STOREHAX, IRDataType.WORD, reg1=resultReg)
RegisterOrPair.AY -> chunk += IRInstruction(Opcode.STOREHAY, IRDataType.WORD, reg1=resultReg)
RegisterOrPair.XY -> chunk += IRInstruction(Opcode.STOREHXY, IRDataType.WORD, reg1=resultReg)
RegisterOrPair.FAC1 -> chunk += IRInstruction(Opcode.STOREHFACZERO, IRDataType.FLOAT, fpReg1 = resultFpReg)
RegisterOrPair.FAC2 -> chunk += IRInstruction(Opcode.STOREHFACONE, IRDataType.FLOAT, fpReg1 = resultFpReg)
in Cx16VirtualRegisters -> {
chunk += IRInstruction(Opcode.STOREM, paramDt, reg1=resultReg, labelSymbol = "cx16.${registerOrFlag.registerOrPair.toString().lowercase()}")
}
null -> when(registerOrFlag.statusflag) {
// TODO: do the statusflag argument as last
Statusflag.Pc -> chunk += IRInstruction(Opcode.LSR, paramDt, reg1=resultReg)
else -> throw AssemblyError("unsupported statusflag as param")
}
else -> throw AssemblyError("unsupported register arg")
}
return chunk
}
internal fun evaluatePointerAddressIntoReg(result: MutableList<IRCodeChunkBase>, deref: PtPointerDeref): Pair<Int, UInt> {
// 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: UInt, 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

@ -23,6 +23,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
private fun optimizeOnlyJoinChunks() { private fun optimizeOnlyJoinChunks() {
// this chunk-joining is REQUIRED (optimization or no) to end up with a structurally sound chunk list
irprog.foreachSub { sub -> irprog.foreachSub { sub ->
joinChunks(sub) joinChunks(sub)
removeEmptyChunks(sub) removeEmptyChunks(sub)
@ -84,7 +85,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
/* /*
Empty Code chunk with label -> Empty Code chunk with label ->
If next chunk has no label -> move label to next chunk, remove original If next chunk has no label -> move label to next chunk, remove original
If next chunk has label -> label name should be the same, remove original, otherwise merge both labels into 1. If next chunk has label -> label name should be the same, in which case remove original, otherwise leave everything untouched.
If is last chunk -> keep chunk in place because of the label. If is last chunk -> keep chunk in place because of the label.
Empty Code chunk without label -> Empty Code chunk without label ->
should not have been generated! ERROR. should not have been generated! ERROR.
@ -111,14 +112,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
if(index>0) { if(index>0) {
if (chunk.label == nextchunk.label) if (chunk.label == nextchunk.label)
removeChunks += index removeChunks += index
else {
removeChunks += index
replaceLabels[chunk.label!!] = nextchunk.label!!
replaceLabels.entries.forEach { (key, value) ->
if (value == chunk.label)
replaceLabels[key] = nextchunk.label!!
}
}
} }
} }
} }
@ -364,7 +357,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
var changed = false var changed = false
indexedInstructions.reversed().forEach { (idx, ins) -> indexedInstructions.reversed().forEach { (idx, ins) ->
when (ins.opcode) { when (ins.opcode) {
Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MOD -> { Opcode.DIV, Opcode.DIVS, Opcode.MUL, Opcode.MULS, Opcode.MOD -> {
if (ins.immediate == 1) { if (ins.immediate == 1) {
chunk.instructions.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true
@ -384,15 +377,19 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
} }
Opcode.AND -> { Opcode.AND -> {
if (ins.immediate == 0) { when (ins.immediate) {
chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0) 0 -> {
changed = true chunk.instructions[idx] = IRInstruction(Opcode.LOAD, ins.type, reg1 = ins.reg1, immediate = 0)
} else if (ins.immediate == 255 && ins.type == IRDataType.BYTE) { changed = true
chunk.instructions.removeAt(idx) }
changed = true 255 if ins.type == IRDataType.BYTE -> {
} else if (ins.immediate == 65535 && ins.type == IRDataType.WORD) { chunk.instructions.removeAt(idx)
chunk.instructions.removeAt(idx) changed = true
changed = true }
65535 if ins.type == IRDataType.WORD -> {
chunk.instructions.removeAt(idx)
changed = true
}
} }
} }
Opcode.OR -> { Opcode.OR -> {

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,7 +50,50 @@ 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)
}
private fun removeBlockInits(code: IRProgram, blockLabel: String) {
val instructions = code.globalInits.instructions
instructions.toTypedArray().forEach {ins ->
if(ins.labelSymbol?.startsWith(blockLabel)==true) {
instructions.remove(ins)
}
}
// remove stray loads
instructions.toTypedArray().forEach { ins ->
if(ins.opcode in arrayOf(Opcode.LOAD, Opcode.LOADR, Opcode.LOADM)) {
if(ins.reg1!=0) {
if(instructions.count { it.reg1==ins.reg1 || it.reg2==ins.reg1 } <2) {
if(ins.labelSymbol!=null)
code.st.removeIfExists(ins.labelSymbol!!)
instructions.remove(ins)
}
}
else if(ins.fpReg1!=0) {
if (instructions.count { it.fpReg1 == ins.fpReg1 || it.fpReg2 == ins.fpReg1 } < 2) {
if(ins.labelSymbol!=null)
code.st.removeIfExists(ins.labelSymbol!!)
instructions.remove(ins)
}
}
}
}
} }
private fun removeUnusedSubroutines(): Int { private fun removeUnusedSubroutines(): Int {
@ -173,7 +219,7 @@ class IRUnusedCodeRemover(
if(chunk!=null) if(chunk!=null)
new+=chunk new+=chunk
else else
allLabeledChunks[label]?.let { new += it } allLabeledChunks[label]?.let { c -> new += c }
} }
else else
new += instr.branchTarget!! new += instr.branchTarget!!
@ -226,7 +272,7 @@ class IRUnusedCodeRemover(
chunk.instructions.forEach { chunk.instructions.forEach {
if(it.labelSymbol!=null) { if(it.labelSymbol!=null) {
val chunkName = it.labelSymbol!!.substringBeforeLast('.') val chunkName = it.labelSymbol!!.substringBeforeLast('.')
allLabeledChunks[chunkName]?.let { linkedChunks+=it } allLabeledChunks[chunkName]?.let { c -> linkedChunks += c }
} }
} }
} }

View File

@ -0,0 +1,149 @@
package prog8.codegen.intermediate
import prog8.code.*
import prog8.code.core.DataType
import prog8.intermediate.*
fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
val st = IRSymbolTable()
if (sourceSt != null) {
sourceSt.flat.forEach {
when(it.value.type) {
StNodeType.STATICVAR -> st.add(convert(it.value as StStaticVariable))
StNodeType.MEMVAR -> st.add(convert(it.value as StMemVar))
StNodeType.CONSTANT -> st.add(convert(it.value as StConstant))
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 -> { }
}
}
st.validate()
st.allVariables().forEach { variable ->
variable.onetimeInitializationArrayValue?.let {
it.forEach { arrayElt ->
val addrOfSymbol = arrayElt.addressOfSymbol
if (addrOfSymbol != null) {
require(addrOfSymbol.contains('.')) {
"pointer var in array should be properly scoped: $addrOfSymbol in ${variable.name}"
}
}
}
}
}
}
return st
}
private fun convert(struct: StStruct): IRStStructDef =
IRStStructDef(struct.scopedNameString, struct.fields, struct.size)
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
else
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
private fun convert(variable: StStaticVariable): IRStStaticVariable {
if('.' in variable.name) {
return IRStStaticVariable(variable.name,
variable.dt,
variable.initializationNumericValue,
variable.initializationStringValue,
variable.initializationArrayValue?.map { convertArrayElt(it) },
variable.length,
variable.zpwish,
variable.align,
variable.dirty)
} else {
fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? {
if(array==null)
return null
val newArray = mutableListOf<IRStArrayElement>()
array.forEach {
if(it.addressOfSymbol!=null) {
val target = variable.lookup(it.addressOfSymbol!!) ?: throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
newArray.add(IRStArrayElement(null, null, target.scopedNameString))
} else {
newArray.add(convertArrayElt(it))
}
}
return newArray
}
val scopedName = variable.scopedNameString
return IRStStaticVariable(scopedName,
variable.dt,
variable.initializationNumericValue,
variable.initializationStringValue,
fixupAddressOfInArray(variable.initializationArrayValue),
variable.length,
variable.zpwish,
variable.align,
variable.dirty
)
}
}
private fun convert(variable: StMemVar): IRStMemVar {
if('.' in variable.name) {
return IRStMemVar(
variable.name,
variable.dt,
variable.address,
variable.length
)
} else {
val scopedName = try {
variable.scopedNameString
} catch (_: UninitializedPropertyAccessException) {
variable.name
}
return IRStMemVar(scopedName, variable.dt, variable.address, variable.length)
}
}
private fun convert(constant: StConstant): IRStConstant {
val dt = DataType.forDt(constant.dt)
val scopedName = if('.' in constant.name) {
constant.name
} else {
try {
constant.scopedNameString
} catch (_: UninitializedPropertyAccessException) {
constant.name
}
}
return IRStConstant(scopedName, dt, constant.value)
}
private fun convert(variable: StMemorySlab): IRStMemorySlab {
return if('.' in variable.name)
IRStMemorySlab(variable.name, variable.size, variable.align)
else
IRStMemorySlab("$StMemorySlabPrefix.${variable.name}", variable.size, variable.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 IRStStructInstance(instance.name, instance.structName, values, instance.size)
}
internal const val StMemorySlabPrefix = "prog8_slabs" // TODO also add ".prog8_memoryslab_" ?

View File

@ -3,8 +3,8 @@ package prog8.codegen.vm
import prog8.code.SymbolTable import prog8.code.SymbolTable
import prog8.code.ast.PtProgram import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram import prog8.code.IAssemblyProgram
import prog8.code.core.ICodeGeneratorBackend import prog8.code.ICodeGeneratorBackend
import prog8.code.core.IErrorReporter import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter import prog8.intermediate.IRFileWriter

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
@ -66,6 +68,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
override fun noErrors(): Boolean = errors.isEmpty() override fun noErrors(): Boolean = errors.isEmpty()
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it } override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
override fun report() { override fun report() {
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") } infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }

View File

@ -21,6 +21,7 @@ class TestIRPeepholeOpt: FunSpec({
CompilationOptions.AllZeropageAllowed, CompilationOptions.AllZeropageAllowed,
floats = false, floats = false,
noSysInit = true, noSysInit = true,
romable = false,
compTarget = target, compTarget = target,
loadAddress = target.PROGRAM_LOAD_ADDRESS, loadAddress = target.PROGRAM_LOAD_ADDRESS,
memtopAddress = 0xffffu memtopAddress = 0xffffu
@ -52,7 +53,7 @@ class TestIRPeepholeOpt: FunSpec({
irProg.chunks().single().instructions.size shouldBe 1 irProg.chunks().single().instructions.size shouldBe 1
} }
test("remove jmp to label below") { test("remove jmp to label below but keep labels") {
val c1 = IRCodeChunk("main.start", null) val c1 = IRCodeChunk("main.start", null)
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label") c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label")
val c2 = IRCodeChunk("label", null) val c2 = IRCodeChunk("label", null)
@ -68,13 +69,16 @@ class TestIRPeepholeOpt: FunSpec({
irProg.chunks().flatMap { it.instructions }.size shouldBe 5 irProg.chunks().flatMap { it.instructions }.size shouldBe 5
val opt = IRPeepholeOptimizer(irProg) val opt = IRPeepholeOptimizer(irProg)
opt.optimize(true, ErrorReporterForTests()) opt.optimize(true, ErrorReporterForTests())
irProg.chunks().size shouldBe 3 val chunks = irProg.chunks()
irProg.chunks()[0].label shouldBe "main.start" chunks.size shouldBe 4
irProg.chunks()[1].label shouldBe "label2" chunks[0].label shouldBe "main.start"
irProg.chunks()[2].label shouldBe "label3" chunks[1].label shouldBe "label"
irProg.chunks()[0].isEmpty() shouldBe true chunks[2].label shouldBe "label2"
irProg.chunks()[1].isEmpty() shouldBe false chunks[3].label shouldBe "label3"
irProg.chunks()[2].isEmpty() shouldBe true chunks[0].isEmpty() shouldBe true
chunks[1].isEmpty() shouldBe true
chunks[2].isEmpty() shouldBe false
chunks[3].isEmpty() shouldBe true
val instr = irProg.chunks().flatMap { it.instructions } val instr = irProg.chunks().flatMap { it.instructions }
instr.size shouldBe 2 instr.size shouldBe 2
instr[0].opcode shouldBe Opcode.JUMP instr[0].opcode shouldBe Opcode.JUMP

View File

@ -23,6 +23,7 @@ class TestVmCodeGen: FunSpec({
zpAllowed = CompilationOptions.AllZeropageAllowed, zpAllowed = CompilationOptions.AllZeropageAllowed,
floats = true, floats = true,
noSysInit = false, noSysInit = false,
romable = false,
compTarget = target, compTarget = target,
loadAddress = target.PROGRAM_LOAD_ADDRESS, loadAddress = target.PROGRAM_LOAD_ADDRESS,
memtopAddress = 0xffffu memtopAddress = 0xffffu
@ -48,9 +49,10 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY) val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
sub.add(PtVariable( sub.add(PtVariable(
"pi", "pi",
DataType.forDt(BaseDataType.UBYTE), DataType.UBYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@ -60,6 +62,7 @@ class TestVmCodeGen: FunSpec({
DataType.arrayFor(BaseDataType.UBYTE), DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
3u, 3u,
Position.DUMMY Position.DUMMY
@ -69,15 +72,17 @@ class TestVmCodeGen: FunSpec({
DataType.arrayFor(BaseDataType.UBYTE), DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
3u, 3u,
Position.DUMMY Position.DUMMY
)) ))
sub.add(PtVariable( sub.add(PtVariable(
"xx", "xx",
DataType.forDt(BaseDataType.WORD), DataType.WORD,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
@ -85,7 +90,7 @@ class TestVmCodeGen: FunSpec({
val assign = PtAugmentedAssign("+=", Position.DUMMY) val assign = PtAugmentedAssign("+=", Position.DUMMY)
val target = PtAssignTarget(false, Position.DUMMY).also { val target = PtAssignTarget(false, Position.DUMMY).also {
val targetIdx = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY).also { idx -> val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX", idx.add(PtIdentifier("main.start.particleX",
DataType.arrayFor(BaseDataType.UBYTE), DataType.arrayFor(BaseDataType.UBYTE),
Position.DUMMY)) Position.DUMMY))
@ -93,7 +98,7 @@ class TestVmCodeGen: FunSpec({
} }
it.add(targetIdx) it.add(targetIdx)
} }
val value = PtArrayIndexer(DataType.forDt(BaseDataType.UBYTE), Position.DUMMY) val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX", value.add(PtIdentifier("main.start.particleDX",
DataType.arrayFor(BaseDataType.UBYTE), DataType.arrayFor(BaseDataType.UBYTE),
Position.DUMMY)) Position.DUMMY))
@ -104,15 +109,15 @@ class TestVmCodeGen: FunSpec({
val prefixAssign = PtAugmentedAssign("-", Position.DUMMY) val prefixAssign = PtAugmentedAssign("-", Position.DUMMY)
val prefixTarget = PtAssignTarget(false, Position.DUMMY).also { val prefixTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
prefixAssign.add(prefixTarget) prefixAssign.add(prefixTarget)
prefixAssign.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY)) prefixAssign.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
sub.add(prefixAssign) sub.add(prefixAssign)
val numberAssign = PtAugmentedAssign("+=", Position.DUMMY) val numberAssign = PtAugmentedAssign("+=", Position.DUMMY)
val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also { val numberAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
numberAssign.add(numberAssignTarget) numberAssign.add(numberAssignTarget)
numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY)) numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
@ -120,10 +125,10 @@ class TestVmCodeGen: FunSpec({
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY) val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also { val cxregAssignTarget = PtAssignTarget(false, Position.DUMMY).also {
it.add(PtIdentifier("main.start.xx", DataType.forDt(BaseDataType.WORD), Position.DUMMY)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
cxregAssign.add(cxregAssignTarget) cxregAssign.add(cxregAssignTarget)
cxregAssign.add(PtIdentifier("cx16.r0", DataType.forDt(BaseDataType.UWORD), Position.DUMMY)) cxregAssign.add(PtIdentifier("cx16.r0", DataType.UWORD, Position.DUMMY))
sub.add(cxregAssign) sub.add(cxregAssign)
block.add(sub) block.add(sub)
@ -131,7 +136,7 @@ class TestVmCodeGen: FunSpec({
// define the "cx16.r0" virtual register // define the "cx16.r0" virtual register
val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY) val cx16block = PtBlock("cx16", false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
cx16block.add(PtMemMapped("r0", DataType.forDt(BaseDataType.UWORD), 100u, null, Position.DUMMY)) cx16block.add(PtMemMapped("r0", DataType.UWORD, 100u, null, Position.DUMMY))
program.add(cx16block) program.add(cx16block)
val options = getTestOptions() val options = getTestOptions()
@ -163,40 +168,41 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY) val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
sub.add(PtVariable( sub.add(PtVariable(
"f1", "f1",
DataType.forDt(BaseDataType.FLOAT), DataType.FLOAT,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
)) ))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY)) cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY)) cmp1.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY)) cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY)) cmp2.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2) sub.add(if2)
val if3 = PtIfElse(Position.DUMMY) val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY)) cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp3.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY)) cmp3.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
if3.add(cmp3) if3.add(cmp3)
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3) sub.add(if3)
val if4 = PtIfElse(Position.DUMMY) val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY)) cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp4.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY)) cmp4.add(PtNumber(BaseDataType.FLOAT, 0.0, Position.DUMMY))
if4.add(cmp4) if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
@ -234,40 +240,41 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY) val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
sub.add(PtVariable( sub.add(PtVariable(
"f1", "f1",
DataType.forDt(BaseDataType.FLOAT), DataType.FLOAT,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
)) ))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY)) cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY)) cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY)) cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY)) cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2) sub.add(if2)
val if3 = PtIfElse(Position.DUMMY) val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY)) cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp3.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY)) cmp3.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if3.add(cmp3) if3.add(cmp3)
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3) sub.add(if3)
val if4 = PtIfElse(Position.DUMMY) val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY)) cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp4.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY)) cmp4.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if4.add(cmp4) if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
@ -301,24 +308,25 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY) val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
sub.add(PtVariable( sub.add(PtVariable(
"f1", "f1",
DataType.forDt(BaseDataType.FLOAT), DataType.FLOAT,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
)) ))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY)) cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY)) cmp1.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) }) if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
if1.add(PtNodeGroup()) if1.add(PtNodeGroup())
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.forDt(BaseDataType.FLOAT), Position.DUMMY)) cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY)) cmp2.add(PtNumber(BaseDataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) }) if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
@ -356,40 +364,41 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY) val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
sub.add(PtVariable( sub.add(PtVariable(
"sb1", "sb1",
DataType.forDt(BaseDataType.BYTE), DataType.BYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
)) ))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY)) cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY)) cmp1.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY)) cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY)) cmp2.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2) sub.add(if2)
val if3 = PtIfElse(Position.DUMMY) val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY)) cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp3.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY)) cmp3.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
if3.add(cmp3) if3.add(cmp3)
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3) sub.add(if3)
val if4 = PtIfElse(Position.DUMMY) val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY)) cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp4.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY)) cmp4.add(PtNumber(BaseDataType.BYTE, 0.0, Position.DUMMY))
if4.add(cmp4) if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
@ -427,40 +436,41 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY) val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
sub.add(PtVariable( sub.add(PtVariable(
"sb1", "sb1",
DataType.forDt(BaseDataType.BYTE), DataType.BYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
)) ))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY)) cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY)) cmp1.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp2 = PtBinaryExpression("!=", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY)) cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY)) cmp2.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2) sub.add(if2)
val if3 = PtIfElse(Position.DUMMY) val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp3 = PtBinaryExpression("<", DataType.BOOL, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY)) cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp3.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY)) cmp3.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
if3.add(cmp3) if3.add(cmp3)
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3) sub.add(if3)
val if4 = PtIfElse(Position.DUMMY) val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp4 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.forDt(BaseDataType.BYTE), Position.DUMMY)) cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp4.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY)) cmp4.add(PtNumber(BaseDataType.BYTE, 42.0, Position.DUMMY))
if4.add(cmp4) if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) }) if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
@ -494,24 +504,25 @@ class TestVmCodeGen: FunSpec({
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY) val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
sub.add(PtVariable( sub.add(PtVariable(
"ub1", "ub1",
DataType.forDt(BaseDataType.BYTE), DataType.BYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
null, null,
Position.DUMMY Position.DUMMY
)) ))
val if1 = PtIfElse(Position.DUMMY) val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp1 = PtBinaryExpression("==", DataType.BOOL, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)) cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp1.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY)) cmp1.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
if1.add(cmp1) if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) }) if1.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also { it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
if1.add(PtNodeGroup()) if1.add(PtNodeGroup())
sub.add(if1) sub.add(if1)
val if2 = PtIfElse(Position.DUMMY) val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression(">", DataType.forDt(BaseDataType.BOOL), Position.DUMMY) val cmp2 = PtBinaryExpression(">", DataType.BOOL, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.ub1", DataType.forDt(BaseDataType.UBYTE), Position.DUMMY)) cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp2.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY)) cmp2.add(PtNumber(BaseDataType.UBYTE, 42.0, Position.DUMMY))
if2.add(cmp2) if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also {it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) }) if2.add(PtNodeGroup().also { it.add(PtJump(Position.DUMMY).also {it.add(PtNumber(BaseDataType.UWORD, 0xc000.toDouble(), Position.DUMMY)) }) })
@ -542,7 +553,7 @@ class TestVmCodeGen: FunSpec({
val extsub = PtAsmSub("routine", PtAsmSub.Address(null, null, 0x5000u), setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY) val extsub = PtAsmSub("routine", PtAsmSub.Address(null, null, 0x5000u), setOf(CpuRegister.Y), emptyList(), emptyList(), false, Position.DUMMY)
block.add(extsub) block.add(extsub)
val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY) val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
val call = PtFunctionCall("main.routine", true, DataType.forDt(BaseDataType.UNDEFINED), Position.DUMMY) val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
sub.add(call) sub.add(call)
block.add(sub) block.add(sub)
program.add(block) program.add(block)
@ -553,11 +564,8 @@ class TestVmCodeGen: FunSpec({
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
irChunks.size shouldBe 1 irChunks.size shouldBe 1
irChunks[0].instructions.size shouldBe 2 irChunks[0].instructions.size shouldBe 1
val preparecallInstr = irChunks[0].instructions[0] val callInstr = irChunks[0].instructions[0]
preparecallInstr.opcode shouldBe Opcode.PREPARECALL
preparecallInstr.immediate shouldBe 0
val callInstr = irChunks[0].instructions[1]
callInstr.opcode shouldBe Opcode.CALL callInstr.opcode shouldBe Opcode.CALL
callInstr.address shouldBe 0x5000 callInstr.address shouldBe 0x5000
} }

View File

@ -5,10 +5,7 @@ import prog8.ast.FatalAstException
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.expressions.FunctionCallExpression import prog8.ast.expressions.FunctionCallExpression
import prog8.ast.expressions.NumericLiteral import prog8.ast.expressions.NumericLiteral
import prog8.code.core.BaseDataType import prog8.code.core.*
import prog8.code.core.Position
import prog8.code.core.isInteger
import prog8.code.core.isIntegerOrBool
import kotlin.math.* import kotlin.math.*
@ -70,52 +67,45 @@ class ConstExprEvaluator {
} }
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral { private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) { if(right.type.isIntegerOrBool) {
if(right.type.isIntegerOrBool) { val leftDt = left.type
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position) if(leftDt.isByteOrBool)
} return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() xor right.number.toInt()) and 255).toDouble(), left.position)
} else if(left.type==BaseDataType.UWORD) { else if(leftDt.isInteger) {
if(right.type.isInteger) { if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position) return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() xor right.number.toInt()) and 65535).toDouble(), left.position)
} else if (leftDt == BaseDataType.LONG)
} else if(left.type==BaseDataType.LONG) { return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
if(right.type.isInteger) {
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
} }
} }
throw ExpressionError("cannot calculate $left ^ $right", left.position) throw ExpressionError("cannot calculate $left ^ $right", left.position)
} }
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral { private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) { if(right.type.isIntegerOrBool) {
if(right.type.isIntegerOrBool) { val leftDt = left.type
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position) if(leftDt.isByteOrBool)
} return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() or right.number.toInt()) and 255).toDouble(), left.position)
} else if(left.type==BaseDataType.UWORD) { else if(leftDt.isInteger) {
if(right.type.isInteger) { if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position) return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() or right.number.toInt()) and 65535).toDouble(), left.position)
} else if (leftDt == BaseDataType.LONG)
} else if(left.type==BaseDataType.LONG) { return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
if(right.type.isInteger) {
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
} }
} }
throw ExpressionError("cannot calculate $left | $right", left.position) throw ExpressionError("cannot calculate $left | $right", left.position)
} }
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral { private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) { if(right.type.isIntegerOrBool) {
if(right.type.isIntegerOrBool) { val leftDt = left.type
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position) if(leftDt.isByteOrBool)
} return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() and right.number.toInt()) and 255).toDouble(), left.position)
} else if(left.type==BaseDataType.UWORD) { else if(leftDt.isInteger) {
if(right.type.isInteger) { if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position) return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() and right.number.toInt()) and 65535).toDouble(), left.position)
} else if (leftDt == BaseDataType.LONG)
} else if(left.type==BaseDataType.LONG) { return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
if(right.type.isInteger) {
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
} }
} }
throw ExpressionError("cannot calculate $left & $right", left.position) throw ExpressionError("cannot calculate $left & $right", left.position)

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
} }
@ -129,7 +129,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
return listOf(IAstModification.ReplaceNode(expr, newArray, parent)) return listOf(IAstModification.ReplaceNode(expr, newArray, parent))
} }
else { else {
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl(program) val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl()
if(leftTarget!=null && leftTarget.origin==VarDeclOrigin.ARRAYLITERAL) if(leftTarget!=null && leftTarget.origin==VarDeclOrigin.ARRAYLITERAL)
throw FatalAstException("shouldn't see an array literal converted to an autovar here") throw FatalAstException("shouldn't see an array literal converted to an autovar here")
} }
@ -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(program) 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]")
} }
} }
} }
@ -387,10 +391,10 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val rangeTo = iterableRange.to as? NumericLiteral val rangeTo = iterableRange.to as? NumericLiteral
if(rangeFrom==null || rangeTo==null) return noModifications if(rangeFrom==null || rangeTo==null) return noModifications
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: 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 val loopvarSimpleDt = loopvar.datatype.base
when(loopvarSimpleDt) { when(loopvarSimpleDt) {
BaseDataType.UBYTE -> { BaseDataType.UBYTE -> {

View File

@ -1,11 +1,6 @@
package prog8.optimizer package prog8.optimizer
import prog8.ast.FatalAstException import prog8.ast.*
import prog8.ast.IFunctionCall
import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.UndefinedSymbolError
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
@ -328,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)
@ -457,7 +452,7 @@ internal class ConstantIdentifierReplacer(
if(declArraySize!=null && declArraySize!=rangeExpr.size()) if(declArraySize!=null && declArraySize!=rangeExpr.size())
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!) errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
if(constRange!=null) { if(constRange!=null) {
val rangeType = rangeExpr.inferType(program).getOr(DataType.forDt(BaseDataType.UBYTE)) val rangeType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
return if(rangeType.isByte) { return if(rangeType.isByte) {
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype), ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
constRange.map { NumericLiteral(rangeType.base, it.toDouble(), decl.value!!.position) }.toTypedArray(), constRange.map { NumericLiteral(rangeType.base, it.toDouble(), decl.value!!.position) }.toTypedArray(),

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)
} }
@ -49,7 +49,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
if(truepart.statements.singleOrNull() is Jump) { if(truepart.statements.singleOrNull() is Jump) {
return listOf( return listOf(
IAstModification.InsertAfter(ifElse, elsepart, parent as IStatementContainer), IAstModification.InsertAfter(ifElse, elsepart, parent as IStatementContainer),
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse) IAstModification.ReplaceNode(elsepart, AnonymousScope.empty(), ifElse)
) )
} }
if(elsepart.statements.singleOrNull() is Jump) { if(elsepart.statements.singleOrNull() is Jump) {
@ -57,7 +57,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return listOf( return listOf(
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse), IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer), IAstModification.InsertAfter(ifElse, truepart, parent as IStatementContainer),
IAstModification.ReplaceNode(elsepart, AnonymousScope(mutableListOf(), elsepart.position), ifElse), IAstModification.ReplaceNode(elsepart, AnonymousScope.empty(), ifElse),
IAstModification.ReplaceNode(truepart, elsepart, ifElse) IAstModification.ReplaceNode(truepart, elsepart, ifElse)
) )
} }
@ -423,6 +423,29 @@ 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) {
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 +522,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 +539,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))
} }
} }
@ -531,7 +554,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
// useless msb() of byte value that was typecasted to word, replace with 0 // useless msb() of byte value that was typecasted to word, replace with 0
return listOf(IAstModification.ReplaceNode( return listOf(IAstModification.ReplaceNode(
functionCallExpr, functionCallExpr,
NumericLiteral(valueDt.getOr(DataType.forDt(BaseDataType.UBYTE)).base, 0.0, arg.expression.position), NumericLiteral(valueDt.getOr(DataType.UBYTE).base, 0.0, arg.expression.position),
parent)) parent))
} }
} else { } else {
@ -546,7 +569,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
// useless msb() of byte value, replace with 0 // useless msb() of byte value, replace with 0
return listOf(IAstModification.ReplaceNode( return listOf(IAstModification.ReplaceNode(
functionCallExpr, functionCallExpr,
NumericLiteral(argDt.getOr(DataType.forDt(BaseDataType.UBYTE)).base, 0.0, arg.position), NumericLiteral(argDt.getOr(DataType.UBYTE).base, 0.0, arg.position),
parent)) parent))
} }
} }
@ -555,12 +578,12 @@ 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))
} }
} }
else if(functionCallExpr.target.nameInSource == listOf("strings", "contains")) { else if(functionCallExpr.target.nameInSource == listOf("strings", "contains")) {
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl(program) val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl()
if(target?.value is StringLiteral) { if(target?.value is StringLiteral) {
errors.info("for actual strings, use a regular containment check instead: 'char in string'", functionCallExpr.position) errors.info("for actual strings, use a regular containment check instead: 'char in string'", functionCallExpr.position)
val contains = ContainmentCheck(functionCallExpr.args[1], functionCallExpr.args[0], functionCallExpr.position) val contains = ContainmentCheck(functionCallExpr.args[1], functionCallExpr.args[0], functionCallExpr.position)
@ -711,9 +734,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 +862,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 +910,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 +926,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

@ -23,9 +23,11 @@ fun Program.constantFold(errors: IErrorReporter, options: CompilationOptions) {
val optimizer = ConstantFoldingOptimizer(this, errors) val optimizer = ConstantFoldingOptimizer(this, errors)
optimizer.visit(this) optimizer.visit(this)
while (errors.noErrors() && optimizer.applyModifications() > 0) { var tries=0
while (errors.noErrors() && optimizer.applyModifications() > 0 && tries++ < 100000) {
optimizer.visit(this) optimizer.visit(this)
} }
require(tries<100000) { "endless loop in constantfold" }
if (errors.noErrors()) { if (errors.noErrors()) {
replacer.visit(this) replacer.visit(this)

View File

@ -13,10 +13,10 @@ import prog8.code.core.InternalCompilerException
import prog8.code.target.VMTarget import prog8.code.target.VMTarget
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.values.size==0 private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.values.isEmpty()
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined. // inliner potentially enables *ONE LINED* subroutines, without to be inlined.
class Inliner(private val program: Program, private val options: CompilationOptions): AstWalker() { class Inliner(private val program: Program, private val options: CompilationOptions): AstWalker() {
@ -45,7 +45,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
else if (stmt.values[0] is IdentifierReference) { else if (stmt.values[0] is IdentifierReference) {
makeFullyScoped(stmt.values[0] as IdentifierReference) makeFullyScoped(stmt.values[0] as IdentifierReference)
true true
} else if (stmt.values[0]!! is IFunctionCall && (stmt.values[0] as IFunctionCall).args.size <= 1 && (stmt.values[0] as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) { } else if (stmt.values[0] is IFunctionCall && (stmt.values[0] as IFunctionCall).args.size <= 1 && (stmt.values[0] as IFunctionCall).args.all { it is NumericLiteral || it is IdentifierReference }) {
if (stmt.values[0] is FunctionCallExpression) { if (stmt.values[0] is FunctionCallExpression) {
makeFullyScoped(stmt.values[0] as FunctionCallExpression) makeFullyScoped(stmt.values[0] as FunctionCallExpression)
true true
@ -105,7 +105,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
if (subroutine.inline && subroutine.statements.size > 1) { if (subroutine.inline && subroutine.statements.size > 1) {
require(subroutine.statements.size == 2 && isEmptyReturn(subroutine.statements[1])) require(subroutine.statements.size == 2 && isEmptyReturn(subroutine.statements[1]))
subroutine.statements.removeLast() // get rid of the Return, to be able to inline the (single) statement preceding it. subroutine.statements.removeLastOrNull() // get rid of the Return, to be able to inline the (single) statement preceding it.
} }
} }
} }
@ -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)
@ -122,7 +122,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
private fun makeFullyScoped(call: FunctionCallStatement) { private fun makeFullyScoped(call: FunctionCallStatement) {
makeFullyScoped(call.target) makeFullyScoped(call.target)
call.target.targetSubroutine(program)?.let { sub -> call.target.targetSubroutine()?.let { sub ->
val scopedName = IdentifierReference(sub.scopedName, call.target.position) val scopedName = IdentifierReference(sub.scopedName, call.target.position)
val scopedArgs = makeScopedArgs(call.args) val scopedArgs = makeScopedArgs(call.args)
if(scopedArgs.any()) { if(scopedArgs.any()) {
@ -134,7 +134,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
private fun makeFullyScoped(call: FunctionCallExpression) { private fun makeFullyScoped(call: FunctionCallExpression) {
makeFullyScoped(call.target) makeFullyScoped(call.target)
call.target.targetSubroutine(program)?.let { sub -> call.target.targetSubroutine()?.let { sub ->
val scopedName = IdentifierReference(sub.scopedName, call.target.position) val scopedName = IdentifierReference(sub.scopedName, call.target.position)
val scopedArgs = makeScopedArgs(call.args) val scopedArgs = makeScopedArgs(call.args)
if(scopedArgs.any()) { if(scopedArgs.any()) {
@ -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}"
@ -249,7 +249,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
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

@ -5,11 +5,7 @@ import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.AssociativeOperators import prog8.code.core.*
import prog8.code.core.BaseDataType
import prog8.code.core.CompilationOptions
import prog8.code.core.IErrorReporter
import prog8.code.core.Position
class StatementOptimizer(private val program: Program, class StatementOptimizer(private val program: Program,
@ -33,12 +29,19 @@ 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
} }
if(stringVar!=null && stringVar.wasStringLiteral(program)) { if(stringVar!=null && stringVar.wasStringLiteral()) {
val string = stringVar.targetVarDecl(program)?.value as? StringLiteral val string = stringVar.targetVarDecl()?.value as? StringLiteral
if(string!=null) { if(string!=null) {
val pos = functionCallStatement.position val pos = functionCallStatement.position
if (string.value.length == 1) { if (string.value.length == 1) {
@ -86,12 +89,11 @@ class StatementOptimizer(private val program: Program,
// empty true part? switch with the else part // empty true part? switch with the else part
if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) { if(ifElse.truepart.isEmpty() && ifElse.elsepart.isNotEmpty()) {
val invertedCondition = invertCondition(ifElse.condition, program) val invertedCondition = invertCondition(ifElse.condition, program)
val emptyscope = AnonymousScope(mutableListOf(), ifElse.elsepart.position)
val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position) val truepart = AnonymousScope(ifElse.elsepart.statements, ifElse.truepart.position)
return listOf( return listOf(
IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse), IAstModification.ReplaceNode(ifElse.condition, invertedCondition, ifElse),
IAstModification.ReplaceNode(ifElse.truepart, truepart, ifElse), IAstModification.ReplaceNode(ifElse.truepart, truepart, ifElse),
IAstModification.ReplaceNode(ifElse.elsepart, emptyscope, ifElse) IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope.empty(), ifElse)
) )
} }
@ -110,7 +112,7 @@ class StatementOptimizer(private val program: Program,
if(ifElse.truepart.statements.singleOrNull() is Return) { if(ifElse.truepart.statements.singleOrNull() is Return) {
val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position) val elsePart = AnonymousScope(ifElse.elsepart.statements, ifElse.elsepart.position)
return listOf( return listOf(
IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope(mutableListOf(), ifElse.elsepart.position), ifElse), IAstModification.ReplaceNode(ifElse.elsepart, AnonymousScope.empty(), ifElse),
IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer) IAstModification.InsertAfter(ifElse, elsePart, parent as IStatementContainer)
) )
} }
@ -150,13 +152,13 @@ class StatementOptimizer(private val program: Program,
if (range.size() == 1) { if (range.size() == 1) {
// 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(mutableListOf(), 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))
} }
} }
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program) val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl()
if(iterable!=null) { if(iterable!=null) {
if(iterable.datatype.isString) { if(iterable.datatype.isString) {
val sv = iterable.value as StringLiteral val sv = iterable.value as StringLiteral
@ -165,8 +167,8 @@ class StatementOptimizer(private val program: Program,
// loop over string of length 1 -> just assign the single character // loop over string of length 1 -> just assign the single character
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(mutableListOf(), 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))
} }
@ -177,9 +179,9 @@ class StatementOptimizer(private val program: Program,
// loop over array of length 1 -> just assign the single value // loop over array of length 1 -> just assign the single value
val av = (iterable.value as ArrayLiteral).value[0].constValue(program)?.number val av = (iterable.value as ArrayLiteral).value[0].constValue(program)?.number
if(av!=null) { if(av!=null) {
val scope = AnonymousScope(mutableListOf(), 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))
@ -315,17 +317,6 @@ class StatementOptimizer(private val program: Program,
val bexpr=assignment.value as? BinaryExpression val bexpr=assignment.value as? BinaryExpression
if(bexpr!=null) { if(bexpr!=null) {
val rightCv = bexpr.right.constValue(program)?.number val rightCv = bexpr.right.constValue(program)?.number
if(bexpr.operator=="-" && rightCv==null && targetIDt.isInteger) {
if(bexpr.right.isSimple && bexpr.right isSameAs assignment.target) {
// X = value - X --> X = -X ; X += value (to avoid need of stack-evaluation, for integers)
val negation = PrefixExpression("-", bexpr.right.copy(), bexpr.position)
val addValue = Assignment(assignment.target.copy(), BinaryExpression(bexpr.right, "+", bexpr.left, bexpr.position), AssignmentOrigin.OPTIMIZER, assignment.position)
return listOf(
IAstModification.ReplaceNode(bexpr, negation, assignment),
IAstModification.InsertAfter(assignment, addValue, parent as IStatementContainer)
)
}
}
if (rightCv != null && assignment.target isSameAs bexpr.left) { if (rightCv != null && assignment.target isSameAs bexpr.left) {
// assignments of the form: X = X <operator> <expr> // assignments of the form: X = X <operator> <expr>
@ -481,7 +472,7 @@ class StatementOptimizer(private val program: Program,
return IfElse( return IfElse(
compare, compare,
AnonymousScope(mutableListOf(assign), position), AnonymousScope(mutableListOf(assign), position),
AnonymousScope(mutableListOf(), position), AnonymousScope.empty(),
position position
) )
} }
@ -516,6 +507,27 @@ class StatementOptimizer(private val program: Program,
} }
} }
if(whenStmt.betterAsOnGoto(program, options)) {
// rewrite when into a on..goto , which is faster and also smaller for ~5+ cases
var elseJump: Jump? = null
val jumps = mutableListOf<Pair<Int, Jump>>()
whenStmt.choices.forEach { choice ->
if(choice.values==null) {
elseJump = choice.statements.statements.single() as Jump
} else {
choice.values!!.forEach { value ->
jumps.add(value.constValue(program)!!.number.toInt() to choice.statements.statements.single() as Jump)
}
}
}
val jumpLabels = jumps.sortedBy { it.first }.map { it.second.target as IdentifierReference }
val elsePart = if(elseJump==null) null else AnonymousScope(mutableListOf(elseJump), elseJump.position)
val onGoto = OnGoto(false, whenStmt.condition, jumpLabels, elsePart, whenStmt.position)
return listOf(IAstModification.ReplaceNode(whenStmt, onGoto, parent))
}
return noModifications return noModifications
} }

View File

@ -8,9 +8,9 @@ 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.core.ICompilationTarget import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter import prog8.code.core.IErrorReporter
import prog8.code.internedStringsModuleName
import prog8.compiler.CallGraph import prog8.compiler.CallGraph
@ -27,10 +27,10 @@ class UnusedCodeRemover(private val program: Program,
program.allBlocks.singleOrNull { it.name=="sys" } ?.let { program.allBlocks.singleOrNull { it.name=="sys" } ?.let {
val subroutines = it.statements.filterIsInstance<Subroutine>() val subroutines = it.statements.filterIsInstance<Subroutine>()
val push = subroutines.single { it.name == "push" } val push = subroutines.single { s -> s.name == "push" }
val pushw = subroutines.single { it.name == "pushw" } val pushw = subroutines.single { s -> s.name == "pushw" }
val pop = subroutines.single { it.name == "pop" } val pop = subroutines.single { s -> s.name == "pop" }
val popw = subroutines.single { it.name == "popw" } val popw = subroutines.single { s -> s.name == "popw" }
neverRemoveSubroutines.add(push) neverRemoveSubroutines.add(push)
neverRemoveSubroutines.add(pushw) neverRemoveSubroutines.add(pushw)
neverRemoveSubroutines.add(pop) neverRemoveSubroutines.add(pop)
@ -39,8 +39,8 @@ class UnusedCodeRemover(private val program: Program,
program.allBlocks.singleOrNull { it.name=="floats" } ?.let { program.allBlocks.singleOrNull { it.name=="floats" } ?.let {
val subroutines = it.statements.filterIsInstance<Subroutine>() val subroutines = it.statements.filterIsInstance<Subroutine>()
val push = subroutines.single { it.name == "push" } val push = subroutines.single { s -> s.name == "push" }
val pop = subroutines.single { it.name == "pop" } val pop = subroutines.single { s -> s.name == "pop" }
neverRemoveSubroutines.add(push) neverRemoveSubroutines.add(push)
neverRemoveSubroutines.add(pop) neverRemoveSubroutines.add(pop)
} }
@ -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 != internedStringsModuleName && "ignore_unused" !in block.options()) { if (block.name != INTERNED_STRINGS_MODULENAME && "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")
@ -10,6 +8,7 @@ plugins {
dependencies { dependencies {
implementation(project(":codeCore")) implementation(project(":codeCore"))
implementation(project(":simpleAst"))
implementation(project(":codeOptimizers")) implementation(project(":codeOptimizers"))
implementation(project(":compilerAst")) implementation(project(":compilerAst"))
implementation(project(":codeGenCpu6502")) implementation(project(":codeGenCpu6502"))
@ -17,7 +16,6 @@ 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")

View File

@ -17,13 +17,13 @@
<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" />
<orderEntry type="module" module-name="codeCore" /> <orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="simpleAst" />
<orderEntry type="module" module-name="compilerAst" /> <orderEntry type="module" module-name="compilerAst" />
<orderEntry type="module" module-name="codeOptimizers" /> <orderEntry type="module" module-name="codeOptimizers" />
<orderEntry type="module" module-name="codeGenCpu6502" /> <orderEntry type="module" module-name="codeGenCpu6502" />
<orderEntry type="module" module-name="codeGenExperimental" /> <orderEntry type="module" module-name="codeGenExperimental" />
<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" scope="TEST" /> <orderEntry type="module" module-name="intermediate" />
<orderEntry type="library" name="antlr.antlr4" level="project" />
</component> </component>
</module> </module>

View File

@ -1,4 +1,7 @@
%import shared_cbm_diskio %import shared_cbm_diskio
; No alterations here; everything is taken from the shared module. diskio {
%option no_symbol_prefixing, ignore_unused
; No alterations here; everything is taken from the shared module.
}

View File

@ -419,12 +419,14 @@ sys {
const ubyte target = 128 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502 const ubyte target = 128 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
const ubyte SIZEOF_BOOL = 1 const ubyte SIZEOF_BOOL = sizeof(bool)
const ubyte SIZEOF_BYTE = 1 const ubyte SIZEOF_BYTE = sizeof(byte)
const ubyte SIZEOF_UBYTE = 1 const ubyte SIZEOF_UBYTE = sizeof(ubyte)
const ubyte SIZEOF_WORD = 2 const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = 2 const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_FLOAT = 5 const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
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
const ubyte MIN_UBYTE = 0 const ubyte MIN_UBYTE = 0
@ -459,10 +461,12 @@ sys {
lda P8ZP_SCRATCH_W2+1 lda P8ZP_SCRATCH_W2+1
sta save_SCRATCH_ZPWORD2+1 sta save_SCRATCH_ZPWORD2+1
rts rts
save_SCRATCH_ZPB1 .byte 0 .section BSS
save_SCRATCH_ZPREG .byte 0 save_SCRATCH_ZPB1 .byte ?
save_SCRATCH_ZPWORD1 .word 0 save_SCRATCH_ZPREG .byte ?
save_SCRATCH_ZPWORD2 .word 0 save_SCRATCH_ZPWORD1 .word ?
save_SCRATCH_ZPWORD2 .word ?
.send BSS
; !notreached! ; !notreached!
}} }}
} }
@ -488,8 +492,8 @@ save_SCRATCH_ZPWORD2 .word 0
asmsub set_irq(uword handler @AY) clobbers(A) { asmsub set_irq(uword handler @AY) clobbers(A) {
%asm {{ %asm {{
sei sei
sta _modified+1 sta _vector
sty _modified+2 sty _vector+1
lda #<_irq_handler lda #<_irq_handler
sta cbm.CINV sta cbm.CINV
lda #>_irq_handler lda #>_irq_handler
@ -499,8 +503,8 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
_irq_handler _irq_handler
jsr sys.save_prog8_internals jsr sys.save_prog8_internals
cld cld
_modified
jsr $ffff ; modified jsr _run_custom
pha pha
jsr sys.restore_prog8_internals jsr sys.restore_prog8_internals
pla pla
@ -515,6 +519,12 @@ _modified
tax tax
pla pla
rti rti
_run_custom
jmp (_vector)
.section BSS
_vector .word ?
.send BSS
; !notreached!
}} }}
} }
@ -525,8 +535,8 @@ asmsub restore_irq() clobbers(A) {
sta cbm.CINV sta cbm.CINV
lda #>cbm.IRQDFRT lda #>cbm.IRQDFRT
sta cbm.CINV+1 sta cbm.CINV+1
lda #0 lda #1
sta c64.IREQMASK ; disable raster irq sta c64.IREQMASK ; enable raster irq
lda #%10000001 lda #%10000001
sta c64.CIA1ICR ; restore CIA1 irq sta c64.CIA1ICR ; restore CIA1 irq
cli cli
@ -537,8 +547,8 @@ asmsub restore_irq() clobbers(A) {
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) { asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
%asm {{ %asm {{
sei sei
sta _modified+1 sta _vector
sty _modified+2 sty _vector+1
lda cx16.r0 lda cx16.r0
ldy cx16.r0+1 ldy cx16.r0+1
jsr _setup_raster_irq jsr _setup_raster_irq
@ -552,8 +562,8 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
_raster_irq_handler _raster_irq_handler
jsr sys.save_prog8_internals jsr sys.save_prog8_internals
cld cld
_modified
jsr $ffff ; modified jsr _run_custom
pha pha
jsr sys.restore_prog8_internals jsr sys.restore_prog8_internals
lda #$ff lda #$ff
@ -567,6 +577,12 @@ _modified
tax tax
pla pla
rti rti
_run_custom
jmp (_vector)
.section BSS
_vector .word ?
.send BSS
_setup_raster_irq _setup_raster_irq
pha pha
@ -881,7 +897,7 @@ _no_msb_size
sta p8_sys_startup.cleanup_at_exit._exitcode sta p8_sys_startup.cleanup_at_exit._exitcode
lda #0 lda #0
rol a rol a
sta p8_sys_startup.cleanup_at_exit._exitcodeCarry sta p8_sys_startup.cleanup_at_exit._exitcarry
stx p8_sys_startup.cleanup_at_exit._exitcodeX stx p8_sys_startup.cleanup_at_exit._exitcodeX
sty p8_sys_startup.cleanup_at_exit._exitcodeY sty p8_sys_startup.cleanup_at_exit._exitcodeY
ldx prog8_lib.orig_stackpointer ldx prog8_lib.orig_stackpointer
@ -932,6 +948,18 @@ _no_msb_size
}} }}
} }
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
%asm {{
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
cpx #0
bne +
dey
+ dex
tya
rts
}}
}
inline asmsub pop() -> ubyte @A { inline asmsub pop() -> ubyte @A {
%asm {{ %asm {{
pla pla
@ -1064,9 +1092,11 @@ cx16 {
bpl - bpl -
rts rts
.section BSS
_cx16_vreg_storage _cx16_vreg_storage
.word 0,0,0,0,0,0,0,0 .word ?,?,?,?,?,?,?,?
.word 0,0,0,0,0,0,0,0 .word ?,?,?,?,?,?,?,?
.send BSS
; !notreached! ; !notreached!
}} }}
} }
@ -1136,16 +1166,21 @@ asmsub cleanup_at_exit() {
sta $ff00 ; default bank 15 sta $ff00 ; default bank 15
jsr cbm.CLRCHN ; reset i/o channels jsr cbm.CLRCHN ; reset i/o channels
jsr enable_runstop_and_charsetswitch jsr enable_runstop_and_charsetswitch
_exitcodeCarry = *+1 lda _exitcarry
lda #0
lsr a lsr a
_exitcode = *+1 lda _exitcode
lda #0 ; exit code possibly modified in exit() ldx _exitcodeX
_exitcodeX = *+1 ldy _exitcodeY
ldx #0
_exitcodeY = *+1
ldy #0
rts rts
.section BSS
_exitcarry .byte ?
_exitcode .byte ?
_exitcodeX .byte ?
_exitcodeY .byte ?
.send BSS
; !notreached!
}} }}
} }

View File

@ -302,15 +302,16 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
asl a asl a
tay tay
lda _screenrows+1,y lda _screenrows+1,y
sta _mod+2 sta P8ZP_SCRATCH_W1+1
txa txa
clc clc
adc _screenrows,y adc _screenrows,y
sta _mod+1 sta P8ZP_SCRATCH_W1
bcc + bcc +
inc _mod+2 inc P8ZP_SCRATCH_W1+1
+ pla + pla
_mod sta $ffff ; modified ldy #0
sta (P8ZP_SCRATCH_W1),y
rts rts
_screenrows .word cbm.Screen + range(0, 1000, 40) _screenrows .word cbm.Screen + range(0, 1000, 40)
@ -326,14 +327,15 @@ asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
asl a asl a
tay tay
lda setchr._screenrows+1,y lda setchr._screenrows+1,y
sta _mod+2 sta P8ZP_SCRATCH_W1+1
pla pla
clc clc
adc setchr._screenrows,y adc setchr._screenrows,y
sta _mod+1 sta P8ZP_SCRATCH_W1
bcc _mod bcc +
inc _mod+2 inc P8ZP_SCRATCH_W1+1
_mod lda $ffff ; modified + ldy #0
lda (P8ZP_SCRATCH_W1),y
rts rts
}} }}
} }
@ -346,15 +348,16 @@ asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A, Y) {
asl a asl a
tay tay
lda _colorrows+1,y lda _colorrows+1,y
sta _mod+2 sta P8ZP_SCRATCH_W1+1
txa txa
clc clc
adc _colorrows,y adc _colorrows,y
sta _mod+1 sta P8ZP_SCRATCH_W1
bcc + bcc +
inc _mod+2 inc P8ZP_SCRATCH_W1+1
+ pla + pla
_mod sta $ffff ; modified ldy #0
sta (P8ZP_SCRATCH_W1),y
rts rts
_colorrows .word $d800 + range(0, 1000, 40) _colorrows .word $d800 + range(0, 1000, 40)
@ -370,14 +373,15 @@ asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
asl a asl a
tay tay
lda setclr._colorrows+1,y lda setclr._colorrows+1,y
sta _mod+2 sta P8ZP_SCRATCH_W1+1
pla pla
clc clc
adc setclr._colorrows,y adc setclr._colorrows,y
sta _mod+1 sta P8ZP_SCRATCH_W1
bcc _mod bcc +
inc _mod+2 inc P8ZP_SCRATCH_W1+1
_mod lda $ffff ; modified + ldy #0
lda (P8ZP_SCRATCH_W1),y
rts rts
}} }}
} }
@ -385,25 +389,28 @@ _mod lda $ffff ; modified
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) { sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
; ---- set char+color at the given position on the screen ; ---- set char+color at the given position on the screen
%asm {{ %asm {{
_charptr = P8ZP_SCRATCH_W1
_colorptr = P8ZP_SCRATCH_W2
lda row lda row
asl a asl a
tay tay
lda setchr._screenrows+1,y lda setchr._screenrows+1,y
sta _charmod+2 sta _charptr+1
adc #$d4 adc #$d4
sta _colormod+2 sta _colorptr+1
lda setchr._screenrows,y lda setchr._screenrows,y
clc clc
adc col adc col
sta _charmod+1 sta _charptr
sta _colormod+1 sta _colorptr
bcc + bcc +
inc _charmod+2 inc _charptr+1
inc _colormod+2 inc _colorptr+1
+ lda character + lda character
_charmod sta $ffff ; modified ldy #0
sta (_charptr),y
lda charcolor lda charcolor
_colormod sta $ffff ; modified sta (_colorptr),y
rts rts
}} }}
} }

View File

@ -1,4 +1,7 @@
%import shared_cbm_diskio %import shared_cbm_diskio
; No alterations here; everything is taken from the shared module. diskio {
%option no_symbol_prefixing, ignore_unused
; No alterations here; everything is taken from the shared module.
}

View File

@ -4,8 +4,9 @@ FL_ONE_const .byte 129 ; 1.0
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0 FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2) FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
.section BSS
floats_temp_var .byte 0,0,0,0,0 ; temporary storage for a float floats_temp_var .byte ?,?,?,?,? ; temporary storage for a float
.send BSS
ub2float .proc ub2float .proc
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y ; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
@ -178,10 +179,10 @@ dec_var_f .proc
jmp MOVMF jmp MOVMF
.pend .pend
.section BSS
fmath_float1 .byte 0,0,0,0,0 ; storage for a mflpt5 value fmath_float1 .byte ?,?,?,?,? ; storage for a mflpt5 value
fmath_float2 .byte 0,0,0,0,0 ; storage for a mflpt5 value fmath_float2 .byte ?,?,?,?,? ; storage for a mflpt5 value
.send BSS
var_fac1_less_f .proc var_fac1_less_f .proc
; -- is the float in FAC1 < the variable AY? Result in A. Clobbers X. ; -- is the float in FAC1 < the variable AY? Result in A. Clobbers X.

View File

@ -193,6 +193,7 @@ graphics {
if length!=0 { if length!=0 {
%asm {{ %asm {{
_pixeladdr = P8ZP_SCRATCH_W1
lda p8v_length lda p8v_length
and #7 and #7
sta p8v_separate_pixels sta p8v_separate_pixels
@ -203,37 +204,35 @@ graphics {
lsr p8v_length+1 lsr p8v_length+1
ror p8v_length ror p8v_length
lda p8v_pixaddr lda p8v_pixaddr
sta _modified+1 sta _pixeladdr
lda p8v_pixaddr+1 lda p8v_pixaddr+1
sta _modified+2 sta _pixeladdr+1
lda p8v_length lda p8v_length
ora p8v_length+1 ora p8v_length+1
beq _zero beq _zero
ldy p8v_length ldy p8v_length
sty P8ZP_SCRATCH_B1 ; length
ldx #$ff ldx #$ff
_modified stx $ffff ; modified _more txa
lda _modified+1 ldy #0
sta (_pixeladdr),y
lda _pixeladdr
clc clc
adc #8 adc #8
sta _modified+1 sta _pixeladdr
bcc + bcc +
inc _modified+2 inc _pixeladdr+1
+ dey + dec P8ZP_SCRATCH_B1 ; length
bne _modified bne _more
_zero _zero
ldy p8v_separate_pixels ldy p8v_separate_pixels
beq hline_zero2 beq _end
lda _modified+1
sta P8ZP_SCRATCH_W1
lda _modified+2
sta P8ZP_SCRATCH_W1+1
lda hline_filled_right,y lda hline_filled_right,y
ldy #0 ldy #0
ora (P8ZP_SCRATCH_W1),y ora (_pixeladdr),y
sta (P8ZP_SCRATCH_W1),y sta (_pixeladdr),y
jmp hline_zero2 _end rts
hline_filled_right .byte 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110 hline_filled_right .byte 0, %10000000, %11000000, %11100000, %11110000, %11111000, %11111100, %11111110
hline_zero2
}} }}
} }
} }
@ -334,10 +333,10 @@ hline_zero2
; @(pixaddr) |= ormask[lsb(px) & 7] ; @(pixaddr) |= ormask[lsb(px) & 7]
; } ; }
inline asmsub plot(uword plotx @XY, ubyte ploty @A) clobbers (A, X, Y) { inline asmsub plot(uword plotx @AX, ubyte ploty @Y) clobbers (A, X, Y) {
%asm {{ %asm {{
stx p8b_graphics.p8v_internal_plotx sta p8b_graphics.p8v_internal_plotx
sty p8b_graphics.p8v_internal_plotx+1 stx p8b_graphics.p8v_internal_plotx+1
jsr p8b_graphics.p8s_internal_plot jsr p8b_graphics.p8s_internal_plot
}} }}
} }

View File

@ -349,10 +349,10 @@ inline asmsub getbanks() -> ubyte @A {
; retrieve arguments ; retrieve arguments
ldy #$01 ldy #$01
lda (P8ZP_SCRATCH_W1),y ; grab low byte of target address lda (P8ZP_SCRATCH_W1),y ; grab low byte of target address
sta _jmpfar+1 sta _jmpfar_vec
iny iny
lda (P8ZP_SCRATCH_W1),y ; now the high byte lda (P8ZP_SCRATCH_W1),y ; now the high byte
sta _jmpfar+2 sta _jmpfar_vec+1
iny iny
lda (P8ZP_SCRATCH_W1),y ; then the target bank lda (P8ZP_SCRATCH_W1),y ; then the target bank
sta P8ZP_SCRATCH_B1 sta P8ZP_SCRATCH_B1
@ -377,7 +377,7 @@ inline asmsub getbanks() -> ubyte @A {
lda P8ZP_SCRATCH_W2 lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1 ldy P8ZP_SCRATCH_W2+1
plp plp
jsr _jmpfar ; do the actual call jsr _jsrfar ; do the actual call
; restore bank without clobbering status flags and A register ; restore bank without clobbering status flags and A register
sta P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1
php php
@ -390,8 +390,13 @@ inline asmsub getbanks() -> ubyte @A {
lda P8ZP_SCRATCH_W1 lda P8ZP_SCRATCH_W1
plp plp
rts rts
_jsrfar jmp (_jmpfar_vec)
_jmpfar jmp $0000 ; modified .section BSS
_jmpfar_vec .word ?
.send BSS
; !notreached!
}} }}
} }
@ -434,12 +439,14 @@ sys {
const ubyte target = 64 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502 const ubyte target = 64 ; compilation target specifier. 255=virtual, 128=C128, 64=C64, 32=PET, 16=CommanderX16, 8=atari800XL, 7=Neo6502
const ubyte SIZEOF_BOOL = 1 const ubyte SIZEOF_BOOL = sizeof(bool)
const ubyte SIZEOF_BYTE = 1 const ubyte SIZEOF_BYTE = sizeof(byte)
const ubyte SIZEOF_UBYTE = 1 const ubyte SIZEOF_UBYTE = sizeof(ubyte)
const ubyte SIZEOF_WORD = 2 const ubyte SIZEOF_WORD = sizeof(word)
const ubyte SIZEOF_UWORD = 2 const ubyte SIZEOF_UWORD = sizeof(uword)
const ubyte SIZEOF_FLOAT = 5 const ubyte SIZEOF_LONG = sizeof(long)
const ubyte SIZEOF_POINTER = sizeof(&sys.wait)
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
const ubyte MIN_UBYTE = 0 const ubyte MIN_UBYTE = 0
@ -474,10 +481,12 @@ sys {
lda P8ZP_SCRATCH_W2+1 lda P8ZP_SCRATCH_W2+1
sta save_SCRATCH_ZPWORD2+1 sta save_SCRATCH_ZPWORD2+1
rts rts
save_SCRATCH_ZPB1 .byte 0 .section BSS
save_SCRATCH_ZPREG .byte 0 save_SCRATCH_ZPB1 .byte ?
save_SCRATCH_ZPWORD1 .word 0 save_SCRATCH_ZPREG .byte ?
save_SCRATCH_ZPWORD2 .word 0 save_SCRATCH_ZPWORD1 .word ?
save_SCRATCH_ZPWORD2 .word ?
.send BSS
; !notreached! ; !notreached!
}} }}
} }
@ -503,8 +512,8 @@ save_SCRATCH_ZPWORD2 .word 0
asmsub set_irq(uword handler @AY) clobbers(A) { asmsub set_irq(uword handler @AY) clobbers(A) {
%asm {{ %asm {{
sei sei
sta _modified+1 sta _vector
sty _modified+2 sty _vector+1
lda #<_irq_handler lda #<_irq_handler
sta cbm.CINV sta cbm.CINV
lda #>_irq_handler lda #>_irq_handler
@ -514,8 +523,8 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
_irq_handler _irq_handler
jsr sys.save_prog8_internals jsr sys.save_prog8_internals
cld cld
_modified
jsr $ffff ; modified jsr _run_custom
pha pha
jsr sys.restore_prog8_internals jsr sys.restore_prog8_internals
pla pla
@ -530,6 +539,13 @@ _modified
tax tax
pla pla
rti rti
_run_custom
jmp (_vector)
.section BSS
_vector .word ?
.send BSS
; !notreached!
}} }}
} }
@ -552,8 +568,8 @@ asmsub restore_irq() clobbers(A) {
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) { asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
%asm {{ %asm {{
sei sei
sta _modified+1 sta _vector
sty _modified+2 sty _vector+1
lda cx16.r0 lda cx16.r0
ldy cx16.r0+1 ldy cx16.r0+1
jsr _setup_raster_irq jsr _setup_raster_irq
@ -567,8 +583,8 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
_raster_irq_handler _raster_irq_handler
jsr sys.save_prog8_internals jsr sys.save_prog8_internals
cld cld
_modified
jsr $ffff ; modified jsr _run_custom
pha pha
jsr sys.restore_prog8_internals jsr sys.restore_prog8_internals
lda #$ff lda #$ff
@ -583,6 +599,12 @@ _modified
pla pla
rti rti
_run_custom
jmp (_vector)
.section BSS
_vector .word ?
.send BSS
_setup_raster_irq _setup_raster_irq
pha pha
lda #%01111111 lda #%01111111
@ -897,7 +919,7 @@ _no_msb_size
sta p8_sys_startup.cleanup_at_exit._exitcode sta p8_sys_startup.cleanup_at_exit._exitcode
lda #0 lda #0
rol a rol a
sta p8_sys_startup.cleanup_at_exit._exitcodeCarry sta p8_sys_startup.cleanup_at_exit._exitcarry
stx p8_sys_startup.cleanup_at_exit._exitcodeX stx p8_sys_startup.cleanup_at_exit._exitcodeX
sty p8_sys_startup.cleanup_at_exit._exitcodeY sty p8_sys_startup.cleanup_at_exit._exitcodeY
ldx prog8_lib.orig_stackpointer ldx prog8_lib.orig_stackpointer
@ -948,6 +970,18 @@ _no_msb_size
}} }}
} }
asmsub get_as_returnaddress(uword address @XY) -> uword @AX {
%asm {{
; return the address like JSR would push onto the stack: address-1, MSB first then LSB
cpx #0
bne +
dey
+ dex
tya
rts
}}
}
inline asmsub pop() -> ubyte @A { inline asmsub pop() -> ubyte @A {
%asm {{ %asm {{
pla pla
@ -1080,9 +1114,11 @@ cx16 {
bpl - bpl -
rts rts
.section BSS
_cx16_vreg_storage _cx16_vreg_storage
.word 0,0,0,0,0,0,0,0 .word ?,?,?,?,?,?,?,?
.word 0,0,0,0,0,0,0,0 .word ?,?,?,?,?,?,?,?
.send BSS
; !notreached! ; !notreached!
}} }}
} }
@ -1168,16 +1204,21 @@ asmsub cleanup_at_exit() {
jsr cbm.MEMTOP ; adjust MEMTOP down again jsr cbm.MEMTOP ; adjust MEMTOP down again
jsr cbm.CLRCHN ; reset i/o channels jsr cbm.CLRCHN ; reset i/o channels
jsr enable_runstop_and_charsetswitch jsr enable_runstop_and_charsetswitch
_exitcodeCarry = *+1 lda _exitcarry
lda #0
lsr a lsr a
_exitcode = *+1 lda _exitcode
lda #0 ; exit code possibly modified in exit() ldx _exitcodeX
_exitcodeX = *+1 ldy _exitcodeY
ldx #0
_exitcodeY = *+1
ldy #0
rts rts
.section BSS
_exitcarry .byte ?
_exitcode .byte ?
_exitcodeX .byte ?
_exitcodeY .byte ?
.send BSS
; !notreached!
}} }}
} }

View File

@ -305,15 +305,16 @@ asmsub setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers(A, Y)
asl a asl a
tay tay
lda _screenrows+1,y lda _screenrows+1,y
sta _mod+2 sta P8ZP_SCRATCH_W1+1
txa txa
clc clc
adc _screenrows,y adc _screenrows,y
sta _mod+1 sta P8ZP_SCRATCH_W1
bcc + bcc +
inc _mod+2 inc P8ZP_SCRATCH_W1+1
+ pla + pla
_mod sta $ffff ; modified ldy #0
sta (P8ZP_SCRATCH_W1),y
rts rts
_screenrows .word cbm.Screen + range(0, 1000, 40) _screenrows .word cbm.Screen + range(0, 1000, 40)
@ -329,14 +330,15 @@ asmsub getchr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
asl a asl a
tay tay
lda setchr._screenrows+1,y lda setchr._screenrows+1,y
sta _mod+2 sta P8ZP_SCRATCH_W1+1
pla pla
clc clc
adc setchr._screenrows,y adc setchr._screenrows,y
sta _mod+1 sta P8ZP_SCRATCH_W1
bcc _mod bcc +
inc _mod+2 inc P8ZP_SCRATCH_W1+1
_mod lda $ffff ; modified + ldy #0
lda (P8ZP_SCRATCH_W1),y
rts rts
}} }}
} }
@ -349,15 +351,16 @@ asmsub setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers(A, Y) {
asl a asl a
tay tay
lda _colorrows+1,y lda _colorrows+1,y
sta _mod+2 sta P8ZP_SCRATCH_W1+1
txa txa
clc clc
adc _colorrows,y adc _colorrows,y
sta _mod+1 sta P8ZP_SCRATCH_W1
bcc + bcc +
inc _mod+2 inc P8ZP_SCRATCH_W1+1
+ pla + pla
_mod sta $ffff ; modified ldy #0
sta (P8ZP_SCRATCH_W1),y
rts rts
_colorrows .word $d800 + range(0, 1000, 40) _colorrows .word $d800 + range(0, 1000, 40)
@ -373,14 +376,15 @@ asmsub getclr (ubyte col @A, ubyte row @Y) clobbers(Y) -> ubyte @ A {
asl a asl a
tay tay
lda setclr._colorrows+1,y lda setclr._colorrows+1,y
sta _mod+2 sta P8ZP_SCRATCH_W1+1
pla pla
clc clc
adc setclr._colorrows,y adc setclr._colorrows,y
sta _mod+1 sta P8ZP_SCRATCH_W1
bcc _mod bcc +
inc _mod+2 inc P8ZP_SCRATCH_W1+1
_mod lda $ffff ; modified + ldy #0
lda (P8ZP_SCRATCH_W1),y
rts rts
}} }}
} }
@ -388,25 +392,28 @@ _mod lda $ffff ; modified
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) { sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
; ---- set char+color at the given position on the screen ; ---- set char+color at the given position on the screen
%asm {{ %asm {{
_charptr = P8ZP_SCRATCH_W1
_colorptr = P8ZP_SCRATCH_W2
lda row lda row
asl a asl a
tay tay
lda setchr._screenrows+1,y lda setchr._screenrows+1,y
sta _charmod+2 sta _charptr+1
adc #$d4 adc #$d4
sta _colormod+2 sta _colorptr+1
lda setchr._screenrows,y lda setchr._screenrows,y
clc clc
adc col adc col
sta _charmod+1 sta _charptr
sta _colormod+1 sta _colorptr
bcc + bcc +
inc _charmod+2 inc _charptr+1
inc _colormod+2 inc _colorptr+1
+ lda character + lda character
_charmod sta $ffff ; modified ldy #0
sta (_charptr),y
lda charcolor lda charcolor
_colormod sta $ffff ; modified sta (_colorptr),y
rts rts
}} }}
} }

View File

@ -6,7 +6,7 @@ conv {
; ----- number conversions to decimal strings ---- ; ----- number conversions to decimal strings ----
str @shared string_out = "????????????????" ; result buffer for the string conversion routines ubyte[16] @shared string_out ; result buffer for the string conversion routines (note: uses uninitialized ARRAY instead of STR, to force it to be allocated in BSS area so it's ROM-compatible)
asmsub str_ub0(ubyte value @A) clobbers(X) -> str @AY { asmsub str_ub0(ubyte value @A) clobbers(X) -> str @AY {
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total) ; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
@ -402,7 +402,9 @@ _digit
+ iny + iny
bne _parse bne _parse
; never reached ; never reached
_negative .byte 0 .section BSS
_negative .byte ?
.send BSS
; !notreached! ; !notreached!
}} }}
} }
@ -699,12 +701,14 @@ ShiftedBcdTab
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C .byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C .byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
decTenThousands .byte 0 .section BSS
decThousands .byte 0 decTenThousands .byte ?
decHundreds .byte 0 decThousands .byte ?
decTens .byte 0 decHundreds .byte ?
decOnes .byte 0 decTens .byte ?
.byte 0 ; zero-terminate the decimal output string decOnes .byte ?
.byte ? ; zero-terminate the decimal output string, set to 0 by bss init mechanisms
.send BSS
; !notreached! ; !notreached!
}} }}
} }
@ -756,7 +760,9 @@ asmsub internal_uword2hex (uword value @AY) clobbers(A,Y) {
sta output+2 sta output+2
sty output+3 sty output+3
rts rts
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier) .section BSS
output .fill 5 ; 0-terminated output buffer (to make printing easier)
.send BSS
; !notreached! ; !notreached!
}} }}
} }

View File

@ -28,11 +28,20 @@
; that routine can for instance call current() (or just look at the active_task variable) to get the id of the next task to execute. ; that routine can for instance call current() (or just look at the active_task variable) to get the id of the next task to execute.
; It has then to return a boolean: true=next task is to be executed, false=skip the task this time. ; It has then to return a boolean: true=next task is to be executed, false=skip the task this time.
; - in tasks: call yield() to pass control to the next task. Use the returned userdata value to do different things. ; - in tasks: call yield() to pass control to the next task. Use the returned userdata value to do different things.
; For now, you MUST call yield() only from the actual subroutine that has been registered as a task!
; (this is because otherwise the cpu call stack gets messed up and an RTS in task1 could suddenly pop a return address belonging to another tasks' call frame)
; - call current() to get the current task id. ; - call current() to get the current task id.
; - call kill(taskid) to kill a task by id. ; - call kill(taskid) to kill a task by id.
; - call killall() to kill all tasks. ; - call killall() to kill all tasks.
; - IMPORTANT: if you add the same subroutine multiple times, IT CANNOT DEPEND ON ANY LOCAL VARIABLES OR R0-R15 TO KEEP STATE. NOT EVEN REPEAT LOOP COUNTERS. ; - IMPORTANT: if you add the same subroutine multiple times, IT CANNOT DEPEND ON ANY LOCAL VARIABLES OR R0-R15 TO KEEP STATE. NOT EVEN REPEAT LOOP COUNTERS.
; Those are all shared in the different tasks! You HAVE to use a mechanism around the userdata value (pointer?) to keep separate state elsewhere! ; Those are all shared in the different tasks! You HAVE to use a mechanism around the userdata value (pointer?) to keep separate state elsewhere!
; - IMPORTANT: ``defer`` cannot be used inside a coroutine that is reused for multiple tasks!!!
;
; TIP: HOW TO WAIT without BLOCKING other coroutines?
; Make sure you call yield() in the waiting loop, for example:
; uword timer = cbm.RDTIM16() + 60
; while cbm.RDTIM16() != timer
; void coroutines.yield()
coroutines { coroutines {
%option ignore_unused %option ignore_unused
@ -40,19 +49,17 @@ coroutines {
const ubyte MAX_TASKS = 64 const ubyte MAX_TASKS = 64
uword[MAX_TASKS] tasklist uword[MAX_TASKS] tasklist
uword[MAX_TASKS] userdatas uword[MAX_TASKS] userdatas
uword[MAX_TASKS] returnaddresses
ubyte active_task ubyte active_task
uword supervisor uword supervisor
sub add(uword taskaddress, uword userdata) -> ubyte { sub add(uword @nozp taskaddress, uword @nozp userdata) -> ubyte {
; find the next empty slot in the tasklist and stick it there ; find the next empty slot in the tasklist and stick it there
; returns the task id of the new task, or 255 if there was no space for more tasks. 0 is a valid task id! ; returns the task id of the new task, or 255 if there was no space for more tasks. 0 is a valid task id!
; also returns the success in the Carry flag (carry set=success, carry clear = task was not added) ; also returns the success in the Carry flag (carry set=success, carry clear = task was not added)
for cx16.r0L in 0 to len(tasklist)-1 { for cx16.r0L in 0 to len(tasklist)-1 {
if tasklist[cx16.r0L] == 0 { if tasklist[cx16.r0L] == 0 {
tasklist[cx16.r0L] = taskaddress tasklist[cx16.r0L] = sys.get_as_returnaddress(taskaddress)
userdatas[cx16.r0L] = userdata userdatas[cx16.r0L] = userdata
returnaddresses[cx16.r0L] = 0
sys.set_carry() sys.set_carry()
return cx16.r0L return cx16.r0L
} }
@ -69,57 +76,48 @@ coroutines {
} }
} }
sub run(uword supervisor_routine) { sub run(uword @nozp supervisor_routine) {
supervisor = supervisor_routine supervisor = supervisor_routine
for active_task in 0 to len(tasklist)-1 { for active_task in 0 to len(tasklist)-1 {
if tasklist[active_task]!=0 { if tasklist[active_task]!=0 {
; activate the termination handler and start the first task ; activate the termination handler and start the first task
; note: cannot use pushw() because JSR doesn't push the return address in the same way ; note: cannot use pushw() because JSR doesn't push the return address in the same way
sys.push_returnaddress(&termination) sys.push_returnaddress(&termination)
goto tasklist[active_task] sys.pushw(tasklist[active_task])
return
} }
} }
} }
sub yield() -> uword { sub yield() -> uword {
; Store the return address of the yielding task, ; Store the return address of the yielding task, and continue with the next one instead (round-robin)
; and continue with the next one instead (round-robin) ; Returns the associated userdata value.
; Returns the associated userdata value ; NOTE: CAN ONLY BE CALLED FROM THE SCOPE OF THE SUBROUTINE THAT HAS BEEN REGISTERED AS THE TASK!
uword task_start, task_continue uword task_return_address
returnaddresses[active_task] = sys.popw() tasklist[active_task] = sys.popw()
resume_with_next_task: skip_task:
if not next_task() { if not next_task() {
void sys.popw() ; remove return to the termination handler void sys.popw() ; remove return to the termination handler
return 0 ; exiting here will now actually return from the start() call back to the calling program :) return 0 ; exiting here will now actually return back to the calling program that called run()
} }
if supervisor!=0 { if supervisor!=0
if lsb(call(supervisor))==0 if lsb(call(supervisor))==0
goto resume_with_next_task goto skip_task
}
if task_continue==0 { ; returning from yield then continues with the next coroutine:
; fetch start address of next task. sys.pushw(task_return_address)
; address on the stack must be pushed in reverse byte order
; also, subtract 1 from the start address because JSR pushes returnaddress minus 1
; note: cannot use pushw() because JSR doesn't push the return address in the same way
sys.push_returnaddress(task_start)
} else
sys.pushw(task_continue)
; returning from yield then continues with the next coroutine
return userdatas[active_task] return userdatas[active_task]
sub next_task() -> bool { sub next_task() -> bool {
; search through the task list for the next active task ; search through the task list for the next active task
repeat len(tasklist) { repeat len(tasklist) {
active_task++ active_task++
if active_task==len(returnaddresses) if active_task==len(tasklist)
active_task=0 active_task=0
task_start = tasklist[active_task] task_return_address = tasklist[active_task]
if task_start!=0 { if task_return_address!=0 {
task_continue = returnaddresses[active_task]
return true return true
} }
} }
@ -127,9 +125,8 @@ resume_with_next_task:
} }
} }
sub kill(ubyte taskid) { sub kill(ubyte @nozp taskid) {
tasklist[taskid] = 0 tasklist[taskid] = 0
returnaddresses[taskid] = 0
} }
sub current() -> ubyte { sub current() -> ubyte {
@ -137,12 +134,10 @@ resume_with_next_task:
} }
sub termination() { sub termination() {
; a task has terminated. wipe it from the list. ; internal routine: a task has terminated. wipe it from the list.
; this is an internal routine
kill(active_task) kill(active_task)
; reactivate this termination handler ; reactivate this termination handler and go to the next task
; note: cannot use pushw() because JSR doesn't push the return address in the same way
sys.push_returnaddress(&termination) sys.push_returnaddress(&termination)
goto coroutines.yield.resume_with_next_task goto coroutines.yield.skip_task
} }
} }

View File

@ -4,12 +4,16 @@
; NOTE: If you experience weird behavior with these routines and you are using them ; NOTE: If you experience weird behavior with these routines and you are using them
; in the X16 emulator using HostFs, please try again with an SD-card image instead first. ; in the X16 emulator using HostFs, please try again with an SD-card image instead first.
; It is possible that there are still small differences between HostFS and actual CBM DOS in the emulator. ; It is possible that there are still small differences between HostFS and actual CBM DOS in the emulator.
;
; About the secondary addresses: ; About the secondary addresses:
; for writes (new files or overwrites), you can use 1 (without the mode string) or 2-14 (with the mode string) ; for writes (new files or overwrites), you can use 1 (without the mode string) or 2-14 (with the mode string)
; for reads (existing files) you can use 0 or 2-14 (mode string is optional) ; for reads (existing files) you can use 0 or 2-14 (mode string is optional)
; for modify mode (new files* or existing files), you must use 2-14, and the mode string ,s,m is required ; for modify mode (new files* or existing files), you must use 2-14, and the mode string ,s,m is required
; About the Dos/Drive error status message:
; The routines don't usually read/clear the dos/drive error status and message. ("blinking red led")
; In case of an error, you usually have to read/clear that yourself (with status() for example).
%import textio %import textio
%import conv %import conv
@ -21,6 +25,7 @@ diskio {
const ubyte READ_IO_CHANNEL=12 const ubyte READ_IO_CHANNEL=12
const ubyte WRITE_IO_CHANNEL=13 const ubyte WRITE_IO_CHANNEL=13
const ubyte STATUS_EOF=$40
ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to! ubyte @shared drivenumber = 8 ; user programs can set this to the drive number they want to load/save to!
@ -94,7 +99,7 @@ io_error:
cbm.CLRCHN() ; restore default i/o devices cbm.CLRCHN() ; restore default i/o devices
cbm.CLOSE(READ_IO_CHANNEL) cbm.CLOSE(READ_IO_CHANNEL)
if status!=0 and status & $40 == 0 { ; bit 6=end of file if status!=0 and status & STATUS_EOF == 0 { ; bit 6=end of file
txt.print("\ni/o error, status: ") txt.print("\ni/o error, status: ")
txt.print_ub(status) txt.print_ub(status)
txt.nl() txt.nl()
@ -346,7 +351,7 @@ close_end:
if cbm.READST()==0 { if cbm.READST()==0 {
iteration_in_progress = true iteration_in_progress = true
void cbm.CHRIN() ; read first byte to test for file not found void cbm.CHRIN() ; read first byte to test for file not found
if cbm.READST()==0 { if cbm.READST() & ~STATUS_EOF == 0 {
cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte cbm.CLOSE(READ_IO_CHANNEL) ; close file because we already consumed first byte
void cbm.OPEN() ; re-open the file void cbm.OPEN() ; re-open the file
cbm.CLRCHN() ; reset default i/o channels cbm.CLRCHN() ; reset default i/o channels
@ -375,7 +380,7 @@ close_end:
cbm.CLRCHN() ; reset default i/o channels cbm.CLRCHN() ; reset default i/o channels
if cx16.r0L!=0 { if cx16.r0L!=0 {
f_close() f_close()
if cx16.r0L & $40 == 0 if cx16.r0L & STATUS_EOF == 0
return 0 return 0
} }
return 1 return 1
@ -395,7 +400,7 @@ close_end:
if msb(bufferpointer) == $c0 if msb(bufferpointer) == $c0
bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary bufferpointer = mkword($a0, lsb(bufferpointer)) ; wrap over bank boundary
num_bytes -= readsize num_bytes -= readsize
if cbm.READST() & $40 !=0 { if cbm.READST() & STATUS_EOF !=0 {
f_close() ; end of file, close it f_close() ; end of file, close it
break break
} }
@ -406,39 +411,39 @@ close_end:
byte_read_loop: ; fallback if MACPTR isn't supported on the device byte_read_loop: ; fallback if MACPTR isn't supported on the device
%asm {{ %asm {{
lda bufferpointer lda bufferpointer
sta m_in_buffer+1 sta P8ZP_SCRATCH_W1
lda bufferpointer+1 lda bufferpointer+1
sta m_in_buffer+2 sta P8ZP_SCRATCH_W1+1
}} }}
while num_bytes!=0 { while num_bytes!=0 {
%asm {{ %asm {{
jsr cbm.CHRIN jsr cbm.CHRIN
m_in_buffer sta $ffff sta (P8ZP_SCRATCH_W1)
inc m_in_buffer+1 inc P8ZP_SCRATCH_W1
bne + bne +
inc m_in_buffer+2 inc P8ZP_SCRATCH_W1+1
+ +
}} }}
list_blocks++
cx16.r0L = cbm.READST() cx16.r0L = cbm.READST()
if_nz { if_nz {
f_close() f_close()
cbm.CLRCHN() ; reset default i/o channels cbm.CLRCHN() ; reset default i/o channels
if cx16.r0L & $40 !=0 ; eof? if cx16.r0L & STATUS_EOF !=0 ; eof?
return list_blocks ; number of bytes read return list_blocks ; number of bytes read
return 0 ; error. return 0 ; error.
} }
list_blocks++
num_bytes-- num_bytes--
} }
cbm.CLRCHN() ; reset default i/o channels cbm.CLRCHN() ; reset default i/o channels
return list_blocks ; number of bytes read return list_blocks ; number of bytes read
} }
; optimized for Commander X16 to use MACPTR block read kernal call
sub f_read_all(uword bufferpointer) -> uword { sub f_read_all(uword bufferpointer) -> uword {
; -- read the full contents 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.
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.MACPTR() yourself with the vera data register as address. ; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.MACPTR() yourself with the vera data register as address.
; Usually you will just be using load() / load_raw() or vload() / vload_raw() to read entire files!
if not iteration_in_progress if not iteration_in_progress
return 0 return 0
@ -446,6 +451,8 @@ m_in_buffer sta $ffff
uword total_read = 0 uword total_read = 0
while cbm.READST()==0 { while cbm.READST()==0 {
cx16.r0 = f_read(bufferpointer, 256) cx16.r0 = f_read(bufferpointer, 256)
if cx16.r0==0
break
total_read += cx16.r0 total_read += cx16.r0
bufferpointer += cx16.r0 bufferpointer += cx16.r0
} }
@ -724,6 +731,12 @@ io_error:
} }
; Load a prog8 compiled library binary blob at the given location into memory.
sub loadlib(uword libnameptr, uword libaddress) -> uword {
return internal_load_routine(libnameptr, libaddress, true)
}
sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword { sub internal_load_routine(uword 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
@ -941,7 +954,7 @@ internal_vload:
io_error: io_error:
cbm.CLRCHN() cbm.CLRCHN()
cbm.CLOSE(READ_IO_CHANNEL) cbm.CLOSE(READ_IO_CHANNEL)
if status!=0 and status & $40 == 0 if status!=0 and status & STATUS_EOF == 0
return 0 return 0
if @(cx16.r12)==0 { if @(cx16.r12)==0 {
cx16.r12-- cx16.r12--
@ -973,6 +986,7 @@ io_error:
sub exists(str filename) -> bool { sub exists(str filename) -> bool {
; -- returns true if the given file exists on the disk, otherwise false ; -- returns true if the given file exists on the disk, otherwise false
; DON'T use this if you already have a file open with f_open! ; DON'T use this if you already have a file open with f_open!
; NOTE: doesn't clear the dos error status and message, you'll have to read/clear that yourself (with status() for example)
if f_open(filename) { if f_open(filename) {
f_close() f_close()
return true return true

View File

@ -154,9 +154,9 @@ gfx_hires {
inc cx16.VERA_ADDR_L inc cx16.VERA_ADDR_L
bne ++ bne ++
inc cx16.VERA_ADDR_M inc cx16.VERA_ADDR_M
+ bne + + bne +
inc cx16.VERA_ADDR_H inc cx16.VERA_ADDR_H
+ inx ; next pixel + inx ; next pixel
}} }}
} }
} }

View File

@ -22,18 +22,23 @@ monogfx {
const ubyte MODE_STIPPLE = %00000001 const ubyte MODE_STIPPLE = %00000001
const ubyte MODE_INVERT = %00000010 const ubyte MODE_INVERT = %00000010
uword buffer_visible, buffer_back
sub lores() { sub lores() {
; enable 320*240 bitmap mode ; enable 320*240 bitmap mode
buffer_visible = buffer_back = $0000
cx16.VERA_CTRL=0 cx16.VERA_CTRL=0
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1 cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
cx16.VERA_DC_HSCALE = 64 cx16.VERA_DC_HSCALE = 64
cx16.VERA_DC_VSCALE = 64 cx16.VERA_DC_VSCALE = 64
cx16.VERA_L1_CONFIG = %00000100 cx16.VERA_L1_CONFIG = %00000100
cx16.VERA_L1_MAPBASE = 0 cx16.VERA_L1_MAPBASE = 0
cx16.VERA_L1_TILEBASE = 0 cx16.VERA_L1_TILEBASE = 0 ; lores
width = 320 width = 320
height = 240 height = 240
lores_mode = true lores_mode = true
buffer_visible = buffer_back = $0000
mode = MODE_NORMAL mode = MODE_NORMAL
clear_screen(false) clear_screen(false)
} }
@ -46,14 +51,40 @@ monogfx {
cx16.VERA_DC_VSCALE = 128 cx16.VERA_DC_VSCALE = 128
cx16.VERA_L1_CONFIG = %00000100 cx16.VERA_L1_CONFIG = %00000100
cx16.VERA_L1_MAPBASE = 0 cx16.VERA_L1_MAPBASE = 0
cx16.VERA_L1_TILEBASE = %00000001 cx16.VERA_L1_TILEBASE = %00000001 ; hires
width = 640 width = 640
height = 480 height = 480
lores_mode = false lores_mode = false
buffer_visible = buffer_back = $0000
mode = MODE_NORMAL mode = MODE_NORMAL
clear_screen(false) clear_screen(false)
} }
sub enable_doublebuffer() {
; enable double buffering mode
if lores_mode {
buffer_visible = $0000
buffer_back = $2800
} else {
buffer_visible = $0000
buffer_back = $9800
}
}
sub swap_buffers(bool wait_for_vsync) {
; flip the buffers: make the back buffer visible and the other one now the backbuffer.
; to avoid any screen tearing it is advised to call this during the vertical blank (pass true)
if wait_for_vsync
sys.waitvsync()
cx16.r0 = buffer_back
buffer_back = buffer_visible
buffer_visible = cx16.r0
cx16.VERA_CTRL = 0
cx16.r0 &= %1111110000000000
cx16.VERA_L1_TILEBASE = cx16.VERA_L1_TILEBASE & 1 | (cx16.r0H >>1 )
}
sub textmode() { sub textmode() {
; back to normal text mode ; back to normal text mode
cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode cx16.r15L = cx16.VERA_DC_VIDEO & %00000111 ; retain chroma + output mode
@ -559,6 +590,7 @@ drawmode: ora cx16.r15L
sub disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) { sub disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen. ; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen.
; Midpoint algorithm, filled ; Midpoint algorithm, filled
; Note: has problems with INVERT draw mode because of horizontal span overdrawing. Horizontal lines may occur.
if radius==0 if radius==0
return return
ubyte @zp yy = 0 ubyte @zp yy = 0
@ -597,6 +629,7 @@ drawmode: ora cx16.r15L
sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) { sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
; Does bounds checking and clipping. ; Does bounds checking and clipping.
; Midpoint algorithm, filled ; Midpoint algorithm, filled
; Note: has problems with INVERT draw mode because of horizontal span overdrawing. Horizontal lines may occur.
if radius==0 if radius==0
return return
ubyte @zp yy = 0 ubyte @zp yy = 0
@ -672,8 +705,7 @@ nostipple:
invert: invert:
prepare() prepare()
%asm {{ %asm {{
lda cx16.VERA_DATA0 eor cx16.VERA_DATA0
eor p8v_maskbits,y
sta cx16.VERA_DATA0 sta cx16.VERA_DATA0
}} }}
return return
@ -696,7 +728,7 @@ invert:
adc p8v_times40_lsb,y adc p8v_times40_lsb,y
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda p8v_times40_msb,y lda p8v_times40_msb,y
adc #0 adc p8v_buffer_back+1
sta cx16.VERA_ADDR_M sta cx16.VERA_ADDR_M
lda p8v_xx lda p8v_xx
@ -708,18 +740,29 @@ invert:
; width=640 (hires) ; width=640 (hires)
%asm {{ %asm {{
stz cx16.VERA_CTRL stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda p8v_xx lda p8v_xx
and #7 and #7
pha ; xbits pha ; xbits
; xx /= 8
lsr p8v_xx+1
ror p8v_xx
lsr p8v_xx+1
ror p8v_xx
lsr p8v_xx
}} }}
xx /= 8 ;xx /= 8
xx += yy*(640/8) xx += yy*(640/8)
%asm {{ %asm {{
lda p8v_xx+1
sta cx16.VERA_ADDR_M
lda p8v_xx lda p8v_xx
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda p8v_xx+1
clc
adc p8v_buffer_back+1
sta cx16.VERA_ADDR_M
lda #0
rol a ; hi bit carry also needed when double-buffering
sta cx16.VERA_ADDR_H
plx ; xbits plx ; xbits
lda p8v_maskbits,x lda p8v_maskbits,x
}} }}
@ -761,11 +804,15 @@ invert:
%asm {{ %asm {{
stz cx16.VERA_CTRL stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda p8v_xx+1
sta cx16.VERA_ADDR_M
lda p8v_xx lda p8v_xx
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda p8v_xx+1
clc
adc p8v_buffer_back+1
sta cx16.VERA_ADDR_M
lda #0
rol a ; hi bit carry also needed when double-buffering
sta cx16.VERA_ADDR_H
ply ; xbits ply ; xbits
lda p8s_plot.p8v_maskbits,y lda p8s_plot.p8v_maskbits,y
and cx16.VERA_DATA0 and cx16.VERA_DATA0
@ -848,8 +895,8 @@ skip:
} }
sub fill_scanline_right() { sub fill_scanline_right() {
; TODO maybe this could use vera auto increment, but that requires some clever masking calculations ; TODO maybe this could use vera auto increment, but that requires some clever masking calculations
cx16.r9s = xx cx16.r9s = xx
while xx <= width-1 { while xx <= width-1 {
if pgetset() if pgetset()
break break
@ -884,11 +931,15 @@ skip:
%asm {{ %asm {{
stz cx16.VERA_CTRL stz cx16.VERA_CTRL
stz cx16.VERA_ADDR_H
lda p8v_xpos+1
sta cx16.VERA_ADDR_M
lda p8v_xpos lda p8v_xpos
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda p8v_xpos+1
clc
adc p8v_buffer_back+1
sta cx16.VERA_ADDR_M
lda #0
rol a ; hi bit carry also needed when double-buffering
sta cx16.VERA_ADDR_H
ply ; xbits ply ; xbits
lda p8s_plot.p8v_maskbits,y lda p8s_plot.p8v_maskbits,y
and cx16.VERA_DATA0 and cx16.VERA_DATA0
@ -935,12 +986,12 @@ _doplot beq +
ror a ror a
lsr a lsr a
lsr a lsr a
clc
ldy p8v_yy ldy p8v_yy
clc
adc p8v_times40_lsb,y adc p8v_times40_lsb,y
sta cx16.VERA_ADDR_L sta cx16.VERA_ADDR_L
lda p8v_times40_msb,y lda p8v_times40_msb,y
adc #0 adc p8v_buffer_back+1
sta cx16.VERA_ADDR_M sta cx16.VERA_ADDR_M
lda #%00010000 ; autoincr lda #%00010000 ; autoincr
sta cx16.VERA_ADDR_H sta cx16.VERA_ADDR_H
@ -948,7 +999,33 @@ _doplot beq +
} }
else { else {
cx16.r0 = yy*(640/8) cx16.r0 = yy*(640/8)
cx16.vaddr(0, cx16.r0+(xx/8), 0, 1) ;cx16.r0 += xx/8
%asm {{
ldy p8v_xx+1
lda p8v_xx
sty P8ZP_SCRATCH_B1
lsr P8ZP_SCRATCH_B1
ror a
lsr P8ZP_SCRATCH_B1
ror a
lsr a
clc
adc cx16.r0
sta cx16.r0
bcc +
inc cx16.r0+1
+
stz cx16.VERA_CTRL
lda cx16.r0L
sta cx16.VERA_ADDR_L
lda cx16.r0H
clc
adc p8v_buffer_back+1
sta cx16.VERA_ADDR_M
lda #%00001000 ; autoincr (1 bit shifted)
rol a ; hi bit carry also needed when double-buffering
sta cx16.VERA_ADDR_H
}}
} }
return return
} }
@ -1116,15 +1193,11 @@ cdraw_mod2 ora cx16.VERA_DATA1
cmp #0 cmp #0
beq + beq +
lda #255 lda #255
+ ldy #80 + ldy #40
- sta cx16.VERA_DATA0 -
sta cx16.VERA_DATA0 .rept 16
sta cx16.VERA_DATA0
sta cx16.VERA_DATA0
sta cx16.VERA_DATA0
sta cx16.VERA_DATA0
sta cx16.VERA_DATA0
sta cx16.VERA_DATA0 sta cx16.VERA_DATA0
.endrept
dey dey
bne - bne -
rts rts

View File

@ -257,6 +257,7 @@ palette {
sub set_default16() { sub set_default16() {
; set first 16 colors to the defaults on the X16 ; set first 16 colors to the defaults on the X16
; (doesn't use the rom table so this works on roms older than 49 as well)
uword[] @nosplit colors = [ uword[] @nosplit colors = [
$000, ; 0 = black $000, ; 0 = black
$fff, ; 1 = white $fff, ; 1 = white
@ -277,4 +278,12 @@ palette {
] ]
set_rgb_nosplit(colors, len(colors), 0) set_rgb_nosplit(colors, len(colors), 0)
} }
; set the full 256 colors in the palette back to its default values on the X16
; NOTE: this routine requires rom version 49+
alias set_default = cx16.set_default_palette
; get the bank and address of the word-array containing the 256 default palette colors
; NOTE: this routine requires rom version 49+
alias get_default = cx16.get_default_palette
} }

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