Compare commits

..

510 Commits

Author SHA1 Message Date
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
0814ea9711 symboldumps 2025-02-13 13:10:07 +01:00
daefe839d8 version 11.1 2025-02-13 12:31:12 +01:00
e6088dd315 optimize byte comparisons against small word constants 2025-02-13 12:02:11 +01:00
fc03d6f332 changed -sourcelines option to -nosourcelines (default is now to include them) 2025-02-12 21:27:46 +01:00
2aeb7a838e finalize extracting neo and atari compiler targets into configuration files instead 2025-02-12 14:01:04 +01:00
99ff5dd078 extracting neo and atari compiler targets into configuration files instead 2025-02-12 13:58:34 +01:00
49982b49b6 extracting neo and atari compiler targets into configuration files instead 2025-02-12 13:58:34 +01:00
fd39c22616 Update FUNDING.yml
added paypal sponsor link
2025-02-12 13:48:49 +01:00
9e79722a7f fix rangeexpression value type casting 2025-02-11 22:23:07 +01:00
17334a1c58 fix 6502 codegen error for mixed case register params and normal params 2025-02-10 22:06:11 +01:00
c7f0ff11ac fix crash when initializing string variable with a non-string value 2025-02-10 02:54:53 +01:00
cd2cc89e6a fix type errors in Range containment check 2025-02-10 02:27:34 +01:00
069143092d fix Golden ram issues 2025-02-09 15:57:14 +01:00
efd41260f2 added %jmptable 2025-02-09 15:02:59 +01:00
8d2410622c make 64tass less strict about implied register addressing modes on instructions like "asl" 2025-02-05 22:33:50 +01:00
60554389b3 for fileselector library save/restore the prog8 temporary ZP locations 2025-02-04 00:17:55 +01:00
a940dc7d43 improve dumpvars output for zeropage variables 2025-02-03 22:36:58 +01:00
06ca68a625 add drivenumber to fileselector config 2025-02-02 23:09:27 +01:00
5b58e5b158 fix unit tests 2025-02-02 21:52:33 +01:00
74dd8fe80b %output library header generation depends on compiler target
fileselector example tweaks
2025-02-02 20:51:45 +01:00
75ddcda5f3 simplify DirectiveArg 2025-02-02 04:35:20 +01:00
216825b98a cx16: made fileselector example into a loadable library 2025-02-02 04:13:03 +01:00
a96defab86 diskio: added several diskio routines to list only the files or dir entries on the disk
uses CBM DOS filtering $:*=c and $:*=p
2025-02-02 02:44:23 +01:00
0864b0a1b7 cx16: added several diskio routines to list only the files or dir entries on the disk
uses CBM DOS filtering $:*=d and $:*=p
2025-02-01 22:41:34 +01:00
8b158d9240 fix 64tass ascii encoding option for atari and neo6502 2025-02-01 21:47:01 +01:00
f335251c2b added ability to specify additional assembler options in custom target configurations 2025-02-01 16:09:43 +01:00
67bc0b6931 Added txt.cls() to prog8_builtins.vim (#159) 2025-02-01 14:21:40 +01:00
e646dd1ed1 Add an encoding for the C64 OS custom character set. Use c64os: (#158) 2025-01-31 23:41:55 +01:00
2b7947f9b0 fix address check issue when using custom launcher 2025-01-31 23:34:12 +01:00
ec0cfb4b3f doc fix 2025-01-30 23:55:14 +01:00
9cdf53019c some tweaks to the configurable targets 2025-01-30 23:52:44 +01:00
1a04a3eb3a added ability to configure custom ASM launcher code in target configuration file 2025-01-30 22:10:42 +01:00
105d3995e0 some docs about the configurable targets, update kotlin to 2.1.10 2025-01-29 21:14:40 +01:00
8ce3204f93 program start label is back for library outputs 2025-01-28 19:28:34 +01:00
d0f15f1285 missing pic 2025-01-27 23:40:32 +01:00
66d6f67120 usage 2025-01-27 23:36:39 +01:00
a106c88054 unit test for %output library, and docs. 2025-01-27 23:26:21 +01:00
ee784e1ccc fix indication for when imported modules are library modules or not.
This fixes a bug where syslib and such gets optimized away when it is loaded from an alternative library location using the configurable target library path property setting.
2025-01-26 21:19:29 +01:00
bb75be0b44 library now includes the 2 byte PRG header
fixed some assorted things
2025-01-26 19:48:44 +01:00
2478aea316 add %output library 2025-01-24 23:25:57 +01:00
1e17df5296 optimize word+byte*2 expression to word+byte+byte (more efficient in 6502 codegen) 2025-01-24 21:30:02 +01:00
8583a96519 optimized gnomesorts 2025-01-24 00:40:11 +01:00
d0c184c7de remove needless 0 initializations of multi decl's
fix outdated text
2025-01-23 22:42:58 +01:00
0191acb2b3 fix IR codegen for the RETURN 4,5,6,7
added cx16.EXTAPI_memory_decompress_from_func for cx16
2025-01-22 23:31:51 +01:00
277a1a32b2 fix crash when declaring a string array with wrong variable type str vs str[] 2025-01-21 03:12:12 +01:00
7a13f57ab0 enforce variable init values are only strings or arrays 2025-01-20 01:25:17 +01:00
0c882836d9 support multi-value variable initialization: ubyte a,b,c = multi() 2025-01-18 22:08:31 +01:00
228be5cd04 callgraph no longer forgets some identifier occurrences 2025-01-18 21:18:08 +01:00
08cd2fd6e8 fix check for assigning to a constant, for multi-value assigns. 2025-01-18 20:38:02 +01:00
bc7b086f0f fix a configurable compilation target, add working example 2025-01-17 22:58:51 +01:00
e8f3af6981 adding a configurable compilation target 2025-01-17 00:56:44 +01:00
f9c7c7dab7 fix subroutine calling convention for @Rx parameters: don't pass them via cpu registers 2025-01-14 23:02:17 +01:00
09a17743ad merge IMachineDefinition into ICompilationTarget 2025-01-13 21:45:38 +01:00
4f096a7511 added sprites.getxy()
fix compiler crash on return xxx,yyy  when symbol is not defined
2025-01-10 22:26:38 +01:00
2ab2130000 renamed "intermediate AST" to "simplified AST" 2025-01-10 20:33:44 +01:00
66558f7638 IR support for multi-value returns in normal subroutines, documentation. 2025-01-09 22:39:08 +01:00
a6f9ed07e7 6502 codegen for multi-assigns 2025-01-09 00:38:47 +01:00
7268a8736f working on 6502 codegen for multi-assigns 2025-01-07 22:13:13 +01:00
8f6b5676d7 working on codegen for multi-value returns 2025-01-07 20:35:49 +01:00
ca9422bbe9 be able to parse multiple return values (sub + return) 2025-01-07 01:51:54 +01:00
35d9412559 better error message 2025-01-07 00:53:48 +01:00
f071c07dd9 IR: only put align on lsb array of split word array. tag split word arrays with split. (but this isn't actually used yet) 2025-01-06 02:50:54 +01:00
e5ff3c1ff3 fix compiler crash when using strings in if-expression. Remove harmless info message. 2025-01-06 01:34:42 +01:00
f0e8ff0326 get rid of some empty lines in the generated asm 2025-01-05 19:34:05 +01:00
3b5cda85ff fix register clobber on @() 2025-01-05 16:34:51 +01:00
420793f9e2 mkword() avoids unneeded push/pop for simple arguments 2025-01-05 13:41:40 +01:00
cf1dbaf0d8 no longer clear msb on asmsub argument byte @R0
avoid unneeded push/pop for 2 byte arguments to a functioncall
2025-01-05 13:06:39 +01:00
d187cef6b7 optimize x=min(x,100) and some other simple min() and max() cases 2025-01-05 11:59:00 +01:00
3b3616afda optimized pointer access @(pointer - constantoffset) 2025-01-05 06:31:34 +01:00
0ffebc25d0 tweak pointer access, fix and optimize reading memory from a label as pointer 2025-01-05 05:44:51 +01:00
478e2b4ebd abs() return type changed to ubyte/uword 2025-01-04 23:16:51 +01:00
a56ae7539a optimized codegen for word*128 (word << 7): no longer do 7 shifts 2025-01-04 23:08:21 +01:00
407773bda2 IR: don't lose inline asm chunks, fix carry behavior for multi-shifts 2025-01-04 22:12:54 +01:00
823eaa8918 when using @requirezp in a subroutine that is unused (and is pruned), don't give an error there when using %zeropage dontuse
give warning when a pointer var is declared as @nozp  (very inefficient)
2025-01-04 16:39:09 +01:00
a2be42c5ca txt.get_cursor() now returns the column and row as 2 values, no longer requires 2 pointer arguments 2025-01-04 04:06:27 +01:00
a76b8d66ff cx16: added syslib.get_charset()
updated fileselector
2025-01-04 01:02:43 +01:00
b7f47d354f IR: implemented << and >> for split word arrays 2025-01-03 22:18:02 +01:00
5d33c93af9 IR: implemented rol(), rol2(), ror(), ror2() for split word arrays 2025-01-03 20:01:50 +01:00
4db6859f3f IR: strict register pool type assignment, 1 unique type per register + verification during IR writing 2025-01-03 04:18:16 +01:00
45fe1bb16e reduce error clutter for undefined symbols 2025-01-03 01:55:44 +01:00
b014facbd3 cx16 fileselector improvements 2025-01-03 01:35:20 +01:00
3b4b37f16b optimize codegen for x += array[index] (and others) 2025-01-02 01:11:25 +01:00
68d5983a14 optimize monogfx.plot() to use a *40 lookup table in lores mode. Speeds up a lot of other routines too (line etc) 2025-01-01 20:25:58 +01:00
f2cfcfdf31 cx16: monogfx can fill with stipple mode again.
cx16: charset switching enabled again on program exit.
cx16: fileselector example: can now also show directories
2025-01-01 18:27:25 +01:00
10b9162dc5 improving fileselector 2024-12-30 21:34:03 +01:00
c84cc8f8c9 word arrays for sorting should be @nosplit 2024-12-30 00:00:31 +01:00
78c71bbf0e adding file selector example 2024-12-30 00:00:31 +01:00
37c2c1bf0b support &, &< and &> on array elements from split word arrays, not just on the array as a whole 2024-12-30 00:00:31 +01:00
c8996418da ir: tweak register pool, prepare to register the types there uniquely 2024-12-30 00:00:31 +01:00
76b29aa629 fixed register reuse and types on syscall interface 2024-12-30 00:00:31 +01:00
ee521793f8 ir: enforce single reg type 2024-12-30 00:00:31 +01:00
f42e12bc13 ir: fix more register typing errors 2024-12-30 00:00:31 +01:00
427451a23f ir: remove remaining SL* opcodes 2024-12-30 00:00:31 +01:00
af7930d494 ir: remove SL* opcodes 2024-12-30 00:00:31 +01:00
e2882d37bf ir: remove a bunch of strange in-place assignment operators, and problematic opcodes 2024-12-30 00:00:31 +01:00
942d3ee640 ir: improve register type detection 2024-12-30 00:00:31 +01:00
7b4a82b91a IR: report register usage and types in code blocks 2024-12-30 00:00:31 +01:00
056c0a24d9 better way of doing BIT instructions 2024-12-30 00:00:31 +01:00
827df04b32 IR: BIT instruction added 2024-12-30 00:00:31 +01:00
e174b31344 IR: fix up missing indirect goto codegen 2024-12-30 00:00:31 +01:00
49959af752 IR: support %align in code chunks, and load/store FAC0/FAC1 2024-12-30 00:00:31 +01:00
c86c0912f8 latest symboldumps 2024-12-29 20:07:45 +01:00
268b0c9365 fix memory top boundary address on c64 and c128 2024-12-29 17:01:22 +01:00
099fe280ba improved diskio.diskname() error detection 2024-12-29 10:29:31 +01:00
f786f60e9c cmb.PLOT: fixed order of return registers (Y then X, column then row) - same as argument order 2024-12-29 07:42:13 +01:00
f40e1eb1f2 fixed clipping sample 2024-12-29 00:56:12 +01:00
8b9da65357 Added supervisor to coroutines.run(). update symboldumps for 11.0.1. 2024-12-28 04:44:45 +01:00
2cbbe0d48a remove syscall from docs, it doesn't exist anymore 2024-12-27 22:35:56 +01:00
b6e1fb3ba8 emphasize that str[] is also split by default 2024-12-27 17:00:39 +01:00
bdccffbb8e stricter type checking in multivalue assigns, avoids possible invalid output due to missing type cast 2024-12-26 22:20:08 +01:00
5a85474712 pet32: fixed txt.plot() mixing up row and column 2024-12-26 19:37:39 +01:00
f50899c6fa coroutines: make yield() return a configured uword so that a task subroutine can get reused for multiple different things 2024-12-26 18:55:32 +01:00
4daa909f32 fix path normalization problems,
allow ~ in srcdirs compiler flag
2024-12-26 17:42:20 +01:00
4555edf369 update zsmkit to fix zsm_clearisr routine 2024-12-26 13:06:41 +01:00
529ea5bf58 added coroutines library and multitasking example. Added sys.push_returnaddress(). 2024-12-26 00:57:39 +01:00
fe011de934 fix the missing cases in certain expressions that need the address of a split word array 2024-12-25 16:55:07 +01:00
0653d430a7 fix compiler crash related to uword array parameters type checking 2024-12-25 12:12:20 +01:00
a587f6e9a0 make imported module cache case-insensitive
this avoids crashes when using case-insensitive filesystems and mistyping the exact path case
2024-12-25 11:51:24 +01:00
3850e1dbb5 Merge branch 'better-ir' 2024-12-24 21:23:57 +01:00
91cde072e0 added txt.t256c() on the cx16 to turn 256 color tile mode on or off 2024-12-24 12:10:02 +01:00
2ca4aed566 IR: prefix immediate values with '#' for human readability reasons (no technical reason) 2024-12-24 09:35:10 +01:00
5071da6784 retain constants in IR
some IR related cleanups
2024-12-24 00:30:08 +01:00
4c1e2f3110 refactor package nesting of ast exception classes 2024-12-23 18:14:46 +01:00
2727a4dcb3 tweak DataType class and memsizer related to subtypes/elementtypes 2024-12-23 17:28:25 +01:00
126d4c69e6 fix cx16images.py script for new pillow library version 2024-12-23 16:31:15 +01:00
7657edcb7d latest symboldumps 2024-12-22 09:24:45 +01:00
580e786952 change math.crc32 to the same algorithm as pkzip/zlib uses (ISO-HDLC). Add math.crc32_end_result(). Fix a parse error in profiler.py script. 2024-12-22 09:19:54 +01:00
c0ae35b3a3 tweaks, bump version 11.0 2024-12-22 06:34:17 +01:00
c3dc74788a added diskio.get_loadaddress()
added compression.decode_tscrunch_inplace()
2024-12-22 03:17:16 +01:00
379d241a0d various library modules now also use regular asm symbol prefixing rules: buffers, compression, cx16logo, test_stack. 2024-12-21 06:34:55 +01:00
1f49e8fe75 in diskio.f_readline make sure AY result isn't clobbered 2024-12-21 06:25:56 +01:00
d70cfbb661 added sorting module and sortingbench example 2024-12-21 06:18:35 +01:00
5482ac0302 simplify grammar of @tags, also improving their error message 2024-12-21 01:44:58 +01:00
131d5ceb4f avoid re-reading all source files when sourcelines are requested in the asm 2024-12-21 00:06:18 +01:00
512ddd1694 cleanups 2024-12-20 22:59:20 +01:00
14a213bff9 compression module: added decode_zx0 and decode_tscrunch
two very fast decompressors while still having pretty good compression ratio
2024-12-20 20:44:57 +01:00
d586846bc5 use simpler set_screen_mode() 2024-12-19 22:17:09 +01:00
ef4efcb112 cleanup 2024-12-19 21:06:51 +01:00
b01555d75e cx16.set_screen_mode() no longer returns anything.
tweak when codegen slightly.
allow trailing comma in array literals.

set_screen_mode failure status is really uncommon and still returned by the real kernal routine screen_mode().
2024-12-19 20:56:07 +01:00
3804fba0f1 moved jdk version config back to main gradle build file, version consistency 2024-12-19 13:39:27 +01:00
f93b7e3303 changed IR JUMPI instruction to support more indirect jump cases 2024-12-19 04:29:16 +01:00
73baaeff1f avoid compiler crash when using char literal in str initialization
fix compiler crash when using str var in an expression without &
2024-12-18 15:08:45 +01:00
7c79cdbd2f fix symbol prefixing on goto with expression
added coroutines example
2024-12-17 16:16:38 +01:00
8ea032ed66 fix compiler crash on certain split array values 2024-12-17 12:31:47 +01:00
e7a0cb636c add $< and $> operators to get the lsb and msb addresses of a @split array respectively.
document the new split array things.
2024-12-16 17:45:54 +01:00
02f3f5d0f5 @split is back to force splitting of word arrays 2024-12-16 14:51:32 +01:00
1e9bbd662b add palette.set_rgb_nosplit() and set_rbg_be_nosplit()
fix stream-wav missing rts which corrupted playback
fix showbmx example palette and image centering
2024-12-16 02:00:51 +01:00
8644a4ae91 more split array fixes 2024-12-15 22:54:06 +01:00
1e85f7812f removed anyall library module altogether. The routines weren't very optimized and didn't work on split word arrays. 2024-12-15 17:45:31 +01:00
80d88b3c61 fix many split array issues 2024-12-15 17:08:07 +01:00
d2827a7431 fix ast printer for arrays containing label addresses 2024-12-15 13:53:24 +01:00
28c721fa7d add a split-array version for word containment check 2024-12-15 13:45:47 +01:00
8f799567cf make word arrays split by default (w.i.p.) 2024-12-15 08:12:34 +01:00
9e8cc8b54d goto can now accept any expression as address (instead of just a constant), and ofcourse a label name still. 2024-12-15 05:22:37 +01:00
cc59069876 allow goto to take any expression, not only an integer or an identifier (part 1) 2024-12-14 01:01:32 +01:00
697d54e10a fix asmgen for call $3000 2024-12-13 22:33:26 +01:00
1679ca79b4 can now use boolean params mapped to Rx register 2024-12-13 20:47:23 +01:00
124ec77b58 update zsmkit to version 2.4, including the new on_deck routines 2024-12-13 20:15:36 +01:00
3675d7961b boolean variables can now also be memory-mapped (including boolean arrays) 2024-12-11 18:25:27 +01:00
f8aaa2d13c explicit integer type check for @R0-R15 parameters
avoids weird type inconsistency for boolean parameters that would get aliased as unsigned byte instead invisibly
2024-12-10 23:19:41 +01:00
b7afda781a Optimize 6502 bitwise operations on word values where only the msb or lsb is touched 2024-12-10 21:42:42 +01:00
535ec13072 improved codegen for testing for single bits: x & mask == mask 2024-12-09 04:05:00 +01:00
26d0a174db optimize codegen for while loops with empty body 2024-12-09 03:21:20 +01:00
b2e821755c optimized palette module
removed palette.set_monochrome(), added start color index to several color set functions
removed mcf example
update gradle wrapper
2024-12-08 15:30:42 +01:00
2e303041c1 fix crash when using undefined variable in for loop 2024-12-06 21:50:22 +01:00
96bed8f57f tweaks 2024-12-06 00:37:16 +01:00
86d4a4309f cleanups 2024-12-05 21:56:00 +01:00
1a1ab0dac6 changed the data type system to composite types 2024-12-05 21:48:51 +01:00
ba8c3d14f7 diskio docs, remove super harmless warning message 2024-12-05 20:51:44 +01:00
617ea15c3a fix failing optimization of 'not' in if statements 2024-12-04 19:03:24 +01:00
ef192a5778 easier notation for builtin function signatures by using varargs 2024-12-04 01:57:02 +01:00
565973c520 diskio read & write routines now always reset the io channels back to the defaults before returning
This means you don't have to call CLRCHN yourself anymore inbetween if you want to do screen output or keyboard input while a file is open
2024-12-03 23:46:07 +01:00
25b1043572 c64 diskio: Always call CLRCHN before CHKIN/CHKOUT calls
this seems to work around a Vice emulator issue when using host filesystem disk emulation.
Fixes #156
2024-12-03 19:15:44 +01:00
1ebfff7c7b add -plaintext and -ignorefootguns options 2024-12-03 19:12:30 +01:00
8341f9c066 diskio.status(): remove unreliable device not present error detection 2024-12-02 23:33:33 +01:00
28cac291de diskio.f_open_w() now also resets io channels back to defaults, like f_open() already did 2024-12-02 22:25:32 +01:00
8fa14a10e2 Optimize diskio.f_read for size=1, also improve ST check 2024-12-02 21:25:38 +01:00
55dbd095ed fix IR codegen missing a CMPI after if not condition
fix IR codegen for containmentcheck
2024-12-02 03:06:06 +01:00
31ad8bdd8d remove bankof(), documented msw() and lsw() 2024-12-01 21:24:26 +01:00
181f3e9eb1 remove the unary/prefix operators ^ and << again 2024-12-01 20:50:33 +01:00
50c3d809dc fix type casting issues and unary ^ operator
signed numbers are no longer implicitly converted to unsigned
proper range check on bankof()
2024-12-01 17:43:53 +01:00
58f696d00a document the @R0 - @R15 register support for normal subroutine parameters 2024-11-30 20:46:31 +01:00
f603c543d3 restructure documentation to get rid of redundant syntax chapter 2024-11-30 20:26:06 +01:00
6aaa0f928e IR: fix invalid asm name matching that resulted in not removing subs with a name matching an IR asm instruction 2024-11-30 00:10:57 +01:00
feb8aa435e monogfx, gfx_lores, gfx_hires now all uses 8kb stack from buffers module; no more broken flood fills. fill() has an extra byte parameter now where you need to pass in the ram bank to use for the stack. (not on virtual target) 2024-11-29 21:28:34 +01:00
310e8f15cd update to latest zsmkit lib v2.2 2024-11-29 18:37:06 +01:00
da03941582 fix build 2024-11-29 01:01:59 +01:00
dcbb36a3bd update gradle wrapper version 2024-11-29 00:54:21 +01:00
53558f5c1d add zmskit example for zsmkit v2 2024-11-29 00:04:57 +01:00
189399d5f8 update to kotlin 2.1.0 2024-11-28 03:49:07 +01:00
5406a992f5 improved buffers library, added to docs 2024-11-28 03:30:32 +01:00
bc9683cc54 add compression.decode_rle_vram() to decompress RLE data directly to X16's VRAM.
Document the compression library.
2024-11-26 02:06:35 +01:00
2eed75f602 call convention for @Rx parameters, also use cpu registers if possible, like normal parameters 2024-11-25 22:22:24 +01:00
d58f9f56c4 tests for register args for normal subs
some warnings demoted into infos
2024-11-24 19:21:45 +01:00
2e35f3c3a3 code check cleanups 2024-11-24 16:14:22 +01:00
5c6bd9c091 register params support for normal subroutines 2024-11-24 15:56:54 +01:00
857d2eefca added floats.interpolate(), math.interpolate(), and LERP example 2024-11-24 10:00:21 +01:00
90f1e7fd6a ast printing fixes, added alias to syntax files 2024-11-24 07:28:33 +01:00
18e37accf9 improve detection of register re-use in parameters 2024-11-24 05:27:43 +01:00
cc53d698bf added msw() and lsw() builtin functions (experimental) 2024-11-24 03:53:37 +01:00
cb86206698 added unary ^ and << operators (experimental) (gets bank and address of a long integer) 2024-11-24 03:07:18 +01:00
d77b1944fb rename bnk() to bankof() 2024-11-24 00:53:09 +01:00
a58cb43c4a fixed weird error messages when attempting to create variable with type long 2024-11-23 21:35:57 +01:00
88574c87c4 convert vtui and zsmkit to new extsub address expression capability 2024-11-23 21:21:52 +01:00
3a7a7091c0 update some docs 2024-11-23 21:01:18 +01:00
906b137a7c renamed 'string' module to 'strings' for consistency 2024-11-23 15:51:38 +01:00
42e2c5f605 fix some deprecated code in tests
silence redundant error about unused txt block
2024-11-23 15:48:18 +01:00
cc13a51493 fix import order problem related to %option merge 2024-11-23 12:15:15 +01:00
f569ce6141 setting a byte >=128 or word >=32768 now results in an out-of-range error, instead of an invalid casted value 2024-11-22 21:24:04 +01:00
4958463e75 moved floats.MIN/MAX to sys.MIN_FLOAT/MAX_FLOAT
added txt.print_f as alias to floats.print
2024-11-22 00:46:23 +01:00
2360625927 added min/max values for the various integer types as sys.MAX_XXX and sys.MIN_XXX
renamed sys.sizeof_xxx into sys.SIZEOF_XXX to be consistent with the uppercasing of the other constants
2024-11-21 23:25:02 +01:00
8badc40883 added several float limits contants such as floats.EPSILON, E, MIN, MAX
fix VM float min max limits
2024-11-21 23:25:02 +01:00
844c97930f fix Antlr grammar build and convert final build.gradle to build.gradle.kts (kotlin DSL) 2024-11-20 23:23:26 +01:00
5c09dc10ae convert build.gradle to build.gradle.kts (kotlin DSL) 2024-11-20 23:23:26 +01:00
9fd9e9ab5f change block sort order so that blocks with address are now sorted last 2024-11-20 23:23:26 +01:00
35c477b5a6 Make extsub address a (constant) expression instead of a numeric literal
this makes it easier to define API jump tables
2024-11-20 23:23:26 +01:00
ae0cadb383 added bnk() builtin function 2024-11-20 23:23:21 +01:00
984230e8fa removed txt.VERA_TEXTMATRIX_BANK/VERA_TEXTMATRIX_ADDR it's now just txt.VERA_TEXTMATRIX (long const) 2024-11-20 23:22:56 +01:00
a874aec6a1 implementing const long 2024-11-20 23:22:56 +01:00
ea1daa97d3 remove the 'addmissingrts' compiler option 2024-11-20 23:22:56 +01:00
fb0d9b46b0 remove 'romsub' as a recognised alternative for 'extsub' 2024-11-20 23:22:56 +01:00
9da70bdf05 simplify ReturnConvention a little 2024-11-20 23:22:56 +01:00
d640cfbe13 removed BuiltinFunctionCallStatement redundant ast node type 2024-11-20 23:22:56 +01:00
51a05ec4b7 removed BuiltinFunctionCall redundant ast node type 2024-11-20 23:22:56 +01:00
459 changed files with 35805 additions and 22321 deletions

3
.github/FUNDING.yml vendored
View File

@ -2,7 +2,7 @@
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] #github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
#patreon: # Replace with a single Patreon username #patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username #open_collective: # Replace with a single Open Collective username
ko_fi: irmen ko_fi: irmen
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
@ -11,3 +11,4 @@ ko_fi: irmen
#otechie: # Replace with a single Otechie username #otechie: # Replace with a single Otechie username
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry #lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] #custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
custom: ['https://paypal.me/irmendejong']

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

12
.idea/kotlinc.xml generated
View File

@ -1,9 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="Kotlin2JsCompilerArguments">
<option name="moduleKind" value="plain" />
</component>
<component name="Kotlin2JvmCompilerArguments"> <component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="11" /> <option name="jvmTarget" value="11" />
</component> </component>
<component name="KotlinCommonCompilerArguments">
<option name="apiVersion" value="2.1" />
<option name="languageVersion" value="2.1" />
</component>
<component name="KotlinCompilerSettings">
<option name="additionalArguments" value="-Xwhen-guards" />
</component>
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="2.0.21" /> <option name="version" value="2.1.10" />
</component> </component>
</project> </project>

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.0.21" /> <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.0.21/kotlin-stdlib-jdk8-2.0.21.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.0.21/kotlin-stdlib-2.0.21.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.0.21/kotlin-stdlib-jdk7-2.0.21.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.0.21/kotlin-stdlib-jdk8-2.0.21-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.0.21/kotlin-stdlib-2.0.21-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.0.21/kotlin-stdlib-jdk7-2.0.21-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.0.21/kotlin-stdlib-jdk8-2.0.21-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.0.21/kotlin-stdlib-2.0.21-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.0.21/kotlin-stdlib-jdk7-2.0.21-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 />

View File

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

7
.idea/misc.xml generated
View File

@ -4,14 +4,15 @@
<option name="perGrammarGenerationSettings"> <option name="perGrammarGenerationSettings">
<list> <list>
<PerGrammarGenerationSettings> <PerGrammarGenerationSettings>
<option name="fileName" value="$PROJECT_DIR$/parser/antlr/Prog8ANTLR.g4" /> <option name="fileName" value="$PROJECT_DIR$/parser/src/main/antlr/Prog8ANTLR.g4" />
<option name="autoGen" value="true" /> <option name="autoGen" value="true" />
<option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" /> <option name="outputDir" value="$PROJECT_DIR$/parser/src/prog8/parser" />
<option name="libDir" value="" /> <option name="libDir" value="" />
<option name="encoding" value="" /> <option name="encoding" value="" />
<option name="pkg" value="" /> <option name="pkg" value="" />
<option name="language" value="" /> <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_17" default="true" project-jdk-name="openjdk-17" 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

@ -1,4 +1,7 @@
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/H2H6S0FFF) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/H2H6S0FFF)
PayPal: [![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://paypal.me/irmendejong)
[![Documentation](https://readthedocs.org/projects/prog8/badge/?version=latest)](https://prog8.readthedocs.io/) [![Documentation](https://readthedocs.org/projects/prog8/badge/?version=latest)](https://prog8.readthedocs.io/)
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
@ -14,7 +17,7 @@ which aims to provide many conveniences over raw assembly code (even when using
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can. This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza, If you like Prog8, and think it's worth a nice cup of hot coffee or a delicious pizza,
you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen). you can help me out a little bit over at [ko-fi.com/irmen](https://ko-fi.com/irmen) or [PayPal](https://paypal.me/irmendejong)
Documentation Documentation
@ -59,16 +62,21 @@ What does Prog8 provide?
- modularity, symbol scoping, subroutines. No need for forward declarations. - modularity, symbol scoping, subroutines. No need for forward declarations.
- various data types other than just bytes (16-bit words, floats, strings) - various data types other than just bytes (16-bit words, floats, strings)
- 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
- 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``
- various powerful built-in libraries to do I/O, number conversions, graphics and more - various powerful built-in libraries to do I/O, number conversions, graphics and more
- subroutines can return more than one result value
- inline assembly allows you to have full control when every cycle or byte matters - inline assembly allows you to have full control when every cycle or byte matters
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets) - supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
- encode strings and characters into petscii or screencodes or even other encodings - encode strings and characters into petscii or screencodes or even other encodings
@ -84,11 +92,11 @@ What does Prog8 provide?
*Multiple supported compiler targets* (contributions to improve these or to add support for other machines are welcome!): *Multiple supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU)
- "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)
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) - "pet32": Commodore PET (limited support)
- "pet32": Commodore PET (experimental) - via external configurable targets: Atari 800 XL, Neo6502, NES, C64OS, ...
- "atari": Atari 8 bit such as 800XL (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

@ -1,13 +1,12 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
kotlin("jvm")
id("application") id("application")
kotlin("jvm")
} }
val serverMainClassName = "prog8lsp.MainKt" val serverMainClassName = "prog8lsp.MainKt"
val applicationName = "prog8-beanshell" val applicationName = "prog8-beanshell"
val javaVersion: String by project
application { application {
mainClass.set(serverMainClassName) mainClass.set(serverMainClassName)
@ -45,11 +44,6 @@ sourceSets.main {
resources.srcDir("resources") resources.srcDir("resources")
} }
sourceSets.test {
java.srcDir("src")
resources.srcDir("resources")
}
tasks.startScripts { tasks.startScripts {
applicationName = "prog8-beanshell" applicationName = "prog8-beanshell"
} }
@ -62,13 +56,6 @@ tasks.register<Exec>("fixFilePermissions") {
commandLine("chmod", "+x", "${tasks.installDist.get().destinationDir}/bin/prog8-beanshell") commandLine("chmod", "+x", "${tasks.installDist.get().destinationDir}/bin/prog8-beanshell")
} }
tasks.withType<Test>() {
testLogging {
events("failed")
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
}
}
tasks.installDist { tasks.installDist {
finalizedBy("fixFilePermissions") finalizedBy("fixFilePermissions")
} }
@ -76,16 +63,3 @@ tasks.installDist {
tasks.build { tasks.build {
finalizedBy("installDist") finalizedBy("installDist")
} }
java {
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_11
}
kotlin {
compilerOptions.jvmTarget = JvmTarget.JVM_11
// jvmToolchain {
// languageVersion = JavaLanguageVersion.of(javaVersion.toInt())
// }
}

View File

@ -64,14 +64,14 @@ rotate3d {
matrix_math { matrix_math {
; vertices ; vertices
word[] @split xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ] word[] xcoor = [ -40, -40, -40, -40, 40, 40, 40, 40 ]
word[] @split ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ] word[] ycoor = [ -40, -40, 40, 40, -40, -40, 40, 40 ]
word[] @split zcoor = [ -40, 40, -40, 40, -40, 40, -40, 40 ] word[] zcoor = [ -40, 40, -40, 40, -40, 40, -40, 40 ]
; storage for rotated coordinates ; storage for rotated coordinates
word[len(xcoor)] @split rotatedx word[len(xcoor)] rotatedx
word[len(ycoor)] @split rotatedy word[len(ycoor)] rotatedy
word[len(zcoor)] @split rotatedz word[len(zcoor)] rotatedz
sub rotate_vertices(ubyte ax, ubyte ay, ubyte az) { sub rotate_vertices(ubyte ax, ubyte ay, ubyte az) {
; rotate around origin (0,0,0) ; rotate around origin (0,0,0)

View File

@ -25,8 +25,8 @@ adpcm {
; IMA ADPCM decoder. Supports mono and stereo streams. ; IMA ADPCM decoder. Supports mono and stereo streams.
ubyte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8] byte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8]
uword[] @split t_step = [ uword[] t_step = [
7, 8, 9, 10, 11, 12, 13, 14, 7, 8, 9, 10, 11, 12, 13, 14,
16, 17, 19, 21, 23, 25, 28, 31, 16, 17, 19, 21, 23, 25, 28, 31,
34, 37, 41, 45, 50, 55, 60, 66, 34, 37, 41, 45, 50, 55, 60, 66,
@ -79,7 +79,7 @@ adpcm {
; elif predicted < -32767: ; elif predicted < -32767:
; predicted = - 32767 ; predicted = - 32767
index += t_index[nibble] index += t_index[nibble] as ubyte
if_neg if_neg
index = 0 index = 0
else if index >= len(t_step)-1 else if index >= len(t_step)-1

View File

@ -4,15 +4,15 @@
circles { circles {
const ubyte MAX_NUM_CIRCLES = 80 const ubyte MAX_NUM_CIRCLES = 80
const ubyte GROWTH_RATE = 4 const ubyte GROWTH_RATE = 4
uword[MAX_NUM_CIRCLES] @split circle_x uword[MAX_NUM_CIRCLES] circle_x
uword[MAX_NUM_CIRCLES] @split circle_y uword[MAX_NUM_CIRCLES] circle_y
ubyte[MAX_NUM_CIRCLES] circle_radius ubyte[MAX_NUM_CIRCLES] circle_radius
ubyte color ubyte color
uword total_num_circles uword total_num_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

@ -1,7 +1,6 @@
%import textio %import textio
%import conv %import conv
%import string %import strings
%import string
textelite { textelite {
@ -172,7 +171,7 @@ textelite {
sub next_input(str buffer) -> ubyte { sub next_input(str buffer) -> ubyte {
input_index++ input_index++
return string.copy(inputs[input_index], buffer) return strings.copy(inputs[input_index], buffer)
} }
} }
@ -571,7 +570,7 @@ elite_galaxy {
ubyte distance = elite_planet.distance(px, py) ubyte distance = elite_planet.distance(px, py)
if distance <= max_distance { if distance <= max_distance {
elite_planet.name = make_current_planet_name() elite_planet.name = make_current_planet_name()
elite_planet.name[0] = string.upperchar(elite_planet.name[0]) elite_planet.name[0] = strings.upperchar(elite_planet.name[0])
uword tx = elite_planet.x uword tx = elite_planet.x
uword ty = elite_planet.y uword ty = elite_planet.y
if local { if local {
@ -743,42 +742,42 @@ elite_galaxy {
} }
elite_planet { elite_planet {
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"] str[] @nosplit words81 = ["fabled", "notable", "well known", "famous", "noted"]
str[] words82 = ["very", "mildly", "most", "reasonably", ""] str[] @nosplit words82 = ["very", "mildly", "most", "reasonably", ""]
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"] str[] @nosplit words83 = ["ancient", "\x95", "great", "vast", "pink"]
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"] str[] @nosplit words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"] str[] @nosplit words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"] str[] @nosplit words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"] str[] @nosplit words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"] str[] @nosplit words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"] str[] @nosplit words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"] str[] @nosplit words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"] str[] @nosplit words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"] str[] @nosplit words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"] str[] @nosplit words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "] str[] @nosplit words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
str[] words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"] str[] @nosplit words8F = ["\xB0", "The planet \xB0", "The world \xB0", "This planet", "This world"]
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"] str[] @nosplit words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
str[] words91 = ["planet", "world", "place", "little planet", "dump"] str[] @nosplit words91 = ["planet", "world", "place", "little planet", "dump"]
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"] str[] @nosplit words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"] str[] @nosplit words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"] str[] @nosplit words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"] str[] @nosplit words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"] str[] @nosplit words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"] str[] @nosplit words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"] str[] @nosplit words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"] str[] @nosplit words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"] str[] @nosplit words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"] str[] @nosplit words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"] str[] @nosplit words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"] str[] @nosplit words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"] str[] @nosplit words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"] str[] @nosplit words9F = ["shrew", "beast", "bison", "snake", "wolf"]
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"] str[] @nosplit wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"] str[] @nosplit wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"] str[] @nosplit wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"] str[] @nosplit wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"] str[] @nosplit wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
uword[] @shared wordlists = [ uword[] @shared wordlists = [
words81, words82, words83, words84, words85, words86, words87, words88, words81, words82, words83, words84, words85, words86, words87, words88,
@ -840,7 +839,7 @@ elite_planet {
} }
} }
randname[nx] = 0 randname[nx] = 0
randname[0] = string.upperchar(randname[0]) randname[0] = strings.upperchar(randname[0])
return randname return randname
} }
@ -912,12 +911,12 @@ elite_planet {
source_ptr = source_stack[stack_ptr] source_ptr = source_stack[stack_ptr]
} else { } else {
if c == $b0 { if c == $b0 {
@(result_ptr) = string.upperchar(name[0]) @(result_ptr) = strings.upperchar(name[0])
result_ptr++ result_ptr++
concat_string(&name + 1) concat_string(&name + 1)
} }
else if c == $b1 { else if c == $b1 {
@(result_ptr) = string.upperchar(name[0]) @(result_ptr) = strings.upperchar(name[0])
result_ptr++ result_ptr++
ubyte ni ubyte ni
for ni in 1 to len(name) { for ni in 1 to len(name) {
@ -981,7 +980,7 @@ elite_util {
if pc == 0 if pc == 0
return true return true
; to lowercase for case insensitive compare: ; to lowercase for case insensitive compare:
if string.lowerchar(pc)!=string.lowerchar(sc) if strings.lowerchar(pc)!=strings.lowerchar(sc)
return false return false
prefixptr++ prefixptr++
stringptr++ stringptr++

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

@ -1,10 +0,0 @@
plugins {
id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" apply false
}
allprojects {
repositories {
mavenLocal()
mavenCentral()
}
}

34
build.gradle.kts Normal file
View File

@ -0,0 +1,34 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
plugins {
kotlin("jvm") version "2.1.21"
}
allprojects {
apply(plugin="kotlin")
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
compilerOptions {
freeCompilerArgs = listOf("-Xwhen-guards")
jvmTarget = JvmTarget.JVM_11
}
sourceSets.all {
languageSettings {
// enable language features like so:
// enableLanguageFeature(LanguageFeature.WhenGuards.name)
}
}
}
java {
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_11
}
}

View File

@ -1,42 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
// should have no dependencies to other modules
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
}
// note: there are no unit tests in this module!

24
codeCore/build.gradle.kts Normal file
View File

@ -0,0 +1,24 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
// should have no dependencies to other modules
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
}
sourceSets {
main {
java {
srcDir("${project.projectDir}/src")
}
resources {
srcDir("${project.projectDir}/res")
}
}
}
// note: there are no unit tests in this module!

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,196 +0,0 @@
package prog8.code.ast
import prog8.code.core.*
sealed interface IPtSubroutine {
val name: String
val scopedName: String
}
class PtAsmSub(
name: String,
val address: Address?,
val clobbers: Set<CpuRegister>,
val parameters: List<Pair<RegisterOrStatusflag, PtSubroutineParameter>>,
val returns: List<Pair<RegisterOrStatusflag, DataType>>,
val inline: Boolean,
position: Position
) : PtNamedNode(name, position), IPtSubroutine {
class Address(val constbank: UByte?, var varbank: PtIdentifier?, val address: UInt)
}
class PtSub(
name: String,
val parameters: List<PtSubroutineParameter>,
val returntype: DataType?,
position: Position
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
init {
// params and return value should not be str
if(parameters.any{ it.type !in NumericDatatypes && it.type!=DataType.BOOL })
throw AssemblyError("non-numeric/non-bool parameter")
if(returntype!=null && returntype !in NumericDatatypes && returntype!=DataType.BOOL)
throw AssemblyError("non-numeric/non-bool returntype $returntype")
parameters.forEach { it.parent=this }
}
}
class PtSubroutineParameter(name: String, val type: DataType, position: Position): PtNamedNode(name, position)
sealed interface IPtAssignment {
val children: MutableList<PtNode>
val target: PtAssignTarget
get() {
if(children.size==2)
return children[0] as PtAssignTarget
else if(children.size<2)
throw AssemblyError("incomplete node")
else
throw AssemblyError("no singular target")
}
val value: PtExpression
get() = children.last() as PtExpression
val multiTarget: Boolean
get() = children.size>2
}
class PtAssignment(position: Position) : PtNode(position), IPtAssignment
class PtAugmentedAssign(val operator: String, position: Position) : PtNode(position), IPtAssignment
class PtAssignTarget(val void: Boolean, position: Position) : PtNode(position) {
val identifier: PtIdentifier?
get() = children.single() as? PtIdentifier
val array: PtArrayIndexer?
get() = children.single() as? PtArrayIndexer
val memory: PtMemoryByte?
get() = children.single() as? PtMemoryByte
val type: DataType
get() {
return when(val tgt = children.single()) {
is PtIdentifier -> tgt.type
is PtArrayIndexer -> tgt.type
is PtMemoryByte -> tgt.type
else -> throw AssemblyError("weird target $tgt")
}
}
infix fun isSameAs(expression: PtExpression): Boolean = !void && expression.isSameAs(this)
}
class PtConditionalBranch(val condition: BranchCondition, position: Position) : PtNode(position) {
val trueScope: PtNodeGroup
get() = children[0] as PtNodeGroup
val falseScope: PtNodeGroup
get() = children[1] as PtNodeGroup
}
class PtForLoop(position: Position) : PtNode(position) {
val variable: PtIdentifier
get() = children[0] as PtIdentifier
val iterable: PtExpression
get() = children[1] as PtExpression
val statements: PtNodeGroup
get() = children[2] as PtNodeGroup
}
class PtIfElse(position: Position) : PtNode(position) {
val condition: PtExpression
get() = children[0] as PtExpression
val ifScope: PtNodeGroup
get() = children[1] as PtNodeGroup
val elseScope: PtNodeGroup
get() = children[2] as PtNodeGroup
fun hasElse(): Boolean = children.size==3 && elseScope.children.isNotEmpty()
}
class PtJump(val identifier: PtIdentifier?, // note: even ad-hoc labels are wrapped as an Identifier to simplify code. Just use dummy type and position.
val address: UInt?,
position: Position) : PtNode(position) {
init {
identifier?.let {it.parent = this }
}
}
class PtRepeatLoop(position: Position) : PtNode(position) {
val count: PtExpression
get() = children[0] as PtExpression
val statements: PtNodeGroup
get() = children[1] as PtNodeGroup
}
class PtReturn(position: Position) : PtNode(position) {
val hasValue: Boolean
get() = children.any()
val value: PtExpression?
get() {
return if(children.any())
children.single() as PtExpression
else
null
}
}
sealed interface IPtVariable {
val name: String
val type: DataType
}
class PtVariable(
name: String,
override val type: DataType,
val zeropage: ZeropageWish,
val align: UInt,
val value: PtExpression?,
val arraySize: UInt?,
position: Position
) : PtNamedNode(name, position), IPtVariable {
init {
value?.let {it.parent=this}
}
}
class PtConstant(name: String, override val type: DataType, val value: Double, position: Position) : PtNamedNode(name, position), IPtVariable
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
init {
require(type!=DataType.BOOL && type!=DataType.ARRAY_BOOL)
}
}
class PtWhen(position: Position) : PtNode(position) {
val value: PtExpression
get() = children[0] as PtExpression
val choices: PtNodeGroup
get() = children[1] as PtNodeGroup
}
class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) {
val values: PtNodeGroup
get() = children[0] as PtNodeGroup
val statements: PtNodeGroup
get() = children[1] as PtNodeGroup
}
class PtDefer(position: Position): PtNode(position), IPtStatementContainer

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

@ -1,7 +1,7 @@
package prog8.code.core package prog8.code.core
class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean) class ReturnConvention(val dt: BaseDataType?, val reg: RegisterOrPair?)
class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean) class ParamConvention(val dt: BaseDataType, val reg: RegisterOrPair?, val variable: Boolean)
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) { class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
override fun toString(): String { override fun toString(): String {
val paramConvs = params.mapIndexed { index, it -> val paramConvs = params.mapIndexed { index, it ->
@ -13,35 +13,41 @@ class CallConvention(val params: List<ParamConvention>, val returns: ReturnConve
} }
val returnConv = val returnConv =
when { when {
returns.reg == RegisterOrPair.FAC1 -> "floatFAC1"
returns.reg != null -> returns.reg.toString() returns.reg != null -> returns.reg.toString()
returns.floatFac1 -> "floatFAC1"
else -> "<no returnvalue>" else -> "<no returnvalue>"
} }
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]" return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
} }
} }
class FParam(val name: String, val possibleDatatypes: Array<DataType>) class FParam(val name: String, vararg val possibleDatatypes: BaseDataType)
private val IterableDatatypes = arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
private val IntegerDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
private val NumericDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
class FSignature(val pure: Boolean, // does it have side effects? class FSignature(val pure: Boolean, // does it have side effects?
val parameters: List<FParam>, val returnType: BaseDataType?,
val returnType: DataType?) { vararg val parameters: FParam) {
fun callConvention(actualParamTypes: List<DataType>): CallConvention { fun callConvention(actualParamTypes: List<BaseDataType>): CallConvention {
val returns: ReturnConvention = when (returnType) { val returns: ReturnConvention = when (returnType) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false) BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A)
DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false) BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY)
DataType.FLOAT -> ReturnConvention(returnType, null, true) BaseDataType.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1)
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false) in IterableDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY)
null -> ReturnConvention(null, null, false) null -> ReturnConvention(null, null)
else -> { else -> {
// return type depends on arg type // return type depends on arg type
when (val paramType = actualParamTypes.first()) { when (val paramType = actualParamTypes.first()) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false) BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A)
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false) BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY)
DataType.FLOAT -> ReturnConvention(paramType, null, true) BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false) in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
else -> ReturnConvention(paramType, null, false) else -> ReturnConvention(paramType, null)
} }
} }
} }
@ -52,16 +58,24 @@ class FSignature(val pure: Boolean, // does it have side effects?
// One parameter goes via register/registerpair. // One parameter goes via register/registerpair.
// this avoids repeated code for every caller to store the value in the subroutine's argument variable. // this avoids repeated code for every caller to store the value in the subroutine's argument variable.
// (that store is still done, but only coded once at the start at the subroutine itself rather than at every call site). // (that store is still done, but only coded once at the start at the subroutine itself rather than at every call site).
// TODO can we start using the X register as well for a third byte or word+byte combo
val paramConv = when(val paramType = actualParamTypes[0]) { val paramConv = when(val paramType = actualParamTypes[0]) {
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false) BaseDataType.UBYTE, BaseDataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false) BaseDataType.UWORD, BaseDataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) // TODO is this correct? shouldn't it be FAC1? BaseDataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) // NOTE: for builtin functions, floating point arguments are passed by reference (so you get a pointer in AY)
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false) in IterableDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
else -> ParamConvention(paramType, null, false) else -> ParamConvention(paramType, null, false)
} }
CallConvention(listOf(paramConv), returns) CallConvention(listOf(paramConv), returns)
} }
actualParamTypes.size==2 && (actualParamTypes[0].isByte && actualParamTypes[1].isWord) -> {
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
}
actualParamTypes.size==2 && (actualParamTypes[0].isWord && actualParamTypes[1].isByte) -> {
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
}
actualParamTypes.size==3 && actualParamTypes.all { it.isByte } -> {
TODO("opportunity to pass 3 byte arguments in A,Y and X registers but not implemented yet")
}
else -> { else -> {
// multiple parameters go via variables // multiple parameters go via variables
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) } val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
@ -72,66 +86,63 @@ class FSignature(val pure: Boolean, // does it have side effects?
} }
val BuiltinFunctions: Map<String, FSignature> = mapOf( val BuiltinFunctions: Map<String, FSignature> = mapOf(
// this set of function have no return value and operate in-place: "setlsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"setlsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null), "setmsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"setmsb" to FSignature(false, listOf(FParam("variable", arrayOf(DataType.WORD, DataType.UWORD)), FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), null), "rol" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), "ror" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), "rol2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), "ror2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"ror2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null), "cmp" to FSignature(false, null, FParam("value1", *IntegerDatatypes), FParam("value2", *NumericDatatypes)), // cmp returns a status in the carry flag, but not a proper return value
// cmp returns a status in the carry flag, but not a proper return value "prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null), "prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"prog8_lib_stringcompare" to FSignature(true, listOf(FParam("str1", arrayOf(DataType.STR)), FParam("str2", arrayOf(DataType.STR))), DataType.BYTE), "prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
"prog8_lib_square_byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE, DataType.UBYTE))), DataType.UBYTE), "abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"prog8_lib_square_word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD, DataType.UWORD))), DataType.UWORD), "abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
"prog8_ifelse_bittest_set" to FSignature(true, listOf(FParam("variable", ByteDatatypes), FParam("bitnumber", arrayOf(DataType.UBYTE))), DataType.BOOL), "abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
"prog8_ifelse_bittest_notset" to FSignature(true, listOf(FParam("variable", ByteDatatypes), FParam("bitnumber", arrayOf(DataType.UBYTE))), DataType.BOOL), "abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null), "len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
"abs__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE))), DataType.BYTE), "sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())),
"abs__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD))), DataType.WORD), "sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
"abs__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT), "sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD), "sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
// normal functions follow: "sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)),
"sizeof" to FSignature(true, listOf(FParam("object", DataType.entries.toTypedArray())), DataType.UBYTE), "sqrt__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE), "divmod" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("divisor", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("quotient", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("remainder", BaseDataType.UBYTE, BaseDataType.UWORD)),
"sqrt" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null), "divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)),
"sqrt__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE), "divmod__uword" to FSignature(false, null, FParam("dividend", BaseDataType.UWORD), FParam("divisor", BaseDataType.UWORD), FParam("quotient", BaseDataType.UWORD), FParam("remainder", BaseDataType.UWORD)),
"sqrt__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE), "lsb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"sqrt__float" to FSignature(true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT), "lsw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"divmod" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("divisor", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("quotient", arrayOf(DataType.UBYTE, DataType.UWORD)), FParam("remainder", arrayOf(DataType.UBYTE, DataType.UWORD))), null), "msb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"divmod__ubyte" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UBYTE)), FParam("divisor", arrayOf(DataType.UBYTE)), FParam("quotient", arrayOf(DataType.UBYTE)), FParam("remainder", arrayOf(DataType.UBYTE))), null), "msw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"divmod__uword" to FSignature(false, listOf(FParam("dividend", arrayOf(DataType.UWORD)), FParam("divisor", arrayOf(DataType.UWORD)), FParam("quotient", arrayOf(DataType.UWORD)), FParam("remainder", arrayOf(DataType.UWORD))), null), "mkword" to FSignature(true, BaseDataType.UWORD, FParam("msb", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), "clamp" to FSignature(true, null, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE), "clamp__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD), "clamp__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE), FParam("minimum", BaseDataType.UBYTE), FParam("maximum", BaseDataType.UBYTE)),
"clamp" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), null), "clamp__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD), FParam("minimum", BaseDataType.WORD), FParam("maximum", BaseDataType.WORD)),
"clamp__byte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.BYTE)), FParam("minimum", arrayOf(DataType.BYTE)), FParam("maximum", arrayOf(DataType.BYTE))), DataType.BYTE), "clamp__uword" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD), FParam("minimum", BaseDataType.UWORD), FParam("maximum", BaseDataType.UWORD)),
"clamp__ubyte" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UBYTE)), FParam("minimum", arrayOf(DataType.UBYTE)), FParam("maximum", arrayOf(DataType.UBYTE))), DataType.UBYTE), "min" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"clamp__word" to FSignature(true, listOf(FParam("value", arrayOf(DataType.WORD)), FParam("minimum", arrayOf(DataType.WORD)), FParam("maximum", arrayOf(DataType.WORD))), DataType.WORD), "min__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"clamp__uword" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD)), FParam("minimum", arrayOf(DataType.UWORD)), FParam("maximum", arrayOf(DataType.UWORD))), DataType.UWORD), "min__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
"min" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null), "min__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
"min__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE), "min__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
"min__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE), "max" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"min__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD), "max__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"min__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD), "max__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
"max" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), null), "max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
"max__byte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.BYTE)), FParam("val2", arrayOf(DataType.BYTE))), DataType.BYTE), "max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
"max__ubyte" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UBYTE)), FParam("val2", arrayOf(DataType.UBYTE))), DataType.UBYTE), "peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
"max__word" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.WORD)), FParam("val2", arrayOf(DataType.WORD))), DataType.WORD), "peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
"max__uword" to FSignature(true, listOf(FParam("val1", arrayOf(DataType.UWORD)), FParam("val2", arrayOf(DataType.UWORD))), DataType.UWORD), "peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE), "poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD), "pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
"peekf" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.FLOAT), "pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null), "pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
"pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null), "rsave" to FSignature(false, null),
"pokef" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.FLOAT))), null), "rrestore" to FSignature(false, null),
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), DataType.UBYTE), "memory" to FSignature(true, BaseDataType.UWORD, FParam("name", BaseDataType.STR), FParam("size", BaseDataType.UWORD), FParam("alignment", BaseDataType.UWORD)),
"rsave" to FSignature(false, emptyList(), null), "callfar" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("arg", BaseDataType.UWORD)),
"rrestore" to FSignature(false, emptyList(), null), "callfar2" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("argA", BaseDataType.UBYTE), FParam("argX", BaseDataType.UBYTE), FParam("argY", BaseDataType.UBYTE), FParam("argC", BaseDataType.BOOL)),
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD), "call" to FSignature(false, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD),
"callfar2" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("argA", arrayOf(DataType.UBYTE)), FParam("argX", arrayOf(DataType.UBYTE)), FParam("argY", arrayOf(DataType.UBYTE)), FParam("argC", arrayOf(DataType.BOOL))), DataType.UWORD),
"call" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
) )
val InplaceModifyingBuiltinFunctions = setOf( val InplaceModifyingBuiltinFunctions = setOf(

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,
@ -27,14 +28,15 @@ class CompilationOptions(val output: OutputType,
var varsGolden: Boolean = false, var varsGolden: Boolean = false,
var slabsHighBank: Int? = null, var slabsHighBank: Int? = null,
var slabsGolden: Boolean = false, var slabsGolden: Boolean = false,
var splitWordArrays: Boolean = false, var dontSplitWordArrays: Boolean = false,
var addMissingRts: Boolean = false, // deprecated, will likely go way in future version
var breakpointCpuInstruction: String? = null, var breakpointCpuInstruction: String? = null,
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 {
compTarget.machine.initializeMemoryAreas(this) compTarget.initializeMemoryAreas(this)
} }
companion object { companion object {

View File

@ -11,6 +11,7 @@ fun Number.toHex(): String {
// 0..15 -> "0".."15" // 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff" // 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff" // 256..65536 -> "$0100".."$ffff"
// larger -> "$12345678"
// negative values are prefixed with '-'. // negative values are prefixed with '-'.
val integer = this.toInt() val integer = this.toInt()
if(integer<0) if(integer<0)
@ -19,7 +20,7 @@ fun Number.toHex(): String {
in 0 until 16 -> integer.toString() in 0 until 16 -> integer.toString()
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0') in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0') in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
else -> throw IllegalArgumentException("number too large for 16 bits $this") else -> "$"+integer.toString(16).padStart(8,'0')
} }
} }
@ -27,11 +28,12 @@ fun UInt.toHex(): String {
// 0..15 -> "0".."15" // 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff" // 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff" // 256..65536 -> "$0100".."$ffff"
// larger -> "$12345678"
return when (this) { return when (this) {
in 0u until 16u -> this.toString() in 0u until 16u -> this.toString()
in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0') in 0u until 0x100u -> "$"+this.toString(16).padStart(2,'0')
in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0') in 0u until 0x10000u -> "$"+this.toString(16).padStart(4,'0')
else -> throw IllegalArgumentException("number too large for 16 bits $this") else -> "$"+this.toString(16).padStart(8,'0')
} }
} }

View File

@ -1,63 +1,380 @@
package prog8.code.core package prog8.code.core
enum class DataType { import java.util.*
enum class BaseDataType {
UBYTE, // pass by value 8 bits unsigned UBYTE, // pass by value 8 bits unsigned
BYTE, // pass by value 8 bits signed BYTE, // pass by value 8 bits signed
UWORD, // pass by value 16 bits unsigned UWORD, // pass by value 16 bits unsigned
WORD, // pass by value 16 bits signed WORD, // pass by value 16 bits signed
LONG, // pass by value 32 bits signed LONG, // pass by value 32 bits signed
FLOAT, // pass by value machine dependent FLOAT, // pass by value machine dependent
BOOL, // pass by value bit 0 of a 8 bit byte BOOL, // pass by value bit 0 of an 8-bit byte
STR, // pass by reference STR, // pass by reference
ARRAY_UB, // pass by reference ARRAY, // pass by reference, subtype is the element type
ARRAY_B, // pass by reference ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
ARRAY_UW, // pass by reference POINTER, // typed pointer, subtype is whatever type is pointed to
ARRAY_UW_SPLIT, // pass by reference, lo/hi byte split STRUCT_INSTANCE, // the actual instance of a struct (not directly supported in the language yet, but we need its type)
ARRAY_W, // pass by reference ARRAY_POINTER, // array of pointers (uwords), subtype is whatever type each element points to
ARRAY_W_SPLIT, // pass by reference, lo/hi byte split
ARRAY_F, // pass by reference
ARRAY_BOOL, // pass by reference
UNDEFINED; UNDEFINED;
/**
* is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
*/
infix fun isAssignableTo(targetType: DataType) =
when(this) {
BOOL -> targetType == BOOL
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT)
BYTE -> targetType.oneOf(BYTE, WORD, LONG, FLOAT)
UWORD -> targetType.oneOf(UWORD, LONG, FLOAT)
WORD -> targetType.oneOf(WORD, LONG, FLOAT)
LONG -> targetType.oneOf(LONG, FLOAT)
FLOAT -> targetType.oneOf(FLOAT)
STR -> targetType.oneOf(STR, UWORD)
in ArrayDatatypes -> targetType == this
else -> false
}
fun oneOf(vararg types: DataType) = this in types fun largerSizeThan(other: BaseDataType) =
infix fun largerThan(other: DataType) =
when { when {
this == other -> false this == other -> false
this in ByteDatatypesWithBoolean -> false this.isByteOrBool -> false
this in WordDatatypes -> other in ByteDatatypesWithBoolean this.isWord -> other.isByteOrBool
this == LONG -> other in ByteDatatypesWithBoolean+WordDatatypes this == LONG -> other.isByteOrBool || other.isWord
this == STR && other == UWORD || this == UWORD && other == STR -> false this == STR && other == UWORD || this == UWORD && other == STR -> false
this.isArray && other.isArray -> false
this.isArray -> other != FLOAT
this == STR -> other != FLOAT
this.isPointer -> other.isByteOrBool
else -> true else -> true
} }
infix fun equalsSize(other: DataType) = fun equalsSize(other: BaseDataType) =
when { when {
this == other -> true this == other -> true
this in ByteDatatypesWithBoolean -> other in ByteDatatypesWithBoolean this.isArray && other.isArray -> true
this in WordDatatypes -> other in WordDatatypes this.isByteOrBool -> other.isByteOrBool
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.isArray && other == STR -> true
else -> false else -> false
} }
} }
val BaseDataType.isByte get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
val BaseDataType.isByteOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL)
val BaseDataType.isWord get() = this in arrayOf(BaseDataType.UWORD, BaseDataType.WORD)
val BaseDataType.isInteger get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.BOOL)
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
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.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER
val BaseDataType.isPointer get() = this == BaseDataType.POINTER
val BaseDataType.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE
val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER
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
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 {
when {
base.isPointerArray -> {
require(sub!=null || subType!=null || subTypeFromAntlr!=null)
}
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"
}
}
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is DataType) return false
return base == other.base && sub == other.sub && (subType==other.subType || subType!!.sameas(other.subType!!))
}
override fun hashCode(): Int = Objects.hash(base, sub, subType)
fun setActualSubType(actualSubType: ISubType) {
subType = actualSubType
subTypeFromAntlr = null
}
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(
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null, null),
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null, null),
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null, null),
BaseDataType.WORD to DataType(BaseDataType.WORD, null, null),
BaseDataType.LONG to DataType(BaseDataType.LONG, null, null),
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null, null),
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null, null),
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE, null),
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null, null)
)
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 {
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
return if(splitwordarray && actualElementDt.isWord)
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null)
else {
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
DataType(BaseDataType.ARRAY, actualElementDt, null)
else
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 {
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, splitwords)
else arrayFor(base, false)
}
fun elementType(): DataType =
when {
isPointerArray -> DataType(BaseDataType.POINTER, sub, subType)
base.isArray || base==BaseDataType.STR -> forDt(sub!!)
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) {
BaseDataType.ARRAY -> {
when(sub) {
BaseDataType.BOOL -> "bool[]"
BaseDataType.FLOAT -> "float[]"
BaseDataType.BYTE -> "byte[]"
BaseDataType.WORD -> "word[]"
BaseDataType.UBYTE -> "ubyte[]"
BaseDataType.UWORD -> "uword[]"
else -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.ARRAY_SPLITW -> {
when(sub) {
BaseDataType.WORD -> "word[] (split)"
BaseDataType.UWORD -> "uword[] (split)"
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()
}
fun sourceString(): String = when (base) {
BaseDataType.BOOL -> "bool"
BaseDataType.UBYTE -> "ubyte"
BaseDataType.BYTE -> "byte"
BaseDataType.UWORD -> "uword"
BaseDataType.WORD -> "word"
BaseDataType.LONG -> "long"
BaseDataType.FLOAT -> "float"
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 -> {
when(sub) {
BaseDataType.UBYTE -> "ubyte["
BaseDataType.UWORD -> "@nosplit uword["
BaseDataType.BOOL -> "bool["
BaseDataType.BYTE -> "byte["
BaseDataType.WORD -> "@nosplit word["
BaseDataType.FLOAT -> "float["
else -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.ARRAY_SPLITW -> {
when(sub) {
BaseDataType.UWORD -> "uword["
BaseDataType.WORD -> "word["
else -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.UNDEFINED -> throw IllegalArgumentException("wrong dt")
}
// is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
infix fun isAssignableTo(targetType: DataType) =
when(base) {
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
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.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.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
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.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
}
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(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 isByte = base.isByte
val isUnsignedByte = base == BaseDataType.UBYTE
val isSignedByte = base == BaseDataType.BYTE
val isByteOrBool = base.isByteOrBool
val isWord = base.isWord
val isUnsignedWord = base == BaseDataType.UWORD
val isSignedWord = base == BaseDataType.WORD
val isInteger = base.isInteger
val isIntegerOrBool = base.isIntegerOrBool
val isNumeric = base.isNumeric
val isNumericOrBool = base.isNumericOrBool
val isSigned = base.isSigned
val isUnsigned = !base.isSigned
val isArray = base.isArray
val isPointer = base.isPointer
val isStructInstance = base.isStructInstance
val isPointerArray = base.isPointerArray
val isBoolArray = base.isArray && sub == BaseDataType.BOOL
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
val isSignedByteArray = base.isArray && sub == BaseDataType.BYTE
val isWordArray = base.isArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
val isUnsignedWordArray = base.isArray && sub == BaseDataType.UWORD
val isSignedWordArray = base.isArray && sub == BaseDataType.WORD
val isFloatArray = base.isArray && sub == BaseDataType.FLOAT
val isString = base == BaseDataType.STR
val isBool = base == BaseDataType.BOOL
val isFloat = base == BaseDataType.FLOAT
val isLong = base == BaseDataType.LONG
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE))
val isSplitWordArray = base.isSplitWordArray
val isSplitUnsignedWordArray = base.isSplitWordArray && sub == BaseDataType.UWORD
val isSplitSignedWordArray = base.isSplitWordArray && sub == BaseDataType.WORD
val isIterable = base.isIterable
val isPassByRef = base.isPassByRef
val isPassByValue = base.isPassByValue
}
enum class CpuRegister { enum class CpuRegister {
A, A,
X, X,
@ -95,6 +412,17 @@ enum class RegisterOrPair {
else -> throw IllegalArgumentException("no cpu hardware register for $this") else -> throw IllegalArgumentException("no cpu hardware register for $this")
} }
fun asScopedNameVirtualReg(type: DataType?): List<String> {
require(this in Cx16VirtualRegisters)
val suffix = when(type?.base) {
BaseDataType.UBYTE, BaseDataType.BOOL -> "L"
BaseDataType.BYTE -> "sL"
BaseDataType.WORD -> "s"
BaseDataType.UWORD, null -> ""
else -> throw IllegalArgumentException("invalid register param type")
}
return listOf("cx16", name.lowercase()+suffix)
}
} // only used in parameter and return value specs in asm subroutines } // only used in parameter and return value specs in asm subroutines
enum class Statusflag { enum class Statusflag {
@ -123,48 +451,6 @@ enum class BranchCondition {
VC VC
} }
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
val ByteDatatypesWithBoolean = ByteDatatypes + DataType.BOOL
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG)
val IntegerDatatypesWithBoolean = IntegerDatatypes + DataType.BOOL
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT)
val NumericDatatypesWithBoolean = NumericDatatypes + DataType.BOOL
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.LONG, DataType.FLOAT)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL)
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT)
val IterableDatatypes = arrayOf(
DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
DataType.ARRAY_F, DataType.ARRAY_BOOL
)
val PassByValueDatatypes = NumericDatatypesWithBoolean
val PassByReferenceDatatypes = IterableDatatypes
val ArrayToElementTypes = mapOf(
DataType.STR to DataType.UBYTE,
DataType.ARRAY_B to DataType.BYTE,
DataType.ARRAY_UB to DataType.UBYTE,
DataType.ARRAY_W to DataType.WORD,
DataType.ARRAY_UW to DataType.UWORD,
DataType.ARRAY_W_SPLIT to DataType.WORD,
DataType.ARRAY_UW_SPLIT to DataType.UWORD,
DataType.ARRAY_F to DataType.FLOAT,
DataType.ARRAY_BOOL to DataType.BOOL
)
val ElementToArrayTypes = mapOf(
DataType.BYTE to DataType.ARRAY_B,
DataType.UBYTE to DataType.ARRAY_UB,
DataType.WORD to DataType.ARRAY_W,
DataType.UWORD to DataType.ARRAY_UW,
DataType.FLOAT to DataType.ARRAY_F,
DataType.BOOL to DataType.ARRAY_BOOL,
DataType.STR to DataType.ARRAY_UW // array of str is just an array of pointers
)
val Cx16VirtualRegisters = arrayOf( val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3, RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7, RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
@ -172,7 +458,7 @@ val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15 RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
) )
val CpuRegisters = setOf( val CpuRegisters = arrayOf(
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y, RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY
) )
@ -181,7 +467,8 @@ val CpuRegisters = setOf(
enum class OutputType { enum class OutputType {
RAW, RAW,
PRG, PRG,
XEX XEX,
LIBRARY
} }
enum class CbmPrgLauncherType { enum class CbmPrgLauncherType {
@ -203,3 +490,9 @@ enum class ZeropageWish {
DONTCARE, DONTCARE,
NOT_IN_ZEROPAGE NOT_IN_ZEROPAGE
} }
enum class SplitWish {
DONTCARE,
SPLIT,
NOSPLIT
}

View File

@ -1,8 +1,43 @@
package prog8.code.core package prog8.code.core
import java.nio.file.Path
enum class CpuType {
CPU6502,
CPU65C02,
VIRTUAL
}
interface ICompilationTarget: IStringEncoding, IMemSizer { interface ICompilationTarget: IStringEncoding, IMemSizer {
val name: String val name: String
val machine: IMachineDefinition
val FLOAT_MAX_NEGATIVE: Double
val FLOAT_MAX_POSITIVE: Double
val FLOAT_MEM_SIZE: Int
val STARTUP_CODE_RESERVED_SIZE: UInt // this is here, so that certain compiler targets are able to tune this
val PROGRAM_LOAD_ADDRESS : UInt
val PROGRAM_MEMTOP_ADDRESS: UInt
val BSSHIGHRAM_START: UInt
val BSSHIGHRAM_END: UInt
val BSSGOLDENRAM_START: UInt
val BSSGOLDENRAM_END: UInt
val cpu: CpuType
var zeropage: Zeropage
var golden: GoldenRam
val libraryPath: Path?
val customLauncher: List<String>
val additionalAssemblerOptions: String?
val defaultOutputType: OutputType
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
fun getFloatAsmBytes(num: Number): String
fun convertFloatToBytes(num: Double): List<UByte>
fun convertBytesToFloat(bytes: List<UByte>): Double
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean)
fun isIOAddress(address: UInt): Boolean
override fun encodeString(str: String, encoding: Encoding): List<UByte> override fun encodeString(str: String, encoding: Encoding): List<UByte>
override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String override fun decodeString(bytes: Iterable<UByte>, encoding: Encoding): String

View File

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

View File

@ -1,36 +0,0 @@
package prog8.code.core
import java.nio.file.Path
enum class CpuType {
CPU6502,
CPU65c02,
VIRTUAL
}
interface IMachineDefinition {
val FLOAT_MAX_NEGATIVE: Double
val FLOAT_MAX_POSITIVE: Double
val FLOAT_MEM_SIZE: Int
val STARTUP_CODE_RESERVED_SIZE: UInt // this is here, so that certain compiler targets are able to tune this
val PROGRAM_LOAD_ADDRESS : UInt
val PROGRAM_MEMTOP_ADDRESS: UInt
val BSSHIGHRAM_START: UInt
val BSSHIGHRAM_END: UInt
val BSSGOLDENRAM_START: UInt
val BSSGOLDENRAM_END: UInt
val cpu: CpuType
var zeropage: Zeropage
var golden: GoldenRam
fun initializeMemoryAreas(compilerOptions: CompilationOptions)
fun getFloatAsmBytes(num: Number): String
fun convertFloatToBytes(num: Double): List<UByte>
fun convertBytesToFloat(bytes: List<UByte>): Double
fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path)
fun isIOAddress(address: UInt): Boolean
}

View File

@ -1,6 +1,15 @@
package prog8.code.core package prog8.code.core
interface IMemSizer { interface IMemSizer {
fun memorySize(dt: DataType): Int fun memorySize(dt: DataType, numElements: Int?): Int
fun memorySize(arrayDt: DataType, numElements: Int): Int
fun memorySize(dt: BaseDataType): Int {
if(dt.isPassByRef)
return memorySize(DataType.UWORD, null) // a pointer size
try {
return memorySize(DataType.forDt(dt), null)
} catch (x: NoSuchElementException) {
throw IllegalArgumentException(x.message)
}
}
} }

View File

@ -9,7 +9,8 @@ enum class Encoding(val prefix: String) {
ISO5("iso5"), // cx16 (iso-8859-5, cyrillic) ISO5("iso5"), // cx16 (iso-8859-5, cyrillic)
ISO16("iso16"), // cx16 (iso-8859-16, eastern european) ISO16("iso16"), // cx16 (iso-8859-16, eastern european)
CP437("cp437"), // cx16 (ibm pc, codepage 437) CP437("cp437"), // cx16 (ibm pc, codepage 437)
KATAKANA("kata") // cx16 (katakana) KATAKANA("kata"), // cx16 (katakana)
C64OS("c64os") // c64 (C64 OS)
} }
interface IStringEncoding { interface IStringEncoding {

View File

@ -22,7 +22,7 @@ abstract class MemoryAllocator(protected val options: CompilationOptions) {
abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) { abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
abstract val SCRATCH_B1 : UInt // temp storage for a single byte abstract val SCRATCH_B1 : UInt // temp storage for a single byte
abstract val SCRATCH_REG : UInt // temp storage for a register, must be B1+1 abstract val SCRATCH_REG : UInt // temp storage for a register byte, must be B1+1
abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc abstract val SCRATCH_W1 : UInt // temp storage 1 for a word $fb+$fc
abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc abstract val SCRATCH_W2 : UInt // temp storage 2 for a word $fb+$fc
@ -38,7 +38,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
for (reserved in options.zpReserved) for (reserved in options.zpReserved)
reserve(reserved) reserve(reserved)
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u)) free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1u, SCRATCH_W2, SCRATCH_W2 + 1u))
} }
} }
@ -70,9 +70,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
return Err(MemAllocationError("zero page usage has been disabled")) return Err(MemAllocationError("zero page usage has been disabled"))
val size: Int = val size: Int =
when (datatype) { when {
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype) datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
DataType.STR, in ArrayDatatypes -> { datatype.isString || datatype.isArray -> {
val memsize = options.compTarget.memorySize(datatype, numElements!!) val memsize = options.compTarget.memorySize(datatype, numElements!!)
if(position!=null) if(position!=null)
errors.warn("allocating a large value in zeropage; str/array $memsize bytes", position) errors.warn("allocating a large value in zeropage; str/array $memsize bytes", position)
@ -80,9 +80,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
errors.warn("$name: allocating a large value in zeropage; str/array $memsize bytes", Position.DUMMY) errors.warn("$name: allocating a large value in zeropage; str/array $memsize bytes", Position.DUMMY)
memsize memsize
} }
DataType.FLOAT -> { datatype.isFloat -> {
if (options.floats) { if (options.floats) {
val memsize = options.compTarget.memorySize(DataType.FLOAT) 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
@ -94,7 +94,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
} }
synchronized(this) { synchronized(this) {
if(free.size > 0) { if(free.isNotEmpty()) {
if(size==1) { if(size==1) {
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) { for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
if(oneSeparateByteFree(candidate)) if(oneSeparateByteFree(candidate))
@ -118,10 +118,10 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
require(size>=0) require(size>=0)
free.removeAll(address until address+size.toUInt()) free.removeAll(address until address+size.toUInt())
if(name.isNotEmpty()) { if(name.isNotEmpty()) {
allocatedVariables[name] = when(datatype) { allocatedVariables[name] = when {
in NumericDatatypes, DataType.BOOL -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments datatype.isNumericOrBool -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
DataType.STR -> VarAllocation(address, datatype, size) datatype.isString -> VarAllocation(address, datatype, size)
in ArrayDatatypes -> VarAllocation(address, datatype, size) datatype.isArray -> VarAllocation(address, datatype, size)
else -> throw AssemblyError("invalid dt") else -> throw AssemblyError("invalid dt")
} }
} }
@ -133,8 +133,6 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
require(size>0) require(size>0)
return free.containsAll((address until address+size.toUInt()).toList()) return free.containsAll((address until address+size.toUInt()).toList())
} }
abstract fun allocateCx16VirtualRegisters()
} }
@ -150,14 +148,13 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> { errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
val size: Int = val size: Int =
when (datatype) { when {
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype) datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
DataType.STR, in ArrayDatatypes -> { datatype.isString -> numElements!!
options.compTarget.memorySize(datatype, numElements!!) datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
} datatype.isFloat -> {
DataType.FLOAT -> {
if (options.floats) { if (options.floats) {
options.compTarget.memorySize(DataType.FLOAT) 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,10 +1,10 @@
package prog8.code.core package prog8.code.core
val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are no longer associative because of Shortcircuit/McCarthy evaluation val AssociativeOperators = arrayOf("+", "*", "&", "|", "^", "==", "!=", "xor") // note: and,or are not associative because of Shortcircuit/McCarthy evaluation
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=") val ComparisonOperators = arrayOf("==", "!=", "<", ">", "<=", ">=")
val LogicalOperators = setOf("and", "or", "xor", "not", "in") val LogicalOperators = arrayOf("and", "or", "xor", "not", "in")
val BitwiseOperators = setOf("&", "|", "^", "~") val BitwiseOperators = arrayOf("&", "|", "^", "~")
val PrefixOperators = setOf("+", "-", "~", "not") val PrefixOperators = arrayOf("+", "-", "~", "not")
fun invertedComparisonOperator(operator: String) = fun invertedComparisonOperator(operator: String) =
when (operator) { when (operator) {

View File

@ -1,21 +1,21 @@
package prog8.code.core package prog8.code.core
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX import prog8.code.sanitize
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}]"
fun toClickableStr(): String { fun toClickableStr(): String {
if(this===DUMMY) if(this===DUMMY)
return "" return ""
if(file.startsWith(LIBRARYFILEPREFIX)) 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(x: 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 ':'
"file://$file:$line:$startCol:" "file://$file:$line:$startCol:"
} }

View File

@ -0,0 +1,74 @@
package prog8.code.source
import prog8.code.core.Position
import prog8.code.sanitize
import java.nio.file.Path
import java.util.*
import kotlin.io.path.Path
// Resource caching "filesystem".
// Note that it leaves the decision to load a resource or an actual disk file to the caller.
object ImportFileSystem {
fun expandTilde(path: String): String = if (path.startsWith("~")) {
val userHome = System.getProperty("user.home")
userHome + path.drop(1)
} else {
path
}
fun expandTilde(path: Path): Path = Path(expandTilde(path.toString()))
fun getFile(path: Path, isLibrary: Boolean=false): SourceCode {
val normalized = path.sanitize()
val cached = cache[normalized.toString()]
if (cached != null)
return cached
val file = SourceCode.File(normalized, isLibrary)
cache[normalized.toString()] = file
return file
}
fun getResource(name: String): SourceCode {
val cached = cache[name]
if (cached != null) return cached
val resource = SourceCode.Resource(name)
cache[name] = resource
return resource
}
fun retrieveSourceLine(position: Position): String {
if(SourceCode.isLibraryResource(position.file)) {
val cached = cache[SourceCode.withoutPrefix(position.file)]
if(cached != null)
return getLine(cached, position.line)
}
val cached = cache[position.file]
if(cached != null)
return getLine(cached, position.line)
val path = Path(position.file).sanitize()
val cached2 = cache[path.toString()]
if(cached2 != null)
return getLine(cached2, position.line)
throw NoSuchElementException("cannot get source line $position, with path $path")
}
private fun getLine(code: SourceCode, lineIndex: Int): String {
var spans = lineSpanCache[code]
if(spans==null) {
val lineSpans = Regex("^", RegexOption.MULTILINE).findAll(code.text).map { it.range.first }
val ends = lineSpans.drop(1) + code.text.length
spans = lineSpans.zip(ends).map { (start, end) -> LineSpan(start, end) }.toList().toTypedArray()
lineSpanCache[code] = spans
}
val span = spans[lineIndex - 1]
return code.text.substring(span.start, span.end).trim()
}
private class LineSpan(val start: Int, val end: Int)
private val cache = TreeMap<String, SourceCode>(String.CASE_INSENSITIVE_ORDER)
private val lineSpanCache = mutableMapOf<SourceCode, Array<LineSpan>>()
}

View File

@ -1,16 +1,13 @@
package prog8.code.core package prog8.code.source
import java.io.File 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
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.io.path.absolute
import kotlin.io.path.readText import kotlin.io.path.readText
const val internedStringsModuleName = "prog8_interned_strings"
/** /**
* Encapsulates - and ties together - actual source code (=text) and its [origin]. * Encapsulates - and ties together - actual source code (=text) and its [origin].
*/ */
@ -26,6 +23,11 @@ sealed class SourceCode {
*/ */
abstract val isFromFilesystem: Boolean abstract val isFromFilesystem: Boolean
/**
* Whether this [SourceCode] instance was created from a library module file
*/
abstract val isFromLibrary: Boolean
/** /**
* The logical name of the source code unit. Usually the module's name. * The logical name of the source code unit. Usually the module's name.
*/ */
@ -55,14 +57,21 @@ sealed class SourceCode {
/** /**
* filename prefix to designate library files that will be retreived from internal resources rather than disk * filename prefix to designate library files that will be retreived from internal resources rather than disk
*/ */
const val LIBRARYFILEPREFIX = "library:" private const val LIBRARYFILEPREFIX = "library:"
const val STRINGSOURCEPREFIX = "string:" private const val STRINGSOURCEPREFIX = "string:"
val curdir: Path = Path(".").toAbsolutePath() val curdir: Path = Path(".").absolute()
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath()) fun relative(path: Path): Path = curdir.relativize(path.sanitize())
fun isRegularFilesystemPath(pathString: String) = fun isRegularFilesystemPath(pathString: String) = !isLibraryResource(pathString) && !isStringResource(pathString)
!(pathString.startsWith(LIBRARYFILEPREFIX) || pathString.startsWith(STRINGSOURCEPREFIX))
fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX) fun isLibraryResource(path: String) = path.startsWith(LIBRARYFILEPREFIX)
fun isStringResource(path: String) = path.startsWith(STRINGSOURCEPREFIX)
fun withoutPrefix(path: String): String {
return if(isLibraryResource(path))
path.removePrefix(LIBRARYFILEPREFIX)
else if(isStringResource(path))
path.removePrefix(STRINGSOURCEPREFIX)
else
path
}
} }
/** /**
@ -73,6 +82,7 @@ sealed class SourceCode {
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
override val isFromResources = false override val isFromResources = false
override val isFromFilesystem = false override val isFromFilesystem = false
override val isFromLibrary = false
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}" override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
override val name = "<unnamed-text>" override val name = "<unnamed-text>"
} }
@ -80,12 +90,13 @@ sealed class SourceCode {
/** /**
* Get [SourceCode] from the file represented by the specified Path. * Get [SourceCode] from the file represented by the specified Path.
* This immediately reads the file fully into memory. * This immediately reads the file fully into memory.
* You can only get an instance of this via the ImportFileSystem object.
* *
* [origin] will be the given path in absolute and normalized form. * [origin] will be the given path in absolute and normalized form.
* @throws NoSuchFileException if the file does not exist * @throws NoSuchFileException if the file does not exist
* @throws FileSystemException if the file cannot be read * @throws FileSystemException if the file cannot be read
*/ */
class File(path: Path): SourceCode() { internal class File(path: Path, override val isFromLibrary: Boolean): SourceCode() {
override val text: String override val text: String
override val origin: String override val origin: String
override val name: String override val name: String
@ -109,12 +120,14 @@ sealed class SourceCode {
/** /**
* [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8" * [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8"
* You can only get an instance of this via the ImportFileSystem object.
*/ */
class Resource(pathString: String): SourceCode() { internal class Resource(pathString: String): SourceCode() {
private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/") private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/")
override val isFromResources = true override val isFromResources = true
override val isFromFilesystem = false override val isFromFilesystem = false
override val isFromLibrary = true
override val origin = "$LIBRARYFILEPREFIX$normalized" override val origin = "$LIBRARYFILEPREFIX$normalized"
override val text: String override val text: String
override val name: String override val name: String
@ -124,7 +137,7 @@ sealed class SourceCode {
if (rscURL == null) { if (rscURL == null) {
val rscRoot = object {}.javaClass.getResource("/") val rscRoot = object {}.javaClass.getResource("/")
throw NoSuchFileException( throw NoSuchFileException(
File(normalized), java.io.File(normalized),
reason = "looked in resources rooted at $rscRoot" reason = "looked in resources rooted at $rscRoot"
) )
} }
@ -141,37 +154,8 @@ sealed class SourceCode {
class Generated(override val name: String) : SourceCode() { class Generated(override val name: String) : SourceCode() {
override val isFromResources: Boolean = false override val isFromResources: Boolean = false
override val isFromFilesystem: Boolean = false override val isFromFilesystem: Boolean = false
override val isFromLibrary: Boolean = false
override val origin: String = name override val origin: String = name
override val text: String = "<generated code node, no text representation>" override val text: String = "<generated code node, no text representation>"
} }
} }
object SourceLineCache {
private val cache = mutableMapOf<String, List<String>>()
private fun getCachedFile(file: String): List<String> {
val existing = cache[file]
if(existing!=null)
return existing
if (SourceCode.isRegularFilesystemPath(file)) {
val source = SourceCode.File(Path(file))
cache[file] = source.text.split('\n', '\r').map { it.trim() }
return cache.getValue(file)
} else if(file.startsWith(SourceCode.LIBRARYFILEPREFIX)) {
val source = SourceCode.Resource(file.drop(SourceCode.LIBRARYFILEPREFIX.length))
cache[file] = source.text.split('\n', '\r').map { it.trim()}
return cache.getValue(file)
}
return emptyList()
}
fun retrieveLine(position: Position): String? {
if (position.line>0) {
val lines = getCachedFile(position.file)
if(lines.isNotEmpty())
return lines[position.line-1]
}
return null
}
}

View File

@ -1,31 +0,0 @@
package prog8.code.target
import prog8.code.core.*
import prog8.code.target.atari.AtariMachineDefinition
class AtariTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override val name = NAME
override val machine = AtariMachineDefinition()
override val defaultEncoding = Encoding.ATASCII
companion object {
const val NAME = "atari"
}
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype")
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
}

View File

@ -1,19 +1,81 @@
package prog8.code.target package prog8.code.target
import prog8.code.core.Encoding import prog8.code.core.*
import prog8.code.core.ICompilationTarget import prog8.code.target.encodings.Encoder
import prog8.code.core.IMemSizer import prog8.code.target.zp.C128Zeropage
import prog8.code.core.IStringEncoding import java.nio.file.Path
import prog8.code.target.c128.C128MachineDefinition
import prog8.code.target.cbm.CbmMemorySizer
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { class C128Target: ICompilationTarget,
IStringEncoding by Encoder(true),
IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
override val name = NAME override val name = NAME
override val machine = C128MachineDefinition()
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII
override val libraryPath = null
override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object { companion object {
const val NAME = "c128" const val NAME = "c128"
} }
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
override val BSSHIGHRAM_START = 0u // TODO address?
override val BSSHIGHRAM_END = 0u // TODO address?
override val BSSGOLDENRAM_START = 0u // TODO address?
override val BSSGOLDENRAM_END = 0u // TODO address?
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
if(selectedEmulator!=1) {
System.err.println("The c128 target only supports the main emulator (Vice).")
return
}
if(!quiet)
println("\nStarting C-128 emulator x128...")
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline)
if(!quiet)
processb.inheritIO()
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = C128Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
}
} }

View File

@ -1,23 +1,95 @@
package prog8.code.target package prog8.code.target
import prog8.code.core.Encoding import prog8.code.core.*
import prog8.code.core.ICompilationTarget import prog8.code.target.encodings.Encoder
import prog8.code.core.IMemSizer import prog8.code.target.zp.C64Zeropage
import prog8.code.core.IStringEncoding import java.io.IOException
import prog8.code.target.c64.C64MachineDefinition import java.nio.file.Path
import prog8.code.target.cbm.CbmMemorySizer
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { 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 machine = C64MachineDefinition()
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII
override val libraryPath = null
override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object { companion object {
const val NAME = "c64" const val NAME = "c64"
fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list" fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
} }
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x0801u
override val PROGRAM_MEMTOP_ADDRESS = 0xcfe0u // $a000 if floats are used
// note that at $cfe0-$cfff are the 16 'virtual registers' R0-R15
override val BSSHIGHRAM_START = 0xc000u
override val BSSHIGHRAM_END = 0xcfdfu
override val BSSGOLDENRAM_START = 0u // no golden ram on C64
override val BSSGOLDENRAM_END = 0u
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
if(selectedEmulator!=1) {
System.err.println("The c64 target only supports the main emulator (Vice).")
return
}
for(emulator in listOf("x64sc", "x64")) {
if(!quiet)
println("\nStarting C-64 emulator $emulator...")
val viceMonlist = viceMonListName(programNameWithPath.toString())
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline)
if(!quiet)
processb.inheritIO()
val process: Process
try {
process=processb.start()
} catch(_: IOException) {
continue // try the next emulator executable
}
process.waitFor()
break
}
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = C64Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
}
} }
@ -26,8 +98,6 @@ val CompilationTargets = listOf(
C128Target.NAME, C128Target.NAME,
Cx16Target.NAME, Cx16Target.NAME,
PETTarget.NAME, PETTarget.NAME,
AtariTarget.NAME,
Neo6502Target.NAME,
VMTarget.NAME VMTarget.NAME
) )
@ -36,8 +106,6 @@ fun getCompilationTargetByName(name: String) = when(name.lowercase()) {
C128Target.NAME -> C128Target() C128Target.NAME -> C128Target()
Cx16Target.NAME -> Cx16Target() Cx16Target.NAME -> Cx16Target()
PETTarget.NAME -> PETTarget() PETTarget.NAME -> PETTarget()
AtariTarget.NAME -> AtariTarget()
VMTarget.NAME -> VMTarget() VMTarget.NAME -> VMTarget()
Neo6502Target.NAME -> Neo6502Target()
else -> throw IllegalArgumentException("invalid compilation target") else -> throw IllegalArgumentException("invalid compilation target")
} }

View File

@ -0,0 +1,173 @@
package prog8.code.target
import prog8.code.core.*
import prog8.code.source.ImportFileSystem.expandTilde
import prog8.code.target.encodings.Encoder
import prog8.code.target.zp.ConfigurableZeropage
import java.io.IOException
import java.nio.file.Path
import java.util.*
import kotlin.io.path.Path
import kotlin.io.path.inputStream
import kotlin.io.path.isDirectory
import kotlin.io.path.nameWithoutExtension
class ConfigFileTarget(
override val name: String,
override val defaultEncoding: Encoding,
override val cpu: CpuType,
override val PROGRAM_LOAD_ADDRESS: UInt,
override val PROGRAM_MEMTOP_ADDRESS: UInt,
override val STARTUP_CODE_RESERVED_SIZE: UInt,
override val BSSHIGHRAM_START: UInt,
override val BSSHIGHRAM_END: UInt,
override val BSSGOLDENRAM_START: UInt,
override val BSSGOLDENRAM_END: UInt,
override val defaultOutputType: OutputType,
override val libraryPath: Path,
override val customLauncher: List<String>,
override val additionalAssemblerOptions: String?,
val ioAddresses: List<UIntRange>,
val zpScratchB1: UInt,
val zpScratchReg: UInt,
val zpScratchW1: UInt,
val zpScratchW2: UInt,
val virtualregistersStart: UInt,
val zpFullsafe: List<UIntRange>,
val zpKernalsafe: List<UIntRange>,
val zpBasicsafe: List<UIntRange>
): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) {
companion object {
private fun Properties.getString(property: String): String {
val value = this.getProperty(property, null)
if(value!=null)
return value
throw NoSuchElementException("string property '$property' not found in config file")
}
private fun Properties.getInteger(property: String): UInt {
val value = this.getProperty(property, null)
if(value!=null) return parseInt(value)
throw NoSuchElementException("integer property '$property' not found in config file")
}
private fun parseInt(value: String): UInt {
if(value.startsWith("0x"))
return value.drop(2).toUInt(16)
if(value.startsWith("$"))
return value.drop(1).toUInt(16)
if(value.startsWith("%"))
return value.drop(1).toUInt(2)
return value.toUInt()
}
private fun parseAddressRanges(key: String, props: Properties): List<UIntRange> {
val rangesStr = props.getString(key)
if(rangesStr.isBlank())
return emptyList()
val result = mutableListOf<UIntRange>()
val ranges = rangesStr.split(",").map { it.trim() }
for(r in ranges) {
if ('-' in r) {
val (fromStr, toStr) = r.split("-")
val from = parseInt(fromStr.trim())
val to = parseInt(toStr.trim())
result.add(from..to)
} else {
val address = parseInt(r)
result.add(address..address)
}
}
return result
}
fun fromConfigFile(configfile: Path): ConfigFileTarget {
val props = Properties()
props.load(configfile.inputStream())
val cpuString = props.getString("cpu").uppercase()
val cpuType = try {
CpuType.valueOf(cpuString)
} catch (_: IllegalArgumentException) {
CpuType.valueOf("CPU$cpuString")
}
val ioAddresses = parseAddressRanges("io_regions", props)
val zpFullsafe = parseAddressRanges("zp_fullsafe", props)
val zpKernalsafe = parseAddressRanges("zp_kernalsafe", props)
val zpBasicsafe = parseAddressRanges("zp_basicsafe", props)
val libraryPath = expandTilde(Path(props.getString("library")))
if(!libraryPath.isDirectory())
throw IOException("invalid library path: $libraryPath")
val customLauncherStr = props.getProperty("custom_launcher_code", null)
val customLauncher =
if(customLauncherStr?.isNotBlank()==true)
(customLauncherStr+"\n").lines().map { it.trimEnd() }
else emptyList()
val assemblerOptionsStr = props.getProperty("assembler_options", "").trim()
val assemblerOptions = assemblerOptionsStr.ifBlank { null }
val outputTypeString = props.getProperty("output_type", "PRG")
val defaultOutputType = OutputType.valueOf(outputTypeString.uppercase())
return ConfigFileTarget(
configfile.nameWithoutExtension,
Encoding.entries.first { it.prefix==props.getString("encoding") },
cpuType,
props.getInteger("load_address"),
props.getInteger("memtop"),
0u, // used only in a very specific error condition check in a certain scenario...
props.getInteger("bss_highram_start"),
props.getInteger("bss_highram_end"),
props.getInteger("bss_goldenram_start"),
props.getInteger("bss_goldenram_end"),
defaultOutputType,
libraryPath,
customLauncher,
assemblerOptions,
ioAddresses,
props.getInteger("zp_scratch_b1"),
props.getInteger("zp_scratch_reg"),
props.getInteger("zp_scratch_w1"),
props.getInteger("zp_scratch_w2"),
props.getInteger("virtual_registers"),
zpFullsafe,
zpKernalsafe,
zpBasicsafe,
)
}
}
// TODO floats are not yet supported here, just enter some values
override val FLOAT_MAX_POSITIVE = 9.999999999e97
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
override val FLOAT_MEM_SIZE = 8
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam // TODO this is not yet used
override fun getFloatAsmBytes(num: Number) = TODO("floats")
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("floats")
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("floats")
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
throw IllegalArgumentException("Custom compiler target cannot automatically launch an emulator. Do this manually.")
}
override fun isIOAddress(address: UInt): Boolean = ioAddresses.any { address in it }
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = ConfigurableZeropage(
zpScratchB1, zpScratchReg, zpScratchW1, zpScratchW2,
virtualregistersStart,
zpBasicsafe,
zpKernalsafe,
zpFullsafe,
compilerOptions
)
// note: there's no golden ram yet
}
}

View File

@ -1,19 +1,94 @@
package prog8.code.target package prog8.code.target
import prog8.code.core.Encoding import prog8.code.core.*
import prog8.code.core.ICompilationTarget import prog8.code.target.encodings.Encoder
import prog8.code.core.IMemSizer import prog8.code.target.zp.CX16Zeropage
import prog8.code.core.IStringEncoding import java.nio.file.Path
import prog8.code.target.cbm.CbmMemorySizer
import prog8.code.target.cx16.CX16MachineDefinition
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { 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 machine = CX16MachineDefinition()
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII
override val libraryPath = null
override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object { companion object {
const val NAME = "cx16" const val NAME = "cx16"
} }
override val cpu = CpuType.CPU65C02
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x0801u
override val PROGRAM_MEMTOP_ADDRESS = 0x9f00u
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
override val BSSGOLDENRAM_START = 0x0400u
override val BSSGOLDENRAM_END = 0x07ffu
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
val emulator: String
val extraArgs: List<String>
when(selectedEmulator) {
1 -> {
emulator = "x16emu"
extraArgs = listOf("-debug")
}
2 -> {
emulator = "box16"
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
}
else -> {
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
return
}
}
if(!quiet)
println("\nStarting Commander X16 emulator $emulator...")
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
val processb = ProcessBuilder(cmdline)
if(!quiet)
processb.inheritIO()
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = CX16Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, BSSGOLDENRAM_START..BSSGOLDENRAM_END)
}
} }

View File

@ -1,10 +1,9 @@
package prog8.code.target.cbm package prog8.code.target
import prog8.code.core.InternalCompilerException import prog8.code.core.InternalCompilerException
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.math.pow import kotlin.math.pow
data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) { data class Mflpt5(val b0: UByte, val b1: UByte, val b2: UByte, val b3: UByte, val b4: UByte) {
companion object { companion object {

View File

@ -1,30 +0,0 @@
package prog8.code.target
import prog8.code.core.*
import prog8.code.target.neo6502.Neo6502MachineDefinition
class Neo6502Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override val name = NAME
override val machine = Neo6502MachineDefinition()
override val defaultEncoding = Encoding.ISO
companion object {
const val NAME = "neo"
}
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype")
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt== DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
}

View File

@ -0,0 +1,38 @@
package prog8.code.target
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
import prog8.code.core.IMemSizer
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
override fun memorySize(dt: DataType, numElements: Int?): Int {
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
return when(dt.sub) {
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
BaseDataType.FLOAT-> numElements * floatsize
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
else -> throw IllegalArgumentException("invalid sub type")
}
}
else if (dt.isString) {
return numElements // treat it as the size of the given string with the length
?: 2 // treat it as the size to store a string pointer
}
return when {
dt.isByteOrBool -> 1 * (numElements ?: 1)
dt.isFloat -> floatsize * (numElements ?: 1)
dt.isLong -> 4 * (numElements ?: 1)
dt.isPointer -> 2 // pointer is just a uword
dt.isStructInstance -> dt.subType!!.memsize(this)
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
else -> 2 * (numElements ?: 1)
}
}
}

View File

@ -1,19 +1,80 @@
package prog8.code.target package prog8.code.target
import prog8.code.core.Encoding import prog8.code.core.*
import prog8.code.core.ICompilationTarget import prog8.code.target.encodings.Encoder
import prog8.code.core.IMemSizer import prog8.code.target.zp.PETZeropage
import prog8.code.core.IStringEncoding import java.nio.file.Path
import prog8.code.target.cbm.CbmMemorySizer
import prog8.code.target.pet.PETMachineDefinition
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer { 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 machine = PETMachineDefinition()
override val defaultEncoding = Encoding.PETSCII override val defaultEncoding = Encoding.PETSCII
override val libraryPath = null
override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object { companion object {
const val NAME = "pet32" const val NAME = "pet32"
} }
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x0401u
override val PROGRAM_MEMTOP_ADDRESS = 0x8000u
override val BSSHIGHRAM_START = 0u
override val BSSHIGHRAM_END = 0u
override val BSSGOLDENRAM_START = 0u
override val BSSGOLDENRAM_END = 0u
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path, quiet: Boolean) {
if(selectedEmulator!=1) {
System.err.println("The pet target only supports the main emulator (Vice).")
return
}
if(!quiet)
println("\nStarting PET emulator...")
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline)
if(!quiet)
processb.inheritIO()
val process=processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = PETZeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
}
} }

View File

@ -1,29 +1,111 @@
package prog8.code.target package prog8.code.target
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.virtual.VirtualMachineDefinition import prog8.code.target.encodings.Encoder
import java.nio.file.Path
import kotlin.io.path.isReadable
import kotlin.io.path.name
import kotlin.io.path.readText
class VMTarget: ICompilationTarget,
IStringEncoding by Encoder(false),
IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer {
override val name = NAME override val name = NAME
override val machine = VirtualMachineDefinition()
override val defaultEncoding = Encoding.ISO override val defaultEncoding = Encoding.ISO
override val libraryPath = null
override val customLauncher: List<String> = emptyList()
override val additionalAssemblerOptions = null
override val defaultOutputType = OutputType.PRG
companion object { companion object {
const val NAME = "virtual" const val NAME = "virtual"
const val FLOAT_MEM_SIZE = 8 // 64-bits double
} }
override fun memorySize(dt: DataType): Int { override val cpu = CpuType.VIRTUAL
return when(dt) {
in ByteDatatypesWithBoolean -> 1 override val FLOAT_MAX_POSITIVE = Double.MAX_VALUE
in WordDatatypes, in PassByReferenceDatatypes -> 2 override val FLOAT_MAX_NEGATIVE = -Double.MAX_VALUE
DataType.FLOAT -> machine.FLOAT_MEM_SIZE override val FLOAT_MEM_SIZE = VMTarget.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype") override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
} override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // not actually used
override val BSSHIGHRAM_START = 0u // not actually used
override val BSSHIGHRAM_END = 0u // not actually used
override val BSSGOLDENRAM_START = 0u // not actually used
override val BSSGOLDENRAM_END = 0u // not actually used
override lateinit var zeropage: Zeropage // not actually used
override lateinit var golden: GoldenRam // not actually used
override fun getFloatAsmBytes(num: Number): String {
// little endian binary representation
val bits = num.toDouble().toBits().toULong()
val hexStr = bits.toString(16).padStart(16, '0')
val parts = hexStr.chunked(2).map { "$$it" }
return parts.joinToString(", ")
} }
override fun memorySize(arrayDt: DataType, numElements: Int) = override fun convertFloatToBytes(num: Double): List<UByte> {
if(arrayDt==DataType.UWORD) val bits = num.toBits().toULong()
numElements // pointer to bytes. val hexStr = bits.toString(16).padStart(16, '0')
val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() }
return parts
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==8) { "need 8 bytes" }
val b0 = bytes[0].toLong() shl (8*7)
val b1 = bytes[1].toLong() shl (8*6)
val b2 = bytes[2].toLong() shl (8*5)
val b3 = bytes[3].toLong() shl (8*4)
val b4 = bytes[4].toLong() shl (8*3)
val b5 = bytes[5].toLong() shl (8*2)
val b6 = bytes[6].toLong() shl (8*1)
val b7 = bytes[7].toLong() shl (8*0)
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, quiet: Boolean) {
if(!quiet)
println("\nStarting Virtual Machine...")
// 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 filename = programNameWithPath.name
if(programNameWithPath.isReadable()) {
vm.runProgram(programNameWithPath.readText(), quiet)
} else {
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
if(withExt.isReadable())
vm.runProgram(withExt.readText(), quiet)
else else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
}
}
override fun isIOAddress(address: UInt): Boolean = false
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = VirtualZeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
}
}
interface IVirtualMachineRunner {
fun runProgram(irSource: String, quiet: Boolean)
}
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
override val SCRATCH_B1: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_REG: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_W1: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_W2: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
} }

View File

@ -1,62 +0,0 @@
package prog8.code.target.atari
import prog8.code.core.*
import java.nio.file.Path
class AtariMachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = 9.999999999e97
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
override val FLOAT_MEM_SIZE = 6
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x2000u
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // TODO what's memtop?
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
override val BSSGOLDENRAM_START = 0u // TODO
override val BSSGOLDENRAM_END = 0u // TODO
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number")
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("atari float to bytes")
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("atari bytes to float")
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
val emulatorName: String
val cmdline: List<String>
when(selectedEmulator) {
1 -> {
emulatorName = "atari800"
cmdline = listOf(emulatorName, "-xl", "-xl-rev", "2", "-nobasic", "-run", "${programNameWithPath}.xex")
}
2 -> {
emulatorName = "altirra"
cmdline = listOf("Altirra64.exe", "${programNameWithPath.normalize()}.xex")
}
else -> {
System.err.println("Atari target only supports atari800 and altirra emulators.")
return
}
}
// TODO monlist?
println("\nStarting Atari800XL emulator $emulatorName...")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu // TODO
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = AtariZeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
}
}

View File

@ -1,57 +0,0 @@
package prog8.code.target.atari
import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException
import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
class AtariZeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0xcbu // temp storage for a single byte
override val SCRATCH_REG = 0xccu // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0xcdu // temp storage 1 for a word $cd+$ce
override val SCRATCH_W2 = 0xcfu // temp storage 2 for a word $cf+$d0 TODO is $d0 okay to use?
init {
if (options.floats) {
throw InternalCompilerException("Atari target doesn't yet support floating point routines")
}
if (options.floats && options.zeropage !in arrayOf(
ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE,
ZeropageType.DONTUSE
))
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
when (options.zeropage) {
ZeropageType.FULL -> {
// TODO all atari usable zero page locations, except the ones used by the system's IRQ routine
free.addAll(0x00u..0xffu)
// TODO atari free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x80u..0xffu) // TODO
}
ZeropageType.BASICSAFE,
ZeropageType.FLOATSAFE -> {
free.addAll(0x80u..0xffu) // TODO
free.removeAll(0xd4u .. 0xefu) // floating point storage
}
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
}
val distinctFree = free.distinct()
free.clear()
free.addAll(distinctFree)
removeReservedFromFreePool()
retainAllowed()
}
override fun allocateCx16VirtualRegisters() {
TODO("Not known if atari can put the virtual regs in ZP")
}
}

View File

@ -1,62 +0,0 @@
package prog8.code.target.c128
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
import java.nio.file.Path
class C128MachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x1c01u
override val PROGRAM_MEMTOP_ADDRESS = 0xff00u
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
override val BSSGOLDENRAM_START = 0u // TODO
override val BSSGOLDENRAM_END = 0u // TODO
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
if(selectedEmulator!=1) {
System.err.println("The c128 target only supports the main emulator (Vice).")
return
}
println("\nStarting C-128 emulator x128...")
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf("x128", "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = C128Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY) // TODO does the c128 have some of this somewhere?
}
}

View File

@ -1,73 +0,0 @@
package prog8.code.target.c64
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
import java.io.IOException
import java.nio.file.Path
class C64MachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x0801u
override val PROGRAM_MEMTOP_ADDRESS = 0xcfe0u // $a000 if floats are used
// note that at $cfe0-$cfff are the 16 'virtual registers' R0-R15
override val BSSHIGHRAM_START = 0xc000u
override val BSSHIGHRAM_END = 0xcfffu
override val BSSGOLDENRAM_START = 0u
override val BSSGOLDENRAM_END = 0u
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
if(selectedEmulator!=1) {
System.err.println("The c64 target only supports the main emulator (Vice).")
return
}
for(emulator in listOf("x64sc", "x64")) {
println("\nStarting C-64 emulator $emulator...")
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf(emulator, "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process
try {
process=processb.start()
} catch(x: IOException) {
continue // try the next emulator executable
}
process.waitFor()
break
}
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0xd000u..0xdfffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = C64Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, 0xc000u until 0xd000u)
}
}

View File

@ -1,21 +0,0 @@
package prog8.code.target.cbm
import prog8.code.core.*
internal object CbmMemorySizer: IMemSizer {
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypesWithBoolean -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
else -> throw IllegalArgumentException("invalid datatype")
}
}
override fun memorySize(arrayDt: DataType, numElements: Int) =
if(arrayDt==DataType.UWORD)
numElements // pointer to bytes.
else
memorySize(ArrayToElementTypes.getValue(arrayDt)) * numElements
}

View File

@ -1,75 +0,0 @@
package prog8.code.target.cx16
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
import java.nio.file.Path
class CX16MachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU65c02
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x0801u
override val PROGRAM_MEMTOP_ADDRESS = 0x9f00u
override val BSSHIGHRAM_START = 0xa000u // hiram bank 1, 8Kb, assumed to be active
override val BSSHIGHRAM_END = 0xbfffu // Rom starts at $c000
override val BSSGOLDENRAM_START = 0x0400u
override val BSSGOLDENRAM_END = 0x07ffu
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
val emulator: String
val extraArgs: List<String>
when(selectedEmulator) {
1 -> {
emulator = "x16emu"
extraArgs = listOf("-debug")
}
2 -> {
emulator = "box16"
extraArgs = listOf("-sym", C64Target.viceMonListName(programNameWithPath.toString()))
}
else -> {
System.err.println("Cx16 target only supports x16emu and box16 emulators.")
return
}
}
println("\nStarting Commander X16 emulator $emulator...")
val cmdline = listOf(emulator, "-scale", "2", "-rtc", "-run", "-prg", "${programNameWithPath}.prg") + extraArgs
val processb = ProcessBuilder(cmdline).inheritIO()
processb.environment()["PULSE_LATENCY_MSEC"] = "10"
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address==0u || address==1u || address in 0x9f00u..0x9fffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = CX16Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, 0x0400u until 0x0800u)
}
}

View File

@ -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

@ -0,0 +1,327 @@
package prog8.code.target.encodings
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import java.io.CharConversionException
object C64osEncoding {
// decoding: from C64 OS Screencodes (0-255) to unicode
// character table from:
// https://www.c64os.com/c64os/usersguide/appendices#charactersets
private val decodingC64os = charArrayOf(
'@' , // @ 0x00 -> COMMERCIAL AT
'a' , // a 0x01 -> LATIN SMALL LETTER A
'b' , // b 0x02 -> LATIN SMALL LETTER B
'c' , // c 0x03 -> LATIN SMALL LETTER C
'd' , // d 0x04 -> LATIN SMALL LETTER D
'e' , // e 0x05 -> LATIN SMALL LETTER E
'f' , // f 0x06 -> LATIN SMALL LETTER F
'g' , // g 0x07 -> LATIN SMALL LETTER G
'h' , // h 0x08 -> LATIN SMALL LETTER H
'i' , // i 0x09 -> LATIN SMALL LETTER I
'j' , // j 0x0A -> LATIN SMALL LETTER J
'k' , // k 0x0B -> LATIN SMALL LETTER K
'l' , // l 0x0C -> LATIN SMALL LETTER L
'm' , // m 0x0D -> LATIN SMALL LETTER M
'n' , // n 0x0E -> LATIN SMALL LETTER N
'o' , // o 0x0F -> LATIN SMALL LETTER O
'p' , // p 0x10 -> LATIN SMALL LETTER P
'q' , // q 0x11 -> LATIN SMALL LETTER Q
'r' , // r 0x12 -> LATIN SMALL LETTER R
's' , // s 0x13 -> LATIN SMALL LETTER S
't' , // t 0x14 -> LATIN SMALL LETTER T
'u' , // u 0x15 -> LATIN SMALL LETTER U
'v' , // v 0x16 -> LATIN SMALL LETTER V
'w' , // w 0x17 -> LATIN SMALL LETTER W
'x' , // x 0x18 -> LATIN SMALL LETTER X
'y' , // y 0x19 -> LATIN SMALL LETTER Y
'z' , // z 0x1A -> LATIN SMALL LETTER Z
'[' , // [ 0x1B -> LEFT SQUARE BRACKET
'\\' , // \ 0x1C -> REVERSE SOLIDUS
']' , // ] 0x1D -> RIGHT SQUARE BRACKET
'^' , // ^ 0x1E -> CIRCUMFLEX
'_' , // _ 0x1F -> UNDERSCORE
' ' , // 0x20 -> SPACE
'!' , // ! 0x21 -> EXCLAMATION MARK
'"' , // " 0x22 -> QUOTATION MARK
'#' , // # 0x23 -> NUMBER SIGN
'$' , // $ 0x24 -> DOLLAR SIGN
'%' , // % 0x25 -> PERCENT SIGN
'&' , // & 0x26 -> AMPERSAND
'\'' , // ' 0x27 -> APOSTROPHE
'(' , // ( 0x28 -> LEFT PARENTHESIS
')' , // ) 0x29 -> RIGHT PARENTHESIS
'*' , // * 0x2A -> ASTERISK
'+' , // + 0x2B -> PLUS SIGN
',' , // , 0x2C -> COMMA
'-' , // - 0x2D -> HYPHEN-MINUS
'.' , // . 0x2E -> FULL STOP
'/' , // / 0x2F -> SOLIDUS
'0' , // 0 0x30 -> DIGIT ZERO
'1' , // 1 0x31 -> DIGIT ONE
'2' , // 2 0x32 -> DIGIT TWO
'3' , // 3 0x33 -> DIGIT THREE
'4' , // 4 0x34 -> DIGIT FOUR
'5' , // 5 0x35 -> DIGIT FIVE
'6' , // 6 0x36 -> DIGIT SIX
'7' , // 7 0x37 -> DIGIT SEVEN
'8' , // 8 0x38 -> DIGIT EIGHT
'9' , // 9 0x39 -> DIGIT NINE
':' , // : 0x3A -> COLON
';' , // ; 0x3B -> SEMICOLON
'<' , // < 0x3C -> LESS-THAN SIGN
'=' , // = 0x3D -> EQUALS SIGN
'>' , // > 0x3E -> GREATER-THAN SIGN
'?' , // ? 0x3F -> QUESTION MARK
'`' , // ` 0x40 -> GRAVE ACCENT
'A' , // A 0x41 -> LATIN CAPITAL LETTER A
'B' , // B 0x42 -> LATIN CAPITAL LETTER B
'C' , // C 0x43 -> LATIN CAPITAL LETTER C
'D' , // D 0x44 -> LATIN CAPITAL LETTER D
'E' , // E 0x45 -> LATIN CAPITAL LETTER E
'F' , // F 0x46 -> LATIN CAPITAL LETTER F
'G' , // G 0x47 -> LATIN CAPITAL LETTER G
'H' , // H 0x48 -> LATIN CAPITAL LETTER H
'I' , // I 0x49 -> LATIN CAPITAL LETTER I
'J' , // J 0x4A -> LATIN CAPITAL LETTER J
'K' , // K 0x4B -> LATIN CAPITAL LETTER K
'L' , // L 0x4C -> LATIN CAPITAL LETTER L
'M' , // M 0x4D -> LATIN CAPITAL LETTER M
'N' , // N 0x4E -> LATIN CAPITAL LETTER N
'O' , // O 0x4F -> LATIN CAPITAL LETTER O
'P' , // P 0x50 -> LATIN CAPITAL LETTER P
'Q' , // Q 0x51 -> LATIN CAPITAL LETTER Q
'R' , // R 0x52 -> LATIN CAPITAL LETTER R
'S' , // S 0x53 -> LATIN CAPITAL LETTER S
'T' , // T 0x54 -> LATIN CAPITAL LETTER T
'U' , // U 0x55 -> LATIN CAPITAL LETTER U
'V' , // V 0x56 -> LATIN CAPITAL LETTER V
'W' , // W 0x57 -> LATIN CAPITAL LETTER W
'X' , // X 0x58 -> LATIN CAPITAL LETTER X
'Y' , // Y 0x59 -> LATIN CAPITAL LETTER Y
'Z' , // Z 0x5A -> LATIN CAPITAL LETTER Z
'{' , // { 0x5B -> LEFT BRACE
'|' , // | 0x5C -> VERTICAL BAR
'}' , // } 0x5D -> RIGHT BRACE
'~' , // ~ 0x5E -> TILDE
'\ufffe', // 0x5F -> RESERVED
'\u00a0', // 0x60 -> NO-BREAK SPACE (TRANSPARENT)
'\ufffe', // 0x61 -> COMMODORE SYMBOL
'\u2191', // ↑ 0x62 -> UP ARROW
'\u2193', // ↓ 0x63 -> DOWN ARROW
'\u2190', // ← 0x64 -> LEFT ARROW
'\u2192', // → 0x65 -> RIGHT ARROW
'\u231A', // ⌚ 0x66 -> WATCH (ANALOG CLOCKFACE)
'\u21BB', // ↻ 0x67 -> CYCLE ARROWS
'\u2026', // … 0x68 -> ELLIPSIS
'\u25a7', // ▧ 0x69 -> DIAGNONAL STRIPES
'\u2610', // ☐ 0x6A -> CHECKBOX UNCHECKED
'\u2611', // ☑ 0x6B -> CHECKBOX CHECKED
'\ufffe', // 0x6C -> RADIO BUTTON UNSELECTED
'\ufffe', // 0x6D -> RADIO BUTTON SELECTED
'\ufffe', // 0x6E -> UTILITY CLOSE BUTTON
'\ufffe', // 0x6F -> UTILITY TITLE BAR
'\u00a9', // © 0x70 -> COPYRIGHT
'\u2713', // ✓ 0x71 -> CHECKMARK
'\u2261', // ≡ 0x72 -> THREE HORIZONTAL STRIPES
'\ufffe', // 0x73 -> TICK TRACK
'\ufffe', // 0x74 -> TICK TRACK NUB
'\ufffe', // 0x75 -> TAB CORNER
'\u2980', // ⦀ 0x76 -> THREE VERTICAL STRIPES
'\ufffe', // 0x77 -> CUSTOM 1
'\ufffe', // 0x78 -> CUSTOM 2
'\ufffe', // 0x79 -> CUSTOM 3
'\ufffe', // 0x7A -> CUSTOM 4
'\ufffe', // 0x7B -> CUSTOM 5
'\ufffe', // 0x7C -> CUSTOM 6
'\ufffe', // 0x7D -> CUSTOM 7
'\ufffe', // 0x7E -> CUSTOM 8
'\ufffe', // 0x7F -> CUSTOM 9
'\ufffe', // 0x80 -> REVERSED COMMERCIAL AT
'\ufffe', // 0x81 -> REVERSED LATIN SMALL LETTER A
'\ufffe', // 0x82 -> REVERSED LATIN SMALL LETTER B
'\ufffe', // 0x83 -> REVERSED LATIN SMALL LETTER C
'\ufffe', // 0x84 -> REVERSED LATIN SMALL LETTER D
'\ufffe', // 0x85 -> REVERSED LATIN SMALL LETTER E
'\ufffe', // 0x86 -> REVERSED LATIN SMALL LETTER F
'\ufffe', // 0x87 -> REVERSED LATIN SMALL LETTER G
'\ufffe', // 0x88 -> REVERSED LATIN SMALL LETTER H
'\ufffe', // 0x89 -> REVERSED LATIN SMALL LETTER I
'\ufffe', // 0x8A -> REVERSED LATIN SMALL LETTER J
'\ufffe', // 0x8B -> REVERSED LATIN SMALL LETTER K
'\ufffe', // 0x8C -> REVERSED LATIN SMALL LETTER L
'\ufffe', // 0x8D -> REVERSED LATIN SMALL LETTER M
'\ufffe', // 0x8E -> REVERSED LATIN SMALL LETTER N
'\ufffe', // 0x8F -> REVERSED LATIN SMALL LETTER O
'\ufffe', // 0x90 -> REVERSED LATIN SMALL LETTER P
'\ufffe', // 0x91 -> REVERSED LATIN SMALL LETTER Q
'\ufffe', // 0x92 -> REVERSED LATIN SMALL LETTER R
'\ufffe', // 0x93 -> REVERSED LATIN SMALL LETTER S
'\ufffe', // 0x94 -> REVERSED LATIN SMALL LETTER T
'\ufffe', // 0x95 -> REVERSED LATIN SMALL LETTER U
'\ufffe', // 0x96 -> REVERSED LATIN SMALL LETTER V
'\ufffe', // 0x97 -> REVERSED LATIN SMALL LETTER W
'\ufffe', // 0x98 -> REVERSED LATIN SMALL LETTER X
'\ufffe', // 0x99 -> REVERSED LATIN SMALL LETTER Y
'\ufffe', // 0x9A -> REVERSED LATIN SMALL LETTER Z
'\ufffe', // 0x9B -> REVERSED LEFT SQUARE BRACKET
'\ufffe', // 0x9C -> REVERSED REVERSE SOLIDUS
'\ufffe', // 0x9D -> REVERSED RIGHT SQUARE BRACKET
'\ufffe', // 0x9E -> REVERSED CIRCUMFLEX
'\ufffe', // 0x9F -> REVERSED UNDERSCORE
'\ufffe', // 0xA0 -> REVERSED SPACE
'\ufffe', // 0xA1 -> REVERSED EXCLAMATION MARK
'\ufffe', // 0xA2 -> REVERSED QUOTATION MARK
'\ufffe', // 0xA3 -> REVERSED NUMBER SIGN
'\ufffe', // 0xA4 -> REVERSED DOLLAR SIGN
'\ufffe', // 0xA5 -> REVERSED PERCENT SIGN
'\ufffe', // 0xA6 -> REVERSED AMPERSAND
'\ufffe', // 0xA7 -> REVERSED APOSTROPHE
'\ufffe', // 0xA8 -> REVERSED LEFT PARENTHESIS
'\ufffe', // 0xA9 -> REVERSED RIGHT PARENTHESIS
'\ufffe', // 0xAA -> REVERSED ASTERISK
'\ufffe', // 0xAB -> REVERSED PLUS SIGN
'\ufffe', // 0xAC -> REVERSED COMMA
'\ufffe', // 0xAD -> REVERSED HYPHEN-MINUS
'\ufffe', // 0xAE -> REVERSED FULL STOP
'\ufffe', // 0xAF -> REVERSED SOLIDUS
'\ufffe', // 0xB0 -> REVERSED DIGIT ZERO
'\ufffe', // 0xB1 -> REVERSED DIGIT ONE
'\ufffe', // 0xB2 -> REVERSED DIGIT TWO
'\ufffe', // 0xB3 -> REVERSED DIGIT THREE
'\ufffe', // 0xB4 -> REVERSED DIGIT FOUR
'\ufffe', // 0xB5 -> REVERSED DIGIT FIVE
'\ufffe', // 0xB6 -> REVERSED DIGIT SIX
'\ufffe', // 0xB7 -> REVERSED DIGIT SEVEN
'\ufffe', // 0xB8 -> REVERSED DIGIT EIGHT
'\ufffe', // 0xB9 -> REVERSED DIGIT NINE
'\ufffe', // 0xBA -> REVERSED COLON
'\ufffe', // 0xBB -> REVERSED SEMICOLON
'\ufffe', // 0xBC -> REVERSED LESS-THAN SIGN
'\ufffe', // 0xBD -> REVERSED EQUALS SIGN
'\ufffe', // 0xBE -> REVERSED GREATER-THAN SIGN
'\ufffe', // 0xBF -> REVERSED QUESTION MARK
'\ufffe', // 0xC0 -> REVERSED GRAVE ACCENT
'\ufffe', // 0xC1 -> REVERSED LATIN CAPITAL LETTER A
'\ufffe', // 0xC2 -> REVERSED LATIN CAPITAL LETTER B
'\ufffe', // 0xC3 -> REVERSED LATIN CAPITAL LETTER C
'\ufffe', // 0xC4 -> REVERSED LATIN CAPITAL LETTER D
'\ufffe', // 0xC5 -> REVERSED LATIN CAPITAL LETTER E
'\ufffe', // 0xC6 -> REVERSED LATIN CAPITAL LETTER F
'\ufffe', // 0xC7 -> REVERSED LATIN CAPITAL LETTER G
'\ufffe', // 0xC8 -> REVERSED LATIN CAPITAL LETTER H
'\ufffe', // 0xC9 -> REVERSED LATIN CAPITAL LETTER I
'\ufffe', // 0xCA -> REVERSED LATIN CAPITAL LETTER J
'\ufffe', // 0xCB -> REVERSED LATIN CAPITAL LETTER K
'\ufffe', // 0xCC -> REVERSED LATIN CAPITAL LETTER L
'\ufffe', // 0xCD -> REVERSED LATIN CAPITAL LETTER M
'\ufffe', // 0xCE -> REVERSED LATIN CAPITAL LETTER N
'\ufffe', // 0xCF -> REVERSED LATIN CAPITAL LETTER O
'\ufffe', // 0xD0 -> REVERSED LATIN CAPITAL LETTER P
'\ufffe', // 0xD1 -> REVERSED LATIN CAPITAL LETTER Q
'\ufffe', // 0xD2 -> REVERSED LATIN CAPITAL LETTER R
'\ufffe', // 0xD3 -> REVERSED LATIN CAPITAL LETTER S
'\ufffe', // 0xD4 -> REVERSED LATIN CAPITAL LETTER T
'\ufffe', // 0xD5 -> REVERSED LATIN CAPITAL LETTER U
'\ufffe', // 0xD6 -> REVERSED LATIN CAPITAL LETTER V
'\ufffe', // 0xD7 -> REVERSED LATIN CAPITAL LETTER W
'\ufffe', // 0xD8 -> REVERSED LATIN CAPITAL LETTER X
'\ufffe', // 0xD9 -> REVERSED LATIN CAPITAL LETTER Y
'\ufffe', // 0xDA -> REVERSED LATIN CAPITAL LETTER Z
'\ufffe', // 0xDB -> REVERSED LEFT BRACE
'\ufffe', // 0xDC -> REVERSED VERTICAL BAR
'\ufffe', // 0xDD -> REVERSED RIGHT BRACE
'\ufffe', // 0xDE -> REVERSED TILDE
'\ufffe', // 0xDF -> RESERVED
'\ufffe', // 0xE0 -> RESERVED
'\ufffe', // 0xE1 -> REVERSED COMMODORE SYMBOL
'\ufffe', // 0xE2 -> REVERSED UP ARROW
'\ufffe', // 0xE3 -> REVERSED DOWN ARROW
'\ufffe', // 0xE4 -> REVERSED LEFT ARROW
'\ufffe', // 0xE5 -> REVERSED RIGHT ARROW
'\ufffe', // 0xE6 -> REVERSED ANALOG CLOCKFACE
'\ufffe', // 0xE7 -> REVERSED CYCLE ARROWS
'\ufffe', // 0xE8 -> REVERSED ELLIPSIS
'\ufffe', // 0xE9 -> REVERSED DIAGONAL STRIPES
'\ufffe', // 0xEA -> REVERSED CHECKBOX UNCHECKED
'\ufffe', // 0xEB -> REVERSED CHECKBOX CHECKED
'\ufffe', // 0xEC -> REVERSED RADIO BUTTON UNSELECTED
'\ufffe', // 0xED -> REVERSED RADIO BUTTON SELECTED
'\ufffe', // 0xEE -> MEMORY CHIP ICON
'\u21e7', // ⇧ 0xEF -> SHIFT SYMBOL
'\ufffe', // 0xF0 -> REVERSED COPYRIGHT SYMBOL
'\ufffe', // 0xF1 -> REVERSED CHECKMARK
'\ufffe', // 0xF2 -> REVERSED THREE HORIZONTAL STRIPES
'\ufffe', // 0xF3 -> REVERSED TICK TRACK
'\ufffe', // 0xF4 -> REVERSED TICK TRACK NUB
'\ufffe', // 0xF5 -> REVERSED TAB CORNER
'\ufffe', // 0xF6 -> REVERSED THREE VERTICAL STRIPES
'\ufffe', // 0xF7 -> CUSTOM 10
'\ufffe', // 0xF8 -> CUSTOM 11
'\ufffe', // 0xF9 -> CUSTOM 12
'\ufffe', // 0xFA -> CUSTOM 13
'\ufffe', // 0xFB -> CUSTOM 14
'\ufffe', // 0xFC -> CUSTOM 15
'\ufffe', // 0xFD -> CUSTOM 16
'\ufffe', // 0xFE -> CUSTOM 17
'\ufffe' // 0xFF -> CUSTOM 18
)
// encoding: from unicode to C64 OS Screencodes (0-255)
private val encodingC64os = decodingC64os.withIndex().associate{it.value to it.index}
private fun replaceSpecial(chr: Char): Char =
when(chr) {
'\r' -> '\n' // to make \r (carriage returrn) equivalent to \n (line feed): RETURN ($0d)
else -> chr
}
fun encode(text: String, lowercase: Boolean = false): Result<List<UByte>, CharConversionException> {
fun encodeChar(chr3: Char, lowercase: Boolean): UByte {
val chr = replaceSpecial(chr3)
val screencode = encodingC64os[chr]
return screencode?.toUByte() ?: when (chr) {
'\u0000' -> 0u
'\n' -> 13u
in '\u8000'..'\u80ff' -> {
// special case: take the lower 8 bit hex value directly
(chr.code - 0x8000).toUByte()
}
else -> {
if(chr.isISOControl())
throw CharConversionException("no c64os character for char #${chr.code}")
else
throw CharConversionException("no c64os character for char #${chr.code} '${chr}'")
}
}
}
return try {
Ok(text.map {
try {
encodeChar(it, lowercase)
} catch (x: CharConversionException) {
encodeChar(it, !lowercase)
}
})
} catch(cx: CharConversionException) {
Err(cx)
}
}
fun decode(screencode: Iterable<UByte>, lowercase: Boolean = false): Result<String, CharConversionException> {
return try {
Ok(screencode.map {
val code = it.toInt()
if(code<0 || code>= decodingC64os.size)
throw CharConversionException("c64os $code out of range 0..${decodingC64os.size-1}")
decodingC64os[code]
}.joinToString(""))
} catch(ce: CharConversionException) {
Err(ce)
}
}
}

View File

@ -1,25 +1,24 @@
package prog8.code.target package prog8.code.target.encodings
import com.github.michaelbull.result.fold import com.github.michaelbull.result.fold
import prog8.code.core.Encoding 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
import prog8.code.target.encodings.*
class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
object Encoder: 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)
else -> throw InternalCompilerException("unsupported encoding $encoding") else -> throw InternalCompilerException("unsupported encoding $encoding")
} }
return coded.fold( return coded.fold(
@ -31,12 +30,13 @@ 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)
else -> throw InternalCompilerException("unsupported encoding $encoding") else -> throw InternalCompilerException("unsupported encoding $encoding")
} }
return decoded.fold( return decoded.fold(

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

@ -7,7 +7,7 @@ import java.io.CharConversionException
object PetsciiEncoding { object PetsciiEncoding {
// decoding: from Petscii/Screencodes (0-255) to unicode // decoding: from Petscii/Screencodes (0-255) to Unicode
// character tables used from https://github.com/irmen/cbmcodecs2 // character tables used from https://github.com/irmen/cbmcodecs2
private val decodingPetsciiLowercase = charArrayOf( private val decodingPetsciiLowercase = charArrayOf(
@ -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)
@ -1089,7 +1089,7 @@ object PetsciiEncoding {
Ok(text.map { Ok(text.map {
try { try {
encodeChar(it, lowercase) encodeChar(it, lowercase)
} catch (x: CharConversionException) { } catch (_: CharConversionException) {
encodeChar(it, !lowercase) encodeChar(it, !lowercase)
} }
}) })
@ -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()
@ -1135,7 +1137,7 @@ object PetsciiEncoding {
Ok(text.map { Ok(text.map {
try { try {
encodeChar(it, lowercase) encodeChar(it, lowercase)
} catch (x: CharConversionException) { } catch (_: CharConversionException) {
encodeChar(it, !lowercase) encodeChar(it, !lowercase)
} }
}) })
@ -1157,16 +1159,16 @@ object PetsciiEncoding {
} }
} }
fun petscii2scr(petscii_code: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> { fun petscii2scr(petsciicode: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> {
val code: UInt = when { val code: UInt = when {
petscii_code <= 0x1fu -> petscii_code + 128u petsciicode <= 0x1fu -> petsciicode + 128u
petscii_code <= 0x3fu -> petscii_code.toUInt() petsciicode <= 0x3fu -> petsciicode.toUInt()
petscii_code <= 0x5fu -> petscii_code - 64u petsciicode <= 0x5fu -> petsciicode - 64u
petscii_code <= 0x7fu -> petscii_code - 32u petsciicode <= 0x7fu -> petsciicode - 32u
petscii_code <= 0x9fu -> petscii_code + 64u petsciicode <= 0x9fu -> petsciicode + 64u
petscii_code <= 0xbfu -> petscii_code - 64u petsciicode <= 0xbfu -> petsciicode - 64u
petscii_code <= 0xfeu -> petscii_code - 128u petsciicode <= 0xfeu -> petsciicode - 128u
petscii_code == 255.toUByte() -> 95u petsciicode == 255.toUByte() -> 95u
else -> return Err(CharConversionException("petscii code out of range")) else -> return Err(CharConversionException("petscii code out of range"))
} }
if(inverseVideo) { if(inverseVideo) {

View File

@ -1,50 +0,0 @@
package prog8.code.target.neo6502
import prog8.code.core.*
import java.nio.file.Path
class Neo6502MachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU65c02
override val FLOAT_MAX_POSITIVE = 9.999999999e97
override val FLOAT_MAX_NEGATIVE = -9.999999999e97
override val FLOAT_MEM_SIZE = 6
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x0800u
override val PROGRAM_MEMTOP_ADDRESS = 0xfc00u // kernal starts here
override val BSSHIGHRAM_START = 0u // TODO
override val BSSHIGHRAM_END = 0u // TODO
override val BSSGOLDENRAM_START = 0u // TODO
override val BSSGOLDENRAM_END = 0u // TODO
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = TODO("atari float asm bytes from number")
override fun convertFloatToBytes(num: Double): List<UByte> = TODO("atari float to bytes")
override fun convertBytesToFloat(bytes: List<UByte>): Double = TODO("atari bytes to float")
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
if(selectedEmulator!=1) {
System.err.println("The neo target only supports the main emulator (neo).")
return
}
val cmdline = listOf("neo", "${programNameWithPath}.bin@800", "cold")
println("\nStarting Neo6502 emulator...")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process = processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address in 0xff00u..0xff0fu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = Neo6502Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, UIntRange.EMPTY)
}
}

View File

@ -1,48 +0,0 @@
package prog8.code.target.neo6502
import prog8.code.core.*
class Neo6502Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0xfau // temp storage for a single byte
override val SCRATCH_REG = 0xfbu // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0xfcu // temp storage 1 for a word $fc+$fd
override val SCRATCH_W2 = 0xfeu // temp storage 2 for a word $fe+$ff
init {
if (options.floats) {
throw InternalCompilerException("Neo6502 target doesn't support floating point routines")
}
when (options.zeropage) {
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
else -> {
free.addAll(0x22u..0xffu)
}
}
val distinctFree = free.distinct()
free.clear()
free.addAll(distinctFree)
removeReservedFromFreePool()
allocateCx16VirtualRegisters()
retainAllowed()
}
override fun allocateCx16VirtualRegisters() {
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// However, to be able for the compiler to "see" them as zero page 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)
for(reg in 0..15) {
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.WORD, 2) // cx16.r0s .. cx16.r15s
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.UBYTE, 1) // cx16.r0H .. cx16.r15H
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.BYTE, 1) // cx16.r0sH .. cx16.r15sH
}
}
}

View File

@ -1,63 +0,0 @@
package prog8.code.target.pet
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
import java.nio.file.Path
class PETMachineDefinition: IMachineDefinition {
override val cpu = CpuType.CPU6502
override val FLOAT_MAX_POSITIVE = Mflpt5.FLOAT_MAX_POSITIVE
override val FLOAT_MAX_NEGATIVE = Mflpt5.FLOAT_MAX_NEGATIVE
override val FLOAT_MEM_SIZE = Mflpt5.FLOAT_MEM_SIZE
override val STARTUP_CODE_RESERVED_SIZE = 20u
override val PROGRAM_LOAD_ADDRESS = 0x0401u
override val PROGRAM_MEMTOP_ADDRESS = 0x8000u
override val BSSHIGHRAM_START = 0u
override val BSSHIGHRAM_END = 0u
override val BSSGOLDENRAM_START = 0u
override val BSSGOLDENRAM_END = 0u
override lateinit var zeropage: Zeropage
override lateinit var golden: GoldenRam
override fun getFloatAsmBytes(num: Number) = Mflpt5.fromNumber(num).makeFloatFillAsm()
override fun convertFloatToBytes(num: Double): List<UByte> {
val m5 = Mflpt5.fromNumber(num)
return listOf(m5.b0, m5.b1, m5.b2, m5.b3, m5.b4)
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==5) { "need 5 bytes" }
val m5 = Mflpt5(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4])
return m5.toDouble()
}
override fun launchEmulator(selectedEmulator: Int, programNameWithPath: Path) {
if(selectedEmulator!=1) {
System.err.println("The pet target only supports the main emulator (Vice).")
return
}
println("\nStarting PET emulator...")
val viceMonlist = C64Target.viceMonListName(programNameWithPath.toString())
val cmdline = listOf("xpet", "-model", "4032", "-ramsize", "32", "-videosize", "40", "-silent", "-moncommands", viceMonlist,
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "${programNameWithPath}.prg")
val processb = ProcessBuilder(cmdline).inheritIO()
val process=processb.start()
process.waitFor()
}
override fun isIOAddress(address: UInt): Boolean = address in 0xe800u..0xe8ffu
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = PETZeropage(compilerOptions)
// there's no golden ram.
}
}

View File

@ -1,93 +0,0 @@
package prog8.code.target.virtual
import prog8.code.core.*
import java.nio.file.Path
import kotlin.io.path.isReadable
import kotlin.io.path.name
import kotlin.io.path.readText
class VirtualMachineDefinition: IMachineDefinition {
override val cpu = CpuType.VIRTUAL
override val FLOAT_MAX_POSITIVE = Float.MAX_VALUE.toDouble()
override val FLOAT_MAX_NEGATIVE = -Float.MAX_VALUE.toDouble()
override val FLOAT_MEM_SIZE = 8 // 64-bits double
override val STARTUP_CODE_RESERVED_SIZE = 0u // not actually used
override val PROGRAM_LOAD_ADDRESS = 0u // not actually used
override val PROGRAM_MEMTOP_ADDRESS = 0xffffu // not actually used
override val BSSHIGHRAM_START = 0u // not actually used
override val BSSHIGHRAM_END = 0u // not actually used
override val BSSGOLDENRAM_START = 0u // not actually used
override val BSSGOLDENRAM_END = 0u // not actually used
override lateinit var zeropage: Zeropage // not actually used
override lateinit var golden: GoldenRam // not actually used
override fun getFloatAsmBytes(num: Number): String {
// little endian binary representation
val bits = num.toDouble().toBits().toULong()
val hexStr = bits.toString(16).padStart(16, '0')
val parts = hexStr.chunked(2).map { "\$" + it }
return parts.joinToString(", ")
}
override fun convertFloatToBytes(num: Double): List<UByte> {
val bits = num.toBits().toULong()
val hexStr = bits.toString(16).padStart(16, '0')
val parts = hexStr.chunked(2).map { it.toInt(16).toUByte() }
return parts
}
override fun convertBytesToFloat(bytes: List<UByte>): Double {
require(bytes.size==8) { "need 8 bytes" }
val b0 = bytes[0].toLong() shl (8*7)
val b1 = bytes[1].toLong() shl (8*6)
val b2 = bytes[2].toLong() shl (8*5)
val b3 = bytes[3].toLong() shl (8*4)
val b4 = bytes[4].toLong() shl (8*3)
val b5 = bytes[5].toLong() shl (8*2)
val b6 = bytes[6].toLong() shl (8*1)
val b7 = bytes[7].toLong() shl (8*0)
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) {
println("\nStarting Virtual Machine...")
// 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 filename = programNameWithPath.name
if(programNameWithPath.isReadable()) {
vm.runProgram(programNameWithPath.readText())
} else {
val withExt = programNameWithPath.resolveSibling("$filename.p8ir")
if(withExt.isReadable())
vm.runProgram(withExt.readText())
else
throw NoSuchFileException(withExt.toFile(), reason="not a .p8ir file")
}
}
override fun isIOAddress(address: UInt): Boolean = false
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = VirtualZeropage(compilerOptions)
}
}
interface IVirtualMachineRunner {
fun runProgram(irSource: String)
}
private class VirtualZeropage(options: CompilationOptions): Zeropage(options) {
override val SCRATCH_B1: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_REG: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_W1: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override val SCRATCH_W2: UInt
get() = throw IllegalStateException("virtual shouldn't use this zeropage variable")
override fun allocateCx16VirtualRegisters() { /* there is no actual zero page in this target to allocate thing in */ }
}

View File

@ -1,4 +1,4 @@
package prog8.code.target.c128 package prog8.code.target.zp
import prog8.code.core.CompilationOptions import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException import prog8.code.core.InternalCompilerException
@ -11,17 +11,11 @@ import prog8.code.core.ZeropageType
class C128Zeropage(options: CompilationOptions) : Zeropage(options) { class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x74u // temp storage for a single byte override val SCRATCH_B1 = 0x74u // temp storage for a single byte
override val SCRATCH_REG = 0x75u // temp storage for a register, must be B1+1 override val SCRATCH_REG = 0x75u // temp storage for a register byte, must be B1+1
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
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,
@ -33,18 +27,18 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
ZeropageType.FULL -> { ZeropageType.FULL -> {
// $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such // $00/$01 are data port IO registers, // $02-$09 are storage locations for JSRFAR and such
free.addAll(0x0au..0xffu) free.addAll(0x0au..0xffu)
free.removeAll(listOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u)) // these are updated/used by IRQ free.removeAll(arrayOf(0x90u, 0x91u, 0xa0u, 0xa1u, 0xa2u, 0xc0u, 0xccu, 0xcdu, 0xd0u, 0xd1u, 0xd2u, 0xd3u, 0xd4u, 0xd5u, 0xf7u)) // these are updated/used by IRQ
} }
ZeropageType.KERNALSAFE -> { ZeropageType.KERNALSAFE -> {
free.addAll(0x0au..0x8fu) // BASIC variables free.addAll(0x0au..0x8fu) // BASIC variables
free.addAll(listOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu, free.addAll(arrayOf(0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u)) 0xb0u, 0xb1u, 0xb4u, 0xb5u, 0xb6u))
} }
ZeropageType.FLOATSAFE, ZeropageType.FLOATSAFE,
ZeropageType.BASICSAFE -> { ZeropageType.BASICSAFE -> {
free.addAll(listOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au)) free.addAll(arrayOf(0x0bu, 0x0cu, 0x0du, 0x0eu, 0x0fu, 0x10u, 0x11u, 0x12u, 0x16u, 0x17u, 0x18u, 0x19u, 0x1au))
free.addAll(0x1bu..0x23u) free.addAll(0x1bu..0x23u)
free.addAll(listOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu, free.addAll(arrayOf(0x3fu, 0x40u, 0x41u, 0x42u, 0x43u, 0x44u, 0x47u, 0x48u, 0x49u, 0x4au, 0x4bu, 0x4cu, 0x4fu,
0x55u, 0x56u, 0x57u, 0x58u, 0x55u, 0x56u, 0x57u, 0x58u,
0x74u, 0x75u, 0x78u, 0x80u, 0x83u, 0x87u, 0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, 0x74u, 0x75u, 0x78u, 0x80u, 0x83u, 0x87u, 0x88u, 0x89u, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu,
0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu, 0x92u, 0x96u, 0x9bu, 0x9cu, 0x9eu, 0x9fu, 0xa4u, 0xa7u, 0xa8u, 0xa9u, 0xaau, 0xabu,
@ -53,7 +47,7 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
// if(options.zeropage==ZeropageType.BASICSAFE) { // if(options.zeropage==ZeropageType.BASICSAFE) {
// can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0 // can also clobber the FP locations (unconditionally, because the C128 target doesn't support floating point calculations in prog8 at this time0
free.addAll(listOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu, free.addAll(arrayOf(0x14u, 0x28u, 0x29u, 0x2au, 0x2bu, 0x2cu,
0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u, 0x50u, 0x51u, 0x52u, 0x53u, 0x54u, 0x59u, 0x5au, 0x5bu, 0x5cu, 0x5du, 0x5eu, 0x5fu, 0x60u, 0x61u, 0x62u,
0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u, 0x63u, 0x64u, 0x65u, 0x66u, 0x67u, 0x68u,
0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u)) 0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu, 0x6fu, 0x71u))
@ -71,8 +65,4 @@ class C128Zeropage(options: CompilationOptions) : Zeropage(options) {
removeReservedFromFreePool() removeReservedFromFreePool()
retainAllowed() retainAllowed()
} }
override fun allocateCx16VirtualRegisters() {
TODO("Not known if C128 can put the virtual regs in ZP")
}
} }

View File

@ -1,4 +1,4 @@
package prog8.code.target.c64 package prog8.code.target.zp
import prog8.code.core.* import prog8.code.core.*
@ -6,7 +6,7 @@ import prog8.code.core.*
class C64Zeropage(options: CompilationOptions) : Zeropage(options) { class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x02u // temp storage for a single byte override val SCRATCH_B1 = 0x02u // temp storage for a single byte
override val SCRATCH_REG = 0x03u // temp storage for a register, must be B1+1 override val SCRATCH_REG = 0x03u // temp storage for a register byte, must be B1+1
override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc override val SCRATCH_W1 = 0xfbu // temp storage 1 for a word $fb+$fc
override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe override val SCRATCH_W2 = 0xfdu // temp storage 2 for a word $fd+$fe
@ -21,11 +21,11 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
if (options.zeropage == ZeropageType.FULL) { if (options.zeropage == ZeropageType.FULL) {
free.addAll(0x02u..0xffu) free.addAll(0x02u..0xffu)
free.removeAll(setOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u)) free.removeAll(arrayOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1+1u, SCRATCH_W2, SCRATCH_W2+1u))
free.removeAll(setOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ free.removeAll(arrayOf(0xa0u, 0xa1u, 0xa2u, 0x91u, 0xc0u, 0xc5u, 0xcbu, 0xf5u, 0xf6u)) // these are updated by IRQ
} else { } else {
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) { if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
free.addAll(listOf( free.addAll(arrayOf(
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
@ -43,7 +43,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
if (options.zeropage == ZeropageType.FLOATSAFE) { if (options.zeropage == ZeropageType.FLOATSAFE) {
// remove the zeropage locations used for floating point operations from the free list // remove the zeropage locations used for floating point operations from the free list
free.removeAll(listOf( free.removeAll(arrayOf(
0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12, 0x03, 0x04, 0x05, 0x06, 0x10, 0x11, 0x12,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
@ -56,7 +56,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
if(options.zeropage != ZeropageType.DONTUSE) { if(options.zeropage != ZeropageType.DONTUSE) {
// add the free Zp addresses // add the free Zp addresses
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O* // these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
free.addAll(listOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e, free.addAll(arrayOf(0x02, 0x03, 0x04, 0x05, 0x06, 0x0a, 0x0e,
0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6, 0x92, 0x96, 0x9b, 0x9c, 0x9e, 0x9f, 0xa6,
0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()}) 0xb0, 0xb1, 0xbe, 0xbf, 0xf9).map{it.toUInt()})
} else { } else {
@ -79,7 +79,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
retainAllowed() retainAllowed()
} }
override fun allocateCx16VirtualRegisters() { private fun allocateCx16VirtualRegisters() {
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses. // Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// 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)

View File

@ -1,4 +1,4 @@
package prog8.code.target.cx16 package prog8.code.target.zp
import prog8.code.core.* import prog8.code.core.*
@ -6,7 +6,7 @@ import prog8.code.core.*
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) { class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x7au // temp storage for a single byte override val SCRATCH_B1 = 0x7au // temp storage for a single byte
override val SCRATCH_REG = 0x7bu // temp storage for a register, must be B1+1 override val SCRATCH_REG = 0x7bu // temp storage for a register byte, must be B1+1
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
@ -52,7 +52,7 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
} }
} }
override fun allocateCx16VirtualRegisters() { private fun allocateCx16VirtualRegisters() {
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses. // Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// 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)

View File

@ -0,0 +1,61 @@
package prog8.code.target.zp
import prog8.code.core.CompilationOptions
import prog8.code.core.DataType
import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
class ConfigurableZeropage(
override val SCRATCH_B1: UInt, // temp storage for a single byte
override val SCRATCH_REG: UInt, // temp storage for a register byte, must be B1+1
override val SCRATCH_W1: UInt, // temp storage 1 for a word
override val SCRATCH_W2: UInt, // temp storage 2 for a word
val virtualRegistersStart: UInt, // location of 32 bytes for the r0-r15 virtual registers
basicsafe: List<UIntRange>,
kernalsafe: List<UIntRange>,
fullsafe: List<UIntRange>,
options: CompilationOptions
) : Zeropage(options) {
init {
if (options.floats) {
TODO("floats in configurable target zp")
}
if(SCRATCH_REG!=SCRATCH_B1+1u)
throw IllegalArgumentException("Zero page scratch variable REG should be B1+1")
when (options.zeropage) {
ZeropageType.DONTUSE -> { /* don't use any zeropage at all */ }
ZeropageType.FULL -> fullsafe.forEach { free.addAll(it) }
ZeropageType.BASICSAFE -> basicsafe.forEach { free.addAll(it) }
ZeropageType.KERNALSAFE -> kernalsafe.forEach { free.addAll(it) }
ZeropageType.FLOATSAFE -> TODO("floatsafe in configurable target zp")
}
val distinctFree = free.distinct()
free.clear()
free.addAll(distinctFree)
removeReservedFromFreePool()
allocateCx16VirtualRegisters()
retainAllowed()
}
private fun allocateCx16VirtualRegisters() {
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// 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)
for(reg in 0..15) {
val address = virtualRegistersStart + (2*reg).toUInt()
if(address<=0xffu) {
allocatedVariables["cx16.r${reg}"] = VarAllocation(address, DataType.UWORD, 2) // cx16.r0 .. cx16.r15
allocatedVariables["cx16.r${reg}s"] = VarAllocation(address, DataType.WORD, 2) // cx16.r0s .. cx16.r15s
allocatedVariables["cx16.r${reg}L"] = VarAllocation(address, DataType.UBYTE, 1) // cx16.r0L .. cx16.r15L
allocatedVariables["cx16.r${reg}H"] = VarAllocation(address+1u, DataType.UBYTE, 1) // cx16.r0H .. cx16.r15H
allocatedVariables["cx16.r${reg}sL"] = VarAllocation(address, DataType.BYTE, 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables["cx16.r${reg}sH"] = VarAllocation(address+1u, DataType.BYTE, 1) // cx16.r0sH .. cx16.r15sH
}
}
}
}

View File

@ -1,4 +1,4 @@
package prog8.code.target.pet package prog8.code.target.zp
import prog8.code.core.CompilationOptions import prog8.code.core.CompilationOptions
import prog8.code.core.InternalCompilerException import prog8.code.core.InternalCompilerException
@ -11,15 +11,11 @@ import prog8.code.core.ZeropageType
class PETZeropage(options: CompilationOptions) : Zeropage(options) { class PETZeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0xb3u // temp storage for a single byte override val SCRATCH_B1 = 0xb3u // temp storage for a single byte
override val SCRATCH_REG = 0xb4u // temp storage for a register, must be B1+1 override val SCRATCH_REG = 0xb4u // temp storage for a register byte, must be B1+1
override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word override val SCRATCH_W1 = 0xb6u // temp storage 1 for a word
override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word override val SCRATCH_W2 = 0xb8u // temp storage 2 for a word
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,
@ -52,8 +48,4 @@ class PETZeropage(options: CompilationOptions) : Zeropage(options) {
removeReservedFromFreePool() removeReservedFromFreePool()
retainAllowed() retainAllowed()
} }
override fun allocateCx16VirtualRegisters() {
TODO("Not known if PET can put the virtual regs in ZP")
}
} }

View File

@ -1,63 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.1'
testImplementation 'io.kotest:kotest-framework-datatest:5.9.1'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
test {
java {
srcDir "${project.projectDir}/test"
}
}
}
test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}

View File

@ -0,0 +1,47 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
implementation(project(":codeCore"))
implementation(project(":simpleAst"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
sourceSets {
main {
java {
setSrcDirs(listOf("$projectDir/src"))
}
resources {
setSrcDirs(listOf("$projectDir/res"))
}
}
test {
java {
setSrcDirs(listOf("$projectDir/test"))
}
}
}
tasks.test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn("cleanTest")
// Show test results.
testLogging {
events("skipped", "failed")
}
}

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" />

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,16 @@
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.IMachineDefinition
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations // note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, symbolTable: SymbolTable): Int { internal fun optimizeAssembly(lines: MutableList<String>, machine: ICompilationTarget, symbolTable: SymbolTable): Int {
var numberOfOptimizations = 0 var numberOfOptimizations = 0
@ -123,7 +123,7 @@ private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
private fun optimizeSameAssignments( private fun optimizeSameAssignments(
linesByFourteen: Sequence<List<IndexedValue<String>>>, linesByFourteen: Sequence<List<IndexedValue<String>>>,
machine: IMachineDefinition, machine: ICompilationTarget,
symbolTable: SymbolTable symbolTable: SymbolTable
): List<Modification> { ): List<Modification> {
@ -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.GeneratedLabelPrefix 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:"))) {
@ -386,7 +386,7 @@ This gets generated after certain if conditions, and only the branch instruction
private fun optimizeStoreLoadSame( private fun optimizeStoreLoadSame(
linesByFour: Sequence<List<IndexedValue<String>>>, linesByFour: Sequence<List<IndexedValue<String>>>,
machine: IMachineDefinition, machine: ICompilationTarget,
symbolTable: SymbolTable symbolTable: SymbolTable
): List<Modification> { ): List<Modification> {
val mods = mutableListOf<Modification>() val mods = mutableListOf<Modification>()
@ -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,20 +701,22 @@ 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) {
"phy" if second.startsWith("ldy ") && third=="pla" -> {
mods.add(Modification(lines[3].index, true, null)) mods.add(Modification(lines[3].index, true, null))
mods.add(Modification(lines[1].index, false, " tya")) mods.add(Modification(lines[1].index, false, " tya"))
} }
else if(first=="phx" && second.startsWith("ldx ") && third=="pla") { "phx" if second.startsWith("ldx ") && third=="pla" -> {
mods.add(Modification(lines[3].index, true, null)) mods.add(Modification(lines[3].index, true, null))
mods.add(Modification(lines[1].index, false, " txa")) mods.add(Modification(lines[1].index, false, " txa"))
} }
else if(first=="pha" && second.startsWith("lda ") && third=="pla") { "pha" if second.startsWith("lda ") && third=="pla" -> {
mods.add(Modification(lines[1].index, true, null)) mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[2].index, true, null)) mods.add(Modification(lines[2].index, true, null))
mods.add(Modification(lines[3].index, true, null)) mods.add(Modification(lines[3].index, true, null))
} }
} }
}
return mods return mods

View File

@ -1,16 +1,20 @@
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.target.AtariTarget 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.C64Target import prog8.code.target.C64Target
import prog8.code.target.Neo6502Target import prog8.code.target.PETTarget
import java.nio.file.Path import java.nio.file.Path
internal class AssemblyProgram( internal class AssemblyProgram(
override val name: String, override val name: String,
private val outputDir: Path, outputDir: Path,
private val compTarget: ICompilationTarget) : IAssemblyProgram { private val compTarget: ICompilationTarget) : IAssemblyProgram {
private val assemblyFile = outputDir.resolve("$name.asm") private val assemblyFile = outputDir.resolve("$name.asm")
@ -19,21 +23,17 @@ internal class AssemblyProgram(
private val binFile = outputDir.resolve("$name.bin") private val binFile = outputDir.resolve("$name.bin")
private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name)) private val viceMonListFile = outputDir.resolve(C64Target.viceMonListName(name))
private val listFile = outputDir.resolve("$name.list") private val listFile = outputDir.resolve("$name.list")
private val targetWithoutBreakpointsForEmulator = setOf(AtariTarget.NAME, Neo6502Target.NAME)
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean { override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
val assemblerCommand: List<String> val assemblerCommand: List<String>
when (compTarget.name) { when(options.output) {
in setOf("c64", "c128", "cx16", "pet32") -> { OutputType.PRG -> {
// CBM machines .prg generation. // CBM machines .prg generation.
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently) val command = mutableListOf("64tass", "--cbm-prg", "--ascii", "--case-sensitive", "--long-branch",
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch", "-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
"-Wall", // "-Wno-strict-bool", "-Werror",
"--dump-labels", "--vice-labels", "--labels=$viceMonListFile"
)
if(options.warnSymbolShadowing) if(options.warnSymbolShadowing)
command.add("-Wshadow") command.add("-Wshadow")
@ -44,34 +44,19 @@ internal class AssemblyProgram(
command.add("--quiet") command.add("--quiet")
if(options.asmListfile) { if(options.asmListfile) {
command.addAll(listOf("--list=$listFile", "--no-monitor")) command.add("--list=$listFile")
} }
val outFile = when (options.output) { command.addAll(listOf("--output", prgFile.toString(), assemblyFile.toString()))
OutputType.PRG -> {
command.add("--cbm-prg")
println("\nCreating prg for target ${compTarget.name}.")
prgFile
}
OutputType.RAW -> {
command.add("--nostart")
println("\nCreating raw binary for target ${compTarget.name}.")
binFile
}
else -> throw AssemblyError("invalid output type")
}
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
assemblerCommand = command assemblerCommand = command
if(!options.quiet)
println("\nCreating prg for target ${compTarget.name}.")
} }
"atari" -> { OutputType.XEX -> {
// Atari800XL .xex generation. // Atari800XL .xex generation.
// TODO are these options okay for atari? val command = mutableListOf("64tass", "--atari-xex", "--case-sensitive", "--long-branch",
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch", "-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
"-Wall", // "-Werror", "-Wno-strict-bool"
"--no-monitor"
)
if(options.warnSymbolShadowing) if(options.warnSymbolShadowing)
command.add("-Wshadow") command.add("-Wshadow")
@ -84,34 +69,15 @@ internal class AssemblyProgram(
if(options.asmListfile) if(options.asmListfile)
command.add("--list=$listFile") command.add("--list=$listFile")
val outFile = when (options.output) { command.addAll(listOf("--output", xexFile.toString(), assemblyFile.toString()))
OutputType.XEX -> { assemblerCommand = command
command.add("--atari-xex") if(!options.quiet)
println("\nCreating xex for target ${compTarget.name}.") println("\nCreating xex for target ${compTarget.name}.")
xexFile
} }
OutputType.RAW -> { OutputType.RAW -> {
command.add("--nostart") // Neo6502/headerless raw program generation.
println("\nCreating raw binary for target ${compTarget.name}.") val command = mutableListOf("64tass", "--nostart", "--case-sensitive", "--long-branch",
binFile "-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
}
else -> throw AssemblyError("invalid output type")
}
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
assemblerCommand = command
}
"neo" -> {
// Neo6502 raw program generation.
if(options.output!=OutputType.RAW || options.loadAddress!=0x0800u || options.launcher!=CbmPrgLauncherType.NONE) {
throw AssemblyError("invalid program compilation options. Neo6502 requires %output raw, %launcher none, %address $0800")
}
// TODO are these options okay for neo?
val command = mutableListOf("64tass", "--case-sensitive", "--long-branch",
"-Wall", // "-Werror", "-Wno-strict-bool"
"--no-monitor"
)
if(options.warnSymbolShadowing) if(options.warnSymbolShadowing)
command.add("-Wshadow") command.add("-Wshadow")
@ -124,23 +90,52 @@ internal class AssemblyProgram(
if(options.asmListfile) if(options.asmListfile)
command.add("--list=$listFile") command.add("--list=$listFile")
val outFile = when (options.output) { command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
OutputType.RAW -> {
command.add("--nostart")
println("\nCreating raw binary for target ${compTarget.name}.")
binFile
}
else -> throw AssemblyError("invalid output type, need 'raw'")
}
command.addAll(listOf("--output", outFile.toString(), assemblyFile.toString()))
assemblerCommand = command assemblerCommand = command
if(!options.quiet)
println("\nCreating raw binary for target ${compTarget.name}.")
} }
else -> throw AssemblyError("invalid compilation target") OutputType.LIBRARY -> {
// CBM machines library (.bin) generation (with or without 2 byte load address header depending on the compilation target machine)
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-implied-reg", "--no-monitor", "--dump-labels", "--vice-labels", "--labels=$viceMonListFile")
if(options.warnSymbolShadowing)
command.add("-Wshadow")
else
command.add("-Wno-shadow")
if(options.asmQuiet)
command.add("--quiet")
if(options.asmListfile)
command.add("--list=$listFile")
if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) {
if(!options.quiet)
println("\nCreating binary library file with header for target ${compTarget.name}.")
command.add("--cbm-prg")
} else {
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
} }
val proc = ProcessBuilder(assemblerCommand).inheritIO().start() command.addAll(listOf("--output", binFile.toString(), assemblyFile.toString()))
val result = proc.waitFor() assemblerCommand = command
if (result == 0 && compTarget.name !in targetWithoutBreakpointsForEmulator) { }
}
if(options.compTarget.additionalAssemblerOptions!=null)
assemblerCommand.add(options.compTarget.additionalAssemblerOptions!!)
val proc = ProcessBuilder(assemblerCommand)
if(!options.quiet)
proc.inheritIO()
val process = proc.start()
val result = process.waitFor()
if (result == 0) {
removeGeneratedLabelsFromMonlist() removeGeneratedLabelsFromMonlist()
generateBreakpointList() generateBreakpointList()
} }
@ -148,7 +143,7 @@ internal class AssemblyProgram(
} }
private fun removeGeneratedLabelsFromMonlist() { private fun removeGeneratedLabelsFromMonlist() {
val pattern = Regex("""al (\w+) \S+${PtLabel.GeneratedLabelPrefix}.+?""") 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) {
@ -165,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

@ -9,7 +9,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private val asmgen: AsmGen6502Internal, private val asmgen: AsmGen6502Internal,
private val assignAsmGen: AssignmentAsmGen) { private val assignAsmGen: AssignmentAsmGen) {
internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): DataType? { internal fun translateFunctioncallExpression(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?): BaseDataType? {
return translateFunctioncall(fcall, discardResult = false, resultRegister = resultRegister) return translateFunctioncall(fcall, discardResult = false, resultRegister = resultRegister)
} }
@ -17,13 +17,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
translateFunctioncall(fcall, discardResult = true, resultRegister = null) translateFunctioncall(fcall, discardResult = true, resultRegister = null)
} }
private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?): DataType? { private fun translateFunctioncall(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?): BaseDataType? {
if (discardResult && fcall.hasNoSideEffects) if (discardResult && fcall.hasNoSideEffects)
return null // can just ignore the whole function call altogether return null // can just ignore the whole function call altogether
val sscope = fcall.definingISub() val sscope = fcall.definingISub()
when (fcall.name) { when (fcall.name) {
"lsw" -> throw AssemblyError("lsw() should have been removed or replaced by a const value")
"msw" -> throw AssemblyError("msw() should have been removed or replaced by a const value")
"msb" -> funcMsb(fcall, resultRegister) "msb" -> funcMsb(fcall, resultRegister)
"lsb" -> funcLsb(fcall, resultRegister) "lsb" -> funcLsb(fcall, resultRegister)
"mkword" -> funcMkword(fcall, resultRegister) "mkword" -> funcMkword(fcall, resultRegister)
@ -42,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 @()")
@ -51,7 +54,7 @@ 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.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)
@ -64,28 +67,26 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"callfar" -> funcCallFar(fcall, resultRegister) "callfar" -> funcCallFar(fcall, resultRegister)
"callfar2" -> funcCallFar2(fcall, resultRegister) "callfar2" -> funcCallFar2(fcall, resultRegister)
"call" -> funcCall(fcall) "call" -> funcCall(fcall)
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse statement")
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse statement")
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister) "prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
"prog8_lib_square_byte" -> funcSquare(fcall, DataType.UBYTE, resultRegister) "prog8_lib_square_byte" -> funcSquare(fcall, BaseDataType.UBYTE, resultRegister)
"prog8_lib_square_word" -> funcSquare(fcall, DataType.UWORD, resultRegister) "prog8_lib_square_word" -> funcSquare(fcall, BaseDataType.UWORD, resultRegister)
else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}") else -> throw AssemblyError("missing asmgen for builtin func ${fcall.name}")
} }
return BuiltinFunctions.getValue(fcall.name).returnType return BuiltinFunctions.getValue(fcall.name).returnType
} }
private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: DataType, resultRegister: RegisterOrPair?) { private fun funcSquare(fcall: PtBuiltinFunctionCall, resultType: BaseDataType, resultRegister: RegisterOrPair?) {
// square of word value is faster with dedicated routine, square of byte just use the regular multiplication routine. // square of word value is faster with dedicated routine, square of byte just use the regular multiplication routine.
when (resultType) { when (resultType) {
DataType.UBYTE -> { BaseDataType.UBYTE -> {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A)
asmgen.out(" tay | jsr prog8_math.multiply_bytes") asmgen.out(" tay | jsr prog8_math.multiply_bytes")
if(resultRegister!=null) { if(resultRegister!=null) {
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister, false, fcall.position, null, asmgen), CpuRegister.A, false, false) assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister, false, fcall.position, null, asmgen), CpuRegister.A, false, false)
} }
} }
DataType.UWORD -> { BaseDataType.UWORD -> {
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY) asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
asmgen.out(" jsr prog8_math.square") asmgen.out(" jsr prog8_math.square")
if(resultRegister!=null) { if(resultRegister!=null) {
@ -116,8 +117,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcDivmodW(fcall: PtBuiltinFunctionCall) { private fun funcDivmodW(fcall: PtBuiltinFunctionCall) {
asmgen.assignWordOperandsToAYAndVar(fcall.args[1], fcall.args[0], "P8ZP_SCRATCH_W1") asmgen.assignWordOperandsToAYAndVar(fcall.args[1], fcall.args[0], "P8ZP_SCRATCH_W1")
// math.divmod_uw_asm: -- divide two unsigned words (16 bit each) into 16 bit results // math.divmod_uw_asm: -- divide two unsigned words (16 bit each) into 16 bit results
// input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor // input: P8ZP_SCRATCH_W1 in ZP: 16-bit number, A/Y: 16 bit divisor
// 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.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name) val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)
@ -139,7 +140,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
private fun funcRsave() { private fun funcRsave() {
if (asmgen.isTargetCpu(CpuType.CPU65c02)) if (asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out(""" asmgen.out("""
php php
pha pha
@ -159,7 +160,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
private fun funcRrestore() { private fun funcRrestore() {
if (asmgen.isTargetCpu(CpuType.CPU65c02)) if (asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out(""" asmgen.out("""
plx plx
ply ply
@ -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
@ -303,8 +310,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcCmp(fcall: PtBuiltinFunctionCall) { private fun funcCmp(fcall: PtBuiltinFunctionCall) {
val arg1 = fcall.args[0] val arg1 = fcall.args[0]
val arg2 = fcall.args[1] val arg2 = fcall.args[1]
if(arg1.type in ByteDatatypes) { if(arg1.type.isByte) {
if(arg2.type in ByteDatatypes) { if(arg2.type.isByte) {
when (arg2) { when (arg2) {
is PtIdentifier -> { is PtIdentifier -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
@ -336,7 +343,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
throw AssemblyError("args for cmp() should have same dt") throw AssemblyError("args for cmp() should have same dt")
} else { } else {
// arg1 is a word // arg1 is a word
if(arg2.type in WordDatatypes) { if(arg2.type.isWord) {
when (arg2) { when (arg2) {
is PtIdentifier -> { is PtIdentifier -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY) asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
@ -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,28 +381,39 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
throw AssemblyError("should not discard result of memory allocation at $fcall") throw AssemblyError("should not discard result of memory allocation at $fcall")
val name = (fcall.args[0] as PtString).value val name = (fcall.args[0] as PtString).value
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"} require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position) val slabname = PtIdentifier("prog8_slabs.prog8_memoryslab_$name", DataType.UWORD, fcall.position)
val addressOf = PtAddressOf(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.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, 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) { when(fcall.args[0].type.base) {
DataType.UBYTE -> { BaseDataType.UBYTE -> {
asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_into_A") asmgen.out(" ldy #0 | jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false, false) assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false, false)
} }
DataType.UWORD -> { BaseDataType.UWORD -> {
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A") asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false, false) assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, false, false)
} }
DataType.FLOAT -> { BaseDataType.FLOAT -> {
asmgen.out(" jsr floats.func_sqrt_into_FAC1") asmgen.out(" jsr floats.func_sqrt_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen)) assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
} }
@ -405,18 +423,21 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcRor2(fcall: PtBuiltinFunctionCall) { private fun funcRor2(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single() val what = fcall.args.single()
when (what.type) { when (what.type.base) {
DataType.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,24 +445,26 @@ 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")
} }
} }
DataType.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")
} }
@ -452,40 +475,33 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcRor(fcall: PtBuiltinFunctionCall) { private fun funcRor(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single() val what = fcall.args.single()
when (what.type) { when (what.type.base) {
DataType.UBYTE -> { BaseDataType.UBYTE -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
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 {
what.address is PtNumber -> {
val number = (what.address as PtNumber).number val number = (what.address as PtNumber).number
asmgen.out(" ror ${number.toHex()}") asmgen.out(" ror ${number.toHex()}")
} else { }
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address) what.address is PtIdentifier -> {
if(ptrAndIndex!=null) { asmgen.out(" php") // save Carry
asmgen.out(" php") val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A) asmgen.out(" plp | ror a")
asmgen.saveRegisterStack(CpuRegister.A, true) asmgen.storeAIntoZpPointerVar(sourceName, false)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY) }
asmgen.out(" sta (+) + 1 | sty (+) + 2") else -> {
asmgen.restoreRegisterStack(CpuRegister.X, false) TODO("ror ptr-expression ${what.position}")
asmgen.out("""
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""")
} }
} }
} }
@ -496,13 +512,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} }
DataType.UWORD -> { BaseDataType.UWORD -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
if(!what.index.isSimple()) asmgen.out(" php") // save Carry if(!what.index.isSimple()) asmgen.out(" php") // save Carry
asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X) asmgen.loadScaledArrayIndexIntoRegister(what, CpuRegister.X)
if(!what.index.isSimple()) asmgen.out(" plp") if(!what.index.isSimple()) asmgen.out(" plp")
val varname = asmgen.asmVariableName(what.variable) if(what.variable==null)
TODO("support for ptr indexing ${what.position}")
val varname = asmgen.asmVariableName(what.variable!!)
if(what.splitWords) if(what.splitWords)
asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x") asmgen.out(" ror ${varname}_msb,x | ror ${varname}_lsb,x")
else else
@ -521,18 +539,20 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcRol2(fcall: PtBuiltinFunctionCall) { private fun funcRol2(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single() val what = fcall.args.single()
when (what.type) { when (what.type.base) {
DataType.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,18 +560,20 @@ 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")
} }
} }
DataType.UWORD -> { BaseDataType.UWORD -> {
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 |+ ")
} }
@ -568,40 +590,32 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcRol(fcall: PtBuiltinFunctionCall) { private fun funcRol(fcall: PtBuiltinFunctionCall) {
val what = fcall.args.single() val what = fcall.args.single()
when (what.type) { when (what.type.base) {
DataType.UBYTE -> { BaseDataType.UBYTE -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
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 {
what.address is PtNumber -> {
val number = (what.address as PtNumber).number val number = (what.address as PtNumber).number
asmgen.out(" rol ${number.toHex()}") asmgen.out(" rol ${number.toHex()}")
} else { }
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.address) what.address is PtIdentifier -> {
if(ptrAndIndex!=null) { asmgen.out(" php") // save Carry
asmgen.out(" php") val sourceName = asmgen.loadByteFromPointerIntoA(what.address as PtIdentifier)
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.A) asmgen.out(" plp | rol a")
asmgen.saveRegisterStack(CpuRegister.A, true) asmgen.storeAIntoZpPointerVar(sourceName, false)
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY) }
asmgen.out(" sta (+) + 1 | sty (+) + 2") else -> {
asmgen.restoreRegisterStack(CpuRegister.X, false) TODO("rol ptr-expression ${what.position}")
asmgen.out("""
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""")
} }
} }
} }
@ -612,13 +626,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
} }
} }
DataType.UWORD -> { BaseDataType.UWORD -> {
when (what) { when (what) {
is PtArrayIndexer -> { is PtArrayIndexer -> {
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
@ -642,33 +658,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
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.UBYTE, fcall.definingSub(), fcall.position, variableAsmName = varname) target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, variableAsmName = varname)
} }
is PtNumber -> {
val num = (fcall.args[0] as PtNumber).number + if(msb) 1 else 0
val mem = PtMemoryByte(fcall.position)
mem.add(PtNumber(DataType.UBYTE, num, fcall.position))
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, memory = mem)
}
is PtAddressOf -> {
val mem = PtMemoryByte(fcall.position)
if((fcall.args[0] as PtAddressOf).isFromArrayElement)
TODO("address-of arrayelement")
if(msb) {
val address = PtBinaryExpression("+", DataType.UWORD, fcall.args[0].position)
address.add(fcall.args[0])
address.add(PtNumber(address.type, 1.0, fcall.args[0].position))
mem.add(address)
} else {
mem.add(fcall.args[0])
}
target = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingSub(), fcall.position, memory = mem)
}
is PtArrayIndexer -> { is PtArrayIndexer -> {
val indexer = fcall.args[0] as PtArrayIndexer val indexer = fcall.args[0] as PtArrayIndexer
val elementSize: Int val elementSize: Int
val msbAdd: Int val msbAdd: Int
if(indexer.splitWords) { if(indexer.splitWords) {
val arrayVariable = indexer.variable val arrayVariable = indexer.variable
indexer.children[0] = PtIdentifier(arrayVariable.name + if(msb) "_msb" else "_lsb", DataType.ARRAY_UB, arrayVariable.position) 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].parent = indexer indexer.children[0].parent = indexer
elementSize = 1 elementSize = 1
msbAdd = 0 msbAdd = 0
@ -680,7 +678,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
// double the index because of word array (if not split), add one if msb (if not split) // double the index because of word array (if not split), add one if msb (if not split)
val constIndexNum = (indexer.index as? PtNumber)?.number val constIndexNum = (indexer.index as? PtNumber)?.number
if(constIndexNum!=null) { if(constIndexNum!=null) {
indexer.children[1] = PtNumber(indexer.index.type, constIndexNum*elementSize + msbAdd, indexer.position) indexer.children[1] = PtNumber(indexer.index.type.base, constIndexNum*elementSize + msbAdd, indexer.position)
indexer.children[1].parent = indexer indexer.children[1].parent = indexer
} else { } else {
val multipliedIndex: PtExpression val multipliedIndex: PtExpression
@ -689,12 +687,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} else { } else {
multipliedIndex = PtBinaryExpression("<<", indexer.index.type, indexer.position) multipliedIndex = PtBinaryExpression("<<", indexer.index.type, indexer.position)
multipliedIndex.add(indexer.index) multipliedIndex.add(indexer.index)
multipliedIndex.add(PtNumber(DataType.UBYTE, 1.0, indexer.position)) multipliedIndex.add(PtNumber(BaseDataType.UBYTE, 1.0, indexer.position))
} }
if(msbAdd>0) { if(msbAdd>0) {
val msbIndex = PtBinaryExpression("+", indexer.index.type, indexer.position) val msbIndex = PtBinaryExpression("+", indexer.index.type, indexer.position)
msbIndex.add(multipliedIndex) msbIndex.add(multipliedIndex)
msbIndex.add(PtNumber(DataType.UBYTE, msbAdd.toDouble(), indexer.position)) msbIndex.add(PtNumber(BaseDataType.UBYTE, msbAdd.toDouble(), indexer.position))
indexer.children[1] = msbIndex indexer.children[1] = msbIndex
msbIndex.parent = indexer msbIndex.parent = indexer
} else { } else {
@ -710,19 +708,19 @@ 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)
} }
} }
private fun funcSgn(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) { private fun funcSgn(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope) translateArguments(fcall, scope)
when (val dt = fcall.args.single().type) { when (val dt = fcall.args.single().type.base) {
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A") BaseDataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A") BaseDataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A") BaseDataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
DataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_into_A") BaseDataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_into_A")
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A") BaseDataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
else -> throw AssemblyError("weird type $dt") else -> throw AssemblyError("weird type $dt")
} }
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true, true) assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A, true, true)
@ -730,24 +728,24 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcAbs(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) { private fun funcAbs(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
translateArguments(fcall, scope) translateArguments(fcall, scope)
val dt = fcall.args.single().type val dt = fcall.args.single().type.base
when (dt) { when (dt) {
DataType.BYTE -> { BaseDataType.BYTE -> {
asmgen.out(" jsr prog8_lib.abs_b_into_A") asmgen.out(" jsr prog8_lib.abs_b_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A,false, true) assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, fcall.position, scope, asmgen), CpuRegister.A,false, true)
} }
DataType.WORD -> { BaseDataType.WORD -> {
asmgen.out(" jsr prog8_lib.abs_w_into_AY") asmgen.out(" jsr prog8_lib.abs_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY) assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
} }
DataType.FLOAT -> { BaseDataType.FLOAT -> {
asmgen.out(" jsr floats.func_abs_f_into_FAC1") asmgen.out(" jsr floats.func_abs_f_into_FAC1")
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen)) assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
} }
DataType.UBYTE -> { BaseDataType.UBYTE -> {
asmgen.assignRegister(RegisterOrPair.A, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.A, false, fcall.position, scope, asmgen)) asmgen.assignRegister(RegisterOrPair.A, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.A, false, fcall.position, scope, asmgen))
} }
DataType.UWORD -> { BaseDataType.UWORD -> {
asmgen.assignRegister(RegisterOrPair.AY, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.AY, false, fcall.position, scope, asmgen)) asmgen.assignRegister(RegisterOrPair.AY, AsmAssignTarget.fromRegisters(resultRegister?:RegisterOrPair.AY, false, fcall.position, scope, asmgen))
} }
else -> throw AssemblyError("weird type") else -> throw AssemblyError("weird type")
@ -773,7 +771,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.assignConstFloatToPointerAY(number) asmgen.assignConstFloatToPointerAY(number)
} }
else -> { else -> {
val tempvar = asmgen.getTempVarName(DataType.FLOAT) val tempvar = asmgen.createTempVarReused(BaseDataType.FLOAT, false, fcall)
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.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("""
@ -801,7 +799,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(asmgen.isZpVar(addrExpr)) { if(asmgen.isZpVar(addrExpr)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX) asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
if (asmgen.isTargetCpu(CpuType.CPU65c02)) { if (asmgen.isTargetCpu(CpuType.CPU65C02)) {
asmgen.out(""" asmgen.out("""
sta ($varname) sta ($varname)
txa txa
@ -849,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.FLOAT, fcall.definingISub(), fcall.position, null, null, null, resultRegister, null)) AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position))
} }
} }
@ -867,7 +865,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val varname = asmgen.asmVariableName(addrExpr) val varname = asmgen.asmVariableName(addrExpr)
if(asmgen.isZpVar(addrExpr)) { if(asmgen.isZpVar(addrExpr)) {
// pointervar is already in the zero page, no need to copy // pointervar is already in the zero page, no need to copy
if (asmgen.isTargetCpu(CpuType.CPU65c02)) { if (asmgen.isTargetCpu(CpuType.CPU65C02)) {
asmgen.out(""" asmgen.out("""
ldy #1 ldy #1
lda ($varname),y lda ($varname),y
@ -917,9 +915,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) { private fun funcClamp(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes val signed = fcall.type.isSigned
when(fcall.type) { when {
in ByteDatatypes -> { fcall.type.isByte -> {
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W1+1", fcall.args[2].type) // maximum assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W1+1", fcall.args[2].type) // maximum
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, signed) // value assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A, signed) // value
@ -927,7 +925,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen) val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
assignAsmGen.assignRegisterByte(targetReg, CpuRegister.A, signed, true) assignAsmGen.assignRegisterByte(targetReg, CpuRegister.A, signed, true)
} }
in WordDatatypes -> { fcall.type.isWord -> {
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W1", fcall.args[1].type) // minimum
assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W2", fcall.args[2].type) // maximum assignAsmGen.assignExpressionToVariable(fcall.args[2], "P8ZP_SCRATCH_W2", fcall.args[2].type) // maximum
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY, signed) // value assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY, signed) // value
@ -940,9 +938,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
private fun funcMin(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) { private fun funcMin(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes val signed = fcall.type.isSigned
when (fcall.type) { when {
in ByteDatatypes -> { fcall.type.isByte -> {
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_B1", fcall.type) // right asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_B1", fcall.type) // right
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // left asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // left
asmgen.out(" cmp P8ZP_SCRATCH_B1") asmgen.out(" cmp P8ZP_SCRATCH_B1")
@ -953,7 +951,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen) val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.A, targetReg) asmgen.assignRegister(RegisterOrPair.A, targetReg)
} }
in WordDatatypes -> { fcall.type.isWord -> {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
if(signed) { if(signed) {
@ -998,8 +996,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
private fun funcMax(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) { private fun funcMax(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val signed = fcall.type in SignedDatatypes val signed = fcall.type.isSigned
if(fcall.type in ByteDatatypes) { if(fcall.type.isByte) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // right asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // right
asmgen.out(" cmp P8ZP_SCRATCH_B1") asmgen.out(" cmp P8ZP_SCRATCH_B1")
@ -1009,7 +1007,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
+""") +""")
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen) val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
asmgen.assignRegister(RegisterOrPair.A, targetReg) asmgen.assignRegister(RegisterOrPair.A, targetReg)
} else if(fcall.type in WordDatatypes) { } else if(fcall.type.isWord) {
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
if(signed) { if(signed) {
@ -1053,38 +1051,58 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcMkword(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) { private fun funcMkword(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val reg = resultRegister ?: RegisterOrPair.AY val reg = resultRegister ?: RegisterOrPair.AY
var needAsave = asmgen.needAsaveForExpr(fcall.args[0]) var needAsaveForArg0 = asmgen.needAsaveForExpr(fcall.args[0])
if(!needAsave) { if(!needAsaveForArg0) {
val mr0 = fcall.args[0] as? PtMemoryByte val mr0 = fcall.args[0] as? PtMemoryByte
val mr1 = fcall.args[1] as? PtMemoryByte val mr1 = fcall.args[1] as? PtMemoryByte
if (mr0 != null) if (mr0 != null)
needAsave = mr0.address !is PtNumber needAsaveForArg0 = mr0.address !is PtNumber
if (mr1 != null) if (mr1 != null)
needAsave = needAsave or (mr1.address !is PtNumber) needAsaveForArg0 = needAsaveForArg0 or (mr1.address !is PtNumber)
} }
when(reg) { when(reg) {
RegisterOrPair.AX -> { RegisterOrPair.AX -> {
if(needAsaveForArg0 && !asmgen.needAsaveForExpr(fcall.args[1])) {
// first 0 then 1
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if(needAsave) } else if(!needAsaveForArg0 && asmgen.needAsaveForExpr(fcall.args[1])) {
// first 1 then 0
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
} else {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if (needAsaveForArg0)
asmgen.out(" pha") asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.X) // msb
if(needAsave) if (needAsaveForArg0)
asmgen.out(" pla") asmgen.out(" pla")
} }
}
RegisterOrPair.AY -> { RegisterOrPair.AY -> {
if(needAsaveForArg0 && !asmgen.needAsaveForExpr(fcall.args[1])) {
// first 0 then 1
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if(needAsave) } else if(!needAsaveForArg0 && asmgen.needAsaveForExpr(fcall.args[1])) {
// first 1 then 0
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
} else {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if (needAsaveForArg0)
asmgen.out(" pha") asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
if(needAsave) if (needAsaveForArg0)
asmgen.out(" pla") asmgen.out(" pla")
} }
}
RegisterOrPair.XY -> { RegisterOrPair.XY -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
if(needAsave) if (needAsaveForArg0)
asmgen.out(" pha") asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.Y) // msb
if(needAsave) if (needAsaveForArg0)
asmgen.out(" pla") asmgen.out(" pla")
asmgen.out(" tax") asmgen.out(" tax")
} }
@ -1100,7 +1118,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcMsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) { private fun funcMsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val arg = fcall.args.single() val arg = fcall.args.single()
if (arg.type !in WordDatatypes) if (!arg.type.isWord)
throw AssemblyError("msb required word argument") throw AssemblyError("msb required word argument")
if (arg is PtNumber) if (arg is PtNumber)
throw AssemblyError("msb(const) should have been const-folded away") throw AssemblyError("msb(const) should have been const-folded away")
@ -1115,7 +1133,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0") RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
in Cx16VirtualRegisters -> { in Cx16VirtualRegisters -> {
val regname = resultRegister.name.lowercase() val regname = resultRegister.name.lowercase()
if(asmgen.isTargetCpu(CpuType.CPU65c02)) if(asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | stz cx16.$regname+1") asmgen.out(" lda $sourceName+1 | sta cx16.$regname | stz cx16.$regname+1")
else else
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1") asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
@ -1123,10 +1141,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("invalid reg") else -> throw AssemblyError("invalid reg")
} }
} else { } else {
if(arg is PtArrayIndexer && resultRegister in setOf(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)
@ -1199,7 +1221,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcLsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) { private fun funcLsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
val arg = fcall.args.single() val arg = fcall.args.single()
if (arg.type !in WordDatatypes) if (!arg.type.isWord)
throw AssemblyError("lsb required word argument") throw AssemblyError("lsb required word argument")
if (arg is PtNumber) if (arg is PtNumber)
throw AssemblyError("lsb(const) should have been const-folded away") throw AssemblyError("lsb(const) should have been const-folded away")
@ -1215,7 +1237,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0") RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
in Cx16VirtualRegisters -> { in Cx16VirtualRegisters -> {
val regname = resultRegister.name.lowercase() val regname = resultRegister.name.lowercase()
if(asmgen.isTargetCpu(CpuType.CPU65c02)) if(asmgen.isTargetCpu(CpuType.CPU65C02))
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1") asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
else else
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1") asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
@ -1223,9 +1245,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
else -> throw AssemblyError("invalid reg") else -> throw AssemblyError("invalid reg")
} }
} else { } else {
if(arg is PtArrayIndexer && resultRegister in setOf(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)
@ -1282,12 +1307,15 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) { private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) {
val signature = BuiltinFunctions.getValue(call.name) val signature = BuiltinFunctions.getValue(call.name)
val callConv = signature.callConvention(call.args.map { it.type}) val callConv = signature.callConvention(call.args.map {
require(it.type.isNumericOrBool)
it.type.base
})
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)
@ -1301,7 +1329,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true asmgen.subroutineExtra(scope).usedFloatEvalResultVar2 = true
val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position) val variable = PtIdentifier(subroutineFloatEvalResultVar2, DataType.FLOAT, value.position)
val addr = PtAddressOf(value.position) val addr = PtAddressOf(DataType.pointer(BaseDataType.FLOAT), value.position)
addr.add(variable) addr.add(variable)
addr.parent = call addr.parent = call
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT) asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT)
@ -1317,11 +1345,11 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
when { when {
conv.variable -> { conv.variable -> {
val varname = "prog8_lib.func_${call.name}._arg_${paramName}" val varname = "prog8_lib.func_${call.name}._arg_${paramName}"
val src = when (conv.dt) { val src = when {
DataType.FLOAT -> getSourceForFloat(value) conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
in PassByReferenceDatatypes -> { 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)
@ -1330,16 +1358,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
AsmAssignSource.fromAstSource(value, program, asmgen) AsmAssignSource.fromAstSource(value, program, asmgen)
} }
} }
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, conv.dt, null, value.position, variableAsmName = varname) val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.forDt(conv.dt), null, value.position, variableAsmName = varname)
val assign = AsmAssignment(src, tgt, program.memsizer, value.position) val assign = AsmAssignment(src, listOf(tgt), program.memsizer, value.position)
asmgen.translateNormalAssignment(assign, scope) asmgen.translateNormalAssignment(assign, scope)
} }
conv.reg != null -> { conv.reg != null -> {
val src = when (conv.dt) { val src = when {
DataType.FLOAT -> getSourceForFloat(value) conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
in PassByReferenceDatatypes -> { 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)
@ -1349,7 +1377,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
} }
} }
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, value.position, null, asmgen) val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, value.position, null, asmgen)
val assign = AsmAssignment(src, tgt, program.memsizer, value.position) val assign = AsmAssignment(src, listOf(tgt), program.memsizer, value.position)
asmgen.translateNormalAssignment(assign, scope) asmgen.translateNormalAssignment(assign, scope)
} }
else -> throw AssemblyError("callconv") else -> throw AssemblyError("callconv")

View File

@ -1,40 +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.*
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 value
return if(returntype==null)
emptyList()
else {
val register = when (returntype!!) {
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
}
listOf(Pair(register, returntype!!))
}
}
}
}
internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
return when(returntype) {
in ByteDatatypesWithBoolean -> RegisterOrStatusflag(RegisterOrPair.A, null)
in WordDatatypes -> RegisterOrStatusflag(RegisterOrPair.AY, null)
DataType.FLOAT -> RegisterOrStatusflag(RegisterOrPair.FAC1, null)
null -> null
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
}
}

View File

@ -33,24 +33,31 @@ 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(iterableDt) { when {
DataType.ARRAY_B, DataType.ARRAY_UB -> { iterableDt.isByteArray -> forOverNonconstByteRange(stmt, iterableDt, range)
iterableDt.isWordArray && !iterableDt.isSplitWordArray -> forOverNonconstWordRange(stmt, iterableDt, range)
else -> throw AssemblyError("range expression can only be byte or word")
}
asmgen.loopEndLabels.removeLast()
}
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) val varname = asmgen.asmVariableName(stmt.variable)
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
if (stepsize==-1 && range.to.asConstInteger()==0) { asmgen.assignExpressionToVariable(range.from, varname, iterableDt.elementType())
when (stepsize) {
-1 if range.to.asConstInteger()==0 -> {
// simple loop downto 0 step -1 // simple loop downto 0 step -1
asmgen.out(loopLabel) asmgen.out(loopLabel)
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
@ -60,7 +67,7 @@ internal class ForLoopsAsmGen(
cmp #255 cmp #255
bne $loopLabel""") bne $loopLabel""")
} }
else if (stepsize==-1 && range.to.asConstInteger()==1) { -1 if range.to.asConstInteger()==1 -> {
// simple loop downto 1 step -1 // simple loop downto 1 step -1
asmgen.out(loopLabel) asmgen.out(loopLabel)
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
@ -68,30 +75,41 @@ internal class ForLoopsAsmGen(
dec $varname dec $varname
bne $loopLabel""") bne $loopLabel""")
} }
else if (stepsize==1 || stepsize==-1) { 1, -1 -> forOverBytesRangeStepOne(range, varname, iterableDt, loopLabel, endLabel, stmt)
// bytes array, step 1 or -1 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" val incdec = if(stepsize==1) "inc" else "dec"
// loop over byte range via loopvar
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false) 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 // pre-check for end already reached
if(iterableDt==DataType.ARRAY_B) { if(iterableDt.isSignedByteArray) {
asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0) { if(stepsize<0) {
asmgen.out(""" asmgen.out("""
clc clc
sbc $varname sbc $varname
bvc + bvc +
eor #${'$'}80 eor #$80
+ bpl $endLabel""") + bpl $endLabel""")
} }
else else {
asmgen.out(""" asmgen.out("""
sec sec
sbc $varname sbc $varname
bvc + bvc +
eor #${'$'}80 eor #$80
+ bmi $endLabel""") + bmi $endLabel""")
}
} else { } else {
if(stepsize<0) { if(stepsize<0) {
asmgen.out(""" asmgen.out("""
@ -100,15 +118,15 @@ internal class ForLoopsAsmGen(
bcs $endLabel bcs $endLabel
+""") +""")
} }
else else {
asmgen.out(" cmp $varname | bcc $endLabel") asmgen.out(" cmp $varname | bcc $endLabel")
asmgen.out(" sta $modifiedLabel+1") }
} }
asmgen.out(loopLabel) asmgen.out(loopLabel)
asmgen.translate(stmt.statements) asmgen.translate(forloop.statements)
asmgen.out(""" asmgen.out("""
lda $varname lda $varname
$modifiedLabel cmp #0 ; modified cmp $toValueVar
beq $endLabel beq $endLabel
$incdec $varname""") $incdec $varname""")
asmgen.jmp(loopLabel) asmgen.jmp(loopLabel)
@ -116,26 +134,75 @@ $modifiedLabel cmp #0 ; modified
} else { } else {
// bytes, step >= 2 or <= -2 // use self-modifying code to store the loop end comparison value
val modifiedLabel = asmgen.makeLabel("for_modified")
// loop over byte range via loopvar asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
// pre-check for end already reached // pre-check for end already reached
if(iterableDt==DataType.ARRAY_B) { 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.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") asmgen.out(" sta $modifiedLabel+1")
if(stepsize<0) if(stepsize<0)
asmgen.out(""" asmgen.out("""
clc clc
sbc $varname sbc $varname
bvc + bvc +
eor #${'$'}80 eor #$80
+ bpl $endLabel""") + bpl $endLabel""")
else else
asmgen.out(""" asmgen.out("""
sec sec
sbc $varname sbc $varname
bvc + bvc +
eor #${'$'}80 eor #$80
+ bmi $endLabel""") + bmi $endLabel""")
} else { } else {
if(stepsize<0) if(stepsize<0)
@ -144,12 +211,15 @@ $modifiedLabel cmp #0 ; modified
beq + beq +
bcs $endLabel bcs $endLabel
+""") +""")
else else {
asmgen.out(" cmp $varname | bcc $endLabel") asmgen.out(" cmp $varname | bcc $endLabel")
}
asmgen.out(" sta $modifiedLabel+1") asmgen.out(" sta $modifiedLabel+1")
} }
asmgen.out(loopLabel) asmgen.out(loopLabel)
asmgen.translate(stmt.statements) 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) { if(stepsize>0) {
asmgen.out(""" asmgen.out("""
lda $varname lda $varname
@ -170,8 +240,12 @@ $modifiedLabel cmp #0 ; modified
} }
asmgen.out(endLabel) asmgen.out(endLabel)
} }
}
DataType.ARRAY_W, DataType.ARRAY_UW -> { 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) val varname = asmgen.asmVariableName(stmt.variable)
assignLoopvarWord(stmt, range) assignLoopvarWord(stmt, range)
if(stepsize==-1 && range.to.asConstInteger()==0) { if(stepsize==-1 && range.to.asConstInteger()==0) {
@ -207,15 +281,62 @@ $modifiedLabel cmp #0 ; modified
asmgen.jmp(loopLabel) asmgen.jmp(loopLabel)
asmgen.out(endLabel) asmgen.out(endLabel)
} }
else if (stepsize == 1 || stepsize == -1) { else if (stepsize == 1 || stepsize == -1)
// words, step 1 or -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) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel) precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out(""" 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
@ -239,8 +360,13 @@ $modifiedLabel2 cmp #0 ; modified
} }
asmgen.out(endLabel) asmgen.out(endLabel)
} }
else if (stepsize > 0) { }
private fun forOverWordsRangeStepGreaterOne(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
// (u)words, step >= 2 // (u)words, step >= 2
val stepsize = range.step.asConstInteger()!!
val modifiedLabel = asmgen.makeLabel("for_modified")
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel) precheckFromToWord(iterableDt, stepsize, varname, endLabel)
asmgen.out(""" asmgen.out("""
@ -249,7 +375,8 @@ $modifiedLabel2 cmp #0 ; modified
$loopLabel""") $loopLabel""")
asmgen.translate(stmt.statements) asmgen.translate(stmt.statements)
if (iterableDt == DataType.ARRAY_UW) { asmgen.romableError("self-modifying code (forloop over word range)", stmt.position) // TODO fix romable; there is self-modifying code below
if (iterableDt.isUnsignedWordArray) {
asmgen.out(""" asmgen.out("""
lda $varname lda $varname
clc clc
@ -285,17 +412,20 @@ $modifiedLabel lda #0 ; modified
$endLabel""") $endLabel""")
} }
} }
else {
private fun forOverWordsRangeStepGreaterOneDescending(range: PtRange, varname: String, iterableDt: DataType, loopLabel: String, endLabel: String, stmt: PtForLoop) {
// (u)words, step <= -2 // (u)words, step <= -2
val stepsize = range.step.asConstInteger()!!
val modifiedLabel = asmgen.makeLabel("for_modified")
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY) asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
precheckFromToWord(iterableDt, stepsize, varname, endLabel) 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(""" 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
@ -314,17 +444,11 @@ $modifiedLabel sbc #0 ; modified
+ 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) {
// pre-check for end already reached. // pre-check for end already reached.
// 'to' is in AY, do NOT clobber this! // 'to' is in AY, do NOT clobber this!
if(iterableDt==DataType.ARRAY_W) { if(iterableDt.isSignedWordArray) {
if(stepsize<0) if(stepsize<0)
asmgen.out(""" asmgen.out("""
sta P8ZP_SCRATCH_W2 ; to sta P8ZP_SCRATCH_W2 ; to
@ -334,7 +458,7 @@ $endLabel""")
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""")
@ -345,7 +469,7 @@ $endLabel""")
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 {
@ -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(iterableDt) { when {
DataType.STR -> { iterableDt.isString -> {
if(asmgen.options.romable) {
val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
asmgen.out(""" asmgen.out("""
lda #<$iterableName ldy #0
ldy #>$iterableName sty $indexVar
sta $loopLabel+1 $loopLabel lda $iterableName,y
sty $loopLabel+2
$loopLabel lda ${65535.toHex()} ; modified
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 {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_BOOL -> {
val indexVar = asmgen.makeLabel("for_index") val indexVar = asmgen.makeLabel("for_index")
asmgen.out(""" asmgen.out("""
ldy #0 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 -> {
val indexVar = if(asmgen.options.romable)
asmgen.createTempVarReused(iterableDt.elementType().base, false, stmt)
else
asmgen.makeLabel("for_index")
asmgen.out("""
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,8 +564,9 @@ $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) {
// allocate index var on ZP if possible, otherwise inline
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors) val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold( result.fold(
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") }, success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
@ -432,51 +575,14 @@ $loopLabel sty $indexVar
} else { } else {
asmgen.out("$indexVar .byte 0") asmgen.out("$indexVar .byte 0")
} }
asmgen.out(endLabel)
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
val length = numElements * 2
val indexVar = asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
ldy #0
$loopLabel sty $indexVar
lda $iterableName,y
sta $loopvarName
lda $iterableName+1,y
sta $loopvarName+1""")
asmgen.translate(stmt.statements)
if(length<=127) {
asmgen.out("""
ldy $indexVar
iny
iny
cpy #$length
beq $endLabel
bne $loopLabel""")
} else {
// length is 128 words, 256 bytes
asmgen.out("""
ldy $indexVar
iny
iny
bne $loopLabel
beq $endLabel""")
}
if(length>=16) {
// allocate index var on ZP if possible
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
)
} else {
asmgen.out("$indexVar .byte 0")
} }
asmgen.out(endLabel) asmgen.out(endLabel)
} }
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT -> { 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
@ -486,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
@ -501,8 +607,9 @@ $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) {
// allocate index var on ZP if possible, otherwise inline
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors) val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold( result.fold(
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") }, success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
@ -511,9 +618,55 @@ $loopLabel sty $indexVar
} else { } else {
asmgen.out("$indexVar .byte 0") asmgen.out("$indexVar .byte 0")
} }
}
asmgen.out(endLabel) asmgen.out(endLabel)
} }
DataType.ARRAY_F -> { iterableDt.isWordArray -> {
val indexVar = if(asmgen.options.romable)
asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
else
asmgen.makeLabel("for_index")
val loopvarName = asmgen.asmVariableName(stmt.variable)
asmgen.out("""
ldy #0
$loopLabel sty $indexVar
lda $iterableName,y
sta $loopvarName
lda $iterableName+1,y
sta $loopvarName+1""")
asmgen.translate(stmt.statements)
if(numElements<=127u) {
asmgen.out("""
ldy $indexVar
iny
iny
cpy #${numElements*2u}
beq $endLabel
bne $loopLabel""")
} else {
// array size is 128 words, 256 bytes
asmgen.out("""
ldy $indexVar
iny
iny
bne $loopLabel
beq $endLabel""")
}
if(!asmgen.options.romable) {
if(numElements>=16u) {
// allocate index var on ZP if possible, otherwise inline
val result = zeropage.allocate(indexVar, DataType.UBYTE, null, stmt.position, asmgen.errors)
result.fold(
success = { (address, _, _)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
)
} else {
asmgen.out("$indexVar .byte 0")
}
}
asmgen.out(endLabel)
}
iterableDt.isFloatArray -> {
throw AssemblyError("for loop with floating point variables is not supported") throw AssemblyError("for loop with floating point variables is not supported")
} }
else -> throw AssemblyError("can't iterate over $iterableDt") else -> throw AssemblyError("can't iterate over $iterableDt")
@ -524,12 +677,12 @@ $loopLabel sty $indexVar
private fun translateForOverConstRange(stmt: PtForLoop, iterableDt: DataType, range: IntProgression) { private fun translateForOverConstRange(stmt: PtForLoop, iterableDt: DataType, range: IntProgression) {
if (range.isEmpty() || range.step==0) if (range.isEmpty() || range.step==0)
throw AssemblyError("empty range or step 0") throw AssemblyError("empty range or step 0")
if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) { if(iterableDt.isByteArray) {
if(range.last==range.first) return translateForSimpleByteRangeAsc(stmt, range) if(range.last==range.first) return translateForSimpleByteRangeAsc(stmt, range)
if(range.step==1 && range.last>range.first) return translateForSimpleByteRangeAsc(stmt, range) if(range.step==1 && range.last>range.first) return translateForSimpleByteRangeAsc(stmt, range)
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range, iterableDt==DataType.ARRAY_UB) if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range, iterableDt.isUnsignedByteArray)
} }
else if(iterableDt==DataType.ARRAY_W || iterableDt==DataType.ARRAY_UW) { else if(iterableDt.isWordArray) {
if(range.last==range.first) return translateForSimpleWordRangeAsc(stmt, range) if(range.last==range.first) return translateForSimpleWordRangeAsc(stmt, range)
if(range.step==1 && range.last>range.first) return translateForSimpleWordRangeAsc(stmt, range) if(range.step==1 && range.last>range.first) return translateForSimpleWordRangeAsc(stmt, range)
if(range.step==-1 && range.last<range.first) return translateForSimpleWordRangeDesc(stmt, range) if(range.step==-1 && range.last<range.first) return translateForSimpleWordRangeDesc(stmt, range)
@ -539,8 +692,8 @@ $loopLabel sty $indexVar
val loopLabel = asmgen.makeLabel("for_loop") val loopLabel = asmgen.makeLabel("for_loop")
val endLabel = asmgen.makeLabel("for_end") val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.add(endLabel) asmgen.loopEndLabels.add(endLabel)
when(iterableDt) { when {
DataType.ARRAY_B, DataType.ARRAY_UB -> { iterableDt.isByteArray -> {
// loop over byte range via loopvar, step >= 2 or <= -2 // loop over byte range via loopvar, step >= 2 or <= -2
val varname = asmgen.asmVariableName(stmt.variable) val varname = asmgen.asmVariableName(stmt.variable)
asmgen.out(""" asmgen.out("""
@ -605,7 +758,7 @@ $loopLabel""")
} }
asmgen.out(endLabel) asmgen.out(endLabel)
} }
DataType.ARRAY_W, DataType.ARRAY_UW -> { iterableDt.isWordArray && !iterableDt.isSplitWordArray -> {
// loop over word range via loopvar, step >= 2 or <= -2 // loop over word range via loopvar, step >= 2 or <= -2
val varname = asmgen.asmVariableName(stmt.variable) val varname = asmgen.asmVariableName(stmt.variable)
when (range.step) { when (range.step) {

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
@ -15,10 +16,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// just ignore any result values from the function call. // just ignore any result values from the function call.
} }
// TODO tweak subroutine call convention to also make use of X register to pass a third byte? internal fun optimizeIntArgsViaCpuRegisters(params: List<PtSubroutineParameter>): Boolean {
internal fun optimizeIntArgsViaRegisters(sub: PtSub) = // When the parameter(s) are passed via an explicit register or register pair,
(sub.parameters.size==1 && sub.parameters[0].type in IntegerDatatypesWithBoolean) // we consider them NOT to be optimized into (possibly different) CPU registers.
|| (sub.parameters.size==2 && sub.parameters[0].type in ByteDatatypesWithBoolean && sub.parameters[1].type in ByteDatatypesWithBoolean) // Just load them in whatever the register spec says.
return when (params.size) {
1 -> params[0].type.isIntegerOrBool && params[0].register == null
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null
else -> false
}
}
internal fun translateFunctionCall(call: PtFunctionCall) { internal fun translateFunctionCall(call: PtFunctionCall) {
// Output only the code to set up the parameters and perform the actual call // Output only the code to set up the parameters and perform the actual call
@ -26,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) {
@ -45,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!
@ -122,23 +139,25 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} }
} }
else if(sub is PtSub) { else if(sub is PtSub) {
if(optimizeIntArgsViaRegisters(sub)) { val parameters = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
if(sub.parameters.size==1) { if(optimizeIntArgsViaCpuRegisters(parameters)) {
val register = if (sub.parameters[0].type in ByteDatatypesWithBoolean) RegisterOrPair.A else RegisterOrPair.AY // Note that if the args fit into cpu registers, we don't concern ourselves here
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], register) // if they should be put into regular subroutine parameter variables, or the R0-R15 register variables.
} else { // That is now up to the subroutine itself.
// 2 byte params, second in Y, first in A useCpuRegistersForArgs(call.args, sub)
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], RegisterOrPair.A)
if(asmgen.needAsaveForExpr(call.args[1]))
asmgen.out(" pha")
argumentViaRegister(sub, IndexedValue(1, sub.parameters[1]), call.args[1], RegisterOrPair.Y)
if(asmgen.needAsaveForExpr(call.args[1]))
asmgen.out(" pla")
}
} else { } else {
// arguments via variables // arguments via variables
for(arg in sub.parameters.withIndex().zip(call.args)) val paramValues = parameters.zip(call.args)
argumentViaVariable(sub, arg.first.value, arg.second) val (normalParams, registerParams) = paramValues.partition { (it.first.register == null) }
if (normalParams.isNotEmpty()) {
for (p in normalParams)
argumentViaVariable(sub, p.first, p.second)
}
if (registerParams.isNotEmpty()) {
// the R0-R15 'registers' are not really registers. They're just special variables.
for (p in registerParams)
argumentViaVariable(sub, p.first, p.second)
}
} }
asmgen.out(" jsr $subAsmName") asmgen.out(" jsr $subAsmName")
} }
@ -147,11 +166,45 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// remember: dealing with the X register and/or dealing with return values is the responsibility of the caller // remember: dealing with the X register and/or dealing with return values is the responsibility of the caller
} }
private fun useCpuRegistersForArgs(args: List<PtExpression>, sub: PtSub) {
val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
when(params.size) {
1 -> {
val register = if (params[0].type.isByteOrBool) RegisterOrPair.A else RegisterOrPair.AY
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], register)
}
2 -> {
if(params[0].type.isByteOrBool && params[1].type.isByteOrBool) {
// 2 byte params, second in Y, first in A
if(asmgen.needAsaveForExpr(args[0]) && !asmgen.needAsaveForExpr(args[1])) {
// first 0 then 1
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
} else if(!asmgen.needAsaveForExpr(args[0]) && asmgen.needAsaveForExpr(args[1])) {
// first 1 then 0
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
} else {
argumentViaRegister(sub, IndexedValue(0, params[0]), args[0], RegisterOrPair.A)
if (asmgen.needAsaveForExpr(args[1]))
asmgen.out(" pha")
argumentViaRegister(sub, IndexedValue(1, params[1]), args[1], RegisterOrPair.Y)
if (asmgen.needAsaveForExpr(args[1]))
asmgen.out(" pla")
}
} else {
throw AssemblyError("cannot use registers for word+byte")
}
}
else -> throw AssemblyError("cannot use cpu registers for >2 arguments")
}
}
private fun usesOtherRegistersWhileEvaluating(arg: PtExpression): Boolean { private fun usesOtherRegistersWhileEvaluating(arg: PtExpression): Boolean {
return when(arg) { return when(arg) {
is PtBuiltinFunctionCall -> { is PtBuiltinFunctionCall -> {
if (arg.name == "lsb" || arg.name == "msb") if (arg.name in arrayOf("lsb", "msb", "lsw", "msw"))
return usesOtherRegistersWhileEvaluating(arg.args[0]) return usesOtherRegistersWhileEvaluating(arg.args[0])
if (arg.name == "mkword") if (arg.name == "mkword")
return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1]) return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1])
@ -182,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 {
@ -209,9 +262,16 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
if(!isArgumentTypeCompatible(value.type, parameter.type)) if(!isArgumentTypeCompatible(value.type, parameter.type))
throw AssemblyError("argument type incompatible") throw AssemblyError("argument type incompatible")
val reg = parameter.register
if(reg!=null) {
require(reg in Cx16VirtualRegisters) { "can only use R0-R15 'registers' here" }
val varName = "cx16.${reg.name.lowercase()}"
asmgen.assignExpressionToVariable(value, varName, parameter.type)
} else {
val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name) val varName = asmgen.asmVariableName(sub.scopedName + "." + parameter.name)
asmgen.assignExpressionToVariable(value, varName, parameter.type) asmgen.assignExpressionToVariable(value, varName, parameter.type)
} }
}
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag { private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag {
// pass argument via a register parameter // pass argument via a register parameter
@ -226,7 +286,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
val register = paramRegister.registerOrPair val register = paramRegister.registerOrPair
val requiredDt = parameter.value.type val requiredDt = parameter.value.type
if(requiredDt!=value.type) { if(requiredDt!=value.type) {
if(value.type largerThan requiredDt) if(value.type.largerSizeThan(requiredDt))
throw AssemblyError("can only convert byte values to word param types") throw AssemblyError("can only convert byte values to word param types")
} }
if (statusflag!=null) { if (statusflag!=null) {
@ -265,23 +325,22 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
else { else {
// via register or register pair // via register or register pair
register!! register!!
if(requiredDt largerThan 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.UBYTE) asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE)
asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", value.type) 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 {
val scope = value.definingISub() val scope = value.definingISub()
val target: AsmAssignTarget = val target: AsmAssignTarget =
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters)) if(parameter.value.type.isByte && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register) AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, parameter.value.type, scope, value.position, register = register)
else { else {
val signed = parameter.value.type == DataType.BYTE || parameter.value.type == DataType.WORD AsmAssignTarget.fromRegisters(register, parameter.value.type.isSigned, value.position, scope, asmgen)
AsmAssignTarget.fromRegisters(register, signed, value.position, scope, asmgen)
} }
val src = if(value.type in PassByReferenceDatatypes) { 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)
@ -291,7 +350,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} else { } else {
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target) AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
} }
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, target.position), scope) asmgen.translateNormalAssignment(AsmAssignment(src, listOf(target), program.memsizer, target.position), scope)
} }
return RegisterOrStatusflag(register, null) return RegisterOrStatusflag(register, null)
} }
@ -300,18 +359,18 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean { private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
if(argType isAssignableTo paramType) if(argType isAssignableTo paramType)
return true return true
if(argType==DataType.BOOL && paramType==DataType.BOOL) if(argType.isBool && paramType.isBool)
return true return true
if(argType in ByteDatatypes && paramType in ByteDatatypes) if(argType.isByte && paramType.isByte)
return true return true
if(argType in WordDatatypes && paramType in WordDatatypes) if(argType.isWord && paramType.isWord)
return true return true
// we have a special rule for some types. // we have a special rule for some types.
// strings are assignable to UWORD, for example, and vice versa // strings are assignable to UWORD, for example, and vice versa
if(argType==DataType.STR && paramType==DataType.UWORD) if(argType.isString && paramType.isUnsignedWord)
return true return true
if(argType==DataType.UWORD && paramType == DataType.STR) if(argType.isUnsignedWord && paramType.isString)
return true return true
return false return false

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,362 @@
package prog8.codegen.cpu6502
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
import prog8.codegen.cpu6502.assignment.AssignmentAsmGen
import prog8.codegen.cpu6502.assignment.TargetStorageKind
internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, private val assignmentAsmGen: AssignmentAsmGen, private val errors: IErrorReporter) {
internal fun assignIfExpression(target: AsmAssignTarget, expr: PtIfExpression) {
require(target.datatype==expr.type || target.datatype.isUnsignedWord && expr.type.isString)
val falseLabel = asmgen.makeLabel("ifexpr_false")
val endLabel = asmgen.makeLabel("ifexpr_end")
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
when {
expr.type.isByteOrBool -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
}
expr.type.isWord || expr.type.isString -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
}
expr.type.isFloat -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.FAC1, true)
asmgen.out(endLabel)
asmgen.assignRegister(RegisterOrPair.FAC1, target)
}
else -> throw AssemblyError("weird dt")
}
}
private fun evalIfExpressionConditonAndBranchWhenFalse(condition: PtExpression, falseLabel: String) {
when (condition) {
is PtBinaryExpression -> {
val rightDt = condition.right.type
return when {
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
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")
}
}
}
private fun translateIfExpressionByteConditionBranch(condition: PtBinaryExpression, falseLabel: String) {
val signed = condition.left.type.isSigned
val constValue = condition.right.asConstInteger()
if(constValue==0) {
return translateIfCompareWithZeroByteBranch(condition, signed, falseLabel)
}
when(condition.operator) {
"==" -> {
// if X==value
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
asmgen.cmpAwithByteValue(condition.right, false)
asmgen.out(" bne $falseLabel")
}
"!=" -> {
// if X!=value
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, signed)
asmgen.cmpAwithByteValue(condition.right, false)
asmgen.out(" beq $falseLabel")
}
in LogicalOperators -> {
val regAtarget = AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.BOOL, condition.definingISub(), condition.position, register=RegisterOrPair.A)
if (assignmentAsmGen.optimizedLogicalExpr(condition, regAtarget)) {
asmgen.out(" beq $falseLabel")
} else {
errors.warn("SLOW FALLBACK FOR 'IFEXPR' CODEGEN - ask for support", condition.position) // should not occur ;-)
asmgen.assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
}
else -> {
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
// TODO: special cases for <, <=, >, >= above.
asmgen.assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
}
}
private fun translateIfExpressionWordConditionBranch(condition: PtBinaryExpression, falseLabel: String) {
// TODO can we reuse this whole thing from IfElse ?
val constValue = condition.right.asConstInteger()
if(constValue!=null) {
if (constValue == 0) {
when (condition.operator) {
"==" -> return translateWordExprIsZero(condition.left, falseLabel)
"!=" -> return translateWordExprIsNotZero(condition.left, falseLabel)
}
}
if (constValue != 0) {
when (condition.operator) {
"==" -> return translateWordExprEqualsNumber(condition.left, constValue, falseLabel)
"!=" -> return translateWordExprNotEqualsNumber(condition.left, constValue, falseLabel)
}
}
}
val variable = condition.right as? PtIdentifier
if(variable!=null) {
when (condition.operator) {
"==" -> return translateWordExprEqualsVariable(condition.left, variable, falseLabel)
"!=" -> return translateWordExprNotEqualsVariable(condition.left, variable, falseLabel)
}
}
// TODO don't store condition as expression result but just use the flags, like a normal PtIfElse translation does
asmgen.assignConditionValueToRegisterAndTest(condition)
asmgen.out(" beq $falseLabel")
}
private fun translateIfExpressionFloatConditionBranch(condition: PtBinaryExpression, elseLabel: String) {
val constValue = (condition.right as? PtNumber)?.number
if(constValue==0.0) {
if (condition.operator == "==") {
// if FL==0.0
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | cmp #0 | bne $elseLabel")
return
} else if(condition.operator=="!=") {
// if FL!=0.0
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.FAC1, true)
asmgen.out(" jsr floats.SIGN | cmp #0 | beq $elseLabel")
return
}
}
when(condition.operator) {
"==" -> {
asmgen.translateFloatsEqualsConditionIntoA(condition.left, condition.right)
asmgen.out(" beq $elseLabel")
}
"!=" -> {
asmgen.translateFloatsEqualsConditionIntoA(condition.left, condition.right)
asmgen.out(" bne $elseLabel")
}
"<" -> {
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, false)
asmgen.out(" beq $elseLabel")
}
"<=" -> {
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, true)
asmgen.out(" beq $elseLabel")
}
">" -> {
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, true)
asmgen.out(" bne $elseLabel")
}
">=" -> {
asmgen.translateFloatsLessConditionIntoA(condition.left, condition.right, false)
asmgen.out(" bne $elseLabel")
}
else -> throw AssemblyError("expected comparison operator")
}
}
private fun translateWordExprNotEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
// if w!=variable
// TODO reuse code from ifElse?
val varRight = asmgen.asmVariableName(variable)
if(expr is PtIdentifier) {
val varLeft = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varLeft
cmp $varRight
bne +
lda $varLeft+1
cmp $varRight+1
beq $falseLabel
+""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out("""
cmp $varRight
bne +
cpy $varRight+1
beq $falseLabel
+""")
}
}
private fun translateWordExprEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
// if w==variable
// TODO reuse code from ifElse?
val varRight = asmgen.asmVariableName(variable)
if(expr is PtIdentifier) {
val varLeft = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varLeft
cmp $varRight
bne $falseLabel
lda $varLeft+1
cmp $varRight+1
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out("""
cmp $varRight
bne $falseLabel
cpy $varRight+1
bne $falseLabel""")
}
}
private fun translateWordExprNotEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
// if w!=number
// TODO reuse code from ifElse?
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varname
cmp #<$number
bne +
lda $varname+1
cmp #>$number
beq $falseLabel
+""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out("""
cmp #<$number
bne +
cpy #>$number
beq $falseLabel
+""")
}
}
private fun translateWordExprEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
// if w==number
// TODO reuse code from ifElse?
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varname
cmp #<$number
bne $falseLabel
lda $varname+1
cmp #>$number
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out( """
cmp #<$number
bne $falseLabel
cpy #>$number
bne $falseLabel""")
}
}
private fun translateWordExprIsNotZero(expr: PtExpression, falseLabel: String) {
// if w!=0
// TODO reuse code from ifElse?
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varname
ora $varname+1
beq $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
}
}
private fun translateWordExprIsZero(expr: PtExpression, falseLabel: String) {
// if w==0
// TODO reuse code from ifElse?
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
lda $varname
ora $varname+1
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
}
}
private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) {
// optimized code for byte comparisons with 0
val useBIT = asmgen.checkIfConditionCanUseBIT(condition)
if(useBIT!=null) {
// use a BIT instruction to test for bit 7 or 6 set/clear
val (testForBitSet, variable, bitmask) = useBIT
when (bitmask) {
128 -> {
// test via bit + N flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) asmgen.out(" bpl $falseLabel")
else asmgen.out(" bmi $falseLabel")
return
}
64 -> {
// test via bit + V flag
asmgen.out(" bit ${variable.name}")
if(testForBitSet) asmgen.out(" bvc $falseLabel")
else asmgen.out(" bvs $falseLabel")
return
}
else -> throw AssemblyError("BIT can only work on bits 7 and 6")
}
}
asmgen.assignConditionValueToRegisterAndTest(condition.left)
when (condition.operator) {
"==" -> asmgen.out(" bne $falseLabel")
"!=" -> asmgen.out(" beq $falseLabel")
">" -> {
if(signed) asmgen.out(" bmi $falseLabel | beq $falseLabel")
else asmgen.out(" beq $falseLabel")
}
">=" -> {
if(signed) asmgen.out(" bmi $falseLabel")
else { /* always true for unsigned */ }
}
"<" -> {
if(signed) asmgen.out(" bpl $falseLabel")
else asmgen.jmp(falseLabel)
}
"<=" -> {
if(signed) {
// inverted '>'
asmgen.out("""
beq +
bpl $falseLabel
+""")
} else asmgen.out(" bne $falseLabel")
}
else -> throw AssemblyError("expected comparison operator")
}
}
}

View File

@ -39,22 +39,21 @@ internal class ProgramAndVarsGen(
// the global list of all floating point constants for the whole program // the global list of all floating point constants for the whole program
asmgen.out("; global float constants") asmgen.out("; global float constants")
for (flt in allocator.globalFloatConsts) { for (flt in allocator.globalFloatConsts) {
val floatFill = compTarget.machine.getFloatAsmBytes(flt.key) val floatFill = compTarget.getFloatAsmBytes(flt.key)
val floatvalue = flt.key val floatvalue = flt.key
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue") asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
} }
memorySlabs() memorySlabs()
tempVars()
footer() footer()
} }
} }
private fun header() { private fun header() {
val ourName = this.javaClass.name val ourName = this.javaClass.name
val cpu = when(compTarget.machine.cpu) { val cpu = when(compTarget.cpu) {
CpuType.CPU6502 -> "6502" CpuType.CPU6502 -> "6502"
CpuType.CPU65c02 -> "w65c02" CpuType.CPU65C02 -> "w65c02"
else -> "unsupported" else -> "unsupported"
} }
@ -87,7 +86,30 @@ internal class ProgramAndVarsGen(
} }
} }
if(options.compTarget.customLauncher.isNotEmpty()) {
asmgen.out("; ---- custom launcher assembler program ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out("prog8_program_start\t; start of program label")
for(line in options.compTarget.customLauncher) {
asmgen.out(line)
}
return
}
if(options.output == OutputType.LIBRARY) {
asmgen.out("; ---- library assembler program ----")
asmgen.out("* = ${options.loadAddress.toHex()}")
asmgen.out("prog8_program_start\t; start of program label")
asmgen.out(" jmp p8b_main.p8s_start")
// note: the jmp above has 2 effects:
// 1. it prevents 64tass from stripping away all procs as unused code
// 2. it functions as the first entrypoint of the library, required anyway, to run the variable initialization/bss clear bootstrap code.
} else {
when (options.output) { when (options.output) {
OutputType.LIBRARY -> { }
OutputType.RAW -> { OutputType.RAW -> {
asmgen.out("; ---- raw assembler program ----") asmgen.out("; ---- raw assembler program ----")
asmgen.out("* = ${options.loadAddress.toHex()}") asmgen.out("* = ${options.loadAddress.toHex()}")
@ -102,8 +124,11 @@ internal class ProgramAndVarsGen(
OutputType.PRG -> { OutputType.PRG -> {
when (options.launcher) { when (options.launcher) {
CbmPrgLauncherType.BASIC -> { CbmPrgLauncherType.BASIC -> {
if (options.loadAddress != options.compTarget.machine.PROGRAM_LOAD_ADDRESS) { if (options.loadAddress != options.compTarget.PROGRAM_LOAD_ADDRESS) {
errors.err("BASIC output must have load address ${options.compTarget.machine.PROGRAM_LOAD_ADDRESS.toHex()}", program.position) errors.err(
"BASIC output must have load address ${options.compTarget.PROGRAM_LOAD_ADDRESS.toHex()}",
program.position
)
} }
asmgen.out("; ---- basic program with sys call ----") asmgen.out("; ---- basic program with sys call ----")
asmgen.out("* = ${options.loadAddress.toHex()}") asmgen.out("* = ${options.loadAddress.toHex()}")
@ -120,6 +145,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" jsr p8_sys_startup.init_system") asmgen.out(" jsr p8_sys_startup.init_system")
asmgen.out(" jsr p8_sys_startup.init_system_phase2") asmgen.out(" jsr p8_sys_startup.init_system_phase2")
} }
CbmPrgLauncherType.NONE -> { CbmPrgLauncherType.NONE -> {
// this is the same as RAW // this is the same as RAW
asmgen.out("; ---- program without basic sys call ----") asmgen.out("; ---- program without basic sys call ----")
@ -177,6 +203,7 @@ internal class ProgramAndVarsGen(
} }
} }
} }
}
private fun memorySlabs() { private fun memorySlabs() {
if(symboltable.allMemorySlabs.isNotEmpty()) { if(symboltable.allMemorySlabs.isNotEmpty()) {
@ -191,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) {
DataType.BOOL -> asmgen.out("$name .byte ?")
DataType.BYTE -> asmgen.out("$name .char ?")
DataType.UBYTE -> asmgen.out("$name .byte ?")
DataType.WORD -> asmgen.out("$name .sint ?")
DataType.UWORD -> asmgen.out("$name .word ?")
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.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
@ -219,49 +225,49 @@ internal class ProgramAndVarsGen(
var relocatedBssEnd = 0u var relocatedBssEnd = 0u
if(options.varsGolden) { if(options.varsGolden) {
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u || if(options.compTarget.BSSGOLDENRAM_START == 0u ||
options.compTarget.machine.BSSGOLDENRAM_END == 0u || options.compTarget.BSSGOLDENRAM_END == 0u ||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) { options.compTarget.BSSGOLDENRAM_END <= options.compTarget.BSSGOLDENRAM_START) {
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available") throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
} }
relocateBssVars = true relocateBssVars = true
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START relocatedBssStart = options.compTarget.BSSGOLDENRAM_START
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END relocatedBssEnd = options.compTarget.BSSGOLDENRAM_END
} }
else if(options.varsHighBank!=null) { else if(options.varsHighBank!=null) {
if(options.compTarget.machine.BSSHIGHRAM_START == 0u || if(options.compTarget.BSSHIGHRAM_START == 0u ||
options.compTarget.machine.BSSHIGHRAM_END == 0u || options.compTarget.BSSHIGHRAM_END == 0u ||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) { options.compTarget.BSSHIGHRAM_END <= options.compTarget.BSSHIGHRAM_START) {
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available") throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
} }
if(options.slabsHighBank!=null && options.varsHighBank!=options.slabsHighBank) if(options.slabsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
throw AssemblyError("slabs and vars high bank must be the same") throw AssemblyError("slabs and vars high bank must be the same")
relocateBssVars = true relocateBssVars = true
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START relocatedBssStart = options.compTarget.BSSHIGHRAM_START
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END relocatedBssEnd = options.compTarget.BSSHIGHRAM_END
} }
if(options.slabsGolden) { if(options.slabsGolden) {
if(options.compTarget.machine.BSSGOLDENRAM_START == 0u || if(options.compTarget.BSSGOLDENRAM_START == 0u ||
options.compTarget.machine.BSSGOLDENRAM_END == 0u || options.compTarget.BSSGOLDENRAM_END == 0u ||
options.compTarget.machine.BSSGOLDENRAM_END <= options.compTarget.machine.BSSGOLDENRAM_START) { options.compTarget.BSSGOLDENRAM_END <= options.compTarget.BSSGOLDENRAM_START) {
throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available") throw AssemblyError("current compilation target hasn't got the golden ram area properly defined or it is simply not available")
} }
relocateBssSlabs = true relocateBssSlabs = true
relocatedBssStart = options.compTarget.machine.BSSGOLDENRAM_START relocatedBssStart = options.compTarget.BSSGOLDENRAM_START
relocatedBssEnd = options.compTarget.machine.BSSGOLDENRAM_END relocatedBssEnd = options.compTarget.BSSGOLDENRAM_END
} }
else if(options.slabsHighBank!=null) { else if(options.slabsHighBank!=null) {
if(options.compTarget.machine.BSSHIGHRAM_START == 0u || if(options.compTarget.BSSHIGHRAM_START == 0u ||
options.compTarget.machine.BSSHIGHRAM_END == 0u || options.compTarget.BSSHIGHRAM_END == 0u ||
options.compTarget.machine.BSSHIGHRAM_END <= options.compTarget.machine.BSSHIGHRAM_START) { options.compTarget.BSSHIGHRAM_END <= options.compTarget.BSSHIGHRAM_START) {
throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available") throw AssemblyError("current compilation target hasn't got the high ram area properly defined or it is simply not available")
} }
if(options.varsHighBank!=null && options.varsHighBank!=options.slabsHighBank) if(options.varsHighBank!=null && options.varsHighBank!=options.slabsHighBank)
throw AssemblyError("slabs and vars high bank must be the same") throw AssemblyError("slabs and vars high bank must be the same")
relocateBssSlabs = true relocateBssSlabs = true
relocatedBssStart = options.compTarget.machine.BSSHIGHRAM_START relocatedBssStart = options.compTarget.BSSHIGHRAM_START
relocatedBssEnd = options.compTarget.machine.BSSHIGHRAM_END relocatedBssEnd = options.compTarget.BSSHIGHRAM_END
} }
asmgen.out("; bss sections") asmgen.out("; bss sections")
@ -271,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)
@ -278,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")
@ -290,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()}\"")
} }
@ -318,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")
} }
@ -335,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
@ -349,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)
} }
@ -403,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
@ -421,22 +435,35 @@ 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()
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) { val params = sub.signature.children.filterIsInstance<PtSubroutineParameter>()
asmgen.out("; simple int arg(s) passed via register(s)") if(functioncallAsmGen.optimizeIntArgsViaCpuRegisters(params)) {
if(sub.parameters.size==1) { asmgen.out("; simple int arg(s) passed via cpu register(s)")
val dt = sub.parameters[0].type
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name) fun varname(param: PtSubroutineParameter): String =
if(dt in ByteDatatypesWithBoolean) if(param.register==null)
asmgen.assignRegister(RegisterOrPair.A, target) param.name
else else
asmgen.assignRegister(RegisterOrPair.AY, target) param.register!!.asScopedNameVirtualReg(param.type).joinToString(".")
} else {
require(sub.parameters.size==2) when(params.size) {
// 2 simple byte args, first in A, second in Y 1 -> {
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[0].type, sub, sub.parameters[0].position, variableAsmName = sub.parameters[0].name) val dt = params[0].type
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, sub.parameters[1].type, sub, sub.parameters[1].position, variableAsmName = sub.parameters[1].name) val target = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, dt, sub, params[0].position, variableAsmName = varname(params[0]))
if(dt.isByteOrBool)
asmgen.assignRegister(RegisterOrPair.A, target) // single byte in A
else
asmgen.assignRegister(RegisterOrPair.AY, target) // word in AY
}
2 -> {
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, params[0].type, sub, params[0].position, variableAsmName = varname(params[0]))
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, params[1].type, sub, params[1].position, variableAsmName = varname(params[1]))
if(params[0].type.isByteOrBool && params[1].type.isByteOrBool) {
// 2 byte args, first in A, second in Y
asmgen.assignRegister(RegisterOrPair.A, target1) asmgen.assignRegister(RegisterOrPair.A, target1)
asmgen.assignRegister(RegisterOrPair.Y, target2) asmgen.assignRegister(RegisterOrPair.Y, target2)
} else throw AssemblyError("cannot use registers for word+byte args")
}
else -> throw AssemblyError("cannot use registers for >2 args")
} }
} }
@ -444,27 +471,27 @@ 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) {
DataType.UBYTE -> asmgen.out("$name .byte ?") BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL -> asmgen.out("$name .byte ?")
DataType.UWORD -> asmgen.out("$name .word ?") BaseDataType.UWORD, BaseDataType.WORD -> asmgen.out("$name .word ?")
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.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")
} }
} }
if(asmGenInfo.usedFloatEvalResultVar1) if(asmGenInfo.usedFloatEvalResultVar1)
asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}") asmgen.out("$subroutineFloatEvalResultVar1 .fill ${options.compTarget.FLOAT_MEM_SIZE}")
if(asmGenInfo.usedFloatEvalResultVar2) if(asmGenInfo.usedFloatEvalResultVar2)
asmgen.out("$subroutineFloatEvalResultVar2 .fill ${options.compTarget.machine.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)
@ -519,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("+")
@ -544,7 +571,7 @@ internal class ProgramAndVarsGen(
private fun getZpStringVarsWithInitvalue(): Collection<ZpStringWithInitial> { private fun getZpStringVarsWithInitvalue(): Collection<ZpStringWithInitial> {
val result = mutableListOf<ZpStringWithInitial>() val result = mutableListOf<ZpStringWithInitial>()
val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR } val vars = allocator.zeropageVars.filter { it.value.dt.isString }
for (variable in vars) { for (variable in vars) {
val scopedName = variable.key val scopedName = variable.key
val svar = symboltable.lookup(scopedName) as? StStaticVariable val svar = symboltable.lookup(scopedName) as? StStaticVariable
@ -556,7 +583,7 @@ internal class ProgramAndVarsGen(
private fun getZpArrayVarsWithInitvalue(): Collection<ZpArrayWithInitial> { private fun getZpArrayVarsWithInitvalue(): Collection<ZpArrayWithInitial> {
val result = mutableListOf<ZpArrayWithInitial>() val result = mutableListOf<ZpArrayWithInitial>()
val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes } val vars = allocator.zeropageVars.filter { it.value.dt.isArray }
for (variable in vars) { for (variable in vars) {
val scopedName = variable.key val scopedName = variable.key
val svar = symboltable.lookup(scopedName) as? StStaticVariable val svar = symboltable.lookup(scopedName) as? StStaticVariable
@ -572,7 +599,7 @@ internal class ProgramAndVarsGen(
if (scopedName.startsWith("cx16.r")) if (scopedName.startsWith("cx16.r"))
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped
val variable = symboltable.flat.getValue(scopedName) as StStaticVariable val variable = symboltable.flat.getValue(scopedName) as StStaticVariable
if(variable.dt in SplitWordArrayTypes) { if(variable.dt.isSplitWordArray) {
val lsbAddr = zpvar.address val lsbAddr = zpvar.address
val msbAddr = zpvar.address + (zpvar.size/2).toUInt() val msbAddr = zpvar.address + (zpvar.size/2).toUInt()
asmgen.out("${scopedName.substringAfterLast('.')}_lsb \t= $lsbAddr \t; zp ${zpvar.dt} (lsbs)") asmgen.out("${scopedName.substringAfterLast('.')}_lsb \t= $lsbAddr \t; zp ${zpvar.dt} (lsbs)")
@ -588,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 }).forEach { fun generate(section: String, variables: List<StStaticVariable>) {
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) uninitializedVariable2asm(it)
} }
aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt }).forEach { aligned.sortedWith(compareBy<StStaticVariable> { it.align }.thenBy { it.name }.thenBy { it.dt.base })
uninitializedVariable2asm(it) .forEach { uninitializedVariable2asm(it) }
asmgen.out(" .send $section")
}
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 == DataType.STR } 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,
@ -611,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(
@ -619,73 +662,94 @@ 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
} }
} }
} }
private fun uninitializedVariable2asm(variable: StStaticVariable) { private fun uninitializedVariable2asm(variable: StStaticVariable) {
when (variable.dt) { val dt = variable.dt
DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ?") when {
DataType.BYTE -> asmgen.out("${variable.name}\t.char ?") dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ?")
DataType.UWORD -> asmgen.out("${variable.name}\t.word ?") dt.isSignedByte -> asmgen.out("${variable.name}\t.char ?")
DataType.WORD -> asmgen.out("${variable.name}\t.sint ?") dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ?")
DataType.FLOAT -> asmgen.out("${variable.name}\t.fill ${compTarget.machine.FLOAT_MEM_SIZE}") dt.isSignedWord -> asmgen.out("${variable.name}\t.sint ?")
in SplitWordArrayTypes -> { dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.FLOAT_MEM_SIZE}")
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")
} }
in ArrayDatatypes -> { 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()}")
} }
private fun staticVariable2asm(variable: StStaticVariable) { private fun staticVariable2asm(variable: StStaticVariable) {
val initialValue: Number = if(!variable.dt.isArray && !variable.dt.isString) {
if(variable.initializationNumericValue!=null) { throw AssemblyError("static variables with an initialization value can only be an array or a string, not ${variable.dt} (${variable.name} ${variable.astNode?.position}")
if(variable.dt== DataType.FLOAT) // because numeric variables are in the BSS section and get initialized via assignment statements
variable.initializationNumericValue!! }
else
variable.initializationNumericValue!!.toInt()
} else 0
when (variable.dt) { // val initialValue: Number =
DataType.BOOL, DataType.UBYTE -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}") // if(variable.initializationNumericValue!=null) {
DataType.BYTE -> asmgen.out("${variable.name}\t.char $initialValue") // if(variable.dt.isFloat)
DataType.UWORD -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}") // variable.initializationNumericValue!!
DataType.WORD -> asmgen.out("${variable.name}\t.sint $initialValue") // else
DataType.FLOAT -> { // variable.initializationNumericValue!!.toInt()
if(initialValue==0) { // } else 0
asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float") //
} else {
val floatFill = compTarget.machine.getFloatAsmBytes(initialValue) val dt=variable.dt
asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue") when {
} // dt.isBool || dt.isUnsignedByte -> asmgen.out("${variable.name}\t.byte ${initialValue.toHex()}")
} // dt.isSignedByte -> asmgen.out("${variable.name}\t.char $initialValue")
DataType.STR -> { // dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
// dt.isSignedWord -> asmgen.out("${variable.name}\t.sint $initialValue")
// dt.isFloat -> {
// if(initialValue==0) {
// asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
// } else {
// val floatFill = compTarget.getFloatAsmBytes(initialValue)
// asmgen.out("${variable.name}\t.byte $floatFill ; float $initialValue")
// }
// }
dt.isString -> {
throw AssemblyError("all string vars should have been interned into prog") throw AssemblyError("all string vars should have been interned into prog")
} }
in ArrayDatatypes -> { 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")
@ -693,10 +757,10 @@ 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(dt) { when {
DataType.ARRAY_UB, DataType.ARRAY_BOOL -> { dt.isUnsignedByteArray || dt.isBoolArray -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros) val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
if (data.size <= 16) if (data.size <= 16)
asmgen.out("$varname\t.byte ${data.joinToString()}") asmgen.out("$varname\t.byte ${data.joinToString()}")
@ -706,7 +770,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" .byte " + chunk.joinToString()) asmgen.out(" .byte " + chunk.joinToString())
} }
} }
DataType.ARRAY_B -> { dt.isSignedByteArray -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros) val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
if (data.size <= 16) if (data.size <= 16)
asmgen.out("$varname\t.char ${data.joinToString()}") asmgen.out("$varname\t.char ${data.joinToString()}")
@ -716,7 +780,20 @@ internal class ProgramAndVarsGen(
asmgen.out(" .char " + chunk.joinToString()) asmgen.out(" .char " + chunk.joinToString())
} }
} }
DataType.ARRAY_UW -> { dt.isSplitWordArray -> {
if(dt.elementType().isUnsignedWord) {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
} else {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
}
dt.isUnsignedWordArray -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros) val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
if (data.size <= 16) if (data.size <= 16)
asmgen.out("$varname\t.word ${data.joinToString()}") asmgen.out("$varname\t.word ${data.joinToString()}")
@ -726,7 +803,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" .word " + chunk.joinToString()) asmgen.out(" .word " + chunk.joinToString())
} }
} }
DataType.ARRAY_W -> { dt.isSignedWordArray -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros) val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
if (data.size <= 16) if (data.size <= 16)
asmgen.out("$varname\t.sint ${data.joinToString()}") asmgen.out("$varname\t.sint ${data.joinToString()}")
@ -736,22 +813,10 @@ internal class ProgramAndVarsGen(
asmgen.out(" .sint " + chunk.joinToString()) asmgen.out(" .sint " + chunk.joinToString())
} }
} }
DataType.ARRAY_UW_SPLIT -> { dt.isFloatArray -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_W_SPLIT -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
asmgen.out("_array_$varname := ${data.joinToString()}")
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
asmgen.out("${varname}_msb\t.byte >_array_$varname")
}
DataType.ARRAY_F -> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!) val array = value ?: zeroFilledArray(orNumberOfZeros!!)
val floatFills = array.map { val floatFills = array.map {
compTarget.machine.getFloatAsmBytes(it.number!!) compTarget.getFloatAsmBytes(it.number!!)
} }
asmgen.out(varname) asmgen.out(varname)
for (f in array.zip(floatFills)) for (f in array.zip(floatFills))
@ -774,7 +839,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" ${it.name} = ${it.address.toHex()}") asmgen.out(" ${it.name} = ${it.address.toHex()}")
} }
consts.sortedBy { it.name }.forEach { consts.sortedBy { it.name }.forEach {
if(it.dt==DataType.FLOAT) if(it.dt==BaseDataType.FLOAT)
asmgen.out(" ${it.name} = ${it.value}") asmgen.out(" ${it.name} = ${it.value}")
else else
asmgen.out(" ${it.name} = ${it.value.toHex()}") asmgen.out(" ${it.name} = ${it.value.toHex()}")
@ -794,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())
@ -805,8 +870,8 @@ internal class ProgramAndVarsGen(
private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> { private fun makeArrayFillDataUnsigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!) val array = value ?: zeroFilledArray(orNumberOfZeros!!)
return when (dt) { return when {
DataType.ARRAY_BOOL -> dt.isBoolArray ->
// byte array can never contain pointer-to types, so treat values as all integers // byte array can never contain pointer-to types, so treat values as all integers
array.map { array.map {
if(it.boolean!=null) if(it.boolean!=null)
@ -816,18 +881,23 @@ internal class ProgramAndVarsGen(
if(number==0.0) "0" else "1" if(number==0.0) "0" else "1"
} }
} }
DataType.ARRAY_UB -> dt.isUnsignedByteArray ->
// byte array can never contain pointer-to types, so treat values as all integers // byte array can never contain pointer-to types, so treat values as all integers
array.map { array.map {
val number = it.number!!.toInt() val number = it.number!!.toInt()
"$"+number.toString(16).padStart(2, '0') "$"+number.toString(16).padStart(2, '0')
} }
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map { dt.isArray && dt.elementType().isUnsignedWord -> array.map {
if(it.number!=null) { if(it.number!=null) {
"$" + it.number!!.toInt().toString(16).padStart(4, '0') "$" + it.number!!.toInt().toString(16).padStart(4, '0')
} }
else if(it.addressOfSymbol!=null) { else if(it.addressOfSymbol!=null) {
asmgen.asmSymbolName(it.addressOfSymbol!!) val addrOfSymbol = it.addressOfSymbol!!
val symbol = symboltable.lookup(addrOfSymbol)!!
if(symbol is StStaticVariable && symbol.dt.isSplitWordArray)
asmgen.asmSymbolName(addrOfSymbol+"_lsb") // the _lsb split array comes first in memory
else
asmgen.asmSymbolName(addrOfSymbol)
} }
else else
throw AssemblyError("weird array elt") throw AssemblyError("weird array elt")
@ -838,14 +908,14 @@ internal class ProgramAndVarsGen(
private fun makeArrayFillDataSigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> { private fun makeArrayFillDataSigned(dt: DataType, value: StArray?, orNumberOfZeros: Int?): List<String> {
val array = value ?: zeroFilledArray(orNumberOfZeros!!) val array = value ?: zeroFilledArray(orNumberOfZeros!!)
return when (dt) { return when {
// byte array can never contain pointer-to types, so treat values as all integers // byte array can never contain pointer-to types, so treat values as all integers
DataType.ARRAY_UB -> dt.isUnsignedByteArray ->
array.map { array.map {
val number = it.number!!.toInt() val number = it.number!!.toInt()
"$"+number.toString(16).padStart(2, '0') "$"+number.toString(16).padStart(2, '0')
} }
DataType.ARRAY_B -> dt.isSignedByteArray ->
array.map { array.map {
val number = it.number!!.toInt() val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(2, '0') val hexnum = number.absoluteValue.toString(16).padStart(2, '0')
@ -854,11 +924,11 @@ internal class ProgramAndVarsGen(
else else
"-$$hexnum" "-$$hexnum"
} }
DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT -> array.map { dt.isArray && dt.elementType().isUnsignedWord -> array.map {
val number = it.number!!.toInt() val number = it.number!!.toInt()
"$" + number.toString(16).padStart(4, '0') "$" + number.toString(16).padStart(4, '0')
} }
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> array.map { dt.isArray && dt.elementType().isSignedWord -> array.map {
val number = it.number!!.toInt() val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(4, '0') val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
if(number>=0) if(number>=0)

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.*
@ -14,7 +11,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
private val errors: IErrorReporter private val errors: IErrorReporter
) { ) {
private val zeropage = options.compTarget.machine.zeropage private val zeropage = options.compTarget.zeropage
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname) internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
internal val zeropageVars: Map<String, MemoryAllocator.VarAllocation> internal val zeropageVars: Map<String, MemoryAllocator.VarAllocation>
@ -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,10 +60,10 @@ 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, variable.astNode?.position ?: Position.DUMMY,
errors errors
) )
result.fold( result.fold(
@ -68,7 +71,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
numVariablesAllocatedInZP++ numVariablesAllocatedInZP++
}, },
failure = { failure = {
errors.err(it.message!!, variable.astNode.position) errors.err(it.message!!, variable.astNode?.position ?: Position.DUMMY)
} }
) )
} }
@ -76,10 +79,10 @@ 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, variable.astNode?.position ?: Position.DUMMY,
errors errors
) )
result.onSuccess { numVariablesAllocatedInZP++ } result.onSuccess { numVariablesAllocatedInZP++ }
@ -89,17 +92,17 @@ 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 in IntegerDatatypesWithBoolean) { 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, variable.astNode?.position ?: Position.DUMMY,
errors errors
) )
result.onSuccess { numVariablesAllocatedInZP++ } result.onSuccess { numVariablesAllocatedInZP++ }
@ -124,6 +127,6 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
} }
} }
collect(st) collect(st)
return vars.sortedBy { it.dt } return vars.sortedBy { it.dt.base }
} }
} }

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
@ -17,32 +20,38 @@ internal class AnyExprAsmGen(
private val asmgen: AsmGen6502Internal private val asmgen: AsmGen6502Internal
) { ) {
fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean { fun assignAnyExpressionUsingStack(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.type) { when {
in ByteDatatypesWithBoolean -> { expr.type.isByteOrBool -> {
if(expr.left.type in ByteDatatypesWithBoolean && expr.right.type in ByteDatatypesWithBoolean) if(expr.left.type.isByteOrBool && expr.right.type.isByteOrBool)
return assignByteBinExpr(expr, assign) return assignByteBinExpr(expr, assign)
if (expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) { if (expr.left.type.isWord && expr.right.type.isWord) {
require(expr.operator in ComparisonOperators) require(expr.operator in ComparisonOperators)
throw AssemblyError("words operands comparison -> byte, should have been handled elsewhere") throw AssemblyError("words operands comparison -> byte, should have been handled elsewhere")
} }
if (expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) { if (expr.left.type.isFloat && expr.right.type.isFloat) {
require(expr.operator in ComparisonOperators) require(expr.operator in ComparisonOperators)
return assignFloatBinExpr(expr, assign) return assignFloatBinExpr(expr, assign)
} }
throw AssemblyError("weird expr operand types: ${expr.left.type} and ${expr.right.type}") throw AssemblyError("weird expr operand types: ${expr.left.type} and ${expr.right.type}")
} }
in WordDatatypes -> { expr.type.isWord -> {
require(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes) { require(expr.left.type.isWord && expr.right.type.isWord) {
"both operands must be words" "both operands must be words"
} }
throw AssemblyError("expression should have been handled otherwise: word ${expr.operator} at ${expr.position}") throw AssemblyError("expression should have been handled otherwise: word ${expr.operator} at ${expr.position}")
} }
DataType.FLOAT -> { expr.type.isFloat -> {
require(expr.left.type==DataType.FLOAT && expr.right.type==DataType.FLOAT) { require(expr.left.type.isFloat && expr.right.type.isFloat) {
"both operands must be floats" "both operands must be floats"
} }
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,7 +59,7 @@ 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.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")
@ -58,7 +67,7 @@ internal class AnyExprAsmGen(
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.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")
@ -73,7 +82,7 @@ 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.UBYTE) asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | and P8ZP_SCRATCH_B1") asmgen.out(" pla | and P8ZP_SCRATCH_B1")
@ -81,7 +90,7 @@ internal class AnyExprAsmGen(
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.UBYTE) asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | ora P8ZP_SCRATCH_B1") asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
@ -89,7 +98,7 @@ internal class AnyExprAsmGen(
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.UBYTE) asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | eor P8ZP_SCRATCH_B1") asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
@ -190,7 +199,7 @@ 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}")
} }
} }

View File

@ -3,14 +3,14 @@ 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 {
VARIABLE, VARIABLE,
ARRAY, ARRAY,
MEMORY, MEMORY,
REGISTER REGISTER,
VOID // assign nothing - used in multi-value assigns for void placeholders
} }
internal enum class SourceStorageKind { internal enum class SourceStorageKind {
@ -39,16 +39,28 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
val asmVarname: String by lazy { val asmVarname: String by lazy {
if (array == null) if (array == null)
variableAsmName!! variableAsmName!!
else else {
asmgen.asmVariableName(array.variable) if(array.variable==null)
TODO("asmVarname for array with pointer")
asmgen.asmVariableName(array.variable!!)
}
} }
init { init {
if(register!=null && datatype !in NumericDatatypesWithBoolean) if(register!=null && !datatype.isNumericOrBool)
throw AssemblyError("must be numeric type") throw AssemblyError("must be numeric type")
} }
companion object { companion object {
fun fromAstAssignmentMulti(targets: List<PtAssignTarget>, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): List<AsmAssignTarget> {
return targets.map {
if(it.void)
AsmAssignTarget(TargetStorageKind.VOID, asmgen, DataType.UNDEFINED, null, it.position)
else
fromAstAssignment(it, definingSub, asmgen)
}
}
fun fromAstAssignment(target: PtAssignTarget, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget { fun fromAstAssignment(target: PtAssignTarget, definingSub: IPtSubroutine?, asmgen: AsmGen6502Internal): AsmAssignTarget {
with(target) { with(target) {
when { when {
@ -77,12 +89,20 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
when(registers) { when(registers) {
RegisterOrPair.A, RegisterOrPair.A,
RegisterOrPair.X, RegisterOrPair.X,
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, pos, register = registers) RegisterOrPair.Y -> {
val dt = if(signed) DataType.BYTE else DataType.UBYTE
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
}
RegisterOrPair.AX, RegisterOrPair.AX,
RegisterOrPair.AY, RegisterOrPair.AY,
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers) RegisterOrPair.XY -> {
val dt = if(signed) DataType.WORD else DataType.UWORD
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
}
RegisterOrPair.FAC1, RegisterOrPair.FAC1,
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers) RegisterOrPair.FAC2 -> {
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
}
RegisterOrPair.R0, RegisterOrPair.R0,
RegisterOrPair.R1, RegisterOrPair.R1,
RegisterOrPair.R2, RegisterOrPair.R2,
@ -98,7 +118,10 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
RegisterOrPair.R12, RegisterOrPair.R12,
RegisterOrPair.R13, RegisterOrPair.R13,
RegisterOrPair.R14, RegisterOrPair.R14,
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, pos, register = registers) RegisterOrPair.R15 -> {
val dt = if(signed) DataType.WORD else DataType.UWORD
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
}
} }
} }
@ -119,9 +142,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
TargetStorageKind.MEMORY -> { TargetStorageKind.MEMORY -> {
left isSameAs memory!! left isSameAs memory!!
} }
TargetStorageKind.REGISTER -> { TargetStorageKind.REGISTER -> false
false TargetStorageKind.VOID -> false
}
} }
} }
@ -141,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 {
@ -163,7 +188,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}") throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
val varName=asmgen.asmVariableName(value) val varName=asmgen.asmVariableName(value)
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system // special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
if(value.type == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) { if(value.type.isUnsignedWord && varName.lowercase().startsWith("cx16.r")) {
val regStr = varName.lowercase().substring(5) val regStr = varName.lowercase().substring(5)
val reg = RegisterOrPair.valueOf(regStr.uppercase()) val reg = RegisterOrPair.valueOf(regStr.uppercase())
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, value.type, register = reg) AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, value.type, register = reg)
@ -183,9 +208,12 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
is PtFunctionCall -> { is PtFunctionCall -> {
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 = sub.returnsWhatWhere().firstOrNull { rr -> rr.first.registerOrPair != null || rr.first.statusflag!=null }?.second val returnType =
if(sub is PtSub && sub.signature.returns.size>1)
DataType.UNDEFINED // TODO list of types instead?
else
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")
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value) AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType, expression = value)
} }
else -> { else -> {
@ -202,9 +230,9 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, boolean, expression) AsmAssignSource(kind, program, asmgen, newType, variableAsmName, array, memory, register, number, boolean, expression)
if(target.datatype!=datatype) { if(target.datatype!=datatype) {
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) { if(target.datatype.isByte && datatype.isByte) {
return withAdjustedDt(target.datatype) return withAdjustedDt(target.datatype)
} else if(target.datatype in WordDatatypes && datatype in WordDatatypes) { } else if(target.datatype.isWord && datatype.isWord) {
return withAdjustedDt(target.datatype) return withAdjustedDt(target.datatype)
} }
} }
@ -215,26 +243,30 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
internal sealed class AsmAssignmentBase(val source: AsmAssignSource, internal sealed class AsmAssignmentBase(val source: AsmAssignSource,
val target: AsmAssignTarget, val targets: List<AsmAssignTarget>,
val memsizer: IMemSizer, val memsizer: IMemSizer,
val position: Position) { val position: Position) {
init { init {
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY)) targets.forEach { target ->
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype at $position" } if (!source.datatype.isArray && !source.datatype.isUndefined && !target.datatype.isArray && !target.datatype.isUndefined)
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) { require(memsizer.memorySize(source.datatype, null) <= memsizer.memorySize(target.datatype, null)) {
"source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}" "source dt size must be less or equal to target dt size at $position srcdt=${source.datatype} targetdt=${target.datatype}"
} }
} }
} }
val target: AsmAssignTarget
get() = targets.single()
}
internal class AsmAssignment(source: AsmAssignSource, internal class AsmAssignment(source: AsmAssignSource,
target: AsmAssignTarget, targets: List<AsmAssignTarget>,
memsizer: IMemSizer, memsizer: IMemSizer,
position: Position): AsmAssignmentBase(source, target, memsizer, position) position: Position): AsmAssignmentBase(source, targets, memsizer, position)
internal class AsmAugmentedAssignment(source: AsmAssignSource, internal class AsmAugmentedAssignment(source: AsmAssignSource,
val operator: String, val operator: String,
target: AsmAssignTarget, target: AsmAssignTarget,
memsizer: IMemSizer, memsizer: IMemSizer,
position: Position): AsmAssignmentBase(source, target, memsizer, position) position: Position): AsmAssignmentBase(source, listOf(target), memsizer, position)

View File

@ -4,16 +4,27 @@ import prog8.code.core.*
internal object DummyMemsizer : IMemSizer { internal object DummyMemsizer : IMemSizer {
override fun memorySize(dt: DataType) = when(dt) { override fun memorySize(dt: DataType, numElements: Int?): Int {
in ByteDatatypesWithBoolean -> 1 if(dt.isPointerArray)
DataType.FLOAT -> 5 return 2 * numElements!!
else -> 2 else if(dt.isArray) {
require(numElements != null)
return when(dt.sub) {
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
BaseDataType.FLOAT -> numElements*5
else -> throw IllegalArgumentException("invalid sub type")
} }
override fun memorySize(arrayDt: DataType, numElements: Int) = when(arrayDt) { }
DataType.ARRAY_UW -> numElements*2 return when {
DataType.ARRAY_W -> numElements*2 dt.isByteOrBool -> 1 * (numElements ?: 1)
DataType.ARRAY_F -> numElements*5 dt.isFloat -> 5 * (numElements ?: 1)
else -> numElements else -> 2 * (numElements ?: 1)
}
}
override fun memorySize(dt: BaseDataType): Int {
return memorySize(DataType.forDt(dt), null)
} }
} }
@ -59,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,11 +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.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
@ -25,8 +29,9 @@ 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.machine.PROGRAM_LOAD_ADDRESS, loadAddress = target.PROGRAM_LOAD_ADDRESS,
memtopAddress = 0xffffu memtopAddress = 0xffffu
) )
} }
@ -47,30 +52,33 @@ class TestCodegen: FunSpec({
val codegen = AsmGen6502(prefixSymbols = false, 0) val codegen = AsmGen6502(prefixSymbols = false, 0)
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder) val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main",false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY) val block = PtBlock("main",false, SourceCode.Generated("test"), PtBlock.Options(), Position.DUMMY)
val sub = PtSub("start", emptyList(), null, Position.DUMMY) val sub = PtSub("start", emptyList(), emptyList(), Position.DUMMY)
sub.add(PtVariable( sub.add(PtVariable(
"pi", "pi",
DataType.UBYTE, DataType.UBYTE,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
PtNumber(DataType.UBYTE, 0.0, Position.DUMMY), false,
null,
null, null,
Position.DUMMY Position.DUMMY
)) ))
sub.add(PtVariable( sub.add(PtVariable(
"particleX", "particleX",
DataType.ARRAY_UB, 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(
"particleDX", "particleDX",
DataType.ARRAY_UB, DataType.arrayFor(BaseDataType.UBYTE),
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
false,
null, null,
3u, 3u,
Position.DUMMY Position.DUMMY
@ -80,7 +88,8 @@ class TestCodegen: FunSpec({
DataType.WORD, DataType.WORD,
ZeropageWish.DONTCARE, ZeropageWish.DONTCARE,
0u, 0u,
PtNumber(DataType.WORD, 1.0, Position.DUMMY), false,
null,
null, null,
Position.DUMMY Position.DUMMY
)) ))
@ -88,14 +97,14 @@ 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.UBYTE, Position.DUMMY).also { idx -> val targetIdx = PtArrayIndexer(DataType.UBYTE, Position.DUMMY).also { idx ->
idx.add(PtIdentifier("main.start.particleX", DataType.ARRAY_UB, Position.DUMMY)) idx.add(PtIdentifier("main.start.particleX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
idx.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY)) idx.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
} }
it.add(targetIdx) it.add(targetIdx)
} }
val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY) val value = PtArrayIndexer(DataType.UBYTE, Position.DUMMY)
value.add(PtIdentifier("main.start.particleDX", DataType.ARRAY_UB, Position.DUMMY)) value.add(PtIdentifier("main.start.particleDX", DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY))
value.add(PtNumber(DataType.UBYTE, 2.0, Position.DUMMY)) value.add(PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY))
assign.add(target) assign.add(target)
assign.add(value) assign.add(value)
sub.add(assign) sub.add(assign)
@ -113,7 +122,7 @@ class TestCodegen: FunSpec({
it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY)) it.add(PtIdentifier("main.start.xx", DataType.WORD, Position.DUMMY))
} }
numberAssign.add(numberAssignTarget) numberAssign.add(numberAssignTarget)
numberAssign.add(PtNumber(DataType.WORD, 42.0, Position.DUMMY)) numberAssign.add(PtNumber(BaseDataType.WORD, 42.0, Position.DUMMY))
sub.add(numberAssign) sub.add(numberAssign)
val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY) val cxregAssign = PtAugmentedAssign("+=", Position.DUMMY)
@ -158,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

@ -1,46 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
implementation project(':intermediate')
implementation project(':codeGenIntermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
}
// note: there are no unit tests in this module!

View File

@ -0,0 +1,28 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
implementation(project(":codeCore"))
implementation(project(":simpleAst"))
implementation(project(":intermediate"))
implementation(project(":codeGenIntermediate"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
}
sourceSets {
main {
java {
srcDir(file("${project.projectDir}/src"))
}
resources {
srcDir(file("${project.projectDir}/res"))
}
}
}
// note: there are no unit tests in this module!

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
@ -21,10 +21,12 @@ class ExperiCodeGen: ICodeGeneratorBackend {
// but you can also use the Intermediate Representation to build a codegen on: // but you can also use the Intermediate Representation to build a codegen on:
val irCodeGen = IRCodeGen(program, symbolTable, options, errors) val irCodeGen = IRCodeGen(program, symbolTable, options, errors)
val irProgram = irCodeGen.generate() val irProgram = irCodeGen.generate()
irProgram.verifyRegisterTypes(irCodeGen.registerTypes())
// 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()
if(!options.quiet)
println("** experimental codegen stub: no assembly generated **") println("** experimental codegen stub: no assembly generated **")
return EmptyProgram return EmptyProgram
} }
@ -33,6 +35,7 @@ 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 {
if(!options.quiet)
println("** nothing assembled **") println("** nothing assembled **")
return true return true
} }

View File

@ -1,66 +0,0 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
targetCompatibility = JavaLanguageVersion.of(javaVersion)
sourceCompatibility = JavaLanguageVersion.of(javaVersion)
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':codeCore')
implementation project(':intermediate')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.1'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testImplementation 'io.kotest:kotest-framework-datatest:5.9.1'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
sourceSets {
main {
java {
srcDir "${project.projectDir}/src"
}
resources {
srcDir "${project.projectDir}/res"
}
}
test {
java {
srcDir "${project.projectDir}/test"
}
}
}
test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn 'cleanTest'
// Show test results.
testLogging {
events "skipped", "failed"
}
}

View File

@ -0,0 +1,49 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
implementation(project(":codeCore"))
implementation(project(":simpleAst"))
implementation(project(":intermediate"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
sourceSets {
main {
java {
srcDir("${project.projectDir}/src")
}
resources {
srcDir("${project.projectDir}/res")
}
}
test {
java {
srcDir("${project.projectDir}/test")
}
}
}
tasks.test {
// Enable JUnit 5 (Gradle 4.6+).
useJUnitPlatform()
// Always run tests, even when nothing changed.
dependsOn("cleanTest")
// Show test results.
testLogging {
events("skipped", "failed")
}
}

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,7 +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.* import prog8.code.core.AssemblyError
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
import prog8.intermediate.* import prog8.intermediate.*
@ -19,6 +22,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"callfar" -> funcCallfar(call) "callfar" -> funcCallfar(call)
"callfar2" -> funcCallfar2(call) "callfar2" -> funcCallfar2(call)
"call" -> funcCall(call) "call" -> funcCall(call)
"lsw" -> throw AssemblyError("lsw() should have been removed or replaced by a const value")
"msw" -> throw AssemblyError("msw() should have been removed or replaced by a const value")
"msb" -> funcMsb(call) "msb" -> funcMsb(call)
"lsb" -> funcLsb(call) "lsb" -> funcLsb(call)
"memory" -> funcMemory(call) "memory" -> funcMemory(call)
@ -39,11 +44,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"ror" -> funcRolRor(call) "ror" -> funcRolRor(call)
"rol2" -> funcRolRor(call) "rol2" -> funcRolRor(call)
"ror2" -> funcRolRor(call) "ror2" -> funcRolRor(call)
"prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse/ifExpression statement")
"prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse/ifExpression statement")
"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}")
} }
} }
@ -53,12 +58,12 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val valueTr = exprGen.translateExpression(call.args[0]) val valueTr = exprGen.translateExpression(call.args[0])
addToResult(result, valueTr, valueTr.resultReg, valueTr.resultFpReg) addToResult(result, valueTr, valueTr.resultReg, valueTr.resultFpReg)
return if(resultType==IRDataType.FLOAT) { return if(resultType==IRDataType.FLOAT) {
val resultFpReg = codeGen.registers.nextFreeFloat() val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, fpReg1 = resultFpReg, fpReg2 = valueTr.resultFpReg), null) addInstr(result, IRInstruction(Opcode.SQUARE, resultType, fpReg1 = resultFpReg, fpReg2 = valueTr.resultFpReg), null)
ExpressionCodeResult(result, resultType, -1, resultFpReg) ExpressionCodeResult(result, resultType, -1, resultFpReg)
} }
else { else {
val resultReg = codeGen.registers.nextFree() val resultReg = codeGen.registers.next(resultType)
addInstr(result, IRInstruction(Opcode.SQUARE, resultType, reg1 = resultReg, reg2 = valueTr.resultReg), null) addInstr(result, IRInstruction(Opcode.SQUARE, resultType, reg1 = resultReg, reg2 = valueTr.resultReg), null)
ExpressionCodeResult(result, resultType, resultReg, -1) ExpressionCodeResult(result, resultType, resultReg, -1)
} }
@ -72,12 +77,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
return if(call.void) return if(call.void)
ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
else else
ExpressionCodeResult(result, IRDataType.WORD, codeGen.registers.nextFree(), -1) // TODO actually the result is returned in CPU registers AY... ExpressionCodeResult(result, IRDataType.WORD, codeGen.registers.next(IRDataType.WORD), -1) // TODO actually the result is returned in CPU registers AY...
} }
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])
@ -90,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])
@ -122,7 +125,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.DIVMOD, type, reg1 = tr.resultReg, immediate = divident.number.toInt()), null) addInstr(result, IRInstruction(Opcode.DIVMOD, type, reg1 = tr.resultReg, immediate = divident.number.toInt()), null)
divisionReg = tr.resultReg divisionReg = tr.resultReg
remainderReg = codeGen.registers.nextFree() remainderReg = codeGen.registers.next(type)
} else { } else {
val numTr = exprGen.translateExpression(number) val numTr = exprGen.translateExpression(number)
addToResult(result, numTr, numTr.resultReg, -1) addToResult(result, numTr, numTr.resultReg, -1)
@ -142,13 +145,13 @@ 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)
addToResult(result, right, right.resultReg, -1) addToResult(result, right, right.resultReg, -1)
result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to left.resultReg) val resultReg = codeGen.registers.next(IRDataType.BYTE)
return ExpressionCodeResult(result, IRDataType.BYTE, left.resultReg, -1) result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to resultReg)
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
} }
private fun funcCmp(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcCmp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
@ -167,15 +170,15 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcAbs(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcAbs(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val sourceDt = call.args.single().type val sourceDt = call.args.single().type
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
if(sourceDt==DataType.UWORD) if(sourceDt.isUnsignedWord)
return ExpressionCodeResult.EMPTY return ExpressionCodeResult.EMPTY
val tr = exprGen.translateExpression(call.args[0]) val tr = exprGen.translateExpression(call.args[0])
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
when (sourceDt) { when (sourceDt.base) {
DataType.BYTE -> { BaseDataType.BYTE -> {
val notNegativeLabel = codeGen.createLabelName() val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree() val compareReg = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg) it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg)
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel) it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
@ -184,9 +187,9 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
result += IRCodeChunk(notNegativeLabel, null) result += IRCodeChunk(notNegativeLabel, null)
return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1)
} }
DataType.WORD -> { BaseDataType.WORD -> {
val notNegativeLabel = codeGen.createLabelName() val notNegativeLabel = codeGen.createLabelName()
val compareReg = codeGen.registers.nextFree() val compareReg = codeGen.registers.next(IRDataType.WORD)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg) it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg)
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel) it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
@ -195,8 +198,8 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
result += IRCodeChunk(notNegativeLabel, null) result += IRCodeChunk(notNegativeLabel, null)
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1) return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
} }
DataType.FLOAT -> { BaseDataType.FLOAT -> {
val resultFpReg = codeGen.registers.nextFreeFloat() val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null) addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null)
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg) return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg)
} }
@ -207,7 +210,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single()) val tr = exprGen.translateExpression(call.args.single())
val resultReg = codeGen.registers.nextFree() val resultReg = codeGen.registers.next(tr.dt)
if(tr.dt==IRDataType.FLOAT) { if(tr.dt==IRDataType.FLOAT) {
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
@ -227,26 +230,26 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single()) val tr = exprGen.translateExpression(call.args.single())
val dt = call.args[0].type val dt = call.args[0].type
when(dt) { when(dt.base) {
DataType.UBYTE -> { BaseDataType.UBYTE -> {
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree() val resultReg = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.BYTE, reg1=resultReg, reg2=tr.resultReg) it += IRInstruction(Opcode.SQRT, IRDataType.BYTE, reg1=resultReg, reg2=tr.resultReg)
} }
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
} }
DataType.UWORD -> { BaseDataType.UWORD -> {
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree() 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)
} }
DataType.FLOAT -> { BaseDataType.FLOAT -> {
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
val resultFpReg = codeGen.registers.nextFreeFloat() val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg) it += IRInstruction(Opcode.SQRT, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg)
} }
@ -258,7 +261,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcMkword(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcMkword(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val resultReg = codeGen.registers.nextFree() val resultReg = codeGen.registers.next(IRDataType.WORD)
if((call.args[0] as? PtNumber)?.number == 0.0) { if((call.args[0] as? PtNumber)?.number == 0.0) {
// msb is 0, use EXT // msb is 0, use EXT
val lsbTr = exprGen.translateExpression(call.args[1]) val lsbTr = exprGen.translateExpression(call.args[1])
@ -276,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])
@ -294,11 +296,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
) )
return ExpressionCodeResult(result, type, -1, valueTr.resultFpReg) return ExpressionCodeResult(result, type, -1, valueTr.resultFpReg)
} else { } else {
val syscall = when(call.type) { val syscall = when(call.type.base) {
DataType.UBYTE -> IMSyscall.CLAMP_UBYTE BaseDataType.UBYTE -> IMSyscall.CLAMP_UBYTE
DataType.BYTE -> IMSyscall.CLAMP_BYTE BaseDataType.BYTE -> IMSyscall.CLAMP_BYTE
DataType.UWORD -> IMSyscall.CLAMP_UWORD BaseDataType.UWORD -> IMSyscall.CLAMP_UWORD
DataType.WORD -> IMSyscall.CLAMP_WORD BaseDataType.WORD -> IMSyscall.CLAMP_WORD
else -> throw AssemblyError("invalid dt") else -> throw AssemblyError("invalid dt")
} }
result += codeGen.makeSyscall(syscall, listOf( result += codeGen.makeSyscall(syscall, listOf(
@ -318,7 +320,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addToResult(result, leftTr, leftTr.resultReg, -1) addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1]) val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1) addToResult(result, rightTr, rightTr.resultReg, -1)
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR val comparisonOpcode = if(call.type.isSigned) Opcode.BGTSR else Opcode.BGTR
val after = codeGen.createLabelName() val after = codeGen.createLabelName()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(comparisonOpcode, type, reg1 = rightTr.resultReg, reg2 = leftTr.resultReg, labelSymbol = after) it += IRInstruction(comparisonOpcode, type, reg1 = rightTr.resultReg, reg2 = leftTr.resultReg, labelSymbol = after)
@ -337,7 +339,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
addToResult(result, leftTr, leftTr.resultReg, -1) addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = exprGen.translateExpression(call.args[1]) val rightTr = exprGen.translateExpression(call.args[1])
addToResult(result, rightTr, rightTr.resultReg, -1) addToResult(result, rightTr, rightTr.resultReg, -1)
val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR val comparisonOpcode = if(call.type.isSigned) Opcode.BGTSR else Opcode.BGTR
val after = codeGen.createLabelName() val after = codeGen.createLabelName()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(comparisonOpcode, type, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg, labelSymbol = after) it += IRInstruction(comparisonOpcode, type, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg, labelSymbol = after)
@ -403,7 +405,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
return if(dt==IRDataType.FLOAT) { return if(dt==IRDataType.FLOAT) {
if(call.args[0] is PtNumber) { if(call.args[0] is PtNumber) {
val resultFpRegister = codeGen.registers.nextFreeFloat() val resultFpRegister = codeGen.registers.next(IRDataType.FLOAT)
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = resultFpRegister, address = address) it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = resultFpRegister, address = address)
@ -412,7 +414,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} else { } else {
val tr = exprGen.translateExpression(call.args.single()) val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val resultFpReg = codeGen.registers.nextFreeFloat() val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADI, IRDataType.FLOAT, reg1 = tr.resultReg, fpReg1 = resultFpReg) it += IRInstruction(Opcode.LOADI, IRDataType.FLOAT, reg1 = tr.resultReg, fpReg1 = resultFpReg)
} }
@ -420,7 +422,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} }
} else { } else {
if (call.args[0] is PtNumber) { if (call.args[0] is PtNumber) {
val resultRegister = codeGen.registers.nextFree() val resultRegister = codeGen.registers.next(dt)
val address = (call.args[0] as PtNumber).number.toInt() val address = (call.args[0] as PtNumber).number.toInt()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, dt, reg1 = resultRegister, address = address) it += IRInstruction(Opcode.LOADM, dt, reg1 = resultRegister, address = address)
@ -429,7 +431,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} else { } else {
val tr = exprGen.translateExpression(call.args.single()) val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree() val resultReg = codeGen.registers.next(dt)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADI, dt, reg1 = resultReg, reg2 = tr.resultReg) it += IRInstruction(Opcode.LOADI, dt, reg1 = resultReg, reg2 = tr.resultReg)
} }
@ -471,7 +473,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} }
return if(address is PtNumber) { return if(address is PtNumber) {
val resultRegister = codeGen.registers.nextFree() val resultRegister = codeGen.registers.next(IRDataType.BYTE)
val addressNum = address.number.toInt() val addressNum = address.number.toInt()
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, address = addressNum) it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, address = addressNum)
@ -481,7 +483,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} else { } else {
val addressTr = exprGen.translateExpression(address) val addressTr = exprGen.translateExpression(address)
addToResult(result, addressTr, addressTr.resultReg, -1) addToResult(result, addressTr, addressTr.resultReg, -1)
val resultReg = codeGen.registers.nextFree() val resultReg = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = addressTr.resultReg) it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = addressTr.resultReg)
} }
@ -494,22 +496,34 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcMemory(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcMemory(call: PtBuiltinFunctionCall): ExpressionCodeResult {
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.nextFree() 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 {
return exprGen.translateExpression(call.args.single()) val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
// ....To be more strict, maybe we should introduce a new result register that is of type .b? return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
} }
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult { private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val tr = exprGen.translateExpression(call.args.single()) val tr = exprGen.translateExpression(call.args.single())
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.nextFree() val resultReg = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null) addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null)
// note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here.
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
@ -542,10 +556,27 @@ 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) {
if(arr.splitWords) TODO("IR rol/ror on split words array") if(arr.variable==null)
val variable = arr.variable.name TODO("support for ptr indexing ${arr.position}")
val itemsize = codeGen.program.memsizer.memorySize(arr.type) val variable = arr.variable!!.name
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = index*itemsize), null) if(arr.splitWords) {
result += IRCodeChunk(null, null).also {
when(opcodeMemAndReg.first) {
Opcode.ROXRM, Opcode.RORM -> {
it += IRInstruction(opcodeMemAndReg.first, IRDataType.BYTE, labelSymbol = "${variable}_msb", symbolOffset = index)
it += IRInstruction(opcodeMemAndReg.first, IRDataType.BYTE, labelSymbol = "${variable}_lsb", symbolOffset = index)
}
Opcode.ROXLM, Opcode.ROLM -> {
it += IRInstruction(opcodeMemAndReg.first, IRDataType.BYTE, labelSymbol = "${variable}_lsb", symbolOffset = index)
it += IRInstruction(opcodeMemAndReg.first, IRDataType.BYTE, labelSymbol = "${variable}_msb", symbolOffset = index)
}
else -> throw AssemblyError("wrong rol/ror opcode")
}
}
} else {
val offset = codeGen.program.memsizer.memorySize(arr.type, index)
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = offset), null)
}
return ExpressionCodeResult(result, vmDt, -1, -1) return ExpressionCodeResult(result, vmDt, -1, -1)
} }
@ -574,7 +605,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
is PtIdentifier -> { is PtIdentifier -> {
if(isConstZeroValue) { if(isConstZeroValue) {
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
val pointerReg = codeGen.registers.nextFree() val pointerReg = codeGen.registers.next(IRDataType.WORD)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name) it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name)
if (msb) if (msb)
it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg) it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg)
@ -584,7 +615,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val valueTr = exprGen.translateExpression(call.args[1]) val valueTr = exprGen.translateExpression(call.args[1])
addToResult(result, valueTr, valueTr.resultReg, -1) addToResult(result, valueTr, valueTr.resultReg, -1)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
val pointerReg = codeGen.registers.nextFree() val pointerReg = codeGen.registers.next(IRDataType.WORD)
it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name) it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name)
if (msb) if (msb)
it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg) it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg)
@ -596,10 +627,12 @@ 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.nextFree() val offsetReg = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex)
it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = varName) it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = varName)
@ -615,7 +648,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val valueTr = exprGen.translateExpression(call.args[1]) val valueTr = exprGen.translateExpression(call.args[1])
addToResult(result, valueTr, valueTr.resultReg, -1) addToResult(result, valueTr, valueTr.resultReg, -1)
if(constIndex!=null) { if(constIndex!=null) {
val offsetReg = codeGen.registers.nextFree() val offsetReg = codeGen.registers.next(IRDataType.BYTE)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex)
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = varName) it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = varName)
@ -630,46 +663,50 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
} }
} }
else { else {
val eltSize = codeGen.program.memsizer.memorySize(target.type) val targetVariable = target.variable
if(targetVariable==null)
TODO("support for ptr indexing ${target.position}")
val eltSize = codeGen.program.memsizer.memorySize(target.type, null)
val constIndex = target.index.asConstInteger() val constIndex = target.index.asConstInteger()
if(isConstZeroValue) { if(isConstZeroValue) {
if(constIndex!=null) { if(constIndex!=null) {
val offsetReg = codeGen.registers.nextFree() val offsetReg = codeGen.registers.next(IRDataType.BYTE)
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 {
val valueTr = exprGen.translateExpression(call.args[1]) val valueTr = exprGen.translateExpression(call.args[1])
addToResult(result, valueTr, valueTr.resultReg, -1) addToResult(result, valueTr, valueTr.resultReg, -1)
if(constIndex!=null) { if(constIndex!=null) {
val offsetReg = codeGen.registers.nextFree() val offsetReg = codeGen.registers.next(IRDataType.BYTE)
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)
} }
} }
} }

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