Compare commits
922 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
0fcf1f6e3a | |
![]() |
63b468927e | |
![]() |
e8265df4bd | |
![]() |
15c3faa26e | |
![]() |
a9cce557d2 | |
![]() |
0c5dd28b1c | |
![]() |
c41951d49c | |
![]() |
be26878b4c | |
![]() |
66d1091330 | |
![]() |
72c86ade31 | |
![]() |
7d718ada39 | |
![]() |
817b409ec8 | |
![]() |
cba838dd52 | |
![]() |
730b488fe3 | |
![]() |
1d6c9023ff | |
![]() |
0db536c063 | |
![]() |
6a15dbfa12 | |
![]() |
e3fa6f4224 | |
![]() |
98ceadd44c | |
![]() |
dc10df0796 | |
![]() |
97a00e3137 | |
![]() |
d0d010b09d | |
![]() |
f6f993cb41 | |
![]() |
86c6740b24 | |
![]() |
1b3f4664d8 | |
![]() |
13096f9fdc | |
![]() |
c4f7c1bff7 | |
![]() |
317b91533b | |
![]() |
259eb4b408 | |
![]() |
d68091ee9c | |
![]() |
b491e99954 | |
![]() |
bca0457bea | |
![]() |
f1c96281a9 | |
![]() |
bdc8f635de | |
![]() |
9ad3d42252 | |
![]() |
f0503a6a28 | |
![]() |
99ce959913 | |
![]() |
cb7af652f5 | |
![]() |
c01b2bf7a0 | |
![]() |
0ea0af3abf | |
![]() |
99aa5676db | |
![]() |
5e3d2d07c3 | |
![]() |
f0a023b572 | |
![]() |
5f8255bc90 | |
![]() |
584508230f | |
![]() |
023dc0c446 | |
![]() |
871a167d8b | |
![]() |
0a4287c6ad | |
![]() |
5e8beb3ab7 | |
![]() |
7c1d2a64f4 | |
![]() |
b2f6fab6f4 | |
![]() |
4528441c74 | |
![]() |
8e3f6cc519 | |
![]() |
7041b52ebc | |
![]() |
adf143a38d | |
![]() |
8d4f419546 | |
![]() |
5959009ebd | |
![]() |
d8f1d106f0 | |
![]() |
a5389286e8 | |
![]() |
7740634e6a | |
![]() |
58ee191cc8 | |
![]() |
78aae252d5 | |
![]() |
09e4400f3c | |
![]() |
e3e561da3f | |
![]() |
9c8f229fed | |
![]() |
f3bd58f75e | |
![]() |
1b8daa0465 | |
![]() |
6c6cefad6c | |
![]() |
540ebe7256 | |
![]() |
6dc396741f | |
![]() |
4ba8f330c4 | |
![]() |
3877a8e46b | |
![]() |
7a4255b732 | |
![]() |
dfd6338992 | |
![]() |
58ab33210a | |
![]() |
b60f42b281 | |
![]() |
98d969ab15 | |
![]() |
b03bceb5c1 | |
![]() |
12b207d915 | |
![]() |
e42829ea81 | |
![]() |
238c552599 | |
![]() |
d79d45a117 | |
![]() |
94955aee81 | |
![]() |
f2dce621ce | |
![]() |
a61754bb58 | |
![]() |
6d345cc1ea | |
![]() |
e576538268 | |
![]() |
e6f0d77aa0 | |
![]() |
24ca1a5fdb | |
![]() |
2bf0eb7ead | |
![]() |
2d561a60c8 | |
![]() |
4ae4397547 | |
![]() |
b2ae4c7dc5 | |
![]() |
881a740cab | |
![]() |
f375099613 | |
![]() |
1d284f6f1e | |
![]() |
fc3c7440d1 | |
![]() |
979f1ed615 | |
![]() |
8b6628b070 | |
![]() |
287f6642fc | |
![]() |
a97463b0ac | |
![]() |
9ebc96d121 | |
![]() |
75e6856af4 | |
![]() |
3fc065d72d | |
![]() |
1787235e09 | |
![]() |
13b4cea171 | |
![]() |
1666049531 | |
![]() |
6d3ea6a485 | |
![]() |
079341f102 | |
![]() |
82f38f0b7a | |
![]() |
1428bfb2cf | |
![]() |
d68b58f37e | |
![]() |
b993ec10cd | |
![]() |
e5501e555f | |
![]() |
fbf753257b | |
![]() |
216b8e045d | |
![]() |
30441fed24 | |
![]() |
aa443c8846 | |
![]() |
f13c70d028 | |
![]() |
e9446fa9dc | |
![]() |
2eb6d44c2c | |
![]() |
2179ca2a41 | |
![]() |
7ac2eb2d71 | |
![]() |
a3d696121e | |
![]() |
50d32f3c96 | |
![]() |
74f479ce6d | |
![]() |
f719438a6e | |
![]() |
268c4f14c1 | |
![]() |
d18524d5ac | |
![]() |
277b151ada | |
![]() |
4f6498c99c | |
![]() |
824eb370e4 | |
![]() |
0e6235a7c4 | |
![]() |
5b986d3111 | |
![]() |
2fff4c0b5a | |
![]() |
deb1ba2bb2 | |
![]() |
f54b6311c1 | |
![]() |
cb6f60c383 | |
![]() |
c6cab9ed41 | |
![]() |
e290c42360 | |
![]() |
5598065985 | |
![]() |
8d31875902 | |
![]() |
ec71b15505 | |
![]() |
53c58bd777 | |
![]() |
4359bccfcb | |
![]() |
b47563e888 | |
![]() |
a174901412 | |
![]() |
8423dae6ff | |
![]() |
dd386d12a9 | |
![]() |
76c2723f5c | |
![]() |
757a608b6e | |
![]() |
54e87c915f | |
![]() |
12be06beb6 | |
![]() |
161bd9d3d2 | |
![]() |
d0a7239f15 | |
![]() |
29c67f2140 | |
![]() |
327ce45124 | |
![]() |
c6bf5d5181 | |
![]() |
f3f6a6a194 | |
![]() |
37c0320cbe | |
![]() |
b778fbaad1 | |
![]() |
01c2d65f07 | |
![]() |
d62c61ba08 | |
![]() |
12563fb636 | |
![]() |
c0ada2f445 | |
![]() |
ececf337f2 | |
![]() |
d31eabb6fa | |
![]() |
837a582087 | |
![]() |
821f5f5f29 | |
![]() |
9b828c2cde | |
![]() |
5eadd67df6 | |
![]() |
4b17de7e8c | |
![]() |
71f4d7b222 | |
![]() |
6d2ad2a3f0 | |
![]() |
2031f491f0 | |
![]() |
d92c9fcd90 | |
![]() |
94ba7c1594 | |
![]() |
a812a43bda | |
![]() |
841e3eb060 | |
![]() |
a82b5758bc | |
![]() |
b55eb431bd | |
![]() |
1302cbdd64 | |
![]() |
25b5ac91bd | |
![]() |
613569a2ab | |
![]() |
7898b46435 | |
![]() |
4e3592f2d4 | |
![]() |
f2611680ca | |
![]() |
db20771ef3 | |
![]() |
cbb0f4b872 | |
![]() |
e234385c20 | |
![]() |
626d2379bc | |
![]() |
eb2bd73c7d | |
![]() |
ae44ba2475 | |
![]() |
649462ff5c | |
![]() |
890dc4f228 | |
![]() |
d449888405 | |
![]() |
ccc5c955e9 | |
![]() |
8fc403cdad | |
![]() |
25a7b1ca1d | |
![]() |
8e9b88d01d | |
![]() |
a72b79a55a | |
![]() |
cfc49eb286 | |
![]() |
d21bc64cb3 | |
![]() |
a2406e3c0e | |
![]() |
747f50de98 | |
![]() |
5ab8161a21 | |
![]() |
442661747e | |
![]() |
5df83c97c7 | |
![]() |
347f4a79fd | |
![]() |
043244a56d | |
![]() |
c85a2103bb | |
![]() |
10798c3464 | |
![]() |
ee2c6cc7c2 | |
![]() |
474bf6e784 | |
![]() |
35cea5e1d7 | |
![]() |
6112aa120a | |
![]() |
ba8d547dfa | |
![]() |
84474105e2 | |
![]() |
5a852cb00d | |
![]() |
95adc87f6d | |
![]() |
8feeee6103 | |
![]() |
d99c571f94 | |
![]() |
111dc7a563 | |
![]() |
0b87dd5fa6 | |
![]() |
968bd26d85 | |
![]() |
6e26559cd2 | |
![]() |
31a7f53282 | |
![]() |
ea1755bed0 | |
![]() |
5fdd285c9a | |
![]() |
18d1df606f | |
![]() |
b117bb8f58 | |
![]() |
faf3c0f2e0 | |
![]() |
e227902cec | |
![]() |
67ca4997e2 | |
![]() |
21e2a876ec | |
![]() |
9430502b16 | |
![]() |
a8429af131 | |
![]() |
3415e23105 | |
![]() |
646ed3cb32 | |
![]() |
5ffa642980 | |
![]() |
71e1ba8c40 | |
![]() |
17a1bfa673 | |
![]() |
a7575ec7b3 | |
![]() |
d48e5f2da0 | |
![]() |
7dd4152d67 | |
![]() |
890035c688 | |
![]() |
4b576d066e | |
![]() |
77274735d6 | |
![]() |
7897bd387b | |
![]() |
f4c8202b1a | |
![]() |
740305cc25 | |
![]() |
8143f54956 | |
![]() |
f68f55d002 | |
![]() |
d1cbc41115 | |
![]() |
8bfc6df8de | |
![]() |
63141c086a | |
![]() |
1cd8c16bbb | |
![]() |
da26453911 | |
![]() |
a4b2b0c40d | |
![]() |
6d0de509c4 | |
![]() |
345b7439e4 | |
![]() |
5439742578 | |
![]() |
fa835ecf68 | |
![]() |
5a08118c87 | |
![]() |
cbd65a131e | |
![]() |
ab8938a695 | |
![]() |
f905b6fb93 | |
![]() |
fd1e4379b9 | |
![]() |
7f437d48db | |
![]() |
d55a384c88 | |
![]() |
740489f7a4 | |
![]() |
ac3153d86b | |
![]() |
65780e6ba2 | |
![]() |
27ac8dbc14 | |
![]() |
4d3af0d915 | |
![]() |
f16de402cf | |
![]() |
5c90cb939d | |
![]() |
80c6dd524b | |
![]() |
6a1232b9a9 | |
![]() |
f580d20a7b | |
![]() |
de4ae9dd92 | |
![]() |
989b93c92a | |
![]() |
521fc249a3 | |
![]() |
752b37ed82 | |
![]() |
084747abc5 | |
![]() |
ed650f2b46 | |
![]() |
fd3c349735 | |
![]() |
6c6318b63b | |
![]() |
01f8ad009e | |
![]() |
4b4239de62 | |
![]() |
1bec2a9293 | |
![]() |
eedb0ba478 | |
![]() |
e1821d0023 | |
![]() |
24c402af51 | |
![]() |
c867a7f1c0 | |
![]() |
6f47c9ed4c | |
![]() |
9bfc9c08ff | |
![]() |
2cba2e783a | |
![]() |
81219a9f5d | |
![]() |
082310d5d5 | |
![]() |
6949100446 | |
![]() |
5ef35a4ccf | |
![]() |
890a66c0eb | |
![]() |
5dac65f52e | |
![]() |
bbecab6cb0 | |
![]() |
72d4eba477 | |
![]() |
7cef13031f | |
![]() |
399a6af91c | |
![]() |
53e5aa6298 | |
![]() |
733769303c | |
![]() |
582a421447 | |
![]() |
644d190e98 | |
![]() |
f4377e4f0f | |
![]() |
2a35af5bb9 | |
![]() |
1b7b5106e2 | |
![]() |
090627b3c1 | |
![]() |
ae91d89f7c | |
![]() |
2e8cca9ca1 | |
![]() |
bb42c8b639 | |
![]() |
da8d413ad9 | |
![]() |
7caddf9615 | |
![]() |
e973236203 | |
![]() |
c84cb17462 | |
![]() |
ad7b1a8c61 | |
![]() |
346dd4006e | |
![]() |
651b0f680c | |
![]() |
679c37ddce | |
![]() |
544fefa27f | |
![]() |
f2d7a29015 | |
![]() |
e63e29ca91 | |
![]() |
4558be0d8e | |
![]() |
88072a02c5 | |
![]() |
3d3e4240a0 | |
![]() |
8b47178add | |
![]() |
b4ff911fa3 | |
![]() |
24a33e505e | |
![]() |
8fa9705079 | |
![]() |
2b3bba512e | |
![]() |
0e4d082361 | |
![]() |
df571078cf | |
![]() |
0aff9471c5 | |
![]() |
11c22f077d | |
![]() |
54ebf1b1b2 | |
![]() |
7837c169a1 | |
![]() |
8f1b0d4a05 | |
![]() |
2bd09eafeb | |
![]() |
76976fef30 | |
![]() |
ac38faef14 | |
![]() |
f188c2cf1a | |
![]() |
e4f4e94694 | |
![]() |
440b356674 | |
![]() |
70c6750561 | |
![]() |
5ccd3916ff | |
![]() |
2959d089fe | |
![]() |
bff7a0d114 | |
![]() |
eb13bce6e7 | |
![]() |
d11ba63bb0 | |
![]() |
3b4fdea376 | |
![]() |
b8963b0738 | |
![]() |
e89b8a871b | |
![]() |
9a450f5f28 | |
![]() |
21590b0709 | |
![]() |
8c4e5af737 | |
![]() |
bf81b87a60 | |
![]() |
3ab752b8ca | |
![]() |
3d58a338a1 | |
![]() |
d4e51f8060 | |
![]() |
b2fcff97c1 | |
![]() |
bb09ce7d70 | |
![]() |
f8fdc77e43 | |
![]() |
9d9ba83731 | |
![]() |
7d4a7969d9 | |
![]() |
6ca02aab2c | |
![]() |
5bfe51e670 | |
![]() |
db963aa002 | |
![]() |
1aaf22d181 | |
![]() |
2a3a071216 | |
![]() |
b4aa7fafc9 | |
![]() |
b4756c5944 | |
![]() |
bc71618457 | |
![]() |
2bd12669b2 | |
![]() |
a571fe19c3 | |
![]() |
d69745b3a8 | |
![]() |
dc8efb62b8 | |
![]() |
d7369857c3 | |
![]() |
758b5ee7a1 | |
![]() |
ee55677086 | |
![]() |
f454eba3c3 | |
![]() |
5f9e7e19f3 | |
![]() |
7731f66e55 | |
![]() |
3efbf1b813 | |
![]() |
8fd46e5f8c | |
![]() |
9c5cde8109 | |
![]() |
c3943b29ec | |
![]() |
b04c250e2f | |
![]() |
a87dc83279 | |
![]() |
c7afa8d3f6 | |
![]() |
193c7ed97b | |
![]() |
75ae38ec7b | |
![]() |
e6cc4b14b0 | |
![]() |
fbb41bd73d | |
![]() |
cf7375f9ea | |
![]() |
8772258fe7 | |
![]() |
24a4cacaae | |
![]() |
4b7c2ba8c2 | |
![]() |
a2033a62fd | |
![]() |
f432e559d4 | |
![]() |
ca5e8792c8 | |
![]() |
0947e941b8 | |
![]() |
d1ff103259 | |
![]() |
3c6359837d | |
![]() |
b659bce3c1 | |
![]() |
7b948e6ec9 | |
![]() |
52d6265b58 | |
![]() |
4b32fb802c | |
![]() |
74b166cba3 | |
![]() |
391ad8c95e | |
![]() |
ca7fb4f55e | |
![]() |
758db2b986 | |
![]() |
5f991d07a3 | |
![]() |
c6778bc6d5 | |
![]() |
787227b9ae | |
![]() |
e4586e53ad | |
![]() |
81c284b7cf | |
![]() |
38b0d21c22 | |
![]() |
4b170b94d5 | |
![]() |
3ada5b9bc8 | |
![]() |
e8967a937c | |
![]() |
a864f845e0 | |
![]() |
613280d3b4 | |
![]() |
350292fb3c | |
![]() |
41b95b9c8f | |
![]() |
5999fddccb | |
![]() |
b078ca802f | |
![]() |
808292e424 | |
![]() |
79dfb8dc8f | |
![]() |
19280cff2d | |
![]() |
4ba7a2c5e6 | |
![]() |
ca221381e9 | |
![]() |
382155e6fe | |
![]() |
00edeb3c3c | |
![]() |
35f4e504f1 | |
![]() |
105af5cf1a | |
![]() |
3e02d3ff76 | |
![]() |
d83172e595 | |
![]() |
8ec8a6ce09 | |
![]() |
eadfeec7ab | |
![]() |
86d2e60df7 | |
![]() |
bd9c614ceb | |
![]() |
43d091361e | |
![]() |
3111431a3f | |
![]() |
5488e0bf3d | |
![]() |
88c1371a96 | |
![]() |
2256b64fcd | |
![]() |
b069a2acf1 | |
![]() |
726fde4e8d | |
![]() |
9587d35d1d | |
![]() |
31a0d7ecd2 | |
![]() |
ca19ea1ce8 | |
![]() |
659b7214c7 | |
![]() |
9a5e884913 | |
![]() |
c387fb1819 | |
![]() |
430de6b270 | |
![]() |
019120ebb8 | |
![]() |
4ef2e1f4cc | |
![]() |
561781b954 | |
![]() |
936011964c | |
![]() |
9e449d902d | |
![]() |
b5017caa33 | |
![]() |
dd74e936ed | |
![]() |
b6776bd826 | |
![]() |
ac3118cbc5 | |
![]() |
31ba585d39 | |
![]() |
243a02767a | |
![]() |
cdd05c10b4 | |
![]() |
a8fba8cc34 | |
![]() |
349316a078 | |
![]() |
a83fbb6555 | |
![]() |
b33f0434a6 | |
![]() |
9a85bc7453 | |
![]() |
338b8b5bfe | |
![]() |
5e74fecb87 | |
![]() |
b76e5adc1d | |
![]() |
c177fae51f | |
![]() |
5b867eb7a7 | |
![]() |
aa9a6ceed5 | |
![]() |
1a602376c7 | |
![]() |
571d1c403f | |
![]() |
62879c4484 | |
![]() |
af9a77b0b4 | |
![]() |
3f4573574a | |
![]() |
c1c4cbc838 | |
![]() |
37e5e2c3c0 | |
![]() |
86786738cc | |
![]() |
fc11258071 | |
![]() |
b1e4bd5520 | |
![]() |
b5073e6014 | |
![]() |
993928095a | |
![]() |
61de50069b | |
![]() |
c3bd1d2e83 | |
![]() |
9d56055afb | |
![]() |
43b6ef1f60 | |
![]() |
686aecb36c | |
![]() |
21194375f8 | |
![]() |
926f200329 | |
![]() |
08f5a2aa82 | |
![]() |
76c9340920 | |
![]() |
80f76ef34d | |
![]() |
cac1ec8fbd | |
![]() |
d1dbb1f51e | |
![]() |
ce68e883c4 | |
![]() |
32609bbc98 | |
![]() |
d56219c33c | |
![]() |
22b312bc43 | |
![]() |
4c97731468 | |
![]() |
334fc1717d | |
![]() |
f0657e1a9b | |
![]() |
3ad5f3e22e | |
![]() |
5baf5fe77b | |
![]() |
2ba7f961a6 | |
![]() |
98903f8076 | |
![]() |
457dd56b88 | |
![]() |
f5c1094d03 | |
![]() |
35cbda9001 | |
![]() |
f85925fcd6 | |
![]() |
067b44fdfd | |
![]() |
5f581e82d4 | |
![]() |
94b33c924e | |
![]() |
6c7485d017 | |
![]() |
7f4f17de63 | |
![]() |
4cc34121b0 | |
![]() |
ed2121d7ec | |
![]() |
529a690089 | |
![]() |
0294bcb5f2 | |
![]() |
b572d8cd70 | |
![]() |
0feed13cc4 | |
![]() |
86f725f2f2 | |
![]() |
f6a55354fa | |
![]() |
879695070a | |
![]() |
1dfcf2010a | |
![]() |
d296f2832c | |
![]() |
02b859ad9d | |
![]() |
5ca8728cbf | |
![]() |
599deeb2b8 | |
![]() |
709a5980ed | |
![]() |
3756f3cb1f | |
![]() |
5415f8a746 | |
![]() |
d8f87df2b7 | |
![]() |
fb7bea06f8 | |
![]() |
89d33fedd2 | |
![]() |
f7fa0dda50 | |
![]() |
20c39eb3f1 | |
![]() |
cce9de5ee1 | |
![]() |
fe96944fc0 | |
![]() |
2eec033c72 | |
![]() |
532b1c967a | |
![]() |
9394dde67a | |
![]() |
2569c67a13 | |
![]() |
c3adf6f606 | |
![]() |
c66df57256 | |
![]() |
38c8b886ea | |
![]() |
ad7976c04e | |
![]() |
088d49b9cb | |
![]() |
b1565ec87a | |
![]() |
2af8e65f23 | |
![]() |
ef7dfb8872 | |
![]() |
f4f6f6dfc5 | |
![]() |
2c21787f33 | |
![]() |
550db94a87 | |
![]() |
e3ad78e84f | |
![]() |
bb67e186df | |
![]() |
0a1fb5ae50 | |
![]() |
03b5c48088 | |
![]() |
0761fe736f | |
![]() |
915edd777e | |
![]() |
2bda2f127b | |
![]() |
dff8980a27 | |
![]() |
35cc79787d | |
![]() |
0a8f3c9344 | |
![]() |
10d7831917 | |
![]() |
5c719a774f | |
![]() |
80564ba947 | |
![]() |
e665e25bd3 | |
![]() |
dfb2111a00 | |
![]() |
c4cd9da674 | |
![]() |
3d24057155 | |
![]() |
08a19d930c | |
![]() |
f39eeeb9dd | |
![]() |
203cf5164b | |
![]() |
f1c9b42b45 | |
![]() |
14c6bba21f | |
![]() |
151610eb6c | |
![]() |
3300cc8f15 | |
![]() |
44757a8323 | |
![]() |
c04e43702c | |
![]() |
97e599f90f | |
![]() |
e24a4e5e97 | |
![]() |
333b1247d9 | |
![]() |
73d7bada87 | |
![]() |
8100b6da6b | |
![]() |
7a0286a43d | |
![]() |
593eb7c81c | |
![]() |
1472a0ec4b | |
![]() |
1fc775d964 | |
![]() |
08eda46706 | |
![]() |
f73df85d1c | |
![]() |
19ddaee13b | |
![]() |
65c2a844ac | |
![]() |
e8bae0e146 | |
![]() |
3be26f456b | |
![]() |
d5b248bb86 | |
![]() |
13afaa0303 | |
![]() |
5bb8f4c922 | |
![]() |
69715043ca | |
![]() |
c1dcd585be | |
![]() |
53dfcfb18a | |
![]() |
c6d1977448 | |
![]() |
3fad5dcbbb | |
![]() |
139c009207 | |
![]() |
c233d99211 | |
![]() |
6c2ea93173 | |
![]() |
635a47014f | |
![]() |
cca5615748 | |
![]() |
ead8d1b629 | |
![]() |
8b59c73c01 | |
![]() |
d25102a9c8 | |
![]() |
c532059cd3 | |
![]() |
15a66b1be1 | |
![]() |
43daa1c7d2 | |
![]() |
e121953c9a | |
![]() |
bfe50e41b4 | |
![]() |
9d82826cdb | |
![]() |
ae489d9e03 | |
![]() |
ff3f661bb5 | |
![]() |
a8613af2bd | |
![]() |
e0052049b0 | |
![]() |
a9fc0adf96 | |
![]() |
d378b0252f | |
![]() |
b92622b765 | |
![]() |
b7992cc084 | |
![]() |
243077722b | |
![]() |
796d603332 | |
![]() |
737171c906 | |
![]() |
1471c73ea6 | |
![]() |
35e93d5fec | |
![]() |
f21ae77a01 | |
![]() |
66a58f7478 | |
![]() |
a54b076b06 | |
![]() |
8d8a46ad67 | |
![]() |
9514efe4a0 | |
![]() |
aed92e533a | |
![]() |
75b6ea339a | |
![]() |
55ec724fee | |
![]() |
8d2746e517 | |
![]() |
d20543c119 | |
![]() |
e1f3fc75ea | |
![]() |
e7f0923623 | |
![]() |
523552a92d | |
![]() |
b84155e891 | |
![]() |
34af921322 | |
![]() |
36672a4089 | |
![]() |
7395d6a6c0 | |
![]() |
b40ff12a60 | |
![]() |
235da420c8 | |
![]() |
346e8c0b87 | |
![]() |
0d37a0a5fc | |
![]() |
6ad0e8d61a | |
![]() |
883fceb6ce | |
![]() |
c23e158312 | |
![]() |
415ab22f3a | |
![]() |
2aeb452dfb | |
![]() |
418b351986 | |
![]() |
d5a20ad3c8 | |
![]() |
c7325469c2 | |
![]() |
bba14b2bb0 | |
![]() |
2df6b4fdc3 | |
![]() |
b28a9e4d24 | |
![]() |
f900792dc0 | |
![]() |
f792d3e6a1 | |
![]() |
f86ba0fcb3 | |
![]() |
806f081e9f | |
![]() |
996bfe8436 | |
![]() |
85c2a76774 | |
![]() |
946eb7a834 | |
![]() |
1bda85383f | |
![]() |
aaa97c9242 | |
![]() |
9ee2017443 | |
![]() |
6944fdbe78 | |
![]() |
c9b918d305 | |
![]() |
c2152c8f74 | |
![]() |
8513900892 | |
![]() |
b1c2665c39 | |
![]() |
a09ce44c48 | |
![]() |
cd4ef575ee | |
![]() |
89875204dd | |
![]() |
9b5d5f673c | |
![]() |
7aaee5ddb6 | |
![]() |
ebe8d544e1 | |
![]() |
3074ee06d7 | |
![]() |
efd7d62cec | |
![]() |
3bada15e46 | |
![]() |
a135557bcb | |
![]() |
ab48461dc5 | |
![]() |
740071e427 | |
![]() |
fdb6d226fd | |
![]() |
98072d82bf | |
![]() |
3a9e07c0b1 | |
![]() |
e5240a688c | |
![]() |
5a071c4c29 | |
![]() |
aa430608e7 | |
![]() |
dd53b01f76 | |
![]() |
dd2b86d026 | |
![]() |
5b9f972625 | |
![]() |
1cd477db71 | |
![]() |
f94b784c4d | |
![]() |
1793abcfb4 | |
![]() |
e3b4350f44 | |
![]() |
2494058a71 | |
![]() |
af36d10023 | |
![]() |
297563cd97 | |
![]() |
21f5477450 | |
![]() |
f74387a8c1 | |
![]() |
308e5df426 | |
![]() |
e48e45db76 | |
![]() |
9181ab19c7 | |
![]() |
f271bdf325 | |
![]() |
e02bbcb3ac | |
![]() |
4a39a84216 | |
![]() |
eb7bedfc79 | |
![]() |
509107fb59 | |
![]() |
6f91bcc39f | |
![]() |
d3c298bbe4 | |
![]() |
50721719d2 | |
![]() |
b7d5a7db75 | |
![]() |
41cd092a15 | |
![]() |
2ff065e5ea | |
![]() |
cc36f55b8c | |
![]() |
e2e7f65f9f | |
![]() |
464897ea87 | |
![]() |
d6036f9225 | |
![]() |
63415f0eeb | |
![]() |
6a655447d4 | |
![]() |
1004ff7fb3 | |
![]() |
b0e97b229e | |
![]() |
03bfbd2be7 | |
![]() |
33a1bd3ac3 | |
![]() |
f219318292 | |
![]() |
18b1f79b44 | |
![]() |
25ab02c650 | |
![]() |
414c608678 | |
![]() |
da9024e2b7 | |
![]() |
c2f37d44ce | |
![]() |
5e648a8db3 | |
![]() |
8a5078abe3 | |
![]() |
0a718adc77 | |
![]() |
0a3a2ad009 | |
![]() |
eae42fb9f8 | |
![]() |
cffffa887e | |
![]() |
65e9e161f3 | |
![]() |
7af5ff76ed | |
![]() |
d77d4ffc13 | |
![]() |
5a9063089c | |
![]() |
6691c6903b | |
![]() |
3bb78e4f1d | |
![]() |
a2c12f7e66 | |
![]() |
1afb8bfa46 | |
![]() |
bf4052bc3d | |
![]() |
4a5d5116a1 | |
![]() |
7ab19f157d | |
![]() |
55a129e1a2 | |
![]() |
f8bb1aa98d | |
![]() |
18497d396d | |
![]() |
185c97f4ef | |
![]() |
9d9865d256 | |
![]() |
00e4df7421 | |
![]() |
a860c5fbd3 | |
![]() |
bc4a156a4d | |
![]() |
e0cb998591 | |
![]() |
d3d5f45914 | |
![]() |
1846a71265 | |
![]() |
ede6e832d8 | |
![]() |
0834fc1533 | |
![]() |
def272fac4 | |
![]() |
19a0eb4e56 | |
![]() |
d1178a7514 | |
![]() |
ce6c3dbe38 | |
![]() |
68f52dcc4e | |
![]() |
9e20aa8a3e | |
![]() |
796ef95862 | |
![]() |
06e2193c04 | |
![]() |
d5dbef2c22 | |
![]() |
284a9b73b0 | |
![]() |
7e6cf61b4c | |
![]() |
2c2e868de0 | |
![]() |
ac49d8e25c | |
![]() |
6b431a6664 | |
![]() |
f7347b1f7a | |
![]() |
f060162dac | |
![]() |
fbe691a673 | |
![]() |
7f8a58b8fe | |
![]() |
436b3c4c1d | |
![]() |
a08f70e3a5 | |
![]() |
bc63531e00 | |
![]() |
ae7761c33e | |
![]() |
0ea85fdd2b | |
![]() |
6a3aa551da | |
![]() |
cb34032ac8 | |
![]() |
41dd448e28 | |
![]() |
a046eb5038 | |
![]() |
f8692f85a4 | |
![]() |
89051f63d4 | |
![]() |
81980d2836 | |
![]() |
68da2ace58 | |
![]() |
94dcc9523e | |
![]() |
58dd1ec580 | |
![]() |
97643586fa | |
![]() |
532dc57025 | |
![]() |
0aa0ae6c69 | |
![]() |
03b465c5e2 | |
![]() |
a8b2c22306 | |
![]() |
712919ec41 | |
![]() |
64c6654d94 | |
![]() |
295d60e4cb | |
![]() |
f1e0816c1a | |
![]() |
f05bc50d40 | |
![]() |
d63f7977f8 | |
![]() |
a7029aebae | |
![]() |
891427c75c | |
![]() |
e7ee3b7bc8 | |
![]() |
6256a42e00 | |
![]() |
1112162e99 | |
![]() |
2502c8d212 | |
![]() |
7b9b8418cb | |
![]() |
2e999ae1b8 | |
![]() |
40aae154cf | |
![]() |
b5e601bb88 | |
![]() |
0d301c2434 | |
![]() |
f9e701a719 | |
![]() |
a3f4aaf503 | |
![]() |
b75b3f69b7 | |
![]() |
4a28068295 | |
![]() |
aceabe92e6 | |
![]() |
b78bc4cb66 | |
![]() |
ab222ab135 | |
![]() |
54b1a752d4 | |
![]() |
43348210f9 | |
![]() |
b9a56bc4e4 | |
![]() |
536902d610 | |
![]() |
9994d3a644 | |
![]() |
34da7f5cc3 | |
![]() |
f259fd9755 | |
![]() |
5dd94f5ec1 | |
![]() |
faf7cf752d | |
![]() |
8a3a8b7c68 | |
![]() |
7e3d1058a6 | |
![]() |
1e4c0c9d72 | |
![]() |
d42ca1ec4b | |
![]() |
8a068c2294 | |
![]() |
bf0ea26596 | |
![]() |
1494d7aa24 | |
![]() |
3b994fe892 | |
![]() |
771dfaca2e | |
![]() |
31c9d116bd | |
![]() |
e8f06b8ac1 | |
![]() |
cd6859ef6f | |
![]() |
d529b650c0 | |
![]() |
1d6cc3c6ef | |
![]() |
53d5def919 | |
![]() |
8829b0511c | |
![]() |
c109235d01 | |
![]() |
b402cb19b2 | |
![]() |
30a0569c87 | |
![]() |
bf97387f26 | |
![]() |
6b306e18a5 | |
![]() |
c475372372 | |
![]() |
7081c2de65 | |
![]() |
e311eea1c5 | |
![]() |
1d8e302c13 | |
![]() |
641ddf8137 | |
![]() |
e485ce3e13 | |
![]() |
5865b4438e | |
![]() |
8a1f3d8ce2 | |
![]() |
00e9a5e0c7 | |
![]() |
ef75e3cdd1 | |
![]() |
dbb12b48ec | |
![]() |
25455cb7aa | |
![]() |
6e3207f9dc | |
![]() |
6c91157495 | |
![]() |
9e15488e95 | |
![]() |
83648f2d31 | |
![]() |
f070eafce4 | |
![]() |
cb58a422ac | |
![]() |
7d448d911d | |
![]() |
e2c61b28e0 | |
![]() |
18fe5c6759 | |
![]() |
fa4363ede6 | |
![]() |
162a0f4fb6 | |
![]() |
6adf1731c1 | |
![]() |
d2c04c5c51 | |
![]() |
1649c0e089 | |
![]() |
c32e83f140 | |
![]() |
b24e855fb8 | |
![]() |
78419dbce1 | |
![]() |
6b8738bb60 | |
![]() |
0be3f449a7 | |
![]() |
df190b0400 | |
![]() |
d6cade25f4 | |
![]() |
8d70d0926c | |
![]() |
a8851a51f1 | |
![]() |
240175f274 | |
![]() |
a73f3cc7b6 | |
![]() |
6d71f9c832 | |
![]() |
7e5eafe345 | |
![]() |
c351e777b4 | |
![]() |
a5d9f69127 | |
![]() |
7937406af6 | |
![]() |
7fb67570e9 | |
![]() |
c8e934ce97 | |
![]() |
fd4775c3ca | |
![]() |
96e0e37156 | |
![]() |
0009a3ffd1 | |
![]() |
523ff9ff1c |
|
@ -0,0 +1,18 @@
|
||||||
|
# Vendored Dependencies
|
||||||
|
src/frontend/glad/** linguist-vendored
|
||||||
|
src/frontend/qt_sdl/gif-h/** linguist-vendored
|
||||||
|
src/frontend/qt_sdl/toml/** linguist-vendored
|
||||||
|
src/net/libslirp/** linguist-vendored
|
||||||
|
src/net/pcap/** linguist-vendored
|
||||||
|
src/sha1/** linguist-vendored
|
||||||
|
src/teakra/** linguist-vendored
|
||||||
|
src/tiny-AES-c/** linguist-vendored
|
||||||
|
src/xxhash/** linguist-vendored
|
||||||
|
|
||||||
|
# A handful of custom files embedded in the vendored dependencies
|
||||||
|
|
||||||
|
## Ad-hoc CMakeLists.txt for melonDS
|
||||||
|
src/net/libslirp/src/CMakeLists.txt -linguist-vendored
|
||||||
|
|
||||||
|
## glib stub
|
||||||
|
src/net/libslirp/src/glib/** -linguist-vendored
|
|
@ -1 +1,2 @@
|
||||||
patreon: staplebutter
|
patreon: Arisotura
|
||||||
|
custom: ["https://paypal.me/Arisotura", "http://melonds.kuribo64.net/donate.php"]
|
||||||
|
|
|
@ -1,39 +1,94 @@
|
||||||
name: CMake Build (macOS x86-64)
|
name: macOS
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- ci/vcpkg-update
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_TYPE: Release
|
VCPKG_COMMIT: 6f29f12e82a8293156836ad81cc9bf5af41fe836
|
||||||
|
MELONDS_GIT_BRANCH: ${{ github.ref }}
|
||||||
|
MELONDS_GIT_HASH: ${{ github.sha }}
|
||||||
|
MELONDS_BUILD_PROVIDER: GitHub Actions
|
||||||
|
MELONDS_VERSION_SUFFIX: " RC"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build-macos:
|
||||||
|
strategy:
|
||||||
runs-on: macos-latest
|
matrix:
|
||||||
|
arch: [x86_64, arm64]
|
||||||
|
|
||||||
|
name: ${{ matrix.arch }}
|
||||||
|
runs-on: macos-14
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- name: Check out sources
|
||||||
- name: Install dependencies
|
uses: actions/checkout@v3
|
||||||
working-directory: ${{runner.workspace}}
|
- name: Install dependencies for package building
|
||||||
run: |
|
run: |
|
||||||
brew install cmake sdl2 qt5 libslirp
|
brew install autoconf automake autoconf-archive libtool python-setuptools
|
||||||
- name: Create build environment
|
- name: Set up CMake
|
||||||
run: mkdir ${{runner.workspace}}/build
|
uses: lukka/get-cmake@latest
|
||||||
- name: Configure
|
- name: Set up vcpkg
|
||||||
working-directory: ${{runner.workspace}}/build
|
uses: lukka/run-vcpkg@v11
|
||||||
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DQt5_DIR=$(brew --prefix qt5)/lib/cmake/Qt5
|
with:
|
||||||
- name: Make
|
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
|
||||||
working-directory: ${{runner.workspace}}/build
|
- name: Build
|
||||||
run: |
|
uses: lukka/run-cmake@v10
|
||||||
make -j$(sysctl -n hw.ncpu)
|
with:
|
||||||
mkdir dist
|
configurePreset: release-mac-${{ matrix.arch }}
|
||||||
cp -r melonDS.app dist
|
buildPreset: release-mac-${{ matrix.arch }}
|
||||||
- uses: actions/upload-artifact@v1
|
configurePresetAdditionalArgs: "['-DMELONDS_EMBED_BUILD_INFO=ON']"
|
||||||
with:
|
- name: Compress app bundle
|
||||||
name: melonDS.app
|
shell: bash
|
||||||
path: ${{runner.workspace}}/build/dist
|
run: |
|
||||||
|
cd build/release-mac-${{ matrix.arch }}
|
||||||
|
zip -r -y ../../macOS-${{ matrix.arch }}.zip melonDS.app
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: macOS-${{ matrix.arch }}
|
||||||
|
path: macOS-${{ matrix.arch }}.zip
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
universal-binary:
|
||||||
|
name: Universal binary
|
||||||
|
needs: [build-macos]
|
||||||
|
runs-on: macos-13
|
||||||
|
continue-on-error: true
|
||||||
|
steps:
|
||||||
|
- name: Download x86_64
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: macOS-x86_64
|
||||||
|
path: x86_64
|
||||||
|
- name: Download arm64
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: macOS-arm64
|
||||||
|
path: arm64
|
||||||
|
- name: Combine app bundles
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
unzip x86_64/*.zip -d x86_64
|
||||||
|
unzip arm64/*.zip -d arm64
|
||||||
|
lipo {x86_64,arm64}/melonDS.app/Contents/MacOS/melonDS -create -output melonDS
|
||||||
|
cp -a arm64/melonDS.app melonDS.app
|
||||||
|
cp melonDS melonDS.app/Contents/MacOS/melonDS
|
||||||
|
codesign -s - --deep melonDS.app
|
||||||
|
zip -r -y macOS-universal.zip melonDS.app
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: macOS-universal
|
||||||
|
path: macOS-universal.zip
|
||||||
|
# - name: Clean up architecture-specific artifacts
|
||||||
|
# uses: geekyeggo/delete-artifact@v4
|
||||||
|
# with:
|
||||||
|
# failOnError: false
|
||||||
|
# name: |
|
||||||
|
# macOS-x86_64
|
||||||
|
# macOS-arm64
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
name: CMake Build (Ubuntu aarch64)
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
env:
|
|
||||||
BUILD_TYPE: Release
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- name: Upgrade system
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{runner.workspace}}
|
|
||||||
run: | #Fix grub installation error - https://github.com/actions/virtual-environments/issues/1605
|
|
||||||
sudo apt update
|
|
||||||
sudo apt-get install grub-efi
|
|
||||||
sudo update-grub
|
|
||||||
sudo apt full-upgrade
|
|
||||||
- name: Install dependencies
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{runner.workspace}}
|
|
||||||
run: |
|
|
||||||
sudo rm -f /etc/apt/sources.list.d/kubernetes.list
|
|
||||||
sudo dpkg --add-architecture arm64
|
|
||||||
sudo sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new"
|
|
||||||
sudo rm /etc/apt/sources.list
|
|
||||||
sudo mv /etc/apt/sources.list{.new,}
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install aptitude
|
|
||||||
sudo aptitude install -y {gcc-10,g++-10,pkg-config}-aarch64-linux-gnu libsdl2-dev:arm64 qtbase5-dev:arm64 libslirp-dev:arm64
|
|
||||||
- name: Create build environment
|
|
||||||
run: mkdir ${{runner.workspace}}/build
|
|
||||||
- name: Configure
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: |
|
|
||||||
CC=aarch64-linux-gnu-gcc-10 CXX=aarch64-linux-gnu-g++-10 cmake -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE
|
|
||||||
- name: Make
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: |
|
|
||||||
make -j$(nproc --all) \
|
|
||||||
&& mkdir dist \
|
|
||||||
&& cp melonDS dist
|
|
||||||
- uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: melonDS
|
|
||||||
path: ${{runner.workspace}}/build/dist
|
|
|
@ -1,37 +1,63 @@
|
||||||
name: CMake Build (Ubuntu x86-64)
|
name: Ubuntu
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- ci/*
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
MELONDS_GIT_BRANCH: ${{ github.ref }}
|
||||||
|
MELONDS_GIT_HASH: ${{ github.sha }}
|
||||||
|
MELONDS_BUILD_PROVIDER: GitHub Actions
|
||||||
|
MELONDS_VERSION_SUFFIX: " RC"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
continue-on-error: true
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- runner: ubuntu-22.04
|
||||||
|
name: x86_64
|
||||||
|
- runner: ubuntu-22.04-arm
|
||||||
|
name: aarch64
|
||||||
|
|
||||||
runs-on: ubuntu-20.04
|
name: ${{ matrix.arch.name }}
|
||||||
|
runs-on: ${{ matrix.arch.runner }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
|
name: Check out sources
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list
|
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install cmake libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default libslirp0 libslirp-dev --allow-downgrades
|
sudo apt install --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev libenet-dev \
|
||||||
- name: Create build environment
|
qt6-{base,base-private,multimedia}-dev libqt6svg6-dev libarchive-dev libzstd-dev libfuse2
|
||||||
run: mkdir ${{runner.workspace}}/build
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
working-directory: ${{runner.workspace}}/build
|
run: cmake -B build -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DMELONDS_EMBED_BUILD_INFO=ON
|
||||||
run: cmake $GITHUB_WORKSPACE
|
- name: Build
|
||||||
- name: Make
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: |
|
run: |
|
||||||
make -j$(nproc --all)
|
cmake --build build
|
||||||
mkdir dist
|
DESTDIR=AppDir cmake --install build
|
||||||
cp melonDS dist
|
- uses: actions/upload-artifact@v4
|
||||||
- uses: actions/upload-artifact@v1
|
|
||||||
with:
|
with:
|
||||||
name: melonDS
|
name: melonDS-ubuntu-${{ matrix.arch.name }}
|
||||||
path: ${{runner.workspace}}/build/dist
|
path: AppDir/usr/bin/melonDS
|
||||||
|
- name: Fetch AppImage tools
|
||||||
|
run: |
|
||||||
|
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${{ matrix.arch.name }}.AppImage
|
||||||
|
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-${{ matrix.arch.name }}.AppImage
|
||||||
|
chmod a+x linuxdeploy-*.AppImage
|
||||||
|
- name: Build the AppImage
|
||||||
|
env:
|
||||||
|
QMAKE: /usr/lib/qt6/bin/qmake
|
||||||
|
run: |
|
||||||
|
./linuxdeploy-${{ matrix.arch.name }}.AppImage --appdir AppDir --plugin qt --output appimage
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: melonDS-appimage-${{ matrix.arch.name }}
|
||||||
|
path: melonDS*.AppImage
|
||||||
|
|
|
@ -1,43 +1,45 @@
|
||||||
name: CMake Build (Windows x86-64)
|
name: Windows
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- ci/*
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_TYPE: Release
|
VCPKG_COMMIT: 6f29f12e82a8293156836ad81cc9bf5af41fe836
|
||||||
|
MELONDS_GIT_BRANCH: ${{ github.ref }}
|
||||||
|
MELONDS_GIT_HASH: ${{ github.sha }}
|
||||||
|
MELONDS_BUILD_PROVIDER: GitHub Actions
|
||||||
|
MELONDS_VERSION_SUFFIX: " RC"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: msys2 {0}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- name: Check out sources
|
||||||
- name: Install MSYS2
|
uses: actions/checkout@v3
|
||||||
working-directory: ${{runner.workspace}}
|
- name: Set up MSYS2
|
||||||
run: | # Fetch MSYS2 build from XQEmu. Official distribution causes a CI failure due to permission errors.
|
uses: msys2/setup-msys2@v2
|
||||||
choco install msys2
|
|
||||||
C:\tools\msys64\usr\bin\bash.exe -lc "pacman -Syuq --noconfirm"
|
|
||||||
- name: Install dependencies
|
|
||||||
run: C:\tools\msys64\usr\bin\bash.exe -lc "pacman -Sq --noconfirm git make mingw-w64-x86_64-{cmake,mesa,SDL2,qt5-static,libslirp,toolchain}"
|
|
||||||
- name: Create build environment
|
|
||||||
run: |
|
|
||||||
New-Item -ItemType directory -Path ${{runner.workspace}}\melonDS\build
|
|
||||||
Copy-Item -Path ${{runner.workspace}}\melonDS -Destination C:\tools\msys64\home\runneradmin -Recurse
|
|
||||||
- name: Configure
|
|
||||||
run: |
|
|
||||||
C:\tools\msys64\usr\bin\bash.exe -lc "export PATH=`"/mingw64/bin:`$PATH`" \
|
|
||||||
&& cd melonDS/build && cmake .. -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_STATIC=ON -DQT5_STATIC_DIR=C:/tools/msys64/mingw64/qt5-static"
|
|
||||||
- name: Make
|
|
||||||
run: |
|
|
||||||
C:\tools\msys64\usr\bin\bash.exe -lc "export PATH=`"/mingw64/bin:`$PATH`" \
|
|
||||||
&& cd melonDS/build && make -j$(nproc --all)"
|
|
||||||
- uses: actions/upload-artifact@v1
|
|
||||||
with:
|
with:
|
||||||
name: melonDS
|
msystem: ucrt64
|
||||||
path: C:\tools\msys64\home\runneradmin\melonDS\build\melonDS.exe
|
update: true
|
||||||
|
pacboy: gcc:p cmake:p ninja:p make:p
|
||||||
|
- name: Set up vcpkg
|
||||||
|
uses: lukka/run-vcpkg@v11
|
||||||
|
with:
|
||||||
|
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
|
||||||
|
- name: Configure
|
||||||
|
run: cmake --preset=release-mingw-x86_64 -DMELONDS_EMBED_BUILD_INFO=ON
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build --preset=release-mingw-x86_64
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: melonDS-windows-x86_64
|
||||||
|
path: .\build\release-mingw-x86_64\melonDS.exe
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
build
|
build*/
|
||||||
bin
|
bin
|
||||||
obj
|
obj
|
||||||
*.depend
|
*.depend
|
||||||
|
@ -6,10 +6,18 @@ obj
|
||||||
*.o
|
*.o
|
||||||
melon_grc.c
|
melon_grc.c
|
||||||
melon_grc.h
|
melon_grc.h
|
||||||
cmake-build
|
melon.rc
|
||||||
|
cmake-build*
|
||||||
cmake-build-debug
|
cmake-build-debug
|
||||||
|
compile_commands.json
|
||||||
.idea
|
.idea
|
||||||
|
.cache
|
||||||
|
|
||||||
*.exe
|
*.exe
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
.vs
|
||||||
|
.vscode
|
||||||
|
CMakeFiles
|
||||||
|
CMakeCache.txt
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
# Building melonDS
|
||||||
|
|
||||||
|
* [Linux](#linux)
|
||||||
|
* [Windows](#windows)
|
||||||
|
* [macOS](#macos)
|
||||||
|
|
||||||
|
## Linux
|
||||||
|
1. Install dependencies:
|
||||||
|
* Ubuntu:
|
||||||
|
* All versions: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev libarchive-dev libenet-dev libzstd-dev`
|
||||||
|
* 24.04: `sudo apt install qt6-{base,base-private,multimedia,svg}-dev`
|
||||||
|
* 22.04: `sudo apt install qtbase6-dev qtbase6-private-dev qtmultimedia6-dev libqt6svg6-dev`
|
||||||
|
* Older versions: `sudo apt install qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libqt5svg5-dev`
|
||||||
|
Also add `-DUSE_QT6=OFF` to the first CMake command below.
|
||||||
|
* Fedora: `sudo dnf install gcc-c++ cmake extra-cmake-modules SDL2-devel libarchive-devel enet-devel libzstd-devel qt6-{qtbase,qtbase-private,qtmultimedia,qtsvg}-devel wayland-devel`
|
||||||
|
* Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt6-{base,multimedia,svg} libarchive enet zstd`
|
||||||
|
2. Download the melonDS repository and prepare:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/melonDS-emu/melonDS
|
||||||
|
cd melonDS
|
||||||
|
```
|
||||||
|
3. Compile:
|
||||||
|
```bash
|
||||||
|
cmake -B build
|
||||||
|
cmake --build build -j$(nproc --all)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Windows
|
||||||
|
1. Install [MSYS2](https://www.msys2.org/)
|
||||||
|
2. Open the MSYS2 terminal from the Start menu:
|
||||||
|
* For x64 systems (most common), use **MSYS2 UCRT64**
|
||||||
|
* For ARM64 systems, use **MSYS2 CLANGARM64**
|
||||||
|
3. Update the packages using `pacman -Syu` and reopen the same terminal if it asks you to
|
||||||
|
4. Install git and clone the repository
|
||||||
|
```bash
|
||||||
|
pacman -S git
|
||||||
|
git clone https://github.com/melonDS-emu/melonDS
|
||||||
|
cd melonDS
|
||||||
|
```
|
||||||
|
5. Install dependencies:
|
||||||
|
Replace `<prefix>` below with `mingw-w64-ucrt-x86_64` on x64 systems, or `mingw-w64-clang-aarch64` on ARM64 systems.
|
||||||
|
```bash
|
||||||
|
pacman -S <prefix>-{toolchain,cmake,SDL2,libarchive,enet,zstd}
|
||||||
|
```
|
||||||
|
6. Install Qt and configure the build directory
|
||||||
|
* Dynamic builds (with DLLs)
|
||||||
|
1. Install Qt: `pacman -S <prefix>-{qt6-base,qt6-svg,qt6-multimedia,qt6-svg,qt6-tools}`
|
||||||
|
2. Set up the build directory with `cmake -B build`
|
||||||
|
* Static builds (without DLLs, standalone executable)
|
||||||
|
1. Install Qt: `pacman -S <prefix>-qt5-static`
|
||||||
|
(Note: As of writing, the `qt6-static` package does not work.)
|
||||||
|
2. Set up the build directory with `cmake -B build -DBUILD_STATIC=ON -DUSE_QT6=OFF -DCMAKE_PREFIX_PATH=$MSYSTEM_PREFIX/qt5-static`
|
||||||
|
7. Compile: `cmake --build build`
|
||||||
|
|
||||||
|
If everything went well, melonDS should now be in the `build` folder. For dynamic builds, you may need to run melonDS from the MSYS2 terminal in order for it to find the required DLLs.
|
||||||
|
|
||||||
|
## macOS
|
||||||
|
1. Install the [Homebrew Package Manager](https://brew.sh)
|
||||||
|
2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libarchive enet zstd`
|
||||||
|
3. Download the melonDS repository and prepare:
|
||||||
|
```zsh
|
||||||
|
git clone https://github.com/melonDS-emu/melonDS
|
||||||
|
cd melonDS
|
||||||
|
```
|
||||||
|
4. Compile:
|
||||||
|
```zsh
|
||||||
|
cmake -B build -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)"
|
||||||
|
cmake --build build -j$(sysctl -n hw.logicalcpu)
|
||||||
|
```
|
||||||
|
If everything went well, melonDS.app should now be in the `build` directory.
|
||||||
|
|
||||||
|
### Self-contained app bundle
|
||||||
|
If you want an app bundle that can be distributed to other computers without needing to install dependencies through Homebrew, you can additionally run `
|
||||||
|
../tools/mac-libs.rb .` after the build is completed, or add `-DMACOS_BUNDLE_LIBS=ON` to the first CMake command.
|
||||||
|
|
||||||
|
## Nix (macOS/Linux)
|
||||||
|
|
||||||
|
melonDS provides a Nix flake with support for both macOS and Linux. The [Nix package manager](https://nixos.org) needs to be installed to use it.
|
||||||
|
|
||||||
|
* To run melonDS, just type `nix run github:melonDS-emu/melonDS`.
|
||||||
|
* To get a shell for development, clone the melonDS repository and type `nix develop` in its directory.
|
123
CMakeLists.txt
|
@ -1,28 +1,44 @@
|
||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
include(CheckSymbolExists)
|
cmake_policy(VERSION 3.15)
|
||||||
include(CheckLibraryExists)
|
|
||||||
|
|
||||||
cmake_policy(VERSION 3.13)
|
|
||||||
if (POLICY CMP0076)
|
if (POLICY CMP0076)
|
||||||
cmake_policy(SET CMP0076 NEW)
|
cmake_policy(SET CMP0076 NEW)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version")
|
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
|
||||||
|
|
||||||
project(melonDS CXX)
|
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
|
||||||
|
set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/DefaultBuildFlags.cmake")
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
option(USE_VCPKG "Use vcpkg for dependency packages" OFF)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
if (USE_VCPKG)
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
include(ConfigureVcpkg)
|
||||||
|
|
||||||
check_library_exists(m pow "" LIBM)
|
|
||||||
if(LIBM)
|
|
||||||
link_libraries(m)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT CMAKE_BUILD_TYPE)
|
project(melonDS
|
||||||
set(CMAKE_BUILD_TYPE Release)
|
VERSION 1.0
|
||||||
|
DESCRIPTION "DS emulator, sorta"
|
||||||
|
HOMEPAGE_URL "https://melonds.kuribo64.net"
|
||||||
|
LANGUAGES C CXX)
|
||||||
|
|
||||||
|
include(CheckSymbolExists)
|
||||||
|
include(CheckLibraryExists)
|
||||||
|
include(CMakeDependentOption)
|
||||||
|
include(CheckIPOSupported)
|
||||||
|
|
||||||
|
include(SetupCCache)
|
||||||
|
include(Sanitizers)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||||
|
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
|
||||||
|
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
function(detect_architecture symbol arch)
|
function(detect_architecture symbol arch)
|
||||||
|
@ -46,59 +62,40 @@ detect_architecture("__i386__" x86)
|
||||||
detect_architecture("__arm__" ARM)
|
detect_architecture("__arm__" ARM)
|
||||||
detect_architecture("__aarch64__" ARM64)
|
detect_architecture("__aarch64__" ARM64)
|
||||||
|
|
||||||
if (ARCHITECTURE STREQUAL x86_64 OR ARCHITECTURE STREQUAL ARM64)
|
cmake_dependent_option(ENABLE_JIT "Enable JIT recompiler" ON
|
||||||
option(ENABLE_JIT "Enable x64 JIT recompiler" ON)
|
"ARCHITECTURE STREQUAL x86_64 OR ARCHITECTURE STREQUAL ARM64" OFF)
|
||||||
endif()
|
cmake_dependent_option(ENABLE_JIT_PROFILING "Enable JIT profiling with VTune" OFF "ENABLE_JIT" OFF)
|
||||||
|
|
||||||
if (ENABLE_JIT)
|
|
||||||
add_definitions(-DJIT_ENABLED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL Release)
|
|
||||||
option(ENABLE_LTO "Enable link-time optimization" ON)
|
|
||||||
else()
|
|
||||||
option(ENABLE_LTO "Enable link-time optimization" OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(ENABLE_OGLRENDERER "Enable OpenGL renderer" ON)
|
option(ENABLE_OGLRENDERER "Enable OpenGL renderer" ON)
|
||||||
|
|
||||||
if (ENABLE_OGLRENDERER)
|
check_ipo_supported(RESULT IPO_SUPPORTED)
|
||||||
add_definitions(-DOGLRENDERER_ENABLED)
|
cmake_dependent_option(ENABLE_LTO_RELEASE "Enable link-time optimizations for release builds" ON "IPO_SUPPORTED" OFF)
|
||||||
endif()
|
cmake_dependent_option(ENABLE_LTO "Enable link-time optimizations" OFF "IPO_SUPPORTED" OFF)
|
||||||
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL Debug)
|
if (ENABLE_LTO_RELEASE)
|
||||||
add_compile_options(-Og)
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
|
||||||
endif()
|
|
||||||
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL Release)
|
|
||||||
add_compile_options(-O3)
|
|
||||||
add_link_options(-s)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (WIN32)
|
|
||||||
option(BUILD_STATIC "Statically link dependencies" OFF)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_LTO)
|
if (ENABLE_LTO)
|
||||||
if (WIN32)
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||||
add_compile_options(-flto)
|
|
||||||
add_link_options(-flto)
|
|
||||||
else()
|
|
||||||
add_compile_options(-flto -fPIC)
|
|
||||||
add_link_options(-flto -fuse-linker-plugin -pie)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
if (NOT APPLE)
|
||||||
set(CMAKE_AR "gcc-ar")
|
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s")
|
||||||
set(CMAKE_RANLIB "gcc-ranlib")
|
endif()
|
||||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
|
||||||
find_program(LLD NAMES ld.lld ld64.lld lld-link)
|
if (WIN32)
|
||||||
if (NOT LLD STREQUAL "LLD-NOTFOUND")
|
option(BUILD_STATIC "Statically link dependencies" OFF)
|
||||||
add_link_options(-fuse-ld=lld)
|
endif()
|
||||||
endif()
|
|
||||||
set(CMAKE_AR "llvm-ar")
|
if (BUILD_STATIC AND WIN32)
|
||||||
set(CMAKE_RANLIB "llvm-ranlib")
|
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
|
option(ENABLE_GDBSTUB "Enable GDB stub" ON)
|
||||||
|
if (ENABLE_GDBSTUB)
|
||||||
|
add_definitions(-DGDBSTUB_ENABLED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
|
option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
|
||||||
|
@ -106,5 +103,5 @@ option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
if (BUILD_QT_SDL)
|
if (BUILD_QT_SDL)
|
||||||
add_subdirectory(src/frontend/qt_sdl)
|
add_subdirectory(src/frontend/qt_sdl)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
{
|
||||||
|
"version": 6,
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "release",
|
||||||
|
"displayName": "Release",
|
||||||
|
"description": "Default release build configuration.",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/build/release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inherits": "release",
|
||||||
|
"name": "release-vcpkg",
|
||||||
|
"displayName": "Release (vcpkg)",
|
||||||
|
"description": "Release build with packages from vcpkg.",
|
||||||
|
"cacheVariables": {
|
||||||
|
"USE_VCPKG": {
|
||||||
|
"type": "BOOL",
|
||||||
|
"value": "ON"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release-mingw-x86_64",
|
||||||
|
"inherits": "release-vcpkg",
|
||||||
|
"displayName": "Windows MinGW release (x86_64)",
|
||||||
|
"binaryDir": "${sourceDir}/build/release-mingw-x86_64",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"cacheVariables": {
|
||||||
|
"BUILD_STATIC": {
|
||||||
|
"type": "BOOL",
|
||||||
|
"value": "ON"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release-mac-x86_64",
|
||||||
|
"inherits": "release-vcpkg",
|
||||||
|
"displayName": "macOS release (x86_64)",
|
||||||
|
"binaryDir": "${sourceDir}/build/release-mac-x86_64",
|
||||||
|
"cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "x86_64" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release-mac-arm64",
|
||||||
|
"inherits": "release-vcpkg",
|
||||||
|
"displayName": "macOS release (arm64)",
|
||||||
|
"binaryDir": "${sourceDir}/build/release-mac-arm64",
|
||||||
|
"cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "arm64" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buildPresets": [
|
||||||
|
{
|
||||||
|
"name": "release",
|
||||||
|
"configurePreset": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release-vcpkg",
|
||||||
|
"configurePreset": "release-vcpkg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release-mingw-x86_64",
|
||||||
|
"configurePreset": "release-mingw-x86_64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release-mac-x86_64",
|
||||||
|
"configurePreset": "release-mac-x86_64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release-mac-arm64",
|
||||||
|
"configurePreset": "release-mac-arm64"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"workflowPresets": [
|
||||||
|
{
|
||||||
|
"name": "release",
|
||||||
|
"displayName": "Release",
|
||||||
|
"steps": [
|
||||||
|
{ "type": "configure", "name": "release" },
|
||||||
|
{ "type": "build", "name": "release" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release-vcpkg",
|
||||||
|
"displayName": "Release (vcpkg)",
|
||||||
|
"steps": [
|
||||||
|
{ "type": "configure", "name": "release-vcpkg" },
|
||||||
|
{ "type": "build", "name": "release-vcpkg" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release-mac-x86_64",
|
||||||
|
"steps": [
|
||||||
|
{ "type": "configure", "name": "release-mac-x86_64" },
|
||||||
|
{ "type": "build", "name": "release-mac-x86_64" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "release-mac-arm64",
|
||||||
|
"steps": [
|
||||||
|
{ "type": "configure", "name": "release-mac-arm64" },
|
||||||
|
{ "type": "build", "name": "release-mac-arm64" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
# Contributor guide for melonDS
|
||||||
|
|
||||||
|
Please follow a style as documented here. Note that this guide was not always enforced, so some parts of the code violate it.
|
||||||
|
|
||||||
|
Files should use 4 spaces for indentation.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
|
||||||
|
// for single line comments prefer C++ style
|
||||||
|
|
||||||
|
/*
|
||||||
|
for multiline comments
|
||||||
|
both C style comments
|
||||||
|
*/
|
||||||
|
// as well as
|
||||||
|
// C++ style comments are possible
|
||||||
|
|
||||||
|
// when including headers from the C standard library use their C names (so don't use cstdio/cstring, ...)
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// namespaces in PascalCase
|
||||||
|
namespace Component
|
||||||
|
{ // for all constructs curly braces go onto the following line
|
||||||
|
|
||||||
|
// the content of namespaces should not be indented
|
||||||
|
|
||||||
|
int GlobalVariable; // in PascalCase
|
||||||
|
|
||||||
|
// function names should use PascalCase, parameters camelCase:
|
||||||
|
void Test(int someParam)
|
||||||
|
{
|
||||||
|
int variable = someParam * 2; // local variables in camelCase
|
||||||
|
|
||||||
|
// you can slightly vary the spacing around operators:
|
||||||
|
int variable2 = someParam*2 + 1;
|
||||||
|
// but avoid something like this: someParam* 2+ 3
|
||||||
|
|
||||||
|
for (int i = 0; i < variable; i++) // always a space between if/for/while and the braces
|
||||||
|
{
|
||||||
|
// not using curly braces is allowed
|
||||||
|
// if the body of the if/for/while is simple:
|
||||||
|
if ((i % 2) == 0)
|
||||||
|
printf("%d\n", i); // no space between the function name and the braces
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defines should always be in CAPITALISED_SNAKE_CASE
|
||||||
|
#ifdef SOME_CONFIGURATION
|
||||||
|
|
||||||
|
// the content of #ifdef sections should not be indented
|
||||||
|
// the only exception being otherwise hard to read nested sections of #ifdef/#if/#defines
|
||||||
|
// as e.g. in ARMJIT_Memory.cpp
|
||||||
|
|
||||||
|
// prefer #ifdef/#ifndef, only use if defined(...) for complex expressions like this:
|
||||||
|
#if defined(THIS) || defined(THAT)
|
||||||
|
// ...
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class MyClass // PascalCase
|
||||||
|
{
|
||||||
|
public: // access specfications are not indented
|
||||||
|
void Test(int param) // for methods the same rules apply as for functions
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int MemberVariable; // PascalCase, no prefix
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
// enums should always have a common prefix in camelCase
|
||||||
|
// separated by an underscore with the item name
|
||||||
|
// which has to be in PascalCase
|
||||||
|
enumPrefix_FirstElement,
|
||||||
|
enumPrefix_SecondElement,
|
||||||
|
enumPrefix_ThirdElement,
|
||||||
|
enumPrefix_FourthElement,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Some additional notes:
|
||||||
|
|
||||||
|
* Keep the definition and initialisation of local variables in one place and keep the scope of local variables as small as possible.
|
||||||
|
|
||||||
|
**That means avoid code like this**:
|
||||||
|
```cpp
|
||||||
|
void ColorConvert(u32* dst, u16* vram)
|
||||||
|
{
|
||||||
|
u16 color;
|
||||||
|
u8 r, g, b;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
color = vram[i];
|
||||||
|
r = (color & 0x001F) << 1;
|
||||||
|
g = (color & 0x03E0) >> 4;
|
||||||
|
b = (color & 0x7C00) >> 9;
|
||||||
|
|
||||||
|
dst[i] = r | (g << 8) | (b << 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Do this instead:**
|
||||||
|
```cpp
|
||||||
|
void ColorConvert(u32* dst, u16* vram)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
u16 color = vram[i];
|
||||||
|
u8 r = (color & 0x001F) << 1;
|
||||||
|
u8 g = (color & 0x03E0) >> 4;
|
||||||
|
u8 b = (color & 0x7C00) >> 9;
|
||||||
|
|
||||||
|
dst[i] = r | (g << 8) | (b << 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* For integer types preferably use the explictly typed ones. We have short aliases for them defined in types.h (for unsigned types: `u8`, `u16`, `u32`, `u16`. For signed `s8`, `s16`, `s32`, `s64`). In some situations like loop variables, using `int` is possible as well.
|
||||||
|
* Don't overdo object oriented programming. Always try to use a simpler construct first, only use a polymorphic class if a namespace with functions in it doesn't cut it.
|
||||||
|
|
||||||
|
* In doubt put a namespace around your part of the code.
|
||||||
|
|
||||||
|
* C style strings (and the associated functions from the C standard library) are used in most places. We are thinking about changing this, as C strings are a bit of a hassle to deal with, but for the time being this is what we use.
|
||||||
|
|
||||||
|
* Only the C standard IO is used (so use `printf`, `fopen`, … Do not use `std::cout`/`std::ostream`, …).
|
||||||
|
|
||||||
|
* Complex C++ containers can be used (`std::vector`, `std::list`, `std::map`, …). `std::array` is usually not used, unless necessary so that the container can be used with other C++ constructs (e.g. `<algorithm>`). Only use them if a C array doesn't cut it.
|
||||||
|
|
||||||
|
* File names should be in PascalCase
|
||||||
|
|
||||||
|
* If a header file is called MyHeader.h it should be guarded with an ifdef like this:
|
||||||
|
```cpp
|
||||||
|
#ifndef MYHEADER_H
|
||||||
|
#define MYHEADER_H
|
||||||
|
// ...
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
* And at last, if you have any questions, visit us on IRC (see the readme)!
|
100
README.md
|
@ -1,10 +1,15 @@
|
||||||
<p align="center"><img src="https://raw.githubusercontent.com/StapleButter/melonDS/master/icon/melon_128x128.png"></p>
|
<p align="center"><img src="https://raw.githubusercontent.com/melonDS-emu/melonDS/master/res/icon/melon_128x128.png"></p>
|
||||||
<h2 align="center"><b>melonDS</b></h2>
|
<h2 align="center"><b>melonDS</b></h2>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="http://melonds.kuribo64.net/" alt="melonDS website"><img src="https://img.shields.io/badge/website-melonds.kuribo64.net-%2331352e.svg"></a>
|
<a href="http://melonds.kuribo64.net/" alt="melonDS website"><img src="https://img.shields.io/badge/website-melonds.kuribo64.net-%2331352e.svg"></a>
|
||||||
<a href="http://melonds.kuribo64.net/downloads.php" alt="Release: 0.9.1"><img src="https://img.shields.io/badge/release-0.9.1-%235c913b.svg"></a>
|
<a href="http://melonds.kuribo64.net/downloads.php" alt="Release: 0.9.5"><img src="https://img.shields.io/badge/release-0.9.5-%235c913b.svg"></a>
|
||||||
<a href="https://www.gnu.org/licenses/gpl-3.0" alt="License: GPLv3"><img src="https://img.shields.io/badge/License-GPL%20v3-%23ff554d.svg"></a>
|
<a href="https://www.gnu.org/licenses/gpl-3.0" alt="License: GPLv3"><img src="https://img.shields.io/badge/License-GPL%20v3-%23ff554d.svg"></a>
|
||||||
<a href="https://kiwiirc.com/client/irc.badnik.net/?nick=IRC-Source_?#melonds" alt="IRC channel: #melonds"><img src="https://img.shields.io/badge/IRC%20chat-%23melonds-%23dd2e44.svg"></a>
|
<a href="https://kiwiirc.com/client/irc.badnik.net/?nick=IRC-Source_?#melonds" alt="IRC channel: #melonds"><img src="https://img.shields.io/badge/IRC%20chat-%23melonds-%23dd2e44.svg"></a>
|
||||||
|
<a href="https://discord.gg/pAMAtExcqV" alt="Discord"><img src="https://img.shields.io/badge/Discord-Kuribo64-7289da?logo=discord&logoColor=white"></a>
|
||||||
|
<br>
|
||||||
|
<a href="https://github.com/melonDS-emu/melonDS/actions/workflows/build-windows.yml?query=event%3Apush"><img src="https://github.com/melonDS-emu/melonDS/actions/workflows/build-windows.yml/badge.svg" /></a>
|
||||||
|
<a href="https://github.com/melonDS-emu/melonDS/actions/workflows/build-ubuntu.yml?query=event%3Apush"><img src="https://github.com/melonDS-emu/melonDS/actions/workflows/build-ubuntu.yml/badge.svg" /></a>
|
||||||
|
<a href="https://github.com/melonDS-emu/melonDS/actions/workflows/build-macos.yml?query=event%3Apush"><img src="https://github.com/melonDS-emu/melonDS/actions/workflows/build-macos.yml/badge.svg" /></a>
|
||||||
</p>
|
</p>
|
||||||
DS emulator, sorta
|
DS emulator, sorta
|
||||||
|
|
||||||
|
@ -13,12 +18,7 @@ The goal is to do things right and fast, akin to blargSNES (but hopefully better
|
||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
|
|
||||||
melonDS requires BIOS/firmware copies from a DS. Files required:
|
Firmware boot (not direct boot) requires a BIOS/firmware dump from an original DS or DS Lite.
|
||||||
* bios7.bin, 16KB: ARM7 BIOS
|
|
||||||
* bios9.bin, 4KB: ARM9 BIOS
|
|
||||||
* firmware.bin, 128/256/512KB: firmware
|
|
||||||
|
|
||||||
Firmware boot requires a firmware dump from an original DS or DS Lite.
|
|
||||||
DS firmwares dumped from a DSi or 3DS aren't bootable and only contain configuration data, thus they are only suitable when booting games directly.
|
DS firmwares dumped from a DSi or 3DS aren't bootable and only contain configuration data, thus they are only suitable when booting games directly.
|
||||||
|
|
||||||
### Possible firmware sizes
|
### Possible firmware sizes
|
||||||
|
@ -32,86 +32,20 @@ DS BIOS dumps from a DSi or 3DS can be used with no compatibility issues. DSi BI
|
||||||
As for the rest, the interface should be pretty straightforward. If you have a question, don't hesitate to ask, though!
|
As for the rest, the interface should be pretty straightforward. If you have a question, don't hesitate to ask, though!
|
||||||
|
|
||||||
## How to build
|
## How to build
|
||||||
|
See [BUILD.md](./BUILD.md) for build instructions.
|
||||||
|
|
||||||
### Linux:
|
|
||||||
|
|
||||||
* Install dependencies:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo apt-get install cmake libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qtbase5-dev qtdeclarative5-dev libslirp-dev
|
|
||||||
```
|
|
||||||
|
|
||||||
* Compile:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
mkdir -p build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make -j$(nproc --all)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Windows:
|
|
||||||
|
|
||||||
1. Install [MSYS2](https://www.msys2.org/)
|
|
||||||
2. Open the **MSYS2 MinGW 64-bit** terminal
|
|
||||||
3. Update the packages using `pacman -Syu` and reopen the terminal if it asks you to
|
|
||||||
|
|
||||||
#### Dynamic builds (with DLLs)
|
|
||||||
4. Install dependencies: `pacman -S git make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain,qt5,libslirp}`
|
|
||||||
5. Run the following commands
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/Arisotura/melonDS.git
|
|
||||||
cd melonDS
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -G "MSYS Makefiles"
|
|
||||||
make -j$(nproc --all)
|
|
||||||
../msys-dist.sh
|
|
||||||
```
|
|
||||||
If everything went well, melonDS and the libraries it needs should now be in the `dist` folder.
|
|
||||||
|
|
||||||
#### Static builds (without DLLs, standalone executable)
|
|
||||||
4. Install dependencies: `pacman -S git make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain,qt5-static,libslirp}`
|
|
||||||
5. Run the following commands
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/Arisotura/melonDS.git
|
|
||||||
cd melonDS
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -G 'MSYS Makefiles' -DBUILD_STATIC=ON -DQT5_STATIC_DIR=/mingw64/qt5-static
|
|
||||||
make -j$(nproc --all)
|
|
||||||
mkdir dist && cp melonDS.exe dist
|
|
||||||
```
|
|
||||||
If everything went well, melonDS should now be in the `dist` folder.
|
|
||||||
|
|
||||||
### macOS:
|
|
||||||
1. Install the [Homebrew Package Manager](https://brew.sh)
|
|
||||||
2. Install dependencies: `brew install git pkg-config cmake sdl2 qt5 libslirp`
|
|
||||||
3. Compile:
|
|
||||||
```zsh
|
|
||||||
git clone https://github.com/Arisotura/melonDS.git
|
|
||||||
cd melonDS
|
|
||||||
mkdir build && cd build
|
|
||||||
cmake .. -DQt5_DIR=$(brew --prefix qt5)/lib/cmake/Qt5
|
|
||||||
make -j$(sysctl -n hw.ncpu)
|
|
||||||
mkdir dist && cp -r melonDS.app dist
|
|
||||||
```
|
|
||||||
If everything went well, melonDS.app should now be in the `dist` folder.
|
|
||||||
|
|
||||||
|
|
||||||
## TODO LIST
|
## TODO LIST
|
||||||
|
|
||||||
* DSi emulation
|
* better DSi emulation
|
||||||
|
* better OpenGL rendering
|
||||||
|
* netplay
|
||||||
* the impossible quest of pixel-perfect 3D graphics
|
* the impossible quest of pixel-perfect 3D graphics
|
||||||
* improve libui and the emulator UI
|
|
||||||
* support for rendering screens to separate windows
|
* support for rendering screens to separate windows
|
||||||
* emulating some fancy addons
|
* emulating some fancy addons
|
||||||
* other non-core shit (debugger, graphics viewers, cheat crapo, etc)
|
* other non-core shit (debugger, graphics viewers, etc)
|
||||||
|
|
||||||
### TODO LIST FOR LATER
|
### TODO LIST FOR LATER (low priority)
|
||||||
|
|
||||||
* better wifi
|
|
||||||
* maybe emulate flashcarts or other fancy hardware
|
|
||||||
* big-endian compatibility (Wii, etc)
|
* big-endian compatibility (Wii, etc)
|
||||||
* LCD refresh time (used by some games for blending effects)
|
* LCD refresh time (used by some games for blending effects)
|
||||||
* any feature you can eventually ask for that isn't outright stupid
|
* any feature you can eventually ask for that isn't outright stupid
|
||||||
|
@ -123,10 +57,14 @@ If everything went well, melonDS.app should now be in the `dist` folder.
|
||||||
* limittox for the icon
|
* limittox for the icon
|
||||||
* All of you comrades who have been testing melonDS, reporting issues, suggesting shit, etc
|
* All of you comrades who have been testing melonDS, reporting issues, suggesting shit, etc
|
||||||
|
|
||||||
## License
|
## Licenses
|
||||||
|
|
||||||
[](http://www.gnu.org/licenses/gpl-3.0.en.html)
|
[](http://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||||
|
|
||||||
melonDS is free software: you can redistribute it and/or modify
|
melonDS is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
|
### External
|
||||||
|
* Images used in the Input Config Dialog - see `src/frontend/qt_sdl/InputConfig/resources/LICENSE.md`
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
set(_DEFAULT_VCPKG_ROOT "${CMAKE_SOURCE_DIR}/vcpkg")
|
||||||
|
set(VCPKG_ROOT "${_DEFAULT_VCPKG_ROOT}" CACHE STRING "The path to the vcpkg repository")
|
||||||
|
|
||||||
|
if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}")
|
||||||
|
if (APPLE) # this doesn't work on non-macOS
|
||||||
|
file(LOCK "${_DEFAULT_VCPKG_ROOT}" DIRECTORY GUARD FILE)
|
||||||
|
endif()
|
||||||
|
FetchContent_Declare(vcpkg
|
||||||
|
GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git"
|
||||||
|
GIT_TAG 2025.01.13
|
||||||
|
SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg")
|
||||||
|
FetchContent_MakeAvailable(vcpkg)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_SOURCE_DIR}/cmake/overlay-triplets")
|
||||||
|
|
||||||
|
option(USE_RECOMMENDED_TRIPLETS "Use the recommended triplets that are used for official builds" ON)
|
||||||
|
|
||||||
|
# Duplicated here because it needs to be set before project()
|
||||||
|
option(USE_QT6 "Use Qt 6 instead of Qt 5" ON)
|
||||||
|
|
||||||
|
# Since the Linux build pulls in glib anyway, we can just use upstream libslirp
|
||||||
|
if (UNIX AND NOT APPLE)
|
||||||
|
option(USE_SYSTEM_LIBSLIRP "Use system libslirp instead of the bundled version" ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT USE_QT6)
|
||||||
|
list(APPEND VCPKG_MANIFEST_FEATURES qt5)
|
||||||
|
set(VCPKG_MANIFEST_NO_DEFAULT_FEATURES ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_OSX_ARCHITECTURES MATCHES ";")
|
||||||
|
message(FATAL_ERROR "macOS universal builds are not supported. Build them individually and combine afterwards instead.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (USE_RECOMMENDED_TRIPLETS)
|
||||||
|
execute_process(
|
||||||
|
COMMAND uname -m
|
||||||
|
OUTPUT_VARIABLE _HOST_PROCESSOR
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
|
||||||
|
set(_CAN_TARGET_AS_HOST OFF)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
if (NOT CMAKE_OSX_ARCHITECTURES)
|
||||||
|
if (_HOST_PROCESSOR STREQUAL arm64)
|
||||||
|
set(CMAKE_OSX_ARCHITECTURES arm64)
|
||||||
|
else()
|
||||||
|
set(CMAKE_OSX_ARCHITECTURES x86_64)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_OSX_ARCHITECTURES STREQUAL arm64)
|
||||||
|
set(_WANTED_TRIPLET arm64-osx-11-release)
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0)
|
||||||
|
else()
|
||||||
|
set(_WANTED_TRIPLET x64-osx-1015-release)
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||||
|
endif()
|
||||||
|
elseif(WIN32)
|
||||||
|
# TODO Windows arm64 if possible
|
||||||
|
set(_CAN_TARGET_AS_HOST ON)
|
||||||
|
set(_WANTED_TRIPLET x64-mingw-static-release)
|
||||||
|
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux)
|
||||||
|
# Can't really detect cross compiling here.
|
||||||
|
set(_CAN_TARGET_AS_HOST ON)
|
||||||
|
if (_HOST_PROCESSOR STREQUAL x86_64)
|
||||||
|
set(_WANTED_TRIPLET x64-linux-release)
|
||||||
|
elseif(_HOST_PROCESSOR STREQUAL "aarch64")
|
||||||
|
set(_WANTED_TRIPLET arm64-linux-release)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Don't override it if the user set something else
|
||||||
|
if (NOT VCPKG_TARGET_TRIPLET)
|
||||||
|
set(VCPKG_TARGET_TRIPLET "${_WANTED_TRIPLET}")
|
||||||
|
else()
|
||||||
|
set(_WANTED_TRIPLET "${VCPKG_TARGET_TRIPLET}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
if (_HOST_PROCESSOR MATCHES arm64)
|
||||||
|
if (_WANTED_TRIPLET MATCHES "^arm64-osx-")
|
||||||
|
set(_CAN_TARGET_AS_HOST ON)
|
||||||
|
elseif (_WANTED_TRIPLET STREQUAL "x64-osx-1015-release")
|
||||||
|
# Use the default triplet for when building for arm64
|
||||||
|
# because we're probably making a universal build
|
||||||
|
set(VCPKG_HOST_TRIPLET arm64-osx-11-release)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
if (_WANTED_TRIPLET MATCHES "^x64-osx-")
|
||||||
|
set(_CAN_TARGET_AS_HOST ON)
|
||||||
|
elseif (_WANTED_TRIPLET STREQUAL "arm64-osx-11-release")
|
||||||
|
set(VCPKG_HOST_TRIPLET x64-osx-1015-release)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# If host and target triplet differ, vcpkg seems to always assume that the host can't run the target's binaries.
|
||||||
|
# In cases like cross compiling from ARM -> Intel macOS, or target being an older version of the host OS, we *can* do that so the packages built targeting the host are redundant.
|
||||||
|
if (_CAN_TARGET_AS_HOST AND NOT VCPKG_HOST_TRIPLET)
|
||||||
|
option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" ON)
|
||||||
|
else()
|
||||||
|
option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (VCPKG_TARGET_AS_HOST)
|
||||||
|
set(VCPKG_HOST_TRIPLET "${VCPKG_TARGET_TRIPLET}" CACHE STRING "Host triplet to use for vcpkg")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")
|
|
@ -0,0 +1,9 @@
|
||||||
|
if (CMAKE_C_COMPILER_ID STREQUAL GNU)
|
||||||
|
set(CMAKE_C_FLAGS_DEBUG_INIT "-g -Og")
|
||||||
|
endif()
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-g -Og")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE_INIT "${CMAKE_C_FLAGS_RELEASE_INIT}")
|
||||||
|
string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE_INIT "${CMAKE_CXX_FLAGS_RELEASE_INIT}")
|
|
@ -0,0 +1,297 @@
|
||||||
|
#.rst:
|
||||||
|
# ECMFindModuleHelpers
|
||||||
|
# --------------------
|
||||||
|
#
|
||||||
|
# Helper macros for find modules: ecm_find_package_version_check(),
|
||||||
|
# ecm_find_package_parse_components() and
|
||||||
|
# ecm_find_package_handle_library_components().
|
||||||
|
#
|
||||||
|
# ::
|
||||||
|
#
|
||||||
|
# ecm_find_package_version_check(<name>)
|
||||||
|
#
|
||||||
|
# Prints warnings if the CMake version or the project's required CMake version
|
||||||
|
# is older than that required by extra-cmake-modules.
|
||||||
|
#
|
||||||
|
# ::
|
||||||
|
#
|
||||||
|
# ecm_find_package_parse_components(<name>
|
||||||
|
# RESULT_VAR <variable>
|
||||||
|
# KNOWN_COMPONENTS <component1> [<component2> [...]]
|
||||||
|
# [SKIP_DEPENDENCY_HANDLING])
|
||||||
|
#
|
||||||
|
# This macro will populate <variable> with a list of components found in
|
||||||
|
# <name>_FIND_COMPONENTS, after checking that all those components are in the
|
||||||
|
# list of KNOWN_COMPONENTS; if there are any unknown components, it will print
|
||||||
|
# an error or warning (depending on the value of <name>_FIND_REQUIRED) and call
|
||||||
|
# return().
|
||||||
|
#
|
||||||
|
# The order of components in <variable> is guaranteed to match the order they
|
||||||
|
# are listed in the KNOWN_COMPONENTS argument.
|
||||||
|
#
|
||||||
|
# If SKIP_DEPENDENCY_HANDLING is not set, for each component the variable
|
||||||
|
# <name>_<component>_component_deps will be checked for dependent components.
|
||||||
|
# If <component> is listed in <name>_FIND_COMPONENTS, then all its (transitive)
|
||||||
|
# dependencies will also be added to <variable>.
|
||||||
|
#
|
||||||
|
# ::
|
||||||
|
#
|
||||||
|
# ecm_find_package_handle_library_components(<name>
|
||||||
|
# COMPONENTS <component> [<component> [...]]
|
||||||
|
# [SKIP_DEPENDENCY_HANDLING])
|
||||||
|
# [SKIP_PKG_CONFIG])
|
||||||
|
#
|
||||||
|
# Creates an imported library target for each component. The operation of this
|
||||||
|
# macro depends on the presence of a number of CMake variables.
|
||||||
|
#
|
||||||
|
# The <name>_<component>_lib variable should contain the name of this library,
|
||||||
|
# and <name>_<component>_header variable should contain the name of a header
|
||||||
|
# file associated with it (whatever relative path is normally passed to
|
||||||
|
# '#include'). <name>_<component>_header_subdir variable can be used to specify
|
||||||
|
# which subdirectory of the include path the headers will be found in.
|
||||||
|
# ecm_find_package_components() will then search for the library
|
||||||
|
# and include directory (creating appropriate cache variables) and create an
|
||||||
|
# imported library target named <name>::<component>.
|
||||||
|
#
|
||||||
|
# Additional variables can be used to provide additional information:
|
||||||
|
#
|
||||||
|
# If SKIP_PKG_CONFIG, the <name>_<component>_pkg_config variable is set, and
|
||||||
|
# pkg-config is found, the pkg-config module given by
|
||||||
|
# <name>_<component>_pkg_config will be searched for and used to help locate the
|
||||||
|
# library and header file. It will also be used to set
|
||||||
|
# <name>_<component>_VERSION.
|
||||||
|
#
|
||||||
|
# Note that if version information is found via pkg-config,
|
||||||
|
# <name>_<component>_FIND_VERSION can be set to require a particular version
|
||||||
|
# for each component.
|
||||||
|
#
|
||||||
|
# If SKIP_DEPENDENCY_HANDLING is not set, the INTERFACE_LINK_LIBRARIES property
|
||||||
|
# of the imported target for <component> will be set to contain the imported
|
||||||
|
# targets for the components listed in <name>_<component>_component_deps.
|
||||||
|
# <component>_FOUND will also be set to false if any of the compoments in
|
||||||
|
# <name>_<component>_component_deps are not found. This requires the components
|
||||||
|
# in <name>_<component>_component_deps to be listed before <component> in the
|
||||||
|
# COMPONENTS argument.
|
||||||
|
#
|
||||||
|
# The following variables will be set:
|
||||||
|
#
|
||||||
|
# ``<name>_TARGETS``
|
||||||
|
# the imported targets
|
||||||
|
# ``<name>_LIBRARIES``
|
||||||
|
# the found libraries
|
||||||
|
# ``<name>_INCLUDE_DIRS``
|
||||||
|
# the combined required include directories for the components
|
||||||
|
# ``<name>_DEFINITIONS``
|
||||||
|
# the "other" CFLAGS provided by pkg-config, if any
|
||||||
|
# ``<name>_VERSION``
|
||||||
|
# the value of ``<name>_<component>_VERSION`` for the first component that
|
||||||
|
# has this variable set (note that components are searched for in the order
|
||||||
|
# they are passed to the macro), although if it is already set, it will not
|
||||||
|
# be altered
|
||||||
|
#
|
||||||
|
# Note that these variables are never cleared, so if
|
||||||
|
# ecm_find_package_handle_library_components() is called multiple times with
|
||||||
|
# different components (typically because of multiple find_package() calls) then
|
||||||
|
# ``<name>_TARGETS``, for example, will contain all the targets found in any
|
||||||
|
# call (although no duplicates).
|
||||||
|
#
|
||||||
|
# Since pre-1.0.0.
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# Copyright 2014 Alex Merry <alex.merry@kde.org>
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
#
|
||||||
|
# 1. Redistributions of source code must retain the copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# 2. Redistributions in binary form must reproduce the copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# 3. The name of the author may not be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
include(CMakeParseArguments)
|
||||||
|
|
||||||
|
macro(ecm_find_package_version_check module_name)
|
||||||
|
if(CMAKE_VERSION VERSION_LESS 2.8.12)
|
||||||
|
message(FATAL_ERROR "CMake 2.8.12 is required by Find${module_name}.cmake")
|
||||||
|
endif()
|
||||||
|
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12)
|
||||||
|
message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use Find${module_name}.cmake")
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(ecm_find_package_parse_components module_name)
|
||||||
|
set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING)
|
||||||
|
set(ecm_fppc_oneValueArgs RESULT_VAR)
|
||||||
|
set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS)
|
||||||
|
cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
if(ECM_FPPC_UNPARSED_ARGUMENTS)
|
||||||
|
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}")
|
||||||
|
endif()
|
||||||
|
if(NOT ECM_FPPC_RESULT_VAR)
|
||||||
|
message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components")
|
||||||
|
endif()
|
||||||
|
if(NOT ECM_FPPC_KNOWN_COMPONENTS)
|
||||||
|
message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components")
|
||||||
|
endif()
|
||||||
|
if(NOT ECM_FPPC_DEFAULT_COMPONENTS)
|
||||||
|
set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(${module_name}_FIND_COMPONENTS)
|
||||||
|
set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS})
|
||||||
|
|
||||||
|
if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING)
|
||||||
|
# Make sure deps are included
|
||||||
|
foreach(ecm_fppc_comp ${ecm_fppc_requestedComps})
|
||||||
|
foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps})
|
||||||
|
list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index)
|
||||||
|
if("${ecm_fppc_index}" STREQUAL "-1")
|
||||||
|
if(NOT ${module_name}_FIND_QUIETLY)
|
||||||
|
message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}")
|
||||||
|
endif()
|
||||||
|
list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
message(STATUS "Skipping dependency handling for ${module_name}")
|
||||||
|
endif()
|
||||||
|
list(REMOVE_DUPLICATES ecm_fppc_requestedComps)
|
||||||
|
|
||||||
|
# This makes sure components are listed in the same order as
|
||||||
|
# KNOWN_COMPONENTS (potentially important for inter-dependencies)
|
||||||
|
set(${ECM_FPPC_RESULT_VAR})
|
||||||
|
foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS})
|
||||||
|
list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index)
|
||||||
|
if(NOT "${ecm_fppc_index}" STREQUAL "-1")
|
||||||
|
list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}")
|
||||||
|
list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
# if there are any left, they are unknown components
|
||||||
|
if(ecm_fppc_requestedComps)
|
||||||
|
set(ecm_fppc_msgType STATUS)
|
||||||
|
if(${module_name}_FIND_REQUIRED)
|
||||||
|
set(ecm_fppc_msgType FATAL_ERROR)
|
||||||
|
endif()
|
||||||
|
if(NOT ${module_name}_FIND_QUIETLY)
|
||||||
|
message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}")
|
||||||
|
endif()
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS})
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(ecm_find_package_handle_library_components module_name)
|
||||||
|
set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING)
|
||||||
|
set(ecm_fpwc_oneValueArgs)
|
||||||
|
set(ecm_fpwc_multiValueArgs COMPONENTS)
|
||||||
|
cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
if(ECM_FPWC_UNPARSED_ARGUMENTS)
|
||||||
|
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}")
|
||||||
|
endif()
|
||||||
|
if(NOT ECM_FPWC_COMPONENTS)
|
||||||
|
message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package(PkgConfig)
|
||||||
|
foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS})
|
||||||
|
set(ecm_fpwc_dep_vars)
|
||||||
|
set(ecm_fpwc_dep_targets)
|
||||||
|
if(NOT SKIP_DEPENDENCY_HANDLING)
|
||||||
|
foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps})
|
||||||
|
list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND")
|
||||||
|
list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}")
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config)
|
||||||
|
pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET
|
||||||
|
${${module_name}_${ecm_fpwc_comp}_pkg_config})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||||
|
NAMES ${${module_name}_${ecm_fpwc_comp}_header}
|
||||||
|
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS}
|
||||||
|
PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir}
|
||||||
|
)
|
||||||
|
find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||||
|
NAMES ${${module_name}_${ecm_fpwc_comp}_lib}
|
||||||
|
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}")
|
||||||
|
if(NOT ${module_name}_VERSION)
|
||||||
|
set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp}
|
||||||
|
FOUND_VAR
|
||||||
|
${module_name}_${ecm_fpwc_comp}_FOUND
|
||||||
|
REQUIRED_VARS
|
||||||
|
${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||||
|
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||||
|
${ecm_fpwc_dep_vars}
|
||||||
|
VERSION_VAR
|
||||||
|
${module_name}_${ecm_fpwc_comp}_VERSION
|
||||||
|
)
|
||||||
|
|
||||||
|
mark_as_advanced(
|
||||||
|
${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||||
|
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||||
|
)
|
||||||
|
|
||||||
|
if(${module_name}_${ecm_fpwc_comp}_FOUND)
|
||||||
|
list(APPEND ${module_name}_LIBRARIES
|
||||||
|
"${${module_name}_${ecm_fpwc_comp}_LIBRARY}")
|
||||||
|
list(APPEND ${module_name}_INCLUDE_DIRS
|
||||||
|
"${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}")
|
||||||
|
set(${module_name}_DEFINITIONS
|
||||||
|
${${module_name}_DEFINITIONS}
|
||||||
|
${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS})
|
||||||
|
if(NOT TARGET ${module_name}::${ecm_fpwc_comp})
|
||||||
|
add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}"
|
||||||
|
INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}"
|
||||||
|
INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
list(APPEND ${module_name}_TARGETS
|
||||||
|
"${module_name}::${ecm_fpwc_comp}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
if(${module_name}_LIBRARIES)
|
||||||
|
list(REMOVE_DUPLICATES ${module_name}_LIBRARIES)
|
||||||
|
endif()
|
||||||
|
if(${module_name}_INCLUDE_DIRS)
|
||||||
|
list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS)
|
||||||
|
endif()
|
||||||
|
if(${module_name}_DEFINITIONS)
|
||||||
|
list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS)
|
||||||
|
endif()
|
||||||
|
if(${module_name}_TARGETS)
|
||||||
|
list(REMOVE_DUPLICATES ${module_name}_TARGETS)
|
||||||
|
endif()
|
||||||
|
endmacro()
|
|
@ -0,0 +1 @@
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpers.cmake)
|
|
@ -0,0 +1,172 @@
|
||||||
|
#.rst:
|
||||||
|
# FindEGL
|
||||||
|
# -------
|
||||||
|
#
|
||||||
|
# Try to find EGL.
|
||||||
|
#
|
||||||
|
# This will define the following variables:
|
||||||
|
#
|
||||||
|
# ``EGL_FOUND``
|
||||||
|
# True if (the requested version of) EGL is available
|
||||||
|
# ``EGL_VERSION``
|
||||||
|
# The version of EGL; note that this is the API version defined in the
|
||||||
|
# headers, rather than the version of the implementation (eg: Mesa)
|
||||||
|
# ``EGL_LIBRARIES``
|
||||||
|
# This can be passed to target_link_libraries() instead of the ``EGL::EGL``
|
||||||
|
# target
|
||||||
|
# ``EGL_INCLUDE_DIRS``
|
||||||
|
# This should be passed to target_include_directories() if the target is not
|
||||||
|
# used for linking
|
||||||
|
# ``EGL_DEFINITIONS``
|
||||||
|
# This should be passed to target_compile_options() if the target is not
|
||||||
|
# used for linking
|
||||||
|
#
|
||||||
|
# If ``EGL_FOUND`` is TRUE, it will also define the following imported target:
|
||||||
|
#
|
||||||
|
# ``EGL::EGL``
|
||||||
|
# The EGL library
|
||||||
|
#
|
||||||
|
# In general we recommend using the imported target, as it is easier to use.
|
||||||
|
# Bear in mind, however, that if the target is in the link interface of an
|
||||||
|
# exported library, it must be made available by the package config file.
|
||||||
|
#
|
||||||
|
# Since pre-1.0.0.
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# Copyright 2014 Alex Merry <alex.merry@kde.org>
|
||||||
|
# Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
#
|
||||||
|
# 1. Redistributions of source code must retain the copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# 2. Redistributions in binary form must reproduce the copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# 3. The name of the author may not be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#=============================================================================
|
||||||
|
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake)
|
||||||
|
include(CheckCXXSourceCompiles)
|
||||||
|
include(CMakePushCheckState)
|
||||||
|
|
||||||
|
ecm_find_package_version_check(EGL)
|
||||||
|
|
||||||
|
# Use pkg-config to get the directories and then use these values
|
||||||
|
# in the FIND_PATH() and FIND_LIBRARY() calls
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(PKG_EGL QUIET egl)
|
||||||
|
|
||||||
|
set(EGL_DEFINITIONS ${PKG_EGL_CFLAGS_OTHER})
|
||||||
|
|
||||||
|
find_path(EGL_INCLUDE_DIR
|
||||||
|
NAMES
|
||||||
|
EGL/egl.h
|
||||||
|
HINTS
|
||||||
|
${PKG_EGL_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
find_library(EGL_LIBRARY
|
||||||
|
NAMES
|
||||||
|
EGL
|
||||||
|
HINTS
|
||||||
|
${PKG_EGL_LIBRARY_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
# NB: We do *not* use the version information from pkg-config, as that
|
||||||
|
# is the implementation version (eg: the Mesa version)
|
||||||
|
if(EGL_INCLUDE_DIR)
|
||||||
|
# egl.h has defines of the form EGL_VERSION_x_y for each supported
|
||||||
|
# version; so the header for EGL 1.1 will define EGL_VERSION_1_0 and
|
||||||
|
# EGL_VERSION_1_1. Finding the highest supported version involves
|
||||||
|
# finding all these defines and selecting the highest numbered.
|
||||||
|
file(READ "${EGL_INCLUDE_DIR}/EGL/egl.h" _EGL_header_contents)
|
||||||
|
string(REGEX MATCHALL
|
||||||
|
"[ \t]EGL_VERSION_[0-9_]+"
|
||||||
|
_EGL_version_lines
|
||||||
|
"${_EGL_header_contents}"
|
||||||
|
)
|
||||||
|
unset(_EGL_header_contents)
|
||||||
|
foreach(_EGL_version_line ${_EGL_version_lines})
|
||||||
|
string(REGEX REPLACE
|
||||||
|
"[ \t]EGL_VERSION_([0-9_]+)"
|
||||||
|
"\\1"
|
||||||
|
_version_candidate
|
||||||
|
"${_EGL_version_line}"
|
||||||
|
)
|
||||||
|
string(REPLACE "_" "." _version_candidate "${_version_candidate}")
|
||||||
|
if(NOT DEFINED EGL_VERSION OR EGL_VERSION VERSION_LESS _version_candidate)
|
||||||
|
set(EGL_VERSION "${_version_candidate}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
unset(_EGL_version_lines)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
cmake_push_check_state(RESET)
|
||||||
|
list(APPEND CMAKE_REQUIRED_LIBRARIES "${EGL_LIBRARY}")
|
||||||
|
list(APPEND CMAKE_REQUIRED_INCLUDES "${EGL_INCLUDE_DIR}")
|
||||||
|
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
EGLint x = 0; EGLDisplay dpy = 0; EGLContext ctx = 0;
|
||||||
|
eglDestroyContext(dpy, ctx);
|
||||||
|
}" HAVE_EGL)
|
||||||
|
|
||||||
|
cmake_pop_check_state()
|
||||||
|
|
||||||
|
set(required_vars EGL_INCLUDE_DIR HAVE_EGL)
|
||||||
|
if(NOT EMSCRIPTEN)
|
||||||
|
list(APPEND required_vars EGL_LIBRARY)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(EGL
|
||||||
|
FOUND_VAR
|
||||||
|
EGL_FOUND
|
||||||
|
REQUIRED_VARS
|
||||||
|
${required_vars}
|
||||||
|
VERSION_VAR
|
||||||
|
EGL_VERSION
|
||||||
|
)
|
||||||
|
|
||||||
|
if(EGL_FOUND AND NOT TARGET EGL::EGL)
|
||||||
|
if (EMSCRIPTEN)
|
||||||
|
add_library(EGL::EGL INTERFACE IMPORTED)
|
||||||
|
# Nothing further to be done, system include paths have headers and linkage is implicit.
|
||||||
|
else()
|
||||||
|
add_library(EGL::EGL UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(EGL::EGL PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${EGL_LIBRARY}"
|
||||||
|
INTERFACE_COMPILE_OPTIONS "${EGL_DEFINITIONS}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${EGL_INCLUDE_DIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(EGL_LIBRARY EGL_INCLUDE_DIR HAVE_EGL)
|
||||||
|
|
||||||
|
# compatibility variables
|
||||||
|
set(EGL_LIBRARIES ${EGL_LIBRARY})
|
||||||
|
set(EGL_INCLUDE_DIRS ${EGL_INCLUDE_DIR})
|
||||||
|
set(EGL_VERSION_STRING ${EGL_VERSION})
|
||||||
|
|
||||||
|
include(FeatureSummary)
|
||||||
|
set_package_properties(EGL PROPERTIES
|
||||||
|
URL "https://www.khronos.org/egl/"
|
||||||
|
DESCRIPTION "A platform-agnostic mechanism for creating rendering surfaces for use with other graphics libraries, such as OpenGL|ES and OpenVG."
|
||||||
|
)
|
|
@ -0,0 +1,48 @@
|
||||||
|
# - Try to find enet
|
||||||
|
# Once done this will define
|
||||||
|
#
|
||||||
|
# ENET_FOUND - system has enet
|
||||||
|
# ENET_INCLUDE_DIRS - the enet include directory
|
||||||
|
# ENET_LIBRARIES - the libraries needed to use enet
|
||||||
|
#
|
||||||
|
# $ENETDIR is an environment variable used for finding enet.
|
||||||
|
#
|
||||||
|
# Borrowed from The Mana World
|
||||||
|
# http://themanaworld.org/
|
||||||
|
#
|
||||||
|
# Several changes and additions by Fabian 'x3n' Landau
|
||||||
|
# Lots of simplifications by Adrian Friedli
|
||||||
|
# > www.orxonox.net <
|
||||||
|
|
||||||
|
FIND_PATH(ENET_INCLUDE_DIRS enet/enet.h
|
||||||
|
PATHS
|
||||||
|
$ENV{ENETDIR}
|
||||||
|
/usr/local
|
||||||
|
/usr
|
||||||
|
PATH_SUFFIXES include
|
||||||
|
)
|
||||||
|
|
||||||
|
FIND_LIBRARY(ENET_LIBRARY
|
||||||
|
NAMES enet
|
||||||
|
PATHS
|
||||||
|
$ENV{ENETDIR}
|
||||||
|
/usr/local
|
||||||
|
/usr
|
||||||
|
PATH_SUFFIXES lib
|
||||||
|
)
|
||||||
|
|
||||||
|
# handle the QUIETLY and REQUIRED arguments and set ENET_FOUND to TRUE if
|
||||||
|
# all listed variables are TRUE
|
||||||
|
INCLUDE(FindPackageHandleStandardArgs)
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ENet DEFAULT_MSG ENET_LIBRARY ENET_INCLUDE_DIRS)
|
||||||
|
|
||||||
|
IF (ENET_FOUND)
|
||||||
|
IF(WIN32)
|
||||||
|
SET(WINDOWS_ENET_DEPENDENCIES "ws2_32;winmm")
|
||||||
|
SET(ENET_LIBRARIES ${ENET_LIBRARY} ${WINDOWS_ENET_DEPENDENCIES})
|
||||||
|
ELSE(WIN32)
|
||||||
|
SET(ENET_LIBRARIES ${ENET_LIBRARY})
|
||||||
|
ENDIF(WIN32)
|
||||||
|
ENDIF (ENET_FOUND)
|
||||||
|
|
||||||
|
MARK_AS_ADVANCED(ENET_LIBRARY ENET_LIBRARIES ENET_INCLUDE_DIRS)
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
find_path(VTUNE_PATH "")
|
||||||
|
|
||||||
|
set(VTUNE_INCLUDE_DIR "${VTUNE_PATH}/include")
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
set(VTUNE_LIBRARY "${VTUNE_PATH}/lib64/jitprofiling.lib")
|
||||||
|
else()
|
||||||
|
set(VTUNE_LIBRARY "${VTUNE_PATH}/lib64/jitprofiling.a")
|
||||||
|
endif()
|
|
@ -0,0 +1,35 @@
|
||||||
|
# The entire codebase quite reasonably does things like #include <SDL2/SDL.h> or <epoxy/gl.h>
|
||||||
|
# CMake apparently doesn't think you should be doing this, so just includes $PREFIX/include/packagename for a given
|
||||||
|
# package as include directories when using `target_link_libraries` with an imported target, this hacky function fixes
|
||||||
|
# that up so includes can keep working as they always did but we can still use fancy imported targets.
|
||||||
|
# This is stupid.
|
||||||
|
|
||||||
|
function(fix_interface_includes)
|
||||||
|
foreach (target ${ARGN})
|
||||||
|
set(NEW_DIRS)
|
||||||
|
get_target_property(DIRS "${target}" INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
|
||||||
|
if (NOT DIRS)
|
||||||
|
continue()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
foreach (DIR ${DIRS})
|
||||||
|
get_filename_component(PARENT_DIR "${DIR}" DIRECTORY)
|
||||||
|
|
||||||
|
if (PARENT_DIR MATCHES "include$")
|
||||||
|
list(APPEND NEW_DIRS "${PARENT_DIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# HACK
|
||||||
|
# The libarchive pkg-config file in MSYS2 seems to include a UNIX-style path for its
|
||||||
|
# include directory and CMake doesn't like that.
|
||||||
|
if (WIN32 AND MINGW AND target STREQUAL PkgConfig::LibArchive)
|
||||||
|
list(FILTER DIRS EXCLUDE REGEX "^/[^.]+64/.*")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
list(APPEND DIRS ${NEW_DIRS})
|
||||||
|
set_target_properties("${target}" PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${DIRS}")
|
||||||
|
endforeach()
|
||||||
|
endfunction()
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
set(SANITIZE "" CACHE STRING "Sanitizers to enable.")
|
||||||
|
|
||||||
|
string(REGEX MATCHALL "[^,]+" ENABLED_SANITIZERS "${SANITIZE}")
|
||||||
|
|
||||||
|
foreach(SANITIZER ${ENABLED_SANITIZERS})
|
||||||
|
add_compile_options("-fsanitize=${SANITIZER}")
|
||||||
|
add_link_options("-fsanitize=${SANITIZER}")
|
||||||
|
endforeach()
|
|
@ -0,0 +1,19 @@
|
||||||
|
include(FindPackageMessage)
|
||||||
|
|
||||||
|
find_program(CCACHE "ccache")
|
||||||
|
|
||||||
|
cmake_dependent_option(USE_CCACHE "Use CCache to speed up repeated builds." ON CCACHE OFF)
|
||||||
|
|
||||||
|
if (NOT CCACHE OR NOT USE_CCACHE)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Fedora, and probably also Red Hat-based distros in general, use CCache by default if it's installed on the system.
|
||||||
|
# We'll try to detect this here, and exit if that's the case.
|
||||||
|
# Trying to launch ccache with ccache as we'd otherwise do seems to cause build issues.
|
||||||
|
if (CMAKE_C_COMPILER MATCHES "ccache" OR CMAKE_CXX_COMPILER MATCHES "ccache")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package_message(CCache "Using CCache to speed up compilation" "${USE_CCACHE}")
|
||||||
|
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE}")
|
|
@ -0,0 +1,12 @@
|
||||||
|
# Toolchain file for building with Homebrew's LLVM on macOS
|
||||||
|
# This is useful on 10.14 where std::filesystem is not supported.
|
||||||
|
|
||||||
|
set(CMAKE_C_COMPILER /usr/local/opt/llvm/bin/clang)
|
||||||
|
set(CMAKE_CXX_COMPILER /usr/local/opt/llvm/bin/clang++)
|
||||||
|
|
||||||
|
add_link_options(-L/usr/local/opt/llvm/lib)
|
||||||
|
|
||||||
|
# LLVM in Homebrew is built with latest Xcode which has a newer linker than
|
||||||
|
# what is bundled in the default install of Xcode Command Line Tools, so we
|
||||||
|
# override it to prevent it passing flags not supported by the system's ld.
|
||||||
|
add_link_options(-mlinker-version=450)
|
|
@ -0,0 +1,12 @@
|
||||||
|
set(VCPKG_TARGET_ARCHITECTURE arm64)
|
||||||
|
set(VCPKG_CRT_LINKAGE dynamic)
|
||||||
|
set(VCPKG_LIBRARY_LINKAGE static)
|
||||||
|
|
||||||
|
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
|
||||||
|
set(VCPKG_CMAKE_SYSTEM_VERSION 11.0)
|
||||||
|
set(VCPKG_OSX_ARCHITECTURES arm64)
|
||||||
|
set(VCPKG_BUILD_TYPE release)
|
||||||
|
set(VCPKG_OSX_DEPLOYMENT_TARGET 11.0)
|
||||||
|
|
||||||
|
set(VCPKG_C_FLAGS -mmacosx-version-min=11.0)
|
||||||
|
set(VCPKG_CXX_FLAGS -mmacosx-version-min=11.0)
|
|
@ -0,0 +1,7 @@
|
||||||
|
set(VCPKG_TARGET_ARCHITECTURE x64)
|
||||||
|
set(VCPKG_CRT_LINKAGE dynamic)
|
||||||
|
set(VCPKG_LIBRARY_LINKAGE static)
|
||||||
|
set(VCPKG_ENV_PASSTHROUGH PATH)
|
||||||
|
set(VCPKG_BUILD_TYPE release)
|
||||||
|
|
||||||
|
set(VCPKG_CMAKE_SYSTEM_NAME MinGW)
|
|
@ -0,0 +1,12 @@
|
||||||
|
set(VCPKG_TARGET_ARCHITECTURE x64)
|
||||||
|
set(VCPKG_CRT_LINKAGE dynamic)
|
||||||
|
set(VCPKG_LIBRARY_LINKAGE static)
|
||||||
|
|
||||||
|
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
|
||||||
|
set(VCPKG_CMAKE_SYSTEM_VERSION 10.15)
|
||||||
|
set(VCPKG_OSX_ARCHITECTURES x86_64)
|
||||||
|
set(VCPKG_BUILD_TYPE release)
|
||||||
|
set(VCPKG_OSX_DEPLOYMENT_TARGET 10.15)
|
||||||
|
|
||||||
|
set(VCPKG_C_FLAGS -mmacosx-version-min=10.15)
|
||||||
|
set(VCPKG_CXX_FLAGS -mmacosx-version-min=10.15)
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1739020877,
|
||||||
|
"narHash": "sha256-mIvECo/NNdJJ/bXjNqIh8yeoSjVLAuDuTUzAo7dzs8Y=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "a79cfe0ebd24952b580b1cf08cd906354996d547",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
{
|
||||||
|
description = "Nintendo DS emulator";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
inherit (pkgs.lib) cmakeBool optionals makeLibraryPath;
|
||||||
|
inherit (pkgs.stdenv) isLinux isDarwin;
|
||||||
|
|
||||||
|
revision = with self; if sourceInfo?dirtyRev
|
||||||
|
then sourceInfo.dirtyRev
|
||||||
|
else sourceInfo.rev;
|
||||||
|
shortRevision = with self; if sourceInfo?dirtyShortRev
|
||||||
|
then sourceInfo.dirtyShortRev
|
||||||
|
else sourceInfo.shortRev;
|
||||||
|
|
||||||
|
melonDS = pkgs.stdenv.mkDerivation {
|
||||||
|
pname = "melonDS";
|
||||||
|
version = "1.0-${shortRevision}";
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
cmake
|
||||||
|
ninja
|
||||||
|
pkg-config
|
||||||
|
qt6.wrapQtAppsHook
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = (with pkgs; [
|
||||||
|
qt6.qtbase
|
||||||
|
qt6.qtmultimedia
|
||||||
|
SDL2
|
||||||
|
zstd
|
||||||
|
libarchive
|
||||||
|
libGL
|
||||||
|
libslirp
|
||||||
|
enet
|
||||||
|
]) ++ optionals (!isDarwin) (with pkgs; [
|
||||||
|
kdePackages.extra-cmake-modules
|
||||||
|
qt6.qtwayland
|
||||||
|
wayland
|
||||||
|
]);
|
||||||
|
|
||||||
|
cmakeFlags = [
|
||||||
|
(cmakeBool "USE_QT6" true)
|
||||||
|
(cmakeBool "USE_SYSTEM_LIBSLIRP" true)
|
||||||
|
(cmakeBool "MELONDS_EMBED_BUILD_INFO" true)
|
||||||
|
];
|
||||||
|
|
||||||
|
env.MELONDS_GIT_HASH = revision;
|
||||||
|
env.MELONDS_GIT_BRANCH = "(unknown)";
|
||||||
|
env.MELONDS_BUILD_PROVIDER = "Nix";
|
||||||
|
|
||||||
|
qtWrapperArgs = optionals isLinux [
|
||||||
|
"--prefix LD_LIBRARY_PATH : ${makeLibraryPath [ pkgs.libpcap pkgs.wayland ]}"
|
||||||
|
] ++ optionals isDarwin [
|
||||||
|
"--prefix DYLD_LIBRARY_PATH : ${makeLibraryPath [ pkgs.libpcap ]}"
|
||||||
|
];
|
||||||
|
|
||||||
|
passthru = {
|
||||||
|
exePath = if isDarwin then
|
||||||
|
"/Applications/melonDS.app/Contents/MacOS/melonDS"
|
||||||
|
else "/bin/melonDS";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
packages.default = melonDS;
|
||||||
|
apps.default = flake-utils.lib.mkApp {
|
||||||
|
drv = self.packages.${system}.default;
|
||||||
|
};
|
||||||
|
devShells = {
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
inputsFrom = [ self.packages.${system}.default ];
|
||||||
|
packages = with pkgs; [
|
||||||
|
qt6.qttools
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Shell for building static melonDS release builds with vcpkg
|
||||||
|
# Use mkShellNoCC to ensure Nix's gcc/clang and stdlib isn't used
|
||||||
|
vcpkg = pkgs.mkShellNoCC {
|
||||||
|
packages = with pkgs; [
|
||||||
|
autoconf
|
||||||
|
autoconf-archive
|
||||||
|
automake
|
||||||
|
cmake
|
||||||
|
cups.dev # Needed by qtbase despite not enabling print support
|
||||||
|
git
|
||||||
|
iconv.dev
|
||||||
|
libtool
|
||||||
|
ninja
|
||||||
|
pkg-config
|
||||||
|
python3
|
||||||
|
];
|
||||||
|
|
||||||
|
# Undo the SDK setup done by nixpkgs so we can use AppleClang
|
||||||
|
shellHook = ''
|
||||||
|
unset DEVELOPER_DIR SDKROOT MACOSX_DEPLOYMENT_TARGET
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,29 +0,0 @@
|
||||||
---
|
|
||||||
app-id: net.kuribo64.melonDS
|
|
||||||
runtime: org.freedesktop.Platform
|
|
||||||
runtime-version: '18.08'
|
|
||||||
sdk: org.freedesktop.Sdk
|
|
||||||
command: melonDS
|
|
||||||
finish-args:
|
|
||||||
- "--share=ipc"
|
|
||||||
- "--socket=x11"
|
|
||||||
- "--socket=pulseaudio"
|
|
||||||
- "--share=network"
|
|
||||||
- "--device=all"
|
|
||||||
- "--filesystem=home"
|
|
||||||
modules:
|
|
||||||
- name: libpcap
|
|
||||||
sources:
|
|
||||||
- type: archive
|
|
||||||
url: http://www.tcpdump.org/release/libpcap-1.9.0.tar.gz
|
|
||||||
sha256: 2edb88808e5913fdaa8e9c1fcaf272e19b2485338742b5074b9fe44d68f37019
|
|
||||||
|
|
||||||
- name: melonds
|
|
||||||
buildsystem: cmake-ninja
|
|
||||||
sources:
|
|
||||||
- type: git
|
|
||||||
url: https://github.com/Arisotura/melonDS.git
|
|
||||||
branch: master
|
|
||||||
post-install:
|
|
||||||
- "desktop-file-install --dir=/app/share/applications net.kuribo64.melonDS.desktop"
|
|
||||||
- "install -D icon/melon_256x256.png /app/share/icons/hicolor/256x256/apps/net.kuribo64.melonDS.png"
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
TC_PREFIX = /home/exophase/pandora-dev
|
||||||
|
PREFIX = $(TC_PREFIX)/arm-2011.03
|
||||||
|
AS = $(PREFIX)/bin/arm-none-linux-gnueabi-gcc
|
||||||
|
OBJCOPY = $(PREFIX)/bin/arm-none-linux-gnueabi-objcopy
|
||||||
|
|
||||||
|
BIN_ARM7 = drastic_bios_arm7
|
||||||
|
BIN_ARM9 = drastic_bios_arm9
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(AS) bios_common.S -DBIOS_ARM7 -march=armv4 -c -Wa,-asl=$(BIN_ARM7).list -o $(BIN_ARM7).o
|
||||||
|
$(AS) bios_common.S -DBIOS_ARM9 -march=armv5 -c -Wa,-asl=$(BIN_ARM9).list -o $(BIN_ARM9).o
|
||||||
|
$(OBJCOPY) -O binary $(BIN_ARM7).o $(BIN_ARM7).bin
|
||||||
|
$(OBJCOPY) -O binary $(BIN_ARM9).o $(BIN_ARM9).bin
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(BIN_ARM7).bin $(BIN_ARM7).o $(BIN_ARM9).bin $(BIN_ARM9).o
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
Custom NDS ARM7/ARM9 BIOS replacement
|
||||||
|
Copyright (c) 2013, Gilead Kutnick
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1) Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
2) Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
-- Info --
|
||||||
|
|
||||||
|
This archive contains source code and assembly for a custom BIOS replacement
|
||||||
|
for the Nintendo DS system. This code is in no way affiliated with Nintendo
|
||||||
|
and is not derived from Nintendo's BIOS implementation but has been implemented
|
||||||
|
using publically available documentation.
|
||||||
|
|
||||||
|
It can be assembled using the included Makefile along with a proper ARM gcc
|
||||||
|
toolchain. Change the first four lines to point to the proper toolchain of your
|
||||||
|
choice.
|
||||||
|
|
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 730 B |
Before Width: | Height: | Size: 146 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 32 KiB |
|
@ -1,6 +0,0 @@
|
||||||
<!DOCTYPE RCC>
|
|
||||||
<RCC version="1.0">
|
|
||||||
<qresource>
|
|
||||||
<file alias="melon-icon">icon/melon_32x32.png</file>
|
|
||||||
</qresource>
|
|
||||||
</RCC>
|
|
BIN
melonDS.icns
|
@ -1,24 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>melonDS</string>
|
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string>melonDS.icns</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>net.kuribo64.melonDS</string>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>English</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>0.9</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>0.9</string>
|
|
||||||
<key>NSHumanReadableCopyright</key>
|
|
||||||
<string>Licensed under GPLv3</string>
|
|
||||||
<key>NSHighResolutionCapable</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<gresources>
|
|
||||||
<gresource prefix="/org/kuriboland/melonDS">
|
|
||||||
<file preprocess="to-pixdata">icon/melon_16x16.png</file>
|
|
||||||
<file preprocess="to-pixdata">icon/melon_32x32.png</file>
|
|
||||||
<file preprocess="to-pixdata">icon/melon_48x48.png</file>
|
|
||||||
<file preprocess="to-pixdata">icon/melon_64x64.png</file>
|
|
||||||
<file preprocess="to-pixdata">icon/melon_128x128.png</file>
|
|
||||||
<file preprocess="to-pixdata">icon/melon_256x256.png</file>
|
|
||||||
</gresource>
|
|
||||||
</gresources>
|
|
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 983 B |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 132 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
|
@ -0,0 +1,101 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>melonDS</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>melon.icns</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>net.kuribo64.melonDS</string>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>English</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>${melonDS_VERSION}</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>${melonDS_VERSION}</string>
|
||||||
|
<key>LSApplicationCategoryType</key>
|
||||||
|
<string>public.app-category.games</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>Licensed under GPLv3</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string>NSApplication</string>
|
||||||
|
<key>NSHighResolutionCapable</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>We need microphone access so you can use the emulated DS microphone</string>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>Camera access is needed for emulation of the DSi's cameras</string>
|
||||||
|
<key>CFBundleDocumentTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>Nintendo DS ROM</string>
|
||||||
|
<key>CFBundleTypeExtensions</key>
|
||||||
|
<array>
|
||||||
|
<string>nds</string>
|
||||||
|
<string>srl</string>
|
||||||
|
<string>dsi</string>
|
||||||
|
<string>ids</string>
|
||||||
|
<string>nds.zst</string>
|
||||||
|
<string>srl.zst</string>
|
||||||
|
<string>dsi.zst</string>
|
||||||
|
<string>ids.zst</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>Game Boy Advance ROM</string>
|
||||||
|
<key>CFBundleTypeExtensions</key>
|
||||||
|
<array>
|
||||||
|
<string>gba</string>
|
||||||
|
<string>agb</string>
|
||||||
|
<string>gba.zst</string>
|
||||||
|
<string>agb.zst</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Alternate</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>Archive containing ROM</string>
|
||||||
|
<key>CFBundleTypeExtensions</key>
|
||||||
|
<array>
|
||||||
|
<key>zip</key>
|
||||||
|
<key>7z</key>
|
||||||
|
<key>rar</key>
|
||||||
|
<key>tar</key>
|
||||||
|
<key>tar.gz</key>
|
||||||
|
<key>tgz</key>
|
||||||
|
<key>tar.xz</key>
|
||||||
|
<key>txz</key>
|
||||||
|
<key>tar.bz2</key>
|
||||||
|
<key>tbz2</key>
|
||||||
|
<key>tar.lz4</key>
|
||||||
|
<key>tlz4</key>
|
||||||
|
<key>tar.zst</key>
|
||||||
|
<key>tzst</key>
|
||||||
|
<key>tar.Z</key>
|
||||||
|
<key>taz</key>
|
||||||
|
<key>tar.lz</key>
|
||||||
|
<key>tar.lzma</key>
|
||||||
|
<key>tlz</key>
|
||||||
|
<key>tar.lrz</key>
|
||||||
|
<key>tlrz</key>
|
||||||
|
<key>tar.lzo</key>
|
||||||
|
<key>tzo</key>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Alternate</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<!DOCTYPE RCC>
|
||||||
|
<RCC version="1.0">
|
||||||
|
<qresource>
|
||||||
|
<file alias="melon-icon">icon/melon_256x256.png</file>
|
||||||
|
<file alias="melon-logo">melon384.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
|
@ -2,12 +2,12 @@
|
||||||
#define VFT_APP 0x00000001L
|
#define VFT_APP 0x00000001L
|
||||||
|
|
||||||
//this will set your .exe icon
|
//this will set your .exe icon
|
||||||
100 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "melon.ico"
|
100 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "${CMAKE_SOURCE_DIR}/res/melon.ico"
|
||||||
|
|
||||||
//include version information in .exe, modify these values to match your needs
|
//include version information in .exe, modify these values to match your needs
|
||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION 0,9,1,0
|
FILEVERSION ${MELON_RC_VERSION}
|
||||||
PRODUCTVERSION 0,9,1,0
|
PRODUCTVERSION ${MELON_RC_VERSION}
|
||||||
FILETYPE VFT_APP
|
FILETYPE VFT_APP
|
||||||
{
|
{
|
||||||
BLOCK "StringFileInfo"
|
BLOCK "StringFileInfo"
|
||||||
|
@ -15,14 +15,14 @@ FILETYPE VFT_APP
|
||||||
BLOCK "040904E4"
|
BLOCK "040904E4"
|
||||||
{
|
{
|
||||||
VALUE "CompanyName", "Melon Factory of Kuribo64"
|
VALUE "CompanyName", "Melon Factory of Kuribo64"
|
||||||
VALUE "FileVersion", "0.9.1"
|
VALUE "FileVersion", "${melonDS_VERSION}"
|
||||||
VALUE "FileDescription", "DS emulator, sorta. also 1st quality melon."
|
VALUE "FileDescription", "melonDS emulator"
|
||||||
VALUE "InternalName", "SDnolem"
|
VALUE "InternalName", "SDnolem"
|
||||||
VALUE "LegalCopyright", "2016-2020 Arisotura & co."
|
VALUE "LegalCopyright", "2016-2023 melonDS team"
|
||||||
VALUE "LegalTrademarks", ""
|
VALUE "LegalTrademarks", ""
|
||||||
VALUE "OriginalFilename", "zafkflzdasd.exe"
|
VALUE "OriginalFilename", "melonDS.exe"
|
||||||
VALUE "ProductName", "melonDS"
|
VALUE "ProductName", "melonDS"
|
||||||
VALUE "ProductVersion", "0.9.1"
|
VALUE "ProductVersion", "${melonDS_VERSION}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
|
@ -0,0 +1,80 @@
|
||||||
|
<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#5c913b" fill-rule="evenodd" d="M357.62 154.38c56.821-56.821 85.232-85.232 118.463-94.604a132.32 132.32 0 0 1 71.834 0c33.231 9.372 61.642 37.783 118.463 94.604l203.24 203.24c56.821 56.821 85.232 85.232 94.604 118.463a132.32 132.32 0 0 1 0 71.834c-9.372 33.231-37.783 61.642-94.604 118.463L666.38 869.62c-56.821 56.821-85.232 85.232-118.463 94.604a132.319 132.319 0 0 1-71.834 0c-33.231-9.372-61.642-37.783-118.463-94.604L154.38 666.38c-56.821-56.821-85.232-85.232-94.604-118.463a132.318 132.318 0 0 1 0-71.834c9.372-33.231 37.783-61.642 94.604-118.463Z"/>
|
||||||
|
<filter id="a" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feGaussianBlur stdDeviation="18.378"/>
|
||||||
|
<feOffset dy="7.351" result="offsetblur"/>
|
||||||
|
<feFlood flood-color="#000" flood-opacity=".4"/>
|
||||||
|
<feComposite in2="offsetblur" operator="in"/>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode/>
|
||||||
|
<feMergeNode in="SourceGraphic"/>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
<path fill="#ffe8b6" fill-rule="evenodd" filter="url(#a)" d="M383.23 209.478c47.35-47.35 71.026-71.026 98.72-78.836a110.266 110.266 0 0 1 59.86 0c27.694 7.81 51.369 31.486 98.72 78.836L814.66 383.61c47.351 47.351 71.027 71.026 78.837 98.72a110.265 110.265 0 0 1 0 59.86c-7.81 27.694-31.486 51.37-78.837 98.72L640.53 815.042c-47.351 47.35-71.027 71.026-98.72 78.836a110.265 110.265 0 0 1-59.86 0c-27.694-7.81-51.37-31.485-98.72-78.836L209.098 640.91c-47.35-47.35-71.026-71.026-78.836-98.72a110.266 110.266 0 0 1 0-59.86c7.81-27.694 31.485-51.369 78.836-98.72Z"/>
|
||||||
|
<filter id="b" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feGaussianBlur stdDeviation="18.378"/>
|
||||||
|
<feOffset dy="7.351" result="offsetblur"/>
|
||||||
|
<feFlood flood-color="#000" flood-opacity=".4"/>
|
||||||
|
<feComposite in2="offsetblur" operator="in"/>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode/>
|
||||||
|
<feMergeNode in="SourceGraphic"/>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
<path fill="#dd2e44" fill-rule="evenodd" filter="url(#b)" d="M404.822 239.527c39.46-39.46 59.189-59.189 82.266-65.697a91.889 91.889 0 0 1 49.885 0c23.077 6.508 42.807 26.238 82.266 65.697l165.295 165.295c39.459 39.46 59.188 59.189 65.697 82.266a91.888 91.888 0 0 1 0 49.884c-6.509 23.078-26.238 42.808-65.697 82.266L619.239 784.534c-39.46 39.459-59.189 59.188-82.266 65.697a91.888 91.888 0 0 1-49.885 0c-23.077-6.509-42.807-26.238-82.266-65.697L239.527 619.239c-39.46-39.46-59.189-59.189-65.697-82.267a91.888 91.888 0 0 1 0-49.884c6.508-23.077 26.238-42.807 65.697-82.266Z"/>
|
||||||
|
<filter id="c" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feGaussianBlur stdDeviation="3.676"/>
|
||||||
|
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
|
||||||
|
<feFlood flood-color="#000" flood-opacity=".104"/>
|
||||||
|
<feComposite in2="offsetblur" operator="in"/>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode/>
|
||||||
|
<feMergeNode in="SourceGraphic"/>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
<path fill="#ff554d" fill-rule="evenodd" filter="url(#c)" d="M405.568 606.767s-14.487 11.68-31.187 15.594c-9.553 2.238-24.422 1.568-31.188-5.198l-103.96-103.96c-6.766-6.766-7.436-21.635-5.198-31.187 3.914-16.7 15.594-31.188 15.594-31.188l197.523-197.523s14.488-11.68 31.188-15.594c9.553-2.239 24.422-1.568 31.188 5.198l103.96 103.96c6.765 6.765 7.436 21.634 5.197 31.187-3.913 16.7-15.594 31.188-15.594 31.188Z"/>
|
||||||
|
<filter id="d" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feGaussianBlur stdDeviation="3.676"/>
|
||||||
|
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
|
||||||
|
<feFlood flood-color="#000" flood-opacity=".104"/>
|
||||||
|
<feComposite in2="offsetblur" operator="in"/>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode/>
|
||||||
|
<feMergeNode in="SourceGraphic"/>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
<path fill="#ff554d" fill-rule="evenodd" filter="url(#d)" d="M574.579 779.453s-14.488 11.68-31.188 15.594c-9.553 2.238-24.422 1.568-31.188-5.198l-103.96-103.96c-6.765-6.766-7.436-21.635-5.197-31.187 3.914-16.7 15.594-31.188 15.594-31.188L616.163 425.99s14.488-11.68 31.187-15.594c9.553-2.239 24.422-1.568 31.188 5.198l103.96 103.96c6.766 6.765 7.436 21.634 5.198 31.187-3.914 16.7-15.594 31.188-15.594 31.188Z"/>
|
||||||
|
<filter id="e" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feGaussianBlur stdDeviation="3.676"/>
|
||||||
|
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
|
||||||
|
<feFlood flood-color="#000" flood-opacity=".35"/>
|
||||||
|
<feComposite in2="offsetblur" operator="in"/>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode/>
|
||||||
|
<feMergeNode in="SourceGraphic"/>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
<path fill="#31352e" fill-rule="evenodd" filter="url(#e)" d="M414.022 532.978s-8.315 6.704-17.9 8.95c-5.483 1.285-14.016.9-17.9-2.983l-59.667-59.667c-3.883-3.884-4.268-12.417-2.983-17.9 2.246-9.585 8.95-17.9 8.95-17.9L437.89 330.11s8.315-6.704 17.9-8.95c5.482-1.285 14.016-.9 17.9 2.984l59.666 59.666c3.884 3.884 4.269 12.418 2.984 17.9-2.246 9.585-8.95 17.9-8.95 17.9Z"/>
|
||||||
|
<filter id="f" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feGaussianBlur stdDeviation="3.676"/>
|
||||||
|
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
|
||||||
|
<feFlood flood-color="#000" flood-opacity=".35"/>
|
||||||
|
<feComposite in2="offsetblur" operator="in"/>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode/>
|
||||||
|
<feMergeNode in="SourceGraphic"/>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
<path fill="#31352e" fill-rule="evenodd" filter="url(#f)" d="M677.203 544.885c7.177 7.176 18.813 7.176 25.99 0l19.954-19.955c7.177-7.176 7.177-18.813 0-25.99l-19.954-19.954c-7.177-7.176-18.813-7.176-25.99 0l-19.954 19.955c-7.177 7.176-7.177 18.813 0 25.99Z"/>
|
||||||
|
<filter id="g" x="0" y="0" width="1024" height="1024" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feGaussianBlur stdDeviation="3.676"/>
|
||||||
|
<feOffset dx="5.198" dy="5.198" result="offsetblur"/>
|
||||||
|
<feFlood flood-color="#000" flood-opacity=".35"/>
|
||||||
|
<feComposite in2="offsetblur" operator="in"/>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode/>
|
||||||
|
<feMergeNode in="SourceGraphic"/>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
<path fill="#31352e" fill-rule="evenodd" filter="url(#g)" d="M489.752 728.66c7.177 7.177 18.813 7.177 25.99 0l19.954-19.954c7.177-7.177 7.177-18.813 0-25.99l-19.954-19.954c-7.177-7.176-18.813-7.176-25.99 0l-19.954 19.955c-7.177 7.176-7.177 18.812 0 25.99Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 38 KiB |
|
@ -2,7 +2,7 @@
|
||||||
Name=melonDS
|
Name=melonDS
|
||||||
GenericName=Nintendo DS Emulator
|
GenericName=Nintendo DS Emulator
|
||||||
Comment=A fast and accurate Nintendo DS emulator.
|
Comment=A fast and accurate Nintendo DS emulator.
|
||||||
Exec=melonDS
|
Exec=melonDS %f
|
||||||
Type=Application
|
Type=Application
|
||||||
Categories=Game;Emulator;
|
Categories=Game;Emulator;
|
||||||
Terminal=false
|
Terminal=false
|
|
@ -4,7 +4,7 @@
|
||||||
manifestVersion="1.0">
|
manifestVersion="1.0">
|
||||||
<assemblyIdentity
|
<assemblyIdentity
|
||||||
name="Exe.Apps.Project"
|
name="Exe.Apps.Project"
|
||||||
processorArchitecture="amd64"
|
processorArchitecture="${WIN32_ARCHITECTURE}"
|
||||||
version="1.0.0.0"
|
version="1.0.0.0"
|
||||||
type="win32"/>
|
type="win32"/>
|
||||||
<description>Project</description>
|
<description>Project</description>
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
type="win32"
|
type="win32"
|
||||||
name="Microsoft.Windows.Common-Controls"
|
name="Microsoft.Windows.Common-Controls"
|
||||||
version="6.0.0.0"
|
version="6.0.0.0"
|
||||||
processorArchitecture="amd64"
|
processorArchitecture="${WIN32_ARCHITECTURE}"
|
||||||
publicKeyToken="6595b64144ccf1df"
|
publicKeyToken="6595b64144ccf1df"
|
||||||
language="*"
|
language="*"
|
||||||
/>
|
/>
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -21,32 +21,43 @@
|
||||||
#include "ARCodeFile.h"
|
#include "ARCodeFile.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
|
|
||||||
|
namespace melonDS
|
||||||
|
{
|
||||||
|
using namespace Platform;
|
||||||
|
|
||||||
// TODO: import codes from other sources (usrcheat.dat, ...)
|
// TODO: import codes from other sources (usrcheat.dat, ...)
|
||||||
// TODO: more user-friendly error reporting
|
// TODO: more user-friendly error reporting
|
||||||
|
|
||||||
|
|
||||||
ARCodeFile::ARCodeFile(const char* filename)
|
ARCodeFile::ARCodeFile(const std::string& filename)
|
||||||
{
|
{
|
||||||
memset(Filename, 0, sizeof(Filename));
|
Filename = filename;
|
||||||
strncpy(Filename, filename, 1023);
|
|
||||||
|
|
||||||
Error = false;
|
|
||||||
|
|
||||||
Categories.clear();
|
|
||||||
|
|
||||||
if (!Load())
|
if (!Load())
|
||||||
Error = true;
|
Error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ARCodeFile::~ARCodeFile()
|
std::vector<ARCode> ARCodeFile::GetCodes() const noexcept
|
||||||
{
|
{
|
||||||
Categories.clear();
|
if (Error)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::vector<ARCode> codes;
|
||||||
|
|
||||||
|
for (const ARCodeCat& cat : Categories)
|
||||||
|
{
|
||||||
|
for (const ARCode& code : cat.Codes)
|
||||||
|
{
|
||||||
|
codes.push_back(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return codes;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ARCodeFile::Load()
|
bool ARCodeFile::Load()
|
||||||
{
|
{
|
||||||
FILE* f = Platform::OpenFile(Filename, "r");
|
FileHandle* f = OpenFile(Filename, FileMode::ReadText);
|
||||||
if (!f) return true;
|
if (!f) return true;
|
||||||
|
|
||||||
Categories.clear();
|
Categories.clear();
|
||||||
|
@ -58,9 +69,11 @@ bool ARCodeFile::Load()
|
||||||
ARCode curcode;
|
ARCode curcode;
|
||||||
|
|
||||||
char linebuf[1024];
|
char linebuf[1024];
|
||||||
while (!feof(f))
|
while (!IsEndOfFile(f))
|
||||||
{
|
{
|
||||||
fgets(linebuf, 1024, f);
|
if (!FileReadLine(linebuf, 1024, f))
|
||||||
|
break;
|
||||||
|
|
||||||
linebuf[1023] = '\0';
|
linebuf[1023] = '\0';
|
||||||
|
|
||||||
char* start = linebuf;
|
char* start = linebuf;
|
||||||
|
@ -78,8 +91,8 @@ bool ARCodeFile::Load()
|
||||||
|
|
||||||
if (ret < 1)
|
if (ret < 1)
|
||||||
{
|
{
|
||||||
printf("AR: malformed CAT line: %s\n", start);
|
Log(LogLevel::Error, "AR: malformed CAT line: %s\n", start);
|
||||||
fclose(f);
|
CloseFile(f);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +102,7 @@ bool ARCodeFile::Load()
|
||||||
if (isincat) Categories.push_back(curcat);
|
if (isincat) Categories.push_back(curcat);
|
||||||
isincat = true;
|
isincat = true;
|
||||||
|
|
||||||
memcpy(curcat.Name, catname, 128);
|
curcat.Name = catname;
|
||||||
curcat.Codes.clear();
|
curcat.Codes.clear();
|
||||||
}
|
}
|
||||||
else if (!strncasecmp(start, "CODE", 4))
|
else if (!strncasecmp(start, "CODE", 4))
|
||||||
|
@ -101,24 +114,24 @@ bool ARCodeFile::Load()
|
||||||
|
|
||||||
if (ret < 2)
|
if (ret < 2)
|
||||||
{
|
{
|
||||||
printf("AR: malformed CODE line: %s\n", start);
|
Log(LogLevel::Error, "AR: malformed CODE line: %s\n", start);
|
||||||
fclose(f);
|
CloseFile(f);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isincat)
|
if (!isincat)
|
||||||
{
|
{
|
||||||
printf("AR: encountered CODE line with no category started\n");
|
Log(LogLevel::Error, "AR: encountered CODE line with no category started\n");
|
||||||
fclose(f);
|
CloseFile(f);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isincode) curcat.Codes.push_back(curcode);
|
if (isincode) curcat.Codes.push_back(curcode);
|
||||||
isincode = true;
|
isincode = true;
|
||||||
|
|
||||||
memcpy(curcode.Name, codename, 128);
|
curcode.Name = codename;
|
||||||
curcode.Enabled = enable!=0;
|
curcode.Enabled = enable!=0;
|
||||||
curcode.CodeLen = 0;
|
curcode.Code.clear();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -127,65 +140,58 @@ bool ARCodeFile::Load()
|
||||||
|
|
||||||
if (ret < 2)
|
if (ret < 2)
|
||||||
{
|
{
|
||||||
printf("AR: malformed data line: %s\n", start);
|
Log(LogLevel::Error, "AR: malformed data line: %s\n", start);
|
||||||
fclose(f);
|
CloseFile(f);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isincode)
|
if (!isincode)
|
||||||
{
|
{
|
||||||
printf("AR: encountered data line with no code started\n");
|
Log(LogLevel::Error, "AR: encountered data line with no code started\n");
|
||||||
fclose(f);
|
CloseFile(f);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curcode.CodeLen >= 2*64)
|
curcode.Code.push_back(c0);
|
||||||
{
|
curcode.Code.push_back(c1);
|
||||||
printf("AR: code too long!\n");
|
|
||||||
fclose(f);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 idx = curcode.CodeLen;
|
|
||||||
curcode.Code[idx+0] = c0;
|
|
||||||
curcode.Code[idx+1] = c1;
|
|
||||||
curcode.CodeLen += 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isincode) curcat.Codes.push_back(curcode);
|
if (isincode) curcat.Codes.push_back(curcode);
|
||||||
if (isincat) Categories.push_back(curcat);
|
if (isincat) Categories.push_back(curcat);
|
||||||
|
|
||||||
fclose(f);
|
CloseFile(f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ARCodeFile::Save()
|
bool ARCodeFile::Save()
|
||||||
{
|
{
|
||||||
FILE* f = Platform::OpenFile(Filename, "w");
|
FileHandle* f = Platform::OpenFile(Filename, FileMode::WriteText);
|
||||||
if (!f) return false;
|
if (!f) return false;
|
||||||
|
|
||||||
for (ARCodeCatList::iterator it = Categories.begin(); it != Categories.end(); it++)
|
for (ARCodeCatList::iterator it = Categories.begin(); it != Categories.end(); it++)
|
||||||
{
|
{
|
||||||
ARCodeCat& cat = *it;
|
ARCodeCat& cat = *it;
|
||||||
|
|
||||||
if (it != Categories.begin()) fprintf(f, "\n");
|
if (it != Categories.begin()) FileWriteFormatted(f, "\n");
|
||||||
fprintf(f, "CAT %s\n\n", cat.Name);
|
FileWriteFormatted(f, "CAT %s\n\n", cat.Name.c_str());
|
||||||
|
|
||||||
for (ARCodeList::iterator jt = cat.Codes.begin(); jt != cat.Codes.end(); jt++)
|
for (ARCodeList::iterator jt = cat.Codes.begin(); jt != cat.Codes.end(); jt++)
|
||||||
{
|
{
|
||||||
ARCode& code = *jt;
|
ARCode& code = *jt;
|
||||||
fprintf(f, "CODE %d %s\n", code.Enabled, code.Name);
|
FileWriteFormatted(f, "CODE %d %s\n", code.Enabled, code.Name.c_str());
|
||||||
|
|
||||||
for (u32 i = 0; i < code.CodeLen; i+=2)
|
for (size_t i = 0; i < code.Code.size(); i+=2)
|
||||||
{
|
{
|
||||||
fprintf(f, "%08X %08X\n", code.Code[i], code.Code[i+1]);
|
FileWriteFormatted(f, "%08X %08X\n", code.Code[i], code.Code[i + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(f, "\n");
|
FileWriteFormatted(f, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(f);
|
CloseFile(f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -19,27 +19,27 @@
|
||||||
#ifndef ARCODEFILE_H
|
#ifndef ARCODEFILE_H
|
||||||
#define ARCODEFILE_H
|
#define ARCODEFILE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
typedef struct
|
namespace melonDS
|
||||||
{
|
{
|
||||||
char Name[128];
|
struct ARCode
|
||||||
|
{
|
||||||
|
std::string Name;
|
||||||
bool Enabled;
|
bool Enabled;
|
||||||
u32 CodeLen;
|
std::vector<u32> Code;
|
||||||
u32 Code[2*64];
|
};
|
||||||
|
|
||||||
} ARCode;
|
|
||||||
|
|
||||||
typedef std::list<ARCode> ARCodeList;
|
typedef std::list<ARCode> ARCodeList;
|
||||||
|
|
||||||
typedef struct
|
struct ARCodeCat
|
||||||
{
|
{
|
||||||
char Name[128];
|
std::string Name;
|
||||||
ARCodeList Codes;
|
ARCodeList Codes;
|
||||||
|
};
|
||||||
} ARCodeCat;
|
|
||||||
|
|
||||||
typedef std::list<ARCodeCat> ARCodeCatList;
|
typedef std::list<ARCodeCat> ARCodeCatList;
|
||||||
|
|
||||||
|
@ -47,18 +47,21 @@ typedef std::list<ARCodeCat> ARCodeCatList;
|
||||||
class ARCodeFile
|
class ARCodeFile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ARCodeFile(const char* filename);
|
ARCodeFile(const std::string& filename);
|
||||||
~ARCodeFile();
|
~ARCodeFile() noexcept = default;
|
||||||
|
|
||||||
bool Error;
|
[[nodiscard]] std::vector<ARCode> GetCodes() const noexcept;
|
||||||
|
|
||||||
|
bool Error = false;
|
||||||
|
|
||||||
bool Load();
|
bool Load();
|
||||||
bool Save();
|
bool Save();
|
||||||
|
|
||||||
ARCodeCatList Categories;
|
ARCodeCatList Categories {};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char Filename[1024];
|
std::string Filename;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
#endif // ARCODEFILE_H
|
#endif // ARCODEFILE_H
|
||||||
|
|
154
src/AREngine.cpp
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -21,80 +21,34 @@
|
||||||
#include "NDS.h"
|
#include "NDS.h"
|
||||||
#include "DSi.h"
|
#include "DSi.h"
|
||||||
#include "AREngine.h"
|
#include "AREngine.h"
|
||||||
|
#include "Platform.h"
|
||||||
|
|
||||||
|
namespace melonDS
|
||||||
namespace AREngine
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// AR code file - frontend is responsible for managing this
|
using Platform::Log;
|
||||||
ARCodeFile* CodeFile;
|
using Platform::LogLevel;
|
||||||
|
|
||||||
u8 (*BusRead8)(u32 addr);
|
AREngine::AREngine(melonDS::NDS& nds) : NDS(nds)
|
||||||
u16 (*BusRead16)(u32 addr);
|
|
||||||
u32 (*BusRead32)(u32 addr);
|
|
||||||
void (*BusWrite8)(u32 addr, u8 val);
|
|
||||||
void (*BusWrite16)(u32 addr, u16 val);
|
|
||||||
void (*BusWrite32)(u32 addr, u32 val);
|
|
||||||
|
|
||||||
|
|
||||||
bool Init()
|
|
||||||
{
|
|
||||||
CodeFile = nullptr;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeInit()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reset()
|
|
||||||
{
|
|
||||||
CodeFile = nullptr;
|
|
||||||
|
|
||||||
if (NDS::ConsoleType == 1)
|
|
||||||
{
|
|
||||||
BusRead8 = DSi::ARM7Read8;
|
|
||||||
BusRead16 = DSi::ARM7Read16;
|
|
||||||
BusRead32 = DSi::ARM7Read32;
|
|
||||||
BusWrite8 = DSi::ARM7Write8;
|
|
||||||
BusWrite16 = DSi::ARM7Write16;
|
|
||||||
BusWrite32 = DSi::ARM7Write32;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BusRead8 = NDS::ARM7Read8;
|
|
||||||
BusRead16 = NDS::ARM7Read16;
|
|
||||||
BusRead32 = NDS::ARM7Read32;
|
|
||||||
BusWrite8 = NDS::ARM7Write8;
|
|
||||||
BusWrite16 = NDS::ARM7Write16;
|
|
||||||
BusWrite32 = NDS::ARM7Write32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SetCodeFile(ARCodeFile* file)
|
|
||||||
{
|
|
||||||
CodeFile = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define case16(x) \
|
#define case16(x) \
|
||||||
case ((x)+0x00): case ((x)+0x01): case ((x)+0x02): case ((x)+0x03): \
|
case ((x)+0x00): case ((x)+0x01): case ((x)+0x02): case ((x)+0x03): \
|
||||||
case ((x)+0x04): case ((x)+0x05): case ((x)+0x06): case ((x)+0x07): \
|
case ((x)+0x04): case ((x)+0x05): case ((x)+0x06): case ((x)+0x07): \
|
||||||
case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \
|
case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \
|
||||||
case ((x)+0x0C): case ((x)+0x0D): case ((x)+0x0E): case ((x)+0x0F)
|
case ((x)+0x0C): case ((x)+0x0D): case ((x)+0x0E): case ((x)+0x0F)
|
||||||
|
|
||||||
void RunCheat(ARCode& arcode)
|
void AREngine::RunCheat(const ARCode& arcode)
|
||||||
{
|
{
|
||||||
u32* code = &arcode.Code[0];
|
const u32* code = &arcode.Code[0];
|
||||||
|
|
||||||
u32 offset = 0;
|
u32 offset = 0;
|
||||||
u32 datareg = 0;
|
u32 datareg = 0;
|
||||||
u32 cond = 1;
|
u32 cond = 1;
|
||||||
u32 condstack = 0;
|
u32 condstack = 0;
|
||||||
|
|
||||||
u32* loopstart = code;
|
const u32* loopstart = code;
|
||||||
u32 loopcount = 0;
|
u32 loopcount = 0;
|
||||||
u32 loopcond = 1;
|
u32 loopcond = 1;
|
||||||
u32 loopcondstack = 0;
|
u32 loopcondstack = 0;
|
||||||
|
@ -104,7 +58,7 @@ void RunCheat(ARCode& arcode)
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
if (code >= &arcode.Code[arcode.CodeLen])
|
if (code >= &arcode.Code[arcode.Code.size()])
|
||||||
break;
|
break;
|
||||||
|
|
||||||
u32 a = *code++;
|
u32 a = *code++;
|
||||||
|
@ -129,15 +83,15 @@ void RunCheat(ARCode& arcode)
|
||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
case16(0x00): // 32-bit write
|
case16(0x00): // 32-bit write
|
||||||
BusWrite32((a & 0x0FFFFFFF) + offset, b);
|
NDS.ARM7Write32((a & 0x0FFFFFFF) + offset, b);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case16(0x10): // 16-bit write
|
case16(0x10): // 16-bit write
|
||||||
BusWrite16((a & 0x0FFFFFFF) + offset, b & 0xFFFF);
|
NDS.ARM7Write16((a & 0x0FFFFFFF) + offset, b & 0xFFFF);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case16(0x20): // 8-bit write
|
case16(0x20): // 8-bit write
|
||||||
BusWrite8((a & 0x0FFFFFFF) + offset, b & 0xFF);
|
NDS.ARM7Write8((a & 0x0FFFFFFF) + offset, b & 0xFF);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case16(0x30): // IF b > u32[a]
|
case16(0x30): // IF b > u32[a]
|
||||||
|
@ -145,7 +99,9 @@ void RunCheat(ARCode& arcode)
|
||||||
condstack <<= 1;
|
condstack <<= 1;
|
||||||
condstack |= cond;
|
condstack |= cond;
|
||||||
|
|
||||||
u32 chk = BusRead32(a & 0x0FFFFFFF);
|
u32 addr = a & 0x0FFFFFFF;
|
||||||
|
if (!addr) addr = offset;
|
||||||
|
u32 chk = NDS.ARM7Read32(addr);
|
||||||
|
|
||||||
cond = (b > chk) ? 1:0;
|
cond = (b > chk) ? 1:0;
|
||||||
}
|
}
|
||||||
|
@ -156,7 +112,9 @@ void RunCheat(ARCode& arcode)
|
||||||
condstack <<= 1;
|
condstack <<= 1;
|
||||||
condstack |= cond;
|
condstack |= cond;
|
||||||
|
|
||||||
u32 chk = BusRead32(a & 0x0FFFFFFF);
|
u32 addr = a & 0x0FFFFFFF;
|
||||||
|
if (!addr) addr = offset;
|
||||||
|
u32 chk = NDS.ARM7Read32(addr);
|
||||||
|
|
||||||
cond = (b < chk) ? 1:0;
|
cond = (b < chk) ? 1:0;
|
||||||
}
|
}
|
||||||
|
@ -167,7 +125,9 @@ void RunCheat(ARCode& arcode)
|
||||||
condstack <<= 1;
|
condstack <<= 1;
|
||||||
condstack |= cond;
|
condstack |= cond;
|
||||||
|
|
||||||
u32 chk = BusRead32(a & 0x0FFFFFFF);
|
u32 addr = a & 0x0FFFFFFF;
|
||||||
|
if (!addr) addr = offset;
|
||||||
|
u32 chk = NDS.ARM7Read32(addr);
|
||||||
|
|
||||||
cond = (b == chk) ? 1:0;
|
cond = (b == chk) ? 1:0;
|
||||||
}
|
}
|
||||||
|
@ -178,7 +138,9 @@ void RunCheat(ARCode& arcode)
|
||||||
condstack <<= 1;
|
condstack <<= 1;
|
||||||
condstack |= cond;
|
condstack |= cond;
|
||||||
|
|
||||||
u32 chk = BusRead32(a & 0x0FFFFFFF);
|
u32 addr = a & 0x0FFFFFFF;
|
||||||
|
if (!addr) addr = offset;
|
||||||
|
u32 chk = NDS.ARM7Read32(addr);
|
||||||
|
|
||||||
cond = (b != chk) ? 1:0;
|
cond = (b != chk) ? 1:0;
|
||||||
}
|
}
|
||||||
|
@ -189,7 +151,9 @@ void RunCheat(ARCode& arcode)
|
||||||
condstack <<= 1;
|
condstack <<= 1;
|
||||||
condstack |= cond;
|
condstack |= cond;
|
||||||
|
|
||||||
u16 val = BusRead16(a & 0x0FFFFFFF);
|
u32 addr = a & 0x0FFFFFFF;
|
||||||
|
if (!addr) addr = offset;
|
||||||
|
u16 val = NDS.ARM7Read16(addr);
|
||||||
u16 chk = ~(b >> 16);
|
u16 chk = ~(b >> 16);
|
||||||
chk &= val;
|
chk &= val;
|
||||||
|
|
||||||
|
@ -202,7 +166,9 @@ void RunCheat(ARCode& arcode)
|
||||||
condstack <<= 1;
|
condstack <<= 1;
|
||||||
condstack |= cond;
|
condstack |= cond;
|
||||||
|
|
||||||
u16 val = BusRead16(a & 0x0FFFFFFF);
|
u32 addr = a & 0x0FFFFFFF;
|
||||||
|
if (!addr) addr = offset;
|
||||||
|
u16 val = NDS.ARM7Read16(addr);
|
||||||
u16 chk = ~(b >> 16);
|
u16 chk = ~(b >> 16);
|
||||||
chk &= val;
|
chk &= val;
|
||||||
|
|
||||||
|
@ -215,7 +181,9 @@ void RunCheat(ARCode& arcode)
|
||||||
condstack <<= 1;
|
condstack <<= 1;
|
||||||
condstack |= cond;
|
condstack |= cond;
|
||||||
|
|
||||||
u16 val = BusRead16(a & 0x0FFFFFFF);
|
u32 addr = a & 0x0FFFFFFF;
|
||||||
|
if (!addr) addr = offset;
|
||||||
|
u16 val = NDS.ARM7Read16(addr);
|
||||||
u16 chk = ~(b >> 16);
|
u16 chk = ~(b >> 16);
|
||||||
chk &= val;
|
chk &= val;
|
||||||
|
|
||||||
|
@ -228,7 +196,9 @@ void RunCheat(ARCode& arcode)
|
||||||
condstack <<= 1;
|
condstack <<= 1;
|
||||||
condstack |= cond;
|
condstack |= cond;
|
||||||
|
|
||||||
u16 val = BusRead16(a & 0x0FFFFFFF);
|
u32 addr = a & 0x0FFFFFFF;
|
||||||
|
if (!addr) addr = offset;
|
||||||
|
u16 val = NDS.ARM7Read16(addr);
|
||||||
u16 chk = ~(b >> 16);
|
u16 chk = ~(b >> 16);
|
||||||
chk &= val;
|
chk &= val;
|
||||||
|
|
||||||
|
@ -237,7 +207,7 @@ void RunCheat(ARCode& arcode)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case16(0xB0): // offset = u32[a + offset]
|
case16(0xB0): // offset = u32[a + offset]
|
||||||
offset = BusRead32((a & 0x0FFFFFFF) + offset);
|
offset = NDS.ARM7Read32((a & 0x0FFFFFFF) + offset);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xC0: // FOR 0..b
|
case 0xC0: // FOR 0..b
|
||||||
|
@ -252,7 +222,7 @@ void RunCheat(ARCode& arcode)
|
||||||
// in practice could be used for a self-modifying AR code
|
// in practice could be used for a self-modifying AR code
|
||||||
// could be implemented with some hackery, but, does anything even
|
// could be implemented with some hackery, but, does anything even
|
||||||
// use it??
|
// use it??
|
||||||
printf("AR: !! THE FUCKING C4000000 OPCODE. TELL ARISOTURA.\n");
|
Log(LogLevel::Error, "AR: !! THE FUCKING C4000000 OPCODE. TELL ARISOTURA.\n");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0xC5: // count++ / IF (count & b.l) == b.h
|
case 0xC5: // count++ / IF (count & b.l) == b.h
|
||||||
|
@ -274,7 +244,7 @@ void RunCheat(ARCode& arcode)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xC6: // u32[b] = offset
|
case 0xC6: // u32[b] = offset
|
||||||
BusWrite32(b, offset);
|
NDS.ARM7Write32(b, offset);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xD0: // ENDIF
|
case 0xD0: // ENDIF
|
||||||
|
@ -323,30 +293,30 @@ void RunCheat(ARCode& arcode)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xD6: // u32[b+offset] = datareg / offset += 4
|
case 0xD6: // u32[b+offset] = datareg / offset += 4
|
||||||
BusWrite32(b + offset, datareg);
|
NDS.ARM7Write32(b + offset, datareg);
|
||||||
offset += 4;
|
offset += 4;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xD7: // u16[b+offset] = datareg / offset += 2
|
case 0xD7: // u16[b+offset] = datareg / offset += 2
|
||||||
BusWrite16(b + offset, datareg & 0xFFFF);
|
NDS.ARM7Write16(b + offset, datareg & 0xFFFF);
|
||||||
offset += 2;
|
offset += 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xD8: // u8[b+offset] = datareg / offset += 1
|
case 0xD8: // u8[b+offset] = datareg / offset += 1
|
||||||
BusWrite8(b + offset, datareg & 0xFF);
|
NDS.ARM7Write8(b + offset, datareg & 0xFF);
|
||||||
offset += 1;
|
offset += 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xD9: // datareg = u32[b+offset]
|
case 0xD9: // datareg = u32[b+offset]
|
||||||
datareg = BusRead32(b + offset);
|
datareg = NDS.ARM7Read32(b + offset);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xDA: // datareg = u16[b+offset]
|
case 0xDA: // datareg = u16[b+offset]
|
||||||
datareg = BusRead16(b + offset);
|
datareg = NDS.ARM7Read16(b + offset);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xDB: // datareg = u8[b+offset]
|
case 0xDB: // datareg = u8[b+offset]
|
||||||
datareg = BusRead8(b + offset);
|
datareg = NDS.ARM7Read8(b + offset);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xDC: // offset += b
|
case 0xDC: // offset += b
|
||||||
|
@ -361,8 +331,8 @@ void RunCheat(ARCode& arcode)
|
||||||
u32 bytesleft = b;
|
u32 bytesleft = b;
|
||||||
while (bytesleft >= 8)
|
while (bytesleft >= 8)
|
||||||
{
|
{
|
||||||
BusWrite32(dstaddr, *code++); dstaddr += 4;
|
NDS.ARM7Write32(dstaddr, *code++); dstaddr += 4;
|
||||||
BusWrite32(dstaddr, *code++); dstaddr += 4;
|
NDS.ARM7Write32(dstaddr, *code++); dstaddr += 4;
|
||||||
bytesleft -= 8;
|
bytesleft -= 8;
|
||||||
}
|
}
|
||||||
if (bytesleft > 0)
|
if (bytesleft > 0)
|
||||||
|
@ -371,13 +341,13 @@ void RunCheat(ARCode& arcode)
|
||||||
code += 2;
|
code += 2;
|
||||||
if (bytesleft >= 4)
|
if (bytesleft >= 4)
|
||||||
{
|
{
|
||||||
BusWrite32(dstaddr, *(u32*)leftover); dstaddr += 4;
|
NDS.ARM7Write32(dstaddr, *(u32*)leftover); dstaddr += 4;
|
||||||
leftover += 4;
|
leftover += 4;
|
||||||
bytesleft -= 4;
|
bytesleft -= 4;
|
||||||
}
|
}
|
||||||
while (bytesleft > 0)
|
while (bytesleft > 0)
|
||||||
{
|
{
|
||||||
BusWrite8(dstaddr, *leftover++); dstaddr++;
|
NDS.ARM7Write8(dstaddr, *leftover++); dstaddr++;
|
||||||
bytesleft--;
|
bytesleft--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -393,14 +363,14 @@ void RunCheat(ARCode& arcode)
|
||||||
u32 bytesleft = b;
|
u32 bytesleft = b;
|
||||||
while (bytesleft >= 4)
|
while (bytesleft >= 4)
|
||||||
{
|
{
|
||||||
BusWrite32(dstaddr, BusRead32(srcaddr));
|
NDS.ARM7Write32(dstaddr, NDS.ARM7Read32(srcaddr));
|
||||||
srcaddr += 4;
|
srcaddr += 4;
|
||||||
dstaddr += 4;
|
dstaddr += 4;
|
||||||
bytesleft -= 4;
|
bytesleft -= 4;
|
||||||
}
|
}
|
||||||
while (bytesleft > 0)
|
while (bytesleft > 0)
|
||||||
{
|
{
|
||||||
BusWrite8(dstaddr, BusRead8(srcaddr));
|
NDS.ARM7Write8(dstaddr, NDS.ARM7Read8(srcaddr));
|
||||||
srcaddr++;
|
srcaddr++;
|
||||||
dstaddr++;
|
dstaddr++;
|
||||||
bytesleft--;
|
bytesleft--;
|
||||||
|
@ -409,28 +379,20 @@ void RunCheat(ARCode& arcode)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
printf("!! bad AR opcode %08X %08X\n", a, b);
|
Log(LogLevel::Warn, "!! bad AR opcode %08X %08X\n", a, b);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunCheats()
|
void AREngine::RunCheats()
|
||||||
{
|
{
|
||||||
if (!CodeFile) return;
|
if (Cheats.empty()) return;
|
||||||
|
|
||||||
for (ARCodeCatList::iterator i = CodeFile->Categories.begin(); i != CodeFile->Categories.end(); i++)
|
for (const ARCode& code : Cheats)
|
||||||
{
|
{
|
||||||
ARCodeCat& cat = *i;
|
if (code.Enabled)
|
||||||
|
RunCheat(code);
|
||||||
for (ARCodeList::iterator j = cat.Codes.begin(); j != cat.Codes.end(); j++)
|
|
||||||
{
|
|
||||||
ARCode& code = *j;
|
|
||||||
|
|
||||||
if (code.Enabled)
|
|
||||||
RunCheat(code);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -19,19 +19,25 @@
|
||||||
#ifndef ARENGINE_H
|
#ifndef ARENGINE_H
|
||||||
#define ARENGINE_H
|
#define ARENGINE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
#include "ARCodeFile.h"
|
#include "ARCodeFile.h"
|
||||||
|
|
||||||
namespace AREngine
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
class NDS;
|
||||||
|
class AREngine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AREngine(melonDS::NDS& nds);
|
||||||
|
|
||||||
bool Init();
|
std::vector<ARCode> Cheats {};
|
||||||
void DeInit();
|
private:
|
||||||
void Reset();
|
friend class ARM;
|
||||||
|
void RunCheats();
|
||||||
|
void RunCheat(const ARCode& arcode);
|
||||||
|
|
||||||
void SetCodeFile(ARCodeFile* file);
|
melonDS::NDS& NDS;
|
||||||
|
};
|
||||||
void RunCheats();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ARENGINE_H
|
#endif // ARENGINE_H
|
||||||
|
|
1089
src/ARM.cpp
332
src/ARM.h
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -20,10 +20,18 @@
|
||||||
#define ARM_H
|
#define ARM_H
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "NDS.h"
|
#include "MemRegion.h"
|
||||||
|
#include "MemConstants.h"
|
||||||
|
|
||||||
|
#ifdef GDBSTUB_ENABLED
|
||||||
|
#include "debug/GdbStub.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace melonDS
|
||||||
|
{
|
||||||
inline u32 ROR(u32 x, u32 n)
|
inline u32 ROR(u32 x, u32 n)
|
||||||
{
|
{
|
||||||
return (x >> (n&0x1F)) | (x << ((32-n)&0x1F));
|
return (x >> (n&0x1F)) | (x << ((32-n)&0x1F));
|
||||||
|
@ -35,15 +43,33 @@ enum
|
||||||
RWFlags_ForceUser = (1<<21),
|
RWFlags_ForceUser = (1<<21),
|
||||||
};
|
};
|
||||||
|
|
||||||
const u32 ITCMPhysicalSize = 0x8000;
|
enum class CPUExecuteMode : u32
|
||||||
const u32 DTCMPhysicalSize = 0x4000;
|
{
|
||||||
|
Interpreter,
|
||||||
|
InterpreterGDB,
|
||||||
|
#ifdef JIT_ENABLED
|
||||||
|
JIT
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GDBArgs;
|
||||||
|
class ARMJIT;
|
||||||
|
class GPU;
|
||||||
|
class ARMJIT_Memory;
|
||||||
|
class NDS;
|
||||||
|
class Savestate;
|
||||||
|
|
||||||
class ARM
|
class ARM
|
||||||
|
#ifdef GDBSTUB_ENABLED
|
||||||
|
: public Gdb::StubCallbacks
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ARM(u32 num);
|
ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, NDS& nds);
|
||||||
virtual ~ARM(); // destroy shit
|
virtual ~ARM(); // destroy shit
|
||||||
|
|
||||||
|
void SetGdbArgs(std::optional<GDBArgs> gdb);
|
||||||
|
|
||||||
virtual void Reset();
|
virtual void Reset();
|
||||||
|
|
||||||
virtual void DoSavestate(Savestate* file);
|
virtual void DoSavestate(Savestate* file);
|
||||||
|
@ -59,12 +85,9 @@ public:
|
||||||
Halted = halt;
|
Halted = halt;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Execute() = 0;
|
void NocashPrint(u32 addr) noexcept;
|
||||||
#ifdef JIT_ENABLED
|
|
||||||
virtual void ExecuteJIT() = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool CheckCondition(u32 code)
|
bool CheckCondition(u32 code) const
|
||||||
{
|
{
|
||||||
if (code == 0xE) return true;
|
if (code == 0xE) return true;
|
||||||
if (ConditionTable[code] & (1 << (CPSR>>28))) return true;
|
if (ConditionTable[code] & (1 << (CPSR>>28))) return true;
|
||||||
|
@ -93,7 +116,19 @@ public:
|
||||||
if (v) CPSR |= 0x10000000;
|
if (v) CPSR |= 0x10000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateMode(u32 oldmode, u32 newmode);
|
inline bool ModeIs(u32 mode) const
|
||||||
|
{
|
||||||
|
u32 cm = CPSR & 0x1f;
|
||||||
|
mode &= 0x1f;
|
||||||
|
|
||||||
|
if (mode == cm) return true;
|
||||||
|
if (mode == 0x17) return cm >= 0x14 && cm <= 0x17; // abt
|
||||||
|
if (mode == 0x1b) return cm >= 0x18 && cm <= 0x1b; // und
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateMode(u32 oldmode, u32 newmode, bool phony = false);
|
||||||
|
|
||||||
void TriggerIRQ();
|
void TriggerIRQ();
|
||||||
|
|
||||||
|
@ -114,6 +149,7 @@ public:
|
||||||
virtual void AddCycles_CDI() = 0;
|
virtual void AddCycles_CDI() = 0;
|
||||||
virtual void AddCycles_CD() = 0;
|
virtual void AddCycles_CD() = 0;
|
||||||
|
|
||||||
|
void CheckGdbIncoming();
|
||||||
|
|
||||||
u32 Num;
|
u32 Num;
|
||||||
|
|
||||||
|
@ -147,75 +183,101 @@ public:
|
||||||
|
|
||||||
u32 ExceptionBase;
|
u32 ExceptionBase;
|
||||||
|
|
||||||
NDS::MemRegion CodeMem;
|
MemRegion CodeMem;
|
||||||
|
|
||||||
#ifdef JIT_ENABLED
|
#ifdef JIT_ENABLED
|
||||||
u32 FastBlockLookupStart, FastBlockLookupSize;
|
u32 FastBlockLookupStart, FastBlockLookupSize;
|
||||||
u64* FastBlockLookup;
|
u64* FastBlockLookup;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static u32 ConditionTable[16];
|
static const u32 ConditionTable[16];
|
||||||
|
#ifdef GDBSTUB_ENABLED
|
||||||
|
Gdb::GdbStub GdbStub;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
melonDS::NDS& NDS;
|
||||||
|
protected:
|
||||||
|
virtual u8 BusRead8(u32 addr) = 0;
|
||||||
|
virtual u16 BusRead16(u32 addr) = 0;
|
||||||
|
virtual u32 BusRead32(u32 addr) = 0;
|
||||||
|
virtual void BusWrite8(u32 addr, u8 val) = 0;
|
||||||
|
virtual void BusWrite16(u32 addr, u16 val) = 0;
|
||||||
|
virtual void BusWrite32(u32 addr, u32 val) = 0;
|
||||||
|
|
||||||
|
#ifdef GDBSTUB_ENABLED
|
||||||
|
bool IsSingleStep;
|
||||||
|
bool BreakReq;
|
||||||
|
bool BreakOnStartup;
|
||||||
|
u16 Port;
|
||||||
|
|
||||||
|
public:
|
||||||
|
int GetCPU() const override { return Num ? 7 : 9; }
|
||||||
|
|
||||||
|
u32 ReadReg(Gdb::Register reg) override;
|
||||||
|
void WriteReg(Gdb::Register reg, u32 v) override;
|
||||||
|
u32 ReadMem(u32 addr, int size) override;
|
||||||
|
void WriteMem(u32 addr, int size, u32 v) override;
|
||||||
|
|
||||||
|
void ResetGdb() override;
|
||||||
|
int RemoteCmd(const u8* cmd, size_t len) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
u8 (*BusRead8)(u32 addr);
|
#endif
|
||||||
u16 (*BusRead16)(u32 addr);
|
|
||||||
u32 (*BusRead32)(u32 addr);
|
void GdbCheckA();
|
||||||
void (*BusWrite8)(u32 addr, u8 val);
|
void GdbCheckB();
|
||||||
void (*BusWrite16)(u32 addr, u16 val);
|
void GdbCheckC();
|
||||||
void (*BusWrite32)(u32 addr, u32 val);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ARMv5 : public ARM
|
class ARMv5 : public ARM
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ARMv5();
|
ARMv5(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit);
|
||||||
~ARMv5();
|
~ARMv5();
|
||||||
|
|
||||||
void Reset();
|
void Reset() override;
|
||||||
|
|
||||||
void DoSavestate(Savestate* file);
|
void DoSavestate(Savestate* file) override;
|
||||||
|
|
||||||
void UpdateRegionTimings(u32 addrstart, u32 addrend);
|
void UpdateRegionTimings(u32 addrstart, u32 addrend);
|
||||||
|
|
||||||
void FillPipeline();
|
void FillPipeline() override;
|
||||||
|
|
||||||
void JumpTo(u32 addr, bool restorecpsr = false);
|
void JumpTo(u32 addr, bool restorecpsr = false) override;
|
||||||
|
|
||||||
void PrefetchAbort();
|
void PrefetchAbort();
|
||||||
void DataAbort();
|
void DataAbort();
|
||||||
|
|
||||||
|
template <CPUExecuteMode mode>
|
||||||
void Execute();
|
void Execute();
|
||||||
#ifdef JIT_ENABLED
|
|
||||||
void ExecuteJIT();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// all code accesses are forced nonseq 32bit
|
// all code accesses are forced nonseq 32bit
|
||||||
u32 CodeRead32(u32 addr, bool branch);
|
u32 CodeRead32(u32 addr, bool branch);
|
||||||
|
|
||||||
void DataRead8(u32 addr, u32* val);
|
void DataRead8(u32 addr, u32* val) override;
|
||||||
void DataRead16(u32 addr, u32* val);
|
void DataRead16(u32 addr, u32* val) override;
|
||||||
void DataRead32(u32 addr, u32* val);
|
void DataRead32(u32 addr, u32* val) override;
|
||||||
void DataRead32S(u32 addr, u32* val);
|
void DataRead32S(u32 addr, u32* val) override;
|
||||||
void DataWrite8(u32 addr, u8 val);
|
void DataWrite8(u32 addr, u8 val) override;
|
||||||
void DataWrite16(u32 addr, u16 val);
|
void DataWrite16(u32 addr, u16 val) override;
|
||||||
void DataWrite32(u32 addr, u32 val);
|
void DataWrite32(u32 addr, u32 val) override;
|
||||||
void DataWrite32S(u32 addr, u32 val);
|
void DataWrite32S(u32 addr, u32 val) override;
|
||||||
|
|
||||||
void AddCycles_C()
|
void AddCycles_C() override
|
||||||
{
|
{
|
||||||
// code only. always nonseq 32-bit for ARM9.
|
// code only. always nonseq 32-bit for ARM9.
|
||||||
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
|
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
|
||||||
Cycles += numC;
|
Cycles += numC;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddCycles_CI(s32 numI)
|
void AddCycles_CI(s32 numI) override
|
||||||
{
|
{
|
||||||
// code+internal
|
// code+internal
|
||||||
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
|
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
|
||||||
Cycles += numC + numI;
|
Cycles += numC + numI;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddCycles_CDI()
|
void AddCycles_CDI() override
|
||||||
{
|
{
|
||||||
// LDR/LDM cycles. ARM9 seems to skip the internal cycle there.
|
// LDR/LDM cycles. ARM9 seems to skip the internal cycle there.
|
||||||
// TODO: ITCM data fetches shouldn't be parallelized, they say
|
// TODO: ITCM data fetches shouldn't be parallelized, they say
|
||||||
|
@ -228,7 +290,7 @@ public:
|
||||||
// Cycles += numC + numD;
|
// Cycles += numC + numD;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddCycles_CD()
|
void AddCycles_CD() override
|
||||||
{
|
{
|
||||||
// TODO: ITCM data fetches shouldn't be parallelized, they say
|
// TODO: ITCM data fetches shouldn't be parallelized, they say
|
||||||
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
|
s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
|
||||||
|
@ -240,7 +302,7 @@ public:
|
||||||
// Cycles += numC + numD;
|
// Cycles += numC + numD;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetCodeMemRegion(u32 addr, NDS::MemRegion* region);
|
void GetCodeMemRegion(u32 addr, MemRegion* region);
|
||||||
|
|
||||||
void CP15Reset();
|
void CP15Reset();
|
||||||
void CP15DoSavestate(Savestate* file);
|
void CP15DoSavestate(Savestate* file);
|
||||||
|
@ -258,18 +320,19 @@ public:
|
||||||
void ICacheInvalidateAll();
|
void ICacheInvalidateAll();
|
||||||
|
|
||||||
void CP15Write(u32 id, u32 val);
|
void CP15Write(u32 id, u32 val);
|
||||||
u32 CP15Read(u32 id);
|
u32 CP15Read(u32 id) const;
|
||||||
|
|
||||||
u32 CP15Control;
|
u32 CP15Control;
|
||||||
|
|
||||||
u32 RNGSeed;
|
u32 RNGSeed;
|
||||||
|
u32 TraceProcessID;
|
||||||
|
|
||||||
u32 DTCMSetting, ITCMSetting;
|
u32 DTCMSetting, ITCMSetting;
|
||||||
|
|
||||||
// for aarch64 JIT they need to go up here
|
// for aarch64 JIT they need to go up here
|
||||||
// to be addressable by a 12-bit immediate
|
// to be addressable by a 12-bit immediate
|
||||||
u32 ITCMSize;
|
u32 ITCMSize;
|
||||||
u32 DTCMBase, DTCMSize;
|
u32 DTCMBase, DTCMMask;
|
||||||
s32 RegionCodeCycles;
|
s32 RegionCodeCycles;
|
||||||
|
|
||||||
u8 ITCM[ITCMPhysicalSize];
|
u8 ITCM[ITCMPhysicalSize];
|
||||||
|
@ -293,31 +356,41 @@ public:
|
||||||
u8 PU_UserMap[0x100000];
|
u8 PU_UserMap[0x100000];
|
||||||
|
|
||||||
// games operate under system mode, generally
|
// games operate under system mode, generally
|
||||||
#define PU_Map PU_PrivMap
|
//#define PU_Map PU_PrivMap
|
||||||
|
u8* PU_Map;
|
||||||
|
|
||||||
// code/16N/32N/32S
|
// code/16N/32N/32S
|
||||||
u8 MemTimings[0x100000][4];
|
u8 MemTimings[0x100000][4];
|
||||||
|
|
||||||
u8* CurICacheLine;
|
u8* CurICacheLine;
|
||||||
|
|
||||||
bool (*GetMemRegion)(u32 addr, bool write, NDS::MemRegion* region);
|
bool (*GetMemRegion)(u32 addr, bool write, MemRegion* region);
|
||||||
|
|
||||||
|
#ifdef GDBSTUB_ENABLED
|
||||||
|
u32 ReadMem(u32 addr, int size) override;
|
||||||
|
void WriteMem(u32 addr, int size, u32 v) override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
u8 BusRead8(u32 addr) override;
|
||||||
|
u16 BusRead16(u32 addr) override;
|
||||||
|
u32 BusRead32(u32 addr) override;
|
||||||
|
void BusWrite8(u32 addr, u8 val) override;
|
||||||
|
void BusWrite16(u32 addr, u16 val) override;
|
||||||
|
void BusWrite32(u32 addr, u32 val) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ARMv4 : public ARM
|
class ARMv4 : public ARM
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ARMv4();
|
ARMv4(melonDS::NDS& nds, std::optional<GDBArgs> gdb, bool jit);
|
||||||
|
|
||||||
void Reset();
|
void FillPipeline() override;
|
||||||
|
|
||||||
void FillPipeline();
|
void JumpTo(u32 addr, bool restorecpsr = false) override;
|
||||||
|
|
||||||
void JumpTo(u32 addr, bool restorecpsr = false);
|
|
||||||
|
|
||||||
|
template <CPUExecuteMode mode>
|
||||||
void Execute();
|
void Execute();
|
||||||
#ifdef JIT_ENABLED
|
|
||||||
void ExecuteJIT();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
u16 CodeRead16(u32 addr)
|
u16 CodeRead16(u32 addr)
|
||||||
{
|
{
|
||||||
|
@ -329,134 +402,25 @@ public:
|
||||||
return BusRead32(addr);
|
return BusRead32(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataRead8(u32 addr, u32* val)
|
void DataRead8(u32 addr, u32* val) override;
|
||||||
{
|
void DataRead16(u32 addr, u32* val) override;
|
||||||
*val = BusRead8(addr);
|
void DataRead32(u32 addr, u32* val) override;
|
||||||
DataRegion = addr;
|
void DataRead32S(u32 addr, u32* val) override;
|
||||||
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
|
void DataWrite8(u32 addr, u8 val) override;
|
||||||
}
|
void DataWrite16(u32 addr, u16 val) override;
|
||||||
|
void DataWrite32(u32 addr, u32 val) override;
|
||||||
void DataRead16(u32 addr, u32* val)
|
void DataWrite32S(u32 addr, u32 val) override;
|
||||||
{
|
void AddCycles_C() override;
|
||||||
addr &= ~1;
|
void AddCycles_CI(s32 num) override;
|
||||||
|
void AddCycles_CDI() override;
|
||||||
*val = BusRead16(addr);
|
void AddCycles_CD() override;
|
||||||
DataRegion = addr;
|
protected:
|
||||||
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
|
u8 BusRead8(u32 addr) override;
|
||||||
}
|
u16 BusRead16(u32 addr) override;
|
||||||
|
u32 BusRead32(u32 addr) override;
|
||||||
void DataRead32(u32 addr, u32* val)
|
void BusWrite8(u32 addr, u8 val) override;
|
||||||
{
|
void BusWrite16(u32 addr, u16 val) override;
|
||||||
addr &= ~3;
|
void BusWrite32(u32 addr, u32 val) override;
|
||||||
|
|
||||||
*val = BusRead32(addr);
|
|
||||||
DataRegion = addr;
|
|
||||||
DataCycles = NDS::ARM7MemTimings[addr >> 15][2];
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataRead32S(u32 addr, u32* val)
|
|
||||||
{
|
|
||||||
addr &= ~3;
|
|
||||||
|
|
||||||
*val = BusRead32(addr);
|
|
||||||
DataCycles += NDS::ARM7MemTimings[addr >> 15][3];
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataWrite8(u32 addr, u8 val)
|
|
||||||
{
|
|
||||||
BusWrite8(addr, val);
|
|
||||||
DataRegion = addr;
|
|
||||||
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataWrite16(u32 addr, u16 val)
|
|
||||||
{
|
|
||||||
addr &= ~1;
|
|
||||||
|
|
||||||
BusWrite16(addr, val);
|
|
||||||
DataRegion = addr;
|
|
||||||
DataCycles = NDS::ARM7MemTimings[addr >> 15][0];
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataWrite32(u32 addr, u32 val)
|
|
||||||
{
|
|
||||||
addr &= ~3;
|
|
||||||
|
|
||||||
BusWrite32(addr, val);
|
|
||||||
DataRegion = addr;
|
|
||||||
DataCycles = NDS::ARM7MemTimings[addr >> 15][2];
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataWrite32S(u32 addr, u32 val)
|
|
||||||
{
|
|
||||||
addr &= ~3;
|
|
||||||
|
|
||||||
BusWrite32(addr, val);
|
|
||||||
DataCycles += NDS::ARM7MemTimings[addr >> 15][3];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void AddCycles_C()
|
|
||||||
{
|
|
||||||
// code only. this code fetch is sequential.
|
|
||||||
Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?1:3];
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddCycles_CI(s32 num)
|
|
||||||
{
|
|
||||||
// code+internal. results in a nonseq code fetch.
|
|
||||||
Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2] + num;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddCycles_CDI()
|
|
||||||
{
|
|
||||||
// LDR/LDM cycles.
|
|
||||||
s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2];
|
|
||||||
s32 numD = DataCycles;
|
|
||||||
|
|
||||||
if ((DataRegion >> 24) == 0x02) // mainRAM
|
|
||||||
{
|
|
||||||
if (CodeRegion == 0x02)
|
|
||||||
Cycles += numC + numD;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
numC++;
|
|
||||||
Cycles += std::max(numC + numD - 3, std::max(numC, numD));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (CodeRegion == 0x02)
|
|
||||||
{
|
|
||||||
numD++;
|
|
||||||
Cycles += std::max(numC + numD - 3, std::max(numC, numD));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Cycles += numC + numD + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddCycles_CD()
|
|
||||||
{
|
|
||||||
// TODO: max gain should be 5c when writing to mainRAM
|
|
||||||
s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2];
|
|
||||||
s32 numD = DataCycles;
|
|
||||||
|
|
||||||
if ((DataRegion >> 24) == 0x02)
|
|
||||||
{
|
|
||||||
if (CodeRegion == 0x02)
|
|
||||||
Cycles += numC + numD;
|
|
||||||
else
|
|
||||||
Cycles += std::max(numC + numD - 3, std::max(numC, numD));
|
|
||||||
}
|
|
||||||
else if (CodeRegion == 0x02)
|
|
||||||
{
|
|
||||||
Cycles += std::max(numC + numD - 3, std::max(numC, numD));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Cycles += numC + numD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace ARMInterpreter
|
namespace ARMInterpreter
|
||||||
|
@ -466,13 +430,5 @@ void A_UNK(ARM* cpu);
|
||||||
void T_UNK(ARM* cpu);
|
void T_UNK(ARM* cpu);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace NDS
|
|
||||||
{
|
|
||||||
|
|
||||||
extern ARMv5* ARM9;
|
|
||||||
extern ARMv4* ARM7;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ARM_H
|
#endif // ARM_H
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -22,15 +22,24 @@
|
||||||
#include "ARMInterpreter_ALU.h"
|
#include "ARMInterpreter_ALU.h"
|
||||||
#include "ARMInterpreter_Branch.h"
|
#include "ARMInterpreter_Branch.h"
|
||||||
#include "ARMInterpreter_LoadStore.h"
|
#include "ARMInterpreter_LoadStore.h"
|
||||||
|
#include "Platform.h"
|
||||||
|
|
||||||
|
#ifdef GDBSTUB_ENABLED
|
||||||
|
#include "debug/GdbStub.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ARMInterpreter
|
namespace melonDS::ARMInterpreter
|
||||||
{
|
{
|
||||||
|
using Platform::Log;
|
||||||
|
using Platform::LogLevel;
|
||||||
|
|
||||||
|
|
||||||
void A_UNK(ARM* cpu)
|
void A_UNK(ARM* cpu)
|
||||||
{
|
{
|
||||||
printf("undefined ARM%d instruction %08X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-8);
|
Log(LogLevel::Warn, "undefined ARM%d instruction %08X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-8);
|
||||||
|
#ifdef GDBSTUB_ENABLED
|
||||||
|
cpu->GdbStub.Enter(cpu->GdbStub.IsConnected(), Gdb::TgtStatus::FaultInsn, cpu->R[15]-8);
|
||||||
|
#endif
|
||||||
//for (int i = 0; i < 16; i++) printf("R%d: %08X\n", i, cpu->R[i]);
|
//for (int i = 0; i < 16; i++) printf("R%d: %08X\n", i, cpu->R[i]);
|
||||||
//NDS::Halt();
|
//NDS::Halt();
|
||||||
u32 oldcpsr = cpu->CPSR;
|
u32 oldcpsr = cpu->CPSR;
|
||||||
|
@ -45,7 +54,10 @@ void A_UNK(ARM* cpu)
|
||||||
|
|
||||||
void T_UNK(ARM* cpu)
|
void T_UNK(ARM* cpu)
|
||||||
{
|
{
|
||||||
printf("undefined THUMB%d instruction %04X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-4);
|
Log(LogLevel::Warn, "undefined THUMB%d instruction %04X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-4);
|
||||||
|
#ifdef GDBSTUB_ENABLED
|
||||||
|
cpu->GdbStub.Enter(cpu->GdbStub.IsConnected(), Gdb::TgtStatus::FaultInsn, cpu->R[15]-4);
|
||||||
|
#endif
|
||||||
//NDS::Halt();
|
//NDS::Halt();
|
||||||
u32 oldcpsr = cpu->CPSR;
|
u32 oldcpsr = cpu->CPSR;
|
||||||
cpu->CPSR &= ~0xBF;
|
cpu->CPSR &= ~0xBF;
|
||||||
|
@ -69,9 +81,17 @@ void A_MSR_IMM(ARM* cpu)
|
||||||
case 0x11: psr = &cpu->R_FIQ[7]; break;
|
case 0x11: psr = &cpu->R_FIQ[7]; break;
|
||||||
case 0x12: psr = &cpu->R_IRQ[2]; break;
|
case 0x12: psr = &cpu->R_IRQ[2]; break;
|
||||||
case 0x13: psr = &cpu->R_SVC[2]; break;
|
case 0x13: psr = &cpu->R_SVC[2]; break;
|
||||||
|
case 0x14:
|
||||||
|
case 0x15:
|
||||||
|
case 0x16:
|
||||||
case 0x17: psr = &cpu->R_ABT[2]; break;
|
case 0x17: psr = &cpu->R_ABT[2]; break;
|
||||||
|
case 0x18:
|
||||||
|
case 0x19:
|
||||||
|
case 0x1A:
|
||||||
case 0x1B: psr = &cpu->R_UND[2]; break;
|
case 0x1B: psr = &cpu->R_UND[2]; break;
|
||||||
default: printf("bad CPU mode %08X\n", cpu->CPSR); return;
|
default:
|
||||||
|
cpu->AddCycles_C();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -92,6 +112,9 @@ void A_MSR_IMM(ARM* cpu)
|
||||||
|
|
||||||
u32 val = ROR((cpu->CurInstr & 0xFF), ((cpu->CurInstr >> 7) & 0x1E));
|
u32 val = ROR((cpu->CurInstr & 0xFF), ((cpu->CurInstr >> 7) & 0x1E));
|
||||||
|
|
||||||
|
// bit4 is forced to 1
|
||||||
|
val |= 0x00000010;
|
||||||
|
|
||||||
*psr &= ~mask;
|
*psr &= ~mask;
|
||||||
*psr |= (val & mask);
|
*psr |= (val & mask);
|
||||||
|
|
||||||
|
@ -111,9 +134,17 @@ void A_MSR_REG(ARM* cpu)
|
||||||
case 0x11: psr = &cpu->R_FIQ[7]; break;
|
case 0x11: psr = &cpu->R_FIQ[7]; break;
|
||||||
case 0x12: psr = &cpu->R_IRQ[2]; break;
|
case 0x12: psr = &cpu->R_IRQ[2]; break;
|
||||||
case 0x13: psr = &cpu->R_SVC[2]; break;
|
case 0x13: psr = &cpu->R_SVC[2]; break;
|
||||||
|
case 0x14:
|
||||||
|
case 0x15:
|
||||||
|
case 0x16:
|
||||||
case 0x17: psr = &cpu->R_ABT[2]; break;
|
case 0x17: psr = &cpu->R_ABT[2]; break;
|
||||||
|
case 0x18:
|
||||||
|
case 0x19:
|
||||||
|
case 0x1A:
|
||||||
case 0x1B: psr = &cpu->R_UND[2]; break;
|
case 0x1B: psr = &cpu->R_UND[2]; break;
|
||||||
default: printf("bad CPU mode %08X\n", cpu->CPSR); return;
|
default:
|
||||||
|
cpu->AddCycles_C();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -134,6 +165,9 @@ void A_MSR_REG(ARM* cpu)
|
||||||
|
|
||||||
u32 val = cpu->R[cpu->CurInstr & 0xF];
|
u32 val = cpu->R[cpu->CurInstr & 0xF];
|
||||||
|
|
||||||
|
// bit4 is forced to 1
|
||||||
|
val |= 0x00000010;
|
||||||
|
|
||||||
*psr &= ~mask;
|
*psr &= ~mask;
|
||||||
*psr |= (val & mask);
|
*psr |= (val & mask);
|
||||||
|
|
||||||
|
@ -153,9 +187,15 @@ void A_MRS(ARM* cpu)
|
||||||
case 0x11: psr = cpu->R_FIQ[7]; break;
|
case 0x11: psr = cpu->R_FIQ[7]; break;
|
||||||
case 0x12: psr = cpu->R_IRQ[2]; break;
|
case 0x12: psr = cpu->R_IRQ[2]; break;
|
||||||
case 0x13: psr = cpu->R_SVC[2]; break;
|
case 0x13: psr = cpu->R_SVC[2]; break;
|
||||||
|
case 0x14:
|
||||||
|
case 0x15:
|
||||||
|
case 0x16:
|
||||||
case 0x17: psr = cpu->R_ABT[2]; break;
|
case 0x17: psr = cpu->R_ABT[2]; break;
|
||||||
|
case 0x18:
|
||||||
|
case 0x19:
|
||||||
|
case 0x1A:
|
||||||
case 0x1B: psr = cpu->R_UND[2]; break;
|
case 0x1B: psr = cpu->R_UND[2]; break;
|
||||||
default: printf("bad CPU mode %08X\n", cpu->CPSR); return;
|
default: psr = cpu->CPSR; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -168,6 +208,9 @@ void A_MRS(ARM* cpu)
|
||||||
|
|
||||||
void A_MCR(ARM* cpu)
|
void A_MCR(ARM* cpu)
|
||||||
{
|
{
|
||||||
|
if ((cpu->CPSR & 0x1F) == 0x10)
|
||||||
|
return A_UNK(cpu);
|
||||||
|
|
||||||
u32 cp = (cpu->CurInstr >> 8) & 0xF;
|
u32 cp = (cpu->CurInstr >> 8) & 0xF;
|
||||||
//u32 op = (cpu->CurInstr >> 21) & 0x7;
|
//u32 op = (cpu->CurInstr >> 21) & 0x7;
|
||||||
u32 cn = (cpu->CurInstr >> 16) & 0xF;
|
u32 cn = (cpu->CurInstr >> 16) & 0xF;
|
||||||
|
@ -180,11 +223,11 @@ void A_MCR(ARM* cpu)
|
||||||
}
|
}
|
||||||
else if (cpu->Num==1 && cp==14)
|
else if (cpu->Num==1 && cp==14)
|
||||||
{
|
{
|
||||||
printf("MCR p14,%d,%d,%d on ARM7\n", cn, cm, cpinfo);
|
Log(LogLevel::Debug, "MCR p14,%d,%d,%d on ARM7\n", cn, cm, cpinfo);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf("bad MCR opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
|
Log(LogLevel::Warn, "bad MCR opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
|
||||||
return A_UNK(cpu); // TODO: check what kind of exception it really is
|
return A_UNK(cpu); // TODO: check what kind of exception it really is
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,6 +236,9 @@ void A_MCR(ARM* cpu)
|
||||||
|
|
||||||
void A_MRC(ARM* cpu)
|
void A_MRC(ARM* cpu)
|
||||||
{
|
{
|
||||||
|
if ((cpu->CPSR & 0x1F) == 0x10)
|
||||||
|
return A_UNK(cpu);
|
||||||
|
|
||||||
u32 cp = (cpu->CurInstr >> 8) & 0xF;
|
u32 cp = (cpu->CurInstr >> 8) & 0xF;
|
||||||
//u32 op = (cpu->CurInstr >> 21) & 0x7;
|
//u32 op = (cpu->CurInstr >> 21) & 0x7;
|
||||||
u32 cn = (cpu->CurInstr >> 16) & 0xF;
|
u32 cn = (cpu->CurInstr >> 16) & 0xF;
|
||||||
|
@ -205,11 +251,11 @@ void A_MRC(ARM* cpu)
|
||||||
}
|
}
|
||||||
else if (cpu->Num==1 && cp==14)
|
else if (cpu->Num==1 && cp==14)
|
||||||
{
|
{
|
||||||
printf("MRC p14,%d,%d,%d on ARM7\n", cn, cm, cpinfo);
|
Log(LogLevel::Debug, "MRC p14,%d,%d,%d on ARM7\n", cn, cm, cpinfo);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf("bad MRC opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
|
Log(LogLevel::Warn, "bad MRC opcode p%d,%d,%d,%d on ARM%d\n", cp, cn, cm, cpinfo, cpu->Num?7:9);
|
||||||
return A_UNK(cpu); // TODO: check what kind of exception it really is
|
return A_UNK(cpu); // TODO: check what kind of exception it really is
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "ARM.h"
|
#include "ARM.h"
|
||||||
|
|
||||||
|
namespace melonDS
|
||||||
|
{
|
||||||
namespace ARMInterpreter
|
namespace ARMInterpreter
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -41,4 +43,5 @@ void A_BLX_IMM(ARM* cpu); // I'm a special one look at me
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
#endif // ARMINTERPRETER_H
|
#endif // ARMINTERPRETER_H
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -18,18 +18,46 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "ARM.h"
|
#include "ARM.h"
|
||||||
|
#include "NDS.h"
|
||||||
|
|
||||||
|
namespace melonDS::ARMInterpreter
|
||||||
#define CARRY_ADD(a, b) ((0xFFFFFFFF-a) < b)
|
|
||||||
#define CARRY_SUB(a, b) (a >= b)
|
|
||||||
|
|
||||||
#define OVERFLOW_ADD(a, b, res) ((!(((a) ^ (b)) & 0x80000000)) && (((a) ^ (res)) & 0x80000000))
|
|
||||||
#define OVERFLOW_SUB(a, b, res) ((((a) ^ (b)) & 0x80000000) && (((a) ^ (res)) & 0x80000000))
|
|
||||||
|
|
||||||
|
|
||||||
namespace ARMInterpreter
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
inline bool CarryAdd(u32 a, u32 b)
|
||||||
|
{
|
||||||
|
return (0xFFFFFFFF-a) < b;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool CarrySub(u32 a, u32 b)
|
||||||
|
{
|
||||||
|
return a >= b;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool OverflowAdd(u32 a, u32 b)
|
||||||
|
{
|
||||||
|
u32 res = a + b;
|
||||||
|
return (!((a ^ b) & 0x80000000)) && ((a ^ res) & 0x80000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool OverflowSub(u32 a, u32 b)
|
||||||
|
{
|
||||||
|
u32 res = a - b;
|
||||||
|
return ((a ^ b) & 0x80000000) && ((a ^ res) & 0x80000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool OverflowAdc(u32 a, u32 b, u32 carry)
|
||||||
|
{
|
||||||
|
s64 fullResult = (s64)(s32)a + (s32)b + carry;
|
||||||
|
u32 res = a + b + carry;
|
||||||
|
return (s32)res != fullResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool OverflowSbc(u32 a, u32 b, u32 carry)
|
||||||
|
{
|
||||||
|
s64 fullResult = (s64)(s32)a - (s32)b - carry;
|
||||||
|
u32 res = a - b - carry;
|
||||||
|
return (s32)res != fullResult;
|
||||||
|
}
|
||||||
|
|
||||||
#define LSL_IMM(x, s) \
|
#define LSL_IMM(x, s) \
|
||||||
x <<= s;
|
x <<= s;
|
||||||
|
@ -290,7 +318,7 @@ void A_##x##_REG_ROR_REG(ARM* cpu) \
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
cpu->JumpTo(res); \
|
cpu->JumpTo(res & ~1); \
|
||||||
} \
|
} \
|
||||||
else \
|
else \
|
||||||
{ \
|
{ \
|
||||||
|
@ -321,7 +349,7 @@ A_IMPLEMENT_ALU_OP(AND,_S)
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
cpu->JumpTo(res); \
|
cpu->JumpTo(res & ~1); \
|
||||||
} \
|
} \
|
||||||
else \
|
else \
|
||||||
{ \
|
{ \
|
||||||
|
@ -352,7 +380,7 @@ A_IMPLEMENT_ALU_OP(EOR,_S)
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
cpu->JumpTo(res); \
|
cpu->JumpTo(res & ~1); \
|
||||||
} \
|
} \
|
||||||
else \
|
else \
|
||||||
{ \
|
{ \
|
||||||
|
@ -364,8 +392,8 @@ A_IMPLEMENT_ALU_OP(EOR,_S)
|
||||||
u32 res = a - b; \
|
u32 res = a - b; \
|
||||||
cpu->SetNZCV(res & 0x80000000, \
|
cpu->SetNZCV(res & 0x80000000, \
|
||||||
!res, \
|
!res, \
|
||||||
CARRY_SUB(a, b), \
|
CarrySub(a, b), \
|
||||||
OVERFLOW_SUB(a, b, res)); \
|
OverflowSub(a, b)); \
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -385,7 +413,7 @@ A_IMPLEMENT_ALU_OP(SUB,)
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
cpu->JumpTo(res); \
|
cpu->JumpTo(res & ~1); \
|
||||||
} \
|
} \
|
||||||
else \
|
else \
|
||||||
{ \
|
{ \
|
||||||
|
@ -397,8 +425,8 @@ A_IMPLEMENT_ALU_OP(SUB,)
|
||||||
u32 res = b - a; \
|
u32 res = b - a; \
|
||||||
cpu->SetNZCV(res & 0x80000000, \
|
cpu->SetNZCV(res & 0x80000000, \
|
||||||
!res, \
|
!res, \
|
||||||
CARRY_SUB(b, a), \
|
CarrySub(b, a), \
|
||||||
OVERFLOW_SUB(b, a, res)); \
|
OverflowSub(b, a)); \
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -418,7 +446,7 @@ A_IMPLEMENT_ALU_OP(RSB,)
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
cpu->JumpTo(res); \
|
cpu->JumpTo(res & ~1); \
|
||||||
} \
|
} \
|
||||||
else \
|
else \
|
||||||
{ \
|
{ \
|
||||||
|
@ -430,8 +458,8 @@ A_IMPLEMENT_ALU_OP(RSB,)
|
||||||
u32 res = a + b; \
|
u32 res = a + b; \
|
||||||
cpu->SetNZCV(res & 0x80000000, \
|
cpu->SetNZCV(res & 0x80000000, \
|
||||||
!res, \
|
!res, \
|
||||||
CARRY_ADD(a, b), \
|
CarryAdd(a, b), \
|
||||||
OVERFLOW_ADD(a, b, res)); \
|
OverflowAdd(a, b)); \
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -451,7 +479,7 @@ A_IMPLEMENT_ALU_OP(ADD,)
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
cpu->JumpTo(res); \
|
cpu->JumpTo(res & ~1); \
|
||||||
} \
|
} \
|
||||||
else \
|
else \
|
||||||
{ \
|
{ \
|
||||||
|
@ -465,8 +493,8 @@ A_IMPLEMENT_ALU_OP(ADD,)
|
||||||
u32 res = res_tmp + carry; \
|
u32 res = res_tmp + carry; \
|
||||||
cpu->SetNZCV(res & 0x80000000, \
|
cpu->SetNZCV(res & 0x80000000, \
|
||||||
!res, \
|
!res, \
|
||||||
CARRY_ADD(a, b) | CARRY_ADD(res_tmp, carry), \
|
CarryAdd(a, b) | CarryAdd(res_tmp, carry), \
|
||||||
OVERFLOW_ADD(a, b, res_tmp) | OVERFLOW_ADD(res_tmp, carry, res)); \
|
OverflowAdc(a, b, carry)); \
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -486,7 +514,7 @@ A_IMPLEMENT_ALU_OP(ADC,)
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
cpu->JumpTo(res); \
|
cpu->JumpTo(res & ~1); \
|
||||||
} \
|
} \
|
||||||
else \
|
else \
|
||||||
{ \
|
{ \
|
||||||
|
@ -500,8 +528,8 @@ A_IMPLEMENT_ALU_OP(ADC,)
|
||||||
u32 res = res_tmp - carry; \
|
u32 res = res_tmp - carry; \
|
||||||
cpu->SetNZCV(res & 0x80000000, \
|
cpu->SetNZCV(res & 0x80000000, \
|
||||||
!res, \
|
!res, \
|
||||||
CARRY_SUB(a, b) & CARRY_SUB(res_tmp, carry), \
|
CarrySub(a, b) & CarrySub(res_tmp, carry), \
|
||||||
OVERFLOW_SUB(a, b, res_tmp) | OVERFLOW_SUB(res_tmp, carry, res)); \
|
OverflowSbc(a, b, carry)); \
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -521,7 +549,7 @@ A_IMPLEMENT_ALU_OP(SBC,)
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
cpu->JumpTo(res); \
|
cpu->JumpTo(res & ~1); \
|
||||||
} \
|
} \
|
||||||
else \
|
else \
|
||||||
{ \
|
{ \
|
||||||
|
@ -535,8 +563,8 @@ A_IMPLEMENT_ALU_OP(SBC,)
|
||||||
u32 res = res_tmp - carry; \
|
u32 res = res_tmp - carry; \
|
||||||
cpu->SetNZCV(res & 0x80000000, \
|
cpu->SetNZCV(res & 0x80000000, \
|
||||||
!res, \
|
!res, \
|
||||||
CARRY_SUB(b, a) & CARRY_SUB(res_tmp, carry), \
|
CarrySub(b, a) & CarrySub(res_tmp, carry), \
|
||||||
OVERFLOW_SUB(b, a, res_tmp) | OVERFLOW_SUB(res_tmp, carry, res)); \
|
OverflowSbc(b, a, carry)); \
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -575,8 +603,8 @@ A_IMPLEMENT_ALU_TEST(TEQ,_S)
|
||||||
u32 res = a - b; \
|
u32 res = a - b; \
|
||||||
cpu->SetNZCV(res & 0x80000000, \
|
cpu->SetNZCV(res & 0x80000000, \
|
||||||
!res, \
|
!res, \
|
||||||
CARRY_SUB(a, b), \
|
CarrySub(a, b), \
|
||||||
OVERFLOW_SUB(a, b, res)); \
|
OverflowSub(a, b)); \
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C();
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C();
|
||||||
|
|
||||||
A_IMPLEMENT_ALU_TEST(CMP,)
|
A_IMPLEMENT_ALU_TEST(CMP,)
|
||||||
|
@ -587,8 +615,8 @@ A_IMPLEMENT_ALU_TEST(CMP,)
|
||||||
u32 res = a + b; \
|
u32 res = a + b; \
|
||||||
cpu->SetNZCV(res & 0x80000000, \
|
cpu->SetNZCV(res & 0x80000000, \
|
||||||
!res, \
|
!res, \
|
||||||
CARRY_ADD(a, b), \
|
CarryAdd(a, b), \
|
||||||
OVERFLOW_ADD(a, b, res)); \
|
OverflowAdd(a, b)); \
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C();
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C();
|
||||||
|
|
||||||
A_IMPLEMENT_ALU_TEST(CMN,)
|
A_IMPLEMENT_ALU_TEST(CMN,)
|
||||||
|
@ -600,7 +628,7 @@ A_IMPLEMENT_ALU_TEST(CMN,)
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
cpu->JumpTo(res); \
|
cpu->JumpTo(res & ~1); \
|
||||||
} \
|
} \
|
||||||
else \
|
else \
|
||||||
{ \
|
{ \
|
||||||
|
@ -629,7 +657,7 @@ A_IMPLEMENT_ALU_OP(ORR,_S)
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
cpu->JumpTo(b); \
|
cpu->JumpTo(b & ~1); \
|
||||||
} \
|
} \
|
||||||
else \
|
else \
|
||||||
{ \
|
{ \
|
||||||
|
@ -661,8 +689,11 @@ void A_MOV_REG_LSL_IMM_DBG(ARM* cpu)
|
||||||
(cpu->NextInstr[0] & 0xFF000000) == 0xEA000000 && // branch
|
(cpu->NextInstr[0] & 0xFF000000) == 0xEA000000 && // branch
|
||||||
(cpu->NextInstr[1] & 0xFFFF) == 0x6464)
|
(cpu->NextInstr[1] & 0xFFFF) == 0x6464)
|
||||||
{
|
{
|
||||||
u32 addr = cpu->R[15] + 2;
|
// GBATek says the two bytes after the 2nd ID are _reserved_ for flags
|
||||||
NDS::NocashPrint(cpu->Num, addr);
|
// but since they serve no purpose ATTOW, we can skip them
|
||||||
|
u32 addr = cpu->R[15] + 4; // Skip 2nd ID and flags
|
||||||
|
// TODO: Pass flags to NocashPrint
|
||||||
|
cpu->NDS.NocashPrint(cpu->Num, addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,7 +704,7 @@ void A_MOV_REG_LSL_IMM_DBG(ARM* cpu)
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
cpu->JumpTo(res); \
|
cpu->JumpTo(res & ~1); \
|
||||||
} \
|
} \
|
||||||
else \
|
else \
|
||||||
{ \
|
{ \
|
||||||
|
@ -703,7 +734,7 @@ A_IMPLEMENT_ALU_OP(BIC,_S)
|
||||||
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
|
||||||
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
if (((cpu->CurInstr>>12) & 0xF) == 15) \
|
||||||
{ \
|
{ \
|
||||||
cpu->JumpTo(b); \
|
cpu->JumpTo(b & ~1); \
|
||||||
} \
|
} \
|
||||||
else \
|
else \
|
||||||
{ \
|
{ \
|
||||||
|
@ -930,7 +961,7 @@ void A_SMLAxy(ARM* cpu)
|
||||||
u32 res = res_mul + rn;
|
u32 res = res_mul + rn;
|
||||||
|
|
||||||
cpu->R[(cpu->CurInstr >> 16) & 0xF] = res;
|
cpu->R[(cpu->CurInstr >> 16) & 0xF] = res;
|
||||||
if (OVERFLOW_ADD(res_mul, rn, res))
|
if (OverflowAdd(res_mul, rn))
|
||||||
cpu->CPSR |= 0x08000000;
|
cpu->CPSR |= 0x08000000;
|
||||||
|
|
||||||
cpu->AddCycles_C(); // TODO: interlock??
|
cpu->AddCycles_C(); // TODO: interlock??
|
||||||
|
@ -951,7 +982,7 @@ void A_SMLAWy(ARM* cpu)
|
||||||
u32 res = res_mul + rn;
|
u32 res = res_mul + rn;
|
||||||
|
|
||||||
cpu->R[(cpu->CurInstr >> 16) & 0xF] = res;
|
cpu->R[(cpu->CurInstr >> 16) & 0xF] = res;
|
||||||
if (OVERFLOW_ADD(res_mul, rn, res))
|
if (OverflowAdd(res_mul, rn))
|
||||||
cpu->CPSR |= 0x08000000;
|
cpu->CPSR |= 0x08000000;
|
||||||
|
|
||||||
cpu->AddCycles_C(); // TODO: interlock??
|
cpu->AddCycles_C(); // TODO: interlock??
|
||||||
|
@ -1048,7 +1079,7 @@ void A_QADD(ARM* cpu)
|
||||||
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
|
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
|
||||||
|
|
||||||
u32 res = rm + rn;
|
u32 res = rm + rn;
|
||||||
if (OVERFLOW_ADD(rm, rn, res))
|
if (OverflowAdd(rm, rn))
|
||||||
{
|
{
|
||||||
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
|
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
|
||||||
cpu->CPSR |= 0x08000000;
|
cpu->CPSR |= 0x08000000;
|
||||||
|
@ -1066,7 +1097,7 @@ void A_QSUB(ARM* cpu)
|
||||||
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
|
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
|
||||||
|
|
||||||
u32 res = rm - rn;
|
u32 res = rm - rn;
|
||||||
if (OVERFLOW_SUB(rm, rn, res))
|
if (OverflowSub(rm, rn))
|
||||||
{
|
{
|
||||||
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
|
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
|
||||||
cpu->CPSR |= 0x08000000;
|
cpu->CPSR |= 0x08000000;
|
||||||
|
@ -1083,7 +1114,7 @@ void A_QDADD(ARM* cpu)
|
||||||
u32 rm = cpu->R[cpu->CurInstr & 0xF];
|
u32 rm = cpu->R[cpu->CurInstr & 0xF];
|
||||||
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
|
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
|
||||||
|
|
||||||
if (OVERFLOW_ADD(rn, rn, rn<<1))
|
if (OverflowAdd(rn, rn))
|
||||||
{
|
{
|
||||||
rn = (rn & 0x80000000) ? 0x80000000 : 0x7FFFFFFF;
|
rn = (rn & 0x80000000) ? 0x80000000 : 0x7FFFFFFF;
|
||||||
cpu->CPSR |= 0x08000000; // CHECKME
|
cpu->CPSR |= 0x08000000; // CHECKME
|
||||||
|
@ -1092,7 +1123,7 @@ void A_QDADD(ARM* cpu)
|
||||||
rn <<= 1;
|
rn <<= 1;
|
||||||
|
|
||||||
u32 res = rm + rn;
|
u32 res = rm + rn;
|
||||||
if (OVERFLOW_ADD(rm, rn, res))
|
if (OverflowAdd(rm, rn))
|
||||||
{
|
{
|
||||||
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
|
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
|
||||||
cpu->CPSR |= 0x08000000;
|
cpu->CPSR |= 0x08000000;
|
||||||
|
@ -1109,7 +1140,7 @@ void A_QDSUB(ARM* cpu)
|
||||||
u32 rm = cpu->R[cpu->CurInstr & 0xF];
|
u32 rm = cpu->R[cpu->CurInstr & 0xF];
|
||||||
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
|
u32 rn = cpu->R[(cpu->CurInstr >> 16) & 0xF];
|
||||||
|
|
||||||
if (OVERFLOW_ADD(rn, rn, rn<<1))
|
if (OverflowAdd(rn, rn))
|
||||||
{
|
{
|
||||||
rn = (rn & 0x80000000) ? 0x80000000 : 0x7FFFFFFF;
|
rn = (rn & 0x80000000) ? 0x80000000 : 0x7FFFFFFF;
|
||||||
cpu->CPSR |= 0x08000000; // CHECKME
|
cpu->CPSR |= 0x08000000; // CHECKME
|
||||||
|
@ -1118,7 +1149,7 @@ void A_QDSUB(ARM* cpu)
|
||||||
rn <<= 1;
|
rn <<= 1;
|
||||||
|
|
||||||
u32 res = rm - rn;
|
u32 res = rm - rn;
|
||||||
if (OVERFLOW_SUB(rm, rn, res))
|
if (OverflowSub(rm, rn))
|
||||||
{
|
{
|
||||||
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
|
res = (res & 0x80000000) ? 0x7FFFFFFF : 0x80000000;
|
||||||
cpu->CPSR |= 0x08000000;
|
cpu->CPSR |= 0x08000000;
|
||||||
|
@ -1175,8 +1206,8 @@ void T_ADD_REG_(ARM* cpu)
|
||||||
cpu->R[cpu->CurInstr & 0x7] = res;
|
cpu->R[cpu->CurInstr & 0x7] = res;
|
||||||
cpu->SetNZCV(res & 0x80000000,
|
cpu->SetNZCV(res & 0x80000000,
|
||||||
!res,
|
!res,
|
||||||
CARRY_ADD(a, b),
|
CarryAdd(a, b),
|
||||||
OVERFLOW_ADD(a, b, res));
|
OverflowAdd(a, b));
|
||||||
cpu->AddCycles_C();
|
cpu->AddCycles_C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1188,8 +1219,8 @@ void T_SUB_REG_(ARM* cpu)
|
||||||
cpu->R[cpu->CurInstr & 0x7] = res;
|
cpu->R[cpu->CurInstr & 0x7] = res;
|
||||||
cpu->SetNZCV(res & 0x80000000,
|
cpu->SetNZCV(res & 0x80000000,
|
||||||
!res,
|
!res,
|
||||||
CARRY_SUB(a, b),
|
CarrySub(a, b),
|
||||||
OVERFLOW_SUB(a, b, res));
|
OverflowSub(a, b));
|
||||||
cpu->AddCycles_C();
|
cpu->AddCycles_C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1201,8 +1232,8 @@ void T_ADD_IMM_(ARM* cpu)
|
||||||
cpu->R[cpu->CurInstr & 0x7] = res;
|
cpu->R[cpu->CurInstr & 0x7] = res;
|
||||||
cpu->SetNZCV(res & 0x80000000,
|
cpu->SetNZCV(res & 0x80000000,
|
||||||
!res,
|
!res,
|
||||||
CARRY_ADD(a, b),
|
CarryAdd(a, b),
|
||||||
OVERFLOW_ADD(a, b, res));
|
OverflowAdd(a, b));
|
||||||
cpu->AddCycles_C();
|
cpu->AddCycles_C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1214,8 +1245,8 @@ void T_SUB_IMM_(ARM* cpu)
|
||||||
cpu->R[cpu->CurInstr & 0x7] = res;
|
cpu->R[cpu->CurInstr & 0x7] = res;
|
||||||
cpu->SetNZCV(res & 0x80000000,
|
cpu->SetNZCV(res & 0x80000000,
|
||||||
!res,
|
!res,
|
||||||
CARRY_SUB(a, b),
|
CarrySub(a, b),
|
||||||
OVERFLOW_SUB(a, b, res));
|
OverflowSub(a, b));
|
||||||
cpu->AddCycles_C();
|
cpu->AddCycles_C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1235,8 +1266,8 @@ void T_CMP_IMM(ARM* cpu)
|
||||||
u32 res = a - b;
|
u32 res = a - b;
|
||||||
cpu->SetNZCV(res & 0x80000000,
|
cpu->SetNZCV(res & 0x80000000,
|
||||||
!res,
|
!res,
|
||||||
CARRY_SUB(a, b),
|
CarrySub(a, b),
|
||||||
OVERFLOW_SUB(a, b, res));
|
OverflowSub(a, b));
|
||||||
cpu->AddCycles_C();
|
cpu->AddCycles_C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1248,8 +1279,8 @@ void T_ADD_IMM(ARM* cpu)
|
||||||
cpu->R[(cpu->CurInstr >> 8) & 0x7] = res;
|
cpu->R[(cpu->CurInstr >> 8) & 0x7] = res;
|
||||||
cpu->SetNZCV(res & 0x80000000,
|
cpu->SetNZCV(res & 0x80000000,
|
||||||
!res,
|
!res,
|
||||||
CARRY_ADD(a, b),
|
CarryAdd(a, b),
|
||||||
OVERFLOW_ADD(a, b, res));
|
OverflowAdd(a, b));
|
||||||
cpu->AddCycles_C();
|
cpu->AddCycles_C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1261,8 +1292,8 @@ void T_SUB_IMM(ARM* cpu)
|
||||||
cpu->R[(cpu->CurInstr >> 8) & 0x7] = res;
|
cpu->R[(cpu->CurInstr >> 8) & 0x7] = res;
|
||||||
cpu->SetNZCV(res & 0x80000000,
|
cpu->SetNZCV(res & 0x80000000,
|
||||||
!res,
|
!res,
|
||||||
CARRY_SUB(a, b),
|
CarrySub(a, b),
|
||||||
OVERFLOW_SUB(a, b, res));
|
OverflowSub(a, b));
|
||||||
cpu->AddCycles_C();
|
cpu->AddCycles_C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1332,8 +1363,8 @@ void T_ADC_REG(ARM* cpu)
|
||||||
cpu->R[cpu->CurInstr & 0x7] = res;
|
cpu->R[cpu->CurInstr & 0x7] = res;
|
||||||
cpu->SetNZCV(res & 0x80000000,
|
cpu->SetNZCV(res & 0x80000000,
|
||||||
!res,
|
!res,
|
||||||
CARRY_ADD(a, b) | CARRY_ADD(res_tmp, carry),
|
CarryAdd(a, b) | CarryAdd(res_tmp, carry),
|
||||||
OVERFLOW_ADD(a, b, res_tmp) | OVERFLOW_ADD(res_tmp, carry, res));
|
OverflowAdc(a, b, carry));
|
||||||
cpu->AddCycles_C();
|
cpu->AddCycles_C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1347,8 +1378,8 @@ void T_SBC_REG(ARM* cpu)
|
||||||
cpu->R[cpu->CurInstr & 0x7] = res;
|
cpu->R[cpu->CurInstr & 0x7] = res;
|
||||||
cpu->SetNZCV(res & 0x80000000,
|
cpu->SetNZCV(res & 0x80000000,
|
||||||
!res,
|
!res,
|
||||||
CARRY_SUB(a, b) & CARRY_SUB(res_tmp, carry),
|
CarrySub(a, b) & CarrySub(res_tmp, carry),
|
||||||
OVERFLOW_SUB(a, b, res_tmp) | OVERFLOW_SUB(res_tmp, carry, res));
|
OverflowSbc(a, b, carry));
|
||||||
cpu->AddCycles_C();
|
cpu->AddCycles_C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1380,8 +1411,8 @@ void T_NEG_REG(ARM* cpu)
|
||||||
cpu->R[cpu->CurInstr & 0x7] = res;
|
cpu->R[cpu->CurInstr & 0x7] = res;
|
||||||
cpu->SetNZCV(res & 0x80000000,
|
cpu->SetNZCV(res & 0x80000000,
|
||||||
!res,
|
!res,
|
||||||
CARRY_SUB(0, b),
|
CarrySub(0, b),
|
||||||
OVERFLOW_SUB(0, b, res));
|
OverflowSub(0, b));
|
||||||
cpu->AddCycles_C();
|
cpu->AddCycles_C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1392,8 +1423,8 @@ void T_CMP_REG(ARM* cpu)
|
||||||
u32 res = a - b;
|
u32 res = a - b;
|
||||||
cpu->SetNZCV(res & 0x80000000,
|
cpu->SetNZCV(res & 0x80000000,
|
||||||
!res,
|
!res,
|
||||||
CARRY_SUB(a, b),
|
CarrySub(a, b),
|
||||||
OVERFLOW_SUB(a, b, res));
|
OverflowSub(a, b));
|
||||||
cpu->AddCycles_C();
|
cpu->AddCycles_C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1404,8 +1435,8 @@ void T_CMN_REG(ARM* cpu)
|
||||||
u32 res = a + b;
|
u32 res = a + b;
|
||||||
cpu->SetNZCV(res & 0x80000000,
|
cpu->SetNZCV(res & 0x80000000,
|
||||||
!res,
|
!res,
|
||||||
CARRY_ADD(a, b),
|
CarryAdd(a, b),
|
||||||
OVERFLOW_ADD(a, b, res));
|
OverflowAdd(a, b));
|
||||||
cpu->AddCycles_C();
|
cpu->AddCycles_C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1501,8 +1532,8 @@ void T_CMP_HIREG(ARM* cpu)
|
||||||
|
|
||||||
cpu->SetNZCV(res & 0x80000000,
|
cpu->SetNZCV(res & 0x80000000,
|
||||||
!res,
|
!res,
|
||||||
CARRY_SUB(a, b),
|
CarrySub(a, b),
|
||||||
OVERFLOW_SUB(a, b, res));
|
OverflowSub(a, b));
|
||||||
cpu->AddCycles_C();
|
cpu->AddCycles_C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1527,8 +1558,11 @@ void T_MOV_HIREG(ARM* cpu)
|
||||||
(cpu->NextInstr[0] & 0xF800) == 0xE000 && // branch
|
(cpu->NextInstr[0] & 0xF800) == 0xE000 && // branch
|
||||||
(cpu->NextInstr[1] & 0xFFFF) == 0x6464)
|
(cpu->NextInstr[1] & 0xFFFF) == 0x6464)
|
||||||
{
|
{
|
||||||
u32 addr = cpu->R[15] + 2;
|
// GBATek says the two bytes after the 2nd ID are _reserved_ for flags
|
||||||
NDS::NocashPrint(cpu->Num, addr);
|
// but since they serve no purpose ATTOW, we can skip them
|
||||||
|
u32 addr = cpu->R[15] + 4; // Skip 2nd ID and flags
|
||||||
|
// TODO: Pass flags to NocashPrint
|
||||||
|
cpu->NDS.NocashPrint(cpu->Num, addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -19,6 +19,8 @@
|
||||||
#ifndef ARMINTERPRETER_ALU_H
|
#ifndef ARMINTERPRETER_ALU_H
|
||||||
#define ARMINTERPRETER_ALU_H
|
#define ARMINTERPRETER_ALU_H
|
||||||
|
|
||||||
|
namespace melonDS
|
||||||
|
{
|
||||||
namespace ARMInterpreter
|
namespace ARMInterpreter
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -134,4 +136,5 @@ void T_ADD_SP(ARM* cpu);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -16,12 +16,13 @@
|
||||||
with melonDS. If not, see http://www.gnu.org/licenses/.
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "ARM.h"
|
#include "ARM.h"
|
||||||
|
#include "Platform.h"
|
||||||
|
|
||||||
|
namespace melonDS::ARMInterpreter
|
||||||
namespace ARMInterpreter
|
|
||||||
{
|
{
|
||||||
|
using Platform::Log;
|
||||||
|
using Platform::LogLevel;
|
||||||
|
|
||||||
|
|
||||||
void A_B(ARM* cpu)
|
void A_B(ARM* cpu)
|
||||||
|
@ -79,7 +80,7 @@ void T_BLX_REG(ARM* cpu)
|
||||||
{
|
{
|
||||||
if (cpu->Num==1)
|
if (cpu->Num==1)
|
||||||
{
|
{
|
||||||
printf("!! THUMB BLX_REG ON ARM7\n");
|
Log(LogLevel::Warn, "!! THUMB BLX_REG ON ARM7\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -19,6 +19,8 @@
|
||||||
#ifndef ARMINTERPRETER_BRANCH_H
|
#ifndef ARMINTERPRETER_BRANCH_H
|
||||||
#define ARMINTERPRETER_BRANCH_H
|
#define ARMINTERPRETER_BRANCH_H
|
||||||
|
|
||||||
|
namespace melonDS
|
||||||
|
{
|
||||||
namespace ARMInterpreter
|
namespace ARMInterpreter
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -36,4 +38,5 @@ void T_BL_LONG_2(ARM* cpu);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
#include "ARM.h"
|
#include "ARM.h"
|
||||||
|
|
||||||
|
|
||||||
namespace ARMInterpreter
|
namespace melonDS::ARMInterpreter
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,14 +62,20 @@ namespace ARMInterpreter
|
||||||
|
|
||||||
#define A_STR \
|
#define A_STR \
|
||||||
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
|
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
|
||||||
cpu->DataWrite32(offset, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
|
u32 storeval = cpu->R[(cpu->CurInstr>>12) & 0xF]; \
|
||||||
|
if (((cpu->CurInstr>>12) & 0xF) == 0xF) \
|
||||||
|
storeval += 4; \
|
||||||
|
cpu->DataWrite32(offset, storeval); \
|
||||||
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
|
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
|
||||||
cpu->AddCycles_CD();
|
cpu->AddCycles_CD();
|
||||||
|
|
||||||
// TODO: user mode (bit21)
|
// TODO: user mode (bit21)
|
||||||
#define A_STR_POST \
|
#define A_STR_POST \
|
||||||
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
|
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
|
||||||
cpu->DataWrite32(addr, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
|
u32 storeval = cpu->R[(cpu->CurInstr>>12) & 0xF]; \
|
||||||
|
if (((cpu->CurInstr>>12) & 0xF) == 0xF) \
|
||||||
|
storeval += 4; \
|
||||||
|
cpu->DataWrite32(addr, storeval); \
|
||||||
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
|
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
|
||||||
cpu->AddCycles_CD();
|
cpu->AddCycles_CD();
|
||||||
|
|
||||||
|
@ -410,7 +416,7 @@ void A_LDM(ARM* cpu)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
|
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
|
||||||
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10);
|
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10, true);
|
||||||
|
|
||||||
for (int i = 0; i < 15; i++)
|
for (int i = 0; i < 15; i++)
|
||||||
{
|
{
|
||||||
|
@ -424,9 +430,9 @@ void A_LDM(ARM* cpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 pc = 0;
|
||||||
if (cpu->CurInstr & (1<<15))
|
if (cpu->CurInstr & (1<<15))
|
||||||
{
|
{
|
||||||
u32 pc;
|
|
||||||
if (preinc) base += 4;
|
if (preinc) base += 4;
|
||||||
if (first) cpu->DataRead32 (base, &pc);
|
if (first) cpu->DataRead32 (base, &pc);
|
||||||
else cpu->DataRead32S(base, &pc);
|
else cpu->DataRead32S(base, &pc);
|
||||||
|
@ -434,13 +440,8 @@ void A_LDM(ARM* cpu)
|
||||||
|
|
||||||
if (cpu->Num == 1)
|
if (cpu->Num == 1)
|
||||||
pc &= ~0x1;
|
pc &= ~0x1;
|
||||||
|
|
||||||
cpu->JumpTo(pc, cpu->CurInstr & (1<<22));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
|
|
||||||
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR);
|
|
||||||
|
|
||||||
if (cpu->CurInstr & (1<<21))
|
if (cpu->CurInstr & (1<<21))
|
||||||
{
|
{
|
||||||
// post writeback
|
// post writeback
|
||||||
|
@ -460,6 +461,12 @@ void A_LDM(ARM* cpu)
|
||||||
cpu->R[baseid] = wbbase;
|
cpu->R[baseid] = wbbase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
|
||||||
|
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
|
||||||
|
|
||||||
|
if (cpu->CurInstr & (1<<15))
|
||||||
|
cpu->JumpTo(pc, cpu->CurInstr & (1<<22));
|
||||||
|
|
||||||
cpu->AddCycles_CDI();
|
cpu->AddCycles_CDI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,7 +501,7 @@ void A_STM(ARM* cpu)
|
||||||
else if (mode != 0x10 && mode != 0x1F)
|
else if (mode != 0x10 && mode != 0x1F)
|
||||||
isbanked = (baseid >= 13 && baseid < 15);
|
isbanked = (baseid >= 13 && baseid < 15);
|
||||||
|
|
||||||
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10);
|
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (u32 i = 0; i < 16; i++)
|
for (u32 i = 0; i < 16; i++)
|
||||||
|
@ -520,7 +527,7 @@ void A_STM(ARM* cpu)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cpu->CurInstr & (1<<22))
|
if (cpu->CurInstr & (1<<22))
|
||||||
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR);
|
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
|
||||||
|
|
||||||
if ((cpu->CurInstr & (1<<23)) && (cpu->CurInstr & (1<<21)))
|
if ((cpu->CurInstr & (1<<23)) && (cpu->CurInstr & (1<<21)))
|
||||||
cpu->R[baseid] = base;
|
cpu->R[baseid] = base;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
#ifndef ARMINTERPRETER_LOADSTORE_H
|
#ifndef ARMINTERPRETER_LOADSTORE_H
|
||||||
#define ARMINTERPRETER_LOADSTORE_H
|
#define ARMINTERPRETER_LOADSTORE_H
|
||||||
|
|
||||||
namespace ARMInterpreter
|
namespace melonDS::ARMInterpreter
|
||||||
{
|
{
|
||||||
|
|
||||||
#define A_PROTO_WB_LDRSTR(x) \
|
#define A_PROTO_WB_LDRSTR(x) \
|
||||||
|
|
554
src/ARMJIT.cpp
|
@ -1,5 +1,23 @@
|
||||||
#include "ARMJIT.h"
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ARMJIT.h"
|
||||||
|
#include "ARMJIT_Memory.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
@ -7,11 +25,12 @@
|
||||||
#define XXH_STATIC_LINKING_ONLY
|
#define XXH_STATIC_LINKING_ONLY
|
||||||
#include "xxhash/xxhash.h"
|
#include "xxhash/xxhash.h"
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Platform.h"
|
||||||
|
|
||||||
#include "ARMJIT_Internal.h"
|
#include "ARMJIT_Internal.h"
|
||||||
#include "ARMJIT_Memory.h"
|
#include "ARMJIT_Memory.h"
|
||||||
#include "ARMJIT_Compiler.h"
|
#include "ARMJIT_Compiler.h"
|
||||||
|
#include "ARMJIT_Global.h"
|
||||||
|
|
||||||
#include "ARMInterpreter_ALU.h"
|
#include "ARMInterpreter_ALU.h"
|
||||||
#include "ARMInterpreter_LoadStore.h"
|
#include "ARMInterpreter_LoadStore.h"
|
||||||
|
@ -24,130 +43,52 @@
|
||||||
#include "SPU.h"
|
#include "SPU.h"
|
||||||
#include "Wifi.h"
|
#include "Wifi.h"
|
||||||
#include "NDSCart.h"
|
#include "NDSCart.h"
|
||||||
|
#include "Platform.h"
|
||||||
#include "ARMJIT_x64/ARMJIT_Offsets.h"
|
#include "ARMJIT_x64/ARMJIT_Offsets.h"
|
||||||
static_assert(offsetof(ARM, CPSR) == ARM_CPSR_offset);
|
|
||||||
static_assert(offsetof(ARM, Cycles) == ARM_Cycles_offset);
|
|
||||||
static_assert(offsetof(ARM, StopExecution) == ARM_StopExecution_offset);
|
|
||||||
|
|
||||||
namespace ARMJIT
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
using Platform::Log;
|
||||||
|
using Platform::LogLevel;
|
||||||
|
|
||||||
|
static_assert(offsetof(ARM, CPSR) == ARM_CPSR_offset, "");
|
||||||
|
static_assert(offsetof(ARM, Cycles) == ARM_Cycles_offset, "");
|
||||||
|
static_assert(offsetof(ARM, StopExecution) == ARM_StopExecution_offset, "");
|
||||||
|
|
||||||
|
|
||||||
#define JIT_DEBUGPRINT(msg, ...)
|
#define JIT_DEBUGPRINT(msg, ...)
|
||||||
//#define JIT_DEBUGPRINT(msg, ...) printf(msg, ## __VA_ARGS__)
|
//#define JIT_DEBUGPRINT(msg, ...) Platform::Log(Platform::LogLevel::Debug, msg, ## __VA_ARGS__)
|
||||||
|
|
||||||
Compiler* JITCompiler;
|
|
||||||
|
|
||||||
|
|
||||||
std::unordered_map<u32, JitBlock*> JitBlocks9;
|
|
||||||
std::unordered_map<u32, JitBlock*> JitBlocks7;
|
|
||||||
|
|
||||||
std::unordered_map<u32, JitBlock*> RestoreCandidates;
|
|
||||||
|
|
||||||
TinyVector<u32> InvalidLiterals;
|
|
||||||
|
|
||||||
AddressRange CodeIndexITCM[ITCMPhysicalSize / 512];
|
|
||||||
AddressRange CodeIndexMainRAM[NDS::MainRAMMaxSize / 512];
|
|
||||||
AddressRange CodeIndexSWRAM[NDS::SharedWRAMSize / 512];
|
|
||||||
AddressRange CodeIndexVRAM[0x100000 / 512];
|
|
||||||
AddressRange CodeIndexARM9BIOS[sizeof(NDS::ARM9BIOS) / 512];
|
|
||||||
AddressRange CodeIndexARM7BIOS[sizeof(NDS::ARM7BIOS) / 512];
|
|
||||||
AddressRange CodeIndexARM7WRAM[NDS::ARM7WRAMSize / 512];
|
|
||||||
AddressRange CodeIndexARM7WVRAM[0x40000 / 512];
|
|
||||||
AddressRange CodeIndexBIOS9DSi[0x10000 / 512];
|
|
||||||
AddressRange CodeIndexBIOS7DSi[0x10000 / 512];
|
|
||||||
AddressRange CodeIndexNWRAM_A[DSi::NWRAMSize / 512];
|
|
||||||
AddressRange CodeIndexNWRAM_B[DSi::NWRAMSize / 512];
|
|
||||||
AddressRange CodeIndexNWRAM_C[DSi::NWRAMSize / 512];
|
|
||||||
|
|
||||||
u64 FastBlockLookupITCM[ITCMPhysicalSize / 2];
|
|
||||||
u64 FastBlockLookupMainRAM[NDS::MainRAMMaxSize / 2];
|
|
||||||
u64 FastBlockLookupSWRAM[NDS::SharedWRAMSize / 2];
|
|
||||||
u64 FastBlockLookupVRAM[0x100000 / 2];
|
|
||||||
u64 FastBlockLookupARM9BIOS[sizeof(NDS::ARM9BIOS) / 2];
|
|
||||||
u64 FastBlockLookupARM7BIOS[sizeof(NDS::ARM7BIOS) / 2];
|
|
||||||
u64 FastBlockLookupARM7WRAM[NDS::ARM7WRAMSize / 2];
|
|
||||||
u64 FastBlockLookupARM7WVRAM[0x40000 / 2];
|
|
||||||
u64 FastBlockLookupBIOS9DSi[0x10000 / 2];
|
|
||||||
u64 FastBlockLookupBIOS7DSi[0x10000 / 2];
|
|
||||||
u64 FastBlockLookupNWRAM_A[DSi::NWRAMSize / 2];
|
|
||||||
u64 FastBlockLookupNWRAM_B[DSi::NWRAMSize / 2];
|
|
||||||
u64 FastBlockLookupNWRAM_C[DSi::NWRAMSize / 2];
|
|
||||||
|
|
||||||
const u32 CodeRegionSizes[ARMJIT_Memory::memregions_Count] =
|
const u32 CodeRegionSizes[ARMJIT_Memory::memregions_Count] =
|
||||||
{
|
{
|
||||||
0,
|
0,
|
||||||
ITCMPhysicalSize,
|
ITCMPhysicalSize,
|
||||||
0,
|
0,
|
||||||
sizeof(NDS::ARM9BIOS),
|
ARM9BIOSSize,
|
||||||
NDS::MainRAMMaxSize,
|
MainRAMMaxSize,
|
||||||
NDS::SharedWRAMSize,
|
SharedWRAMSize,
|
||||||
0,
|
0,
|
||||||
0x100000,
|
0x100000,
|
||||||
sizeof(NDS::ARM7BIOS),
|
ARM7BIOSSize,
|
||||||
NDS::ARM7WRAMSize,
|
ARM7WRAMSize,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0x40000,
|
0x40000,
|
||||||
0x10000,
|
0x10000,
|
||||||
0x10000,
|
0x10000,
|
||||||
DSi::NWRAMSize,
|
NWRAMSize,
|
||||||
DSi::NWRAMSize,
|
NWRAMSize,
|
||||||
DSi::NWRAMSize,
|
NWRAMSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count] =
|
u32 ARMJIT::LocaliseCodeAddress(u32 num, u32 addr) const noexcept
|
||||||
{
|
|
||||||
NULL,
|
|
||||||
CodeIndexITCM,
|
|
||||||
NULL,
|
|
||||||
CodeIndexARM9BIOS,
|
|
||||||
CodeIndexMainRAM,
|
|
||||||
CodeIndexSWRAM,
|
|
||||||
NULL,
|
|
||||||
CodeIndexVRAM,
|
|
||||||
CodeIndexARM7BIOS,
|
|
||||||
CodeIndexARM7WRAM,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
CodeIndexARM7WVRAM,
|
|
||||||
CodeIndexBIOS9DSi,
|
|
||||||
CodeIndexBIOS7DSi,
|
|
||||||
CodeIndexNWRAM_A,
|
|
||||||
CodeIndexNWRAM_B,
|
|
||||||
CodeIndexNWRAM_C
|
|
||||||
};
|
|
||||||
|
|
||||||
u64* const FastBlockLookupRegions[ARMJIT_Memory::memregions_Count] =
|
|
||||||
{
|
|
||||||
NULL,
|
|
||||||
FastBlockLookupITCM,
|
|
||||||
NULL,
|
|
||||||
FastBlockLookupARM9BIOS,
|
|
||||||
FastBlockLookupMainRAM,
|
|
||||||
FastBlockLookupSWRAM,
|
|
||||||
NULL,
|
|
||||||
FastBlockLookupVRAM,
|
|
||||||
FastBlockLookupARM7BIOS,
|
|
||||||
FastBlockLookupARM7WRAM,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
FastBlockLookupARM7WVRAM,
|
|
||||||
FastBlockLookupBIOS9DSi,
|
|
||||||
FastBlockLookupBIOS7DSi,
|
|
||||||
FastBlockLookupNWRAM_A,
|
|
||||||
FastBlockLookupNWRAM_B,
|
|
||||||
FastBlockLookupNWRAM_C
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 LocaliseCodeAddress(u32 num, u32 addr)
|
|
||||||
{
|
{
|
||||||
int region = num == 0
|
int region = num == 0
|
||||||
? ARMJIT_Memory::ClassifyAddress9(addr)
|
? Memory.ClassifyAddress9(addr)
|
||||||
: ARMJIT_Memory::ClassifyAddress7(addr);
|
: Memory.ClassifyAddress7(addr);
|
||||||
|
|
||||||
if (CodeMemRegions[region])
|
if (CodeMemRegions[region])
|
||||||
return ARMJIT_Memory::LocaliseAddress(region, num, addr);
|
return Memory.LocaliseAddress(region, num, addr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,14 +101,34 @@ T SlowRead9(u32 addr, ARMv5* cpu)
|
||||||
T val;
|
T val;
|
||||||
if (addr < cpu->ITCMSize)
|
if (addr < cpu->ITCMSize)
|
||||||
val = *(T*)&cpu->ITCM[addr & 0x7FFF];
|
val = *(T*)&cpu->ITCM[addr & 0x7FFF];
|
||||||
else if (addr >= cpu->DTCMBase && addr < (cpu->DTCMBase + cpu->DTCMSize))
|
else if ((addr & cpu->DTCMMask) == cpu->DTCMBase)
|
||||||
val = *(T*)&cpu->DTCM[(addr - cpu->DTCMBase) & 0x3FFF];
|
val = *(T*)&cpu->DTCM[addr & 0x3FFF];
|
||||||
else if (std::is_same<T, u32>::value)
|
else if (std::is_same<T, u32>::value)
|
||||||
val = (ConsoleType == 0 ? NDS::ARM9Read32 : DSi::ARM9Read32)(addr);
|
val = NDS::Current->ARM9Read32(addr);
|
||||||
else if (std::is_same<T, u16>::value)
|
else if (std::is_same<T, u16>::value)
|
||||||
val = (ConsoleType == 0 ? NDS::ARM9Read16 : DSi::ARM9Read16)(addr);
|
val = NDS::Current->ARM9Read16(addr);
|
||||||
else
|
else
|
||||||
val = (ConsoleType == 0 ? NDS::ARM9Read8 : DSi::ARM9Read8)(addr);
|
val = NDS::Current->ARM9Read8(addr);
|
||||||
|
|
||||||
|
if (std::is_same<T, u32>::value)
|
||||||
|
return ROR(val, offset << 3);
|
||||||
|
else
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, int ConsoleType>
|
||||||
|
T SlowRead7(u32 addr)
|
||||||
|
{
|
||||||
|
u32 offset = addr & 0x3;
|
||||||
|
addr &= ~(sizeof(T) - 1);
|
||||||
|
|
||||||
|
T val;
|
||||||
|
if (std::is_same<T, u32>::value)
|
||||||
|
val = NDS::Current->ARM7Read32(addr);
|
||||||
|
else if (std::is_same<T, u16>::value)
|
||||||
|
val = NDS::Current->ARM7Read16(addr);
|
||||||
|
else
|
||||||
|
val = NDS::Current->ARM7Read8(addr);
|
||||||
|
|
||||||
if (std::is_same<T, u32>::value)
|
if (std::is_same<T, u32>::value)
|
||||||
return ROR(val, offset << 3);
|
return ROR(val, offset << 3);
|
||||||
|
@ -182,65 +143,45 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 val)
|
||||||
|
|
||||||
if (addr < cpu->ITCMSize)
|
if (addr < cpu->ITCMSize)
|
||||||
{
|
{
|
||||||
CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
|
cpu->NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr);
|
||||||
*(T*)&cpu->ITCM[addr & 0x7FFF] = val;
|
*(T*)&cpu->ITCM[addr & 0x7FFF] = val;
|
||||||
}
|
}
|
||||||
else if (addr >= cpu->DTCMBase && addr < (cpu->DTCMBase + cpu->DTCMSize))
|
else if ((addr & cpu->DTCMMask) == cpu->DTCMBase)
|
||||||
{
|
{
|
||||||
*(T*)&cpu->DTCM[(addr - cpu->DTCMBase) & 0x3FFF] = val;
|
*(T*)&cpu->DTCM[addr & 0x3FFF] = val;
|
||||||
}
|
}
|
||||||
else if (std::is_same<T, u32>::value)
|
else if (std::is_same<T, u32>::value)
|
||||||
{
|
{
|
||||||
(ConsoleType == 0 ? NDS::ARM9Write32 : DSi::ARM9Write32)(addr, val);
|
NDS::Current->ARM9Write32(addr, val);
|
||||||
}
|
}
|
||||||
else if (std::is_same<T, u16>::value)
|
else if (std::is_same<T, u16>::value)
|
||||||
{
|
{
|
||||||
(ConsoleType == 0 ? NDS::ARM9Write16 : DSi::ARM9Write16)(addr, val);
|
NDS::Current->ARM9Write16(addr, val);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(ConsoleType == 0 ? NDS::ARM9Write8 : DSi::ARM9Write8)(addr, val);
|
NDS::Current->ARM9Write8(addr, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, int ConsoleType>
|
|
||||||
T SlowRead7(u32 addr)
|
|
||||||
{
|
|
||||||
u32 offset = addr & 0x3;
|
|
||||||
addr &= ~(sizeof(T) - 1);
|
|
||||||
|
|
||||||
T val;
|
|
||||||
if (std::is_same<T, u32>::value)
|
|
||||||
val = (ConsoleType == 0 ? NDS::ARM7Read32 : DSi::ARM7Read32)(addr);
|
|
||||||
else if (std::is_same<T, u16>::value)
|
|
||||||
val = (ConsoleType == 0 ? NDS::ARM7Read16 : DSi::ARM7Read16)(addr);
|
|
||||||
else
|
|
||||||
val = (ConsoleType == 0 ? NDS::ARM7Read8 : DSi::ARM7Read8)(addr);
|
|
||||||
|
|
||||||
if (std::is_same<T, u32>::value)
|
|
||||||
return ROR(val, offset << 3);
|
|
||||||
else
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, int ConsoleType>
|
template <typename T, int ConsoleType>
|
||||||
void SlowWrite7(u32 addr, u32 val)
|
void SlowWrite7(u32 addr, u32 val)
|
||||||
{
|
{
|
||||||
addr &= ~(sizeof(T) - 1);
|
addr &= ~(sizeof(T) - 1);
|
||||||
|
|
||||||
if (std::is_same<T, u32>::value)
|
if (std::is_same<T, u32>::value)
|
||||||
(ConsoleType == 0 ? NDS::ARM7Write32 : DSi::ARM7Write32)(addr, val);
|
NDS::Current->ARM7Write32(addr, val);
|
||||||
else if (std::is_same<T, u16>::value)
|
else if (std::is_same<T, u16>::value)
|
||||||
(ConsoleType == 0 ? NDS::ARM7Write16 : DSi::ARM7Write16)(addr, val);
|
NDS::Current->ARM7Write16(addr, val);
|
||||||
else
|
else
|
||||||
(ConsoleType == 0 ? NDS::ARM7Write8 : DSi::ARM7Write8)(addr, val);
|
NDS::Current->ARM7Write8(addr, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool Write, int ConsoleType>
|
template <bool Write, int ConsoleType>
|
||||||
void SlowBlockTransfer9(u32 addr, u64* data, u32 num, ARMv5* cpu)
|
void SlowBlockTransfer9(u32 addr, u64* data, u32 num, ARMv5* cpu)
|
||||||
{
|
{
|
||||||
addr &= ~0x3;
|
addr &= ~0x3;
|
||||||
for (int i = 0; i < num; i++)
|
for (u32 i = 0; i < num; i++)
|
||||||
{
|
{
|
||||||
if (Write)
|
if (Write)
|
||||||
SlowWrite9<u32, ConsoleType>(addr, cpu, data[i]);
|
SlowWrite9<u32, ConsoleType>(addr, cpu, data[i]);
|
||||||
|
@ -254,7 +195,7 @@ template <bool Write, int ConsoleType>
|
||||||
void SlowBlockTransfer7(u32 addr, u64* data, u32 num)
|
void SlowBlockTransfer7(u32 addr, u64* data, u32 num)
|
||||||
{
|
{
|
||||||
addr &= ~0x3;
|
addr &= ~0x3;
|
||||||
for (int i = 0; i < num; i++)
|
for (u32 i = 0; i < num; i++)
|
||||||
{
|
{
|
||||||
if (Write)
|
if (Write)
|
||||||
SlowWrite7<u32, ConsoleType>(addr, data[i]);
|
SlowWrite7<u32, ConsoleType>(addr, data[i]);
|
||||||
|
@ -289,26 +230,18 @@ void SlowBlockTransfer7(u32 addr, u64* data, u32 num)
|
||||||
INSTANTIATE_SLOWMEM(0)
|
INSTANTIATE_SLOWMEM(0)
|
||||||
INSTANTIATE_SLOWMEM(1)
|
INSTANTIATE_SLOWMEM(1)
|
||||||
|
|
||||||
void Init()
|
ARMJIT::~ARMJIT() noexcept
|
||||||
{
|
{
|
||||||
JITCompiler = new Compiler();
|
JitEnableWrite();
|
||||||
|
ResetBlockCache();
|
||||||
ARMJIT_Memory::Init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeInit()
|
void ARMJIT::Reset() noexcept
|
||||||
{
|
|
||||||
ResetBlockCache();
|
|
||||||
ARMJIT_Memory::DeInit();
|
|
||||||
|
|
||||||
delete JITCompiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset()
|
|
||||||
{
|
{
|
||||||
|
JitEnableWrite();
|
||||||
ResetBlockCache();
|
ResetBlockCache();
|
||||||
|
|
||||||
ARMJIT_Memory::Reset();
|
Memory.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FloodFillSetFlags(FetchedInstr instrs[], int start, u8 flags)
|
void FloodFillSetFlags(FetchedInstr instrs[], int start, u8 flags)
|
||||||
|
@ -356,7 +289,7 @@ bool DecodeLiteral(bool thumb, const FetchedInstr& instr, u32& addr)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DecodeBranch(bool thumb, const FetchedInstr& instr, u32& cond, bool hasLink, u32 lr, bool& link,
|
bool DecodeBranch(bool thumb, const FetchedInstr& instr, u32& cond, bool hasLink, u32 lr, bool& link,
|
||||||
u32& linkAddr, u32& targetAddr)
|
u32& linkAddr, u32& targetAddr)
|
||||||
{
|
{
|
||||||
if (thumb)
|
if (thumb)
|
||||||
|
@ -399,7 +332,7 @@ bool DecodeBranch(bool thumb, const FetchedInstr& instr, u32& cond, bool hasLink
|
||||||
linkAddr = instr.Addr + 4;
|
linkAddr = instr.Addr + 4;
|
||||||
|
|
||||||
cond = instr.Cond();
|
cond = instr.Cond();
|
||||||
if (instr.Info.Kind == ARMInstrInfo::ak_BL
|
if (instr.Info.Kind == ARMInstrInfo::ak_BL
|
||||||
|| instr.Info.Kind == ARMInstrInfo::ak_B)
|
|| instr.Info.Kind == ARMInstrInfo::ak_B)
|
||||||
{
|
{
|
||||||
s32 offset = (s32)(instr.Instr << 8) >> 6;
|
s32 offset = (s32)(instr.Instr << 8) >> 6;
|
||||||
|
@ -440,7 +373,7 @@ bool IsIdleLoop(bool thumb, FetchedInstr* instrs, int instrsCount)
|
||||||
u16 dstRegs = instrs[i].Info.DstRegs & ~(1 << 15);
|
u16 dstRegs = instrs[i].Info.DstRegs & ~(1 << 15);
|
||||||
|
|
||||||
regsDisallowedToWrite |= srcRegs & ~regsWrittenTo;
|
regsDisallowedToWrite |= srcRegs & ~regsWrittenTo;
|
||||||
|
|
||||||
if (dstRegs & regsDisallowedToWrite)
|
if (dstRegs & regsDisallowedToWrite)
|
||||||
return false;
|
return false;
|
||||||
regsWrittenTo |= dstRegs;
|
regsWrittenTo |= dstRegs;
|
||||||
|
@ -530,12 +463,22 @@ InterpreterFunc InterpretTHUMB[ARMInstrInfo::tk_Count] =
|
||||||
F(LDRB_IMM), F(STRH_IMM), F(LDRH_IMM), F(STR_SPREL), F(LDR_SPREL),
|
F(LDRB_IMM), F(STRH_IMM), F(LDRH_IMM), F(STR_SPREL), F(LDR_SPREL),
|
||||||
F(PUSH), F(POP), F(LDMIA), F(STMIA),
|
F(PUSH), F(POP), F(LDMIA), F(STMIA),
|
||||||
F(BCOND), F(BX), F(BLX_REG), F(B), F(BL_LONG_1), F(BL_LONG_2),
|
F(BCOND), F(BX), F(BLX_REG), F(B), F(BL_LONG_1), F(BL_LONG_2),
|
||||||
F(UNK), F(SVC),
|
F(UNK), F(SVC),
|
||||||
T_BL_LONG // BL_LONG psudo opcode
|
T_BL_LONG // BL_LONG psudo opcode
|
||||||
};
|
};
|
||||||
#undef F
|
#undef F
|
||||||
|
|
||||||
void RetireJitBlock(JitBlock* block)
|
ARMJIT::ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept :
|
||||||
|
NDS(nds),
|
||||||
|
Memory(nds),
|
||||||
|
JITCompiler(nds),
|
||||||
|
MaxBlockSize(jit.has_value() ? std::clamp(jit->MaxBlockSize, 1u, 32u) : 32),
|
||||||
|
LiteralOptimizations(jit.has_value() ? jit->LiteralOptimizations : false),
|
||||||
|
BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false),
|
||||||
|
FastMemory((jit.has_value() ? jit->FastMemory : false) && ARMJIT_Memory::IsFastMemSupported())
|
||||||
|
{}
|
||||||
|
|
||||||
|
void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
|
||||||
{
|
{
|
||||||
auto it = RestoreCandidates.find(block->InstrHash);
|
auto it = RestoreCandidates.find(block->InstrHash);
|
||||||
if (it != RestoreCandidates.end())
|
if (it != RestoreCandidates.end())
|
||||||
|
@ -549,21 +492,53 @@ void RetireJitBlock(JitBlock* block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompileBlock(ARM* cpu)
|
void ARMJIT::SetJITArgs(JITArgs args) noexcept
|
||||||
|
{
|
||||||
|
args.FastMemory = args.FastMemory && ARMJIT_Memory::IsFastMemSupported();
|
||||||
|
args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u);
|
||||||
|
|
||||||
|
if (MaxBlockSize != args.MaxBlockSize
|
||||||
|
|| LiteralOptimizations != args.LiteralOptimizations
|
||||||
|
|| BranchOptimizations != args.BranchOptimizations
|
||||||
|
|| FastMemory != args.FastMemory)
|
||||||
|
ResetBlockCache();
|
||||||
|
|
||||||
|
MaxBlockSize = args.MaxBlockSize;
|
||||||
|
LiteralOptimizations = args.LiteralOptimizations;
|
||||||
|
BranchOptimizations = args.BranchOptimizations;
|
||||||
|
FastMemory = args.FastMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMJIT::SetMaxBlockSize(int size) noexcept
|
||||||
|
{
|
||||||
|
SetJITArgs(JITArgs{static_cast<unsigned>(size), LiteralOptimizations, LiteralOptimizations, FastMemory});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept
|
||||||
|
{
|
||||||
|
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), enabled, BranchOptimizations, FastMemory});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMJIT::SetBranchOptimizations(bool enabled) noexcept
|
||||||
|
{
|
||||||
|
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), LiteralOptimizations, enabled, FastMemory});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMJIT::SetFastMemory(bool enabled) noexcept
|
||||||
|
{
|
||||||
|
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), LiteralOptimizations, BranchOptimizations, enabled});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMJIT::CompileBlock(ARM* cpu) noexcept
|
||||||
{
|
{
|
||||||
bool thumb = cpu->CPSR & 0x20;
|
bool thumb = cpu->CPSR & 0x20;
|
||||||
|
|
||||||
if (Config::JIT_MaxBlockSize < 1)
|
|
||||||
Config::JIT_MaxBlockSize = 1;
|
|
||||||
if (Config::JIT_MaxBlockSize > 32)
|
|
||||||
Config::JIT_MaxBlockSize = 32;
|
|
||||||
|
|
||||||
u32 blockAddr = cpu->R[15] - (thumb ? 2 : 4);
|
u32 blockAddr = cpu->R[15] - (thumb ? 2 : 4);
|
||||||
|
|
||||||
u32 localAddr = LocaliseCodeAddress(cpu->Num, blockAddr);
|
u32 localAddr = LocaliseCodeAddress(cpu->Num, blockAddr);
|
||||||
if (!localAddr)
|
if (!localAddr)
|
||||||
{
|
{
|
||||||
printf("trying to compile non executable code? %x\n", blockAddr);
|
Log(LogLevel::Warn, "trying to compile non executable code? %x\n", blockAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& map = cpu->Num == 0 ? JitBlocks9 : JitBlocks7;
|
auto& map = cpu->Num == 0 ? JitBlocks9 : JitBlocks7;
|
||||||
|
@ -581,29 +556,34 @@ void CompileBlock(ARM* cpu)
|
||||||
|
|
||||||
u64* entry = &FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2];
|
u64* entry = &FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2];
|
||||||
*entry = ((u64)blockAddr | cpu->Num) << 32;
|
*entry = ((u64)blockAddr | cpu->Num) << 32;
|
||||||
*entry |= JITCompiler->SubEntryOffset(existingBlockIt->second->EntryPoint);
|
*entry |= JITCompiler.SubEntryOffset(existingBlockIt->second->EntryPoint);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// some memory has been remapped
|
// some memory has been remapped
|
||||||
RetireJitBlock(existingBlockIt->second);
|
RetireJitBlock(existingBlockIt->second);
|
||||||
map.erase(existingBlockIt);
|
map.erase(existingBlockIt);
|
||||||
}
|
}
|
||||||
|
|
||||||
FetchedInstr instrs[Config::JIT_MaxBlockSize];
|
FetchedInstr instrs[MaxBlockSize];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
u32 r15 = cpu->R[15];
|
u32 r15 = cpu->R[15];
|
||||||
|
|
||||||
u32 addressRanges[Config::JIT_MaxBlockSize];
|
u32 addressRanges[MaxBlockSize];
|
||||||
u32 addressMasks[Config::JIT_MaxBlockSize];
|
u32 addressMasks[MaxBlockSize];
|
||||||
memset(addressMasks, 0, Config::JIT_MaxBlockSize * sizeof(u32));
|
memset(addressMasks, 0, MaxBlockSize * sizeof(u32));
|
||||||
u32 numAddressRanges = 0;
|
u32 numAddressRanges = 0;
|
||||||
|
|
||||||
u32 numLiterals = 0;
|
u32 numLiterals = 0;
|
||||||
u32 literalLoadAddrs[Config::JIT_MaxBlockSize];
|
u32 literalLoadAddrs[MaxBlockSize];
|
||||||
// they are going to be hashed
|
// they are going to be hashed
|
||||||
u32 literalValues[Config::JIT_MaxBlockSize];
|
u32 literalValues[MaxBlockSize];
|
||||||
u32 instrValues[Config::JIT_MaxBlockSize];
|
u32 instrValues[MaxBlockSize];
|
||||||
|
// due to instruction merging i might not reflect the amount of actual instructions
|
||||||
|
u32 numInstrs = 0;
|
||||||
|
|
||||||
|
u32 writeAddrs[MaxBlockSize];
|
||||||
|
u32 numWriteAddrs = 0, writeAddrsTranslated = 0;
|
||||||
|
|
||||||
cpu->FillPipeline();
|
cpu->FillPipeline();
|
||||||
u32 nextInstr[2] = {cpu->NextInstr[0], cpu->NextInstr[1]};
|
u32 nextInstr[2] = {cpu->NextInstr[0], cpu->NextInstr[1]};
|
||||||
|
@ -615,6 +595,8 @@ void CompileBlock(ARM* cpu)
|
||||||
u32 lr;
|
u32 lr;
|
||||||
bool hasLink = false;
|
bool hasLink = false;
|
||||||
|
|
||||||
|
bool hasMemoryInstr = false;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
r15 += thumb ? 2 : 4;
|
r15 += thumb ? 2 : 4;
|
||||||
|
@ -623,13 +605,13 @@ void CompileBlock(ARM* cpu)
|
||||||
instrs[i].SetFlags = 0;
|
instrs[i].SetFlags = 0;
|
||||||
instrs[i].Instr = nextInstr[0];
|
instrs[i].Instr = nextInstr[0];
|
||||||
nextInstr[0] = nextInstr[1];
|
nextInstr[0] = nextInstr[1];
|
||||||
|
|
||||||
instrs[i].Addr = nextInstrAddr[0];
|
instrs[i].Addr = nextInstrAddr[0];
|
||||||
nextInstrAddr[0] = nextInstrAddr[1];
|
nextInstrAddr[0] = nextInstrAddr[1];
|
||||||
nextInstrAddr[1] = r15;
|
nextInstrAddr[1] = r15;
|
||||||
JIT_DEBUGPRINT("instr %08x %x\n", instrs[i].Instr & (thumb ? 0xFFFF : ~0), instrs[i].Addr);
|
JIT_DEBUGPRINT("instr %08x %x\n", instrs[i].Instr & (thumb ? 0xFFFF : ~0), instrs[i].Addr);
|
||||||
|
|
||||||
instrValues[i] = instrs[i].Instr;
|
instrValues[numInstrs++] = instrs[i].Instr;
|
||||||
|
|
||||||
u32 translatedAddr = LocaliseCodeAddress(cpu->Num, instrs[i].Addr);
|
u32 translatedAddr = LocaliseCodeAddress(cpu->Num, instrs[i].Addr);
|
||||||
assert(translatedAddr >> 27);
|
assert(translatedAddr >> 27);
|
||||||
|
@ -637,7 +619,7 @@ void CompileBlock(ARM* cpu)
|
||||||
if (i == 0 || translatedAddrRounded != addressRanges[numAddressRanges - 1])
|
if (i == 0 || translatedAddrRounded != addressRanges[numAddressRanges - 1])
|
||||||
{
|
{
|
||||||
bool returning = false;
|
bool returning = false;
|
||||||
for (int j = 0; j < numAddressRanges; j++)
|
for (u32 j = 0; j < numAddressRanges; j++)
|
||||||
{
|
{
|
||||||
if (addressRanges[j] == translatedAddrRounded)
|
if (addressRanges[j] == translatedAddrRounded)
|
||||||
{
|
{
|
||||||
|
@ -675,7 +657,11 @@ void CompileBlock(ARM* cpu)
|
||||||
nextInstr[1] = cpuv4->CodeRead32(r15);
|
nextInstr[1] = cpuv4->CodeRead32(r15);
|
||||||
instrs[i].CodeCycles = cpu->CodeCycles;
|
instrs[i].CodeCycles = cpu->CodeCycles;
|
||||||
}
|
}
|
||||||
instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr);
|
instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr, LiteralOptimizations);
|
||||||
|
|
||||||
|
hasMemoryInstr |= thumb
|
||||||
|
? (instrs[i].Info.Kind >= ARMInstrInfo::tk_LDR_PCREL && instrs[i].Info.Kind <= ARMInstrInfo::tk_STMIA)
|
||||||
|
: (instrs[i].Info.Kind >= ARMInstrInfo::ak_STR_REG_LSL && instrs[i].Info.Kind <= ARMInstrInfo::ak_STM);
|
||||||
|
|
||||||
cpu->R[15] = r15;
|
cpu->R[15] = r15;
|
||||||
cpu->CurInstr = instrs[i].Instr;
|
cpu->CurInstr = instrs[i].Instr;
|
||||||
|
@ -715,41 +701,47 @@ void CompileBlock(ARM* cpu)
|
||||||
instrs[i].DataRegion = cpu->DataRegion;
|
instrs[i].DataRegion = cpu->DataRegion;
|
||||||
|
|
||||||
u32 literalAddr;
|
u32 literalAddr;
|
||||||
if (Config::JIT_LiteralOptimisations
|
if (LiteralOptimizations
|
||||||
&& instrs[i].Info.SpecialKind == ARMInstrInfo::special_LoadLiteral
|
&& instrs[i].Info.SpecialKind == ARMInstrInfo::special_LoadLiteral
|
||||||
&& DecodeLiteral(thumb, instrs[i], literalAddr))
|
&& DecodeLiteral(thumb, instrs[i], literalAddr))
|
||||||
{
|
{
|
||||||
u32 translatedAddr = LocaliseCodeAddress(cpu->Num, literalAddr);
|
u32 translatedAddr = LocaliseCodeAddress(cpu->Num, literalAddr);
|
||||||
if (!translatedAddr)
|
if (!translatedAddr)
|
||||||
{
|
{
|
||||||
printf("literal in non executable memory?\n");
|
Log(LogLevel::Warn,"literal in non executable memory?\n");
|
||||||
}
|
}
|
||||||
u32 translatedAddrRounded = translatedAddr & ~0x1FF;
|
if (InvalidLiterals.Find(translatedAddr) == -1)
|
||||||
|
{
|
||||||
|
u32 translatedAddrRounded = translatedAddr & ~0x1FF;
|
||||||
|
|
||||||
u32 j = 0;
|
u32 j = 0;
|
||||||
for (; j < numAddressRanges; j++)
|
for (; j < numAddressRanges; j++)
|
||||||
if (addressRanges[j] == translatedAddrRounded)
|
if (addressRanges[j] == translatedAddrRounded)
|
||||||
break;
|
break;
|
||||||
if (j == numAddressRanges)
|
if (j == numAddressRanges)
|
||||||
addressRanges[numAddressRanges++] = translatedAddrRounded;
|
addressRanges[numAddressRanges++] = translatedAddrRounded;
|
||||||
addressMasks[j] |= 1 << ((translatedAddr & 0x1FF) / 16);
|
addressMasks[j] |= 1 << ((translatedAddr & 0x1FF) / 16);
|
||||||
JIT_DEBUGPRINT("literal loading %08x %08x %08x %08x\n", literalAddr, translatedAddr, addressMasks[j], addressRanges[j]);
|
JIT_DEBUGPRINT("literal loading %08x %08x %08x %08x\n", literalAddr, translatedAddr, addressMasks[j], addressRanges[j]);
|
||||||
cpu->DataRead32(literalAddr, &literalValues[numLiterals]);
|
cpu->DataRead32(literalAddr, &literalValues[numLiterals]);
|
||||||
literalLoadAddrs[numLiterals++] = translatedAddr;
|
literalLoadAddrs[numLiterals++] = translatedAddr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else if (instrs[i].Info.SpecialKind == ARMInstrInfo::special_WriteMem)
|
||||||
if (thumb && instrs[i].Info.Kind == ARMInstrInfo::tk_BL_LONG_2 && i > 0
|
writeAddrs[numWriteAddrs++] = instrs[i].DataRegion;
|
||||||
|
else if (thumb && instrs[i].Info.Kind == ARMInstrInfo::tk_BL_LONG_2 && i > 0
|
||||||
&& instrs[i - 1].Info.Kind == ARMInstrInfo::tk_BL_LONG_1)
|
&& instrs[i - 1].Info.Kind == ARMInstrInfo::tk_BL_LONG_1)
|
||||||
{
|
{
|
||||||
instrs[i - 1].Info.Kind = ARMInstrInfo::tk_BL_LONG;
|
|
||||||
instrs[i - 1].Instr = (instrs[i - 1].Instr & 0xFFFF) | (instrs[i].Instr << 16);
|
|
||||||
instrs[i - 1].Info.DstRegs = 0xC000;
|
|
||||||
instrs[i - 1].Info.SrcRegs = 0;
|
|
||||||
instrs[i - 1].Info.EndBlock = true;
|
|
||||||
i--;
|
i--;
|
||||||
|
instrs[i].Info.Kind = ARMInstrInfo::tk_BL_LONG;
|
||||||
|
instrs[i].Instr = (instrs[i].Instr & 0xFFFF) | (instrs[i + 1].Instr << 16);
|
||||||
|
instrs[i].Info.DstRegs = 0xC000;
|
||||||
|
instrs[i].Info.SrcRegs = 0;
|
||||||
|
instrs[i].Info.EndBlock = true;
|
||||||
|
JIT_DEBUGPRINT("merged BL\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instrs[i].Info.Branches() && Config::JIT_BranchOptimisations)
|
if (instrs[i].Info.Branches() && BranchOptimizations
|
||||||
|
&& instrs[i].Info.Kind != (thumb ? ARMInstrInfo::tk_SVC : ARMInstrInfo::ak_SVC))
|
||||||
{
|
{
|
||||||
bool hasBranched = cpu->R[15] != r15;
|
bool hasBranched = cpu->R[15] != r15;
|
||||||
|
|
||||||
|
@ -767,7 +759,7 @@ void CompileBlock(ARM* cpu)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < i; j++)
|
for (int j = 0; j < i; j++)
|
||||||
{
|
{
|
||||||
if (instrs[i].Addr == target)
|
if (instrs[j].Addr == target)
|
||||||
{
|
{
|
||||||
isBackJump = true;
|
isBackJump = true;
|
||||||
break;
|
break;
|
||||||
|
@ -785,14 +777,14 @@ void CompileBlock(ARM* cpu)
|
||||||
JIT_DEBUGPRINT("found %s idle loop %d in block %08x\n", thumb ? "thumb" : "arm", cpu->Num, blockAddr);
|
JIT_DEBUGPRINT("found %s idle loop %d in block %08x\n", thumb ? "thumb" : "arm", cpu->Num, blockAddr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (hasBranched && !isBackJump && i + 1 < Config::JIT_MaxBlockSize)
|
else if (hasBranched && !isBackJump && i + 1 < MaxBlockSize)
|
||||||
{
|
{
|
||||||
if (link)
|
if (link)
|
||||||
{
|
{
|
||||||
lr = linkAddr;
|
lr = linkAddr;
|
||||||
hasLink = true;
|
hasLink = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
r15 = target + (thumb ? 2 : 4);
|
r15 = target + (thumb ? 2 : 4);
|
||||||
assert(r15 == cpu->R[15]);
|
assert(r15 == cpu->R[15]);
|
||||||
|
|
||||||
|
@ -813,8 +805,9 @@ void CompileBlock(ARM* cpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasBranched && cond < 0xE && i + 1 < Config::JIT_MaxBlockSize)
|
if (!hasBranched && cond < 0xE && i + 1 < MaxBlockSize)
|
||||||
{
|
{
|
||||||
|
JIT_DEBUGPRINT("block lengthened by untaken branch\n");
|
||||||
instrs[i].Info.EndBlock = false;
|
instrs[i].Info.EndBlock = false;
|
||||||
instrs[i].BranchFlags |= branch_FollowCondNotTaken;
|
instrs[i].BranchFlags |= branch_FollowCondNotTaken;
|
||||||
}
|
}
|
||||||
|
@ -822,14 +815,34 @@ void CompileBlock(ARM* cpu)
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
bool canCompile = JITCompiler->CanCompile(thumb, instrs[i - 1].Info.Kind);
|
bool canCompile = JITCompiler.CanCompile(thumb, instrs[i - 1].Info.Kind);
|
||||||
bool secondaryFlagReadCond = !canCompile || (instrs[i - 1].BranchFlags & (branch_FollowCondTaken | branch_FollowCondNotTaken));
|
bool secondaryFlagReadCond = !canCompile || (instrs[i - 1].BranchFlags & (branch_FollowCondTaken | branch_FollowCondNotTaken));
|
||||||
if (instrs[i - 1].Info.ReadFlags != 0 || secondaryFlagReadCond)
|
if (instrs[i - 1].Info.ReadFlags != 0 || secondaryFlagReadCond)
|
||||||
FloodFillSetFlags(instrs, i - 2, !secondaryFlagReadCond ? instrs[i - 1].Info.ReadFlags : 0xF);
|
FloodFillSetFlags(instrs, i - 2, !secondaryFlagReadCond ? instrs[i - 1].Info.ReadFlags : 0xF);
|
||||||
} while(!instrs[i - 1].Info.EndBlock && i < Config::JIT_MaxBlockSize && !cpu->Halted && (!cpu->IRQ || (cpu->CPSR & 0x80)));
|
} while(!instrs[i - 1].Info.EndBlock && i < MaxBlockSize && !cpu->Halted && (!cpu->IRQ || (cpu->CPSR & 0x80)));
|
||||||
|
|
||||||
|
if (numLiterals)
|
||||||
|
{
|
||||||
|
for (u32 j = 0; j < numWriteAddrs; j++)
|
||||||
|
{
|
||||||
|
u32 translatedAddr = LocaliseCodeAddress(cpu->Num, writeAddrs[j]);
|
||||||
|
if (translatedAddr)
|
||||||
|
{
|
||||||
|
for (u32 k = 0; k < numLiterals; k++)
|
||||||
|
{
|
||||||
|
if (literalLoadAddrs[k] == translatedAddr)
|
||||||
|
{
|
||||||
|
if (InvalidLiterals.Find(translatedAddr) == -1)
|
||||||
|
InvalidLiterals.Add(translatedAddr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
u32 literalHash = (u32)XXH3_64bits(literalValues, numLiterals * 4);
|
u32 literalHash = (u32)XXH3_64bits(literalValues, numLiterals * 4);
|
||||||
u32 instrHash = (u32)XXH3_64bits(instrValues, i * 4);
|
u32 instrHash = (u32)XXH3_64bits(instrValues, numInstrs * 4);
|
||||||
|
|
||||||
auto prevBlockIt = RestoreCandidates.find(instrHash);
|
auto prevBlockIt = RestoreCandidates.find(instrHash);
|
||||||
JitBlock* prevBlock = NULL;
|
JitBlock* prevBlock = NULL;
|
||||||
|
@ -843,7 +856,7 @@ void CompileBlock(ARM* cpu)
|
||||||
|
|
||||||
if (mayRestore && prevBlock->NumAddresses == numAddressRanges)
|
if (mayRestore && prevBlock->NumAddresses == numAddressRanges)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < numAddressRanges; j++)
|
for (u32 j = 0; j < numAddressRanges; j++)
|
||||||
{
|
{
|
||||||
if (prevBlock->AddressRanges()[j] != addressRanges[j]
|
if (prevBlock->AddressRanges()[j] != addressRanges[j]
|
||||||
|| prevBlock->AddressMasks()[j] != addressMasks[j])
|
|| prevBlock->AddressMasks()[j] != addressMasks[j])
|
||||||
|
@ -870,9 +883,9 @@ void CompileBlock(ARM* cpu)
|
||||||
block = new JitBlock(cpu->Num, i, numAddressRanges, numLiterals);
|
block = new JitBlock(cpu->Num, i, numAddressRanges, numLiterals);
|
||||||
block->LiteralHash = literalHash;
|
block->LiteralHash = literalHash;
|
||||||
block->InstrHash = instrHash;
|
block->InstrHash = instrHash;
|
||||||
for (int j = 0; j < numAddressRanges; j++)
|
for (u32 j = 0; j < numAddressRanges; j++)
|
||||||
block->AddressRanges()[j] = addressRanges[j];
|
block->AddressRanges()[j] = addressRanges[j];
|
||||||
for (int j = 0; j < numAddressRanges; j++)
|
for (u32 j = 0; j < numAddressRanges; j++)
|
||||||
block->AddressMasks()[j] = addressMasks[j];
|
block->AddressMasks()[j] = addressMasks[j];
|
||||||
for (int j = 0; j < numLiterals; j++)
|
for (int j = 0; j < numLiterals; j++)
|
||||||
block->Literals()[j] = literalLoadAddrs[j];
|
block->Literals()[j] = literalLoadAddrs[j];
|
||||||
|
@ -882,7 +895,9 @@ void CompileBlock(ARM* cpu)
|
||||||
|
|
||||||
FloodFillSetFlags(instrs, i - 1, 0xF);
|
FloodFillSetFlags(instrs, i - 1, 0xF);
|
||||||
|
|
||||||
block->EntryPoint = JITCompiler->CompileBlock(cpu, thumb, instrs, i);
|
JitEnableWrite();
|
||||||
|
block->EntryPoint = JITCompiler.CompileBlock(cpu, thumb, instrs, i, hasMemoryInstr);
|
||||||
|
JitEnableExecute();
|
||||||
|
|
||||||
JIT_DEBUGPRINT("block start %p\n", block->EntryPoint);
|
JIT_DEBUGPRINT("block start %p\n", block->EntryPoint);
|
||||||
}
|
}
|
||||||
|
@ -893,7 +908,7 @@ void CompileBlock(ARM* cpu)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert((localAddr & 1) == 0);
|
assert((localAddr & 1) == 0);
|
||||||
for (int j = 0; j < numAddressRanges; j++)
|
for (u32 j = 0; j < numAddressRanges; j++)
|
||||||
{
|
{
|
||||||
assert(addressRanges[j] == block->AddressRanges()[j]);
|
assert(addressRanges[j] == block->AddressRanges()[j]);
|
||||||
assert(addressMasks[j] == block->AddressMasks()[j]);
|
assert(addressMasks[j] == block->AddressMasks()[j]);
|
||||||
|
@ -901,8 +916,8 @@ void CompileBlock(ARM* cpu)
|
||||||
|
|
||||||
AddressRange* region = CodeMemRegions[addressRanges[j] >> 27];
|
AddressRange* region = CodeMemRegions[addressRanges[j] >> 27];
|
||||||
|
|
||||||
if (!PageContainsCode(®ion[(addressRanges[j] & 0x7FFF000) / 512]))
|
if (!PageContainsCode(®ion[(addressRanges[j] & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
|
||||||
ARMJIT_Memory::SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true);
|
Memory.SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true);
|
||||||
|
|
||||||
AddressRange* range = ®ion[(addressRanges[j] & 0x7FFFFFF) / 512];
|
AddressRange* range = ®ion[(addressRanges[j] & 0x7FFFFFF) / 512];
|
||||||
range->Code |= addressMasks[j];
|
range->Code |= addressMasks[j];
|
||||||
|
@ -916,10 +931,10 @@ void CompileBlock(ARM* cpu)
|
||||||
|
|
||||||
u64* entry = &FastBlockLookupRegions[(localAddr >> 27)][(localAddr & 0x7FFFFFF) / 2];
|
u64* entry = &FastBlockLookupRegions[(localAddr >> 27)][(localAddr & 0x7FFFFFF) / 2];
|
||||||
*entry = ((u64)blockAddr | cpu->Num) << 32;
|
*entry = ((u64)blockAddr | cpu->Num) << 32;
|
||||||
*entry |= JITCompiler->SubEntryOffset(block->EntryPoint);
|
*entry |= JITCompiler.SubEntryOffset(block->EntryPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InvalidateByAddr(u32 localAddr)
|
void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
|
||||||
{
|
{
|
||||||
JIT_DEBUGPRINT("invalidating by addr %x\n", localAddr);
|
JIT_DEBUGPRINT("invalidating by addr %x\n", localAddr);
|
||||||
|
|
||||||
|
@ -954,9 +969,9 @@ void InvalidateByAddr(u32 localAddr)
|
||||||
range->Blocks.Remove(i);
|
range->Blocks.Remove(i);
|
||||||
|
|
||||||
if (range->Blocks.Length == 0
|
if (range->Blocks.Length == 0
|
||||||
&& !PageContainsCode(®ion[(localAddr & 0x7FFF000) / 512]))
|
&& !PageContainsCode(®ion[(localAddr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
|
||||||
{
|
{
|
||||||
ARMJIT_Memory::SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false);
|
Memory.SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool literalInvalidation = false;
|
bool literalInvalidation = false;
|
||||||
|
@ -965,7 +980,7 @@ void InvalidateByAddr(u32 localAddr)
|
||||||
u32 addr = block->Literals()[j];
|
u32 addr = block->Literals()[j];
|
||||||
if (addr == localAddr)
|
if (addr == localAddr)
|
||||||
{
|
{
|
||||||
if (InvalidLiterals.Find(localAddr) != -1)
|
if (InvalidLiterals.Find(localAddr) == -1)
|
||||||
{
|
{
|
||||||
InvalidLiterals.Add(localAddr);
|
InvalidLiterals.Add(localAddr);
|
||||||
JIT_DEBUGPRINT("found invalid literal %d\n", InvalidLiterals.Length);
|
JIT_DEBUGPRINT("found invalid literal %d\n", InvalidLiterals.Length);
|
||||||
|
@ -988,8 +1003,8 @@ void InvalidateByAddr(u32 localAddr)
|
||||||
|
|
||||||
if (otherRange->Blocks.Length == 0)
|
if (otherRange->Blocks.Length == 0)
|
||||||
{
|
{
|
||||||
if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000) / 512]))
|
if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
|
||||||
ARMJIT_Memory::SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false);
|
Memory.SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false);
|
||||||
|
|
||||||
otherRange->Code = 0;
|
otherRange->Code = 0;
|
||||||
}
|
}
|
||||||
|
@ -1013,49 +1028,64 @@ void InvalidateByAddr(u32 localAddr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckAndInvalidateITCM()
|
void ARMJIT::CheckAndInvalidateITCM() noexcept
|
||||||
{
|
{
|
||||||
for (u32 i = 0; i < ITCMPhysicalSize; i+=16)
|
for (u32 i = 0; i < ITCMPhysicalSize; i+=512)
|
||||||
{
|
{
|
||||||
if (CodeIndexITCM[i / 512].Code & (1 << ((i & 0x1FF) / 16)))
|
if (CodeIndexITCM[i / 512].Code)
|
||||||
{
|
{
|
||||||
InvalidateByAddr(i | (ARMJIT_Memory::memregion_ITCM << 27));
|
// maybe using bitscan would be better here?
|
||||||
|
// The thing is that in densely populated sets
|
||||||
|
// The old fashioned way can actually be faster
|
||||||
|
for (u32 j = 0; j < 512; j += 16)
|
||||||
|
{
|
||||||
|
if (CodeIndexITCM[i / 512].Code & (1 << ((j & 0x1FF) / 16)))
|
||||||
|
InvalidateByAddr((i+j) | (ARMJIT_Memory::memregion_ITCM << 27));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <u32 num, int region>
|
void ARMJIT::CheckAndInvalidateWVRAM(int bank) noexcept
|
||||||
void CheckAndInvalidate(u32 addr)
|
|
||||||
{
|
{
|
||||||
u32 localAddr = ARMJIT_Memory::LocaliseAddress(region, num, addr);
|
u32 start = bank == 1 ? 0x20000 : 0;
|
||||||
if (CodeMemRegions[region][(localAddr & 0x7FFFFFF) / 512].Code & (1 << ((localAddr & 0x1FF) / 16)))
|
for (u32 i = start; i < start+0x20000; i+=512)
|
||||||
InvalidateByAddr(localAddr);
|
{
|
||||||
|
if (CodeIndexARM7WVRAM[i / 512].Code)
|
||||||
|
{
|
||||||
|
for (u32 j = 0; j < 512; j += 16)
|
||||||
|
{
|
||||||
|
if (CodeIndexARM7WVRAM[i / 512].Code & (1 << ((j & 0x1FF) / 16)))
|
||||||
|
InvalidateByAddr((i+j) | (ARMJIT_Memory::memregion_VWRAM << 27));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr)
|
JitBlockEntry ARMJIT::LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept
|
||||||
{
|
{
|
||||||
u64* entry = &entries[offset / 2];
|
u64* entry = &entries[offset / 2];
|
||||||
if (*entry >> 32 == (addr | num))
|
if (*entry >> 32 == (addr | num))
|
||||||
return JITCompiler->AddEntryOffset((u32)*entry);
|
return JITCompiler.AddEntryOffset((u32)*entry);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry)
|
void ARMJIT::blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept
|
||||||
{
|
{
|
||||||
u32 localAddr = LocaliseCodeAddress(num, blockAddr);
|
u32 localAddr = LocaliseCodeAddress(num, blockAddr);
|
||||||
assert(JITCompiler->AddEntryOffset((u32)FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2]) == entry);
|
assert(JITCompiler.AddEntryOffset((u32)FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2]) == entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size)
|
bool ARMJIT::SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept
|
||||||
{
|
{
|
||||||
// amazingly ignoring the DTCM is the proper behaviour for code fetches
|
// amazingly ignoring the DTCM is the proper behaviour for code fetches
|
||||||
int region = num == 0
|
int region = num == 0
|
||||||
? ARMJIT_Memory::ClassifyAddress9(blockAddr)
|
? Memory.ClassifyAddress9(blockAddr)
|
||||||
: ARMJIT_Memory::ClassifyAddress7(blockAddr);
|
: Memory.ClassifyAddress7(blockAddr);
|
||||||
|
|
||||||
u32 memoryOffset;
|
u32 memoryOffset;
|
||||||
if (FastBlockLookupRegions[region]
|
if (FastBlockLookupRegions[region]
|
||||||
&& ARMJIT_Memory::GetMirrorLocation(region, num, blockAddr, memoryOffset, start, size))
|
&& Memory.GetMirrorLocation(region, num, blockAddr, memoryOffset, start, size))
|
||||||
{
|
{
|
||||||
//printf("setup exec region %d %d %08x %08x %x %x\n", num, region, blockAddr, start, size, memoryOffset);
|
//printf("setup exec region %d %d %08x %08x %x %x\n", num, region, blockAddr, start, size, memoryOffset);
|
||||||
entry = FastBlockLookupRegions[region] + memoryOffset / 2;
|
entry = FastBlockLookupRegions[region] + memoryOffset / 2;
|
||||||
|
@ -1064,28 +1094,28 @@ bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32&
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(u32);
|
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(u32) noexcept;
|
||||||
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(u32);
|
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(u32) noexcept;
|
||||||
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(u32);
|
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(u32) noexcept;
|
||||||
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(u32);
|
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(u32) noexcept;
|
||||||
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(u32);
|
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(u32) noexcept;
|
||||||
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(u32);
|
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(u32) noexcept;
|
||||||
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(u32);
|
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(u32) noexcept;
|
||||||
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(u32);
|
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(u32) noexcept;
|
||||||
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32);
|
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32) noexcept;
|
||||||
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32);
|
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32) noexcept;
|
||||||
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32);
|
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32) noexcept;
|
||||||
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32);
|
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32) noexcept;
|
||||||
template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32);
|
template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32) noexcept;
|
||||||
template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32);
|
template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32) noexcept;
|
||||||
|
|
||||||
void ResetBlockCache()
|
void ARMJIT::ResetBlockCache() noexcept
|
||||||
{
|
{
|
||||||
printf("Resetting JIT block cache...\n");
|
Log(LogLevel::Debug, "Resetting JIT block cache...\n");
|
||||||
|
|
||||||
// could be replace through a function which only resets
|
// could be replace through a function which only resets
|
||||||
// the permissions but we're too lazy
|
// the permissions but we're too lazy
|
||||||
ARMJIT_Memory::Reset();
|
Memory.Reset();
|
||||||
|
|
||||||
InvalidLiterals.Clear();
|
InvalidLiterals.Clear();
|
||||||
for (int i = 0; i < ARMJIT_Memory::memregions_Count; i++)
|
for (int i = 0; i < ARMJIT_Memory::memregions_Count; i++)
|
||||||
|
@ -1123,7 +1153,23 @@ void ResetBlockCache()
|
||||||
JitBlocks9.clear();
|
JitBlocks9.clear();
|
||||||
JitBlocks7.clear();
|
JitBlocks7.clear();
|
||||||
|
|
||||||
JITCompiler->Reset();
|
JITCompiler.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMJIT::JitEnableWrite() noexcept
|
||||||
|
{
|
||||||
|
#if defined(__APPLE__) && defined(__aarch64__)
|
||||||
|
if (__builtin_available(macOS 11.0, *))
|
||||||
|
pthread_jit_write_protect_np(false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMJIT::JitEnableExecute() noexcept
|
||||||
|
{
|
||||||
|
#if defined(__APPLE__) && defined(__aarch64__)
|
||||||
|
if (__builtin_available(macOS 11.0, *))
|
||||||
|
pthread_jit_write_protect_np(true);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
203
src/ARMJIT.h
|
@ -1,37 +1,206 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef ARMJIT_H
|
#ifndef ARMJIT_H
|
||||||
#define ARMJIT_H
|
#define ARMJIT_H
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <optional>
|
||||||
|
#include <memory>
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "MemConstants.h"
|
||||||
|
#include "Args.h"
|
||||||
|
#include "ARMJIT_Memory.h"
|
||||||
|
|
||||||
#include "ARM.h"
|
#ifdef JIT_ENABLED
|
||||||
#include "ARM_InstrInfo.h"
|
#include "JitBlock.h"
|
||||||
|
|
||||||
namespace ARMJIT
|
#if defined(__APPLE__) && defined(__aarch64__)
|
||||||
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ARMJIT_Compiler.h"
|
||||||
|
|
||||||
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
class ARM;
|
||||||
|
|
||||||
typedef void (*JitBlockEntry)();
|
class JitBlock;
|
||||||
|
class ARMJIT
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept;
|
||||||
|
~ARMJIT() noexcept;
|
||||||
|
void InvalidateByAddr(u32) noexcept;
|
||||||
|
void CheckAndInvalidateWVRAM(int) noexcept;
|
||||||
|
void CheckAndInvalidateITCM() noexcept;
|
||||||
|
void Reset() noexcept;
|
||||||
|
void JitEnableWrite() noexcept;
|
||||||
|
void JitEnableExecute() noexcept;
|
||||||
|
void CompileBlock(ARM* cpu) noexcept;
|
||||||
|
void ResetBlockCache() noexcept;
|
||||||
|
|
||||||
void Init();
|
template <u32 num, int region>
|
||||||
void DeInit();
|
void CheckAndInvalidate(u32 addr) noexcept
|
||||||
|
{
|
||||||
|
u32 localAddr = Memory.LocaliseAddress(region, num, addr);
|
||||||
|
if (CodeMemRegions[region][(localAddr & 0x7FFFFFF) / 512].Code & (1 << ((localAddr & 0x1FF) / 16)))
|
||||||
|
InvalidateByAddr(localAddr);
|
||||||
|
}
|
||||||
|
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept;
|
||||||
|
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept;
|
||||||
|
u32 LocaliseCodeAddress(u32 num, u32 addr) const noexcept;
|
||||||
|
|
||||||
void Reset();
|
ARMJIT_Memory Memory;
|
||||||
|
private:
|
||||||
|
int MaxBlockSize {};
|
||||||
|
bool LiteralOptimizations = false;
|
||||||
|
bool BranchOptimizations = false;
|
||||||
|
bool FastMemory = false;
|
||||||
|
|
||||||
void CheckAndInvalidateITCM();
|
public:
|
||||||
|
melonDS::NDS& NDS;
|
||||||
|
TinyVector<u32> InvalidLiterals {};
|
||||||
|
friend class ARMJIT_Memory;
|
||||||
|
void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept;
|
||||||
|
void RetireJitBlock(JitBlock* block) noexcept;
|
||||||
|
|
||||||
void InvalidateByAddr(u32 pseudoPhysical);
|
int GetMaxBlockSize() const noexcept { return MaxBlockSize; }
|
||||||
|
bool LiteralOptimizationsEnabled() const noexcept { return LiteralOptimizations; }
|
||||||
|
bool BranchOptimizationsEnabled() const noexcept { return BranchOptimizations; }
|
||||||
|
bool FastMemoryEnabled() const noexcept { return FastMemory; }
|
||||||
|
|
||||||
template <u32 num, int region>
|
void SetJITArgs(JITArgs args) noexcept;
|
||||||
void CheckAndInvalidate(u32 addr);
|
void SetMaxBlockSize(int size) noexcept;
|
||||||
|
void SetLiteralOptimizations(bool enabled) noexcept;
|
||||||
|
void SetBranchOptimizations(bool enabled) noexcept;
|
||||||
|
void SetFastMemory(bool enabled) noexcept;
|
||||||
|
|
||||||
void CompileBlock(ARM* cpu);
|
Compiler JITCompiler;
|
||||||
|
std::unordered_map<u32, JitBlock*> JitBlocks9 {};
|
||||||
|
std::unordered_map<u32, JitBlock*> JitBlocks7 {};
|
||||||
|
|
||||||
void ResetBlockCache();
|
std::unordered_map<u32, JitBlock*> RestoreCandidates {};
|
||||||
|
|
||||||
JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr);
|
|
||||||
bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size);
|
|
||||||
|
|
||||||
|
AddressRange CodeIndexITCM[ITCMPhysicalSize / 512] {};
|
||||||
|
AddressRange CodeIndexMainRAM[MainRAMMaxSize / 512] {};
|
||||||
|
AddressRange CodeIndexSWRAM[SharedWRAMSize / 512] {};
|
||||||
|
AddressRange CodeIndexVRAM[0x100000 / 512] {};
|
||||||
|
AddressRange CodeIndexARM9BIOS[ARM9BIOSSize / 512] {};
|
||||||
|
AddressRange CodeIndexARM7BIOS[ARM7BIOSSize / 512] {};
|
||||||
|
AddressRange CodeIndexARM7WRAM[ARM7WRAMSize / 512] {};
|
||||||
|
AddressRange CodeIndexARM7WVRAM[0x40000 / 512] {};
|
||||||
|
AddressRange CodeIndexBIOS9DSi[0x10000 / 512] {};
|
||||||
|
AddressRange CodeIndexBIOS7DSi[0x10000 / 512] {};
|
||||||
|
AddressRange CodeIndexNWRAM_A[NWRAMSize / 512] {};
|
||||||
|
AddressRange CodeIndexNWRAM_B[NWRAMSize / 512] {};
|
||||||
|
AddressRange CodeIndexNWRAM_C[NWRAMSize / 512] {};
|
||||||
|
|
||||||
|
u64 FastBlockLookupITCM[ITCMPhysicalSize / 2] {};
|
||||||
|
u64 FastBlockLookupMainRAM[MainRAMMaxSize / 2] {};
|
||||||
|
u64 FastBlockLookupSWRAM[SharedWRAMSize / 2] {};
|
||||||
|
u64 FastBlockLookupVRAM[0x100000 / 2] {};
|
||||||
|
u64 FastBlockLookupARM9BIOS[ARM9BIOSSize / 2] {};
|
||||||
|
u64 FastBlockLookupARM7BIOS[ARM7BIOSSize / 2] {};
|
||||||
|
u64 FastBlockLookupARM7WRAM[ARM7WRAMSize / 2] {};
|
||||||
|
u64 FastBlockLookupARM7WVRAM[0x40000 / 2] {};
|
||||||
|
u64 FastBlockLookupBIOS9DSi[0x10000 / 2] {};
|
||||||
|
u64 FastBlockLookupBIOS7DSi[0x10000 / 2] {};
|
||||||
|
u64 FastBlockLookupNWRAM_A[NWRAMSize / 2] {};
|
||||||
|
u64 FastBlockLookupNWRAM_B[NWRAMSize / 2] {};
|
||||||
|
u64 FastBlockLookupNWRAM_C[NWRAMSize / 2] {};
|
||||||
|
|
||||||
|
AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count] =
|
||||||
|
{
|
||||||
|
NULL,
|
||||||
|
CodeIndexITCM,
|
||||||
|
NULL,
|
||||||
|
CodeIndexARM9BIOS,
|
||||||
|
CodeIndexMainRAM,
|
||||||
|
CodeIndexSWRAM,
|
||||||
|
NULL,
|
||||||
|
CodeIndexVRAM,
|
||||||
|
CodeIndexARM7BIOS,
|
||||||
|
CodeIndexARM7WRAM,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
CodeIndexARM7WVRAM,
|
||||||
|
CodeIndexBIOS9DSi,
|
||||||
|
CodeIndexBIOS7DSi,
|
||||||
|
CodeIndexNWRAM_A,
|
||||||
|
CodeIndexNWRAM_B,
|
||||||
|
CodeIndexNWRAM_C
|
||||||
|
};
|
||||||
|
|
||||||
|
u64* const FastBlockLookupRegions[ARMJIT_Memory::memregions_Count] =
|
||||||
|
{
|
||||||
|
NULL,
|
||||||
|
FastBlockLookupITCM,
|
||||||
|
NULL,
|
||||||
|
FastBlockLookupARM9BIOS,
|
||||||
|
FastBlockLookupMainRAM,
|
||||||
|
FastBlockLookupSWRAM,
|
||||||
|
NULL,
|
||||||
|
FastBlockLookupVRAM,
|
||||||
|
FastBlockLookupARM7BIOS,
|
||||||
|
FastBlockLookupARM7WRAM,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
FastBlockLookupARM7WVRAM,
|
||||||
|
FastBlockLookupBIOS9DSi,
|
||||||
|
FastBlockLookupBIOS7DSi,
|
||||||
|
FastBlockLookupNWRAM_A,
|
||||||
|
FastBlockLookupNWRAM_B,
|
||||||
|
FastBlockLookupNWRAM_C
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void ARM_Dispatch(ARM* cpu, ARMJIT::JitBlockEntry entry);
|
// Defined in assembly
|
||||||
|
extern "C" void ARM_Dispatch(melonDS::ARM* cpu, melonDS::JitBlockEntry entry);
|
||||||
|
#else
|
||||||
|
namespace melonDS
|
||||||
|
{
|
||||||
|
class ARM;
|
||||||
|
|
||||||
|
// This version is a stub; the methods all do nothing,
|
||||||
|
// but there's still a Memory member.
|
||||||
|
class ARMJIT
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ARMJIT(melonDS::NDS& nds, std::optional<JITArgs>) noexcept : Memory(nds) {}
|
||||||
|
~ARMJIT() noexcept {}
|
||||||
|
void InvalidateByAddr(u32) noexcept {}
|
||||||
|
void CheckAndInvalidateWVRAM(int) noexcept {}
|
||||||
|
void CheckAndInvalidateITCM() noexcept {}
|
||||||
|
void Reset() noexcept {}
|
||||||
|
void JitEnableWrite() noexcept {}
|
||||||
|
void JitEnableExecute() noexcept {}
|
||||||
|
void CompileBlock(ARM*) noexcept {}
|
||||||
|
void ResetBlockCache() noexcept {}
|
||||||
|
template <u32, int>
|
||||||
|
void CheckAndInvalidate(u32 addr) noexcept {}
|
||||||
|
|
||||||
|
ARMJIT_Memory Memory;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // JIT_ENABLED
|
||||||
|
|
||||||
|
#endif // ARMJIT_H
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,8 +1,26 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team, RSDuck
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "ARMJIT_Compiler.h"
|
#include "ARMJIT_Compiler.h"
|
||||||
|
|
||||||
using namespace Arm64Gen;
|
using namespace Arm64Gen;
|
||||||
|
|
||||||
namespace ARMJIT
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
|
||||||
void Compiler::Comp_RegShiftReg(int op, bool S, Op2& op2, ARM64Reg rs)
|
void Compiler::Comp_RegShiftReg(int op, bool S, Op2& op2, ARM64Reg rs)
|
||||||
|
@ -306,16 +324,29 @@ void Compiler::Comp_Arithmetic(int op, bool S, ARM64Reg rd, ARM64Reg rn, Op2 op2
|
||||||
UBFX(W2, RCPSR, 29, 1);
|
UBFX(W2, RCPSR, 29, 1);
|
||||||
if (S)
|
if (S)
|
||||||
{
|
{
|
||||||
CVInGPR = true;
|
|
||||||
ADDS(W1, rn, W2);
|
|
||||||
CSET(W2, CC_CS);
|
|
||||||
CSET(W3, CC_VS);
|
|
||||||
if (op2.IsImm)
|
if (op2.IsImm)
|
||||||
ADDSI2R(rd, W1, op2.Imm, W0);
|
{
|
||||||
|
CVInGPR = true;
|
||||||
|
ADDS(W1, rn, W2);
|
||||||
|
CSET(W2, CC_CS);
|
||||||
|
CSET(W3, CC_VS);
|
||||||
|
if (op2.IsImm)
|
||||||
|
ADDSI2R(rd, W1, op2.Imm, W0);
|
||||||
|
else
|
||||||
|
ADDS(rd, W1, op2.Reg.Rm, op2.ToArithOption());
|
||||||
|
CSINC(W2, W2, WZR, CC_CC);
|
||||||
|
CSINC(W3, W3, WZR, CC_VC);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ADDS(rd, W1, op2.Reg.Rm, op2.ToArithOption());
|
{
|
||||||
CSINC(W2, W2, WZR, CC_CC);
|
if (op2.Reg.ShiftAmount > 0)
|
||||||
CSINC(W3, W3, WZR, CC_VC);
|
{
|
||||||
|
MOV(W0, op2.Reg.Rm, op2.ToArithOption());
|
||||||
|
op2 = Op2(W0, ST_LSL, 0);
|
||||||
|
}
|
||||||
|
CMP(W2, 1);
|
||||||
|
ADCS(rd, rn, op2.Reg.Rm);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -328,25 +359,38 @@ void Compiler::Comp_Arithmetic(int op, bool S, ARM64Reg rd, ARM64Reg rn, Op2 op2
|
||||||
break;
|
break;
|
||||||
case 0x6: // SBC
|
case 0x6: // SBC
|
||||||
UBFX(W2, RCPSR, 29, 1);
|
UBFX(W2, RCPSR, 29, 1);
|
||||||
// W1 = -op2 - 1
|
if (S && !op2.IsImm)
|
||||||
if (op2.IsImm)
|
|
||||||
MOVI2R(W1, ~op2.Imm);
|
|
||||||
else
|
|
||||||
ORN(W1, WZR, op2.Reg.Rm, op2.ToArithOption());
|
|
||||||
if (S)
|
|
||||||
{
|
{
|
||||||
CVInGPR = true;
|
if (op2.Reg.ShiftAmount > 0)
|
||||||
ADDS(W1, W2, W1);
|
{
|
||||||
CSET(W2, CC_CS);
|
MOV(W0, op2.Reg.Rm, op2.ToArithOption());
|
||||||
CSET(W3, CC_VS);
|
op2 = Op2(W0, ST_LSL, 0);
|
||||||
ADDS(rd, rn, W1);
|
}
|
||||||
CSINC(W2, W2, WZR, CC_CC);
|
CMP(W2, 1);
|
||||||
CSINC(W3, W3, WZR, CC_VC);
|
SBCS(rd, rn, op2.Reg.Rm);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ADD(W1, W2, W1);
|
// W1 = -op2 - 1
|
||||||
ADD(rd, rn, W1);
|
if (op2.IsImm)
|
||||||
|
MOVI2R(W1, ~op2.Imm);
|
||||||
|
else
|
||||||
|
ORN(W1, WZR, op2.Reg.Rm, op2.ToArithOption());
|
||||||
|
if (S)
|
||||||
|
{
|
||||||
|
CVInGPR = true;
|
||||||
|
ADDS(W1, W2, W1);
|
||||||
|
CSET(W2, CC_CS);
|
||||||
|
CSET(W3, CC_VS);
|
||||||
|
ADDS(rd, rn, W1);
|
||||||
|
CSINC(W2, W2, WZR, CC_CC);
|
||||||
|
CSINC(W3, W3, WZR, CC_VC);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ADD(W1, W2, W1);
|
||||||
|
ADD(rd, rn, W1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x7: // RSC
|
case 0x7: // RSC
|
||||||
|
@ -436,7 +480,7 @@ void Compiler::A_Comp_GetOp2(bool S, Op2& op2)
|
||||||
Comp_AddCycles_C();
|
Comp_AddCycles_C();
|
||||||
|
|
||||||
u32 shift = (CurInstr.Instr >> 7) & 0x1E;
|
u32 shift = (CurInstr.Instr >> 7) & 0x1E;
|
||||||
u32 imm = ::ROR(CurInstr.Instr & 0xFF, shift);
|
u32 imm = melonDS::ROR(CurInstr.Instr & 0xFF, shift);
|
||||||
|
|
||||||
if (S && shift && (CurInstr.SetFlags & 0x2))
|
if (S && shift && (CurInstr.SetFlags & 0x2))
|
||||||
{
|
{
|
||||||
|
@ -515,21 +559,7 @@ void Compiler::A_Comp_ALUMovOp()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// ORR with shifted operand has cycles latency
|
MOV(rd, op2.Reg.Rm, op2.ToArithOption());
|
||||||
if (op2.Reg.ShiftAmount > 0)
|
|
||||||
{
|
|
||||||
switch (op2.Reg.ShiftType)
|
|
||||||
{
|
|
||||||
case ST_LSL: LSL(rd, op2.Reg.Rm, op2.Reg.ShiftAmount); break;
|
|
||||||
case ST_LSR: LSR(rd, op2.Reg.Rm, op2.Reg.ShiftAmount); break;
|
|
||||||
case ST_ASR: ASR(rd, op2.Reg.Rm, op2.Reg.ShiftAmount); break;
|
|
||||||
case ST_ROR: ROR(rd, op2.Reg.Rm, op2.Reg.ShiftAmount); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MOV(rd, op2.Reg.Rm, op2.ToArithOption());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,34 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team, RSDuck
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "ARMJIT_Compiler.h"
|
#include "ARMJIT_Compiler.h"
|
||||||
|
#include "../NDS.h"
|
||||||
|
|
||||||
using namespace Arm64Gen;
|
using namespace Arm64Gen;
|
||||||
|
|
||||||
// hack
|
// hack
|
||||||
const int kCodeCacheTiming = 3;
|
const int kCodeCacheTiming = 3;
|
||||||
|
|
||||||
namespace ARMJIT
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void jumpToTrampoline(T* cpu, u32 addr, bool changeCPSR)
|
void JumpToTrampoline(T* cpu, u32 addr, bool changeCPSR)
|
||||||
{
|
{
|
||||||
cpu->JumpTo(addr, changeCPSR);
|
cpu->JumpTo(addr, changeCPSR);
|
||||||
}
|
}
|
||||||
|
@ -64,7 +83,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
|
||||||
// doesn't matter if we put garbage in the MSbs there
|
// doesn't matter if we put garbage in the MSbs there
|
||||||
if (addr & 0x2)
|
if (addr & 0x2)
|
||||||
{
|
{
|
||||||
cpu9->CodeRead32(addr-2, true) >> 16;
|
cpu9->CodeRead32(addr-2, true);
|
||||||
cycles += cpu9->CodeCycles;
|
cycles += cpu9->CodeCycles;
|
||||||
cpu9->CodeRead32(addr+2, false);
|
cpu9->CodeRead32(addr+2, false);
|
||||||
cycles += CurCPU->CodeCycles;
|
cycles += CurCPU->CodeCycles;
|
||||||
|
@ -114,7 +133,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
|
||||||
u32 compileTimePC = CurCPU->R[15];
|
u32 compileTimePC = CurCPU->R[15];
|
||||||
CurCPU->R[15] = newPC;
|
CurCPU->R[15] = newPC;
|
||||||
|
|
||||||
cycles += NDS::ARM7MemTimings[codeCycles][0] + NDS::ARM7MemTimings[codeCycles][1];
|
cycles += NDS.ARM7MemTimings[codeCycles][0] + NDS.ARM7MemTimings[codeCycles][1];
|
||||||
|
|
||||||
CurCPU->R[15] = compileTimePC;
|
CurCPU->R[15] = compileTimePC;
|
||||||
}
|
}
|
||||||
|
@ -126,7 +145,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
|
||||||
u32 compileTimePC = CurCPU->R[15];
|
u32 compileTimePC = CurCPU->R[15];
|
||||||
CurCPU->R[15] = newPC;
|
CurCPU->R[15] = newPC;
|
||||||
|
|
||||||
cycles += NDS::ARM7MemTimings[codeCycles][2] + NDS::ARM7MemTimings[codeCycles][3];
|
cycles += NDS.ARM7MemTimings[codeCycles][2] + NDS.ARM7MemTimings[codeCycles][3];
|
||||||
|
|
||||||
CurCPU->R[15] = compileTimePC;
|
CurCPU->R[15] = compileTimePC;
|
||||||
}
|
}
|
||||||
|
@ -217,7 +236,7 @@ void* Compiler::Gen_JumpTo7(int kind)
|
||||||
LSR(W1, W0, 15);
|
LSR(W1, W0, 15);
|
||||||
STR(INDEX_UNSIGNED, W1, RCPU, offsetof(ARM, CodeCycles));
|
STR(INDEX_UNSIGNED, W1, RCPU, offsetof(ARM, CodeCycles));
|
||||||
|
|
||||||
MOVP2R(X2, NDS::ARM7MemTimings);
|
MOVP2R(X2, NDS.ARM7MemTimings);
|
||||||
LDR(W3, X2, ArithOption(W1, true));
|
LDR(W3, X2, ArithOption(W1, true));
|
||||||
|
|
||||||
FixupBranch switchToThumb;
|
FixupBranch switchToThumb;
|
||||||
|
@ -283,7 +302,7 @@ void Compiler::Comp_JumpTo(Arm64Gen::ARM64Reg addr, bool switchThumb, bool resto
|
||||||
bool cpsrDirty = CPSRDirty;
|
bool cpsrDirty = CPSRDirty;
|
||||||
SaveCPSR();
|
SaveCPSR();
|
||||||
SaveCycles();
|
SaveCycles();
|
||||||
PushRegs(restoreCPSR);
|
PushRegs(restoreCPSR, true);
|
||||||
|
|
||||||
if (switchThumb)
|
if (switchThumb)
|
||||||
MOV(W1, addr);
|
MOV(W1, addr);
|
||||||
|
@ -297,11 +316,11 @@ void Compiler::Comp_JumpTo(Arm64Gen::ARM64Reg addr, bool switchThumb, bool resto
|
||||||
MOV(X0, RCPU);
|
MOV(X0, RCPU);
|
||||||
MOVI2R(W2, restoreCPSR);
|
MOVI2R(W2, restoreCPSR);
|
||||||
if (Num == 0)
|
if (Num == 0)
|
||||||
QuickCallFunction(X3, jumpToTrampoline<ARMv5>);
|
QuickCallFunction(X3, JumpToTrampoline<ARMv5>);
|
||||||
else
|
else
|
||||||
QuickCallFunction(X3, jumpToTrampoline<ARMv4>);
|
QuickCallFunction(X3, JumpToTrampoline<ARMv4>);
|
||||||
|
|
||||||
PopRegs(restoreCPSR);
|
PopRegs(restoreCPSR, true);
|
||||||
LoadCycles();
|
LoadCycles();
|
||||||
LoadCPSR();
|
LoadCPSR();
|
||||||
if (CurInstr.Cond() < 0xE)
|
if (CurInstr.Cond() < 0xE)
|
||||||
|
@ -370,7 +389,7 @@ void Compiler::T_Comp_BranchXchangeReg()
|
||||||
{
|
{
|
||||||
if (Num == 1)
|
if (Num == 1)
|
||||||
{
|
{
|
||||||
printf("BLX unsupported on ARM7!!!\n");
|
Log(LogLevel::Warn, "BLX unsupported on ARM7!!!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MOV(W0, MapReg(CurInstr.A_Reg(3)));
|
MOV(W0, MapReg(CurInstr.A_Reg(3)));
|
||||||
|
@ -418,4 +437,4 @@ void Compiler::T_Comp_BL_Merged()
|
||||||
Comp_JumpTo(target);
|
Comp_JumpTo(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,28 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team, RSDuck
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "ARMJIT_Compiler.h"
|
#include "ARMJIT_Compiler.h"
|
||||||
|
|
||||||
#include "../ARMJIT_Internal.h"
|
#include "../ARMJIT_Internal.h"
|
||||||
#include "../ARMInterpreter.h"
|
#include "../ARMInterpreter.h"
|
||||||
#include "../Config.h"
|
#include "../ARMJIT.h"
|
||||||
|
#include "../NDS.h"
|
||||||
#ifdef __SWITCH__
|
#include "../ARMJIT_Global.h"
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
extern char __start__;
|
|
||||||
#else
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
@ -19,7 +30,7 @@ using namespace Arm64Gen;
|
||||||
|
|
||||||
extern "C" void ARM_Ret();
|
extern "C" void ARM_Ret();
|
||||||
|
|
||||||
namespace ARMJIT
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -36,14 +47,14 @@ namespace ARMJIT
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
const ARM64Reg RegisterCache<Compiler, ARM64Reg>::NativeRegAllocOrder[] =
|
const ARM64Reg RegisterCache<Compiler, ARM64Reg>::NativeRegAllocOrder[] =
|
||||||
{W19, W20, W21, W22, W23, W24, W25, W26};
|
{
|
||||||
|
W19, W20, W21, W22, W23, W24, W25,
|
||||||
|
W8, W9, W10, W11, W12, W13, W14, W15
|
||||||
|
};
|
||||||
template <>
|
template <>
|
||||||
const int RegisterCache<Compiler, ARM64Reg>::NativeRegsAvailable = 8;
|
const int RegisterCache<Compiler, ARM64Reg>::NativeRegsAvailable = 15;
|
||||||
|
|
||||||
const int JitMemSize = 16 * 1024 * 1024;
|
const BitSet32 CallerSavedPushRegs({W8, W9, W10, W11, W12, W13, W14, W15});
|
||||||
#ifndef __SWITCH__
|
|
||||||
u8 JitMem[JitMemSize];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void Compiler::MovePC()
|
void Compiler::MovePC()
|
||||||
{
|
{
|
||||||
|
@ -81,7 +92,7 @@ void Compiler::A_Comp_MSR()
|
||||||
if (CurInstr.Instr & (1 << 25))
|
if (CurInstr.Instr & (1 << 25))
|
||||||
{
|
{
|
||||||
val = W0;
|
val = W0;
|
||||||
MOVI2R(val, ::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)));
|
MOVI2R(val, melonDS::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -142,52 +153,67 @@ void Compiler::A_Comp_MSR()
|
||||||
MOV(W2, RCPSR);
|
MOV(W2, RCPSR);
|
||||||
MOV(X0, RCPU);
|
MOV(X0, RCPU);
|
||||||
|
|
||||||
PushRegs(true);
|
PushRegs(true, true);
|
||||||
|
QuickCallFunction(X3, UpdateModeTrampoline);
|
||||||
QuickCallFunction(X3, (void*)&UpdateModeTrampoline);
|
PopRegs(true, true);
|
||||||
|
|
||||||
PopRegs(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::PushRegs(bool saveHiRegs)
|
|
||||||
|
void Compiler::PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload)
|
||||||
{
|
{
|
||||||
|
BitSet32 loadedRegs(RegCache.LoadedRegs);
|
||||||
|
|
||||||
if (saveHiRegs)
|
if (saveHiRegs)
|
||||||
{
|
{
|
||||||
if (Thumb || CurInstr.Cond() == 0xE)
|
BitSet32 hiRegsLoaded(RegCache.LoadedRegs & 0x7F00);
|
||||||
|
for (int reg : hiRegsLoaded)
|
||||||
{
|
{
|
||||||
BitSet16 hiRegsLoaded(RegCache.LoadedRegs & 0x7F00);
|
if (Thumb || CurInstr.Cond() == 0xE)
|
||||||
for (int reg : hiRegsLoaded)
|
|
||||||
RegCache.UnloadRegister(reg);
|
RegCache.UnloadRegister(reg);
|
||||||
|
else
|
||||||
|
SaveReg(reg, RegCache.Mapping[reg]);
|
||||||
|
// prevent saving the register twice
|
||||||
|
loadedRegs[reg] = false;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
|
||||||
|
for (int reg : loadedRegs)
|
||||||
|
{
|
||||||
|
if (CallerSavedPushRegs[RegCache.Mapping[reg]]
|
||||||
|
&& (saveRegsToBeChanged || !((1<<reg) & CurInstr.Info.DstRegs && !((1<<reg) & CurInstr.Info.SrcRegs))))
|
||||||
{
|
{
|
||||||
BitSet16 hiRegsDirty(RegCache.LoadedRegs & 0x7F00);
|
if ((Thumb || CurInstr.Cond() == 0xE) && !((1 << reg) & (CurInstr.Info.DstRegs|CurInstr.Info.SrcRegs)) && allowUnload)
|
||||||
for (int reg : hiRegsDirty)
|
RegCache.UnloadRegister(reg);
|
||||||
|
else
|
||||||
SaveReg(reg, RegCache.Mapping[reg]);
|
SaveReg(reg, RegCache.Mapping[reg]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::PopRegs(bool saveHiRegs)
|
void Compiler::PopRegs(bool saveHiRegs, bool saveRegsToBeChanged)
|
||||||
{
|
{
|
||||||
if (saveHiRegs)
|
BitSet32 loadedRegs(RegCache.LoadedRegs);
|
||||||
|
for (int reg : loadedRegs)
|
||||||
{
|
{
|
||||||
BitSet16 hiRegsLoaded(RegCache.LoadedRegs & 0x7F00);
|
if ((saveHiRegs && reg >= 8 && reg < 15)
|
||||||
|
|| (CallerSavedPushRegs[RegCache.Mapping[reg]]
|
||||||
for (int reg : hiRegsLoaded)
|
&& (saveRegsToBeChanged || !((1<<reg) & CurInstr.Info.DstRegs && !((1<<reg) & CurInstr.Info.SrcRegs)))))
|
||||||
|
{
|
||||||
LoadReg(reg, RegCache.Mapping[reg]);
|
LoadReg(reg, RegCache.Mapping[reg]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Compiler::Compiler()
|
Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
|
||||||
{
|
{
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
JitRWBase = aligned_alloc(0x1000, JitMemSize);
|
JitRWBase = aligned_alloc(0x1000, JitMemSize);
|
||||||
|
|
||||||
JitRXStart = (u8*)&__start__ - JitMemSize - 0x1000;
|
JitRXStart = (u8*)&__start__ - JitMemSize - 0x1000;
|
||||||
JitRWStart = virtmemReserve(JitMemSize);
|
virtmemLock();
|
||||||
|
JitRWStart = virtmemFindAslr(JitMemSize, 0x1000);
|
||||||
MemoryInfo info = {0};
|
MemoryInfo info = {0};
|
||||||
u32 pageInfo = {0};
|
u32 pageInfo = {0};
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -200,7 +226,7 @@ Compiler::Compiler()
|
||||||
break;
|
break;
|
||||||
if (i++ > 8)
|
if (i++ > 8)
|
||||||
{
|
{
|
||||||
printf("couldn't find unmapped place for jit memory\n");
|
Log(LogLevel::Error, "couldn't find unmapped place for jit memory\n");
|
||||||
JitRXStart = NULL;
|
JitRXStart = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,16 +240,18 @@ Compiler::Compiler()
|
||||||
succeded = R_SUCCEEDED(svcMapProcessMemory(JitRWStart, envGetOwnProcessHandle(), (u64)JitRXStart, JitMemSize));
|
succeded = R_SUCCEEDED(svcMapProcessMemory(JitRWStart, envGetOwnProcessHandle(), (u64)JitRXStart, JitMemSize));
|
||||||
assert(succeded);
|
assert(succeded);
|
||||||
|
|
||||||
|
virtmemUnlock();
|
||||||
|
|
||||||
SetCodeBase((u8*)JitRWStart, (u8*)JitRXStart);
|
SetCodeBase((u8*)JitRWStart, (u8*)JitRXStart);
|
||||||
JitMemMainSize = JitMemSize;
|
JitMemMainSize = JitMemSize;
|
||||||
#else
|
#else
|
||||||
u64 pageSize = sysconf(_SC_PAGE_SIZE);
|
ARMJIT_Global::Init();
|
||||||
u8* pageAligned = (u8*)(((u64)JitMem & ~(pageSize - 1)) + pageSize);
|
|
||||||
u64 alignedSize = (((u64)JitMem + sizeof(JitMem)) & ~(pageSize - 1)) - (u64)pageAligned;
|
|
||||||
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
|
|
||||||
|
|
||||||
SetCodeBase(pageAligned, pageAligned);
|
CodeMemBase = ARMJIT_Global::AllocateCodeMem();
|
||||||
JitMemMainSize = alignedSize;
|
nds.JIT.JitEnableWrite();
|
||||||
|
|
||||||
|
SetCodeBase(reinterpret_cast<u8*>(CodeMemBase), reinterpret_cast<u8*>(CodeMemBase));
|
||||||
|
JitMemMainSize = ARMJIT_Global::CodeMemorySliceSize;
|
||||||
#endif
|
#endif
|
||||||
SetCodePtr(0);
|
SetCodePtr(0);
|
||||||
|
|
||||||
|
@ -234,6 +262,7 @@ Compiler::Compiler()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
W4 - whether the register was written to
|
||||||
W5 - mode
|
W5 - mode
|
||||||
W1 - reg num
|
W1 - reg num
|
||||||
W3 - in/out value of reg
|
W3 - in/out value of reg
|
||||||
|
@ -323,9 +352,11 @@ Compiler::Compiler()
|
||||||
{
|
{
|
||||||
for (int size = 0; size < 3; size++)
|
for (int size = 0; size < 3; size++)
|
||||||
{
|
{
|
||||||
for (int reg = 0; reg < 8; reg++)
|
for (int reg = 0; reg < 32; reg++)
|
||||||
{
|
{
|
||||||
ARM64Reg rdMapped = (ARM64Reg)(W19 + reg);
|
if (!(reg == W4 || (reg >= W8 && reg <= W15) || (reg >= W19 && reg <= W25)))
|
||||||
|
continue;
|
||||||
|
ARM64Reg rdMapped = (ARM64Reg)reg;
|
||||||
PatchedStoreFuncs[consoleType][num][size][reg] = GetRXPtr();
|
PatchedStoreFuncs[consoleType][num][size][reg] = GetRXPtr();
|
||||||
if (num == 0)
|
if (num == 0)
|
||||||
{
|
{
|
||||||
|
@ -336,7 +367,7 @@ Compiler::Compiler()
|
||||||
{
|
{
|
||||||
MOV(W1, rdMapped);
|
MOV(W1, rdMapped);
|
||||||
}
|
}
|
||||||
ABI_PushRegisters({30});
|
ABI_PushRegisters(BitSet32({30}) | CallerSavedPushRegs);
|
||||||
if (consoleType == 0)
|
if (consoleType == 0)
|
||||||
{
|
{
|
||||||
switch ((8 << size) | num)
|
switch ((8 << size) | num)
|
||||||
|
@ -362,7 +393,7 @@ Compiler::Compiler()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ABI_PopRegisters({30});
|
ABI_PopRegisters(BitSet32({30}) | CallerSavedPushRegs);
|
||||||
RET();
|
RET();
|
||||||
|
|
||||||
for (int signextend = 0; signextend < 2; signextend++)
|
for (int signextend = 0; signextend < 2; signextend++)
|
||||||
|
@ -370,7 +401,7 @@ Compiler::Compiler()
|
||||||
PatchedLoadFuncs[consoleType][num][size][signextend][reg] = GetRXPtr();
|
PatchedLoadFuncs[consoleType][num][size][signextend][reg] = GetRXPtr();
|
||||||
if (num == 0)
|
if (num == 0)
|
||||||
MOV(X1, RCPU);
|
MOV(X1, RCPU);
|
||||||
ABI_PushRegisters({30});
|
ABI_PushRegisters(BitSet32({30}) | CallerSavedPushRegs);
|
||||||
if (consoleType == 0)
|
if (consoleType == 0)
|
||||||
{
|
{
|
||||||
switch ((8 << size) | num)
|
switch ((8 << size) | num)
|
||||||
|
@ -395,7 +426,7 @@ Compiler::Compiler()
|
||||||
case 9: QuickCallFunction(X3, SlowRead7<u8, 1>); break;
|
case 9: QuickCallFunction(X3, SlowRead7<u8, 1>); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ABI_PopRegisters({30});
|
ABI_PopRegisters(BitSet32({30}) | CallerSavedPushRegs);
|
||||||
if (size == 32)
|
if (size == 32)
|
||||||
MOV(rdMapped, W0);
|
MOV(rdMapped, W0);
|
||||||
else if (signextend)
|
else if (signextend)
|
||||||
|
@ -426,12 +457,14 @@ Compiler::~Compiler()
|
||||||
{
|
{
|
||||||
bool succeded = R_SUCCEEDED(svcUnmapProcessMemory(JitRWStart, envGetOwnProcessHandle(), (u64)JitRXStart, JitMemSize));
|
bool succeded = R_SUCCEEDED(svcUnmapProcessMemory(JitRWStart, envGetOwnProcessHandle(), (u64)JitRXStart, JitMemSize));
|
||||||
assert(succeded);
|
assert(succeded);
|
||||||
virtmemFree(JitRWStart, JitMemSize);
|
|
||||||
succeded = R_SUCCEEDED(svcUnmapProcessCodeMemory(envGetOwnProcessHandle(), (u64)JitRXStart, (u64)JitRWBase, JitMemSize));
|
succeded = R_SUCCEEDED(svcUnmapProcessCodeMemory(envGetOwnProcessHandle(), (u64)JitRXStart, (u64)JitRWBase, JitMemSize));
|
||||||
assert(succeded);
|
assert(succeded);
|
||||||
free(JitRWBase);
|
free(JitRWBase);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ARMJIT_Global::FreeCodeMem(CodeMemBase);
|
||||||
|
ARMJIT_Global::DeInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::LoadCycles()
|
void Compiler::LoadCycles()
|
||||||
|
@ -449,12 +482,12 @@ void Compiler::LoadReg(int reg, ARM64Reg nativeReg)
|
||||||
if (reg == 15)
|
if (reg == 15)
|
||||||
MOVI2R(nativeReg, R15);
|
MOVI2R(nativeReg, R15);
|
||||||
else
|
else
|
||||||
LDR(INDEX_UNSIGNED, nativeReg, RCPU, offsetof(ARM, R[reg]));
|
LDR(INDEX_UNSIGNED, nativeReg, RCPU, offsetof(ARM, R) + reg*4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::SaveReg(int reg, ARM64Reg nativeReg)
|
void Compiler::SaveReg(int reg, ARM64Reg nativeReg)
|
||||||
{
|
{
|
||||||
STR(INDEX_UNSIGNED, nativeReg, RCPU, offsetof(ARM, R[reg]));
|
STR(INDEX_UNSIGNED, nativeReg, RCPU, offsetof(ARM, R) + reg*4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::LoadCPSR()
|
void Compiler::LoadCPSR()
|
||||||
|
@ -634,22 +667,23 @@ void Compiler::Comp_BranchSpecialBehaviour(bool taken)
|
||||||
{
|
{
|
||||||
RegCache.PrepareExit();
|
RegCache.PrepareExit();
|
||||||
|
|
||||||
ADD(RCycles, RCycles, ConstantCycles);
|
if (ConstantCycles)
|
||||||
|
ADD(RCycles, RCycles, ConstantCycles);
|
||||||
QuickTailCall(X0, ARM_Ret);
|
QuickTailCall(X0, ARM_Ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount)
|
JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount, bool hasMemInstr)
|
||||||
{
|
{
|
||||||
if (JitMemMainSize - GetCodeOffset() < 1024 * 16)
|
if (JitMemMainSize - GetCodeOffset() < 1024 * 16)
|
||||||
{
|
{
|
||||||
printf("JIT near memory full, resetting...\n");
|
Log(LogLevel::Debug, "JIT near memory full, resetting...\n");
|
||||||
ResetBlockCache();
|
NDS.JIT.ResetBlockCache();
|
||||||
}
|
}
|
||||||
if ((JitMemMainSize + JitMemSecondarySize) - OtherCodeRegion < 1024 * 8)
|
if ((JitMemMainSize + JitMemSecondarySize) - OtherCodeRegion < 1024 * 8)
|
||||||
{
|
{
|
||||||
printf("JIT far memory full, resetting...\n");
|
Log(LogLevel::Debug, "JIT far memory full, resetting...\n");
|
||||||
ResetBlockCache();
|
NDS.JIT.ResetBlockCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
JitBlockEntry res = (JitBlockEntry)GetRXPtr();
|
JitBlockEntry res = (JitBlockEntry)GetRXPtr();
|
||||||
|
@ -661,6 +695,9 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
||||||
RegCache = RegisterCache<Compiler, ARM64Reg>(this, instrs, instrsCount, true);
|
RegCache = RegisterCache<Compiler, ARM64Reg>(this, instrs, instrsCount, true);
|
||||||
CPSRDirty = false;
|
CPSRDirty = false;
|
||||||
|
|
||||||
|
if (hasMemInstr)
|
||||||
|
MOVP2R(RMemBase, Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start);
|
||||||
|
|
||||||
for (int i = 0; i < instrsCount; i++)
|
for (int i = 0; i < instrsCount; i++)
|
||||||
{
|
{
|
||||||
CurInstr = instrs[i];
|
CurInstr = instrs[i];
|
||||||
|
@ -709,7 +746,9 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
||||||
QuickCallFunction(X1, InterpretTHUMB[CurInstr.Info.Kind]);
|
QuickCallFunction(X1, InterpretTHUMB[CurInstr.Info.Kind]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
(this->*comp)();
|
(this->*comp)();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -725,10 +764,12 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (cond == 0xF)
|
else if (cond == 0xF)
|
||||||
|
{
|
||||||
Comp_AddCycles_C();
|
Comp_AddCycles_C();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IrregularCycles = false;
|
IrregularCycles = comp == NULL;
|
||||||
|
|
||||||
FixupBranch skipExecute;
|
FixupBranch skipExecute;
|
||||||
if (cond < 0xE)
|
if (cond < 0xE)
|
||||||
|
@ -753,14 +794,17 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
||||||
FixupBranch skipNop = B();
|
FixupBranch skipNop = B();
|
||||||
SetJumpTarget(skipExecute);
|
SetJumpTarget(skipExecute);
|
||||||
|
|
||||||
Comp_AddCycles_C();
|
if (IrregularCycles)
|
||||||
|
Comp_AddCycles_C(true);
|
||||||
|
|
||||||
Comp_BranchSpecialBehaviour(false);
|
Comp_BranchSpecialBehaviour(false);
|
||||||
|
|
||||||
SetJumpTarget(skipNop);
|
SetJumpTarget(skipNop);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
SetJumpTarget(skipExecute);
|
SetJumpTarget(skipExecute);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -775,7 +819,8 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
||||||
|
|
||||||
RegCache.Flush();
|
RegCache.Flush();
|
||||||
|
|
||||||
ADD(RCycles, RCycles, ConstantCycles);
|
if (ConstantCycles)
|
||||||
|
ADD(RCycles, RCycles, ConstantCycles);
|
||||||
QuickTailCall(X0, ARM_Ret);
|
QuickTailCall(X0, ARM_Ret);
|
||||||
|
|
||||||
FlushIcache();
|
FlushIcache();
|
||||||
|
@ -799,7 +844,7 @@ void Compiler::Reset()
|
||||||
void Compiler::Comp_AddCycles_C(bool forceNonConstant)
|
void Compiler::Comp_AddCycles_C(bool forceNonConstant)
|
||||||
{
|
{
|
||||||
s32 cycles = Num ?
|
s32 cycles = Num ?
|
||||||
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3]
|
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3]
|
||||||
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles);
|
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles);
|
||||||
|
|
||||||
if (forceNonConstant)
|
if (forceNonConstant)
|
||||||
|
@ -813,7 +858,7 @@ void Compiler::Comp_AddCycles_CI(u32 numI)
|
||||||
IrregularCycles = true;
|
IrregularCycles = true;
|
||||||
|
|
||||||
s32 cycles = (Num ?
|
s32 cycles = (Num ?
|
||||||
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
|
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
|
||||||
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + numI;
|
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + numI;
|
||||||
|
|
||||||
if (Thumb || CurInstr.Cond() == 0xE)
|
if (Thumb || CurInstr.Cond() == 0xE)
|
||||||
|
@ -827,7 +872,7 @@ void Compiler::Comp_AddCycles_CI(u32 c, ARM64Reg numI, ArithOption shift)
|
||||||
IrregularCycles = true;
|
IrregularCycles = true;
|
||||||
|
|
||||||
s32 cycles = (Num ?
|
s32 cycles = (Num ?
|
||||||
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
|
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
|
||||||
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + c;
|
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + c;
|
||||||
|
|
||||||
ADD(RCycles, RCycles, cycles);
|
ADD(RCycles, RCycles, cycles);
|
||||||
|
@ -847,7 +892,7 @@ void Compiler::Comp_AddCycles_CDI()
|
||||||
|
|
||||||
s32 cycles;
|
s32 cycles;
|
||||||
|
|
||||||
s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
|
s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
|
||||||
s32 numD = CurInstr.DataCycles;
|
s32 numD = CurInstr.DataCycles;
|
||||||
|
|
||||||
if ((CurInstr.DataRegion >> 24) == 0x02) // mainRAM
|
if ((CurInstr.DataRegion >> 24) == 0x02) // mainRAM
|
||||||
|
@ -892,7 +937,7 @@ void Compiler::Comp_AddCycles_CD()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
|
s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
|
||||||
s32 numD = CurInstr.DataCycles;
|
s32 numD = CurInstr.DataCycles;
|
||||||
|
|
||||||
if ((CurInstr.DataRegion >> 24) == 0x02)
|
if ((CurInstr.DataRegion >> 24) == 0x02)
|
||||||
|
|
|
@ -1,8 +1,27 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team, RSDuck
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef ARMJIT_A64_COMPILER_H
|
#ifndef ARMJIT_A64_COMPILER_H
|
||||||
#define ARMJIT_A64_COMPILER_H
|
#define ARMJIT_A64_COMPILER_H
|
||||||
|
|
||||||
|
#if defined(JIT_ENABLED) && defined(__aarch64__)
|
||||||
|
|
||||||
#include "../ARM.h"
|
#include "../ARM.h"
|
||||||
#include "../ARMJIT.h"
|
|
||||||
|
|
||||||
#include "../dolphin/Arm64Emitter.h"
|
#include "../dolphin/Arm64Emitter.h"
|
||||||
|
|
||||||
|
@ -11,9 +30,10 @@
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace ARMJIT
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
class ARMJIT;
|
||||||
|
const Arm64Gen::ARM64Reg RMemBase = Arm64Gen::X26;
|
||||||
const Arm64Gen::ARM64Reg RCPSR = Arm64Gen::W27;
|
const Arm64Gen::ARM64Reg RCPSR = Arm64Gen::W27;
|
||||||
const Arm64Gen::ARM64Reg RCycles = Arm64Gen::W28;
|
const Arm64Gen::ARM64Reg RCycles = Arm64Gen::W28;
|
||||||
const Arm64Gen::ARM64Reg RCPU = Arm64Gen::X29;
|
const Arm64Gen::ARM64Reg RCPU = Arm64Gen::X29;
|
||||||
|
@ -49,7 +69,7 @@ struct Op2
|
||||||
bool IsSimpleReg()
|
bool IsSimpleReg()
|
||||||
{ return !IsImm && !Reg.ShiftAmount && Reg.ShiftType == Arm64Gen::ST_LSL; }
|
{ return !IsImm && !Reg.ShiftAmount && Reg.ShiftType == Arm64Gen::ST_LSL; }
|
||||||
bool ImmFits12Bit()
|
bool ImmFits12Bit()
|
||||||
{ return IsImm && (Imm & 0xFFF == Imm); }
|
{ return IsImm && ((Imm & 0xFFF) == Imm); }
|
||||||
bool IsZero()
|
bool IsZero()
|
||||||
{ return IsImm && !Imm; }
|
{ return IsImm && !Imm; }
|
||||||
|
|
||||||
|
@ -78,11 +98,11 @@ class Compiler : public Arm64Gen::ARM64XEmitter
|
||||||
public:
|
public:
|
||||||
typedef void (Compiler::*CompileFunc)();
|
typedef void (Compiler::*CompileFunc)();
|
||||||
|
|
||||||
Compiler();
|
explicit Compiler(melonDS::NDS& nds);
|
||||||
~Compiler();
|
~Compiler() override;
|
||||||
|
|
||||||
void PushRegs(bool saveHiRegs);
|
void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true);
|
||||||
void PopRegs(bool saveHiRegs);
|
void PopRegs(bool saveHiRegs, bool saveRegsToBeChanged);
|
||||||
|
|
||||||
Arm64Gen::ARM64Reg MapReg(int reg)
|
Arm64Gen::ARM64Reg MapReg(int reg)
|
||||||
{
|
{
|
||||||
|
@ -90,11 +110,11 @@ public:
|
||||||
return RegCache.Mapping[reg];
|
return RegCache.Mapping[reg];
|
||||||
}
|
}
|
||||||
|
|
||||||
JitBlockEntry CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount);
|
JitBlockEntry CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount, bool hasMemInstr);
|
||||||
|
|
||||||
bool CanCompile(bool thumb, u16 kind);
|
bool CanCompile(bool thumb, u16 kind);
|
||||||
|
|
||||||
bool FlagsNZNeeded()
|
bool FlagsNZNeeded() const
|
||||||
{
|
{
|
||||||
return CurInstr.SetFlags & 0xC;
|
return CurInstr.SetFlags & 0xC;
|
||||||
}
|
}
|
||||||
|
@ -166,7 +186,7 @@ public:
|
||||||
void T_Comp_BL_LONG_2();
|
void T_Comp_BL_LONG_2();
|
||||||
void T_Comp_BL_Merged();
|
void T_Comp_BL_Merged();
|
||||||
|
|
||||||
s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode);
|
s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn);
|
||||||
|
|
||||||
void Comp_Mul_Mla(bool S, bool mla, Arm64Gen::ARM64Reg rd, Arm64Gen::ARM64Reg rm, Arm64Gen::ARM64Reg rs, Arm64Gen::ARM64Reg rn);
|
void Comp_Mul_Mla(bool S, bool mla, Arm64Gen::ARM64Reg rd, Arm64Gen::ARM64Reg rm, Arm64Gen::ARM64Reg rs, Arm64Gen::ARM64Reg rn);
|
||||||
|
|
||||||
|
@ -214,7 +234,7 @@ public:
|
||||||
return (u8*)entry - GetRXBase();
|
return (u8*)entry - GetRXBase();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsJITFault(u8* pc);
|
bool IsJITFault(const u8* pc);
|
||||||
u8* RewriteMemAccess(u8* pc);
|
u8* RewriteMemAccess(u8* pc);
|
||||||
|
|
||||||
void SwapCodeRegion()
|
void SwapCodeRegion()
|
||||||
|
@ -224,6 +244,7 @@ public:
|
||||||
OtherCodeRegion = offset;
|
OtherCodeRegion = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
melonDS::NDS& NDS;
|
||||||
ptrdiff_t OtherCodeRegion;
|
ptrdiff_t OtherCodeRegion;
|
||||||
|
|
||||||
bool Exit;
|
bool Exit;
|
||||||
|
@ -241,17 +262,8 @@ public:
|
||||||
u32 JitMemSecondarySize;
|
u32 JitMemSecondarySize;
|
||||||
u32 JitMemMainSize;
|
u32 JitMemMainSize;
|
||||||
|
|
||||||
void* ReadBanked, *WriteBanked;
|
|
||||||
|
|
||||||
void* JumpToFuncs9[3];
|
|
||||||
void* JumpToFuncs7[3];
|
|
||||||
|
|
||||||
std::unordered_map<ptrdiff_t, LoadStorePatch> LoadStorePatches;
|
std::unordered_map<ptrdiff_t, LoadStorePatch> LoadStorePatches;
|
||||||
|
|
||||||
// [Console Type][Num][Size][Sign Extend][Output register]
|
|
||||||
void* PatchedLoadFuncs[2][2][3][2][8];
|
|
||||||
void* PatchedStoreFuncs[2][2][3][8];
|
|
||||||
|
|
||||||
RegisterCache<Compiler, Arm64Gen::ARM64Reg> RegCache;
|
RegisterCache<Compiler, Arm64Gen::ARM64Reg> RegCache;
|
||||||
|
|
||||||
bool CPSRDirty = false;
|
bool CPSRDirty = false;
|
||||||
|
@ -263,8 +275,20 @@ public:
|
||||||
void* JitRWStart;
|
void* JitRWStart;
|
||||||
void* JitRXStart;
|
void* JitRXStart;
|
||||||
#endif
|
#endif
|
||||||
|
void* CodeMemBase;
|
||||||
|
|
||||||
|
void* ReadBanked, *WriteBanked;
|
||||||
|
|
||||||
|
void* JumpToFuncs9[3];
|
||||||
|
void* JumpToFuncs7[3];
|
||||||
|
|
||||||
|
// [Console Type][Num][Size][Sign Extend][Output register]
|
||||||
|
void* PatchedLoadFuncs[2][2][3][2][32];
|
||||||
|
void* PatchedStoreFuncs[2][2][3][32];
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,3 +1,21 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team, RSDuck
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "../ARMJIT_x64/ARMJIT_Offsets.h"
|
#include "../ARMJIT_x64/ARMJIT_Offsets.h"
|
||||||
|
|
||||||
.text
|
.text
|
||||||
|
@ -76,3 +94,8 @@ ARM_RestoreContext:
|
||||||
mov sp, x17
|
mov sp, x17
|
||||||
|
|
||||||
br x18
|
br x18
|
||||||
|
|
||||||
|
#if !defined(__APPLE__) && !defined(__WIN32__)
|
||||||
|
.section .note.GNU-stack,"",@progbits
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,34 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team, RSDuck
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "ARMJIT_Compiler.h"
|
#include "ARMJIT_Compiler.h"
|
||||||
|
|
||||||
#include "../Config.h"
|
#include "../ARMJIT.h"
|
||||||
|
|
||||||
#include "../ARMJIT_Memory.h"
|
#include "../ARMJIT_Memory.h"
|
||||||
|
#include "../NDS.h"
|
||||||
|
|
||||||
using namespace Arm64Gen;
|
using namespace Arm64Gen;
|
||||||
|
|
||||||
namespace ARMJIT
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
|
||||||
bool Compiler::IsJITFault(u8* pc)
|
bool Compiler::IsJITFault(const u8* pc)
|
||||||
{
|
{
|
||||||
return (u64)pc >= (u64)GetRXBase() && (u64)pc - (u64)GetRXBase() < (JitMemMainSize + JitMemSecondarySize);
|
return (u64)pc >= (u64)GetRXBase() && (u64)pc - (u64)GetRXBase() < (JitMemMainSize + JitMemSecondarySize);
|
||||||
}
|
}
|
||||||
|
@ -38,18 +57,17 @@ u8* Compiler::RewriteMemAccess(u8* pc)
|
||||||
|
|
||||||
return pc + (ptrdiff_t)patch.PatchOffset;
|
return pc + (ptrdiff_t)patch.PatchOffset;
|
||||||
}
|
}
|
||||||
printf("this is a JIT bug! %08x\n", __builtin_bswap32(*(u32*)pc));
|
Log(LogLevel::Error, "this is a JIT bug! %08x\n", __builtin_bswap32(*(u32*)pc));
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
|
bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
|
||||||
{
|
{
|
||||||
u32 localAddr = LocaliseCodeAddress(Num, addr);
|
u32 localAddr = NDS.JIT.LocaliseCodeAddress(Num, addr);
|
||||||
|
|
||||||
int invalidLiteralIdx = InvalidLiterals.Find(localAddr);
|
int invalidLiteralIdx = NDS.JIT.InvalidLiterals.Find(localAddr);
|
||||||
if (invalidLiteralIdx != -1)
|
if (invalidLiteralIdx != -1)
|
||||||
{
|
{
|
||||||
InvalidLiterals.Remove(invalidLiteralIdx);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +80,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
|
||||||
if (size == 32)
|
if (size == 32)
|
||||||
{
|
{
|
||||||
CurCPU->DataRead32(addr & ~0x3, &val);
|
CurCPU->DataRead32(addr & ~0x3, &val);
|
||||||
val = ::ROR(val, (addr & 0x3) << 3);
|
val = melonDS::ROR(val, (addr & 0x3) << 3);
|
||||||
}
|
}
|
||||||
else if (size == 16)
|
else if (size == 16)
|
||||||
{
|
{
|
||||||
|
@ -94,7 +112,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||||
if (size == 16)
|
if (size == 16)
|
||||||
addressMask = ~1;
|
addressMask = ~1;
|
||||||
|
|
||||||
if (Config::JIT_LiteralOptimisations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
|
if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
|
||||||
{
|
{
|
||||||
u32 addr = R15 + offset.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
|
u32 addr = R15 + offset.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
|
||||||
|
|
||||||
|
@ -116,6 +134,12 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||||
rnMapped = W3;
|
rnMapped = W3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flags & memop_Store && flags & (memop_Post|memop_Writeback) && rd == rn)
|
||||||
|
{
|
||||||
|
MOV(W4, rdMapped);
|
||||||
|
rdMapped = W4;
|
||||||
|
}
|
||||||
|
|
||||||
ARM64Reg finalAddr = W0;
|
ARM64Reg finalAddr = W0;
|
||||||
if (flags & memop_Post)
|
if (flags & memop_Post)
|
||||||
{
|
{
|
||||||
|
@ -123,7 +147,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||||
MOV(W0, rnMapped);
|
MOV(W0, rnMapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool addrIsStatic = Config::JIT_LiteralOptimisations
|
bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled()
|
||||||
&& RegCache.IsLiteral(rn) && offset.IsImm && !(flags & (memop_Writeback|memop_Post));
|
&& RegCache.IsLiteral(rn) && offset.IsImm && !(flags & (memop_Writeback|memop_Post));
|
||||||
u32 staticAddress;
|
u32 staticAddress;
|
||||||
if (addrIsStatic)
|
if (addrIsStatic)
|
||||||
|
@ -162,20 +186,18 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||||
MOV(rnMapped, W0);
|
MOV(rnMapped, W0);
|
||||||
|
|
||||||
u32 expectedTarget = Num == 0
|
u32 expectedTarget = Num == 0
|
||||||
? ARMJIT_Memory::ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion)
|
? NDS.JIT.Memory.ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion)
|
||||||
: ARMJIT_Memory::ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion);
|
: NDS.JIT.Memory.ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion);
|
||||||
|
|
||||||
if (Config::JIT_FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)))
|
if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
|
||||||
{
|
{
|
||||||
ptrdiff_t memopStart = GetCodeOffset();
|
ptrdiff_t memopStart = GetCodeOffset();
|
||||||
LoadStorePatch patch;
|
LoadStorePatch patch;
|
||||||
|
|
||||||
|
assert((rdMapped >= W8 && rdMapped <= W15) || (rdMapped >= W19 && rdMapped <= W25) || rdMapped == W4);
|
||||||
patch.PatchFunc = flags & memop_Store
|
patch.PatchFunc = flags & memop_Store
|
||||||
? PatchedStoreFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped - W19]
|
? PatchedStoreFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped]
|
||||||
: PatchedLoadFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped - W19];
|
: PatchedLoadFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped];
|
||||||
assert(rdMapped - W19 >= 0 && rdMapped - W19 < 8);
|
|
||||||
|
|
||||||
MOVP2R(X7, Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
|
|
||||||
|
|
||||||
// take a chance at fastmem
|
// take a chance at fastmem
|
||||||
if (size > 8)
|
if (size > 8)
|
||||||
|
@ -184,11 +206,11 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||||
ptrdiff_t loadStorePosition = GetCodeOffset();
|
ptrdiff_t loadStorePosition = GetCodeOffset();
|
||||||
if (flags & memop_Store)
|
if (flags & memop_Store)
|
||||||
{
|
{
|
||||||
STRGeneric(size, rdMapped, size > 8 ? X1 : X0, X7);
|
STRGeneric(size, rdMapped, size > 8 ? X1 : X0, RMemBase);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LDRGeneric(size, flags & memop_SignExtend, rdMapped, size > 8 ? X1 : X0, X7);
|
LDRGeneric(size, flags & memop_SignExtend, rdMapped, size > 8 ? X1 : X0, RMemBase);
|
||||||
if (size == 32 && !addrIsStatic)
|
if (size == 32 && !addrIsStatic)
|
||||||
{
|
{
|
||||||
UBFIZ(W0, W0, 3, 2);
|
UBFIZ(W0, W0, 3, 2);
|
||||||
|
@ -204,7 +226,9 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||||
{
|
{
|
||||||
void* func = NULL;
|
void* func = NULL;
|
||||||
if (addrIsStatic)
|
if (addrIsStatic)
|
||||||
func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
|
func = NDS.JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
|
||||||
|
|
||||||
|
PushRegs(false, false);
|
||||||
|
|
||||||
if (func)
|
if (func)
|
||||||
{
|
{
|
||||||
|
@ -212,6 +236,8 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||||
MOV(W1, rdMapped);
|
MOV(W1, rdMapped);
|
||||||
QuickCallFunction(X2, (void (*)())func);
|
QuickCallFunction(X2, (void (*)())func);
|
||||||
|
|
||||||
|
PopRegs(false, false);
|
||||||
|
|
||||||
if (!(flags & memop_Store))
|
if (!(flags & memop_Store))
|
||||||
{
|
{
|
||||||
if (size == 32)
|
if (size == 32)
|
||||||
|
@ -238,7 +264,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||||
if (flags & memop_Store)
|
if (flags & memop_Store)
|
||||||
{
|
{
|
||||||
MOV(W2, rdMapped);
|
MOV(W2, rdMapped);
|
||||||
switch (size | NDS::ConsoleType)
|
switch (size | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 32: QuickCallFunction(X3, SlowWrite9<u32, 0>); break;
|
case 32: QuickCallFunction(X3, SlowWrite9<u32, 0>); break;
|
||||||
case 33: QuickCallFunction(X3, SlowWrite9<u32, 1>); break;
|
case 33: QuickCallFunction(X3, SlowWrite9<u32, 1>); break;
|
||||||
|
@ -250,7 +276,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (size | NDS::ConsoleType)
|
switch (size | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 32: QuickCallFunction(X3, SlowRead9<u32, 0>); break;
|
case 32: QuickCallFunction(X3, SlowRead9<u32, 0>); break;
|
||||||
case 33: QuickCallFunction(X3, SlowRead9<u32, 1>); break;
|
case 33: QuickCallFunction(X3, SlowRead9<u32, 1>); break;
|
||||||
|
@ -266,7 +292,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||||
if (flags & memop_Store)
|
if (flags & memop_Store)
|
||||||
{
|
{
|
||||||
MOV(W1, rdMapped);
|
MOV(W1, rdMapped);
|
||||||
switch (size | NDS::ConsoleType)
|
switch (size | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 32: QuickCallFunction(X3, SlowWrite7<u32, 0>); break;
|
case 32: QuickCallFunction(X3, SlowWrite7<u32, 0>); break;
|
||||||
case 33: QuickCallFunction(X3, SlowWrite7<u32, 1>); break;
|
case 33: QuickCallFunction(X3, SlowWrite7<u32, 1>); break;
|
||||||
|
@ -278,7 +304,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (size | NDS::ConsoleType)
|
switch (size | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 32: QuickCallFunction(X3, SlowRead7<u32, 0>); break;
|
case 32: QuickCallFunction(X3, SlowRead7<u32, 0>); break;
|
||||||
case 33: QuickCallFunction(X3, SlowRead7<u32, 1>); break;
|
case 33: QuickCallFunction(X3, SlowRead7<u32, 1>); break;
|
||||||
|
@ -290,6 +316,8 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PopRegs(false, false);
|
||||||
|
|
||||||
if (!(flags & memop_Store))
|
if (!(flags & memop_Store))
|
||||||
{
|
{
|
||||||
if (size == 32)
|
if (size == 32)
|
||||||
|
@ -305,7 +333,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags)
|
||||||
if (CurInstr.Info.Branches())
|
if (CurInstr.Info.Branches())
|
||||||
{
|
{
|
||||||
if (size < 32)
|
if (size < 32)
|
||||||
printf("LDR size < 32 branching?\n");
|
Log(LogLevel::Debug, "LDR size < 32 branching?\n");
|
||||||
Comp_JumpTo(rdMapped, Num == 0, false);
|
Comp_JumpTo(rdMapped, Num == 0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -425,7 +453,7 @@ void Compiler::T_Comp_LoadPCRel()
|
||||||
u32 offset = ((CurInstr.Instr & 0xFF) << 2);
|
u32 offset = ((CurInstr.Instr & 0xFF) << 2);
|
||||||
u32 addr = (R15 & ~0x2) + offset;
|
u32 addr = (R15 & ~0x2) + offset;
|
||||||
|
|
||||||
if (!Config::JIT_LiteralOptimisations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
|
if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
|
||||||
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
|
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,7 +465,7 @@ void Compiler::T_Comp_MemSPRel()
|
||||||
Comp_MemAccess(CurInstr.T_Reg(8), 13, Op2(offset), 32, load ? 0 : memop_Store);
|
Comp_MemAccess(CurInstr.T_Reg(8), 13, Op2(offset), 32, load ? 0 : memop_Store);
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode)
|
s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn)
|
||||||
{
|
{
|
||||||
IrregularCycles = true;
|
IrregularCycles = true;
|
||||||
|
|
||||||
|
@ -446,7 +474,8 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
if (regsCount == 0)
|
if (regsCount == 0)
|
||||||
return 0; // actually not the right behaviour TODO: fix me
|
return 0; // actually not the right behaviour TODO: fix me
|
||||||
|
|
||||||
if (regsCount == 1 && !usermode && RegCache.LoadedRegs & (1 << *regs.begin()))
|
int firstReg = *regs.begin();
|
||||||
|
if (regsCount == 1 && !usermode && RegCache.LoadedRegs & (1 << firstReg) && !(firstReg == rn && skipLoadingRn))
|
||||||
{
|
{
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
if (store)
|
if (store)
|
||||||
|
@ -455,7 +484,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
flags |= memop_SubtractOffset;
|
flags |= memop_SubtractOffset;
|
||||||
Op2 offset = preinc ? Op2(4) : Op2(0);
|
Op2 offset = preinc ? Op2(4) : Op2(0);
|
||||||
|
|
||||||
Comp_MemAccess(*regs.begin(), rn, offset, 32, flags);
|
Comp_MemAccess(firstReg, rn, offset, 32, flags);
|
||||||
|
|
||||||
return decrement ? -4 : 4;
|
return decrement ? -4 : 4;
|
||||||
}
|
}
|
||||||
|
@ -466,40 +495,32 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
Comp_AddCycles_CDI();
|
Comp_AddCycles_CDI();
|
||||||
|
|
||||||
int expectedTarget = Num == 0
|
int expectedTarget = Num == 0
|
||||||
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
|
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
|
||||||
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
|
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
|
||||||
|
|
||||||
bool compileFastPath = Config::JIT_FastMemory
|
bool compileFastPath = NDS.JIT.FastMemoryEnabled()
|
||||||
&& store && !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget));
|
&& store && !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget));
|
||||||
|
|
||||||
if (decrement)
|
|
||||||
{
|
{
|
||||||
s32 offset = -regsCount * 4 + (preinc ? 0 : 4);
|
s32 offset = decrement
|
||||||
|
? -regsCount * 4 + (preinc ? 0 : 4)
|
||||||
|
: (preinc ? 4 : 0);
|
||||||
|
|
||||||
if (offset)
|
if (offset)
|
||||||
{
|
|
||||||
ADDI2R(W0, MapReg(rn), offset);
|
ADDI2R(W0, MapReg(rn), offset);
|
||||||
ANDI2R(W0, W0, ~3);
|
else if (compileFastPath)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ANDI2R(W0, MapReg(rn), ~3);
|
ANDI2R(W0, MapReg(rn), ~3);
|
||||||
}
|
else
|
||||||
}
|
MOV(W0, MapReg(rn));
|
||||||
else
|
|
||||||
{
|
|
||||||
ANDI2R(W0, MapReg(rn), ~3);
|
|
||||||
if (preinc)
|
|
||||||
ADD(W0, W0, 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u8* patchFunc;
|
u8* patchFunc;
|
||||||
if (compileFastPath)
|
if (compileFastPath)
|
||||||
{
|
{
|
||||||
ptrdiff_t fastPathStart = GetCodeOffset();
|
ptrdiff_t fastPathStart = GetCodeOffset();
|
||||||
ptrdiff_t loadStoreOffsets[16];
|
ptrdiff_t loadStoreOffsets[8];
|
||||||
|
|
||||||
MOVP2R(X1, Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start);
|
ADD(X1, RMemBase, X0);
|
||||||
ADD(X1, X1, X0);
|
|
||||||
|
|
||||||
u32 offset = 0;
|
u32 offset = 0;
|
||||||
BitSet16::Iterator it = regs.begin();
|
BitSet16::Iterator it = regs.begin();
|
||||||
|
@ -519,12 +540,16 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
loadStoreOffsets[i++] = GetCodeOffset();
|
loadStoreOffsets[i++] = GetCodeOffset();
|
||||||
|
|
||||||
if (store)
|
if (store)
|
||||||
|
{
|
||||||
STR(INDEX_UNSIGNED, first, X1, offset);
|
STR(INDEX_UNSIGNED, first, X1, offset);
|
||||||
else
|
}
|
||||||
|
else if (!(reg == rn && skipLoadingRn))
|
||||||
|
{
|
||||||
LDR(INDEX_UNSIGNED, first, X1, offset);
|
LDR(INDEX_UNSIGNED, first, X1, offset);
|
||||||
|
|
||||||
if (!(RegCache.LoadedRegs & (1 << reg)) && !store)
|
if (!(RegCache.LoadedRegs & (1 << reg)))
|
||||||
SaveReg(reg, first);
|
SaveReg(reg, first);
|
||||||
|
}
|
||||||
|
|
||||||
offset += 4;
|
offset += 4;
|
||||||
}
|
}
|
||||||
|
@ -538,25 +563,38 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
|
|
||||||
ARM64Reg first = W3, second = W4;
|
ARM64Reg first = W3, second = W4;
|
||||||
if (RegCache.LoadedRegs & (1 << reg))
|
if (RegCache.LoadedRegs & (1 << reg))
|
||||||
first = MapReg(reg);
|
{
|
||||||
|
if (!(reg == rn && skipLoadingRn))
|
||||||
|
first = MapReg(reg);
|
||||||
|
}
|
||||||
else if (store)
|
else if (store)
|
||||||
|
{
|
||||||
LoadReg(reg, first);
|
LoadReg(reg, first);
|
||||||
|
}
|
||||||
if (RegCache.LoadedRegs & (1 << nextReg))
|
if (RegCache.LoadedRegs & (1 << nextReg))
|
||||||
second = MapReg(nextReg);
|
{
|
||||||
|
if (!(nextReg == rn && skipLoadingRn))
|
||||||
|
second = MapReg(nextReg);
|
||||||
|
}
|
||||||
else if (store)
|
else if (store)
|
||||||
|
{
|
||||||
LoadReg(nextReg, second);
|
LoadReg(nextReg, second);
|
||||||
|
}
|
||||||
|
|
||||||
loadStoreOffsets[i++] = GetCodeOffset();
|
loadStoreOffsets[i++] = GetCodeOffset();
|
||||||
|
|
||||||
if (store)
|
if (store)
|
||||||
|
{
|
||||||
STP(INDEX_SIGNED, first, second, X1, offset);
|
STP(INDEX_SIGNED, first, second, X1, offset);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
LDP(INDEX_SIGNED, first, second, X1, offset);
|
LDP(INDEX_SIGNED, first, second, X1, offset);
|
||||||
|
|
||||||
if (!(RegCache.LoadedRegs & (1 << reg)) && !store)
|
if (!(RegCache.LoadedRegs & (1 << reg)))
|
||||||
SaveReg(reg, first);
|
SaveReg(reg, first);
|
||||||
if (!(RegCache.LoadedRegs & (1 << nextReg)) && !store)
|
if (!(RegCache.LoadedRegs & (1 << nextReg)))
|
||||||
SaveReg(nextReg, second);
|
SaveReg(nextReg, second);
|
||||||
|
}
|
||||||
|
|
||||||
offset += 8;
|
offset += 8;
|
||||||
}
|
}
|
||||||
|
@ -566,7 +604,8 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
SwapCodeRegion();
|
SwapCodeRegion();
|
||||||
patchFunc = (u8*)GetRXPtr();
|
patchFunc = (u8*)GetRXPtr();
|
||||||
patch.PatchFunc = patchFunc;
|
patch.PatchFunc = patchFunc;
|
||||||
for (i = 0; i < regsCount; i++)
|
u32 numLoadStores = i;
|
||||||
|
for (i = 0; i < numLoadStores; i++)
|
||||||
{
|
{
|
||||||
patch.PatchOffset = fastPathStart - loadStoreOffsets[i];
|
patch.PatchOffset = fastPathStart - loadStoreOffsets[i];
|
||||||
LoadStorePatches[loadStoreOffsets[i]] = patch;
|
LoadStorePatches[loadStoreOffsets[i]] = patch;
|
||||||
|
@ -634,13 +673,15 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PushRegs(false, false, !compileFastPath);
|
||||||
|
|
||||||
ADD(X1, SP, 0);
|
ADD(X1, SP, 0);
|
||||||
MOVI2R(W2, regsCount);
|
MOVI2R(W2, regsCount);
|
||||||
|
|
||||||
if (Num == 0)
|
if (Num == 0)
|
||||||
{
|
{
|
||||||
MOV(X3, RCPU);
|
MOV(X3, RCPU);
|
||||||
switch ((u32)store * 2 | NDS::ConsoleType)
|
switch ((u32)store * 2 | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 0: QuickCallFunction(X4, SlowBlockTransfer9<false, 0>); break;
|
case 0: QuickCallFunction(X4, SlowBlockTransfer9<false, 0>); break;
|
||||||
case 1: QuickCallFunction(X4, SlowBlockTransfer9<false, 1>); break;
|
case 1: QuickCallFunction(X4, SlowBlockTransfer9<false, 1>); break;
|
||||||
|
@ -650,7 +691,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch ((u32)store * 2 | NDS::ConsoleType)
|
switch ((u32)store * 2 | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 0: QuickCallFunction(X4, SlowBlockTransfer7<false, 0>); break;
|
case 0: QuickCallFunction(X4, SlowBlockTransfer7<false, 0>); break;
|
||||||
case 1: QuickCallFunction(X4, SlowBlockTransfer7<false, 1>); break;
|
case 1: QuickCallFunction(X4, SlowBlockTransfer7<false, 1>); break;
|
||||||
|
@ -659,6 +700,8 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PopRegs(false, false);
|
||||||
|
|
||||||
if (!store)
|
if (!store)
|
||||||
{
|
{
|
||||||
if (usermode && !regs[15] && (regs & BitSet16(0x7f00)))
|
if (usermode && !regs[15] && (regs & BitSet16(0x7f00)))
|
||||||
|
@ -677,20 +720,23 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
LDR(INDEX_UNSIGNED, W3, SP, i * 8);
|
LDR(INDEX_UNSIGNED, W3, SP, i * 8);
|
||||||
MOVI2R(W1, reg - 8);
|
MOVI2R(W1, reg - 8);
|
||||||
BL(WriteBanked);
|
BL(WriteBanked);
|
||||||
FixupBranch alreadyWritten = CBNZ(W4);
|
if (!(reg == rn && skipLoadingRn))
|
||||||
if (RegCache.LoadedRegs & (1 << reg))
|
{
|
||||||
MOV(MapReg(reg), W3);
|
FixupBranch alreadyWritten = CBNZ(W4);
|
||||||
else
|
if (RegCache.LoadedRegs & (1 << reg))
|
||||||
SaveReg(reg, W3);
|
MOV(MapReg(reg), W3);
|
||||||
SetJumpTarget(alreadyWritten);
|
else
|
||||||
|
SaveReg(reg, W3);
|
||||||
|
SetJumpTarget(alreadyWritten);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!usermode && nextReg != regs.end())
|
else if (!usermode && nextReg != regs.end())
|
||||||
{
|
{
|
||||||
ARM64Reg first = W3, second = W4;
|
ARM64Reg first = W3, second = W4;
|
||||||
|
|
||||||
if (RegCache.LoadedRegs & (1 << reg))
|
if (RegCache.LoadedRegs & (1 << reg) && !(reg == rn && skipLoadingRn))
|
||||||
first = MapReg(reg);
|
first = MapReg(reg);
|
||||||
if (RegCache.LoadedRegs & (1 << *nextReg))
|
if (RegCache.LoadedRegs & (1 << *nextReg) && !(*nextReg == rn && skipLoadingRn))
|
||||||
second = MapReg(*nextReg);
|
second = MapReg(*nextReg);
|
||||||
|
|
||||||
LDP(INDEX_SIGNED, EncodeRegTo64(first), EncodeRegTo64(second), SP, i * 8);
|
LDP(INDEX_SIGNED, EncodeRegTo64(first), EncodeRegTo64(second), SP, i * 8);
|
||||||
|
@ -705,8 +751,11 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
}
|
}
|
||||||
else if (RegCache.LoadedRegs & (1 << reg))
|
else if (RegCache.LoadedRegs & (1 << reg))
|
||||||
{
|
{
|
||||||
ARM64Reg mapped = MapReg(reg);
|
if (!(reg == rn && skipLoadingRn))
|
||||||
LDR(INDEX_UNSIGNED, mapped, SP, i * 8);
|
{
|
||||||
|
ARM64Reg mapped = MapReg(reg);
|
||||||
|
LDR(INDEX_UNSIGNED, mapped, SP, i * 8);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -750,13 +799,13 @@ void Compiler::A_Comp_LDM_STM()
|
||||||
|
|
||||||
ARM64Reg rn = MapReg(CurInstr.A_Reg(16));
|
ARM64Reg rn = MapReg(CurInstr.A_Reg(16));
|
||||||
|
|
||||||
s32 offset = Comp_MemAccessBlock(CurInstr.A_Reg(16), regs, !load, pre, !add, usermode);
|
|
||||||
|
|
||||||
if (load && writeback && regs[CurInstr.A_Reg(16)])
|
if (load && writeback && regs[CurInstr.A_Reg(16)])
|
||||||
writeback = Num == 0
|
writeback = Num == 0
|
||||||
? (!(regs & ~BitSet16(1 << CurInstr.A_Reg(16)))) || (regs & ~BitSet16((2 << CurInstr.A_Reg(16)) - 1))
|
&& (!(regs & ~BitSet16(1 << CurInstr.A_Reg(16)))) || (regs & ~BitSet16((2 << CurInstr.A_Reg(16)) - 1));
|
||||||
: false;
|
|
||||||
if (writeback)
|
s32 offset = Comp_MemAccessBlock(CurInstr.A_Reg(16), regs, !load, pre, !add, usermode, load && writeback);
|
||||||
|
|
||||||
|
if (writeback && offset)
|
||||||
{
|
{
|
||||||
if (offset > 0)
|
if (offset > 0)
|
||||||
ADD(rn, rn, offset);
|
ADD(rn, rn, offset);
|
||||||
|
@ -778,12 +827,15 @@ void Compiler::T_Comp_PUSH_POP()
|
||||||
}
|
}
|
||||||
|
|
||||||
ARM64Reg sp = MapReg(13);
|
ARM64Reg sp = MapReg(13);
|
||||||
s32 offset = Comp_MemAccessBlock(13, regs, !load, !load, !load, false);
|
s32 offset = Comp_MemAccessBlock(13, regs, !load, !load, !load, false, false);
|
||||||
|
|
||||||
if (offset > 0)
|
if (offset)
|
||||||
|
{
|
||||||
|
if (offset > 0)
|
||||||
ADD(sp, sp, offset);
|
ADD(sp, sp, offset);
|
||||||
else
|
else
|
||||||
SUB(sp, sp, -offset);
|
SUB(sp, sp, -offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::T_Comp_LDMIA_STMIA()
|
void Compiler::T_Comp_LDMIA_STMIA()
|
||||||
|
@ -792,10 +844,12 @@ void Compiler::T_Comp_LDMIA_STMIA()
|
||||||
ARM64Reg rb = MapReg(CurInstr.T_Reg(8));
|
ARM64Reg rb = MapReg(CurInstr.T_Reg(8));
|
||||||
bool load = CurInstr.Instr & (1 << 11);
|
bool load = CurInstr.Instr & (1 << 11);
|
||||||
u32 regsCount = regs.Count();
|
u32 regsCount = regs.Count();
|
||||||
|
|
||||||
s32 offset = Comp_MemAccessBlock(CurInstr.T_Reg(8), regs, !load, false, false, false);
|
|
||||||
|
|
||||||
if (!load || !regs[CurInstr.T_Reg(8)])
|
bool writeback = !load || !regs[CurInstr.T_Reg(8)];
|
||||||
|
|
||||||
|
s32 offset = Comp_MemAccessBlock(CurInstr.T_Reg(8), regs, !load, false, false, false, load && writeback);
|
||||||
|
|
||||||
|
if (writeback && offset)
|
||||||
{
|
{
|
||||||
if (offset > 0)
|
if (offset > 0)
|
||||||
ADD(rb, rb, offset);
|
ADD(rb, rb, offset);
|
||||||
|
|
|
@ -1,6 +1,26 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team, RSDuck
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef ARMJIT_COMPILER_H
|
#ifndef ARMJIT_COMPILER_H
|
||||||
#define ARMJIT_COMPILER_H
|
#define ARMJIT_COMPILER_H
|
||||||
|
|
||||||
|
#ifdef JIT_ENABLED
|
||||||
|
|
||||||
#if defined(__x86_64__)
|
#if defined(__x86_64__)
|
||||||
#include "ARMJIT_x64/ARMJIT_Compiler.h"
|
#include "ARMJIT_x64/ARMJIT_Compiler.h"
|
||||||
#elif defined(__aarch64__)
|
#elif defined(__aarch64__)
|
||||||
|
@ -9,9 +29,6 @@
|
||||||
#error "The current target platform doesn't have a JIT backend"
|
#error "The current target platform doesn't have a JIT backend"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace ARMJIT
|
#endif
|
||||||
{
|
|
||||||
extern Compiler* JITCompiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
#include "ARMJIT_Global.h"
|
||||||
|
#include "ARMJIT_Memory.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace melonDS
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace ARMJIT_Global
|
||||||
|
{
|
||||||
|
|
||||||
|
std::mutex globalMutex;
|
||||||
|
|
||||||
|
#if defined(__APPLE__) && defined(__aarch64__)
|
||||||
|
#define APPLE_AARCH64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef APPLE_AARCH64
|
||||||
|
static constexpr size_t NumCodeMemSlices = 4;
|
||||||
|
static constexpr size_t CodeMemoryAlignedSize = NumCodeMemSlices * CodeMemorySliceSize;
|
||||||
|
|
||||||
|
// I haven't heard of pages larger than 16 KB
|
||||||
|
u8 CodeMemory[CodeMemoryAlignedSize + 16*1024];
|
||||||
|
|
||||||
|
u32 AvailableCodeMemSlices = (1 << NumCodeMemSlices) - 1;
|
||||||
|
|
||||||
|
u8* GetAlignedCodeMemoryStart()
|
||||||
|
{
|
||||||
|
return reinterpret_cast<u8*>((reinterpret_cast<intptr_t>(CodeMemory) + (16*1024-1)) & ~static_cast<intptr_t>(16*1024-1));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int RefCounter = 0;
|
||||||
|
|
||||||
|
void* AllocateCodeMem()
|
||||||
|
{
|
||||||
|
std::lock_guard guard(globalMutex);
|
||||||
|
|
||||||
|
#ifndef APPLE_AARCH64
|
||||||
|
if (AvailableCodeMemSlices)
|
||||||
|
{
|
||||||
|
int slice = __builtin_ctz(AvailableCodeMemSlices);
|
||||||
|
AvailableCodeMemSlices &= ~(1 << slice);
|
||||||
|
//printf("allocating slice %d\n", slice);
|
||||||
|
return &GetAlignedCodeMemoryStart()[slice * CodeMemorySliceSize];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// allocate
|
||||||
|
#ifdef _WIN32
|
||||||
|
return VirtualAlloc(nullptr, CodeMemorySliceSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||||
|
#elif defined(APPLE_AARCH64)
|
||||||
|
return mmap(NULL, CodeMemorySliceSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
|
||||||
|
#else
|
||||||
|
//printf("mmaping...\n");
|
||||||
|
return mmap(nullptr, CodeMemorySliceSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeCodeMem(void* codeMem)
|
||||||
|
{
|
||||||
|
std::lock_guard guard(globalMutex);
|
||||||
|
|
||||||
|
#ifndef APPLE_AARCH64
|
||||||
|
for (int i = 0; i < NumCodeMemSlices; i++)
|
||||||
|
{
|
||||||
|
if (codeMem == &GetAlignedCodeMemoryStart()[CodeMemorySliceSize * i])
|
||||||
|
{
|
||||||
|
//printf("freeing slice\n");
|
||||||
|
AvailableCodeMemSlices |= 1 << i;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
VirtualFree(codeMem, CodeMemorySliceSize, MEM_RELEASE|MEM_DECOMMIT);
|
||||||
|
#else
|
||||||
|
munmap(codeMem, CodeMemorySliceSize);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init()
|
||||||
|
{
|
||||||
|
std::lock_guard guard(globalMutex);
|
||||||
|
|
||||||
|
RefCounter++;
|
||||||
|
if (RefCounter == 1)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD dummy;
|
||||||
|
VirtualProtect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PAGE_EXECUTE_READWRITE, &dummy);
|
||||||
|
#elif defined(APPLE_AARCH64)
|
||||||
|
// Apple aarch64 always uses dynamic allocation
|
||||||
|
#else
|
||||||
|
mprotect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ARMJIT_Memory::RegisterFaultHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeInit()
|
||||||
|
{
|
||||||
|
std::lock_guard guard(globalMutex);
|
||||||
|
|
||||||
|
RefCounter--;
|
||||||
|
if (RefCounter == 0)
|
||||||
|
{
|
||||||
|
ARMJIT_Memory::UnregisterFaultHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016-2019 Arisotura
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
This file is part of melonDS.
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
@ -16,9 +16,29 @@
|
||||||
with melonDS. If not, see http://www.gnu.org/licenses/.
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef DSICRYPTO_H
|
#ifndef ARMJIT_GLOBAL_H
|
||||||
#define DSICRYPTO_H
|
#define ARMJIT_GLOBAL_H
|
||||||
|
|
||||||
//
|
#include "types.h"
|
||||||
|
|
||||||
#endif // DSICRYPTO_H
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
namespace melonDS
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace ARMJIT_Global
|
||||||
|
{
|
||||||
|
|
||||||
|
static constexpr size_t CodeMemorySliceSize = 1024*1024*32;
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
void DeInit();
|
||||||
|
|
||||||
|
void* AllocateCodeMem();
|
||||||
|
void FreeCodeMem(void* codeMem);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,3 +1,21 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team, RSDuck
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef ARMJIT_INTERNAL_H
|
#ifndef ARMJIT_INTERNAL_H
|
||||||
#define ARMJIT_INTERNAL_H
|
#define ARMJIT_INTERNAL_H
|
||||||
|
|
||||||
|
@ -6,13 +24,17 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include "ARMJIT.h"
|
#include "ARM_InstrInfo.h"
|
||||||
#include "ARMJIT_Memory.h"
|
#include "JitBlock.h"
|
||||||
|
#include "TinyVector.h"
|
||||||
|
|
||||||
|
namespace melonDS
|
||||||
|
{
|
||||||
|
class ARM;
|
||||||
|
class ARMv5;
|
||||||
|
|
||||||
// here lands everything which doesn't fit into ARMJIT.h
|
// here lands everything which doesn't fit into ARMJIT.h
|
||||||
// where it would be included by pretty much everything
|
// where it would be included by pretty much everything
|
||||||
namespace ARMJIT
|
|
||||||
{
|
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -51,139 +73,6 @@ struct FetchedInstr
|
||||||
ARMInstrInfo::Info Info;
|
ARMInstrInfo::Info Info;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
TinyVector
|
|
||||||
- because reinventing the wheel is the best!
|
|
||||||
|
|
||||||
- meant to be used very often, with not so many elements
|
|
||||||
max 1 << 16 elements
|
|
||||||
- doesn't allocate while no elements are inserted
|
|
||||||
- not stl confirmant of course
|
|
||||||
- probably only works with POD types
|
|
||||||
- remove operations don't preserve order, but O(1)!
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
struct __attribute__((packed)) TinyVector
|
|
||||||
{
|
|
||||||
T* Data = NULL;
|
|
||||||
u16 Capacity = 0;
|
|
||||||
u16 Length = 0;
|
|
||||||
|
|
||||||
~TinyVector()
|
|
||||||
{
|
|
||||||
delete[] Data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MakeCapacity(u32 capacity)
|
|
||||||
{
|
|
||||||
assert(capacity <= UINT16_MAX);
|
|
||||||
assert(capacity > Capacity);
|
|
||||||
T* newMem = new T[capacity];
|
|
||||||
if (Data != NULL)
|
|
||||||
memcpy(newMem, Data, sizeof(T) * Length);
|
|
||||||
|
|
||||||
T* oldData = Data;
|
|
||||||
Data = newMem;
|
|
||||||
if (oldData != NULL)
|
|
||||||
delete[] oldData;
|
|
||||||
|
|
||||||
Capacity = capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetLength(u16 length)
|
|
||||||
{
|
|
||||||
if (Capacity < length)
|
|
||||||
MakeCapacity(length);
|
|
||||||
|
|
||||||
Length = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Clear()
|
|
||||||
{
|
|
||||||
Length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Add(T element)
|
|
||||||
{
|
|
||||||
assert(Length + 1 <= UINT16_MAX);
|
|
||||||
if (Length + 1 > Capacity)
|
|
||||||
MakeCapacity(((Capacity + 4) * 3) / 2);
|
|
||||||
|
|
||||||
Data[Length++] = element;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Remove(int index)
|
|
||||||
{
|
|
||||||
assert(Length > 0);
|
|
||||||
assert(index >= 0 && index < Length);
|
|
||||||
|
|
||||||
Length--;
|
|
||||||
Data[index] = Data[Length];
|
|
||||||
/*for (int i = index; i < Length; i++)
|
|
||||||
Data[i] = Data[i + 1];*/
|
|
||||||
}
|
|
||||||
|
|
||||||
int Find(T needle)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < Length; i++)
|
|
||||||
{
|
|
||||||
if (Data[i] == needle)
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RemoveByValue(T needle)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < Length; i++)
|
|
||||||
{
|
|
||||||
if (Data[i] == needle)
|
|
||||||
{
|
|
||||||
Remove(i);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& operator[](int index)
|
|
||||||
{
|
|
||||||
assert(index >= 0 && index < Length);
|
|
||||||
return Data[index];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class JitBlock
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
JitBlock(u32 num, u32 literalHash, u32 numAddresses, u32 numLiterals)
|
|
||||||
{
|
|
||||||
Num = num;
|
|
||||||
NumAddresses = numAddresses;
|
|
||||||
NumLiterals = numLiterals;
|
|
||||||
Data.SetLength(numAddresses * 2 + numLiterals);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 StartAddr;
|
|
||||||
u32 StartAddrLocal;
|
|
||||||
u32 InstrHash, LiteralHash;
|
|
||||||
u8 Num;
|
|
||||||
u16 NumAddresses;
|
|
||||||
u16 NumLiterals;
|
|
||||||
|
|
||||||
JitBlockEntry EntryPoint;
|
|
||||||
|
|
||||||
u32* AddressRanges()
|
|
||||||
{ return &Data[0]; }
|
|
||||||
u32* AddressMasks()
|
|
||||||
{ return &Data[NumAddresses]; }
|
|
||||||
u32* Literals()
|
|
||||||
{ return &Data[NumAddresses * 2]; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
TinyVector<u32> Data;
|
|
||||||
};
|
|
||||||
|
|
||||||
// size should be 16 bytes because I'm to lazy to use mul and whatnot
|
// size should be 16 bytes because I'm to lazy to use mul and whatnot
|
||||||
struct __attribute__((packed)) AddressRange
|
struct __attribute__((packed)) AddressRange
|
||||||
{
|
{
|
||||||
|
@ -196,13 +85,9 @@ typedef void (*InterpreterFunc)(ARM* cpu);
|
||||||
extern InterpreterFunc InterpretARM[];
|
extern InterpreterFunc InterpretARM[];
|
||||||
extern InterpreterFunc InterpretTHUMB[];
|
extern InterpreterFunc InterpretTHUMB[];
|
||||||
|
|
||||||
extern TinyVector<u32> InvalidLiterals;
|
inline bool PageContainsCode(const AddressRange* range, u32 pageSize)
|
||||||
|
|
||||||
extern AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count];
|
|
||||||
|
|
||||||
inline bool PageContainsCode(AddressRange* range)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 8; i++)
|
for (int i = 0; i < pageSize / 512; i++)
|
||||||
{
|
{
|
||||||
if (range[i].Blocks.Length > 0)
|
if (range[i].Blocks.Length > 0)
|
||||||
return true;
|
return true;
|
||||||
|
@ -210,11 +95,6 @@ inline bool PageContainsCode(AddressRange* range)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 LocaliseCodeAddress(u32 num, u32 addr);
|
|
||||||
|
|
||||||
template <u32 Num>
|
|
||||||
void LinkBlock(ARM* cpu, u32 codeOffset);
|
|
||||||
|
|
||||||
template <typename T, int ConsoleType> T SlowRead9(u32 addr, ARMv5* cpu);
|
template <typename T, int ConsoleType> T SlowRead9(u32 addr, ARMv5* cpu);
|
||||||
template <typename T, int ConsoleType> void SlowWrite9(u32 addr, ARMv5* cpu, u32 val);
|
template <typename T, int ConsoleType> void SlowWrite9(u32 addr, ARMv5* cpu, u32 val);
|
||||||
template <typename T, int ConsoleType> T SlowRead7(u32 addr);
|
template <typename T, int ConsoleType> T SlowRead7(u32 addr);
|
||||||
|
|
|
@ -1,63 +1,244 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef ARMJIT_MEMORY
|
#ifndef ARMJIT_MEMORY
|
||||||
#define ARMJIT_MEMORY
|
#define ARMJIT_MEMORY
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "MemConstants.h"
|
||||||
|
|
||||||
#include "ARM.h"
|
#ifdef JIT_ENABLED
|
||||||
|
# include <mutex>
|
||||||
|
# include "TinyVector.h"
|
||||||
|
# include "ARM.h"
|
||||||
|
# if defined(__SWITCH__)
|
||||||
|
# include <switch.h>
|
||||||
|
# elif defined(_WIN32)
|
||||||
|
#include <windows.h>
|
||||||
|
# else
|
||||||
|
# include <sys/mman.h>
|
||||||
|
# include <sys/stat.h>
|
||||||
|
# include <fcntl.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <signal.h>
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# include <array>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ARMJIT_Memory
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
#ifdef JIT_ENABLED
|
||||||
|
namespace Platform { struct DynamicLibrary; }
|
||||||
|
class Compiler;
|
||||||
|
class ARMJIT;
|
||||||
|
#endif
|
||||||
|
|
||||||
extern void* FastMem9Start;
|
static constexpr u32 LargePageSize = 0x4000;
|
||||||
extern void* FastMem7Start;
|
static constexpr u32 RegularPageSize = 0x1000;
|
||||||
|
|
||||||
void Init();
|
constexpr u32 RoundUp(u32 size) noexcept
|
||||||
void DeInit();
|
|
||||||
|
|
||||||
void Reset();
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
{
|
||||||
memregion_Other = 0,
|
return (size + LargePageSize - 1) & ~(LargePageSize - 1);
|
||||||
memregion_ITCM,
|
|
||||||
memregion_DTCM,
|
|
||||||
memregion_BIOS9,
|
|
||||||
memregion_MainRAM,
|
|
||||||
memregion_SharedWRAM,
|
|
||||||
memregion_IO9,
|
|
||||||
memregion_VRAM,
|
|
||||||
memregion_BIOS7,
|
|
||||||
memregion_WRAM7,
|
|
||||||
memregion_IO7,
|
|
||||||
memregion_Wifi,
|
|
||||||
memregion_VWRAM,
|
|
||||||
|
|
||||||
// DSi
|
|
||||||
memregion_BIOS9DSi,
|
|
||||||
memregion_BIOS7DSi,
|
|
||||||
memregion_NewSharedWRAM_A,
|
|
||||||
memregion_NewSharedWRAM_B,
|
|
||||||
memregion_NewSharedWRAM_C,
|
|
||||||
|
|
||||||
memregions_Count
|
|
||||||
};
|
|
||||||
|
|
||||||
int ClassifyAddress9(u32 addr);
|
|
||||||
int ClassifyAddress7(u32 addr);
|
|
||||||
|
|
||||||
bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize);
|
|
||||||
u32 LocaliseAddress(int region, u32 num, u32 addr);
|
|
||||||
|
|
||||||
bool IsFastmemCompatible(int region);
|
|
||||||
|
|
||||||
void RemapDTCM(u32 newBase, u32 newSize);
|
|
||||||
void RemapSWRAM();
|
|
||||||
void RemapNWRAM(int num);
|
|
||||||
|
|
||||||
void SetCodeProtection(int region, u32 offset, bool protect);
|
|
||||||
|
|
||||||
void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
static constexpr u32 MemBlockMainRAMOffset = 0;
|
||||||
|
static constexpr u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize);
|
||||||
|
static constexpr u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize);
|
||||||
|
static constexpr u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize);
|
||||||
|
static constexpr u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize);
|
||||||
|
static constexpr u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize);
|
||||||
|
static constexpr u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize);
|
||||||
|
static constexpr u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize);
|
||||||
|
|
||||||
|
class ARMJIT_Memory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
memregion_Other = 0,
|
||||||
|
memregion_ITCM,
|
||||||
|
memregion_DTCM,
|
||||||
|
memregion_BIOS9,
|
||||||
|
memregion_MainRAM,
|
||||||
|
memregion_SharedWRAM,
|
||||||
|
memregion_IO9,
|
||||||
|
memregion_VRAM,
|
||||||
|
memregion_BIOS7,
|
||||||
|
memregion_WRAM7,
|
||||||
|
memregion_IO7,
|
||||||
|
memregion_Wifi,
|
||||||
|
memregion_VWRAM,
|
||||||
|
|
||||||
|
// DSi
|
||||||
|
memregion_BIOS9DSi,
|
||||||
|
memregion_BIOS7DSi,
|
||||||
|
memregion_NewSharedWRAM_A,
|
||||||
|
memregion_NewSharedWRAM_B,
|
||||||
|
memregion_NewSharedWRAM_C,
|
||||||
|
|
||||||
|
memregions_Count
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef JIT_ENABLED
|
||||||
|
public:
|
||||||
|
explicit ARMJIT_Memory(melonDS::NDS& nds);
|
||||||
|
~ARMJIT_Memory() noexcept;
|
||||||
|
ARMJIT_Memory(const ARMJIT_Memory&) = delete;
|
||||||
|
ARMJIT_Memory(ARMJIT_Memory&&) = delete;
|
||||||
|
ARMJIT_Memory& operator=(const ARMJIT_Memory&) = delete;
|
||||||
|
ARMJIT_Memory& operator=(ARMJIT_Memory&&) = delete;
|
||||||
|
void Reset() noexcept;
|
||||||
|
void RemapDTCM(u32 newBase, u32 newSize) noexcept;
|
||||||
|
void RemapSWRAM() noexcept;
|
||||||
|
void RemapNWRAM(int num) noexcept;
|
||||||
|
void SetCodeProtection(int region, u32 offset, bool protect) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetMainRAM() noexcept { return MemoryBase + MemBlockMainRAMOffset; }
|
||||||
|
[[nodiscard]] const u8* GetMainRAM() const noexcept { return MemoryBase + MemBlockMainRAMOffset; }
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetSharedWRAM() noexcept { return MemoryBase + MemBlockSWRAMOffset; }
|
||||||
|
[[nodiscard]] const u8* GetSharedWRAM() const noexcept { return MemoryBase + MemBlockSWRAMOffset; }
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetARM7WRAM() noexcept { return MemoryBase + MemBlockARM7WRAMOffset; }
|
||||||
|
[[nodiscard]] const u8* GetARM7WRAM() const noexcept { return MemoryBase + MemBlockARM7WRAMOffset; }
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetARM9DTCM() noexcept { return MemoryBase + MemBlockDTCMOffset; }
|
||||||
|
[[nodiscard]] const u8* GetARM9DTCM() const noexcept { return MemoryBase + MemBlockDTCMOffset; }
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetNWRAM_A() noexcept { return MemoryBase + MemBlockNWRAM_AOffset; }
|
||||||
|
[[nodiscard]] const u8* GetNWRAM_A() const noexcept { return MemoryBase + MemBlockNWRAM_AOffset; }
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetNWRAM_B() noexcept { return MemoryBase + MemBlockNWRAM_BOffset; }
|
||||||
|
[[nodiscard]] const u8* GetNWRAM_B() const noexcept { return MemoryBase + MemBlockNWRAM_BOffset; }
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetNWRAM_C() noexcept { return MemoryBase + MemBlockNWRAM_COffset; }
|
||||||
|
[[nodiscard]] const u8* GetNWRAM_C() const noexcept { return MemoryBase + MemBlockNWRAM_COffset; }
|
||||||
|
|
||||||
|
int ClassifyAddress9(u32 addr) const noexcept;
|
||||||
|
int ClassifyAddress7(u32 addr) const noexcept;
|
||||||
|
bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize) const noexcept;
|
||||||
|
u32 LocaliseAddress(int region, u32 num, u32 addr) const noexcept;
|
||||||
|
bool IsFastmemCompatible(int region) const noexcept;
|
||||||
|
void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept;
|
||||||
|
bool MapAtAddress(u32 addr) noexcept;
|
||||||
|
|
||||||
|
static bool IsFastMemSupported();
|
||||||
|
|
||||||
|
static void RegisterFaultHandler();
|
||||||
|
static void UnregisterFaultHandler();
|
||||||
|
|
||||||
|
static u32 PageSize;
|
||||||
|
static u32 PageShift;
|
||||||
|
private:
|
||||||
|
friend class Compiler;
|
||||||
|
struct Mapping
|
||||||
|
{
|
||||||
|
u32 Addr;
|
||||||
|
u32 Size, LocalOffset;
|
||||||
|
u32 Num;
|
||||||
|
|
||||||
|
void Unmap(int region, NDS& nds) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FaultDescription
|
||||||
|
{
|
||||||
|
u32 EmulatedFaultAddr;
|
||||||
|
u8* FaultPC;
|
||||||
|
};
|
||||||
|
static bool FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds);
|
||||||
|
bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept;
|
||||||
|
bool UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept;
|
||||||
|
void SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept;
|
||||||
|
|
||||||
|
melonDS::NDS& NDS;
|
||||||
|
void* FastMem9Start;
|
||||||
|
void* FastMem7Start;
|
||||||
|
u8* MemoryBase = nullptr;
|
||||||
|
|
||||||
|
#if defined(__SWITCH__)
|
||||||
|
VirtmemReservation* FastMem9Reservation, *FastMem7Reservation;
|
||||||
|
u8* MemoryBaseCodeMem;
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
struct VirtmemPlaceholder
|
||||||
|
{
|
||||||
|
uintptr_t Start;
|
||||||
|
size_t Size;
|
||||||
|
};
|
||||||
|
std::vector<VirtmemPlaceholder> VirtmemPlaceholders;
|
||||||
|
|
||||||
|
static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo);
|
||||||
|
HANDLE MemoryFile = INVALID_HANDLE_VALUE;
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext);
|
||||||
|
int MemoryFile = -1;
|
||||||
|
#endif
|
||||||
|
#ifdef ANDROID
|
||||||
|
Platform::DynamicLibrary* Libandroid = nullptr;
|
||||||
|
#endif
|
||||||
|
u8 MappingStatus9[1 << (32-12)] {};
|
||||||
|
u8 MappingStatus7[1 << (32-12)] {};
|
||||||
|
TinyVector<Mapping> Mappings[memregions_Count] {};
|
||||||
|
#else
|
||||||
|
public:
|
||||||
|
explicit ARMJIT_Memory(melonDS::NDS&) {};
|
||||||
|
~ARMJIT_Memory() = default;
|
||||||
|
ARMJIT_Memory(const ARMJIT_Memory&) = delete;
|
||||||
|
ARMJIT_Memory(ARMJIT_Memory&&) = delete;
|
||||||
|
ARMJIT_Memory& operator=(const ARMJIT_Memory&) = delete;
|
||||||
|
ARMJIT_Memory& operator=(ARMJIT_Memory&&) = delete;
|
||||||
|
|
||||||
|
void Reset() noexcept {}
|
||||||
|
void RemapDTCM(u32 newBase, u32 newSize) noexcept {}
|
||||||
|
void RemapSWRAM() noexcept {}
|
||||||
|
void RemapNWRAM(int num) noexcept {}
|
||||||
|
void SetCodeProtection(int region, u32 offset, bool protect) noexcept {}
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetMainRAM() noexcept { return MainRAM.data(); }
|
||||||
|
[[nodiscard]] const u8* GetMainRAM() const noexcept { return MainRAM.data(); }
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetSharedWRAM() noexcept { return SharedWRAM.data(); }
|
||||||
|
[[nodiscard]] const u8* GetSharedWRAM() const noexcept { return SharedWRAM.data(); }
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetARM7WRAM() noexcept { return ARM7WRAM.data(); }
|
||||||
|
[[nodiscard]] const u8* GetARM7WRAM() const noexcept { return ARM7WRAM.data(); }
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetARM9DTCM() noexcept { return DTCM.data(); }
|
||||||
|
[[nodiscard]] const u8* GetARM9DTCM() const noexcept { return DTCM.data(); }
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetNWRAM_A() noexcept { return NWRAM_A.data(); }
|
||||||
|
[[nodiscard]] const u8* GetNWRAM_A() const noexcept { return NWRAM_A.data(); }
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetNWRAM_B() noexcept { return NWRAM_B.data(); }
|
||||||
|
[[nodiscard]] const u8* GetNWRAM_B() const noexcept { return NWRAM_B.data(); }
|
||||||
|
|
||||||
|
[[nodiscard]] u8* GetNWRAM_C() noexcept { return NWRAM_C.data(); }
|
||||||
|
[[nodiscard]] const u8* GetNWRAM_C() const noexcept { return NWRAM_C.data(); }
|
||||||
|
private:
|
||||||
|
std::array<u8, MainRAMMaxSize> MainRAM {};
|
||||||
|
std::array<u8, ARM7WRAMSize> ARM7WRAM {};
|
||||||
|
std::array<u8, SharedWRAMSize> SharedWRAM {};
|
||||||
|
std::array<u8, DTCMPhysicalSize> DTCM {};
|
||||||
|
std::array<u8, NWRAMSize> NWRAM_A {};
|
||||||
|
std::array<u8, NWRAMSize> NWRAM_B {};
|
||||||
|
std::array<u8, NWRAMSize> NWRAM_C {};
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,15 +1,38 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team, RSDuck
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef ARMJIT_REGCACHE_H
|
#ifndef ARMJIT_REGCACHE_H
|
||||||
#define ARMJIT_REGCACHE_H
|
#define ARMJIT_REGCACHE_H
|
||||||
|
|
||||||
#include "ARMJIT.h"
|
#include "ARMJIT_Internal.h"
|
||||||
|
#include "Platform.h"
|
||||||
|
|
||||||
// TODO: replace this in the future
|
// TODO: replace this in the future
|
||||||
#include "dolphin/BitSet.h"
|
#include "dolphin/BitSet.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
namespace ARMJIT
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
using Platform::Log;
|
||||||
|
using Platform::LogLevel;
|
||||||
|
using namespace Common;
|
||||||
|
// Imported inside the namespace so that other headers aren't polluted
|
||||||
|
|
||||||
template <typename T, typename Reg>
|
template <typename T, typename Reg>
|
||||||
class RegisterCache
|
class RegisterCache
|
||||||
|
@ -23,7 +46,7 @@ public:
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 16; i++)
|
for (int i = 0; i < 16; i++)
|
||||||
Mapping[i] = (Reg)-1;
|
Mapping[i] = (Reg)-1;
|
||||||
|
|
||||||
PCAllocatableAsSrc = ~(pcAllocatableAsSrc
|
PCAllocatableAsSrc = ~(pcAllocatableAsSrc
|
||||||
? 0
|
? 0
|
||||||
: (1 << 15));
|
: (1 << 15));
|
||||||
|
@ -61,7 +84,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("this is a JIT bug! LoadRegister failed\n");
|
Log(LogLevel::Error, "this is a JIT bug! LoadRegister failed\n");
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +99,7 @@ public:
|
||||||
LiteralsLoaded &= ~(1 << reg);
|
LiteralsLoaded &= ~(1 << reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsLiteral(int reg)
|
bool IsLiteral(int reg) const
|
||||||
{
|
{
|
||||||
return LiteralsLoaded & (1 << reg);
|
return LiteralsLoaded & (1 << reg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,27 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "ARMJIT_Compiler.h"
|
#include "ARMJIT_Compiler.h"
|
||||||
|
#include "../ARM.h"
|
||||||
|
|
||||||
using namespace Gen;
|
using namespace Gen;
|
||||||
|
|
||||||
namespace ARMJIT
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
|
||||||
// uses RSCRATCH3
|
// uses RSCRATCH3
|
||||||
|
@ -110,7 +129,7 @@ OpArg Compiler::A_Comp_GetALUOp2(bool S, bool& carryUsed)
|
||||||
Comp_AddCycles_C();
|
Comp_AddCycles_C();
|
||||||
|
|
||||||
u32 shift = (CurInstr.Instr >> 7) & 0x1E;
|
u32 shift = (CurInstr.Instr >> 7) & 0x1E;
|
||||||
u32 imm = ::ROR(CurInstr.Instr & 0xFF, shift);
|
u32 imm = melonDS::ROR(CurInstr.Instr & 0xFF, shift);
|
||||||
|
|
||||||
carryUsed = false;
|
carryUsed = false;
|
||||||
if (S && shift)
|
if (S && shift)
|
||||||
|
@ -209,7 +228,7 @@ void Compiler::A_Comp_Arith()
|
||||||
Comp_ArithTriOp(&Compiler::AND, rd, rn, op2, carryUsed, sFlag|opSymmetric|opInvertOp2);
|
Comp_ArithTriOp(&Compiler::AND, rd, rn, op2, carryUsed, sFlag|opSymmetric|opInvertOp2);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printf("this is a JIT bug! %04x\n", op);
|
Log(LogLevel::Error, "this is a JIT bug! %04x\n", op);
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +365,7 @@ void Compiler::A_Comp_Mul_Long()
|
||||||
{
|
{
|
||||||
BSR(32, RSCRATCH, R(RSCRATCH3));
|
BSR(32, RSCRATCH, R(RSCRATCH3));
|
||||||
}
|
}
|
||||||
|
|
||||||
SHR(32, R(RSCRATCH), Imm8(3));
|
SHR(32, R(RSCRATCH), Imm8(3));
|
||||||
SetJumpTarget(zeroBSR); // fortunately that's even right
|
SetJumpTarget(zeroBSR); // fortunately that's even right
|
||||||
Comp_AddCycles_CI(RSCRATCH, 2);
|
Comp_AddCycles_CI(RSCRATCH, 2);
|
||||||
|
@ -599,7 +618,7 @@ void Compiler::T_Comp_AddSub_()
|
||||||
int op = (CurInstr.Instr >> 9) & 0x3;
|
int op = (CurInstr.Instr >> 9) & 0x3;
|
||||||
|
|
||||||
OpArg rn = op >= 2 ? Imm32((CurInstr.Instr >> 6) & 0x7) : MapReg(CurInstr.T_Reg(6));
|
OpArg rn = op >= 2 ? Imm32((CurInstr.Instr >> 6) & 0x7) : MapReg(CurInstr.T_Reg(6));
|
||||||
|
|
||||||
Comp_AddCycles_C();
|
Comp_AddCycles_C();
|
||||||
|
|
||||||
// special case for thumb mov being alias to add rd, rn, #0
|
// special case for thumb mov being alias to add rd, rn, #0
|
||||||
|
|
|
@ -1,8 +1,28 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "ARMJIT_Compiler.h"
|
#include "ARMJIT_Compiler.h"
|
||||||
|
#include "../ARM.h"
|
||||||
|
#include "../NDS.h"
|
||||||
|
|
||||||
using namespace Gen;
|
using namespace Gen;
|
||||||
|
|
||||||
namespace ARMJIT
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -12,7 +32,7 @@ int squeezePointer(T* ptr)
|
||||||
assert((T*)((u64)truncated) == ptr);
|
assert((T*)((u64)truncated) == ptr);
|
||||||
return truncated;
|
return truncated;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
|
void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
|
||||||
{
|
{
|
||||||
// we can simplify constant branches by a lot
|
// we can simplify constant branches by a lot
|
||||||
|
@ -101,7 +121,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
|
||||||
u32 compileTimePC = CurCPU->R[15];
|
u32 compileTimePC = CurCPU->R[15];
|
||||||
CurCPU->R[15] = newPC;
|
CurCPU->R[15] = newPC;
|
||||||
|
|
||||||
cycles += NDS::ARM7MemTimings[codeCycles][0] + NDS::ARM7MemTimings[codeCycles][1];
|
cycles += NDS.ARM7MemTimings[codeCycles][0] + NDS.ARM7MemTimings[codeCycles][1];
|
||||||
|
|
||||||
CurCPU->R[15] = compileTimePC;
|
CurCPU->R[15] = compileTimePC;
|
||||||
}
|
}
|
||||||
|
@ -113,7 +133,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
|
||||||
u32 compileTimePC = CurCPU->R[15];
|
u32 compileTimePC = CurCPU->R[15];
|
||||||
CurCPU->R[15] = newPC;
|
CurCPU->R[15] = newPC;
|
||||||
|
|
||||||
cycles += NDS::ARM7MemTimings[codeCycles][2] + NDS::ARM7MemTimings[codeCycles][3];
|
cycles += NDS.ARM7MemTimings[codeCycles][2] + NDS.ARM7MemTimings[codeCycles][3];
|
||||||
|
|
||||||
CurCPU->R[15] = compileTimePC;
|
CurCPU->R[15] = compileTimePC;
|
||||||
}
|
}
|
||||||
|
@ -147,7 +167,7 @@ void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR)
|
||||||
bool cpsrDirty = CPSRDirty;
|
bool cpsrDirty = CPSRDirty;
|
||||||
SaveCPSR();
|
SaveCPSR();
|
||||||
|
|
||||||
PushRegs(restoreCPSR);
|
PushRegs(restoreCPSR, true);
|
||||||
|
|
||||||
MOV(64, R(ABI_PARAM1), R(RCPU));
|
MOV(64, R(ABI_PARAM1), R(RCPU));
|
||||||
MOV(32, R(ABI_PARAM2), R(addr));
|
MOV(32, R(ABI_PARAM2), R(addr));
|
||||||
|
@ -156,11 +176,11 @@ void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR)
|
||||||
else
|
else
|
||||||
MOV(32, R(ABI_PARAM3), Imm32(true)); // what a waste
|
MOV(32, R(ABI_PARAM3), Imm32(true)); // what a waste
|
||||||
if (Num == 0)
|
if (Num == 0)
|
||||||
CALL((void*)&ARMv5JumpToTrampoline);
|
ABI_CallFunction(ARMv5JumpToTrampoline);
|
||||||
else
|
else
|
||||||
CALL((void*)&ARMv4JumpToTrampoline);
|
ABI_CallFunction(ARMv4JumpToTrampoline);
|
||||||
|
|
||||||
PopRegs(restoreCPSR);
|
PopRegs(restoreCPSR, true);
|
||||||
|
|
||||||
LoadCPSR();
|
LoadCPSR();
|
||||||
// in case this instruction is skipped
|
// in case this instruction is skipped
|
||||||
|
@ -229,7 +249,7 @@ void Compiler::T_Comp_BranchXchangeReg()
|
||||||
{
|
{
|
||||||
if (Num == 1)
|
if (Num == 1)
|
||||||
{
|
{
|
||||||
printf("BLX unsupported on ARM7!!!\n");
|
Log(LogLevel::Warn, "BLX unsupported on ARM7!!!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MOV(32, R(RSCRATCH), MapReg(CurInstr.A_Reg(3)));
|
MOV(32, R(RSCRATCH), MapReg(CurInstr.A_Reg(3)));
|
||||||
|
@ -275,7 +295,7 @@ void Compiler::T_Comp_BL_Merged()
|
||||||
target |= 1;
|
target |= 1;
|
||||||
|
|
||||||
MOV(32, MapReg(14), Imm32((R15 - 2) | 1));
|
MOV(32, MapReg(14), Imm32((R15 - 2) | 1));
|
||||||
|
|
||||||
Comp_JumpTo(target);
|
Comp_JumpTo(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,39 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "ARMJIT_Compiler.h"
|
#include "ARMJIT_Compiler.h"
|
||||||
|
|
||||||
|
#include "../ARMJIT.h"
|
||||||
#include "../ARMInterpreter.h"
|
#include "../ARMInterpreter.h"
|
||||||
#include "../Config.h"
|
#include "../NDS.h"
|
||||||
|
#include "../ARMJIT_Global.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
#include "../dolphin/CommonFuncs.h"
|
#include "../dolphin/CommonFuncs.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace Gen;
|
using namespace Gen;
|
||||||
|
using namespace Common;
|
||||||
|
|
||||||
extern "C" void ARM_Ret();
|
extern "C" void ARM_Ret();
|
||||||
|
|
||||||
namespace ARMJIT
|
namespace melonDS
|
||||||
{
|
{
|
||||||
template <>
|
template <>
|
||||||
const X64Reg RegisterCache<Compiler, X64Reg>::NativeRegAllocOrder[] =
|
const X64Reg RegisterCache<Compiler, X64Reg>::NativeRegAllocOrder[] =
|
||||||
|
@ -46,7 +61,7 @@ const BitSet32 CallerSavedPushRegs({R10, R11});
|
||||||
const BitSet32 CallerSavedPushRegs({R9, R10, R11});
|
const BitSet32 CallerSavedPushRegs({R9, R10, R11});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Compiler::PushRegs(bool saveHiRegs)
|
void Compiler::PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload)
|
||||||
{
|
{
|
||||||
BitSet32 loadedRegs(RegCache.LoadedRegs);
|
BitSet32 loadedRegs(RegCache.LoadedRegs);
|
||||||
|
|
||||||
|
@ -65,17 +80,26 @@ void Compiler::PushRegs(bool saveHiRegs)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int reg : loadedRegs)
|
for (int reg : loadedRegs)
|
||||||
if (BitSet32(1 << RegCache.Mapping[reg]) & ABI_ALL_CALLER_SAVED)
|
{
|
||||||
SaveReg(reg, RegCache.Mapping[reg]);
|
if (CallerSavedPushRegs[RegCache.Mapping[reg]]
|
||||||
|
&& (saveRegsToBeChanged || !((1<<reg) & CurInstr.Info.DstRegs && !((1<<reg) & CurInstr.Info.SrcRegs))))
|
||||||
|
{
|
||||||
|
if ((Thumb || CurInstr.Cond() == 0xE) && !((1 << reg) & (CurInstr.Info.DstRegs|CurInstr.Info.SrcRegs)) && allowUnload)
|
||||||
|
RegCache.UnloadRegister(reg);
|
||||||
|
else
|
||||||
|
SaveReg(reg, RegCache.Mapping[reg]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::PopRegs(bool saveHiRegs)
|
void Compiler::PopRegs(bool saveHiRegs, bool saveRegsToBeChanged)
|
||||||
{
|
{
|
||||||
BitSet32 loadedRegs(RegCache.LoadedRegs);
|
BitSet32 loadedRegs(RegCache.LoadedRegs);
|
||||||
for (int reg : loadedRegs)
|
for (int reg : loadedRegs)
|
||||||
{
|
{
|
||||||
if ((saveHiRegs && reg >= 8 && reg < 15)
|
if ((saveHiRegs && reg >= 8 && reg < 15)
|
||||||
|| BitSet32(1 << RegCache.Mapping[reg]) & ABI_ALL_CALLER_SAVED)
|
|| (CallerSavedPushRegs[RegCache.Mapping[reg]]
|
||||||
|
&& (saveRegsToBeChanged || !((1<<reg) & CurInstr.Info.DstRegs && !((1<<reg) & CurInstr.Info.SrcRegs)))))
|
||||||
{
|
{
|
||||||
LoadReg(reg, RegCache.Mapping[reg]);
|
LoadReg(reg, RegCache.Mapping[reg]);
|
||||||
}
|
}
|
||||||
|
@ -98,7 +122,9 @@ void Compiler::A_Comp_MRS()
|
||||||
MOV(32, rd, R(RSCRATCH3));
|
MOV(32, rd, R(RSCRATCH3));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
MOV(32, rd, R(RCPSR));
|
MOV(32, rd, R(RCPSR));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateModeTrampoline(ARM* arm, u32 oldmode, u32 newmode)
|
void UpdateModeTrampoline(ARM* arm, u32 oldmode, u32 newmode)
|
||||||
|
@ -111,7 +137,7 @@ void Compiler::A_Comp_MSR()
|
||||||
Comp_AddCycles_C();
|
Comp_AddCycles_C();
|
||||||
|
|
||||||
OpArg val = CurInstr.Instr & (1 << 25)
|
OpArg val = CurInstr.Instr & (1 << 25)
|
||||||
? Imm32(::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)))
|
? Imm32(melonDS::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E)))
|
||||||
: MapReg(CurInstr.A_Reg(0));
|
: MapReg(CurInstr.A_Reg(0));
|
||||||
|
|
||||||
u32 mask = 0;
|
u32 mask = 0;
|
||||||
|
@ -185,51 +211,26 @@ void Compiler::A_Comp_MSR()
|
||||||
AND(32, R(RSCRATCH2), val);
|
AND(32, R(RSCRATCH2), val);
|
||||||
OR(32, R(RCPSR), R(RSCRATCH2));
|
OR(32, R(RCPSR), R(RSCRATCH2));
|
||||||
|
|
||||||
PushRegs(true);
|
PushRegs(true, true);
|
||||||
|
|
||||||
MOV(32, R(ABI_PARAM3), R(RCPSR));
|
MOV(32, R(ABI_PARAM3), R(RCPSR));
|
||||||
MOV(32, R(ABI_PARAM2), R(RSCRATCH3));
|
MOV(32, R(ABI_PARAM2), R(RSCRATCH3));
|
||||||
MOV(64, R(ABI_PARAM1), R(RCPU));
|
MOV(64, R(ABI_PARAM1), R(RCPU));
|
||||||
CALL((void*)&UpdateModeTrampoline);
|
ABI_CallFunction(UpdateModeTrampoline);
|
||||||
|
|
||||||
PopRegs(true);
|
PopRegs(true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
|
||||||
We'll repurpose this .bss memory
|
|
||||||
|
|
||||||
*/
|
|
||||||
u8 CodeMemory[1024 * 1024 * 32];
|
|
||||||
|
|
||||||
Compiler::Compiler()
|
|
||||||
{
|
{
|
||||||
{
|
ARMJIT_Global::Init();
|
||||||
#ifdef _WIN32
|
|
||||||
SYSTEM_INFO sysInfo;
|
|
||||||
GetSystemInfo(&sysInfo);
|
|
||||||
|
|
||||||
u64 pageSize = (u64)sysInfo.dwPageSize;
|
CodeMemBase = static_cast<u8*>(ARMJIT_Global::AllocateCodeMem());
|
||||||
#else
|
CodeMemSize = ARMJIT_Global::CodeMemorySliceSize;
|
||||||
u64 pageSize = sysconf(_SC_PAGE_SIZE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
u8* pageAligned = (u8*)(((u64)CodeMemory & ~(pageSize - 1)) + pageSize);
|
ResetStart = CodeMemBase;
|
||||||
u64 alignedSize = (((u64)CodeMemory + sizeof(CodeMemory)) & ~(pageSize - 1)) - (u64)pageAligned;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
DWORD dummy;
|
|
||||||
VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
pageAligned = (u8*)mmap(NULL, 1024*1024*32, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS ,-1, 0);
|
|
||||||
#else
|
|
||||||
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ResetStart = pageAligned;
|
|
||||||
CodeMemSize = alignedSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
Reset();
|
Reset();
|
||||||
|
|
||||||
|
@ -269,6 +270,10 @@ Compiler::Compiler()
|
||||||
SetJumpTarget(und);
|
SetJumpTarget(und);
|
||||||
MOV(32, R(RSCRATCH3), MComplex(RCPU, RSCRATCH2, SCALE_4, offsetof(ARM, R_UND)));
|
MOV(32, R(RSCRATCH3), MComplex(RCPU, RSCRATCH2, SCALE_4, offsetof(ARM, R_UND)));
|
||||||
RET();
|
RET();
|
||||||
|
|
||||||
|
#ifdef JIT_PROFILING_ENABLED
|
||||||
|
CreateMethod("ReadBanked", ReadBanked);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// RSCRATCH mode
|
// RSCRATCH mode
|
||||||
|
@ -312,6 +317,10 @@ Compiler::Compiler()
|
||||||
MOV(32, MComplex(RCPU, RSCRATCH2, SCALE_4, offsetof(ARM, R_UND)), R(RSCRATCH3));
|
MOV(32, MComplex(RCPU, RSCRATCH2, SCALE_4, offsetof(ARM, R_UND)), R(RSCRATCH3));
|
||||||
CLC();
|
CLC();
|
||||||
RET();
|
RET();
|
||||||
|
|
||||||
|
#ifdef JIT_PROFILING_ENABLED
|
||||||
|
CreateMethod("WriteBanked", WriteBanked);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int consoleType = 0; consoleType < 2; consoleType++)
|
for (int consoleType = 0; consoleType < 2; consoleType++)
|
||||||
|
@ -372,6 +381,10 @@ Compiler::Compiler()
|
||||||
ABI_PopRegistersAndAdjustStack(CallerSavedPushRegs, 8);
|
ABI_PopRegistersAndAdjustStack(CallerSavedPushRegs, 8);
|
||||||
RET();
|
RET();
|
||||||
|
|
||||||
|
#ifdef JIT_PROFILING_ENABLED
|
||||||
|
CreateMethod("FastMemStorePatch%d_%d_%d", PatchedStoreFuncs[consoleType][num][size][reg], num, size, reg);
|
||||||
|
#endif
|
||||||
|
|
||||||
for (int signextend = 0; signextend < 2; signextend++)
|
for (int signextend = 0; signextend < 2; signextend++)
|
||||||
{
|
{
|
||||||
PatchedLoadFuncs[consoleType][num][size][signextend][reg] = GetWritableCodePtr();
|
PatchedLoadFuncs[consoleType][num][size][signextend][reg] = GetWritableCodePtr();
|
||||||
|
@ -410,6 +423,10 @@ Compiler::Compiler()
|
||||||
else
|
else
|
||||||
MOVZX(32, 8 << size, rdMapped, R(RSCRATCH));
|
MOVZX(32, 8 << size, rdMapped, R(RSCRATCH));
|
||||||
RET();
|
RET();
|
||||||
|
|
||||||
|
#ifdef JIT_PROFILING_ENABLED
|
||||||
|
CreateMethod("FastMemLoadPatch%d_%d_%d_%d", PatchedLoadFuncs[consoleType][num][size][signextend][reg], num, size, reg, signextend);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,6 +444,13 @@ Compiler::Compiler()
|
||||||
FarSize = (ResetStart + CodeMemSize) - FarStart;
|
FarSize = (ResetStart + CodeMemSize) - FarStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Compiler::~Compiler()
|
||||||
|
{
|
||||||
|
ARMJIT_Global::FreeCodeMem(CodeMemBase);
|
||||||
|
|
||||||
|
ARMJIT_Global::DeInit();
|
||||||
|
}
|
||||||
|
|
||||||
void Compiler::LoadCPSR()
|
void Compiler::LoadCPSR()
|
||||||
{
|
{
|
||||||
assert(!CPSRDirty);
|
assert(!CPSRDirty);
|
||||||
|
@ -447,14 +471,14 @@ void Compiler::SaveCPSR(bool flagClean)
|
||||||
void Compiler::LoadReg(int reg, X64Reg nativeReg)
|
void Compiler::LoadReg(int reg, X64Reg nativeReg)
|
||||||
{
|
{
|
||||||
if (reg != 15)
|
if (reg != 15)
|
||||||
MOV(32, R(nativeReg), MDisp(RCPU, offsetof(ARM, R[reg])));
|
MOV(32, R(nativeReg), MDisp(RCPU, offsetof(ARM, R) + reg*4));
|
||||||
else
|
else
|
||||||
MOV(32, R(nativeReg), Imm32(R15));
|
MOV(32, R(nativeReg), Imm32(R15));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::SaveReg(int reg, X64Reg nativeReg)
|
void Compiler::SaveReg(int reg, X64Reg nativeReg)
|
||||||
{
|
{
|
||||||
MOV(32, MDisp(RCPU, offsetof(ARM, R[reg])), R(nativeReg));
|
MOV(32, MDisp(RCPU, offsetof(ARM, R) + reg*4), R(nativeReg));
|
||||||
}
|
}
|
||||||
|
|
||||||
// invalidates RSCRATCH and RSCRATCH3
|
// invalidates RSCRATCH and RSCRATCH3
|
||||||
|
@ -592,18 +616,18 @@ const Compiler::CompileFunc T_Comp[ARMInstrInfo::tk_Count] = {
|
||||||
// LDR/STR sp rel
|
// LDR/STR sp rel
|
||||||
F(T_Comp_MemSPRel), F(T_Comp_MemSPRel),
|
F(T_Comp_MemSPRel), F(T_Comp_MemSPRel),
|
||||||
// PUSH/POP
|
// PUSH/POP
|
||||||
F(T_Comp_PUSH_POP), F(T_Comp_PUSH_POP),
|
F(T_Comp_PUSH_POP), F(T_Comp_PUSH_POP),
|
||||||
// LDMIA, STMIA
|
// LDMIA, STMIA
|
||||||
F(T_Comp_LDMIA_STMIA), F(T_Comp_LDMIA_STMIA),
|
F(T_Comp_LDMIA_STMIA), F(T_Comp_LDMIA_STMIA),
|
||||||
// Branch
|
// Branch
|
||||||
F(T_Comp_BCOND), F(T_Comp_BranchXchangeReg), F(T_Comp_BranchXchangeReg), F(T_Comp_B), F(T_Comp_BL_LONG_1), F(T_Comp_BL_LONG_2),
|
F(T_Comp_BCOND), F(T_Comp_BranchXchangeReg), F(T_Comp_BranchXchangeReg), F(T_Comp_B), F(T_Comp_BL_LONG_1), F(T_Comp_BL_LONG_2),
|
||||||
// Unk, SVC
|
// Unk, SVC
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
F(T_Comp_BL_Merged)
|
F(T_Comp_BL_Merged)
|
||||||
};
|
};
|
||||||
#undef F
|
#undef F
|
||||||
|
|
||||||
bool Compiler::CanCompile(bool thumb, u16 kind)
|
bool Compiler::CanCompile(bool thumb, u16 kind) const
|
||||||
{
|
{
|
||||||
return (thumb ? T_Comp[kind] : A_Comp[kind]) != NULL;
|
return (thumb ? T_Comp[kind] : A_Comp[kind]) != NULL;
|
||||||
}
|
}
|
||||||
|
@ -619,7 +643,7 @@ void Compiler::Reset()
|
||||||
LoadStorePatches.clear();
|
LoadStorePatches.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Compiler::IsJITFault(u8* addr)
|
bool Compiler::IsJITFault(const u8* addr)
|
||||||
{
|
{
|
||||||
return (u64)addr >= (u64)ResetStart && (u64)addr < (u64)ResetStart + CodeMemSize;
|
return (u64)addr >= (u64)ResetStart && (u64)addr < (u64)ResetStart + CodeMemSize;
|
||||||
}
|
}
|
||||||
|
@ -634,22 +658,45 @@ void Compiler::Comp_SpecialBranchBehaviour(bool taken)
|
||||||
{
|
{
|
||||||
RegCache.PrepareExit();
|
RegCache.PrepareExit();
|
||||||
|
|
||||||
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
|
if (ConstantCycles)
|
||||||
JMP((u8*)&ARM_Ret, true);
|
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
|
||||||
|
ABI_TailCall(ARM_Ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount)
|
#ifdef JIT_PROFILING_ENABLED
|
||||||
|
void Compiler::CreateMethod(const char* namefmt, void* start, ...)
|
||||||
|
{
|
||||||
|
if (iJIT_IsProfilingActive())
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, start);
|
||||||
|
char name[64];
|
||||||
|
vsprintf(name, namefmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
iJIT_Method_Load method = {0};
|
||||||
|
method.method_id = iJIT_GetNewMethodID();
|
||||||
|
method.method_name = name;
|
||||||
|
method.method_load_address = start;
|
||||||
|
method.method_size = GetWritableCodePtr() - (u8*)start;
|
||||||
|
|
||||||
|
iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount, bool hasMemoryInstr)
|
||||||
{
|
{
|
||||||
if (NearSize - (GetCodePtr() - NearStart) < 1024 * 32) // guess...
|
if (NearSize - (GetCodePtr() - NearStart) < 1024 * 32) // guess...
|
||||||
{
|
{
|
||||||
printf("near reset\n");
|
Log(LogLevel::Debug, "near reset\n");
|
||||||
ResetBlockCache();
|
NDS.JIT.ResetBlockCache();
|
||||||
}
|
}
|
||||||
if (FarSize - (FarCode - FarStart) < 1024 * 32) // guess...
|
if (FarSize - (FarCode - FarStart) < 1024 * 32) // guess...
|
||||||
{
|
{
|
||||||
printf("far reset\n");
|
Log(LogLevel::Debug, "far reset\n");
|
||||||
ResetBlockCache();
|
NDS.JIT.ResetBlockCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstantCycles = 0;
|
ConstantCycles = 0;
|
||||||
|
@ -703,7 +750,9 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
||||||
ABI_CallFunction(InterpretTHUMB[CurInstr.Info.Kind]);
|
ABI_CallFunction(InterpretTHUMB[CurInstr.Info.Kind]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
(this->*comp)();
|
(this->*comp)();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -724,7 +773,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IrregularCycles = false;
|
IrregularCycles = comp == NULL;
|
||||||
|
|
||||||
FixupBranch skipExecute;
|
FixupBranch skipExecute;
|
||||||
if (cond < 0xE)
|
if (cond < 0xE)
|
||||||
|
@ -737,7 +786,9 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
||||||
ABI_CallFunction(InterpretARM[CurInstr.Info.Kind]);
|
ABI_CallFunction(InterpretARM[CurInstr.Info.Kind]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
(this->*comp)();
|
(this->*comp)();
|
||||||
|
}
|
||||||
|
|
||||||
Comp_SpecialBranchBehaviour(true);
|
Comp_SpecialBranchBehaviour(true);
|
||||||
|
|
||||||
|
@ -755,9 +806,11 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
||||||
SetJumpTarget(skipFailed);
|
SetJumpTarget(skipFailed);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
SetJumpTarget(skipExecute);
|
SetJumpTarget(skipExecute);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -767,8 +820,13 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
||||||
|
|
||||||
RegCache.Flush();
|
RegCache.Flush();
|
||||||
|
|
||||||
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
|
if (ConstantCycles)
|
||||||
JMP((u8*)ARM_Ret, true);
|
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
|
||||||
|
ABI_TailCall(ARM_Ret);
|
||||||
|
|
||||||
|
#ifdef JIT_PROFILING_ENABLED
|
||||||
|
CreateMethod("JIT_Block_%d_%d_%08X", (void*)res, Num, Thumb, instrs[0].Addr);
|
||||||
|
#endif
|
||||||
|
|
||||||
/*FILE* codeout = fopen("codeout", "a");
|
/*FILE* codeout = fopen("codeout", "a");
|
||||||
fprintf(codeout, "beginning block argargarg__ %x!!!", instrs[0].Addr);
|
fprintf(codeout, "beginning block argargarg__ %x!!!", instrs[0].Addr);
|
||||||
|
@ -782,7 +840,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
||||||
void Compiler::Comp_AddCycles_C(bool forceNonConstant)
|
void Compiler::Comp_AddCycles_C(bool forceNonConstant)
|
||||||
{
|
{
|
||||||
s32 cycles = Num ?
|
s32 cycles = Num ?
|
||||||
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3]
|
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3]
|
||||||
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles);
|
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles);
|
||||||
|
|
||||||
if ((!Thumb && CurInstr.Cond() < 0xE) || forceNonConstant)
|
if ((!Thumb && CurInstr.Cond() < 0xE) || forceNonConstant)
|
||||||
|
@ -794,7 +852,7 @@ void Compiler::Comp_AddCycles_C(bool forceNonConstant)
|
||||||
void Compiler::Comp_AddCycles_CI(u32 i)
|
void Compiler::Comp_AddCycles_CI(u32 i)
|
||||||
{
|
{
|
||||||
s32 cycles = (Num ?
|
s32 cycles = (Num ?
|
||||||
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
|
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
|
||||||
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + i;
|
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + i;
|
||||||
|
|
||||||
if (!Thumb && CurInstr.Cond() < 0xE)
|
if (!Thumb && CurInstr.Cond() < 0xE)
|
||||||
|
@ -806,9 +864,9 @@ void Compiler::Comp_AddCycles_CI(u32 i)
|
||||||
void Compiler::Comp_AddCycles_CI(Gen::X64Reg i, int add)
|
void Compiler::Comp_AddCycles_CI(Gen::X64Reg i, int add)
|
||||||
{
|
{
|
||||||
s32 cycles = Num ?
|
s32 cycles = Num ?
|
||||||
NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
|
NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]
|
||||||
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles);
|
: ((R15 & 0x2) ? 0 : CurInstr.CodeCycles);
|
||||||
|
|
||||||
if (!Thumb && CurInstr.Cond() < 0xE)
|
if (!Thumb && CurInstr.Cond() < 0xE)
|
||||||
{
|
{
|
||||||
LEA(32, RSCRATCH, MDisp(i, add + cycles));
|
LEA(32, RSCRATCH, MDisp(i, add + cycles));
|
||||||
|
@ -831,7 +889,7 @@ void Compiler::Comp_AddCycles_CDI()
|
||||||
|
|
||||||
s32 cycles;
|
s32 cycles;
|
||||||
|
|
||||||
s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
|
s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
|
||||||
s32 numD = CurInstr.DataCycles;
|
s32 numD = CurInstr.DataCycles;
|
||||||
|
|
||||||
if ((CurInstr.DataRegion >> 24) == 0x02) // mainRAM
|
if ((CurInstr.DataRegion >> 24) == 0x02) // mainRAM
|
||||||
|
@ -853,7 +911,7 @@ void Compiler::Comp_AddCycles_CDI()
|
||||||
{
|
{
|
||||||
cycles = numC + numD + 1;
|
cycles = numC + numD + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Thumb && CurInstr.Cond() < 0xE)
|
if (!Thumb && CurInstr.Cond() < 0xE)
|
||||||
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm8(cycles));
|
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm8(cycles));
|
||||||
else
|
else
|
||||||
|
@ -876,7 +934,7 @@ void Compiler::Comp_AddCycles_CD()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
|
s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2];
|
||||||
s32 numD = CurInstr.DataCycles;
|
s32 numD = CurInstr.DataCycles;
|
||||||
|
|
||||||
if ((CurInstr.DataRegion >> 4) == 0x02)
|
if ((CurInstr.DataRegion >> 4) == 0x02)
|
||||||
|
|
|
@ -1,17 +1,43 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef ARMJIT_X64_COMPILER_H
|
#ifndef ARMJIT_X64_COMPILER_H
|
||||||
#define ARMJIT_X64_COMPILER_H
|
#define ARMJIT_X64_COMPILER_H
|
||||||
|
|
||||||
|
#if defined(JIT_ENABLED) && defined(__x86_64__)
|
||||||
|
|
||||||
#include "../dolphin/x64Emitter.h"
|
#include "../dolphin/x64Emitter.h"
|
||||||
|
|
||||||
#include "../ARMJIT.h"
|
|
||||||
#include "../ARMJIT_Internal.h"
|
#include "../ARMJIT_Internal.h"
|
||||||
#include "../ARMJIT_RegisterCache.h"
|
#include "../ARMJIT_RegisterCache.h"
|
||||||
|
|
||||||
|
#ifdef JIT_PROFILING_ENABLED
|
||||||
|
#include <jitprofiling.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace ARMJIT
|
|
||||||
{
|
|
||||||
|
|
||||||
|
namespace melonDS
|
||||||
|
{
|
||||||
|
class ARMJIT;
|
||||||
|
class ARMJIT_Memory;
|
||||||
|
class NDS;
|
||||||
const Gen::X64Reg RCPU = Gen::RBP;
|
const Gen::X64Reg RCPU = Gen::RBP;
|
||||||
const Gen::X64Reg RCPSR = Gen::R15;
|
const Gen::X64Reg RCPSR = Gen::R15;
|
||||||
|
|
||||||
|
@ -57,16 +83,17 @@ struct Op2
|
||||||
class Compiler : public Gen::XEmitter
|
class Compiler : public Gen::XEmitter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Compiler();
|
explicit Compiler(melonDS::NDS& nds);
|
||||||
|
~Compiler();
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
JitBlockEntry CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount);
|
JitBlockEntry CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount, bool hasMemoryInstr);
|
||||||
|
|
||||||
void LoadReg(int reg, Gen::X64Reg nativeReg);
|
void LoadReg(int reg, Gen::X64Reg nativeReg);
|
||||||
void SaveReg(int reg, Gen::X64Reg nativeReg);
|
void SaveReg(int reg, Gen::X64Reg nativeReg);
|
||||||
|
|
||||||
bool CanCompile(bool thumb, u16 kind);
|
bool CanCompile(bool thumb, u16 kind) const;
|
||||||
|
|
||||||
typedef void (Compiler::*CompileFunc)();
|
typedef void (Compiler::*CompileFunc)();
|
||||||
|
|
||||||
|
@ -99,7 +126,7 @@ public:
|
||||||
void A_Comp_Mul_Long();
|
void A_Comp_Mul_Long();
|
||||||
|
|
||||||
void A_Comp_CLZ();
|
void A_Comp_CLZ();
|
||||||
|
|
||||||
void A_Comp_MemWB();
|
void A_Comp_MemWB();
|
||||||
void A_Comp_MemHalf();
|
void A_Comp_MemHalf();
|
||||||
void A_Comp_LDM_STM();
|
void A_Comp_LDM_STM();
|
||||||
|
@ -145,10 +172,10 @@ public:
|
||||||
memop_SubtractOffset = 1 << 4
|
memop_SubtractOffset = 1 << 4
|
||||||
};
|
};
|
||||||
void Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flags);
|
void Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flags);
|
||||||
s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode);
|
s32 Comp_MemAccessBlock(int rn, Common::BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn);
|
||||||
bool Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr);
|
bool Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr);
|
||||||
|
|
||||||
void Comp_ArithTriOp(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
|
void Comp_ArithTriOp(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
|
||||||
Gen::OpArg rd, Gen::OpArg rn, Gen::OpArg op2, bool carryUsed, int opFlags);
|
Gen::OpArg rd, Gen::OpArg rn, Gen::OpArg op2, bool carryUsed, int opFlags);
|
||||||
void Comp_ArithTriOpReverse(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
|
void Comp_ArithTriOpReverse(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
|
||||||
Gen::OpArg rd, Gen::OpArg rn, Gen::OpArg op2, bool carryUsed, int opFlags);
|
Gen::OpArg rd, Gen::OpArg rn, Gen::OpArg op2, bool carryUsed, int opFlags);
|
||||||
|
@ -174,8 +201,8 @@ public:
|
||||||
|
|
||||||
Gen::FixupBranch CheckCondition(u32 cond);
|
Gen::FixupBranch CheckCondition(u32 cond);
|
||||||
|
|
||||||
void PushRegs(bool saveHiRegs);
|
void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true);
|
||||||
void PopRegs(bool saveHiRegs);
|
void PopRegs(bool saveHiRegs, bool saveRegsToBeChanged);
|
||||||
|
|
||||||
Gen::OpArg MapReg(int reg)
|
Gen::OpArg MapReg(int reg)
|
||||||
{
|
{
|
||||||
|
@ -208,48 +235,55 @@ public:
|
||||||
SetCodePtr(FarCode);
|
SetCodePtr(FarCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsJITFault(u8* addr);
|
bool IsJITFault(const u8* addr);
|
||||||
|
|
||||||
u8* RewriteMemAccess(u8* pc);
|
u8* RewriteMemAccess(u8* pc);
|
||||||
|
|
||||||
u8* FarCode;
|
#ifdef JIT_PROFILING_ENABLED
|
||||||
u8* NearCode;
|
void CreateMethod(const char* namefmt, void* start, ...);
|
||||||
u32 FarSize;
|
#endif
|
||||||
u32 NearSize;
|
|
||||||
|
|
||||||
u8* NearStart;
|
melonDS::NDS& NDS;
|
||||||
u8* FarStart;
|
u8* FarCode {};
|
||||||
|
u8* NearCode {};
|
||||||
|
u32 FarSize {};
|
||||||
|
u32 NearSize {};
|
||||||
|
|
||||||
void* PatchedStoreFuncs[2][2][3][16];
|
u8* NearStart {};
|
||||||
void* PatchedLoadFuncs[2][2][3][2][16];
|
u8* FarStart {};
|
||||||
|
|
||||||
std::unordered_map<u8*, LoadStorePatch> LoadStorePatches;
|
void* PatchedStoreFuncs[2][2][3][16] {};
|
||||||
|
void* PatchedLoadFuncs[2][2][3][2][16] {};
|
||||||
|
|
||||||
u8* ResetStart;
|
std::unordered_map<u8*, LoadStorePatch> LoadStorePatches {};
|
||||||
u32 CodeMemSize;
|
|
||||||
|
|
||||||
bool Exit;
|
u8* CodeMemBase;
|
||||||
bool IrregularCycles;
|
u8* ResetStart {};
|
||||||
|
u32 CodeMemSize {};
|
||||||
|
|
||||||
void* ReadBanked;
|
bool Exit {};
|
||||||
void* WriteBanked;
|
bool IrregularCycles {};
|
||||||
|
|
||||||
|
void* ReadBanked {};
|
||||||
|
void* WriteBanked {};
|
||||||
|
|
||||||
bool CPSRDirty = false;
|
bool CPSRDirty = false;
|
||||||
|
|
||||||
FetchedInstr CurInstr;
|
FetchedInstr CurInstr {};
|
||||||
|
|
||||||
RegisterCache<Compiler, Gen::X64Reg> RegCache;
|
RegisterCache<Compiler, Gen::X64Reg> RegCache {};
|
||||||
|
|
||||||
bool Thumb;
|
bool Thumb {};
|
||||||
u32 Num;
|
u32 Num {};
|
||||||
u32 R15;
|
u32 R15 {};
|
||||||
u32 CodeRegion;
|
u32 CodeRegion {};
|
||||||
|
|
||||||
u32 ConstantCycles;
|
u32 ConstantCycles {};
|
||||||
|
|
||||||
ARM* CurCPU;
|
ARM* CurCPU {};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,23 @@
|
||||||
#include "../ARM.h"
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team, RSDuck
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../ARM.h"
|
||||||
|
using namespace melonDS;
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
FILE* f = fopen("ARMJIT_Offsets.h", "w");
|
FILE* f = fopen("ARMJIT_Offsets.h", "w");
|
||||||
|
|
|
@ -1,3 +1,21 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
.intel_syntax noprefix
|
.intel_syntax noprefix
|
||||||
|
|
||||||
#include "ARMJIT_Offsets.h"
|
#include "ARMJIT_Offsets.h"
|
||||||
|
@ -86,3 +104,8 @@ ARM_Ret:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
#if !defined(__APPLE__) && !defined(WIN64)
|
||||||
|
.section .note.GNU-stack,"",@progbits
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,28 @@
|
||||||
#include "ARMJIT_Compiler.h"
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
#include "../Config.h"
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ARMJIT_Compiler.h"
|
||||||
|
#include "../ARMJIT.h"
|
||||||
|
#include "../NDS.h"
|
||||||
|
|
||||||
using namespace Gen;
|
using namespace Gen;
|
||||||
|
|
||||||
namespace ARMJIT
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -35,7 +53,7 @@ u8* Compiler::RewriteMemAccess(u8* pc)
|
||||||
return pc + (ptrdiff_t)patch.Offset;
|
return pc + (ptrdiff_t)patch.Offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("this is a JIT bug %llx\n", pc);
|
Log(LogLevel::Error, "this is a JIT bug %sx\n", pc);
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,12 +69,11 @@ u8* Compiler::RewriteMemAccess(u8* pc)
|
||||||
|
|
||||||
bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
|
bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
|
||||||
{
|
{
|
||||||
u32 localAddr = LocaliseCodeAddress(Num, addr);
|
u32 localAddr = NDS.JIT.LocaliseCodeAddress(Num, addr);
|
||||||
|
|
||||||
int invalidLiteralIdx = InvalidLiterals.Find(localAddr);
|
int invalidLiteralIdx = NDS.JIT.InvalidLiterals.Find(localAddr);
|
||||||
if (invalidLiteralIdx != -1)
|
if (invalidLiteralIdx != -1)
|
||||||
{
|
{
|
||||||
InvalidLiterals.Remove(invalidLiteralIdx);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +86,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
|
||||||
if (size == 32)
|
if (size == 32)
|
||||||
{
|
{
|
||||||
CurCPU->DataRead32(addr & ~0x3, &val);
|
CurCPU->DataRead32(addr & ~0x3, &val);
|
||||||
val = ::ROR(val, (addr & 0x3) << 3);
|
val = melonDS::ROR(val, (addr & 0x3) << 3);
|
||||||
}
|
}
|
||||||
else if (size == 16)
|
else if (size == 16)
|
||||||
{
|
{
|
||||||
|
@ -89,7 +106,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr)
|
||||||
|
|
||||||
if (Thumb || CurInstr.Cond() == 0xE)
|
if (Thumb || CurInstr.Cond() == 0xE)
|
||||||
RegCache.PutLiteral(rd, val);
|
RegCache.PutLiteral(rd, val);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,10 +119,10 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
if (size == 16)
|
if (size == 16)
|
||||||
addressMask = ~1;
|
addressMask = ~1;
|
||||||
|
|
||||||
if (Config::JIT_LiteralOptimisations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
|
if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback)))
|
||||||
{
|
{
|
||||||
u32 addr = R15 + op2.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
|
u32 addr = R15 + op2.Imm * ((flags & memop_SubtractOffset) ? -1 : 1);
|
||||||
|
|
||||||
if (Comp_MemLoadLiteral(size, flags & memop_SignExtend, rd, addr))
|
if (Comp_MemLoadLiteral(size, flags & memop_SignExtend, rd, addr))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -119,7 +136,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
Comp_AddCycles_CDI();
|
Comp_AddCycles_CDI();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool addrIsStatic = Config::JIT_LiteralOptimisations
|
bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled()
|
||||||
&& RegCache.IsLiteral(rn) && op2.IsImm && !(flags & (memop_Writeback|memop_Post));
|
&& RegCache.IsLiteral(rn) && op2.IsImm && !(flags & (memop_Writeback|memop_Post));
|
||||||
u32 staticAddress;
|
u32 staticAddress;
|
||||||
if (addrIsStatic)
|
if (addrIsStatic)
|
||||||
|
@ -130,6 +147,12 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
if (Thumb && rn == 15)
|
if (Thumb && rn == 15)
|
||||||
rnMapped = Imm32(R15 & ~0x2);
|
rnMapped = Imm32(R15 & ~0x2);
|
||||||
|
|
||||||
|
if (flags & memop_Store && flags & (memop_Post|memop_Writeback) && rd == rn)
|
||||||
|
{
|
||||||
|
MOV(32, R(RSCRATCH4), rdMapped);
|
||||||
|
rdMapped = R(RSCRATCH4);
|
||||||
|
}
|
||||||
|
|
||||||
X64Reg finalAddr = RSCRATCH3;
|
X64Reg finalAddr = RSCRATCH3;
|
||||||
if (flags & memop_Post)
|
if (flags & memop_Post)
|
||||||
{
|
{
|
||||||
|
@ -149,7 +172,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
if (!(flags & memop_SubtractOffset) && rm.IsSimpleReg() && rnMapped.IsSimpleReg()
|
if (!(flags & memop_SubtractOffset) && rm.IsSimpleReg() && rnMapped.IsSimpleReg()
|
||||||
&& op2.Reg.Op == 0 && op2.Reg.Amount > 0 && op2.Reg.Amount <= 3)
|
&& op2.Reg.Op == 0 && op2.Reg.Amount > 0 && op2.Reg.Amount <= 3)
|
||||||
{
|
{
|
||||||
LEA(32, finalAddr,
|
LEA(32, finalAddr,
|
||||||
MComplex(rnMapped.GetSimpleReg(), rm.GetSimpleReg(), 1 << op2.Reg.Amount, 0));
|
MComplex(rnMapped.GetSimpleReg(), rm.GetSimpleReg(), 1 << op2.Reg.Amount, 0));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -174,10 +197,10 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
MOV(32, rnMapped, R(finalAddr));
|
MOV(32, rnMapped, R(finalAddr));
|
||||||
|
|
||||||
u32 expectedTarget = Num == 0
|
u32 expectedTarget = Num == 0
|
||||||
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
|
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
|
||||||
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
|
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
|
||||||
|
|
||||||
if (Config::JIT_FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)))
|
if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)))
|
||||||
{
|
{
|
||||||
if (rdMapped.IsImm())
|
if (rdMapped.IsImm())
|
||||||
{
|
{
|
||||||
|
@ -190,12 +213,12 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
|
|
||||||
assert(rdMapped.GetSimpleReg() >= 0 && rdMapped.GetSimpleReg() < 16);
|
assert(rdMapped.GetSimpleReg() >= 0 && rdMapped.GetSimpleReg() < 16);
|
||||||
patch.PatchFunc = flags & memop_Store
|
patch.PatchFunc = flags & memop_Store
|
||||||
? PatchedStoreFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped.GetSimpleReg()]
|
? PatchedStoreFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped.GetSimpleReg()]
|
||||||
: PatchedLoadFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped.GetSimpleReg()];
|
: PatchedLoadFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped.GetSimpleReg()];
|
||||||
|
|
||||||
assert(patch.PatchFunc != NULL);
|
assert(patch.PatchFunc != NULL);
|
||||||
|
|
||||||
MOV(64, R(RSCRATCH), ImmPtr(Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start));
|
MOV(64, R(RSCRATCH), ImmPtr(Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start));
|
||||||
|
|
||||||
X64Reg maskedAddr = RSCRATCH3;
|
X64Reg maskedAddr = RSCRATCH3;
|
||||||
if (size > 8)
|
if (size > 8)
|
||||||
|
@ -242,11 +265,11 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PushRegs(false);
|
PushRegs(false, false);
|
||||||
|
|
||||||
void* func = NULL;
|
void* func = NULL;
|
||||||
if (addrIsStatic)
|
if (addrIsStatic)
|
||||||
func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
|
func = NDS.JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size);
|
||||||
|
|
||||||
if (func)
|
if (func)
|
||||||
{
|
{
|
||||||
|
@ -259,7 +282,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
|
|
||||||
ABI_CallFunction((void (*)())func);
|
ABI_CallFunction((void (*)())func);
|
||||||
|
|
||||||
PopRegs(false);
|
PopRegs(false, false);
|
||||||
|
|
||||||
if (!(flags & memop_Store))
|
if (!(flags & memop_Store))
|
||||||
{
|
{
|
||||||
|
@ -282,33 +305,35 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
{
|
{
|
||||||
if (Num == 0)
|
if (Num == 0)
|
||||||
{
|
{
|
||||||
|
// on Windows param 3 is R8 which is also scratch 4 which can be used for rd
|
||||||
|
if (flags & memop_Store)
|
||||||
|
MOV(32, R(ABI_PARAM3), rdMapped);
|
||||||
|
|
||||||
MOV(64, R(ABI_PARAM2), R(RCPU));
|
MOV(64, R(ABI_PARAM2), R(RCPU));
|
||||||
if (ABI_PARAM1 != RSCRATCH3)
|
if (ABI_PARAM1 != RSCRATCH3)
|
||||||
MOV(32, R(ABI_PARAM1), R(RSCRATCH3));
|
MOV(32, R(ABI_PARAM1), R(RSCRATCH3));
|
||||||
if (flags & memop_Store)
|
if (flags & memop_Store)
|
||||||
{
|
{
|
||||||
MOV(32, R(ABI_PARAM3), rdMapped);
|
switch (size | NDS.ConsoleType)
|
||||||
|
|
||||||
switch (size | NDS::ConsoleType)
|
|
||||||
{
|
{
|
||||||
case 32: CALL((void*)&SlowWrite9<u32, 0>); break;
|
case 32: ABI_CallFunction(SlowWrite9<u32, 0>); break;
|
||||||
case 16: CALL((void*)&SlowWrite9<u16, 0>); break;
|
case 16: ABI_CallFunction(SlowWrite9<u16, 0>); break;
|
||||||
case 8: CALL((void*)&SlowWrite9<u8, 0>); break;
|
case 8: ABI_CallFunction(&SlowWrite9<u8, 0>); break;
|
||||||
case 33: CALL((void*)&SlowWrite9<u32, 1>); break;
|
case 33: ABI_CallFunction(&SlowWrite9<u32, 1>); break;
|
||||||
case 17: CALL((void*)&SlowWrite9<u16, 1>); break;
|
case 17: ABI_CallFunction(&SlowWrite9<u16, 1>); break;
|
||||||
case 9: CALL((void*)&SlowWrite9<u8, 1>); break;
|
case 9: ABI_CallFunction(&SlowWrite9<u8, 1>); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (size | NDS::ConsoleType)
|
switch (size | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 32: CALL((void*)&SlowRead9<u32, 0>); break;
|
case 32: ABI_CallFunction(&SlowRead9<u32, 0>); break;
|
||||||
case 16: CALL((void*)&SlowRead9<u16, 0>); break;
|
case 16: ABI_CallFunction(&SlowRead9<u16, 0>); break;
|
||||||
case 8: CALL((void*)&SlowRead9<u8, 0>); break;
|
case 8: ABI_CallFunction(&SlowRead9<u8, 0>); break;
|
||||||
case 33: CALL((void*)&SlowRead9<u32, 1>); break;
|
case 33: ABI_CallFunction(&SlowRead9<u32, 1>); break;
|
||||||
case 17: CALL((void*)&SlowRead9<u16, 1>); break;
|
case 17: ABI_CallFunction(&SlowRead9<u16, 1>); break;
|
||||||
case 9: CALL((void*)&SlowRead9<u8, 1>); break;
|
case 9: ABI_CallFunction(&SlowRead9<u8, 1>); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,32 +345,32 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
{
|
{
|
||||||
MOV(32, R(ABI_PARAM2), rdMapped);
|
MOV(32, R(ABI_PARAM2), rdMapped);
|
||||||
|
|
||||||
switch (size | NDS::ConsoleType)
|
switch (size | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 32: CALL((void*)&SlowWrite7<u32, 0>); break;
|
case 32: ABI_CallFunction(&SlowWrite7<u32, 0>); break;
|
||||||
case 16: CALL((void*)&SlowWrite7<u16, 0>); break;
|
case 16: ABI_CallFunction(&SlowWrite7<u16, 0>); break;
|
||||||
case 8: CALL((void*)&SlowWrite7<u8, 0>); break;
|
case 8: ABI_CallFunction(&SlowWrite7<u8, 0>); break;
|
||||||
case 33: CALL((void*)&SlowWrite7<u32, 1>); break;
|
case 33: ABI_CallFunction(&SlowWrite7<u32, 1>); break;
|
||||||
case 17: CALL((void*)&SlowWrite7<u16, 1>); break;
|
case 17: ABI_CallFunction(&SlowWrite7<u16, 1>); break;
|
||||||
case 9: CALL((void*)&SlowWrite7<u8, 1>); break;
|
case 9: ABI_CallFunction(&SlowWrite7<u8, 1>); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (size | NDS::ConsoleType)
|
switch (size | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 32: CALL((void*)&SlowRead7<u32, 0>); break;
|
case 32: ABI_CallFunction(&SlowRead7<u32, 0>); break;
|
||||||
case 16: CALL((void*)&SlowRead7<u16, 0>); break;
|
case 16: ABI_CallFunction(&SlowRead7<u16, 0>); break;
|
||||||
case 8: CALL((void*)&SlowRead7<u8, 0>); break;
|
case 8: ABI_CallFunction(&SlowRead7<u8, 0>); break;
|
||||||
case 33: CALL((void*)&SlowRead7<u32, 1>); break;
|
case 33: ABI_CallFunction(&SlowRead7<u32, 1>); break;
|
||||||
case 17: CALL((void*)&SlowRead7<u16, 1>); break;
|
case 17: ABI_CallFunction(&SlowRead7<u16, 1>); break;
|
||||||
case 9: CALL((void*)&SlowRead7<u8, 1>); break;
|
case 9: ABI_CallFunction(&SlowRead7<u8, 1>); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PopRegs(false);
|
PopRegs(false, false);
|
||||||
|
|
||||||
if (!(flags & memop_Store))
|
if (!(flags & memop_Store))
|
||||||
{
|
{
|
||||||
if (flags & memop_SignExtend)
|
if (flags & memop_SignExtend)
|
||||||
|
@ -359,7 +384,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
if (!(flags & memop_Store) && rd == 15)
|
if (!(flags & memop_Store) && rd == 15)
|
||||||
{
|
{
|
||||||
if (size < 32)
|
if (size < 32)
|
||||||
printf("!!! LDR <32 bit PC %08X %x\n", R15, CurInstr.Instr);
|
Log(LogLevel::Debug, "!!! LDR <32 bit PC %08X %x\n", R15, CurInstr.Instr);
|
||||||
{
|
{
|
||||||
if (Num == 1)
|
if (Num == 1)
|
||||||
{
|
{
|
||||||
|
@ -373,14 +398,15 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode)
|
s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn)
|
||||||
{
|
{
|
||||||
int regsCount = regs.Count();
|
int regsCount = regs.Count();
|
||||||
|
|
||||||
if (regsCount == 0)
|
if (regsCount == 0)
|
||||||
return 0; // actually not the right behaviour TODO: fix me
|
return 0; // actually not the right behaviour TODO: fix me
|
||||||
|
|
||||||
if (regsCount == 1 && !usermode && RegCache.LoadedRegs & (1 << *regs.begin()))
|
int firstReg = *regs.begin();
|
||||||
|
if (regsCount == 1 && !usermode && RegCache.LoadedRegs & (1 << firstReg) && !(firstReg == rn && skipLoadingRn))
|
||||||
{
|
{
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
if (store)
|
if (store)
|
||||||
|
@ -389,7 +415,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
flags |= memop_SubtractOffset;
|
flags |= memop_SubtractOffset;
|
||||||
Op2 offset = preinc ? Op2(4) : Op2(0);
|
Op2 offset = preinc ? Op2(4) : Op2(0);
|
||||||
|
|
||||||
Comp_MemAccess(*regs.begin(), rn, offset, 32, flags);
|
Comp_MemAccess(firstReg, rn, offset, 32, flags);
|
||||||
|
|
||||||
return decrement ? -4 : 4;
|
return decrement ? -4 : 4;
|
||||||
}
|
}
|
||||||
|
@ -397,16 +423,16 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
s32 offset = (regsCount * 4) * (decrement ? -1 : 1);
|
s32 offset = (regsCount * 4) * (decrement ? -1 : 1);
|
||||||
|
|
||||||
int expectedTarget = Num == 0
|
int expectedTarget = Num == 0
|
||||||
? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion)
|
? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion)
|
||||||
: ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion);
|
: NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion);
|
||||||
|
|
||||||
if (!store)
|
if (!store)
|
||||||
Comp_AddCycles_CDI();
|
Comp_AddCycles_CDI();
|
||||||
else
|
else
|
||||||
Comp_AddCycles_CD();
|
Comp_AddCycles_CD();
|
||||||
|
|
||||||
bool compileFastPath = Config::JIT_FastMemory
|
bool compileFastPath = NDS.JIT.FastMemoryEnabled()
|
||||||
&& !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget));
|
&& !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget));
|
||||||
|
|
||||||
// we need to make sure that the stack stays aligned to 16 bytes
|
// we need to make sure that the stack stays aligned to 16 bytes
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -429,7 +455,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
u8* fastPathStart = GetWritableCodePtr();
|
u8* fastPathStart = GetWritableCodePtr();
|
||||||
u8* loadStoreAddr[16];
|
u8* loadStoreAddr[16];
|
||||||
|
|
||||||
MOV(64, R(RSCRATCH2), ImmPtr(Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start));
|
MOV(64, R(RSCRATCH2), ImmPtr(Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start));
|
||||||
ADD(64, R(RSCRATCH2), R(RSCRATCH4));
|
ADD(64, R(RSCRATCH2), R(RSCRATCH4));
|
||||||
|
|
||||||
u32 offset = 0;
|
u32 offset = 0;
|
||||||
|
@ -456,7 +482,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
{
|
{
|
||||||
if (RegCache.LoadedRegs & (1 << reg))
|
if (RegCache.LoadedRegs & (1 << reg))
|
||||||
{
|
{
|
||||||
MOV(32, MapReg(reg), mem);
|
if (!(reg == rn && skipLoadingRn))
|
||||||
|
MOV(32, MapReg(reg), mem);
|
||||||
|
else
|
||||||
|
MOV(32, R(RSCRATCH), mem); // just touch the memory
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -482,7 +511,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
|
|
||||||
if (!store)
|
if (!store)
|
||||||
{
|
{
|
||||||
PushRegs(false);
|
PushRegs(false, false, !compileFastPath);
|
||||||
|
|
||||||
MOV(32, R(ABI_PARAM1), R(RSCRATCH4));
|
MOV(32, R(ABI_PARAM1), R(RSCRATCH4));
|
||||||
MOV(32, R(ABI_PARAM3), Imm32(regsCount));
|
MOV(32, R(ABI_PARAM3), Imm32(regsCount));
|
||||||
|
@ -495,15 +524,15 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
if (Num == 0)
|
if (Num == 0)
|
||||||
MOV(64, R(ABI_PARAM4), R(RCPU));
|
MOV(64, R(ABI_PARAM4), R(RCPU));
|
||||||
|
|
||||||
switch (Num * 2 | NDS::ConsoleType)
|
switch (Num * 2 | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 0: CALL((void*)&SlowBlockTransfer9<false, 0>); break;
|
case 0: ABI_CallFunction(&SlowBlockTransfer9<false, 0>); break;
|
||||||
case 1: CALL((void*)&SlowBlockTransfer9<false, 1>); break;
|
case 1: ABI_CallFunction(&SlowBlockTransfer9<false, 1>); break;
|
||||||
case 2: CALL((void*)&SlowBlockTransfer7<false, 0>); break;
|
case 2: ABI_CallFunction(&SlowBlockTransfer7<false, 0>); break;
|
||||||
case 3: CALL((void*)&SlowBlockTransfer7<false, 1>); break;
|
case 3: ABI_CallFunction(&SlowBlockTransfer7<false, 1>); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
PopRegs(false);
|
PopRegs(false, false);
|
||||||
|
|
||||||
if (allocOffset)
|
if (allocOffset)
|
||||||
ADD(64, R(RSP), Imm8(allocOffset));
|
ADD(64, R(RSP), Imm8(allocOffset));
|
||||||
|
@ -522,12 +551,15 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
MOV(32, R(RSCRATCH2), Imm32(reg - 8));
|
MOV(32, R(RSCRATCH2), Imm32(reg - 8));
|
||||||
POP(RSCRATCH3);
|
POP(RSCRATCH3);
|
||||||
CALL(WriteBanked);
|
CALL(WriteBanked);
|
||||||
FixupBranch sucessfulWritten = J_CC(CC_NC);
|
if (!(reg == rn && skipLoadingRn))
|
||||||
if (RegCache.LoadedRegs & (1 << reg))
|
{
|
||||||
MOV(32, R(RegCache.Mapping[reg]), R(RSCRATCH3));
|
FixupBranch sucessfulWritten = J_CC(CC_NC);
|
||||||
else
|
if (RegCache.LoadedRegs & (1 << reg))
|
||||||
SaveReg(reg, RSCRATCH3);
|
MOV(32, R(RegCache.Mapping[reg]), R(RSCRATCH3));
|
||||||
SetJumpTarget(sucessfulWritten);
|
else
|
||||||
|
SaveReg(reg, RSCRATCH3);
|
||||||
|
SetJumpTarget(sucessfulWritten);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!(RegCache.LoadedRegs & (1 << reg)))
|
else if (!(RegCache.LoadedRegs & (1 << reg)))
|
||||||
{
|
{
|
||||||
|
@ -536,6 +568,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
POP(RSCRATCH);
|
POP(RSCRATCH);
|
||||||
SaveReg(reg, RSCRATCH);
|
SaveReg(reg, RSCRATCH);
|
||||||
}
|
}
|
||||||
|
else if (reg == rn && skipLoadingRn)
|
||||||
|
{
|
||||||
|
ADD(64, R(RSP), Imm8(8));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
POP(MapReg(reg).GetSimpleReg());
|
POP(MapReg(reg).GetSimpleReg());
|
||||||
|
@ -580,29 +616,29 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
if (allocOffset)
|
if (allocOffset)
|
||||||
SUB(64, R(RSP), Imm8(allocOffset));
|
SUB(64, R(RSP), Imm8(allocOffset));
|
||||||
|
|
||||||
PushRegs(false);
|
PushRegs(false, false, !compileFastPath);
|
||||||
|
|
||||||
MOV(32, R(ABI_PARAM1), R(RSCRATCH4));
|
MOV(32, R(ABI_PARAM1), R(RSCRATCH4));
|
||||||
if (allocOffset)
|
if (allocOffset)
|
||||||
LEA(64, ABI_PARAM2, MDisp(RSP, allocOffset));
|
LEA(64, ABI_PARAM2, MDisp(RSP, allocOffset));
|
||||||
else
|
else
|
||||||
MOV(64, R(ABI_PARAM2), R(RSP));
|
MOV(64, R(ABI_PARAM2), R(RSP));
|
||||||
|
|
||||||
MOV(32, R(ABI_PARAM3), Imm32(regsCount));
|
MOV(32, R(ABI_PARAM3), Imm32(regsCount));
|
||||||
if (Num == 0)
|
if (Num == 0)
|
||||||
MOV(64, R(ABI_PARAM4), R(RCPU));
|
MOV(64, R(ABI_PARAM4), R(RCPU));
|
||||||
|
|
||||||
switch (Num * 2 | NDS::ConsoleType)
|
switch (Num * 2 | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 0: CALL((void*)&SlowBlockTransfer9<true, 0>); break;
|
case 0: ABI_CallFunction(&SlowBlockTransfer9<true, 0>); break;
|
||||||
case 1: CALL((void*)&SlowBlockTransfer9<true, 1>); break;
|
case 1: ABI_CallFunction(&SlowBlockTransfer9<true, 1>); break;
|
||||||
case 2: CALL((void*)&SlowBlockTransfer7<true, 0>); break;
|
case 2: ABI_CallFunction(&SlowBlockTransfer7<true, 0>); break;
|
||||||
case 3: CALL((void*)&SlowBlockTransfer7<true, 1>); break;
|
case 3: ABI_CallFunction(&SlowBlockTransfer7<true, 1>); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ADD(64, R(RSP), stackAlloc <= INT8_MAX ? Imm8(stackAlloc) : Imm32(stackAlloc));
|
ADD(64, R(RSP), stackAlloc <= INT8_MAX ? Imm8(stackAlloc) : Imm32(stackAlloc));
|
||||||
|
|
||||||
PopRegs(false);
|
PopRegs(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compileFastPath)
|
if (compileFastPath)
|
||||||
|
@ -632,7 +668,7 @@ void Compiler::A_Comp_MemWB()
|
||||||
bool load = CurInstr.Instr & (1 << 20);
|
bool load = CurInstr.Instr & (1 << 20);
|
||||||
bool byte = CurInstr.Instr & (1 << 22);
|
bool byte = CurInstr.Instr & (1 << 22);
|
||||||
int size = byte ? 8 : 32;
|
int size = byte ? 8 : 32;
|
||||||
|
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
if (!load)
|
if (!load)
|
||||||
flags |= memop_Store;
|
flags |= memop_Store;
|
||||||
|
@ -706,7 +742,7 @@ void Compiler::T_Comp_MemReg()
|
||||||
bool load = op & 0x2;
|
bool load = op & 0x2;
|
||||||
bool byte = op & 0x1;
|
bool byte = op & 0x1;
|
||||||
|
|
||||||
Comp_MemAccess(CurInstr.T_Reg(0), CurInstr.T_Reg(3), Op2(CurInstr.T_Reg(6), 0, 0),
|
Comp_MemAccess(CurInstr.T_Reg(0), CurInstr.T_Reg(3), Op2(CurInstr.T_Reg(6), 0, 0),
|
||||||
byte ? 8 : 32, load ? 0 : memop_Store);
|
byte ? 8 : 32, load ? 0 : memop_Store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,14 +758,14 @@ void Compiler::A_Comp_LDM_STM()
|
||||||
|
|
||||||
OpArg rn = MapReg(CurInstr.A_Reg(16));
|
OpArg rn = MapReg(CurInstr.A_Reg(16));
|
||||||
|
|
||||||
s32 offset = Comp_MemAccessBlock(CurInstr.A_Reg(16), regs, !load, pre, !add, usermode);
|
|
||||||
|
|
||||||
if (load && writeback && regs[CurInstr.A_Reg(16)])
|
if (load && writeback && regs[CurInstr.A_Reg(16)])
|
||||||
writeback = Num == 0
|
writeback = Num == 0
|
||||||
? (!(regs & ~BitSet16(1 << CurInstr.A_Reg(16)))) || (regs & ~BitSet16((2 << CurInstr.A_Reg(16)) - 1))
|
&& (!(regs & ~BitSet16(1 << CurInstr.A_Reg(16)))) || (regs & ~BitSet16((2 << CurInstr.A_Reg(16)) - 1));
|
||||||
: false;
|
|
||||||
if (writeback)
|
s32 offset = Comp_MemAccessBlock(CurInstr.A_Reg(16), regs, !load, pre, !add, usermode, load && writeback);
|
||||||
ADD(32, rn, offset >= INT8_MIN && offset < INT8_MAX ? Imm8(offset) : Imm32(offset));
|
|
||||||
|
if (writeback && offset)
|
||||||
|
ADD(32, rn, Imm32(offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::T_Comp_MemImm()
|
void Compiler::T_Comp_MemImm()
|
||||||
|
@ -773,7 +809,7 @@ void Compiler::T_Comp_LoadPCRel()
|
||||||
{
|
{
|
||||||
u32 offset = (CurInstr.Instr & 0xFF) << 2;
|
u32 offset = (CurInstr.Instr & 0xFF) << 2;
|
||||||
u32 addr = (R15 & ~0x2) + offset;
|
u32 addr = (R15 & ~0x2) + offset;
|
||||||
if (!Config::JIT_LiteralOptimisations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
|
if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr))
|
||||||
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
|
Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -799,9 +835,10 @@ void Compiler::T_Comp_PUSH_POP()
|
||||||
}
|
}
|
||||||
|
|
||||||
OpArg sp = MapReg(13);
|
OpArg sp = MapReg(13);
|
||||||
s32 offset = Comp_MemAccessBlock(13, regs, !load, !load, !load, false);
|
s32 offset = Comp_MemAccessBlock(13, regs, !load, !load, !load, false, false);
|
||||||
|
|
||||||
ADD(32, sp, Imm8(offset)); // offset will be always be in range since PUSH accesses 9 regs max
|
if (offset)
|
||||||
|
ADD(32, sp, Imm8(offset)); // offset will be always be in range since PUSH accesses 9 regs max
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::T_Comp_LDMIA_STMIA()
|
void Compiler::T_Comp_LDMIA_STMIA()
|
||||||
|
@ -810,9 +847,11 @@ void Compiler::T_Comp_LDMIA_STMIA()
|
||||||
OpArg rb = MapReg(CurInstr.T_Reg(8));
|
OpArg rb = MapReg(CurInstr.T_Reg(8));
|
||||||
bool load = CurInstr.Instr & (1 << 11);
|
bool load = CurInstr.Instr & (1 << 11);
|
||||||
|
|
||||||
s32 offset = Comp_MemAccessBlock(CurInstr.T_Reg(8), regs, !load, false, false, false);
|
bool writeback = !load || !regs[CurInstr.T_Reg(8)];
|
||||||
|
|
||||||
if (!load || !regs[CurInstr.T_Reg(8)])
|
s32 offset = Comp_MemAccessBlock(CurInstr.T_Reg(8), regs, !load, false, false, false, load && writeback);
|
||||||
|
|
||||||
|
if (writeback && offset)
|
||||||
ADD(32, rb, Imm8(offset));
|
ADD(32, rb, Imm8(offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,21 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team, RSDuck
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#define ARM_CPSR_offset 0x64
|
#define ARM_CPSR_offset 0x64
|
||||||
#define ARM_Cycles_offset 0xc
|
#define ARM_Cycles_offset 0xc
|
||||||
#define ARM_StopExecution_offset 0x10
|
#define ARM_StopExecution_offset 0x10
|
||||||
|
|
|
@ -1,10 +1,28 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "ARM_InstrInfo.h"
|
#include "ARM_InstrInfo.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "Config.h"
|
#include "ARMJIT.h"
|
||||||
|
|
||||||
namespace ARMInstrInfo
|
namespace melonDS::ARMInstrInfo
|
||||||
{
|
{
|
||||||
|
|
||||||
#define ak(x) ((x) << 23)
|
#define ak(x) ((x) << 23)
|
||||||
|
@ -212,7 +230,7 @@ enum {
|
||||||
T_SetMaybeC = 1 << 17,
|
T_SetMaybeC = 1 << 17,
|
||||||
T_ReadC = 1 << 18,
|
T_ReadC = 1 << 18,
|
||||||
T_SetC = 1 << 19,
|
T_SetC = 1 << 19,
|
||||||
|
|
||||||
T_WriteMem = 1 << 20,
|
T_WriteMem = 1 << 20,
|
||||||
T_LoadMem = 1 << 21,
|
T_LoadMem = 1 << 21,
|
||||||
};
|
};
|
||||||
|
@ -297,7 +315,7 @@ const u32 T_SVC = T_BranchAlways | T_WriteR14 | tk(tk_SVC);
|
||||||
#include "ARM_InstrTable.h"
|
#include "ARM_InstrTable.h"
|
||||||
#undef INSTRFUNC_PROTO
|
#undef INSTRFUNC_PROTO
|
||||||
|
|
||||||
Info Decode(bool thumb, u32 num, u32 instr)
|
Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations)
|
||||||
{
|
{
|
||||||
const u8 FlagsReadPerCond[7] = {
|
const u8 FlagsReadPerCond[7] = {
|
||||||
flag_Z,
|
flag_Z,
|
||||||
|
@ -327,7 +345,7 @@ Info Decode(bool thumb, u32 num, u32 instr)
|
||||||
res.DstRegs |= 1 << (instr & 0x7);
|
res.DstRegs |= 1 << (instr & 0x7);
|
||||||
if (data & T_Write8)
|
if (data & T_Write8)
|
||||||
res.DstRegs |= 1 << ((instr >> 8) & 0x7);
|
res.DstRegs |= 1 << ((instr >> 8) & 0x7);
|
||||||
|
|
||||||
if (data & T_ReadHi0)
|
if (data & T_ReadHi0)
|
||||||
res.SrcRegs |= 1 << ((instr & 0x7) | ((instr >> 4) & 0x8));
|
res.SrcRegs |= 1 << ((instr & 0x7) | ((instr >> 4) & 0x8));
|
||||||
if (data & T_ReadHi3)
|
if (data & T_ReadHi3)
|
||||||
|
@ -363,12 +381,12 @@ Info Decode(bool thumb, u32 num, u32 instr)
|
||||||
|
|
||||||
if (data & T_WriteMem)
|
if (data & T_WriteMem)
|
||||||
res.SpecialKind = special_WriteMem;
|
res.SpecialKind = special_WriteMem;
|
||||||
|
|
||||||
if (data & T_LoadMem)
|
if (data & T_LoadMem)
|
||||||
{
|
{
|
||||||
if (res.Kind == tk_LDR_PCREL)
|
if (res.Kind == tk_LDR_PCREL)
|
||||||
{
|
{
|
||||||
if (!Config::JIT_LiteralOptimisations)
|
if (!literaloptimizations)
|
||||||
res.SrcRegs |= 1 << 15;
|
res.SrcRegs |= 1 << 15;
|
||||||
res.SpecialKind = special_LoadLiteral;
|
res.SpecialKind = special_LoadLiteral;
|
||||||
}
|
}
|
||||||
|
@ -453,18 +471,18 @@ Info Decode(bool thumb, u32 num, u32 instr)
|
||||||
res.SrcRegs |= 1 << ((instr >> 8) & 0xF);
|
res.SrcRegs |= 1 << ((instr >> 8) & 0xF);
|
||||||
if (data & A_Read12)
|
if (data & A_Read12)
|
||||||
res.SrcRegs |= 1 << ((instr >> 12) & 0xF);
|
res.SrcRegs |= 1 << ((instr >> 12) & 0xF);
|
||||||
|
|
||||||
if (data & A_Write12)
|
if (data & A_Write12)
|
||||||
res.DstRegs |= 1 << ((instr >> 12) & 0xF);
|
res.DstRegs |= 1 << ((instr >> 12) & 0xF);
|
||||||
if (data & A_Write16)
|
if (data & A_Write16)
|
||||||
res.DstRegs |= 1 << ((instr >> 16) & 0xF);
|
res.DstRegs |= 1 << ((instr >> 16) & 0xF);
|
||||||
|
|
||||||
if (data & A_MemWriteback && instr & (1 << 21))
|
if (data & A_MemWriteback && instr & (1 << 21))
|
||||||
res.DstRegs |= 1 << ((instr >> 16) & 0xF);
|
res.DstRegs |= 1 << ((instr >> 16) & 0xF);
|
||||||
|
|
||||||
if (data & A_BranchAlways)
|
if (data & A_BranchAlways)
|
||||||
res.DstRegs |= 1 << 15;
|
res.DstRegs |= 1 << 15;
|
||||||
|
|
||||||
if (data & A_Read12Double)
|
if (data & A_Read12Double)
|
||||||
{
|
{
|
||||||
res.SrcRegs |= 1 << ((instr >> 12) & 0xF);
|
res.SrcRegs |= 1 << ((instr >> 12) & 0xF);
|
||||||
|
@ -508,16 +526,21 @@ Info Decode(bool thumb, u32 num, u32 instr)
|
||||||
if (data & A_LoadMem)
|
if (data & A_LoadMem)
|
||||||
{
|
{
|
||||||
if (res.SrcRegs == (1 << 15))
|
if (res.SrcRegs == (1 << 15))
|
||||||
res.SpecialKind = special_LoadLiteral;
|
res.SpecialKind = special_LoadLiteral;
|
||||||
else
|
else
|
||||||
res.SpecialKind = special_LoadMem;
|
res.SpecialKind = special_LoadMem;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.Kind == ak_LDM)
|
if (res.Kind == ak_LDM)
|
||||||
{
|
{
|
||||||
u16 set = (instr & 0xFFFF);
|
u16 set = (instr & 0xFFFF);
|
||||||
res.NotStrictlyNeeded |= set & ~(res.SrcRegs|res.DstRegs|(1<<15));
|
res.NotStrictlyNeeded |= set & ~(res.SrcRegs|res.DstRegs|(1<<15));
|
||||||
res.DstRegs |= set;
|
res.DstRegs |= set;
|
||||||
|
// when the instruction is executed not in usermode a banked register in memory will be written to
|
||||||
|
// but the unbanked register will still be allocated, so it is expected to carry the proper value
|
||||||
|
// thus it is a source register
|
||||||
|
if (instr & (1<<22))
|
||||||
|
res.SrcRegs |= set & 0x7F00;
|
||||||
}
|
}
|
||||||
if (res.Kind == ak_STM)
|
if (res.Kind == ak_STM)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +1,27 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef ARMINSTRINFO_H
|
#ifndef ARMINSTRINFO_H
|
||||||
#define ARMINSTRINFO_H
|
#define ARMINSTRINFO_H
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
namespace ARMInstrInfo
|
namespace melonDS::ARMInstrInfo
|
||||||
{
|
{
|
||||||
|
|
||||||
// Instruction kinds, for faster dispatch
|
// Instruction kinds, for faster dispatch
|
||||||
|
@ -204,7 +222,7 @@ enum
|
||||||
tk_POP,
|
tk_POP,
|
||||||
tk_LDMIA,
|
tk_LDMIA,
|
||||||
tk_STMIA,
|
tk_STMIA,
|
||||||
|
|
||||||
tk_BCOND,
|
tk_BCOND,
|
||||||
tk_BX,
|
tk_BX,
|
||||||
tk_BLX_REG,
|
tk_BLX_REG,
|
||||||
|
@ -256,8 +274,8 @@ struct Info
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Info Decode(bool thumb, u32 num, u32 instr);
|
Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|