mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-09 17:03:16 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39025af1fe | ||
|
|
5cdefb088d | ||
|
|
d0253599a6 | ||
|
|
1e83da0241 | ||
|
|
ed0a1c8852 | ||
|
|
dd9c728a12 | ||
|
|
7ec1e1430c | ||
|
|
16379aabff | ||
|
|
11f24b47c3 | ||
|
|
32f205975c | ||
|
|
21ca9cf76e | ||
|
|
d32b3ffe45 | ||
|
|
5864382c67 | ||
|
|
f392e4f428 | ||
|
|
d0075978f8 | ||
|
|
c1e5777dc1 | ||
|
|
ec6c9fc330 | ||
|
|
9bc95db1f0 | ||
|
|
6d80a8e940 | ||
|
|
2a0f9651b9 | ||
|
|
51a10b0bd0 | ||
|
|
7e2f490fd0 | ||
|
|
2063393f20 | ||
|
|
4c15ed753e | ||
|
|
704fc91b9c | ||
|
|
757d74bac0 | ||
|
|
ff36889d47 | ||
|
|
3d10cd34d6 | ||
|
|
acaa1cabb5 | ||
|
|
e8f38d15f7 | ||
|
|
527aaefef1 | ||
|
|
a938ef8aa8 | ||
|
|
91658b989a | ||
|
|
801eaa4088 | ||
|
|
d6b2ca53e2 | ||
|
|
d823641ccc | ||
|
|
dec675896d | ||
|
|
e2caf175d6 | ||
|
|
322c99ca75 | ||
|
|
b28bb3dbd7 | ||
|
|
3f4334e5ff | ||
|
|
99a40e43ef | ||
|
|
9bbe36d73c | ||
|
|
4bae7965f9 | ||
|
|
9e4b3795f1 | ||
|
|
a82913c1f9 | ||
|
|
b7db1c074b | ||
|
|
1478637209 | ||
|
|
432c4e3b2a | ||
|
|
18c84a83e4 | ||
|
|
c4bc588082 | ||
|
|
ed3083db8a | ||
|
|
6d5ef628e8 | ||
|
|
6e8f4f224a | ||
|
|
4bdeb3727f | ||
|
|
20a3aac83b | ||
|
|
2950d3fe44 | ||
|
|
b55c5010f3 | ||
|
|
f893afca89 | ||
|
|
0d131bf4d8 | ||
|
|
7e99155f8e | ||
|
|
c95dc1bad7 | ||
|
|
b85395fe7b | ||
|
|
91c46dbad4 | ||
|
|
6a253b17af | ||
|
|
4116855924 | ||
|
|
1146c0def3 | ||
|
|
e176d8b3ee | ||
|
|
9b5e339cc1 | ||
|
|
8f626e304a | ||
|
|
bbe9a9b0a4 | ||
|
|
c00e402eeb | ||
|
|
5e6afeaa5e | ||
|
|
a3222abe79 | ||
|
|
b0ee66d4ad | ||
|
|
6567d9dcf0 | ||
|
|
9a8e7f8b6e | ||
|
|
1c9d38895d | ||
|
|
cd11421d72 | ||
|
|
079295e2ca | ||
|
|
2a819403db | ||
|
|
df031a7d4c | ||
|
|
0dd3edebe0 | ||
|
|
f9d38f5daf | ||
|
|
d381bb4678 | ||
|
|
3392783359 | ||
|
|
67c6bfa4a5 | ||
|
|
8cea0b8329 | ||
|
|
4f7feeafd7 | ||
|
|
bcb60a6e1b | ||
|
|
af0713858c | ||
|
|
d3b11e4339 | ||
|
|
7faebad713 | ||
|
|
92c1c2ec44 | ||
|
|
bff2738163 | ||
|
|
8a4bf9d82c | ||
|
|
4537145d24 | ||
|
|
4544e1df69 | ||
|
|
30cc370e40 | ||
|
|
4a48753ea3 | ||
|
|
52df41a709 | ||
|
|
dc385dbe6e | ||
|
|
df8d5a6157 | ||
|
|
de1c12737a | ||
|
|
da9c82956a | ||
|
|
4dd837ae41 | ||
|
|
fa0f9a715f | ||
|
|
87ae65a094 | ||
|
|
0ceaf17be6 | ||
|
|
e7760bfc51 | ||
|
|
c27f369e31 | ||
|
|
020410ea0b | ||
|
|
6fbea44d3d | ||
|
|
d2e20f0d05 | ||
|
|
e589f8d9cf | ||
|
|
d201d6a547 | ||
|
|
7dc11a1c39 | ||
|
|
89e0001275 | ||
|
|
5b744c324d | ||
|
|
a47bbea7a2 | ||
|
|
414c205256 | ||
|
|
f1fb33af8e | ||
|
|
84e3663e80 | ||
|
|
77bfad91d8 | ||
|
|
cd8d84ed5b | ||
|
|
44b0c2c532 | ||
|
|
02dcc63431 | ||
|
|
864bc6e6d6 | ||
|
|
a572a97d10 | ||
|
|
939617aad9 | ||
|
|
ca6ada73ab | ||
|
|
d8e462035d | ||
|
|
e457e05c8b | ||
|
|
da3adb4daa | ||
|
|
3ebfb4433e | ||
|
|
0bea9d3a2e | ||
|
|
785edf60c8 | ||
|
|
62f5c36c17 | ||
|
|
ed35f2249b | ||
|
|
3fa22458f4 | ||
|
|
708247ab38 | ||
|
|
88a14fb5f2 | ||
|
|
5f55b6cf27 | ||
|
|
74e10c45f3 | ||
|
|
5984867fce | ||
|
|
dd01cf762b | ||
|
|
bfa1eeafde | ||
|
|
0661e9858c | ||
|
|
7b433f305f | ||
|
|
1c61fc95e6 | ||
|
|
401df969b0 | ||
|
|
5deec7ede5 | ||
|
|
6a7de62674 | ||
|
|
b7be6612b9 | ||
|
|
f976cdc3a8 | ||
|
|
e369049d0a | ||
|
|
72d8179bf0 | ||
|
|
8ed4af8b57 | ||
|
|
bcddb7a68f | ||
|
|
5c9f5419b0 | ||
|
|
4b3b78b77e | ||
|
|
07c320e55a | ||
|
|
506820bac7 | ||
|
|
46ddcac339 | ||
|
|
1349d45a51 | ||
|
|
ff11f77f92 | ||
|
|
f9c110cd49 | ||
|
|
379091bada | ||
|
|
ba475a7a59 | ||
|
|
06e1ac13ef | ||
|
|
675026ffdd | ||
|
|
f0449e36ff | ||
|
|
0f265028f2 | ||
|
|
0b2544a6cf | ||
|
|
99afcff4c7 | ||
|
|
a652e85069 | ||
|
|
51061e16af | ||
|
|
4e66bc0b32 | ||
|
|
0c6784294c | ||
|
|
2a6454a710 | ||
|
|
4f5b5da64f | ||
|
|
562bcd7b20 | ||
|
|
db5f7d7867 | ||
|
|
3e9ee7423d | ||
|
|
1215b37448 | ||
|
|
85488a7404 | ||
|
|
5eccee8457 | ||
|
|
671669b209 | ||
|
|
fe9a80b9eb | ||
|
|
4216a0c2c4 | ||
|
|
8ba3bd6348 | ||
|
|
546aed6852 | ||
|
|
b11ff923b9 | ||
|
|
4509471ce2 | ||
|
|
72cd3c5a65 | ||
|
|
929ad45380 | ||
|
|
bb67742864 | ||
|
|
b439a0d90a | ||
|
|
a96395a1c1 | ||
|
|
70675c0644 | ||
|
|
950d9a1967 | ||
|
|
2682e929d8 | ||
|
|
cd924569d2 | ||
|
|
0d27a86e01 | ||
|
|
38628f9c85 | ||
|
|
405ac8289c | ||
|
|
b29fb91777 | ||
|
|
9280622d29 | ||
|
|
714ecc1322 | ||
|
|
04525a7426 | ||
|
|
cc3f4543b1 | ||
|
|
2f65f315e1 | ||
|
|
de57072f83 | ||
|
|
9225847d40 | ||
|
|
6669e3e84b | ||
|
|
703e2ea49a | ||
|
|
40a6b8d36a | ||
|
|
d5381e47a2 | ||
|
|
7ea42e3201 | ||
|
|
c7c80d07ee | ||
|
|
5faa14b504 | ||
|
|
1647d3f4ef | ||
|
|
62eadd89e0 | ||
|
|
9fae1a48c2 | ||
|
|
166856f4dd | ||
|
|
48e4ee55c0 | ||
|
|
e8b85524d9 | ||
|
|
3854a1d1e8 | ||
|
|
090f8765c2 | ||
|
|
8ae96164ef | ||
|
|
5a647e77ad | ||
|
|
6c7bdf9c1a | ||
|
|
21a730b037 | ||
|
|
5d38bd3744 | ||
|
|
3d3838d8b4 | ||
|
|
d1ddbd504a | ||
|
|
c88f26230e | ||
|
|
ecab4fdca0 | ||
|
|
3581be3c44 | ||
|
|
2950cef605 | ||
|
|
aab3b447e7 | ||
|
|
ea665be25a | ||
|
|
e8f4554f24 | ||
|
|
0ba0e5f85e | ||
|
|
7dea8162db | ||
|
|
d4fd326630 | ||
|
|
cb19cdbf90 | ||
|
|
ec12ccc6ff | ||
|
|
59deec8b45 | ||
|
|
ef53e87b31 | ||
|
|
631085f1e6 | ||
|
|
543dec41b8 | ||
|
|
72ab5278b2 | ||
|
|
93937e8603 | ||
|
|
8476c880bc | ||
|
|
e39efe170b | ||
|
|
133044afa9 | ||
|
|
c78f06e208 | ||
|
|
f5dc3bbd48 | ||
|
|
ea688c7721 | ||
|
|
35c8f5f760 | ||
|
|
97aa37f040 | ||
|
|
8752695b4d | ||
|
|
cea1adcb44 | ||
|
|
91b6c7ac14 | ||
|
|
862bfec9e9 | ||
|
|
dad3db5498 | ||
|
|
f396a9cf60 | ||
|
|
beaab1791a | ||
|
|
cc6b26c8d7 | ||
|
|
a5319fdc07 | ||
|
|
c2c3c0f0f8 | ||
|
|
3c773a90eb | ||
|
|
8574a5879b | ||
|
|
3fd03e7a32 | ||
|
|
0130187f82 | ||
|
|
fde1f55f42 | ||
|
|
5337a4bfb8 | ||
|
|
6bf0d1ffcd | ||
|
|
aff3513931 | ||
|
|
9fb87dc0c9 | ||
|
|
a10b9ce0e9 | ||
|
|
ffcce5823c | ||
|
|
78f439dad1 | ||
|
|
251ea7c0de | ||
|
|
12b4530f39 | ||
|
|
c4a5fcea9a | ||
|
|
ef3d784ae4 | ||
|
|
14552167a2 | ||
|
|
8593f3e980 | ||
|
|
a617f2160d | ||
|
|
a18ad47482 | ||
|
|
e6c0c15114 | ||
|
|
2cfb4a98ec | ||
|
|
8a89b5a53a | ||
|
|
352f2202e2 | ||
|
|
7233efbc39 | ||
|
|
dbd779376a | ||
|
|
ecba464a58 | ||
|
|
fb711deb0d | ||
|
|
d901f4e336 | ||
|
|
631b0f6452 | ||
|
|
3de3f99ad1 | ||
|
|
d201c4827a | ||
|
|
2ccd7ba670 | ||
|
|
8e62cd8162 | ||
|
|
8b45d04288 | ||
|
|
c94c7a9b85 | ||
|
|
ee6a7fe78d | ||
|
|
d3a71803ed | ||
|
|
1e5eed8f21 | ||
|
|
697b3b6503 | ||
|
|
7812cc1818 | ||
|
|
499c964986 | ||
|
|
49fac42e69 | ||
|
|
2b746cd26c | ||
|
|
dafb57010c | ||
|
|
cab7096e0f | ||
|
|
a3d6392e1c | ||
|
|
b4221bcaaf | ||
|
|
feab2746da | ||
|
|
eccd72d17f | ||
|
|
d3228ea52d | ||
|
|
bff8a1413f | ||
|
|
d6f216928c | ||
|
|
92099bec6f | ||
|
|
1b148ab9cb | ||
|
|
d3e7ced3b4 | ||
|
|
9cf118a72f | ||
|
|
cc81fd1015 | ||
|
|
da45ad792b | ||
|
|
8feff4afa3 | ||
|
|
104e4bd66d | ||
|
|
5f891de18a | ||
|
|
83b43e8405 | ||
|
|
682fc36ac7 | ||
|
|
d7fd2ccba8 | ||
|
|
66e01e02cc | ||
|
|
76cf49834a | ||
|
|
f8f1729cbe | ||
|
|
181570e50d | ||
|
|
878419da0e | ||
|
|
fbaf7d8118 | ||
|
|
345a7cb89f | ||
|
|
2650062d70 | ||
|
|
2b0c91e3c3 | ||
|
|
15bb0e0593 | ||
|
|
2e472e8401 | ||
|
|
53af7cbbcc | ||
|
|
bafa58534a | ||
|
|
495803ea63 | ||
|
|
7f09b67df9 | ||
|
|
8ddd630850 | ||
|
|
5f9804fb65 | ||
|
|
e142c3be5a | ||
|
|
6e367916da | ||
|
|
c4863ef672 | ||
|
|
ad978f3daf | ||
|
|
fbdd42beb6 | ||
|
|
a349770472 | ||
|
|
08c00d186a | ||
|
|
4c3786b067 | ||
|
|
a8f3e353cd | ||
|
|
98dc6faefb | ||
|
|
41157ad555 | ||
|
|
3e9ce82ccb | ||
|
|
43cfbd629c | ||
|
|
509d749663 | ||
|
|
71d5a59c19 | ||
|
|
bf9caa706c | ||
|
|
8bac0decbe | ||
|
|
4e73783cc1 | ||
|
|
257a6db658 | ||
|
|
e02938c23f | ||
|
|
f0fe40d5da | ||
|
|
990fd4e21c | ||
|
|
6769031bfb | ||
|
|
013b4f2645 | ||
|
|
75821d404b | ||
|
|
0311d1ca56 | ||
|
|
6a7898396a | ||
|
|
a4dbeb45d1 | ||
|
|
d13715d8bb | ||
|
|
03bfe5a3ab | ||
|
|
9b32ef7faa | ||
|
|
c05d7b2070 | ||
|
|
239a902a77 | ||
|
|
b7da32113f | ||
|
|
cbc3acada1 | ||
|
|
75e17ca213 | ||
|
|
0d376ddb24 | ||
|
|
cff8b55310 | ||
|
|
acff6a93ad | ||
|
|
fdea53cad4 | ||
|
|
ca03794534 | ||
|
|
4c646c539e | ||
|
|
7c74821b78 | ||
|
|
019ceea8d6 | ||
|
|
33a406701e | ||
|
|
9bfa1fe7ce | ||
|
|
21c24b31ad | ||
|
|
98b3c8ef7b | ||
|
|
f514327594 | ||
|
|
4c3b7b636f | ||
|
|
2c867d37e6 | ||
|
|
901cfd7e52 | ||
|
|
7565591cf8 | ||
|
|
6502327ad2 | ||
|
|
d5cba61203 | ||
|
|
3ae339ae14 | ||
|
|
26d63dd04a | ||
|
|
f9d54ebbb0 | ||
|
|
1508f6b488 | ||
|
|
146bc3764d | ||
|
|
4cfa728413 | ||
|
|
600964d722 | ||
|
|
241f0e9a36 | ||
|
|
462afe89f8 | ||
|
|
7e237528b4 | ||
|
|
076d73fafb | ||
|
|
9ab140b289 | ||
|
|
4418470316 | ||
|
|
fda7c79b51 | ||
|
|
4713fb37cc | ||
|
|
84b5222332 | ||
|
|
f11ad490cd | ||
|
|
4b2b00a834 | ||
|
|
608556e0a9 | ||
|
|
f2ca7a973c | ||
|
|
be869f9772 | ||
|
|
5a4d333734 | ||
|
|
81692a076c | ||
|
|
b6350b3c0f | ||
|
|
0734768955 | ||
|
|
4aa3b27de2 | ||
|
|
89b3e5a20b | ||
|
|
65a3d38a2a | ||
|
|
aa9c641083 | ||
|
|
b178d3d51a | ||
|
|
32bfb2ed08 | ||
|
|
7f7cc868c4 | ||
|
|
334101d599 | ||
|
|
65e6d0a397 | ||
|
|
fff03a3d56 | ||
|
|
bbbd621130 | ||
|
|
fc50ad03ae | ||
|
|
b7a1fe926a | ||
|
|
c4588ee4f4 | ||
|
|
21d5bf4183 | ||
|
|
e38b7e586d | ||
|
|
e01d5c1866 | ||
|
|
4e96e39ab2 | ||
|
|
9fce98f7d7 | ||
|
|
91c0844f19 | ||
|
|
b872687145 | ||
|
|
8914915b31 | ||
|
|
188171b49d | ||
|
|
4befb14e47 | ||
|
|
42118b22e4 | ||
|
|
8af10a6816 | ||
|
|
6dd3fdca68 | ||
|
|
56a848b59d | ||
|
|
ac06c41145 | ||
|
|
267b6e0f21 | ||
|
|
164775e4a7 | ||
|
|
c2004f6ad5 | ||
|
|
832a2ada10 | ||
|
|
383d2db303 | ||
|
|
358985e8c1 | ||
|
|
41b6147ca7 | ||
|
|
6868f1bf1d | ||
|
|
4d43b2ce1f | ||
|
|
c41666fee6 | ||
|
|
768a4724af | ||
|
|
2ed64ef858 | ||
|
|
b269647a9f | ||
|
|
db97949965 | ||
|
|
81a67475dd | ||
|
|
92211c78ec | ||
|
|
1359f36703 | ||
|
|
ae9c3ae728 | ||
|
|
d61664e724 | ||
|
|
ec21b9f44c | ||
|
|
6822628135 | ||
|
|
0a6147c177 | ||
|
|
9b521dd87b | ||
|
|
e4fee670ad | ||
|
|
3a6113846f | ||
|
|
a0c7b3bf6a | ||
|
|
11003eb5f1 | ||
|
|
dadf0d4228 | ||
|
|
4fede70705 | ||
|
|
b984bb52e9 | ||
|
|
b6bdd06c4e | ||
|
|
c57130a4bf | ||
|
|
7a9a99f62c | ||
|
|
8156a7b760 | ||
|
|
ff5c8acd9a | ||
|
|
6584f6e691 | ||
|
|
662c1f7b66 | ||
|
|
4b70e4e891 | ||
|
|
2e6531a036 | ||
|
|
a79a9f0fbf | ||
|
|
326616f936 | ||
|
|
d213010a72 | ||
|
|
8b80201d16 | ||
|
|
c23d842a90 | ||
|
|
8b93f57293 | ||
|
|
a0f46b4dac | ||
|
|
d4f9caee65 | ||
|
|
5e48762c48 | ||
|
|
0ba4f49e04 | ||
|
|
cc8cf2539f | ||
|
|
663e341584 | ||
|
|
b4f6912d68 | ||
|
|
9f6a1548d1 | ||
|
|
2e8ac93a42 | ||
|
|
41b0a55af3 | ||
|
|
a6d4fd7c3e | ||
|
|
99d711497e | ||
|
|
518666b18a | ||
|
|
6d6c058d25 | ||
|
|
48e7bdc3b4 | ||
|
|
c46abdf02e | ||
|
|
0cbac7df02 | ||
|
|
9355fc2228 | ||
|
|
08b48cec4c | ||
|
|
0480285fa7 | ||
|
|
7770327d3c | ||
|
|
e57e77cb11 | ||
|
|
4c73d18494 | ||
|
|
1918c912c5 | ||
|
|
e6912d4514 | ||
|
|
12dbcb1b40 | ||
|
|
45a4f1a366 | ||
|
|
a51a52263a | ||
|
|
b395886327 | ||
|
|
95215dec1d | ||
|
|
b697623c13 | ||
|
|
d965cc8f13 | ||
|
|
00de20af93 | ||
|
|
8097ae3ae5 | ||
|
|
a63d20c0d1 | ||
|
|
5c05e80c48 | ||
|
|
f9b5a8a3a6 | ||
|
|
5db4443529 | ||
|
|
2f27d00d60 | ||
|
|
c03099fa51 | ||
|
|
ba3b00cd05 | ||
|
|
9ccab8b676 | ||
|
|
188bda9259 | ||
|
|
d749af17db | ||
|
|
539cd5cc15 | ||
|
|
1ad25e180c | ||
|
|
5220b1483e | ||
|
|
6869bbb971 | ||
|
|
458e4f6ca5 | ||
|
|
e9bcabf693 | ||
|
|
a1adfbba22 | ||
|
|
1d8dc20148 | ||
|
|
7f9346e5ec | ||
|
|
ad16c4a67a | ||
|
|
21c985b3da | ||
|
|
cb01795df6 | ||
|
|
ec87c304fa | ||
|
|
8eb2ff3eab | ||
|
|
de24f56cba | ||
|
|
90b0b19bf5 | ||
|
|
142ae1be99 | ||
|
|
e57c241424 | ||
|
|
1888b2934b | ||
|
|
3af6f1bf59 | ||
|
|
f088cfc31c | ||
|
|
3eff1d2efb | ||
|
|
88c347fdaa | ||
|
|
8f69189429 | ||
|
|
95ed00c0d2 | ||
|
|
463c52afe6 | ||
|
|
e39a4d979d | ||
|
|
6092f2bcff | ||
|
|
7bafe50098 | ||
|
|
fe50e29827 | ||
|
|
d862eeaf79 | ||
|
|
f08e7c66b3 | ||
|
|
7d1d5d1e68 | ||
|
|
f333f28cea | ||
|
|
a370a0eb5a | ||
|
|
5018755374 | ||
|
|
3d4db007a9 | ||
|
|
706a5850a1 | ||
|
|
0a9a6b58c2 | ||
|
|
0d588e0c0a | ||
|
|
9b74e87507 | ||
|
|
6b15519c9d | ||
|
|
e655b3c096 | ||
|
|
f3f1e4adcb | ||
|
|
516c2c6eba | ||
|
|
131e353b9b | ||
|
|
6d1a3a420c | ||
|
|
7b1a58aa47 | ||
|
|
ef1f05848e | ||
|
|
89718ab50a | ||
|
|
21cf7c99bf | ||
|
|
dabc688b26 | ||
|
|
ae179fef67 | ||
|
|
ec055dafcc | ||
|
|
8a71af4be1 | ||
|
|
c47afd5c76 | ||
|
|
f037be9248 | ||
|
|
48fa446eeb | ||
|
|
3fd666c583 | ||
|
|
9eb14aea6f | ||
|
|
3db5740bd3 | ||
|
|
3c226ed0ca | ||
|
|
191393445e | ||
|
|
621301990a | ||
|
|
d40b890d83 | ||
|
|
1891477e12 | ||
|
|
86207ff3a3 | ||
|
|
72956e4ba0 | ||
|
|
1256987e9c | ||
|
|
517dd3efc6 | ||
|
|
9176ed7418 | ||
|
|
4f4ec10cdf | ||
|
|
14db9f1ead | ||
|
|
81be3393dc | ||
|
|
d15439a292 | ||
|
|
7514b29e31 | ||
|
|
0afef3389a | ||
|
|
2dadc1e899 | ||
|
|
b07423749a | ||
|
|
1120921a1b | ||
|
|
9a826fd416 | ||
|
|
edd062c76f | ||
|
|
2429999b3c | ||
|
|
8cb9a4a9c9 | ||
|
|
a2bd8a46f5 | ||
|
|
1dcaf2d2c1 | ||
|
|
e7f4d8cd2b | ||
|
|
0ad3a7d8da | ||
|
|
92bf0c6669 | ||
|
|
e536ff34a8 | ||
|
|
519423fa68 | ||
|
|
3f31de661d | ||
|
|
7c57dfa7bc |
No files matched your search
@@ -13,7 +13,8 @@ Before submitting an issue, please use the search to make sure an issue doesn't
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Rom Information**
|
||||
Specify the impacted rom filename and any associated hashes (sha256 preferred)
|
||||
Specify the impacted rom filename and any associated hashes (sha256 preferred);.
|
||||
The easiest way to do this is to open the game in ares, and go to "Tools -> Manifest" and copy/paste the contents.
|
||||
|
||||
```
|
||||
file: Some Game (REGION).ext
|
||||
|
||||
+41
-21
@@ -8,6 +8,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
program:
|
||||
- ares
|
||||
@@ -18,14 +19,14 @@ jobs:
|
||||
windres: windres
|
||||
shell: 'msys2 {0}'
|
||||
msystem: clang64
|
||||
msys-env: clang-x86_64
|
||||
install: mingw-w64-clang-x86_64-clang mingw-w64-clang-x86_64-pkgconf mingw-w64-clang-x86_64-SDL2
|
||||
- name: windows-arm64
|
||||
os: windows-latest
|
||||
compiler: clang++ --target=aarch64-w64-windows-gnu --sysroot=/clangarm64 -resource-dir=/clangarm64/lib/clang/$resourcever
|
||||
compiler: clang++ --target=aarch64-w64-windows-gnu --sysroot=/clangarm64 -resource-dir=/clangarm64/lib/clang/$(basename "$(clang++ -print-resource-dir)")
|
||||
windres: windres --target=aarch64-w64-windows-gnu
|
||||
shell: 'msys2 {0}'
|
||||
msystem: clang64
|
||||
msys-env: clang-x86_64
|
||||
install: mingw-w64-clang-x86_64-clang mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-pkgconf mingw-w64-clang-aarch64-SDL2
|
||||
- name: windows-msvc-x64
|
||||
os: windows-latest
|
||||
compiler: clang++
|
||||
@@ -38,6 +39,18 @@ jobs:
|
||||
windres: llvm-rc
|
||||
shell: bash
|
||||
msvc-arch: x64_arm64
|
||||
- name: windows-cl-x64
|
||||
os: windows-latest
|
||||
compiler: cl
|
||||
windres: rc
|
||||
shell: bash
|
||||
msvc-arch: x64
|
||||
- name: windows-cl-arm64
|
||||
os: windows-latest
|
||||
compiler: cl
|
||||
windres: rc
|
||||
shell: bash
|
||||
msvc-arch: x64_arm64
|
||||
- name: macos
|
||||
os: macos-latest
|
||||
compiler: clang++
|
||||
@@ -57,22 +70,16 @@ jobs:
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.platform.msystem }}
|
||||
update: true
|
||||
install: make mingw-w64-${{ matrix.platform.msys-env }}-toolchain
|
||||
- name: Install MSYS2 ARM64 Dependencies
|
||||
if: matrix.platform.name == 'windows-arm64'
|
||||
run: | # enable clang arm64 package source, refresh package database, and install clang package
|
||||
echo "resourcever=$(basename "$(clang++ -print-resource-dir)")" >> $GITHUB_ENV
|
||||
grep -qFx '[clangarm64]' /etc/pacman.conf || sed -i '/^# \[clangarm64\]/,/^$/ s|^# ||g' /etc/pacman.conf
|
||||
pacman --noconfirm -Sy mingw-w64-clang-aarch64-clang
|
||||
install: make ${{ matrix.platform.install }}
|
||||
- name: Install Linux Dependencies
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get update -y -qq
|
||||
sudo apt-get install libsdl2-dev libgtk2.0-dev libao-dev libopenal-dev
|
||||
sudo apt-get install libsdl2-dev libgtk-3-dev libao-dev libopenal-dev
|
||||
- name: "macOS: Import Certificate"
|
||||
if: runner.os == 'macOS' && github.event_name != 'pull_request'
|
||||
uses: devbotsxyz/import-signing-certificate@2ac4f44d28045073d23153256efbb4c4b2d8aa22 # Don't use rolling branch for security reasons
|
||||
continue-on-error: ${{ github.repository != 'ares-emulator/ares' }}
|
||||
uses: wpilibsuite/import-signing-certificate@2ac4f44d28045073d23153256efbb4c4b2d8aa22 # Don't use rolling branch for security reasons
|
||||
with:
|
||||
certificate-data: ${{ secrets.MACOS_CERTIFICATE_DATA }}
|
||||
certificate-passphrase: ${{ secrets.MACOS_CERTIFICATE_PASSPHRASE }}
|
||||
@@ -80,7 +87,7 @@ jobs:
|
||||
keychain-password: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
- name: "macOS: recover cache"
|
||||
- name: "macOS: recover MoltenVK cache"
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
@@ -88,25 +95,37 @@ jobs:
|
||||
with:
|
||||
path: thirdparty/MoltenVK/MoltenVK
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('thirdparty/MoltenVK/HEAD') }}
|
||||
- name: "macOS: recover SDL cache"
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: sdl
|
||||
with:
|
||||
path: thirdparty/SDL/SDL
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('thirdparty/SDL/HEAD') }}
|
||||
- name: Install macOS Dependencies
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install make ninja
|
||||
brew install make ninja cmake
|
||||
pip install --no-input setuptools
|
||||
echo "MAKE=gmake" >> $GITHUB_ENV
|
||||
pushd thirdparty/SDL
|
||||
./build-SDL.sh
|
||||
popd
|
||||
pushd thirdparty/MoltenVK
|
||||
./build-moltenvk.sh
|
||||
popd
|
||||
- name: Set up MSVC environment
|
||||
if: matrix.msvc-arch != ''
|
||||
if: matrix.platform.msvc-arch != ''
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: ${{ matrix.msvc-arch }}
|
||||
arch: ${{ matrix.platform.msvc-arch }}
|
||||
- name: Make
|
||||
if: runner.os != 'macOS'
|
||||
run: ${MAKE:-make} -j4 -C desktop-ui build=optimized local=false compiler="${{ matrix.platform.compiler }}" windres="${{ matrix.platform.windres }}"
|
||||
- name: "macOS: Make universal app"
|
||||
if: runner.os == 'macOS'
|
||||
run: scripts/macos-make-universal.sh
|
||||
run: scripts/macos-make-universal.sh build=optimized local=false
|
||||
env:
|
||||
MAKEFLAGS: -j3
|
||||
MACOS_CERTIFICATE_NAME: ${{ secrets.MACOS_CERTIFICATE_NAME }}
|
||||
@@ -124,6 +143,7 @@ jobs:
|
||||
timestampUrl: 'http://timestamp.digicert.com'
|
||||
- name: "macOS: notarize"
|
||||
if: runner.os == 'macOS' && github.event_name != 'pull_request'
|
||||
continue-on-error: ${{ github.repository != 'ares-emulator/ares' }}
|
||||
run: |
|
||||
ditto -c -k --keepParent desktop-ui/out/ares.app /tmp/ares.zip
|
||||
xcrun notarytool submit /tmp/ares.zip --apple-id "$MACOS_NOTARIZATION_USERNAME" --password "$MACOS_NOTARIZATION_PASSWORD" --team-id "$MACOS_NOTARIZATION_TEAMID" --wait
|
||||
@@ -167,7 +187,7 @@ jobs:
|
||||
# Hack: Workaround for GitHub artifacts losing attributes.
|
||||
chmod +x ${bindir}/ares-macos/ares.app/Contents/MacOS/ares
|
||||
|
||||
for package in windows-msvc-x64 windows-msvc-arm64 macos
|
||||
for package in windows windows-msvc-arm64 macos
|
||||
do
|
||||
mkdir "${package}"
|
||||
cd "${package}"
|
||||
@@ -259,10 +279,10 @@ jobs:
|
||||
echo "UPLOAD_URL=$(make_nightly_release | jq -r .upload_url)" >> $GITHUB_ENV
|
||||
;;
|
||||
esac
|
||||
- name: Upload ares-windows-msvc-x64
|
||||
- name: Upload ares-windows
|
||||
uses: actions/upload-release-asset@v1
|
||||
env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
|
||||
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-windows-msvc-x64.zip', asset_name: 'ares-windows-msvc-x64.zip', asset_content_type: 'application/zip' }
|
||||
with: { upload_url: '${{ env.UPLOAD_URL }}', asset_path: 'ares-windows.zip', asset_name: 'ares-windows.zip', asset_content_type: 'application/zip' }
|
||||
- name: Upload ares-windows-msvc-arm64
|
||||
uses: actions/upload-release-asset@v1
|
||||
env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' }
|
||||
|
||||
+7
-1
@@ -1,6 +1,12 @@
|
||||
*.moc
|
||||
*.user
|
||||
.vs/
|
||||
.vscode/
|
||||
.idea/
|
||||
cmake-*/
|
||||
obj/
|
||||
out/
|
||||
obj-amd64/
|
||||
obj-arm64/
|
||||
out-amd64/
|
||||
out-arm64/
|
||||
out-arm64/
|
||||
@@ -45,38 +45,6 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
MAME
|
||||
Copyright (c) 1997-2021 MAMEdev and contributors
|
||||
|
||||
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.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
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.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
libchdr
|
||||
|
||||
@@ -441,3 +409,64 @@ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
TZXFile
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 MrReeMachine
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
ymfm
|
||||
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, Aaron Giles
|
||||
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.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
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.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[](https://github.com/higan-emu/ares/blob/master/LICENSE)
|
||||
|
||||
**ares** is a multi-system emulator that began development on October 14th, 2004.
|
||||
It is a descendent of [higan](https://github.com/higan-emu/higan) and [bsnes](https://github.com/bsnes-emu/bsnes/), and focuses on accuracy and preservation.
|
||||
It is a descendant of [higan](https://github.com/higan-emu/higan) and [bsnes](https://github.com/bsnes-emu/bsnes/), and focuses on accuracy and preservation.
|
||||
|
||||
Official Releases
|
||||
-----------------
|
||||
@@ -20,20 +20,48 @@ Only the latest nightly build is kept.
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
#### *nix building
|
||||
### *nix building
|
||||
|
||||
###### Minimum required packages:
|
||||
```
|
||||
g++ make pkg-config libgtk2.0-dev libcanberra-gtk-module libgl-dev libasound2-dev
|
||||
```
|
||||
g++ make pkg-config libgtk-3-dev libcanberra-gtk-module libgl-dev libasound2-dev
|
||||
```
|
||||
###### Additional Audio Drivers
|
||||
ares supports additional audio drivers besides the ALSA drivers included above. Installing these additional packages will allow them to be selected in Settings > Drivers:
|
||||
`libao-dev libopenal-dev'
|
||||
|
||||
By default, GTK2 is used, but support for GTK3 is available. You will need to install the additional package `libgtk-3-dev` as well
|
||||
as specifying the command line option `hiro=gtk3` at compile time.
|
||||
###### GTK2 & GTK3
|
||||
By default, GTK3 is used, but support for GTK2 is available. You will need to install the additional package `libgtk2.0-dev` as well as specifying the command line option `hiro=gtk2` at compile time.
|
||||
|
||||
#### Windows building
|
||||
###### SDL2 for input
|
||||
If you would like to use SDL for input (e.g. for using a controller), you will need to install the `libsdl2-dev` and `libsdl2-2.0-0` packages and perform a clean build of ares.
|
||||
You should then be able to select SDL for input in the Settings > Drivers menu.
|
||||
|
||||
##### Building with clang
|
||||
|
||||
clang++ is now the preferred compiler for ares. If clang is detected on Windows/macOS/BSD, it will be selected by default. On Linux and other platforms, g++ remains the default if present. To build with clang, it is necessary to install both the `clang` and `lld` packages. If you would like to manually specify a compiler, you can use the following option: `compiler=[g++|clang++]`
|
||||
|
||||
--------------
|
||||
|
||||
### Windows building
|
||||
|
||||
To build on Windows, using MSYS2 is recommended which can be download [here](https://www.msys2.org/). Follow the instructions
|
||||
on this page to install and setup an appropriate MINGW64 environment. Running the command `pacman -S --needed base-devel mingw-w64-x86_64-toolchain` from the MSYS2 MSYS terminal should setup everything you need to compile Ares. Note that in order
|
||||
to compile, you will want to be in a MINGW64 terminal window after install and setup is complete.
|
||||
on this page to install and setup an appropriate MINGW64 environment. Running the command:
|
||||
```
|
||||
pacman -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-SDL2
|
||||
```
|
||||
from the MSYS2 MSYS terminal should setup everything you need to compile ares. Note that in order to compile, you will want to be in a MINGW64 terminal window after install and setup is complete.
|
||||
|
||||
##### Building with clang
|
||||
|
||||
clang is available through Visual Studio (or Build Tools for Visual Studio) through its installer and can be used to build ares. You will still need to supply GNU make in this instance. MSYS2 also offers a clang environment. You will want to make sure you select the clangw64 option during installation of MSYS2 which should provide and additional CLANG64 pre-configured environment. Install the clang toolchain package from the MSYS2 terminal:
|
||||
```
|
||||
pacman -S mingw-w64-clang-x86_64-toolchain mingw-w64-clang-x86_64-SDL2
|
||||
```
|
||||
Once complete, open a CLANG64 terminal window and proceed with building ares.
|
||||
|
||||
###### Debug Symbols
|
||||
When building with clang, by default symbols will be generated for debug builds using an MSVC compatible format (CodeView) for use with Windows debugging tools. In order to generate GDB compatible symbols, specify the following option: `symformat=gdb`
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
@@ -47,12 +75,12 @@ git clone https://github.com/ares-emulator/ares.git
|
||||
From the root of the project directory run:
|
||||
|
||||
```
|
||||
make -j4 build=release
|
||||
make -j4
|
||||
```
|
||||
|
||||
`-j#` indicates number of parallel build processes, and shouldn't be set higher than N-1 cores on your processor. Specifying this option can significantly decrease the time to build this project. There are multiple build types available and it defaults to an 'optimized' build. Build types can be specified using: `build=[debug|stable|release|minified|optimized]`
|
||||
|
||||
that builds with build type of type 'release'.
|
||||
`-j4` indicates number of parallel build processes, and shouldn't be set higher than N-1 cores on your processor. Specifying this option can significantly decrease the time to build this project. There are multiple build types available (debug, etc.). Most additional options can be
|
||||
found in nall's make file (nall/GNUmakefile).
|
||||
Build options can be found in the following two make files: nall/GNUmakefile desktop-ui/GNUmakefile
|
||||
|
||||
To start compilation from the beginning, run the following prior to compiling:
|
||||
|
||||
@@ -60,6 +88,12 @@ To start compilation from the beginning, run the following prior to compiling:
|
||||
make clean
|
||||
```
|
||||
|
||||
#### Building specific cores
|
||||
If you would like to build a subset of cores, you can specify the `cores="core1 core2"` option. Currently available cores:
|
||||
```
|
||||
a26 fc sfc n64 sg ms md ps1 pce ng msx cv myvision gb gba ws ngp spec
|
||||
```
|
||||
|
||||
Build Output
|
||||
------------
|
||||
|
||||
@@ -72,11 +106,15 @@ Command-line options
|
||||
When started from the command-line, ares accepts a few options.
|
||||
|
||||
```
|
||||
Usage: ./ares [options] game
|
||||
Usage: ./ares [options] game(s)
|
||||
|
||||
--help Displays available options and exit
|
||||
--fullscreen Start in full screen mode
|
||||
--system system Specify the system name
|
||||
--shader shader Specify GLSL shader name to load (requires OpenGL driver)
|
||||
--setting name=value Specify a value for a setting
|
||||
--dump-all-settings Show a list of all existing settings and exit
|
||||
--no-file-prompt Do not prompt to load (optional) additional roms (eg: 64DD)
|
||||
```
|
||||
|
||||
The --system option is useful when the system type cannot be auto-detected.
|
||||
@@ -85,6 +123,17 @@ The --system option is useful when the system type cannot be auto-detected.
|
||||
Example:
|
||||
`ares --system MSX examples.rom --fullscreen`
|
||||
|
||||
Specifying multiple games allows for multi-cart support. For example, to load
|
||||
the Super GameBoy BIOS and a game in one command (to avoid a file prompt), you
|
||||
can do:
|
||||
|
||||
`ares "Super GameBoy.sfc" "Super Mario Land.gb"`
|
||||
|
||||
The --no-file-prompt option is useful if you wish to launch a game from CLI
|
||||
without being prompted to load additional roms. For example, some Nintendo 64
|
||||
games optionally support 64DD expansion disks, so this option can be used to
|
||||
suppress the "64DD Disk" file dialog, and assume any secondary content is
|
||||
disconnected.
|
||||
|
||||
High-level Components
|
||||
---------------------
|
||||
@@ -96,3 +145,8 @@ High-level Components
|
||||
* __ruby__: interface between a hiro application and platform-specific APIs for emulator video, audio, and input
|
||||
* __mia__: internal ROM database and ROM/image loader
|
||||
* __libco__: cooperative multithreading library
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Please join our discord to chat with other ares developers: https://discord.com/invite/gz2quhk2kv
|
||||
+9
-5
@@ -1,5 +1,5 @@
|
||||
ifeq ($(platform),windows)
|
||||
options += -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
|
||||
options += $(call lib,uuid kernel32 user32 gdi32 comctl32 comdlg32 shell32)
|
||||
else ifeq ($(platform),macos)
|
||||
else ifneq ($(filter $(platform),linux bsd),)
|
||||
options += -Wl,-export-dynamic
|
||||
@@ -13,10 +13,6 @@ ares.objects := ares ares-fixed-allocator
|
||||
$(object.path)/ares.o: $(ares.path)/ares/ares.cpp
|
||||
$(object.path)/ares-fixed-allocator.o: $(ares.path)/ares/memory/fixed-allocator.cpp
|
||||
|
||||
ifeq ($(platform),macos)
|
||||
ares.dylibs :=
|
||||
endif
|
||||
|
||||
ifeq ($(vulkan),true)
|
||||
flags += -DVULKAN
|
||||
endif
|
||||
@@ -77,6 +73,10 @@ ifneq ($(filter $(cores),cv),)
|
||||
include $(ares.path)/cv/GNUmakefile
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(cores),myvision),)
|
||||
include $(ares.path)/myvision/GNUmakefile
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(cores),gb),)
|
||||
include $(ares.path)/gb/GNUmakefile
|
||||
endif
|
||||
@@ -97,6 +97,10 @@ ifneq ($(filter $(cores),ngp),)
|
||||
include $(ares.path)/ngp/GNUmakefile
|
||||
endif
|
||||
|
||||
ifneq ($(filter $(cores),spec),)
|
||||
include $(ares.path)/spec/GNUmakefile
|
||||
endif
|
||||
|
||||
include $(ares.path)/component/GNUmakefile
|
||||
|
||||
flags += $(foreach c,$(call strupper,$(cores)),-DCORE_$c)
|
||||
|
||||
@@ -10,6 +10,7 @@ auto ControllerPort::load(Node::Object parent) -> void {
|
||||
port->setType("Controller");
|
||||
port->setHotSwappable(true);
|
||||
port->setAllocate([&](auto name) { return allocate(name); });
|
||||
port->setDisconnect([&] { device.reset(); });
|
||||
port->setSupported({"Gamepad"});
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ auto CPU::main() -> void {
|
||||
}
|
||||
|
||||
auto CPU::step(u32 clocks) -> void {
|
||||
if(io.rdyLine == 1) io.scanlineCycles += clocks;
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize();
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ struct CPU : MOS6502, Thread {
|
||||
//protected:
|
||||
struct IO {
|
||||
n1 rdyLine = 1;
|
||||
n32 scanlineCycles = 0;
|
||||
} io;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ auto CPU::Debugger::load(Node::Object parent) -> void {
|
||||
|
||||
auto CPU::Debugger::instruction() -> void {
|
||||
if(tracer.instruction->enabled() && tracer.instruction->address(cpu.PC)) {
|
||||
tracer.instruction->notify(cpu.disassembleInstruction(), cpu.disassembleContext());
|
||||
tracer.instruction->notify(cpu.disassembleInstruction(), {cpu.disassembleContext(), " [c:", cpu.io.scanlineCycles, " l:", tia.io.vcounter, " p:", tia.io.hcounter, "]"});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
inline auto CPU::readBus(n16 address) -> n8 {
|
||||
address &= 0x1fff;
|
||||
if(auto result = platform->cheat(address)) return *result;
|
||||
|
||||
n8 data = cartridge.read(address);
|
||||
|
||||
if(address.bit(12) == 0 && address.bit(7) == 0) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
auto CPU::read(n16 address) -> n8 {
|
||||
step(1);
|
||||
|
||||
if(io.rdyLine == 0) {
|
||||
while (io.rdyLine == 0) {
|
||||
while(io.rdyLine == 0) {
|
||||
step(1);
|
||||
}
|
||||
|
||||
@@ -8,7 +10,6 @@ auto CPU::read(n16 address) -> n8 {
|
||||
return MDR;
|
||||
}
|
||||
|
||||
step(1);
|
||||
MDR = readBus(address);
|
||||
return MDR;
|
||||
}
|
||||
|
||||
+13
-4
@@ -7,12 +7,18 @@ auto RIOT::writeRam(n8 address, n8 data) -> void {
|
||||
}
|
||||
|
||||
auto RIOT::readIo(n8 address) -> n8 {
|
||||
switch(address) {
|
||||
switch(address.bit(0, 2)) {
|
||||
case 0x00: return readPortA();
|
||||
case 0x01: return port[0].direction;
|
||||
case 0x02: return readPortB();
|
||||
case 0x03: return port[1].direction;
|
||||
case 0x04: return timer.counter;
|
||||
case 0x04: timer.interruptFlag = address.bit(3); return timer.counter;
|
||||
case 0x05: case 0x07: {
|
||||
n8 output = 0;
|
||||
output.bit(6) = 0; //TODO: PA7 IRQ Flag
|
||||
output.bit(7) = timer.interruptFlag;
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
debug(unimplemented, "[RIOT] IO read ", hex(address));
|
||||
@@ -25,11 +31,14 @@ auto RIOT::writeIo(n8 address, n8 data) -> void {
|
||||
case 0x01: port[0].direction = data; return;
|
||||
case 0x02: writePortB(data); return;
|
||||
case 0x03: port[1].direction = data; return;
|
||||
case 0x14: case 0x15: case 0x16: case 0x17: {
|
||||
case 0x14: case 0x15: case 0x16: case 0x17:
|
||||
case 0x1c: case 0x1d: case 0x1e: case 0x1f: {
|
||||
const n16 intervals[] = {1, 8, 64, 1024};
|
||||
timer.counter = data;
|
||||
timer.interval = intervals[address - 0x14];
|
||||
timer.interval = intervals[address.bit(0, 1)];
|
||||
timer.reload = timer.interval;
|
||||
timer.interruptFlag = 0;
|
||||
timer.interruptEnable = address.bit(3);
|
||||
return; }
|
||||
}
|
||||
|
||||
|
||||
+9
-13
@@ -19,18 +19,12 @@ auto RIOT::unload() -> void {
|
||||
}
|
||||
|
||||
auto RIOT::main() -> void {
|
||||
decrementTimer();
|
||||
step(1);
|
||||
}
|
||||
|
||||
auto RIOT::decrementTimer() -> void {
|
||||
if(--timer.interval == 0) {
|
||||
if(--timer.counter == 0xff) {
|
||||
timer.reload = 1;
|
||||
}
|
||||
|
||||
timer.interval = timer.reload;
|
||||
if(--timer.counter == 0xff) {
|
||||
timer.interval = 1;
|
||||
timer.interruptFlag = 1;
|
||||
}
|
||||
|
||||
step(timer.interval);
|
||||
}
|
||||
|
||||
auto RIOT::step(u32 clocks) -> void {
|
||||
@@ -41,8 +35,10 @@ auto RIOT::step(u32 clocks) -> void {
|
||||
auto RIOT::power(bool reset) -> void {
|
||||
Thread::create(system.frequency() / 3, {&RIOT::main, this});
|
||||
timer = {};
|
||||
port[0] = {.data = 0xff};
|
||||
port[1] = {.data = 0xff};
|
||||
port[0] = {};
|
||||
port[0].data = 0xff;
|
||||
port[1] = {};
|
||||
port[1].data = 0xff;
|
||||
leftDifficulty = 1;
|
||||
rightDifficulty = 1;
|
||||
tvType = 1;
|
||||
|
||||
@@ -30,6 +30,8 @@ struct RIOT : Thread {
|
||||
u8 counter;
|
||||
n16 interval;
|
||||
n16 reload;
|
||||
n1 interruptEnable;
|
||||
n1 interruptFlag;
|
||||
} timer;
|
||||
|
||||
struct Port {
|
||||
|
||||
@@ -6,6 +6,8 @@ auto RIOT::serialize(serializer& s) -> void {
|
||||
s(timer.counter);
|
||||
s(timer.interval);
|
||||
s(timer.reload);
|
||||
s(timer.interruptEnable);
|
||||
s(timer.interruptFlag);
|
||||
|
||||
for(auto n : range(2)) {
|
||||
s(port[n].data);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
static const string SerializerVersion = "v132";
|
||||
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
serializer s;
|
||||
|
||||
@@ -20,7 +20,7 @@ struct System {
|
||||
|
||||
auto name() const -> string { return information.name; }
|
||||
auto region() const -> Region { return information.region; }
|
||||
auto frequency() const -> u32 { return information.frequency; }
|
||||
auto frequency() const -> f64 { return information.frequency; }
|
||||
|
||||
//system.cpp
|
||||
auto game() -> string;
|
||||
@@ -39,7 +39,7 @@ private:
|
||||
struct Information {
|
||||
string name = "Atari 2600";
|
||||
Region region = Region::NTSC;
|
||||
u32 frequency = Constants::Colorburst::NTSC;
|
||||
f64 frequency = Constants::Colorburst::NTSC;
|
||||
} information;
|
||||
|
||||
//serialization.cpp
|
||||
|
||||
+10
-16
@@ -33,9 +33,9 @@ auto TIA::write(n8 address, n8 data) -> void {
|
||||
case 0x0a: ctrlpf(data); return; // CTRLPF
|
||||
case 0x0b: writeQueue.add(address, data, 1); return; // REFP0
|
||||
case 0x0c: writeQueue.add(address, data, 1); return; // REFP1
|
||||
case 0x0d: writeQueue.add(address, data, 6); return; // PF0
|
||||
case 0x0e: writeQueue.add(address, data, 6); return; // PF1
|
||||
case 0x0f: writeQueue.add(address, data, 6); return; // PF2
|
||||
case 0x0d: writeQueue.add(address, data, 2); return; // PF0
|
||||
case 0x0e: writeQueue.add(address, data, 2); return; // PF1
|
||||
case 0x0f: writeQueue.add(address, data, 2); return; // PF2
|
||||
case 0x10: resp(0); return; // RESP0
|
||||
case 0x11: resp(1); return; // RESP1
|
||||
case 0x12: resm(0); return; // RESM0
|
||||
@@ -47,8 +47,8 @@ auto TIA::write(n8 address, n8 data) -> void {
|
||||
case 0x18: audio[1].frequency = data; return; // AUDF1
|
||||
case 0x19: audio[0].volume = data; return; // AUDV0
|
||||
case 0x1a: audio[1].volume = data; return; // AUDV1
|
||||
case 0x1b: writeQueue.add(address, data, 3); return; // GRP0
|
||||
case 0x1c: writeQueue.add(address, data, 3); return; // GRP1
|
||||
case 0x1b: writeQueue.add(address, data, 1); return; // GRP0
|
||||
case 0x1c: writeQueue.add(address, data, 1); return; // GRP1
|
||||
case 0x1d: writeQueue.add(address, data, 1); return; // ENAM0
|
||||
case 0x1e: writeQueue.add(address, data, 1); return; // ENAM1
|
||||
case 0x1f: writeQueue.add(address, data, 1); return; // ENAB1
|
||||
@@ -72,9 +72,9 @@ auto TIA::write(n8 address, n8 data) -> void {
|
||||
|
||||
|
||||
auto TIA::vsync(n1 state) -> void {
|
||||
if(io.vsync && !state) {
|
||||
io.vcounter = 0;
|
||||
if(!io.vsync && state) {
|
||||
frame();
|
||||
tia.io.vcounter = 0;
|
||||
}
|
||||
|
||||
io.vsync = state;
|
||||
@@ -125,23 +125,17 @@ auto TIA::grp(n1 index, n8 data) -> void {
|
||||
}
|
||||
|
||||
auto TIA::resp(n1 index) -> void {
|
||||
player[index].position = (io.hcounter < 68 ? 3 : io.hcounter - 68 + 8) % 160;
|
||||
player[index].position = (io.hcounter - 68 + 5) % 160;
|
||||
}
|
||||
|
||||
auto TIA::resm(n1 index) -> void {
|
||||
missile[index].position = (io.hcounter < 68 ? 2 : io.hcounter - 68 + 7) % 160;
|
||||
missile[index].position = (io.hcounter - 68 + 4) % 160;
|
||||
}
|
||||
|
||||
auto TIA::resmp(n1 index, n8 data) -> void {
|
||||
missile[index].reset = data.bit(1);
|
||||
if(missile[index].reset) {
|
||||
auto offset = 3;
|
||||
if(player[index].size == 5) offset = 6;
|
||||
if(player[index].size == 7) offset = 10;
|
||||
missile[index].position = player[index].position + offset;
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::resbl() -> void {
|
||||
ball.position = (io.hcounter < 68 ? 2 : io.hcounter - 68 + 7);
|
||||
ball.position = (io.hcounter - 68 + 4) % 160;
|
||||
}
|
||||
+67
-18
@@ -13,12 +13,12 @@ TIA tia;
|
||||
auto TIA::load(Node::Object parent) -> void {
|
||||
node = parent->append<Node::Object>("TIA");
|
||||
|
||||
screen = node->append<Node::Video::Screen>("Screen", 160, vlines());
|
||||
screen = node->append<Node::Video::Screen>("Screen", 180, displayHeight());
|
||||
screen->colors(1 << 7, {&TIA::color, this});
|
||||
screen->setSize(160, vlines());
|
||||
screen->setScale(2.0, 2.0);
|
||||
screen->setAspect(12.0, 7.0);
|
||||
screen->setViewport(0, 0, 160, vlines());
|
||||
screen->setSize(180, displayHeight());
|
||||
screen->setScale(2.0, 1.0);
|
||||
Region::PAL() ? screen->setAspect(38.0, 45.0) : screen->setAspect(4.0, 5.0);
|
||||
screen->setViewport(0, 0, 180, displayHeight());
|
||||
|
||||
stream = node->append<Node::Audio::Stream>("Audio");
|
||||
stream->setChannels(1);
|
||||
@@ -35,24 +35,35 @@ auto TIA::unload() -> void {
|
||||
}
|
||||
|
||||
auto TIA::main() -> void {
|
||||
scanline();
|
||||
io.vcounter++;
|
||||
io.hmoveTriggered = 255;
|
||||
scanline();
|
||||
io.vcounter++;
|
||||
io.hmoveTriggered = 255;
|
||||
}
|
||||
|
||||
auto TIA::scanline() -> void {
|
||||
cpu.rdyLine(1);
|
||||
cpu.io.scanlineCycles = 0;
|
||||
|
||||
for(io.hcounter = 0; io.hcounter < 228; io.hcounter++) {
|
||||
auto x = io.hcounter - 68;
|
||||
if(x >= 0 && io.vcounter < vlines()) pixel(x);
|
||||
writeQueue.step();
|
||||
auto x = io.hcounter - 68;
|
||||
auto y = io.vcounter - voffset();
|
||||
if(x >= 0 && y > 0 && y < vlines()) pixel(x);
|
||||
runAudio();
|
||||
step();
|
||||
if(io.hcounter == 0) cpu.io.rdyLine = 1;
|
||||
}
|
||||
|
||||
// Prevent an infinite emulator hang when games miss vblank
|
||||
if(io.vcounter > vlines()) {
|
||||
scheduler.exit(Event::Frame);
|
||||
io.vcounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto TIA::pixel(n8 x) -> void {
|
||||
auto output = screen->pixels().data() + (io.vcounter * 160);
|
||||
auto y = io.vcounter - voffset();
|
||||
auto output = screen->pixels().data() + (y * 180) + 10;
|
||||
if(y >= displayHeight()) return;
|
||||
|
||||
// Output only black during vblank, or for the first 8px of a scanline where hmove was triggered
|
||||
if (io.vblank || (io.hmoveTriggered == io.vcounter && x < 8)) {
|
||||
@@ -92,10 +103,13 @@ auto TIA::pixel(n8 x) -> void {
|
||||
}
|
||||
|
||||
auto TIA::runPlayfield(n8 x) -> n1 {
|
||||
auto pos = x >> 2;
|
||||
if((x % 4) == 0) {
|
||||
auto pos = x >> 2;
|
||||
playfield.pixel = (!playfield.mirror || pos < 20) ? playfield.graphics.bit(pos % 20)
|
||||
: playfield.graphics.bit(19 - (pos % 20));
|
||||
}
|
||||
|
||||
if(!playfield.mirror || pos < 20) return playfield.graphics.bit(pos % 20);
|
||||
return playfield.graphics.bit(19 - (pos % 20));
|
||||
return playfield.pixel;
|
||||
}
|
||||
|
||||
auto TIA::runPlayer(n8 x, n1 index) -> n1 {
|
||||
@@ -130,10 +144,45 @@ auto TIA::runPlayer(n8 x, n1 index) -> n1 {
|
||||
|
||||
auto TIA::runMissile(n8 x, n1 index) -> n1 {
|
||||
auto& missile = this->missile[index];
|
||||
if (!missile.enable || missile.reset) return 0;
|
||||
auto& player = this->player[index];
|
||||
auto position = missile.position;
|
||||
|
||||
if(missile.reset) {
|
||||
auto offset = 3;
|
||||
if(player.size == 5) offset = 6;
|
||||
if(player.size == 7) offset = 10;
|
||||
missile.position = player.position + offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!missile.enable) return 0;
|
||||
|
||||
const int missileSizes[4] = {1, 2, 4, 8};
|
||||
return x >= missile.position && x < missile.position + missileSizes[missile.size];
|
||||
|
||||
// Handle player stretch capability
|
||||
auto width = missileSizes[missile.size];
|
||||
auto repeatWidth = width;
|
||||
if(player.size == 5) repeatWidth *= 2;
|
||||
if(player.size == 7) repeatWidth *= 4;
|
||||
|
||||
// Handle repeat capability
|
||||
auto repeat = 1;
|
||||
if(player.size == 1 || player.size == 2 || player.size == 4) repeat = 2;
|
||||
if(player.size == 3 || player.size == 6) repeat = 3;
|
||||
|
||||
auto spacing = 8;
|
||||
if(player.size == 2 || player.size == 6) spacing = 24;
|
||||
if(player.size == 4 ) spacing = 56;
|
||||
|
||||
for(int i = 0; i < repeat; i++) {
|
||||
if (x >= position && x < position + width) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
position = (position + (spacing + repeatWidth)) % 160;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto TIA::runBall(n8 x) -> n1 {
|
||||
@@ -163,7 +212,7 @@ auto TIA::power(bool reset) -> void {
|
||||
ball = {};
|
||||
collision = {};
|
||||
for(auto n : range(writeQueue.maxItems)) writeQueue.items[n] = {};
|
||||
|
||||
audio[0] = {}; audio[1] = {};
|
||||
for(u32 level : range(15)) {
|
||||
volume[level] = pow(2, level * -2.0 / 6.0);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ struct TIA : Thread {
|
||||
Node::Audio::Stream stream;
|
||||
|
||||
auto vlines() const -> u32 { return Region::NTSC() ? 262 : 312; }
|
||||
auto displayHeight() const -> u32 { return Region::PAL() ? 243 : 228; }
|
||||
auto voffset() const -> s32 { return Region::PAL() ? 40 : 20; }
|
||||
|
||||
//tia.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
@@ -78,6 +80,7 @@ struct TIA : Thread {
|
||||
|
||||
struct {
|
||||
n20 graphics;
|
||||
n1 pixel;
|
||||
n1 mirror;
|
||||
n1 scoreMode;
|
||||
n1 priority;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include <ares/debug/debug.cpp>
|
||||
#include <nall/gdb/server.cpp>
|
||||
#include <ares/node/node.cpp>
|
||||
#include <ares/resource/resource.cpp>
|
||||
|
||||
@@ -8,4 +9,13 @@ namespace ares {
|
||||
Platform* platform = nullptr;
|
||||
bool _runAhead = false;
|
||||
|
||||
const string Name = "ares";
|
||||
const string Version = "136";
|
||||
const string Copyright = "ares team, Near";
|
||||
const string License = "ISC";
|
||||
const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
const string Website = "ares-emu.net";
|
||||
const string WebsiteURI = "https://ares-emu.net/";
|
||||
const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
|
||||
|
||||
}
|
||||
+9
-11
@@ -8,6 +8,7 @@
|
||||
#include <nall/any.hpp>
|
||||
#include <nall/array.hpp>
|
||||
#include <nall/bump-allocator.hpp>
|
||||
#include <nall/case-range.hpp>
|
||||
#include <nall/chrono.hpp>
|
||||
#include <nall/directory.hpp>
|
||||
#include <nall/dl.hpp>
|
||||
@@ -39,17 +40,14 @@ using namespace nall;
|
||||
using namespace nall::primitives;
|
||||
|
||||
namespace ares {
|
||||
static const string Name = "ares";
|
||||
static const string Version = "131";
|
||||
static const string Copyright = "ares team, Near";
|
||||
static const string License = "ISC";
|
||||
static const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
static const string Website = "ares-emu.net";
|
||||
static const string WebsiteURI = "https://ares-emu.net/";
|
||||
|
||||
//incremented only when serialization format changes
|
||||
static const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
|
||||
static const string SerializerVersion = "131";
|
||||
extern const string Name;
|
||||
extern const string Version;
|
||||
extern const string Copyright;
|
||||
extern const string License;
|
||||
extern const string LicenseURI;
|
||||
extern const string Website;
|
||||
extern const string WebsiteURI;
|
||||
extern const u32 SerializerSignature;
|
||||
|
||||
namespace VFS {
|
||||
using Pak = shared_pointer<vfs::directory>;
|
||||
|
||||
@@ -1,18 +1,32 @@
|
||||
#include <ares/ares.hpp>
|
||||
|
||||
#if !defined(PLATFORM_MACOS)
|
||||
#define STATIC_ALLOCATION
|
||||
#endif
|
||||
|
||||
namespace ares::Memory {
|
||||
|
||||
#if defined(PLATFORM_MACOS)
|
||||
//stub for unsupported platforms
|
||||
FixedAllocator::FixedAllocator() {
|
||||
}
|
||||
#else
|
||||
alignas(4096) u8 fixedBuffer[128_MiB];
|
||||
constexpr u32 fixedBufferSize = 128_MiB;
|
||||
|
||||
#if defined(STATIC_ALLOCATION)
|
||||
u8 fixedBuffer[fixedBufferSize + 64_KiB];
|
||||
#endif
|
||||
|
||||
FixedAllocator::FixedAllocator() {
|
||||
_allocator.resize(sizeof(fixedBuffer), 0, fixedBuffer);
|
||||
u8* buffer = nullptr;
|
||||
|
||||
#if defined(STATIC_ALLOCATION)
|
||||
//align to 64 KiB (maximum page size of any supported OS)
|
||||
auto offset = -(uintptr)fixedBuffer % 64_KiB;
|
||||
//set protection to executable
|
||||
if(memory::protect(fixedBuffer + offset, fixedBufferSize, true)) {
|
||||
//use static allocation
|
||||
buffer = fixedBuffer + offset;
|
||||
}
|
||||
#endif
|
||||
|
||||
_allocator.resize(fixedBufferSize, bump_allocator::executable, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
auto FixedAllocator::get() -> bump_allocator& {
|
||||
static FixedAllocator allocator;
|
||||
|
||||
@@ -25,7 +25,7 @@ struct Stream : Audio {
|
||||
template<typename... P>
|
||||
auto frame(P&&... p) -> void {
|
||||
if(runAhead()) return;
|
||||
f64 samples[sizeof...(p)] = {std::forward<P>(p)...};
|
||||
f64 samples[sizeof...(p)] = {f64(std::forward<P>(p))...};
|
||||
write(samples);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,9 +71,7 @@ struct Instruction : Tracer {
|
||||
if(!enabled()) return;
|
||||
|
||||
if(_omitted) {
|
||||
PlatformLog({
|
||||
"[Omitted: ", _omitted, "]\n"}
|
||||
);
|
||||
PlatformLog(shared(), {"[Omitted: ", _omitted, "]"});
|
||||
_omitted = 0;
|
||||
}
|
||||
|
||||
@@ -84,7 +82,7 @@ struct Instruction : Tracer {
|
||||
context, " ",
|
||||
extra
|
||||
};
|
||||
PlatformLog({output.strip(), "\n"});
|
||||
PlatformLog(shared(), {output.strip()});
|
||||
}
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
|
||||
@@ -6,12 +6,7 @@ struct Notification : Tracer {
|
||||
|
||||
auto notify(const string& message = {}) -> void {
|
||||
if(!enabled()) return;
|
||||
|
||||
if(message) {
|
||||
PlatformLog({_component, " ", _name, ": ", message, "\n"});
|
||||
} else {
|
||||
PlatformLog({_component, " ", _name, "\n"});
|
||||
}
|
||||
PlatformLog(shared(), message);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -6,24 +6,40 @@ struct Tracer : Debugger {
|
||||
}
|
||||
|
||||
auto component() const -> string { return _component; }
|
||||
auto enabled() const -> bool { return _enabled; }
|
||||
auto enabled() const -> bool { return file() || terminal(); }
|
||||
auto prefix() const -> bool { return _prefix; }
|
||||
auto terminal() const -> bool { return _terminal; }
|
||||
auto file() const -> bool { return _file; }
|
||||
auto autoLineBreak() const -> bool { return _autoLineBreak; }
|
||||
|
||||
auto setToggle(function<void ()> toggle) -> void { _toggle = toggle; }
|
||||
auto setComponent(string component) -> void { _component = component; }
|
||||
auto setEnabled(bool enabled) -> void { _enabled = enabled; }
|
||||
auto setPrefix(bool prefix) -> void { _prefix = prefix; }
|
||||
auto setTerminal(bool terminal) -> void { _terminal = terminal; if(_toggle) _toggle(); }
|
||||
auto setFile(bool file) -> void { _file = file; if(_toggle) _toggle(); }
|
||||
auto setAutoLineBreak(bool autoLineBreak) -> void { _autoLineBreak = autoLineBreak; }
|
||||
|
||||
auto serialize(string& output, string depth) -> void override {
|
||||
Debugger::serialize(output, depth);
|
||||
output.append(depth, " component: ", _component, "\n");
|
||||
output.append(depth, " enabled: ", _enabled, "\n");
|
||||
output.append(depth, " prefix: ", _prefix, "\n");
|
||||
output.append(depth, " terminal: ", _terminal, "\n");
|
||||
output.append(depth, " file: ", _file, "\n");
|
||||
}
|
||||
|
||||
auto unserialize(Markup::Node node) -> void override {
|
||||
Debugger::unserialize(node);
|
||||
_component = node["component"].string();
|
||||
_enabled = node["enabled"].boolean();
|
||||
_prefix = node["prefix"].boolean();
|
||||
_terminal = node["terminal"].boolean();
|
||||
_file = node["file"].boolean();
|
||||
}
|
||||
|
||||
protected:
|
||||
function<void ()> _toggle;
|
||||
string _component;
|
||||
bool _enabled = false;
|
||||
bool _prefix = false;
|
||||
bool _terminal = false;
|
||||
bool _file = false;
|
||||
bool _autoLineBreak = true;
|
||||
};
|
||||
@@ -2,9 +2,17 @@ struct Rumble : Input {
|
||||
DeclareClass(Rumble, "input.rumble")
|
||||
using Input::Input;
|
||||
|
||||
auto enable() const -> bool { return _enable; }
|
||||
auto setEnable(bool enable) -> void { _enable = enable; }
|
||||
auto weakValue() const -> u16 { return _weak; }
|
||||
auto strongValue() const -> u16 { return _strong; }
|
||||
|
||||
auto setValues(u16 strong, u16 weak) -> void { _weak = weak; _strong = strong; }
|
||||
auto setWeak(u16 weak) -> void { _weak = weak; }
|
||||
auto setStrong(u16 strong) -> void { _strong = strong; }
|
||||
|
||||
// For systems with binary motors
|
||||
auto enable() const -> bool { return _weak > 0 || _strong > 0; }
|
||||
auto setEnable(bool enable) -> void { _weak = enable ? 65535 : 0; _strong = enable ? 65535 : 0; }
|
||||
protected:
|
||||
bool _enable = 0;
|
||||
u16 _weak = 0;
|
||||
u16 _strong = 0;
|
||||
};
|
||||
@@ -94,7 +94,7 @@ namespace ares::Core {
|
||||
// <ares/platform.hpp> forward declarations
|
||||
static auto PlatformAttach(Node::Object) -> void;
|
||||
static auto PlatformDetach(Node::Object) -> void;
|
||||
static auto PlatformLog(string_view) -> void;
|
||||
static auto PlatformLog(Node::Debugger::Tracer::Tracer tracer, string_view) -> void;
|
||||
|
||||
#include <ares/node/attribute.hpp>
|
||||
#include <ares/node/class.hpp>
|
||||
|
||||
@@ -76,6 +76,11 @@ auto Screen::setViewport(u32 x, u32 y, u32 width, u32 height) -> void {
|
||||
_viewportHeight = height;
|
||||
}
|
||||
|
||||
auto Screen::setOverscan(bool overscan) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_overscan = overscan;
|
||||
}
|
||||
|
||||
auto Screen::setSize(u32 width, u32 height) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_width = width;
|
||||
@@ -122,6 +127,11 @@ auto Screen::setColorBleed(bool colorBleed) -> void {
|
||||
_colorBleed = colorBleed;
|
||||
}
|
||||
|
||||
auto Screen::setColorBleedWidth(u32 width) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_colorBleedWidth = width;
|
||||
}
|
||||
|
||||
auto Screen::setInterframeBlending(bool interframeBlending) -> void {
|
||||
lock_guard<recursive_mutex> lock(_mutex);
|
||||
_interframeBlending = interframeBlending;
|
||||
@@ -228,14 +238,18 @@ auto Screen::refresh() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
if(_colorBleed) {
|
||||
if (_colorBleed) {
|
||||
n32 mask = 1 << 24 | 1 << 16 | 1 << 8 | 1 << 0;
|
||||
for(u32 y : range(height)) {
|
||||
for (u32 y : range(height)) {
|
||||
auto target = output + y * width;
|
||||
for(u32 x : range(width)) {
|
||||
auto a = target[x];
|
||||
auto b = target[x + (x != width - 1)];
|
||||
target[x] = (a + b - ((a ^ b) & mask)) >> 1;
|
||||
for (u32 x : range(0, width, _colorBleedWidth)) {
|
||||
for (u32 offset = 0; offset < _colorBleedWidth && (x + offset) < width; ++offset) {
|
||||
u32 next = x + _colorBleedWidth;
|
||||
if (next + offset >= width) next = x;
|
||||
auto a = target[x + offset];
|
||||
auto b = target[next + offset];
|
||||
target[x + offset] = (a + b - ((a ^ b) & mask)) >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ struct Screen : Video {
|
||||
auto scaleY() const -> f64 { return _scaleY; }
|
||||
auto aspectX() const -> f64 { return _aspectX; }
|
||||
auto aspectY() const -> f64 { return _aspectY; }
|
||||
auto overscan() const -> bool { return _overscan; }
|
||||
auto colors() const -> u32 { return _colors; }
|
||||
auto pixels(bool frame = 0) -> array_span<u32>;
|
||||
|
||||
@@ -37,6 +38,7 @@ struct Screen : Video {
|
||||
auto setSize(u32 width, u32 height) -> void;
|
||||
auto setScale(f64 scaleX, f64 scaleY) -> void;
|
||||
auto setAspect(f64 aspectX, f64 aspectY) -> void;
|
||||
auto setOverscan(bool overscan) -> void;
|
||||
|
||||
auto setSaturation(f64 saturation) -> void;
|
||||
auto setGamma(f64 gamma) -> void;
|
||||
@@ -44,6 +46,7 @@ struct Screen : Video {
|
||||
|
||||
auto setFillColor(u32 fillColor) -> void;
|
||||
auto setColorBleed(bool colorBleed) -> void;
|
||||
auto setColorBleedWidth(u32 width) -> void;
|
||||
auto setInterframeBlending(bool interframeBlending) -> void;
|
||||
auto setRotation(u32 rotation) -> void;
|
||||
|
||||
@@ -78,7 +81,9 @@ protected:
|
||||
f64 _luminance = 1.0;
|
||||
u32 _fillColor = 0;
|
||||
bool _colorBleed = false;
|
||||
bool _colorBleedWidth = 1;
|
||||
bool _interframeBlending = false;
|
||||
bool _overscan = true;
|
||||
u32 _rotation = 0; //counter-clockwise (90 = left, 270 = right)
|
||||
|
||||
function<n64 (n32)> _color;
|
||||
|
||||
@@ -15,11 +15,12 @@ struct Platform {
|
||||
virtual auto detach(Node::Object) -> void {}
|
||||
virtual auto pak(Node::Object) -> shared_pointer<vfs::directory> { return {}; }
|
||||
virtual auto event(Event) -> void {}
|
||||
virtual auto log(string_view message) -> void {}
|
||||
virtual auto log(Node::Debugger::Tracer::Tracer, string_view message) -> void {}
|
||||
virtual auto status(string_view message) -> void {}
|
||||
virtual auto video(Node::Video::Screen, const u32* data, u32 pitch, u32 width, u32 height) -> void {}
|
||||
virtual auto audio(Node::Audio::Stream) -> void {}
|
||||
virtual auto input(Node::Input::Input) -> void {}
|
||||
virtual auto cheat(u32 addr) -> maybe<u32> { return nothing; }
|
||||
};
|
||||
|
||||
extern Platform* platform;
|
||||
@@ -30,5 +31,5 @@ namespace ares::Core {
|
||||
// <ares/node/node.hpp> forward declarations
|
||||
auto PlatformAttach(Node::Object node) -> void { if(platform && node->name()) platform->attach(node); }
|
||||
auto PlatformDetach(Node::Object node) -> void { if(platform && node->name()) platform->detach(node); }
|
||||
auto PlatformLog(string_view text) -> void { if(platform) platform->log(text); }
|
||||
auto PlatformLog(Node::Debugger::Tracer::Tracer node, string_view text) -> void { if(platform) platform->log(node, text); }
|
||||
}
|
||||
@@ -41,7 +41,7 @@ private:
|
||||
vector<Thread*> _threads;
|
||||
bool _synchronize = false;
|
||||
|
||||
friend class Thread;
|
||||
friend struct Thread;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
||||
@@ -46,5 +46,5 @@ protected:
|
||||
u64 _scalar = 0;
|
||||
u64 _clock = 0;
|
||||
|
||||
friend class Scheduler;
|
||||
friend struct Scheduler;
|
||||
};
|
||||
@@ -12,10 +12,13 @@ ares.objects += $(if $(findstring ym2612,$(ares.components)),ares-audio-ym2612)
|
||||
ares.objects += $(if $(findstring m24c,$(ares.components)),ares-eeprom-m24c)
|
||||
ares.objects += $(if $(findstring m93lcx6,$(ares.components)),ares-eeprom-m93lcx6)
|
||||
|
||||
ares.objects += $(if $(findstring i8255,$(ares.components)),ares-io-i8255)
|
||||
|
||||
ares.objects += $(if $(findstring arm7tdmi,$(ares.components)),ares-processor-arm7tdmi)
|
||||
ares.objects += $(if $(findstring gsu,$(ares.components)),ares-processor-gsu)
|
||||
ares.objects += $(if $(findstring hg51b,$(ares.components)),ares-processor-hg51b)
|
||||
ares.objects += $(if $(findstring huc6280,$(ares.components)),ares-processor-huc6280)
|
||||
ares.objects += $(if $(findstring i8080,$(ares.components)),ares-processor-i8080)
|
||||
ares.objects += $(if $(findstring m68hc05,$(ares.components)),ares-processor-m68hc05)
|
||||
ares.objects += $(if $(findstring m68000,$(ares.components)),ares-processor-m68000)
|
||||
ares.objects += $(if $(findstring mos6502,$(ares.components)),ares-processor-mos6502)
|
||||
@@ -30,6 +33,8 @@ ares.objects += $(if $(findstring v30mz,$(ares.components)),ares-processor-v30mz
|
||||
ares.objects += $(if $(findstring wdc65816,$(ares.components)),ares-processor-wdc65816)
|
||||
ares.objects += $(if $(findstring z80,$(ares.components)),ares-processor-z80)
|
||||
|
||||
ares.objects += $(if $(findstring sst39sf0x0,$(ares.components)),ares-flash-sst39sf0x0)
|
||||
|
||||
ares.objects += $(if $(findstring tms9918,$(ares.components)),ares-video-tms9918)
|
||||
ares.objects += $(if $(findstring v9938,$(ares.components)),ares-video-v9938)
|
||||
|
||||
@@ -39,16 +44,18 @@ $(object.path)/ares-audio-sn76489.o: $(ares.path)/component/audio/sn76489/s
|
||||
$(object.path)/ares-audio-t6w28.o: $(ares.path)/component/audio/t6w28/t6w28.cpp
|
||||
$(object.path)/ares-audio-ym2149.o: $(ares.path)/component/audio/ym2149/ym2149.cpp
|
||||
$(object.path)/ares-audio-ym2413.o: $(ares.path)/component/audio/ym2413/ym2413.cpp
|
||||
$(object.path)/ares-audio-ym2610.o: $(ares.path)/component/audio/ym2610/ym2610.cpp
|
||||
$(object.path)/ares-audio-ym2612.o: $(ares.path)/component/audio/ym2612/ym2612.cpp
|
||||
|
||||
$(object.path)/ares-eeprom-m24c.o: $(ares.path)/component/eeprom/m24c/m24c.cpp
|
||||
$(object.path)/ares-eeprom-m93lcx6.o: $(ares.path)/component/eeprom/m93lcx6/m93lcx6.cpp
|
||||
|
||||
$(object.path)/ares-io-i8255.o: $(ares.path)/component/io/i8255/i8255.cpp
|
||||
|
||||
$(object.path)/ares-processor-arm7tdmi.o: $(ares.path)/component/processor/arm7tdmi/arm7tdmi.cpp
|
||||
$(object.path)/ares-processor-gsu.o: $(ares.path)/component/processor/gsu/gsu.cpp
|
||||
$(object.path)/ares-processor-hg51b.o: $(ares.path)/component/processor/hg51b/hg51b.cpp
|
||||
$(object.path)/ares-processor-huc6280.o: $(ares.path)/component/processor/huc6280/huc6280.cpp
|
||||
$(object.path)/ares-processor-i8080.o: $(ares.path)/component/processor/i8080/i8080.cpp
|
||||
$(object.path)/ares-processor-m68hc05.o: $(ares.path)/component/processor/m68hc05/m68hc05.cpp
|
||||
$(object.path)/ares-processor-m68000.o: $(ares.path)/component/processor/m68000/m68000.cpp
|
||||
$(object.path)/ares-processor-mos6502.o: $(ares.path)/component/processor/mos6502/mos6502.cpp
|
||||
@@ -63,5 +70,7 @@ $(object.path)/ares-processor-v30mz.o: $(ares.path)/component/processor/v30mz
|
||||
$(object.path)/ares-processor-wdc65816.o: $(ares.path)/component/processor/wdc65816/wdc65816.cpp
|
||||
$(object.path)/ares-processor-z80.o: $(ares.path)/component/processor/z80/z80.cpp
|
||||
|
||||
$(object.path)/ares-flash-sst39sf0x0.o: $(ares.path)/component/flash/sst39sf0x0/sst39sf0x0.cpp
|
||||
|
||||
$(object.path)/ares-video-tms9918.o: $(ares.path)/component/video/tms9918/tms9918.cpp
|
||||
$(object.path)/ares-video-v9938.o: $(ares.path)/component/video/v9938/v9938.cpp
|
||||
@@ -20,17 +20,17 @@ auto SN76489::clock() -> array<n4[4]> {
|
||||
}
|
||||
|
||||
auto SN76489::Tone::clock() -> void {
|
||||
if(!counter--) {
|
||||
if(!counter || !--counter) {
|
||||
counter = pitch;
|
||||
output ^= 1;
|
||||
}
|
||||
}
|
||||
|
||||
auto SN76489::Noise::clock() -> void {
|
||||
if(!counter--) {
|
||||
if(!counter || !--counter) {
|
||||
counter = array<n10[4]>{0x10, 0x20, 0x40, pitch}[rate];
|
||||
if(flip ^= 1) { //0->1 transition
|
||||
output = !lfsr.bit(0);
|
||||
output = lfsr.bit(0);
|
||||
lfsr = (lfsr.bit(0) ^ lfsr.bit(3) & enable) << 15 | lfsr >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
auto YM2610::read(n2 address) -> n8 {
|
||||
switch(address) {
|
||||
case 0: return fm.readStatus();
|
||||
case 1: return ssg.read();
|
||||
|
||||
}
|
||||
|
||||
debug(unimplemented, "[YM2610] read ", hex(address));
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto YM2610::write(n2 address, n8 data) -> void {
|
||||
switch(address) {
|
||||
case 0: {
|
||||
registerAddress = 0x000 | data;
|
||||
if (registerAddress <= 0xf) ssg.select(registerAddress);
|
||||
else fm.writeAddress(registerAddress);
|
||||
return;
|
||||
}
|
||||
case 1: return writeLower(data);
|
||||
case 2: registerAddress = 0x100 | data; fm.writeAddress(registerAddress); return;
|
||||
case 3: return writeUpper(data);
|
||||
}
|
||||
}
|
||||
|
||||
auto YM2610::writeLower(n8 data) -> void {
|
||||
switch(registerAddress) {
|
||||
case 0x000 ... 0x00f:
|
||||
ssg.write(data);
|
||||
return;
|
||||
case 0x010:
|
||||
pcmB.repeat = data.bit(4);
|
||||
if(data.bit(0)) pcmB.power();
|
||||
if(data.bit(7)) pcmB.beginPlay();
|
||||
else pcmB.playing = 0;
|
||||
return;
|
||||
case 0x011: pcmB.left = data.bit(7);
|
||||
pcmB.right = data.bit(6); return;
|
||||
case 0x012: pcmB.startAddress.bit( 8, 15) = data; return;
|
||||
case 0x013: pcmB.startAddress.bit(16, 23) = data; return;
|
||||
case 0x014: pcmB.endAddress.bit ( 8, 15) = data; return;
|
||||
case 0x015: pcmB.endAddress.bit (16, 23) = data; return;
|
||||
case 0x016 ... 0x018: debug(unimplemented, "[YM2610::ADPCMB] Write ", hex(registerAddress), " = ", data);
|
||||
case 0x019: pcmB.delta.bit ( 0, 7) = data; return;
|
||||
case 0x01a: pcmB.delta.bit ( 8, 15) = data; return;
|
||||
case 0x01b: pcmB.volume = data; return;
|
||||
case 0x01c:
|
||||
//EOS
|
||||
debug(unimplemented, "[YM2610::EOS] Write ", hex(registerAddress), " = ", data);
|
||||
return;
|
||||
case 0x01d ... 0x1ff:
|
||||
fm.writeData(data);
|
||||
return;
|
||||
}
|
||||
|
||||
debug(unimplemented, "[YM2610] Write ", hex(registerAddress), " = ", data);
|
||||
}
|
||||
|
||||
auto YM2610::writeUpper(n8 data) -> void {
|
||||
switch(registerAddress) {
|
||||
case 0x100:
|
||||
for(auto n : range(6)) {
|
||||
if(!data.bit(n)) continue;
|
||||
if(data.bit(7)) {
|
||||
pcmA.channels[n].keyOff();
|
||||
continue;
|
||||
}
|
||||
|
||||
pcmA.channels[n].keyOn();
|
||||
}
|
||||
return;
|
||||
case 0x101: pcmA.volume = data.bit(0, 5); return;
|
||||
case 0x108 ... 0x10d: pcmA.channels[registerAddress - 0x108].volume = data.bit(0,4);
|
||||
pcmA.channels[registerAddress - 0x108].left = data.bit(7);
|
||||
pcmA.channels[registerAddress - 0x108].right = data.bit(6); return;
|
||||
case 0x110 ... 0x115: pcmA.channels[registerAddress - 0x110].startAddress.bit( 8, 15) = data; return;
|
||||
case 0x118 ... 0x11d: pcmA.channels[registerAddress - 0x118].startAddress.bit(16, 23) = data; return;
|
||||
case 0x120 ... 0x125: pcmA.channels[registerAddress - 0x120].endAddress.bit ( 8, 15) = data; return;
|
||||
case 0x128 ... 0x12d: pcmA.channels[registerAddress - 0x128].endAddress.bit (16, 23) = data; return;
|
||||
case 0x130 ... 0x1ff: fm.writeAddress(registerAddress); fm.writeData(data); return;
|
||||
}
|
||||
|
||||
debug(unimplemented, "[YM2610] Write ", hex(registerAddress), " = ", data);
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
auto YM2610::PCMA::power() -> void {
|
||||
for(auto c : range(6)) {
|
||||
channels[c].startAddress.bit(0, 7) = 0x00;
|
||||
channels[c].endAddress.bit(0, 7) = 0x00;
|
||||
channels[c].decodeAccumulator = 0;
|
||||
channels[c].decodeStep = 0;
|
||||
}
|
||||
|
||||
i16 stepSize[] = {
|
||||
16, 17, 19, 21, 23, 25, 28, 31, 34, 37,
|
||||
41, 45, 50, 55, 60, 66, 73, 80, 88, 97,
|
||||
107, 118, 130, 143, 157, 173, 190, 209, 230, 253,
|
||||
279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
|
||||
724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
|
||||
};
|
||||
|
||||
// Generate a decode table
|
||||
for (int step = 0; step < 49; step++) {
|
||||
for (int nibble = 0; nibble < 16; nibble++) {
|
||||
int value = (2 * (nibble & 0x07) + 1) * stepSize[step] / 8;
|
||||
decodeTable[step * 16 + nibble] = ((nibble & 0x08) != 0) ? -value : value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto YM2610::PCMA::clock() -> array<i16[2]> {
|
||||
f32 left = 0;
|
||||
f32 right = 0;
|
||||
|
||||
for(auto c : range(6)) {
|
||||
if(!channels[c].playing) continue;
|
||||
if(((channels[c].currentAddress ^ (channels[c].endAddress + (1 << 8))) & 0xfffff) == 0) {
|
||||
channels[c].playing = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto sample = channels[c].decode(self.readPCMA(channels[c].currentAddress));
|
||||
|
||||
int totalVolume = (volume ^ 0x3f) + (channels[c].volume ^ 0x1f);
|
||||
int volumeMultiplier = 15 - (totalVolume & 7);
|
||||
int volumeShift = 5 + (totalVolume >> 3);
|
||||
|
||||
sample = ((sample * volumeMultiplier) >> volumeShift) & ~3;
|
||||
|
||||
if(channels[c].left) left += sample / 2047.0f;
|
||||
if(channels[c].right) right += sample / 2047.0f;
|
||||
}
|
||||
|
||||
return {sclamp<16>(left * 32768.0), sclamp<16>(right * 32768.0)};
|
||||
}
|
||||
|
||||
auto YM2610::PCMA::Channel::keyOn() -> void {
|
||||
playing = 1;
|
||||
currentAddress = startAddress;
|
||||
decodeAccumulator = 0;
|
||||
decodeStep = 0;
|
||||
currentNibble = 0;
|
||||
}
|
||||
|
||||
auto YM2610::PCMA::Channel::keyOff() -> void {
|
||||
playing = 0;
|
||||
}
|
||||
|
||||
auto YM2610::PCMA::Channel::decode(n8 value) -> i12 {
|
||||
int stepAdjust[] = { -1, -1, -1, -1, 2, 5, 7, 9, -1, -1, -1, -1, 2, 5, 7, 9 };
|
||||
|
||||
value = currentNibble ? value.bit(0, 3) : value.bit(4, 7);
|
||||
if(currentNibble) currentAddress++;
|
||||
currentNibble = !currentNibble;
|
||||
|
||||
decodeAccumulator += self.decodeTable[decodeStep + value];
|
||||
|
||||
decodeStep += stepAdjust[value & 7] * 16;
|
||||
decodeStep = std::clamp((int)decodeStep, 0, 48 * 16);
|
||||
|
||||
return decodeAccumulator;
|
||||
}
|
||||
|
||||
auto YM2610::PCMB::power() -> void {
|
||||
currentNibble = 0;
|
||||
currentAddress = 0;
|
||||
decodeAccumulator = 0;
|
||||
previousAccumulator = 0;
|
||||
decodePosition = 0;
|
||||
decodeStep = 127;
|
||||
volume = 255;
|
||||
}
|
||||
|
||||
auto YM2610::PCMB::clock() -> array<i16[2]> {
|
||||
f32 leftSample = 0;
|
||||
f32 rightSample = 0;
|
||||
|
||||
decode();
|
||||
|
||||
i32 sample = (previousAccumulator * (i32)((decodePosition ^ 0xffff) + 1) + decodeAccumulator * (i32)(decodePosition)) >> 16;
|
||||
sample = (sample * (i32)volume) >> 9;
|
||||
|
||||
if(left) leftSample += (sample / 65535.0);
|
||||
if(right) rightSample += (sample / 65535.0);
|
||||
|
||||
return {sclamp<16>(leftSample * 32768.0), sclamp<16>(rightSample * 32768.0)};
|
||||
}
|
||||
|
||||
auto YM2610::PCMB::decode() -> void {
|
||||
if(!playing) {
|
||||
return;
|
||||
}
|
||||
|
||||
u8 stepSize[8] = { 57, 57, 57, 57, 77, 102, 128, 153 };
|
||||
|
||||
u32 position = decodePosition + delta;
|
||||
decodePosition = (u16)position;
|
||||
if(position < 0x10000) return;
|
||||
|
||||
n8 value = self.readPCMB(currentAddress);
|
||||
value = currentNibble ? value.bit(0, 3) : value.bit(4, 7);
|
||||
currentNibble = !currentNibble;
|
||||
|
||||
if(currentNibble == 0) {
|
||||
currentAddress++;
|
||||
if(currentAddress == endAddress) {
|
||||
if(repeat) beginPlay();
|
||||
else {
|
||||
decodeAccumulator = 0;
|
||||
previousAccumulator = 0;
|
||||
playing = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
previousAccumulator = decodeAccumulator;
|
||||
|
||||
i32 _delta = (value.bit(0, 2) * 2 + 1) * decodeStep / 8;
|
||||
if(value.bit(3)) _delta = -_delta;
|
||||
|
||||
decodeAccumulator = sclamp<16>(decodeAccumulator + _delta);
|
||||
|
||||
decodeStep = std::clamp(((int)decodeStep * stepSize[value.bit(0, 2)]) / 64, 127, 24576);
|
||||
}
|
||||
|
||||
auto YM2610::PCMB::beginPlay() -> void {
|
||||
currentAddress = startAddress;
|
||||
currentNibble = 0;
|
||||
decodeAccumulator = 0;
|
||||
previousAccumulator = 0;
|
||||
decodePosition = 0;
|
||||
decodeStep = 127;
|
||||
playing = 1;
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
auto YM2610::serialize(serializer& s) -> void {
|
||||
s(registerAddress);
|
||||
s(fm);
|
||||
s(ssg);
|
||||
s(pcmA);
|
||||
s(pcmB);
|
||||
}
|
||||
|
||||
auto YM2610::PCMA::serialize(serializer& s) -> void {
|
||||
for(auto n : range(6)) {
|
||||
s(channels[n].playing);
|
||||
s(channels[n].ended);
|
||||
s(channels[n].left);
|
||||
s(channels[n].right);
|
||||
s(channels[n].volume);
|
||||
s(channels[n].startAddress);
|
||||
s(channels[n].endAddress);
|
||||
s(channels[n].currentAddress);
|
||||
s(channels[n].decodeAccumulator);
|
||||
s(channels[n].decodeStep);
|
||||
s(channels[n].currentNibble);
|
||||
}
|
||||
}
|
||||
|
||||
auto YM2610::PCMB::serialize(serializer& s) -> void {
|
||||
s(playing);
|
||||
s(repeat);
|
||||
s(left);
|
||||
s(right);
|
||||
s(startAddress);
|
||||
s(endAddress);
|
||||
s(delta);
|
||||
s(volume);
|
||||
|
||||
s(currentAddress);
|
||||
s(currentNibble);
|
||||
s(decodeAccumulator);
|
||||
s(decodePosition);
|
||||
s(previousAccumulator);
|
||||
s(decodeStep);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "ym2610.hpp"
|
||||
|
||||
namespace ares {
|
||||
|
||||
#include "io.cpp"
|
||||
#include "pcm.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto YM2610::power() -> void {
|
||||
fm.power();
|
||||
ssg.power();
|
||||
pcmA.power();
|
||||
pcmB.power();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <component/audio/ym2612/ym2612.hpp>
|
||||
#include <component/audio/ym2149/ym2149.hpp>
|
||||
|
||||
namespace ares {
|
||||
|
||||
//Yamaha YM2610 (OPNB)
|
||||
|
||||
struct YM2610 {
|
||||
//ym2610.cpp
|
||||
auto power() -> void;
|
||||
|
||||
//io.cpp
|
||||
auto read(n2 address) -> n8;
|
||||
auto write(n2 address, n8 data) -> void;
|
||||
auto writeLower(n8 data) -> void;
|
||||
auto writeUpper(n8 data) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
virtual auto readPCMA(u32 address) -> u8 { return 0; }
|
||||
virtual auto readPCMB(u32 address) -> u8 { return 0; }
|
||||
|
||||
protected:
|
||||
n9 registerAddress;
|
||||
YM2612 fm;
|
||||
YM2149 ssg;
|
||||
|
||||
struct PCMA {
|
||||
YM2610& self;
|
||||
|
||||
// pcm.cpp
|
||||
auto power() -> void;
|
||||
auto clock() -> array<i16[2]>;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct Channel {
|
||||
PCMA& self;
|
||||
auto keyOn() -> void;
|
||||
auto keyOff() -> void;
|
||||
auto decode(n8) -> i12;
|
||||
n1 playing;
|
||||
n1 ended;
|
||||
n1 left;
|
||||
n1 right;
|
||||
n5 volume;
|
||||
n32 startAddress;
|
||||
n32 endAddress;
|
||||
n32 currentAddress;
|
||||
i12 decodeAccumulator;
|
||||
i32 decodeStep;
|
||||
bool currentNibble;
|
||||
} channels[6]{{*this}, {*this}, {*this}, {*this}, {*this}, {*this}};
|
||||
|
||||
n6 volume;
|
||||
int decodeTable[16 * 49];
|
||||
} pcmA{*this};
|
||||
|
||||
struct PCMB {
|
||||
YM2610& self;
|
||||
|
||||
// pcm.cpp
|
||||
auto power() -> void;
|
||||
auto clock() -> array<i16[2]>;
|
||||
auto decode() -> void;
|
||||
auto beginPlay() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
n1 playing;
|
||||
n1 repeat;
|
||||
n1 left;
|
||||
n2 right;
|
||||
n32 startAddress;
|
||||
n32 endAddress;
|
||||
n16 delta;
|
||||
n8 volume;
|
||||
|
||||
n32 currentAddress;
|
||||
bool currentNibble;
|
||||
i32 decodeAccumulator;
|
||||
u32 decodePosition;
|
||||
i32 previousAccumulator;
|
||||
i32 decodeStep;
|
||||
} pcmB{*this};
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
auto YM2612::Channel::Operator::updateKeyState(bool state) -> void {
|
||||
if(keyOn == state) return; //no change
|
||||
keyOn = state;
|
||||
auto YM2612::Channel::Operator::updateKeyState() -> void {
|
||||
if(keyOn == keyLine) return; //no change
|
||||
keyOn = keyLine;
|
||||
|
||||
if(keyOn) {
|
||||
//restart phase and envelope generators
|
||||
@@ -27,7 +27,6 @@ auto YM2612::Channel::Operator::updateKeyState(bool state) -> void {
|
||||
}
|
||||
|
||||
auto YM2612::Channel::Operator::runEnvelope() -> void {
|
||||
updateKeyState(keyLine);
|
||||
if(ym2612.envelope.clock & (1 << envelope.divider) - 1) return;
|
||||
|
||||
u32 value = ym2612.envelope.clock >> envelope.divider;
|
||||
@@ -56,6 +55,7 @@ auto YM2612::Channel::Operator::runEnvelope() -> void {
|
||||
}
|
||||
|
||||
auto YM2612::Channel::Operator::runPhase() -> void {
|
||||
updateKeyState();
|
||||
phase.value += phase.delta; //advance wave position
|
||||
if(!(ssg.enable && envelope.value >= 0x200)) return; //SSG loop check
|
||||
|
||||
@@ -156,6 +156,7 @@ auto YM2612::Channel::power() -> void {
|
||||
op.outputLevel = 0x1fff;
|
||||
op.output = 0;
|
||||
op.prior = 0;
|
||||
op.priorBuffer = 0;
|
||||
|
||||
op.pitch.value = 0;
|
||||
op.pitch.reload = 0;
|
||||
|
||||
@@ -50,6 +50,7 @@ auto YM2612::Channel::Operator::serialize(serializer& s) -> void {
|
||||
s(outputLevel);
|
||||
s(output);
|
||||
s(prior);
|
||||
s(priorBuffer);
|
||||
|
||||
s(pitch.value);
|
||||
s(pitch.reload);
|
||||
|
||||
@@ -30,13 +30,17 @@ auto YM2612::clock() -> array<i16[2]> {
|
||||
return y < 0x1a00 ? pow2[y & 0x1ff] << 2 >> (y >> 9) : 0; // -78 dB floor
|
||||
};
|
||||
|
||||
s32 feedback = modMask & op[0].output + op[0].prior >> 9 - channel.feedback;
|
||||
s32 feedback = modMask & op[0].prior + op[0].priorBuffer >> 9 - channel.feedback;
|
||||
s32 accumulator = 0;
|
||||
|
||||
op[0].priorBuffer = op[0].prior; // only need to buffer the output for feedback
|
||||
for(auto n : range(4)) op[n].prior = op[n].output;
|
||||
|
||||
op[0].output = wave(0, feedback * (channel.feedback > 0));
|
||||
|
||||
// Note: Despite not emulating per cycle, operator pipelining has been accounted for.
|
||||
// Given the op order 0-2-1-3, newly generated output is not available for the following op.
|
||||
|
||||
if(channel.algorithm == 0) {
|
||||
//0 -> 1 -> 2 -> 3
|
||||
op[1].output = wave(1, mod(0));
|
||||
@@ -48,7 +52,7 @@ auto YM2612::clock() -> array<i16[2]> {
|
||||
if(channel.algorithm == 1) {
|
||||
//(0 + 1) -> 2 -> 3
|
||||
op[1].output = wave(1, 0);
|
||||
op[2].output = wave(2, mod(0) + old(1));
|
||||
op[2].output = wave(2, old(0) + old(1));
|
||||
op[3].output = wave(3, mod(2));
|
||||
accumulator += out(3);
|
||||
}
|
||||
@@ -65,7 +69,7 @@ auto YM2612::clock() -> array<i16[2]> {
|
||||
//(0 -> 1) + 2 -> 3
|
||||
op[1].output = wave(1, mod(0));
|
||||
op[2].output = wave(2, 0);
|
||||
op[3].output = wave(3, mod(1) + mod(2));
|
||||
op[3].output = wave(3, old(1) + mod(2));
|
||||
accumulator += out(3);
|
||||
}
|
||||
|
||||
@@ -104,8 +108,18 @@ auto YM2612::clock() -> array<i16[2]> {
|
||||
s32 voiceData = sclamp<14>(accumulator) & outMask;
|
||||
if(dac.enable && (&channel == &channels[5])) voiceData = (s32)dac.sample - 0x80 << 6;
|
||||
|
||||
if(channel.leftEnable ) left += voiceData;
|
||||
if(channel.rightEnable) right += voiceData;
|
||||
// DAC output + voltage offset (crossover distortion)
|
||||
if(voiceData < 0) {
|
||||
left += channel.leftEnable * (voiceData + (1<<5)) - (4<<5);
|
||||
right += channel.rightEnable * (voiceData + (1<<5)) - (4<<5);
|
||||
} else {
|
||||
left += channel.leftEnable * (voiceData + (0<<5)) + (4<<5);
|
||||
right += channel.rightEnable * (voiceData + (0<<5)) + (4<<5);
|
||||
}
|
||||
|
||||
// DAC output (ym3438 fix)
|
||||
//if(channel.leftEnable ) left += voiceData;
|
||||
//if(channel.rightEnable) right += voiceData;
|
||||
}
|
||||
|
||||
timerA.run();
|
||||
|
||||
@@ -91,7 +91,7 @@ protected:
|
||||
Operator(Channel& channel) : channel(channel), ym2612(channel.ym2612) {}
|
||||
|
||||
//channel.cpp
|
||||
auto updateKeyState(bool) -> void;
|
||||
auto updateKeyState() -> void;
|
||||
|
||||
auto runEnvelope() -> void;
|
||||
auto runPhase() -> void;
|
||||
@@ -114,6 +114,7 @@ protected:
|
||||
n16 outputLevel = 0x1fff;
|
||||
i16 output = 0;
|
||||
i16 prior = 0;
|
||||
i16 priorBuffer = 0;
|
||||
|
||||
struct Pitch {
|
||||
n11 value = 0;
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "sst39sf0x0.hpp"
|
||||
|
||||
namespace ares {
|
||||
|
||||
auto SST39SF0x0::reset() -> void {
|
||||
mode = Mode::Data;
|
||||
unlockStep = UnlockStep::Unlock0;
|
||||
}
|
||||
|
||||
auto SST39SF0x0::read(n32 address) -> n8 {
|
||||
if(mode == Mode::ID) {
|
||||
if(address == 0x00) return 0xBF; /* Manufacturer ID */
|
||||
if(address == 0x01) { /* Device ID */
|
||||
if(flash.size() <= 128 * 1024) return 0xB5;
|
||||
if(flash.size() <= 256 * 1024) return 0xB6;
|
||||
return 0xB7;
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
return flash.read(address);
|
||||
}
|
||||
|
||||
auto SST39SF0x0::write(n32 address, n8 data) -> void {
|
||||
if(mode == Mode::Program) {
|
||||
flash.write(address, data);
|
||||
mode = Mode::Data;
|
||||
} else if (mode == Mode::EraseBlock) {
|
||||
address &= ~0xFFF;
|
||||
for(u32 i = 0; i < 4096; i++) {
|
||||
flash.write(address + i, 0xFF);
|
||||
}
|
||||
mode = Mode::Data;
|
||||
} else if(mode == Mode::ID && data == 0xF0) {
|
||||
unlockStep = UnlockStep::Unlock0;
|
||||
mode = Mode::Data;
|
||||
} else {
|
||||
if(unlockStep == UnlockStep::Unlock0) {
|
||||
if((address & 0x7FFF) == 0x5555 && (data & 0xFF) == 0xAA) unlockStep = UnlockStep::Unlock1;
|
||||
} else if(unlockStep == UnlockStep::Unlock1) {
|
||||
unlockStep = UnlockStep::Unlock0;
|
||||
if((address & 0x7FFF) == 0x2AAA && (data & 0xFF) == 0x55) unlockStep = UnlockStep::Unlock2;
|
||||
} else {
|
||||
unlockStep = UnlockStep::Unlock0;
|
||||
if((address & 0x7FFF) == 0x5555) {
|
||||
switch(data) {
|
||||
case 0xA0:
|
||||
mode = Mode::Program;
|
||||
break;
|
||||
case 0x80:
|
||||
mode = Mode::Erase;
|
||||
break;
|
||||
case 0x10:
|
||||
if(mode != Mode::Erase) return;
|
||||
for(u32 i = 0; i < flash.size(); i++) {
|
||||
flash.write(i, 0xFF);
|
||||
}
|
||||
break;
|
||||
case 0x30:
|
||||
if(mode != Mode::Erase) return;
|
||||
mode = Mode::EraseBlock;
|
||||
break;
|
||||
case 0x90:
|
||||
mode = Mode::ID;
|
||||
break;
|
||||
case 0xF0:
|
||||
default:
|
||||
mode = Mode::Data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
//Generic flash
|
||||
|
||||
namespace ares {
|
||||
|
||||
struct SST39SF0x0 {
|
||||
auto reset() -> void;
|
||||
auto read(n32 address) -> n8;
|
||||
auto write(n32 address, n8 data) -> void;
|
||||
|
||||
Memory::Writable<n8> flash;
|
||||
|
||||
enum class Mode : u32 {
|
||||
Data,
|
||||
ID, /* 0x90 */
|
||||
Program, /* 0xA0 */
|
||||
Erase, /* 0x80 */
|
||||
EraseBlock /* 0x80 0x30 */
|
||||
} mode;
|
||||
|
||||
enum class UnlockStep : u32 {
|
||||
Unlock0, /* 0xF0 */
|
||||
Unlock1,
|
||||
Unlock2
|
||||
} unlockStep;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "i8255.hpp"
|
||||
|
||||
namespace ares {
|
||||
auto I8255::power() -> void {
|
||||
io.portCLowerInput = 1;
|
||||
io.portBInput = 1;
|
||||
io.groupBMode = 0;
|
||||
io.portCHigherInput = 1;
|
||||
io.portAInput = 1;
|
||||
io.groupAMode = 0;
|
||||
io.ioMode = 0;
|
||||
state.portA = 0xff;
|
||||
state.portB = 0xff;
|
||||
state.portC = 0xff;
|
||||
}
|
||||
|
||||
auto I8255::read(n2 port) -> n8 {
|
||||
switch(port) {
|
||||
case 0: return io.portAInput ? readPortA() : state.portA;
|
||||
case 1: return io.portBInput ? readPortB() : state.portB;
|
||||
case 2: {
|
||||
n8 result = readPortC();
|
||||
if(!io.portCLowerInput) result.bit(0,3) = state.portC.bit(0,3);
|
||||
if(!io.portCHigherInput) result.bit(4,7) = state.portC.bit(4,7);
|
||||
return result;
|
||||
}
|
||||
case 3: return 0xff;
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto I8255::write(n2 port, n8 data) -> void {
|
||||
switch(port) {
|
||||
case 0: state.portA = data; return writePortA(data);
|
||||
case 1: state.portB = data; return writePortB(data);
|
||||
case 2: state.portC = data; return writePortC(data);
|
||||
case 3: return writeControl(data);
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto I8255::writeControl(n8 data) -> void {
|
||||
io.portCLowerInput = data.bit(0);
|
||||
io.portBInput = data.bit(1);
|
||||
io.groupBMode = data.bit(2);
|
||||
io.portCHigherInput = data.bit(3);
|
||||
io.portAInput = data.bit(4);
|
||||
io.groupAMode = data.bit(5,6);
|
||||
io.ioMode = data.bit(7);
|
||||
|
||||
if(!io.ioMode) debug(unimplemented, "[I8255] BSR Mode");
|
||||
if(io.groupAMode != 0) debug(unimplemented, "[I8255] Group A Mode != 0");
|
||||
if(io.groupBMode != 0) debug(unimplemented, "[I8255] Group B Mode != 0");
|
||||
}
|
||||
|
||||
auto I8255::serialize(serializer& s) -> void {
|
||||
s(io.portCLowerInput);
|
||||
s(io.portBInput);
|
||||
s(io.groupBMode);
|
||||
s(io.portCHigherInput);
|
||||
s(io.portAInput);
|
||||
s(io.groupAMode);
|
||||
s(io.ioMode);
|
||||
|
||||
s(state.portA);
|
||||
s(state.portB);
|
||||
s(state.portC);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
namespace ares {
|
||||
struct I8255 {
|
||||
//i8255.cpp
|
||||
auto power() -> void;
|
||||
auto read(n2 port) -> n8;
|
||||
auto write(n2 port, n8 data) -> void;
|
||||
auto writeControl(n8 data) -> void;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
virtual auto readPortA() -> n8 { return 0xff; };
|
||||
virtual auto readPortB() -> n8 { return 0xff; };
|
||||
virtual auto readPortC() -> n8 { return 0xff; };
|
||||
virtual auto writePortA(n8 data) -> void {};
|
||||
virtual auto writePortB(n8 data) -> void {};
|
||||
virtual auto writePortC(n8 data) -> void {};
|
||||
|
||||
struct IO {
|
||||
n1 portCLowerInput;
|
||||
n1 portBInput;
|
||||
n1 groupBMode;
|
||||
n1 portCHigherInput;
|
||||
n1 portAInput;
|
||||
n2 groupAMode;
|
||||
n1 ioMode;
|
||||
} io;
|
||||
|
||||
struct State {
|
||||
n8 portA;
|
||||
n8 portB;
|
||||
n8 portC;
|
||||
} state;
|
||||
};
|
||||
}
|
||||
@@ -62,7 +62,7 @@ struct ARM7TDMI {
|
||||
auto thumbInitialize() -> void;
|
||||
|
||||
//instructions-arm.cpp
|
||||
auto armALU(n4 mode, n4 target, n4 source, n32 data) -> void;
|
||||
auto armALU(n4 mode, n4 target, n32 source, n32 data) -> void;
|
||||
auto armMoveToStatus(n4 field, n1 source, n32 data) -> void;
|
||||
|
||||
auto armInstructionBranch(i24, n1) -> void;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
auto ARM7TDMI::armALU(n4 mode, n4 d, n4 n, n32 rm) -> void {
|
||||
n32 rn = r(n);
|
||||
|
||||
auto ARM7TDMI::armALU(n4 mode, n4 d, n32 rn, n32 rm) -> void {
|
||||
switch(mode) {
|
||||
case 0: r(d) = BIT(rn & rm); break; //AND
|
||||
case 1: r(d) = BIT(rn ^ rm); break; //EOR
|
||||
@@ -64,14 +62,16 @@ auto ARM7TDMI::armInstructionBranchExchangeRegister
|
||||
|
||||
auto ARM7TDMI::armInstructionDataImmediate
|
||||
(n8 immediate, n4 shift, n4 d, n4 n, n1 save, n4 mode) -> void {
|
||||
n32 rn = r(n);
|
||||
n32 data = immediate;
|
||||
carry = cpsr().c;
|
||||
if(shift) data = ROR(data, shift << 1);
|
||||
armALU(mode, d, n, data);
|
||||
armALU(mode, d, rn, data);
|
||||
}
|
||||
|
||||
auto ARM7TDMI::armInstructionDataImmediateShift
|
||||
(n4 m, n2 type, n5 shift, n4 d, n4 n, n1 save, n4 mode) -> void {
|
||||
n32 rn = r(n);
|
||||
n32 rm = r(m);
|
||||
carry = cpsr().c;
|
||||
|
||||
@@ -82,12 +82,13 @@ auto ARM7TDMI::armInstructionDataImmediateShift
|
||||
case 3: rm = shift ? ROR(rm, shift) : RRX(rm); break;
|
||||
}
|
||||
|
||||
armALU(mode, d, n, rm);
|
||||
armALU(mode, d, rn, rm);
|
||||
}
|
||||
|
||||
auto ARM7TDMI::armInstructionDataRegisterShift
|
||||
(n4 m, n2 type, n4 s, n4 d, n4 n, n1 save, n4 mode) -> void {
|
||||
n8 rs = r(s) + (s == 15 ? 4 : 0);
|
||||
n32 rn = r(n) + (n == 15 ? 4 : 0);
|
||||
n32 rm = r(m) + (m == 15 ? 4 : 0);
|
||||
carry = cpsr().c;
|
||||
|
||||
@@ -98,7 +99,7 @@ auto ARM7TDMI::armInstructionDataRegisterShift
|
||||
case 3: if(rs) rm = ROR(rm, rs & 31 ? u32(rs & 31) : 32); break;
|
||||
}
|
||||
|
||||
armALU(mode, d, n, rm);
|
||||
armALU(mode, d, rn, rm);
|
||||
}
|
||||
|
||||
auto ARM7TDMI::armInstructionLoadImmediate
|
||||
@@ -167,7 +168,7 @@ auto ARM7TDMI::armInstructionMoveHalfRegister
|
||||
auto ARM7TDMI::armInstructionMoveImmediateOffset
|
||||
(n12 immediate, n4 d, n4 n, n1 mode, n1 writeback, n1 byte, n1 up, n1 pre) -> void {
|
||||
n32 rn = r(n);
|
||||
n32 rd = r(d);
|
||||
n32 rd = r(d) + (d == 15 ? 4 : 0);
|
||||
|
||||
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
|
||||
if(mode == 1) rd = load((byte ? Byte : Word) | Nonsequential, rn);
|
||||
@@ -201,7 +202,7 @@ auto ARM7TDMI::armInstructionMoveMultiple
|
||||
for(u32 m : range(16)) {
|
||||
if(!list.bit(m)) continue;
|
||||
if(mode == 1) r(m) = read(Word | sequential, rn);
|
||||
if(mode == 0) write(Word | sequential, rn, r(m));
|
||||
if(mode == 0) write(Word | sequential, rn, r(m) + (m == 15 ? 4 : 0));
|
||||
rn += 4;
|
||||
sequential = Sequential;
|
||||
}
|
||||
|
||||
@@ -126,17 +126,23 @@ auto ARM7TDMI::thumbInstructionMoveMultiple
|
||||
(n8 list, n3 n, n1 mode) -> void {
|
||||
n32 rn = r(n);
|
||||
|
||||
u32 sequential = Nonsequential;
|
||||
for(u32 m : range(8)) {
|
||||
if(!list.bit(m)) continue;
|
||||
switch(mode) {
|
||||
case 0: write(Word | Nonsequential, rn, r(m)); break; //STMIA
|
||||
case 1: r(m) = read(Word | Nonsequential, rn); break; //LDMIA
|
||||
case 0: write(Word | sequential, rn, r(m)); break; //STMIA
|
||||
case 1: r(m) = read(Word | sequential, rn); break; //LDMIA
|
||||
}
|
||||
rn += 4;
|
||||
sequential = Sequential;
|
||||
}
|
||||
|
||||
if(mode == 0 || !list.bit(n)) r(n) = rn;
|
||||
if(mode == 1) idle();
|
||||
if(mode == 1) {
|
||||
idle();
|
||||
} else {
|
||||
pipeline.nonsequential = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto ARM7TDMI::thumbInstructionMoveRegisterOffset
|
||||
|
||||
@@ -30,20 +30,6 @@ auto GSU::disassembleOpcode(char* output) -> void {
|
||||
while(length++ < 20) strcat(output, " ");
|
||||
}
|
||||
|
||||
#define case4(id) \
|
||||
case id+ 0: case id+ 1: case id+ 2: case id+ 3
|
||||
#define case6(id) \
|
||||
case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5
|
||||
#define case12(id) \
|
||||
case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \
|
||||
case id+ 8: case id+ 9: case id+10: case id+11
|
||||
#define case15(id) \
|
||||
case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \
|
||||
case id+ 8: case id+ 9: case id+10: case id+11: case id+12: case id+13: case id+14
|
||||
#define case16(id) \
|
||||
case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \
|
||||
case id+ 8: case id+ 9: case id+10: case id+11: case id+12: case id+13: case id+14: case id+15
|
||||
|
||||
#define op0 regs.pipeline
|
||||
#define op1 read((regs.pbr << 16) + regs.r[15] + 0)
|
||||
#define op2 read((regs.pbr << 16) + regs.r[15] + 1)
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
auto I8080::ADD(n8 x, n8 y, bool c) -> n8 {
|
||||
n9 z = x + y + c;
|
||||
|
||||
CF = z.bit(8);
|
||||
PF = parity(z);
|
||||
HF = n8(x ^ y ^ z).bit(4);
|
||||
ZF = n8(z) == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
auto I8080::AND(n8 x, n8 y) -> n8 {
|
||||
n8 z = x & y;
|
||||
|
||||
CF = 0;
|
||||
HF = n8(x | y).bit(3);
|
||||
PF = parity(z);
|
||||
ZF = z == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
auto I8080::CP(n8 x, n8 y) -> void {
|
||||
SUB(x, y, 0);
|
||||
}
|
||||
|
||||
auto I8080::DEC(n8 x) -> n8 {
|
||||
n8 z = x - 1;
|
||||
|
||||
PF = parity(z);
|
||||
HF = x.bit(0,3) != 0x00;
|
||||
ZF = z == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
auto I8080::INC(n8 x) -> n8 {
|
||||
n8 z = x + 1;
|
||||
|
||||
PF = parity(z);
|
||||
HF = x.bit(0,3) == 0x0f;
|
||||
ZF = z == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
auto I8080::OR(n8 x, n8 y) -> n8 {
|
||||
n8 z = x | y;
|
||||
|
||||
CF = 0;
|
||||
PF = parity(z);
|
||||
HF = 0;
|
||||
ZF = z == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
auto I8080::SUB(n8 x, n8 y, bool c) -> n8 {
|
||||
n9 z = x + ~y + !c; // internally implemented as negated ADD
|
||||
|
||||
CF = z.bit(8);
|
||||
PF = parity(z);
|
||||
HF = n8(x ^ ~y ^ z).bit(4);
|
||||
ZF = n8(z) == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
auto I8080::XOR(n8 x, n8 y) -> n8 {
|
||||
n8 z = x ^ y;
|
||||
|
||||
CF = 0;
|
||||
HF = 0;
|
||||
PF = parity(z);
|
||||
ZF = z == 0;
|
||||
SF = z.bit(7);
|
||||
|
||||
return z;
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
no_optimize
|
||||
auto I8080::disassembleInstruction(maybe<n16> _pc) -> string {
|
||||
auto pc = _pc ? *_pc : PC;
|
||||
string s, output;
|
||||
|
||||
n8 prefix = 0x00;
|
||||
auto code = bus->read(pc++);
|
||||
if(code == 0xdd || code == 0xfd) {
|
||||
prefix = code;
|
||||
|
||||
code = bus->read(pc++);
|
||||
if(code == 0xdd || code == 0xfd) {
|
||||
if(prefix == 0xdd) {
|
||||
s.append("ix:");
|
||||
goto finish;
|
||||
}
|
||||
if(prefix == 0xfd) {
|
||||
s.append("iy:");
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output = disassemble(pc, prefix, code);
|
||||
finish:
|
||||
return pad(output, -18L);
|
||||
}
|
||||
|
||||
no_optimize
|
||||
auto I8080::disassembleContext() -> string {
|
||||
string s;
|
||||
|
||||
s.append("AF:", hex(af.word, 4L), " ");
|
||||
s.append("BC:", hex(bc.word, 4L), " ");
|
||||
s.append("DE:", hex(de.word, 4L), " ");
|
||||
s.append("HL:", hex(hl.word, 4L), " ");
|
||||
s.append("IX:", hex(ix.word, 4L), " ");
|
||||
s.append("IY:", hex(iy.word, 4L), " ");
|
||||
s.append("SP:", hex(SP, 4L), " ");
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
#define op(id, name, ...) case id: return {name, " ", vector<string>{__VA_ARGS__}.merge(",")};
|
||||
|
||||
#define N string{"$", hex(byte(), 2L)}
|
||||
#define IN string{"(", N, ")"}
|
||||
#define NN string{"$", hex(word(), 4L)}
|
||||
#define INN string{"(", NN, ")"}
|
||||
#define REL string{"$", hex(branch(), 4L)}
|
||||
|
||||
#define A "a"
|
||||
#define F "f"
|
||||
#define B "b"
|
||||
#define C "c"
|
||||
#define D "d"
|
||||
#define E "e"
|
||||
#define H prefix == 0xdd ? "ixh" : prefix == 0xfd ? "iyh" : "h"
|
||||
#define L prefix == 0xdd ? "ixl" : prefix == 0xfd ? "iyl" : "l"
|
||||
#define _H "h"
|
||||
#define _L "l"
|
||||
#define _HL "hl"
|
||||
|
||||
#define AF "af"
|
||||
#define BC "bc"
|
||||
#define DE "de"
|
||||
#define HL prefix == 0xdd ? "ix" : prefix == 0xfd ? "iy" : "hl"
|
||||
|
||||
#define AF_ "af'"
|
||||
#define BC_ "bc'"
|
||||
#define DE_ "de'"
|
||||
#define HL_ "hl'"
|
||||
|
||||
#define SP "sp"
|
||||
#define PC "pc"
|
||||
|
||||
#define I "i"
|
||||
#define R "r"
|
||||
|
||||
#define IC "(c)"
|
||||
#define IBC "(bc)"
|
||||
#define IDE "(de)"
|
||||
#define IHL "(hl)"
|
||||
#define ISP "(sp)"
|
||||
|
||||
no_optimize
|
||||
auto I8080::disassemble(n16 pc, n8 prefix, n8 code) -> string {
|
||||
auto byte = [&] {
|
||||
return bus->read(pc++);
|
||||
};
|
||||
|
||||
auto word = [&] {
|
||||
n16 data = byte() << 0;
|
||||
return data | byte() << 8;
|
||||
};
|
||||
|
||||
auto branch = [&] {
|
||||
auto d = byte();
|
||||
return pc + (i8)d;
|
||||
};
|
||||
|
||||
auto displace = [&] {
|
||||
if(!prefix) return string{};
|
||||
auto d = (i8)byte();
|
||||
return d >= 0 ? string{"+$", hex(d, 2L)} : string{"-$", hex(-d, 2L)};
|
||||
};
|
||||
|
||||
switch(code) {
|
||||
op(0x00, "nop ")
|
||||
op(0x01, "ld ", BC, NN)
|
||||
op(0x02, "ld ", IBC, A)
|
||||
op(0x03, "inc ", BC)
|
||||
op(0x04, "inc ", B)
|
||||
op(0x05, "dec ", B)
|
||||
op(0x06, "ld ", B, N)
|
||||
op(0x07, "rlca")
|
||||
op(0x08, "ex ", AF, AF_)
|
||||
op(0x09, "add ", HL, BC)
|
||||
op(0x0a, "ld ", A, IBC)
|
||||
op(0x0b, "dec ", BC)
|
||||
op(0x0c, "inc ", C)
|
||||
op(0x0d, "dec ", C)
|
||||
op(0x0e, "ld ", C, N)
|
||||
op(0x0f, "rrca")
|
||||
op(0x10, "djnz", REL)
|
||||
op(0x11, "ld ", DE, NN)
|
||||
op(0x12, "ld ", IDE, A)
|
||||
op(0x13, "inc ", DE)
|
||||
op(0x14, "inc ", D)
|
||||
op(0x15, "dec ", D)
|
||||
op(0x16, "ld ", D, N)
|
||||
op(0x17, "rla ")
|
||||
op(0x18, "jr ", REL)
|
||||
op(0x19, "add ", HL, DE)
|
||||
op(0x1a, "ld ", A, IDE)
|
||||
op(0x1b, "dec ", DE)
|
||||
op(0x1c, "inc ", E)
|
||||
op(0x1d, "dec ", E)
|
||||
op(0x1e, "ld ", E, N)
|
||||
op(0x1f, "rra ")
|
||||
op(0x20, "jr ", "nz", REL)
|
||||
op(0x21, "ld ", HL, NN)
|
||||
op(0x22, "ld ", INN, HL)
|
||||
op(0x23, "inc ", HL)
|
||||
op(0x24, "inc ", H)
|
||||
op(0x25, "dec ", H)
|
||||
op(0x26, "ld ", H, N)
|
||||
op(0x27, "daa ")
|
||||
op(0x28, "jr ", "z", REL)
|
||||
op(0x29, "add ", HL, HL)
|
||||
op(0x2a, "ld ", HL, INN)
|
||||
op(0x2b, "dec ", HL)
|
||||
op(0x2c, "inc ", L)
|
||||
op(0x2d, "dec ", L)
|
||||
op(0x2e, "ld ", L, N)
|
||||
op(0x2f, "cpl ")
|
||||
op(0x30, "jr ", "nc", REL)
|
||||
op(0x31, "ld ", SP, NN)
|
||||
op(0x32, "ld ", INN, A)
|
||||
op(0x33, "inc ", SP)
|
||||
op(0x34, "inc ", IHL)
|
||||
op(0x35, "dec ", IHL)
|
||||
op(0x36, "ld ", IHL, N)
|
||||
op(0x37, "scf ")
|
||||
op(0x38, "jr ", "c", REL)
|
||||
op(0x39, "add ", HL, SP)
|
||||
op(0x3a, "ld ", A, INN)
|
||||
op(0x3b, "dec ", SP)
|
||||
op(0x3c, "inc ", A)
|
||||
op(0x3d, "dec ", A)
|
||||
op(0x3e, "ld ", A, N)
|
||||
op(0x3f, "ccf ")
|
||||
op(0x40, "ld ", B, B)
|
||||
op(0x41, "ld ", B, C)
|
||||
op(0x42, "ld ", B, D)
|
||||
op(0x43, "ld ", B, E)
|
||||
op(0x44, "ld ", B, H)
|
||||
op(0x45, "ld ", B, L)
|
||||
op(0x46, "ld ", B, IHL)
|
||||
op(0x47, "ld ", B, A)
|
||||
op(0x48, "ld ", C, B)
|
||||
op(0x49, "ld ", C, C)
|
||||
op(0x4a, "ld ", C, D)
|
||||
op(0x4b, "ld ", C, E)
|
||||
op(0x4c, "ld ", C, H)
|
||||
op(0x4d, "ld ", C, L)
|
||||
op(0x4e, "ld ", C, IHL)
|
||||
op(0x4f, "ld ", C, A)
|
||||
op(0x50, "ld ", D, B)
|
||||
op(0x51, "ld ", D, C)
|
||||
op(0x52, "ld ", D, D)
|
||||
op(0x53, "ld ", D, E)
|
||||
op(0x54, "ld ", D, H)
|
||||
op(0x55, "ld ", D, L)
|
||||
op(0x56, "ld ", D, IHL)
|
||||
op(0x57, "ld ", D, A)
|
||||
op(0x58, "ld ", E, B)
|
||||
op(0x59, "ld ", E, C)
|
||||
op(0x5a, "ld ", E, D)
|
||||
op(0x5b, "ld ", E, E)
|
||||
op(0x5c, "ld ", E, H)
|
||||
op(0x5d, "ld ", E, L)
|
||||
op(0x5e, "ld ", E, IHL)
|
||||
op(0x5f, "ld ", E, A)
|
||||
op(0x60, "ld ", H, B)
|
||||
op(0x61, "ld ", H, C)
|
||||
op(0x62, "ld ", H, D)
|
||||
op(0x63, "ld ", H, E)
|
||||
op(0x64, "ld ", H, H)
|
||||
op(0x65, "ld ", H, L)
|
||||
op(0x66, "ld ", _H, IHL)
|
||||
op(0x67, "ld ", H, A)
|
||||
op(0x68, "ld ", L, B)
|
||||
op(0x69, "ld ", L, C)
|
||||
op(0x6a, "ld ", L, D)
|
||||
op(0x6b, "ld ", L, E)
|
||||
op(0x6c, "ld ", L, H)
|
||||
op(0x6d, "ld ", L, L)
|
||||
op(0x6e, "ld ", _L, IHL)
|
||||
op(0x6f, "ld ", L, A)
|
||||
op(0x70, "ld ", IHL, B)
|
||||
op(0x71, "ld ", IHL, C)
|
||||
op(0x72, "ld ", IHL, D)
|
||||
op(0x73, "ld ", IHL, E)
|
||||
op(0x74, "ld ", IHL, _H)
|
||||
op(0x75, "ld ", IHL, _L)
|
||||
op(0x76, "halt")
|
||||
op(0x77, "ld ", IHL, A)
|
||||
op(0x78, "ld ", A, B)
|
||||
op(0x79, "ld ", A, C)
|
||||
op(0x7a, "ld ", A, D)
|
||||
op(0x7b, "ld ", A, E)
|
||||
op(0x7c, "ld ", A, H)
|
||||
op(0x7d, "ld ", A, L)
|
||||
op(0x7e, "ld ", A, IHL)
|
||||
op(0x7f, "ld ", A, A)
|
||||
op(0x80, "add ", A, B)
|
||||
op(0x81, "add ", A, C)
|
||||
op(0x82, "add ", A, D)
|
||||
op(0x83, "add ", A, E)
|
||||
op(0x84, "add ", A, H)
|
||||
op(0x85, "add ", A, L)
|
||||
op(0x86, "add ", A, IHL)
|
||||
op(0x87, "add ", A, A)
|
||||
op(0x88, "adc ", A, B)
|
||||
op(0x89, "adc ", A, C)
|
||||
op(0x8a, "adc ", A, D)
|
||||
op(0x8b, "adc ", A, E)
|
||||
op(0x8c, "adc ", A, H)
|
||||
op(0x8d, "adc ", A, L)
|
||||
op(0x8e, "adc ", A, IHL)
|
||||
op(0x8f, "adc ", A, A)
|
||||
op(0x90, "sub ", A, B)
|
||||
op(0x91, "sub ", A, C)
|
||||
op(0x92, "sub ", A, D)
|
||||
op(0x93, "sub ", A, E)
|
||||
op(0x94, "sub ", A, H)
|
||||
op(0x95, "sub ", A, L)
|
||||
op(0x96, "sub ", A, IHL)
|
||||
op(0x97, "sub ", A, A)
|
||||
op(0x98, "sbc ", A, B)
|
||||
op(0x99, "sbc ", A, C)
|
||||
op(0x9a, "sbc ", A, D)
|
||||
op(0x9b, "sbc ", A, E)
|
||||
op(0x9c, "sbc ", A, H)
|
||||
op(0x9d, "sbc ", A, L)
|
||||
op(0x9e, "sbc ", A, IHL)
|
||||
op(0x9f, "sbc ", A, A)
|
||||
op(0xa0, "and ", A, B)
|
||||
op(0xa1, "and ", A, C)
|
||||
op(0xa2, "and ", A, D)
|
||||
op(0xa3, "and ", A, E)
|
||||
op(0xa4, "and ", A, H)
|
||||
op(0xa5, "and ", A, L)
|
||||
op(0xa6, "and ", A, IHL)
|
||||
op(0xa7, "and ", A, A)
|
||||
op(0xa8, "xor ", A, B)
|
||||
op(0xa9, "xor ", A, C)
|
||||
op(0xaa, "xor ", A, D)
|
||||
op(0xab, "xor ", A, E)
|
||||
op(0xac, "xor ", A, H)
|
||||
op(0xad, "xor ", A, L)
|
||||
op(0xae, "xor ", A, IHL)
|
||||
op(0xaf, "xor ", A, A)
|
||||
op(0xb0, "or ", A, B)
|
||||
op(0xb1, "or ", A, C)
|
||||
op(0xb2, "or ", A, D)
|
||||
op(0xb3, "or ", A, E)
|
||||
op(0xb4, "or ", A, H)
|
||||
op(0xb5, "or ", A, L)
|
||||
op(0xb6, "or ", A, IHL)
|
||||
op(0xb7, "or ", A, A)
|
||||
op(0xb8, "cp ", A, B)
|
||||
op(0xb9, "cp ", A, C)
|
||||
op(0xba, "cp ", A, D)
|
||||
op(0xbb, "cp ", A, E)
|
||||
op(0xbc, "cp ", A, H)
|
||||
op(0xbd, "cp ", A, L)
|
||||
op(0xbe, "cp ", A, IHL)
|
||||
op(0xbf, "cp ", A, A)
|
||||
op(0xc0, "ret ", "nz")
|
||||
op(0xc1, "pop ", BC)
|
||||
op(0xc2, "jp ", "nz", NN)
|
||||
op(0xc3, "jp ", NN)
|
||||
op(0xc4, "call", "nz", NN)
|
||||
op(0xc5, "push", BC)
|
||||
op(0xc6, "add ", A, N)
|
||||
op(0xc7, "rst ", "$0000")
|
||||
op(0xc8, "ret ", "z")
|
||||
op(0xc9, "ret ")
|
||||
op(0xca, "jp ", "z", NN)
|
||||
op(0xcb, "cb: ")
|
||||
op(0xcc, "call", "z", NN)
|
||||
op(0xcd, "call", NN)
|
||||
op(0xce, "adc ", A, N)
|
||||
op(0xcf, "rst ", "$0008")
|
||||
op(0xd0, "ret ", "nc")
|
||||
op(0xd1, "pop ", DE)
|
||||
op(0xd2, "jp ", "nc", NN)
|
||||
op(0xd3, "out ", IN, A)
|
||||
op(0xd4, "call", "nc", NN)
|
||||
op(0xd5, "push", DE)
|
||||
op(0xd6, "sub ", A, N)
|
||||
op(0xd7, "rst ", "$0010")
|
||||
op(0xd8, "ret ", "c")
|
||||
op(0xd9, "exx ")
|
||||
op(0xda, "jp ", "c", NN)
|
||||
op(0xdb, "in ", A, IN)
|
||||
op(0xdc, "call", "c", NN)
|
||||
op(0xdd, "ix: ")
|
||||
op(0xde, "sbc ", A, N)
|
||||
op(0xdf, "rst ", "$0018")
|
||||
op(0xe0, "ret ", "po")
|
||||
op(0xe1, "pop ", HL)
|
||||
op(0xe2, "jp ", "po", NN)
|
||||
op(0xe3, "ex ", ISP, HL)
|
||||
op(0xe4, "call", "po", NN)
|
||||
op(0xe5, "push", HL)
|
||||
op(0xe6, "and ", A, N)
|
||||
op(0xe7, "rst ", "$0020")
|
||||
op(0xe8, "ret ", "pe")
|
||||
op(0xe9, "jp ", HL) //officially jp (hl); but as read is not indirect, use jp hl
|
||||
op(0xea, "jp ", "pe", NN)
|
||||
op(0xeb, "ex ", DE, _HL)
|
||||
op(0xec, "call", "pe", NN)
|
||||
op(0xed, "ed: ")
|
||||
op(0xee, "xor ", A, N)
|
||||
op(0xef, "rst ", "$0028")
|
||||
op(0xf0, "ret ", "p")
|
||||
op(0xf1, "pop ", AF)
|
||||
op(0xf2, "jp ", "p", NN)
|
||||
op(0xf3, "di ")
|
||||
op(0xf4, "call", "p", NN)
|
||||
op(0xf5, "push", AF)
|
||||
op(0xf6, "or ", A, N)
|
||||
op(0xf7, "rst ", "$0030")
|
||||
op(0xf8, "ret ", "m")
|
||||
op(0xf9, "ld ", SP, HL)
|
||||
op(0xfa, "jp ", "m", NN)
|
||||
op(0xfb, "ei ")
|
||||
op(0xfc, "call", "m", NN)
|
||||
op(0xfd, "iy: ")
|
||||
op(0xfe, "cp ", A, N)
|
||||
op(0xff, "rst ", "$0038")
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
#undef op
|
||||
|
||||
#undef N
|
||||
#undef IN
|
||||
#undef NN
|
||||
#undef INN
|
||||
#undef REL
|
||||
|
||||
#undef A
|
||||
#undef F
|
||||
#undef B
|
||||
#undef C
|
||||
#undef D
|
||||
#undef E
|
||||
#undef H
|
||||
#undef L
|
||||
#undef _H
|
||||
#undef _L
|
||||
#undef _HL
|
||||
|
||||
#undef AF
|
||||
#undef BC
|
||||
#undef DE
|
||||
#undef HL
|
||||
|
||||
#undef AF_
|
||||
#undef BC_
|
||||
#undef DE_
|
||||
#undef HL_
|
||||
|
||||
#undef SP
|
||||
#undef PC
|
||||
|
||||
#undef I
|
||||
#undef R
|
||||
|
||||
#undef IC
|
||||
#undef IBC
|
||||
#undef IDE
|
||||
#undef IHL
|
||||
#undef ISP
|
||||
@@ -0,0 +1,81 @@
|
||||
#include <ares/ares.hpp>
|
||||
#include "i8080.hpp"
|
||||
|
||||
namespace ares {
|
||||
|
||||
#include "disassembler.cpp"
|
||||
#include "registers.hpp"
|
||||
#include "memory.cpp"
|
||||
#include "instruction.cpp"
|
||||
#include "algorithms.cpp"
|
||||
#include "instructions.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto I8080::power() -> void {
|
||||
prefix = Prefix::hl;
|
||||
af.word = 0xffd7; af_.word = 0x0000;
|
||||
bc.word = 0x0000; bc_.word = 0x0000;
|
||||
de.word = 0x0000; de_.word = 0x0000;
|
||||
hl.word = 0x0000; hl_.word = 0x0000;
|
||||
ix.word = 0x0000;
|
||||
iy.word = 0x0000;
|
||||
ir.word = 0x0000;
|
||||
wz.word = 0x0000;
|
||||
SP = 0xffff;
|
||||
PC = 0x0000;
|
||||
EI = 0;
|
||||
P = 0;
|
||||
Q = 0;
|
||||
HALT = 0;
|
||||
INTE = 0;
|
||||
}
|
||||
|
||||
auto I8080::reset() -> void {
|
||||
prefix = Prefix::hl;
|
||||
ir.word = 0x0000;
|
||||
WZ = PC = 0x0000;
|
||||
EI = 0;
|
||||
HALT = 0;
|
||||
INTE = 0;
|
||||
}
|
||||
|
||||
auto I8080::irq(n8 extbus) -> bool {
|
||||
//do not execute maskable interrupts if disabled or immediately after EI instruction
|
||||
if(!INTE || EI) return false;
|
||||
|
||||
WZ = extbus;
|
||||
wait(6);
|
||||
instruction(WZ);
|
||||
|
||||
INTE = 0;
|
||||
HALT = 0;
|
||||
if(P) PF = 0;
|
||||
P = 0;
|
||||
Q = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto I8080::nmi() -> bool {
|
||||
push(PC);
|
||||
WZ = 0x66;
|
||||
wait(5);
|
||||
PC = WZ;
|
||||
|
||||
INTE = 0;
|
||||
HALT = 0;
|
||||
if(P) PF = 0;
|
||||
P = 0;
|
||||
Q = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto I8080::parity(n8 value) const -> bool {
|
||||
value ^= value >> 4;
|
||||
value ^= value >> 2;
|
||||
value ^= value >> 1;
|
||||
return !(value & 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
|
||||
//Intel 8080
|
||||
|
||||
namespace ares {
|
||||
|
||||
struct I8080 {
|
||||
struct Bus {
|
||||
virtual auto read(n16 address) -> n8 = 0;
|
||||
virtual auto write(n16 address, n8 data) -> void = 0;
|
||||
|
||||
virtual auto in(n16 address) -> n8 = 0;
|
||||
virtual auto out(n16 address, n8 data) -> void = 0;
|
||||
};
|
||||
|
||||
virtual auto step(u32 clocks) -> void = 0;
|
||||
virtual auto synchronizing() const -> bool = 0;
|
||||
|
||||
//i8080.cpp
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
auto irq(n8 extbus = 0xff) -> bool;
|
||||
auto nmi() -> bool;
|
||||
auto parity(n8) const -> bool;
|
||||
|
||||
//memory.cpp
|
||||
auto wait(u32 clocks = 1) -> void;
|
||||
auto opcode() -> n8;
|
||||
auto operand() -> n8;
|
||||
auto operands() -> n16;
|
||||
auto push(n16) -> void;
|
||||
auto pop() -> n16;
|
||||
auto read(n16 address) -> n8;
|
||||
auto write(n16 address, n8 data) -> void;
|
||||
auto in(n16 address) -> n8;
|
||||
auto out(n16 address, n8 data) -> void;
|
||||
|
||||
//instruction.cpp
|
||||
auto instruction() -> void;
|
||||
auto instruction(n8 code) -> void;
|
||||
|
||||
//algorithms.cpp
|
||||
auto ADD(n8, n8, bool = false) -> n8;
|
||||
auto AND(n8, n8) -> n8;
|
||||
auto CP (n8, n8) -> void;
|
||||
auto DEC(n8) -> n8;
|
||||
auto INC(n8) -> n8;
|
||||
auto OR (n8, n8) -> n8;
|
||||
auto SUB(n8, n8, bool = false) -> n8;
|
||||
auto XOR(n8, n8) -> n8;
|
||||
|
||||
//instructions.cpp
|
||||
auto instructionADC_a_irr(n16&) -> void;
|
||||
auto instructionADC_a_n() -> void;
|
||||
auto instructionADC_a_r(n8&) -> void;
|
||||
auto instructionADD_a_irr(n16&) -> void;
|
||||
auto instructionADD_a_n() -> void;
|
||||
auto instructionADD_a_r(n8&) -> void;
|
||||
auto instructionADD_hl_rr(n16&) -> void;
|
||||
auto instructionAND_a_irr(n16&) -> void;
|
||||
auto instructionAND_a_n() -> void;
|
||||
auto instructionAND_a_r(n8&) -> void;
|
||||
auto instructionCALL_c_nn(bool c) -> void;
|
||||
auto instructionCCF() -> void;
|
||||
auto instructionCP_a_irr(n16& x) -> void;
|
||||
auto instructionCP_a_n() -> void;
|
||||
auto instructionCP_a_r(n8& x) -> void;
|
||||
auto instructionCPL() -> void;
|
||||
auto instructionDAA() -> void;
|
||||
auto instructionDEC_irr(n16&) -> void;
|
||||
auto instructionDEC_r(n8&) -> void;
|
||||
auto instructionDEC_rr(n16&) -> void;
|
||||
auto instructionDI() -> void;
|
||||
auto instructionEI() -> void;
|
||||
auto instructionEX_irr_rr(n16&, n16&) -> void;
|
||||
auto instructionEX_rr_rr(n16&, n16&) -> void;
|
||||
auto instructionHALT() -> void;
|
||||
auto instructionIN_a_in() -> void;
|
||||
auto instructionINC_irr(n16&) -> void;
|
||||
auto instructionINC_r(n8&) -> void;
|
||||
auto instructionINC_rr(n16&) -> void;
|
||||
auto instructionJP_c_nn(bool) -> void;
|
||||
auto instructionJP_rr(n16&) -> void;
|
||||
auto instructionLD_a_inn() -> void;
|
||||
auto instructionLD_a_irr(n16& x) -> void;
|
||||
auto instructionLD_inn_a() -> void;
|
||||
auto instructionLD_inn_rr(n16&) -> void;
|
||||
auto instructionLD_irr_a(n16&) -> void;
|
||||
auto instructionLD_irr_n(n16&) -> void;
|
||||
auto instructionLD_irr_r(n16&, n8&) -> void;
|
||||
auto instructionLD_r_n(n8&) -> void;
|
||||
auto instructionLD_r_irr(n8&, n16&) -> void;
|
||||
auto instructionLD_r_r(n8&, n8&) -> void;
|
||||
auto instructionLD_rr_inn(n16&) -> void;
|
||||
auto instructionLD_rr_nn(n16&) -> void;
|
||||
auto instructionLD_sp_rr(n16&) -> void;
|
||||
auto instructionNOP() -> void;
|
||||
auto instructionOR_a_irr(n16&) -> void;
|
||||
auto instructionOR_a_n() -> void;
|
||||
auto instructionOR_a_r(n8&) -> void;
|
||||
auto instructionOUT_in_a() -> void;
|
||||
auto instructionPOP_rr(n16&) -> void;
|
||||
auto instructionPUSH_rr(n16&) -> void;
|
||||
auto instructionRET() -> void;
|
||||
auto instructionRET_c(bool c) -> void;
|
||||
auto instructionRLA() -> void;
|
||||
auto instructionRLCA() -> void;
|
||||
auto instructionRRA() -> void;
|
||||
auto instructionRRCA() -> void;
|
||||
auto instructionRST_o(n3) -> void;
|
||||
auto instructionSBC_a_irr(n16&) -> void;
|
||||
auto instructionSBC_a_n() -> void;
|
||||
auto instructionSBC_a_r(n8&) -> void;
|
||||
auto instructionSCF() -> void;
|
||||
auto instructionSUB_a_irr(n16&) -> void;
|
||||
auto instructionSUB_a_n() -> void;
|
||||
auto instructionSUB_a_r(n8&) -> void;
|
||||
auto instructionXOR_a_irr(n16&) -> void;
|
||||
auto instructionXOR_a_n() -> void;
|
||||
auto instructionXOR_a_r(n8&) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
|
||||
auto disassemble(n16 pc, n8 prefix, n8 code) -> string;
|
||||
|
||||
enum class Prefix : u32 { hl, ix, iy } prefix = Prefix::hl;
|
||||
|
||||
union Pair {
|
||||
Pair() : word(0) {}
|
||||
n16 word;
|
||||
struct Byte { n8 order_msb2(hi, lo); } byte;
|
||||
};
|
||||
|
||||
Pair af, af_;
|
||||
Pair bc, bc_;
|
||||
Pair de, de_;
|
||||
Pair hl, hl_;
|
||||
Pair ix;
|
||||
Pair iy;
|
||||
Pair ir;
|
||||
Pair wz;
|
||||
|
||||
n16 SP;
|
||||
n16 PC;
|
||||
b1 EI; //"ei" executed last
|
||||
b1 P; //"ld a,i" or "ld a,r" executed last
|
||||
b1 Q; //opcode that updated flag registers executed last
|
||||
b1 HALT; //"halt" instruction executed
|
||||
b1 INTE; //interrupt enable
|
||||
|
||||
Bus* bus = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
auto I8080::instruction() -> void {
|
||||
EI = 0;
|
||||
P = 0;
|
||||
|
||||
if(HALT) {
|
||||
return wait(1);
|
||||
}
|
||||
|
||||
n8 code;
|
||||
while(true) {
|
||||
code = opcode();
|
||||
if(code == 0xdd) { prefix = Prefix::ix; continue; }
|
||||
if(code == 0xfd) { prefix = Prefix::iy; continue; }
|
||||
break;
|
||||
}
|
||||
|
||||
instruction(code);
|
||||
|
||||
prefix = Prefix::hl;
|
||||
}
|
||||
|
||||
#define op(id, name, ...) case id: return instruction##name(__VA_ARGS__);
|
||||
|
||||
auto I8080::instruction(n8 code) -> void {
|
||||
switch(code) {
|
||||
op(0x00, NOP)
|
||||
op(0x01, LD_rr_nn, BC)
|
||||
op(0x02, LD_irr_a, BC)
|
||||
op(0x03, INC_rr, BC)
|
||||
op(0x04, INC_r, B)
|
||||
op(0x05, DEC_r, B)
|
||||
op(0x06, LD_r_n, B)
|
||||
op(0x07, RLCA)
|
||||
op(0x09, ADD_hl_rr, BC)
|
||||
op(0x0a, LD_a_irr, BC)
|
||||
op(0x0b, DEC_rr, BC)
|
||||
op(0x0c, INC_r, C)
|
||||
op(0x0d, DEC_r, C)
|
||||
op(0x0e, LD_r_n, C)
|
||||
op(0x0f, RRCA)
|
||||
op(0x11, LD_rr_nn, DE)
|
||||
op(0x12, LD_irr_a, DE)
|
||||
op(0x13, INC_rr, DE)
|
||||
op(0x14, INC_r, D)
|
||||
op(0x15, DEC_r, D)
|
||||
op(0x16, LD_r_n, D)
|
||||
op(0x17, RLA)
|
||||
op(0x19, ADD_hl_rr, DE)
|
||||
op(0x1a, LD_a_irr, DE)
|
||||
op(0x1b, DEC_rr, DE)
|
||||
op(0x1c, INC_r, E)
|
||||
op(0x1d, DEC_r, E)
|
||||
op(0x1e, LD_r_n, E)
|
||||
op(0x1f, RRA)
|
||||
op(0x21, LD_rr_nn, HL)
|
||||
op(0x22, LD_inn_rr, HL)
|
||||
op(0x23, INC_rr, HL)
|
||||
op(0x24, INC_r, H)
|
||||
op(0x25, DEC_r, H)
|
||||
op(0x26, LD_r_n, H)
|
||||
op(0x27, DAA)
|
||||
op(0x29, ADD_hl_rr, HL)
|
||||
op(0x2a, LD_rr_inn, HL)
|
||||
op(0x2b, DEC_rr, HL)
|
||||
op(0x2c, INC_r, L)
|
||||
op(0x2d, DEC_r, L)
|
||||
op(0x2e, LD_r_n, L)
|
||||
op(0x2f, CPL)
|
||||
op(0x31, LD_rr_nn, SP)
|
||||
op(0x32, LD_inn_a)
|
||||
op(0x33, INC_rr, SP)
|
||||
op(0x34, INC_irr, HL)
|
||||
op(0x35, DEC_irr, HL)
|
||||
op(0x36, LD_irr_n, HL)
|
||||
op(0x37, SCF)
|
||||
op(0x39, ADD_hl_rr, SP)
|
||||
op(0x3a, LD_a_inn)
|
||||
op(0x3b, DEC_rr, SP)
|
||||
op(0x3c, INC_r, A)
|
||||
op(0x3d, DEC_r, A)
|
||||
op(0x3e, LD_r_n, A)
|
||||
op(0x3f, CCF)
|
||||
op(0x40, LD_r_r, B, B)
|
||||
op(0x41, LD_r_r, B, C)
|
||||
op(0x42, LD_r_r, B, D)
|
||||
op(0x43, LD_r_r, B, E)
|
||||
op(0x44, LD_r_r, B, H)
|
||||
op(0x45, LD_r_r, B, L)
|
||||
op(0x46, LD_r_irr, B, HL)
|
||||
op(0x47, LD_r_r, B, A)
|
||||
op(0x48, LD_r_r, C, B)
|
||||
op(0x49, LD_r_r, C, C)
|
||||
op(0x4a, LD_r_r, C, D)
|
||||
op(0x4b, LD_r_r, C, E)
|
||||
op(0x4c, LD_r_r, C, H)
|
||||
op(0x4d, LD_r_r, C, L)
|
||||
op(0x4e, LD_r_irr, C, HL)
|
||||
op(0x4f, LD_r_r, C, A)
|
||||
op(0x50, LD_r_r, D, B)
|
||||
op(0x51, LD_r_r, D, C)
|
||||
op(0x52, LD_r_r, D, D)
|
||||
op(0x53, LD_r_r, D, E)
|
||||
op(0x54, LD_r_r, D, H)
|
||||
op(0x55, LD_r_r, D, L)
|
||||
op(0x56, LD_r_irr, D, HL)
|
||||
op(0x57, LD_r_r, D, A)
|
||||
op(0x58, LD_r_r, E, B)
|
||||
op(0x59, LD_r_r, E, C)
|
||||
op(0x5a, LD_r_r, E, D)
|
||||
op(0x5b, LD_r_r, E, E)
|
||||
op(0x5c, LD_r_r, E, H)
|
||||
op(0x5d, LD_r_r, E, L)
|
||||
op(0x5e, LD_r_irr, E, HL)
|
||||
op(0x5f, LD_r_r, E, A)
|
||||
op(0x60, LD_r_r, H, B)
|
||||
op(0x61, LD_r_r, H, C)
|
||||
op(0x62, LD_r_r, H, D)
|
||||
op(0x63, LD_r_r, H, E)
|
||||
op(0x64, LD_r_r, H, H)
|
||||
op(0x65, LD_r_r, H, L)
|
||||
op(0x66, LD_r_irr, H, HL)
|
||||
op(0x67, LD_r_r, H, A)
|
||||
op(0x68, LD_r_r, L, B)
|
||||
op(0x69, LD_r_r, L, C)
|
||||
op(0x6a, LD_r_r, L, D)
|
||||
op(0x6b, LD_r_r, L, E)
|
||||
op(0x6c, LD_r_r, L, H)
|
||||
op(0x6d, LD_r_r, L, L)
|
||||
op(0x6e, LD_r_irr, L, HL)
|
||||
op(0x6f, LD_r_r, L, A)
|
||||
op(0x70, LD_irr_r, HL, B)
|
||||
op(0x71, LD_irr_r, HL, C)
|
||||
op(0x72, LD_irr_r, HL, D)
|
||||
op(0x73, LD_irr_r, HL, E)
|
||||
op(0x74, LD_irr_r, HL, H)
|
||||
op(0x75, LD_irr_r, HL, L)
|
||||
op(0x76, HALT)
|
||||
op(0x77, LD_irr_r, HL, A)
|
||||
op(0x78, LD_r_r, A, B)
|
||||
op(0x79, LD_r_r, A, C)
|
||||
op(0x7a, LD_r_r, A, D)
|
||||
op(0x7b, LD_r_r, A, E)
|
||||
op(0x7c, LD_r_r, A, H)
|
||||
op(0x7d, LD_r_r, A, L)
|
||||
op(0x7e, LD_r_irr, A, HL)
|
||||
op(0x7f, LD_r_r, A, A)
|
||||
op(0x80, ADD_a_r, B)
|
||||
op(0x81, ADD_a_r, C)
|
||||
op(0x82, ADD_a_r, D)
|
||||
op(0x83, ADD_a_r, E)
|
||||
op(0x84, ADD_a_r, H)
|
||||
op(0x85, ADD_a_r, L)
|
||||
op(0x86, ADD_a_irr, HL)
|
||||
op(0x87, ADD_a_r, A)
|
||||
op(0x88, ADC_a_r, B)
|
||||
op(0x89, ADC_a_r, C)
|
||||
op(0x8a, ADC_a_r, D)
|
||||
op(0x8b, ADC_a_r, E)
|
||||
op(0x8c, ADC_a_r, H)
|
||||
op(0x8d, ADC_a_r, L)
|
||||
op(0x8e, ADC_a_irr, HL)
|
||||
op(0x8f, ADC_a_r, A)
|
||||
op(0x90, SUB_a_r, B)
|
||||
op(0x91, SUB_a_r, C)
|
||||
op(0x92, SUB_a_r, D)
|
||||
op(0x93, SUB_a_r, E)
|
||||
op(0x94, SUB_a_r, H)
|
||||
op(0x95, SUB_a_r, L)
|
||||
op(0x96, SUB_a_irr, HL)
|
||||
op(0x97, SUB_a_r, A)
|
||||
op(0x98, SBC_a_r, B)
|
||||
op(0x99, SBC_a_r, C)
|
||||
op(0x9a, SBC_a_r, D)
|
||||
op(0x9b, SBC_a_r, E)
|
||||
op(0x9c, SBC_a_r, H)
|
||||
op(0x9d, SBC_a_r, L)
|
||||
op(0x9e, SBC_a_irr, HL)
|
||||
op(0x9f, SBC_a_r, A)
|
||||
op(0xa0, AND_a_r, B)
|
||||
op(0xa1, AND_a_r, C)
|
||||
op(0xa2, AND_a_r, D)
|
||||
op(0xa3, AND_a_r, E)
|
||||
op(0xa4, AND_a_r, H)
|
||||
op(0xa5, AND_a_r, L)
|
||||
op(0xa6, AND_a_irr, HL)
|
||||
op(0xa7, AND_a_r, A)
|
||||
op(0xa8, XOR_a_r, B)
|
||||
op(0xa9, XOR_a_r, C)
|
||||
op(0xaa, XOR_a_r, D)
|
||||
op(0xab, XOR_a_r, E)
|
||||
op(0xac, XOR_a_r, H)
|
||||
op(0xad, XOR_a_r, L)
|
||||
op(0xae, XOR_a_irr, HL)
|
||||
op(0xaf, XOR_a_r, A)
|
||||
op(0xb0, OR_a_r, B)
|
||||
op(0xb1, OR_a_r, C)
|
||||
op(0xb2, OR_a_r, D)
|
||||
op(0xb3, OR_a_r, E)
|
||||
op(0xb4, OR_a_r, H)
|
||||
op(0xb5, OR_a_r, L)
|
||||
op(0xb6, OR_a_irr, HL)
|
||||
op(0xb7, OR_a_r, A)
|
||||
op(0xb8, CP_a_r, B)
|
||||
op(0xb9, CP_a_r, C)
|
||||
op(0xba, CP_a_r, D)
|
||||
op(0xbb, CP_a_r, E)
|
||||
op(0xbc, CP_a_r, H)
|
||||
op(0xbd, CP_a_r, L)
|
||||
op(0xbe, CP_a_irr, HL)
|
||||
op(0xbf, CP_a_r, A)
|
||||
op(0xc0, RET_c, ZF == 0)
|
||||
op(0xc1, POP_rr, BC)
|
||||
op(0xc2, JP_c_nn, ZF == 0)
|
||||
op(0xc3, JP_c_nn, 1)
|
||||
op(0xc4, CALL_c_nn, ZF == 0)
|
||||
op(0xc5, PUSH_rr, BC)
|
||||
op(0xc6, ADD_a_n)
|
||||
op(0xc7, RST_o, 0)
|
||||
op(0xc8, RET_c, ZF == 1)
|
||||
op(0xc9, RET)
|
||||
op(0xca, JP_c_nn, ZF == 1)
|
||||
op(0xcc, CALL_c_nn, ZF == 1)
|
||||
op(0xcd, CALL_c_nn, true)
|
||||
op(0xce, ADC_a_n)
|
||||
op(0xcf, RST_o, 1)
|
||||
op(0xd0, RET_c, CF == 0)
|
||||
op(0xd1, POP_rr, DE)
|
||||
op(0xd2, JP_c_nn, CF == 0)
|
||||
op(0xd3, OUT_in_a)
|
||||
op(0xd4, CALL_c_nn, CF == 0)
|
||||
op(0xd5, PUSH_rr, DE)
|
||||
op(0xd6, SUB_a_n)
|
||||
op(0xd7, RST_o, 2)
|
||||
op(0xd8, RET_c, CF == 1)
|
||||
op(0xda, JP_c_nn, CF == 1)
|
||||
op(0xdb, IN_a_in)
|
||||
op(0xdc, CALL_c_nn, CF == 1)
|
||||
op(0xde, SBC_a_n)
|
||||
op(0xdf, RST_o, 3)
|
||||
op(0xe0, RET_c, PF == 0)
|
||||
op(0xe1, POP_rr, HL)
|
||||
op(0xe2, JP_c_nn, PF == 0)
|
||||
op(0xe3, EX_irr_rr, SP, HL)
|
||||
op(0xe4, CALL_c_nn, PF == 0)
|
||||
op(0xe5, PUSH_rr, HL)
|
||||
op(0xe6, AND_a_n)
|
||||
op(0xe7, RST_o, 4)
|
||||
op(0xe8, RET_c, PF == 1)
|
||||
op(0xe9, JP_rr, HL)
|
||||
op(0xea, JP_c_nn, PF == 1)
|
||||
op(0xeb, EX_rr_rr, DE, HL)
|
||||
op(0xec, CALL_c_nn, PF == 1)
|
||||
op(0xee, XOR_a_n)
|
||||
op(0xef, RST_o, 5)
|
||||
op(0xf0, RET_c, SF == 0)
|
||||
op(0xf1, POP_rr, AF)
|
||||
op(0xf2, JP_c_nn, SF == 0)
|
||||
op(0xf3, DI)
|
||||
op(0xf4, CALL_c_nn, SF == 0)
|
||||
op(0xf5, PUSH_rr, AF)
|
||||
op(0xf6, OR_a_n)
|
||||
op(0xf7, RST_o, 6)
|
||||
op(0xf8, RET_c, SF == 1)
|
||||
op(0xf9, LD_sp_rr, HL)
|
||||
op(0xfa, JP_c_nn, SF == 1)
|
||||
op(0xfb, EI)
|
||||
op(0xfc, CALL_c_nn, SF == 1)
|
||||
op(0xfe, CP_a_n)
|
||||
op(0xff, RST_o, 7)
|
||||
}
|
||||
}
|
||||
|
||||
#undef op
|
||||
@@ -0,0 +1,364 @@
|
||||
//legend:
|
||||
// a = register A
|
||||
// c = condition
|
||||
// e = relative operand
|
||||
// in = (operand)
|
||||
// inn = (operand-word)
|
||||
// irr = (register-word)
|
||||
// o = opcode bits
|
||||
// n = operand
|
||||
// nn = operand-word
|
||||
// r = register
|
||||
|
||||
auto I8080::instructionADC_a_irr(n16& x) -> void { Q = 1;
|
||||
A = ADD(A, read(x), CF);
|
||||
}
|
||||
|
||||
auto I8080::instructionADC_a_n() -> void { Q = 1;
|
||||
A = ADD(A, operand(), CF);
|
||||
}
|
||||
|
||||
auto I8080::instructionADC_a_r(n8& x) -> void { Q = 1;
|
||||
A = ADD(A, x, CF);
|
||||
}
|
||||
|
||||
auto I8080::instructionADD_a_irr(n16& x) -> void { Q = 1;
|
||||
A = ADD(A, read(x));
|
||||
}
|
||||
|
||||
auto I8080::instructionADD_a_n() -> void { Q = 1;
|
||||
A = ADD(A, operand());
|
||||
}
|
||||
|
||||
auto I8080::instructionADD_a_r(n8& x) -> void { Q = 1;
|
||||
A = ADD(A, x);
|
||||
}
|
||||
|
||||
auto I8080::instructionADD_hl_rr(n16& x) -> void { Q = 1;
|
||||
WZ = HL + 1;
|
||||
bool pf = PF, zf = ZF, sf = SF, hf = HF;
|
||||
wait(3);
|
||||
auto lo = ADD(HL >> 0, x >> 0);
|
||||
wait(4);
|
||||
auto hi = ADD(HL >> 8, x >> 8, CF);
|
||||
HL = hi << 8 | lo << 0;
|
||||
PF = pf, ZF = zf, SF = sf, HF = hf; //restore unaffected flags
|
||||
}
|
||||
|
||||
auto I8080::instructionAND_a_irr(n16& x) -> void { Q = 1;
|
||||
A = AND(A, read(x));
|
||||
}
|
||||
|
||||
auto I8080::instructionAND_a_n() -> void { Q = 1;
|
||||
A = AND(A, operand());
|
||||
}
|
||||
|
||||
auto I8080::instructionAND_a_r(n8& x) -> void { Q = 1;
|
||||
A = AND(A, x);
|
||||
}
|
||||
|
||||
auto I8080::instructionCALL_c_nn(bool c) -> void { Q = 0;
|
||||
WZ = operands();
|
||||
if(!c) return;
|
||||
wait(1);
|
||||
push(PC);
|
||||
PC = WZ;
|
||||
}
|
||||
|
||||
auto I8080::instructionCCF() -> void {
|
||||
CF = !CF;
|
||||
Q = 1;
|
||||
}
|
||||
|
||||
auto I8080::instructionCP_a_irr(n16& x) -> void { Q = 1;
|
||||
CP(A, read(x));
|
||||
}
|
||||
|
||||
auto I8080::instructionCP_a_n() -> void { Q = 1;
|
||||
CP(A, operand());
|
||||
}
|
||||
|
||||
auto I8080::instructionCP_a_r(n8& x) -> void { Q = 1;
|
||||
CP(A, x);
|
||||
}
|
||||
|
||||
auto I8080::instructionCPL() -> void { Q = 1;
|
||||
A = ~A;
|
||||
}
|
||||
|
||||
auto I8080::instructionDAA() -> void { Q = 1;
|
||||
auto a = A;
|
||||
if(CF || (A.bit(0,7) > 0x99)) { A += 0x60; CF = 1; }
|
||||
if(HF || (A.bit(0,3) > 0x09)) { A += 0x06; }
|
||||
|
||||
PF = parity(A);
|
||||
HF = n8(A ^ a).bit(4);
|
||||
ZF = A == 0;
|
||||
SF = A.bit(7);
|
||||
}
|
||||
|
||||
auto I8080::instructionDEC_irr(n16& x) -> void { Q = 1;
|
||||
auto addr = x;
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, DEC(data));
|
||||
}
|
||||
|
||||
auto I8080::instructionDEC_r(n8& x) -> void { Q = 1;
|
||||
x = DEC(x);
|
||||
}
|
||||
|
||||
auto I8080::instructionDEC_rr(n16& x) -> void { Q = 0;
|
||||
wait(2);
|
||||
x--;
|
||||
}
|
||||
|
||||
auto I8080::instructionDI() -> void { Q = 0;
|
||||
INTE = 0;
|
||||
}
|
||||
|
||||
auto I8080::instructionEI() -> void { Q = 0;
|
||||
INTE = 1;
|
||||
EI = 1; //ignore maskable interrupts until after the next instruction
|
||||
}
|
||||
|
||||
auto I8080::instructionEX_irr_rr(n16& x, n16& y) -> void { Q = 0;
|
||||
WZL = read(x + 0);
|
||||
WZH = read(x + 1);
|
||||
wait(1);
|
||||
write(x + 0, y >> 0);
|
||||
write(x + 1, y >> 8);
|
||||
wait(2);
|
||||
y = WZ;
|
||||
}
|
||||
|
||||
auto I8080::instructionEX_rr_rr(n16& x, n16& y) -> void { Q = 0;
|
||||
swap(x, y);
|
||||
}
|
||||
|
||||
auto I8080::instructionHALT() -> void { Q = 0;
|
||||
HALT = 1;
|
||||
}
|
||||
|
||||
auto I8080::instructionIN_a_in() -> void { Q = 0;
|
||||
WZL = operand();
|
||||
WZH = A;
|
||||
A = in(WZ++);
|
||||
}
|
||||
|
||||
auto I8080::instructionINC_irr(n16& x) -> void { Q = 1;
|
||||
auto addr = x;
|
||||
auto data = read(addr);
|
||||
wait(1);
|
||||
write(addr, INC(data));
|
||||
}
|
||||
|
||||
auto I8080::instructionINC_r(n8& x) -> void { Q = 1;
|
||||
x = INC(x);
|
||||
}
|
||||
|
||||
auto I8080::instructionINC_rr(n16& x) -> void { Q = 0;
|
||||
wait(2);
|
||||
x++;
|
||||
}
|
||||
|
||||
auto I8080::instructionJP_c_nn(bool c) -> void { Q = 0;
|
||||
WZ = operands();
|
||||
if(c) PC = WZ;
|
||||
}
|
||||
|
||||
auto I8080::instructionJP_rr(n16& x) -> void { Q = 0;
|
||||
PC = x;
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_a_inn() -> void { Q = 0;
|
||||
WZ = operands();
|
||||
A = read(WZ++);
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_a_irr(n16& x) -> void { Q = 0;
|
||||
WZ = x;
|
||||
A = read(x);
|
||||
WZ++;
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_inn_a() -> void { Q = 0;
|
||||
WZ = operands();
|
||||
write(WZ++, A);
|
||||
WZH = A;
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_inn_rr(n16& x) -> void { Q = 0;
|
||||
WZ = operands();
|
||||
write(WZ + 0, x >> 0);
|
||||
write(WZ + 1, x >> 8);
|
||||
WZ++;
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_irr_a(n16& x) -> void { Q = 0;
|
||||
WZ = x;
|
||||
write(x, A);
|
||||
WZL++;
|
||||
WZH = A;
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_irr_n(n16& x) -> void { Q = 0;
|
||||
auto addr = x;
|
||||
auto data = operand();
|
||||
if(&x == &ix.word || &x == &iy.word) wait(2);
|
||||
write(addr, data);
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_irr_r(n16& x, n8& y) -> void { Q = 0;
|
||||
write(x, y);
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_r_n(n8& x) -> void { Q = 0;
|
||||
x = operand();
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_r_irr(n8& x, n16& y) -> void { Q = 0;
|
||||
x = read(y);
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_r_r(n8& x, n8& y) -> void { Q = 0;
|
||||
x = y;
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_rr_inn(n16& x) -> void { Q = 0;
|
||||
WZ = operands();
|
||||
x.byte(0) = read(WZ);
|
||||
WZ++;
|
||||
x.byte(1) = read(WZ);
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_rr_nn(n16& x) -> void { Q = 0;
|
||||
x = operands();
|
||||
}
|
||||
|
||||
auto I8080::instructionLD_sp_rr(n16& x) -> void { Q = 0;
|
||||
wait(2);
|
||||
SP = x;
|
||||
}
|
||||
|
||||
auto I8080::instructionNOP() -> void { Q = 0;
|
||||
}
|
||||
|
||||
auto I8080::instructionOR_a_irr(n16& x) -> void { Q = 1;
|
||||
A = OR(A, read(x));
|
||||
}
|
||||
|
||||
auto I8080::instructionOR_a_n() -> void { Q = 1;
|
||||
A = OR(A, operand());
|
||||
}
|
||||
|
||||
auto I8080::instructionOR_a_r(n8& x) -> void { Q = 1;
|
||||
A = OR(A, x);
|
||||
}
|
||||
|
||||
auto I8080::instructionOUT_in_a() -> void { Q = 0;
|
||||
WZL = operand();
|
||||
WZH = A;
|
||||
out(WZ, A);
|
||||
WZL++;
|
||||
}
|
||||
|
||||
//note: even though "pop af" affects flags, it does not set Q
|
||||
auto I8080::instructionPOP_rr(n16& x) -> void { Q = 0;
|
||||
x = pop();
|
||||
if(&x == &af.word) F = (F & 0xd7) | 2;
|
||||
}
|
||||
|
||||
auto I8080::instructionPUSH_rr(n16& x) -> void { Q = 0;
|
||||
wait(1);
|
||||
if(&x == &af.word) F = (F & 0xd7) | 2;
|
||||
push(x);
|
||||
}
|
||||
|
||||
auto I8080::instructionRET() -> void { Q = 0;
|
||||
WZ = pop();
|
||||
PC = WZ;
|
||||
}
|
||||
|
||||
auto I8080::instructionRET_c(bool c) -> void { Q = 0;
|
||||
wait(1);
|
||||
if(!c) return;
|
||||
WZ = pop();
|
||||
PC = WZ;
|
||||
}
|
||||
|
||||
auto I8080::instructionRLA() -> void { Q = 1;
|
||||
bool c = A.bit(7);
|
||||
A = A << 1 | CF;
|
||||
|
||||
CF = c;
|
||||
}
|
||||
|
||||
auto I8080::instructionRLCA() -> void { Q = 1;
|
||||
bool c = A.bit(7);
|
||||
A = A << 1 | c;
|
||||
|
||||
CF = c;
|
||||
}
|
||||
|
||||
auto I8080::instructionRRA() -> void { Q = 1;
|
||||
bool c = A.bit(0);
|
||||
A = CF << 7 | A >> 1;
|
||||
|
||||
CF = c;
|
||||
}
|
||||
|
||||
auto I8080::instructionRRCA() -> void { Q = 1;
|
||||
bool c = A.bit(0);
|
||||
A = c << 7 | A >> 1;
|
||||
|
||||
CF = c;
|
||||
}
|
||||
|
||||
auto I8080::instructionRST_o(n3 vector) -> void { Q = 0;
|
||||
wait(1);
|
||||
push(PC);
|
||||
WZ = vector << 3;
|
||||
PC = WZ;
|
||||
}
|
||||
|
||||
auto I8080::instructionSBC_a_irr(n16& x) -> void { Q = 1;
|
||||
A = SUB(A, read(x), CF);
|
||||
}
|
||||
|
||||
auto I8080::instructionSBC_a_n() -> void { Q = 1;
|
||||
A = SUB(A, operand(), CF);
|
||||
}
|
||||
|
||||
auto I8080::instructionSBC_a_r(n8& x) -> void { Q = 1;
|
||||
A = SUB(A, x, CF);
|
||||
}
|
||||
|
||||
auto I8080::instructionSCF() -> void {
|
||||
CF = 1;
|
||||
Q = 1;
|
||||
}
|
||||
|
||||
auto I8080::instructionSUB_a_irr(n16& x) -> void { Q = 1;
|
||||
A = SUB(A, read(x));
|
||||
}
|
||||
|
||||
auto I8080::instructionSUB_a_n() -> void { Q = 1;
|
||||
A = SUB(A, operand());
|
||||
}
|
||||
|
||||
auto I8080::instructionSUB_a_r(n8& x) -> void { Q = 1;
|
||||
A = SUB(A, x);
|
||||
}
|
||||
|
||||
auto I8080::instructionXOR_a_irr(n16& x) -> void { Q = 1;
|
||||
A = XOR(A, read(x));
|
||||
}
|
||||
|
||||
auto I8080::instructionXOR_a_n() -> void { Q = 1;
|
||||
A = XOR(A, operand());
|
||||
}
|
||||
|
||||
auto I8080::instructionXOR_a_r(n8& x) -> void { Q = 1;
|
||||
A = XOR(A, x);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
auto I8080::wait(u32 clocks) -> void {
|
||||
step(clocks);
|
||||
}
|
||||
|
||||
auto I8080::opcode() -> n8 {
|
||||
step(4);
|
||||
return bus->read(PC++);
|
||||
}
|
||||
|
||||
auto I8080::operand() -> n8 {
|
||||
step(3);
|
||||
return bus->read(PC++);
|
||||
}
|
||||
|
||||
auto I8080::operands() -> n16 {
|
||||
n16 data = operand() << 0;
|
||||
return data | operand() << 8;
|
||||
}
|
||||
|
||||
auto I8080::push(n16 x) -> void {
|
||||
write(--SP, x >> 8);
|
||||
write(--SP, x >> 0);
|
||||
}
|
||||
|
||||
auto I8080::pop() -> n16 {
|
||||
n16 data = read(SP++) << 0;
|
||||
return data | read(SP++) << 8;
|
||||
}
|
||||
|
||||
auto I8080::read(n16 address) -> n8 {
|
||||
step(3);
|
||||
return bus->read(address);
|
||||
}
|
||||
|
||||
auto I8080::write(n16 address, n8 data) -> void {
|
||||
step(3);
|
||||
return bus->write(address, data);
|
||||
}
|
||||
|
||||
auto I8080::in(n16 address) -> n8 {
|
||||
step(3);
|
||||
return bus->in(address);
|
||||
}
|
||||
|
||||
auto I8080::out(n16 address, n8 data) -> void {
|
||||
step(3);
|
||||
return bus->out(address, data);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#define AF af.word
|
||||
#define BC bc.word
|
||||
#define DE de.word
|
||||
#define HL hl.word
|
||||
#define WZ wz.word
|
||||
#define WZH wz.byte.hi
|
||||
#define WZL wz.byte.lo
|
||||
|
||||
#define A af.byte.hi
|
||||
#define F af.byte.lo
|
||||
#define B bc.byte.hi
|
||||
#define C bc.byte.lo
|
||||
#define D de.byte.hi
|
||||
#define E de.byte.lo
|
||||
#define H hl.byte.hi
|
||||
#define L hl.byte.lo
|
||||
|
||||
#define CF af.byte.lo.bit(0)
|
||||
#define PF af.byte.lo.bit(2)
|
||||
#define HF af.byte.lo.bit(4)
|
||||
#define ZF af.byte.lo.bit(6)
|
||||
#define SF af.byte.lo.bit(7)
|
||||
@@ -0,0 +1,22 @@
|
||||
auto I8080::serialize(serializer& s) -> void {
|
||||
s((u32&)prefix);
|
||||
s(af.word);
|
||||
s(bc.word);
|
||||
s(de.word);
|
||||
s(hl.word);
|
||||
s(af_.word);
|
||||
s(bc_.word);
|
||||
s(de_.word);
|
||||
s(hl_.word);
|
||||
s(ix.word);
|
||||
s(iy.word);
|
||||
s(ir.word);
|
||||
s(wz.word);
|
||||
s(SP);
|
||||
s(PC);
|
||||
s(EI);
|
||||
s(P);
|
||||
s(Q);
|
||||
s(HALT);
|
||||
s(INTE);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -66,7 +66,7 @@ auto M68000::exception(u32 exception, u32 vector, u32 priority) -> void {
|
||||
if(exception == Exception::Interrupt) {
|
||||
// IACK vector number acquisition (+4 cyc normal or +10 to +18 cyc autovectored)
|
||||
// & justify vector number (+4 cyc)
|
||||
idle(16+4); // assuming autovector cycles (approximated for Megadrive)
|
||||
idle(14+4); // assuming autovector cycles (approximated for Megadrive)
|
||||
r.i = priority;
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,12 @@ auto MOS6502::algorithmDEC(n8 i) -> n8 {
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmDCP(n8 i) -> n8 {
|
||||
auto value = algorithmDEC(i);
|
||||
A = algorithmCMP(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmEOR(n8 i) -> n8 {
|
||||
n8 o = A ^ i;
|
||||
Z = o == 0;
|
||||
@@ -137,6 +143,12 @@ auto MOS6502::algorithmINC(n8 i) -> n8 {
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmISC(n8 i) -> n8 {
|
||||
auto value = algorithmINC(i);
|
||||
A = algorithmSBC(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmLD(n8 i) -> n8 {
|
||||
Z = i == 0;
|
||||
N = i.bit(7);
|
||||
@@ -151,6 +163,12 @@ auto MOS6502::algorithmLSR(n8 i) -> n8 {
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSRE(n8 i) -> n8 {
|
||||
auto value = algorithmLSR(i);
|
||||
A = algorithmEOR(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmORA(n8 i) -> n8 {
|
||||
n8 o = A | i;
|
||||
Z = o == 0;
|
||||
@@ -167,6 +185,12 @@ auto MOS6502::algorithmROL(n8 i) -> n8 {
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmRLA(n8 i) -> n8 {
|
||||
auto value = algorithmROL(i);
|
||||
A = algorithmAND(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmROR(n8 i) -> n8 {
|
||||
bool c = C;
|
||||
C = i.bit(0);
|
||||
@@ -176,6 +200,12 @@ auto MOS6502::algorithmROR(n8 i) -> n8 {
|
||||
return i;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmRRA(n8 i) -> n8 {
|
||||
auto value = algorithmROR(i);
|
||||
A = algorithmADC(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSBC(n8 i) -> n8 {
|
||||
i = ~i;
|
||||
i16 o = A + i + C;
|
||||
@@ -200,13 +230,7 @@ auto MOS6502::algorithmSBC(n8 i) -> n8 {
|
||||
}
|
||||
|
||||
auto MOS6502::algorithmSLO(n8 i) -> n8 {
|
||||
C = i.bit(7);
|
||||
i <<= 1;
|
||||
Z = i == 0;
|
||||
N = i.bit(7);
|
||||
|
||||
A |= i;
|
||||
Z = A == 0;
|
||||
N = A.bit(7);
|
||||
return i;
|
||||
auto value = algorithmASL(i);
|
||||
A = algorithmORA(value);
|
||||
return value;
|
||||
}
|
||||
@@ -51,6 +51,7 @@ auto MOS6502::instruction() -> void {
|
||||
op(0x24, ZeroPageRead, fp(BIT), A)
|
||||
op(0x25, ZeroPageRead, fp(AND), A)
|
||||
op(0x26, ZeroPageModify, fp(ROL))
|
||||
op(0x27, ZeroPageModify, fp(RLA))
|
||||
op(0x28, PullP)
|
||||
op(0x29, Immediate, fp(AND), A)
|
||||
op(0x2a, Implied, fp(ROL), A)
|
||||
@@ -58,22 +59,28 @@ auto MOS6502::instruction() -> void {
|
||||
op(0x2c, AbsoluteRead, fp(BIT), A)
|
||||
op(0x2d, AbsoluteRead, fp(AND), A)
|
||||
op(0x2e, AbsoluteModify, fp(ROL))
|
||||
op(0x2f, AbsoluteModify, fp(RLA))
|
||||
op(0x30, Branch, N == 1)
|
||||
op(0x31, IndirectYRead, fp(AND), A)
|
||||
op(0x34, NoOperationZeroPage, X)
|
||||
op(0x35, ZeroPageRead, fp(AND), A, X)
|
||||
op(0x36, ZeroPageModify, fp(ROL), X)
|
||||
op(0x37, ZeroPageModify, fp(RLA), X)
|
||||
op(0x38, Set, C)
|
||||
op(0x39, AbsoluteRead, fp(AND), A, Y)
|
||||
op(0x3a, NoOperation)
|
||||
op(0x3b, AbsoluteModify, fp(RLA), Y)
|
||||
op(0x3c, NoOperationAbsolute, X)
|
||||
op(0x3d, AbsoluteRead, fp(AND), A, X)
|
||||
op(0x3e, AbsoluteModify, fp(ROL), X)
|
||||
op(0x3f, AbsoluteModify, fp(RLA), X)
|
||||
op(0x40, ReturnInterrupt)
|
||||
op(0x41, IndirectXRead, fp(EOR), A)
|
||||
op(0x43, IndirectXRead, fp(SRE), X)
|
||||
op(0x44, NoOperationZeroPage)
|
||||
op(0x45, ZeroPageRead, fp(EOR), A)
|
||||
op(0x46, ZeroPageModify, fp(LSR))
|
||||
op(0x47, ZeroPageModify, fp(SRE))
|
||||
op(0x48, Push, A)
|
||||
op(0x49, Immediate, fp(EOR), A)
|
||||
op(0x4a, Implied, fp(LSR), A)
|
||||
@@ -81,22 +88,27 @@ auto MOS6502::instruction() -> void {
|
||||
op(0x4c, JumpAbsolute)
|
||||
op(0x4d, AbsoluteRead, fp(EOR), A)
|
||||
op(0x4e, AbsoluteModify, fp(LSR))
|
||||
op(0x4f, AbsoluteModify, fp(SRE))
|
||||
op(0x50, Branch, V == 0)
|
||||
op(0x51, IndirectYRead, fp(EOR), A)
|
||||
op(0x54, NoOperationZeroPage, X)
|
||||
op(0x55, ZeroPageRead, fp(EOR), A, X)
|
||||
op(0x56, ZeroPageModify, fp(LSR), X)
|
||||
op(0x57, ZeroPageModify, fp(SRE), X)
|
||||
op(0x58, Clear, I)
|
||||
op(0x59, AbsoluteRead, fp(EOR), A, Y)
|
||||
op(0x5a, NoOperation)
|
||||
op(0x5b, AbsoluteModify, fp(SRE), Y)
|
||||
op(0x5c, NoOperationAbsolute, X)
|
||||
op(0x5d, AbsoluteRead, fp(EOR), A, X)
|
||||
op(0x5e, AbsoluteModify, fp(LSR), X)
|
||||
op(0x5f, AbsoluteModify, fp(SRE), X)
|
||||
op(0x60, ReturnSubroutine)
|
||||
op(0x61, IndirectXRead, fp(ADC), A)
|
||||
op(0x64, NoOperationZeroPage)
|
||||
op(0x65, ZeroPageRead, fp(ADC), A)
|
||||
op(0x66, ZeroPageModify, fp(ROR))
|
||||
op(0x67, ZeroPageModify, fp(RRA))
|
||||
op(0x68, Pull, A)
|
||||
op(0x69, Immediate, fp(ADC), A)
|
||||
op(0x6a, Implied, fp(ROR), A)
|
||||
@@ -104,17 +116,21 @@ auto MOS6502::instruction() -> void {
|
||||
op(0x6c, JumpIndirect)
|
||||
op(0x6d, AbsoluteRead, fp(ADC), A)
|
||||
op(0x6e, AbsoluteModify, fp(ROR))
|
||||
op(0x6f, AbsoluteModify, fp(RRA))
|
||||
op(0x70, Branch, V == 1)
|
||||
op(0x71, IndirectYRead, fp(ADC), A)
|
||||
op(0x74, NoOperationZeroPage, X)
|
||||
op(0x75, ZeroPageRead, fp(ADC), A, X)
|
||||
op(0x76, ZeroPageModify, fp(ROR), X)
|
||||
op(0x77, ZeroPageModify, fp(RRA), X)
|
||||
op(0x78, Set, I)
|
||||
op(0x79, AbsoluteRead, fp(ADC), A, Y)
|
||||
op(0x7a, NoOperation)
|
||||
op(0x7b, AbsoluteModify, fp(RRA), Y)
|
||||
op(0x7c, NoOperationAbsolute, X)
|
||||
op(0x7d, AbsoluteRead, fp(ADC), A, X)
|
||||
op(0x7e, AbsoluteModify, fp(ROR), X)
|
||||
op(0x7f, AbsoluteModify, fp(RRA), X)
|
||||
op(0x80, NoOperationImmediate)
|
||||
op(0x81, IndirectXWrite, A)
|
||||
op(0x82, NoOperationImmediate)
|
||||
@@ -167,6 +183,7 @@ auto MOS6502::instruction() -> void {
|
||||
op(0xc4, ZeroPageRead, fp(CPY), Y)
|
||||
op(0xc5, ZeroPageRead, fp(CMP), A)
|
||||
op(0xc6, ZeroPageModify, fp(DEC))
|
||||
op(0xc7, ZeroPageModify, fp(DCP))
|
||||
op(0xc8, Implied, fp(INC), Y)
|
||||
op(0xc9, Immediate, fp(CMP), A)
|
||||
op(0xca, Implied, fp(DEC), X)
|
||||
@@ -174,23 +191,28 @@ auto MOS6502::instruction() -> void {
|
||||
op(0xcc, AbsoluteRead, fp(CPY), Y)
|
||||
op(0xcd, AbsoluteRead, fp(CMP), A)
|
||||
op(0xce, AbsoluteModify, fp(DEC))
|
||||
op(0xcf, AbsoluteModify, fp(DCP))
|
||||
op(0xd0, Branch, Z == 0)
|
||||
op(0xd1, IndirectYRead, fp(CMP), A)
|
||||
op(0xd4, NoOperationZeroPage, X)
|
||||
op(0xd5, ZeroPageRead, fp(CMP), A, X)
|
||||
op(0xd6, ZeroPageModify, fp(DEC), X)
|
||||
op(0xd7, ZeroPageModify, fp(DCP), X)
|
||||
op(0xd8, Clear, D)
|
||||
op(0xd9, AbsoluteRead, fp(CMP), A, Y)
|
||||
op(0xda, NoOperation)
|
||||
op(0xdb, AbsoluteModify, fp(DCP), Y)
|
||||
op(0xdc, NoOperationAbsolute, X)
|
||||
op(0xdd, AbsoluteRead, fp(CMP), A, X)
|
||||
op(0xde, AbsoluteModify, fp(DEC), X)
|
||||
op(0xdf, AbsoluteModify, fp(DCP), X)
|
||||
op(0xe0, Immediate, fp(CPX), X)
|
||||
op(0xe1, IndirectXRead, fp(SBC), A)
|
||||
op(0xe2, NoOperationImmediate)
|
||||
op(0xe4, ZeroPageRead, fp(CPX), X)
|
||||
op(0xe5, ZeroPageRead, fp(SBC), A)
|
||||
op(0xe6, ZeroPageModify, fp(INC))
|
||||
op(0xe7, ZeroPageModify, fp(ISC))
|
||||
op(0xe8, Implied, fp(INC), X)
|
||||
op(0xe9, Immediate, fp(SBC), A)
|
||||
op(0xea, NoOperation)
|
||||
@@ -198,17 +220,21 @@ auto MOS6502::instruction() -> void {
|
||||
op(0xec, AbsoluteRead, fp(CPX), X)
|
||||
op(0xed, AbsoluteRead, fp(SBC), A)
|
||||
op(0xee, AbsoluteModify, fp(INC))
|
||||
op(0xef, AbsoluteModify, fp(ISC))
|
||||
op(0xf0, Branch, Z == 1)
|
||||
op(0xf1, IndirectYRead, fp(SBC), A)
|
||||
op(0xf4, NoOperationZeroPage, X)
|
||||
op(0xf5, ZeroPageRead, fp(SBC), A, X)
|
||||
op(0xf6, ZeroPageModify, fp(INC), X)
|
||||
op(0xf7, ZeroPageModify, fp(ISC), X)
|
||||
op(0xf8, Set, D)
|
||||
op(0xf9, AbsoluteRead, fp(SBC), A, Y)
|
||||
op(0xfa, NoOperation)
|
||||
op(0xfb, AbsoluteModify, fp(ISC), Y)
|
||||
op(0xfc, NoOperationAbsolute, X)
|
||||
op(0xfd, AbsoluteRead, fp(SBC), A, X)
|
||||
op(0xfe, AbsoluteModify, fp(INC), X)
|
||||
op(0xff, AbsoluteModify, fp(ISC), X)
|
||||
}
|
||||
|
||||
//unimplemented instruction
|
||||
|
||||
@@ -32,11 +32,17 @@ namespace ares {
|
||||
#undef N
|
||||
#undef L
|
||||
|
||||
auto MOS6502::power() -> void {
|
||||
auto MOS6502::power(bool reset) -> void {
|
||||
if(reset) {
|
||||
S-=3;
|
||||
P.i = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
A = 0x00;
|
||||
X = 0x00;
|
||||
Y = 0x00;
|
||||
S = 0xff;
|
||||
S = 0xfd;
|
||||
P = 0x04;
|
||||
MDR = 0x00;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ struct MOS6502 {
|
||||
virtual auto readDebugger(n16 addr) -> n8 { return 0; }
|
||||
|
||||
//mos6502.cpp
|
||||
auto power() -> void;
|
||||
auto power(bool reset = false) -> void;
|
||||
|
||||
//memory.cpp
|
||||
auto idle() -> void;
|
||||
@@ -42,13 +42,18 @@ struct MOS6502 {
|
||||
auto algorithmCPX(n8) -> n8;
|
||||
auto algorithmCPY(n8) -> n8;
|
||||
auto algorithmDEC(n8) -> n8;
|
||||
auto algorithmDCP(n8) -> n8;
|
||||
auto algorithmEOR(n8) -> n8;
|
||||
auto algorithmINC(n8) -> n8;
|
||||
auto algorithmISC(n8) -> n8;
|
||||
auto algorithmLD (n8) -> n8;
|
||||
auto algorithmLSR(n8) -> n8;
|
||||
auto algorithmSRE(n8) -> n8;
|
||||
auto algorithmORA(n8) -> n8;
|
||||
auto algorithmROL(n8) -> n8;
|
||||
auto algorithmRLA(n8) -> n8;
|
||||
auto algorithmROR(n8) -> n8;
|
||||
auto algorithmRRA(n8) -> n8;
|
||||
auto algorithmSBC(n8) -> n8;
|
||||
auto algorithmSLO(n8) -> n8;
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ struct Accuracy {
|
||||
//enable all accuracy flags
|
||||
static constexpr bool Reference = 0;
|
||||
|
||||
static constexpr bool Interpreter = 0 | Reference;
|
||||
static constexpr bool Interpreter = 0 | Reference | !recompiler::generic::supported;
|
||||
static constexpr bool CachedInterpreter = 0 & !Interpreter;
|
||||
static constexpr bool Recompiler = !Interpreter;
|
||||
|
||||
//exceptions when the CPU or DMA accesses unaligned memory addresses
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
auto SH2::Recompiler::invalidate(u32 address) -> void {
|
||||
auto pool = pools[address >> 8 & 0xffffff];
|
||||
if(!pool) return;
|
||||
memory::jitprotect(false);
|
||||
pool->blocks[address >> 1 & 0x7f] = nullptr;
|
||||
memory::jitprotect(true);
|
||||
}
|
||||
|
||||
auto SH2::Recompiler::pool(u32 address) -> Pool* {
|
||||
auto& pool = pools[address >> 8 & 0xffffff];
|
||||
if(!pool) pool = (Pool*)allocator.acquire(sizeof(Pool));
|
||||
return pool;
|
||||
}
|
||||
|
||||
auto SH2::Recompiler::block(u32 address) -> Block* {
|
||||
if(auto block = pool(address)->blocks[address >> 1 & 0x7f]) return block;
|
||||
auto block = emit(address);
|
||||
pool(address)->blocks[address >> 1 & 0x7f] = block;
|
||||
memory::jitprotect(true);
|
||||
return block;
|
||||
}
|
||||
|
||||
alwaysinline auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
#define op(id, name, ...) \
|
||||
case id: \
|
||||
call(&SH2::name, &self, ##__VA_ARGS__); \
|
||||
return 0;
|
||||
#define br(id, name, ...) \
|
||||
case id: \
|
||||
call(&SH2::name, &self, ##__VA_ARGS__); \
|
||||
return 1;
|
||||
#include "decoder.hpp"
|
||||
#undef op
|
||||
#undef br
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CCR mem(sreg(1), offsetof(Registers, CCR))
|
||||
|
||||
auto SH2::Recompiler::emit(u32 address) -> Block* {
|
||||
if(unlikely(allocator.available() < 1_MiB)) {
|
||||
print("SH2 allocator flush\n");
|
||||
memory::jitprotect(false);
|
||||
allocator.release(bump_allocator::zero_fill);
|
||||
memory::jitprotect(true);
|
||||
reset();
|
||||
}
|
||||
|
||||
auto block = (Block*)allocator.acquire(sizeof(Block));
|
||||
beginFunction(2);
|
||||
|
||||
bool hasBranched = 0;
|
||||
while(true) {
|
||||
u16 instruction = self.readWord(address);
|
||||
bool branched = emitInstruction(instruction);
|
||||
add64(CCR, CCR, imm(1));
|
||||
call(&SH2::instructionEpilogue);
|
||||
address += 2;
|
||||
if(hasBranched || (address & 0xfe) == 0) break; //block boundary
|
||||
hasBranched = branched;
|
||||
testJumpEpilog();
|
||||
}
|
||||
jumpEpilog();
|
||||
|
||||
memory::jitprotect(false);
|
||||
block->code = endFunction();
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
#undef CCR
|
||||
@@ -6,68 +6,68 @@
|
||||
#define d8 (opcode >> 0 & 0x0ff)
|
||||
#define d12 (opcode >> 0 & 0xfff)
|
||||
switch(opcode >> 8 & 0x00f0 | opcode & 0x000f) {
|
||||
op(0x04, MOVBS0, m, n); //MOV.B Rm,@(R0,Rn)
|
||||
op(0x05, MOVWS0, m, n); //MOV.W Rm,@(R0,Rn)
|
||||
op(0x06, MOVLS0, m, n); //MOV.L Rm,@(R0,Rn)
|
||||
op(0x07, MULL, m, n); //MUL.L Rm,Rn
|
||||
op(0x0c, MOVBL0, m, n); //MOV.B @(R0,Rm),Rn
|
||||
op(0x0d, MOVWL0, m, n); //MOV.W @(R0,Rm),Rn
|
||||
op(0x0e, MOVLL0, m, n); //MOV.L @(R0,Rm),Rn
|
||||
op(0x0f, MACL_, m, n); //MAC.L @Rm+,@Rn+
|
||||
op(0x10 ... 0x1f, MOVLS4, m, d4, n); //MOV.L Rm,@(disp,Rn)
|
||||
op(0x20, MOVBS, m, n); //MOV.B Rm,@Rn
|
||||
op(0x21, MOVWS, m, n); //MOV.W Rm,@Rn
|
||||
op(0x22, MOVLS, m, n); //MOV.L Rm,@Rn
|
||||
op(0x24, MOVBM, m, n); //MOV.B Rm,@-Rn
|
||||
op(0x25, MOVWM, m, n); //MOV.W Rm,@-Rn
|
||||
op(0x26, MOVLM, m, n); //MOV.L Rm,@-Rn
|
||||
op(0x27, DIV0S, m, n); //DIV0S Rm,Rn
|
||||
op(0x28, TST, m, n); //TST Rm,Rn
|
||||
op(0x29, AND, m, n); //AND Rm,Rn
|
||||
op(0x2a, XOR, m, n); //XOR Rm,Rn
|
||||
op(0x2b, OR, m, n); //OR Rm,Rn
|
||||
op(0x2c, CMPSTR, m, n); //CMP/STR Rm,Rn
|
||||
op(0x2d, XTRCT, m, n); //XTRCT Rm,Rn
|
||||
op(0x2e, MULU, m, n); //MULU Rm,Rn
|
||||
op(0x2f, MULS, m, n); //MULS Rm,Rn
|
||||
op(0x30, CMPEQ, m, n); //CMP/EQ Rm,Rn
|
||||
op(0x32, CMPHS, m, n); //CMP/HS Rm,Rn
|
||||
op(0x33, CMPGE, m, n); //CMP/GE Rm,Rn
|
||||
op(0x34, DIV1, m, n); //DIV1 Rm,Rn
|
||||
op(0x35, DMULU, m, n); //DMULU.L Rm,Rn
|
||||
op(0x36, CMPHI, m, n); //CMP/HI Rm,Rn
|
||||
op(0x37, CMPGT, m, n); //CMP/GT Rm,Rn
|
||||
op(0x38, SUB, m, n); //SUB Rm,Rn
|
||||
op(0x3a, SUBC, m, n); //SUBC Rm,Rn
|
||||
op(0x3b, SUBV, m, n); //SUBV Rm,Rn
|
||||
op(0x3c, ADD, m, n); //ADD Rm,Rn
|
||||
op(0x3d, DMULS, m, n); //DMULS.L Rm,Rn
|
||||
op(0x3e, ADDC, m, n); //ADDC Rm,Rn
|
||||
op(0x3f, ADDV, m, n); //ADDV Rm,Rn
|
||||
op(0x4f, MACW, m, n); //MAC.W @Rm+,@Rn+
|
||||
op(0x50 ... 0x5f, MOVLL4, m, d4, n); //MOV.L @(disp,Rm),Rn
|
||||
op(0x60, MOVBL, m, n); //MOV.B @Rm,Rn
|
||||
op(0x61, MOVWL, m, n); //MOV.W @Rm,Rn
|
||||
op(0x62, MOVLL, m, n); //MOV.L @Rm,Rn
|
||||
op(0x63, MOV, m, n); //MOV Rm,Rn
|
||||
op(0x64, MOVBP, m, n); //MOV.B @Rm+,Rn
|
||||
op(0x65, MOVWP, m, n); //MOV.W @Rm+,Rn
|
||||
op(0x66, MOVLP, m, n); //MOV.L @Rm+,Rn
|
||||
op(0x67, NOT, m, n); //NOT Rm,Rn
|
||||
op(0x68, SWAPB, m, n); //SWAP.B Rm,Rn
|
||||
op(0x69, SWAPW, m, n); //SWAP.W Rm,Rn
|
||||
op(0x6a, NEGC, m, n); //NEGC Rm,Rn
|
||||
op(0x6b, NEG, m, n); //NEG Rm,Rn
|
||||
op(0x6c, EXTUB, m, n); //EXTU.B Rm,Rn
|
||||
op(0x6d, EXTUW, m, n); //EXTU.W Rm,Rn
|
||||
op(0x6e, EXTSB, m, n); //EXTS.B Rm,Rn
|
||||
op(0x6f, EXTSW, m, n); //EXTS.W Rm,Rn
|
||||
op(0x70 ... 0x7f, ADDI, i, n); //ADD #imm,Rn
|
||||
op(0x90 ... 0x9f, MOVWI, d8, n); //MOV.W @(disp,PC),Rn
|
||||
br(0xa0 ... 0xaf, BRA, d12); //BRA disp
|
||||
br(0xb0 ... 0xbf, BSR, d12); //BSR disp
|
||||
op(0xd0 ... 0xdf, MOVLI, d8, n); //MOV.L @(disp.PC),Rn
|
||||
op(0xe0 ... 0xef, MOVI, i, n); //MOV #imm,Rn
|
||||
op(0x04, MOVBS0, m, n); //MOV.B Rm,@(R0,Rn)
|
||||
op(0x05, MOVWS0, m, n); //MOV.W Rm,@(R0,Rn)
|
||||
op(0x06, MOVLS0, m, n); //MOV.L Rm,@(R0,Rn)
|
||||
op(0x07, MULL, m, n); //MUL.L Rm,Rn
|
||||
op(0x0c, MOVBL0, m, n); //MOV.B @(R0,Rm),Rn
|
||||
op(0x0d, MOVWL0, m, n); //MOV.W @(R0,Rm),Rn
|
||||
op(0x0e, MOVLL0, m, n); //MOV.L @(R0,Rm),Rn
|
||||
op(0x0f, MACL_, m, n); //MAC.L @Rm+,@Rn+
|
||||
op(range16(0x10, 0x1f), MOVLS4, m, d4, n); //MOV.L Rm,@(disp,Rn)
|
||||
op(0x20, MOVBS, m, n); //MOV.B Rm,@Rn
|
||||
op(0x21, MOVWS, m, n); //MOV.W Rm,@Rn
|
||||
op(0x22, MOVLS, m, n); //MOV.L Rm,@Rn
|
||||
op(0x24, MOVBM, m, n); //MOV.B Rm,@-Rn
|
||||
op(0x25, MOVWM, m, n); //MOV.W Rm,@-Rn
|
||||
op(0x26, MOVLM, m, n); //MOV.L Rm,@-Rn
|
||||
op(0x27, DIV0S, m, n); //DIV0S Rm,Rn
|
||||
op(0x28, TST, m, n); //TST Rm,Rn
|
||||
op(0x29, AND, m, n); //AND Rm,Rn
|
||||
op(0x2a, XOR, m, n); //XOR Rm,Rn
|
||||
op(0x2b, OR, m, n); //OR Rm,Rn
|
||||
op(0x2c, CMPSTR, m, n); //CMP/STR Rm,Rn
|
||||
op(0x2d, XTRCT, m, n); //XTRCT Rm,Rn
|
||||
op(0x2e, MULU, m, n); //MULU Rm,Rn
|
||||
op(0x2f, MULS, m, n); //MULS Rm,Rn
|
||||
op(0x30, CMPEQ, m, n); //CMP/EQ Rm,Rn
|
||||
op(0x32, CMPHS, m, n); //CMP/HS Rm,Rn
|
||||
op(0x33, CMPGE, m, n); //CMP/GE Rm,Rn
|
||||
op(0x34, DIV1, m, n); //DIV1 Rm,Rn
|
||||
op(0x35, DMULU, m, n); //DMULU.L Rm,Rn
|
||||
op(0x36, CMPHI, m, n); //CMP/HI Rm,Rn
|
||||
op(0x37, CMPGT, m, n); //CMP/GT Rm,Rn
|
||||
op(0x38, SUB, m, n); //SUB Rm,Rn
|
||||
op(0x3a, SUBC, m, n); //SUBC Rm,Rn
|
||||
op(0x3b, SUBV, m, n); //SUBV Rm,Rn
|
||||
op(0x3c, ADD, m, n); //ADD Rm,Rn
|
||||
op(0x3d, DMULS, m, n); //DMULS.L Rm,Rn
|
||||
op(0x3e, ADDC, m, n); //ADDC Rm,Rn
|
||||
op(0x3f, ADDV, m, n); //ADDV Rm,Rn
|
||||
op(0x4f, MACW, m, n); //MAC.W @Rm+,@Rn+
|
||||
op(range16(0x50, 0x5f), MOVLL4, m, d4, n); //MOV.L @(disp,Rm),Rn
|
||||
op(0x60, MOVBL, m, n); //MOV.B @Rm,Rn
|
||||
op(0x61, MOVWL, m, n); //MOV.W @Rm,Rn
|
||||
op(0x62, MOVLL, m, n); //MOV.L @Rm,Rn
|
||||
op(0x63, MOV, m, n); //MOV Rm,Rn
|
||||
op(0x64, MOVBP, m, n); //MOV.B @Rm+,Rn
|
||||
op(0x65, MOVWP, m, n); //MOV.W @Rm+,Rn
|
||||
op(0x66, MOVLP, m, n); //MOV.L @Rm+,Rn
|
||||
op(0x67, NOT, m, n); //NOT Rm,Rn
|
||||
op(0x68, SWAPB, m, n); //SWAP.B Rm,Rn
|
||||
op(0x69, SWAPW, m, n); //SWAP.W Rm,Rn
|
||||
op(0x6a, NEGC, m, n); //NEGC Rm,Rn
|
||||
op(0x6b, NEG, m, n); //NEG Rm,Rn
|
||||
op(0x6c, EXTUB, m, n); //EXTU.B Rm,Rn
|
||||
op(0x6d, EXTUW, m, n); //EXTU.W Rm,Rn
|
||||
op(0x6e, EXTSB, m, n); //EXTS.B Rm,Rn
|
||||
op(0x6f, EXTSW, m, n); //EXTS.W Rm,Rn
|
||||
op(range16(0x70, 0x7f), ADDI, i, n); //ADD #imm,Rn
|
||||
op(range16(0x90, 0x9f), MOVWI, d8, n); //MOV.W @(disp,PC),Rn
|
||||
br(range16(0xa0, 0xaf), BRA, d12); //BRA disp
|
||||
br(range16(0xb0, 0xbf), BSR, d12); //BSR disp
|
||||
op(range16(0xd0, 0xdf), MOVLI, d8, n); //MOV.L @(disp.PC),Rn
|
||||
op(range16(0xe0, 0xef), MOVI, i, n); //MOV #imm,Rn
|
||||
}
|
||||
#undef n
|
||||
#undef m
|
||||
@@ -94,7 +94,7 @@
|
||||
op(0xc0, MOVBSG, d8); //MOV.B R0,@(disp,GBR)
|
||||
op(0xc1, MOVWSG, d8); //MOV.W R0,@(disp,GBR)
|
||||
op(0xc2, MOVLSG, d8); //MOV.L R0,@(disp,GBR)
|
||||
op(0xc3, TRAPA, i); //TRAPA #imm
|
||||
br(0xc3, TRAPA, i); //TRAPA #imm
|
||||
op(0xc4, MOVBLG, d8); //MOV.B @(disp,GBR),R0
|
||||
op(0xc5, MOVWLG, d8); //MOV.W @(disp,GBR),R0
|
||||
op(0xc6, MOVLLG, d8); //MOV.L @(disp,GBR),R0
|
||||
@@ -174,7 +174,7 @@
|
||||
br(0x000b, RTS); //RTS
|
||||
op(0x0018, SETT); //SETT
|
||||
op(0x0019, DIV0U); //DIV0U
|
||||
op(0x001b, SLEEP); //SLEEP
|
||||
br(0x001b, SLEEP); //SLEEP
|
||||
op(0x0028, CLRMAC); //CLRMAC
|
||||
br(0x002b, RTE); //RTE
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
template<typename... P>
|
||||
auto SH2::hint(P&&... p) const -> string {
|
||||
if(1) return {};
|
||||
if(1) return {"\e[0m\e[37m", std::forward<P>(p)..., "\e[0m"};
|
||||
if(1) return {terminal::csi, "0m", terminal::csi, "37m", std::forward<P>(p)..., terminal::csi, "0m"};
|
||||
return {std::forward<P>(p)...};
|
||||
}
|
||||
|
||||
auto SH2::disassembleInstruction() -> string {
|
||||
auto SH2::disassembleInstruction(u16 opcode) -> string {
|
||||
vector<string> s;
|
||||
|
||||
auto registerName = [&](u32 r) -> string {
|
||||
@@ -48,8 +48,6 @@ auto SH2::disassembleInstruction() -> string {
|
||||
return {"0x", hex(PC + (i12)d * 2, 8L)};
|
||||
};
|
||||
|
||||
u16 opcode = readWord(PC - 4);
|
||||
|
||||
#define n (opcode >> 8 & 0x00f)
|
||||
#define m (opcode >> 4 & 0x00f)
|
||||
#define i (opcode >> 0 & 0x0ff)
|
||||
@@ -81,7 +79,7 @@ auto SH2::disassembleInstruction() -> string {
|
||||
case 0x0f: //MAC.L @Rm+,@Rn+
|
||||
s = {"mac.l", postincrementIndirectRegister(m), postincrementIndirectRegister(n)};
|
||||
break;
|
||||
case 0x10 ... 0x1f: //MOV.L Rm,@(disp,Rn)
|
||||
case range16(0x10, 0x1f): //MOV.L Rm,@(disp,Rn)
|
||||
s = {"mov.l", registerValue(m), indirectRegisterDisplacement(n, d4, 4)};
|
||||
break;
|
||||
case 0x20: //MOV.B Rm,@Rn
|
||||
@@ -174,7 +172,7 @@ auto SH2::disassembleInstruction() -> string {
|
||||
case 0x4f: //MAC.W @Rm+,@Rn+
|
||||
s = {"mac.w", postincrementIndirectRegister(m), postincrementIndirectRegister(n)};
|
||||
break;
|
||||
case 0x50 ... 0x5f: //MOV.L @(disp,Rm),Rn
|
||||
case range16(0x50, 0x5f): //MOV.L @(disp,Rm),Rn
|
||||
s = {"mov.l", indirectRegisterDisplacement(m, d4, 4), registerName(n)};
|
||||
break;
|
||||
case 0x60: //MOV.B @Rm,Rn
|
||||
@@ -225,22 +223,22 @@ auto SH2::disassembleInstruction() -> string {
|
||||
case 0x6f: //EXTS.W Rm,Rn
|
||||
s = {"exts.w", registerValue(m), registerName(n)};
|
||||
break;
|
||||
case 0x70 ... 0x7f: //ADD #imm,Rn
|
||||
case range16(0x70, 0x7f): //ADD #imm,Rn
|
||||
s = {"add", immediate(i), registerValue(n)};
|
||||
break;
|
||||
case 0x90 ... 0x9f: //MOV.W @(disp,PC),Rn
|
||||
case range16(0x90, 0x9f): //MOV.W @(disp,PC),Rn
|
||||
s = {"mov.w", pcRelativeDisplacement(d8, 2), registerName(n)};
|
||||
break;
|
||||
case 0xa0 ... 0xaf: //BRA disp
|
||||
case range16(0xa0, 0xaf): //BRA disp
|
||||
s = {"bra", branch12(d12)};
|
||||
break;
|
||||
case 0xb0 ... 0xbf: //BSR disp
|
||||
case range16(0xb0, 0xbf): //BSR disp
|
||||
s = {"bsr", branch12(d12)};
|
||||
break;
|
||||
case 0xd0 ... 0xdf: //MOV.L @(disp,PC),Rn
|
||||
case range16(0xd0, 0xdf): //MOV.L @(disp,PC),Rn
|
||||
s = {"mov.l", pcRelativeDisplacement(d8, 4), registerName(n)};
|
||||
break;
|
||||
case 0xe0 ... 0xef: //MOV #imm,Rn
|
||||
case range16(0xe0, 0xef): //MOV #imm,Rn
|
||||
s = {"mov", immediate(i), registerName(n)};
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
auto SH2::exceptionHandler() -> void {
|
||||
if(!exceptions) return;
|
||||
if(inDelaySlot()) return;
|
||||
if(inDelaySlot()) { cyclesUntilRecompilerExit = 0; return; }
|
||||
|
||||
if(exceptions & ResetCold) {
|
||||
exceptions &= ~ResetCold;
|
||||
|
||||
@@ -4,13 +4,11 @@ auto SH2::jump(u32 pc) -> void {
|
||||
}
|
||||
|
||||
auto SH2::branch(u32 pc) -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
PPC = pc;
|
||||
PPM = Branch::Take;
|
||||
}
|
||||
|
||||
auto SH2::delaySlot(u32 pc) -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
PPC = pc;
|
||||
PPM = Branch::Slot;
|
||||
}
|
||||
@@ -25,6 +23,7 @@ auto SH2::instruction() -> void {
|
||||
}
|
||||
ID = 0;
|
||||
u16 opcode = readWord(PC - 4);
|
||||
instructionPrologue(opcode);
|
||||
execute(opcode);
|
||||
instructionEpilogue();
|
||||
}
|
||||
@@ -37,7 +36,10 @@ auto SH2::instruction() -> void {
|
||||
do {
|
||||
auto block = recompiler.block(PC - 4);
|
||||
block->execute(*this);
|
||||
} while (CCR < cyclesUntilSync);
|
||||
} while (CCR < cyclesUntilRecompilerExit);
|
||||
|
||||
// Reset the count as it may have been set to 0 for an early exit
|
||||
cyclesUntilRecompilerExit = recompilerStepCycles;
|
||||
|
||||
step(CCR);
|
||||
CCR = 0;
|
||||
|
||||
@@ -43,6 +43,7 @@ auto SH2::ANDM(u32 i) -> void {
|
||||
|
||||
//BF disp
|
||||
auto SH2::BF(u32 d) -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
if(SR.T == 0) {
|
||||
branch(PC + 4 + (s8)d * 2);
|
||||
}
|
||||
@@ -50,6 +51,7 @@ auto SH2::BF(u32 d) -> void {
|
||||
|
||||
//BF/S disp
|
||||
auto SH2::BFS(u32 d) -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
if(SR.T == 0) {
|
||||
delaySlot(PC + 4 + (s8)d * 2);
|
||||
}
|
||||
@@ -57,28 +59,33 @@ auto SH2::BFS(u32 d) -> void {
|
||||
|
||||
//BRA disp
|
||||
auto SH2::BRA(u32 d) -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
delaySlot(PC + 4 + (i12)d * 2);
|
||||
}
|
||||
|
||||
//BRAF disp
|
||||
auto SH2::BRAF(u32 m) -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
delaySlot(PC + 4 + R[m]);
|
||||
}
|
||||
|
||||
//BSR disp
|
||||
auto SH2::BSR(u32 d) -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
PR = PC;
|
||||
delaySlot(PC + 4 + (i12)d * 2);
|
||||
}
|
||||
|
||||
//BSRF Rm
|
||||
auto SH2::BSRF(u32 m) -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
PR = PC;
|
||||
delaySlot(PC + 4 + R[m]);
|
||||
}
|
||||
|
||||
//BT disp
|
||||
auto SH2::BT(u32 d) -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
if(SR.T == 1) {
|
||||
branch(PC + 4 + (s8)d * 2);
|
||||
}
|
||||
@@ -86,6 +93,7 @@ auto SH2::BT(u32 d) -> void {
|
||||
|
||||
//BT/S disp
|
||||
auto SH2::BTS(u32 d) -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
if(SR.T == 1) {
|
||||
delaySlot(PC + 4 + (s8)d * 2);
|
||||
}
|
||||
@@ -221,11 +229,13 @@ auto SH2::ILLEGAL() -> void {
|
||||
|
||||
//JMP @Rm
|
||||
auto SH2::JMP(u32 m) -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
delaySlot(R[m] + 4);
|
||||
}
|
||||
|
||||
//JSR @Rm
|
||||
auto SH2::JSR(u32 m) -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
PR = PC;
|
||||
delaySlot(R[m] + 4);
|
||||
}
|
||||
@@ -605,6 +615,7 @@ auto SH2::ROTR(u32 n) -> void {
|
||||
|
||||
//RTE
|
||||
auto SH2::RTE() -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
delaySlot(readLong(SP + 0) + 4);
|
||||
SR = readLong(SP + 4);
|
||||
SP += 8;
|
||||
@@ -612,6 +623,7 @@ auto SH2::RTE() -> void {
|
||||
|
||||
//RTS
|
||||
auto SH2::RTS() -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
delaySlot(PR + 4);
|
||||
}
|
||||
|
||||
@@ -810,6 +822,7 @@ auto SH2::TAS(u32 n) -> void {
|
||||
|
||||
//TRAPA #imm
|
||||
auto SH2::TRAPA(u32 i) -> void {
|
||||
if(inDelaySlot()) return illegalSlotInstruction();
|
||||
push(SR);
|
||||
push(PC - 2);
|
||||
branch(readLong(VBR + i * 4) + 4);
|
||||
|
||||
@@ -20,56 +20,134 @@
|
||||
#define ET Reg(ET)
|
||||
#define ID Reg(ID)
|
||||
|
||||
auto SH2::Recompiler::invalidate(u32 address) -> void {
|
||||
auto SH2::Recompiler::mask(u8 address, u8 size) -> u64 {
|
||||
//1 bit per 4 bytes
|
||||
u6 s = address >> 2;
|
||||
u6 e = address + size - 1 >> 2;
|
||||
u64 smask = ~0ull << s;
|
||||
u64 emask = ~0ull >> 63 - e;
|
||||
assert(s <= e);
|
||||
return smask & emask;
|
||||
}
|
||||
|
||||
auto SH2::Recompiler::invalidate(u32 address, u8 size) -> void {
|
||||
auto pool = pools[address >> 8 & 0xffffff];
|
||||
if(!pool) return;
|
||||
memory::jitprotect(false);
|
||||
pool->blocks[address >> 1 & 0x7f] = nullptr;
|
||||
pool->dirty |= mask(address, size);
|
||||
memory::jitprotect(true);
|
||||
}
|
||||
|
||||
auto SH2::Recompiler::pool(u32 address) -> Pool* {
|
||||
auto& pool = pools[address >> 8 & 0xffffff];
|
||||
if(!pool) pool = (Pool*)allocator.acquire(sizeof(Pool));
|
||||
if(!pool) {
|
||||
pool = (Pool*)allocator.acquire(sizeof(Pool));
|
||||
memory::jitprotect(false);
|
||||
*pool = {};
|
||||
pool->generation = generation;
|
||||
memory::jitprotect(true);
|
||||
} else if(address >> 29 == Area::Cached && pool->generation != generation) {
|
||||
memory::jitprotect(false);
|
||||
for(auto& block : pool->blocks) block = nullptr;
|
||||
pool->generation = generation;
|
||||
pool->dirty = 0;
|
||||
memory::jitprotect(true);
|
||||
} else if(pool->dirty) {
|
||||
memory::jitprotect(false);
|
||||
u8 address = 0;
|
||||
for(auto& block : pool->blocks) {
|
||||
if(block && (pool->dirty & mask(address, block->size)) != 0) {
|
||||
block = nullptr;
|
||||
}
|
||||
address += 2;
|
||||
}
|
||||
pool->dirty = 0;
|
||||
memory::jitprotect(true);
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
auto SH2::Recompiler::block(u32 address) -> Block* {
|
||||
if(auto block = pool(address)->blocks[address >> 1 & 0x7f]) return block;
|
||||
|
||||
auto size = measure(address);
|
||||
auto hashcode = hash(address, size);
|
||||
|
||||
BlockHashPair pair;
|
||||
pair.hashcode = hashcode;
|
||||
if(auto result = blocks.find(pair)) {
|
||||
memory::jitprotect(false);
|
||||
pool(address)->blocks[address >> 1 & 0x7f] = result->block;
|
||||
memory::jitprotect(true);
|
||||
return result->block;
|
||||
}
|
||||
|
||||
auto block = emit(address);
|
||||
assert(block->size == size);
|
||||
pool(address)->blocks[address >> 1 & 0x7f] = block;
|
||||
memory::jitprotect(true);
|
||||
|
||||
pair.block = block;
|
||||
blocks.insert(pair);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
auto SH2::Recompiler::measure(u32 address) -> u8 {
|
||||
u32 start = address;
|
||||
u32 index = address >> 1 & 0x7f;
|
||||
bool hasBranched = 0;
|
||||
while(true) {
|
||||
u16 instruction = self.readWord(address);
|
||||
instructions[index++] = instruction;
|
||||
bool branched = isTerminal(instruction);
|
||||
address += 2;
|
||||
if(hasBranched || (address & 0xfe) == 0) break; //block boundary
|
||||
hasBranched = branched;
|
||||
}
|
||||
|
||||
return address - start;
|
||||
}
|
||||
|
||||
auto SH2::Recompiler::hash(u32 address, u8 size) -> u64 {
|
||||
return XXH3_64bits(&instructions[address >> 1 & 0x7f], size ? size : 0x100);
|
||||
}
|
||||
|
||||
auto SH2::Recompiler::emit(u32 address) -> Block* {
|
||||
if(unlikely(allocator.available() < 1_MiB)) {
|
||||
print("SH2 allocator flush\n");
|
||||
memory::jitprotect(false);
|
||||
allocator.release(bump_allocator::zero_fill);
|
||||
memory::jitprotect(true);
|
||||
allocator.release();
|
||||
reset();
|
||||
}
|
||||
|
||||
auto block = (Block*)allocator.acquire(sizeof(Block));
|
||||
beginFunction(2);
|
||||
|
||||
u32 start = address;
|
||||
u32 index = address >> 1 & 0x7f;
|
||||
bool hasBranched = 0;
|
||||
inDelaySlot = 1; //force runtime check on first instruction
|
||||
while(true) {
|
||||
u16 instruction = self.readWord(address);
|
||||
bool branched = emitInstruction(instruction);
|
||||
u16 instruction = instructions[index++];
|
||||
if(callInstructionPrologue) {
|
||||
mov32(reg(1), imm(instruction));
|
||||
call(&SH2::instructionPrologue);
|
||||
}
|
||||
auto branch = emitInstruction(instruction);
|
||||
inDelaySlot = branch == Branch::Slot;
|
||||
add64(CCR, CCR, imm(1));
|
||||
//add64(CCR, CCR, imm(2)); //underclocking hack
|
||||
call(&SH2::instructionEpilogue);
|
||||
address += 2;
|
||||
if(hasBranched || (address & 0xfe) == 0) break; //block boundary
|
||||
hasBranched = branched;
|
||||
hasBranched = branch != Branch::Step;
|
||||
testJumpEpilog();
|
||||
}
|
||||
jumpEpilog();
|
||||
|
||||
memory::jitprotect(false);
|
||||
block->code = endFunction();
|
||||
block->size = address - start;
|
||||
|
||||
return block;
|
||||
}
|
||||
@@ -84,7 +162,9 @@ auto SH2::Recompiler::emit(u32 address) -> Block* {
|
||||
#define writeB &SH2::writeByte<>
|
||||
#define illegal &SH2::illegalInstruction
|
||||
|
||||
auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
auto SH2::Recompiler::emitInstruction(u16 opcode) -> u32 {
|
||||
if constexpr(!Accuracy::CachedInterpreter) {
|
||||
|
||||
#define n (opcode >> 8 & 0x00f)
|
||||
#define m (opcode >> 4 & 0x00f)
|
||||
#define i (opcode >> 0 & 0x0ff)
|
||||
@@ -173,7 +253,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
}
|
||||
|
||||
//MOV.L Rm,@(disp,Rn)
|
||||
case 0x10 ... 0x1f: {
|
||||
case range16(0x10, 0x1f): {
|
||||
add32(reg(1), Rn, imm(d4*4));
|
||||
mov32(reg(2), Rm);
|
||||
call(writeL);
|
||||
@@ -452,7 +532,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
}
|
||||
|
||||
//MOV.L @(disp,Rm),Rn
|
||||
case 0x50 ... 0x5f: {
|
||||
case range16(0x50, 0x5f): {
|
||||
add32(reg(1), Rm, imm(d4*4));
|
||||
call(readL);
|
||||
mov32(Rn, reg(0));
|
||||
@@ -528,7 +608,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
|
||||
//NOT Rm,Rn
|
||||
case 0x67: {
|
||||
not32(Rn, Rm);
|
||||
xor32(Rn, Rm, imm(-1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -603,13 +683,13 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
}
|
||||
|
||||
//ADD #imm,Rn
|
||||
case 0x70 ... 0x7f: {
|
||||
case range16(0x70, 0x7f): {
|
||||
add32(Rn, Rn, imm((s8)i));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//MOV.W @(disp,PC),Rn
|
||||
case 0x90 ... 0x9f: {
|
||||
case range16(0x90, 0x9f): {
|
||||
auto delay = cmp32_jump(PPM, imm(0), flag_ne);
|
||||
mov32(reg(0), PC);
|
||||
auto tail = jump();
|
||||
@@ -624,24 +704,28 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
}
|
||||
|
||||
//BRA disp
|
||||
case 0xa0 ... 0xaf: {
|
||||
add32(PPC, PC, imm(4 + (i12)d12 * 2));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
return 1;
|
||||
case range16(0xa0, 0xaf): {
|
||||
checkDelaySlot([=] {
|
||||
add32(PPC, PC, imm(4 + (i12)d12 * 2));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
});
|
||||
return Branch::Slot;
|
||||
}
|
||||
|
||||
//BSR disp
|
||||
case 0xb0 ... 0xbf: {
|
||||
mov32(reg(0), PC);
|
||||
mov32(PR, reg(0));
|
||||
add32(reg(0), reg(0), imm(4 + (i12)d12 * 2));
|
||||
mov32(PPC, reg(0));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
return 1;
|
||||
case range16(0xb0, 0xbf): {
|
||||
checkDelaySlot([=] {
|
||||
mov32(reg(0), PC);
|
||||
mov32(PR, reg(0));
|
||||
add32(reg(0), reg(0), imm(4 + (i12)d12 * 2));
|
||||
mov32(PPC, reg(0));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
});
|
||||
return Branch::Slot;
|
||||
}
|
||||
|
||||
//MOV.L @(disp,PC),Rn
|
||||
case 0xd0 ... 0xdf: {
|
||||
case range16(0xd0, 0xdf): {
|
||||
auto delay = cmp32_jump(PPM, imm(0), flag_ne);
|
||||
mov32(reg(0), PC);
|
||||
auto tail = jump();
|
||||
@@ -656,7 +740,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
}
|
||||
|
||||
//MOV #imm,Rn
|
||||
case 0xe0 ... 0xef: {
|
||||
case range16(0xe0, 0xef): {
|
||||
mov32(Rn, imm((s8)i));
|
||||
return 0;
|
||||
}
|
||||
@@ -723,38 +807,46 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
|
||||
//BT disp
|
||||
case 0x89: {
|
||||
auto skip = cmp32_jump(T, imm(0), flag_eq);
|
||||
add32(PPC, PC, imm(4 + (s8)d8 * 2));
|
||||
mov32(PPM, imm(Branch::Take));
|
||||
setLabel(skip);
|
||||
return 1;
|
||||
checkDelaySlot([=] {
|
||||
auto skip = cmp32_jump(T, imm(0), flag_eq);
|
||||
add32(PPC, PC, imm(4 + (s8)d8 * 2));
|
||||
mov32(PPM, imm(Branch::Take));
|
||||
setLabel(skip);
|
||||
});
|
||||
return Branch::Take;
|
||||
}
|
||||
|
||||
//BF disp
|
||||
case 0x8b: {
|
||||
auto skip = cmp32_jump(T, imm(0), flag_ne);
|
||||
add32(PPC, PC, imm(4 + (s8)d8 * 2));
|
||||
mov32(PPM, imm(Branch::Take));
|
||||
setLabel(skip);
|
||||
return 1;
|
||||
checkDelaySlot([=] {
|
||||
auto skip = cmp32_jump(T, imm(0), flag_ne);
|
||||
add32(PPC, PC, imm(4 + (s8)d8 * 2));
|
||||
mov32(PPM, imm(Branch::Take));
|
||||
setLabel(skip);
|
||||
});
|
||||
return Branch::Take;
|
||||
}
|
||||
|
||||
//BT/S disp
|
||||
case 0x8d: {
|
||||
auto skip = cmp32_jump(T, imm(0), flag_eq);
|
||||
add32(PPC, PC, imm(4 + (s8)d8 * 2));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
setLabel(skip);
|
||||
return 1;
|
||||
checkDelaySlot([=] {
|
||||
auto skip = cmp32_jump(T, imm(0), flag_eq);
|
||||
add32(PPC, PC, imm(4 + (s8)d8 * 2));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
setLabel(skip);
|
||||
});
|
||||
return Branch::Slot;
|
||||
}
|
||||
|
||||
//BF/S disp
|
||||
case 0x8f: {
|
||||
auto skip = cmp32_jump(T, imm(0), flag_ne);
|
||||
add32(PPC, PC, imm(4 + (s8)d8 * 2));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
setLabel(skip);
|
||||
return 1;
|
||||
checkDelaySlot([=] {
|
||||
auto skip = cmp32_jump(T, imm(0), flag_ne);
|
||||
add32(PPC, PC, imm(4 + (s8)d8 * 2));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
setLabel(skip);
|
||||
});
|
||||
return Branch::Slot;
|
||||
}
|
||||
|
||||
//MOV.B R0,@(disp,GBR)
|
||||
@@ -783,20 +875,22 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
|
||||
//TRAPA #imm
|
||||
case 0xc3: {
|
||||
getSR(reg(2));
|
||||
sub32(reg(1), R15, imm(4));
|
||||
mov32(R15, reg(1));
|
||||
call(writeL);
|
||||
sub32(reg(2), PC, imm(2));
|
||||
sub32(reg(1), R15, imm(4));
|
||||
mov32(R15, reg(1));
|
||||
call(writeL);
|
||||
add32(reg(1), VBR, imm(i * 4));
|
||||
call(readL);
|
||||
add32(reg(0), reg(0), imm(4));
|
||||
mov32(PPC, reg(0));
|
||||
mov32(PPM, imm(Branch::Take));
|
||||
return 1;
|
||||
checkDelaySlot([=] {
|
||||
getSR(reg(2));
|
||||
sub32(reg(1), R15, imm(4));
|
||||
mov32(R15, reg(1));
|
||||
call(writeL);
|
||||
sub32(reg(2), PC, imm(2));
|
||||
sub32(reg(1), R15, imm(4));
|
||||
mov32(R15, reg(1));
|
||||
call(writeL);
|
||||
add32(reg(1), VBR, imm(i * 4));
|
||||
call(readL);
|
||||
add32(reg(0), reg(0), imm(4));
|
||||
mov32(PPC, reg(0));
|
||||
mov32(PPM, imm(Branch::Take));
|
||||
});
|
||||
return Branch::Take;
|
||||
}
|
||||
|
||||
//MOV.B @(disp,GBR),R0
|
||||
@@ -928,13 +1022,15 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
|
||||
//BSRF Rm
|
||||
case 0x003: {
|
||||
mov32(reg(0), PC);
|
||||
mov32(PR, reg(0));
|
||||
add32(reg(0), reg(0), Rm);
|
||||
add32(reg(0), reg(0), imm(4));
|
||||
mov32(PPC, reg(0));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
return 1;
|
||||
checkDelaySlot([=] {
|
||||
mov32(reg(0), PC);
|
||||
mov32(PR, reg(0));
|
||||
add32(reg(0), reg(0), Rm);
|
||||
add32(reg(0), reg(0), imm(4));
|
||||
mov32(PPC, reg(0));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
});
|
||||
return Branch::Slot;
|
||||
}
|
||||
|
||||
//STS MACH,Rn
|
||||
@@ -963,11 +1059,13 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
|
||||
//BRAF Rm
|
||||
case 0x023: {
|
||||
add32(reg(0), PC, Rm);
|
||||
add32(reg(0), reg(0), imm(4));
|
||||
mov32(PPC, reg(0));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
return 1;
|
||||
checkDelaySlot([=] {
|
||||
add32(reg(0), PC, Rm);
|
||||
add32(reg(0), reg(0), imm(4));
|
||||
mov32(PPC, reg(0));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
});
|
||||
return Branch::Slot;
|
||||
}
|
||||
|
||||
//MOVT Rn
|
||||
@@ -1070,10 +1168,12 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
|
||||
//JSR @Rm
|
||||
case 0x40b: {
|
||||
mov32(PR, PC);
|
||||
add32(PPC, Rm, imm(4));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
return 1;
|
||||
checkDelaySlot([=] {
|
||||
mov32(PR, PC);
|
||||
add32(PPC, Rm, imm(4));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
});
|
||||
return Branch::Slot;
|
||||
}
|
||||
|
||||
//LDC Rm,SR
|
||||
@@ -1270,9 +1370,11 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
|
||||
//JMP @Rm
|
||||
case 0x42b: {
|
||||
add32(PPC, Rm, imm(4));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
return 1;
|
||||
checkDelaySlot([=] {
|
||||
add32(PPC, Rm, imm(4));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
});
|
||||
return Branch::Slot;
|
||||
}
|
||||
|
||||
//LDC Rm,VBR
|
||||
@@ -1303,9 +1405,11 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
|
||||
//RTS
|
||||
case 0x000b: {
|
||||
add32(PPC, PR, imm(4));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
return 1;
|
||||
checkDelaySlot([=] {
|
||||
add32(PPC, PR, imm(4));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
});
|
||||
return Branch::Slot;
|
||||
}
|
||||
|
||||
//SETT
|
||||
@@ -1331,7 +1435,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
setLabel(skip);
|
||||
mov32(ET, imm(0));
|
||||
setLabel(skip2);
|
||||
return 1;
|
||||
return Branch::Take;
|
||||
}
|
||||
|
||||
//CLRMAC
|
||||
@@ -1343,17 +1447,19 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
|
||||
//RTE
|
||||
case 0x002b: {
|
||||
mov32(reg(1), R15);
|
||||
add32(R15, reg(1), imm(4));
|
||||
call(readL);
|
||||
add32(reg(0), reg(0), imm(4));
|
||||
mov32(PPC, reg(0));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
mov32(reg(1), R15);
|
||||
add32(R15, reg(1), imm(4));
|
||||
call(readL);
|
||||
setSR(reg(0));
|
||||
return 1;
|
||||
checkDelaySlot([=] {
|
||||
mov32(reg(1), R15);
|
||||
add32(R15, reg(1), imm(4));
|
||||
call(readL);
|
||||
add32(reg(0), reg(0), imm(4));
|
||||
mov32(PPC, reg(0));
|
||||
mov32(PPM, imm(Branch::Slot));
|
||||
mov32(reg(1), R15);
|
||||
add32(R15, reg(1), imm(4));
|
||||
call(readL);
|
||||
setSR(reg(0));
|
||||
});
|
||||
return Branch::Slot;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1363,12 +1469,13 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
//ILLEGAL
|
||||
case 0: {
|
||||
call(illegal);
|
||||
return 1;
|
||||
return Branch::Take;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
} else { //Accuracy::CachedInterpreter
|
||||
|
||||
#define op(id, name, ...) \
|
||||
case id: \
|
||||
call(&SH2::name, &self, ##__VA_ARGS__); \
|
||||
@@ -1376,11 +1483,12 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
|
||||
#define br(id, name, ...) \
|
||||
case id: \
|
||||
call(&SH2::name, &self, ##__VA_ARGS__); \
|
||||
return 1;
|
||||
return Branch::Take;
|
||||
#include "decoder.hpp"
|
||||
#undef op
|
||||
#undef br
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1409,6 +1517,30 @@ auto SH2::Recompiler::setSR(reg src) -> void {
|
||||
and32(M, src, imm(1));
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
auto SH2::Recompiler::checkDelaySlot(F body) -> void {
|
||||
if(!inDelaySlot) return body();
|
||||
auto skip = cmp32_jump(PPM, imm(Branch::Step), flag_ne);
|
||||
body();
|
||||
auto skip2 = jump();
|
||||
setLabel(skip);
|
||||
call(&SH2::illegalSlotInstruction);
|
||||
setLabel(skip2);
|
||||
}
|
||||
|
||||
auto SH2::Recompiler::isTerminal(u16 opcode) -> bool {
|
||||
#define op(id, name, ...) \
|
||||
case id: \
|
||||
return 0;
|
||||
#define br(id, name, ...) \
|
||||
case id: \
|
||||
return 1;
|
||||
#include "decoder.hpp"
|
||||
#undef op
|
||||
#undef br
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef Reg
|
||||
#undef RegR
|
||||
#undef R0
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#define XXH_INLINE_ALL
|
||||
#include <xxhash.h>
|
||||
|
||||
#include <ares/ares.hpp>
|
||||
#include "sh2.hpp"
|
||||
|
||||
@@ -45,7 +48,7 @@ auto SH2::power(bool reset) -> void {
|
||||
ET = 0;
|
||||
ID = 0;
|
||||
exceptions = !reset ? ResetCold : ResetWarm;
|
||||
cyclesUntilSync = 0;
|
||||
cyclesUntilRecompilerExit = 0;
|
||||
|
||||
cache = {*this};
|
||||
intc = {*this};
|
||||
@@ -61,8 +64,10 @@ auto SH2::power(bool reset) -> void {
|
||||
cache.power();
|
||||
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
|
||||
recompiler.allocator.resize(64_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
|
||||
if(!reset) {
|
||||
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
|
||||
recompiler.allocator.resize(64_MiB, bump_allocator::executable, buffer);
|
||||
}
|
||||
recompiler.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
namespace ares {
|
||||
|
||||
struct SH2 {
|
||||
virtual auto instructionPrologue(u16 instruction) -> void = 0;
|
||||
virtual auto step(u32 clocks) -> void = 0;
|
||||
virtual auto busReadByte(u32 address) -> u32 = 0;
|
||||
virtual auto busReadWord(u32 address) -> u32 = 0;
|
||||
@@ -189,7 +190,7 @@ struct SH2 {
|
||||
|
||||
//disassembler.cpp
|
||||
template<typename... P> auto hint(P&&...) const -> string;
|
||||
auto disassembleInstruction() -> string;
|
||||
auto disassembleInstruction(u16 opcode) -> string;
|
||||
auto disassembleContext() -> string;
|
||||
|
||||
static constexpr u32 undefined = 0;
|
||||
@@ -245,8 +246,8 @@ struct SH2 {
|
||||
};
|
||||
u32 exceptions = 0; //delayed exception flags
|
||||
|
||||
s32 cyclesUntilSync = 0;
|
||||
s32 minCyclesBetweenSyncs = 0;
|
||||
s32 cyclesUntilRecompilerExit = 0;
|
||||
s32 recompilerStepCycles = 0;
|
||||
|
||||
struct Recompiler : recompiler::generic {
|
||||
SH2& self;
|
||||
@@ -258,25 +259,53 @@ struct SH2 {
|
||||
}
|
||||
|
||||
u8* code;
|
||||
u8 size;
|
||||
};
|
||||
|
||||
struct Pool {
|
||||
u32 generation;
|
||||
u64 dirty;
|
||||
Block* blocks[1 << 7];
|
||||
};
|
||||
|
||||
struct BlockHashPair {
|
||||
auto operator==(const BlockHashPair& source) const -> bool { return hashcode == source.hashcode; }
|
||||
auto hash() const -> u32 { return hashcode; }
|
||||
|
||||
Block* block;
|
||||
u64 hashcode;
|
||||
};
|
||||
|
||||
auto reset() -> void {
|
||||
generation = 0;
|
||||
blocks.reset();
|
||||
for(u32 index : range(1 << 24)) pools[index] = nullptr;
|
||||
}
|
||||
|
||||
auto invalidate(u32 address) -> void;
|
||||
auto invalidateCached() -> void {
|
||||
generation++;
|
||||
}
|
||||
|
||||
auto invalidate(u32 address, u8 size) -> void;
|
||||
auto pool(u32 address) -> Pool*;
|
||||
auto block(u32 address) -> Block*;
|
||||
auto measure(u32 address) -> u8;
|
||||
auto hash(u32 address, u8 size) -> u64;
|
||||
auto emit(u32 address) -> Block*;
|
||||
auto emitInstruction(u16 opcode) -> bool;
|
||||
auto emitInstruction(u16 opcode) -> u32;
|
||||
auto getSR(reg dst) -> void;
|
||||
auto setSR(reg src) -> void;
|
||||
template<typename F> auto checkDelaySlot(F body) -> void;
|
||||
auto isTerminal(u16 instruction) -> bool;
|
||||
|
||||
static auto mask(u8 address, u8 size) -> u64;
|
||||
|
||||
bool callInstructionPrologue = false;
|
||||
bool inDelaySlot;
|
||||
u32 generation;
|
||||
bump_allocator allocator;
|
||||
hashset<BlockHashPair> blocks;
|
||||
u16 instructions[1 << 7];
|
||||
Pool* pools[1 << 24];
|
||||
} recompiler{*this};
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ template<u32 Origin> auto SH2::readLong(u32 address) -> u32 {
|
||||
|
||||
template<u32 Origin> auto SH2::writeByte(u32 address, u32 data) -> void {
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
recompiler.invalidate(address);
|
||||
recompiler.invalidate(address, 1);
|
||||
}
|
||||
|
||||
switch(address >> 29) {
|
||||
@@ -137,7 +137,7 @@ template<u32 Origin> auto SH2::writeByte(u32 address, u32 data) -> void {
|
||||
}
|
||||
|
||||
case Area::Uncached: {
|
||||
cyclesUntilSync = 0;
|
||||
cyclesUntilRecompilerExit = 0;
|
||||
return busWriteByte(address & 0x1fff'ffff, data);
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ template<u32 Origin> auto SH2::writeWord(u32 address, u32 data) -> void {
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
recompiler.invalidate(address);
|
||||
recompiler.invalidate(address, 2);
|
||||
}
|
||||
|
||||
switch(address >> 29) {
|
||||
@@ -186,7 +186,7 @@ template<u32 Origin> auto SH2::writeWord(u32 address, u32 data) -> void {
|
||||
}
|
||||
|
||||
case Area::Uncached: {
|
||||
cyclesUntilSync = 0;
|
||||
cyclesUntilRecompilerExit = 0;
|
||||
return busWriteWord(address & 0x1fff'fffe, data);
|
||||
}
|
||||
|
||||
@@ -219,8 +219,7 @@ template<u32 Origin> auto SH2::writeLong(u32 address, u32 data) -> void {
|
||||
}
|
||||
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
recompiler.invalidate(address + 0);
|
||||
recompiler.invalidate(address + 2);
|
||||
recompiler.invalidate(address, 4);
|
||||
}
|
||||
|
||||
switch(address >> 29) {
|
||||
@@ -233,7 +232,7 @@ template<u32 Origin> auto SH2::writeLong(u32 address, u32 data) -> void {
|
||||
}
|
||||
|
||||
case Area::Uncached: {
|
||||
cyclesUntilSync = 0;
|
||||
cyclesUntilRecompilerExit = 0;
|
||||
return busWriteLong(address & 0x1fff'fffc, data);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,12 +88,20 @@ auto SH2::Cache::purge(u32 address) -> void {
|
||||
if(tags[Way1 | entry] == tag) tags[Way1 | entry] |= Invalid;
|
||||
if(tags[Way2 | entry] == tag) tags[Way2 | entry] |= Invalid;
|
||||
if(tags[Way3 | entry] == tag) tags[Way3 | entry] |= Invalid;
|
||||
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
self->recompiler.invalidate(address & 0x1fff'fff0, 0x10);
|
||||
}
|
||||
}
|
||||
|
||||
template<u32 Ways>
|
||||
auto SH2::Cache::purge() -> void {
|
||||
for(auto index : range(64)) lrus[index] = 0;
|
||||
for(auto index : range(64 * Ways)) tags[index] |= Invalid;
|
||||
|
||||
if constexpr(Accuracy::Recompiler) {
|
||||
self->recompiler.invalidateCached();
|
||||
}
|
||||
}
|
||||
|
||||
auto SH2::Cache::power() -> void {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
auto SH2::INTC::run() -> void {
|
||||
if(self->inDelaySlot()) return;
|
||||
if(self->inDelaySlot()) { self->cyclesUntilRecompilerExit = 0; return; }
|
||||
|
||||
if(self->frt.pendingOutputIRQ) {
|
||||
if(self->SR.I < iprb.frtip) {
|
||||
@@ -13,11 +13,9 @@ auto SH2::INTC::run() -> void {
|
||||
if(self->dmac.pendingIRQ & 1) {
|
||||
self->interrupt(ipra.dmacip, self->dmac.vcrdma[0] & 0x7f);
|
||||
self->dmac.pendingIRQ &= ~1;
|
||||
print("dreq0\n");
|
||||
} else if(self->dmac.pendingIRQ & 2) {
|
||||
self->interrupt(ipra.dmacip, self->dmac.vcrdma[1] & 0x7f);
|
||||
self->dmac.pendingIRQ &= ~2;
|
||||
print("dreq1\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,6 +177,7 @@ auto SH2::internalReadByte(u32 address, n8 data) -> n8 {
|
||||
|
||||
//CCR: cache control register
|
||||
case 0xffff'fe92:
|
||||
case 0xffff'fe93:
|
||||
data.bit(0) = cache.enable;
|
||||
data.bit(1) = cache.disableCode;
|
||||
data.bit(2) = cache.disableData;
|
||||
@@ -719,6 +720,7 @@ auto SH2::internalWriteByte(u32 address, n8 data) -> void {
|
||||
|
||||
//CCR: cache control register
|
||||
case 0xffff'fe92:
|
||||
case 0xffff'fe93:
|
||||
cache.enable = data.bit(0);
|
||||
cache.disableCode = data.bit(1);
|
||||
cache.disableData = data.bit(2);
|
||||
|
||||
@@ -27,7 +27,7 @@ auto SH2::FRT::run() -> void {
|
||||
}
|
||||
}
|
||||
auto SH2::WDT::run() -> void {
|
||||
static constexpr u32 frequencies[8] = {2, 64, 128, 256, 512, 1024, 2096, 8192};
|
||||
static constexpr u32 frequencies[8] = {2, 64, 128, 256, 512, 1024, 4096, 8192};
|
||||
|
||||
if(!wtcsr.tme) return;
|
||||
if(wtcsr.wtit) return; // Watchdog mode currently unsupported
|
||||
|
||||
@@ -12,54 +12,54 @@ auto SM5K::disassembleInstruction() -> string {
|
||||
string pc = {"0x", hex(n4(opcode) << 8 | operand, 3L)};
|
||||
|
||||
switch(opcode) {
|
||||
case 0x00 ... 0x0f: s = {"adx ", p4}; break;
|
||||
case 0x10 ... 0x1f: s = {"lax ", p4}; break;
|
||||
case 0x20 ... 0x2f: s = {"lblx ", p4}; break;
|
||||
case 0x30 ... 0x3f: s = {"lbmx ", p4}; break;
|
||||
case 0x40 ... 0x43: s = {"rm ", p2}; break;
|
||||
case 0x44 ... 0x47: s = {"sm ", p2}; break;
|
||||
case 0x48 ... 0x4b: s = {"tm ", p2}; break;
|
||||
case 0x4c ... 0x4f: s = {"tpb ", p2}; break;
|
||||
case 0x50 ... 0x53: s = {"lda ", p2}; break;
|
||||
case 0x54 ... 0x57: s = {"exc ", p2}; break;
|
||||
case 0x58 ... 0x5b: s = {"exci ", p2}; break;
|
||||
case 0x5c ... 0x5f: s = {"excd ", p2}; break;
|
||||
case 0x60: s = {"rc " }; break;
|
||||
case 0x61: s = {"sc " }; break;
|
||||
case 0x62: s = {"id " }; break;
|
||||
case 0x63: s = {"ie " }; break;
|
||||
case 0x64: s = {"exax " }; break;
|
||||
case 0x65: s = {"atx " }; break;
|
||||
case 0x66: s = {"exbm " }; break;
|
||||
case 0x67: s = {"exbl " }; break;
|
||||
case 0x68: s = {"ex " }; break;
|
||||
case 0x69: s = {"dta ", p8}; break;
|
||||
case 0x6a: s = {"pat ", p8}; break;
|
||||
case 0x6b: s = {"tabl " }; break;
|
||||
case 0x6c: s = {"ta " }; break;
|
||||
case 0x6d: s = {"tb " }; break;
|
||||
case 0x6e: s = {"tc " }; break;
|
||||
case 0x6f: s = {"tam " }; break;
|
||||
case 0x70: s = {"inl " }; break;
|
||||
case 0x71: s = {"outl " }; break;
|
||||
case 0x72: s = {"anp " }; break;
|
||||
case 0x73: s = {"orp " }; break;
|
||||
case 0x74: s = {"in " }; break;
|
||||
case 0x75: s = {"out " }; break;
|
||||
case 0x76: s = {"stop " }; break;
|
||||
case 0x77: s = {"halt " }; break;
|
||||
case 0x78: s = {"incb " }; break;
|
||||
case 0x79: s = {"coma " }; break;
|
||||
case 0x7a: s = {"add " }; break;
|
||||
case 0x7b: s = {"adc " }; break;
|
||||
case 0x7c: s = {"decb " }; break;
|
||||
case 0x7d: s = {"rtn " }; break;
|
||||
case 0x7e: s = {"rtns " }; break;
|
||||
case 0x7f: s = {"rtni " }; break;
|
||||
case 0x80 ... 0xbf: s = {"tr ", p6}; break;
|
||||
case 0xc0 ... 0xdf: s = {"trs ", p5}; break;
|
||||
case 0xe0 ... 0xef: s = {"tl ", pc}; break;
|
||||
case 0xf0 ... 0xff: s = {"call ", pc}; break;
|
||||
case range16(0x00, 0x0f): s = {"adx ", p4}; break;
|
||||
case range16(0x10, 0x1f): s = {"lax ", p4}; break;
|
||||
case range16(0x20, 0x2f): s = {"lblx ", p4}; break;
|
||||
case range16(0x30, 0x3f): s = {"lbmx ", p4}; break;
|
||||
case range4 (0x40, 0x43): s = {"rm ", p2}; break;
|
||||
case range4 (0x44, 0x47): s = {"sm ", p2}; break;
|
||||
case range4 (0x48, 0x4b): s = {"tm ", p2}; break;
|
||||
case range4 (0x4c, 0x4f): s = {"tpb ", p2}; break;
|
||||
case range4 (0x50, 0x53): s = {"lda ", p2}; break;
|
||||
case range4 (0x54, 0x57): s = {"exc ", p2}; break;
|
||||
case range4 (0x58, 0x5b): s = {"exci ", p2}; break;
|
||||
case range4 (0x5c, 0x5f): s = {"excd ", p2}; break;
|
||||
case 0x60: s = {"rc " }; break;
|
||||
case 0x61: s = {"sc " }; break;
|
||||
case 0x62: s = {"id " }; break;
|
||||
case 0x63: s = {"ie " }; break;
|
||||
case 0x64: s = {"exax " }; break;
|
||||
case 0x65: s = {"atx " }; break;
|
||||
case 0x66: s = {"exbm " }; break;
|
||||
case 0x67: s = {"exbl " }; break;
|
||||
case 0x68: s = {"ex " }; break;
|
||||
case 0x69: s = {"dta ", p8}; break;
|
||||
case 0x6a: s = {"pat ", p8}; break;
|
||||
case 0x6b: s = {"tabl " }; break;
|
||||
case 0x6c: s = {"ta " }; break;
|
||||
case 0x6d: s = {"tb " }; break;
|
||||
case 0x6e: s = {"tc " }; break;
|
||||
case 0x6f: s = {"tam " }; break;
|
||||
case 0x70: s = {"inl " }; break;
|
||||
case 0x71: s = {"outl " }; break;
|
||||
case 0x72: s = {"anp " }; break;
|
||||
case 0x73: s = {"orp " }; break;
|
||||
case 0x74: s = {"in " }; break;
|
||||
case 0x75: s = {"out " }; break;
|
||||
case 0x76: s = {"stop " }; break;
|
||||
case 0x77: s = {"halt " }; break;
|
||||
case 0x78: s = {"incb " }; break;
|
||||
case 0x79: s = {"coma " }; break;
|
||||
case 0x7a: s = {"add " }; break;
|
||||
case 0x7b: s = {"adc " }; break;
|
||||
case 0x7c: s = {"decb " }; break;
|
||||
case 0x7d: s = {"rtn " }; break;
|
||||
case 0x7e: s = {"rtns " }; break;
|
||||
case 0x7f: s = {"rtni " }; break;
|
||||
case range64(0x80, 0xbf): s = {"tr ", p6}; break;
|
||||
case range32(0xc0, 0xdf): s = {"trs ", p5}; break;
|
||||
case range16(0xe0, 0xef): s = {"tl ", pc}; break;
|
||||
case range16(0xf0, 0xff): s = {"call ", pc}; break;
|
||||
}
|
||||
|
||||
while(s.size() < 10) s.append(" ");
|
||||
|
||||
@@ -20,54 +20,54 @@ auto SM5K::instruction() -> void {
|
||||
|
||||
n8 opcode = fetch();
|
||||
switch(opcode) {
|
||||
op(0x00 ... 0x0f, ADX, n4(opcode));
|
||||
op(0x10 ... 0x1f, LAX, n4(opcode));
|
||||
op(0x20 ... 0x2f, LBLX, n4(opcode));
|
||||
op(0x30 ... 0x3f, LBMX, n4(opcode));
|
||||
op(0x40 ... 0x43, RM, n2(opcode));
|
||||
op(0x44 ... 0x47, SM, n2(opcode));
|
||||
op(0x48 ... 0x4b, TM, n2(opcode));
|
||||
op(0x4c ... 0x4f, TPB, n2(opcode));
|
||||
op(0x50 ... 0x53, LDA, n2(opcode));
|
||||
op(0x54 ... 0x57, EXC, n2(opcode));
|
||||
op(0x58 ... 0x5b, EXCI, n2(opcode));
|
||||
op(0x5c ... 0x5f, EXCD, n2(opcode));
|
||||
op(0x60, RC );
|
||||
op(0x61, SC );
|
||||
op(0x62, ID );
|
||||
op(0x63, IE );
|
||||
op(0x64, EXAX );
|
||||
op(0x65, ATX );
|
||||
op(0x66, EXBM );
|
||||
op(0x67, EXBL );
|
||||
op(0x68, EX );
|
||||
op(0x69, DTA, fetch());
|
||||
op(0x6a, PAT, fetch());
|
||||
op(0x6b, TABL );
|
||||
op(0x6c, TA );
|
||||
op(0x6d, TB );
|
||||
op(0x6e, TC );
|
||||
op(0x6f, TAM );
|
||||
op(0x70, INL );
|
||||
op(0x71, OUTL );
|
||||
op(0x72, ANP );
|
||||
op(0x73, ORP );
|
||||
op(0x74, IN );
|
||||
op(0x75, OUT );
|
||||
op(0x76, STOP );
|
||||
op(0x77, HALT );
|
||||
op(0x78, INCB );
|
||||
op(0x79, COMA );
|
||||
op(0x7a, ADD );
|
||||
op(0x7b, ADC );
|
||||
op(0x7c, DECB );
|
||||
op(0x7d, RTN );
|
||||
op(0x7e, RTNS );
|
||||
op(0x7f, RTNI );
|
||||
op(0x80 ... 0xbf, TR, n6(opcode));
|
||||
op(0xc0 ... 0xdf, TRS, n5(opcode));
|
||||
op(0xe0 ... 0xef, TL, n4(opcode) << 8 | fetch());
|
||||
op(0xf0 ... 0xff, CALL, n4(opcode) << 8 | fetch());
|
||||
op(range16(0x00, 0x0f), ADX, n4(opcode));
|
||||
op(range16(0x10, 0x1f), LAX, n4(opcode));
|
||||
op(range16(0x20, 0x2f), LBLX, n4(opcode));
|
||||
op(range16(0x30, 0x3f), LBMX, n4(opcode));
|
||||
op(range4 (0x40, 0x43), RM, n2(opcode));
|
||||
op(range4 (0x44, 0x47), SM, n2(opcode));
|
||||
op(range4 (0x48, 0x4b), TM, n2(opcode));
|
||||
op(range4 (0x4c, 0x4f), TPB, n2(opcode));
|
||||
op(range4 (0x50, 0x53), LDA, n2(opcode));
|
||||
op(range4 (0x54, 0x57), EXC, n2(opcode));
|
||||
op(range4 (0x58, 0x5b), EXCI, n2(opcode));
|
||||
op(range4 (0x5c, 0x5f), EXCD, n2(opcode));
|
||||
op(0x60, RC );
|
||||
op(0x61, SC );
|
||||
op(0x62, ID );
|
||||
op(0x63, IE );
|
||||
op(0x64, EXAX );
|
||||
op(0x65, ATX );
|
||||
op(0x66, EXBM );
|
||||
op(0x67, EXBL );
|
||||
op(0x68, EX );
|
||||
op(0x69, DTA, fetch());
|
||||
op(0x6a, PAT, fetch());
|
||||
op(0x6b, TABL );
|
||||
op(0x6c, TA );
|
||||
op(0x6d, TB );
|
||||
op(0x6e, TC );
|
||||
op(0x6f, TAM );
|
||||
op(0x70, INL );
|
||||
op(0x71, OUTL );
|
||||
op(0x72, ANP );
|
||||
op(0x73, ORP );
|
||||
op(0x74, IN );
|
||||
op(0x75, OUT );
|
||||
op(0x76, STOP );
|
||||
op(0x77, HALT );
|
||||
op(0x78, INCB );
|
||||
op(0x79, COMA );
|
||||
op(0x7a, ADD );
|
||||
op(0x7b, ADC );
|
||||
op(0x7c, DECB );
|
||||
op(0x7d, RTN );
|
||||
op(0x7e, RTNS );
|
||||
op(0x7f, RTNI );
|
||||
op(range64(0x80, 0xbf), TR, n6(opcode));
|
||||
op(range32(0xc0, 0xdf), TRS, n5(opcode));
|
||||
op(range16(0xe0, 0xef), TL, n4(opcode) << 8 | fetch());
|
||||
op(range16(0xf0, 0xff), CALL, n4(opcode) << 8 | fetch());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,13 +45,13 @@ auto SSP1601::cmp(u32 source) -> void {
|
||||
N = result >> 31;
|
||||
}
|
||||
|
||||
auto SSP1601::and(u32 v) -> void {
|
||||
auto SSP1601::and_(u32 v) -> void {
|
||||
A &= v;
|
||||
Z = A == 0;
|
||||
N = A >> 31;
|
||||
}
|
||||
|
||||
auto SSP1601::or(u32 v) -> void {
|
||||
auto SSP1601::or_(u32 v) -> void {
|
||||
A |= v;
|
||||
Z = A == 0;
|
||||
N = A >> 31;
|
||||
|
||||
@@ -210,7 +210,7 @@ auto SSP1601::disassembleInstruction(u16 pc, u16 op, u16 imm) -> string {
|
||||
}
|
||||
|
||||
//ldi ri,simm
|
||||
case 0x0c ... 0x0f: {
|
||||
case range4(0x0c, 0x0f): {
|
||||
o = "ldi";
|
||||
p = {"r", (op >> 8 & 7), ",0x", hex(op & 0xff, 2L)};
|
||||
break;
|
||||
|
||||
@@ -95,7 +95,7 @@ auto SSP1601::instruction() -> void {
|
||||
}
|
||||
|
||||
//ldi ri,simm
|
||||
case 0x0c ... 0x0f: {
|
||||
case range4(0x0c, 0x0f): {
|
||||
R[n3(op >> 8)] = n8(op);
|
||||
break;
|
||||
}
|
||||
@@ -173,56 +173,56 @@ auto SSP1601::instruction() -> void {
|
||||
case 0x10: { auto v = n4(op) == 3 ? A : n4(op) == 7 ? updateP() : n32(readGR(n4(op)) << 16); sub(v); break; }
|
||||
case 0x30: { auto v = n4(op) == 3 ? A : n4(op) == 7 ? updateP() : n32(readGR(n4(op)) << 16); cmp(v); break; }
|
||||
case 0x40: { auto v = n4(op) == 3 ? A : n4(op) == 7 ? updateP() : n32(readGR(n4(op)) << 16); add(v); break; }
|
||||
case 0x50: { auto v = n4(op) == 3 ? A : n4(op) == 7 ? updateP() : n32(readGR(n4(op)) << 16); and(v); break; }
|
||||
case 0x60: { auto v = n4(op) == 3 ? A : n4(op) == 7 ? updateP() : n32(readGR(n4(op)) << 16); or (v); break; }
|
||||
case 0x50: { auto v = n4(op) == 3 ? A : n4(op) == 7 ? updateP() : n32(readGR(n4(op)) << 16); and_(v); break; }
|
||||
case 0x60: { auto v = n4(op) == 3 ? A : n4(op) == 7 ? updateP() : n32(readGR(n4(op)) << 16); or_(v); break; }
|
||||
case 0x70: { auto v = n4(op) == 3 ? A : n4(op) == 7 ? updateP() : n32(readGR(n4(op)) << 16); eor(v); break; }
|
||||
|
||||
//alu a,(ri)
|
||||
case 0x11: { auto v = readPR1(op) << 16; sub(v); break; }
|
||||
case 0x31: { auto v = readPR1(op) << 16; cmp(v); break; }
|
||||
case 0x41: { auto v = readPR1(op) << 16; add(v); break; }
|
||||
case 0x51: { auto v = readPR1(op) << 16; and(v); break; }
|
||||
case 0x61: { auto v = readPR1(op) << 16; or (v); break; }
|
||||
case 0x51: { auto v = readPR1(op) << 16; and_(v); break; }
|
||||
case 0x61: { auto v = readPR1(op) << 16; or_(v); break; }
|
||||
case 0x71: { auto v = readPR1(op) << 16; eor(v); break; }
|
||||
|
||||
//alu a,addr
|
||||
case 0x13: { auto v = RAM[n9(op)] << 16; sub(v); break; }
|
||||
case 0x33: { auto v = RAM[n9(op)] << 16; cmp(v); break; }
|
||||
case 0x43: { auto v = RAM[n9(op)] << 16; add(v); break; }
|
||||
case 0x53: { auto v = RAM[n9(op)] << 16; and(v); break; }
|
||||
case 0x63: { auto v = RAM[n9(op)] << 16; or (v); break; }
|
||||
case 0x53: { auto v = RAM[n9(op)] << 16; and_(v); break; }
|
||||
case 0x63: { auto v = RAM[n9(op)] << 16; or_(v); break; }
|
||||
case 0x73: { auto v = RAM[n9(op)] << 16; eor(v); break; }
|
||||
|
||||
//alu a,imm
|
||||
case 0x14: { auto v = fetch() << 16; sub(v); break; }
|
||||
case 0x34: { auto v = fetch() << 16; cmp(v); break; }
|
||||
case 0x44: { auto v = fetch() << 16; add(v); break; }
|
||||
case 0x54: { auto v = fetch() << 16; and(v); break; }
|
||||
case 0x64: { auto v = fetch() << 16; or (v); break; }
|
||||
case 0x54: { auto v = fetch() << 16; and_(v); break; }
|
||||
case 0x64: { auto v = fetch() << 16; or_(v); break; }
|
||||
case 0x74: { auto v = fetch() << 16; eor(v); break; }
|
||||
|
||||
//alu a,((ri))
|
||||
case 0x15: { auto v = readPR2(op) << 16; sub(v); break; }
|
||||
case 0x35: { auto v = readPR2(op) << 16; cmp(v); break; }
|
||||
case 0x45: { auto v = readPR2(op) << 16; add(v); break; }
|
||||
case 0x55: { auto v = readPR2(op) << 16; and(v); break; }
|
||||
case 0x65: { auto v = readPR2(op) << 16; or (v); break; }
|
||||
case 0x55: { auto v = readPR2(op) << 16; and_(v); break; }
|
||||
case 0x65: { auto v = readPR2(op) << 16; or_(v); break; }
|
||||
case 0x75: { auto v = readPR2(op) << 16; eor(v); break; }
|
||||
|
||||
//alu a,ri
|
||||
case 0x19: { auto v = R[n2(op) | (op >> 6 & 4)] << 16; sub(v); break; }
|
||||
case 0x39: { auto v = R[n2(op) | (op >> 6 & 4)] << 16; cmp(v); break; }
|
||||
case 0x49: { auto v = R[n2(op) | (op >> 6 & 4)] << 16; add(v); break; }
|
||||
case 0x59: { auto v = R[n2(op) | (op >> 6 & 4)] << 16; and(v); break; }
|
||||
case 0x69: { auto v = R[n2(op) | (op >> 6 & 4)] << 16; or (v); break; }
|
||||
case 0x59: { auto v = R[n2(op) | (op >> 6 & 4)] << 16; and_(v); break; }
|
||||
case 0x69: { auto v = R[n2(op) | (op >> 6 & 4)] << 16; or_(v); break; }
|
||||
case 0x79: { auto v = R[n2(op) | (op >> 6 & 4)] << 16; eor(v); break; }
|
||||
|
||||
//alu simm
|
||||
case 0x1c: { auto v = u8(op) << 16; sub(v); break; }
|
||||
case 0x3c: { auto v = u8(op) << 16; cmp(v); break; }
|
||||
case 0x4c: { auto v = u8(op) << 16; add(v); break; }
|
||||
case 0x5c: { auto v = u8(op) << 16; and(v); break; }
|
||||
case 0x6c: { auto v = u8(op) << 16; or (v); break; }
|
||||
case 0x5c: { auto v = u8(op) << 16; and_(v); break; }
|
||||
case 0x6c: { auto v = u8(op) << 16; or_(v); break; }
|
||||
case 0x7c: { auto v = u8(op) << 16; eor(v); break; }
|
||||
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ struct SSP1601 {
|
||||
auto add(u32) -> void;
|
||||
auto sub(u32) -> void;
|
||||
auto cmp(u32) -> void;
|
||||
auto and(u32) -> void;
|
||||
auto or (u32) -> void;
|
||||
auto and_(u32) -> void;
|
||||
auto or_(u32) -> void;
|
||||
auto eor(u32) -> void;
|
||||
auto shr() -> void;
|
||||
auto shl() -> void;
|
||||
|
||||
@@ -144,23 +144,23 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
case 0x1d: name = "call"; lhs.immediate(24, read24()); break;
|
||||
case 0x1e: name = "calr"; lhs.displacementPC(16, read16()); break;
|
||||
case 0x1f: break;
|
||||
case 0x20 ... 0x27: name = "ld"; lhs.register3(8, fetch); rhs.immediate(8, read8()); break;
|
||||
case 0x28 ... 0x2f: name = "push"; lhs.register3(16, fetch); break;
|
||||
case 0x30 ... 0x37: name = "ld"; lhs.register3(16, fetch); rhs.immediate(16, read16()); break;
|
||||
case 0x38 ... 0x3f: name = "push"; lhs.register3(32, fetch); break;
|
||||
case 0x40 ... 0x47: name = "ld"; lhs.register3(32, fetch); rhs.immediate(32, read32()); break;
|
||||
case 0x48 ... 0x4f: name = "pop"; lhs.register3(16, fetch); break;
|
||||
case 0x50 ... 0x5f: name = "pop"; lhs.register3(32, fetch); break;
|
||||
case 0x60 ... 0x6f: name = "jr"; lhs.condition(fetch); rhs.displacementPC(8, read8()); break;
|
||||
case 0x70 ... 0x7f: name = "jrl"; lhs.condition(fetch); rhs.displacementPC(16, read16()); break;
|
||||
case 0x80 ... 0x87: opSourceMemory = true; lhs.indirectRegister3(8, fetch); break;
|
||||
case 0x88 ... 0x8f: opSourceMemory = true; lhs.indirectRegister3Displacement8(8, fetch, read8()); break;
|
||||
case 0x90 ... 0x97: opSourceMemory = true; lhs.indirectRegister3(16, fetch); break;
|
||||
case 0x98 ... 0x9f: opSourceMemory = true; lhs.indirectRegister3Displacement8(16, fetch, read8()); break;
|
||||
case 0xa0 ... 0xa7: opSourceMemory = true; lhs.indirectRegister3(32, fetch); break;
|
||||
case 0xa8 ... 0xaf: opSourceMemory = true; lhs.indirectRegister3Displacement8(32, fetch, read8()); break;
|
||||
case 0xb0 ... 0xb7: opTargetMemory = true; lhs.indirectRegister3(0, fetch); break;
|
||||
case 0xb8 ... 0xbf: opTargetMemory = true; lhs.indirectRegister3Displacement8(0, fetch, read8()); break;
|
||||
case range8(0x20, 0x27): name = "ld"; lhs.register3(8, fetch); rhs.immediate(8, read8()); break;
|
||||
case range8(0x28, 0x2f): name = "push"; lhs.register3(16, fetch); break;
|
||||
case range8(0x30, 0x37): name = "ld"; lhs.register3(16, fetch); rhs.immediate(16, read16()); break;
|
||||
case range8(0x38, 0x3f): name = "push"; lhs.register3(32, fetch); break;
|
||||
case range8(0x40, 0x47): name = "ld"; lhs.register3(32, fetch); rhs.immediate(32, read32()); break;
|
||||
case range8(0x48, 0x4f): name = "pop"; lhs.register3(16, fetch); break;
|
||||
case range16(0x50, 0x5f): name = "pop"; lhs.register3(32, fetch); break;
|
||||
case range16(0x60, 0x6f): name = "jr"; lhs.condition(fetch); rhs.displacementPC(8, read8()); break;
|
||||
case range16(0x70, 0x7f): name = "jrl"; lhs.condition(fetch); rhs.displacementPC(16, read16()); break;
|
||||
case range8(0x80, 0x87): opSourceMemory = true; lhs.indirectRegister3(8, fetch); break;
|
||||
case range8(0x88, 0x8f): opSourceMemory = true; lhs.indirectRegister3Displacement8(8, fetch, read8()); break;
|
||||
case range8(0x90, 0x97): opSourceMemory = true; lhs.indirectRegister3(16, fetch); break;
|
||||
case range8(0x98, 0x9f): opSourceMemory = true; lhs.indirectRegister3Displacement8(16, fetch, read8()); break;
|
||||
case range8(0xa0, 0xa7): opSourceMemory = true; lhs.indirectRegister3(32, fetch); break;
|
||||
case range8(0xa8, 0xaf): opSourceMemory = true; lhs.indirectRegister3Displacement8(32, fetch, read8()); break;
|
||||
case range8(0xb0, 0xb7): opTargetMemory = true; lhs.indirectRegister3(0, fetch); break;
|
||||
case range8(0xb8, 0xbf): opTargetMemory = true; lhs.indirectRegister3Displacement8(0, fetch, read8()); break;
|
||||
case 0xc0: case 0xd0: case 0xe0: case 0xf0:
|
||||
opSourceMemory = fetch < 0xf0; opTargetMemory = !opSourceMemory;
|
||||
lhs.indirectImmediate8(opSize, read8()); break;
|
||||
@@ -203,10 +203,10 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
read8(); lhs.indirectImmediate8(8, read8());
|
||||
read8(); rhs.immediate(8, read8());
|
||||
read8(); break;
|
||||
case 0xc8 ... 0xcf: opRegister = true; lhs.register3(8, fetch); break;
|
||||
case 0xd8 ... 0xdf: opRegister = true; lhs.register3(16, fetch); break;
|
||||
case 0xe8 ... 0xef: opRegister = true; lhs.register3(32, fetch); break;
|
||||
case 0xf8 ... 0xff: name = "swi"; lhs.immediate(3, (n3)fetch); break;
|
||||
case range8(0xc8, 0xcf): opRegister = true; lhs.register3(8, fetch); break;
|
||||
case range8(0xd8, 0xdf): opRegister = true; lhs.register3(16, fetch); break;
|
||||
case range8(0xe8, 0xef): opRegister = true; lhs.register3(32, fetch); break;
|
||||
case range8(0xf8, 0xff): name = "swi"; lhs.immediate(3, (n3)fetch); break;
|
||||
}
|
||||
#undef opSize
|
||||
|
||||
@@ -221,7 +221,7 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
//size defined
|
||||
if(opRegister)
|
||||
switch(auto fetch = read8()) {
|
||||
case 0x00 ... 0x02: break;
|
||||
case range3(0x00, 0x02): break;
|
||||
case 0x03: name = "ld"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
|
||||
case 0x04: name = "push"; break;
|
||||
case 0x05: name = "pop"; break;
|
||||
@@ -242,17 +242,17 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
case 0x14: name = "paa"; break;
|
||||
case 0x15: break;
|
||||
case 0x16: name = "mirr"; break;
|
||||
case 0x17 ... 0x18: break;
|
||||
case range2(0x17, 0x18): break;
|
||||
case 0x19: name = "mula"; break;
|
||||
case 0x1a ... 0x1b: break;
|
||||
case range2(0x1a, 0x1b): break;
|
||||
case 0x1c: name = "djnz"; rhs.displacementPC(8, read8()); break;
|
||||
case 0x1d ... 0x1f: break;
|
||||
case range3(0x1d, 0x1f): break;
|
||||
case 0x20: name = "andcf"; rhs.immediate(4, (n4)read8()); break;
|
||||
case 0x21: name = "orcf"; rhs.immediate(4, (n4)read8()); break;
|
||||
case 0x22: name = "xorcf"; rhs.immediate(4, (n4)read8()); break;
|
||||
case 0x23: name = "ldcf"; rhs.immediate(4, (n4)read8()); break;
|
||||
case 0x24: name = "stcf"; rhs.immediate(4, (n4)read8()); break;
|
||||
case 0x25 ... 0x27: break;
|
||||
case range3(0x25, 0x27): break;
|
||||
case 0x28: name = "andcf"; rhs.register(8, A.id); break;
|
||||
case 0x29: name = "orcf"; rhs.register(8, A.id); break;
|
||||
case 0x2a: name = "xorcf"; rhs.register(8, A.id); break;
|
||||
@@ -266,7 +266,7 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
case 0x32: name = "chg"; rhs.immediate(4, (n4)read8()); break;
|
||||
case 0x33: name = "bit"; rhs.immediate(4, (n4)read8()); break;
|
||||
case 0x34: name = "tset"; rhs.immediate(4, (n4)read8()); break;
|
||||
case 0x35 ... 0x37: break;
|
||||
case range3(0x35, 0x37): break;
|
||||
case 0x38: name = "minc1"; rhs.immediate(16, read16()); break;
|
||||
case 0x39: name = "minc2"; rhs.immediate(16, read16()); break;
|
||||
case 0x3a: name = "minc4"; rhs.immediate(16, read16()); break;
|
||||
@@ -275,22 +275,22 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
case 0x3d: name = "mdec2"; rhs.immediate(16, read16()); break;
|
||||
case 0x3e: name = "mdec4"; rhs.immediate(16, read16()); break;
|
||||
case 0x3f: break;
|
||||
case 0x40 ... 0x47: name = "mul"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x48 ... 0x4f: name = "muls"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x50 ... 0x57: name = "div"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x58 ... 0x5f: name = "divs"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x60 ... 0x67: name = "inc"; rhs.immediate(4, fetch ? natural((n3)fetch) : 8_n); break;
|
||||
case 0x68 ... 0x6f: name = "dec"; rhs.immediate(4, fetch ? natural((n3)fetch) : 8_n); break;
|
||||
case 0x70 ... 0x7f: name = "scc"; rhs = lhs; lhs.condition(fetch); break;
|
||||
case 0x80 ... 0x87: name = "add"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x88 ... 0x8f: name = "ld"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x90 ... 0x97: name = "adc"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x98 ... 0x9f: name = "ld"; rhs.register3(lhs.size(), fetch); break;
|
||||
case 0xa0 ... 0xa7: name = "sub"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0xa8 ... 0xaf: name = "ld"; rhs.immediate(3, (n3)fetch); break;
|
||||
case 0xb0 ... 0xb7: name = "sbb"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0xb8 ... 0xbf: name = "ex"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0xc0 ... 0xc7: name = "and"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x40, 0x47): name = "mul"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x48, 0x4f): name = "muls"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x50, 0x57): name = "div"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x58, 0x5f): name = "divs"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x60, 0x67): name = "inc"; rhs.immediate(4, fetch ? natural((n3)fetch) : 8_n); break;
|
||||
case range8(0x68, 0x6f): name = "dec"; rhs.immediate(4, fetch ? natural((n3)fetch) : 8_n); break;
|
||||
case range16(0x70, 0x7f): name = "scc"; rhs = lhs; lhs.condition(fetch); break;
|
||||
case range8(0x80, 0x87): name = "add"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x88, 0x8f): name = "ld"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x90, 0x97): name = "adc"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x98, 0x9f): name = "ld"; rhs.register3(lhs.size(), fetch); break;
|
||||
case range8(0xa0, 0xa7): name = "sub"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0xa8, 0xaf): name = "ld"; rhs.immediate(3, (n3)fetch); break;
|
||||
case range8(0xb0, 0xb7): name = "sbb"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0xb8, 0xbf): name = "ex"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0xc0, 0xc7): name = "and"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0xc8: name = "add"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
|
||||
case 0xc9: name = "adc"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
|
||||
case 0xca: name = "sub"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
|
||||
@@ -299,9 +299,9 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
case 0xcd: name = "xor"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
|
||||
case 0xce: name = "or"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
|
||||
case 0xcf: name = "cp"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
|
||||
case 0xd0 ... 0xd7: name = "xor"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0xd8 ... 0xdf: name = "cp"; rhs.immediate(3, (n3)fetch); break;
|
||||
case 0xe0 ... 0xe7: name = "or"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0xd0, 0xd7): name = "xor"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0xd8, 0xdf): name = "cp"; rhs.immediate(3, (n3)fetch); break;
|
||||
case range8(0xe0, 0xe7): name = "or"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0xe8: name = "rlc"; { auto data = read8(); rhs.immediate(5, data ? natural(data) : 16_n); } break;
|
||||
case 0xe9: name = "rrc"; { auto data = read8(); rhs.immediate(5, data ? natural(data) : 16_n); } break;
|
||||
case 0xea: name = "rl"; { auto data = read8(); rhs.immediate(5, data ? natural(data) : 16_n); } break;
|
||||
@@ -310,7 +310,7 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
case 0xed: name = "sra"; { auto data = read8(); rhs.immediate(5, data ? natural(data) : 16_n); } break;
|
||||
case 0xee: name = "sll"; { auto data = read8(); rhs.immediate(5, data ? natural(data) : 16_n); } break;
|
||||
case 0xef: name = "srl"; { auto data = read8(); rhs.immediate(5, data ? natural(data) : 16_n); } break;
|
||||
case 0xf0 ... 0xf7: name = "cp"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0xf0, 0xf7): name = "cp"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0xf8: name = "rlc"; rhs.register(8, A.id); break;
|
||||
case 0xf9: name = "rrc"; rhs.register(8, A.id); break;
|
||||
case 0xfa: name = "rl"; rhs.register(8, A.id); break;
|
||||
@@ -324,12 +324,12 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
//size defined
|
||||
if(opSourceMemory)
|
||||
switch(auto fetch = read8()) {
|
||||
case 0x00 ... 0x03: break;
|
||||
case range4(0x00, 0x03): break;
|
||||
case 0x04: name = lhs.size() == 8 ? "push" : "pushw"; break;
|
||||
case 0x05: break;
|
||||
case 0x06: name = "rld"; rhs = lhs; lhs.register(8, A.id); break;
|
||||
case 0x07: name = "rrd"; rhs = lhs; lhs.register(8, A.id); break;
|
||||
case 0x08 ... 0x0f: break;
|
||||
case range8(0x08, 0x0f): break;
|
||||
case 0x10:
|
||||
name = lhs.size() == 8 ? "ldi" : "ldiw";
|
||||
lhs.indirectRegisterIncrement(32, (n3)op[0] != 5 ? XDE.id : XIX.id);
|
||||
@@ -372,10 +372,10 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
break;
|
||||
case 0x18: break;
|
||||
case 0x19: name = "ld"; rhs = lhs; lhs.indirectImmediate16(16, read16()); break;
|
||||
case 0x1a ... 0x1f: break;
|
||||
case 0x20 ... 0x27: name = "ld"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x28 ... 0x2f: break;
|
||||
case 0x30 ... 0x37: name = "ex"; rhs.register3(lhs.size(), fetch); break;
|
||||
case range6(0x1a, 0x1f): break;
|
||||
case range8(0x20, 0x27): name = "ld"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x28, 0x2f): break;
|
||||
case range8(0x30, 0x37): name = "ex"; rhs.register3(lhs.size(), fetch); break;
|
||||
case 0x38: name = "add"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
|
||||
case 0x39: name = "adc"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
|
||||
case 0x3a: name = "sub"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
|
||||
@@ -384,13 +384,13 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
case 0x3d: name = "xor"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
|
||||
case 0x3e: name = "or"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
|
||||
case 0x3f: name = "cp"; rhs.immediate(lhs.size(), reads(lhs.size())); break;
|
||||
case 0x40 ... 0x47: name = "mul"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x48 ... 0x4f: name = "muls"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x50 ... 0x57: name = "div"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x58 ... 0x5f: name = "divs"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x60 ... 0x67: name = "inc"; rhs.immediate(4, (n3)fetch ? natural((n3)fetch) : 8_n); break;
|
||||
case 0x68 ... 0x6f: name = "dec"; rhs.immediate(4, (n3)fetch ? natural((n3)fetch) : 8_n); break;
|
||||
case 0x70 ... 0x77: break;
|
||||
case range8(0x40, 0x47): name = "mul"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x48, 0x4f): name = "muls"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x50, 0x57): name = "div"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x58, 0x5f): name = "divs"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x60, 0x67): name = "inc"; rhs.immediate(4, (n3)fetch ? natural((n3)fetch) : 8_n); break;
|
||||
case range8(0x68, 0x6f): name = "dec"; rhs.immediate(4, (n3)fetch ? natural((n3)fetch) : 8_n); break;
|
||||
case range8(0x70, 0x77): break;
|
||||
case 0x78: name = "rlc"; break;
|
||||
case 0x79: name = "rrc"; break;
|
||||
case 0x7a: name = "rl"; break;
|
||||
@@ -399,22 +399,22 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
case 0x7d: name = "sra"; break;
|
||||
case 0x7e: name = "sll"; break;
|
||||
case 0x7f: name = "srl"; break;
|
||||
case 0x80 ... 0x87: name = "add"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x88 ... 0x8f: name = "add"; rhs.register3(lhs.size(), fetch); break;
|
||||
case 0x90 ... 0x97: name = "adc"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x98 ... 0x9f: name = "adc"; rhs.register3(lhs.size(), fetch); break;
|
||||
case 0xa0 ... 0xa7: name = "sub"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0xa8 ... 0xaf: name = "sub"; rhs.register3(lhs.size(), fetch); break;
|
||||
case 0xb0 ... 0xb7: name = "sbb"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0xb8 ... 0xbf: name = "sbb"; rhs.register3(lhs.size(), fetch); break;
|
||||
case 0xc0 ... 0xc7: name = "and"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0xc8 ... 0xcf: name = "and"; rhs.register3(lhs.size(), fetch); break;
|
||||
case 0xd0 ... 0xd7: name = "xor"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0xd8 ... 0xdf: name = "xor"; rhs.register3(lhs.size(), fetch); break;
|
||||
case 0xe0 ... 0xe7: name = "or"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0xe8 ... 0xef: name = "or"; rhs.register3(lhs.size(), fetch); break;
|
||||
case 0xf0 ... 0xf7: name = "cp"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0xf8 ... 0xff: name = "cp"; rhs.register3(lhs.size(), fetch); break;
|
||||
case range8(0x80, 0x87): name = "add"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x88, 0x8f): name = "add"; rhs.register3(lhs.size(), fetch); break;
|
||||
case range8(0x90, 0x97): name = "adc"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x98, 0x9f): name = "adc"; rhs.register3(lhs.size(), fetch); break;
|
||||
case range8(0xa0, 0xa7): name = "sub"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0xa8, 0xaf): name = "sub"; rhs.register3(lhs.size(), fetch); break;
|
||||
case range8(0xb0, 0xb7): name = "sbb"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0xb8, 0xbf): name = "sbb"; rhs.register3(lhs.size(), fetch); break;
|
||||
case range8(0xc0, 0xc7): name = "and"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0xc8, 0xcf): name = "and"; rhs.register3(lhs.size(), fetch); break;
|
||||
case range8(0xd0, 0xd7): name = "xor"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0xd8, 0xdf): name = "xor"; rhs.register3(lhs.size(), fetch); break;
|
||||
case range8(0xe0, 0xe7): name = "or"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0xe8, 0xef): name = "or"; rhs.register3(lhs.size(), fetch); break;
|
||||
case range8(0xf0, 0xf7): name = "cp"; rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0xf8, 0xff): name = "cp"; rhs.register3(lhs.size(), fetch); break;
|
||||
}
|
||||
|
||||
//size undefined
|
||||
@@ -427,41 +427,41 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
case 0x04: name = "pop"; lhs.size(8); break;
|
||||
case 0x05: break;
|
||||
case 0x06: name = "pop"; lhs.size(16); break;
|
||||
case 0x07 ... 0x13: break;
|
||||
case range13(0x07, 0x13): break;
|
||||
case 0x14: name = "ld"; lhs.size(8); rhs.indirectImmediate16(16, read16()); break;
|
||||
case 0x15: break;
|
||||
case 0x16: name = "ld"; lhs.size(16); rhs.indirectImmediate16(16, read16()); break;
|
||||
case 0x17 ... 0x1f: break;
|
||||
case 0x20 ... 0x27: name = "lda"; lhs.size(16); rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range9(0x17, 0x1f): break;
|
||||
case range8(0x20, 0x27): name = "lda"; lhs.size(16); rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x28: name = "andcf"; lhs.size(8); rhs.register3(8, A.id); break;
|
||||
case 0x29: name = "orcf"; lhs.size(8); rhs.register3(8, A.id); break;
|
||||
case 0x2a: name = "xorcf"; lhs.size(8); rhs.register3(8, A.id); break;
|
||||
case 0x2b: name = "ldcf"; lhs.size(8); rhs.register3(8, A.id); break;
|
||||
case 0x2c: name = "stcf"; lhs.size(8); rhs.register3(8, A.id); break;
|
||||
case 0x2d ... 0x2f: break;
|
||||
case 0x30 ... 0x37: name = "lda"; lhs.size(32); rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case 0x38 ... 0x3f: break;
|
||||
case 0x40 ... 0x47: name = "ld"; lhs.size(8); rhs.register3(lhs.size(), fetch); break;
|
||||
case 0x48 ... 0x4f: break;
|
||||
case 0x50 ... 0x57: name = "ld"; lhs.size(16); rhs.register3(lhs.size(), fetch); break;
|
||||
case 0x58 ... 0x5f: break;
|
||||
case 0x60 ... 0x67: name = "ld"; lhs.size(32); rhs.register3(lhs.size(), fetch); break;
|
||||
case 0x68 ... 0x6f: break;
|
||||
case 0x70 ... 0x77: break;
|
||||
case 0x78 ... 0x7f: break;
|
||||
case 0x80 ... 0x87: name = "andcf"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case 0x88 ... 0x8f: name = "orcf"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case 0x90 ... 0x97: name = "xorcf"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case 0x98 ... 0x9f: name = "ldcf"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case 0xa0 ... 0xa7: name = "stcf"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case 0xa8 ... 0xaf: name = "tset"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case 0xb0 ... 0xb7: name = "res"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case 0xb8 ... 0xbf: name = "set"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case 0xc0 ... 0xc7: name = "chg"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case 0xc8 ... 0xcf: name = "bit"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case 0xd0 ... 0xdf: name = "jp"; lhs.size(32); rhs = lhs; lhs.condition(fetch); break;
|
||||
case 0xe0 ... 0xef: name = "call"; lhs.size(32); rhs = lhs; lhs.condition(fetch); break;
|
||||
case 0xf0 ... 0xff: name = "ret"; lhs.condition(fetch); break;
|
||||
case range3(0x2d, 0x2f): break;
|
||||
case range8(0x30, 0x37): name = "lda"; lhs.size(32); rhs = lhs; lhs.register3(rhs.size(), fetch); break;
|
||||
case range8(0x38, 0x3f): break;
|
||||
case range8(0x40, 0x47): name = "ld"; lhs.size(8); rhs.register3(lhs.size(), fetch); break;
|
||||
case range8(0x48, 0x4f): break;
|
||||
case range8(0x50, 0x57): name = "ld"; lhs.size(16); rhs.register3(lhs.size(), fetch); break;
|
||||
case range8(0x58, 0x5f): break;
|
||||
case range8(0x60, 0x67): name = "ld"; lhs.size(32); rhs.register3(lhs.size(), fetch); break;
|
||||
case range8(0x68, 0x6f): break;
|
||||
case range8(0x70, 0x77): break;
|
||||
case range8(0x78, 0x7f): break;
|
||||
case range8(0x80, 0x87): name = "andcf"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case range8(0x88, 0x8f): name = "orcf"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case range8(0x90, 0x97): name = "xorcf"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case range8(0x98, 0x9f): name = "ldcf"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case range8(0xa0, 0xa7): name = "stcf"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case range8(0xa8, 0xaf): name = "tset"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case range8(0xb0, 0xb7): name = "res"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case range8(0xb8, 0xbf): name = "set"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case range8(0xc0, 0xc7): name = "chg"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case range8(0xc8, 0xcf): name = "bit"; lhs.size(8); rhs.immediate(3, (n3)fetch); break;
|
||||
case range16(0xd0, 0xdf): name = "jp"; lhs.size(32); rhs = lhs; lhs.condition(fetch); break;
|
||||
case range16(0xe0, 0xef): name = "call"; lhs.size(32); rhs = lhs; lhs.condition(fetch); break;
|
||||
case range16(0xf0, 0xff): name = "ret"; lhs.condition(fetch); break;
|
||||
}
|
||||
|
||||
auto operand = [&](Operand& operand) -> string {
|
||||
|
||||
@@ -187,41 +187,41 @@ auto TLCS900H::instruction() -> void {
|
||||
case 0x1f: {
|
||||
return undefined();
|
||||
}
|
||||
case 0x20 ... 0x27: { //LD R,n
|
||||
case range8(0x20, 0x27): { //LD R,n
|
||||
auto immediate = fetchImmediate<n8>();
|
||||
prefetch(4);
|
||||
return instructionLoad(toRegister3<n8>(data), immediate);
|
||||
}
|
||||
case 0x28 ... 0x2f: { //PUSH.W rr
|
||||
case range8(0x28, 0x2f): { //PUSH.W rr
|
||||
prefetch(4);
|
||||
return instructionPush(toRegister3<n16>(data));
|
||||
}
|
||||
case 0x30 ... 0x37: { //LD RR,nn
|
||||
case range8(0x30, 0x37): { //LD RR,nn
|
||||
auto immediate = fetchImmediate<n16>();
|
||||
prefetch(6);
|
||||
return instructionLoad(toRegister3<n16>(data), immediate);
|
||||
}
|
||||
case 0x38 ... 0x3f: { //PUSH.L XRR
|
||||
case range8(0x38, 0x3f): { //PUSH.L XRR
|
||||
prefetch(6);
|
||||
return instructionPush(toRegister3<n32>(data));
|
||||
}
|
||||
case 0x40 ... 0x47: { //LD XRR,nnnn
|
||||
case range8(0x40, 0x47): { //LD XRR,nnnn
|
||||
auto immediate = fetchImmediate<n32>();
|
||||
prefetch(10);
|
||||
return instructionLoad(toRegister3<n32>(data), immediate);
|
||||
}
|
||||
case 0x48 ... 0x4f: { //POP.W RR
|
||||
case range8(0x48, 0x4f): { //POP.W RR
|
||||
prefetch(6);
|
||||
return instructionPop(toRegister3<n16>(data));
|
||||
}
|
||||
case 0x50 ... 0x57: {
|
||||
case range8(0x50, 0x57): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x58 ... 0x5f: { //POP.L XRR
|
||||
case range8(0x58, 0x5f): { //POP.L XRR
|
||||
prefetch(8);
|
||||
return instructionPop(toRegister3<n32>(data));
|
||||
}
|
||||
case 0x60 ... 0x6f: { //JR cc,PC+d
|
||||
case range16(0x60, 0x6f): { //JR cc,PC+d
|
||||
auto immediate = fetchImmediate<i8>();
|
||||
prefetch(4);
|
||||
if(!condition((n4)data)) return;
|
||||
@@ -229,7 +229,7 @@ auto TLCS900H::instruction() -> void {
|
||||
instructionJumpRelative(immediate);
|
||||
return prefetch(2);
|
||||
}
|
||||
case 0x70 ... 0x7f: { //JRL cc,PC+dd
|
||||
case range16(0x70, 0x7f): { //JRL cc,PC+dd
|
||||
auto immediate = fetchImmediate<i16>();
|
||||
prefetch(4);
|
||||
if(!condition((n4)data)) return;
|
||||
@@ -237,34 +237,34 @@ auto TLCS900H::instruction() -> void {
|
||||
instructionJumpRelative(immediate);
|
||||
return prefetch(2);
|
||||
}
|
||||
case 0x80 ... 0x87: { //src.B
|
||||
case range8(0x80, 0x87): { //src.B
|
||||
return instructionSourceMemory(toMemory<n8>(load(toRegister3<n32>(data))));
|
||||
}
|
||||
case 0x88 ... 0x8f: { //src.B
|
||||
case range8(0x88, 0x8f): { //src.B
|
||||
auto immediate = fetch<i8>();
|
||||
prefetch(2);
|
||||
return instructionSourceMemory(toMemory<n8>(load(toRegister3<n32>(data)) + immediate));
|
||||
}
|
||||
case 0x90 ... 0x97: { //src.W
|
||||
case range8(0x90, 0x97): { //src.W
|
||||
return instructionSourceMemory(toMemory<n16>(load(toRegister3<n32>(data))));
|
||||
}
|
||||
case 0x98 ... 0x9f: { //src.W
|
||||
case range8(0x98, 0x9f): { //src.W
|
||||
auto immediate = fetch<i8>();
|
||||
prefetch(2);
|
||||
return instructionSourceMemory(toMemory<n16>(load(toRegister3<n32>(data)) + immediate));
|
||||
}
|
||||
case 0xa0 ... 0xa7: { //src.L
|
||||
case range8(0xa0, 0xa7): { //src.L
|
||||
return instructionSourceMemory(toMemory<n32>(load(toRegister3<n32>(data))));
|
||||
}
|
||||
case 0xa8 ... 0xaf: { //src.L
|
||||
case range8(0xa8, 0xaf): { //src.L
|
||||
auto immediate = fetch<i8>();
|
||||
prefetch(2);
|
||||
return instructionSourceMemory(toMemory<n32>(load(toRegister3<n32>(data)) + immediate));
|
||||
}
|
||||
case 0xb0 ... 0xb7: { //dst
|
||||
case range8(0xb0, 0xb7): { //dst
|
||||
return instructionTargetMemory(load(toRegister3<n32>(data)));
|
||||
}
|
||||
case 0xb8 ... 0xbf: { //dst
|
||||
case range8(0xb8, 0xbf): { //dst
|
||||
auto immediate = fetch<i8>();
|
||||
prefetch(2);
|
||||
return instructionTargetMemory(load(toRegister3<n32>(data)) + immediate);
|
||||
@@ -342,7 +342,7 @@ auto TLCS900H::instruction() -> void {
|
||||
prefetch(2);
|
||||
return instructionRegister(register);
|
||||
}
|
||||
case 0xc8 ... 0xcf: { //reg.B
|
||||
case range8(0xc8, 0xcf): { //reg.B
|
||||
return instructionRegister(toRegister3<n8>(data));
|
||||
}
|
||||
case 0xd0: { //src.W
|
||||
@@ -418,7 +418,7 @@ auto TLCS900H::instruction() -> void {
|
||||
prefetch(2);
|
||||
return instructionRegister(register);
|
||||
}
|
||||
case 0xd8 ... 0xdf: { //reg.W
|
||||
case range8(0xd8, 0xdf): { //reg.W
|
||||
return instructionRegister(toRegister3<n16>(data));
|
||||
}
|
||||
case 0xe0: { //src.L
|
||||
@@ -494,7 +494,7 @@ auto TLCS900H::instruction() -> void {
|
||||
prefetch(2);
|
||||
return instructionRegister(register);
|
||||
}
|
||||
case 0xe8 ... 0xef: { //reg.L
|
||||
case range8(0xe8, 0xef): { //reg.L
|
||||
return instructionRegister(toRegister3<n32>(data));
|
||||
}
|
||||
case 0xf0: { //dst
|
||||
@@ -542,10 +542,10 @@ auto TLCS900H::instruction() -> void {
|
||||
auto address = load(PC) + immediate; //load PC before final byte fetch
|
||||
data = fetch();
|
||||
switch(data) {
|
||||
case 0x20 ... 0x27:
|
||||
case range8(0x20, 0x27):
|
||||
prefetch(14);
|
||||
return instructionLoad(toRegister3<n16>(data), toImmediate<n16>(address));
|
||||
case 0x30 ... 0x37:
|
||||
case range8(0x30, 0x37):
|
||||
prefetch(14);
|
||||
return instructionLoad(toRegister3<n32>(data), toImmediate<n32>(address));
|
||||
}
|
||||
@@ -587,7 +587,7 @@ auto TLCS900H::instruction() -> void {
|
||||
prefetch(14);
|
||||
return instructionLoad(memory, immediate);
|
||||
}
|
||||
case 0xf8 ... 0xff: { //SWI n
|
||||
case range8(0xf8, 0xff): { //SWI n
|
||||
return instructionSoftwareInterrupt((n3)data);
|
||||
}
|
||||
|
||||
@@ -602,7 +602,7 @@ auto TLCS900H::instructionRegister(R register) -> void {
|
||||
|
||||
switch(data) {
|
||||
|
||||
case 0x00 ... 0x02: {
|
||||
case range3(0x00, 0x02): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x03: { //LD r,#
|
||||
@@ -744,7 +744,7 @@ auto TLCS900H::instructionRegister(R register) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x17 ... 0x18: {
|
||||
case range2(0x17, 0x18): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x19: { //MULA r
|
||||
@@ -754,7 +754,7 @@ auto TLCS900H::instructionRegister(R register) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x1a ... 0x1b: {
|
||||
case range2(0x1a, 0x1b): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x1c: { //DJNZ r,d
|
||||
@@ -765,7 +765,7 @@ auto TLCS900H::instructionRegister(R register) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x1d ... 0x1f: {
|
||||
case range3(0x1d, 0x1f): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x20: { //ANDCF #,r
|
||||
@@ -808,7 +808,7 @@ auto TLCS900H::instructionRegister(R register) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x25 ... 0x27: {
|
||||
case range3(0x25, 0x27): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x28: { //ANDCF A,r
|
||||
@@ -899,7 +899,7 @@ auto TLCS900H::instructionRegister(R register) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x35 ... 0x37: {
|
||||
case range3(0x35, 0x37): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x38: { //MINC1 #,r
|
||||
@@ -956,7 +956,7 @@ auto TLCS900H::instructionRegister(R register) -> void {
|
||||
case 0x3f: {
|
||||
return undefined();
|
||||
}
|
||||
case 0x40 ... 0x47: { //MUL R,r
|
||||
case range8(0x40, 0x47): { //MUL R,r
|
||||
if constexpr(!Long) {
|
||||
if constexpr(Byte) prefetch(22);
|
||||
if constexpr(Word) prefetch(28);
|
||||
@@ -964,7 +964,7 @@ auto TLCS900H::instructionRegister(R register) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x48 ... 0x4f: { //MULS R,r
|
||||
case range8(0x48, 0x4f): { //MULS R,r
|
||||
if constexpr(!Long) {
|
||||
if constexpr(Byte) prefetch(18);
|
||||
if constexpr(Word) prefetch(24);
|
||||
@@ -972,7 +972,7 @@ auto TLCS900H::instructionRegister(R register) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x50 ... 0x57: { //DIV R,r
|
||||
case range8(0x50, 0x57): { //DIV R,r
|
||||
if constexpr(!Long) {
|
||||
if constexpr(Byte) prefetch(30);
|
||||
if constexpr(Word) prefetch(46);
|
||||
@@ -980,7 +980,7 @@ auto TLCS900H::instructionRegister(R register) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x58 ... 0x5f: { //DIVS R,r
|
||||
case range8(0x58, 0x5f): { //DIVS R,r
|
||||
if constexpr(!Long) {
|
||||
if constexpr(Byte) prefetch(36);
|
||||
if constexpr(Word) prefetch(52);
|
||||
@@ -988,57 +988,57 @@ auto TLCS900H::instructionRegister(R register) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x60 ... 0x67: { //INC #3,r
|
||||
case range8(0x60, 0x67): { //INC #3,r
|
||||
prefetch(4);
|
||||
return instructionIncrement(register, toImmediate<T>((n3)data));
|
||||
}
|
||||
case 0x68 ... 0x6f: { //DEC #3,r
|
||||
case range8(0x68, 0x6f): { //DEC #3,r
|
||||
prefetch(4);
|
||||
return instructionDecrement(register, toImmediate<T>((n3)data));
|
||||
}
|
||||
case 0x70 ... 0x7f: { //SCC cc,r
|
||||
case range16(0x70, 0x7f): { //SCC cc,r
|
||||
if constexpr(!Long) {
|
||||
prefetch(4);
|
||||
return instructionSetConditionCode((n4)data, register);
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x80 ... 0x87: { //ADD R,r
|
||||
case range8(0x80, 0x87): { //ADD R,r
|
||||
prefetch(4);
|
||||
return instructionAdd(toRegister3<T>(data), register);
|
||||
}
|
||||
case 0x88 ... 0x8f: { //LD R,r
|
||||
case range8(0x88, 0x8f): { //LD R,r
|
||||
prefetch(4);
|
||||
return instructionLoad(toRegister3<T>(data), register);
|
||||
}
|
||||
case 0x90 ... 0x97: { //ADC R,r
|
||||
case range8(0x90, 0x97): { //ADC R,r
|
||||
prefetch(4);
|
||||
return instructionAddCarry(toRegister3<T>(data), register);
|
||||
}
|
||||
case 0x98 ... 0x9f: { //LD r,R
|
||||
case range8(0x98, 0x9f): { //LD r,R
|
||||
prefetch(4);
|
||||
return instructionLoad(register, toRegister3<T>(data));
|
||||
}
|
||||
case 0xa0 ... 0xa7: { //SUB R,r
|
||||
case range8(0xa0, 0xa7): { //SUB R,r
|
||||
prefetch(4);
|
||||
return instructionSubtract(toRegister3<T>(data), register);
|
||||
}
|
||||
case 0xa8 ... 0xaf: { //LD r,#3
|
||||
case range8(0xa8, 0xaf): { //LD r,#3
|
||||
prefetch(4);
|
||||
return instructionLoad(register, toImmediate<T>((n3)data));
|
||||
}
|
||||
case 0xb0 ... 0xb7: { //SBC R,r
|
||||
case range8(0xb0, 0xb7): { //SBC R,r
|
||||
prefetch(4);
|
||||
return instructionSubtractBorrow(toRegister3<T>(data), register);
|
||||
}
|
||||
case 0xb8 ... 0xbf: { //EX R,r
|
||||
case range8(0xb8, 0xbf): { //EX R,r
|
||||
if constexpr(!Long) {
|
||||
prefetch(6);
|
||||
return instructionExchange(toRegister3<T>(data), register);
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0xc0 ... 0xc7: { //AND R,r
|
||||
case range8(0xc0, 0xc7): { //AND R,r
|
||||
prefetch(4);
|
||||
return instructionAnd(toRegister3<T>(data), register);
|
||||
}
|
||||
@@ -1098,18 +1098,18 @@ auto TLCS900H::instructionRegister(R register) -> void {
|
||||
if constexpr(Long) prefetch(12);
|
||||
return instructionCompare(register, immediate);
|
||||
}
|
||||
case 0xd0 ... 0xd7: { //XOR R,r
|
||||
case range8(0xd0, 0xd7): { //XOR R,r
|
||||
prefetch(4);
|
||||
return instructionXor(toRegister3<T>(data), register);
|
||||
}
|
||||
case 0xd8 ... 0xdf: { //CP r,#3
|
||||
case range8(0xd8, 0xdf): { //CP r,#3
|
||||
if constexpr(!Long) {
|
||||
prefetch(4);
|
||||
return instructionCompare(register, toImmediate<T>((n3)data));
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0xe0 ... 0xe7: { //OR R,r
|
||||
case range8(0xe0, 0xe7): { //OR R,r
|
||||
prefetch(4);
|
||||
return instructionOr(toRegister3<T>(data), register);
|
||||
}
|
||||
@@ -1153,7 +1153,7 @@ auto TLCS900H::instructionRegister(R register) -> void {
|
||||
prefetch(6);
|
||||
return instructionShiftRightLogical(register, immediate);
|
||||
}
|
||||
case 0xf0 ... 0xf7: { //CP R,r
|
||||
case range8(0xf0, 0xf7): { //CP R,r
|
||||
prefetch(4);
|
||||
return instructionCompare(toRegister3<T>(data), register);
|
||||
}
|
||||
@@ -1201,7 +1201,7 @@ auto TLCS900H::instructionSourceMemory(M memory) -> void {
|
||||
|
||||
switch(data) {
|
||||
|
||||
case 0x00 ... 0x03: {
|
||||
case range4(0x00, 0x03): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x04: { //PUSH (mem)
|
||||
@@ -1228,7 +1228,7 @@ auto TLCS900H::instructionSourceMemory(M memory) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x08 ... 0x0f: {
|
||||
case range8(0x08, 0x0f): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x10: { //LDI
|
||||
@@ -1301,19 +1301,19 @@ auto TLCS900H::instructionSourceMemory(M memory) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x1a ... 0x1f: {
|
||||
case range6(0x1a, 0x1f): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x20 ... 0x27: { //LD R,(mem)
|
||||
case range8(0x20, 0x27): { //LD R,(mem)
|
||||
if constexpr(Byte) prefetch(6);
|
||||
if constexpr(Word) prefetch(6);
|
||||
if constexpr(Long) prefetch(8);
|
||||
return instructionLoad(toRegister3<T>(data), memory);
|
||||
}
|
||||
case 0x28 ... 0x2f: {
|
||||
case range8(0x28, 0x2f): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x30 ... 0x37: { //EX (mem),R
|
||||
case range8(0x30, 0x37): { //EX (mem),R
|
||||
if constexpr(!Long) {
|
||||
prefetch(8);
|
||||
return instructionExchange(memory, toRegister3<T>(data));
|
||||
@@ -1391,7 +1391,7 @@ auto TLCS900H::instructionSourceMemory(M memory) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x40 ... 0x47: { //MUL R,(mem)
|
||||
case range8(0x40, 0x47): { //MUL R,(mem)
|
||||
if constexpr(!Long) {
|
||||
if constexpr(Byte) prefetch(24);
|
||||
if constexpr(Word) prefetch(30);
|
||||
@@ -1399,7 +1399,7 @@ auto TLCS900H::instructionSourceMemory(M memory) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x48 ... 0x4f: { //MULS R,(mem)
|
||||
case range8(0x48, 0x4f): { //MULS R,(mem)
|
||||
if constexpr(!Long) {
|
||||
if constexpr(Byte) prefetch(20);
|
||||
if constexpr(Word) prefetch(26);
|
||||
@@ -1407,7 +1407,7 @@ auto TLCS900H::instructionSourceMemory(M memory) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x50 ... 0x57: { //DIV R,(mem)
|
||||
case range8(0x50, 0x57): { //DIV R,(mem)
|
||||
if constexpr(!Long) {
|
||||
if constexpr(Byte) prefetch(30);
|
||||
if constexpr(Word) prefetch(46);
|
||||
@@ -1415,7 +1415,7 @@ auto TLCS900H::instructionSourceMemory(M memory) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x58 ... 0x5f: { //DIVS R,(mem)
|
||||
case range8(0x58, 0x5f): { //DIVS R,(mem)
|
||||
if constexpr(!Long) {
|
||||
if constexpr(Byte) prefetch(36);
|
||||
if constexpr(Word) prefetch(52);
|
||||
@@ -1423,21 +1423,21 @@ auto TLCS900H::instructionSourceMemory(M memory) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x60 ... 0x67: { //INC #3,(mem)
|
||||
case range8(0x60, 0x67): { //INC #3,(mem)
|
||||
if constexpr(!Long) {
|
||||
prefetch(8);
|
||||
return instructionIncrement(memory, toImmediate<T>((n3)data));
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x68 ... 0x6f: { //DEC #3,(mem)
|
||||
case range8(0x68, 0x6f): { //DEC #3,(mem)
|
||||
if constexpr(!Long) {
|
||||
prefetch(8);
|
||||
return instructionDecrement(memory, toImmediate<T>((n3)data));
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x70 ... 0x77: {
|
||||
case range8(0x70, 0x77): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x78: { //RLC (mem)
|
||||
@@ -1496,97 +1496,97 @@ auto TLCS900H::instructionSourceMemory(M memory) -> void {
|
||||
}
|
||||
return undefined();
|
||||
}
|
||||
case 0x80 ... 0x87: { //ADD R,(mem)
|
||||
case range8(0x80, 0x87): { //ADD R,(mem)
|
||||
if constexpr(Byte) prefetch(6);
|
||||
if constexpr(Word) prefetch(6);
|
||||
if constexpr(Long) prefetch(8);
|
||||
return instructionAdd(toRegister3<T>(data), memory);
|
||||
}
|
||||
case 0x88 ... 0x8f: { //ADD (mem),R
|
||||
case range8(0x88, 0x8f): { //ADD (mem),R
|
||||
if constexpr(Byte) prefetch(8);
|
||||
if constexpr(Word) prefetch(8);
|
||||
if constexpr(Long) prefetch(12);
|
||||
return instructionAdd(memory, toRegister3<T>(data));
|
||||
}
|
||||
case 0x90 ... 0x97: { //ADC R,(mem)
|
||||
case range8(0x90, 0x97): { //ADC R,(mem)
|
||||
if constexpr(Byte) prefetch(6);
|
||||
if constexpr(Word) prefetch(6);
|
||||
if constexpr(Long) prefetch(8);
|
||||
return instructionAddCarry(toRegister3<T>(data), memory);
|
||||
}
|
||||
case 0x98 ... 0x9f: { //ADC (mem),R
|
||||
case range8(0x98, 0x9f): { //ADC (mem),R
|
||||
if constexpr(Byte) prefetch(8);
|
||||
if constexpr(Word) prefetch(8);
|
||||
if constexpr(Long) prefetch(12);
|
||||
return instructionAddCarry(memory, toRegister3<T>(data));
|
||||
}
|
||||
case 0xa0 ... 0xa7: { //SUB R,(mem)
|
||||
case range8(0xa0, 0xa7): { //SUB R,(mem)
|
||||
if constexpr(Byte) prefetch(6);
|
||||
if constexpr(Word) prefetch(6);
|
||||
if constexpr(Long) prefetch(8);
|
||||
return instructionSubtract(toRegister3<T>(data), memory);
|
||||
}
|
||||
case 0xa8 ... 0xaf: { //SUB (mem),R
|
||||
case range8(0xa8, 0xaf): { //SUB (mem),R
|
||||
if constexpr(Byte) prefetch(8);
|
||||
if constexpr(Word) prefetch(8);
|
||||
if constexpr(Long) prefetch(12);
|
||||
return instructionSubtract(memory, toRegister3<T>(data));
|
||||
}
|
||||
case 0xb0 ... 0xb7: { //SBC R,(mem)
|
||||
case range8(0xb0, 0xb7): { //SBC R,(mem)
|
||||
if constexpr(Byte) prefetch(6);
|
||||
if constexpr(Word) prefetch(6);
|
||||
if constexpr(Long) prefetch(8);
|
||||
return instructionSubtractBorrow(toRegister3<T>(data), memory);
|
||||
}
|
||||
case 0xb8 ... 0xbf: { //SBC (mem),R
|
||||
case range8(0xb8, 0xbf): { //SBC (mem),R
|
||||
if constexpr(Byte) prefetch(8);
|
||||
if constexpr(Word) prefetch(8);
|
||||
if constexpr(Long) prefetch(12);
|
||||
return instructionSubtractBorrow(memory, toRegister3<T>(data));
|
||||
}
|
||||
case 0xc0 ... 0xc7: { //AND R,(mem)
|
||||
case range8(0xc0, 0xc7): { //AND R,(mem)
|
||||
if constexpr(Byte) prefetch(6);
|
||||
if constexpr(Word) prefetch(6);
|
||||
if constexpr(Long) prefetch(8);
|
||||
return instructionAnd(toRegister3<T>(data), memory);
|
||||
}
|
||||
case 0xc8 ... 0xcf: { //AND (mem),R
|
||||
case range8(0xc8, 0xcf): { //AND (mem),R
|
||||
if constexpr(Byte) prefetch(8);
|
||||
if constexpr(Word) prefetch(8);
|
||||
if constexpr(Long) prefetch(12);
|
||||
return instructionAnd(memory, toRegister3<T>(data));
|
||||
}
|
||||
case 0xd0 ... 0xd7: { //XOR R,(mem)
|
||||
case range8(0xd0, 0xd7): { //XOR R,(mem)
|
||||
if constexpr(Byte) prefetch(6);
|
||||
if constexpr(Word) prefetch(6);
|
||||
if constexpr(Long) prefetch(8);
|
||||
return instructionXor(toRegister3<T>(data), memory);
|
||||
}
|
||||
case 0xd8 ... 0xdf: { //XOR (mem),R
|
||||
case range8(0xd8, 0xdf): { //XOR (mem),R
|
||||
if constexpr(Byte) prefetch(8);
|
||||
if constexpr(Word) prefetch(8);
|
||||
if constexpr(Long) prefetch(12);
|
||||
return instructionXor(memory, toRegister3<T>(data));
|
||||
}
|
||||
case 0xe0 ... 0xe7: { //OR R,(mem)
|
||||
case range8(0xe0, 0xe7): { //OR R,(mem)
|
||||
if constexpr(Byte) prefetch(6);
|
||||
if constexpr(Word) prefetch(6);
|
||||
if constexpr(Long) prefetch(8);
|
||||
return instructionOr(toRegister3<T>(data), memory);
|
||||
}
|
||||
case 0xe8 ... 0xef: { //OR (mem),R
|
||||
case range8(0xe8, 0xef): { //OR (mem),R
|
||||
if constexpr(Byte) prefetch(8);
|
||||
if constexpr(Word) prefetch(8);
|
||||
if constexpr(Long) prefetch(12);
|
||||
return instructionOr(memory, toRegister3<T>(data));
|
||||
}
|
||||
case 0xf0 ... 0xf7: { //CP R,(mem)
|
||||
case range8(0xf0, 0xf7): { //CP R,(mem)
|
||||
if constexpr(Byte) prefetch(6);
|
||||
if constexpr(Word) prefetch(6);
|
||||
if constexpr(Long) prefetch(8);
|
||||
return instructionCompare(toRegister3<T>(data), memory);
|
||||
}
|
||||
case 0xf8 ... 0xff: { //CP (mem),R
|
||||
case range8(0xf8, 0xff): { //CP (mem),R
|
||||
if constexpr(Byte) prefetch(6);
|
||||
if constexpr(Word) prefetch(6);
|
||||
if constexpr(Long) prefetch(8);
|
||||
@@ -1628,7 +1628,7 @@ auto TLCS900H::instructionTargetMemory(n32 address) -> void {
|
||||
prefetch(10);
|
||||
return instructionPop(toMemory<n16>(address));
|
||||
}
|
||||
case 0x07 ... 0x13: {
|
||||
case range13(0x07, 0x13): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x14: { //LD.B (m),(nn)
|
||||
@@ -1644,10 +1644,10 @@ auto TLCS900H::instructionTargetMemory(n32 address) -> void {
|
||||
prefetch(12);
|
||||
return instructionLoad(toMemory<n16>(address), source);
|
||||
}
|
||||
case 0x17 ... 0x1f: {
|
||||
case range9(0x17, 0x1f): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x20 ... 0x27: { //LDA.W R,mem
|
||||
case range8(0x20, 0x27): { //LDA.W R,mem
|
||||
prefetch(8);
|
||||
return instructionLoad(toRegister3<n16>(data), toImmediate<n16>(address));
|
||||
}
|
||||
@@ -1671,91 +1671,91 @@ auto TLCS900H::instructionTargetMemory(n32 address) -> void {
|
||||
prefetch(12);
|
||||
return instructionStoreCarry(toMemory<n8>(address), A);
|
||||
}
|
||||
case 0x2d ... 0x2f: {
|
||||
case range3(0x2d, 0x2f): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x30 ... 0x37: { //LDA.L R,mem
|
||||
case range8(0x30, 0x37): { //LDA.L R,mem
|
||||
prefetch(8);
|
||||
return instructionLoad(toRegister3<n32>(data), toImmediate<n32>(address));
|
||||
}
|
||||
case 0x38 ... 0x3f: {
|
||||
case range8(0x38, 0x3f): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x40 ... 0x47: { //LD.B (mem),R
|
||||
case range8(0x40, 0x47): { //LD.B (mem),R
|
||||
prefetch(6);
|
||||
return instructionLoad(toMemory<n8>(address), toRegister3<n8>(data));
|
||||
}
|
||||
case 0x48 ... 0x4f: {
|
||||
case range8(0x48, 0x4f): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x50 ... 0x57: { //LD.W (mem),R
|
||||
case range8(0x50, 0x57): { //LD.W (mem),R
|
||||
prefetch(6);
|
||||
return instructionLoad(toMemory<n16>(address), toRegister3<n16>(data));
|
||||
}
|
||||
case 0x58 ... 0x5f: {
|
||||
case range8(0x58, 0x5f): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x60 ... 0x67: { //LD.L (mem),R
|
||||
case range8(0x60, 0x67): { //LD.L (mem),R
|
||||
prefetch(8);
|
||||
return instructionLoad(toMemory<n32>(address), toRegister3<n32>(data));
|
||||
}
|
||||
case 0x68 ... 0x7f: {
|
||||
case range24(0x68, 0x7f): {
|
||||
return undefined();
|
||||
}
|
||||
case 0x80 ... 0x87: { //ANDCF #3,(mem)
|
||||
case range8(0x80, 0x87): { //ANDCF #3,(mem)
|
||||
prefetch(10);
|
||||
return instructionAndCarry(toMemory<n8>(address), toImmediate<n3>(data));
|
||||
}
|
||||
case 0x88 ... 0x8f: { //ORCF #3,(mem)
|
||||
case range8(0x88, 0x8f): { //ORCF #3,(mem)
|
||||
prefetch(10);
|
||||
return instructionOrCarry(toMemory<n8>(address), toImmediate<n3>(data));
|
||||
}
|
||||
case 0x90 ... 0x97: { //XORCF #3,(mem)
|
||||
case range8(0x90, 0x97): { //XORCF #3,(mem)
|
||||
prefetch(10);
|
||||
return instructionXorCarry(toMemory<n8>(address), toImmediate<n3>(data));
|
||||
}
|
||||
case 0x98 ... 0x9f: { //LDCF #3,(mem)
|
||||
case range8(0x98, 0x9f): { //LDCF #3,(mem)
|
||||
prefetch(10);
|
||||
return instructionLoadCarry(toMemory<n8>(address), toImmediate<n3>(data));
|
||||
}
|
||||
case 0xa0 ... 0xa7: { //STCF #3,(mem)
|
||||
case range8(0xa0, 0xa7): { //STCF #3,(mem)
|
||||
prefetch(10);
|
||||
return instructionStoreCarry(toMemory<n8>(address), toImmediate<n3>(data));
|
||||
}
|
||||
case 0xa8 ... 0xaf: { //TSET #3,(mem)
|
||||
case range8(0xa8, 0xaf): { //TSET #3,(mem)
|
||||
prefetch(10);
|
||||
return instructionTestSet(toMemory<n8>(address), toImmediate<n3>(data));
|
||||
}
|
||||
case 0xb0 ... 0xb7: { //RES #3,(mem)
|
||||
case range8(0xb0, 0xb7): { //RES #3,(mem)
|
||||
prefetch(10);
|
||||
return instructionReset(toMemory<n8>(address), toImmediate<n3>(data));
|
||||
}
|
||||
case 0xb8 ... 0xbf: { //SET #3,(mem)
|
||||
case range8(0xb8, 0xbf): { //SET #3,(mem)
|
||||
prefetch(10);
|
||||
return instructionSet(toMemory<n8>(address), toImmediate<n3>(data));
|
||||
}
|
||||
case 0xc0 ... 0xc7: { //CHG #3,(mem)
|
||||
case range8(0xc0, 0xc7): { //CHG #3,(mem)
|
||||
prefetch(10);
|
||||
return instructionChange(toMemory<n8>(address), toImmediate<n3>(data));
|
||||
}
|
||||
case 0xc8 ... 0xcf: { //BIT #3,(mem)
|
||||
case range8(0xc8, 0xcf): { //BIT #3,(mem)
|
||||
prefetch(10);
|
||||
return instructionBit(toMemory<n8>(address), toImmediate<n3>(data));
|
||||
}
|
||||
case 0xd0 ... 0xdf: { //JP cc,mem
|
||||
case range16(0xd0, 0xdf): { //JP cc,mem
|
||||
prefetch(8);
|
||||
if(!condition((n4)data)) return;
|
||||
prefetch(4);
|
||||
instructionJump(toImmediate<n32>(address));
|
||||
return prefetch(2);
|
||||
}
|
||||
case 0xe0 ... 0xef: { //CALL cc,mem
|
||||
case range16(0xe0, 0xef): { //CALL cc,mem
|
||||
if(!condition((n4)data)) return prefetch(8);
|
||||
prefetch(20);
|
||||
instructionCall(toImmediate<n32>(address));
|
||||
return prefetch(2);
|
||||
}
|
||||
case 0xf0 ... 0xff: { //RET cc
|
||||
case range16(0xf0, 0xff): { //RET cc
|
||||
if(!condition((n4)data)) return prefetch(8);
|
||||
prefetch(20);
|
||||
instructionReturn();
|
||||
|
||||
@@ -11,18 +11,23 @@ auto V30MZ::parity(u8 value) const -> bool {
|
||||
#define sign (size == Byte ? 0x80 : 0x8000)
|
||||
|
||||
template<u32 size> auto V30MZ::ADC(u16 x, u16 y) -> u16 {
|
||||
return ADD<size>(x, y + PSW.CY);
|
||||
return ADD<size>(x, y, PSW.CY);
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::ADD(u16 x, u16 y) -> u16 {
|
||||
u16 result = (x + y) & mask;
|
||||
PSW.CY = x + y > mask;
|
||||
return ADD<size>(x, y, 0);
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::ADD(u16 x, u16 y, u16 c) -> u16 {
|
||||
u32 result = x + y + c;
|
||||
PSW.CY = result > mask;
|
||||
result &= mask;
|
||||
PSW.P = parity(result);
|
||||
PSW.AC = (u4)x + (u4)y >= 16;
|
||||
PSW.AC = (u4)x + (u4)y + (u4)c >= 16;
|
||||
PSW.Z = result == 0;
|
||||
PSW.S = result & sign;
|
||||
PSW.V = (result ^ x) & (result ^ y) & sign;
|
||||
return result;
|
||||
return result & mask;
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::AND(u16 x, u16 y) -> u16 {
|
||||
@@ -47,20 +52,20 @@ template<u32 size> auto V30MZ::DEC(u16 x) -> u16 {
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::DIVI(s32 x, s32 y) -> u32 {
|
||||
if(y == 0) return interrupt(0), x;
|
||||
if(y == 0) return interrupt(0, InterruptSource::CPU), x;
|
||||
x = size == Byte ? (s16)x : (s32)x;
|
||||
y = size == Byte ? ( s8)y : (s16)y;
|
||||
s32 quotient = x / y;
|
||||
s32 remainder = x % y;
|
||||
if(quotient > mask >> 1 || quotient < -sign) return interrupt(0), x;
|
||||
if(quotient > mask >> 1 || quotient < -sign) return interrupt(0, InterruptSource::CPU), x;
|
||||
return (remainder & mask) << bits | (quotient & mask);
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::DIVU(u32 x, u32 y) -> u32 {
|
||||
if(y == 0) return interrupt(0), x;
|
||||
if(y == 0) return interrupt(0, InterruptSource::CPU), x;
|
||||
u32 quotient = x / y;
|
||||
u32 remainder = x % y;
|
||||
if(quotient > mask) return interrupt(0), x;
|
||||
if(quotient > mask) return interrupt(0, InterruptSource::CPU), x;
|
||||
return (remainder & mask) << bits | (quotient & mask);
|
||||
}
|
||||
|
||||
@@ -77,9 +82,14 @@ template<u32 size> auto V30MZ::INC(u16 x) -> u16 {
|
||||
template<u32 size> auto V30MZ::MULI(s16 x, s16 y) -> u32 {
|
||||
x = size == Byte ? (s8)x : (s16)x;
|
||||
y = size == Byte ? (s8)y : (s16)y;
|
||||
u32 result = x * y;
|
||||
PSW.CY = result >> bits;
|
||||
PSW.V = result >> bits;
|
||||
s32 result = x * y;
|
||||
s32 truncatedResult = size == Byte ? (s8)result : (s16)result;
|
||||
PSW.CY = result != truncatedResult;
|
||||
PSW.V = result != truncatedResult;
|
||||
PSW.P = 0; // undefined
|
||||
PSW.AC = 0; // undefined
|
||||
PSW.Z = 1; // undefined
|
||||
PSW.S = 0; // undefined
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -87,6 +97,10 @@ template<u32 size> auto V30MZ::MULU(u16 x, u16 y) -> u32 {
|
||||
u32 result = x * y;
|
||||
PSW.CY = result >> bits;
|
||||
PSW.V = result >> bits;
|
||||
PSW.P = 0; // undefined
|
||||
PSW.AC = 0; // undefined
|
||||
PSW.Z = 1; // undefined
|
||||
PSW.S = 0; // undefined
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -124,7 +138,7 @@ template<u32 size> auto V30MZ::RCL(u16 x, u5 y) -> u16 {
|
||||
result = (result << 1) | PSW.CY;
|
||||
PSW.CY = carry;
|
||||
}
|
||||
PSW.V = (x ^ result) & sign;
|
||||
PSW.V = (result ^ (PSW.CY << (bits - 1))) & sign;
|
||||
return result & mask;
|
||||
}
|
||||
|
||||
@@ -135,7 +149,7 @@ template<u32 size> auto V30MZ::RCR(u16 x, u5 y) -> u16 {
|
||||
result = (PSW.CY ? sign : 0) | (result >> 1);
|
||||
PSW.CY = carry;
|
||||
}
|
||||
PSW.V = (x ^ result) & sign;
|
||||
PSW.V = (result ^ (result << 1)) & sign;
|
||||
return result & mask;
|
||||
}
|
||||
|
||||
@@ -145,7 +159,7 @@ template<u32 size> auto V30MZ::ROL(u16 x, u5 y) -> u16 {
|
||||
PSW.CY = result & sign;
|
||||
result = (result << 1) | PSW.CY;
|
||||
}
|
||||
PSW.V = (x ^ result) & sign;
|
||||
PSW.V = (result ^ (PSW.CY << (bits - 1))) & sign;
|
||||
return result & mask;
|
||||
}
|
||||
|
||||
@@ -155,20 +169,10 @@ template<u32 size> auto V30MZ::ROR(u16 x, u5 y) -> u16 {
|
||||
PSW.CY = result & 1;
|
||||
result = (PSW.CY ? sign : 0) | (result >> 1);
|
||||
}
|
||||
PSW.V = (x ^ result) & sign;
|
||||
PSW.V = (result ^ (result << 1)) & sign;
|
||||
return result & mask;
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::SAL(u16 x, u5 y) -> u16 {
|
||||
if(y != 0) PSW.CY = (x << y) & (1 << bits);
|
||||
u16 result = (x << y) & mask;
|
||||
PSW.P = parity(result);
|
||||
PSW.Z = result == 0;
|
||||
PSW.S = result & sign;
|
||||
PSW.V = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::SAR(u16 x, u5 y) -> u16 {
|
||||
u16 result = x;
|
||||
for(u32 n = 0; n < y; n++) {
|
||||
@@ -176,42 +180,50 @@ template<u32 size> auto V30MZ::SAR(u16 x, u5 y) -> u16 {
|
||||
result = (result & sign) | (result >> 1);
|
||||
}
|
||||
result &= mask;
|
||||
PSW.AC = 0;
|
||||
PSW.P = parity(result);
|
||||
PSW.Z = result == 0;
|
||||
PSW.S = result & sign;
|
||||
PSW.V = 0;
|
||||
PSW.V = (result ^ (result << 1)) & sign;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::SBB(u16 x, u16 y) -> u16 {
|
||||
return SUB<size>(x, y + PSW.CY);
|
||||
return SUB<size>(x, y, PSW.CY);
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::SHL(u16 x, u5 y) -> u16 {
|
||||
if(y != 0) PSW.CY = (x << y) & (1 << bits);
|
||||
u16 result = (x << y) & mask;
|
||||
PSW.AC = 0;
|
||||
PSW.P = parity(result);
|
||||
PSW.Z = result == 0;
|
||||
PSW.S = result & sign;
|
||||
PSW.V = (x ^ result) & sign;
|
||||
PSW.V = (result ^ (PSW.CY << (bits - 1))) & sign;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::SHR(u16 x, u5 y) -> u16 {
|
||||
if(y != 0) PSW.CY = (x >> (y - 1)) & 1;
|
||||
u16 result = (x >> y) & mask;
|
||||
PSW.AC = 0;
|
||||
PSW.P = parity(result);
|
||||
PSW.Z = result == 0;
|
||||
PSW.S = result & sign;
|
||||
PSW.V = (x ^ result) & sign;
|
||||
PSW.V = (result ^ (result << 1)) & sign;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::SUB(u16 x, u16 y) -> u16 {
|
||||
u16 result = (x - y) & mask;
|
||||
PSW.CY = y > x;
|
||||
return SUB<size>(x, y, 0);
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::SUB(u16 x, u16 y, u16 c) -> u16 {
|
||||
s32 result = ((s32)x - (s32)y - (s32)c);
|
||||
PSW.CY = result < 0;
|
||||
result &= mask;
|
||||
PSW.P = parity(result);
|
||||
PSW.AC = (u4)y > (u4)x;
|
||||
PSW.AC = (((s16)(x & 0xF)) - ((s16)(y & 0xF)) - ((s16)(c & 0xF))) < 0;
|
||||
PSW.Z = result == 0;
|
||||
PSW.S = result & sign;
|
||||
PSW.V = (x ^ y) & (x ^ result) & sign;
|
||||
|
||||
@@ -6,7 +6,7 @@ auto V30MZ::disassembleInstruction(u16 ps, u16 pc) -> string {
|
||||
string output, repeat, prefix;
|
||||
|
||||
auto read = [&](u32 offset) -> n8 {
|
||||
return V30MZ::read<Byte>(ps, pc + offset);
|
||||
return this->read(ps * 16 + ((pc + offset) & 0xFFFF));
|
||||
};
|
||||
|
||||
auto modRM = [&](u32 offset = 1) -> u32 {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
auto V30MZ::interrupt(u8 vector) -> void {
|
||||
wait(32);
|
||||
auto V30MZ::interrupt(u8 vector, InterruptSource source) -> bool {
|
||||
if(source == InterruptSource::INT) wait(32);
|
||||
else if (source == InterruptSource::NMI) wait(26);
|
||||
else wait(25);
|
||||
|
||||
state.halt = 0;
|
||||
state.poll = 1;
|
||||
@@ -8,7 +10,7 @@ auto V30MZ::interrupt(u8 vector) -> void {
|
||||
//if an IRQ fires during a rep string instruction;
|
||||
//flush prefix queue and seek back to first prefix.
|
||||
//this allows the transfer to resume after the IRQ.
|
||||
if(!prefixes.empty()) {
|
||||
if(repeat()) {
|
||||
PC -= prefixes.size();
|
||||
prefixes.flush();
|
||||
}
|
||||
@@ -17,16 +19,28 @@ auto V30MZ::interrupt(u8 vector) -> void {
|
||||
auto ps = read<Word>(0x0000, vector * 4 + 2);
|
||||
|
||||
push(PSW);
|
||||
push(PS);
|
||||
push(PC);
|
||||
|
||||
PSW.MD = 1;
|
||||
PSW.IE = 0;
|
||||
PSW.BRK = 0;
|
||||
|
||||
PC = pc;
|
||||
push(PS);
|
||||
PS = ps;
|
||||
push(PC);
|
||||
PC = pc;
|
||||
|
||||
flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto V30MZ::interrupt(u8 vector) -> bool {
|
||||
state.halt = false;
|
||||
if(!PSW.IE) return false;
|
||||
return interrupt(vector, InterruptSource::INT);
|
||||
}
|
||||
|
||||
auto V30MZ::nonMaskableInterrupt() -> bool {
|
||||
return interrupt(2, InterruptSource::NMI);
|
||||
}
|
||||
|
||||
#define op(id, name, ...) case id: instruction##name(__VA_ARGS__); break;
|
||||
@@ -52,7 +66,7 @@ auto V30MZ::instruction() -> void {
|
||||
op(0x0c, OrAccImm<Byte>)
|
||||
op(0x0d, OrAccImm<Word>)
|
||||
op(0x0e, PushSeg, PS)
|
||||
op(0x0f, PopSeg, PS)
|
||||
op(0x0f, Undefined)
|
||||
op(0x10, AdcMemReg<Byte>)
|
||||
op(0x11, AdcMemReg<Word>)
|
||||
op(0x12, AdcRegMem<Byte>)
|
||||
@@ -156,13 +170,13 @@ auto V30MZ::instruction() -> void {
|
||||
op(0x74, JumpIf, PSW.Z == 1)
|
||||
op(0x75, JumpIf, PSW.Z == 0)
|
||||
op(0x76, JumpIf, PSW.Z == 1 || PSW.CY == 1)
|
||||
op(0x77, JumpIf, PSW.Z != 1 && PSW.CY != 1)
|
||||
op(0x77, JumpIf, PSW.Z == 0 && PSW.CY == 0)
|
||||
op(0x78, JumpIf, PSW.S == 1)
|
||||
op(0x79, JumpIf, PSW.S == 0)
|
||||
op(0x7a, JumpIf, PSW.P == 1)
|
||||
op(0x7b, JumpIf, PSW.P == 0)
|
||||
op(0x7c, JumpIf, PSW.S != PSW.V && PSW.Z == 0)
|
||||
op(0x7d, JumpIf, PSW.S == PSW.V || PSW.Z == 1)
|
||||
op(0x7c, JumpIf, PSW.S != PSW.V)
|
||||
op(0x7d, JumpIf, PSW.S == PSW.V)
|
||||
op(0x7e, JumpIf, PSW.S != PSW.V || PSW.Z == 1)
|
||||
op(0x7f, JumpIf, PSW.S == PSW.V && PSW.Z == 0)
|
||||
op(0x80, Group1MemImm<Byte>, 0)
|
||||
@@ -253,14 +267,14 @@ auto V30MZ::instruction() -> void {
|
||||
op(0xd5, AdjustAfterDivide)
|
||||
op(0xd6, SetALCarry) //salc
|
||||
op(0xd7, Translate) //xlat
|
||||
op(0xd8, Undefined)
|
||||
op(0xd9, Undefined)
|
||||
op(0xda, Undefined)
|
||||
op(0xdb, Undefined)
|
||||
op(0xdc, Undefined)
|
||||
op(0xdd, Undefined)
|
||||
op(0xde, Undefined)
|
||||
op(0xdf, Undefined)
|
||||
op(0xd8, Undefined1)
|
||||
op(0xd9, Undefined1)
|
||||
op(0xda, Undefined1)
|
||||
op(0xdb, Undefined1)
|
||||
op(0xdc, Undefined1)
|
||||
op(0xdd, Undefined1)
|
||||
op(0xde, Undefined1)
|
||||
op(0xdf, Undefined1)
|
||||
op(0xe0, LoopWhile, 0) //loopnz
|
||||
op(0xe1, LoopWhile, 1) //loopz
|
||||
op(0xe2, Loop)
|
||||
@@ -296,6 +310,7 @@ auto V30MZ::instruction() -> void {
|
||||
}
|
||||
|
||||
if(!state.prefix) prefixes.flush();
|
||||
if(PSW.BRK) interrupt(1, InterruptSource::SingleStep);
|
||||
}
|
||||
|
||||
#undef op
|
||||
@@ -1,13 +1,23 @@
|
||||
auto V30MZ::instructionDecimalAdjust(bool negate) -> void {
|
||||
wait(10 + negate);
|
||||
n8 al = AL;
|
||||
PSW.V = 0; // undefined
|
||||
if(PSW.AC || ((al & 0x0f) > 0x09)) {
|
||||
AL += negate ? -0x06 : 0x06;
|
||||
PSW.AC = 1;
|
||||
if(negate) PSW.V |= (al ^ 0x06) & (al ^ AL) & 0x80; // undefined
|
||||
else PSW.V |= (AL ^ al) & (AL ^ 0x06) & 0x80; // undefined
|
||||
} else {
|
||||
PSW.AC = 0;
|
||||
}
|
||||
if(PSW.CY || (al > 0x99)) {
|
||||
al = AL;
|
||||
AL += negate ? -0x60 : 0x60;
|
||||
PSW.CY = 1;
|
||||
if(negate) PSW.V |= (al ^ 0x06) & (al ^ AL) & 0x80; // undefined
|
||||
else PSW.V |= (AL ^ al) & (AL ^ 0x60) & 0x80; // undefined
|
||||
} else {
|
||||
PSW.CY = 0;
|
||||
}
|
||||
PSW.S = AL & 0x80;
|
||||
PSW.Z = AL == 0;
|
||||
@@ -21,17 +31,24 @@ auto V30MZ::instructionAsciiAdjust(bool negate) -> void {
|
||||
AH += negate ? -0x01 : 0x01;
|
||||
PSW.AC = 1;
|
||||
PSW.CY = 1;
|
||||
PSW.S = 0; // undefined
|
||||
PSW.Z = 1; // undefined
|
||||
} else {
|
||||
AL &= 0x0f;
|
||||
PSW.AC = 0;
|
||||
PSW.CY = 0;
|
||||
PSW.S = 1; // undefined
|
||||
PSW.Z = 0; // undefined
|
||||
}
|
||||
AL &= 0x0f;
|
||||
PSW.V = 0; // undefined
|
||||
PSW.P = 1; // undefined
|
||||
}
|
||||
|
||||
auto V30MZ::instructionAdjustAfterMultiply() -> void {
|
||||
wait(16);
|
||||
auto imm = fetch<Byte>();
|
||||
if(imm == 0) return interrupt(0);
|
||||
if(imm == 0) { interrupt(0, InterruptSource::CPU); return; }
|
||||
AH = AL / imm;
|
||||
AL %= imm;
|
||||
PSW.P = parity(AL);
|
||||
@@ -42,9 +59,6 @@ auto V30MZ::instructionAdjustAfterMultiply() -> void {
|
||||
auto V30MZ::instructionAdjustAfterDivide() -> void {
|
||||
wait(6);
|
||||
auto imm = fetch<Byte>();
|
||||
AL += AH * imm;
|
||||
AL = ADD<Byte>(AL, MULU<Byte>(AH, imm) & 0xFF, 0);
|
||||
AH = 0;
|
||||
PSW.P = parity(AL);
|
||||
PSW.S = AW & 0x8000;
|
||||
PSW.Z = AW == 0;
|
||||
}
|
||||
@@ -29,7 +29,7 @@ auto V30MZ::instructionJumpIf(bool condition) -> void {
|
||||
wait(1);
|
||||
auto offset = (i8)fetch<Byte>();
|
||||
if(condition) {
|
||||
wait(3);
|
||||
wait(2);
|
||||
PC += offset;
|
||||
flush();
|
||||
}
|
||||
@@ -51,7 +51,7 @@ auto V30MZ::instructionJumpFar() -> void {
|
||||
}
|
||||
|
||||
auto V30MZ::instructionCallNear() -> void {
|
||||
wait(4);
|
||||
wait(2);
|
||||
auto offset = (i16)fetch<Word>();
|
||||
push(PC);
|
||||
PC += offset;
|
||||
@@ -59,7 +59,7 @@ auto V30MZ::instructionCallNear() -> void {
|
||||
}
|
||||
|
||||
auto V30MZ::instructionCallFar() -> void {
|
||||
wait(9);
|
||||
wait(6);
|
||||
auto pc = fetch<Word>();
|
||||
auto ps = fetch<Word>();
|
||||
push(PS);
|
||||
@@ -70,13 +70,13 @@ auto V30MZ::instructionCallFar() -> void {
|
||||
}
|
||||
|
||||
auto V30MZ::instructionReturn() -> void {
|
||||
wait(5);
|
||||
wait(4);
|
||||
PC = pop();
|
||||
flush();
|
||||
}
|
||||
|
||||
auto V30MZ::instructionReturnImm() -> void {
|
||||
wait(5);
|
||||
wait(4);
|
||||
auto offset = fetch<Word>();
|
||||
PC = pop();
|
||||
SP += offset;
|
||||
@@ -84,14 +84,14 @@ auto V30MZ::instructionReturnImm() -> void {
|
||||
}
|
||||
|
||||
auto V30MZ::instructionReturnFar() -> void {
|
||||
wait(7);
|
||||
wait(5);
|
||||
PC = pop();
|
||||
PS = pop();
|
||||
flush();
|
||||
}
|
||||
|
||||
auto V30MZ::instructionReturnFarImm() -> void {
|
||||
wait(8);
|
||||
wait(6);
|
||||
auto offset = fetch<Word>();
|
||||
PC = pop();
|
||||
PS = pop();
|
||||
@@ -100,7 +100,7 @@ auto V30MZ::instructionReturnFarImm() -> void {
|
||||
}
|
||||
|
||||
auto V30MZ::instructionReturnInt() -> void {
|
||||
wait(9);
|
||||
wait(6);
|
||||
PC = pop();
|
||||
PS = pop();
|
||||
PSW = pop();
|
||||
@@ -109,18 +109,21 @@ auto V30MZ::instructionReturnInt() -> void {
|
||||
}
|
||||
|
||||
auto V30MZ::instructionInt3() -> void {
|
||||
wait(8);
|
||||
interrupt(3);
|
||||
wait(9);
|
||||
interrupt(3, InterruptSource::CPU);
|
||||
}
|
||||
|
||||
auto V30MZ::instructionIntImm() -> void {
|
||||
wait(9);
|
||||
interrupt(fetch<Byte>());
|
||||
wait(10);
|
||||
interrupt(fetch<Byte>(), InterruptSource::CPU);
|
||||
}
|
||||
|
||||
auto V30MZ::instructionInto() -> void {
|
||||
wait(5);
|
||||
interrupt(4);
|
||||
wait(6);
|
||||
if(PSW.V) {
|
||||
wait(7);
|
||||
interrupt(4, InterruptSource::CPU);
|
||||
}
|
||||
}
|
||||
|
||||
auto V30MZ::instructionEnter() -> void {
|
||||
@@ -128,28 +131,32 @@ auto V30MZ::instructionEnter() -> void {
|
||||
auto offset = fetch<Word>();
|
||||
auto length = fetch<Byte>() & 0x1f;
|
||||
push(BP);
|
||||
BP = SP;
|
||||
SP -= offset;
|
||||
u16 bp = SP;
|
||||
|
||||
if(length) {
|
||||
wait(length > 1 ? 7 : 6);
|
||||
for(auto index = 1; index < length; index++) {
|
||||
wait(4);
|
||||
wait(2);
|
||||
auto data = read<Word>(segment(SS), BP - index * 2);
|
||||
push(data);
|
||||
}
|
||||
push(BP);
|
||||
push(bp);
|
||||
}
|
||||
|
||||
BP = bp;
|
||||
SP -= offset;
|
||||
}
|
||||
|
||||
auto V30MZ::instructionLeave() -> void {
|
||||
wait(1);
|
||||
wait(2);
|
||||
SP = BP;
|
||||
BP = pop();
|
||||
}
|
||||
|
||||
auto V30MZ::instructionPushReg(u16& reg) -> void {
|
||||
push(reg);
|
||||
// Emulate the 8086/80186 "PUSH SP" bug.
|
||||
if(® == &SP) push(reg - 2);
|
||||
else push(reg);
|
||||
}
|
||||
|
||||
auto V30MZ::instructionPushSeg(u16& seg) -> void {
|
||||
|
||||
@@ -30,7 +30,7 @@ template<u32 size> auto V30MZ::instructionGroup2MemImm(u8 clocks, maybe<u8> imm)
|
||||
case 3: setMemory<size>(RCR<size>(mem, *imm)); break;
|
||||
case 4: setMemory<size>(SHL<size>(mem, *imm)); break;
|
||||
case 5: setMemory<size>(SHR<size>(mem, *imm)); break;
|
||||
case 6: setMemory<size>(SAL<size>(mem, *imm)); break;
|
||||
case 6: setMemory<size>(0); break; // undefined opcode
|
||||
case 7: setMemory<size>(SAR<size>(mem, *imm)); break;
|
||||
}
|
||||
}
|
||||
@@ -62,13 +62,13 @@ template<u32 size> auto V30MZ::instructionGroup4MemImm() -> void {
|
||||
setMemory<size>(DEC<size>(getMemory<size>()));
|
||||
break;
|
||||
case 2: //CALL
|
||||
wait(5);
|
||||
wait(3);
|
||||
push(PC);
|
||||
PC = getMemory<Word>();
|
||||
flush();
|
||||
break;
|
||||
case 3: //CALLF
|
||||
wait(11);
|
||||
wait(7);
|
||||
push(PS);
|
||||
push(PC);
|
||||
PC = getMemory<Word>(0);
|
||||
@@ -89,8 +89,8 @@ template<u32 size> auto V30MZ::instructionGroup4MemImm() -> void {
|
||||
case 6: //PUSH
|
||||
push(getMemory<Word>());
|
||||
break;
|
||||
case 7: //PUSH (undocumented mirror)
|
||||
push(getMemory<Word>());
|
||||
case 7: //undefined opcode
|
||||
wait(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ auto V30MZ::instructionWait() -> void {
|
||||
}
|
||||
|
||||
auto V30MZ::instructionHalt() -> void {
|
||||
wait(8);
|
||||
wait(9);
|
||||
state.halt = 1;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,11 @@ auto V30MZ::instructionUndefined() -> void {
|
||||
wait(1);
|
||||
}
|
||||
|
||||
auto V30MZ::instructionUndefined1() -> void {
|
||||
wait(1);
|
||||
fetch<Byte>();
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::instructionIn() -> void {
|
||||
wait(6);
|
||||
setAccumulator<size>(in<size>(fetch<Byte>()));
|
||||
@@ -48,12 +53,12 @@ template<u32 size> auto V30MZ::instructionOut() -> void {
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::instructionInDW() -> void {
|
||||
wait(4);
|
||||
wait(5);
|
||||
setAccumulator<size>(in<size>(DW));
|
||||
}
|
||||
|
||||
template<u32 size> auto V30MZ::instructionOutDW() -> void {
|
||||
wait(4);
|
||||
wait(5);
|
||||
out<size>(DW, getAccumulator<size>());
|
||||
}
|
||||
|
||||
@@ -73,5 +78,5 @@ auto V30MZ::instructionBound() -> void {
|
||||
auto lo = getMemory<Word>(0);
|
||||
auto hi = getMemory<Word>(2);
|
||||
auto reg = getRegister<Word>();
|
||||
if(reg < lo || reg > hi) interrupt(5);
|
||||
if(reg < lo || reg > hi) interrupt(5, InterruptSource::CPU);
|
||||
}
|
||||
@@ -1,62 +1,69 @@
|
||||
template<> auto V30MZ::read<Byte>(u16 segment, u16 address) -> u32 {
|
||||
step(1);
|
||||
step(speed(segment * 16 + address));
|
||||
u32 data = 0;
|
||||
data |= read(segment * 16 + address++) << 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
template<> auto V30MZ::read<Word>(u16 segment, u16 address) -> u32 {
|
||||
step(1 + (address & 1));
|
||||
u32 data = 0;
|
||||
step(speed(segment * 16 + address));
|
||||
data |= read(segment * 16 + address++) << 0;
|
||||
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
data |= read(segment * 16 + address++) << 8;
|
||||
return data;
|
||||
}
|
||||
|
||||
template<> auto V30MZ::read<Long>(u16 segment, u16 address) -> u32 {
|
||||
step(2 + (address & 1));
|
||||
u32 data = 0;
|
||||
step(speed(segment * 16 + address));
|
||||
data |= read(segment * 16 + address++) << 0;
|
||||
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
data |= read(segment * 16 + address++) << 8;
|
||||
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
data |= read(segment * 16 + address++) << 16;
|
||||
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
data |= read(segment * 16 + address++) << 24;
|
||||
return data;
|
||||
}
|
||||
|
||||
template<> auto V30MZ::write<Byte>(u16 segment, u16 address, u16 data) -> void {
|
||||
step(1);
|
||||
step(speed(segment * 16 + address));
|
||||
write(segment * 16 + address++, data >> 0);
|
||||
}
|
||||
|
||||
template<> auto V30MZ::write<Word>(u16 segment, u16 address, u16 data) -> void {
|
||||
step(1 + (address & 1));
|
||||
step(speed(segment * 16 + address));
|
||||
write(segment * 16 + address++, data >> 0);
|
||||
if(width(segment * 16 + address) == Byte || !(address & 1)) step(speed(segment * 16 + address));
|
||||
write(segment * 16 + address++, data >> 8);
|
||||
}
|
||||
|
||||
template<> auto V30MZ::in<Byte>(u16 address) -> u16 {
|
||||
step(1);
|
||||
step(ioSpeed(address));
|
||||
u16 data = 0;
|
||||
data |= in(address++) << 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
template<> auto V30MZ::in<Word>(u16 address) -> u16 {
|
||||
step(1 + (address & 1));
|
||||
u16 data = 0;
|
||||
step(ioSpeed(address));
|
||||
data |= in(address++) << 0;
|
||||
if(ioWidth(address) == Byte || !(address & 1)) step(ioSpeed(address));
|
||||
data |= in(address++) << 8;
|
||||
return data;
|
||||
}
|
||||
|
||||
template<> auto V30MZ::out<Byte>(u16 address, u16 data) -> void {
|
||||
step(1);
|
||||
step(ioSpeed(address));
|
||||
out(address++, data >> 0);
|
||||
}
|
||||
|
||||
template<> auto V30MZ::out<Word>(u16 address, u16 data) -> void {
|
||||
step(1 + (address & 1));
|
||||
step(ioSpeed(address));
|
||||
out(address++, data >> 0);
|
||||
if(ioWidth(address) == Byte || !(address & 1)) step(ioSpeed(address));
|
||||
out(address++, data >> 8);
|
||||
}
|
||||
|
||||
|
||||
Loaded 100 of 2066 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user