mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
78
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ea8ca59b7 | ||
|
|
71ffbe2ba7 | ||
|
|
e63921009c | ||
|
|
db1aa3f257 | ||
|
|
8abdb837b2 | ||
|
|
cdeabd4b66 | ||
|
|
efff74c0f1 | ||
|
|
0e177cb531 | ||
|
|
35b921b062 | ||
|
|
a7d98e43b8 | ||
|
|
845ee2dd83 | ||
|
|
f9b0bfe31b | ||
|
|
f303d15628 | ||
|
|
67f7ffa52d | ||
|
|
88c5d9783a | ||
|
|
e7fc0360ad | ||
|
|
cd1862dd9f | ||
|
|
045c4909a8 | ||
|
|
f1bfe619b2 | ||
|
|
e0107bacbd | ||
|
|
b3bd2a6a09 | ||
|
|
ff1f58e022 | ||
|
|
557b12668d | ||
|
|
b058f1c7c2 | ||
|
|
3e07b6ca70 | ||
|
|
d66dc664de | ||
|
|
a2b9d78cf3 | ||
|
|
44f70da113 | ||
|
|
c87eaf576d | ||
|
|
c09b1395f6 | ||
|
|
d6a8201291 | ||
|
|
4187f97f7a | ||
|
|
9d3b2f12fd | ||
|
|
87c1bbbf40 | ||
|
|
a7ad6abdb9 | ||
|
|
a611406020 | ||
|
|
75da38224d | ||
|
|
8d6f3301c8 | ||
|
|
86b52a1c5e | ||
|
|
2c8b1c2022 | ||
|
|
ab1f065752 | ||
|
|
2c7256a443 | ||
|
|
97420b28e5 | ||
|
|
1467c7039d | ||
|
|
d319badc6c | ||
|
|
65d6c1c438 | ||
|
|
abeefb5655 | ||
|
|
50fecbcebe | ||
|
|
4fe8b72d42 | ||
|
|
f3b060df51 | ||
|
|
09d1cb6925 | ||
|
|
54fa72fa98 | ||
|
|
fd62fe7511 | ||
|
|
bfb34dff62 | ||
|
|
817b623596 | ||
|
|
f6dbeb1f63 | ||
|
|
c4b9bdd33f | ||
|
|
68e0d5f1b5 | ||
|
|
4939e3df55 | ||
|
|
19f19f3880 | ||
|
|
ad0c767ea8 | ||
|
|
ed5f4d5855 | ||
|
|
c2f5d37486 | ||
|
|
231b50dacb | ||
|
|
a71895cbe8 | ||
|
|
a8bede17b2 | ||
|
|
f6c8e693a5 | ||
|
|
9461e4088c | ||
|
|
efd73fd10d | ||
|
|
ddf6e84a1a | ||
|
|
633d6c34e2 | ||
|
|
6e7fbc6683 | ||
|
|
124ea1230b | ||
|
|
8b48a295b6 | ||
|
|
d285d37fdb | ||
|
|
8bb927b483 | ||
|
|
1af4cd0d63 | ||
|
|
db2f28c4cd |
No files matched your search
@@ -0,0 +1,17 @@
|
||||
# An .aiignore file follows the same syntax as a .gitignore file.
|
||||
# .gitignore documentation: https://git-scm.com/docs/gitignore
|
||||
|
||||
# you can ignore files
|
||||
.DS_Store
|
||||
*.log
|
||||
*.tmp
|
||||
*.bin
|
||||
*.prg
|
||||
|
||||
# or folders
|
||||
dist/
|
||||
build/
|
||||
out/
|
||||
output/
|
||||
.gradle/
|
||||
.kotlin/
|
||||
@@ -20,10 +20,10 @@ jobs:
|
||||
make -j4
|
||||
sudo make install
|
||||
|
||||
- name: Set up JDK 11
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 11
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
|
||||
- name: Build and test with Gradle
|
||||
|
||||
Generated
+1
@@ -15,6 +15,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/languageServer/languageServer.iml" filepath="$PROJECT_DIR$/languageServer/languageServer.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$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
; Binary Search Tree.
|
||||
; It's a simple implementation for test/demonstration purposes of the pointer support;
|
||||
; no balancing is done and memory is not freed when elements are removed.
|
||||
|
||||
%import textio
|
||||
|
||||
btree {
|
||||
|
||||
sub benchmark(uword max_time) -> uword {
|
||||
txt.nl()
|
||||
cbm.SETTIM(0,0,0)
|
||||
uword score
|
||||
while cbm.RDTIM16() < max_time {
|
||||
bench_operations()
|
||||
txt.chrout('.')
|
||||
score++
|
||||
}
|
||||
txt.nl()
|
||||
return score
|
||||
}
|
||||
|
||||
sub bench_operations() {
|
||||
arena.freeall()
|
||||
btree.root = 0
|
||||
|
||||
for cx16.r0 in [321, 719, 194, 550, 187, 203, 520, 562, 221, 676, 97, 852, 273, 326, 589, 606, 275, 794, 63, 716]
|
||||
btree.add(cx16.r0)
|
||||
|
||||
cx16.r0L = btree.size()
|
||||
btree.process_tree_inorder()
|
||||
btree.process_tree_preorder()
|
||||
|
||||
void btree.contains(203)
|
||||
void btree.contains(204)
|
||||
void btree.contains(605)
|
||||
void btree.contains(606)
|
||||
|
||||
btree.remove(9999)
|
||||
btree.remove(97)
|
||||
btree.remove(187)
|
||||
btree.remove(203)
|
||||
btree.remove(275)
|
||||
btree.remove(321)
|
||||
btree.remove(520)
|
||||
btree.remove(562)
|
||||
btree.remove(606)
|
||||
btree.remove(719)
|
||||
btree.remove(794)
|
||||
|
||||
cx16.r0L = btree.size()
|
||||
btree.process_tree_inorder()
|
||||
btree.process_tree_preorder()
|
||||
}
|
||||
|
||||
struct Node {
|
||||
^^Node left
|
||||
^^Node right
|
||||
uword value
|
||||
}
|
||||
|
||||
^^Node root = 0
|
||||
|
||||
sub add(uword value) {
|
||||
^^Node node = arena.alloc(sizeof(Node))
|
||||
node.value = value
|
||||
node.left = node.right = 0
|
||||
|
||||
if root==0
|
||||
root=node
|
||||
else {
|
||||
^^Node parent = root
|
||||
repeat {
|
||||
if parent.value >= value {
|
||||
if parent.left!=0
|
||||
parent = parent.left
|
||||
else {
|
||||
parent.left = node
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if parent.right!=0
|
||||
parent = parent.right
|
||||
else {
|
||||
parent.right = node
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub contains(uword value) -> bool {
|
||||
^^Node r = root
|
||||
while r!=0 {
|
||||
if r.value==value
|
||||
return true
|
||||
if r.value>value
|
||||
r = r.left
|
||||
else
|
||||
r = r.right
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub size() -> ubyte {
|
||||
ubyte count
|
||||
|
||||
if root!=0
|
||||
count_node(root)
|
||||
|
||||
return count
|
||||
|
||||
sub count_node(^^Node r) {
|
||||
count++
|
||||
if r.left!=0 {
|
||||
sys.pushw(r)
|
||||
count_node(r.left)
|
||||
r = sys.popw()
|
||||
}
|
||||
if r.right!=0 {
|
||||
sys.pushw(r)
|
||||
count_node(r.right)
|
||||
r = sys.popw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub remove(uword value) {
|
||||
; note: we don't deallocate the memory from the node, for simplicity sake
|
||||
^^Node n = root
|
||||
^^Node parent = 0
|
||||
while n!=0 {
|
||||
if n.value==value {
|
||||
if n.left==0
|
||||
replacechild(parent, n, n.right)
|
||||
else if n.right==0
|
||||
replacechild(parent, n, n.left)
|
||||
else {
|
||||
; Both left & right subtrees are present.
|
||||
; N = node to delete.
|
||||
; Find N's successor S. (N's right subtree's minimum element)
|
||||
; Attach N's left subtree to S.left (S doesn't have a left child)
|
||||
; Attach N's right subtree to Parent in place of N.
|
||||
^^Node successor = find_successor(n)
|
||||
successor.left = n.left
|
||||
replacechild(parent, n, n.right)
|
||||
}
|
||||
return
|
||||
}
|
||||
parent = n
|
||||
if n.value>value
|
||||
n = n.left
|
||||
else
|
||||
n = n.right
|
||||
}
|
||||
|
||||
sub find_successor(^^Node p) -> ^^Node {
|
||||
^^Node succ = p
|
||||
p = p.right
|
||||
while p!=0 {
|
||||
succ = p
|
||||
p = p.left
|
||||
}
|
||||
return succ
|
||||
}
|
||||
|
||||
sub replacechild(^^Node p, ^^Node child, ^^Node newchild) {
|
||||
if p.left==child
|
||||
p.left = newchild
|
||||
else
|
||||
p.right = newchild
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub process_tree_inorder() {
|
||||
if root!=0
|
||||
process_tree(root)
|
||||
|
||||
sub process_tree(^^Node r) {
|
||||
if r.left!=0 {
|
||||
sys.pushw(r)
|
||||
process_tree(r.left)
|
||||
r = sys.popw()
|
||||
}
|
||||
cx16.r0 = r.value
|
||||
if r.right!=0 {
|
||||
sys.pushw(r)
|
||||
process_tree(r.right)
|
||||
r = sys.popw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub process_tree_preorder() {
|
||||
if root!=0
|
||||
process_tree(root,0)
|
||||
|
||||
sub process_tree(^^Node r, ubyte depth) {
|
||||
cx16.r0 = r.value
|
||||
if r.left!=0 {
|
||||
sys.pushw(r)
|
||||
sys.push(depth)
|
||||
process_tree(r.left, depth+1)
|
||||
depth = sys.pop()
|
||||
r = sys.popw()
|
||||
}
|
||||
if r.right!=0 {
|
||||
sys.pushw(r)
|
||||
sys.push(depth)
|
||||
process_tree(r.right, depth+1)
|
||||
depth = sys.pop()
|
||||
r = sys.popw()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
arena {
|
||||
; extremely trivial arena allocator (that never frees)
|
||||
uword buffer = memory("arena", 2000, 0)
|
||||
uword next = buffer
|
||||
|
||||
sub alloc(ubyte size) -> uword {
|
||||
defer next += size
|
||||
return next
|
||||
}
|
||||
|
||||
sub freeall() {
|
||||
next = buffer
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
%import b_textelite
|
||||
%import b_maze
|
||||
%import b_sprites
|
||||
%import b_btree
|
||||
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
@@ -67,10 +68,6 @@ main {
|
||||
benchmark_score[benchmark_number] = circles.draw(false, 300)
|
||||
benchmark_number++
|
||||
|
||||
; announce_benchmark("circles with kernal")
|
||||
; benchmark_score[benchmark_number] = circles.draw(true, 300)
|
||||
; benchmark_number++
|
||||
|
||||
announce_benchmark("text-elite")
|
||||
benchmark_score[benchmark_number] = textelite.bench(120)
|
||||
benchmark_number++
|
||||
@@ -79,13 +76,17 @@ main {
|
||||
benchmark_score[benchmark_number] = animsprites.benchmark(300)
|
||||
benchmark_number++
|
||||
|
||||
announce_benchmark("btree-struct-pointers")
|
||||
benchmark_score[benchmark_number] = btree.benchmark(200)
|
||||
benchmark_number++
|
||||
|
||||
benchmark_names[benchmark_number] = 0
|
||||
benchmark_score[benchmark_number] = 0
|
||||
|
||||
cx16.set_screen_mode(3)
|
||||
txt.uppercase()
|
||||
txt.color2(1, 6)
|
||||
uword final_score
|
||||
uword total_score
|
||||
benchmark_number = 0
|
||||
txt.print("\nscore benchmark\n\n")
|
||||
do {
|
||||
@@ -93,14 +94,14 @@ main {
|
||||
txt.print_uw(benchmark_score[benchmark_number])
|
||||
txt.column(6)
|
||||
txt.print(benchmark_names[benchmark_number])
|
||||
final_score += benchmark_score[benchmark_number]
|
||||
total_score += benchmark_score[benchmark_number]
|
||||
txt.nl()
|
||||
benchmark_number++
|
||||
} until benchmark_names[benchmark_number]==0
|
||||
|
||||
txt.print("\n\nfinal score : ")
|
||||
txt.print_uw(final_score)
|
||||
txt.nl()
|
||||
txt.print("\n\ntotal score : ")
|
||||
txt.print_uw(total_score)
|
||||
txt.print(" (higher=better)\n")
|
||||
|
||||
sub announce_benchmark(str name) {
|
||||
benchmark_names[benchmark_number] = name
|
||||
|
||||
@@ -5,9 +5,10 @@ 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"
|
||||
|
||||
val PROG8_CONTAINER_MODULES = arrayOf(INTERNED_STRINGS_MODULENAME) // option to add more if needed one day
|
||||
|
||||
// all automatically generated labels everywhere need to have the same label name prefix:
|
||||
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ class FSignature(val pure: Boolean, // does it have side effects?
|
||||
val returns: ReturnConvention = when (returnType) {
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A)
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY)
|
||||
BaseDataType.LONG -> ReturnConvention(returnType, RegisterOrPair.R0R1_32)
|
||||
BaseDataType.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1)
|
||||
in IterableDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY)
|
||||
null -> ReturnConvention(null, null)
|
||||
@@ -45,6 +46,7 @@ class FSignature(val pure: Boolean, // does it have side effects?
|
||||
when (val paramType = actualParamTypes.first()) {
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A)
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY)
|
||||
BaseDataType.LONG -> ReturnConvention(returnType, RegisterOrPair.R0R1_32)
|
||||
BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
|
||||
in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
|
||||
else -> ReturnConvention(paramType, null)
|
||||
@@ -61,6 +63,7 @@ class FSignature(val pure: Boolean, // does it have side effects?
|
||||
val paramConv = when(val paramType = actualParamTypes[0]) {
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||
BaseDataType.LONG -> ParamConvention(paramType, RegisterOrPair.R0R1_32, false)
|
||||
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 IterableDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||
else -> ParamConvention(paramType, null, false)
|
||||
@@ -88,10 +91,10 @@ class FSignature(val pure: Boolean, // does it have side effects?
|
||||
val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"setlsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"setmsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
"rol" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"ror" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"rol2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"ror2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
|
||||
"rol" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)),
|
||||
"ror" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)),
|
||||
"rol2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)),
|
||||
"ror2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD, BaseDataType.LONG)),
|
||||
"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
|
||||
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
|
||||
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
|
||||
@@ -100,9 +103,11 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"abs__byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE)),
|
||||
"abs__word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD)),
|
||||
"abs__long" to FSignature(true, BaseDataType.LONG, FParam("value", BaseDataType.LONG)),
|
||||
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
|
||||
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
|
||||
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *(BaseDataType.entries - BaseDataType.STRUCT_INSTANCE).toTypedArray())),
|
||||
"offsetof" to FSignature(true, BaseDataType.UBYTE, FParam("field", BaseDataType.UBYTE)),
|
||||
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
|
||||
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
|
||||
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
|
||||
@@ -116,21 +121,26 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
"msb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||
"msw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
|
||||
"mkword" to FSignature(true, BaseDataType.UWORD, FParam("msb", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
|
||||
"mklong" to FSignature(true, BaseDataType.LONG, FParam("msb", BaseDataType.UBYTE), FParam("b2", BaseDataType.UBYTE), FParam("b1", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
|
||||
"mklong2" to FSignature(true, BaseDataType.LONG, FParam("msw", BaseDataType.UWORD), FParam("lsw", BaseDataType.UWORD)),
|
||||
"clamp" to FSignature(true, null, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
||||
"clamp__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
|
||||
"clamp__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE), FParam("minimum", BaseDataType.UBYTE), FParam("maximum", BaseDataType.UBYTE)),
|
||||
"clamp__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD), FParam("minimum", BaseDataType.WORD), FParam("maximum", BaseDataType.WORD)),
|
||||
"clamp__uword" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD), FParam("minimum", BaseDataType.UWORD), FParam("maximum", BaseDataType.UWORD)),
|
||||
"clamp__long" to FSignature(true, BaseDataType.LONG, FParam("value", BaseDataType.LONG), FParam("minimum", BaseDataType.LONG), FParam("maximum", BaseDataType.LONG)),
|
||||
"min" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"min__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"min__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
||||
"min__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||
"min__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||
"min__long" to FSignature(true, BaseDataType.LONG, FParam("val1", BaseDataType.LONG), FParam("val2", BaseDataType.LONG)),
|
||||
"max" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"max__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
|
||||
"max__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
|
||||
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
|
||||
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
|
||||
"max__long" to FSignature(true, BaseDataType.LONG, FParam("val1", BaseDataType.LONG), FParam("val2", BaseDataType.LONG)),
|
||||
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
|
||||
"peekbool" to FSignature(true, BaseDataType.BOOL, FParam("address", BaseDataType.UWORD)),
|
||||
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
|
||||
|
||||
@@ -13,6 +13,7 @@ class CompilationOptions(val output: OutputType,
|
||||
val noSysInit: Boolean,
|
||||
val romable: Boolean,
|
||||
val compTarget: ICompilationTarget,
|
||||
val compilerVersion: String,
|
||||
// these are set later, based on command line arguments or options in the source code:
|
||||
var loadAddress: UInt,
|
||||
var memtopAddress: UInt,
|
||||
|
||||
@@ -50,8 +50,10 @@ enum class BaseDataType {
|
||||
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.isLong get() = this == BaseDataType.LONG
|
||||
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.isWordOrByteOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, 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)
|
||||
@@ -143,7 +145,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
return if(splitwordarray && actualElementDt.isWord)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null)
|
||||
else {
|
||||
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
||||
if(actualElementDt.isNumericOrBool)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt, null)
|
||||
else
|
||||
throw NoSuchElementException("invalid basic element dt $elementDt")
|
||||
@@ -222,6 +224,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
BaseDataType.WORD -> "word[]"
|
||||
BaseDataType.UBYTE -> "ubyte[]"
|
||||
BaseDataType.UWORD -> "uword[]"
|
||||
BaseDataType.LONG -> "long[]"
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
@@ -284,6 +287,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
BaseDataType.BOOL -> "bool["
|
||||
BaseDataType.BYTE -> "byte["
|
||||
BaseDataType.WORD -> "@nosplit word["
|
||||
BaseDataType.LONG -> "long["
|
||||
BaseDataType.FLOAT -> "float["
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
@@ -344,6 +348,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
val isUnsignedWord = base == BaseDataType.UWORD
|
||||
val isSignedWord = base == BaseDataType.WORD
|
||||
val isInteger = base.isInteger
|
||||
val isWordOrByteOrBool = base.isWordOrByteOrBool
|
||||
val isIntegerOrBool = base.isIntegerOrBool
|
||||
val isNumeric = base.isNumeric
|
||||
val isNumericOrBool = base.isNumericOrBool
|
||||
@@ -362,6 +367,7 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
val isWordArray = base.isArray && !base.isPointerArray && (sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
val isUnsignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.UWORD
|
||||
val isSignedWordArray = base.isArray && !base.isPointerArray && sub == BaseDataType.WORD
|
||||
val isLongArray = base.isArray && sub == BaseDataType.LONG
|
||||
val isFloatArray = base.isArray && !base.isPointerArray && sub == BaseDataType.FLOAT
|
||||
val isString = base == BaseDataType.STR
|
||||
val isBool = base == BaseDataType.BOOL
|
||||
@@ -394,7 +400,9 @@ enum class RegisterOrPair {
|
||||
FAC2,
|
||||
// cx16 virtual registers:
|
||||
R0, R1, R2, R3, R4, R5, R6, R7,
|
||||
R8, R9, R10, R11, R12, R13, R14, R15;
|
||||
R8, R9, R10, R11, R12, R13, R14, R15,
|
||||
// combined virtual registers to store 32 bits longs:
|
||||
R0R1_32, R2R3_32, R4R5_32, R6R7_32, R8R9_32, R10R11_32, R12R13_32, R14R15_32;
|
||||
|
||||
companion object {
|
||||
val names by lazy { entries.map { it.toString()} }
|
||||
@@ -407,6 +415,18 @@ enum class RegisterOrPair {
|
||||
}
|
||||
}
|
||||
|
||||
fun startregname() = when(this) {
|
||||
R0R1_32 -> "r0"
|
||||
R2R3_32 -> "r2"
|
||||
R4R5_32 -> "r4"
|
||||
R6R7_32 -> "r6"
|
||||
R8R9_32 -> "r8"
|
||||
R10R11_32 -> "r10"
|
||||
R12R13_32 -> "r12"
|
||||
R14R15_32 -> "r14"
|
||||
else -> throw IllegalArgumentException("must be a combined virtual register $this")
|
||||
}
|
||||
|
||||
fun asCpuRegister(): CpuRegister = when(this) {
|
||||
A -> CpuRegister.A
|
||||
X -> CpuRegister.X
|
||||
@@ -421,12 +441,13 @@ enum class RegisterOrPair {
|
||||
BaseDataType.BYTE -> "sL"
|
||||
BaseDataType.WORD -> "s"
|
||||
BaseDataType.UWORD, null -> ""
|
||||
else -> throw IllegalArgumentException("invalid register param type")
|
||||
else -> throw IllegalArgumentException("invalid register param type for cx16 virtual reg")
|
||||
}
|
||||
return listOf("cx16", name.lowercase()+suffix)
|
||||
}
|
||||
|
||||
fun isWord() = this==AX || this == AY || this==XY || this in Cx16VirtualRegisters
|
||||
fun isLong() = this in combinedLongRegisters
|
||||
|
||||
} // only used in parameter and return value specs in asm subroutines
|
||||
|
||||
@@ -463,6 +484,17 @@ val Cx16VirtualRegisters = arrayOf(
|
||||
RegisterOrPair.R12, RegisterOrPair.R13, RegisterOrPair.R14, RegisterOrPair.R15
|
||||
)
|
||||
|
||||
val combinedLongRegisters = arrayOf(
|
||||
RegisterOrPair.R0R1_32,
|
||||
RegisterOrPair.R2R3_32,
|
||||
RegisterOrPair.R4R5_32,
|
||||
RegisterOrPair.R6R7_32,
|
||||
RegisterOrPair.R8R9_32,
|
||||
RegisterOrPair.R10R11_32,
|
||||
RegisterOrPair.R12R13_32,
|
||||
RegisterOrPair.R14R15_32
|
||||
)
|
||||
|
||||
val CpuRegisters = arrayOf(
|
||||
RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y,
|
||||
RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY
|
||||
|
||||
@@ -14,6 +14,7 @@ internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.STR -> numElements * 2
|
||||
BaseDataType.LONG -> numElements * 4
|
||||
BaseDataType.FLOAT-> numElements * floatsize
|
||||
BaseDataType.UNDEFINED -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
|
||||
@@ -252,6 +252,14 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
|
||||
newValue.add(newAddr)
|
||||
}
|
||||
}
|
||||
is PtBuiltinFunctionCall -> {
|
||||
// could be a struct instance or memory slab "allocation"
|
||||
if (elt.name != "prog8_lib_structalloc" && elt.name != "memory")
|
||||
throw AssemblyError("weird array value element $elt")
|
||||
else {
|
||||
newValue.add(elt)
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird array value element $elt")
|
||||
}
|
||||
}
|
||||
@@ -550,7 +558,7 @@ class AsmGen6502Internal (
|
||||
} else {
|
||||
return if (allocator.isZpVar((target as PtNamedNode).scopedName)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
loadAFromZpPointerVar(sourceName, true)
|
||||
loadAFromZpPointerVar(sourceName)
|
||||
sourceName
|
||||
} else {
|
||||
out("""
|
||||
@@ -609,7 +617,7 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
|
||||
internal fun loadAFromZpPointerVar(zpPointerVar: String, keepY: Boolean) {
|
||||
internal fun loadAFromZpPointerVar(zpPointerVar: String, keepY: Boolean=false) {
|
||||
if (isTargetCpu(CpuType.CPU65C02))
|
||||
out(" lda ($zpPointerVar)")
|
||||
else {
|
||||
@@ -798,6 +806,7 @@ class AsmGen6502Internal (
|
||||
RegisterOrPair.AY,
|
||||
RegisterOrPair.XY -> assignmentAsmGen.assignRegisterpairWord(target, reg)
|
||||
in Cx16VirtualRegisters -> assignmentAsmGen.assignVirtualRegister(target, reg)
|
||||
in combinedLongRegisters -> assignmentAsmGen.assignVirtualRegister(target, reg)
|
||||
RegisterOrPair.FAC1 -> assignmentAsmGen.assignFAC1float(target)
|
||||
RegisterOrPair.FAC2 -> assignmentAsmGen.assignFAC2float(target)
|
||||
else -> throw AssemblyError("invalid register")
|
||||
@@ -805,6 +814,9 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
internal fun assignExpressionTo(value: PtExpression, target: AsmAssignTarget) {
|
||||
|
||||
// this is basically the fallback assignment routine, it is RARELY called
|
||||
|
||||
when {
|
||||
target.datatype.isByteOrBool -> {
|
||||
if (value.asConstInteger()==0) {
|
||||
@@ -852,6 +864,98 @@ class AsmGen6502Internal (
|
||||
), value.definingISub()
|
||||
)
|
||||
}
|
||||
target.datatype.isLong -> {
|
||||
if(value is PtNumber) {
|
||||
val hex = value.asConstInteger()!!.toString(16).padStart(8, '0')
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
out("""
|
||||
lda #${hex.substring(6,8)}
|
||||
sta ${target.asmVarname}
|
||||
lda #${hex.substring(4, 6)}
|
||||
sta ${target.asmVarname}+1
|
||||
lda #${hex.substring(2, 4)}
|
||||
sta ${target.asmVarname}+2
|
||||
lda #${hex.take(2)}
|
||||
sta ${target.asmVarname}+3""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}")
|
||||
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
} else if(value is PtIdentifier && value.type.isLong) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
val valuesym = asmSymbolName(value)
|
||||
out(
|
||||
"""
|
||||
lda $valuesym
|
||||
sta ${target.asmVarname}
|
||||
lda $valuesym+1
|
||||
sta ${target.asmVarname}+1
|
||||
lda $valuesym+2
|
||||
sta ${target.asmVarname}+2
|
||||
lda $valuesym+3
|
||||
sta ${target.asmVarname}+3"""
|
||||
)
|
||||
}
|
||||
|
||||
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}")
|
||||
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
} else if(value is PtTypeCast && value.type.isLong) {
|
||||
if(value.value is PtIdentifier) {
|
||||
val valuesym = asmSymbolName((value.value as PtIdentifier).name)
|
||||
if(value.value.type.isByte) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
out(" lda $valuesym | sta ${target.asmVarname}")
|
||||
signExtendLongVariable(target.asmVarname, value.value.type.base)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}")
|
||||
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
} else if(value.value.type.isWord) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
out("""
|
||||
lda $valuesym
|
||||
sta ${target.asmVarname}
|
||||
lda $valuesym+1
|
||||
sta ${target.asmVarname}+1""")
|
||||
signExtendLongVariable(target.asmVarname, value.value.type.base)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.REGISTER -> {
|
||||
require(target.register in combinedLongRegisters)
|
||||
val startreg = target.register.toString().take(2).lowercase()
|
||||
out("""
|
||||
lda $valuesym
|
||||
sta cx16.$startreg
|
||||
lda $valuesym+1
|
||||
sta cx16.$startreg+1""")
|
||||
signExtendLongVariable("cx16.$startreg", value.value.type.base)
|
||||
}
|
||||
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
} else throw AssemblyError("weird casted type")
|
||||
} else {
|
||||
TODO("assign typecasted expression $value to a long target ${target.position} - use simple expressions and temporary variables for now")
|
||||
}
|
||||
} else {
|
||||
TODO("assign long expression $value to a target ${target.position} - use simple expressions and temporary variables for now")
|
||||
}
|
||||
}
|
||||
target.datatype.isFloat -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.FAC1)
|
||||
assignRegister(RegisterOrPair.FAC1, target)
|
||||
@@ -1182,7 +1286,7 @@ $repeatLabel""")
|
||||
if(returnvalue!=null) {
|
||||
val returnDt = sub.signature.returns.single()
|
||||
if (returnDt.isNumericOrBool || returnDt.isPointer) {
|
||||
assignExpressionToRegister(returnvalue, returnRegs.single().first.registerOrPair!!)
|
||||
assignExpressionToRegister(returnvalue, returnRegs.single().first.registerOrPair!!, returnDt.isSigned)
|
||||
}
|
||||
else {
|
||||
// all else take its address and assign that also to AY register pair
|
||||
@@ -1203,7 +1307,7 @@ $repeatLabel""")
|
||||
assignExpressionTo(it.first as PtExpression, tgt)
|
||||
}
|
||||
assigns.first().also {
|
||||
assignExpressionToRegister(it.first as PtExpression, it.second.first.registerOrPair!!)
|
||||
assignExpressionToRegister(it.first as PtExpression, it.second.first.registerOrPair!!, (it.first as PtExpression).type.isSigned)
|
||||
}
|
||||
}
|
||||
out(" rts")
|
||||
@@ -1289,6 +1393,50 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun signExtendLongVariable(asmvar: String, valueDt: BaseDataType) {
|
||||
// sign extend signed word in a var to a full long in that variable
|
||||
when(valueDt) {
|
||||
BaseDataType.UBYTE -> {
|
||||
if(isTargetCpu(CpuType.CPU65C02)) {
|
||||
out("""
|
||||
stz $asmvar+1
|
||||
stz $asmvar+2
|
||||
stz $asmvar+3""")
|
||||
}
|
||||
else {
|
||||
out("""
|
||||
lda #0
|
||||
sta $asmvar+1
|
||||
sta $asmvar+2
|
||||
sta $asmvar+3""")
|
||||
}
|
||||
}
|
||||
BaseDataType.BYTE -> {
|
||||
out("""
|
||||
lda $asmvar
|
||||
ora #$7f
|
||||
bmi +
|
||||
lda #0
|
||||
+ sta $asmvar+1
|
||||
sta $asmvar+2
|
||||
sta $asmvar+3""")
|
||||
}
|
||||
BaseDataType.UWORD -> {
|
||||
out(" lda #0 | sta $asmvar+2 | sta $asmvar+3")
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
out("""
|
||||
lda $asmvar+1
|
||||
ora #$7f
|
||||
bmi +
|
||||
lda #0
|
||||
+ sta $asmvar+2
|
||||
sta $asmvar+3""")
|
||||
}
|
||||
else -> throw AssemblyError("need byte or word type")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun isZpVar(variable: PtIdentifier): Boolean = allocator.isZpVar(variable.name)
|
||||
|
||||
internal fun jmp(asmLabel: String, indirect: Boolean=false, indexedX: Boolean=false) {
|
||||
|
||||
@@ -463,15 +463,19 @@ private fun optimizeStoreLoadSame(
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
}
|
||||
|
||||
// all 3 registers: lda VALUE + sta SOMEWHERE + lda VALUE -> last load can be eliminated
|
||||
// all 3 registers: lda VALUE + sta SOMEWHERE + lda VALUE -> last load can be eliminated IF NOT IO ADDRESS
|
||||
if (first.startsWith("lda ") && second.startsWith("sta ") && third.startsWith("lda ") ||
|
||||
first.startsWith("ldx ") && second.startsWith("stx ") && third.startsWith("ldx ") ||
|
||||
first.startsWith("ldy ") && second.startsWith("sty ") && third.startsWith("ldy ")
|
||||
) {
|
||||
val firstVal = first.substring(4).trimStart()
|
||||
val thirdVal = third.substring(4).trimStart()
|
||||
if (firstVal == thirdVal)
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
if (firstVal == thirdVal) {
|
||||
val address = getAddressArg(third, symbolTable)
|
||||
if (address != null && !machine.isIOAddress(address)) {
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mods
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.StMemorySlabBlockName
|
||||
import prog8.code.StStructInstanceBlockName
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
@@ -25,15 +27,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val sscope = fcall.definingISub()
|
||||
|
||||
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")
|
||||
"msw" -> funcMsw(fcall, resultRegister)
|
||||
"lsw" -> funcLsw(fcall, resultRegister)
|
||||
"msb" -> funcMsb(fcall, resultRegister)
|
||||
"lsb" -> funcLsb(fcall, resultRegister)
|
||||
"mkword" -> funcMkword(fcall, resultRegister)
|
||||
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(fcall, resultRegister)
|
||||
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(fcall, resultRegister)
|
||||
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(fcall, resultRegister)
|
||||
"abs__byte", "abs__word", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
|
||||
"mklong", "mklong2" -> funcMklong(fcall) // result is in R0:R1
|
||||
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword", "clamp__long" -> funcClamp(fcall, resultRegister)
|
||||
"min__byte", "min__ubyte", "min__word", "min__uword", "min__long" -> funcMin(fcall, resultRegister)
|
||||
"max__byte", "max__ubyte", "max__word", "max__uword", "max__long" -> funcMax(fcall, resultRegister)
|
||||
"abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
|
||||
"sgn" -> funcSgn(fcall, resultRegister, sscope)
|
||||
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
|
||||
"divmod__ubyte" -> funcDivmod(fcall)
|
||||
@@ -46,10 +49,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
"setmsb" -> funcSetLsbMsb(fcall, true)
|
||||
"memory" -> funcMemory(fcall, discardResult, resultRegister)
|
||||
"peekw" -> funcPeekW(fcall, resultRegister)
|
||||
"peekl" -> funcPeekL(fcall, resultRegister)
|
||||
"peekf" -> funcPeekF(fcall, resultRegister)
|
||||
"peekbool" -> funcPeekBool(fcall, resultRegister)
|
||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||
"pokew" -> funcPokeW(fcall)
|
||||
"pokel" -> funcPokeL(fcall)
|
||||
"pokef" -> funcPokeF(fcall)
|
||||
"pokemon" -> {
|
||||
val memread = PtMemoryByte(fcall.position)
|
||||
@@ -344,8 +349,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
} else
|
||||
throw AssemblyError("args for cmp() should have same dt")
|
||||
} else {
|
||||
// arg1 is a word
|
||||
} else if(arg1.type.isWord) {
|
||||
if(arg2.type.isWord) {
|
||||
when (arg2) {
|
||||
is PtIdentifier -> {
|
||||
@@ -376,6 +380,60 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
} else
|
||||
throw AssemblyError("args for cmp() should have same dt")
|
||||
} else if(arg1.type.isLong) {
|
||||
if(arg2.type.isLong) {
|
||||
if(arg1 is PtIdentifier && arg2 is PtNumber) {
|
||||
val var1 = asmgen.asmVariableName(arg1)
|
||||
val hex = arg2.number.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
lda $var1
|
||||
cmp #${hex.substring(6, 8)}
|
||||
bne +
|
||||
lda $var1+1
|
||||
cmp #${hex.substring(4, 6)}
|
||||
bne +
|
||||
lda $var1+2
|
||||
cmp #${hex.substring(2, 4)}
|
||||
bne +
|
||||
lda $var1+3
|
||||
cmp #${hex.take(2)}
|
||||
+""")
|
||||
} else if(arg1 is PtIdentifier && arg2 is PtIdentifier) {
|
||||
val var1 = asmgen.asmVariableName(arg1)
|
||||
val var2 = asmgen.asmVariableName(arg2)
|
||||
asmgen.out("""
|
||||
lda $var1
|
||||
cmp $var2
|
||||
bne +
|
||||
lda $var1+1
|
||||
cmp $var2+1
|
||||
bne +
|
||||
lda $var1+2
|
||||
cmp $var2+2
|
||||
bne +
|
||||
lda $var1+3
|
||||
cmp $var2+3
|
||||
+""")
|
||||
} else {
|
||||
assignAsmGen.assignExpressionToRegister(arg2, RegisterOrPair.R2R3_32, true)
|
||||
assignAsmGen.assignExpressionToRegister(arg1, RegisterOrPair.R0R1_32, true)
|
||||
asmgen.out("""
|
||||
lda cx16.r0
|
||||
cmp cx16.r2
|
||||
bne +
|
||||
lda cx16.r0+1
|
||||
cmp cx16.r2+1
|
||||
bne +
|
||||
lda cx16.r0+2
|
||||
cmp cx16.r2+2
|
||||
bne +
|
||||
lda cx16.r0+3
|
||||
cmp cx16.r2+3
|
||||
+""")
|
||||
}
|
||||
// TODO: carry flag is not correct......
|
||||
} else
|
||||
throw AssemblyError("args for cmp() should have same dt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,7 +443,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val name = (fcall.args[0] as PtString).value
|
||||
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("$StMemorySlabBlockName.memory_$name", DataType.UWORD, fcall.position)
|
||||
val addressOf = PtAddressOf(DataType.pointer(BaseDataType.UBYTE), false, fcall.position)
|
||||
addressOf.add(slabname)
|
||||
addressOf.parent = fcall
|
||||
@@ -399,9 +457,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(discardResult)
|
||||
throw AssemblyError("should not discard result of struct allocation at $fcall")
|
||||
// ... don't need to pay attention to args here because struct instance is put together elsewhere we just have to get a pointer to it
|
||||
val slabname = SymbolTable.labelnameForStructInstance(fcall)
|
||||
val prefix = if(fcall.args.isEmpty()) "${StStructInstanceBlockName}_bss" else StStructInstanceBlockName
|
||||
val labelname = PtIdentifier("$prefix.${SymbolTable.labelnameForStructInstance(fcall)}", fcall.type, fcall.position)
|
||||
val addressOf = PtAddressOf(fcall.type, true, fcall.position)
|
||||
addressOf.add(PtIdentifier(slabname, fcall.type, fcall.position))
|
||||
addressOf.add(labelname)
|
||||
addressOf.parent = fcall
|
||||
val src = AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, fcall.type, expression = addressOf)
|
||||
val target = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, null, asmgen)
|
||||
@@ -421,6 +480,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
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)
|
||||
}
|
||||
BaseDataType.LONG -> TODO("sqrt LONG")
|
||||
BaseDataType.FLOAT -> {
|
||||
asmgen.out(" jsr floats.func_sqrt_into_FAC1")
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||
@@ -477,6 +537,26 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
// a bit strange, rotating a signed type, but it has to do for now
|
||||
when(what) {
|
||||
is PtArrayIndexer -> TODO("ror2 long array ${what.position}")
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out("""
|
||||
lsr $variable+3
|
||||
ror $variable+2
|
||||
ror $variable+1
|
||||
ror $variable
|
||||
bcc +
|
||||
lda $variable+3
|
||||
ora #$80
|
||||
sta $variable+3
|
||||
+""")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
@@ -541,6 +621,17 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
// a bit strange, rotating a signed type, but it has to do for now
|
||||
when(what) {
|
||||
is PtArrayIndexer -> TODO("ror long array ${what.position}")
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" ror $variable+3 | ror $variable+2 | ror $variable+1 | ror $variable")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
@@ -592,6 +683,24 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
// a bit strange, rotating a signed type, but it has to do for now
|
||||
when(what) {
|
||||
is PtArrayIndexer -> TODO("rol2 long array ${what.position}")
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out("""
|
||||
asl $variable
|
||||
rol $variable+1
|
||||
rol $variable+2
|
||||
rol $variable+3
|
||||
bcc +
|
||||
inc $variable
|
||||
+""")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
@@ -655,6 +764,17 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
// a bit strange, rotating a signed type, but it has to do for now
|
||||
when(what) {
|
||||
is PtArrayIndexer -> TODO("rol long array ${what.position}")
|
||||
is PtIdentifier -> {
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" rol $variable | rol $variable+1 | rol $variable+2 | rol $variable+3")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
@@ -726,6 +846,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
BaseDataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
|
||||
BaseDataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
|
||||
BaseDataType.WORD -> asmgen.out(" jsr prog8_lib.func_sign_w_into_A")
|
||||
BaseDataType.LONG -> TODO("sgn LONG")
|
||||
BaseDataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
@@ -744,6 +865,10 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, fcall.position, scope, asmgen), RegisterOrPair.AY)
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
asmgen.out(" jsr prog8_lib.abs_l_into_R0R1")
|
||||
assignAsmGen.assignRegisterLong(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.R0R1_32, true, fcall.position, scope, asmgen), RegisterOrPair.R0R1_32)
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
asmgen.out(" jsr floats.func_abs_f_into_FAC1")
|
||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, fcall.position, scope, asmgen))
|
||||
@@ -881,6 +1006,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr prog8_lib.func_pokew")
|
||||
}
|
||||
|
||||
private fun funcPokeL(fcall: PtBuiltinFunctionCall) {
|
||||
val addrExpr = fcall.args[0]
|
||||
val value = fcall.args[1]
|
||||
TODO("pokel $addrExpr, $value")
|
||||
}
|
||||
|
||||
private fun funcPeekF(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out(" jsr floats.MOVFM")
|
||||
@@ -977,6 +1108,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcPeekL(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val addrExpr = fcall.args[0]
|
||||
val value = fcall.args[1]
|
||||
TODO("peekl $addrExpr, $value")
|
||||
}
|
||||
|
||||
|
||||
private fun funcClamp(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val signed = fcall.type.isSigned
|
||||
when {
|
||||
@@ -996,6 +1134,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
assignAsmGen.assignRegisterpairWord(targetReg, RegisterOrPair.AY)
|
||||
}
|
||||
fcall.type.isLong -> {
|
||||
TODO("clamp long ${fcall.position}")
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
@@ -1052,6 +1193,9 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||
}
|
||||
fcall.type.isLong -> {
|
||||
TODO("min long ${fcall.position}")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("min float not supported")
|
||||
}
|
||||
@@ -1060,55 +1204,109 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
|
||||
private fun funcMax(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val signed = fcall.type.isSigned
|
||||
if(fcall.type.isByte) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // right
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
if(signed) asmgen.out(" bpl +") else asmgen.out(" bcs +")
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_B1
|
||||
+""")
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.A, targetReg)
|
||||
} else if(fcall.type.isWord) {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
|
||||
if(signed) {
|
||||
when {
|
||||
fcall.type.isByte -> {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_B1", fcall.type) // left
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // right
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
if (signed) asmgen.out(" bpl +") else asmgen.out(" bcs +")
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
tya
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi +
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jmp ++
|
||||
+ lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
lda P8ZP_SCRATCH_B1
|
||||
+"""
|
||||
)
|
||||
val targetReg = AsmAssignTarget.fromRegisters(
|
||||
resultRegister ?: RegisterOrPair.A,
|
||||
signed,
|
||||
fcall.position,
|
||||
fcall.definingISub(),
|
||||
asmgen
|
||||
)
|
||||
asmgen.assignRegister(RegisterOrPair.A, targetReg)
|
||||
}
|
||||
fcall.type.isWord -> {
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", fcall.type) // left
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", fcall.type) // right
|
||||
if (signed) {
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
tya
|
||||
sbc P8ZP_SCRATCH_W2+1
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bmi +
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jmp ++
|
||||
+ lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
+""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
cmp P8ZP_SCRATCH_W2+1
|
||||
bcc ++
|
||||
bne +
|
||||
lda P8ZP_SCRATCH_W1
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
bcc ++
|
||||
+ lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jmp ++
|
||||
+ lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
+""")
|
||||
}
|
||||
val targetReg = AsmAssignTarget.fromRegisters(
|
||||
resultRegister ?: RegisterOrPair.AY,
|
||||
signed,
|
||||
fcall.position,
|
||||
fcall.definingISub(),
|
||||
asmgen
|
||||
)
|
||||
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||
}
|
||||
fcall.type.isLong -> {
|
||||
TODO("max long ${fcall.position}")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("max float not supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMklong(fcall: PtBuiltinFunctionCall) {
|
||||
// result long in R0:R1 (r0=lsw, r1=msw)
|
||||
|
||||
fun isArgRegister(expression: PtExpression, reg: RegisterOrPair): Boolean {
|
||||
if(expression !is PtIdentifier)
|
||||
return false
|
||||
return expression.name.startsWith("cx16.${reg.name.lowercase()}")
|
||||
}
|
||||
|
||||
if(fcall.args.size==2) {
|
||||
// mklong2(msw, lsw)
|
||||
if(isArgRegister(fcall.args[0], RegisterOrPair.R0) || isArgRegister(fcall.args[0], RegisterOrPair.R1) ||
|
||||
isArgRegister(fcall.args[1], RegisterOrPair.R0) || isArgRegister(fcall.args[1], RegisterOrPair.R1)) {
|
||||
error("cannot use R0 and/or R1 as arguments for mklong2 because the result should go into R0:R1 ${fcall.position}")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
cmp P8ZP_SCRATCH_W2+1
|
||||
bcc ++
|
||||
bne +
|
||||
lda P8ZP_SCRATCH_W1
|
||||
cmp P8ZP_SCRATCH_W2
|
||||
bcc ++
|
||||
+ lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jmp ++
|
||||
+ lda P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
+""")
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[0], "cx16.r1", DataType.UWORD)
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[1], "cx16.r0", DataType.UWORD)
|
||||
}
|
||||
val targetReg = AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, signed, fcall.position, fcall.definingISub(), asmgen)
|
||||
asmgen.assignRegister(RegisterOrPair.AY, targetReg)
|
||||
} else {
|
||||
throw AssemblyError("max float not supported")
|
||||
// mklong(msb, b2, b1, lsb)
|
||||
if(isArgRegister(fcall.args[0], RegisterOrPair.R0) || isArgRegister(fcall.args[0], RegisterOrPair.R1) ||
|
||||
isArgRegister(fcall.args[1], RegisterOrPair.R0) || isArgRegister(fcall.args[1], RegisterOrPair.R1) ||
|
||||
isArgRegister(fcall.args[2], RegisterOrPair.R0) || isArgRegister(fcall.args[2], RegisterOrPair.R1) ||
|
||||
isArgRegister(fcall.args[3], RegisterOrPair.R0) || isArgRegister(fcall.args[3], RegisterOrPair.R1)) {
|
||||
error("cannot use R0 and/or R1 as arguments for mklong because the result should go into R0:R1 ${fcall.position}")
|
||||
} else {
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[0], "cx16.r1H", DataType.UBYTE)
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[1], "cx16.r1L", DataType.UBYTE)
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[2], "cx16.r0H", DataType.UBYTE)
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[3], "cx16.r0L", DataType.UBYTE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1182,7 +1380,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
private fun funcMsb(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val arg = fcall.args.single()
|
||||
if (!arg.type.isWord)
|
||||
throw AssemblyError("msb required word argument")
|
||||
throw AssemblyError("msb requires word argument")
|
||||
if (arg is PtNumber)
|
||||
throw AssemblyError("msb(const) should have been const-folded away")
|
||||
if (arg is PtIdentifier) {
|
||||
@@ -1368,6 +1566,53 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcMsw(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val arg = fcall.args.single()
|
||||
if (!arg.type.isLong)
|
||||
throw AssemblyError("msw requires long argument")
|
||||
if (arg is PtNumber)
|
||||
throw AssemblyError("msw(const) should have been const-folded away")
|
||||
if (arg is PtIdentifier) {
|
||||
val sourceName = asmgen.asmVariableName(arg)
|
||||
when(resultRegister) {
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+2 | ldx $sourceName+3")
|
||||
null, RegisterOrPair.AY -> asmgen.out(" lda $sourceName+2 | ldy $sourceName+3")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+2 | ldy $sourceName+3")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
asmgen.out(" lda $sourceName+2 | sta cx16.$regname | lda $sourceName+3 | sta cx16.$regname+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
} else {
|
||||
TODO("msw(expression) ${fcall.position} - use a temporary variable for now")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcLsw(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) {
|
||||
val arg = fcall.args.single()
|
||||
if (!arg.type.isLong)
|
||||
throw AssemblyError("lsw requires long argument")
|
||||
if (arg is PtNumber)
|
||||
throw AssemblyError("lsw(const) should have been const-folded away")
|
||||
if (arg is PtIdentifier) {
|
||||
val sourceName = asmgen.asmVariableName(arg)
|
||||
when(resultRegister) {
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx $sourceName+1")
|
||||
null, RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy $sourceName+1")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy $sourceName+1")
|
||||
in Cx16VirtualRegisters -> {
|
||||
val regname = resultRegister.name.lowercase()
|
||||
asmgen.out(" lda $sourceName | sta cx16.$regname | lda $sourceName+1 | sta cx16.$regname+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
} else {
|
||||
TODO("lsw(expression) ${fcall.position} - use a temporary variable for now")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun translateArguments(call: PtBuiltinFunctionCall, scope: IPtSubroutine?) {
|
||||
val signature = BuiltinFunctions.getValue(call.name)
|
||||
val callConv = signature.callConvention(call.args.map {
|
||||
@@ -1410,6 +1655,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val varname = "prog8_lib.func_${call.name}._arg_${paramName}"
|
||||
val src = when {
|
||||
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
|
||||
conv.dt==BaseDataType.LONG -> TODO("LONG argument for builtin func")
|
||||
conv.dt.isPassByRef -> {
|
||||
// put the address of the argument in AY
|
||||
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false, value.position)
|
||||
@@ -1428,6 +1674,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
conv.reg != null -> {
|
||||
val src = when {
|
||||
conv.dt==BaseDataType.FLOAT -> getSourceForFloat(value)
|
||||
conv.dt==BaseDataType.LONG -> AsmAssignSource.fromAstSource(value, program, asmgen)
|
||||
conv.dt.isPassByRef -> {
|
||||
// put the address of the argument in AY
|
||||
val addr = PtAddressOf(DataType.forDt(conv.dt).typeForAddressOf(false), false,value.position)
|
||||
@@ -1439,7 +1686,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
AsmAssignSource.fromAstSource(value, program, asmgen)
|
||||
}
|
||||
}
|
||||
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, value.position, null, asmgen)
|
||||
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, conv.dt.isSigned, value.position, null, asmgen)
|
||||
val assign = AsmAssignment(src, listOf(tgt), program.memsizer, value.position)
|
||||
asmgen.translateNormalAssignment(assign, scope)
|
||||
}
|
||||
|
||||
@@ -505,137 +505,158 @@ $endLabel""")
|
||||
is StMemVar -> symbol.length!!
|
||||
else -> 0u
|
||||
}
|
||||
when {
|
||||
iterableDt.isString -> {
|
||||
if(asmgen.options.romable) {
|
||||
val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
asmgen.out("""
|
||||
|
||||
fun iterateStrings() {
|
||||
if(asmgen.options.romable) {
|
||||
val indexVar = asmgen.createTempVarReused(BaseDataType.UBYTE, false, stmt)
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sty $indexVar
|
||||
$loopLabel lda $iterableName,y
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
inc $indexVar
|
||||
ldy $indexVar
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
} else {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
} else {
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
sty $indexVar
|
||||
$loopLabel lda $iterableName,y
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
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("""
|
||||
}
|
||||
|
||||
fun iterateBytes() {
|
||||
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
|
||||
lda $iterableName,y
|
||||
sta ${asmgen.asmVariableName(stmt.variable)}""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
cpy #$numElements
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 256
|
||||
asmgen.out("""
|
||||
} else {
|
||||
// length is 256
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
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.isSplitWordArray -> {
|
||||
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("""
|
||||
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)
|
||||
}
|
||||
|
||||
fun iterateSplitWords() {
|
||||
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}_lsb,y
|
||||
sta $loopvarName
|
||||
lda ${iterableName}_msb,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=255u) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
cpy #$numElements
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 256
|
||||
asmgen.out("""
|
||||
} else {
|
||||
// length is 256
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
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.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("""
|
||||
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)
|
||||
}
|
||||
|
||||
fun iterateWords(actuallyLongs: Boolean = false) {
|
||||
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) {
|
||||
if(actuallyLongs) {
|
||||
asmgen.out("""
|
||||
lda $iterableName+2,y
|
||||
sta $loopvarName+2
|
||||
lda $iterableName+3,y
|
||||
sta $loopvarName+3""")
|
||||
}
|
||||
asmgen.translate(stmt.statements)
|
||||
if(numElements<=127u) {
|
||||
if(actuallyLongs) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
iny
|
||||
iny
|
||||
cpy #${numElements*4u}
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
@@ -643,6 +664,19 @@ $loopLabel sty $indexVar
|
||||
cpy #${numElements*2u}
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
}
|
||||
} else {
|
||||
if(actuallyLongs) {
|
||||
// array size is 64 longs, 256 bytes
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
iny
|
||||
iny
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
|
||||
} else {
|
||||
// array size is 128 words, 256 bytes
|
||||
asmgen.out("""
|
||||
@@ -652,23 +686,29 @@ $loopLabel sty $indexVar
|
||||
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")
|
||||
}
|
||||
}
|
||||
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")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
|
||||
when {
|
||||
iterableDt.isString -> iterateStrings()
|
||||
iterableDt.isByteArray || iterableDt.isBoolArray -> iterateBytes()
|
||||
iterableDt.isSplitWordArray -> iterateSplitWords()
|
||||
iterableDt.isWordArray -> iterateWords()
|
||||
iterableDt.isLongArray -> iterateWords(true)
|
||||
iterableDt.isFloatArray -> throw AssemblyError("for loop with floating point variables is not supported")
|
||||
else -> throw AssemblyError("can't iterate over $iterableDt")
|
||||
}
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
|
||||
@@ -21,7 +21,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
// we consider them NOT to be optimized into (possibly different) CPU registers.
|
||||
// Just load them in whatever the register spec says.
|
||||
return when (params.size) {
|
||||
1 -> params[0].register == null && (params[0].type.isIntegerOrBool || params[0].type.isPointer)
|
||||
1 -> params[0].register == null && (params[0].type.isWordOrByteOrBool || params[0].type.isPointer)
|
||||
2 -> params[0].type.isByteOrBool && params[1].type.isByteOrBool && params[0].register == null && params[1].register == null
|
||||
else -> false
|
||||
}
|
||||
@@ -206,8 +206,11 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
is PtBuiltinFunctionCall -> {
|
||||
if (arg.name in arrayOf("lsb", "msb", "lsw", "msw"))
|
||||
return usesOtherRegistersWhileEvaluating(arg.args[0])
|
||||
if (arg.name == "mkword")
|
||||
if (arg.name == "mkword" || arg.name == "mklong2")
|
||||
return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1])
|
||||
if (arg.name == "mklong")
|
||||
return usesOtherRegistersWhileEvaluating(arg.args[0]) || usesOtherRegistersWhileEvaluating(arg.args[1]) ||
|
||||
usesOtherRegistersWhileEvaluating(arg.args[2]) || usesOtherRegistersWhileEvaluating(arg.args[3])
|
||||
return !arg.isSimple()
|
||||
}
|
||||
is PtAddressOf -> false
|
||||
|
||||
@@ -46,6 +46,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfByte(stmt, jumpAfterIf)
|
||||
rightDt.isWord || rightDt.isPointer -> translateIfWord(stmt, compareCond, jumpAfterIf)
|
||||
rightDt.isLong -> translateIfLong(stmt, compareCond, jumpAfterIf)
|
||||
rightDt.isFloat -> translateIfFloat(stmt, compareCond, jumpAfterIf)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
@@ -575,6 +576,32 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfLong(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
|
||||
val constValue = condition.right.asConstInteger()
|
||||
if(constValue==0) {
|
||||
// optimized comparisons with zero
|
||||
return when (condition.operator) {
|
||||
"==" -> longEqualsZero(condition.left, false, jumpAfterIf, stmt)
|
||||
"!=" -> longEqualsZero(condition.left, true, jumpAfterIf, stmt)
|
||||
"<" -> longLessZero(condition.left, false, jumpAfterIf, stmt)
|
||||
"<=" -> longLessZero(condition.left, true, jumpAfterIf, stmt)
|
||||
">" -> longGreaterZero(condition.left, false, jumpAfterIf, stmt)
|
||||
">=" -> longGreaterZero(condition.left, true, jumpAfterIf, stmt)
|
||||
else -> throw AssemblyError("expected comparison operator")
|
||||
}
|
||||
}
|
||||
|
||||
return when (condition.operator) {
|
||||
"==" -> longEqualsValue(condition.left, condition.right, false, jumpAfterIf, stmt)
|
||||
"!=" -> longEqualsValue(condition.left, condition.right, true, jumpAfterIf, stmt)
|
||||
"<" -> TODO("long < 0")
|
||||
"<=" -> TODO("long <= 0")
|
||||
">" -> TODO("long > 0")
|
||||
">=" -> TODO("long >= 0")
|
||||
else -> throw AssemblyError("expected comparison operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfWord(stmt: PtIfElse, condition: PtBinaryExpression, jumpAfterIf: PtJump?) {
|
||||
val signed = condition.left.type.isSigned
|
||||
val constValue = condition.right.asConstInteger()
|
||||
@@ -606,7 +633,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
private fun wordGreaterEqualsZero(value: PtExpression, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
// special case for word>=0
|
||||
if(signed) {
|
||||
loadAndCmp0MSB(value)
|
||||
loadAndCmp0MSB(value, false)
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("bpl", "bmi", jump, stmt.elseScope)
|
||||
else
|
||||
@@ -619,7 +646,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
private fun wordLessZero(value: PtExpression, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
// special case for word<0
|
||||
if(signed) {
|
||||
loadAndCmp0MSB(value)
|
||||
loadAndCmp0MSB(value, false)
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("bmi", "bpl", jump, stmt.elseScope)
|
||||
else
|
||||
@@ -894,23 +921,31 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
|
||||
|
||||
private fun loadAndCmp0MSB(value: PtExpression) {
|
||||
private fun loadAndCmp0MSB(value: PtExpression, long: Boolean) {
|
||||
when(value) {
|
||||
is PtArrayIndexer -> {
|
||||
if(value.variable==null)
|
||||
TODO("support for ptr indexing ${value.position}")
|
||||
val varname = asmgen.asmVariableName(value.variable!!)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
if(value.splitWords)
|
||||
if(value.splitWords) {
|
||||
require(!long)
|
||||
asmgen.out(" lda ${varname}_msb,y")
|
||||
}
|
||||
else if(long)
|
||||
asmgen.out(" lda $varname+3,y")
|
||||
else
|
||||
asmgen.out(" lda $varname+1,y")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val varname = asmgen.asmVariableName(value)
|
||||
asmgen.out(" lda $varname+1")
|
||||
if(long)
|
||||
asmgen.out(" lda $varname+3")
|
||||
else
|
||||
asmgen.out(" lda $varname+1")
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
require(!long)
|
||||
if(value.isFromArrayElement) {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
|
||||
asmgen.out(" cpy #0")
|
||||
@@ -923,6 +958,7 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
require(!long)
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
|
||||
asmgen.out(" cpy #0")
|
||||
}
|
||||
@@ -1226,6 +1262,50 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
|
||||
private fun longEqualsZero(value: PtExpression, notEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.R0R1_32, value.type.isSigned)
|
||||
asmgen.out("""
|
||||
lda cx16.r0
|
||||
ora cx16.r0+1
|
||||
ora cx16.r0+2
|
||||
ora cx16.r0+3""")
|
||||
if(notEquals) {
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("bne", "beq", jump, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("beq", stmt)
|
||||
} else {
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("beq", "bne", jump, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bne", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun longLessZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
if(lessEquals) {
|
||||
TODO("long <= 0")
|
||||
} else {
|
||||
loadAndCmp0MSB(value, true)
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("bmi", "bpl", jump, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bpl", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun longGreaterZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
if(lessEquals) {
|
||||
TODO("long >= 0")
|
||||
} else {
|
||||
loadAndCmp0MSB(value, true)
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("bpl", "bmi", jump, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bmi", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun wordEqualsZero(value: PtExpression, notEquals: Boolean, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
|
||||
// special case for (u)word == 0
|
||||
@@ -1303,6 +1383,93 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
|
||||
private fun longEqualsValue(
|
||||
left: PtExpression,
|
||||
right: PtExpression,
|
||||
notEquals: Boolean,
|
||||
jump: PtJump?,
|
||||
stmt: PtIfElse
|
||||
) {
|
||||
// TODO this can be optimized somewhat more when the left operand is a variable as well
|
||||
// we only optimize for a const right value for now
|
||||
|
||||
val constRight = right.asConstInteger()
|
||||
val variableRight = (right as? PtIdentifier)?.name
|
||||
if(constRight!=null) {
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.R0R1_32, left.type.isSigned)
|
||||
val hex = constRight.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
lda cx16.r0
|
||||
cmp #$${hex.substring(6,8)}
|
||||
bne +
|
||||
lda cx16.r0+1
|
||||
cmp #$${hex.substring(4, 6)}
|
||||
bne +
|
||||
lda cx16.r0+2
|
||||
cmp #$${hex.substring(2, 4)}
|
||||
bne +
|
||||
lda cx16.r0+3
|
||||
cmp #$${hex.take(2)}
|
||||
+""")
|
||||
} else if(variableRight!=null) {
|
||||
require(right.type.isLong)
|
||||
val variableLeft = (left as? PtIdentifier)?.name
|
||||
if(variableLeft!=null) {
|
||||
asmgen.out("""
|
||||
lda #<$variableLeft
|
||||
ldy #>$variableLeft
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<$variableRight
|
||||
ldy #>$variableRight
|
||||
jsr prog8_lib.long_not_equals""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.R0R1_32, left.type.isSigned)
|
||||
asmgen.out("""
|
||||
lda cx16.r0
|
||||
cmp $variableRight
|
||||
bne +
|
||||
lda cx16.r0+1
|
||||
cmp $variableRight+1
|
||||
bne +
|
||||
lda cx16.r0+2
|
||||
cmp $variableRight+2
|
||||
bne +
|
||||
lda cx16.r0+3
|
||||
cmp $variableRight+3
|
||||
+""")
|
||||
}
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.R2R3_32, left.type.isSigned)
|
||||
asmgen.assignExpressionToRegister(right, RegisterOrPair.R0R1_32, right.type.isSigned)
|
||||
asmgen.out("""
|
||||
lda cx16.r0
|
||||
cmp cx16.r2
|
||||
bne +
|
||||
lda cx16.r0+1
|
||||
cmp cx16.r2+1
|
||||
bne +
|
||||
lda cx16.r0+2
|
||||
cmp cx16.r2+2
|
||||
bne +
|
||||
lda cx16.r0+3
|
||||
cmp cx16.r2+3
|
||||
+""")
|
||||
}
|
||||
|
||||
if(notEquals) {
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("bne", "beq", jump, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("beq", stmt)
|
||||
} else {
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("beq", "bne", jump, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bne", stmt)
|
||||
}
|
||||
}
|
||||
|
||||
private fun wordEqualsValue(
|
||||
left: PtExpression,
|
||||
right: PtExpression,
|
||||
|
||||
@@ -32,6 +32,14 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
expr.type.isLong -> {
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.R0R1_32, true)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(falseLabel)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.R0R1_32, true)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterLong(target, RegisterOrPair.R0R1_32)
|
||||
}
|
||||
expr.type.isFloat -> {
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.FAC1, true)
|
||||
asmgen.jmp(endLabel)
|
||||
@@ -114,6 +122,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
return when {
|
||||
rightDt.isByteOrBool -> translateIfExpressionByteConditionBranch(condition, falseLabel)
|
||||
rightDt.isWord -> translateIfExpressionWordConditionBranch(condition, falseLabel)
|
||||
rightDt.isLong -> translateIfExpressionLongConditionBranch(condition, falseLabel)
|
||||
rightDt.isFloat -> translateIfExpressionFloatConditionBranch(condition, falseLabel)
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
@@ -200,6 +209,36 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
asmgen.out(" beq $falseLabel")
|
||||
}
|
||||
|
||||
private fun translateIfExpressionLongConditionBranch(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 translateLongExprIsZero(condition.left, falseLabel)
|
||||
"!=" -> return translateLongExprIsNotZero(condition.left, falseLabel)
|
||||
}
|
||||
}
|
||||
if (constValue != 0) {
|
||||
when (condition.operator) {
|
||||
"==" -> return translateLongExprEqualsNumber(condition.left, constValue, falseLabel)
|
||||
"!=" -> return translateLongExprNotEqualsNumber(condition.left, constValue, falseLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
val variable = condition.right as? PtIdentifier
|
||||
if(variable!=null) {
|
||||
when (condition.operator) {
|
||||
"==" -> return translateLongExprEqualsVariable(condition.left, variable, falseLabel)
|
||||
"!=" -> return translateLongExprNotEqualsVariable(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) {
|
||||
@@ -339,6 +378,89 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateLongExprEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
|
||||
// if L==number
|
||||
// TODO reuse code from ifElse?
|
||||
val hex = number.toUInt().toString(16).padStart(8, '0')
|
||||
if(expr is PtIdentifier) {
|
||||
val varname = asmgen.asmVariableName(expr)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #${hex.substring(6, 8)}
|
||||
bne $falseLabel
|
||||
lda $varname+1
|
||||
cmp #${hex.substring(4, 6)}
|
||||
bne $falseLabel
|
||||
lda $varname+2
|
||||
cmp #${hex.substring(2, 4)}
|
||||
bne $falseLabel
|
||||
lda $varname+3
|
||||
cmp #${hex.take(2)}
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R0R1_32)
|
||||
asmgen.out("""
|
||||
lda cx16.r0
|
||||
cmp #${hex.substring(6, 8)}
|
||||
bne $falseLabel
|
||||
lda cx16.r0+1
|
||||
cmp #${hex.substring(4, 6)}
|
||||
bne $falseLabel
|
||||
lda cx16.r0+2
|
||||
cmp #${hex.substring(2, 4)}
|
||||
bne $falseLabel
|
||||
lda cx16.r0+3
|
||||
cmp #${hex.take(2)}
|
||||
bne $falseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateLongExprEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
|
||||
// if L==variable
|
||||
// TODO reuse code from ifElse?
|
||||
val varname2 = asmgen.asmVariableName(variable)
|
||||
if(expr is PtIdentifier) {
|
||||
val varname = asmgen.asmVariableName(expr)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp $varname2
|
||||
bne $falseLabel
|
||||
lda $varname+1
|
||||
cmp $varname2+1
|
||||
bne $falseLabel
|
||||
lda $varname+2
|
||||
cmp $varname2+2
|
||||
bne $falseLabel
|
||||
lda $varname+3
|
||||
cmp $varname2+3
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R0R1_32)
|
||||
asmgen.out("""
|
||||
lda cx16.r0
|
||||
cmp $varname2
|
||||
bne $falseLabel
|
||||
lda cx16.r0+1
|
||||
cmp $varname2+1
|
||||
bne $falseLabel
|
||||
lda cx16.r0+2
|
||||
cmp $varname2+2
|
||||
bne $falseLabel
|
||||
lda cx16.r0+3
|
||||
cmp $varname2+3
|
||||
bne $falseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateLongExprNotEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
|
||||
TODO("if expression LONG != number ${expr.position}")
|
||||
}
|
||||
|
||||
private fun translateLongExprNotEqualsVariable(expr: PtExpression, variable: PtIdentifier, falseLabel: String) {
|
||||
// if L!=variable
|
||||
TODO("if expression LONG != variable ${expr.position}")
|
||||
}
|
||||
|
||||
private fun translateWordExprIsNotZero(expr: PtExpression, falseLabel: String) {
|
||||
// if w!=0
|
||||
// TODO reuse code from ifElse?
|
||||
@@ -369,6 +491,50 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateLongExprIsZero(expr: PtExpression, falseLabel: String) {
|
||||
// if L==0
|
||||
// TODO reuse code from ifElse?
|
||||
if(expr is PtIdentifier) {
|
||||
val varname = asmgen.asmVariableName(expr)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
ora $varname+1
|
||||
ora $varname+2
|
||||
ora $varname+3
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R0R1_32)
|
||||
asmgen.out("""
|
||||
lda cx16.r0
|
||||
ora cx16.r0+1
|
||||
ora cx16.r0+2
|
||||
ora cx16.r0+3
|
||||
bne $falseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateLongExprIsNotZero(expr: PtExpression, falseLabel: String) {
|
||||
// if L!=0
|
||||
// TODO reuse code from ifElse?
|
||||
if(expr is PtIdentifier) {
|
||||
val varname = asmgen.asmVariableName(expr)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
ora $varname+1
|
||||
ora $varname+2
|
||||
ora $varname+3
|
||||
beq $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R0R1_32)
|
||||
asmgen.out("""
|
||||
lda cx16.r0
|
||||
ora cx16.r0+1
|
||||
ora cx16.r0+2
|
||||
ora cx16.r0+3
|
||||
beq $falseLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateIfCompareWithZeroByteBranch(condition: PtBinaryExpression, signed: Boolean, falseLabel: String) {
|
||||
// optimized code for byte comparisons with 0
|
||||
|
||||
|
||||
@@ -210,7 +210,7 @@ internal class ProgramAndVarsGen(
|
||||
private fun memorySlabs() {
|
||||
if(symboltable.allMemorySlabs.isNotEmpty()) {
|
||||
asmgen.out("; memory slabs\n .section BSS_SLABS")
|
||||
asmgen.out("prog8_slabs\t.block")
|
||||
asmgen.out("$StMemorySlabBlockName\t.block")
|
||||
for (slab in symboltable.allMemorySlabs) {
|
||||
if (slab.align > 1u)
|
||||
asmgen.out("\t.align ${slab.align.toHex()}")
|
||||
@@ -377,6 +377,7 @@ internal class ProgramAndVarsGen(
|
||||
dt.isSignedByte -> ".char"
|
||||
dt.isUnsignedWord || dt.isPointer -> ".word"
|
||||
dt.isSignedWord -> ".sint"
|
||||
dt.isLong -> ".dint"
|
||||
dt.isFloat -> ".byte"
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
@@ -418,14 +419,7 @@ internal class ProgramAndVarsGen(
|
||||
structtype.fields.withIndex().forEach { (index, field) ->
|
||||
val dt = field.first
|
||||
val varname = "f${index}"
|
||||
val type = when {
|
||||
dt.isBool || dt.isUnsignedByte -> ".byte"
|
||||
dt.isSignedByte -> ".char"
|
||||
dt.isUnsignedWord || dt.isPointer -> ".word"
|
||||
dt.isSignedWord -> ".sint"
|
||||
dt.isFloat -> ".byte" // TODO check that float bytes are passed as an array parameter
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
val type = asmTypeString(dt)
|
||||
asmgen.out("p8v_${field.second} $type \\$varname") // note: struct field symbol prefixing done here because that is a lot simpler than fixing up all expressions in the AST
|
||||
}
|
||||
asmgen.out(" .endstruct\n")
|
||||
@@ -434,6 +428,7 @@ internal class ProgramAndVarsGen(
|
||||
val (instancesNoInit, instances) = symboltable.allStructInstances.partition { it.initialValues.isEmpty() }
|
||||
asmgen.out("; struct instances without initialization values, as BSS zeroed at startup\n")
|
||||
asmgen.out(" .section BSS\n")
|
||||
asmgen.out("${StStructInstanceBlockName}_bss .block\n")
|
||||
instancesNoInit.forEach {
|
||||
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
|
||||
val zerovalues = structtype.fields.map { field ->
|
||||
@@ -445,11 +440,17 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
asmgen.out("${it.name} .dstruct ${it.structName}, ${zerovalues.joinToString(",")}\n")
|
||||
}
|
||||
asmgen.out(" .endblock\n")
|
||||
asmgen.out(" .send BSS\n")
|
||||
|
||||
asmgen.out("; struct instances with initialization values\n")
|
||||
asmgen.out(" .section STRUCTINSTANCES\n")
|
||||
instances.forEach { asmgen.out("${it.name} .dstruct ${it.structName}, ${initValues(it).joinToString(",")}\n") }
|
||||
asmgen.out("$StStructInstanceBlockName .block\n")
|
||||
instances.forEach {
|
||||
val instancename = it.name.substringAfter('.')
|
||||
asmgen.out("$instancename .dstruct ${it.structName}, ${initValues(it).joinToString(",")}\n")
|
||||
}
|
||||
asmgen.out(" .endblock\n")
|
||||
asmgen.out(" .send STRUCTINSTANCES\n")
|
||||
}
|
||||
|
||||
@@ -564,6 +565,7 @@ internal class ProgramAndVarsGen(
|
||||
else when(dt) {
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL -> asmgen.out("$name .byte ?")
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> asmgen.out("$name .word ?")
|
||||
BaseDataType.LONG -> asmgen.out("$name .sint ?")
|
||||
BaseDataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.FLOAT_MEM_SIZE}")
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
@@ -774,6 +776,7 @@ internal class ProgramAndVarsGen(
|
||||
dt.isSignedByte -> asmgen.out("${variable.name}\t.char ?")
|
||||
dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ?")
|
||||
dt.isSignedWord -> asmgen.out("${variable.name}\t.sint ?")
|
||||
dt.isLong -> asmgen.out("${variable.name}\t.dint ?")
|
||||
dt.isFloat -> asmgen.out("${variable.name}\t.fill ${compTarget.FLOAT_MEM_SIZE}")
|
||||
dt.isSplitWordArray -> {
|
||||
alignVar(variable.align)
|
||||
@@ -822,6 +825,7 @@ internal class ProgramAndVarsGen(
|
||||
// dt.isSignedByte -> asmgen.out("${variable.name}\t.char $initialValue")
|
||||
// dt.isUnsignedWord -> asmgen.out("${variable.name}\t.word ${initialValue.toHex()}")
|
||||
// dt.isSignedWord -> asmgen.out("${variable.name}\t.sint $initialValue")
|
||||
// dt.isLong -> asmgen.out("${variable.name}\t.dint $initialValue")
|
||||
// dt.isFloat -> {
|
||||
// if(initialValue==0) {
|
||||
// asmgen.out("${variable.name}\t.byte 0,0,0,0,0 ; float")
|
||||
@@ -866,7 +870,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
dt.isSplitWordArray -> {
|
||||
if(dt.elementType().isUnsignedWord) {
|
||||
if(dt.elementType().isUnsignedWord || dt.elementType().isPointer) {
|
||||
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
|
||||
asmgen.out("_array_$varname := ${data.joinToString()}")
|
||||
asmgen.out("${varname}_lsb\t.byte <_array_$varname")
|
||||
@@ -898,6 +902,16 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out(" .sint " + chunk.joinToString())
|
||||
}
|
||||
}
|
||||
dt.isLongArray -> {
|
||||
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
|
||||
if (data.size <= 16)
|
||||
asmgen.out("$varname\t.dint ${data.joinToString()}")
|
||||
else {
|
||||
asmgen.out(varname)
|
||||
for (chunk in data.chunked(16))
|
||||
asmgen.out(" .dint " + chunk.joinToString())
|
||||
}
|
||||
}
|
||||
dt.isFloatArray -> {
|
||||
val array = value ?: zeroFilledArray(orNumberOfZeros!!)
|
||||
val floatFills = array.map {
|
||||
@@ -914,7 +928,7 @@ internal class ProgramAndVarsGen(
|
||||
private fun zeroFilledArray(numElts: Int): StArray {
|
||||
val values = mutableListOf<StArrayElement>()
|
||||
repeat(numElts) {
|
||||
values.add(StArrayElement(0.0, null, null))
|
||||
values.add(StArrayElement(0.0, null, null,null,null))
|
||||
}
|
||||
return values
|
||||
}
|
||||
@@ -972,7 +986,7 @@ internal class ProgramAndVarsGen(
|
||||
val number = it.number!!.toInt()
|
||||
"$"+number.toString(16).padStart(2, '0')
|
||||
}
|
||||
dt.isArray && dt.elementType().isUnsignedWord -> array.map {
|
||||
dt.isArray && (dt.elementType().isUnsignedWord || dt.elementType().isPointer) -> array.map {
|
||||
if(it.number!=null) {
|
||||
"$" + it.number!!.toInt().toString(16).padStart(4, '0')
|
||||
}
|
||||
@@ -984,8 +998,15 @@ internal class ProgramAndVarsGen(
|
||||
else
|
||||
asmgen.asmSymbolName(addrOfSymbol)
|
||||
}
|
||||
else
|
||||
else if(it.structInstance!=null) {
|
||||
asmgen.asmSymbolName("${StStructInstanceBlockName}.${it.structInstance!!}")
|
||||
}
|
||||
else if(it.structInstanceUninitialized!=null) {
|
||||
asmgen.asmSymbolName("${StStructInstanceBlockName}_bss.${it.structInstanceUninitialized!!}")
|
||||
}
|
||||
else {
|
||||
throw AssemblyError("weird array elt")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
@@ -1013,7 +1034,7 @@ internal class ProgramAndVarsGen(
|
||||
val number = it.number!!.toInt()
|
||||
"$" + number.toString(16).padStart(4, '0')
|
||||
}
|
||||
dt.isArray && dt.elementType().isSignedWord -> array.map {
|
||||
dt.isSignedWordArray -> array.map {
|
||||
val number = it.number!!.toInt()
|
||||
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
|
||||
if(number>=0)
|
||||
@@ -1021,6 +1042,14 @@ internal class ProgramAndVarsGen(
|
||||
else
|
||||
"-$$hexnum"
|
||||
}
|
||||
dt.isLongArray -> array.map {
|
||||
val number = it.number!!.toInt()
|
||||
val hexnum = number.absoluteValue.toString(16).padStart(8, '0')
|
||||
if(number>=0)
|
||||
"$$hexnum"
|
||||
else
|
||||
"-$$hexnum"
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,12 @@ internal class AnyExprAsmGen(
|
||||
}
|
||||
throw AssemblyError("expression should have been handled otherwise: word ${expr.operator} at ${expr.position}")
|
||||
}
|
||||
expr.type.isLong -> {
|
||||
require(expr.left.type.isLong && expr.right.type.isLong) {
|
||||
"both operands must be longs"
|
||||
}
|
||||
throw AssemblyError("expression should have been handled otherwise: long ${expr.operator} at ${expr.position}")
|
||||
}
|
||||
expr.type.isFloat -> {
|
||||
require(expr.left.type.isFloat && expr.right.type.isFloat) {
|
||||
"both operands must be floats"
|
||||
|
||||
@@ -10,8 +10,8 @@ internal enum class TargetStorageKind {
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
POINTER, // wherever the pointer variable points to
|
||||
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||
POINTER,
|
||||
VOID // assign nothing - used in multi-value assigns for void placeholders
|
||||
}
|
||||
|
||||
internal enum class SourceStorageKind {
|
||||
@@ -114,25 +114,17 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
RegisterOrPair.FAC2 -> {
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers)
|
||||
}
|
||||
RegisterOrPair.R0,
|
||||
RegisterOrPair.R1,
|
||||
RegisterOrPair.R2,
|
||||
RegisterOrPair.R3,
|
||||
RegisterOrPair.R4,
|
||||
RegisterOrPair.R5,
|
||||
RegisterOrPair.R6,
|
||||
RegisterOrPair.R7,
|
||||
RegisterOrPair.R8,
|
||||
RegisterOrPair.R9,
|
||||
RegisterOrPair.R10,
|
||||
RegisterOrPair.R11,
|
||||
RegisterOrPair.R12,
|
||||
RegisterOrPair.R13,
|
||||
RegisterOrPair.R14,
|
||||
RegisterOrPair.R15 -> {
|
||||
in Cx16VirtualRegisters -> {
|
||||
val dt = if(signed) DataType.WORD else DataType.UWORD
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||
}
|
||||
in combinedLongRegisters -> {
|
||||
val dt = if(signed) DataType.LONG
|
||||
else
|
||||
TODO("unsigned long")
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers)
|
||||
}
|
||||
else -> throw AssemblyError("weird register $registers")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.AsmGen6502Internal
|
||||
import prog8.codegen.cpu6502.VariableAllocator
|
||||
import kotlin.math.log2
|
||||
|
||||
|
||||
internal class AssignmentAsmGen(
|
||||
@@ -209,6 +210,7 @@ internal class AssignmentAsmGen(
|
||||
when (assign.target.datatype.base) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> assignConstantByte(assign.target, num)
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> assignConstantWord(assign.target, num)
|
||||
BaseDataType.LONG -> assignConstantLong(assign.target, num)
|
||||
BaseDataType.FLOAT -> assignConstantFloat(assign.target, num.toDouble())
|
||||
else -> throw AssemblyError("weird numval type")
|
||||
}
|
||||
@@ -221,6 +223,7 @@ internal class AssignmentAsmGen(
|
||||
BaseDataType.BOOL -> assignConstantByte(assign.target, if(num==0.0) 0 else 1)
|
||||
BaseDataType.UBYTE, BaseDataType.BYTE -> assignConstantByte(assign.target, num.toInt())
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> assignConstantWord(assign.target, num.toInt())
|
||||
BaseDataType.LONG -> assignConstantLong(assign.target, num.toInt())
|
||||
BaseDataType.FLOAT -> assignConstantFloat(assign.target, num)
|
||||
BaseDataType.POINTER -> assignConstantWord(assign.target, num.toInt())
|
||||
else -> throw AssemblyError("weird numval type")
|
||||
@@ -243,6 +246,7 @@ internal class AssignmentAsmGen(
|
||||
else
|
||||
assignVariableWord(assign.target, variable, assign.source.datatype)
|
||||
}
|
||||
targetDt.isLong -> assignVariableLong(assign.target, variable, assign.source.datatype)
|
||||
targetDt.isFloat -> assignVariableFloat(assign.target, variable)
|
||||
targetDt.isString -> assignVariableString(assign.target, variable)
|
||||
targetDt.isPointer -> assignVariableWord(assign.target, variable, assign.source.datatype)
|
||||
@@ -392,7 +396,7 @@ internal class AssignmentAsmGen(
|
||||
|
||||
// fallback assignmen through temporary pointer var
|
||||
assignExpressionToVariable(address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false)
|
||||
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
}
|
||||
|
||||
@@ -535,7 +539,7 @@ internal class AssignmentAsmGen(
|
||||
private fun assignPrefixExpr(assign: AsmAssignment, value: PtPrefix, scope: IPtSubroutine?) {
|
||||
if(assign.target.array==null) {
|
||||
if(assign.source.datatype isAssignableTo assign.target.datatype || (assign.source.datatype.isBool && assign.target.datatype.isByte)) {
|
||||
if(assign.source.datatype.isIntegerOrBool) {
|
||||
if(assign.source.datatype.isWordOrByteOrBool) {
|
||||
val signed = assign.source.datatype.isSigned
|
||||
if(assign.source.datatype.isByteOrBool) {
|
||||
assignExpressionToRegister(value.value, RegisterOrPair.A, signed)
|
||||
@@ -650,6 +654,10 @@ internal class AssignmentAsmGen(
|
||||
else -> throw AssemblyError("str return value type mismatch with target")
|
||||
}
|
||||
}
|
||||
returnDt== BaseDataType.LONG -> {
|
||||
// longs are in R0:R1 (r0=lsw, r1=msw)
|
||||
assignRegisterLong(target, RegisterOrPair.R0R1_32)
|
||||
}
|
||||
returnDt==BaseDataType.FLOAT -> {
|
||||
// float result from function sits in FAC1
|
||||
assignFAC1float(target)
|
||||
@@ -702,22 +710,8 @@ internal class AssignmentAsmGen(
|
||||
RegisterOrPair.AX -> assignVirtualRegister(target, RegisterOrPair.AX)
|
||||
RegisterOrPair.AY -> assignVirtualRegister(target, RegisterOrPair.AY)
|
||||
RegisterOrPair.XY -> assignVirtualRegister(target, RegisterOrPair.XY)
|
||||
RegisterOrPair.R0 -> assignVirtualRegister(target, RegisterOrPair.R0)
|
||||
RegisterOrPair.R1 -> assignVirtualRegister(target, RegisterOrPair.R1)
|
||||
RegisterOrPair.R2 -> assignVirtualRegister(target, RegisterOrPair.R2)
|
||||
RegisterOrPair.R3 -> assignVirtualRegister(target, RegisterOrPair.R3)
|
||||
RegisterOrPair.R4 -> assignVirtualRegister(target, RegisterOrPair.R4)
|
||||
RegisterOrPair.R5 -> assignVirtualRegister(target, RegisterOrPair.R5)
|
||||
RegisterOrPair.R6 -> assignVirtualRegister(target, RegisterOrPair.R6)
|
||||
RegisterOrPair.R7 -> assignVirtualRegister(target, RegisterOrPair.R7)
|
||||
RegisterOrPair.R8 -> assignVirtualRegister(target, RegisterOrPair.R8)
|
||||
RegisterOrPair.R9 -> assignVirtualRegister(target, RegisterOrPair.R9)
|
||||
RegisterOrPair.R10 -> assignVirtualRegister(target, RegisterOrPair.R10)
|
||||
RegisterOrPair.R11 -> assignVirtualRegister(target, RegisterOrPair.R11)
|
||||
RegisterOrPair.R12 -> assignVirtualRegister(target, RegisterOrPair.R12)
|
||||
RegisterOrPair.R13 -> assignVirtualRegister(target, RegisterOrPair.R13)
|
||||
RegisterOrPair.R14 -> assignVirtualRegister(target, RegisterOrPair.R14)
|
||||
RegisterOrPair.R15 -> assignVirtualRegister(target, RegisterOrPair.R15)
|
||||
in Cx16VirtualRegisters -> assignVirtualRegister(target, returnValue.first.registerOrPair!!)
|
||||
in combinedLongRegisters -> assignVirtualRegister(target, returnValue.first.registerOrPair!!)
|
||||
else -> {
|
||||
val sflag = returnValue.first.statusflag
|
||||
if(sflag!=null)
|
||||
@@ -770,6 +764,10 @@ internal class AssignmentAsmGen(
|
||||
assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
target.datatype.isWord || target.datatype.isPointer -> assignRegisterpairWord(target, register)
|
||||
target.datatype.isLong -> {
|
||||
require(register in combinedLongRegisters)
|
||||
assignRegisterLong(target, register)
|
||||
}
|
||||
else -> throw AssemblyError("expected byte or word")
|
||||
}
|
||||
}
|
||||
@@ -1124,7 +1122,7 @@ internal class AssignmentAsmGen(
|
||||
asmgen.out(" pla")
|
||||
asmgen.out(" sta cx16.r0 | sty cx16.r0+1")
|
||||
}
|
||||
asmgen.out(" jsr verafx.muls")
|
||||
asmgen.out(" jsr verafx.muls16")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return true
|
||||
} else {
|
||||
@@ -1145,6 +1143,14 @@ internal class AssignmentAsmGen(
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.A, expr.type.isSigned)
|
||||
if (value in asmgen.optimizedByteMultiplications)
|
||||
asmgen.out(" jsr prog8_math.mul_byte_${value}")
|
||||
else if(value in powersOfTwoInt) {
|
||||
val shifts = log2(value.toDouble()).toInt()
|
||||
if(shifts>=8) {
|
||||
asmgen.out(" lda #0")
|
||||
} else {
|
||||
repeat(shifts) { asmgen.out(" asl a") }
|
||||
}
|
||||
}
|
||||
else
|
||||
asmgen.out(" ldy #$value | jsr prog8_math.multiply_bytes")
|
||||
assignRegisterByte(target, CpuRegister.A, false, true)
|
||||
@@ -1155,14 +1161,33 @@ internal class AssignmentAsmGen(
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type.isSigned)
|
||||
asmgen.out(" jsr prog8_math.mul_word_${value}")
|
||||
}
|
||||
else if(value in powersOfTwoInt) {
|
||||
val shifts = log2(value.toDouble()).toInt()
|
||||
if(shifts>=16) {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type.isSigned)
|
||||
asmgen.out(" lda #0 | ldy #0")
|
||||
} else {
|
||||
if(target.kind==TargetStorageKind.VARIABLE && target.datatype.isWord) {
|
||||
assignExpressionToVariable(expr.left, target.asmVarname, target.datatype)
|
||||
repeat(shifts) { asmgen.out(" asl ${target.asmVarname} | rol ${target.asmVarname}+1") }
|
||||
return true
|
||||
} else {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type.isSigned)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_REG")
|
||||
repeat(shifts) { asmgen.out(" asl a | rol P8ZP_SCRATCH_REG") }
|
||||
asmgen.out(" ldy P8ZP_SCRATCH_REG")
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type.isSigned)
|
||||
if(expr.definingBlock()!!.options.veraFxMuls){
|
||||
// cx16 verafx hardware mul
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "cx16.r1")
|
||||
asmgen.out("""
|
||||
sta cx16.r0
|
||||
sty cx16.r0+1
|
||||
jsr verafx.muls""")
|
||||
jsr verafx.muls16""")
|
||||
} else {
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "prog8_math.multiply_words.multiplier")
|
||||
asmgen.out(" jsr prog8_math.multiply_words")
|
||||
@@ -1209,7 +1234,7 @@ internal class AssignmentAsmGen(
|
||||
asmgen.out(" jsr prog8_math.asl_byte_A")
|
||||
assignRegisterByte(target, CpuRegister.A, signed, true)
|
||||
return true
|
||||
} else {
|
||||
} else if(dt.isWord) {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, signed)
|
||||
asmgen.restoreRegisterStack(CpuRegister.X, true)
|
||||
if(expr.operator==">>")
|
||||
@@ -1221,6 +1246,27 @@ internal class AssignmentAsmGen(
|
||||
asmgen.out(" jsr prog8_math.asl_word_AY")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return true
|
||||
} else if(dt.isLong) {
|
||||
|
||||
if(target.kind==TargetStorageKind.VARIABLE) {
|
||||
asmgen.out(" pla")
|
||||
asmgen.assignExpressionTo(expr.left, target)
|
||||
require(expr.right.type.isByte)
|
||||
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.X)
|
||||
asmgen.out(" lda #<${target.asmVarname} | ldy #>${target.asmVarname}")
|
||||
if (expr.operator == "<<") {
|
||||
asmgen.out(" jsr prog8_lib.long_shiftleftX_inplace")
|
||||
} else {
|
||||
asmgen.out(" jsr prog8_lib.long_shiftrightX_inplace")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.R0R1_32, signed)
|
||||
asmgen.out(" pla | sta P8ZP_SCRATCH_REG")
|
||||
augmentableAsmGen.inplacemodificationLongWithVariable("cx16.r0", expr.operator, "P8ZP_SCRATCH_REG")
|
||||
assignRegisterLong(target, RegisterOrPair.R0R1_32)
|
||||
return true
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1340,12 +1386,32 @@ internal class AssignmentAsmGen(
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else if(dt.isLong) {
|
||||
|
||||
if(target.kind==TargetStorageKind.VARIABLE) {
|
||||
asmgen.assignExpressionTo(expr.left, target)
|
||||
require(expr.right.type.isByte)
|
||||
asmgen.out(" lda #<${target.asmVarname} | ldy #>${target.asmVarname}")
|
||||
asmgen.out(" ldx #$shifts")
|
||||
if (expr.operator == "<<") {
|
||||
asmgen.out(" jsr prog8_lib.long_shiftleftX_inplace")
|
||||
} else {
|
||||
asmgen.out(" jsr prog8_lib.long_shiftrightX_inplace")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.R0R1_32, signed)
|
||||
augmentableAsmGen.inplacemodificationLongWithLiteralval("cx16.r0", expr.operator, shifts)
|
||||
assignRegisterLong(target, RegisterOrPair.R0R1_32)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun optimizedPlusMinExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
||||
// TODO can this use the target directly as intermediate, when it's a variable? To save the final assignRegister call.
|
||||
val dt = expr.type
|
||||
val left = expr.left
|
||||
val right = expr.right
|
||||
@@ -1660,9 +1726,87 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(dt.isLong) {
|
||||
return optimizedPlusMinLongExpr(expr, target)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun optimizedPlusMinLongExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
||||
val left = expr.left
|
||||
val right = expr.right
|
||||
when(right) {
|
||||
is PtIdentifier -> {
|
||||
if(target.kind == TargetStorageKind.VARIABLE) {
|
||||
asmgen.assignExpressionTo(left, target)
|
||||
val rightsym = asmgen.asmVariableName(right)
|
||||
asmgen.out("""
|
||||
lda #<$rightsym
|
||||
ldy #>$rightsym
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<${target.asmVarname}
|
||||
ldy #>${target.asmVarname}""")
|
||||
if (expr.operator == "+") {
|
||||
asmgen.out(" jsr prog8_lib.long_add_inplace")
|
||||
} else {
|
||||
asmgen.out(" jsr prog8_lib.long_sub_inplace")
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
TODO("add/subtract long ${target.position} - use simple expressions and temporary variables for now")
|
||||
}
|
||||
}
|
||||
is PtNumber -> {
|
||||
val hex = right.number.toInt().toString(16).padStart(8, '0')
|
||||
if (target.kind == TargetStorageKind.VARIABLE) {
|
||||
asmgen.assignExpressionTo(left, target)
|
||||
if (expr.operator == "+") {
|
||||
asmgen.out("""
|
||||
lda ${target.asmVarname}
|
||||
clc
|
||||
adc #$${hex.substring(6,8)}
|
||||
sta ${target.asmVarname}
|
||||
lda ${target.asmVarname}+1
|
||||
adc #$${hex.substring(4, 6)}
|
||||
sta ${target.asmVarname}+1
|
||||
lda ${target.asmVarname}+2
|
||||
adc #$${hex.substring(2, 4)}
|
||||
sta ${target.asmVarname}+2
|
||||
lda ${target.asmVarname}+3
|
||||
adc #$${hex.take(2)}
|
||||
sta ${target.asmVarname}+3""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda ${target.asmVarname}
|
||||
sec
|
||||
sbc #$${hex.substring(6, 8)}
|
||||
sta ${target.asmVarname}
|
||||
lda ${target.asmVarname}+1
|
||||
sbc #$${hex.substring(4, 6)}
|
||||
sta ${target.asmVarname}+1
|
||||
lda ${target.asmVarname}+2
|
||||
sbc #$${hex.substring(2, 4)}
|
||||
sta ${target.asmVarname}+2
|
||||
lda ${target.asmVarname}+3
|
||||
sbc #$${hex.take(2)}
|
||||
sta ${target.asmVarname}+3""")
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
TODO("add/subtract long ${target.position} - use simple expressions and temporary variables for now")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.R2R3_32, expr.left.type.isSigned)
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.R0R1_32, expr.right.type.isSigned)
|
||||
augmentableAsmGen.inplacemodificationLongWithVariable("cx16.r2", expr.operator, "cx16.r0")
|
||||
assignRegisterLong(target, RegisterOrPair.R2R3_32)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun optimizedPointerIndexPlusMinusByteIntoA(value: PtExpression, operator: String, mem: PtMemoryByte): Boolean {
|
||||
// special optimization for bytevalue +/- pointervar[y] (actually: bytevalue +/- @(address) )
|
||||
val address = mem.address as? PtBinaryExpression
|
||||
@@ -1783,6 +1927,58 @@ internal class AssignmentAsmGen(
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
return true
|
||||
}
|
||||
else if (expr.left.type.isLong && expr.right.type.isLong) {
|
||||
|
||||
if(target.kind == TargetStorageKind.VARIABLE) {
|
||||
if(expr.left is PtIdentifier) {
|
||||
asmgen.assignExpressionTo(expr.right, target)
|
||||
val varname = asmgen.asmVariableName(expr.left as PtIdentifier)
|
||||
asmgen.out("""
|
||||
lda #<$varname
|
||||
ldy #>$varname
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<${target.asmVarname}
|
||||
ldy #>${target.asmVarname}""")
|
||||
when(expr.operator) {
|
||||
"|" -> asmgen.out(" jsr prog8_lib.long_or_inplace")
|
||||
"&" -> asmgen.out(" jsr prog8_lib.long_and_inplace")
|
||||
"^" -> asmgen.out(" jsr prog8_lib.long_xor_inplace")
|
||||
else -> throw AssemblyError("wrong bitwise operator")
|
||||
}
|
||||
return true
|
||||
} else if(expr.right is PtIdentifier) {
|
||||
asmgen.assignExpressionTo(expr.left, target)
|
||||
val varname = asmgen.asmVariableName(expr.right as PtIdentifier)
|
||||
asmgen.out("""
|
||||
lda #<$varname
|
||||
ldy #>$varname
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<${target.asmVarname}
|
||||
ldy #>${target.asmVarname}""")
|
||||
when(expr.operator) {
|
||||
"|" -> asmgen.out(" jsr prog8_lib.long_or_inplace")
|
||||
"&" -> asmgen.out(" jsr prog8_lib.long_and_inplace")
|
||||
"^" -> asmgen.out(" jsr prog8_lib.long_xor_inplace")
|
||||
else -> throw AssemblyError("wrong bitwise operator")
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.R2R3_32, expr.left.type.isSigned)
|
||||
val constval = expr.right.asConstInteger()
|
||||
val varname = (expr.right as? PtIdentifier)?.name
|
||||
if(constval!=null)
|
||||
augmentableAsmGen.inplacemodificationLongWithLiteralval("cx16.r2", expr.operator, constval)
|
||||
else if(varname!=null)
|
||||
augmentableAsmGen.inplacemodificationLongWithVariable("cx16.r2", expr.operator, varname)
|
||||
else
|
||||
augmentableAsmGen.inplacemodificationLongWithExpression("cx16.r2", expr.operator, expr.right)
|
||||
assignRegisterLong(target, RegisterOrPair.R2R3_32)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -2217,7 +2413,7 @@ $endLabel""")
|
||||
|
||||
fun assignViaExprEval(addressExpression: PtExpression) {
|
||||
asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2", false)
|
||||
asmgen.loadAFromZpPointerVar("P8ZP_SCRATCH_W2")
|
||||
asmgen.out(" ldy #0")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
@@ -2292,6 +2488,9 @@ $endLabel""")
|
||||
assignExpressionToRegister(value, RegisterOrPair.A, valueDt.isSigned)
|
||||
assignTypeCastedRegisters(target.asmVarname, targetDt.base, RegisterOrPair.A, valueDt.base)
|
||||
}
|
||||
valueDt.isLong -> {
|
||||
TODO("assign typecasted long to $targetDt ${value.position}")
|
||||
}
|
||||
valueDt.isWord || valueDt.isPointer -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.AY, valueDt.isSigned)
|
||||
assignTypeCastedRegisters(target.asmVarname, targetDt.base, RegisterOrPair.AY, valueDt.base)
|
||||
@@ -2389,9 +2588,9 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
if(targetDt.isInteger && valueDt.isIntegerOrBool && valueDt.isAssignableTo(targetDt)) {
|
||||
require(targetDt.isWord && valueDt.isByteOrBool) {
|
||||
"should be byte to word assignment ${origTypeCastExpression.position}"
|
||||
if(targetDt.isInteger && valueDt.isByteOrBool && valueDt.isAssignableTo(targetDt)) {
|
||||
require(targetDt.isWord || targetDt.isLong) {
|
||||
"should be byte to word or long assignment ${origTypeCastExpression.position}"
|
||||
}
|
||||
when(target.kind) {
|
||||
// TargetStorageKind.VARIABLE -> {
|
||||
@@ -2412,7 +2611,7 @@ $endLabel""")
|
||||
return
|
||||
}
|
||||
TargetStorageKind.REGISTER -> {
|
||||
// byte to word, just assign to registers
|
||||
// byte to word or long, just assign to registers
|
||||
assignExpressionToRegister(value, target.register!!, targetDt.isSigned)
|
||||
return
|
||||
}
|
||||
@@ -2526,6 +2725,7 @@ $endLabel""")
|
||||
else
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
}
|
||||
BaseDataType.LONG -> TODO("assign typecasted to LONG")
|
||||
BaseDataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
lda #<$targetAsmVarName
|
||||
@@ -2553,6 +2753,11 @@ $endLabel""")
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE)
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE)
|
||||
asmgen.signExtendLongVariable(targetAsmVarName, BaseDataType.WORD)
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
lda #<$targetAsmVarName
|
||||
@@ -2581,6 +2786,7 @@ $endLabel""")
|
||||
BaseDataType.WORD -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
|
||||
}
|
||||
BaseDataType.LONG -> TODO("assign typecasted to LONG")
|
||||
BaseDataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
lda #<$targetAsmVarName
|
||||
@@ -2610,6 +2816,14 @@ $endLabel""")
|
||||
BaseDataType.UWORD, BaseDataType.POINTER -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
asmgen.out("""
|
||||
lda $sourceAsmVarName
|
||||
sta $targetAsmVarName
|
||||
lda $sourceAsmVarName+1
|
||||
sta $targetAsmVarName+1""")
|
||||
asmgen.signExtendLongVariable(targetAsmVarName, BaseDataType.WORD)
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
lda #<$targetAsmVarName
|
||||
@@ -2670,6 +2884,7 @@ $endLabel""")
|
||||
else
|
||||
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
}
|
||||
BaseDataType.LONG -> TODO("assign typecasted to LONG")
|
||||
BaseDataType.FLOAT -> {
|
||||
when(regs) {
|
||||
RegisterOrPair.A -> asmgen.out(" tay")
|
||||
@@ -2709,6 +2924,7 @@ $endLabel""")
|
||||
asmgen.signExtendAYlsb(sourceDt)
|
||||
asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
|
||||
}
|
||||
BaseDataType.LONG -> TODO("assign typecasted to LONG")
|
||||
BaseDataType.FLOAT -> {
|
||||
when(regs) {
|
||||
RegisterOrPair.A -> {}
|
||||
@@ -2740,6 +2956,7 @@ $endLabel""")
|
||||
else -> throw AssemblyError("non-word regs")
|
||||
}
|
||||
}
|
||||
BaseDataType.LONG -> TODO("assign typecasted to LONG")
|
||||
BaseDataType.FLOAT -> {
|
||||
if(regs!=RegisterOrPair.AY)
|
||||
throw AssemblyError("only supports AY here")
|
||||
@@ -2769,6 +2986,15 @@ $endLabel""")
|
||||
else -> throw AssemblyError("non-word regs")
|
||||
}
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
when(regs) {
|
||||
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
|
||||
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
|
||||
RegisterOrPair.XY -> asmgen.out(" stx $targetAsmVarName | sty $targetAsmVarName+1")
|
||||
else -> throw AssemblyError("non-word regs")
|
||||
}
|
||||
asmgen.signExtendLongVariable(targetAsmVarName, BaseDataType.WORD)
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
if(regs!=RegisterOrPair.AY)
|
||||
throw AssemblyError("only supports AY here")
|
||||
@@ -2975,6 +3201,112 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignVariableLong(target: AsmAssignTarget, varName: String, sourceDt: DataType) {
|
||||
require(sourceDt.isByte || sourceDt.isWord || sourceDt.isLong) {
|
||||
"need byte/word/long as source value to assign to long variable $varName ${target.position}"
|
||||
}
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
when(sourceDt) {
|
||||
DataType.BYTE -> {
|
||||
TODO("signed byte to long var")
|
||||
}
|
||||
DataType.UBYTE -> {
|
||||
TODO("ubyte to long var")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
TODO("signed word to long var")
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
TODO("ubyte to long var")
|
||||
}
|
||||
DataType.LONG -> {
|
||||
asmgen.out("""
|
||||
lda $varName
|
||||
sta ${target.asmVarname}
|
||||
lda $varName+1
|
||||
sta ${target.asmVarname}+1
|
||||
lda $varName+2
|
||||
sta ${target.asmVarname}+2
|
||||
lda $varName+3
|
||||
sta ${target.asmVarname}+3""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong dt ${target.position}")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
require(sourceDt.isLong)
|
||||
val deref = target.array!!.pointerderef
|
||||
if(deref!=null) {
|
||||
pointergen.assignLongVar(IndexedPtrTarget(target), varName)
|
||||
return
|
||||
}
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.out("""
|
||||
lda $varName
|
||||
sta ${target.asmVarname},y
|
||||
lda $varName+1
|
||||
sta ${target.asmVarname}+1,y
|
||||
lda $varName+2
|
||||
sta ${target.asmVarname}+2,y
|
||||
lda $varName+3
|
||||
sta ${target.asmVarname}+3,y""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.REGISTER -> {
|
||||
require(target.register in combinedLongRegisters)
|
||||
val regstart = target.register.toString().take(2).lowercase()
|
||||
when(sourceDt) {
|
||||
DataType.BYTE -> {
|
||||
asmgen.out(" lda $varName | sta cx16.$regstart")
|
||||
asmgen.signExtendLongVariable("cx16.$regstart", sourceDt.base)
|
||||
}
|
||||
DataType.UBYTE -> {
|
||||
asmgen.out("""
|
||||
lda $varName
|
||||
sta cx16.$regstart
|
||||
lda #0
|
||||
sta cx16.$regstart+1
|
||||
sta cx16.$regstart+2
|
||||
sta cx16.$regstart+3""")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
asmgen.out("""
|
||||
lda $varName
|
||||
sta cx16.$regstart
|
||||
lda $varName+1
|
||||
sta cx16.$regstart+1""")
|
||||
asmgen.signExtendLongVariable("cx16.$regstart", sourceDt.base)
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
asmgen.out("""
|
||||
lda $varName
|
||||
sta cx16.$regstart
|
||||
lda $varName+1
|
||||
sta cx16.$regstart+1
|
||||
lda #0
|
||||
sta cx16.$regstart+2
|
||||
sta cx16.$regstart+3""")
|
||||
}
|
||||
DataType.LONG -> {
|
||||
asmgen.out("""
|
||||
lda $varName
|
||||
sta cx16.$regstart
|
||||
lda $varName+1
|
||||
sta cx16.$regstart+1
|
||||
lda $varName+2
|
||||
sta cx16.$regstart+2
|
||||
lda $varName+3
|
||||
sta cx16.$regstart+3""")
|
||||
}
|
||||
else -> throw AssemblyError("wrong dt ${target.position}")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignVariableWord(target: AsmAssignTarget, varName: String, sourceDt: DataType) {
|
||||
if(sourceDt.isSignedByte) {
|
||||
// need to sign extend
|
||||
@@ -3454,6 +3786,49 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignRegisterLong(target: AsmAssignTarget, pairedRegisters: RegisterOrPair) {
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
if(pairedRegisters in combinedLongRegisters) {
|
||||
val startreg = pairedRegisters.startregname()
|
||||
asmgen.out("""
|
||||
lda cx16.$startreg
|
||||
sta ${target.asmVarname}
|
||||
lda cx16.$startreg+1
|
||||
sta ${target.asmVarname}+1
|
||||
lda cx16.$startreg+2
|
||||
sta ${target.asmVarname}+2
|
||||
lda cx16.$startreg+3
|
||||
sta ${target.asmVarname}+3""")
|
||||
}
|
||||
else throw AssemblyError("only combined vreg allowed as long target ${target.position}")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
TODO("assign 32 bits int in R0:R1 into array ${target.position}")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.REGISTER -> {
|
||||
val targetreg = target.register!!
|
||||
require(targetreg in combinedLongRegisters && pairedRegisters in combinedLongRegisters)
|
||||
if(targetreg!=pairedRegisters) {
|
||||
val sourceStartReg = pairedRegisters.startregname()
|
||||
val targetStartReg = targetreg.startregname()
|
||||
asmgen.out("""
|
||||
lda cx16.$sourceStartReg
|
||||
sta cx16.$targetStartReg
|
||||
lda cx16.$sourceStartReg+1
|
||||
sta cx16.$targetStartReg+1
|
||||
lda cx16.$sourceStartReg+2
|
||||
sta cx16.$targetStartReg+2
|
||||
lda cx16.$sourceStartReg+3
|
||||
sta cx16.$targetStartReg+3""")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister, signed: Boolean, extendWord: Boolean) {
|
||||
val assignAsWord = target.datatype.isWord
|
||||
|
||||
@@ -3892,7 +4267,7 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
|
||||
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register ${target.position}")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("can't store word into memory byte")
|
||||
@@ -3901,6 +4276,92 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignConstantLong(target: AsmAssignTarget, long: Int) {
|
||||
if(long==0 && asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
// optimize setting zero value for this processor
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out("""
|
||||
stz ${target.asmVarname}
|
||||
stz ${target.asmVarname}+1
|
||||
stz ${target.asmVarname}+2
|
||||
stz ${target.asmVarname}+3""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val deref = target.array!!.pointerderef
|
||||
if(deref!=null) {
|
||||
pointergen.assignLong(IndexedPtrTarget(target), 0)
|
||||
return
|
||||
}
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
sta ${target.asmVarname},y
|
||||
sta ${target.asmVarname}+1,y
|
||||
sta ${target.asmVarname}+2,y
|
||||
sta ${target.asmVarname}+3,y""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}")
|
||||
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
fun store(hexbyte: String, offset: Int) {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02) && hexbyte=="00") {
|
||||
asmgen.out(" stz ${target.asmVarname}+$offset")
|
||||
} else {
|
||||
asmgen.out(" lda #$$hexbyte | sta ${target.asmVarname}+$offset")
|
||||
}
|
||||
}
|
||||
val hex = long.toUInt().toString(16).padStart(8, '0')
|
||||
store(hex.substring(6,8), 0)
|
||||
store(hex.substring(4,6), 1)
|
||||
store(hex.substring(2,4), 2)
|
||||
store(hex.substring(0,2), 3)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val deref = target.array!!.pointerderef
|
||||
if(deref!=null) {
|
||||
pointergen.assignLong(IndexedPtrTarget(target), long)
|
||||
return
|
||||
}
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
val hex = long.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
lda #$${hex.substring(6,8)}
|
||||
sta ${target.asmVarname},y
|
||||
lda #$${hex.substring(4, 6)}
|
||||
sta ${target.asmVarname}+1,y
|
||||
lda #$${hex.substring(2, 4)}
|
||||
sta ${target.asmVarname}+2,y
|
||||
lda #$${hex.take(2)}
|
||||
sta ${target.asmVarname}+3,y""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.REGISTER -> {
|
||||
require(target.register in combinedLongRegisters)
|
||||
val regstart = target.register!!.startregname()
|
||||
val hex = long.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
lda #$${hex.substring(6,8)}
|
||||
sta cx16.$regstart
|
||||
lda #$${hex.substring(4,6)}
|
||||
sta cx16.$regstart+1
|
||||
lda #$${hex.substring(2,4)}
|
||||
sta cx16.$regstart+2
|
||||
lda #$${hex.take(2)}
|
||||
sta cx16.$regstart+3""")
|
||||
}
|
||||
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignConstantWord(target: AsmAssignTarget, word: Int) {
|
||||
if(word==0 && asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
// optimize setting zero value for this processor
|
||||
@@ -3914,7 +4375,7 @@ $endLabel""")
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val deref = target.array!!.pointerderef
|
||||
if(deref!=null) {
|
||||
pointergen.assignWord(IndexedPtrTarget(target), word)
|
||||
pointergen.assignWord(IndexedPtrTarget(target), 0)
|
||||
return
|
||||
}
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
|
||||
@@ -4490,6 +4951,26 @@ $endLabel""")
|
||||
else -> throw AssemblyError("weird target")
|
||||
}
|
||||
}
|
||||
targetDt.isLong -> {
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out("""
|
||||
lda ${target.asmVarname}
|
||||
eor #255
|
||||
sta ${target.asmVarname}
|
||||
lda ${target.asmVarname}+1
|
||||
eor #255
|
||||
sta ${target.asmVarname}+1
|
||||
lda ${target.asmVarname}+2
|
||||
eor #255
|
||||
sta ${target.asmVarname}+2
|
||||
lda ${target.asmVarname}+3
|
||||
eor #255
|
||||
sta ${target.asmVarname}+3""")
|
||||
}
|
||||
else -> TODO("LONG INVERT ${target.kind} ${target.position}")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invert of invalid type")
|
||||
}
|
||||
}
|
||||
@@ -4599,6 +5080,31 @@ $endLabel""")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
datatype.isLong -> {
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
sec
|
||||
sbc ${target.asmVarname}
|
||||
sta ${target.asmVarname}
|
||||
lda #0
|
||||
sbc ${target.asmVarname}+1
|
||||
sta ${target.asmVarname}+1
|
||||
lda #0
|
||||
sbc ${target.asmVarname}+2
|
||||
sta ${target.asmVarname}+2
|
||||
lda #0
|
||||
sbc ${target.asmVarname}+3
|
||||
sta ${target.asmVarname}+3""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> TODO(" - long array ${target.position}")
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.REGISTER -> TODO("32 bits register assign? (we have no 32 bits registers right now) ${target.position}")
|
||||
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
datatype.isFloat -> {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.REGISTER -> {
|
||||
|
||||
+484
-8
@@ -106,6 +106,17 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
}
|
||||
target.datatype.isLong -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationLongWithLiteralval(target.asmVarname, operator, value.boolean!!.asInt())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationLongWithLiteralval(target.asmVarname, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationLongWithVariable(target.asmVarname, operator, value.asmVarname)
|
||||
SourceStorageKind.EXPRESSION -> inplacemodificationLongWithExpression(target.asmVarname, operator, value.expression!!)
|
||||
SourceStorageKind.REGISTER -> TODO("32 bits register inplace modification? ${target.position}")
|
||||
SourceStorageKind.ARRAY -> TODO("inplace modify long with array ${target.position}")
|
||||
SourceStorageKind.MEMORY -> TODO("memread into long ${target.position}")
|
||||
}
|
||||
}
|
||||
target.datatype.isFloat -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationFloatWithLiteralval(target.asmVarname, operator, value.boolean!!.asInt().toDouble())
|
||||
@@ -291,6 +302,25 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
target.datatype.isLong -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationLongWithLiteralval(targetVarName, operator, value.boolean!!.asInt())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationLongWithLiteralval(targetVarName, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationLongWithVariable(targetVarName, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationLongWithVariable(targetVarName, operator, regName(value))
|
||||
SourceStorageKind.MEMORY -> TODO("inplace long modifiication ${target.position}")
|
||||
SourceStorageKind.ARRAY -> TODO("inplace long modifiication ${target.position}")
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
|
||||
TODO("inplace long modifiication ${target.position}")
|
||||
} else {
|
||||
TODO("inplace long modifiication ${target.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target.datatype.isFloat -> {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationFloatWithLiteralval(targetVarName, operator, value.boolean!!.asInt().toDouble())
|
||||
@@ -523,6 +553,448 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
internal fun inplacemodificationLongWithExpression(targetVar: String, operator: String, value: PtExpression) {
|
||||
assignmentAsmGen.assignExpressionToRegister(value, RegisterOrPair.R0R1_32, value.type.isSigned)
|
||||
inplacemodificationLongWithVariable(targetVar, operator, "cx16.r0")
|
||||
}
|
||||
|
||||
internal fun inplacemodificationLongWithVariable(targetVar: String, operator: String, sourceVar: String) {
|
||||
when(operator) {
|
||||
"+" -> {
|
||||
asmgen.out("""
|
||||
clc
|
||||
lda $targetVar
|
||||
adc $sourceVar
|
||||
sta $targetVar
|
||||
lda $targetVar+1
|
||||
adc $sourceVar+1
|
||||
sta $targetVar+1
|
||||
lda $targetVar+2
|
||||
adc $sourceVar+2
|
||||
sta $targetVar+2
|
||||
lda $targetVar+3
|
||||
adc $sourceVar+3
|
||||
sta $targetVar+3""")
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.out("""
|
||||
sec
|
||||
lda $targetVar
|
||||
sbc $sourceVar
|
||||
sta $targetVar
|
||||
lda $targetVar+1
|
||||
sbc $sourceVar+1
|
||||
sta $targetVar+1
|
||||
lda $targetVar+2
|
||||
sbc $sourceVar+2
|
||||
sta $targetVar+2
|
||||
lda $targetVar+3
|
||||
sbc $sourceVar+3
|
||||
sta $targetVar+3""")
|
||||
}
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
ldy $sourceVar
|
||||
- asl $targetVar
|
||||
rol $targetVar+1
|
||||
rol $targetVar+2
|
||||
rol $targetVar+3
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
">>" -> {
|
||||
asmgen.out("""
|
||||
ldy $sourceVar
|
||||
- lda $targetVar+3
|
||||
asl a ; save sign bit
|
||||
ror $targetVar+3
|
||||
ror $targetVar+2
|
||||
ror $targetVar+1
|
||||
ror $targetVar
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
"|" -> {
|
||||
asmgen.out("""
|
||||
lda $targetVar
|
||||
ora $sourceVar
|
||||
sta $targetVar
|
||||
lda $targetVar+1
|
||||
ora $sourceVar+1
|
||||
sta $targetVar+1
|
||||
lda $targetVar+2
|
||||
ora $sourceVar+2
|
||||
sta $targetVar+2
|
||||
lda $targetVar+3
|
||||
ora $sourceVar+3
|
||||
sta $targetVar+3""")
|
||||
}
|
||||
"&" -> {
|
||||
asmgen.out("""
|
||||
lda $targetVar
|
||||
and $sourceVar
|
||||
sta $targetVar
|
||||
lda $targetVar+1
|
||||
and $sourceVar+1
|
||||
sta $targetVar+1
|
||||
lda $targetVar+2
|
||||
and $sourceVar+2
|
||||
sta $targetVar+2
|
||||
lda $targetVar+3
|
||||
and $sourceVar+3
|
||||
sta $targetVar+3""")
|
||||
}
|
||||
"^" -> {
|
||||
asmgen.out("""
|
||||
lda $targetVar
|
||||
eor $sourceVar
|
||||
sta $targetVar
|
||||
lda $targetVar+1
|
||||
eor $sourceVar+1
|
||||
sta $targetVar+1
|
||||
lda $targetVar+2
|
||||
eor $sourceVar+2
|
||||
sta $targetVar+2
|
||||
lda $targetVar+3
|
||||
eor $sourceVar+3
|
||||
sta $targetVar+3""")
|
||||
}
|
||||
else -> {
|
||||
TODO("in-place modify LONG with variable")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun inplacemodificationLongWithLiteralval(variable: String, operator: String, value: Int) {
|
||||
|
||||
fun inplaceLongShiftLeft() {
|
||||
when {
|
||||
value in 0..2 -> {
|
||||
repeat(value) {
|
||||
asmgen.out("""
|
||||
asl $variable
|
||||
rol $variable+1
|
||||
rol $variable+2
|
||||
rol $variable+3""")
|
||||
}
|
||||
}
|
||||
value in 3..7 -> {
|
||||
asmgen.out("""
|
||||
ldy #$value
|
||||
- asl $variable
|
||||
rol $variable+1
|
||||
rol $variable+2
|
||||
rol $variable+3
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
value == 8 -> {
|
||||
asmgen.out("""
|
||||
lda $variable+2
|
||||
sta $variable+3
|
||||
lda $variable+1
|
||||
sta $variable+2
|
||||
lda $variable
|
||||
sta $variable+1
|
||||
lda #0
|
||||
sta $variable""")
|
||||
}
|
||||
value in 9..15 -> {
|
||||
val shift = value - 8
|
||||
asmgen.out("""
|
||||
lda $variable+2
|
||||
sta $variable+3
|
||||
lda $variable+1
|
||||
sta $variable+2
|
||||
lda $variable
|
||||
sta $variable+1
|
||||
lda #0
|
||||
sta $variable
|
||||
ldy #$shift
|
||||
- asl $variable+1
|
||||
rol $variable+2
|
||||
rol $variable+3
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
value == 16 -> {
|
||||
asmgen.out("""
|
||||
lda $variable+1
|
||||
sta $variable+3
|
||||
lda $variable
|
||||
sta $variable+2
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable+1""")
|
||||
}
|
||||
value in 17..23 -> {
|
||||
val shift = value-16
|
||||
asmgen.out("""
|
||||
lda $variable+1
|
||||
sta $variable+3
|
||||
lda $variable
|
||||
sta $variable+2
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable+1
|
||||
ldy #$shift
|
||||
- asl $variable+2
|
||||
rol $variable+3
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
value == 24 -> {
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
sta $variable+3
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable+1
|
||||
sta $variable+2""")
|
||||
}
|
||||
value <= 31 -> {
|
||||
val shift = value-24
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
ldy #$shift
|
||||
- asl a
|
||||
dey
|
||||
bne -
|
||||
sta $variable+3
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable+1
|
||||
sta $variable+2""")
|
||||
}
|
||||
else -> {
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable+1
|
||||
sta $variable+2
|
||||
sta $variable+3""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun inplaceLongShiftRight() {
|
||||
when {
|
||||
value in 0..2 -> {
|
||||
repeat(value) {
|
||||
asmgen.out("""
|
||||
lda $variable+3
|
||||
asl a ; save sign bit
|
||||
ror $variable+3
|
||||
ror $variable+2
|
||||
ror $variable+1
|
||||
ror $variable""")
|
||||
}
|
||||
}
|
||||
// TODO optimize for more cases 8, 16, 24 etc but don't forget to take the sign bit into account!
|
||||
value <= 31 -> {
|
||||
asmgen.out("""
|
||||
ldy #$value
|
||||
- lda $variable+3
|
||||
asl a ; save sign bit
|
||||
ror $variable+3
|
||||
ror $variable+2
|
||||
ror $variable+1
|
||||
ror $variable
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
else -> {
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable+1
|
||||
sta $variable+2
|
||||
sta $variable+3""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when(operator) {
|
||||
"+" -> {
|
||||
when(value) {
|
||||
0 -> {}
|
||||
1 -> {
|
||||
asmgen.out("""
|
||||
inc $variable
|
||||
bne +
|
||||
inc $variable+1
|
||||
bne +
|
||||
inc $variable+2
|
||||
bne +
|
||||
inc $variable+3
|
||||
+""")
|
||||
}
|
||||
else -> {
|
||||
if(value in 1..255) {
|
||||
asmgen.out("""
|
||||
clc
|
||||
lda $variable
|
||||
adc #$value
|
||||
sta $variable
|
||||
bcc +
|
||||
inc $variable+1
|
||||
bne +
|
||||
inc $variable+2
|
||||
bne +
|
||||
inc $variable+3
|
||||
+""")
|
||||
} else if(value in 1..65535) {
|
||||
asmgen.out("""
|
||||
clc
|
||||
lda $variable
|
||||
adc #<$value
|
||||
sta $variable
|
||||
lda $variable+1
|
||||
adc #>$value
|
||||
sta $variable+1
|
||||
bcc +
|
||||
inc $variable+2
|
||||
bne +
|
||||
inc $variable+3
|
||||
+""")
|
||||
} else {
|
||||
val hex = value.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
clc
|
||||
lda $variable
|
||||
adc #$${hex.substring(6,8)}
|
||||
sta $variable
|
||||
lda $variable+1
|
||||
adc #$${hex.substring(4, 6)}
|
||||
sta $variable+1
|
||||
lda $variable+2
|
||||
adc #$${hex.substring(2, 4)}
|
||||
sta $variable+2
|
||||
lda $variable+3
|
||||
adc #$${hex.take(2)}
|
||||
sta $variable+3""")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
when(value) {
|
||||
0 -> {}
|
||||
1 -> {
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
bne +
|
||||
dec $variable+1
|
||||
bne +
|
||||
dec $variable+2
|
||||
bne +
|
||||
dec $variable+3
|
||||
+ dec $variable""")
|
||||
}
|
||||
else -> {
|
||||
if(value in 1..255) {
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
sec
|
||||
sbc #$value
|
||||
sta $variable
|
||||
bcs +
|
||||
dec $variable+1
|
||||
bne +
|
||||
dec $variable+2
|
||||
bne +
|
||||
dec $variable+3
|
||||
+""")
|
||||
} else if(value in 1..65535) {
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
sec
|
||||
sbc #<$value
|
||||
sta $variable
|
||||
lda $variable+1
|
||||
sbc #>$value
|
||||
sta $variable+1
|
||||
bcs +
|
||||
dec $variable+2
|
||||
bne +
|
||||
dec $variable+3
|
||||
+""")
|
||||
} else {
|
||||
val hex = value.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
sec
|
||||
lda $variable
|
||||
sbc #$${hex.substring(6,8)}
|
||||
sta $variable
|
||||
lda $variable+1
|
||||
sbc #$${hex.substring(4, 6)}
|
||||
sta $variable+1
|
||||
lda $variable+2
|
||||
sbc #$${hex.substring(2, 4)}
|
||||
sta $variable+2
|
||||
lda $variable+3
|
||||
sbc #$${hex.take(2)}
|
||||
sta $variable+3""")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"<<" -> if (value > 0) inplaceLongShiftLeft()
|
||||
">>" -> if (value > 0) inplaceLongShiftRight()
|
||||
"|" -> {
|
||||
val hex = value.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
ora #$${hex.substring(6,8)}
|
||||
sta $variable
|
||||
lda $variable+1
|
||||
ora #$${hex.substring(4, 6)}
|
||||
sta $variable+1
|
||||
lda $variable+2
|
||||
ora #$${hex.substring(2, 4)}
|
||||
sta $variable+2
|
||||
lda $variable+3
|
||||
ora #$${hex.take(2)}
|
||||
sta $variable+3""")
|
||||
}
|
||||
"&" -> {
|
||||
val hex = value.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
and #$${hex.substring(6,8)}
|
||||
sta $variable
|
||||
lda $variable+1
|
||||
and #$${hex.substring(4, 6)}
|
||||
sta $variable+1
|
||||
lda $variable+2
|
||||
and #$${hex.substring(2, 4)}
|
||||
sta $variable+2
|
||||
lda $variable+3
|
||||
and #$${hex.take(2)}
|
||||
sta $variable+3""")
|
||||
}
|
||||
"^" -> {
|
||||
val hex = value.toUInt().toString(16).padStart(8, '0')
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
eor #$${hex.substring(6,8)}
|
||||
sta $variable
|
||||
lda $variable+1
|
||||
eor #$${hex.substring(4, 6)}
|
||||
sta $variable+1
|
||||
lda $variable+2
|
||||
eor #$${hex.substring(2, 4)}
|
||||
sta $variable+2
|
||||
lda $variable+3
|
||||
eor #$${hex.take(2)}
|
||||
sta $variable+3""")
|
||||
}
|
||||
else -> {
|
||||
TODO("inplace long $operator $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryIndexedIncDec(array: PtArrayIndexer, operator: String): Boolean {
|
||||
val arrayVar = array.variable
|
||||
if(arrayVar==null) {
|
||||
@@ -1851,7 +2323,7 @@ $shortcutLabel:""")
|
||||
if(value in asmgen.optimizedWordMultiplications) {
|
||||
asmgen.out(" lda $lsb | ldy $msb | jsr prog8_math.mul_word_$value | sta $lsb | sty $msb")
|
||||
} else {
|
||||
if(block?.options?.veraFxMuls==true)
|
||||
if(block?.options?.veraFxMuls==true) {
|
||||
// cx16 verafx hardware mul
|
||||
asmgen.out("""
|
||||
lda $lsb
|
||||
@@ -1862,9 +2334,10 @@ $shortcutLabel:""")
|
||||
ldy #>$value
|
||||
sta cx16.r1
|
||||
sty cx16.r1+1
|
||||
jsr verafx.muls
|
||||
jsr verafx.muls16
|
||||
sta $lsb
|
||||
sty $msb""")
|
||||
}
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $lsb
|
||||
@@ -2349,9 +2822,10 @@ $shortcutLabel:""")
|
||||
ldy $name+1
|
||||
sta cx16.r0
|
||||
sty cx16.r0+1
|
||||
jsr verafx.muls
|
||||
jsr verafx.muls16
|
||||
sta $name
|
||||
sty $name+1""")
|
||||
|
||||
} else {
|
||||
if(valueDt.isUnsignedByte) {
|
||||
asmgen.out(" lda $otherName | sta prog8_math.multiply_words.multiplier")
|
||||
@@ -2494,7 +2968,7 @@ $shortcutLabel:""")
|
||||
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
|
||||
"-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1")
|
||||
"*" -> {
|
||||
if(block?.options?.veraFxMuls==true)
|
||||
if(block?.options?.veraFxMuls==true) {
|
||||
// cx16 verafx hardware muls
|
||||
asmgen.out("""
|
||||
lda $name
|
||||
@@ -2505,9 +2979,10 @@ $shortcutLabel:""")
|
||||
ldy $otherName+1
|
||||
sta cx16.r1
|
||||
sty cx16.r1+1
|
||||
jsr verafx.muls
|
||||
jsr verafx.muls16
|
||||
sta $name
|
||||
sty $name+1""")
|
||||
}
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $otherName
|
||||
@@ -2698,7 +3173,7 @@ $shortcutLabel:""")
|
||||
private fun inplacemodificationWordWithValue(name: String, dt: DataType, operator: String, value: PtExpression, block: PtBlock?) {
|
||||
require(dt.isWord)
|
||||
fun multiplyVarByWordInAX() {
|
||||
if(block?.options?.veraFxMuls==true)
|
||||
if(block?.options?.veraFxMuls==true) {
|
||||
// cx16 verafx hardware muls
|
||||
asmgen.out("""
|
||||
sta cx16.r1
|
||||
@@ -2707,9 +3182,10 @@ $shortcutLabel:""")
|
||||
ldx $name+1
|
||||
sta cx16.r0
|
||||
stx cx16.r0+1
|
||||
jsr verafx.muls
|
||||
jsr verafx.muls16
|
||||
sta $name
|
||||
sty $name+1""")
|
||||
}
|
||||
else
|
||||
asmgen.out("""
|
||||
sta prog8_math.multiply_words.multiplier
|
||||
@@ -3003,7 +3479,7 @@ $shortcutLabel:""")
|
||||
if(value is PtNumber && value.number<=255) {
|
||||
TODO("shift a word var by ${value.number}")
|
||||
} else {
|
||||
throw AssemblyError("shift by a word value not supported, max is a byte")
|
||||
throw AssemblyError("bit shift value can not be larger than a byte")
|
||||
}
|
||||
}
|
||||
"&" -> {
|
||||
|
||||
@@ -257,16 +257,26 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
internal fun inplaceModification(target: PtrTarget, operator: String, value: AsmAssignSource) {
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
if(target.dt.isByte) inplaceByteAdd(target, value)
|
||||
else if(target.dt.isWord) inplaceWordAdd(target, value)
|
||||
else if(target.dt.isFloat) inplaceFloatAddOrMul(target, "FADD", value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
if(target.dt.isByte && value.number?.number==1.0 || value.number?.number==2.0) {
|
||||
val amount = value.number.number.toInt()
|
||||
inplaceByteInc(target, amount)
|
||||
} else {
|
||||
if (target.dt.isByte) inplaceByteAdd(target, value)
|
||||
else if (target.dt.isWord) inplaceWordAdd(target, value)
|
||||
else if (target.dt.isFloat) inplaceFloatAddOrMul(target, "FADD", value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
if(target.dt.isByte) inplaceByteSub(target, value)
|
||||
else if(target.dt.isWord) inplaceWordSub(target, value)
|
||||
else if(target.dt.isFloat) inplaceFloatSubOrDiv(target, "FSUB", value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
if(target.dt.isByte && value.number?.number==1.0 || value.number?.number==2.0) {
|
||||
val amount = value.number.number.toInt()
|
||||
inplaceByteDec(target, amount)
|
||||
} else {
|
||||
if (target.dt.isByte) inplaceByteSub(target, value)
|
||||
else if (target.dt.isWord) inplaceWordSub(target, value)
|
||||
else if (target.dt.isFloat) inplaceFloatSubOrDiv(target, "FSUB", value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
}
|
||||
"*" -> {
|
||||
if(target.dt.isByte) TODO("inplaceByteMul(target, value) ${target.position}")
|
||||
@@ -282,22 +292,26 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
"%" -> TODO("inplace ptr %")
|
||||
"<<" -> {
|
||||
if(target.dt.isByte) TODO("inplaceByteShiftLeft(target, value) ${target.position}")
|
||||
if(target.dt.isByte) inplaceByteShiftLeft(target, value)
|
||||
else if(target.dt.isWord) inplaceWordShiftLeft(target, value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
">>" -> {
|
||||
if(target.dt.isByte) TODO("inplaceByteShiftRight(target, value) ${target.position}")
|
||||
if(target.dt.isByte) inplaceByteShiftRight(target, value)
|
||||
else if(target.dt.isWord) inplaceWordShiftRight(target, value)
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
"&", "and" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
|
||||
TODO("inplace ptr &")
|
||||
if(target.dt.isByteOrBool) inplaceByteAnd(target, value)
|
||||
else if(target.dt.isWord) inplaceWordAnd(target, value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
}
|
||||
"|", "or" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
|
||||
TODO("inplace ptr |")
|
||||
if(target.dt.isByteOrBool) inplaceByteOr(target, value)
|
||||
else if(target.dt.isWord) inplaceWordOr(target, value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
}
|
||||
"^", "xor" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
|
||||
@@ -305,12 +319,6 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
else if(target.dt.isWord) inplaceWordXor(target, value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
}
|
||||
"==" -> TODO("inplace ptr ==")
|
||||
"!=" -> TODO("inplace ptr !=")
|
||||
"<" -> TODO("inplace ptr <")
|
||||
"<=" -> TODO("inplace ptr <=")
|
||||
">" -> TODO("inplace ptr >")
|
||||
">=" -> TODO("inplace ptr >=")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
@@ -398,6 +406,10 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
TODO("array ptr assign const word ${target.position}")
|
||||
}
|
||||
|
||||
internal fun assignLong(target: IndexedPtrTarget, long: Int) {
|
||||
TODO("array ptr assign const long ${target.position}")
|
||||
}
|
||||
|
||||
internal fun assignFloat(target: IndexedPtrTarget, float: Double) {
|
||||
TODO("array ptr assign const float ${target.position}")
|
||||
}
|
||||
@@ -434,6 +446,10 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
TODO("array ptr assign word var ${target.position}")
|
||||
}
|
||||
|
||||
internal fun assignLongVar(target: IndexedPtrTarget, varName: String) {
|
||||
TODO("array ptr assign long var ${target.position}")
|
||||
}
|
||||
|
||||
internal fun operatorDereference(binExpr: PtBinaryExpression): Triple<String, UByte, DataType> {
|
||||
// the only case we support here is: a.b.c[i] . value
|
||||
// returns the ZP var to use as a pointer, and a Y register offset (which can be zero), and finally the datatype of the field
|
||||
@@ -491,10 +507,9 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
|
||||
private fun addUnsignedByteOrWordToAY(value: PtExpression, scale: Int?) {
|
||||
// TODO generate more optimal code from this, a bit too much stack and temporary variable juggling seems to be done in some easy cases...
|
||||
|
||||
fun complicatedFallback() {
|
||||
// slow fallback routine that can deal with any expression for value
|
||||
// slow fallback routine that can deal with any expression for value (const number and variable have been dealt with already)
|
||||
|
||||
fun restoreAYandAddAsmVariable(varname: String, isByte: Boolean) {
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false) // msb
|
||||
@@ -693,7 +708,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
|
||||
if(target.dt.isSigned)
|
||||
TODO("signed word shift rigth ${target.position} $value")
|
||||
TODO("signed word shift right ${target.position} $value")
|
||||
|
||||
fun shift1unsigned() {
|
||||
asmgen.out("""
|
||||
@@ -720,21 +735,92 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
require(value.datatype.isByte)
|
||||
val varname = value.asmVarname
|
||||
TODO("<< variable")
|
||||
asmgen.out(" ldx $varname")
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isWord)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
|
||||
TODO("<< expression")
|
||||
require(value.datatype.isByte)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isWord)
|
||||
require(value.datatype.isByte)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, null, target.position, variableAsmName = "P8ZP_SCRATCH_PTR"))
|
||||
require(register.isWord())
|
||||
TODO("<< register")
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteShiftRight(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
|
||||
if(target.dt.isSigned)
|
||||
TODO("signed byte shift right ${target.position} $value")
|
||||
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(number==1) {
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
lsr a
|
||||
sta ($zpPtrVar),y""")
|
||||
} else if(number>1) {
|
||||
asmgen.out("""
|
||||
ldx #$number
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- lsr a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isByte)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out("""
|
||||
ldx $varname
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- lsr a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isByte)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- lsr a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isByte)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UWORD, null, target.position, register = RegisterOrPair.X))
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- lsr a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
@@ -768,21 +854,90 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
require(value.datatype.isByte)
|
||||
val varname = value.asmVarname
|
||||
TODO("<< variable")
|
||||
asmgen.out(" ldx $varname")
|
||||
asmgen.out("-")
|
||||
shift1()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isWord)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
|
||||
TODO("<< expression")
|
||||
require(value.datatype.isByte)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
|
||||
asmgen.out("-")
|
||||
shift1()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isWord)
|
||||
require(value.datatype.isByte)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, null, target.position, variableAsmName = "P8ZP_SCRATCH_PTR"))
|
||||
require(register.isWord())
|
||||
TODO("<< register")
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
|
||||
asmgen.out("-")
|
||||
shift1()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteShiftLeft(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(number==1) {
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
asl a
|
||||
sta ($zpPtrVar),y""")
|
||||
} else if(number>1) {
|
||||
asmgen.out("""
|
||||
ldx #$number
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- asl a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isByte)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out("""
|
||||
ldx $varname
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- asl a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isByte)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- asl a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isByte)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
|
||||
asmgen.out("-")
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
- asl a
|
||||
dex
|
||||
bne -
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
@@ -1046,6 +1201,46 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteInc(target: PtrTarget, amount: Int) {
|
||||
require(amount==1 || amount==2)
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
asmgen.out(" lda ($zpPtrVar)")
|
||||
repeat(amount) {
|
||||
asmgen.out(" inc a")
|
||||
}
|
||||
asmgen.out(" sta ($zpPtrVar)")
|
||||
}
|
||||
else {
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
clc
|
||||
adc #$amount
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteDec(target: PtrTarget, amount: Int) {
|
||||
require(amount==1 || amount==2)
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02)) {
|
||||
asmgen.out(" lda ($zpPtrVar)")
|
||||
repeat(amount) {
|
||||
asmgen.out(" dec a")
|
||||
}
|
||||
asmgen.out(" sta ($zpPtrVar)")
|
||||
}
|
||||
else {
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
sec
|
||||
sbc #$amount
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteAdd(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
@@ -1230,48 +1425,222 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
}
|
||||
|
||||
fun assignIndexedPointer(target: AsmAssignTarget, arrayVarName: String, index: PtExpression, arrayDt: DataType) {
|
||||
TODO("assign indexed pointer from array $arrayVarName at ${target.position}")
|
||||
val ptrZp = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, target.scope, target.position, variableAsmName="P8ZP_SCRATCH_PTR")
|
||||
assignAddressOfIndexedPointer(ptrZp, arrayVarName, arrayDt, index)
|
||||
when {
|
||||
target.datatype.isByteOrBool -> {
|
||||
asmgen.out("""
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_PTR),y""")
|
||||
asmgen.assignRegister(RegisterOrPair.A, target)
|
||||
}
|
||||
target.datatype.isWord || target.datatype.isPointer -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
private fun inplaceByteOr(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out("""
|
||||
ldy #1
|
||||
lda (P8ZP_SCRATCH_PTR),y
|
||||
tax
|
||||
lda (P8ZP_SCRATCH_PTR)""")
|
||||
lda ($zpPtrVar)
|
||||
ora #$number
|
||||
sta ($zpPtrVar)""")
|
||||
else
|
||||
asmgen.out("""
|
||||
ldy #1
|
||||
lda (P8ZP_SCRATCH_PTR),y
|
||||
tax
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_PTR),y""")
|
||||
asmgen.assignRegister(RegisterOrPair.AX, target)
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
ora #$number
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
target.datatype.isLong -> {
|
||||
TODO("assign long from pointer to $target ${target.position}")
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
val varname = value.asmVarname
|
||||
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out("""
|
||||
lda ($zpPtrVar)
|
||||
ora $varname
|
||||
sta ($zpPtrVar)""")
|
||||
else
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
ora $varname
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
target.datatype.isFloat -> {
|
||||
// TODO optimize the float copying to avoid having to go through FAC1
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
lda P8ZP_SCRATCH_PTR
|
||||
ldy P8ZP_SCRATCH_PTR+1
|
||||
jsr floats.MOVFM""")
|
||||
asmgen.assignRegister(RegisterOrPair.FAC1, target)
|
||||
ldy #$offset
|
||||
ora ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
else -> throw AssemblyError("weird dt ${target.datatype}")
|
||||
SourceStorageKind.REGISTER -> TODO("register | byte")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordOr(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
ora #<$number
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
ora #>$number
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
ora $varname
|
||||
sta ($zpPtrVar),y
|
||||
lda ($zpPtrVar),y
|
||||
ora $varname+1
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isWord)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
ora ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
txa
|
||||
ora ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> TODO("register | word")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteAnd(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out("""
|
||||
lda ($zpPtrVar)
|
||||
and #$number
|
||||
sta ($zpPtrVar)""")
|
||||
else
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
and #$number
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
val varname = value.asmVarname
|
||||
if(offset==0.toUByte() && asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out("""
|
||||
lda ($zpPtrVar)
|
||||
and $varname
|
||||
sta ($zpPtrVar)""")
|
||||
else
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
and $varname
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
and ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> TODO("register & byte")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordAnd(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
and #<$number
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
and #>$number
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
and $varname
|
||||
sta ($zpPtrVar),y
|
||||
lda ($zpPtrVar),y
|
||||
and $varname+1
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isWord)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AX)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
and ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
txa
|
||||
and ($zpPtrVar),y
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> TODO("register & word")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
fun assignIndexedPointer(target: AsmAssignTarget, arrayVarName: String, index: PtExpression, arrayDt: DataType) {
|
||||
TODO("assign indexed pointer from array $arrayVarName at ${target.position}")
|
||||
// val ptrZp = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, target.scope, target.position, variableAsmName="P8ZP_SCRATCH_PTR")
|
||||
// assignAddressOfIndexedPointer(ptrZp, arrayVarName, arrayDt, index)
|
||||
// when {
|
||||
// target.datatype.isByteOrBool -> {
|
||||
// asmgen.out("""
|
||||
// ldy #0
|
||||
// lda (P8ZP_SCRATCH_PTR),y""")
|
||||
// asmgen.assignRegister(RegisterOrPair.A, target)
|
||||
// }
|
||||
// target.datatype.isWord || target.datatype.isPointer -> {
|
||||
// if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
// asmgen.out("""
|
||||
// ldy #1
|
||||
// lda (P8ZP_SCRATCH_PTR),y
|
||||
// tax
|
||||
// lda (P8ZP_SCRATCH_PTR)""")
|
||||
// else
|
||||
// asmgen.out("""
|
||||
// ldy #1
|
||||
// lda (P8ZP_SCRATCH_PTR),y
|
||||
// tax
|
||||
// dey
|
||||
// lda (P8ZP_SCRATCH_PTR),y""")
|
||||
// asmgen.assignRegister(RegisterOrPair.AX, target)
|
||||
// }
|
||||
// target.datatype.isLong -> {
|
||||
// TODO("assign long from pointer to $target ${target.position}")
|
||||
// }
|
||||
// target.datatype.isFloat -> {
|
||||
// // TODO optimize the float copying to avoid having to go through FAC1
|
||||
// asmgen.out("""
|
||||
// lda P8ZP_SCRATCH_PTR
|
||||
// ldy P8ZP_SCRATCH_PTR+1
|
||||
// jsr floats.MOVFM""")
|
||||
// asmgen.assignRegister(RegisterOrPair.FAC1, target)
|
||||
// }
|
||||
// else -> throw AssemblyError("weird dt ${target.datatype}")
|
||||
// }
|
||||
}
|
||||
|
||||
private fun saveOnStack(regs: RegisterOrPair) {
|
||||
when(regs) {
|
||||
RegisterOrPair.AX -> {
|
||||
|
||||
@@ -12,12 +12,14 @@ internal object DummyMemsizer : IMemSizer {
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
|
||||
BaseDataType.LONG -> numElements*4
|
||||
BaseDataType.FLOAT -> numElements*5
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isLong -> 4 * (numElements ?: 1)
|
||||
dt.isFloat -> 5 * (numElements ?: 1)
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ class TestCodegen: FunSpec({
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
compilerVersion="99.99",
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
|
||||
@@ -76,6 +76,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
RegisterOrPair.AY -> addInstr(result, IRInstruction(Opcode.LOADHAY, IRDataType.WORD, reg1=regNum), null)
|
||||
RegisterOrPair.XY -> addInstr(result, IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum), null)
|
||||
in Cx16VirtualRegisters -> addInstr(result, IRInstruction(Opcode.LOADM, irType(returns.type), reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}"), null)
|
||||
in combinedLongRegisters -> {
|
||||
require(returns.type.isLong)
|
||||
val startreg = returns.register.registerOrPair!!.startregname()
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.LONG, reg1=regNum, labelSymbol = "cx16.${startreg}"), null)
|
||||
}
|
||||
RegisterOrPair.FAC1 -> addInstr(result, IRInstruction(Opcode.LOADHFACZERO, IRDataType.FLOAT, fpReg1 = regNum), null)
|
||||
RegisterOrPair.FAC2 -> addInstr(result, IRInstruction(Opcode.LOADHFACONE, IRDataType.FLOAT, fpReg1 = regNum), null)
|
||||
null -> if(returns.register.statusflag!=null)
|
||||
@@ -137,63 +142,75 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val inplaceInstrs = mutableListOf<IRCodeChunkBase>()
|
||||
val (addressReg, fieldOffset) = codeGen.evaluatePointerAddressIntoReg(inplaceInstrs, pointerDeref)
|
||||
val oldvalueReg = codeGen.registers.next(targetDt)
|
||||
var operandTr = ExpressionCodeResult(emptyList(), IRDataType.BYTE, -1, -1)
|
||||
if(augAssign.operator!="or=" && augAssign.operator!="and=") {
|
||||
// for everything except the shortcircuit boolean operators, we can evaluate the value here unconditionally
|
||||
operandTr = expressionEval.translateExpression(value)
|
||||
inplaceInstrs += operandTr.chunks
|
||||
}
|
||||
if(targetDt== IRDataType.FLOAT) {
|
||||
if(fieldOffset>0u)
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADFIELD, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg, immediate = fieldOffset.toInt()), null)
|
||||
else
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg), null)
|
||||
when(augAssign.operator) {
|
||||
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVSR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"+" -> { /* inplace + is a no-op */ }
|
||||
else -> throw AssemblyError("invalid augmented assign operator for floats ${augAssign.operator}")
|
||||
|
||||
if((augAssign.operator=="+=" || augAssign.operator=="-=") && value.asConstInteger()==1 || value.asConstInteger()==2) {
|
||||
// INC/DEC optimization instead of ADD/SUB
|
||||
|
||||
loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
|
||||
val instr = if(augAssign.operator=="+=") Opcode.INC else Opcode.DEC
|
||||
repeat(value.asConstInteger()!!) {
|
||||
addInstr(inplaceInstrs, IRInstruction(instr, targetDt, reg1 = oldvalueReg), null)
|
||||
}
|
||||
|
||||
} else {
|
||||
if(fieldOffset>0u)
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADFIELD, targetDt, reg1 = oldvalueReg, reg2 = addressReg, immediate = fieldOffset.toInt()), null)
|
||||
else
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.LOADI, targetDt, reg1 = oldvalueReg, reg2 = addressReg), null)
|
||||
when(augAssign.operator) {
|
||||
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"|=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"&=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"^=", "xor=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"<<=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSLN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
">>=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSRN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"or=" -> {
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)
|
||||
val valueTr = expressionEval.translateExpression(value)
|
||||
inplaceInstrs += valueTr.chunks
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
||||
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
|
||||
|
||||
var operandTr = ExpressionCodeResult(emptyList(), IRDataType.BYTE, -1, -1)
|
||||
if(augAssign.operator!="or=" && augAssign.operator!="and=") {
|
||||
// for everything except the shortcircuit boolean operators, we can evaluate the value here unconditionally
|
||||
operandTr = expressionEval.translateExpression(value)
|
||||
// note: the instructions to load the value will be placed after the LOADFIELD instruction so that later optimizations about what modification is actually done, are easier
|
||||
}
|
||||
if(targetDt== IRDataType.FLOAT) {
|
||||
|
||||
loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
|
||||
inplaceInstrs += operandTr.chunks
|
||||
when(augAssign.operator) {
|
||||
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVSR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, fpReg1 = oldvalueReg, fpReg2 = operandTr.resultFpReg), null)
|
||||
"+" -> { /* inplace + is a no-op */ }
|
||||
else -> throw AssemblyError("invalid augmented assign operator for floats ${augAssign.operator}")
|
||||
}
|
||||
"and=" -> {
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTEQ, labelSymbol = shortcutLabel), null)
|
||||
val valueTr = expressionEval.translateExpression(value)
|
||||
inplaceInstrs += valueTr.chunks
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
||||
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
|
||||
|
||||
} else {
|
||||
|
||||
loadfield(inplaceInstrs, addressReg, fieldOffset, targetDt, oldvalueReg)
|
||||
inplaceInstrs += operandTr.chunks
|
||||
when(augAssign.operator) {
|
||||
"+=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ADDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"-=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.SUBR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"*=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MULR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"/=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.DIVR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"%=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.MODR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"|=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"&=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"^=", "xor=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"<<=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSLN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
">>=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSRN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"or=" -> {
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)
|
||||
val valueTr = expressionEval.translateExpression(value)
|
||||
inplaceInstrs += valueTr.chunks
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.ORR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
||||
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
|
||||
}
|
||||
"and=" -> {
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTEQ, labelSymbol = shortcutLabel), null)
|
||||
val valueTr = expressionEval.translateExpression(value)
|
||||
inplaceInstrs += valueTr.chunks
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1=oldvalueReg, reg2=valueTr.resultReg), null)
|
||||
inplaceInstrs += IRCodeChunk(shortcutLabel, null)
|
||||
}
|
||||
"-" -> addInstr(inplaceInstrs, IRInstruction(Opcode.NEG, targetDt, reg1 = oldvalueReg), null)
|
||||
"~" -> addInstr(inplaceInstrs, IRInstruction(Opcode.INV, targetDt, reg1 = oldvalueReg), null)
|
||||
"not" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XOR, targetDt, reg1 = oldvalueReg, immediate = 1), null)
|
||||
"+" -> { /* inplace + is a no-op */ }
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
||||
}
|
||||
"-" -> addInstr(inplaceInstrs, IRInstruction(Opcode.NEG, targetDt, reg1 = oldvalueReg), null)
|
||||
"~" -> addInstr(inplaceInstrs, IRInstruction(Opcode.INV, targetDt, reg1 = oldvalueReg), null)
|
||||
"not" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XOR, targetDt, reg1 = oldvalueReg, immediate = 1), null)
|
||||
"+" -> { /* inplace + is a no-op */ }
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,6 +239,42 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return chunks
|
||||
}
|
||||
|
||||
private fun loadfield(
|
||||
inplaceInstrs: MutableList<IRCodeChunkBase>,
|
||||
addressReg: Int,
|
||||
fieldOffset: UByte,
|
||||
targetDt: IRDataType,
|
||||
oldvalueReg: Int
|
||||
) {
|
||||
if (targetDt == IRDataType.FLOAT) {
|
||||
if (fieldOffset > 0u)
|
||||
addInstr(
|
||||
inplaceInstrs,
|
||||
IRInstruction(Opcode.LOADFIELD, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg, immediate = fieldOffset.toInt()),
|
||||
null
|
||||
)
|
||||
else
|
||||
addInstr(
|
||||
inplaceInstrs,
|
||||
IRInstruction(Opcode.LOADI, targetDt, fpReg1 = oldvalueReg, reg1 = addressReg),
|
||||
null
|
||||
)
|
||||
} else {
|
||||
if (fieldOffset > 0u)
|
||||
addInstr(
|
||||
inplaceInstrs,
|
||||
IRInstruction(Opcode.LOADFIELD, targetDt, reg1 = oldvalueReg, reg2 = addressReg, immediate = fieldOffset.toInt()),
|
||||
null
|
||||
)
|
||||
else
|
||||
addInstr(
|
||||
inplaceInstrs,
|
||||
IRInstruction(Opcode.LOADI, targetDt, reg1 = oldvalueReg, reg2 = addressReg),
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
|
||||
val value: PtExpression
|
||||
if(origAssign.operator in PrefixOperators) {
|
||||
@@ -415,10 +468,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
|
||||
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
|
||||
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
|
||||
val targetIdent = assignment.target.identifier
|
||||
val targetMemory = assignment.target.memory
|
||||
val targetArray = assignment.target.array
|
||||
val targetPointerDeref = assignment.target.pointerDeref
|
||||
val valueDt = irType(assignment.value.type)
|
||||
val targetDt = irType(assignment.target.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
@@ -459,98 +508,104 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
}
|
||||
}
|
||||
|
||||
if(targetIdent!=null) {
|
||||
val instruction = if(zero) {
|
||||
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = targetIdent.name)
|
||||
} else {
|
||||
if (targetDt == IRDataType.FLOAT) {
|
||||
require(valueFpRegister>=0)
|
||||
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = targetIdent.name)
|
||||
}
|
||||
else {
|
||||
require(valueRegister>=0)
|
||||
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = targetIdent.name)
|
||||
}
|
||||
}
|
||||
result += IRCodeChunk(null, null).also { it += instruction }
|
||||
return result
|
||||
}
|
||||
else if(targetArray!=null) {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(targetArray.type, null)
|
||||
val variable = targetArray.variable
|
||||
if(variable==null)
|
||||
translateRegularAssignPointerIndexed(result, targetArray.pointerderef!!, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
|
||||
else if(variable.type.isPointer)
|
||||
assignToIndexedSimplePointer(result, variable, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
|
||||
else
|
||||
translateRegularAssignArrayIndexed(result, variable.name, eltSize, targetArray, zero, targetDt, valueRegister, valueFpRegister)
|
||||
return result
|
||||
}
|
||||
else if(targetMemory!=null) {
|
||||
require(targetDt == IRDataType.BYTE) { "must be byte type ${targetMemory.position}"}
|
||||
if(zero) {
|
||||
if(targetMemory.address is PtNumber) {
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, address = (targetMemory.address as PtNumber).number.toInt()) }
|
||||
result += chunk
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val constAddress = targetMemory.address as? PtNumber
|
||||
if(constAddress!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=constAddress.number.toInt()), null)
|
||||
return result
|
||||
}
|
||||
val ptrWithOffset = targetMemory.address as? PtBinaryExpression
|
||||
if(ptrWithOffset!=null) {
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
|
||||
if(constOffset in 0..255) {
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1=pointerReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(Opcode.STOREFIELD, IRDataType.BYTE, reg1=valueRegister, reg2=pointerReg, immediate = constOffset)
|
||||
}
|
||||
return result
|
||||
with(assignment.target) {
|
||||
when {
|
||||
identifier != null -> {
|
||||
val instruction = if(zero) {
|
||||
IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = identifier!!.name)
|
||||
} else {
|
||||
if (targetDt == IRDataType.FLOAT) {
|
||||
require(valueFpRegister>=0)
|
||||
IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = identifier!!.name)
|
||||
}
|
||||
else {
|
||||
require(valueRegister>=0)
|
||||
IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = identifier!!.name)
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// STOREIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
expressionEval.translateExpression(offsetTypecast.value)
|
||||
else
|
||||
expressionEval.translateExpression(ptrWithOffset.right)
|
||||
result += IRCodeChunk(null, null).also { it += instruction }
|
||||
return result
|
||||
}
|
||||
memory != null -> {
|
||||
require(targetDt == IRDataType.BYTE) { "must be byte type ${memory!!.position}"}
|
||||
if(zero) {
|
||||
if(memory!!.address is PtNumber) {
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, targetDt, address = (memory!!.address as PtNumber).number.toInt()) }
|
||||
result += chunk
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(memory!!.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val constAddress = memory!!.address as? PtNumber
|
||||
if(constAddress!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=constAddress.number.toInt()), null)
|
||||
return result
|
||||
}
|
||||
val ptrWithOffset = memory!!.address as? PtBinaryExpression
|
||||
if(ptrWithOffset!=null) {
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
|
||||
if(constOffset in 0..255) {
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1=pointerReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(Opcode.STOREFIELD, IRDataType.BYTE, reg1=valueRegister, reg2=pointerReg, immediate = constOffset)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// STOREIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
expressionEval.translateExpression(offsetTypecast.value)
|
||||
else
|
||||
expressionEval.translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
val tr = expressionEval.translateExpression(memory!!.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
addInstr(result, IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg), null)
|
||||
return result
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
array != null -> {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array!!.type, null)
|
||||
val variable = array!!.variable
|
||||
if(variable==null)
|
||||
translateRegularAssignPointerIndexed(result, array!!.pointerderef!!, eltSize, array!!, zero, targetDt, valueRegister, valueFpRegister)
|
||||
else if(variable.type.isPointer)
|
||||
assignToIndexedSimplePointer(result, variable, eltSize, array!!, zero, targetDt, valueRegister, valueFpRegister)
|
||||
else
|
||||
translateRegularAssignArrayIndexed(result, variable.name, eltSize, array!!, zero, targetDt, valueRegister, valueFpRegister)
|
||||
return result
|
||||
}
|
||||
pointerDeref != null -> {
|
||||
val (addressReg, offset) = codeGen.evaluatePointerAddressIntoReg(result, pointerDeref!!)
|
||||
val actualValueReg = if(pointerDeref!!.type.isFloat) valueFpRegister else valueRegister
|
||||
codeGen.storeValueAtPointersLocation(result, addressReg, offset, pointerDeref!!.type, zero, actualValueReg)
|
||||
return result
|
||||
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg), null)
|
||||
return result
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird assigntarget")
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
else if(targetPointerDeref!=null) {
|
||||
val (addressReg, offset) = codeGen.evaluatePointerAddressIntoReg(result, targetPointerDeref)
|
||||
val actualValueReg = if(targetPointerDeref.type.isFloat) valueFpRegister else valueRegister
|
||||
codeGen.storeValueAtPointersLocation(result, addressReg, offset, targetPointerDeref.type, zero, actualValueReg)
|
||||
return result
|
||||
}
|
||||
else
|
||||
throw AssemblyError("weird assigntarget")
|
||||
}
|
||||
|
||||
private fun assignToIndexedSimplePointer(
|
||||
@@ -1054,14 +1109,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(constAddress==null && memory!=null)
|
||||
return null // TODO("optimized memory in-place -"")
|
||||
|
||||
val constValue = (operand as? PtNumber)?.number
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
if(constValue==1.0) {
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.DECM, vmDt, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol), null)
|
||||
}
|
||||
else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
@@ -1069,16 +1124,14 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol)
|
||||
, null)
|
||||
IRInstruction(Opcode.SUBM, vmDt, fpReg1=tr.resultFpReg, labelSymbol = symbol), null)
|
||||
}
|
||||
} else {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
if(constValue==1.0) {
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.DECM, vmDt, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol)
|
||||
, null)
|
||||
IRInstruction(Opcode.DECM, vmDt, labelSymbol = symbol), null)
|
||||
}
|
||||
else {
|
||||
val tr = expressionEval.translateExpression(operand)
|
||||
@@ -1086,8 +1139,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
addInstr(result, if(constAddress!=null)
|
||||
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, address = constAddress)
|
||||
else
|
||||
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, labelSymbol = symbol)
|
||||
, null)
|
||||
IRInstruction(Opcode.SUBM, vmDt, reg1=tr.resultReg, labelSymbol = symbol), null)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -1153,9 +1205,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return null // TODO("optimized memory in-place +"")
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val constValue = (operand as? PtNumber)?.number
|
||||
|
||||
if(vmDt==IRDataType.FLOAT) {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
if(constValue==1.0) {
|
||||
addInstr(result, if (constAddress != null)
|
||||
IRInstruction(Opcode.INCM, vmDt, address = constAddress)
|
||||
else
|
||||
@@ -1170,7 +1223,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
IRInstruction(Opcode.ADDM, vmDt, fpReg1 = tr.resultFpReg, labelSymbol = symbol) , null)
|
||||
}
|
||||
} else {
|
||||
if((operand as? PtNumber)?.number==1.0) {
|
||||
if(constValue==1.0) {
|
||||
addInstr(result, if (constAddress != null)
|
||||
IRInstruction(Opcode.INCM, vmDt, address = constAddress)
|
||||
else
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import prog8.code.StMemorySlabBlockName
|
||||
import prog8.code.StStructInstanceBlockName
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
@@ -12,7 +14,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
fun translate(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
return when(call.name) {
|
||||
"abs__byte", "abs__word", "abs__float" -> funcAbs(call)
|
||||
"abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(call)
|
||||
"cmp" -> funcCmp(call)
|
||||
"sgn" -> funcSgn(call)
|
||||
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
|
||||
@@ -22,25 +24,28 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"callfar" -> funcCallfar(call)
|
||||
"callfar2" -> funcCallfar2(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")
|
||||
"msw" -> funcMsw(call)
|
||||
"lsw" -> funcLsw(call)
|
||||
"msb" -> funcMsb(call)
|
||||
"lsb" -> funcLsb(call)
|
||||
"memory" -> funcMemory(call)
|
||||
"peek" -> funcPeek(call, IRDataType.BYTE)
|
||||
"peekbool" -> funcPeek(call, IRDataType.BYTE)
|
||||
"peekw" -> funcPeek(call, IRDataType.WORD)
|
||||
"peekl" -> funcPeek(call, IRDataType.LONG)
|
||||
"peekf" -> funcPeek(call, IRDataType.FLOAT)
|
||||
"poke" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokebool" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokebowl" -> funcPoke(call, IRDataType.BYTE)
|
||||
"pokew" -> funcPoke(call, IRDataType.WORD)
|
||||
"pokel" -> funcPoke(call, IRDataType.LONG)
|
||||
"pokef" -> funcPoke(call, IRDataType.FLOAT)
|
||||
"pokemon" -> funcPokemon(call)
|
||||
"mkword" -> funcMkword(call)
|
||||
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(call)
|
||||
"min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(call)
|
||||
"max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(call)
|
||||
"mklong", "mklong2" -> funcMklong(call)
|
||||
"clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword", "clamp__long" -> funcClamp(call)
|
||||
"min__byte", "min__ubyte", "min__word", "min__uword", "min__long" -> funcMin(call)
|
||||
"max__byte", "max__ubyte", "max__word", "max__uword", "max__long" -> funcMax(call)
|
||||
"setlsb" -> funcSetLsbMsb(call, false)
|
||||
"setmsb" -> funcSetLsbMsb(call, true)
|
||||
"rol" -> funcRolRor(call)
|
||||
@@ -52,6 +57,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||
"prog8_lib_structalloc" -> funcStructAlloc(call)
|
||||
"sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
|
||||
"offsetof" -> throw AssemblyError("offsetof must have been replaced with a constant")
|
||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||
}
|
||||
}
|
||||
@@ -201,6 +207,17 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
result += IRCodeChunk(notNegativeLabel, null)
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1)
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
val notNegativeLabel = codeGen.createLabelName()
|
||||
val compareReg = codeGen.registers.next(IRDataType.LONG)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADR, IRDataType.LONG, reg1=compareReg, reg2=tr.resultReg)
|
||||
it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel)
|
||||
it += IRInstruction(Opcode.NEG, IRDataType.LONG, reg1=tr.resultReg)
|
||||
}
|
||||
result += IRCodeChunk(notNegativeLabel, null)
|
||||
return ExpressionCodeResult(result, IRDataType.LONG, tr.resultReg, -1)
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null)
|
||||
@@ -250,6 +267,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
BaseDataType.LONG -> TODO("SQRT LONG")
|
||||
BaseDataType.FLOAT -> {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
val resultFpReg = codeGen.registers.next(IRDataType.FLOAT)
|
||||
@@ -280,6 +298,52 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcMklong(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val resultReg = codeGen.registers.next(IRDataType.LONG)
|
||||
if(call.args.size==2) {
|
||||
// mklong2(word, word)
|
||||
if((call.args[0] as? PtNumber)?.number == 0.0) {
|
||||
// msw is 0, use EXT
|
||||
val lswTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, lswTr, lswTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.WORD, reg1=resultReg, reg2 = lswTr.resultReg), null)
|
||||
} else {
|
||||
val mswTr = exprGen.translateExpression(call.args[0])
|
||||
addToResult(result, mswTr, mswTr.resultReg, -1)
|
||||
val lswTr = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, lswTr, lswTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.WORD, reg1=resultReg, reg2 = mswTr.resultReg, reg3 = lswTr.resultReg), null)
|
||||
}
|
||||
} else {
|
||||
// mklong(msb, b3, b2, lsb)
|
||||
if((call.args[0] as? PtNumber)?.number == 0.0 && (call.args[1] as? PtNumber)?.number == 0.0 && (call.args[2] as? PtNumber)?.number == 0.0) {
|
||||
// use EXT.b + EXT.w
|
||||
val lsbTr = exprGen.translateExpression(call.args[3])
|
||||
addToResult(result, lsbTr, lsbTr.resultReg, -1)
|
||||
val wordReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=wordReg, reg2 = lsbTr.resultReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.WORD, reg1=resultReg, reg2 = wordReg), null)
|
||||
} else {
|
||||
val msbTr = exprGen.translateExpression(call.args[0])
|
||||
val b2Tr = exprGen.translateExpression(call.args[1])
|
||||
val b1Tr = exprGen.translateExpression(call.args[2])
|
||||
val lsbTr = exprGen.translateExpression(call.args[3])
|
||||
addToResult(result, msbTr, msbTr.resultReg, -1)
|
||||
addToResult(result, b2Tr, b2Tr.resultReg, -1)
|
||||
addToResult(result, b1Tr, b1Tr.resultReg, -1)
|
||||
addToResult(result, lsbTr, lsbTr.resultReg, -1)
|
||||
val lswReg = codeGen.registers.next(IRDataType.WORD)
|
||||
val mswReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=mswReg, reg2 = msbTr.resultReg, reg3 = b2Tr.resultReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=lswReg, reg2 = b1Tr.resultReg, reg3 = lsbTr.resultReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.WORD, reg1=resultReg, reg2 = mswReg, reg3 = lswReg), null)
|
||||
}
|
||||
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.LONG, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val type = irType(call.type)
|
||||
@@ -304,6 +368,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
BaseDataType.BYTE -> IMSyscall.CLAMP_BYTE
|
||||
BaseDataType.UWORD -> IMSyscall.CLAMP_UWORD
|
||||
BaseDataType.WORD -> IMSyscall.CLAMP_WORD
|
||||
BaseDataType.LONG -> IMSyscall.CLAMP_LONG
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
result += codeGen.makeSyscall(syscall, listOf(
|
||||
@@ -500,7 +565,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val name = (call.args[0] as PtString).value
|
||||
val code = IRCodeChunk(null, null)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "$StMemorySlabPrefix.prog8_memoryslab_$name")
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "$StMemorySlabBlockName.memory_$name")
|
||||
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
@@ -508,7 +573,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
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)
|
||||
code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "${StStructInstanceBlockName}.$labelname")
|
||||
return ExpressionCodeResult(code, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
@@ -522,6 +587,15 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcLsw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.WORD, reg1 = resultReg, reg2 = tr.resultReg), null)
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
@@ -532,6 +606,15 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcMsw(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val tr = exprGen.translateExpression(call.args.single())
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.WORD, reg1 = resultReg, reg2 = tr.resultReg), null)
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||
}
|
||||
|
||||
private fun funcRolRor(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val arg = call.args[0]
|
||||
|
||||
@@ -446,7 +446,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else (it as PtNumber).number.toInt()
|
||||
}
|
||||
when {
|
||||
elementDt.isIntegerOrBool -> {
|
||||
elementDt.isWordOrByteOrBool -> {
|
||||
if (elementDt.isByteOrBool) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_BYTE)
|
||||
if (elementDt.isWord) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_WORD)
|
||||
val gottemLabel = codeGen.createLabelName()
|
||||
@@ -686,6 +686,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.WORD, reg1=tr.resultReg, immediate = 0), null)
|
||||
actualResultReg2 = loadStatusAsBooleanResult(Opcode.BSTNE, result)
|
||||
}
|
||||
valueDt.isLong -> TODO("typecast long ${cast.position}")
|
||||
valueDt.isFloat -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
@@ -705,6 +706,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
|
||||
}
|
||||
BaseDataType.LONG -> TODO("cast UBYTE to LONG ${cast.position}")
|
||||
BaseDataType.FLOAT -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
addInstr(result, IRInstruction(Opcode.FTOUB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
@@ -721,6 +723,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
addInstr(result, IRInstruction(Opcode.LSIG, IRDataType.BYTE, reg1=actualResultReg2, reg2=tr.resultReg, immediate = 0), null)
|
||||
}
|
||||
BaseDataType.LONG -> TODO("cast BYTE to LONG ${cast.position}")
|
||||
BaseDataType.FLOAT -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.BYTE)
|
||||
addInstr(result, IRInstruction(Opcode.FTOSB, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
@@ -743,6 +746,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
BaseDataType.WORD -> {
|
||||
actualResultReg2 = tr.resultReg
|
||||
}
|
||||
BaseDataType.LONG -> TODO("cast UWORD to LONG ${cast.position}")
|
||||
BaseDataType.FLOAT -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
@@ -768,6 +772,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
BaseDataType.UWORD -> {
|
||||
actualResultReg2 = tr.resultReg
|
||||
}
|
||||
BaseDataType.LONG -> TODO("cast WORD to LONG ${cast.position}}")
|
||||
BaseDataType.FLOAT -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.FTOSW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
@@ -775,6 +780,33 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
when(valueDt.base) {
|
||||
BaseDataType.UBYTE, BaseDataType.BOOL -> {
|
||||
// ubyte to long: double sign extend
|
||||
val wordreg = codeGen.registers.next(IRDataType.WORD)
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.BYTE, reg1 = wordreg, reg2=tr.resultReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=wordreg), null)
|
||||
}
|
||||
BaseDataType.BYTE -> {
|
||||
// byte to long: double sign extend
|
||||
val wordreg = codeGen.registers.next(IRDataType.WORD)
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
|
||||
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.BYTE, reg1 = wordreg, reg2=tr.resultReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=wordreg), null)
|
||||
}
|
||||
BaseDataType.UWORD -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null)
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
|
||||
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
actualResultFpReg2 = codeGen.registers.next(IRDataType.FLOAT)
|
||||
when(valueDt.base) {
|
||||
|
||||
@@ -47,6 +47,7 @@ class IRCodeGen(
|
||||
ensureFirstChunkLabels(irProg)
|
||||
irProg.linkChunks()
|
||||
irProg.convertAsmChunks()
|
||||
irProg.splitSSAchunks()
|
||||
|
||||
// the optimizer also does 1 essential step regardless of optimizations: joining adjacent chunks.
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
@@ -1975,12 +1976,17 @@ class IRCodeGen(
|
||||
in Cx16VirtualRegisters -> {
|
||||
chunk += IRInstruction(Opcode.STOREM, paramDt, reg1=resultReg, labelSymbol = "cx16.${registerOrFlag.registerOrPair.toString().lowercase()}")
|
||||
}
|
||||
in combinedLongRegisters -> {
|
||||
require(paramDt==IRDataType.LONG)
|
||||
val startreg = registerOrFlag.registerOrPair!!.startregname()
|
||||
chunk += IRInstruction(Opcode.STOREM, paramDt, reg1=resultReg, labelSymbol = "cx16.${startreg}")
|
||||
}
|
||||
null -> when(registerOrFlag.statusflag) {
|
||||
// TODO: do the statusflag argument as last
|
||||
Statusflag.Pc -> chunk += IRInstruction(Opcode.LSR, paramDt, reg1=resultReg)
|
||||
else -> throw AssemblyError("unsupported statusflag as param")
|
||||
}
|
||||
else -> throw AssemblyError("unsupported register arg")
|
||||
else -> throw AssemblyError("unsupported register arg $registerOrFlag")
|
||||
}
|
||||
return chunk
|
||||
}
|
||||
|
||||
@@ -161,10 +161,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
if(chunk.label!=null)
|
||||
return false
|
||||
if(previous is IRCodeChunk && chunk is IRCodeChunk) {
|
||||
// if the previous chunk doesn't end in a jump or a return, flow continues into the next chunk
|
||||
// if the previous chunk doesn't end in a SSA branching instruction, flow continues into the next chunk, so they may be joined
|
||||
val lastInstruction = previous.instructions.lastOrNull()
|
||||
if(lastInstruction!=null)
|
||||
return lastInstruction.opcode !in OpcodesThatJump
|
||||
return lastInstruction.opcode !in OpcodesThatEndSSAblock
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -300,16 +300,16 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
// remove useless RETURN
|
||||
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR || ins.opcode==Opcode.RETURNI)) {
|
||||
val previous = chunk.instructions[idx-1]
|
||||
if(previous.opcode in OpcodesThatJump) {
|
||||
if(previous.opcode in OpcodesThatBranchUnconditionally) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
// replace subsequent opcodes that jump by just the first
|
||||
if(idx>0 && (ins.opcode in OpcodesThatJump)) {
|
||||
if(idx>0 && (ins.opcode in OpcodesThatBranchUnconditionally)) {
|
||||
val previous = chunk.instructions[idx-1]
|
||||
if(previous.opcode in OpcodesThatJump) {
|
||||
if(previous.opcode in OpcodesThatBranchUnconditionally) {
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
@@ -362,7 +362,7 @@ jump p8_label_gen_2
|
||||
*/
|
||||
var changed=false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if(idx>=2 && ins.opcode in OpcodesThatJump) {
|
||||
if(idx>=2 && ins.opcode in OpcodesThatBranchUnconditionally) {
|
||||
val previous = indexedInstructions[idx-1].value
|
||||
val previous2 = indexedInstructions[idx-2].value
|
||||
if(previous.opcode==Opcode.LOADR && previous2.opcode in OpcodesThatLoad) {
|
||||
|
||||
@@ -7,6 +7,7 @@ internal class RegisterPool {
|
||||
// everything from 99000 onwards is reserved for special purposes:
|
||||
// 99000 - 99099 : WORD registers for syscall arguments and response value(s)
|
||||
// 99100 - 99199 : BYTE registers for syscall arguments and response value(s)
|
||||
// 99200 - 99299 : LONG registers for syscall arguments and response value(s)
|
||||
|
||||
private var nextRegister: Int=1
|
||||
private val registerTypes: MutableMap<Int, IRDataType> = mutableMapOf()
|
||||
@@ -18,6 +19,8 @@ internal class RegisterPool {
|
||||
registerTypes[i] = IRDataType.WORD
|
||||
for(i in 99100..99199)
|
||||
registerTypes[i] = IRDataType.BYTE
|
||||
for(i in 99200..99299)
|
||||
registerTypes[i] = IRDataType.LONG
|
||||
}
|
||||
|
||||
fun next(type: IRDataType): Int {
|
||||
|
||||
@@ -47,10 +47,16 @@ private fun convert(struct: StStruct): IRStStructDef =
|
||||
IRStStructDef(struct.scopedNameString, struct.fields, struct.size)
|
||||
|
||||
|
||||
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
|
||||
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
|
||||
else
|
||||
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
||||
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement {
|
||||
return if (elt.boolean != null)
|
||||
IRStArrayElement(elt.boolean, null, null)
|
||||
else if(elt.number!=null)
|
||||
IRStArrayElement(null, elt.number, null)
|
||||
else {
|
||||
val symbol = elt.addressOfSymbol ?: (StStructInstanceBlockName + "." + (elt.structInstance ?: elt.structInstanceUninitialized))
|
||||
IRStArrayElement(null, null, symbol)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
@@ -72,7 +78,8 @@ private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
val newArray = mutableListOf<IRStArrayElement>()
|
||||
array.forEach {
|
||||
if(it.addressOfSymbol!=null) {
|
||||
val target = variable.lookup(it.addressOfSymbol!!) ?: throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
|
||||
val target = variable.lookup(it.addressOfSymbol!!) ?:
|
||||
throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
|
||||
newArray.add(IRStArrayElement(null, null, target.scopedNameString))
|
||||
} else {
|
||||
newArray.add(convertArrayElt(it))
|
||||
@@ -129,11 +136,11 @@ private fun convert(constant: StConstant): IRStConstant {
|
||||
}
|
||||
|
||||
|
||||
private fun convert(variable: StMemorySlab): IRStMemorySlab {
|
||||
return if('.' in variable.name)
|
||||
IRStMemorySlab(variable.name, variable.size, variable.align)
|
||||
private fun convert(mem: StMemorySlab): IRStMemorySlab {
|
||||
return if('.' in mem.name)
|
||||
IRStMemorySlab(mem.name, mem.size, mem.align)
|
||||
else
|
||||
IRStMemorySlab("$StMemorySlabPrefix.${variable.name}", variable.size, variable.align)
|
||||
IRStMemorySlab("$StMemorySlabBlockName.${mem.name}", mem.size, mem.align)
|
||||
}
|
||||
|
||||
|
||||
@@ -142,8 +149,8 @@ private fun convert(instance: StStructInstance, fields: Iterable<Pair<DataType,
|
||||
val elt = convertArrayElt(value)
|
||||
IRStructInitValue(field.first.base, elt)
|
||||
}
|
||||
return IRStStructInstance(instance.name, instance.structName, values, instance.size)
|
||||
return if('.' in instance.name)
|
||||
IRStStructInstance(instance.name, instance.structName, values, instance.size)
|
||||
else
|
||||
IRStStructInstance("${StStructInstanceBlockName}.${instance.name}", instance.structName, values, instance.size)
|
||||
}
|
||||
|
||||
|
||||
internal const val StMemorySlabPrefix = "prog8_slabs" // TODO also add ".prog8_memoryslab_" ?
|
||||
@@ -10,12 +10,14 @@ internal object DummyMemsizer : IMemSizer {
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
|
||||
BaseDataType.LONG -> numElements*4
|
||||
BaseDataType.FLOAT -> numElements*5
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isLong -> 4 * (numElements ?: 1)
|
||||
dt.isFloat -> 5 * (numElements ?: 1)
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
noSysInit = true,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
compilerVersion="99.99",
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
|
||||
@@ -25,6 +25,7 @@ class TestVmCodeGen: FunSpec({
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
compilerVersion="99.99",
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
@@ -340,7 +341,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBe 1
|
||||
irChunks.size shouldBe 2
|
||||
}
|
||||
|
||||
test("integer comparison expressions against zero") {
|
||||
@@ -536,7 +537,7 @@ class TestVmCodeGen: FunSpec({
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBe 1
|
||||
irChunks.size shouldBe 2
|
||||
}
|
||||
|
||||
test("extsub allowed in ir-codegen") {
|
||||
|
||||
@@ -39,7 +39,10 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
// see if LONG values may be reduced to something smaller
|
||||
val smaller = NumericLiteral.optimalInteger(numLiteral.number.toInt(), numLiteral.position)
|
||||
if(smaller.type!=BaseDataType.LONG) {
|
||||
return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent))
|
||||
if(parent !is Assignment || !parent.target.inferType(program).isLong) {
|
||||
// do NOT reduce the type if the target of the assignment is a long
|
||||
return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +88,8 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
* (X + c1) - c2 -> X + (c1-c2)
|
||||
*/
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator==".")
|
||||
return noModifications
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val leftconst = expr.left.constValue(program)
|
||||
val rightconst = expr.right.constValue(program)
|
||||
@@ -438,7 +443,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
val numval = decl.value as? NumericLiteral
|
||||
if(decl.type== VarDeclType.CONST && numval!=null) {
|
||||
val valueDt = numval.inferType(program)
|
||||
if(valueDt issimpletype BaseDataType.LONG) {
|
||||
if(valueDt issimpletype BaseDataType.LONG || decl.datatype.isLong) {
|
||||
return noModifications // this is handled in the numericalvalue case
|
||||
}
|
||||
if(!(valueDt istype decl.datatype)) {
|
||||
|
||||
@@ -26,19 +26,15 @@ class VarConstantValueTypeAdjuster(
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if(decl.parent is AnonymousScope)
|
||||
throw FatalAstException("vardecl may no longer occur in anonymousscope")
|
||||
throw FatalAstException("vardecl may no longer occur in anonymousscope ${decl.position}")
|
||||
|
||||
try {
|
||||
val declConstValue = decl.value?.constValue(program)
|
||||
if(declConstValue!=null && (decl.type== VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
||||
&& declConstValue.type != decl.datatype.base) {
|
||||
if(decl.datatype.isInteger && declConstValue.type == BaseDataType.FLOAT) {
|
||||
// avoid silent float roundings
|
||||
errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position)
|
||||
}
|
||||
val declConstValue = decl.value?.constValue(program)
|
||||
if(declConstValue!=null && (decl.type== VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
||||
&& declConstValue.type != decl.datatype.base) {
|
||||
if(decl.datatype.isInteger && declConstValue.type == BaseDataType.FLOAT) {
|
||||
// avoid silent float roundings
|
||||
errors.err("refused truncating of float to avoid loss of precision", decl.value!!.position)
|
||||
}
|
||||
} catch (x: UndefinedSymbolError) {
|
||||
errors.err(x.message, x.position)
|
||||
}
|
||||
|
||||
// replace variables by constants, if possible
|
||||
@@ -173,7 +169,12 @@ class VarConstantValueTypeAdjuster(
|
||||
val replaceFunc = if(t1.isBytes) {
|
||||
if(t1 issimpletype BaseDataType.BYTE) "clamp__byte" else "clamp__ubyte"
|
||||
} else if(t1.isInteger) {
|
||||
if(t1 issimpletype BaseDataType.WORD) "clamp__word" else "clamp__uword"
|
||||
when {
|
||||
t1 issimpletype BaseDataType.WORD -> "clamp__word"
|
||||
t1 issimpletype BaseDataType.UWORD -> "clamp__uword"
|
||||
t1 issimpletype BaseDataType.LONG -> "clamp__long"
|
||||
else -> throw FatalAstException("clamp type")
|
||||
}
|
||||
} else {
|
||||
errors.err("clamp builtin not supported for floats, use floats.clamp", functionCallExpr.position)
|
||||
return noModifications
|
||||
@@ -195,10 +196,12 @@ class VarConstantValueTypeAdjuster(
|
||||
else
|
||||
"${funcName}__ubyte"
|
||||
} else if(t1.isInteger && t2.isInteger) {
|
||||
replaceFunc = if(t1 issimpletype BaseDataType.WORD || t2 issimpletype BaseDataType.WORD)
|
||||
"${funcName}__word"
|
||||
else
|
||||
"${funcName}__uword"
|
||||
replaceFunc = when {
|
||||
t1 issimpletype BaseDataType.LONG || t2 issimpletype BaseDataType.LONG -> "${funcName}__long"
|
||||
t1 issimpletype BaseDataType.WORD || t2 issimpletype BaseDataType.WORD -> "${funcName}__word"
|
||||
t1 issimpletype BaseDataType.UWORD || t2 issimpletype BaseDataType.UWORD -> "${funcName}__uword"
|
||||
else -> throw FatalAstException("min/max type")
|
||||
}
|
||||
} else if(t1.isNumeric && t2.isNumeric) {
|
||||
errors.err("min/max not supported for floats", functionCallExpr.position)
|
||||
return noModifications
|
||||
@@ -218,6 +221,7 @@ class VarConstantValueTypeAdjuster(
|
||||
val replaceFunc = when {
|
||||
dt.isSignedByte -> "abs__byte"
|
||||
dt.isSignedWord -> "abs__word"
|
||||
dt.isLong -> "abs__long"
|
||||
dt.isFloat -> "abs__float"
|
||||
dt.isUnsignedByte || dt.isUnsignedWord -> {
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
|
||||
@@ -313,41 +317,36 @@ internal class ConstantIdentifierReplacer(
|
||||
if(!dt.isKnown || !dt.isNumeric && !dt.isBool)
|
||||
return noModifications
|
||||
|
||||
try {
|
||||
val cval = identifier.constValue(program) ?: return noModifications
|
||||
val arrayIdx = identifier.parent as? ArrayIndexedExpression
|
||||
if(arrayIdx!=null && cval.type.isNumeric) {
|
||||
// special case when the identifier is used as a pointer var
|
||||
// var = constpointer[x] --> var = @(constvalue+x) [directmemoryread]
|
||||
// constpointer[x] = var -> @(constvalue+x) [directmemorywrite] = var
|
||||
val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
|
||||
return if(arrayIdx.parent is AssignTarget) {
|
||||
val memwrite = DirectMemoryWrite(add, identifier.position)
|
||||
val assignTarget = AssignTarget(null, null, memwrite, null, false, position = identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
|
||||
} else {
|
||||
val memread = DirectMemoryRead(add, identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx, memread, arrayIdx.parent))
|
||||
}
|
||||
val cval = identifier.constValue(program) ?: return noModifications
|
||||
val arrayIdx = identifier.parent as? ArrayIndexedExpression
|
||||
if(arrayIdx!=null && cval.type.isNumeric) {
|
||||
// special case when the identifier is used as a pointer var
|
||||
// var = constpointer[x] --> var = @(constvalue+x) [directmemoryread]
|
||||
// constpointer[x] = var -> @(constvalue+x) [directmemorywrite] = var
|
||||
val add = BinaryExpression(NumericLiteral(cval.type, cval.number, identifier.position), "+", arrayIdx.indexer.indexExpr, identifier.position)
|
||||
return if(arrayIdx.parent is AssignTarget) {
|
||||
val memwrite = DirectMemoryWrite(add, identifier.position)
|
||||
val assignTarget = AssignTarget(null, null, memwrite, null, false, position = identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx.parent, assignTarget, arrayIdx.parent.parent))
|
||||
} else {
|
||||
val memread = DirectMemoryRead(add, identifier.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIdx, memread, arrayIdx.parent))
|
||||
}
|
||||
when {
|
||||
cval.type.isNumericOrBool -> {
|
||||
if(parent is AddressOf)
|
||||
return noModifications // cannot replace the identifier INSIDE the addr-of here, let's do it later.
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
identifier,
|
||||
NumericLiteral(cval.type, cval.number, identifier.position),
|
||||
identifier.parent
|
||||
)
|
||||
}
|
||||
when {
|
||||
cval.type.isNumericOrBool -> {
|
||||
if(parent is AddressOf)
|
||||
return noModifications // cannot replace the identifier INSIDE the addr-of here, let's do it later.
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
identifier,
|
||||
NumericLiteral(cval.type, cval.number, identifier.position),
|
||||
identifier.parent
|
||||
)
|
||||
}
|
||||
cval.type.isPassByRef -> throw InternalCompilerException("pass-by-reference type should not be considered a constant")
|
||||
else -> return noModifications
|
||||
)
|
||||
}
|
||||
} catch (x: UndefinedSymbolError) {
|
||||
errors.err(x.message, x.position)
|
||||
return noModifications
|
||||
cval.type.isPassByRef -> throw InternalCompilerException("pass-by-reference type should not be considered a constant")
|
||||
else -> return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.log2
|
||||
|
||||
|
||||
@@ -90,6 +91,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator==".")
|
||||
return noModifications
|
||||
val newExpr = applyAbsorptionLaws(expr)
|
||||
if(newExpr!=null)
|
||||
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||
@@ -244,15 +247,19 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
if (rightVal?.number == 1.0) {
|
||||
if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
if(!dt.isLong) {
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if (rightDt != leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
if(!dt.isLong) {
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,15 +273,19 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
if (rightVal?.number == 1.0) {
|
||||
if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
if(!dt.isLong) {
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if(rightDt!=leftDt && !(leftDt.isPointer && rightDt.isUnsignedWord)) {
|
||||
val dt = if(leftDt.isPointer) BaseDataType.UWORD else leftDt.base
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
if(!dt.isLong) {
|
||||
val right = NumericLiteral(dt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -424,19 +435,92 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
}
|
||||
|
||||
// fun isPowerOfTwo(number: Double): Boolean {
|
||||
// val intValue = number.toInt()
|
||||
// return intValue > 0 && (intValue and (intValue - 1)) == 0
|
||||
// }
|
||||
|
||||
fun isFactorOf256(number: Double): Boolean {
|
||||
val intValue = number.toInt()
|
||||
return intValue >= 256 && (intValue and 0xFF) == 0
|
||||
}
|
||||
|
||||
if (leftDt.isUnsignedWord && rightVal!=null) {
|
||||
if (expr.operator == ">" && rightVal.number == 255.0 || expr.operator == ">=" && rightVal.number == 256.0) {
|
||||
// uword > 255 --> msb(value)!=0
|
||||
// uword >= 256 --> msb(value)!=0
|
||||
expr.operator = "!="
|
||||
expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
|
||||
expr.right = NumericLiteral(BaseDataType.UBYTE, 0.0, expr.right.position)
|
||||
expr.linkParents(parent)
|
||||
}
|
||||
else if(expr.operator==">=" && isFactorOf256(rightVal.number)) {
|
||||
// uword >= $xx00 --> msb(value)>=xx
|
||||
val msbFraction = floor(rightVal.number / 256.0)
|
||||
expr.operator = ">="
|
||||
expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
|
||||
expr.right = NumericLiteral(BaseDataType.UBYTE, msbFraction, expr.right.position)
|
||||
expr.linkParents(parent)
|
||||
}
|
||||
else if(expr.operator==">" && isFactorOf256(rightVal.number+1)) {
|
||||
// uword > $xxFF --> msb(value)>xx
|
||||
val msbFraction = floor((rightVal.number) / 256.0)
|
||||
expr.operator = ">"
|
||||
expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
|
||||
expr.right = NumericLiteral(BaseDataType.UBYTE, msbFraction, expr.right.position)
|
||||
expr.linkParents(parent)
|
||||
}
|
||||
else if(expr.operator == "<" && rightVal.number == 256.0 || expr.operator == "<=" && rightVal.number == 255.0) {
|
||||
// uword < 256 --> msb(value)==0
|
||||
// uword <= 255 --> msb(value)==0
|
||||
expr.operator = "=="
|
||||
expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
|
||||
expr.right = NumericLiteral(BaseDataType.UBYTE, 0.0, expr.right.position)
|
||||
expr.linkParents(parent)
|
||||
}
|
||||
else if(expr.operator == "<" && isFactorOf256(rightVal.number)) {
|
||||
// uword < $xx00 --> msb(value)<xx
|
||||
val msbFraction = floor(rightVal.number / 256.0)
|
||||
expr.operator = "<"
|
||||
expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
|
||||
expr.right = NumericLiteral(BaseDataType.UBYTE, msbFraction, expr.right.position)
|
||||
expr.linkParents(parent)
|
||||
}
|
||||
else if(expr.operator=="<=" && isFactorOf256(rightVal.number+1)) {
|
||||
// uword <= $xxFF --> msb(value)<=xx
|
||||
val msbFraction = floor((rightVal.number) / 256.0)
|
||||
expr.operator = "<="
|
||||
expr.left = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.left.position), mutableListOf(expr.left), expr.left.position)
|
||||
expr.right = NumericLiteral(BaseDataType.UBYTE, msbFraction, expr.right.position)
|
||||
expr.linkParents(parent)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(arrayIndexedExpression.indexer.constIndex()==0) {
|
||||
if(arrayIndexedExpression.plainarrayvar!=null) {
|
||||
if((arrayIndexedExpression.parent as? BinaryExpression)?.operator !=".") {
|
||||
val binexprParent = arrayIndexedExpression.parent as? BinaryExpression
|
||||
if(binexprParent?.operator!=".") {
|
||||
val dt = arrayIndexedExpression.plainarrayvar!!.inferType(program).getOrUndef()
|
||||
if(dt.isPointer) {
|
||||
// pointer[0] --> pointer^^
|
||||
val deref = PtrDereference(arrayIndexedExpression.plainarrayvar!!.nameInSource, true, arrayIndexedExpression.plainarrayvar!!.position)
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression,deref, parent))
|
||||
}
|
||||
} else if(arrayIndexedExpression.pointerderef==null) {
|
||||
// possibly pointer[0].field --> pointer.field
|
||||
val target = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
|
||||
if(target?.datatype?.isPointer==true) {
|
||||
val field = (binexprParent.right as? IdentifierReference)?.nameInSource
|
||||
if(field!=null) {
|
||||
val deref = PtrDereference(arrayIndexedExpression.plainarrayvar!!.nameInSource + field, false, arrayIndexedExpression.plainarrayvar!!.position)
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression.parent, deref, arrayIndexedExpression.parent.parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val ptrDeref = arrayIndexedExpression.pointerderef
|
||||
|
||||
@@ -8,7 +8,7 @@ import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.PROG8_CONTAINER_MODULES
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.compiler.CallGraph
|
||||
@@ -93,7 +93,7 @@ class UnusedCodeRemover(private val program: Program,
|
||||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||
if("force_output" !in block.options()) {
|
||||
if (block.containsNoCodeNorVars) {
|
||||
if (block.name != INTERNED_STRINGS_MODULENAME && "ignore_unused" !in block.options()) {
|
||||
if (block.name !in PROG8_CONTAINER_MODULES && "ignore_unused" !in block.options()) {
|
||||
if (!block.statements.any { it is Subroutine && it.hasBeenInlined })
|
||||
errors.info("removing unused block '${block.name}'", block.position)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ plugins {
|
||||
id("application")
|
||||
kotlin("jvm")
|
||||
// id("com.github.johnrengelman.shadow") version "8.1.1"
|
||||
id("io.github.goooler.shadow") version "8.1.8"
|
||||
// id("io.github.goooler.shadow") version "8.1.8"
|
||||
id("com.gradleup.shadow") version "9.2.2"
|
||||
id("com.peterabeles.gversion") version "1.10.3"
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
bcd {
|
||||
; Decimal addition an subtraction routines.
|
||||
; For CPUs that support BCD mode (binary coded decimal) (not all 6502 variants support this mode...)
|
||||
; This is useful for example for counting decimal score in a game, to avoid costly conversion to a decimal display string. Just print the hex representation.
|
||||
|
||||
sub addb(byte a, byte b) -> byte {
|
||||
setbcd()
|
||||
a += b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub addub(ubyte a, ubyte b) -> ubyte {
|
||||
setbcd()
|
||||
a += b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub addw(word a, word b) -> word {
|
||||
setbcd()
|
||||
a += b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub adduw(uword a, uword b) -> uword {
|
||||
setbcd()
|
||||
a += b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub addl(long a, long b) -> long {
|
||||
setbcd()
|
||||
a += b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub subb(byte a, byte b) -> byte {
|
||||
setbcd()
|
||||
a -= b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub subub(ubyte a, ubyte b) -> ubyte {
|
||||
setbcd()
|
||||
a -= b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub subuw(uword a, uword b) -> uword {
|
||||
setbcd()
|
||||
a -= b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
sub subl(long a, long b) -> long {
|
||||
setbcd()
|
||||
a -= b
|
||||
clearbcd()
|
||||
return a
|
||||
}
|
||||
|
||||
inline asmsub setbcd() {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sed
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub clearbcd() {
|
||||
%asm {{
|
||||
plp
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -133,6 +133,29 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub SETTIML(long jiffies @R0R1_32) {
|
||||
; -- just like SETTIM, but with a single 32 bit (lower 24 bits used) argument.
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
ldx cx16.r0+1
|
||||
ldy cx16.r0+2
|
||||
jmp SETTIM
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIML() clobbers(X) -> long @R0R1_32 {
|
||||
; -- like RDTIM() and returning the timer value as a 32 bit (lower 24 bits used) value.
|
||||
%asm {{
|
||||
jsr RDTIM
|
||||
sta cx16.r0
|
||||
stx cx16.r0+1
|
||||
sty cx16.r0+2
|
||||
lda #0
|
||||
sta cx16.r0+3
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub CLEARST() {
|
||||
; -- Set the ST status variable back to 0. (there's no direct kernal call for this)
|
||||
; Note: a drive error state (blinking led) isn't cleared! You can use diskio.status() to clear that.
|
||||
@@ -506,6 +529,7 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
|
||||
asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
@@ -513,7 +537,7 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
sta cbm.CINV
|
||||
lda #>_irq_handler
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
@@ -545,6 +569,7 @@ _vector .word ?
|
||||
|
||||
asmsub restore_irq() clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
lda #<cbm.IRQDFRT
|
||||
sta cbm.CINV
|
||||
@@ -554,24 +579,34 @@ asmsub restore_irq() clobbers(A) {
|
||||
sta c64.IREQMASK ; enable raster irq
|
||||
lda #%10000001
|
||||
sta c64.CIA1ICR ; restore CIA1 irq
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
sta user_vector
|
||||
sty user_vector+1
|
||||
|
||||
lda #%01111111
|
||||
sta c64.CIA1ICR ; "switch off" interrupts signals from cia-1
|
||||
sta c64.CIA2ICR ; "switch off" interrupts signals from cia-2
|
||||
lda c64.CIA1ICR ; ack previous irq
|
||||
lda c64.CIA2ICR ; ack previous irq
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr _setup_raster_irq
|
||||
jsr sys.set_rasterline
|
||||
lda #%00000001
|
||||
sta c64.IREQMASK ; enable raster interrupt signals from vic
|
||||
|
||||
lda #<_raster_irq_handler
|
||||
sta cbm.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
@@ -593,34 +628,46 @@ _raster_irq_handler
|
||||
pla
|
||||
rti
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
jmp (user_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
user_vector .word ?
|
||||
.send BSS
|
||||
|
||||
|
||||
_setup_raster_irq
|
||||
pha
|
||||
lda #%01111111
|
||||
sta c64.CIA1ICR ; "switch off" interrupts signals from cia-1
|
||||
sta c64.CIA2ICR ; "switch off" interrupts signals from cia-2
|
||||
and c64.SCROLY
|
||||
sta c64.SCROLY ; clear most significant bit of raster position
|
||||
lda c64.CIA1ICR ; ack previous irq
|
||||
lda c64.CIA2ICR ; ack previous irq
|
||||
pla
|
||||
sta c64.RASTER ; set the raster line number where interrupt should occur
|
||||
cpy #0
|
||||
beq +
|
||||
lda c64.SCROLY
|
||||
ora #%10000000
|
||||
sta c64.SCROLY ; set most significant bit of raster position
|
||||
+ lda #%00000001
|
||||
sta c64.IREQMASK ; enable raster interrupt signals from vic
|
||||
rts
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub update_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
; -- just update the IRQ handler and raster line position for the raster IRQ
|
||||
; this is much more efficient than calling set_rasterirq() again every time.
|
||||
; (but you have to call that one initially at least once to setup the prog8 handler itself)
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta sys.set_rasterirq.user_vector
|
||||
sty sys.set_rasterirq.user_vector+1
|
||||
lda cx16.r0L
|
||||
ldy cx16.r0H
|
||||
jsr sys.set_rasterline
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterline(uword line @AY) {
|
||||
; -- only set a new raster line for the raster IRQ
|
||||
%asm {{
|
||||
sta c64.RASTER ; set the raster line number where interrupt should occur
|
||||
lda c64.SCROLY
|
||||
and #%01111111
|
||||
cpy #0
|
||||
beq +
|
||||
ora #%10000000
|
||||
+ sta c64.SCROLY ; clear most significant bit of raster position
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
%asm {{
|
||||
@@ -680,6 +727,26 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub waitrasterline(uword line @AY) {
|
||||
; -- CPU busy wait until the given raster line is reached
|
||||
%asm {{
|
||||
cpy #0
|
||||
bne _larger
|
||||
- cmp c64.RASTER
|
||||
bne -
|
||||
bit c64.SCROLY
|
||||
bmi -
|
||||
rts
|
||||
_larger
|
||||
cmp c64.RASTER
|
||||
bne _larger
|
||||
bit c64.SCROLY
|
||||
bpl _larger
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
@@ -989,6 +1056,48 @@ _no_msb_size
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushl(long value @R0R1_32) {
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
pha
|
||||
lda cx16.r0+1
|
||||
pha
|
||||
lda cx16.r0+2
|
||||
pha
|
||||
lda cx16.r0+3
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popl() -> long @R0R1_32 {
|
||||
%asm {{
|
||||
pla
|
||||
sta cx16.r0+3
|
||||
pla
|
||||
sta cx16.r0+2
|
||||
pla
|
||||
sta cx16.r0+1
|
||||
pla
|
||||
sta cx16.r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
; The SuperCPU expansion for the C64/C128 contains a 65816.
|
||||
%asm {{
|
||||
php
|
||||
clv
|
||||
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
|
||||
bvc +
|
||||
lda #1
|
||||
plp
|
||||
rts
|
||||
+ lda #0
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
cx16 {
|
||||
@@ -1165,12 +1274,6 @@ cx16 {
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
p8_sys_startup {
|
||||
|
||||
@@ -132,6 +132,29 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub SETTIML(long jiffies @R0R1_32) {
|
||||
; -- just like SETTIM, but with a single 32 bit (lower 24 bits used) argument.
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
ldx cx16.r0+1
|
||||
ldy cx16.r0+2
|
||||
jmp SETTIM
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIML() clobbers(X) -> long @R0R1_32 {
|
||||
; -- like RDTIM() and returning the timer value as a 32 bit (lower 24 bits used) value.
|
||||
%asm {{
|
||||
jsr RDTIM
|
||||
sta cx16.r0
|
||||
stx cx16.r0+1
|
||||
sty cx16.r0+2
|
||||
lda #0
|
||||
sta cx16.r0+3
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub CLEARST() {
|
||||
; -- Set the ST status variable back to 0. (there's no direct kernal call for this)
|
||||
; Note: a drive error state (blinking led) isn't cleared! You can use diskio.status() to clear that.
|
||||
@@ -313,12 +336,13 @@ asmsub banks(ubyte banks @A) {
|
||||
%asm {{
|
||||
and #%00000111
|
||||
sta P8ZP_SCRATCH_REG
|
||||
php
|
||||
sei
|
||||
lda $01
|
||||
and #%11111000
|
||||
ora P8ZP_SCRATCH_REG
|
||||
sta $01
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@@ -511,6 +535,7 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
|
||||
asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
@@ -518,7 +543,7 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
sta cbm.CINV
|
||||
lda #>_irq_handler
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
_irq_handler
|
||||
jsr sys.save_prog8_internals
|
||||
@@ -551,6 +576,7 @@ _vector .word ?
|
||||
|
||||
asmsub restore_irq() clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
lda #<cbm.IRQDFRT
|
||||
sta cbm.CINV
|
||||
@@ -560,24 +586,34 @@ asmsub restore_irq() clobbers(A) {
|
||||
sta c64.IREQMASK ; disable raster irq
|
||||
lda #%10000001
|
||||
sta c64.CIA1ICR ; restore CIA1 irq
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
sta user_vector
|
||||
sty user_vector+1
|
||||
|
||||
lda #%01111111
|
||||
sta c64.CIA1ICR ; "switch off" interrupts signals from cia-1
|
||||
sta c64.CIA2ICR ; "switch off" interrupts signals from cia-2
|
||||
lda c64.CIA1ICR ; ack previous irq
|
||||
lda c64.CIA2ICR ; ack previous irq
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
jsr _setup_raster_irq
|
||||
jsr sys.set_rasterline
|
||||
lda #%00000001
|
||||
sta c64.IREQMASK ; enable raster interrupt signals from vic
|
||||
|
||||
lda #<_raster_irq_handler
|
||||
sta cbm.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
@@ -600,33 +636,46 @@ _raster_irq_handler
|
||||
rti
|
||||
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
jmp (user_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
user_vector .word ?
|
||||
.send BSS
|
||||
|
||||
_setup_raster_irq
|
||||
pha
|
||||
lda #%01111111
|
||||
sta c64.CIA1ICR ; "switch off" interrupts signals from cia-1
|
||||
sta c64.CIA2ICR ; "switch off" interrupts signals from cia-2
|
||||
and c64.SCROLY
|
||||
sta c64.SCROLY ; clear most significant bit of raster position
|
||||
lda c64.CIA1ICR ; ack previous irq
|
||||
lda c64.CIA2ICR ; ack previous irq
|
||||
pla
|
||||
sta c64.RASTER ; set the raster line number where interrupt should occur
|
||||
cpy #0
|
||||
beq +
|
||||
lda c64.SCROLY
|
||||
ora #%10000000
|
||||
sta c64.SCROLY ; set most significant bit of raster position
|
||||
+ lda #%00000001
|
||||
sta c64.IREQMASK ; enable raster interrupt signals from vic
|
||||
rts
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub update_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
; -- just update the IRQ handler and raster line position for the raster IRQ
|
||||
; this is much more efficient than calling set_rasterirq() again every time.
|
||||
; (but you have to call that one initially at least once to setup the prog8 handler itself)
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta sys.set_rasterirq.user_vector
|
||||
sty sys.set_rasterirq.user_vector+1
|
||||
lda cx16.r0L
|
||||
ldy cx16.r0H
|
||||
jsr sys.set_rasterline
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterline(uword line @AY) {
|
||||
; -- only set a new raster line for the raster IRQ
|
||||
%asm {{
|
||||
sta c64.RASTER ; set the raster line number where interrupt should occur
|
||||
lda c64.SCROLY
|
||||
and #%01111111
|
||||
cpy #0
|
||||
beq +
|
||||
ora #%10000000
|
||||
+ sta c64.SCROLY ; clear most significant bit of raster position
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub reset_system() {
|
||||
; Soft-reset the system back to initial power-on Basic prompt.
|
||||
@@ -687,6 +736,26 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub waitrasterline(uword line @AY) {
|
||||
; -- CPU busy wait until the given raster line is reached
|
||||
%asm {{
|
||||
cpy #0
|
||||
bne _larger
|
||||
- cmp c64.RASTER
|
||||
bne -
|
||||
bit c64.SCROLY
|
||||
bmi -
|
||||
rts
|
||||
_larger
|
||||
cmp c64.RASTER
|
||||
bne _larger
|
||||
bit c64.SCROLY
|
||||
bpl _larger
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
@@ -995,6 +1064,49 @@ _no_msb_size
|
||||
pla
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushl(long value @R0R1_32) {
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
pha
|
||||
lda cx16.r0+1
|
||||
pha
|
||||
lda cx16.r0+2
|
||||
pha
|
||||
lda cx16.r0+3
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popl() -> long @R0R1_32 {
|
||||
%asm {{
|
||||
pla
|
||||
sta cx16.r0+3
|
||||
pla
|
||||
sta cx16.r0+2
|
||||
pla
|
||||
sta cx16.r0+1
|
||||
pla
|
||||
sta cx16.r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
; The SuperCPU expansion for the C64/C128 contains a 65816.
|
||||
%asm {{
|
||||
php
|
||||
clv
|
||||
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
|
||||
bvc +
|
||||
lda #1
|
||||
plp
|
||||
rts
|
||||
+ lda #0
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
cx16 {
|
||||
@@ -1172,12 +1284,6 @@ cx16 {
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
p8_sys_startup {
|
||||
|
||||
+108
-10
@@ -155,6 +155,30 @@ asmsub str_uwhex (uword value @ AY) -> str @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
sub str_ulhex(long value) -> str {
|
||||
%asm {{
|
||||
lda value+3
|
||||
jsr internal_ubyte2hex
|
||||
sta string_out
|
||||
sty string_out+1
|
||||
lda value+2
|
||||
jsr internal_ubyte2hex
|
||||
sta string_out+2
|
||||
sty string_out+3
|
||||
lda value+1
|
||||
jsr internal_ubyte2hex
|
||||
sta string_out+4
|
||||
sty string_out+5
|
||||
lda value
|
||||
jsr internal_ubyte2hex
|
||||
sta string_out+6
|
||||
sty string_out+7
|
||||
lda #0
|
||||
sta string_out+8
|
||||
}}
|
||||
return &string_out
|
||||
}
|
||||
|
||||
asmsub str_uw0 (uword value @ AY) clobbers(X) -> str @AY {
|
||||
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
@@ -228,15 +252,95 @@ asmsub str_w (word value @ AY) clobbers(X) -> str @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
sub str_l (long value) -> str {
|
||||
; ---- convert the long value into decimal string form, without left padding 0s
|
||||
; source: https://codebase64.net/doku.php?id=base:32_bit_hexadecimal_to_decimal_conversion
|
||||
; NOTE: this is a pretty slow conversion, don't use for time critical displays!
|
||||
bool negative
|
||||
if value<0 {
|
||||
negative = true
|
||||
value = -value
|
||||
}
|
||||
%asm {{
|
||||
jsr hex2dec
|
||||
|
||||
ldx #0
|
||||
- lda result,x
|
||||
bne +
|
||||
inx ; skip leading zeros
|
||||
cpx #9
|
||||
bne -
|
||||
|
||||
; convert to petscii numbers and return ptr to first nonzero
|
||||
+ stx P8ZP_SCRATCH_B1
|
||||
inc P8ZP_SCRATCH_B1
|
||||
- lda result,x
|
||||
ora #$30
|
||||
sta result,x
|
||||
inx
|
||||
cpx #10
|
||||
bne -
|
||||
lda #0
|
||||
sta result,x
|
||||
; sign needed?
|
||||
lda negative
|
||||
beq +
|
||||
dec P8ZP_SCRATCH_B1
|
||||
ldx P8ZP_SCRATCH_B1
|
||||
lda #'-'
|
||||
sta result_sign,x
|
||||
+ lda #<result_sign
|
||||
ldy #>result_sign
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
iny
|
||||
+ rts
|
||||
|
||||
; converts 10 digits (32 bit values have max. 10 decimal digits)
|
||||
hex2dec
|
||||
ldx #9
|
||||
l3 jsr div10
|
||||
sta result,x
|
||||
dex
|
||||
bpl l3
|
||||
rts
|
||||
|
||||
; divides a 32 bit value by 10
|
||||
; remainder is returned in akku
|
||||
div10
|
||||
ldy #32 ; 32 bits
|
||||
lda #0
|
||||
clc
|
||||
l4 rol
|
||||
cmp #10
|
||||
bcc skip
|
||||
sbc #10
|
||||
skip rol value
|
||||
rol value+1
|
||||
rol value+2
|
||||
rol value+3
|
||||
dey
|
||||
bpl l4
|
||||
rts
|
||||
|
||||
.section BSS
|
||||
result_sign .byte ? ; room for the '-' sign
|
||||
result .fill 10
|
||||
.byte ? ; string 0 terminator
|
||||
.send
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
; ---- string conversion to numbers -----
|
||||
|
||||
asmsub any2uword(str string @AY) clobbers(Y) -> ubyte @A {
|
||||
asmsub any2uword(str string @AY) -> uword @AY, ubyte @X {
|
||||
; -- parses a string into a 16 bit unsigned number. String may be in decimal, hex or binary format.
|
||||
; (the latter two require a $ or % prefix to be recognised)
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
; returns amount of processed characters in A, and the parsed number will be in cx16.r15.
|
||||
; if the string was invalid, 0 will be returned in A.
|
||||
; returns the parsed number word in AY, and the number of processed characters (including the prefix symbol) in X.
|
||||
; if the string was invalid, 0 will be returned as count in X (and the word value in AY will be undefined).
|
||||
%asm {{
|
||||
pha
|
||||
sta P8ZP_SCRATCH_W1
|
||||
@@ -257,13 +361,7 @@ _hex pla
|
||||
_bin pla
|
||||
jsr bin2uword
|
||||
_result
|
||||
pha
|
||||
lda cx16.r15
|
||||
sta P8ZP_SCRATCH_B1 ; result value
|
||||
pla
|
||||
sta cx16.r15
|
||||
sty cx16.r15+1
|
||||
lda P8ZP_SCRATCH_B1
|
||||
ldx cx16.r15
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ palette {
|
||||
; NOTE: this routine requires rom version 49+
|
||||
alias set_default = cx16.set_default_palette
|
||||
|
||||
; get the bank and address of the word-array containing the 256 default palette colors
|
||||
; get the bank and address (in A, and XY) of the word-array containing the 256 default palette colors
|
||||
; NOTE: this routine requires rom version 49+
|
||||
alias get_default = cx16.get_default_palette
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
; Simple routines to control sprites.
|
||||
|
||||
; They're not written for high performance, but for simplicity.
|
||||
; That's why they control 1 sprite at a time. \
|
||||
; That's why they control 1 sprite at a time.
|
||||
; The exceptions are pos_batch() and pos_batch_split(); these are quite efficient
|
||||
; to update sprite positions of multiple sprites in one call.
|
||||
|
||||
; HIGH PERFORMANCE sprite handling would probably have a copy of the sprite registers for each sprite instead,
|
||||
; and a unrolled loop that copies those into the VERA registers when needed.
|
||||
|
||||
; note: sprites z-order will be in front of all layers.
|
||||
; note: collision mask is not supported here yet.
|
||||
; note: "palette offset" is counted as 0-15 (vera multiplies the offset by 16 to get at the actual color index)
|
||||
|
||||
@@ -127,6 +127,28 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub SETTIML(long jiffies @R0R1_32) {
|
||||
; -- just like SETTIM, but with a single 32 bit (lower 24 bits used) argument.
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
ldx cx16.r0+1
|
||||
ldy cx16.r0+2
|
||||
jmp SETTIM
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIML() clobbers(X) -> long @R0R1_32 {
|
||||
; -- like RDTIM_safe() and returning the timer value as a 32 bit (lower 24 bits used) value.
|
||||
%asm {{
|
||||
jsr RDTIM_safe
|
||||
sta cx16.r0
|
||||
stx cx16.r0+1
|
||||
sty cx16.r0+2
|
||||
stz cx16.r0+3
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub CLEARST() {
|
||||
; -- Set the ST status variable back to 0. (there's no direct kernal call for this)
|
||||
; Note: a drive error state (blinking led) isn't cleared! You can use diskio.status() to clear that.
|
||||
@@ -1454,22 +1476,6 @@ sub search_x16edit() -> ubyte {
|
||||
return 255
|
||||
}
|
||||
|
||||
asmsub cpu_is_65816() -> bool @A {
|
||||
; -- Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
%asm {{
|
||||
php
|
||||
clv
|
||||
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
|
||||
bvc +
|
||||
lda #1
|
||||
plp
|
||||
rts
|
||||
+ lda #0
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub set_program_args(str args_ptr, ubyte args_size) {
|
||||
; -- Set the inter-program arguments.
|
||||
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
|
||||
@@ -1576,6 +1582,7 @@ sys {
|
||||
asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
; Sets the handler for the VSYNC interrupt, and enable that interrupt.
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
@@ -1585,7 +1592,7 @@ asmsub set_irq(uword handler @AY) clobbers(A) {
|
||||
sta cbm.CINV+1
|
||||
lda #1
|
||||
tsb cx16.VERA_IEN ; enable the vsync irq
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
|
||||
_irq_handler
|
||||
@@ -1615,6 +1622,7 @@ _vector .word ?
|
||||
|
||||
asmsub restore_irq() clobbers(A) {
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
lda _orig_irqvec
|
||||
sta cbm.CINV
|
||||
@@ -1624,7 +1632,7 @@ asmsub restore_irq() clobbers(A) {
|
||||
and #%11110000 ; disable all Vera IRQs but the vsync
|
||||
ora #%00000001
|
||||
sta cx16.VERA_IEN
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
.section BSS_NOCLEAR
|
||||
_orig_irqvec .word ?
|
||||
@@ -1636,9 +1644,10 @@ _orig_irqvec .word ?
|
||||
asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
; Sets the handler for the LINE interrupt, and enable (only) that interrupt.
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta _vector
|
||||
sty _vector+1
|
||||
sta user_vector
|
||||
sty user_vector+1
|
||||
lda cx16.r0
|
||||
ldy cx16.r0+1
|
||||
lda cx16.VERA_IEN
|
||||
@@ -1652,7 +1661,7 @@ asmsub set_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
sta cbm.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta cbm.CINV+1
|
||||
cli
|
||||
plp
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
@@ -1668,15 +1677,32 @@ _raster_irq_handler
|
||||
pla
|
||||
rti
|
||||
_run_custom
|
||||
jmp (_vector)
|
||||
jmp (user_vector)
|
||||
.section BSS
|
||||
_vector .word ?
|
||||
user_vector .word ?
|
||||
.send BSS
|
||||
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub update_rasterirq(uword handler @AY, uword rasterpos @R0) clobbers(A) {
|
||||
; -- just update the IRQ handler and raster line position for the raster IRQ
|
||||
; this is much more efficient than calling set_rasterirq() again every time.
|
||||
; (but you have to call that one initially at least once to setup the prog8 handler itself)
|
||||
%asm {{
|
||||
php
|
||||
sei
|
||||
sta sys.set_rasterirq.user_vector
|
||||
sty sys.set_rasterirq.user_vector+1
|
||||
lda cx16.r0L
|
||||
ldy cx16.r0H
|
||||
jsr set_rasterline
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterline(uword line @AY) {
|
||||
%asm {{
|
||||
php
|
||||
@@ -1732,13 +1758,15 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
rts
|
||||
|
||||
+ sei
|
||||
+ php
|
||||
sei
|
||||
jsr cbm.RDTIM
|
||||
cli
|
||||
plp
|
||||
sta P8ZP_SCRATCH_B1
|
||||
- sei
|
||||
- php
|
||||
sei
|
||||
jsr cbm.RDTIM
|
||||
cli
|
||||
plp
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
beq -
|
||||
|
||||
@@ -1759,6 +1787,26 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub waitrasterline(uword line @AY) {
|
||||
; -- CPU busy wait until the given raster line is reached
|
||||
%asm {{
|
||||
cpy #0
|
||||
bne _larger
|
||||
- cmp cx16.VERA_SCANLINE_L
|
||||
bne -
|
||||
bit cx16.VERA_IEN
|
||||
bvs -
|
||||
rts
|
||||
_larger
|
||||
cmp cx16.VERA_SCANLINE_L
|
||||
bne _larger
|
||||
bit cx16.VERA_IEN
|
||||
bvc _larger
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub internal_stringcopy(str source @R0, str target @AY) clobbers (A,Y) {
|
||||
; Called when the compiler wants to assign a string value to another string.
|
||||
%asm {{
|
||||
@@ -2093,6 +2141,47 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushl(long value @R0R1_32) {
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
pha
|
||||
lda cx16.r0+1
|
||||
pha
|
||||
lda cx16.r0+2
|
||||
pha
|
||||
lda cx16.r0+3
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popl() -> long @R0R1_32 {
|
||||
%asm {{
|
||||
pla
|
||||
sta cx16.r0+3
|
||||
pla
|
||||
sta cx16.r0+2
|
||||
pla
|
||||
sta cx16.r0+1
|
||||
pla
|
||||
sta cx16.r0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub cpu_is_65816() -> bool @A {
|
||||
; -- Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
%asm {{
|
||||
php
|
||||
clv
|
||||
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
|
||||
bvc +
|
||||
lda #1
|
||||
plp
|
||||
rts
|
||||
+ lda #0
|
||||
plp
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
p8_sys_startup {
|
||||
|
||||
@@ -116,26 +116,29 @@ verafx {
|
||||
|
||||
|
||||
asmsub mult16(uword value1 @R0, uword value2 @R1) clobbers(X) -> uword @AY {
|
||||
; Returns the 16 bits unsigned result of R0*R1 in AY.
|
||||
; Returns the lower 16 bits unsigned result of R0*R1 in AY
|
||||
; Note: only the lower 16 bits! (the upper 16 bits are not valid for unsigned word multiplications, only for signed)
|
||||
; Verafx doesn't support unsigned values like this for full 32 bit result.
|
||||
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda cx16.r0+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
jsr verafx.muls
|
||||
ldx P8ZP_SCRATCH_W1
|
||||
stx cx16.r0
|
||||
ldx P8ZP_SCRATCH_W1+1
|
||||
stx cx16.r0+1
|
||||
jmp muls16
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub muls16(word value1 @R0, word value2 @R1) clobbers(X) -> word @AY {
|
||||
; Returns just the lower 16 bits signed result of the multiplication in cx16.AY.
|
||||
; Note: clobbers R0, R1, and VRAM $1f9bc - $1f9bf (inclusive)
|
||||
%asm {{
|
||||
jsr muls
|
||||
lda cx16.r0L
|
||||
ldy cx16.r0H
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub muls(word value1 @R0, word value2 @R1) clobbers(X) -> word @AY, word @R0 {
|
||||
; Returns the 32 bits signed result in AY and R0 (lower word, upper word).
|
||||
|
||||
asmsub muls(word value1 @R0, word value2 @R1) clobbers(X) -> long @R0R1_32 {
|
||||
; Returns the 32 bits signed result in R0:R1 (lower word, upper word).
|
||||
; Vera Fx multiplication support only works on signed values!
|
||||
; Note: clobbers VRAM $1f9bc - $1f9bf (inclusive)
|
||||
%asm {{
|
||||
@@ -171,12 +174,14 @@ verafx {
|
||||
stz cx16.VERA_DATA0 ; multiply and write out result
|
||||
lda #%00010001 ; $01 with Increment 1
|
||||
sta cx16.VERA_ADDR_H ; so we can read out the result
|
||||
lda cx16.VERA_DATA0 ; store the lower 16 bits of the result in AY
|
||||
lda cx16.VERA_DATA0 ; store the lower 16 bits of the result in R0
|
||||
ldy cx16.VERA_DATA0
|
||||
ldx cx16.VERA_DATA0 ; store the upper 16 bits of the result in R0
|
||||
stx cx16.r0s
|
||||
ldx cx16.VERA_DATA0
|
||||
stx cx16.r0s+1
|
||||
sta cx16.r0L
|
||||
sty cx16.r0H
|
||||
lda cx16.VERA_DATA0 ; store the upper 16 bits of the result in R1
|
||||
ldy cx16.VERA_DATA0 ; store the upper 16 bits of the result in R1
|
||||
sta cx16.r1L
|
||||
sty cx16.r1H
|
||||
stz cx16.VERA_FX_CTRL ; Cache write disable
|
||||
stz cx16.VERA_FX_MULT ; $9F2C reset multiply bit
|
||||
stz cx16.VERA_CTRL ; reset DCSEL
|
||||
|
||||
@@ -220,6 +220,12 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
|
||||
}}
|
||||
}
|
||||
|
||||
sub mul32(uword a, uword b) -> long {
|
||||
; return 32 bits result of a*b
|
||||
cx16.r2 = a*b
|
||||
return mklong2(mul16_last_upper(), cx16.r2)
|
||||
}
|
||||
|
||||
sub direction_sc(byte x1, byte y1, byte x2, byte y2) -> ubyte {
|
||||
; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A.
|
||||
cx16.r0L = 3 ; quadrant
|
||||
@@ -602,10 +608,9 @@ log2_tab
|
||||
return cx16.r15
|
||||
}
|
||||
|
||||
sub crc32(^^ubyte data, uword length) {
|
||||
sub crc32(^^ubyte data, uword length) -> long {
|
||||
; Calculates the CRC-32 (ISO-HDLC/PKZIP) checksum of the buffer.
|
||||
; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words.
|
||||
; result stored in cx16.r14 (low word) and cx16.r15 (high word)
|
||||
; Clobbers R12 through R15, returns the 32 bits result as long
|
||||
; There are also "streaming" crc32_start/update/end routines below, that allow you to calculate crc32 for data that doesn't fit in a single memory block.
|
||||
crc32_start()
|
||||
cx16.r12 = data
|
||||
@@ -614,7 +619,7 @@ log2_tab
|
||||
crc32_update(@(cx16.r12))
|
||||
cx16.r12++
|
||||
}
|
||||
crc32_end()
|
||||
return crc32_end()
|
||||
}
|
||||
|
||||
sub crc32_start() {
|
||||
@@ -666,13 +671,12 @@ log2_tab
|
||||
; }
|
||||
}
|
||||
|
||||
sub crc32_end() {
|
||||
; finalize the "streaming" crc32
|
||||
; result stored in cx16.r14 (low word) and cx16.r15 (high word)
|
||||
void crc32_end_result()
|
||||
sub crc32_end() -> long {
|
||||
; finalize the "streaming" crc32 and returns the result
|
||||
return crc32_end_result()
|
||||
}
|
||||
|
||||
asmsub crc32_end_result() -> uword @R15, uword @R14 {
|
||||
asmsub crc32_end_result() -> long @R14R15_32 {
|
||||
; finalize the "streaming" crc32
|
||||
; returns the result value in cx16.r15 (high word) and r14 (low word)
|
||||
%asm {{
|
||||
|
||||
@@ -81,6 +81,29 @@ asmsub RDTIM16() clobbers(X) -> uword @AY {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub SETTIML(long jiffies @R0R1_32) {
|
||||
; -- just like SETTIM, but with a single 32 bit (lower 24 bits used) argument.
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
ldx cx16.r0+1
|
||||
ldy cx16.r0+2
|
||||
jmp SETTIM
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub RDTIML() clobbers(X) -> long @R0R1_32 {
|
||||
; -- like RDTIM() and returning the timer value as a 32 bit (lower 24 bits used) value.
|
||||
%asm {{
|
||||
jsr RDTIM
|
||||
sta cx16.r0
|
||||
stx cx16.r0+1
|
||||
sty cx16.r0+2
|
||||
lda #0
|
||||
sta cx16.r0+3
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub kbdbuf_clear() {
|
||||
; -- convenience helper routine to clear the keyboard buffer
|
||||
%asm {{
|
||||
@@ -509,6 +532,37 @@ save_SCRATCH_ZPWORD2 .word ?
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub pushl(long value @R0R1_32) {
|
||||
%asm {{
|
||||
lda cx16.r0
|
||||
pha
|
||||
lda cx16.r0+1
|
||||
pha
|
||||
lda cx16.r0+2
|
||||
pha
|
||||
lda cx16.r0+3
|
||||
pha
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub popl() -> long @R0R1_32 {
|
||||
%asm {{
|
||||
pla
|
||||
sta cx16.r0+3
|
||||
pla
|
||||
sta cx16.r0+2
|
||||
pla
|
||||
sta cx16.r0+1
|
||||
pla
|
||||
sta cx16.r0
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cx16 {
|
||||
@@ -686,11 +740,6 @@ cx16 {
|
||||
}}
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
p8_sys_startup {
|
||||
|
||||
@@ -29,6 +29,36 @@ abs_w_into_AY .proc
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
abs_l_into_R0R1 .proc
|
||||
; -- R0:R1 = abs(R0:R1)
|
||||
lda cx16.r1H
|
||||
bmi + ; Negative if high bit of highest byte is set
|
||||
rts
|
||||
+
|
||||
; Invert all four bytes
|
||||
lda cx16.r0L
|
||||
eor #$FF
|
||||
sta cx16.r0L
|
||||
lda cx16.r0H
|
||||
eor #$FF
|
||||
sta cx16.r0H
|
||||
lda cx16.r1L
|
||||
eor #$FF
|
||||
sta cx16.r1L
|
||||
lda cx16.r1H
|
||||
eor #$FF
|
||||
sta cx16.r1H
|
||||
; Add 1 to whole 32-bit value
|
||||
inc cx16.r0L
|
||||
bne +
|
||||
inc cx16.r0H
|
||||
bne +
|
||||
inc cx16.r1L
|
||||
bne +
|
||||
inc cx16.r1H
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
func_sign_b_into_A .proc
|
||||
cmp #0
|
||||
beq _zero
|
||||
|
||||
@@ -449,3 +449,195 @@ _fullpage lda (P8ZP_SCRATCH_W1),y
|
||||
bne _fullpage
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
long_not_equals .proc
|
||||
; checks if the 32 bits long value pointed to by AY is NOT equal to the one pointed to by P8ZP_SCRATCH_W1
|
||||
; returns A=1 if NOT equals otherwise A=0
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #3
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
cmp (P8ZP_SCRATCH_W2),y
|
||||
bne _notequal
|
||||
dey
|
||||
bpl -
|
||||
lda #0
|
||||
rts
|
||||
_notequal lda #1
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
long_add_inplace .proc
|
||||
; long pointed to by AY += long pointed to by P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
clc
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
adc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
adc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
adc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
adc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
long_sub_inplace .proc
|
||||
; long pointed to by AY -= long pointed to by P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
sec
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sbc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sbc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sbc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sbc (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
long_or_inplace .proc
|
||||
; long pointed to by AY OR= long pointed to by P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
long_and_inplace .proc
|
||||
; long pointed to by AY AND= long pointed to by P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
and (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
and (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
and (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
and (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
long_xor_inplace .proc
|
||||
; long pointed to by AY XOR= long pointed to by P8ZP_SCRATCH_W1
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
eor (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
eor (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
eor (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
eor (P8ZP_SCRATCH_W1),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
long_shiftleftX_inplace .proc
|
||||
; bit shift left X bits the long value pointed to by AY
|
||||
cpx #0
|
||||
beq _end
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
- ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
asl a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rol a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rol a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
rol a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dex
|
||||
bne -
|
||||
_end rts
|
||||
.pend
|
||||
|
||||
long_shiftrightX_inplace .proc
|
||||
; bit shift right X bits the long value pointed to by AY
|
||||
cpx #0
|
||||
beq _end
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
- ldy #3
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
asl a ; save sign bit
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ror a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ror a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ror a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ror a
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dex
|
||||
bne -
|
||||
_end rts
|
||||
.pend
|
||||
@@ -109,6 +109,12 @@ txt {
|
||||
}}
|
||||
}
|
||||
|
||||
sub print_ulhex(long value, bool prefix) {
|
||||
if prefix
|
||||
cbm.CHROUT('$')
|
||||
print(conv.str_ulhex(value))
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
@@ -165,6 +171,11 @@ _allzero lda #'0'
|
||||
}}
|
||||
}
|
||||
|
||||
sub print_l(long value) {
|
||||
; prints a 32 bit value to the screen
|
||||
print(conv.str_l(value))
|
||||
}
|
||||
|
||||
asmsub input_chars (^^ubyte buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard, in PETSCII encoding.
|
||||
; Returns length in Y. (string is terminated with a 0 byte as well)
|
||||
|
||||
@@ -102,7 +102,7 @@ sub str_uwbin (uword value) -> str {
|
||||
}
|
||||
|
||||
sub str_uwhex (uword value) -> str {
|
||||
; ---- convert the uword in A/Y in hexadecimal string form (4 digits)
|
||||
; ---- convert the uword in hexadecimal string form (4 digits)
|
||||
ubyte bits = msb(value)
|
||||
string_out[0] = hex_digits[bits>>4]
|
||||
string_out[1] = hex_digits[bits&15]
|
||||
@@ -113,6 +113,26 @@ sub str_uwhex (uword value) -> str {
|
||||
return string_out
|
||||
}
|
||||
|
||||
sub str_ulhex (long value) -> str {
|
||||
; ---- convert the long in hexadecimal string form (8 digits)
|
||||
uword upperw = msw(value)
|
||||
uword lowerw = lsw(value)
|
||||
ubyte bits = msb(upperw)
|
||||
string_out[0] = hex_digits[bits>>4]
|
||||
string_out[1] = hex_digits[bits&15]
|
||||
bits = lsb(upperw)
|
||||
string_out[2] = hex_digits[bits>>4]
|
||||
string_out[3] = hex_digits[bits&15]
|
||||
bits = msb(lowerw)
|
||||
string_out[4] = hex_digits[bits>>4]
|
||||
string_out[5] = hex_digits[bits&15]
|
||||
bits = lsb(lowerw)
|
||||
string_out[6] = hex_digits[bits>>4]
|
||||
string_out[7] = hex_digits[bits&15]
|
||||
string_out[8] = 0
|
||||
return string_out
|
||||
}
|
||||
|
||||
sub str_uw0 (uword value) -> str {
|
||||
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
|
||||
uword value2 = value/10
|
||||
@@ -141,7 +161,7 @@ sub str_uw (uword value) -> str {
|
||||
}
|
||||
|
||||
sub str_w (word value) -> str {
|
||||
; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's
|
||||
; ---- convert the (signed) word into decimal string form, without left padding 0's
|
||||
^^ubyte out_ptr = &string_out
|
||||
if value<0 {
|
||||
@(out_ptr) = '-'
|
||||
@@ -152,6 +172,16 @@ sub str_w (word value) -> str {
|
||||
return string_out
|
||||
}
|
||||
|
||||
sub str_l (long value) -> str {
|
||||
; ---- convert the (signed) long into decimal string form, without left padding 0's
|
||||
%ir {{
|
||||
loadm.l r99200,conv.str_l.value
|
||||
load.w r99000,conv.string_out
|
||||
syscall 60 (r99200.l, r99000.w) : r99000.w
|
||||
returnr.w r99000
|
||||
}}
|
||||
}
|
||||
|
||||
sub internal_str_uw(uword value, str out_ptr) {
|
||||
uword value2 = value/10
|
||||
ubyte digits = value-value2*10 as ubyte
|
||||
@@ -267,12 +297,15 @@ sub bin2uword(str string) -> uword {
|
||||
}
|
||||
}
|
||||
|
||||
sub any2uword(str string) -> uword {
|
||||
sub any2uword(str string) -> uword, ubyte {
|
||||
; -- convert any number string (any prefix allowed) to uword.
|
||||
; returns the parsed word value, and the number of processed characters (including the prefix symbol)
|
||||
ubyte length
|
||||
while string[length]!=0 length++
|
||||
when string[0] {
|
||||
'$' -> return hex2uword(string)
|
||||
'%' -> return bin2uword(string)
|
||||
else -> return str2uword(string)
|
||||
'$' -> return hex2uword(string), length
|
||||
'%' -> return bin2uword(string), length
|
||||
else -> return str2uword(string), length
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -304,6 +304,12 @@ math {
|
||||
}}
|
||||
}
|
||||
|
||||
sub mul32(uword a, uword b) -> long {
|
||||
; return 32 bits result of a*b
|
||||
cx16.r2 = a*b
|
||||
return mklong2(mul16_last_upper(), cx16.r2)
|
||||
}
|
||||
|
||||
sub diff(ubyte b1, ubyte b2) -> ubyte {
|
||||
if b1>b2
|
||||
return b1-b2
|
||||
@@ -353,10 +359,9 @@ math {
|
||||
return cx16.r15
|
||||
}
|
||||
|
||||
sub crc32(^^ubyte data, uword length) {
|
||||
sub crc32(^^ubyte data, uword length) -> long {
|
||||
; Calculates the CRC-32 (ISO-HDLC/PKZIP) checksum of the buffer.
|
||||
; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words.
|
||||
; result stored in cx16.r14 (low word) and cx16.r15 (high word)
|
||||
; Clobbers R12 through R15, returns the result in R0:R1
|
||||
; There are also "streaming" crc32_start/update/end routines below, that allow you to calculate crc32 for data that doesn't fit in a single memory block.
|
||||
crc32_start()
|
||||
cx16.r12 = data
|
||||
@@ -365,7 +370,7 @@ math {
|
||||
crc32_update(@(cx16.r12))
|
||||
cx16.r12++
|
||||
}
|
||||
crc32_end()
|
||||
return crc32_end()
|
||||
}
|
||||
|
||||
sub crc32_start() {
|
||||
@@ -391,11 +396,11 @@ math {
|
||||
}
|
||||
}
|
||||
|
||||
sub crc32_end() {
|
||||
; finalize the "streaming" crc32
|
||||
; result stored in cx16.r14 (low word) and cx16.r15 (high word)
|
||||
sub crc32_end() -> long {
|
||||
; finalize the "streaming" crc32 and return the result in R0:R1
|
||||
cx16.r15 ^= $ffff
|
||||
cx16.r14 ^= $ffff
|
||||
return mklong2(cx16.r15, cx16.r14)
|
||||
}
|
||||
|
||||
; there's no crc32_end_result() here because IR cannot return multiple values yet
|
||||
|
||||
@@ -192,6 +192,14 @@ sys {
|
||||
}}
|
||||
}
|
||||
|
||||
sub pushl(long l) {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
loadm.l r99200,sys.pushl.l
|
||||
push.l r99200
|
||||
}}
|
||||
}
|
||||
|
||||
sub push_returnaddress(uword w) {
|
||||
; note: this actually doesn't do anything useful on the VM because the code execution doesn't use the simulated cpu stack
|
||||
%ir {{
|
||||
@@ -222,6 +230,14 @@ sys {
|
||||
}}
|
||||
}
|
||||
|
||||
sub popl() -> long {
|
||||
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
|
||||
%ir {{
|
||||
pop.l r99200
|
||||
returnr.l r99200
|
||||
}}
|
||||
}
|
||||
|
||||
sub read_flags() -> ubyte {
|
||||
; "simulate" the 6502 status register a little bit
|
||||
if_neg {
|
||||
@@ -244,6 +260,11 @@ sys {
|
||||
|
||||
return cx16.r0L
|
||||
}
|
||||
|
||||
sub cpu_is_65816() -> bool {
|
||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
cx16 {
|
||||
|
||||
@@ -136,6 +136,13 @@ sub print_uwhex (uword value, bool prefix) {
|
||||
print(conv.str_uwhex(value))
|
||||
}
|
||||
|
||||
sub print_ulhex (long value, bool prefix) {
|
||||
; ---- print the ulong in hexadecimal form (4 digits)
|
||||
if prefix
|
||||
chrout('$')
|
||||
print(conv.str_ulhex(value))
|
||||
}
|
||||
|
||||
sub print_uw0 (uword value) {
|
||||
; ---- print the uword value in decimal form, with left padding 0s (5 positions total)
|
||||
print(conv.str_uw0(value))
|
||||
@@ -151,6 +158,14 @@ sub print_w (word value) {
|
||||
print(conv.str_w(value))
|
||||
}
|
||||
|
||||
sub print_l (long value) {
|
||||
; ---- print the (signed) long in decimal form, without left padding 0's
|
||||
%ir {{
|
||||
loadm.l r99200,txt.print_l.value
|
||||
syscall 59 (r99200.l)
|
||||
}}
|
||||
}
|
||||
|
||||
sub input_chars (str buffer) -> ubyte {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length of input. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
|
||||
@@ -5,6 +5,7 @@ import prog8.ast.FatalAstException
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.SyntaxError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.StructDecl
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.*
|
||||
@@ -15,6 +16,7 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
|
||||
"abs" to ::builtinAbs,
|
||||
"len" to ::builtinLen,
|
||||
"sizeof" to ::builtinSizeof,
|
||||
"offsetof" to ::builtinOffsetof,
|
||||
"sgn" to ::builtinSgn,
|
||||
"sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
|
||||
"sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
|
||||
@@ -24,6 +26,8 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
|
||||
"msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x ushr 8 and 255).toDouble()} },
|
||||
"msw" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x ushr 16 and 65535).toDouble()} },
|
||||
"mkword" to ::builtinMkword,
|
||||
"mklong" to ::builtinMklong,
|
||||
"mklong2" to ::builtinMklong,
|
||||
"clamp__ubyte" to ::builtinClampUByte,
|
||||
"clamp__byte" to ::builtinClampByte,
|
||||
"clamp__uword" to ::builtinClampUWord,
|
||||
@@ -32,10 +36,12 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
|
||||
"min__byte" to ::builtinMinByte,
|
||||
"min__uword" to ::builtinMinUWord,
|
||||
"min__word" to ::builtinMinWord,
|
||||
"min__long" to ::builtinMinLong,
|
||||
"max__ubyte" to ::builtinMaxUByte,
|
||||
"max__byte" to ::builtinMaxByte,
|
||||
"max__uword" to ::builtinMaxUWord,
|
||||
"max__word" to ::builtinMaxWord
|
||||
"max__word" to ::builtinMaxWord,
|
||||
"max__long" to ::builtinMaxLong
|
||||
)
|
||||
|
||||
internal fun builtinFunctionReturnType(function: String): InferredTypes.InferredType {
|
||||
@@ -90,6 +96,25 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
|
||||
else throw SyntaxError("abs requires one integer argument", position)
|
||||
}
|
||||
|
||||
private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
// 1 arg, "Struct.field"
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("offsetof requires one argument", position)
|
||||
val identifier = (args[0] as? IdentifierReference)?.nameInSource
|
||||
if(identifier==null || identifier.size<2)
|
||||
throw CannotEvaluateException("offsetof","argument should be an identifier of the form Struct.field")
|
||||
|
||||
val structname = identifier.dropLast(1)
|
||||
val fieldname = identifier.last()
|
||||
val struct = args[0].definingScope.lookup(structname) as? StructDecl
|
||||
if(struct==null)
|
||||
throw SyntaxError("cannot find struct '$structname'", args[0].position)
|
||||
val offset = struct.offsetof(fieldname, program.memsizer)
|
||||
if(offset==null)
|
||||
throw SyntaxError("no such field '${identifier.joinToString(".")}'", args[0].position)
|
||||
return NumericLiteral.optimalInteger(offset.toInt(), position)
|
||||
}
|
||||
|
||||
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
// 1 arg, type = anything, result type = ubyte or uword
|
||||
if(args.size!=1)
|
||||
@@ -182,6 +207,29 @@ private fun builtinMkword(args: List<Expression>, position: Position, program: P
|
||||
return NumericLiteral(BaseDataType.UWORD, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMklong(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
when(args.size) {
|
||||
2 -> {
|
||||
val constMsw = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val constLsw = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = (constMsw.number.toInt() shl 16) or constLsw.number.toInt()
|
||||
return NumericLiteral(BaseDataType.LONG, result.toDouble(), position)
|
||||
}
|
||||
4 -> {
|
||||
val constMsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val constB2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val constB1 = args[2].constValue(program) ?: throw NotConstArgumentException()
|
||||
val constLsb = args[3].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = (constMsb.number.toInt() shl 24) or
|
||||
(constB2.number.toInt() shl 16) or
|
||||
(constB1.number.toInt() shl 8) or
|
||||
constLsb.number.toInt()
|
||||
return NumericLiteral(BaseDataType.LONG, result.toDouble(), position)
|
||||
}
|
||||
else -> throw SyntaxError("mkword requires msw and lsw, or msb,b2,b1,lsb arguments", position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun builtinSgn(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sgn requires one argument", position)
|
||||
@@ -225,6 +273,15 @@ private fun builtinMinUWord(args: List<Expression>, position: Position, program:
|
||||
return NumericLiteral(BaseDataType.UWORD, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMinLong(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("min requires 2 arguments", position)
|
||||
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = min(val1.number.toInt(), val2.number.toInt())
|
||||
return NumericLiteral(BaseDataType.LONG, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMaxByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("max requires 2 arguments", position)
|
||||
@@ -261,6 +318,15 @@ private fun builtinMaxUWord(args: List<Expression>, position: Position, program:
|
||||
return NumericLiteral(BaseDataType.UWORD, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinMaxLong(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("max requires 2 arguments", position)
|
||||
val val1 = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val val2 = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = max(val1.number.toInt(), val2.number.toInt())
|
||||
return NumericLiteral(BaseDataType.LONG, result.toDouble(), position)
|
||||
}
|
||||
|
||||
private fun builtinClampUByte(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
if(args.size!=3)
|
||||
throw SyntaxError("clamp requires 3 arguments", position)
|
||||
|
||||
@@ -5,6 +5,8 @@ import prog8.ast.*
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.buildversion.VERSION
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.ast.printAst
|
||||
@@ -176,17 +178,21 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
println("*********** COMPILER AST END *************\n")
|
||||
}
|
||||
|
||||
var symbolTable: SymbolTable
|
||||
|
||||
val (intermediateAst, simplifiedAstDuration2) = measureTimedValue {
|
||||
val intermediateAst = SimplifiedAstMaker(program, args.errors).transform()
|
||||
val stMaker = SymbolTableMaker(intermediateAst, compilationOptions)
|
||||
val symbolTable = stMaker.make()
|
||||
symbolTable = stMaker.make()
|
||||
|
||||
postprocessSimplifiedAst(intermediateAst, symbolTable, compilationOptions, args.errors)
|
||||
args.errors.report()
|
||||
symbolTable = stMaker.make() // need an updated ST because the postprocessing changes stuff
|
||||
|
||||
if (compilationOptions.optimize) {
|
||||
optimizeSimplifiedAst(intermediateAst, compilationOptions, symbolTable, args.errors)
|
||||
args.errors.report()
|
||||
symbolTable = stMaker.make() // need an updated ST because the optimization changes stuff
|
||||
}
|
||||
|
||||
if (args.printAst2) {
|
||||
@@ -204,6 +210,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
createAssemblyDuration = measureTime {
|
||||
if (!createAssemblyAndAssemble(
|
||||
intermediateAst,
|
||||
symbolTable,
|
||||
args.errors,
|
||||
compilationOptions,
|
||||
program.generatedLabelSequenceNumber
|
||||
@@ -450,7 +457,7 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
|
||||
return CompilationOptions(
|
||||
outputType, launcherType,
|
||||
zpType, zpReserved, zpAllowed, floatsEnabled, noSysInit, rombale,
|
||||
compTarget, 0u, 0xffffu
|
||||
compTarget, VERSION, 0u, 0xffffu
|
||||
)
|
||||
}
|
||||
|
||||
@@ -493,6 +500,8 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
||||
}
|
||||
|
||||
removeUnusedCode(program, errors,compilerOptions)
|
||||
program.constantFold(errors, compilerOptions)
|
||||
|
||||
for(numCycles in 0..10000) {
|
||||
// keep optimizing expressions and statements until no more steps remain
|
||||
val optsDone1 = program.simplifyExpressions(errors)
|
||||
@@ -558,6 +567,7 @@ private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOpt
|
||||
}
|
||||
|
||||
private fun createAssemblyAndAssemble(program: PtProgram,
|
||||
symbolTable: SymbolTable,
|
||||
errors: IErrorReporter,
|
||||
compilerOptions: CompilationOptions,
|
||||
lastGeneratedLabelSequenceNr: Int
|
||||
@@ -572,10 +582,6 @@ private fun createAssemblyAndAssemble(program: PtProgram,
|
||||
else
|
||||
throw NotImplementedError("no code generator for cpu ${compilerOptions.compTarget.cpu}")
|
||||
|
||||
// need to make a new symboltable here to capture possible changes made by optimization steps performed earlier!
|
||||
val stMaker = SymbolTableMaker(program, compilerOptions)
|
||||
val symbolTable = stMaker.make()
|
||||
|
||||
val assembly = asmgen.generate(program, symbolTable, compilerOptions, errors)
|
||||
errors.report()
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
checkLongType(identifier)
|
||||
val stmt = identifier.targetStatement(program.builtinFunctions)
|
||||
if(stmt==null) {
|
||||
if(identifier.parent is ArrayIndexedExpression) {
|
||||
@@ -241,6 +240,11 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("word loop variable can only loop over bytes or words", forLoop.position)
|
||||
}
|
||||
|
||||
BaseDataType.LONG -> {
|
||||
if(!iterableDt.elementType().isInteger)
|
||||
errors.err("long loop variable can only loop over integers", forLoop.position)
|
||||
}
|
||||
|
||||
BaseDataType.FLOAT -> {
|
||||
// Looping over float variables is very inefficient because the loopvar is going to
|
||||
// get copied over with new values all the time. We don't support this for now.
|
||||
@@ -363,10 +367,6 @@ internal class AstChecker(private val program: Program,
|
||||
super.visit(label)
|
||||
}
|
||||
|
||||
override fun visit(numLiteral: NumericLiteral) {
|
||||
checkLongType(numLiteral)
|
||||
}
|
||||
|
||||
private fun hasReturnOrExternalJumpOrRts(scope: IStatementContainer): Boolean {
|
||||
class Searcher: IAstVisitor
|
||||
{
|
||||
@@ -813,7 +813,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(addressOf: AddressOf) {
|
||||
checkLongType(addressOf)
|
||||
val variable=addressOf.identifier?.targetVarDecl()
|
||||
if (variable!=null) {
|
||||
if (variable.type == VarDeclType.CONST && addressOf.arrayIndex == null)
|
||||
@@ -862,8 +861,6 @@ internal class AstChecker(private val program: Program,
|
||||
if(decl.names.size>1)
|
||||
throw InternalCompilerException("vardecls with multiple names should have been converted into individual vardecls")
|
||||
|
||||
if(decl.datatype.isLong && decl.type!=VarDeclType.CONST)
|
||||
errors.err("cannot use long type for variables; only for constants", decl.position)
|
||||
if(decl.type==VarDeclType.MEMORY) {
|
||||
if (decl.datatype.isString)
|
||||
errors.err("strings cannot be memory-mapped", decl.position)
|
||||
@@ -949,6 +946,9 @@ internal class AstChecker(private val program: Program,
|
||||
dt.isWordArray ->
|
||||
if(arraySize > 128)
|
||||
err("regular word array length must be 1-128, use split array to get to 256")
|
||||
dt.isLongArray ->
|
||||
if(arraySize > 64)
|
||||
err("long array length must be 1-64")
|
||||
dt.isFloatArray ->
|
||||
if(arraySize > 51)
|
||||
err("float array length must be 1-51")
|
||||
@@ -1052,6 +1052,10 @@ internal class AstChecker(private val program: Program,
|
||||
if (length == 0 || length > 128)
|
||||
err("regular word array length must be 1-128, use split array to get to 256")
|
||||
}
|
||||
decl.datatype.isLongArray -> {
|
||||
if (length == 0 || length > 64)
|
||||
err("long array length must be 1-64")
|
||||
}
|
||||
decl.datatype.isFloatArray -> {
|
||||
if (length == 0 || length > 51)
|
||||
err("float array length must be 1-51")
|
||||
@@ -1092,7 +1096,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
if(decl.datatype.isStructInstance && decl.origin!=VarDeclOrigin.SUBROUTINEPARAM) {
|
||||
if(decl.type==VarDeclType.MEMORY)
|
||||
errors.err("cannot declare memory mapped struct instances, use a pointer and memory allocation call or direct address assignment instead", decl.position)
|
||||
errors.err("struct instances cannot be declared as memory-mapped currently, use a pointer and memory allocation call or direct address assignment instead", decl.position)
|
||||
else
|
||||
errors.err("struct instances cannot be declared directly, use a pointer and memory allocation call or direct address assignment instead", decl.position)
|
||||
}
|
||||
@@ -1269,17 +1273,23 @@ internal class AstChecker(private val program: Program,
|
||||
val arrayspec = ArrayIndex.forArray(array)
|
||||
checkValueTypeAndRangeArray(array.type.getOrUndef(), arrayspec, array)
|
||||
} else {
|
||||
errors.err("undefined array type (multiple element types?)", array.position)
|
||||
errors.err("undefined array type (multiple or incompatible element types?)", array.position)
|
||||
}
|
||||
|
||||
if(array.parent is VarDecl) {
|
||||
if (!array.value.all { it is NumericLiteral || it is AddressOf || (it is FunctionCallExpression && it.target.targetStructDecl()!=null) }) {
|
||||
if (!array.value.all { it is NumericLiteral || it is AddressOf || it is StaticStructInitializer }) {
|
||||
errors.err("initialization value contains non-constant elements", array.value[0].position)
|
||||
}
|
||||
|
||||
if(array.value.any { it is FunctionCallExpression }) {
|
||||
errors.err("it is not yet possible to use struct initializations in an array, you have to do it one by one for now", array.value[0].position)
|
||||
// TODO this is because later in the simplified AST the allocate struct variable is still missing somehow
|
||||
val elementDt = (array.parent as VarDecl).datatype.elementType()
|
||||
if(elementDt.isPointer) {
|
||||
// all elements in the initializer array should be of the same element type
|
||||
array.value.forEach {
|
||||
val valueDt = it.inferType(program).getOrUndef()
|
||||
if(!valueDt.isUnsignedWord && valueDt != elementDt) {
|
||||
errors.err("struct initializer element has invalid type, expected $elementDt or uword but got $valueDt", it.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if(array.parent is ForLoop) {
|
||||
@@ -1323,7 +1333,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
checkLongType(expr)
|
||||
val dt = expr.expression.inferType(program).getOrUndef()
|
||||
if(!dt.isUndefined) {
|
||||
|
||||
@@ -1468,8 +1477,6 @@ internal class AstChecker(private val program: Program,
|
||||
return
|
||||
}
|
||||
|
||||
checkLongType(expr)
|
||||
|
||||
val leftIDt = expr.left.inferType(program)
|
||||
val rightIDt = expr.right.inferType(program)
|
||||
if(!leftIDt.isKnown || !rightIDt.isKnown) {
|
||||
@@ -1515,7 +1522,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(rightDt.isWord) {
|
||||
val shift = expr.right.constValue(program)?.number?.toInt()
|
||||
if(shift==null || shift > 255) {
|
||||
errors.err("shift by a word value not supported, max is a byte", expr.position)
|
||||
errors.err("bit shift value can not be larger than a byte", expr.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1546,8 +1553,13 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("can only use string repeat with a constant number value", expr.left.position)
|
||||
} else if(leftDt.isBool && rightDt.isByte || leftDt.isByte && rightDt.isBool) {
|
||||
// expression with one side BOOL other side (U)BYTE is allowed; bool==byte
|
||||
} else if((expr.operator == "<<" || expr.operator == ">>") && (leftDt.isWord && rightDt.isByte)) {
|
||||
// exception allowed: shifting a word by a byte
|
||||
} else if((expr.operator == "<<" || expr.operator == ">>")
|
||||
&& (leftDt.isByte && rightDt.isByte ||
|
||||
leftDt.isWord && rightDt.isByte ||
|
||||
leftDt.isWord && rightDt.isWord ||
|
||||
leftDt.isLong && rightDt.isByte ||
|
||||
leftDt.isLong && rightDt.isWord)) {
|
||||
// exception allowed: shifting a word by a byte, long by a word or byte
|
||||
} else if((expr.operator in BitwiseOperators) && (leftDt.isInteger && rightDt.isInteger)) {
|
||||
// exception allowed: bitwise operations with any integers
|
||||
} else if((leftDt.isUnsignedWord && rightDt.isString) || (leftDt.isString && rightDt.isUnsignedWord)) {
|
||||
@@ -1608,7 +1620,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(typecast: TypecastExpression) {
|
||||
checkLongType(typecast)
|
||||
if(typecast.type.isPassByRef)
|
||||
errors.err("cannot type cast to string or array type", typecast.position)
|
||||
|
||||
@@ -1665,7 +1676,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(functionCallExpr: FunctionCallExpression) {
|
||||
checkLongType(functionCallExpr)
|
||||
// this function call is (part of) an expression, which should be in a statement somewhere.
|
||||
val stmtOfExpression = findParentNode<Statement>(functionCallExpr)
|
||||
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCallExpr.position}")
|
||||
@@ -1898,36 +1908,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(target is StructDecl) {
|
||||
// it's a static struct inializer, check the values
|
||||
if(args.isNotEmpty()) {
|
||||
args.forEach {
|
||||
if(it is IdentifierReference) {
|
||||
val target = it.targetVarDecl()
|
||||
if(target!=null && target.datatype.isPointer) {
|
||||
errors.err("a pointer variable cannot be used in a static initialization value because its value is only known at runtime (use 0 here, and assign it later manually)", it.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!args.all { it is NumericLiteral || it is AddressOf || (it is TypecastExpression && it.expression is NumericLiteral)})
|
||||
errors.err("initialization value contains non-constant elements", args[0].position)
|
||||
if (target.fields.size != args.size)
|
||||
errors.err("initialization value needs to have same number of values as the struct has fields, or be empty: expected ${target.fields.size} or 0, got ${args.size}", args[0].position)
|
||||
else
|
||||
target.fields.zip(args).withIndex().forEach { (index, fv) ->
|
||||
val (field, value) = fv
|
||||
val valueDt = value.inferType(program)
|
||||
if(valueDt isNotAssignableTo field.first) {
|
||||
errors.err("value #${index+1} has incompatible type $valueDt for field '${field.second}' (${field.first})", value.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO rest?
|
||||
}
|
||||
|
||||
args.forEach{
|
||||
checkLongType(it)
|
||||
if(it.inferType(program).isStructInstance)
|
||||
errors.err("structs can only be passed via a pointer", it.position)
|
||||
}
|
||||
@@ -1940,7 +1921,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||
checkLongType(arrayIndexedExpression)
|
||||
val target = arrayIndexedExpression.plainarrayvar?.targetStatement(program.builtinFunctions)
|
||||
if(target is VarDecl) {
|
||||
if (!target.datatype.isIterable && !target.datatype.isUnsignedWord && !target.datatype.isPointer)
|
||||
@@ -1974,7 +1954,10 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
|
||||
}
|
||||
} else if(target!=null) {
|
||||
throw FatalAstException("target is not a variable")
|
||||
if(target is Subroutine || target is Label )
|
||||
errors.err("cannot array index a subroutine or label directly, use an intermediate uword pointer = &symbol instead", arrayIndexedExpression.position)
|
||||
else
|
||||
errors.err("indexed symbol is not a suitable variable", arrayIndexedExpression.position)
|
||||
}
|
||||
|
||||
if(arrayIndexedExpression.pointerderef!=null) {
|
||||
@@ -2179,17 +2162,8 @@ internal class AstChecker(private val program: Program,
|
||||
else
|
||||
errors.err("unable to determine type of dereferenced pointer expression", deref.position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkLongType(expression: Expression) {
|
||||
if(expression.inferType(program) issimpletype BaseDataType.LONG) {
|
||||
if((expression.parent as? VarDecl)?.type!=VarDeclType.CONST) {
|
||||
if (expression.parent !is RepeatLoop) {
|
||||
if (errors.noErrorForLine(expression.position))
|
||||
errors.err("integer overflow", expression.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visit(deref)
|
||||
}
|
||||
|
||||
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteral) : Boolean {
|
||||
@@ -2282,6 +2256,20 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
return err("invalid word array initialization value ${value.type}, expected $targetDt")
|
||||
}
|
||||
targetDt.isPointerArray -> {
|
||||
val arraySpecSize = arrayspec.constIndex()
|
||||
val arraySize = value.value.size
|
||||
val maxLength = if(targetDt.isSplitWordArray) 256 else 128
|
||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||
if(arraySpecSize>maxLength)
|
||||
return err("array length must be 1-$maxLength")
|
||||
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
|
||||
if (arraySize != expectedSize)
|
||||
return err("array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||
return true
|
||||
}
|
||||
return err("invalid array size, must be 1-$maxLength")
|
||||
}
|
||||
targetDt.isFloatArray -> {
|
||||
// value may be either a single float, or a float arraysize
|
||||
if(value.type istype targetDt) {
|
||||
@@ -2396,6 +2384,7 @@ internal class AstChecker(private val program: Program,
|
||||
else
|
||||
cast.valueOrZero().number
|
||||
}
|
||||
is StaticStructInitializer -> it.structname.hashCode() and 0xffff
|
||||
else -> -9999999
|
||||
}
|
||||
}
|
||||
@@ -2420,7 +2409,7 @@ internal class AstChecker(private val program: Program,
|
||||
else -> throw FatalAstException("invalid type $targetDt")
|
||||
}
|
||||
if (!correct) {
|
||||
if (value.parent is VarDecl && !value.value.all { it is NumericLiteral || it is AddressOf })
|
||||
if (value.parent is VarDecl && !value.value.all { it is NumericLiteral || it is AddressOf || it is StaticStructInitializer })
|
||||
errors.err("initialization value contains non-constant elements", value.value[0].position)
|
||||
else
|
||||
errors.err("array element out of range for type $targetDt", value.position)
|
||||
@@ -2481,7 +2470,7 @@ internal class AstChecker(private val program: Program,
|
||||
else if (targetDatatype.isPointer) {
|
||||
if(sourceDatatype.isPointer) {
|
||||
if(!(sourceDatatype isAssignableTo targetDatatype))
|
||||
errors.err("cannot assign different pointer type, expected $targetDatatype got $sourceDatatype", position)
|
||||
errors.err("cannot assign different pointer type, expected $targetDatatype or uword but got $sourceDatatype", position)
|
||||
} else if(sourceDatatype.isString && targetDatatype.sub?.isByte==true) {
|
||||
// assigning a string to a byte pointer is allowed.
|
||||
} else if(!sourceDatatype.isUnsignedWord && !sourceDatatype.isStructInstance)
|
||||
@@ -2512,11 +2501,40 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(deref: ArrayIndexedPtrDereference) {
|
||||
if(deref.parent is AssignTarget)
|
||||
errors.err("no support for assigning to a array indexed pointer target like this yet. Split the assignment statement by using an intermediate variable.", deref.position)
|
||||
else
|
||||
if(deref.parent !is AssignTarget)
|
||||
errors.err("no support for getting the target value of pointer array indexing like this yet. Split the expression by using an intermediate variable.", deref.position) // this may never occur anymore since more ArrayIndexedPtrDereference got rewritten
|
||||
|
||||
super.visit(deref)
|
||||
}
|
||||
|
||||
override fun visit(initializer: StaticStructInitializer) {
|
||||
val args = initializer.args
|
||||
if(args.isNotEmpty()) {
|
||||
args.forEach {
|
||||
if(it is IdentifierReference) {
|
||||
val target = it.targetVarDecl()
|
||||
if(target!=null && target.datatype.isPointer) {
|
||||
errors.err("a pointer variable cannot be used in a static initialization value because its value is only known at runtime (use 0 here, and assign it later manually)", it.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!args.all { it is NumericLiteral || it is AddressOf || (it is TypecastExpression && it.expression is NumericLiteral)})
|
||||
errors.err("initialization value contains non-constant elements", args[0].position)
|
||||
val struct = initializer.structname.targetStructDecl()
|
||||
if(struct!=null) {
|
||||
require(args.size==struct.fields.size)
|
||||
struct.fields.zip(args).withIndex().forEach { (index, fv) ->
|
||||
val (field, value) = fv
|
||||
val valueDt = value.inferType(program)
|
||||
if(valueDt isNotAssignableTo field.first)
|
||||
errors.err("value #${index+1} has incompatible type $valueDt for field '${field.second}' (${field.first})", value.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.visit(initializer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, errors: IErrorReporter) {
|
||||
|
||||
@@ -165,14 +165,23 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
super.visit(string)
|
||||
}
|
||||
|
||||
override fun visit(functionCallExpr: FunctionCallExpression) = visitFunctionCall(functionCallExpr)
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) = visitFunctionCall(functionCallStatement)
|
||||
override fun visit(functionCallExpr: FunctionCallExpression) {
|
||||
visitFunctionCall(functionCallExpr)
|
||||
super.visit(functionCallExpr)
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||
visitFunctionCall(functionCallStatement)
|
||||
super.visit(functionCallStatement)
|
||||
}
|
||||
|
||||
override fun visit(initializer: StaticStructInitializer) {
|
||||
val fields = initializer.structname.targetStructDecl()!!.fields
|
||||
if(initializer.args.isNotEmpty() && initializer.args.size != fields.size) {
|
||||
val pos = (if(initializer.args.any()) initializer.args[0] else initializer).position
|
||||
invalidNumberOfArgsError(pos, initializer.args.size, fields.map { it.second }, true)
|
||||
val struct = initializer.structname.targetStructDecl()
|
||||
if(struct!=null) {
|
||||
if (initializer.args.isNotEmpty() && initializer.args.size != struct.fields.size) {
|
||||
val pos = (if (initializer.args.any()) initializer.args[0] else initializer).position
|
||||
invalidNumberOfArgsError(pos, initializer.args.size, struct.fields.map { it.second }, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -174,6 +174,8 @@ class AstPreprocessor(val program: Program,
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator==".")
|
||||
return noModifications
|
||||
if(expr.operator=="in") {
|
||||
val containment = ContainmentCheck(expr.left, expr.right, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, containment, parent))
|
||||
@@ -267,7 +269,7 @@ class AstPreprocessor(val program: Program,
|
||||
.filter { it.name !in namesInSub && it.name !in existingAliases }
|
||||
.forEach {
|
||||
if (it.registerOrPair in Cx16VirtualRegisters) {
|
||||
if(it.type.isIntegerOrBool) {
|
||||
if(it.type.isWordOrByteOrBool) {
|
||||
val mappedParamVar = VarDecl.fromParameter(it)
|
||||
mods += IAstModification.InsertFirst(mappedParamVar, subroutine)
|
||||
} else {
|
||||
|
||||
@@ -153,6 +153,8 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator==".")
|
||||
return noModifications
|
||||
if (options.compTarget.name == VMTarget.NAME)
|
||||
return noModifications
|
||||
|
||||
|
||||
@@ -79,18 +79,26 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
||||
val (replaced, cast) = arg1.typecastTo(if(dt1.isUnsignedByte) BaseDataType.UWORD else BaseDataType.WORD, dt1, true)
|
||||
if(replaced)
|
||||
return listOf(IAstModification.ReplaceNode(arg1, cast, functionCallStatement))
|
||||
} else {
|
||||
} else if(dt1.isWord) {
|
||||
if(dt2.isWord)
|
||||
return noModifications
|
||||
val (replaced, cast) = arg2.typecastTo(if(dt2.isUnsignedByte) BaseDataType.UWORD else BaseDataType.WORD, dt2, true)
|
||||
if(replaced)
|
||||
return listOf(IAstModification.ReplaceNode(arg2, cast, functionCallStatement))
|
||||
} else if(dt1.isLong) {
|
||||
if(dt2.isLong)
|
||||
return noModifications
|
||||
val (replaced, cast) = arg2.typecastTo(BaseDataType.LONG, dt2, true)
|
||||
if(replaced)
|
||||
return listOf(IAstModification.ReplaceNode(arg2, cast, functionCallStatement))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator==".")
|
||||
return noModifications
|
||||
if(expr.operator=="<<" || expr.operator==">>") {
|
||||
val shifts = expr.right.constValue(program)
|
||||
if(shifts!=null) {
|
||||
|
||||
@@ -605,11 +605,15 @@ _after:
|
||||
}
|
||||
|
||||
val replacementScope = AnonymousScope(if(conditionVar==null)
|
||||
mutableListOf(ifSt, jumplistArray)
|
||||
mutableListOf(ifSt)
|
||||
else
|
||||
mutableListOf(conditionVar, assignIndex!!, ifSt, jumplistArray)
|
||||
mutableListOf(conditionVar, assignIndex!!, ifSt)
|
||||
, ongoto.position)
|
||||
return listOf(IAstModification.ReplaceNode(ongoto, replacementScope, parent))
|
||||
val declscope = parent.definingScope
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(ongoto, replacementScope, parent),
|
||||
IAstModification.InsertFirst(jumplistArray, declscope)
|
||||
)
|
||||
}
|
||||
|
||||
override fun after(deref: PtrDereference, parent: Node): Iterable<IAstModification> {
|
||||
@@ -658,6 +662,61 @@ _after:
|
||||
override fun after(deref: ArrayIndexedPtrDereference, parent: Node): Iterable<IAstModification> {
|
||||
// get rid of the ArrayIndexedPtrDereference AST node, replace it with other AST nodes that are equivalent
|
||||
|
||||
fun pokeFunc(dt: DataType): Pair<String, DataType?> {
|
||||
return when {
|
||||
dt.isBool -> "pokebool" to null
|
||||
dt.isUnsignedByte -> "poke" to null
|
||||
dt.isSignedByte -> "poke" to DataType.UBYTE
|
||||
dt.isUnsignedWord -> "pokew" to null
|
||||
dt.isSignedWord -> "pokew" to DataType.UWORD
|
||||
dt.isLong -> "pokel" to null
|
||||
dt.isFloat -> "pokef" to null
|
||||
else -> throw FatalAstException("can only deref a numeric or boolean pointer here")
|
||||
}
|
||||
}
|
||||
|
||||
if(parent is AssignTarget) {
|
||||
if(!deref.derefLast) {
|
||||
val assignment = parent.parent as Assignment
|
||||
val field = deref.chain.last()
|
||||
val ptr = deref.chain.dropLast(1)
|
||||
if(field.second==null && ptr.last().second!=null) {
|
||||
val ptrName = ptr.map { it.first }
|
||||
val ptrVar = deref.definingScope.lookup(ptrName) as? VarDecl
|
||||
if(ptrVar!=null && (ptrVar.datatype.isPointer || ptrVar.datatype.isPointerArray)) {
|
||||
val struct = ptrVar.datatype.subType!! as StructDecl
|
||||
val offsetNumber = NumericLiteral.optimalInteger(struct.offsetof(field.first, program.memsizer)!!.toInt(), deref.position)
|
||||
val pointerIdentifier = IdentifierReference(ptrName, deref.position)
|
||||
val address: Expression
|
||||
if(ptrVar.datatype.isPointer) {
|
||||
// pointer[idx].field = value --> pokeXXX(pointer as uword + idx*sizeof(Struct) + offsetof(Struct.field), value)
|
||||
val structSize = ptrVar.datatype.dereference().size(program.memsizer)
|
||||
val pointerAsUword = TypecastExpression(pointerIdentifier, DataType.UWORD, true, deref.position)
|
||||
val idx = ptr.last().second!!.indexExpr
|
||||
val scaledIndex = BinaryExpression(idx, "*", NumericLiteral(BaseDataType.UWORD, structSize.toDouble(), deref.position), deref.position)
|
||||
val structAddr = BinaryExpression(pointerAsUword, "+", scaledIndex, deref.position)
|
||||
address = BinaryExpression(structAddr, "+", offsetNumber, deref.position)
|
||||
}
|
||||
else {
|
||||
// pointerarray[idx].field = value --> pokeXXX(pointerarray[idx] as uword + offsetof(Struct.field), value)
|
||||
val index = ArrayIndexedExpression(pointerIdentifier, null, ptr.last().second!!, deref.position)
|
||||
val pointerAsUword = TypecastExpression(index, DataType.UWORD, true, deref.position)
|
||||
address = BinaryExpression(pointerAsUword, "+", offsetNumber, deref.position)
|
||||
}
|
||||
val (pokeFunc, valueCast) = pokeFunc(parent.inferType(program).getOrUndef())
|
||||
val value = if(valueCast==null) assignment.value else TypecastExpression(assignment.value, valueCast, true, assignment.value.position)
|
||||
val pokeCall = FunctionCallStatement(IdentifierReference(listOf(pokeFunc), assignment.position),
|
||||
mutableListOf(address, value), false, assignment.position)
|
||||
|
||||
if(assignment.isAugmentable)
|
||||
errors.warn("in-place assignment of indexed pointer variable currently is very inefficient, maybe use a temporary pointer variable", assignment.position)
|
||||
return listOf(IAstModification.ReplaceNode(assignment, pokeCall, assignment.parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(deref.chain.last().second!=null && deref.derefLast && deref.chain.dropLast(1).all { it.second==null } ) {
|
||||
|
||||
// parent could be Assigment directly, or a binexpr chained pointer expression (with '.' operator)
|
||||
@@ -710,15 +769,7 @@ _after:
|
||||
val dt = deref.inferType(program).getOrUndef()
|
||||
if(dt.isNumericOrBool) {
|
||||
// if it's something else beside number (like, a struct instance) we don't support rewriting that...
|
||||
val (pokeFunc, cast) =
|
||||
if (dt.isBool) "pokebool" to null
|
||||
else if (dt.isUnsignedByte) "poke" to null
|
||||
else if (dt.isSignedByte) "poke" to DataType.UBYTE
|
||||
else if (dt.isUnsignedWord) "pokew" to null
|
||||
else if (dt.isSignedWord) "pokew" to DataType.UWORD
|
||||
else if (dt.isLong) "pokel" to null
|
||||
else if (dt.isFloat) "pokef" to null
|
||||
else throw FatalAstException("can only deref a numeric or boolean pointer here")
|
||||
val (pokeFunc, cast) = pokeFunc(dt)
|
||||
val indexer = deref.chain.last().second!!
|
||||
val identifier = IdentifierReference(deref.chain.map { it.first }, deref.position)
|
||||
val indexed = ArrayIndexedExpression(identifier, null, indexer, deref.position)
|
||||
@@ -872,4 +923,68 @@ _after:
|
||||
errors.err(error, ifExpr.condition.position)
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(array: ArrayLiteral, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
fun convertArrayIntoStructInitializer(array: ArrayLiteral, struct: ISubType): StaticStructInitializer {
|
||||
val structname = IdentifierReference(struct.scopedNameString.split("."), array.position)
|
||||
return StaticStructInitializer(structname, array.value.toMutableList(), array.position)
|
||||
}
|
||||
|
||||
fun checkNumberOfElements(struct: StructDecl, array: ArrayLiteral): Boolean {
|
||||
val numValues = array.value.size
|
||||
if (numValues>0 && struct.fields.size != numValues) {
|
||||
if (numValues < struct.fields.size) {
|
||||
val missing = struct.fields.drop(numValues).joinToString(", ") { it.second }
|
||||
errors.err("invalid number of field values: expected ${struct.fields.size} or 0 but got ${numValues}, missing: $missing", array.position)
|
||||
} else
|
||||
errors.err("invalid number of field values: expected ${struct.fields.size} or 0 but got ${numValues}", array.position)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if(parent is VarDecl) {
|
||||
if (parent.datatype.isPointerArray && parent.datatype.elementType().subType!=null) {
|
||||
val struct = parent.datatype.elementType().subType as StructDecl
|
||||
val allremovals = mutableListOf<VarDecl>()
|
||||
var noErrors = true
|
||||
var changes = false
|
||||
array.value.withIndex().forEach { (index, elt) ->
|
||||
if(elt is ArrayLiteral) {
|
||||
noErrors = noErrors and checkNumberOfElements(struct, elt)
|
||||
if(noErrors) {
|
||||
array.value[index] = convertArrayIntoStructInitializer(elt, struct)
|
||||
changes = true
|
||||
}
|
||||
} else if(elt is IdentifierReference) {
|
||||
val arrayvar = elt.targetVarDecl()!!.value as ArrayLiteral
|
||||
noErrors = noErrors and checkNumberOfElements(struct, arrayvar)
|
||||
if(noErrors) {
|
||||
array.value[index] = convertArrayIntoStructInitializer(arrayvar, struct)
|
||||
allremovals += elt.targetVarDecl()!!
|
||||
changes = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(changes && noErrors) {
|
||||
array.linkParents(parent)
|
||||
return allremovals.map { IAstModification.Remove(it, it.parent as IStatementContainer) }
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(parent is Assignment) {
|
||||
val targetDt = parent.target.inferType(program).getOrUndef()
|
||||
if(targetDt.isPointer && targetDt.subType!=null) {
|
||||
val struct = targetDt.subType as StructDecl
|
||||
if(checkNumberOfElements(struct, array)) {
|
||||
val initializser = convertArrayIntoStructInitializer(array, struct)
|
||||
return listOf(IAstModification.ReplaceNode(array, initializser, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
@@ -270,7 +270,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
srcTarget.arrayindexed!=null -> target.add(transform(srcTarget.arrayindexed!!))
|
||||
srcTarget.memoryAddress!=null -> target.add(transform(srcTarget.memoryAddress!!))
|
||||
srcTarget.pointerDereference!=null -> target.add(transform(srcTarget.pointerDereference!!))
|
||||
srcTarget.arrayIndexedDereference!=null -> TODO("this array indexed dereference should have been converted to some other ast nodes ${srcTarget.position}")
|
||||
srcTarget.arrayIndexedDereference!=null -> throw FatalAstException("this should have been converted to some other ast nodes ${srcTarget.position}")
|
||||
!srcTarget.void -> throw FatalAstException("invalid AssignTarget")
|
||||
}
|
||||
return target
|
||||
|
||||
@@ -21,6 +21,8 @@ internal fun postprocessSimplifiedAst(
|
||||
private fun processSubtypesIntoStReferences(program: PtProgram, st: SymbolTable) {
|
||||
|
||||
fun getStStruct(subType: ISubType): StStruct {
|
||||
if(subType is StStruct)
|
||||
return subType
|
||||
val stNode = st.lookup(subType.scopedNameString) as? StStruct
|
||||
if(stNode != null)
|
||||
return stNode
|
||||
@@ -28,7 +30,7 @@ private fun processSubtypesIntoStReferences(program: PtProgram, st: SymbolTable)
|
||||
throw FatalAstException("cannot find in ST: ${subType.scopedNameString} $subType")
|
||||
}
|
||||
|
||||
fun fixSubtype(type: DataType) {
|
||||
fun fixSubtypeIntoStType(type: DataType) {
|
||||
if(type.subType!=null && type.subType !is StStruct) {
|
||||
type.subType = getStStruct(type.subType!!)
|
||||
}
|
||||
@@ -36,13 +38,21 @@ private fun processSubtypesIntoStReferences(program: PtProgram, st: SymbolTable)
|
||||
|
||||
fun fixSubtypes(node: PtNode) {
|
||||
when(node) {
|
||||
is IPtVariable -> fixSubtype(node.type)
|
||||
is PtPointerDeref -> fixSubtype(node.type)
|
||||
is PtStructDecl -> node.fields.forEach { fixSubtype(it.first) }
|
||||
is PtAsmSub -> node.returns.forEach { fixSubtype(it.second) }
|
||||
is PtExpression -> fixSubtype(node.type)
|
||||
is PtSubSignature -> node.returns.forEach { fixSubtype(it) }
|
||||
is PtSubroutineParameter -> fixSubtype(node.type)
|
||||
is IPtVariable -> {
|
||||
fixSubtypeIntoStType(node.type)
|
||||
// if it's an array, fix the subtypes of its elements as well
|
||||
if(node.type.isArray && node is PtVariable) {
|
||||
(node.value as? PtArray)?.let {array ->
|
||||
array.children.forEach { fixSubtypes(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
is PtPointerDeref -> fixSubtypeIntoStType(node.type)
|
||||
is PtStructDecl -> node.fields.forEach { fixSubtypeIntoStType(it.first) }
|
||||
is PtAsmSub -> node.returns.forEach { fixSubtypeIntoStType(it.second) }
|
||||
is PtExpression -> fixSubtypeIntoStType(node.type)
|
||||
is PtSubSignature -> node.returns.forEach { fixSubtypeIntoStType(it) }
|
||||
is PtSubroutineParameter -> fixSubtypeIntoStType(node.type)
|
||||
else -> { /* has no datatype */ }
|
||||
}
|
||||
}
|
||||
@@ -162,7 +172,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
|
||||
is PtAddressOf -> value.arrayIndexExpr == null || notComplex(value.arrayIndexExpr!!)
|
||||
is PtBuiltinFunctionCall -> {
|
||||
when (value.name) {
|
||||
in arrayOf("msb", "lsb", "msw", "lsw", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> value.args.all { notComplex(it) }
|
||||
in arrayOf("msb", "lsb", "msw", "lsw", "mkword", "mklong", "mklong2", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> value.args.all { notComplex(it) }
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
@@ -175,7 +185,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
|
||||
is PtNumber,
|
||||
is PtRange,
|
||||
is PtString -> true
|
||||
is PtIdentifier -> true // actually PtIdentifier IS "complex" this time (it's a variable that might change) but it's kinda annoying to give a warning message for this very common case
|
||||
// note that PtIdentifier als is "complex" this time (it's a variable that might change)
|
||||
else -> false
|
||||
}
|
||||
|
||||
@@ -192,7 +202,6 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
|
||||
}
|
||||
|
||||
// complex return value, need to store it before calling the defer block
|
||||
errors.warn("using defer with nontrivial return value(s) incurs stack overhead", ret.children.first { !notComplex(it as PtExpression)}.position)
|
||||
val pushAndPopCalls = ret.children.map { makePushPopFunctionCalls(it as PtExpression) }
|
||||
val pushCalls = pushAndPopCalls.map { it.first }.reversed() // push in reverse order
|
||||
val popCalls = pushAndPopCalls.map { it.second }
|
||||
|
||||
@@ -384,4 +384,25 @@ internal class StatementReorderer(
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(assign, strcopy, assign.parent))
|
||||
}
|
||||
|
||||
override fun after(deref: ArrayIndexedPtrDereference, parent: Node): Iterable<IAstModification> {
|
||||
if(parent is AssignTarget) {
|
||||
val zeroIndexer = deref.chain.firstOrNull { it.second?.constIndex()==0 }
|
||||
if(zeroIndexer!=null) {
|
||||
val target = deref.definingScope.lookup(listOf(zeroIndexer.first))
|
||||
if(target is VarDecl && target.datatype.isPointer) {
|
||||
val position = deref.chain.indexOf(zeroIndexer)
|
||||
val rest = deref.chain.drop(position + 1)
|
||||
if (rest.size == 1 && rest[0].second == null) {
|
||||
// pointer[0]^^.field = xxx --> pointer^^.field = xxx
|
||||
val noindexer = zeroIndexer.first to null
|
||||
val newchain = deref.chain.take(position) + noindexer + rest
|
||||
val newDeref = PtrDereference(newchain.map { it.first }, false, deref.position)
|
||||
return listOf(IAstModification.ReplaceNode(deref, newDeref, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,9 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(expr.operator==".")
|
||||
return noModifications
|
||||
|
||||
val leftDt = expr.left.inferType(program)
|
||||
val rightDt = expr.right.inferType(program)
|
||||
val leftCv = expr.left.constValue(program)
|
||||
@@ -170,7 +173,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
|
||||
|
||||
if((expr.operator!="<<" && expr.operator!=">>") || !leftDt.isWords || !rightDt.isBytes) {
|
||||
if((expr.operator!="<<" && expr.operator!=">>") || !leftDt.isInteger || !rightDt.isBytes) {
|
||||
// determine common datatype and add typecast as required to make left and right equal types
|
||||
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.getOrUndef(), rightDt.getOrUndef(), expr.left, expr.right)
|
||||
if(toFix!=null) {
|
||||
@@ -627,9 +630,12 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
return if(initializer.args.isEmpty())
|
||||
noModifications
|
||||
else {
|
||||
val struct = initializer.structname.targetStructDecl()!!
|
||||
val paramsPossibleDatatypes = struct.fields.map { listOf(it.first) }
|
||||
fixupArgumentList(paramsPossibleDatatypes, initializer.args, initializer)
|
||||
val struct = initializer.structname.targetStructDecl()
|
||||
if(struct!=null) {
|
||||
val paramsPossibleDatatypes = struct.fields.map { listOf(it.first) }
|
||||
fixupArgumentList(paramsPossibleDatatypes, initializer.args, initializer)
|
||||
}
|
||||
else noModifications
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ internal fun makePushPopFunctionCalls(value: PtExpression): Pair<PtFunctionCall,
|
||||
var pushTypecast: DataType? = null
|
||||
var pushWord = false
|
||||
var pushFloat = false
|
||||
var pushLong = false
|
||||
|
||||
when {
|
||||
value.type.isBool -> {
|
||||
@@ -29,13 +30,14 @@ internal fun makePushPopFunctionCalls(value: PtExpression): Pair<PtFunctionCall,
|
||||
}
|
||||
value.type.isUnsignedByte -> {}
|
||||
value.type.isUnsignedWord -> pushWord = true
|
||||
value.type.isLong -> pushLong = true
|
||||
value.type.isPassByRef -> pushWord = true
|
||||
value.type.isFloat -> pushFloat = true
|
||||
else -> throw FatalAstException("unsupported return value type ${value.type} with defer")
|
||||
}
|
||||
|
||||
val pushFunc = if(pushFloat) "floats.push" else if(pushWord) "sys.pushw" else "sys.push"
|
||||
val popFunc = if(pushFloat) "floats.pop" else if(pushWord) "sys.popw" else "sys.pop"
|
||||
val pushFunc = if(pushFloat) "floats.push" else if(pushWord) "sys.pushw" else if (pushLong) "sys.pushl" else "sys.push"
|
||||
val popFunc = if(pushFloat) "floats.pop" else if(pushWord) "sys.popw" else if(pushLong) "sys.popl" else "sys.pop"
|
||||
val pushCall = PtFunctionCall(pushFunc, true, DataType.UNDEFINED, value.position)
|
||||
if(pushTypecast!=null) {
|
||||
val typecast = PtTypeCast(pushTypecast, value.position).also {
|
||||
@@ -47,7 +49,7 @@ internal fun makePushPopFunctionCalls(value: PtExpression): Pair<PtFunctionCall,
|
||||
}
|
||||
val popCall = if(popTypecast!=null) {
|
||||
PtTypeCast(popTypecast, value.position).also {
|
||||
val returnDt = if(pushWord) DataType.UWORD else DataType.UBYTE
|
||||
val returnDt = if(pushWord) DataType.UWORD else if(pushLong) DataType.LONG else if(pushFloat) DataType.FLOAT else DataType.UBYTE
|
||||
it.add(PtFunctionCall(popFunc, false, returnDt, value.position))
|
||||
}
|
||||
} else
|
||||
|
||||
@@ -49,9 +49,11 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
val constValue = decl.value!!.constValue(program)!!
|
||||
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
|
||||
} else {
|
||||
// don't make it signed if it was unsigned and vice versa
|
||||
if(valueDt.isSigned && decl.datatype.isUnsigned ||
|
||||
valueDt.isUnsigned && decl.datatype.isSigned) {
|
||||
// don't make it signed if it was unsigned and vice versa, except when it is a long const declaration
|
||||
if(!decl.datatype.isLong &&
|
||||
(valueDt.isSigned && decl.datatype.isUnsigned ||
|
||||
valueDt.isUnsigned && decl.datatype.isSigned))
|
||||
{
|
||||
val constValue = decl.value!!.constValue(program)!!
|
||||
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
|
||||
} else {
|
||||
@@ -124,8 +126,12 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||
return if(parent is IStatementContainer)
|
||||
listOf(ScopeFlatten(scope, parent as IStatementContainer))
|
||||
else
|
||||
else {
|
||||
if(scope.statements.any {it is VarDecl}) {
|
||||
throw FatalAstException("there are leftover vardecls in the nested scope at ${scope.position}, they should have been moved/placed in the declaration scope (subroutine) by now")
|
||||
}
|
||||
noModifications
|
||||
}
|
||||
}
|
||||
|
||||
private class ScopeFlatten(val scope: AnonymousScope, val into: IStatementContainer) : IAstModification {
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import prog8.ast.Program
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.PROG8_CONTAINER_MODULES
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.source.SourceCode
|
||||
import prog8.compiler.ModuleImporter
|
||||
@@ -49,7 +50,7 @@ class TestModuleImporter: FunSpec({
|
||||
withClue(".file should point to specified path") {
|
||||
error1.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
||||
}
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
val error2 = importer.importMainModule(srcPathAbs).getErrorOrElse { error("should have import error") }
|
||||
withClue(".file should be normalized") {
|
||||
"${error2.file}" shouldBe "${error2.file.normalize()}"
|
||||
@@ -57,7 +58,7 @@ class TestModuleImporter: FunSpec({
|
||||
withClue(".file should point to specified path") {
|
||||
error2.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
||||
}
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
}
|
||||
|
||||
test("testDirectory") {
|
||||
@@ -75,7 +76,7 @@ class TestModuleImporter: FunSpec({
|
||||
it.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
||||
}
|
||||
}
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
|
||||
shouldThrow<FileSystemException> { importer.importMainModule(srcPathAbs) }
|
||||
.let {
|
||||
@@ -86,7 +87,7 @@ class TestModuleImporter: FunSpec({
|
||||
it.file.absolutePath shouldBe "${srcPathAbs.normalize()}"
|
||||
}
|
||||
}
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +102,7 @@ class TestModuleImporter: FunSpec({
|
||||
val path = assumeReadableFile(searchIn[0], fileName)
|
||||
|
||||
val module = importer.importMainModule(path.absolute()).getOrElse { throw it }
|
||||
program.modules.size shouldBe 2
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size+1
|
||||
module shouldBeIn program.modules
|
||||
module.program shouldBe program
|
||||
}
|
||||
@@ -118,7 +119,7 @@ class TestModuleImporter: FunSpec({
|
||||
}
|
||||
|
||||
val module = importer.importMainModule(path).getOrElse { throw it }
|
||||
program.modules.size shouldBe 2
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size+1
|
||||
module shouldBeIn program.modules
|
||||
module.program shouldBe program
|
||||
}
|
||||
@@ -131,7 +132,7 @@ class TestModuleImporter: FunSpec({
|
||||
assumeReadableFile(searchIn, path)
|
||||
|
||||
val module = importer.importMainModule(path).getOrElse { throw it }
|
||||
program.modules.size shouldBe 2
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size+1
|
||||
module shouldBeIn program.modules
|
||||
module.program shouldBe program
|
||||
}
|
||||
@@ -152,7 +153,7 @@ class TestModuleImporter: FunSpec({
|
||||
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
|
||||
}
|
||||
}
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,9 +171,10 @@ class TestModuleImporter: FunSpec({
|
||||
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
|
||||
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 4 }
|
||||
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
|
||||
}
|
||||
}
|
||||
}
|
||||
withClue("imported module with error in it should not be present") { program.modules.size shouldBe 1 }
|
||||
withClue("imported module with error in it should not be present") { program.modules.size shouldBe PROG8_CONTAINER_MODULES.size }
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
program.modules[0].name shouldBe INTERNED_STRINGS_MODULENAME
|
||||
}
|
||||
}
|
||||
@@ -203,14 +205,14 @@ class TestModuleImporter: FunSpec({
|
||||
withClue(count[n] + " call / NO .p8 extension") { errors.noErrors() shouldBe false }
|
||||
errors.errors.single() shouldContain "0:0: no module found with name i_do_not_exist"
|
||||
errors.report()
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
|
||||
val result2 = importer.importImplicitLibraryModule(filenameWithExt)
|
||||
withClue(count[n] + " call / with .p8 extension") { result2 shouldBe null }
|
||||
withClue(count[n] + " call / with .p8 extension") { importer.errors.noErrors() shouldBe false }
|
||||
errors.errors.single() shouldContain "0:0: no module found with name i_do_not_exist.p8"
|
||||
errors.report()
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,7 +234,7 @@ class TestModuleImporter: FunSpec({
|
||||
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
|
||||
}
|
||||
}
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +256,7 @@ class TestModuleImporter: FunSpec({
|
||||
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
|
||||
}
|
||||
}
|
||||
withClue("imported module with error in it should not be present") { program.modules.size shouldBe 1 }
|
||||
withClue("imported module with error in it should not be present") { program.modules.size shouldBe PROG8_CONTAINER_MODULES.size }
|
||||
program.modules[0].name shouldBe INTERNED_STRINGS_MODULENAME
|
||||
importer.errors.report()
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ class TestCompilerOnExamplesC64: FunSpec({
|
||||
"library/main",
|
||||
"plasma",
|
||||
"rasterbars",
|
||||
"simplemultiplexer",
|
||||
"sprites",
|
||||
"starfield",
|
||||
"tehtriz",
|
||||
@@ -149,6 +150,7 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
"bdmusic",
|
||||
"bobs",
|
||||
"bubbleuniverse",
|
||||
"charfade",
|
||||
"charsets",
|
||||
"circles",
|
||||
"cobramk3-gfx",
|
||||
@@ -223,6 +225,7 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
"textelite",
|
||||
"pointers/animalgame",
|
||||
"pointers/binarytree",
|
||||
"pointers/hashtable",
|
||||
"pointers/sortedlist",
|
||||
"pointers/sorting"
|
||||
),
|
||||
@@ -253,6 +256,7 @@ class TestCompilerOnExamplesVirtual: FunSpec({
|
||||
"sincos",
|
||||
"pointers/animalgame",
|
||||
"pointers/binarytree",
|
||||
"pointers/hashtable",
|
||||
"pointers/sortedlist",
|
||||
"pointers/fountain-virtual",
|
||||
"pointers/sorting"
|
||||
|
||||
@@ -21,6 +21,7 @@ class TestGoldenRam: FunSpec({
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = VMTarget(),
|
||||
compilerVersion="99.99",
|
||||
loadAddress = 999u,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ class TestLaunchEmu: FunSpec({
|
||||
val target = VMTarget()
|
||||
val tmpfile = kotlin.io.path.createTempFile(suffix=".p8ir")
|
||||
tmpfile.writeText("""<?xml version="1.0" encoding="utf-8"?>
|
||||
<PROGRAM NAME="test">
|
||||
<PROGRAM NAME="test" COMPILERVERSION="99.99">
|
||||
<OPTIONS>
|
||||
</OPTIONS>
|
||||
|
||||
|
||||
@@ -386,9 +386,6 @@ class TestMemory: FunSpec({
|
||||
shouldThrow<IllegalArgumentException> {
|
||||
target.memorySize(BaseDataType.UNDEFINED)
|
||||
}
|
||||
shouldThrow<NoSuchElementException> {
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.LONG), 10)
|
||||
}
|
||||
target.memorySize(BaseDataType.BOOL) shouldBe 1
|
||||
target.memorySize(BaseDataType.BYTE) shouldBe 1
|
||||
target.memorySize(BaseDataType.WORD) shouldBe 2
|
||||
@@ -397,6 +394,7 @@ class TestMemory: FunSpec({
|
||||
|
||||
target.memorySize(DataType.BOOL, null) shouldBe 1
|
||||
target.memorySize(DataType.WORD, null) shouldBe 2
|
||||
target.memorySize(DataType.LONG, null) shouldBe 4
|
||||
target.memorySize(DataType.FLOAT, null) shouldBe target.FLOAT_MEM_SIZE
|
||||
|
||||
target.memorySize(DataType.STR, null) shouldBe 2
|
||||
@@ -409,6 +407,7 @@ class TestMemory: FunSpec({
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.BYTE), 10) shouldBe 10
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.WORD), 10) shouldBe 20
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.UWORD), 10) shouldBe 20
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.LONG), 10) shouldBe 40
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.FLOAT), 10) shouldBe 10*target.FLOAT_MEM_SIZE
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.WORD, true), 10) shouldBe 20
|
||||
target.memorySize(DataType.arrayFor(BaseDataType.UWORD, true), 10) shouldBe 20
|
||||
|
||||
+194
-145
@@ -3,7 +3,6 @@ package prog8tests.compiler
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.engine.spec.tempdir
|
||||
import io.kotest.matchers.comparables.shouldBeGreaterThanOrEqualTo
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
@@ -764,7 +763,6 @@ main {
|
||||
test("address-of struct fields") {
|
||||
val src="""
|
||||
%import floats
|
||||
%import textio
|
||||
|
||||
main {
|
||||
struct List {
|
||||
@@ -1027,118 +1025,6 @@ main {
|
||||
st[8] shouldBe instanceOf<Assignment>()
|
||||
}
|
||||
|
||||
test("indexing pointers with index 0 is just a direct pointer dereference except when followed by a struct field lookup") {
|
||||
val src="""
|
||||
%import floats
|
||||
main {
|
||||
struct List {
|
||||
^^uword s
|
||||
ubyte n
|
||||
}
|
||||
sub start() {
|
||||
^^List l1 = ^^List : []
|
||||
^^word @shared wptr
|
||||
^^float @shared fptr
|
||||
float f1,f2
|
||||
|
||||
cx16.r0 = l1.s^^
|
||||
cx16.r1 = l1^^.s^^
|
||||
cx16.r2 = l1.s^^
|
||||
cx16.r3 = l1.s[0]
|
||||
cx16.r4 = l1^^.s[0]
|
||||
|
||||
l1.s^^ = 4242
|
||||
l1^^.s^^ = 4242
|
||||
l1.s^^ = 4242
|
||||
l1.s[0] = 4242
|
||||
;; l1^^.s[0] = 4242 ; TODO fix parse syntax error
|
||||
|
||||
cx16.r0s = wptr[0]
|
||||
cx16.r1s = wptr^^
|
||||
wptr^^ = 4242
|
||||
wptr[0] = 4242
|
||||
|
||||
f1 = fptr^^
|
||||
f2 = fptr[0]
|
||||
fptr^^ = 1.234
|
||||
fptr[0] = 1.234
|
||||
|
||||
; not changed to dereference:
|
||||
cx16.r0L = l1[0].n
|
||||
cx16.r1L = l1[1].n
|
||||
}
|
||||
}"""
|
||||
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 30
|
||||
val dr0 = (st[10] as Assignment).value as PtrDereference
|
||||
val dr1 = (st[11] as Assignment).value as PtrDereference
|
||||
val dr2 = (st[12] as Assignment).value as PtrDereference
|
||||
val dr3 = (st[13] as Assignment).value as PtrDereference
|
||||
val dr4 = (st[14] as Assignment).value as PtrDereference
|
||||
|
||||
val dr5 = (st[15] as Assignment).target.pointerDereference!!
|
||||
val dr6 = (st[16] as Assignment).target.pointerDereference!!
|
||||
val dr7 = (st[17] as Assignment).target.pointerDereference!!
|
||||
val dr8 = (st[18] as Assignment).target.pointerDereference!!
|
||||
|
||||
val dr9 = (st[19] as Assignment).value as FunctionCallExpression
|
||||
val dr10 = (st[20] as Assignment).value as PtrDereference
|
||||
val dr11 = (st[21] as Assignment).target.pointerDereference!!
|
||||
(st[22] as FunctionCallStatement).target.nameInSource shouldBe listOf("pokew")
|
||||
|
||||
val dr13 = (st[23] as Assignment).value as PtrDereference
|
||||
((st[24] as Assignment).value as FunctionCallExpression).target.nameInSource shouldBe listOf("peekf")
|
||||
val dr15 = (st[25] as Assignment).target.pointerDereference!!
|
||||
(st[26] as FunctionCallStatement).target.nameInSource shouldBe listOf("pokef")
|
||||
|
||||
dr0.chain shouldBe listOf("l1", "s")
|
||||
dr0.derefLast shouldBe true
|
||||
dr1.chain shouldBe listOf("l1", "s")
|
||||
dr1.derefLast shouldBe true
|
||||
dr2.chain shouldBe listOf("l1", "s")
|
||||
dr2.derefLast shouldBe true
|
||||
dr3.chain shouldBe listOf("l1", "s")
|
||||
dr3.derefLast shouldBe true
|
||||
dr4.chain shouldBe listOf("l1", "s")
|
||||
dr4.derefLast shouldBe true
|
||||
|
||||
dr5.chain shouldBe listOf("l1", "s")
|
||||
dr5.derefLast shouldBe true
|
||||
dr6.chain shouldBe listOf("l1", "s")
|
||||
dr6.derefLast shouldBe true
|
||||
dr7.chain shouldBe listOf("l1", "s")
|
||||
dr7.derefLast shouldBe true
|
||||
dr8.chain shouldBe listOf("l1", "s")
|
||||
dr8.derefLast shouldBe true
|
||||
|
||||
dr9.target.nameInSource shouldBe listOf("peekw")
|
||||
dr10.chain shouldBe listOf("wptr")
|
||||
dr10.derefLast shouldBe true
|
||||
dr11.chain shouldBe listOf("wptr")
|
||||
dr11.derefLast shouldBe true
|
||||
|
||||
dr13.chain shouldBe listOf("fptr")
|
||||
dr13.derefLast shouldBe true
|
||||
dr15.chain shouldBe listOf("fptr")
|
||||
dr15.derefLast shouldBe true
|
||||
|
||||
val list0 = (st[27] as Assignment).value as BinaryExpression
|
||||
val list1 = (st[28] as Assignment).value as BinaryExpression
|
||||
|
||||
list0.operator shouldBe "."
|
||||
(list0.right as IdentifierReference).nameInSource shouldBe listOf("n")
|
||||
val list0left = list0.left as ArrayIndexedExpression
|
||||
list0left.plainarrayvar!!.nameInSource shouldBe listOf("l1")
|
||||
list0left.indexer.constIndex() shouldBe 0
|
||||
list1.operator shouldBe "."
|
||||
(list1.right as IdentifierReference).nameInSource shouldBe listOf("n")
|
||||
val list1left = list0.left as ArrayIndexedExpression
|
||||
list1left.plainarrayvar!!.nameInSource shouldBe listOf("l1")
|
||||
list1left.indexer.constIndex() shouldBe 0
|
||||
}
|
||||
|
||||
test("indexing pointers to structs") {
|
||||
val src="""
|
||||
%import floats
|
||||
@@ -1458,33 +1344,32 @@ other {
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("a.b.c[i]^^.value = X where pointer is struct gives good error message") {
|
||||
test("support for assigning to indexed pointers") {
|
||||
val src="""
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
other.foo.listarray[3]^^.value = cx16.r0
|
||||
other.foo()
|
||||
}
|
||||
}
|
||||
sprptr[2]^^.y = 99
|
||||
pokew(sprptr as uword + (sizeof(Sprite) as uword)*2 + offsetof(Sprite.y), 99)
|
||||
sprptr[cx16.r0L]^^.y = 99
|
||||
pokew(sprptr as uword + (sizeof(Sprite) as uword)*cx16.r0L + offsetof(Sprite.y), 99)
|
||||
|
||||
other {
|
||||
sub foo() {
|
||||
struct List {
|
||||
bool b
|
||||
uword value
|
||||
}
|
||||
|
||||
^^List[10] listarray
|
||||
listarray[3]^^.value = cx16.r0
|
||||
listarray[3]^^ = 999 ; cannot assign word value to struct instance
|
||||
sprites[2]^^.y = 99
|
||||
pokew(sprites[2] as uword + offsetof(Sprite.y), 99)
|
||||
sprites[cx16.r0L]^^.y = 99
|
||||
pokew(sprites[cx16.r0L] as uword + offsetof(Sprite.y), 99)
|
||||
}
|
||||
|
||||
struct Sprite {
|
||||
ubyte x
|
||||
uword y
|
||||
}
|
||||
|
||||
^^Sprite[4] @shared sprites
|
||||
^^Sprite @shared sprptr
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), false, src, outputDir, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBeGreaterThanOrEqualTo 3
|
||||
errors.errors[0] shouldContain "no support for"
|
||||
errors.errors[1] shouldContain "no support for"
|
||||
errors.errors[2] shouldContain "assigning this value to struct instance not supported"
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
xtest("array indexed assignment parses with and without explicit dereference after struct pointer [IGNORED because it's a parser error right now]") {
|
||||
@@ -1572,7 +1457,6 @@ other {
|
||||
test("passing arrays to subroutines via typed pointer parameters") {
|
||||
val src="""
|
||||
%import floats
|
||||
%import textio
|
||||
|
||||
main {
|
||||
struct Node {
|
||||
@@ -1731,7 +1615,34 @@ main {
|
||||
compileText(Cx16Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("type error for invalid bool field initializer") {
|
||||
test("struct initializers in array") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
main {
|
||||
struct Node {
|
||||
ubyte id
|
||||
str name
|
||||
uword array
|
||||
bool flag
|
||||
float speed
|
||||
}
|
||||
|
||||
sub start() {
|
||||
^^Node[] @shared nodes = [
|
||||
^^Node:[1,"one", 1000, true, 1.111 ],
|
||||
^^Node:[2,"two", 2000, false, 2.222 ],
|
||||
^^Node:[3,"three", 3000, true, 3.333 ],
|
||||
^^Node:[],
|
||||
^^Node:[],
|
||||
^^Node:[],
|
||||
]
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("type error for invalid field initializer") {
|
||||
val src="""
|
||||
main {
|
||||
struct Enemy {
|
||||
@@ -1743,9 +1654,9 @@ main {
|
||||
sub start() {
|
||||
^^Enemy @shared e1 = ^^Enemy: []
|
||||
^^Enemy @shared e2 = ^^Enemy: [1,2,3,true]
|
||||
^^Enemy @shared e3 = ^^Enemy: [1,2,3,4] ; TODO type error for the boolean
|
||||
^^Enemy @shared e4 = ^^Enemy: [1,2,3,4.555] ; TODO type error for the boolean
|
||||
|
||||
^^Enemy @shared e3 = ^^Enemy: [1,2,3,4]
|
||||
^^Enemy @shared e4 = ^^Enemy: [1,2,3,4.444]
|
||||
|
||||
e3.elite = 99
|
||||
e4.elite = 3.444
|
||||
}
|
||||
@@ -1753,9 +1664,78 @@ main {
|
||||
|
||||
val errors=ErrorReporterForTests()
|
||||
compileText(VMTarget(), false, src, outputDir, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "doesn't match target type"
|
||||
errors.errors[1] shouldContain "doesn't match target type"
|
||||
errors.errors.size shouldBe 4
|
||||
errors.errors[0] shouldContain "value #4 has incompatible type"
|
||||
errors.errors[1] shouldContain "value #4 has incompatible type"
|
||||
errors.errors[2] shouldContain "doesn't match target type"
|
||||
errors.errors[3] shouldContain "doesn't match target type"
|
||||
}
|
||||
|
||||
test("long and short form struct initializers") {
|
||||
val src="""
|
||||
%option enable_floats
|
||||
|
||||
main {
|
||||
struct Node {
|
||||
ubyte id
|
||||
str name
|
||||
uword array
|
||||
bool flag
|
||||
float perc
|
||||
}
|
||||
|
||||
sub start() {
|
||||
^^Node[] @shared nodeswithtype = [
|
||||
^^Node: [1,"one", 1000, true, 1.111],
|
||||
^^Node: [],
|
||||
]
|
||||
|
||||
^^Node[] @shared nodeswithout = [
|
||||
[2,"two", 2000, false, 2.222],
|
||||
[],
|
||||
]
|
||||
|
||||
^^Node @shared nptrwithtype = ^^Node : [1, "one", 1000, false, 3.333]
|
||||
^^Node @shared nptrwithouttype = [1, "one", 1000, false, 3.333]
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("type error for wrong type in pointer array and assignment") {
|
||||
val src="""
|
||||
main {
|
||||
struct Node {
|
||||
ubyte id
|
||||
}
|
||||
struct Foobar {
|
||||
bool thing
|
||||
}
|
||||
|
||||
sub start() {
|
||||
^^Node[] onlynodes = [
|
||||
^^Node: [],
|
||||
^^Foobar: []
|
||||
]
|
||||
|
||||
uword multipleok = [
|
||||
^^Node: [],
|
||||
^^Foobar: []
|
||||
]
|
||||
|
||||
^^Node node = ^^Foobar: []
|
||||
}
|
||||
}"""
|
||||
val errors=ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, outputDir, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 6
|
||||
errors.errors[0] shouldContain "11:30: initialization value for pointer array"
|
||||
errors.errors[1] shouldContain "11:30: undefined array type" // a bit redundant but can't be helped
|
||||
errors.errors[2] shouldContain "13:13: struct initializer element has invalid type"
|
||||
errors.errors[3] shouldContain "16:28: invalid assignment value"
|
||||
errors.errors[4] shouldContain "16:28: undefined array type"
|
||||
errors.errors[5] shouldContain "21:23: cannot assign different pointer type"
|
||||
}
|
||||
|
||||
test("local and global struct pointer qualified name lookups") {
|
||||
@@ -1982,8 +1962,8 @@ main {
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), false, src, outputDir, errors = errors, writeAssembly = false) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "7:24: cannot assign different pointer type, expected ^^uword got ^^ubyte"
|
||||
errors.errors[1] shouldContain "10:24: cannot assign different pointer type, expected ^^ubyte got ^^uword"
|
||||
errors.errors[0] shouldContain "7:24: cannot assign different pointer type, expected ^^uword or uword but got ^^ubyte"
|
||||
errors.errors[1] shouldContain "10:24: cannot assign different pointer type, expected ^^ubyte or uword but got ^^uword"
|
||||
}
|
||||
|
||||
test("passing nosplit array of structpointers to a subroutine in various forms should be param type ptr to struct") {
|
||||
@@ -2456,4 +2436,73 @@ structdefs {
|
||||
a5t.pointerDereference!!.chain shouldBe listOf("structdefs", "element", "value2")
|
||||
a6t.pointerDereference!!.chain shouldBe listOf("structdefs", "element", "value2")
|
||||
}
|
||||
|
||||
test("assigning field with same name should not confuse compiler") {
|
||||
val src="""
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
ubyte @shared ok = sprites[2].y ; this one is fine...
|
||||
ubyte @shared y = sprites[2].y ; this used to crash
|
||||
}
|
||||
|
||||
struct Sprite {
|
||||
uword x
|
||||
ubyte y
|
||||
}
|
||||
|
||||
^^Sprite[4] @shared sprites
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("0-indexed optimizations") {
|
||||
val src="""
|
||||
main {
|
||||
struct Sprite {
|
||||
uword x
|
||||
ubyte y
|
||||
}
|
||||
|
||||
^^Sprite[4] @shared sprites
|
||||
^^Sprite @shared sprptr
|
||||
|
||||
sub start() {
|
||||
sprptr.y = 99
|
||||
sprptr[0]^^.y = 99
|
||||
;; sprites[0]^^.y = 99 ; no change here. TODO: this syntax doesn't compile yet...
|
||||
cx16.r0 = &sprptr[0]
|
||||
|
||||
cx16.r2L = sprptr.y
|
||||
cx16.r0L = sprptr[0].y
|
||||
cx16.r1L = sprites[0].y ; no change here, need first array element
|
||||
cx16.r0 = sprites[0] ; no change here, need first array element
|
||||
cx16.r0 = sprites[0] ; no change here, need first array element
|
||||
}
|
||||
}"""
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 8
|
||||
val a1 = st[0] as Assignment
|
||||
val a2 = st[1] as Assignment
|
||||
val a3 = st[2] as Assignment
|
||||
val a4 = st[3] as Assignment
|
||||
val a5 = st[4] as Assignment
|
||||
val a6 = st[5] as Assignment
|
||||
val a7 = st[6] as Assignment
|
||||
|
||||
a1.target.arrayIndexedDereference shouldBe null
|
||||
a1.target.pointerDereference!!.chain shouldBe listOf("sprptr", "y")
|
||||
a2.target.arrayIndexedDereference shouldBe null
|
||||
a2.target.pointerDereference!!.chain shouldBe listOf("sprptr", "y")
|
||||
|
||||
(a3.value as? AddressOf)?.identifier?.nameInSource shouldBe listOf("sprptr")
|
||||
(a4.value as? PtrDereference)?.chain shouldBe listOf("sprptr", "y")
|
||||
(a5.value as? PtrDereference)?.chain shouldBe listOf("sprptr", "y")
|
||||
val be6 = a6.value as BinaryExpression // this one is an actual array and we need the first element so no change here
|
||||
be6.operator shouldBe "."
|
||||
be6.left shouldBe instanceOf<ArrayIndexedExpression>()
|
||||
be6.right shouldBe instanceOf<IdentifierReference>()
|
||||
(a7.value as? ArrayIndexedExpression)?.indexer?.constIndex() shouldBe 0
|
||||
}
|
||||
})
|
||||
@@ -240,6 +240,33 @@ main {
|
||||
errors.errors[1] shouldContain "9:25: invalid number of arguments"
|
||||
}
|
||||
|
||||
test("invalid number of args check on normal subroutine (in expression)") {
|
||||
val text="""
|
||||
main {
|
||||
sub thing(ubyte a1, ubyte a2) -> ubyte {
|
||||
return 42
|
||||
}
|
||||
|
||||
sub wrapper(ubyte value) {
|
||||
value++
|
||||
}
|
||||
|
||||
sub start() {
|
||||
wrapper(thing(1))
|
||||
wrapper(thing(1,2))
|
||||
wrapper(thing(1,2,3))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, outputDir, writeAssembly = false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "12:33: invalid number of arguments"
|
||||
errors.errors[1] shouldContain "14:33: invalid number of arguments"
|
||||
}
|
||||
|
||||
|
||||
test("invalid number of args check on asm subroutine") {
|
||||
val text="""
|
||||
main {
|
||||
@@ -261,6 +288,35 @@ main {
|
||||
errors.errors[1] shouldContain "9:25: invalid number of arguments"
|
||||
}
|
||||
|
||||
test("invalid number of args check on asm subroutine (in expression)") {
|
||||
val text="""
|
||||
main {
|
||||
asmsub thing(ubyte a1 @A, ubyte a2 @Y) -> ubyte @A {
|
||||
%asm {{
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub wrapper(ubyte value) {
|
||||
value++
|
||||
}
|
||||
|
||||
sub start() {
|
||||
wrapper(thing(1))
|
||||
wrapper(thing(1,2))
|
||||
wrapper(thing(1,2,3))
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, outputDir, writeAssembly = false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "14:33: invalid number of arguments"
|
||||
errors.errors[1] shouldContain "16:33: invalid number of arguments"
|
||||
}
|
||||
|
||||
|
||||
test("invalid number of args check on call to label and builtin func") {
|
||||
val text="""
|
||||
main {
|
||||
|
||||
@@ -88,8 +88,8 @@ class TestSymbolTable: FunSpec({
|
||||
val stVar1 = StStaticVariable("initialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false,node)
|
||||
stVar1.setOnetimeInitNumeric(99.0)
|
||||
val stVar2 = StStaticVariable("uninitialized", DataType.UBYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, node)
|
||||
val arrayInitNonzero = listOf(StArrayElement(1.1, null, null), StArrayElement(2.2, null, null), StArrayElement(3.3, null, null))
|
||||
val arrayInitAllzero = listOf(StArrayElement(0.0, null, null), StArrayElement(0.0, null, null), StArrayElement(0.0, null, null))
|
||||
val arrayInitNonzero = listOf(StArrayElement(1.1, null, null, null, null), StArrayElement(2.2, null, null, null, null), StArrayElement(3.3, null, null,null, null))
|
||||
val arrayInitAllzero = listOf(StArrayElement(0.0, null, null, null, null), StArrayElement(0.0, null, null,null, null), StArrayElement(0.0, null, null,null, null))
|
||||
val stVar3 = StStaticVariable("initialized", DataType.arrayFor(BaseDataType.UWORD), null, arrayInitNonzero, 3u, ZeropageWish.DONTCARE, 0u, false, node)
|
||||
val stVar4 = StStaticVariable("initialized", DataType.arrayFor(BaseDataType.UWORD), null, arrayInitAllzero, 3u, ZeropageWish.DONTCARE, 0u, false, node)
|
||||
val stVar5 = StStaticVariable("uninitialized", DataType.arrayFor(BaseDataType.UWORD), null, null, 3u, ZeropageWish.DONTCARE, 0u, false, node)
|
||||
|
||||
@@ -49,6 +49,7 @@ class TestAbstractZeropage: FunSpec({
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = C64Target(),
|
||||
compilerVersion="99.99",
|
||||
loadAddress = 999u,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
@@ -69,7 +70,7 @@ class TestC64Zeropage: FunSpec({
|
||||
floats = false,
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = c64target, loadAddress = 999u, memtopAddress = 0xffffu
|
||||
compTarget = c64target, compilerVersion="99.99", loadAddress = 999u, memtopAddress = 0xffffu
|
||||
))
|
||||
|
||||
var result = zp.allocate("", DataType.UBYTE, null, null, errors)
|
||||
@@ -84,33 +85,33 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testZpFloatEnable") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
var result = zp.allocate("", DataType.FLOAT, null, null, errors)
|
||||
result.expectError { "should be allocation error due to disabled floats" }
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
result = zp2.allocate("", DataType.FLOAT, null, null, errors)
|
||||
result.expectError { "should be allocation error due to disabled ZP use" }
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
zp3.allocate("", DataType.FLOAT, null, null, errors)
|
||||
}
|
||||
|
||||
test("testZpModesWithFloats") {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
shouldThrow<InternalCompilerException> {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
}
|
||||
shouldThrow<InternalCompilerException> {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
}
|
||||
}
|
||||
|
||||
test("testZpDontuse") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
println(zp.free)
|
||||
zp.availableBytes() shouldBe 0
|
||||
val result = zp.allocate("", DataType.BYTE, null, null, errors)
|
||||
@@ -118,13 +119,13 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testFreeSpacesBytes") {
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
zp1.availableBytes() shouldBe 15
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
zp2.availableBytes() shouldBe 85
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
zp3.availableBytes() shouldBe 94
|
||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
zp4.availableBytes() shouldBe 205
|
||||
zp4.allocate("test", DataType.UBYTE, null, null, errors)
|
||||
zp4.availableBytes() shouldBe 204
|
||||
@@ -133,7 +134,7 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testReservedSpace") {
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
zp1.availableBytes() shouldBe 205
|
||||
4u shouldNotBeIn zp1.free
|
||||
5u shouldNotBeIn zp1.free
|
||||
@@ -146,7 +147,7 @@ class TestC64Zeropage: FunSpec({
|
||||
200u shouldBeIn zp1.free
|
||||
255u shouldBeIn zp1.free
|
||||
199u shouldBeIn zp1.free
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, listOf(50u .. 100u, 200u..255u), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, listOf(50u .. 100u, 200u..255u), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
zp2.availableBytes() shouldBe 105
|
||||
4u shouldNotBeIn zp2.free
|
||||
5u shouldNotBeIn zp2.free
|
||||
@@ -159,7 +160,7 @@ class TestC64Zeropage: FunSpec({
|
||||
200u shouldNotBeIn zp2.free
|
||||
255u shouldNotBeIn zp2.free
|
||||
199u shouldBeIn zp2.free
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, listOf(50u .. 100u, 200u..255u), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, listOf(50u .. 100u, 200u..255u), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
zp2.availableBytes() shouldBe 105
|
||||
4u shouldBeIn zp3.free
|
||||
5u shouldBeIn zp3.free
|
||||
@@ -168,7 +169,7 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testBasicsafeAllocation") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
zp.availableBytes() shouldBe 15
|
||||
zp.hasByteAvailable() shouldBe true
|
||||
zp.hasWordAvailable() shouldBe true
|
||||
@@ -190,7 +191,7 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testFullAllocation") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
zp.availableBytes() shouldBe 205
|
||||
zp.hasByteAvailable() shouldBe true
|
||||
zp.hasWordAvailable() shouldBe true
|
||||
@@ -222,7 +223,7 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testEfficientAllocation") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
zp.availableBytes() shouldBe 15
|
||||
zp.allocate("", DataType.WORD, null, null, errors).getOrElse{throw it}.address shouldBe 0x04u
|
||||
zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x06u
|
||||
@@ -239,7 +240,7 @@ class TestC64Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testReservedLocations") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
withClue("zp _B1 and _REG must be next to each other to create a word") {
|
||||
zp.SCRATCH_B1 + 1u shouldBe zp.SCRATCH_REG
|
||||
}
|
||||
@@ -252,18 +253,18 @@ class TestCx16Zeropage: FunSpec({
|
||||
val cx16target = Cx16Target()
|
||||
|
||||
test("testReservedLocations") {
|
||||
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, 999u, 0xffffu))
|
||||
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||
withClue("zp _B1 and _REG must be next to each other to create a word") {
|
||||
zp.SCRATCH_B1 + 1u shouldBe zp.SCRATCH_REG
|
||||
}
|
||||
}
|
||||
|
||||
test("testFreeSpacesBytes") {
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, cx16target, 999u, 0xffffu))
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||
zp1.availableBytes() shouldBe 86
|
||||
val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, 999u, 0xffffu))
|
||||
val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||
zp2.availableBytes() shouldBe 173
|
||||
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, 999u, 0xffffu))
|
||||
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||
zp3.availableBytes() shouldBe 214
|
||||
zp3.allocate("test", DataType.UBYTE, null, null, errors)
|
||||
zp3.availableBytes() shouldBe 213
|
||||
@@ -272,7 +273,7 @@ class TestCx16Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("testReservedSpace") {
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, 999u, 0xffffu))
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||
zp1.availableBytes() shouldBe 214
|
||||
0x22u shouldNotBeIn zp1.free
|
||||
0x23u shouldNotBeIn zp1.free
|
||||
@@ -284,7 +285,7 @@ class TestCx16Zeropage: FunSpec({
|
||||
}
|
||||
|
||||
test("preallocated zp vars") {
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, 999u, 0xffffu))
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||
zp1.allocatedVariables["test"] shouldBe null
|
||||
zp1.allocatedVariables["cx16.r0"] shouldNotBe null
|
||||
zp1.allocatedVariables["cx16.r15"] shouldNotBe null
|
||||
|
||||
@@ -5,7 +5,7 @@ import io.kotest.matchers.string.shouldContain
|
||||
import prog8.ast.AstToSourceTextConverter
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Program
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.PROG8_CONTAINER_MODULES
|
||||
import prog8.code.source.SourceCode
|
||||
import prog8.parser.ParseError
|
||||
import prog8.parser.Prog8Parser.parseModule
|
||||
@@ -38,10 +38,12 @@ class TestAstToSourceText: AnnotationSpec() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMentionsInternedStringsModule() {
|
||||
fun testMentionsProg8ContainerModules() {
|
||||
val orig = SourceCode.Text("\n")
|
||||
val (txt, _) = roundTrip(parseModule(orig))
|
||||
txt shouldContain Regex(";.*$INTERNED_STRINGS_MODULENAME")
|
||||
PROG8_CONTAINER_MODULES.forEach {
|
||||
txt shouldContain Regex(";.*$it")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -466,4 +466,15 @@ main {
|
||||
compileText(C64Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
|
||||
}
|
||||
|
||||
test("const long with small values") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
const long notkaputt = 42
|
||||
cx16.r0L = notkaputt
|
||||
}
|
||||
}"""
|
||||
compileText(Cx16Target(), true, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
}
|
||||
})
|
||||
@@ -13,10 +13,11 @@ import io.kotest.matchers.types.shouldBeSameInstanceAs
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.statements.Block
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.PROG8_CONTAINER_MODULES
|
||||
import prog8.code.ast.PtBlock
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.source.SourceCode
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.target.C64Target
|
||||
import prog8tests.helpers.DummyFunctions
|
||||
import prog8tests.helpers.DummyMemsizer
|
||||
@@ -30,7 +31,7 @@ class TestProgram: FunSpec({
|
||||
context("Constructor") {
|
||||
test("withNameBuiltinsAndMemsizer") {
|
||||
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||
program.modules.size shouldBe 1
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
|
||||
program.modules[0].name shouldBe INTERNED_STRINGS_MODULENAME
|
||||
program.modules[0].program shouldBeSameInstanceAs program
|
||||
program.modules[0].parent shouldBeSameInstanceAs program.namespace
|
||||
@@ -45,7 +46,7 @@ class TestProgram: FunSpec({
|
||||
val retVal = program.addModule(m1)
|
||||
|
||||
retVal shouldBeSameInstanceAs program
|
||||
program.modules.size shouldBe 2
|
||||
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size + 1
|
||||
m1 shouldBeIn program.modules
|
||||
m1.program shouldBeSameInstanceAs program
|
||||
m1.parent shouldBeSameInstanceAs program.namespace
|
||||
@@ -163,7 +164,7 @@ datablock2 ${'$'}8000 {
|
||||
|
||||
val result = compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=true)!!
|
||||
result.compilerAst.allBlocks.size shouldBeGreaterThan 5
|
||||
result.compilerAst.modules.drop(2).all { it.isLibrary } shouldBe true
|
||||
result.compilerAst.modules.drop(PROG8_CONTAINER_MODULES.size+1).all { it.isLibrary } shouldBe true
|
||||
val mainMod = result.compilerAst.modules[0]
|
||||
mainMod.name shouldStartWith "on_the_fly"
|
||||
result.compilerAst.modules[1].name shouldBe "prog8_interned_strings"
|
||||
@@ -183,7 +184,7 @@ datablock2 ${'$'}8000 {
|
||||
blocks[1].name shouldBe "p8_sys_startup"
|
||||
blocks[2].name shouldBe "p8b_otherblock1"
|
||||
blocks[3].name shouldBe "p8b_otherblock2"
|
||||
blocks[4].name shouldBe "prog8_interned_strings"
|
||||
blocks[4].name shouldBe INTERNED_STRINGS_MODULENAME
|
||||
blocks[5].name shouldBe "txt"
|
||||
blocks[5].library shouldBe true
|
||||
blocks[13].name shouldBe "p8b_datablock2"
|
||||
|
||||
@@ -871,6 +871,7 @@ main {
|
||||
DataType.forDt(BaseDataType.FLOAT).isFloat shouldBe true
|
||||
|
||||
DataType.arrayFor(BaseDataType.UBYTE, true).isUnsignedByteArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.LONG).isLongArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.FLOAT).isFloatArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.UWORD).isUnsignedWordArray shouldBe true
|
||||
DataType.arrayFor(BaseDataType.UWORD).isArray shouldBe true
|
||||
@@ -886,9 +887,6 @@ main {
|
||||
shouldThrow<NoSuchElementException> {
|
||||
DataType.arrayFor(BaseDataType.ARRAY)
|
||||
}
|
||||
shouldThrow<NoSuchElementException> {
|
||||
DataType.arrayFor(BaseDataType.LONG)
|
||||
}
|
||||
shouldThrow<NoSuchElementException> {
|
||||
DataType.arrayFor(BaseDataType.UNDEFINED)
|
||||
}
|
||||
@@ -1157,5 +1155,46 @@ main {
|
||||
decls.all { it.datatype.isPointerArray } shouldBe true
|
||||
}
|
||||
|
||||
test("on..call in nested scope compiles correctly with temp variable introduced") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
if_cs {
|
||||
on cx16.r0L call (func,func)
|
||||
}
|
||||
}
|
||||
|
||||
sub func() {
|
||||
}
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("breakpoint after expression") {
|
||||
val src="""
|
||||
main {
|
||||
ubyte a,b
|
||||
|
||||
sub start() {
|
||||
func1()
|
||||
func2()
|
||||
}
|
||||
|
||||
sub func1() {
|
||||
a = b
|
||||
%breakpoint ; parse error
|
||||
}
|
||||
|
||||
sub func2() {
|
||||
a = b
|
||||
%breakpoint! ; parse ok
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), optimize=false, src, outputDir, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 3
|
||||
errors.errors[1] shouldContain "12:10: undefined symbol: breakpoint"
|
||||
}
|
||||
})
|
||||
|
||||
@@ -88,7 +88,7 @@ class TestAsmGenSymbols: StringSpec({
|
||||
|
||||
fun createTestAsmGen6502(program: Program): AsmGen6502Internal {
|
||||
val errors = ErrorReporterForTests()
|
||||
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, true, false, C64Target(), 999u, 0xffffu)
|
||||
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, true, false, C64Target(), "99.99", 999u, 0xffffu)
|
||||
val astchecker = AstChecker(program, errors, options)
|
||||
astchecker.visit(program)
|
||||
errors.report()
|
||||
|
||||
@@ -28,12 +28,14 @@ internal object DummyMemsizer : IMemSizer {
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
BaseDataType.UWORD, BaseDataType.WORD -> numElements*2
|
||||
BaseDataType.LONG -> numElements*4
|
||||
BaseDataType.FLOAT -> numElements*5
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
return when {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isLong -> 4 * (numElements ?: 1)
|
||||
dt.isFloat -> 5 * (numElements ?: 1)
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package prog8tests.vm
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.engine.spec.tempdir
|
||||
import io.kotest.matchers.collections.shouldBeIn
|
||||
import io.kotest.matchers.ints.shouldBeLessThanOrEqual
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
@@ -12,10 +14,7 @@ import prog8.ast.statements.Assignment
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.intermediate.IRDataType
|
||||
import prog8.intermediate.IRFileReader
|
||||
import prog8.intermediate.IRSubroutine
|
||||
import prog8.intermediate.Opcode
|
||||
import prog8.intermediate.*
|
||||
import prog8.vm.VmRunner
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
@@ -548,4 +547,42 @@ main {
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
VmRunner().runProgram(virtfile.readText(), true)
|
||||
}
|
||||
|
||||
test("correct reg types with sqrt") {
|
||||
val src= """
|
||||
main {
|
||||
sub start() {
|
||||
cx16.r0L= sqrt(cx16.r1)
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("SSA basic blocks") {
|
||||
val src= """
|
||||
main {
|
||||
sub start() {
|
||||
func()
|
||||
}
|
||||
|
||||
sub func() {
|
||||
if cx16.r0<10 or cx16.r0>319 {
|
||||
cx16.r1++
|
||||
}
|
||||
}
|
||||
}"""
|
||||
val result = compileText(VMTarget(), false, src, outputDir)!!
|
||||
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||
val irProgram = IRFileReader().read(virtfile)
|
||||
val func = irProgram.blocks[0].children[1] as IRSubroutine
|
||||
func.label shouldBe "main.func"
|
||||
func.chunks.size shouldBe 11
|
||||
for(chunk in func.chunks) {
|
||||
if(chunk.next == null) {
|
||||
chunk.instructions.last().opcode shouldBeIn OpcodesThatBranchUnconditionally
|
||||
}
|
||||
val branches = chunk.instructions.filter { it.opcode in OpcodesThatEndSSAblock }
|
||||
branches.size shouldBeLessThanOrEqual 1
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -213,7 +213,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
||||
}
|
||||
output(") ")
|
||||
if(subroutine.asmClobbers.isNotEmpty()) {
|
||||
output("-> clobbers (")
|
||||
output(" clobbers (")
|
||||
val regs = subroutine.asmClobbers.toList().sorted()
|
||||
for(r in regs) {
|
||||
output(r.toString())
|
||||
@@ -545,9 +545,9 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
||||
}
|
||||
|
||||
override fun visit(onGoto: OnGoto) {
|
||||
output(if(onGoto.isCall) "selectcall " else "selectgoto ")
|
||||
output("on ")
|
||||
onGoto.index.accept(this)
|
||||
output(" from (")
|
||||
output(if(onGoto.isCall) " call (" else " goto (")
|
||||
onGoto.labels.forEachIndexed { idx, label ->
|
||||
label.accept(this)
|
||||
if(idx!=onGoto.labels.lastIndex)
|
||||
@@ -571,7 +571,18 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
||||
}
|
||||
|
||||
override fun visit(deref: ArrayIndexedPtrDereference) {
|
||||
output("???? Array Indexed Ptr Dereference should have been converted to other AST nodes ????")
|
||||
for(c in deref.chain) {
|
||||
output(c.first)
|
||||
c.second?.let {
|
||||
output("[")
|
||||
it.accept(this)
|
||||
output("]")
|
||||
}
|
||||
if(c !== deref.chain.last())
|
||||
output("^^.")
|
||||
}
|
||||
if(deref.derefLast)
|
||||
output("^^")
|
||||
}
|
||||
|
||||
override fun visit(initializer: StaticStructInitializer) {
|
||||
|
||||
@@ -427,6 +427,7 @@ fun defaultZero(dt: BaseDataType, position: Position) = when(dt) {
|
||||
BaseDataType.BYTE -> NumericLiteral(BaseDataType.BYTE, 0.0, position)
|
||||
BaseDataType.UWORD, BaseDataType.STR -> NumericLiteral(BaseDataType.UWORD, 0.0, position)
|
||||
BaseDataType.WORD -> NumericLiteral(BaseDataType.WORD, 0.0, position)
|
||||
BaseDataType.LONG -> NumericLiteral(BaseDataType.LONG, 0.0, position)
|
||||
BaseDataType.FLOAT -> NumericLiteral(BaseDataType.FLOAT, 0.0, position)
|
||||
BaseDataType.POINTER -> NumericLiteral(BaseDataType.UWORD, 0.0, position)
|
||||
else -> throw FatalAstException("can only determine default zero value for a numeric type")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package prog8.ast
|
||||
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.code.core.Position
|
||||
|
||||
/**
|
||||
@@ -19,6 +18,3 @@ open class SyntaxError(override var message: String, val position: Position) : A
|
||||
class ExpressionError(message: String, val position: Position) : AstException(message) {
|
||||
override fun toString() = "${position.toClickableStr()} Error: $message"
|
||||
}
|
||||
|
||||
class UndefinedSymbolError(symbol: IdentifierReference)
|
||||
: SyntaxError("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
|
||||
@@ -6,6 +6,7 @@ import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.GENERATED_LABEL_PREFIX
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.PROG8_CONTAINER_MODULES
|
||||
import prog8.code.core.*
|
||||
import prog8.code.source.SourceCode
|
||||
|
||||
@@ -22,17 +23,19 @@ class Program(val name: String,
|
||||
val namespace: GlobalNamespace = GlobalNamespace(_modules)
|
||||
|
||||
init {
|
||||
// insert a container module for all interned strings later
|
||||
val internedStringsModule = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated(INTERNED_STRINGS_MODULENAME))
|
||||
val block = Block(INTERNED_STRINGS_MODULENAME, null, mutableListOf(), true, Position.DUMMY)
|
||||
val directive = Directive("%option", listOf(DirectiveArg("no_symbol_prefixing", null, Position.DUMMY)), Position.DUMMY)
|
||||
block.statements.add(directive)
|
||||
directive.linkParents(block)
|
||||
internedStringsModule.statements.add(block)
|
||||
// insert container modules for all interned strings and struct instances
|
||||
PROG8_CONTAINER_MODULES.forEach { containername ->
|
||||
val module = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated(containername))
|
||||
val block = Block(containername, null, mutableListOf(), true, Position.DUMMY)
|
||||
val directive = Directive("%option", listOf(DirectiveArg("no_symbol_prefixing", null, Position.DUMMY)), Position.DUMMY)
|
||||
block.statements.add(directive)
|
||||
directive.linkParents(block)
|
||||
module.statements.add(block)
|
||||
|
||||
_modules.add(0, internedStringsModule)
|
||||
internedStringsModule.linkParents(namespace)
|
||||
internedStringsModule.program = this
|
||||
_modules.add(0, module)
|
||||
module.linkParents(namespace)
|
||||
module.program = this
|
||||
}
|
||||
}
|
||||
|
||||
fun addModule(module: Module): Program {
|
||||
@@ -67,7 +70,7 @@ class Program(val name: String,
|
||||
}
|
||||
|
||||
val toplevelModule: Module
|
||||
get() = modules.first { it.name!= INTERNED_STRINGS_MODULENAME }
|
||||
get() = modules.first { it.name !in PROG8_CONTAINER_MODULES }
|
||||
|
||||
private val internedStringsReferenceCounts = mutableMapOf<VarDecl, Int>()
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ private class SymbolDumper(val skipLibraries: Boolean): IAstVisitor {
|
||||
}
|
||||
output(") ")
|
||||
if(subroutine.asmClobbers.isNotEmpty()) {
|
||||
output("-> clobbers (")
|
||||
output(" clobbers (")
|
||||
val regs = subroutine.asmClobbers.toList().sorted()
|
||||
for(r in regs) {
|
||||
output(r.toString())
|
||||
|
||||
@@ -372,7 +372,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
|
||||
else if(literalText.length>2)
|
||||
datatype = BaseDataType.UWORD
|
||||
try {
|
||||
integer = literalText.toInt(16)
|
||||
integer = literalText.lowercase().toLong(16).toInt()
|
||||
} catch(x: NumberFormatException) {
|
||||
throw SyntaxError("invalid hexadecimal literal ${x.message}", ctx.toPosition())
|
||||
}
|
||||
|
||||
@@ -126,11 +126,13 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
|
||||
"~" -> {
|
||||
if(inferred.isBytes) InferredTypes.knownFor(BaseDataType.UBYTE)
|
||||
else if(inferred.isWords) InferredTypes.knownFor(BaseDataType.UWORD)
|
||||
else if(inferred.isLong) InferredTypes.knownFor(BaseDataType.LONG) // TODO should be ULONG, but we don't have that type yet
|
||||
else InferredTypes.unknown()
|
||||
}
|
||||
"-" -> {
|
||||
if(inferred.isBytes) InferredTypes.knownFor(BaseDataType.BYTE)
|
||||
else if(inferred.isWords) InferredTypes.knownFor(BaseDataType.WORD)
|
||||
else if(inferred.isLong) InferredTypes.knownFor(BaseDataType.LONG)
|
||||
else inferred
|
||||
}
|
||||
else -> throw FatalAstException("weird prefix expression operator")
|
||||
@@ -179,7 +181,12 @@ class BinaryExpression(
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||
|
||||
override fun referencesIdentifier(nameInSource: List<String>) = left.referencesIdentifier(nameInSource) || right.referencesIdentifier(nameInSource)
|
||||
override fun referencesIdentifier(nameInSource: List<String>): Boolean {
|
||||
return if(operator==".")
|
||||
left.referencesIdentifier(nameInSource)
|
||||
else
|
||||
left.referencesIdentifier(nameInSource) || right.referencesIdentifier(nameInSource)
|
||||
}
|
||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||
|
||||
val leftDt = left.inferType(program)
|
||||
@@ -326,6 +333,7 @@ class BinaryExpression(
|
||||
BaseDataType.BYTE -> Pair(DataType.BYTE, left)
|
||||
BaseDataType.UWORD -> Pair(DataType.UWORD, left)
|
||||
BaseDataType.WORD -> Pair(DataType.WORD, left)
|
||||
BaseDataType.LONG -> Pair(DataType.LONG, left)
|
||||
BaseDataType.FLOAT -> Pair(DataType.FLOAT, left)
|
||||
else -> Pair(leftDt, null) // non-numeric datatype
|
||||
}
|
||||
@@ -336,6 +344,7 @@ class BinaryExpression(
|
||||
BaseDataType.BYTE -> Pair(DataType.BYTE, null)
|
||||
BaseDataType.UWORD -> Pair(DataType.WORD, left)
|
||||
BaseDataType.WORD -> Pair(DataType.WORD, left)
|
||||
BaseDataType.LONG -> Pair(DataType.LONG, left)
|
||||
BaseDataType.FLOAT -> Pair(DataType.FLOAT, left)
|
||||
else -> Pair(leftDt, null) // non-numeric datatype
|
||||
}
|
||||
@@ -346,6 +355,7 @@ class BinaryExpression(
|
||||
BaseDataType.BYTE -> Pair(DataType.WORD, right)
|
||||
BaseDataType.UWORD -> Pair(DataType.UWORD, null)
|
||||
BaseDataType.WORD -> Pair(DataType.WORD, left)
|
||||
BaseDataType.LONG -> Pair(DataType.LONG, left)
|
||||
BaseDataType.FLOAT -> Pair(DataType.FLOAT, left)
|
||||
else -> Pair(leftDt, null) // non-numeric datatype
|
||||
}
|
||||
@@ -356,6 +366,18 @@ class BinaryExpression(
|
||||
BaseDataType.BYTE -> Pair(DataType.WORD, right)
|
||||
BaseDataType.UWORD -> Pair(DataType.WORD, right)
|
||||
BaseDataType.WORD -> Pair(DataType.WORD, null)
|
||||
BaseDataType.LONG -> Pair(DataType.LONG, left)
|
||||
BaseDataType.FLOAT -> Pair(DataType.FLOAT, left)
|
||||
else -> Pair(leftDt, null) // non-numeric datatype
|
||||
}
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
when (rightDt.base) {
|
||||
BaseDataType.UBYTE -> Pair(DataType.LONG, right)
|
||||
BaseDataType.BYTE -> Pair(DataType.LONG, right)
|
||||
BaseDataType.UWORD -> Pair(DataType.LONG, right)
|
||||
BaseDataType.WORD -> Pair(DataType.LONG, right)
|
||||
BaseDataType.LONG -> Pair(DataType.LONG, null)
|
||||
BaseDataType.FLOAT -> Pair(DataType.FLOAT, left)
|
||||
else -> Pair(leftDt, null) // non-numeric datatype
|
||||
}
|
||||
@@ -1123,7 +1145,7 @@ class ArrayLiteral(val type: InferredTypes.InferredType, // inferred because
|
||||
}
|
||||
|
||||
// otherwise, select the "biggest" datatype based on the elements in the array.
|
||||
require(value.isNotEmpty()) { "can't determine type of empty array" }
|
||||
if(value.isEmpty()) return InferredTypes.unknown()
|
||||
val datatypesInArray = value.map { it.inferType(program) }
|
||||
if(datatypesInArray.any{ it.isUnknown })
|
||||
return InferredTypes.unknown()
|
||||
@@ -1318,9 +1340,30 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
BuiltinFunctionPlaceholder(nameInSource[0], position, parent)
|
||||
else
|
||||
definingScope.lookup(nameInSource)
|
||||
fun targetVarDecl(): VarDecl? = targetStatement() as? VarDecl
|
||||
fun targetSubroutine(): Subroutine? = targetStatement() as? Subroutine
|
||||
fun targetStructDecl(): StructDecl? = targetStatement() as? StructDecl
|
||||
fun targetVarDecl(): VarDecl? {
|
||||
// follows aliases
|
||||
val t = targetStatement()
|
||||
return if(t is Alias)
|
||||
t.target.targetVarDecl()
|
||||
else
|
||||
t as? VarDecl
|
||||
}
|
||||
fun targetSubroutine(): Subroutine? {
|
||||
// follows aliases
|
||||
val t = targetStatement()
|
||||
return if(t is Alias)
|
||||
t.target.targetSubroutine()
|
||||
else
|
||||
t as? Subroutine
|
||||
}
|
||||
fun targetStructDecl(): StructDecl? {
|
||||
// follows aliases
|
||||
val t = targetStatement()
|
||||
return if(t is Alias)
|
||||
t.target.targetStructDecl()
|
||||
else
|
||||
t as? StructDecl
|
||||
}
|
||||
fun targetStructFieldRef(): StructFieldRef? {
|
||||
if(nameInSource.size<2) return null
|
||||
return targetStatement() as? StructFieldRef
|
||||
@@ -1472,7 +1515,7 @@ class FunctionCallExpression(override var target: IdentifierReference,
|
||||
|
||||
override fun copy() = FunctionCallExpression(target.copy(), args.map { it.copy() }.toMutableList(), position)
|
||||
override val isSimple = when (target.nameInSource.singleOrNull()) {
|
||||
in arrayOf("msb", "lsb", "mkword", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple }
|
||||
in arrayOf("msb", "lsb", "mkword", "mklong", "mklong2", "set_carry", "set_irqd", "clear_carry", "clear_irqd") -> this.args.all { it.isSimple }
|
||||
else -> false
|
||||
}
|
||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||
@@ -1783,6 +1826,35 @@ class ArrayIndexedPtrDereference(
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if(parent !is BinaryExpression || (parent as BinaryExpression).operator!=".") {
|
||||
val indexPosition = this.chain.indexOfFirst { it.second!=null }
|
||||
val identifier = this.chain.take(indexPosition+1).map {it.first}
|
||||
val target = this.definingScope.lookup(identifier)
|
||||
if(target is VarDecl && (target.datatype.isPointer || target.datatype.isPointerArray)) {
|
||||
val sub = target.datatype.subType
|
||||
if(sub!=null) {
|
||||
val field = this.chain.drop(indexPosition+1).singleOrNull()
|
||||
if(field==null) {
|
||||
return InferredTypes.knownFor(DataType.structInstance(sub))
|
||||
} else {
|
||||
val fieldDt= sub.getFieldType(field.first)!!
|
||||
return if(derefLast)
|
||||
InferredTypes.knownFor(fieldDt.dereference())
|
||||
else
|
||||
InferredTypes.knownFor(fieldDt)
|
||||
}
|
||||
} else {
|
||||
require(indexPosition==this.chain.size-1) {"when indexing primitive pointer type there cannot be a field after it"}
|
||||
return if(derefLast)
|
||||
InferredTypes.knownFor(target.datatype.dereference())
|
||||
else
|
||||
InferredTypes.knownFor(target.datatype)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// too hard to determine the type....?
|
||||
return InferredTypes.unknown()
|
||||
}
|
||||
@@ -1820,7 +1892,13 @@ class StaticStructInitializer(var structname: IdentifierReference,
|
||||
|
||||
override fun referencesIdentifier(nameInSource: List<String>): Boolean = structname.referencesIdentifier(nameInSource) || args.any{it.referencesIdentifier(nameInSource)}
|
||||
|
||||
override fun inferType(program: Program) = InferredTypes.knownFor(BaseDataType.UWORD)
|
||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||
val struct = structname.targetStructDecl()
|
||||
return if(struct==null)
|
||||
InferredTypes.unknown()
|
||||
else
|
||||
InferredTypes.knownFor(DataType.pointer(struct))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ object InferredTypes {
|
||||
val isBool = datatype?.isBool==true
|
||||
val isBytes = datatype?.isByte==true
|
||||
val isWords = datatype?.isWord==true
|
||||
val isLong = datatype?.isLong==true
|
||||
val isPointer = datatype?.isPointer==true
|
||||
val isStructInstance = datatype?.isStructInstance==true
|
||||
val isUnsignedWord = datatype?.isUnsignedWord==true
|
||||
|
||||
@@ -333,7 +333,7 @@ class VarDecl(
|
||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||
require(replacement is Expression && (value==null || node===value))
|
||||
value = replacement // note: any datatype differences between the value and the decl itself, will be fixed by a separate ast walker step
|
||||
replacement.parent = this
|
||||
replacement.linkParents(this)
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
@@ -414,6 +414,15 @@ class StructDecl(override val name: String, val fields: Array<Pair<DataType, Str
|
||||
|
||||
override fun getFieldType(name: String): DataType? = fields.firstOrNull { it.second==name }?.first
|
||||
override val scopedNameString by lazy { scopedName.joinToString(".") }
|
||||
|
||||
fun offsetof(fieldname: String, sizer: IMemSizer): UByte? {
|
||||
fields.fold(0) { offset, field ->
|
||||
if (field.second == fieldname)
|
||||
return offset.toUByte()
|
||||
offset + sizer.memorySize(field.first, 1)
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
class StructFieldRef(val pointer: IdentifierReference, val struct: StructDecl, val type: DataType, override val name: String, override val position: Position): Statement(), INamedStatement {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
; all library modules for the c128 compiler target
|
||||
|
||||
%import bcd
|
||||
%import buffers
|
||||
%import compression
|
||||
%import conv
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
; all library modules for the c64 compiler target
|
||||
|
||||
%import bcd
|
||||
%import buffers
|
||||
%import compression
|
||||
%import conv
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
; all library modules for the cx16 compiler target
|
||||
|
||||
%import buffers
|
||||
%import bcd
|
||||
%import bmx
|
||||
%import buffers
|
||||
%import compression
|
||||
%import conv
|
||||
%import coroutines
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
; all library modules for the pet32 compiler target
|
||||
|
||||
%import bcd
|
||||
%import buffers
|
||||
%import compression
|
||||
%import conv
|
||||
|
||||
Loaded 100 of 165 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user