mirror of
https://github.com/TomHarte/CLK.git
synced 2025-09-12 02:24:31 +00:00
Compare commits
911 Commits
SeparateFe
...
2024-01-22
Author | SHA1 | Date | |
---|---|---|---|
|
31cbcb206f | ||
|
bf9e913a7b | ||
|
d3cea4a10f | ||
|
a6df20ff84 | ||
|
7122a9ee16 | ||
|
9361f29479 | ||
|
981f8a9aac | ||
|
295d5ae0c3 | ||
|
070efd99e0 | ||
|
789c4a080f | ||
|
8eb38ac437 | ||
|
b08c7f8b68 | ||
|
0c2a4f442e | ||
|
18814f7b2c | ||
|
b61317ba7e | ||
|
b5e3858c46 | ||
|
a1df2ef401 | ||
|
2200940efe | ||
|
7e47329e2a | ||
|
54aae60c92 | ||
|
6497cae39d | ||
|
55d7a7e5f6 | ||
|
040f276bf1 | ||
|
119b45001c | ||
|
70a753d8a8 | ||
|
bdfbf779fa | ||
|
1d6537c3a9 | ||
|
e6ee8acfd6 | ||
|
07fe219148 | ||
|
e8917cd294 | ||
|
16add86372 | ||
|
9cc572ee7f | ||
|
c619e353bd | ||
|
ab4ecccf57 | ||
|
3ee81efe40 | ||
|
a402e30edf | ||
|
16baa05aec | ||
|
1ccb2e72fc | ||
|
20c1f4f0e5 | ||
|
e8d43a28f1 | ||
|
c74305be56 | ||
|
fafe43f013 | ||
|
108c584ddb | ||
|
53d6d311a7 | ||
|
a3d37640aa | ||
|
a1f6f2c7f7 | ||
|
d85afc0caf | ||
|
112f3d1e73 | ||
|
dc53d6e6fa | ||
|
30bf187119 | ||
|
baac0768b8 | ||
|
a315deb60c | ||
|
9ea71c97db | ||
|
a047c6493a | ||
|
8c436c2df8 | ||
|
ca30cf6376 | ||
|
980ccbd45b | ||
|
cc635fd3ea | ||
|
fe34083ab8 | ||
|
ea4cc4c9b3 | ||
|
5b5ae69a18 | ||
|
de038fe28f | ||
|
9990725cfb | ||
|
3ab6953246 | ||
|
ad3632cbe4 | ||
|
42731be11f | ||
|
e919386c79 | ||
|
22b4b4d4e3 | ||
|
177e741bca | ||
|
5f00d29297 | ||
|
f1453d9363 | ||
|
8290220657 | ||
|
0cfd29fafc | ||
|
ed91956c30 | ||
|
24d64bf788 | ||
|
386f572e9a | ||
|
dae80217bc | ||
|
bfbaebb661 | ||
|
4affbe7556 | ||
|
c151a73059 | ||
|
ae48f05c80 | ||
|
cd7a152bed | ||
|
9cc37945b7 | ||
|
9bceb59f6c | ||
|
cf00a709ec | ||
|
ad31c50dfd | ||
|
bbaaa520c8 | ||
|
d01c306187 | ||
|
439535e44b | ||
|
2447029631 | ||
|
ce2337f91e | ||
|
db4c802003 | ||
|
e69c777373 | ||
|
2db83acb98 | ||
|
8c0ac6158c | ||
|
34b98be6d4 | ||
|
8a85ef8426 | ||
|
344fec0683 | ||
|
afa2dad2e2 | ||
|
26123bf399 | ||
|
2698ac2d0f | ||
|
b7e1ac840f | ||
|
a58f643b4d | ||
|
4f846ef8d0 | ||
|
7f26cb0ecf | ||
|
a1da6db0ee | ||
|
a7fda83ea5 | ||
|
c1778a8fee | ||
|
7cb40f5d45 | ||
|
0c770c474b | ||
|
134a11b948 | ||
|
09059ab869 | ||
|
051cdc63b8 | ||
|
1b5b3e575c | ||
|
21e6f4d823 | ||
|
2baae216ea | ||
|
ffb992d04a | ||
|
9344f6a824 | ||
|
74bee31a78 | ||
|
96bcbc741f | ||
|
54ddbb6b2b | ||
|
99351ee2de | ||
|
a617f7305a | ||
|
be8994a7b4 | ||
|
574de0dbf3 | ||
|
518cd69d5c | ||
|
3793fbd978 | ||
|
eca0984739 | ||
|
d6073e0407 | ||
|
001b161568 | ||
|
088445575e | ||
|
180ff2f0a7 | ||
|
11c7f2ee6e | ||
|
29ed449373 | ||
|
f83c3e7af0 | ||
|
7b343b25cc | ||
|
497ae935d6 | ||
|
acee513cd3 | ||
|
14e9ae014c | ||
|
16b646ad2e | ||
|
35f000f93c | ||
|
fc9bea5b32 | ||
|
0537e59486 | ||
|
a15d4610f7 | ||
|
f58ab0d00d | ||
|
e0a07ad22f | ||
|
795529ef97 | ||
|
cbd4f7965b | ||
|
bf9743cb38 | ||
|
13631fb7bc | ||
|
3e328bed61 | ||
|
a3e4d38be3 | ||
|
62410a04ef | ||
|
8b4cabd228 | ||
|
54f1c31c94 | ||
|
66bc1fd6fd | ||
|
c07f8d8ef8 | ||
|
e6ed45cb71 | ||
|
dbb14467ec | ||
|
c266639a0c | ||
|
5e87692102 | ||
|
5b43ea82f3 | ||
|
7c28a77b2b | ||
|
85f814c632 | ||
|
213dfe037d | ||
|
6c4905d961 | ||
|
160fec2439 | ||
|
faaa89bb67 | ||
|
3616a943a5 | ||
|
98be6e690f | ||
|
09867e1e6d | ||
|
dc03b2c03b | ||
|
1f2fbccf1f | ||
|
356d8f469a | ||
|
60bd81c4cc | ||
|
38c3d302a3 | ||
|
24c80060c8 | ||
|
d33deb676f | ||
|
bf5ed98f35 | ||
|
abea3d10cc | ||
|
a779b4668e | ||
|
be7e776fa4 | ||
|
08287edd61 | ||
|
5ef6b190af | ||
|
d12316dfcf | ||
|
81ad864659 | ||
|
cd4498a36a | ||
|
d31ecd8986 | ||
|
bb030fc141 | ||
|
778544b36e | ||
|
dc657bdd51 | ||
|
2352b4e6d8 | ||
|
8256500877 | ||
|
def1f90d86 | ||
|
5033d2046a | ||
|
b12ae263a3 | ||
|
a49a3da21c | ||
|
02b2b9d47e | ||
|
2db04eb671 | ||
|
0c98a78bb5 | ||
|
d523242e08 | ||
|
070f8d8bc3 | ||
|
add3257258 | ||
|
bfd6543067 | ||
|
50b4132db7 | ||
|
e79727d30d | ||
|
a40ae08248 | ||
|
05bfab47d2 | ||
|
44a19e8306 | ||
|
ad6fe75296 | ||
|
d658e00f26 | ||
|
7b9cc2332e | ||
|
04d75a3ab8 | ||
|
dae8a5cda2 | ||
|
98e05fb0e3 | ||
|
ada627d027 | ||
|
cbe5e69aa1 | ||
|
39f2c8097e | ||
|
28cb0ad029 | ||
|
371f109a84 | ||
|
3c3cff568a | ||
|
c481577f9f | ||
|
0dfaca2a20 | ||
|
671e59b27a | ||
|
42291cd789 | ||
|
9ef28da322 | ||
|
2a0176dde6 | ||
|
f823310fe3 | ||
|
1c47c64bcb | ||
|
fbf79c142a | ||
|
0bf453199c | ||
|
604fca7fb1 | ||
|
2ee873d1a1 | ||
|
e703721271 | ||
|
664890cead | ||
|
3a103217d1 | ||
|
e55a09962f | ||
|
4bfde2f30f | ||
|
2966a82690 | ||
|
ba91f461fa | ||
|
d8d33ac419 | ||
|
d61d80afec | ||
|
1985d7da6a | ||
|
75b6030516 | ||
|
e691cc8723 | ||
|
ac47b93542 | ||
|
c5745f71f6 | ||
|
48d1fa7eab | ||
|
71069bef0f | ||
|
8608f0f89d | ||
|
d94791a82d | ||
|
017fd84f36 | ||
|
fc63fcb751 | ||
|
eb4a8bfef9 | ||
|
ad78a03300 | ||
|
0f22ee5de4 | ||
|
83cfacc0a5 | ||
|
ef0c3549d2 | ||
|
e3119a4904 | ||
|
5fde3b8d5d | ||
|
d696c15be5 | ||
|
b385e6edad | ||
|
327dc51ece | ||
|
a44ffa769b | ||
|
87438a3019 | ||
|
1f976b8ccb | ||
|
8645a4b7b1 | ||
|
ff000a5792 | ||
|
1c7f8841f4 | ||
|
fd298ffbe9 | ||
|
987a5dbfbf | ||
|
0bb74f405f | ||
|
1aa168054e | ||
|
6d7c6de32f | ||
|
d3021e8c90 | ||
|
6adaf89934 | ||
|
bf49f745bf | ||
|
c5aa3fc75c | ||
|
d88c7ae148 | ||
|
f455f07ba2 | ||
|
561e50a24d | ||
|
2216fc37a1 | ||
|
23bdf0c2a2 | ||
|
24eaf95e9d | ||
|
e43f960934 | ||
|
fdd599e71a | ||
|
84ee683ad3 | ||
|
b2b05b590f | ||
|
c5ec9ae171 | ||
|
5576588c2d | ||
|
084efdeb2d | ||
|
fd8afb6668 | ||
|
dd04909d58 | ||
|
736a3841ce | ||
|
bdf9c4765b | ||
|
6659a1dec5 | ||
|
f97e6095df | ||
|
23c60ae9bc | ||
|
dc7ba11dff | ||
|
6c0a746470 | ||
|
c78a2c71d7 | ||
|
4265455c31 | ||
|
0bcb17985b | ||
|
711575bf69 | ||
|
2ee062ea9e | ||
|
452e32ca4e | ||
|
54f25b3e4e | ||
|
b1b4404b48 | ||
|
d1bfbac59e | ||
|
0a45e26c50 | ||
|
40de0189d9 | ||
|
7ca9a0841f | ||
|
01cf7462d5 | ||
|
2d75fbe1bf | ||
|
e9420fc48d | ||
|
9601c69e12 | ||
|
790e744bde | ||
|
9f65587a1d | ||
|
b0d1dedb65 | ||
|
8103f8e682 | ||
|
3523278057 | ||
|
bf8a4b7efe | ||
|
edef0732ac | ||
|
9cbc991510 | ||
|
d476a17275 | ||
|
896632b9b6 | ||
|
5ccd232541 | ||
|
9b23984d35 | ||
|
b91a791e01 | ||
|
5845ce0a39 | ||
|
646c6b08f7 | ||
|
0d7646d42a | ||
|
ab608178f3 | ||
|
606a04b0b8 | ||
|
15868eea24 | ||
|
881f9bc824 | ||
|
37c46d579d | ||
|
5d8666b837 | ||
|
4957ae7de1 | ||
|
e77f4457c2 | ||
|
c6b989d85b | ||
|
41bd5298b7 | ||
|
596267f62d | ||
|
92913b7455 | ||
|
208846a166 | ||
|
c31ee968df | ||
|
ad9e0b664a | ||
|
ef134e13a1 | ||
|
1b7e109047 | ||
|
6c10611150 | ||
|
0bae14be8f | ||
|
dde58faaf1 | ||
|
aa15fd1fff | ||
|
691d1cce9e | ||
|
7072a1661c | ||
|
ebe1d53220 | ||
|
bc89cb7d06 | ||
|
b16e3de170 | ||
|
5693b3add3 | ||
|
f50c45cc1a | ||
|
33a0c40c02 | ||
|
365b62b4a7 | ||
|
4d1e976b55 | ||
|
4429448815 | ||
|
88e67d9ad6 | ||
|
d101483714 | ||
|
5feac8ef14 | ||
|
a96cb1ebd6 | ||
|
2da2240d34 | ||
|
ef37b09a78 | ||
|
9fc0d411fd | ||
|
0dc44e8efd | ||
|
c076636df1 | ||
|
c397da3e5a | ||
|
5f6bbec741 | ||
|
ec39c4a5f0 | ||
|
edc36bf3f4 | ||
|
f7acecfbff | ||
|
07b32844af | ||
|
7f0bb716f7 | ||
|
f25aaf2bb3 | ||
|
b7d3633b38 | ||
|
05504c8389 | ||
|
ddf38595ff | ||
|
66b95a8b54 | ||
|
ab586b9965 | ||
|
cd2567d868 | ||
|
c07ae43980 | ||
|
4e10ef2816 | ||
|
8d01829fa7 | ||
|
be842ee2f1 | ||
|
e034daa6c8 | ||
|
ce4bcf9064 | ||
|
a992ae37b1 | ||
|
439104c73a | ||
|
fbbe3ab7f1 | ||
|
6e2e67fd46 | ||
|
3293ab48ce | ||
|
2e314e7a08 | ||
|
3827a084ad | ||
|
afc62f3d94 | ||
|
301442a0b1 | ||
|
dd4bcf68bf | ||
|
b860fba0a3 | ||
|
7171e24ccf | ||
|
c19c356c10 | ||
|
8fec9bef11 | ||
|
bffe3ffa25 | ||
|
993366ac5a | ||
|
b07cc5c2ec | ||
|
36a4629ce0 | ||
|
87eec47b79 | ||
|
2a0375e9c2 | ||
|
032eeb4757 | ||
|
5c7f94d2ef | ||
|
291723e85e | ||
|
d2203484cc | ||
|
003c494aac | ||
|
a6a464c240 | ||
|
9bd75464b5 | ||
|
0bb048e24b | ||
|
8c70317d31 | ||
|
dd135bf3fe | ||
|
2efb5236f7 | ||
|
af70c8847d | ||
|
d5c30e3175 | ||
|
ed31cfd80a | ||
|
89423f28ef | ||
|
593c32f621 | ||
|
019d987623 | ||
|
7e8020df59 | ||
|
44d602e0f6 | ||
|
0674da0325 | ||
|
113fc9f757 | ||
|
2c31452629 | ||
|
505df78108 | ||
|
d92d0e87ac | ||
|
df9e9c2c4d | ||
|
b3c07b76ee | ||
|
e0f72f2048 | ||
|
825f3184eb | ||
|
a293a3a816 | ||
|
b22b489380 | ||
|
231de8440e | ||
|
8993a9c4c1 | ||
|
381537fde9 | ||
|
f249e4ada6 | ||
|
12179e486f | ||
|
80b2ccd418 | ||
|
2af6259963 | ||
|
24d0caf8e7 | ||
|
1828a10885 | ||
|
bcd4a2216a | ||
|
3da3401125 | ||
|
972d1d1ddd | ||
|
0344af986c | ||
|
6329a1208a | ||
|
c11d3b61d6 | ||
|
375a9f9ff5 | ||
|
a1e118a1ff | ||
|
83ca9b3af5 | ||
|
acdf32e820 | ||
|
931e6e7a56 | ||
|
058080f6de | ||
|
c4e9f75709 | ||
|
695282b838 | ||
|
f0e2ef5e28 | ||
|
ee6012f6e9 | ||
|
d3e90ce006 | ||
|
18ddc2c83a | ||
|
abf0eead7a | ||
|
a689f2b63e | ||
|
a3066fc040 | ||
|
7eed254de9 | ||
|
55f466f2fa | ||
|
119c83eb18 | ||
|
4e077701c9 | ||
|
a26bfd938e | ||
|
8844812910 | ||
|
a8f1c72f5c | ||
|
05e93f0eb3 | ||
|
af885ccf08 | ||
|
56aa9d101a | ||
|
2b69081fff | ||
|
c0eed0fe78 | ||
|
a91449555f | ||
|
afc0ca3f1b | ||
|
9bc33c716e | ||
|
f0ac62566c | ||
|
3843102609 | ||
|
d202cfc2ca | ||
|
ec2d878e3f | ||
|
626e4fe6b3 | ||
|
ac12b256d4 | ||
|
f4b1279342 | ||
|
a0ca5e6cdc | ||
|
83c8f9996e | ||
|
f2fdfe86ec | ||
|
3f27338b2c | ||
|
fbe02e3ad5 | ||
|
effddcaf65 | ||
|
4b730c26d0 | ||
|
8af173c4bc | ||
|
e1541543c3 | ||
|
33486e69bf | ||
|
1c7bb6d759 | ||
|
99e7de5a8b | ||
|
095359017f | ||
|
25f0a373f3 | ||
|
233ec7b818 | ||
|
832e31f7e5 | ||
|
164a7fe848 | ||
|
62b6219763 | ||
|
2bc9dfbef9 | ||
|
3b84299a05 | ||
|
6f48ffba16 | ||
|
1a3b2b0620 | ||
|
af7069ac21 | ||
|
7323af0b41 | ||
|
1676ed9850 | ||
|
e927fd00d8 | ||
|
70a4d59517 | ||
|
f83d2a8740 | ||
|
a22ac2f88b | ||
|
aafa7de536 | ||
|
2533fd2da9 | ||
|
29169ffed7 | ||
|
d2187a0442 | ||
|
ac826f90c3 | ||
|
6c405680f2 | ||
|
39df6daf3e | ||
|
480b5bef95 | ||
|
6bb85c98ba | ||
|
1552500b10 | ||
|
60cec9fc67 | ||
|
234292f163 | ||
|
03a2d4df80 | ||
|
08d9cc3bd3 | ||
|
47fc276afc | ||
|
3a782faaf3 | ||
|
19a61f867f | ||
|
2551e73be1 | ||
|
7abd4d9b38 | ||
|
e61dc0466f | ||
|
79b126e6bb | ||
|
a230274306 | ||
|
e78e5c8101 | ||
|
ed3922e458 | ||
|
800c76a4fe | ||
|
bf179e8933 | ||
|
9e61d3e8cf | ||
|
5f1ea6c04c | ||
|
8d2a2bcf4a | ||
|
6b666bc92a | ||
|
38933aa079 | ||
|
502b9d2023 | ||
|
da7582d4b5 | ||
|
ec4a60b7da | ||
|
d7bb1a9ee1 | ||
|
9566a8de67 | ||
|
0fee3ff92c | ||
|
b927cf4159 | ||
|
f608153c1a | ||
|
413e7b7de1 | ||
|
91b7d55871 | ||
|
e56e49a318 | ||
|
0262875088 | ||
|
2bed2c2c5c | ||
|
2af774601f | ||
|
797c9fe129 | ||
|
009915f4de | ||
|
f96c33102a | ||
|
5739862dbb | ||
|
7f4fa914dd | ||
|
ebdf10525c | ||
|
83850d7596 | ||
|
18820644b0 | ||
|
770803b073 | ||
|
8d0deeb20e | ||
|
e4fdf09149 | ||
|
acb55aa4e2 | ||
|
bc095bb9ce | ||
|
097b328075 | ||
|
ef83ac855a | ||
|
78df0d19e4 | ||
|
430c60111e | ||
|
2432396eaa | ||
|
da2aea94e3 | ||
|
9538491ee9 | ||
|
8be03be529 | ||
|
724e08d4f1 | ||
|
1d479ec2d7 | ||
|
02af08ffd2 | ||
|
3dc9b625a1 | ||
|
444c5b94b9 | ||
|
1cd1bbd26c | ||
|
8ecc9835f8 | ||
|
39e803aa71 | ||
|
7a886f938a | ||
|
8153cec455 | ||
|
3ee0fcaaeb | ||
|
b98f54545f | ||
|
0a5c24be19 | ||
|
764a7b60f4 | ||
|
b9891d25ee | ||
|
6da0add100 | ||
|
f9d1a4dd8f | ||
|
66cee41b99 | ||
|
a30cad5e8a | ||
|
f9d98ed219 | ||
|
2d70b44303 | ||
|
5b0d2d754f | ||
|
11b032fb06 | ||
|
8e35a56ff7 | ||
|
167b52c4ff | ||
|
dafb134cdc | ||
|
6ac66dad0c | ||
|
de230fb6be | ||
|
29a921f764 | ||
|
a2826cdee5 | ||
|
3b62638b30 | ||
|
0c09c14baa | ||
|
6dd5628dd6 | ||
|
6cecb84878 | ||
|
18ed36d090 | ||
|
c206c7e2cb | ||
|
98730f1f90 | ||
|
c272632b5a | ||
|
577b01e80b | ||
|
239ce75db6 | ||
|
cc9e8117ab | ||
|
26c2a29b99 | ||
|
e36274e5c2 | ||
|
0e027445d4 | ||
|
20d7079006 | ||
|
49d87c9f27 | ||
|
7a4d74b8e4 | ||
|
82c66e7433 | ||
|
569cf8bf34 | ||
|
817a30332c | ||
|
599c123b36 | ||
|
e3cdf113d1 | ||
|
aade91f043 | ||
|
bf6fd8e5e4 | ||
|
c2ebbe5ad9 | ||
|
4efc181f07 | ||
|
93e90b09a0 | ||
|
dab3dcaafb | ||
|
8caad8b99d | ||
|
bee094eba1 | ||
|
bcebb2e520 | ||
|
0f5e0e17a4 | ||
|
49ac2d8e0c | ||
|
a71db54212 | ||
|
efb854ddfa | ||
|
387a952328 | ||
|
f715cd89a9 | ||
|
14851f407c | ||
|
617be7cba7 | ||
|
02cea3047e | ||
|
a8c7871b0c | ||
|
90f49a6e73 | ||
|
47c535414b | ||
|
440f3bdb10 | ||
|
89743f0ba0 | ||
|
f1779e6067 | ||
|
e38fe7dffc | ||
|
f1cba4eb78 | ||
|
f45d8bcbdb | ||
|
6f7991f54a | ||
|
6ec291d96f | ||
|
1a0f848b21 | ||
|
bf832768e6 | ||
|
d35377c776 | ||
|
97d3a9fa78 | ||
|
da029ee344 | ||
|
cf846f501a | ||
|
e948a67814 | ||
|
56e639e09a | ||
|
e75ef70c96 | ||
|
a768b101f8 | ||
|
7159366360 | ||
|
033ba75376 | ||
|
fbd647080d | ||
|
4a803e2d43 | ||
|
7753497a93 | ||
|
a83b43a1ae | ||
|
5125907048 | ||
|
08867f4970 | ||
|
d0a9b5cb81 | ||
|
0ecc319ee6 | ||
|
de95026076 | ||
|
ec982444f7 | ||
|
f083eab011 | ||
|
3d08953103 | ||
|
dbf7d07609 | ||
|
b420d4cbd7 | ||
|
0412890923 | ||
|
5e830781cc | ||
|
ff6573dd02 | ||
|
e46e42d896 | ||
|
1cb26cb141 | ||
|
f74ca8aee1 | ||
|
58aa1da649 | ||
|
67d364cc89 | ||
|
d24fa381c7 | ||
|
fe6e2eb0a1 | ||
|
08aed3bac5 | ||
|
6bbd896c34 | ||
|
0bf2099a70 | ||
|
1b9e6e8c8e | ||
|
59521f9d38 | ||
|
769aed10ea | ||
|
5a77f0c93c | ||
|
4f14210ee0 | ||
|
f618ca6046 | ||
|
0a0d53103d | ||
|
e3b18708c7 | ||
|
bd0b62232f | ||
|
dbfaef632a | ||
|
6808f2c778 | ||
|
0d2af80f7f | ||
|
6f768d9a3d | ||
|
dd3cc1f510 | ||
|
a4b1d2b00a | ||
|
a5523c9feb | ||
|
6e465b9159 | ||
|
90f0e937e8 | ||
|
6d1dd218d4 | ||
|
6abc3b6cd7 | ||
|
7d093d71b3 | ||
|
ade5828035 | ||
|
5c62606154 | ||
|
16bf7c6f26 | ||
|
cf4603cb33 | ||
|
b6d000ac5e | ||
|
82f0cd790f | ||
|
2d17d9d316 | ||
|
a0ca0bb3c0 | ||
|
c6b311b84a | ||
|
28c7d27cac | ||
|
90a8999b4b | ||
|
6d392852d2 | ||
|
f7d9116811 | ||
|
451c687441 | ||
|
f411a961a3 | ||
|
ada411c0d8 | ||
|
eb100e3b29 | ||
|
15acb1fc7c | ||
|
09b2cfad8a | ||
|
059f300500 | ||
|
524e4ae65c | ||
|
488fceb42b | ||
|
01851874ea | ||
|
7f6e3cf8b7 | ||
|
2d20175472 | ||
|
6597283c34 | ||
|
f6fd49d950 | ||
|
40af162214 | ||
|
92c46faf84 | ||
|
ff9237be9f | ||
|
6cbb434482 | ||
|
9fe6e354a6 | ||
|
103f42f0b0 | ||
|
f2732962d0 | ||
|
ef5ee8cf94 | ||
|
1a6c8a2aed | ||
|
b76899f2bc | ||
|
a24e17c320 | ||
|
245919e67d | ||
|
6936cf1819 | ||
|
ae4a588de3 | ||
|
960cca163e | ||
|
86f12f4d4c | ||
|
249da884a7 | ||
|
035a1265f6 | ||
|
ff4d79e77e | ||
|
95df9bcb1e | ||
|
78cb39ad67 | ||
|
9207de4164 | ||
|
c20e7ed9b6 | ||
|
11c747e3c4 | ||
|
02aeec0173 | ||
|
2d882d2153 | ||
|
638f3d3a53 | ||
|
b59eae3676 | ||
|
2cc60563e0 | ||
|
5368f789f6 | ||
|
b03b408984 | ||
|
cd072e1b57 | ||
|
f16ac603f2 | ||
|
0a0051eb59 | ||
|
92c8e1ca93 | ||
|
4a38e6b4b5 | ||
|
e56a5899bd | ||
|
87097c44b9 | ||
|
7fadf01e4e | ||
|
0d65bf0c1f | ||
|
d2b9c435e5 | ||
|
d36f785428 | ||
|
5fd98e9833 | ||
|
787e9e770e | ||
|
c8c0c3ca6d | ||
|
5a5f71e703 | ||
|
587ec81900 | ||
|
9f63db991c | ||
|
13f49fe8bf | ||
|
5f95b32799 | ||
|
873b1122ab | ||
|
4c32fc9b11 | ||
|
4a87aa06a5 | ||
|
5731ab75a6 | ||
|
797ce89a26 | ||
|
4dc7b1840c | ||
|
3db21b0bed | ||
|
74b5ad93c4 | ||
|
7ebecd2f41 | ||
|
406c838c39 | ||
|
6f5fcf23dc | ||
|
02fcaf0dbd | ||
|
a7cf7d3183 | ||
|
9b3199d97b | ||
|
e5dfc882cb | ||
|
2d928199d6 | ||
|
3582d2c9e3 | ||
|
da953fdf0d | ||
|
710017ada2 | ||
|
9d9194f194 | ||
|
f8dc5b8ebc | ||
|
2ee028d74f | ||
|
2d2eda6f8c | ||
|
5b9c5e250d | ||
|
c95df3cb6c | ||
|
f039d44ee3 | ||
|
7ee5adc481 | ||
|
8d4393275c | ||
|
9417996280 | ||
|
804f80d23c | ||
|
28027385bc | ||
|
53d8322b46 | ||
|
39840feae7 | ||
|
57087cc7c6 | ||
|
a6abe3a51d | ||
|
aa127d9a9f | ||
|
49ec6d19a5 | ||
|
8efb6a9226 | ||
|
211a6e5114 | ||
|
e42a2578da | ||
|
34c631ed3b | ||
|
2f5d710441 | ||
|
b7a27fbc6b | ||
|
e98f78316b | ||
|
8c3ebe23f6 | ||
|
251b8e69ad | ||
|
a21fe92b7a | ||
|
3d5a351306 | ||
|
43dfb729d3 | ||
|
ecec9ff6dc | ||
|
543be49cf8 | ||
|
4f0adc4d5d | ||
|
1fb278c9f1 | ||
|
19ec63b4fb | ||
|
4d6ffa7a2e | ||
|
39ee75d94a | ||
|
13be247495 | ||
|
cdcac7c11c | ||
|
67cd5dd63b | ||
|
139a1a2acc | ||
|
7b569b1a6c | ||
|
3e666a08ae | ||
|
74b416f985 | ||
|
c160482b0a | ||
|
ec8f1b0fe0 | ||
|
5dae726857 | ||
|
598a889c6d | ||
|
e5d3140cd1 | ||
|
525e5ce8b0 | ||
|
79e9de34b6 | ||
|
0a547355db | ||
|
2b58f64161 | ||
|
6cbd152ff5 | ||
|
a5038259bc | ||
|
bb84a5a474 | ||
|
b5dc84c431 | ||
|
357a324e87 | ||
|
fa82fb46b9 | ||
|
b8e7c2b8ac | ||
|
3e2a82b638 | ||
|
1125286b96 | ||
|
17f1f05064 | ||
|
ae56da2b0d | ||
|
90f16026bc | ||
|
d0284917cf | ||
|
7815d18676 | ||
|
222f6e92fb | ||
|
b34403164e | ||
|
3bd931937f | ||
|
d207c13b6b | ||
|
ca75822dbe | ||
|
d9df568dab | ||
|
26343148ae | ||
|
fd0fe66851 | ||
|
c41ed191dc | ||
|
833613b68a | ||
|
0a336baae2 | ||
|
b9bd3f9b8c | ||
|
42024c1573 | ||
|
0222dcf5ce | ||
|
54103f1f34 | ||
|
c0eb401d04 | ||
|
ad5047dbd5 |
51
.github/workflows/build.yml
vendored
Normal file
51
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
name: Build
|
||||||
|
on: [pull_request]
|
||||||
|
jobs:
|
||||||
|
build-mac:
|
||||||
|
name: Mac UI on ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Make
|
||||||
|
working-directory: OSBindings/Mac
|
||||||
|
run: xcodebuild CODE_SIGN_IDENTITY=-
|
||||||
|
build-sdl:
|
||||||
|
name: SDL UI on ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install dependencies
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
case $RUNNER_OS in
|
||||||
|
Linux)
|
||||||
|
sudo apt-get --allow-releaseinfo-change update
|
||||||
|
sudo apt-get --fix-missing install gcc-10 libsdl2-dev scons
|
||||||
|
;;
|
||||||
|
macOS)
|
||||||
|
brew install scons sdl2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
- name: Make
|
||||||
|
working-directory: OSBindings/SDL
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
case $RUNNER_OS in
|
||||||
|
Linux)
|
||||||
|
jobs=$(nproc --all)
|
||||||
|
;;
|
||||||
|
macOS)
|
||||||
|
jobs=$(sysctl -n hw.activecpu)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
jobs=1
|
||||||
|
esac
|
||||||
|
scons -j"$jobs"
|
16
.github/workflows/ccpp.yml
vendored
16
.github/workflows/ccpp.yml
vendored
@@ -1,16 +0,0 @@
|
|||||||
name: SDL/Ubuntu
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install dependencies
|
|
||||||
run: sudo apt-get --allow-releaseinfo-change update && sudo apt-get --fix-missing install libsdl2-dev scons
|
|
||||||
- name: Make
|
|
||||||
working-directory: OSBindings/SDL
|
|
||||||
run: scons -j$(nproc --all)
|
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ActivityObserver_h
|
#pragma once
|
||||||
#define ActivityObserver_h
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -24,6 +23,8 @@ namespace Activity {
|
|||||||
*/
|
*/
|
||||||
class Observer {
|
class Observer {
|
||||||
public:
|
public:
|
||||||
|
virtual ~Observer() {}
|
||||||
|
|
||||||
/// Provides hints as to the sort of information presented on an LED.
|
/// Provides hints as to the sort of information presented on an LED.
|
||||||
enum LEDPresentation: uint8_t {
|
enum LEDPresentation: uint8_t {
|
||||||
/// This LED informs the user of some sort of persistent state, e.g. scroll lock.
|
/// This LED informs the user of some sort of persistent state, e.g. scroll lock.
|
||||||
@@ -56,5 +57,3 @@ class Observer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* ActivityObserver_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ActivitySource_h
|
#pragma once
|
||||||
#define ActivitySource_h
|
|
||||||
|
|
||||||
#include "Observer.hpp"
|
#include "Observer.hpp"
|
||||||
|
|
||||||
@@ -19,6 +18,3 @@ class Source {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif /* ActivitySource_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ConfidenceCounter_hpp
|
#pragma once
|
||||||
#define ConfidenceCounter_hpp
|
|
||||||
|
|
||||||
#include "ConfidenceSource.hpp"
|
#include "ConfidenceSource.hpp"
|
||||||
|
|
||||||
@@ -41,5 +40,3 @@ class ConfidenceCounter: public ConfidenceSource {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* ConfidenceCounter_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ConfidenceSource_hpp
|
#pragma once
|
||||||
#define ConfidenceSource_hpp
|
|
||||||
|
|
||||||
namespace Analyser::Dynamic {
|
namespace Analyser::Dynamic {
|
||||||
|
|
||||||
@@ -22,5 +21,3 @@ struct ConfidenceSource {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* ConfidenceSource_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ConfidenceSummary_hpp
|
#pragma once
|
||||||
#define ConfidenceSummary_hpp
|
|
||||||
|
|
||||||
#include "ConfidenceSource.hpp"
|
#include "ConfidenceSource.hpp"
|
||||||
|
|
||||||
@@ -40,5 +39,3 @@ class ConfidenceSummary: public ConfidenceSource {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* ConfidenceSummary_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef MultiConfigurable_hpp
|
#pragma once
|
||||||
#define MultiConfigurable_hpp
|
|
||||||
|
|
||||||
#include "../../../../Machines/DynamicMachine.hpp"
|
#include "../../../../Machines/DynamicMachine.hpp"
|
||||||
#include "../../../../Configurable/Configurable.hpp"
|
#include "../../../../Configurable/Configurable.hpp"
|
||||||
@@ -36,5 +35,3 @@ class MultiConfigurable: public Configurable::Device {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* MultiConfigurable_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef MultiJoystickMachine_hpp
|
#pragma once
|
||||||
#define MultiJoystickMachine_hpp
|
|
||||||
|
|
||||||
#include "../../../../Machines/DynamicMachine.hpp"
|
#include "../../../../Machines/DynamicMachine.hpp"
|
||||||
|
|
||||||
@@ -34,5 +33,3 @@ class MultiJoystickMachine: public MachineTypes::JoystickMachine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* MultiJoystickMachine_hpp */
|
|
||||||
|
@@ -56,10 +56,10 @@ MultiKeyboardMachine::MultiKeyboard::MultiKeyboard(const std::vector<::MachineTy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MultiKeyboardMachine::MultiKeyboard::set_key_pressed(Key key, char value, bool is_pressed) {
|
bool MultiKeyboardMachine::MultiKeyboard::set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat) {
|
||||||
bool was_consumed = false;
|
bool was_consumed = false;
|
||||||
for(const auto &machine: machines_) {
|
for(const auto &machine: machines_) {
|
||||||
was_consumed |= machine->get_keyboard().set_key_pressed(key, value, is_pressed);
|
was_consumed |= machine->get_keyboard().set_key_pressed(key, value, is_pressed, is_repeat);
|
||||||
}
|
}
|
||||||
return was_consumed;
|
return was_consumed;
|
||||||
}
|
}
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef MultiKeyboardMachine_hpp
|
#pragma once
|
||||||
#define MultiKeyboardMachine_hpp
|
|
||||||
|
|
||||||
#include "../../../../Machines/DynamicMachine.hpp"
|
#include "../../../../Machines/DynamicMachine.hpp"
|
||||||
#include "../../../../Machines/KeyboardMachine.hpp"
|
#include "../../../../Machines/KeyboardMachine.hpp"
|
||||||
@@ -31,7 +30,7 @@ class MultiKeyboardMachine: public MachineTypes::KeyboardMachine {
|
|||||||
public:
|
public:
|
||||||
MultiKeyboard(const std::vector<MachineTypes::KeyboardMachine *> &machines);
|
MultiKeyboard(const std::vector<MachineTypes::KeyboardMachine *> &machines);
|
||||||
|
|
||||||
bool set_key_pressed(Key key, char value, bool is_pressed) final;
|
bool set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat) final;
|
||||||
void reset_all_keys() final;
|
void reset_all_keys() final;
|
||||||
const std::set<Key> &observed_keys() const final;
|
const std::set<Key> &observed_keys() const final;
|
||||||
bool is_exclusive() const final;
|
bool is_exclusive() const final;
|
||||||
@@ -55,5 +54,3 @@ class MultiKeyboardMachine: public MachineTypes::KeyboardMachine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* MultiKeyboardMachine_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef MultiMediaTarget_hpp
|
#pragma once
|
||||||
#define MultiMediaTarget_hpp
|
|
||||||
|
|
||||||
#include "../../../../Machines/MediaTarget.hpp"
|
#include "../../../../Machines/MediaTarget.hpp"
|
||||||
#include "../../../../Machines/DynamicMachine.hpp"
|
#include "../../../../Machines/DynamicMachine.hpp"
|
||||||
@@ -35,5 +34,3 @@ struct MultiMediaTarget: public MachineTypes::MediaTarget {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* MultiMediaTarget_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef MultiProducer_hpp
|
#pragma once
|
||||||
#define MultiProducer_hpp
|
|
||||||
|
|
||||||
#include "../../../../Concurrency/AsyncTaskQueue.hpp"
|
#include "../../../../Concurrency/AsyncTaskQueue.hpp"
|
||||||
#include "../../../../Machines/MachineTypes.hpp"
|
#include "../../../../Machines/MachineTypes.hpp"
|
||||||
@@ -115,5 +114,3 @@ class MultiAudioProducer: public MultiInterface<MachineTypes::AudioProducer>, pu
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* MultiProducer_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef MultiSpeaker_hpp
|
#pragma once
|
||||||
#define MultiSpeaker_hpp
|
|
||||||
|
|
||||||
#include "../../../../Machines/DynamicMachine.hpp"
|
#include "../../../../Machines/DynamicMachine.hpp"
|
||||||
#include "../../../../Outputs/Speaker/Speaker.hpp"
|
#include "../../../../Outputs/Speaker/Speaker.hpp"
|
||||||
@@ -55,5 +54,3 @@ class MultiSpeaker: public Outputs::Speaker::Speaker, Outputs::Speaker::Speaker:
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* MultiSpeaker_hpp */
|
|
||||||
|
@@ -11,6 +11,12 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
Log::Logger<Log::Source::MultiMachine> logger;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
using namespace Analyser::Dynamic;
|
using namespace Analyser::Dynamic;
|
||||||
|
|
||||||
MultiMachine::MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machines) :
|
MultiMachine::MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machines) :
|
||||||
@@ -61,13 +67,14 @@ bool MultiMachine::would_collapse(const std::vector<std::unique_ptr<DynamicMachi
|
|||||||
|
|
||||||
void MultiMachine::did_run_machines(MultiTimedMachine *) {
|
void MultiMachine::did_run_machines(MultiTimedMachine *) {
|
||||||
std::lock_guard machines_lock(machines_mutex_);
|
std::lock_guard machines_lock(machines_mutex_);
|
||||||
#ifndef NDEBUG
|
|
||||||
|
if constexpr (logger.enabled) {
|
||||||
|
auto line = logger.info();
|
||||||
for(const auto &machine: machines_) {
|
for(const auto &machine: machines_) {
|
||||||
auto timed_machine = machine->timed_machine();
|
auto timed_machine = machine->timed_machine();
|
||||||
LOGNBR(PADHEX(2) << timed_machine->get_confidence() << " " << timed_machine->debug_type() << "; ");
|
line.append("%0.4f %s; ", timed_machine->get_confidence(), timed_machine->debug_type().c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
LOGNBR(std::endl);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
DynamicMachine *front = machines_.front().get();
|
DynamicMachine *front = machines_.front().get();
|
||||||
std::stable_sort(machines_.begin(), machines_.end(),
|
std::stable_sort(machines_.begin(), machines_.end(),
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef MultiMachine_hpp
|
#pragma once
|
||||||
#define MultiMachine_hpp
|
|
||||||
|
|
||||||
#include "../../../Machines/DynamicMachine.hpp"
|
#include "../../../Machines/DynamicMachine.hpp"
|
||||||
|
|
||||||
@@ -80,5 +79,3 @@ class MultiMachine: public ::Machine::DynamicMachine, public MultiTimedMachine::
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* MultiMachine_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Machines_h
|
#pragma once
|
||||||
#define Machines_h
|
|
||||||
|
|
||||||
namespace Analyser {
|
namespace Analyser {
|
||||||
|
|
||||||
@@ -25,11 +24,10 @@ enum class Machine {
|
|||||||
MasterSystem,
|
MasterSystem,
|
||||||
MSX,
|
MSX,
|
||||||
Oric,
|
Oric,
|
||||||
|
PCCompatible,
|
||||||
Vic20,
|
Vic20,
|
||||||
ZX8081,
|
ZX8081,
|
||||||
ZXSpectrum,
|
ZXSpectrum,
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Machines_h */
|
|
||||||
|
@@ -19,10 +19,10 @@ using namespace Analyser::Static::Acorn;
|
|||||||
std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetDFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetDFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||||
// c.f. http://beebwiki.mdfs.net/Acorn_DFS_disc_format
|
// c.f. http://beebwiki.mdfs.net/Acorn_DFS_disc_format
|
||||||
auto catalogue = std::make_unique<Catalogue>();
|
auto catalogue = std::make_unique<Catalogue>();
|
||||||
Storage::Encodings::MFM::Parser parser(false, disk);
|
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Single, disk);
|
||||||
|
|
||||||
const Storage::Encodings::MFM::Sector *const names = parser.get_sector(0, 0, 0);
|
const Storage::Encodings::MFM::Sector *const names = parser.sector(0, 0, 0);
|
||||||
const Storage::Encodings::MFM::Sector *const details = parser.get_sector(0, 0, 1);
|
const Storage::Encodings::MFM::Sector *const details = parser.sector(0, 0, 1);
|
||||||
|
|
||||||
if(!names || !details) return nullptr;
|
if(!names || !details) return nullptr;
|
||||||
if(names->samples.empty() || details->samples.empty()) return nullptr;
|
if(names->samples.empty() || details->samples.empty()) return nullptr;
|
||||||
@@ -65,7 +65,7 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetDFSCatalogue(const std::s
|
|||||||
uint8_t track = uint8_t(start_sector / 10);
|
uint8_t track = uint8_t(start_sector / 10);
|
||||||
start_sector++;
|
start_sector++;
|
||||||
|
|
||||||
Storage::Encodings::MFM::Sector *next_sector = parser.get_sector(0, track, sector);
|
const Storage::Encodings::MFM::Sector *next_sector = parser.sector(0, track, sector);
|
||||||
if(!next_sector) break;
|
if(!next_sector) break;
|
||||||
|
|
||||||
long length_from_sector = std::min(data_length, 256l);
|
long length_from_sector = std::min(data_length, 256l);
|
||||||
@@ -84,15 +84,15 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetDFSCatalogue(const std::s
|
|||||||
*/
|
*/
|
||||||
std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetADFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetADFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||||
auto catalogue = std::make_unique<Catalogue>();
|
auto catalogue = std::make_unique<Catalogue>();
|
||||||
Storage::Encodings::MFM::Parser parser(true, disk);
|
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk);
|
||||||
|
|
||||||
Storage::Encodings::MFM::Sector *free_space_map_second_half = parser.get_sector(0, 0, 1);
|
const Storage::Encodings::MFM::Sector *free_space_map_second_half = parser.sector(0, 0, 1);
|
||||||
if(!free_space_map_second_half) return nullptr;
|
if(!free_space_map_second_half) return nullptr;
|
||||||
|
|
||||||
std::vector<uint8_t> root_directory;
|
std::vector<uint8_t> root_directory;
|
||||||
root_directory.reserve(5 * 256);
|
root_directory.reserve(5 * 256);
|
||||||
for(uint8_t c = 2; c < 7; c++) {
|
for(uint8_t c = 2; c < 7; c++) {
|
||||||
const Storage::Encodings::MFM::Sector *const sector = parser.get_sector(0, 0, c);
|
const Storage::Encodings::MFM::Sector *const sector = parser.sector(0, 0, c);
|
||||||
if(!sector) return nullptr;
|
if(!sector) return nullptr;
|
||||||
root_directory.insert(root_directory.end(), sector->samples[0].begin(), sector->samples[0].end());
|
root_directory.insert(root_directory.end(), sector->samples[0].begin(), sector->samples[0].end());
|
||||||
}
|
}
|
||||||
@@ -166,7 +166,7 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetADFSCatalogue(const std::
|
|||||||
|
|
||||||
new_file.data.reserve(size);
|
new_file.data.reserve(size);
|
||||||
while(new_file.data.size() < size) {
|
while(new_file.data.size() < size) {
|
||||||
const Storage::Encodings::MFM::Sector *const sector = parser.get_sector(start_sector / (80 * 16), (start_sector / 16) % 80, start_sector % 16);
|
const Storage::Encodings::MFM::Sector *const sector = parser.sector(start_sector / (80 * 16), (start_sector / 16) % 80, start_sector % 16);
|
||||||
if(!sector) break;
|
if(!sector) break;
|
||||||
|
|
||||||
const auto length_from_sector = std::min(size - new_file.data.size(), sector->samples[0].size());
|
const auto length_from_sector = std::min(size - new_file.data.size(), sector->samples[0].size());
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Acorn_Disk_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Acorn_Disk_hpp
|
|
||||||
|
|
||||||
#include "File.hpp"
|
#include "File.hpp"
|
||||||
#include "../../../Storage/Disk/Disk.hpp"
|
#include "../../../Storage/Disk/Disk.hpp"
|
||||||
@@ -30,5 +29,3 @@ std::unique_ptr<Catalogue> GetDFSCatalogue(const std::shared_ptr<Storage::Disk::
|
|||||||
std::unique_ptr<Catalogue> GetADFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk);
|
std::unique_ptr<Catalogue> GetADFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Disk_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Acorn_File_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Acorn_File_hpp
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -59,5 +58,3 @@ struct File {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* File_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Acorn_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Acorn_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::Acorn {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* AcornAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Acorn_Tape_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Acorn_Tape_hpp
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@@ -19,5 +18,3 @@ namespace Analyser::Static::Acorn {
|
|||||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Tape_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_Acorn_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_Acorn_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
@@ -36,5 +35,3 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_Acorn_Target_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_Amiga_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define Analyser_Static_Amiga_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::Amiga {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_Amiga_StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_Amiga_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_Amiga_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
@@ -40,5 +39,3 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_Amiga_Target_h */
|
|
||||||
|
@@ -159,8 +159,8 @@ void InspectCatalogue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &disk, const std::unique_ptr<Analyser::Static::AmstradCPC::Target> &target) {
|
bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &disk, const std::unique_ptr<Analyser::Static::AmstradCPC::Target> &target) {
|
||||||
Storage::Encodings::MFM::Parser parser(true, disk);
|
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk);
|
||||||
Storage::Encodings::MFM::Sector *boot_sector = parser.get_sector(0, 0, 0x41);
|
const Storage::Encodings::MFM::Sector *boot_sector = parser.sector(0, 0, 0x41);
|
||||||
if(boot_sector != nullptr && !boot_sector->samples.empty() && boot_sector->samples[0].size() == 512) {
|
if(boot_sector != nullptr && !boot_sector->samples.empty() && boot_sector->samples[0].size() == 512) {
|
||||||
// Check that the first 64 bytes of the sector aren't identical; if they are then probably
|
// Check that the first 64 bytes of the sector aren't identical; if they are then probably
|
||||||
// this disk was formatted and the filler byte never replaced.
|
// this disk was formatted and the filler byte never replaced.
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_AmstradCPC_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define Analyser_Static_AmstradCPC_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::AmstradCPC {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_AmstradCPC_StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_AmstradCPC_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_AmstradCPC_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Enum.hpp"
|
#include "../../../Reflection/Enum.hpp"
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
@@ -30,5 +29,3 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_AmstradCPC_Target_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_AppleII_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define Analyser_Static_AppleII_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::AppleII {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_AppleII_StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_AppleII_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_AppleII_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Enum.hpp"
|
#include "../../../Reflection/Enum.hpp"
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
@@ -50,5 +49,3 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_AppleII_Target_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_AppleIIgs_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define Analyser_Static_AppleIIgs_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::AppleIIgs {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_AppleIIgs_StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_AppleIIgs_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_AppleIIgs_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Enum.hpp"
|
#include "../../../Reflection/Enum.hpp"
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
@@ -41,5 +40,3 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_AppleIIgs_Target_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Atari_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Atari_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::Atari2600 {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_Atari2600_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_Atari2600_Target_h
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
|
|
||||||
@@ -37,5 +36,3 @@ struct Target: public ::Analyser::Static::Target {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_Atari_Target_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2019 Thomas Harte. All rights reserved.
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_AtariST_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define Analyser_Static_AtariST_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::AtariST {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_AtariST_StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2019 Thomas Harte. All rights reserved.
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_AtariST_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_AtariST_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
@@ -30,5 +29,3 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_AtariST_Target_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Coleco_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Coleco_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::Coleco {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* StaticAnalyser_hpp */
|
|
||||||
|
@@ -37,7 +37,7 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
|
|||||||
|
|
||||||
@returns a sector if one was found; @c nullptr otherwise.
|
@returns a sector if one was found; @c nullptr otherwise.
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<Sector> get_sector(uint8_t track, uint8_t sector) {
|
std::shared_ptr<Sector> sector(uint8_t track, uint8_t sector) {
|
||||||
int difference = int(track) - int(track_);
|
int difference = int(track) - int(track_);
|
||||||
track_ = track;
|
track_ = track;
|
||||||
|
|
||||||
@@ -182,7 +182,7 @@ std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<St
|
|||||||
uint8_t next_track = 18;
|
uint8_t next_track = 18;
|
||||||
uint8_t next_sector = 1;
|
uint8_t next_sector = 1;
|
||||||
while(1) {
|
while(1) {
|
||||||
sector = parser.get_sector(next_track, next_sector);
|
sector = parser.sector(next_track, next_sector);
|
||||||
if(!sector) break;
|
if(!sector) break;
|
||||||
directory.insert(directory.end(), sector->data.begin(), sector->data.end());
|
directory.insert(directory.end(), sector->data.begin(), sector->data.end());
|
||||||
next_track = sector->data[0];
|
next_track = sector->data[0];
|
||||||
@@ -221,7 +221,7 @@ std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<St
|
|||||||
|
|
||||||
bool is_first_sector = true;
|
bool is_first_sector = true;
|
||||||
while(next_track) {
|
while(next_track) {
|
||||||
sector = parser.get_sector(next_track, next_sector);
|
sector = parser.sector(next_track, next_sector);
|
||||||
if(!sector) break;
|
if(!sector) break;
|
||||||
|
|
||||||
next_track = sector->data[0];
|
next_track = sector->data[0];
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Commodore_Disk_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Commodore_Disk_hpp
|
|
||||||
|
|
||||||
#include "../../../Storage/Disk/Disk.hpp"
|
#include "../../../Storage/Disk/Disk.hpp"
|
||||||
#include "File.hpp"
|
#include "File.hpp"
|
||||||
@@ -19,5 +18,3 @@ namespace Analyser::Static::Commodore {
|
|||||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Disk::Disk> &disk);
|
std::vector<File> GetFiles(const std::shared_ptr<Storage::Disk::Disk> &disk);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Disk_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef File_hpp
|
#pragma once
|
||||||
#define File_hpp
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -35,5 +34,3 @@ struct File {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* File_hpp */
|
|
||||||
|
@@ -93,7 +93,7 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media
|
|||||||
// make a first guess based on loading address
|
// make a first guess based on loading address
|
||||||
switch(files.front().starting_address) {
|
switch(files.front().starting_address) {
|
||||||
default:
|
default:
|
||||||
LOG("Unrecognised loading address for Commodore program: " << PADHEX(4) << files.front().starting_address);
|
Log::Logger<Log::Source::CommodoreStaticAnalyser>().error().append("Unrecognised loading address for Commodore program: %04x", files.front().starting_address);
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case 0x1001:
|
case 0x1001:
|
||||||
memory_model = Target::MemoryModel::Unexpanded;
|
memory_model = Target::MemoryModel::Unexpanded;
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Commodore_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Commodore_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::Commodore {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CommodoreAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Commodore_Tape_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Commodore_Tape_hpp
|
|
||||||
|
|
||||||
#include "../../../Storage/Tape/Tape.hpp"
|
#include "../../../Storage/Tape/Tape.hpp"
|
||||||
#include "File.hpp"
|
#include "File.hpp"
|
||||||
@@ -17,5 +16,3 @@ namespace Analyser::Static::Commodore {
|
|||||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Tape_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_Commodore_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_Commodore_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Enum.hpp"
|
#include "../../../Reflection/Enum.hpp"
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
@@ -70,5 +69,3 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_Commodore_Target_h */
|
|
||||||
|
@@ -236,7 +236,7 @@ static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector<
|
|||||||
case Instruction::Relative: {
|
case Instruction::Relative: {
|
||||||
std::size_t operand_address = address_mapper(address);
|
std::size_t operand_address = address_mapper(address);
|
||||||
if(operand_address >= memory.size()) return;
|
if(operand_address >= memory.size()) return;
|
||||||
address++;
|
++address;
|
||||||
|
|
||||||
instruction.operand = memory[operand_address];
|
instruction.operand = memory[operand_address];
|
||||||
}
|
}
|
||||||
@@ -291,20 +291,34 @@ static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decide on overall flow control.
|
// Decide on overall flow control.
|
||||||
if(instruction.operation == Instruction::RTS || instruction.operation == Instruction::RTI) return;
|
|
||||||
if(instruction.operation == Instruction::BRK) return; // TODO: check whether IRQ vector is within memory range
|
// All relative instructions are flow control.
|
||||||
if(instruction.operation == Instruction::JSR) {
|
|
||||||
disassembly.remaining_entry_points.push_back(instruction.operand);
|
|
||||||
}
|
|
||||||
if(instruction.operation == Instruction::JMP) {
|
|
||||||
if(instruction.addressing_mode == Instruction::Absolute)
|
|
||||||
disassembly.remaining_entry_points.push_back(instruction.operand);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(instruction.addressing_mode == Instruction::Relative) {
|
if(instruction.addressing_mode == Instruction::Relative) {
|
||||||
uint16_t destination = uint16_t(address + int8_t(instruction.operand));
|
uint16_t destination = uint16_t(address + int8_t(instruction.operand));
|
||||||
disassembly.remaining_entry_points.push_back(destination);
|
disassembly.remaining_entry_points.push_back(destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch(instruction.operation) {
|
||||||
|
default: break;
|
||||||
|
|
||||||
|
case Instruction::KIL:
|
||||||
|
case Instruction::RTS:
|
||||||
|
case Instruction::RTI:
|
||||||
|
case Instruction::BRK: // TODO: check whether IRQ vector is within memory range.
|
||||||
|
disassembly.implicit_entry_points.push_back(address);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case Instruction::JMP:
|
||||||
|
// Adding a new entry point for relative jumps was handled above.
|
||||||
|
if(instruction.addressing_mode == Instruction::Absolute) {
|
||||||
|
disassembly.remaining_entry_points.push_back(instruction.operand);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
case Instruction::JSR:
|
||||||
|
disassembly.remaining_entry_points.push_back(instruction.operand);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,5 +330,5 @@ Disassembly Analyser::Static::MOS6502::Disassemble(
|
|||||||
const std::vector<uint8_t> &memory,
|
const std::vector<uint8_t> &memory,
|
||||||
const std::function<std::size_t(uint16_t)> &address_mapper,
|
const std::function<std::size_t(uint16_t)> &address_mapper,
|
||||||
std::vector<uint16_t> entry_points) {
|
std::vector<uint16_t> entry_points) {
|
||||||
return Analyser::Static::Disassembly::Disassemble<Disassembly, uint16_t, MOS6502Disassembler>(memory, address_mapper, entry_points);
|
return Analyser::Static::Disassembly::Disassemble<Disassembly, uint16_t, MOS6502Disassembler>(memory, address_mapper, entry_points, false);
|
||||||
}
|
}
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Disassembler_6502_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Disassembler_6502_hpp
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -93,5 +92,3 @@ Disassembly Disassemble(
|
|||||||
std::vector<uint16_t> entry_points);
|
std::vector<uint16_t> entry_points);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Disassembler6502_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef AddressMapper_hpp
|
#pragma once
|
||||||
#define AddressMapper_hpp
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
@@ -24,5 +23,3 @@ template <typename T> std::function<std::size_t(T)> OffsetMapper(T start_address
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* AddressMapper_hpp */
|
|
||||||
|
@@ -6,33 +6,37 @@
|
|||||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Kernel_hpp
|
#pragma once
|
||||||
#define Kernel_hpp
|
|
||||||
|
|
||||||
namespace Analyser::Static::Disassembly {
|
namespace Analyser::Static::Disassembly {
|
||||||
|
|
||||||
template <typename D, typename S> struct PartialDisassembly {
|
template <typename D, typename S> struct PartialDisassembly {
|
||||||
D disassembly;
|
D disassembly;
|
||||||
std::vector<S> remaining_entry_points;
|
std::vector<S> remaining_entry_points;
|
||||||
|
std::vector<S> implicit_entry_points;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename D, typename S, typename Disassembler> D Disassemble(
|
template <typename D, typename S, typename Disassembler> D Disassemble(
|
||||||
const std::vector<uint8_t> &memory,
|
const std::vector<uint8_t> &memory,
|
||||||
const std::function<std::size_t(S)> &address_mapper,
|
const std::function<std::size_t(S)> &address_mapper,
|
||||||
std::vector<S> entry_points) {
|
std::vector<S> entry_points,
|
||||||
|
bool exhaustive)
|
||||||
|
{
|
||||||
PartialDisassembly<D, S> partial_disassembly;
|
PartialDisassembly<D, S> partial_disassembly;
|
||||||
partial_disassembly.remaining_entry_points = entry_points;
|
partial_disassembly.remaining_entry_points = entry_points;
|
||||||
|
|
||||||
while(!partial_disassembly.remaining_entry_points.empty()) {
|
while(!partial_disassembly.remaining_entry_points.empty()) {
|
||||||
// pull the next entry point from the back of the vector
|
// Do a recursive-style disassembly for all current entry points.
|
||||||
S next_entry_point = partial_disassembly.remaining_entry_points.back();
|
while(!partial_disassembly.remaining_entry_points.empty()) {
|
||||||
|
// Pull the next entry point from the back of the vector.
|
||||||
|
const S next_entry_point = partial_disassembly.remaining_entry_points.back();
|
||||||
partial_disassembly.remaining_entry_points.pop_back();
|
partial_disassembly.remaining_entry_points.pop_back();
|
||||||
|
|
||||||
// if that address has already been visited, forget about it
|
// If that address has already been visited, forget about it.
|
||||||
if( partial_disassembly.disassembly.instructions_by_address.find(next_entry_point)
|
if( partial_disassembly.disassembly.instructions_by_address.find(next_entry_point)
|
||||||
!= partial_disassembly.disassembly.instructions_by_address.end()) continue;
|
!= partial_disassembly.disassembly.instructions_by_address.end()) continue;
|
||||||
|
|
||||||
// if it's outgoing, log it as such and forget about it; otherwise disassemble
|
// If it's outgoing, log it as such and forget about it; otherwise disassemble.
|
||||||
std::size_t mapped_entry_point = address_mapper(next_entry_point);
|
std::size_t mapped_entry_point = address_mapper(next_entry_point);
|
||||||
if(mapped_entry_point >= memory.size())
|
if(mapped_entry_point >= memory.size())
|
||||||
partial_disassembly.disassembly.outward_calls.insert(next_entry_point);
|
partial_disassembly.disassembly.outward_calls.insert(next_entry_point);
|
||||||
@@ -40,9 +44,23 @@ template <typename D, typename S, typename Disassembler> D Disassemble(
|
|||||||
Disassembler::AddToDisassembly(partial_disassembly, memory, address_mapper, next_entry_point);
|
Disassembler::AddToDisassembly(partial_disassembly, memory, address_mapper, next_entry_point);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is not an exhaustive disassembly, that's your lot.
|
||||||
|
if(!exhaustive) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, copy in the new 'implicit entry points' (i.e. all locations that are one after
|
||||||
|
// a disassembled region). There's a test above that'll ignore any which have already been
|
||||||
|
// disassembled from.
|
||||||
|
std::move(
|
||||||
|
partial_disassembly.implicit_entry_points.begin(),
|
||||||
|
partial_disassembly.implicit_entry_points.end(),
|
||||||
|
std::back_inserter(partial_disassembly.remaining_entry_points)
|
||||||
|
);
|
||||||
|
partial_disassembly.implicit_entry_points.clear();
|
||||||
|
}
|
||||||
|
|
||||||
return partial_disassembly.disassembly;
|
return partial_disassembly.disassembly;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Kernel_hpp */
|
|
||||||
|
@@ -56,11 +56,11 @@ class Accessor {
|
|||||||
bool overrun_ = false;
|
bool overrun_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define x(v) (v >> 6)
|
constexpr uint8_t x(uint8_t v) { return v >> 6; }
|
||||||
#define y(v) ((v >> 3) & 7)
|
constexpr uint8_t y(uint8_t v) { return (v >> 3) & 7; }
|
||||||
#define q(v) ((v >> 3) & 1)
|
constexpr uint8_t q(uint8_t v) { return (v >> 3) & 1; }
|
||||||
#define p(v) ((v >> 4) & 3)
|
constexpr uint8_t p(uint8_t v) { return (v >> 4) & 3; }
|
||||||
#define z(v) (v & 7)
|
constexpr uint8_t z(uint8_t v) { return v & 7; }
|
||||||
|
|
||||||
Instruction::Condition condition_table[] = {
|
Instruction::Condition condition_table[] = {
|
||||||
Instruction::Condition::NZ, Instruction::Condition::Z,
|
Instruction::Condition::NZ, Instruction::Condition::Z,
|
||||||
@@ -589,7 +589,7 @@ struct Z80Disassembler {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add any (potentially) newly discovered entry point.
|
// Add any (potentially) newly-discovered entry point.
|
||||||
if( instruction.operation == Instruction::Operation::JP ||
|
if( instruction.operation == Instruction::Operation::JP ||
|
||||||
instruction.operation == Instruction::Operation::JR ||
|
instruction.operation == Instruction::Operation::JR ||
|
||||||
instruction.operation == Instruction::Operation::CALL ||
|
instruction.operation == Instruction::Operation::CALL ||
|
||||||
@@ -598,22 +598,37 @@ struct Z80Disassembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is it if: an unconditional RET, RETI, RETN, JP or JR is found.
|
// This is it if: an unconditional RET, RETI, RETN, JP or JR is found.
|
||||||
if(instruction.condition != Instruction::Condition::None) continue;
|
switch(instruction.operation) {
|
||||||
|
default: break;
|
||||||
|
|
||||||
if(instruction.operation == Instruction::Operation::RET) return;
|
case Instruction::Operation::RET:
|
||||||
if(instruction.operation == Instruction::Operation::RETI) return;
|
case Instruction::Operation::RETI:
|
||||||
if(instruction.operation == Instruction::Operation::RETN) return;
|
case Instruction::Operation::RETN:
|
||||||
if(instruction.operation == Instruction::Operation::JP) return;
|
case Instruction::Operation::JP:
|
||||||
if(instruction.operation == Instruction::Operation::JR) return;
|
case Instruction::Operation::JR:
|
||||||
|
if(instruction.condition == Instruction::Condition::None) {
|
||||||
|
disassembly.implicit_entry_points.push_back(accessor.address());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of anonymous namespace
|
} // end of anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Disassembly Analyser::Static::Z80::Disassemble(
|
Disassembly Analyser::Static::Z80::Disassemble(
|
||||||
const std::vector<uint8_t> &memory,
|
const std::vector<uint8_t> &memory,
|
||||||
const std::function<std::size_t(uint16_t)> &address_mapper,
|
const std::function<std::size_t(uint16_t)> &address_mapper,
|
||||||
std::vector<uint16_t> entry_points) {
|
std::vector<uint16_t> entry_points,
|
||||||
return Analyser::Static::Disassembly::Disassemble<Disassembly, uint16_t, Z80Disassembler>(memory, address_mapper, entry_points);
|
Approach approach)
|
||||||
|
{
|
||||||
|
return Analyser::Static::Disassembly::Disassemble<Disassembly, uint16_t, Z80Disassembler>(
|
||||||
|
memory,
|
||||||
|
address_mapper,
|
||||||
|
entry_points,
|
||||||
|
approach == Approach::Exhaustive
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Disassembler_Z80_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Disassembler_Z80_hpp
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -76,11 +75,18 @@ struct Disassembly {
|
|||||||
std::set<uint16_t> internal_stores, internal_loads, internal_modifies;
|
std::set<uint16_t> internal_stores, internal_loads, internal_modifies;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Approach {
|
||||||
|
/// Disassemble from the supplied entry points until an indeterminate branch or return only, adding other fully-static
|
||||||
|
/// entry points as they are observed.
|
||||||
|
Recursive,
|
||||||
|
/// Disassemble all supplied bytes, regardless of what nonsense may be encountered by accidental parsing of data areas.
|
||||||
|
Exhaustive,
|
||||||
|
};
|
||||||
|
|
||||||
Disassembly Disassemble(
|
Disassembly Disassemble(
|
||||||
const std::vector<uint8_t> &memory,
|
const std::vector<uint8_t> &memory,
|
||||||
const std::function<std::size_t(uint16_t)> &address_mapper,
|
const std::function<std::size_t(uint16_t)> &address_mapper,
|
||||||
std::vector<uint16_t> entry_points);
|
std::vector<uint16_t> entry_points,
|
||||||
|
Approach approach);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* StaticAnalyser_Disassembler_Z80_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_DiskII_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define Analyser_Static_DiskII_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::DiskII {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_DiskII_StaticAnalyser_hpp */
|
|
||||||
|
@@ -40,6 +40,8 @@ Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets(const Medi
|
|||||||
target->basic_version = Target::BASICVersion::Any;
|
target->basic_version = Target::BASICVersion::Any;
|
||||||
|
|
||||||
// Inspect any supplied disks.
|
// Inspect any supplied disks.
|
||||||
|
//
|
||||||
|
// TODO: how best can these be discerned from MS-DOS and MSX disks?
|
||||||
if(!media.disks.empty()) {
|
if(!media.disks.empty()) {
|
||||||
// DOS will be needed.
|
// DOS will be needed.
|
||||||
target->dos = Target::DOS::EXDOS;
|
target->dos = Target::DOS::EXDOS;
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_Enterprise_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define Analyser_Static_Enterprise_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::Enterprise {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_Enterprise_StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_Enterprise_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_Enterprise_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Enum.hpp"
|
#include "../../../Reflection/Enum.hpp"
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
@@ -49,5 +48,3 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_Enterprise_Target_h */
|
|
||||||
|
100
Analyser/Static/FAT12/StaticAnalyser.cpp
Normal file
100
Analyser/Static/FAT12/StaticAnalyser.cpp
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
//
|
||||||
|
// StaticAnalyser.cpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 05/12/2023.
|
||||||
|
// Copyright 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "StaticAnalyser.hpp"
|
||||||
|
|
||||||
|
#include "../Enterprise/StaticAnalyser.hpp"
|
||||||
|
#include "../PCCompatible/StaticAnalyser.hpp"
|
||||||
|
|
||||||
|
#include "../../../Storage/Disk/Track/TrackSerialiser.hpp"
|
||||||
|
#include "../../../Storage/Disk/Encodings/MFM/Constants.hpp"
|
||||||
|
#include "../../../Storage/Disk/Encodings/MFM/SegmentParser.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
Analyser::Static::TargetList Analyser::Static::FAT12::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType platforms) {
|
||||||
|
// This analyser can comprehend disks only.
|
||||||
|
if(media.disks.empty()) return {};
|
||||||
|
|
||||||
|
auto &disk = media.disks.front();
|
||||||
|
TargetList targets;
|
||||||
|
|
||||||
|
// Total list of potential platforms is:
|
||||||
|
//
|
||||||
|
// * the Enterprise (and, by extension, CP/M-targetted software);
|
||||||
|
// * the Atari ST;
|
||||||
|
// * the MSX (ditto on CP/M); and
|
||||||
|
// * the PC.
|
||||||
|
//
|
||||||
|
// (though the MSX and Atari ST don't currently call in here for now)
|
||||||
|
|
||||||
|
// If the disk image is very small or large, map it to the PC. That's the only option old enough
|
||||||
|
// to have used 5.25" media.
|
||||||
|
if(disk->get_maximum_head_position() <= Storage::Disk::HeadPosition(40)) {
|
||||||
|
return Analyser::Static::PCCompatible::GetTargets(media, file_name, platforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to grab MFM track 0, sector 1: the boot sector.
|
||||||
|
const auto track_zero = disk->get_track_at_position(Storage::Disk::Track::Address(0, Storage::Disk::HeadPosition(0)));
|
||||||
|
const auto sector_map = Storage::Encodings::MFM::sectors_from_segment(
|
||||||
|
Storage::Disk::track_serialisation(
|
||||||
|
*track_zero,
|
||||||
|
Storage::Encodings::MFM::MFMBitLength
|
||||||
|
), Storage::Encodings::MFM::Density::Double);
|
||||||
|
|
||||||
|
// If no sectors were found, assume this disk was either single density or high density, which both imply the PC.
|
||||||
|
if(sector_map.empty() || sector_map.size() > 10) {
|
||||||
|
return Analyser::Static::PCCompatible::GetTargets(media, file_name, platforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Storage::Encodings::MFM::Sector *boot_sector = nullptr;
|
||||||
|
for(const auto &pair: sector_map) {
|
||||||
|
if(pair.second.address.sector == 1) {
|
||||||
|
boot_sector = &pair.second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This shouldn't technically be possible since the disk has been identified as FAT12, but be safe.
|
||||||
|
if(!boot_sector) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for key phrases that imply a PC disk.
|
||||||
|
const auto &sample = boot_sector->samples[0];
|
||||||
|
const std::vector<std::string> pc_strings = {
|
||||||
|
// MS-DOS strings.
|
||||||
|
"MSDOS",
|
||||||
|
"Non-System disk or disk error",
|
||||||
|
// DOS Plus strings.
|
||||||
|
"Insert a SYSTEM disk",
|
||||||
|
};
|
||||||
|
for(const auto &string: pc_strings) {
|
||||||
|
if(
|
||||||
|
std::search(sample.begin(), sample.end(), string.begin(), string.end()) != sample.end()
|
||||||
|
) {
|
||||||
|
return Analyser::Static::PCCompatible::GetTargets(media, file_name, platforms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: look for a COM, EXE or BAT, inspect. AUTOEXEC.BAT and/or CONFIG.SYS could be either PC or MSX.
|
||||||
|
// Disassembling the boot sector doesn't necessarily work, as several Enterprise titles out there in the wild seem
|
||||||
|
// to have been created by WINIMAGE which adds an x86 PC-style boot sector.
|
||||||
|
|
||||||
|
// Enterprise notes: EXOS files all start with a 16-byte header which should begin with a 0 and then have a type
|
||||||
|
// byte that will be 0xa or lower; cf http://epbas.lgb.hu/readme.html
|
||||||
|
//
|
||||||
|
// Some disks commonly passed around as Enterprise software are actually CP/M software, expecting IS-DOS (the CP/M
|
||||||
|
// clone) to be present. It's certainly possible the same could be true of MSX disks and MSX-DOS. So analysing COM
|
||||||
|
// files probably means searching for CALL 5s and/or INT 21hs, if not a more rigorous disassembly.
|
||||||
|
//
|
||||||
|
// I have not been able to locate a copy of IS-DOS so there's probably not much that can be done here; perhaps I
|
||||||
|
// could redirect to an MSX2 with MSX-DOS2? Though it'd be nicer if I had a machine that was pure CP/M.
|
||||||
|
|
||||||
|
// Being unable to prove that this is a PC disk, throw it to the Enterprise.
|
||||||
|
return Analyser::Static::Enterprise::GetTargets(media, file_name, platforms);
|
||||||
|
}
|
19
Analyser/Static/FAT12/StaticAnalyser.hpp
Normal file
19
Analyser/Static/FAT12/StaticAnalyser.hpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// StaticAnalyser.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 05/12/2023.
|
||||||
|
// Copyright 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../StaticAnalyser.hpp"
|
||||||
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Analyser::Static::FAT12 {
|
||||||
|
|
||||||
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
|
}
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Cartridge_hpp
|
#pragma once
|
||||||
#define Cartridge_hpp
|
|
||||||
|
|
||||||
#include "../../../Storage/Cartridge/Cartridge.hpp"
|
#include "../../../Storage/Cartridge/Cartridge.hpp"
|
||||||
|
|
||||||
@@ -32,5 +31,3 @@ struct Cartridge: public ::Storage::Cartridge::Cartridge {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Cartridge_hpp */
|
|
||||||
|
@@ -115,97 +115,16 @@ static Analyser::Static::TargetList CartridgeTargetsFrom(
|
|||||||
// be at play; disassemble to try to figure it out.
|
// be at play; disassemble to try to figure it out.
|
||||||
std::vector<uint8_t> first_8k;
|
std::vector<uint8_t> first_8k;
|
||||||
first_8k.insert(first_8k.begin(), segment.data.begin(), segment.data.begin() + 8192);
|
first_8k.insert(first_8k.begin(), segment.data.begin(), segment.data.begin() + 8192);
|
||||||
Analyser::Static::Z80::Disassembly disassembly =
|
const Analyser::Static::Z80::Disassembly disassembly =
|
||||||
Analyser::Static::Z80::Disassemble(
|
Analyser::Static::Z80::Disassemble(
|
||||||
first_8k,
|
first_8k,
|
||||||
Analyser::Static::Disassembler::OffsetMapper(start_address),
|
Analyser::Static::Disassembler::OffsetMapper(start_address),
|
||||||
{ init_address }
|
{ init_address },
|
||||||
|
Analyser::Static::Z80::Approach::Exhaustive
|
||||||
);
|
);
|
||||||
|
|
||||||
// // Look for a indirect store followed by an unconditional JP or CALL into another
|
|
||||||
// // segment, that's a fairly explicit sign where found.
|
|
||||||
using Instruction = Analyser::Static::Z80::Instruction;
|
using Instruction = Analyser::Static::Z80::Instruction;
|
||||||
std::map<uint16_t, Instruction> &instructions = disassembly.instructions_by_address;
|
const std::map<uint16_t, Instruction> &instructions = disassembly.instructions_by_address;
|
||||||
bool is_ascii = false;
|
|
||||||
// auto iterator = instructions.begin();
|
|
||||||
// while(iterator != instructions.end()) {
|
|
||||||
// auto next_iterator = iterator;
|
|
||||||
// next_iterator++;
|
|
||||||
// if(next_iterator == instructions.end()) break;
|
|
||||||
//
|
|
||||||
// if( iterator->second.operation == Instruction::Operation::LD &&
|
|
||||||
// iterator->second.destination == Instruction::Location::Operand_Indirect &&
|
|
||||||
// (
|
|
||||||
// iterator->second.operand == 0x5000 ||
|
|
||||||
// iterator->second.operand == 0x6000 ||
|
|
||||||
// iterator->second.operand == 0x6800 ||
|
|
||||||
// iterator->second.operand == 0x7000 ||
|
|
||||||
// iterator->second.operand == 0x77ff ||
|
|
||||||
// iterator->second.operand == 0x7800 ||
|
|
||||||
// iterator->second.operand == 0x8000 ||
|
|
||||||
// iterator->second.operand == 0x9000 ||
|
|
||||||
// iterator->second.operand == 0xa000
|
|
||||||
// ) &&
|
|
||||||
// (
|
|
||||||
// next_iterator->second.operation == Instruction::Operation::CALL ||
|
|
||||||
// next_iterator->second.operation == Instruction::Operation::JP
|
|
||||||
// ) &&
|
|
||||||
// ((next_iterator->second.operand >> 13) != (0x4000 >> 13))
|
|
||||||
// ) {
|
|
||||||
// const uint16_t address = uint16_t(next_iterator->second.operand);
|
|
||||||
// switch(iterator->second.operand) {
|
|
||||||
// case 0x6000:
|
|
||||||
// if(address >= 0x6000 && address < 0x8000) {
|
|
||||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::KonamiWithSCC;
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case 0x6800:
|
|
||||||
// if(address >= 0x6000 && address < 0x6800) {
|
|
||||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::ASCII8kb;
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case 0x7000:
|
|
||||||
// if(address >= 0x6000 && address < 0x8000) {
|
|
||||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::KonamiWithSCC;
|
|
||||||
// }
|
|
||||||
// if(address >= 0x7000 && address < 0x7800) {
|
|
||||||
// is_ascii = true;
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case 0x77ff:
|
|
||||||
// if(address >= 0x7000 && address < 0x7800) {
|
|
||||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::ASCII16kb;
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case 0x7800:
|
|
||||||
// if(address >= 0xa000 && address < 0xc000) {
|
|
||||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::ASCII8kb;
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case 0x8000:
|
|
||||||
// if(address >= 0x8000 && address < 0xa000) {
|
|
||||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::KonamiWithSCC;
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case 0x9000:
|
|
||||||
// if(address >= 0x8000 && address < 0xa000) {
|
|
||||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::KonamiWithSCC;
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case 0xa000:
|
|
||||||
// if(address >= 0xa000 && address < 0xc000) {
|
|
||||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::Konami;
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case 0xb000:
|
|
||||||
// if(address >= 0xa000 && address < 0xc000) {
|
|
||||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::KonamiWithSCC;
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// iterator = next_iterator;
|
|
||||||
|
|
||||||
// Look for LD (nnnn), A instructions, and collate those addresses.
|
// Look for LD (nnnn), A instructions, and collate those addresses.
|
||||||
std::map<uint16_t, int> address_counts;
|
std::map<uint16_t, int> address_counts;
|
||||||
@@ -217,49 +136,46 @@ static Analyser::Static::TargetList CartridgeTargetsFrom(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weight confidences by number of observed hits.
|
// Weight confidences by number of observed hits; if any is above 60% confidence, just use it.
|
||||||
float total_hits =
|
const auto ascii_8kb_total = address_counts[0x6000] + address_counts[0x6800] + address_counts[0x7000] + address_counts[0x7800];
|
||||||
float(
|
const auto ascii_16kb_total = address_counts[0x6000] + address_counts[0x7000] + address_counts[0x77ff];
|
||||||
address_counts[0x6000] + address_counts[0x6800] +
|
const auto konami_total = address_counts[0x6000] + address_counts[0x8000] + address_counts[0xa000];
|
||||||
address_counts[0x7000] + address_counts[0x7800] +
|
const auto konami_with_scc_total = address_counts[0x5000] + address_counts[0x7000] + address_counts[0x9000] + address_counts[0xb000];
|
||||||
address_counts[0x77ff] + address_counts[0x8000] +
|
|
||||||
address_counts[0xa000] + address_counts[0x5000] +
|
|
||||||
address_counts[0x9000] + address_counts[0xb000]
|
|
||||||
);
|
|
||||||
|
|
||||||
|
const auto total_hits = ascii_8kb_total + ascii_16kb_total + konami_total + konami_with_scc_total;
|
||||||
|
|
||||||
|
const bool is_ascii_8kb = (ascii_8kb_total * 5) / (total_hits * 3);
|
||||||
|
const bool is_ascii_16kb = (ascii_16kb_total * 5) / (total_hits * 3);
|
||||||
|
const bool is_konami = (konami_total * 5) / (total_hits * 3);
|
||||||
|
const bool is_konami_with_scc = (konami_with_scc_total * 5) / (total_hits * 3);
|
||||||
|
|
||||||
|
if(!is_ascii_16kb && !is_konami && !is_konami_with_scc) {
|
||||||
targets.push_back(CartridgeTarget(
|
targets.push_back(CartridgeTarget(
|
||||||
segment,
|
segment,
|
||||||
start_address,
|
start_address,
|
||||||
Analyser::Static::MSX::Cartridge::ASCII8kb,
|
Analyser::Static::MSX::Cartridge::ASCII8kb,
|
||||||
float( address_counts[0x6000] +
|
float(ascii_8kb_total) / float(total_hits)));
|
||||||
address_counts[0x6800] +
|
}
|
||||||
address_counts[0x7000] +
|
if(!is_ascii_8kb && !is_konami && !is_konami_with_scc) {
|
||||||
address_counts[0x7800]) / total_hits));
|
|
||||||
targets.push_back(CartridgeTarget(
|
targets.push_back(CartridgeTarget(
|
||||||
segment,
|
segment,
|
||||||
start_address,
|
start_address,
|
||||||
Analyser::Static::MSX::Cartridge::ASCII16kb,
|
Analyser::Static::MSX::Cartridge::ASCII16kb,
|
||||||
float( address_counts[0x6000] +
|
float(ascii_16kb_total) / float(total_hits)));
|
||||||
address_counts[0x7000] +
|
}
|
||||||
address_counts[0x77ff]) / total_hits));
|
if(!is_ascii_8kb && !is_ascii_16kb && !is_konami_with_scc) {
|
||||||
if(!is_ascii) {
|
|
||||||
targets.push_back(CartridgeTarget(
|
targets.push_back(CartridgeTarget(
|
||||||
segment,
|
segment,
|
||||||
start_address,
|
start_address,
|
||||||
Analyser::Static::MSX::Cartridge::Konami,
|
Analyser::Static::MSX::Cartridge::Konami,
|
||||||
float( address_counts[0x6000] +
|
float(konami_total) / float(total_hits)));
|
||||||
address_counts[0x8000] +
|
|
||||||
address_counts[0xa000]) / total_hits));
|
|
||||||
}
|
}
|
||||||
if(!is_ascii) {
|
if(!is_ascii_8kb && !is_ascii_16kb && !is_konami) {
|
||||||
targets.push_back(CartridgeTarget(
|
targets.push_back(CartridgeTarget(
|
||||||
segment,
|
segment,
|
||||||
start_address,
|
start_address,
|
||||||
Analyser::Static::MSX::Cartridge::KonamiWithSCC,
|
Analyser::Static::MSX::Cartridge::KonamiWithSCC,
|
||||||
float( address_counts[0x5000] +
|
float(konami_with_scc_total) / float(total_hits)));
|
||||||
address_counts[0x7000] +
|
|
||||||
address_counts[0x9000] +
|
|
||||||
address_counts[0xb000]) / total_hits));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_MSX_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_MSX_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::MSX {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* StaticAnalyser_MSX_StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_MSX_Tape_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_MSX_Tape_hpp
|
|
||||||
|
|
||||||
#include "../../../Storage/Tape/Tape.hpp"
|
#include "../../../Storage/Tape/Tape.hpp"
|
||||||
|
|
||||||
@@ -36,5 +35,3 @@ struct File {
|
|||||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* StaticAnalyser_MSX_Tape_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_MSX_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_MSX_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Enum.hpp"
|
#include "../../../Reflection/Enum.hpp"
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
@@ -47,5 +46,3 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_MSX_Target_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2019 Thomas Harte. All rights reserved.
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_Macintosh_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define Analyser_Static_Macintosh_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::Macintosh {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_Macintosh_StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2019 Thomas Harte. All rights reserved.
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_Macintosh_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_Macintosh_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Enum.hpp"
|
#include "../../../Reflection/Enum.hpp"
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
@@ -29,5 +28,3 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_Macintosh_Target_h */
|
|
||||||
|
@@ -85,7 +85,7 @@ bool is_microdisc(Storage::Encodings::MFM::Parser &parser) {
|
|||||||
/*
|
/*
|
||||||
The Microdisc boot sector is sector 2 of track 0 and contains a 23-byte signature.
|
The Microdisc boot sector is sector 2 of track 0 and contains a 23-byte signature.
|
||||||
*/
|
*/
|
||||||
Storage::Encodings::MFM::Sector *sector = parser.get_sector(0, 0, 2);
|
const Storage::Encodings::MFM::Sector *sector = parser.sector(0, 0, 2);
|
||||||
if(!sector) return false;
|
if(!sector) return false;
|
||||||
if(sector->samples.empty()) return false;
|
if(sector->samples.empty()) return false;
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ bool is_400_loader(Storage::Encodings::MFM::Parser &parser, uint16_t range_start
|
|||||||
use disassembly to test for likely matches.
|
use disassembly to test for likely matches.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Storage::Encodings::MFM::Sector *sector = parser.get_sector(0, 0, 1);
|
const Storage::Encodings::MFM::Sector *sector = parser.sector(0, 0, 1);
|
||||||
if(!sector) return false;
|
if(!sector) return false;
|
||||||
if(sector->samples.empty()) return false;
|
if(sector->samples.empty()) return false;
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &med
|
|||||||
// 8-DOS is recognised by a dedicated Disk II analyser, so check only for Microdisc,
|
// 8-DOS is recognised by a dedicated Disk II analyser, so check only for Microdisc,
|
||||||
// Jasmin and BD-DOS formats here.
|
// Jasmin and BD-DOS formats here.
|
||||||
for(auto &disk: media.disks) {
|
for(auto &disk: media.disks) {
|
||||||
Storage::Encodings::MFM::Parser parser(true, disk);
|
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk);
|
||||||
|
|
||||||
if(is_microdisc(parser)) {
|
if(is_microdisc(parser)) {
|
||||||
target->disk_interface = Target::DiskInterface::Microdisc;
|
target->disk_interface = Target::DiskInterface::Microdisc;
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Oric_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Oric_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::Oric {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Oric_Tape_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Oric_Tape_hpp
|
|
||||||
|
|
||||||
#include "../../../Storage/Tape/Tape.hpp"
|
#include "../../../Storage/Tape/Tape.hpp"
|
||||||
|
|
||||||
@@ -32,5 +31,3 @@ struct File {
|
|||||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Tape_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_Oric_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_Oric_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Enum.hpp"
|
#include "../../../Reflection/Enum.hpp"
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
@@ -55,5 +54,3 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_Oric_Target_h */
|
|
||||||
|
25
Analyser/Static/PCCompatible/StaticAnalyser.cpp
Normal file
25
Analyser/Static/PCCompatible/StaticAnalyser.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// StaticAnalyser.cpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 03/10/2019.
|
||||||
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "StaticAnalyser.hpp"
|
||||||
|
#include "Target.hpp"
|
||||||
|
|
||||||
|
Analyser::Static::TargetList Analyser::Static::PCCompatible::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||||
|
// This analyser can comprehend disks only.
|
||||||
|
if(media.disks.empty()) return {};
|
||||||
|
|
||||||
|
// No analysis is applied yet.
|
||||||
|
Analyser::Static::TargetList targets;
|
||||||
|
|
||||||
|
using Target = Analyser::Static::PCCompatible::Target;
|
||||||
|
auto *const target = new Target();
|
||||||
|
target->media = media;
|
||||||
|
targets.push_back(std::unique_ptr<Analyser::Static::Target>(target));
|
||||||
|
|
||||||
|
return targets;
|
||||||
|
}
|
19
Analyser/Static/PCCompatible/StaticAnalyser.hpp
Normal file
19
Analyser/Static/PCCompatible/StaticAnalyser.hpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// StaticAnalyser.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 29/11/2019.
|
||||||
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../StaticAnalyser.hpp"
|
||||||
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Analyser::Static::PCCompatible {
|
||||||
|
|
||||||
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
|
}
|
37
Analyser/Static/PCCompatible/Target.hpp
Normal file
37
Analyser/Static/PCCompatible/Target.hpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// Target.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 29/11/2023.
|
||||||
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../../../Reflection/Struct.hpp"
|
||||||
|
#include "../StaticAnalyser.hpp"
|
||||||
|
|
||||||
|
namespace Analyser::Static::PCCompatible {
|
||||||
|
|
||||||
|
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||||
|
ReflectableEnum(VideoAdaptor,
|
||||||
|
MDA,
|
||||||
|
CGA);
|
||||||
|
VideoAdaptor adaptor = VideoAdaptor::CGA;
|
||||||
|
|
||||||
|
ReflectableEnum(Speed,
|
||||||
|
ApproximatelyOriginal,
|
||||||
|
Fast);
|
||||||
|
Speed speed = Speed::Fast;
|
||||||
|
|
||||||
|
Target() : Analyser::Static::Target(Machine::PCCompatible) {
|
||||||
|
if(needs_declare()) {
|
||||||
|
AnnounceEnum(VideoAdaptor);
|
||||||
|
AnnounceEnum(Speed);
|
||||||
|
DeclareField(adaptor);
|
||||||
|
DeclareField(speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_Sega_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_Sega_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::Sega {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_Sega_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_Sega_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Enum.hpp"
|
#include "../../../Reflection/Enum.hpp"
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
@@ -51,5 +50,3 @@ constexpr bool is_master_system(Analyser::Static::Sega::Target::Model model) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_Sega_Target_h */
|
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
#include "StaticAnalyser.hpp"
|
#include "StaticAnalyser.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
@@ -25,9 +26,11 @@
|
|||||||
#include "Commodore/StaticAnalyser.hpp"
|
#include "Commodore/StaticAnalyser.hpp"
|
||||||
#include "DiskII/StaticAnalyser.hpp"
|
#include "DiskII/StaticAnalyser.hpp"
|
||||||
#include "Enterprise/StaticAnalyser.hpp"
|
#include "Enterprise/StaticAnalyser.hpp"
|
||||||
|
#include "FAT12/StaticAnalyser.hpp"
|
||||||
#include "Macintosh/StaticAnalyser.hpp"
|
#include "Macintosh/StaticAnalyser.hpp"
|
||||||
#include "MSX/StaticAnalyser.hpp"
|
#include "MSX/StaticAnalyser.hpp"
|
||||||
#include "Oric/StaticAnalyser.hpp"
|
#include "Oric/StaticAnalyser.hpp"
|
||||||
|
#include "PCCompatible/StaticAnalyser.hpp"
|
||||||
#include "Sega/StaticAnalyser.hpp"
|
#include "Sega/StaticAnalyser.hpp"
|
||||||
#include "ZX8081/StaticAnalyser.hpp"
|
#include "ZX8081/StaticAnalyser.hpp"
|
||||||
#include "ZXSpectrum/StaticAnalyser.hpp"
|
#include "ZXSpectrum/StaticAnalyser.hpp"
|
||||||
@@ -48,10 +51,12 @@
|
|||||||
#include "../../Storage/Disk/DiskImage/Formats/FAT12.hpp"
|
#include "../../Storage/Disk/DiskImage/Formats/FAT12.hpp"
|
||||||
#include "../../Storage/Disk/DiskImage/Formats/HFE.hpp"
|
#include "../../Storage/Disk/DiskImage/Formats/HFE.hpp"
|
||||||
#include "../../Storage/Disk/DiskImage/Formats/IPF.hpp"
|
#include "../../Storage/Disk/DiskImage/Formats/IPF.hpp"
|
||||||
|
#include "../../Storage/Disk/DiskImage/Formats/IMD.hpp"
|
||||||
#include "../../Storage/Disk/DiskImage/Formats/MacintoshIMG.hpp"
|
#include "../../Storage/Disk/DiskImage/Formats/MacintoshIMG.hpp"
|
||||||
#include "../../Storage/Disk/DiskImage/Formats/MSA.hpp"
|
#include "../../Storage/Disk/DiskImage/Formats/MSA.hpp"
|
||||||
#include "../../Storage/Disk/DiskImage/Formats/NIB.hpp"
|
#include "../../Storage/Disk/DiskImage/Formats/NIB.hpp"
|
||||||
#include "../../Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp"
|
#include "../../Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp"
|
||||||
|
#include "../../Storage/Disk/DiskImage/Formats/PCBooter.hpp"
|
||||||
#include "../../Storage/Disk/DiskImage/Formats/SSD.hpp"
|
#include "../../Storage/Disk/DiskImage/Formats/SSD.hpp"
|
||||||
#include "../../Storage/Disk/DiskImage/Formats/STX.hpp"
|
#include "../../Storage/Disk/DiskImage/Formats/STX.hpp"
|
||||||
#include "../../Storage/Disk/DiskImage/Formats/WOZ.hpp"
|
#include "../../Storage/Disk/DiskImage/Formats/WOZ.hpp"
|
||||||
@@ -84,6 +89,7 @@
|
|||||||
template<class> inline constexpr bool always_false_v = false;
|
template<class> inline constexpr bool always_false_v = false;
|
||||||
|
|
||||||
using namespace Analyser::Static;
|
using namespace Analyser::Static;
|
||||||
|
using namespace Storage;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -97,46 +103,90 @@ std::string get_extension(const std::string &name) {
|
|||||||
return extension;
|
return extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MediaAccumulator {
|
||||||
|
public:
|
||||||
|
MediaAccumulator(const std::string &file_name, TargetPlatform::IntType &potential_platforms) :
|
||||||
|
file_name_(file_name), potential_platforms_(potential_platforms), extension_(get_extension(file_name)) {}
|
||||||
|
|
||||||
|
/// Adds @c instance to the media collection and adds @c platforms to the set of potentials.
|
||||||
|
/// If @c instance is an @c TargetPlatform::TypeDistinguisher then it is given an opportunity to restrict the set of potentials.
|
||||||
|
template <typename InstanceT>
|
||||||
|
void insert(TargetPlatform::IntType platforms, std::shared_ptr<InstanceT> instance) {
|
||||||
|
if constexpr (std::is_base_of_v<Storage::Disk::Disk, InstanceT>) {
|
||||||
|
media.disks.push_back(instance);
|
||||||
|
} else if constexpr (std::is_base_of_v<Storage::Tape::Tape, InstanceT>) {
|
||||||
|
media.tapes.push_back(instance);
|
||||||
|
} else if constexpr (std::is_base_of_v<Storage::Cartridge::Cartridge, InstanceT>) {
|
||||||
|
media.cartridges.push_back(instance);
|
||||||
|
} else if constexpr (std::is_base_of_v<Storage::MassStorage::MassStorageDevice, InstanceT>) {
|
||||||
|
media.mass_storage_devices.push_back(instance);
|
||||||
|
} else {
|
||||||
|
static_assert(always_false_v<InstanceT>, "Unexpected type encountered.");
|
||||||
|
}
|
||||||
|
|
||||||
|
potential_platforms_ |= platforms;
|
||||||
|
|
||||||
|
// Check whether the instance itself has any input on target platforms.
|
||||||
|
TargetPlatform::TypeDistinguisher *const distinguisher =
|
||||||
|
dynamic_cast<TargetPlatform::TypeDistinguisher *>(instance.get());
|
||||||
|
if(distinguisher) potential_platforms_ &= distinguisher->target_platform_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Concstructs a new instance of @c InstanceT supplying @c args and adds it to the back of @c list using @c insert_instance.
|
||||||
|
template <typename InstanceT, typename... Args>
|
||||||
|
void insert(TargetPlatform::IntType platforms, Args &&... args) {
|
||||||
|
insert(platforms, std::make_shared<InstanceT>(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls @c insert with the specified parameters, ignoring any exceptions thrown.
|
||||||
|
template <typename InstanceT, typename... Args>
|
||||||
|
void try_insert(TargetPlatform::IntType platforms, Args &&... args) {
|
||||||
|
try {
|
||||||
|
insert<InstanceT>(platforms, std::forward<Args>(args)...);
|
||||||
|
} catch(...) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs a @c try_insert for an object of @c InstanceT if @c extension matches that of the file name,
|
||||||
|
/// providing the file name as the only construction argument.
|
||||||
|
template <typename InstanceT>
|
||||||
|
void try_standard(TargetPlatform::IntType platforms, const char *extension) {
|
||||||
|
if(name_matches(extension)) {
|
||||||
|
try_insert<InstanceT>(platforms, file_name_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool name_matches(const char *extension) {
|
||||||
|
return extension_ == extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
Media media;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string &file_name_;
|
||||||
|
TargetPlatform::IntType &potential_platforms_;
|
||||||
|
const std::string extension_;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::IntType &potential_platforms) {
|
static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::IntType &potential_platforms) {
|
||||||
Media result;
|
MediaAccumulator accumulator(file_name, potential_platforms);
|
||||||
const std::string extension = get_extension(file_name);
|
|
||||||
|
|
||||||
#define InsertInstance(list, instance, platforms) \
|
|
||||||
list.emplace_back(instance);\
|
|
||||||
potential_platforms |= platforms;\
|
|
||||||
TargetPlatform::TypeDistinguisher *const distinguisher = dynamic_cast<TargetPlatform::TypeDistinguisher *>(list.back().get());\
|
|
||||||
if(distinguisher) potential_platforms &= distinguisher->target_platform_type();
|
|
||||||
|
|
||||||
#define Insert(list, class, platforms, ...) \
|
|
||||||
InsertInstance(list, new Storage::class(__VA_ARGS__), platforms);
|
|
||||||
|
|
||||||
#define TryInsert(list, class, platforms, ...) \
|
|
||||||
try {\
|
|
||||||
Insert(list, class, platforms, __VA_ARGS__) \
|
|
||||||
} catch(...) {}
|
|
||||||
|
|
||||||
#define Format(ext, list, class, platforms) \
|
|
||||||
if(extension == ext) { \
|
|
||||||
TryInsert(list, class, platforms, file_name) \
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2MG
|
// 2MG
|
||||||
if(extension == "2mg") {
|
if(accumulator.name_matches("2mg")) {
|
||||||
// 2MG uses a factory method; defer to it.
|
// 2MG uses a factory method; defer to it.
|
||||||
try {
|
try {
|
||||||
const auto media = Storage::Disk::Disk2MG::open(file_name);
|
const auto media = Disk::Disk2MG::open(file_name);
|
||||||
std::visit([&result, &potential_platforms](auto &&arg) {
|
std::visit([&](auto &&arg) {
|
||||||
using Type = typename std::decay<decltype(arg)>::type;
|
using Type = typename std::decay<decltype(arg)>::type;
|
||||||
|
|
||||||
if constexpr (std::is_same<Type, nullptr_t>::value) {
|
if constexpr (std::is_same<Type, std::nullptr_t>::value) {
|
||||||
// It's valid for no media to be returned.
|
// It's valid for no media to be returned.
|
||||||
} else if constexpr (std::is_same<Type, Storage::Disk::DiskImageHolderBase *>::value) {
|
} else if constexpr (std::is_same<Type, Disk::DiskImageHolderBase *>::value) {
|
||||||
InsertInstance(result.disks, arg, TargetPlatform::DiskII);
|
accumulator.insert(TargetPlatform::DiskII, std::shared_ptr<Disk::DiskImageHolderBase>(arg));
|
||||||
} else if constexpr (std::is_same<Type, Storage::MassStorage::MassStorageDevice *>::value) {
|
} else if constexpr (std::is_same<Type, MassStorage::MassStorageDevice *>::value) {
|
||||||
// TODO: or is it Apple IIgs?
|
// TODO: or is it Apple IIgs?
|
||||||
InsertInstance(result.mass_storage_devices, arg, TargetPlatform::AppleII);
|
accumulator.insert(TargetPlatform::AppleII, std::shared_ptr<MassStorage::MassStorageDevice>(arg));
|
||||||
} else {
|
} else {
|
||||||
static_assert(always_false_v<Type>, "Unexpected type encountered.");
|
static_assert(always_false_v<Type>, "Unexpected type encountered.");
|
||||||
}
|
}
|
||||||
@@ -144,95 +194,108 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
|||||||
} catch(...) {}
|
} catch(...) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Format("80", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 80
|
accumulator.try_standard<Tape::ZX80O81P>(TargetPlatform::ZX8081, "80");
|
||||||
Format("81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 81
|
accumulator.try_standard<Tape::ZX80O81P>(TargetPlatform::ZX8081, "81");
|
||||||
Format("a26", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // A26
|
|
||||||
Format("adf", result.disks, Disk::DiskImageHolder<Storage::Disk::AcornADF>, TargetPlatform::Acorn) // ADF (Acorn)
|
|
||||||
Format("adf", result.disks, Disk::DiskImageHolder<Storage::Disk::AmigaADF>, TargetPlatform::Amiga) // ADF (Amiga)
|
|
||||||
Format("adl", result.disks, Disk::DiskImageHolder<Storage::Disk::AcornADF>, TargetPlatform::Acorn) // ADL
|
|
||||||
Format("bin", result.cartridges, Cartridge::BinaryDump, TargetPlatform::AllCartridge) // BIN (cartridge dump)
|
|
||||||
Format("cas", result.tapes, Tape::CAS, TargetPlatform::MSX) // CAS
|
|
||||||
Format("cdt", result.tapes, Tape::TZX, TargetPlatform::AmstradCPC) // CDT
|
|
||||||
Format("col", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Coleco) // COL
|
|
||||||
Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW
|
|
||||||
Format("d64", result.disks, Disk::DiskImageHolder<Storage::Disk::D64>, TargetPlatform::Commodore) // D64
|
|
||||||
Format("dat", result.mass_storage_devices, MassStorage::DAT, TargetPlatform::Acorn) // DAT
|
|
||||||
Format("dmk", result.disks, Disk::DiskImageHolder<Storage::Disk::DMK>, TargetPlatform::MSX) // DMK
|
|
||||||
Format("do", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DO
|
|
||||||
Format("dsd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // DSD
|
|
||||||
Format( "dsk",
|
|
||||||
result.disks,
|
|
||||||
Disk::DiskImageHolder<Storage::Disk::CPCDSK>,
|
|
||||||
TargetPlatform::AmstradCPC | TargetPlatform::Oric | TargetPlatform::ZXSpectrum) // DSK (Amstrad CPC, etc)
|
|
||||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DSK (Apple II)
|
|
||||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::MacintoshIMG>, TargetPlatform::Macintosh) // DSK (Macintosh, floppy disk)
|
|
||||||
Format("dsk", result.mass_storage_devices, MassStorage::HFV, TargetPlatform::Macintosh) // DSK (Macintosh, hard disk, single volume image)
|
|
||||||
Format("dsk", result.mass_storage_devices, MassStorage::DSK, TargetPlatform::Macintosh) // DSK (Macintosh, hard disk, full device image)
|
|
||||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::FAT12>, TargetPlatform::MSX) // DSK (MSX)
|
|
||||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::OricMFMDSK>, TargetPlatform::Oric) // DSK (Oric)
|
|
||||||
Format("g64", result.disks, Disk::DiskImageHolder<Storage::Disk::G64>, TargetPlatform::Commodore) // G64
|
|
||||||
Format("hdv", result.mass_storage_devices, MassStorage::HDV, TargetPlatform::AppleII) // HDV (Apple II, hard disk, single volume image)
|
|
||||||
Format( "hfe",
|
|
||||||
result.disks,
|
|
||||||
Disk::DiskImageHolder<Storage::Disk::HFE>,
|
|
||||||
TargetPlatform::Acorn | TargetPlatform::AmstradCPC | TargetPlatform::Commodore | TargetPlatform::Oric | TargetPlatform::ZXSpectrum)
|
|
||||||
// HFE (TODO: switch to AllDisk once the MSX stops being so greedy)
|
|
||||||
Format("img", result.disks, Disk::DiskImageHolder<Storage::Disk::MacintoshIMG>, TargetPlatform::Macintosh) // IMG (DiskCopy 4.2)
|
|
||||||
Format("image", result.disks, Disk::DiskImageHolder<Storage::Disk::MacintoshIMG>, TargetPlatform::Macintosh) // IMG (DiskCopy 4.2)
|
|
||||||
Format("img", result.disks, Disk::DiskImageHolder<Storage::Disk::FAT12>, TargetPlatform::Enterprise) // IMG (Enterprise/MS-DOS style)
|
|
||||||
Format( "ipf",
|
|
||||||
result.disks,
|
|
||||||
Disk::DiskImageHolder<Storage::Disk::IPF>,
|
|
||||||
TargetPlatform::Amiga | TargetPlatform::AtariST | TargetPlatform::AmstradCPC | TargetPlatform::ZXSpectrum) // IPF
|
|
||||||
Format("msa", result.disks, Disk::DiskImageHolder<Storage::Disk::MSA>, TargetPlatform::AtariST) // MSA
|
|
||||||
Format("mx2", result.cartridges, Cartridge::BinaryDump, TargetPlatform::MSX) // MX2
|
|
||||||
Format("nib", result.disks, Disk::DiskImageHolder<Storage::Disk::NIB>, TargetPlatform::DiskII) // NIB
|
|
||||||
Format("o", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // O
|
|
||||||
Format("p", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P
|
|
||||||
Format("po", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // PO (original Apple II kind)
|
|
||||||
|
|
||||||
// PO (Apple IIgs kind)
|
accumulator.try_standard<Cartridge::BinaryDump>(TargetPlatform::Atari2600, "a26");
|
||||||
if(extension == "po") {
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::AcornADF>>(TargetPlatform::Acorn, "adf");
|
||||||
TryInsert(result.disks, Disk::DiskImageHolder<Storage::Disk::MacintoshIMG>, TargetPlatform::AppleIIgs, file_name, Storage::Disk::MacintoshIMG::FixedType::GCR)
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::AmigaADF>>(TargetPlatform::Amiga, "adf");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::AcornADF>>(TargetPlatform::Acorn, "adl");
|
||||||
|
|
||||||
|
accumulator.try_standard<Cartridge::BinaryDump>(TargetPlatform::AllCartridge, "bin");
|
||||||
|
|
||||||
|
accumulator.try_standard<Tape::CAS>(TargetPlatform::MSX, "cas");
|
||||||
|
accumulator.try_standard<Tape::TZX>(TargetPlatform::AmstradCPC, "cdt");
|
||||||
|
accumulator.try_standard<Cartridge::BinaryDump>(TargetPlatform::Coleco, "col");
|
||||||
|
accumulator.try_standard<Tape::CSW>(TargetPlatform::AllTape, "csw");
|
||||||
|
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::D64>>(TargetPlatform::Commodore, "d64");
|
||||||
|
accumulator.try_standard<MassStorage::DAT>(TargetPlatform::Acorn, "dat");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::DMK>>(TargetPlatform::MSX, "dmk");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::AppleDSK>>(TargetPlatform::DiskII, "do");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::SSD>>(TargetPlatform::Acorn, "dsd");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::CPCDSK>>(
|
||||||
|
TargetPlatform::AmstradCPC | TargetPlatform::Oric | TargetPlatform::ZXSpectrum, "dsk");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::AppleDSK>>(TargetPlatform::DiskII, "dsk");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::MacintoshIMG>>(TargetPlatform::Macintosh, "dsk");
|
||||||
|
accumulator.try_standard<MassStorage::HFV>(TargetPlatform::Macintosh, "dsk");
|
||||||
|
accumulator.try_standard<MassStorage::DSK>(TargetPlatform::Macintosh, "dsk");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::FAT12>>(TargetPlatform::MSX, "dsk");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::OricMFMDSK>>(TargetPlatform::Oric, "dsk");
|
||||||
|
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::G64>>(TargetPlatform::Commodore, "g64");
|
||||||
|
|
||||||
|
accumulator.try_standard<MassStorage::HDV>(TargetPlatform::AppleII, "hdv");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::HFE>>(
|
||||||
|
TargetPlatform::Acorn | TargetPlatform::AmstradCPC | TargetPlatform::Commodore | TargetPlatform::Oric | TargetPlatform::ZXSpectrum,
|
||||||
|
"hfe"); // TODO: switch to AllDisk once the MSX stops being so greedy.
|
||||||
|
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::FAT12>>(TargetPlatform::PCCompatible, "ima");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::MacintoshIMG>>(TargetPlatform::Macintosh, "image");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::IMD>>(TargetPlatform::PCCompatible, "imd");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::MacintoshIMG>>(TargetPlatform::Macintosh, "img");
|
||||||
|
|
||||||
|
// Treat PC booter as a potential backup only if this doesn't parse as a FAT12.
|
||||||
|
if(accumulator.name_matches("img")) {
|
||||||
|
try {
|
||||||
|
accumulator.insert<Disk::DiskImageHolder<Disk::FAT12>>(TargetPlatform::FAT12, file_name);
|
||||||
|
} catch(...) {
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::PCBooter>>(TargetPlatform::PCCompatible, "img");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Format("p81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P81
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::IPF>>(
|
||||||
|
TargetPlatform::Amiga | TargetPlatform::AtariST | TargetPlatform::AmstradCPC | TargetPlatform::ZXSpectrum,
|
||||||
|
"ipf");
|
||||||
|
|
||||||
// PRG
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::MSA>>(TargetPlatform::AtariST, "msa");
|
||||||
if(extension == "prg") {
|
accumulator.try_standard<Cartridge::BinaryDump>(TargetPlatform::MSX, "mx2");
|
||||||
// try instantiating as a ROM; failing that accept as a tape
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::NIB>>(TargetPlatform::DiskII, "nib");
|
||||||
|
|
||||||
|
accumulator.try_standard<Tape::ZX80O81P>(TargetPlatform::ZX8081, "o");
|
||||||
|
accumulator.try_standard<Tape::ZX80O81P>(TargetPlatform::ZX8081, "p");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::AppleDSK>>(TargetPlatform::DiskII, "po");
|
||||||
|
|
||||||
|
if(accumulator.name_matches("po")) {
|
||||||
|
accumulator.try_insert<Disk::DiskImageHolder<Disk::MacintoshIMG>>(
|
||||||
|
TargetPlatform::AppleIIgs,
|
||||||
|
file_name, Disk::MacintoshIMG::FixedType::GCR);
|
||||||
|
}
|
||||||
|
|
||||||
|
accumulator.try_standard<Tape::ZX80O81P>(TargetPlatform::ZX8081, "p81");
|
||||||
|
|
||||||
|
if(accumulator.name_matches("prg")) {
|
||||||
|
// Try instantiating as a ROM; failing that accept as a tape.
|
||||||
try {
|
try {
|
||||||
Insert(result.cartridges, Cartridge::PRG, TargetPlatform::Commodore, file_name)
|
accumulator.insert<Cartridge::PRG>(TargetPlatform::Commodore, file_name);
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
try {
|
try {
|
||||||
Insert(result.tapes, Tape::PRG, TargetPlatform::Commodore, file_name)
|
accumulator.insert<Tape::PRG>(TargetPlatform::Commodore, file_name);
|
||||||
} catch(...) {}
|
} catch(...) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Format( "rom",
|
accumulator.try_standard<Cartridge::BinaryDump>(
|
||||||
result.cartridges,
|
TargetPlatform::AcornElectron | TargetPlatform::Coleco | TargetPlatform::MSX,
|
||||||
Cartridge::BinaryDump,
|
"rom");
|
||||||
TargetPlatform::AcornElectron | TargetPlatform::Coleco | TargetPlatform::MSX) // ROM
|
|
||||||
Format("sg", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Sega) // SG
|
|
||||||
Format("sms", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Sega) // SMS
|
|
||||||
Format("ssd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // SSD
|
|
||||||
Format("st", result.disks, Disk::DiskImageHolder<Storage::Disk::FAT12>, TargetPlatform::AtariST) // ST
|
|
||||||
Format("stx", result.disks, Disk::DiskImageHolder<Storage::Disk::STX>, TargetPlatform::AtariST) // STX
|
|
||||||
Format("tap", result.tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP (Commodore)
|
|
||||||
Format("tap", result.tapes, Tape::OricTAP, TargetPlatform::Oric) // TAP (Oric)
|
|
||||||
Format("tap", result.tapes, Tape::ZXSpectrumTAP, TargetPlatform::ZXSpectrum) // TAP (ZX Spectrum)
|
|
||||||
Format("tsx", result.tapes, Tape::TZX, TargetPlatform::MSX) // TSX
|
|
||||||
Format("tzx", result.tapes, Tape::TZX, TargetPlatform::ZX8081 | TargetPlatform::ZXSpectrum) // TZX
|
|
||||||
Format("uef", result.tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape)
|
|
||||||
Format("woz", result.disks, Disk::DiskImageHolder<Storage::Disk::WOZ>, TargetPlatform::DiskII) // WOZ
|
|
||||||
|
|
||||||
#undef Format
|
accumulator.try_standard<Cartridge::BinaryDump>(TargetPlatform::Sega, "sg");
|
||||||
#undef Insert
|
accumulator.try_standard<Cartridge::BinaryDump>(TargetPlatform::Sega, "sms");
|
||||||
#undef TryInsert
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::SSD>>(TargetPlatform::Acorn, "ssd");
|
||||||
#undef InsertInstance
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::FAT12>>(TargetPlatform::AtariST, "st");
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::STX>>(TargetPlatform::AtariST, "stx");
|
||||||
|
|
||||||
return result;
|
accumulator.try_standard<Tape::CommodoreTAP>(TargetPlatform::Commodore, "tap");
|
||||||
|
accumulator.try_standard<Tape::OricTAP>(TargetPlatform::Oric, "tap");
|
||||||
|
accumulator.try_standard<Tape::ZXSpectrumTAP>(TargetPlatform::ZXSpectrum, "tap");
|
||||||
|
accumulator.try_standard<Tape::TZX>(TargetPlatform::MSX, "tsx");
|
||||||
|
accumulator.try_standard<Tape::TZX>(TargetPlatform::ZX8081 | TargetPlatform::ZXSpectrum, "tzx");
|
||||||
|
|
||||||
|
accumulator.try_standard<Tape::UEF>(TargetPlatform::Acorn, "uef");
|
||||||
|
|
||||||
|
accumulator.try_standard<Disk::DiskImageHolder<Disk::WOZ>>(TargetPlatform::DiskII, "woz");
|
||||||
|
|
||||||
|
return accumulator.media;
|
||||||
}
|
}
|
||||||
|
|
||||||
Media Analyser::Static::GetMedia(const std::string &file_name) {
|
Media Analyser::Static::GetMedia(const std::string &file_name) {
|
||||||
@@ -241,26 +304,28 @@ Media Analyser::Static::GetMedia(const std::string &file_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TargetList Analyser::Static::GetTargets(const std::string &file_name) {
|
TargetList Analyser::Static::GetTargets(const std::string &file_name) {
|
||||||
TargetList targets;
|
|
||||||
const std::string extension = get_extension(file_name);
|
const std::string extension = get_extension(file_name);
|
||||||
|
TargetList targets;
|
||||||
|
|
||||||
// Check whether the file directly identifies a target; if so then just return that.
|
// Check whether the file directly identifies a target; if so then just return that.
|
||||||
#define Format(ext, class) \
|
const auto try_snapshot = [&](const char *ext, auto loader) -> bool {
|
||||||
if(extension == ext) { \
|
if(extension != ext) {
|
||||||
try { \
|
return false;
|
||||||
auto target = Storage::State::class::load(file_name); \
|
|
||||||
if(target) { \
|
|
||||||
targets.push_back(std::move(target)); \
|
|
||||||
return targets; \
|
|
||||||
} \
|
|
||||||
} catch(...) {} \
|
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
auto target = loader(file_name);
|
||||||
|
if(target) {
|
||||||
|
targets.push_back(std::move(target));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch(...) {}
|
||||||
|
|
||||||
Format("sna", SNA);
|
return false;
|
||||||
Format("szx", SZX);
|
};
|
||||||
Format("z80", Z80);
|
|
||||||
|
|
||||||
#undef TryInsert
|
if(try_snapshot("sna", Storage::State::SNA::load)) return targets;
|
||||||
|
if(try_snapshot("szx", Storage::State::SZX::load)) return targets;
|
||||||
|
if(try_snapshot("z80", Storage::State::Z80::load)) return targets;
|
||||||
|
|
||||||
// Otherwise:
|
// Otherwise:
|
||||||
//
|
//
|
||||||
@@ -271,28 +336,33 @@ TargetList Analyser::Static::GetTargets(const std::string &file_name) {
|
|||||||
|
|
||||||
// Hand off to platform-specific determination of whether these
|
// Hand off to platform-specific determination of whether these
|
||||||
// things are actually compatible and, if so, how to load them.
|
// things are actually compatible and, if so, how to load them.
|
||||||
#define Append(x) if(potential_platforms & TargetPlatform::x) {\
|
const auto append = [&](TargetPlatform::IntType platform, auto evaluator) {
|
||||||
auto new_targets = x::GetTargets(media, file_name, potential_platforms);\
|
if(!(potential_platforms & platform)) {
|
||||||
std::move(new_targets.begin(), new_targets.end(), std::back_inserter(targets));\
|
return;
|
||||||
}
|
}
|
||||||
Append(Acorn);
|
auto new_targets = evaluator(media, file_name, potential_platforms);
|
||||||
Append(AmstradCPC);
|
std::move(new_targets.begin(), new_targets.end(), std::back_inserter(targets));
|
||||||
Append(AppleII);
|
};
|
||||||
Append(AppleIIgs);
|
|
||||||
Append(Amiga);
|
append(TargetPlatform::Acorn, Acorn::GetTargets);
|
||||||
Append(Atari2600);
|
append(TargetPlatform::AmstradCPC, AmstradCPC::GetTargets);
|
||||||
Append(AtariST);
|
append(TargetPlatform::AppleII, AppleII::GetTargets);
|
||||||
Append(Coleco);
|
append(TargetPlatform::AppleIIgs, AppleIIgs::GetTargets);
|
||||||
Append(Commodore);
|
append(TargetPlatform::Amiga, Amiga::GetTargets);
|
||||||
Append(DiskII);
|
append(TargetPlatform::Atari2600, Atari2600::GetTargets);
|
||||||
Append(Enterprise);
|
append(TargetPlatform::AtariST, AtariST::GetTargets);
|
||||||
Append(Macintosh);
|
append(TargetPlatform::Coleco, Coleco::GetTargets);
|
||||||
Append(MSX);
|
append(TargetPlatform::Commodore, Commodore::GetTargets);
|
||||||
Append(Oric);
|
append(TargetPlatform::DiskII, DiskII::GetTargets);
|
||||||
Append(Sega);
|
append(TargetPlatform::Enterprise, Enterprise::GetTargets);
|
||||||
Append(ZX8081);
|
append(TargetPlatform::FAT12, FAT12::GetTargets);
|
||||||
Append(ZXSpectrum);
|
append(TargetPlatform::Macintosh, Macintosh::GetTargets);
|
||||||
#undef Append
|
append(TargetPlatform::MSX, MSX::GetTargets);
|
||||||
|
append(TargetPlatform::Oric, Oric::GetTargets);
|
||||||
|
append(TargetPlatform::PCCompatible, PCCompatible::GetTargets);
|
||||||
|
append(TargetPlatform::Sega, Sega::GetTargets);
|
||||||
|
append(TargetPlatform::ZX8081, ZX8081::GetTargets);
|
||||||
|
append(TargetPlatform::ZXSpectrum, ZXSpectrum::GetTargets);
|
||||||
|
|
||||||
// Reset any tapes to their initial position.
|
// Reset any tapes to their initial position.
|
||||||
for(const auto &target : targets) {
|
for(const auto &target : targets) {
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef StaticAnalyser_hpp
|
#pragma once
|
||||||
#define StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../Machines.hpp"
|
#include "../Machines.hpp"
|
||||||
|
|
||||||
@@ -79,5 +78,3 @@ TargetList GetTargets(const std::string &file_name);
|
|||||||
Media GetMedia(const std::string &file_name);
|
Media GetMedia(const std::string &file_name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_ZX8081_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define Analyser_Static_ZX8081_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::ZX8081 {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_ZX8081_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_ZX8081_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Enum.hpp"
|
#include "../../../Reflection/Enum.hpp"
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
@@ -39,5 +38,3 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Analyser_Static_ZX8081_Target_h */
|
|
||||||
|
@@ -33,14 +33,14 @@ bool IsSpectrumTape(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IsSpectrumDisk(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
bool IsSpectrumDisk(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||||
Storage::Encodings::MFM::Parser parser(true, disk);
|
Storage::Encodings::MFM::Parser parser(Storage::Encodings::MFM::Density::Double, disk);
|
||||||
|
|
||||||
// Get logical sector 1; the Spectrum appears to support various physical
|
// Get logical sector 1; the Spectrum appears to support various physical
|
||||||
// sectors as sector 1.
|
// sectors as sector 1.
|
||||||
Storage::Encodings::MFM::Sector *boot_sector = nullptr;
|
const Storage::Encodings::MFM::Sector *boot_sector = nullptr;
|
||||||
uint8_t sector_mask = 0;
|
uint8_t sector_mask = 0;
|
||||||
while(!boot_sector) {
|
while(!boot_sector) {
|
||||||
boot_sector = parser.get_sector(0, 0, sector_mask + 1);
|
boot_sector = parser.sector(0, 0, sector_mask + 1);
|
||||||
sector_mask += 0x40;
|
sector_mask += 0x40;
|
||||||
if(!sector_mask) break;
|
if(!sector_mask) break;
|
||||||
}
|
}
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_ZXSpectrum_StaticAnalyser_hpp
|
#pragma once
|
||||||
#define Analyser_Static_ZXSpectrum_StaticAnalyser_hpp
|
|
||||||
|
|
||||||
#include "../StaticAnalyser.hpp"
|
#include "../StaticAnalyser.hpp"
|
||||||
#include "../../../Storage/TargetPlatforms.hpp"
|
#include "../../../Storage/TargetPlatforms.hpp"
|
||||||
@@ -18,5 +17,3 @@ namespace Analyser::Static::ZXSpectrum {
|
|||||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* StaticAnalyser_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef Analyser_Static_ZXSpectrum_Target_h
|
#pragma once
|
||||||
#define Analyser_Static_ZXSpectrum_Target_h
|
|
||||||
|
|
||||||
#include "../../../Reflection/Enum.hpp"
|
#include "../../../Reflection/Enum.hpp"
|
||||||
#include "../../../Reflection/Struct.hpp"
|
#include "../../../Reflection/Struct.hpp"
|
||||||
@@ -37,5 +36,3 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* Target_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ClockReceiver_hpp
|
#pragma once
|
||||||
#define ClockReceiver_hpp
|
|
||||||
|
|
||||||
#include "ForceInline.hpp"
|
#include "ForceInline.hpp"
|
||||||
|
|
||||||
@@ -277,5 +276,3 @@ template <class T> class HalfClockReceiver: public T {
|
|||||||
private:
|
private:
|
||||||
HalfCycles half_cycles_;
|
HalfCycles half_cycles_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* ClockReceiver_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ClockingHintSource_hpp
|
#pragma once
|
||||||
#define ClockingHintSource_hpp
|
|
||||||
|
|
||||||
namespace ClockingHint {
|
namespace ClockingHint {
|
||||||
|
|
||||||
@@ -84,5 +83,3 @@ class Source {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* ClockingHintSource_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef DeferredQueue_h
|
#pragma once
|
||||||
#define DeferredQueue_h
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -120,5 +119,3 @@ template <typename TimeUnit> class DeferredQueuePerformer: public DeferredQueue<
|
|||||||
private:
|
private:
|
||||||
std::function<void(TimeUnit)> target_;
|
std::function<void(TimeUnit)> target_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DeferredQueue_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef DeferredValue_h
|
#pragma once
|
||||||
#define DeferredValue_h
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Provides storage for a single deferred value: one with a current value and a certain number
|
Provides storage for a single deferred value: one with a current value and a certain number
|
||||||
@@ -44,5 +43,3 @@ template <int DeferredDepth, typename ValueT> class DeferredValue {
|
|||||||
(backlog[DeferredDepth / elements_per_uint32] & insert_mask) | (value << insert_shift);
|
(backlog[DeferredDepth / elements_per_uint32] & insert_mask) | (value << insert_shift);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* DeferredValue_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ForceInline_hpp
|
#pragma once
|
||||||
#define ForceInline_hpp
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
|
||||||
@@ -22,5 +21,3 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* ForceInline_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2019 Thomas Harte. All rights reserved.
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef JustInTime_h
|
#pragma once
|
||||||
#define JustInTime_h
|
|
||||||
|
|
||||||
#include "ClockReceiver.hpp"
|
#include "ClockReceiver.hpp"
|
||||||
#include "../Concurrency/AsyncTaskQueue.hpp"
|
#include "../Concurrency/AsyncTaskQueue.hpp"
|
||||||
@@ -26,7 +25,7 @@
|
|||||||
Machines that accumulate HalfCycle time but supply to a Cycle-counted device may supply a
|
Machines that accumulate HalfCycle time but supply to a Cycle-counted device may supply a
|
||||||
separate @c TargetTimeScale at template declaration.
|
separate @c TargetTimeScale at template declaration.
|
||||||
|
|
||||||
If the held object implements get_next_sequence_point() then it'll be used to flush implicitly
|
If the held object implements @c next_sequence_point() then it'll be used to flush implicitly
|
||||||
as and when sequence points are hit. Callers can use will_flush() to predict these.
|
as and when sequence points are hit. Callers can use will_flush() to predict these.
|
||||||
|
|
||||||
If the held object is a subclass of ClockingHint::Source, this template will register as an
|
If the held object is a subclass of ClockingHint::Source, this template will register as an
|
||||||
@@ -40,7 +39,7 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
|
|||||||
private:
|
private:
|
||||||
/*!
|
/*!
|
||||||
A std::unique_ptr deleter which causes an update_sequence_point to occur on the actor supplied
|
A std::unique_ptr deleter which causes an update_sequence_point to occur on the actor supplied
|
||||||
to it at construction if it implements get_next_sequence_point(). Otherwise destruction is a no-op.
|
to it at construction if it implements @c next_sequence_point(). Otherwise destruction is a no-op.
|
||||||
|
|
||||||
**Does not delete the object.**
|
**Does not delete the object.**
|
||||||
|
|
||||||
@@ -247,9 +246,9 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
|
|||||||
// going to be applied then do a direct max -> max translation rather than
|
// going to be applied then do a direct max -> max translation rather than
|
||||||
// allowing the arithmetic to overflow.
|
// allowing the arithmetic to overflow.
|
||||||
if constexpr (divider == 1 && std::is_same_v<LocalTimeScale, TargetTimeScale>) {
|
if constexpr (divider == 1 && std::is_same_v<LocalTimeScale, TargetTimeScale>) {
|
||||||
time_until_event_ = object_.get_next_sequence_point();
|
time_until_event_ = object_.next_sequence_point();
|
||||||
} else {
|
} else {
|
||||||
const auto time = object_.get_next_sequence_point();
|
const auto time = object_.next_sequence_point();
|
||||||
if(time == TargetTimeScale::max()) {
|
if(time == TargetTimeScale::max()) {
|
||||||
time_until_event_ = LocalTimeScale::max();
|
time_until_event_ = LocalTimeScale::max();
|
||||||
} else {
|
} else {
|
||||||
@@ -272,7 +271,7 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
|
|||||||
bool did_flush_ = false;
|
bool did_flush_ = false;
|
||||||
|
|
||||||
template <typename S, typename = void> struct has_sequence_points : std::false_type {};
|
template <typename S, typename = void> struct has_sequence_points : std::false_type {};
|
||||||
template <typename S> struct has_sequence_points<S, decltype(void(std::declval<S &>().get_next_sequence_point()))> : std::true_type {};
|
template <typename S> struct has_sequence_points<S, decltype(void(std::declval<S &>().next_sequence_point()))> : std::true_type {};
|
||||||
|
|
||||||
ClockingHint::Preference clocking_preference_ = ClockingHint::Preference::JustInTime;
|
ClockingHint::Preference clocking_preference_ = ClockingHint::Preference::JustInTime;
|
||||||
void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference clocking) {
|
void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference clocking) {
|
||||||
@@ -335,5 +334,3 @@ template <class T, class LocalTimeScale = HalfCycles, class TargetTimeScale = Lo
|
|||||||
bool is_flushed_ = true;
|
bool is_flushed_ = true;
|
||||||
Concurrency::AsyncTaskQueue<true> task_queue_;
|
Concurrency::AsyncTaskQueue<true> task_queue_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* JustInTime_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ScanSynchroniser_h
|
#pragma once
|
||||||
#define ScanSynchroniser_h
|
|
||||||
|
|
||||||
#include "../Outputs/ScanTarget.hpp"
|
#include "../Outputs/ScanTarget.hpp"
|
||||||
|
|
||||||
@@ -84,5 +83,3 @@ class ScanSynchroniser {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* ScanSynchroniser_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef TimeTypes_h
|
#pragma once
|
||||||
#define TimeTypes_h
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
@@ -25,6 +24,3 @@ inline Seconds seconds(Nanos nanos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* TimeTypes_h */
|
|
||||||
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef VSyncPredictor_hpp
|
#pragma once
|
||||||
#define VSyncPredictor_hpp
|
|
||||||
|
|
||||||
#include "TimeTypes.hpp"
|
#include "TimeTypes.hpp"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@@ -151,5 +150,3 @@ class VSyncPredictor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* VSyncPredictor_hpp */
|
|
||||||
|
@@ -9,10 +9,12 @@
|
|||||||
#include "1770.hpp"
|
#include "1770.hpp"
|
||||||
|
|
||||||
#include "../../Storage/Disk/Encodings/MFM/Constants.hpp"
|
#include "../../Storage/Disk/Encodings/MFM/Constants.hpp"
|
||||||
|
|
||||||
#define LOG_PREFIX "[WD FDC] "
|
|
||||||
#include "../../Outputs/Log.hpp"
|
#include "../../Outputs/Log.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Log::Logger<Log::Source::WDFDC> logger;
|
||||||
|
}
|
||||||
|
|
||||||
using namespace WD;
|
using namespace WD;
|
||||||
|
|
||||||
WD1770::WD1770(Personality p) :
|
WD1770::WD1770(Personality p) :
|
||||||
@@ -29,10 +31,10 @@ void WD1770::write(int address, uint8_t value) {
|
|||||||
if((value&0xf0) == 0xd0) {
|
if((value&0xf0) == 0xd0) {
|
||||||
if(value == 0xd0) {
|
if(value == 0xd0) {
|
||||||
// Force interrupt **immediately**.
|
// Force interrupt **immediately**.
|
||||||
LOG("Force interrupt immediately");
|
logger.info().append("Force interrupt immediately");
|
||||||
posit_event(int(Event1770::ForceInterrupt));
|
posit_event(int(Event1770::ForceInterrupt));
|
||||||
} else {
|
} else {
|
||||||
ERROR("!!!TODO: force interrupt!!!");
|
logger.error().append("TODO: force interrupt");
|
||||||
update_status([] (Status &status) {
|
update_status([] (Status &status) {
|
||||||
status.type = Status::One;
|
status.type = Status::One;
|
||||||
});
|
});
|
||||||
@@ -99,14 +101,14 @@ uint8_t WD1770::read(int address) {
|
|||||||
if(status_.type == Status::One)
|
if(status_.type == Status::One)
|
||||||
status |= (status_.spin_up ? Flag::SpinUp : 0);
|
status |= (status_.spin_up ? Flag::SpinUp : 0);
|
||||||
}
|
}
|
||||||
// LOG("Returned status " << PADHEX(2) << int(status) << " of type " << 1+int(status_.type));
|
// logger.info().append("Returned status %02x of type %d", status, 1+int(status_.type));
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
LOG("Returned track " << int(track_));
|
logger.info().append("Returned track %d", track_);
|
||||||
return track_;
|
return track_;
|
||||||
case 2:
|
case 2:
|
||||||
LOG("Returned sector " << int(sector_));
|
logger.info().append("Returned sector %d", sector_);
|
||||||
return sector_;
|
return sector_;
|
||||||
case 3:
|
case 3:
|
||||||
update_status([] (Status &status) {
|
update_status([] (Status &status) {
|
||||||
@@ -212,7 +214,7 @@ void WD1770::posit_event(int new_event_type) {
|
|||||||
// Wait for a new command, branch to the appropriate handler.
|
// Wait for a new command, branch to the appropriate handler.
|
||||||
case 0:
|
case 0:
|
||||||
wait_for_command:
|
wait_for_command:
|
||||||
LOG("Idle...");
|
logger.info().append("Idle...");
|
||||||
set_data_mode(DataMode::Scanning);
|
set_data_mode(DataMode::Scanning);
|
||||||
index_hole_count_ = 0;
|
index_hole_count_ = 0;
|
||||||
|
|
||||||
@@ -229,7 +231,7 @@ void WD1770::posit_event(int new_event_type) {
|
|||||||
status.track_zero = false; // Always reset by a non-type 1; so reset regardless and set properly later.
|
status.track_zero = false; // Always reset by a non-type 1; so reset regardless and set properly later.
|
||||||
});
|
});
|
||||||
|
|
||||||
LOG("Starting " << PADHEX(2) << int(command_));
|
logger.info().append("Starting %02x", command_);
|
||||||
|
|
||||||
if(!(command_ & 0x80)) goto begin_type_1;
|
if(!(command_ & 0x80)) goto begin_type_1;
|
||||||
if(!(command_ & 0x40)) goto begin_type_2;
|
if(!(command_ & 0x40)) goto begin_type_2;
|
||||||
@@ -259,7 +261,7 @@ void WD1770::posit_event(int new_event_type) {
|
|||||||
status.data_request = false;
|
status.data_request = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
LOG("Step/Seek/Restore with track " << int(track_) << " data " << int(data_));
|
logger.info().append("Step/Seek/Restore with track %d data %d", track_, data_);
|
||||||
if(!has_motor_on_line() && !has_head_load_line()) goto test_type1_type;
|
if(!has_motor_on_line() && !has_head_load_line()) goto test_type1_type;
|
||||||
|
|
||||||
if(has_motor_on_line()) goto begin_type1_spin_up;
|
if(has_motor_on_line()) goto begin_type1_spin_up;
|
||||||
@@ -339,7 +341,7 @@ void WD1770::posit_event(int new_event_type) {
|
|||||||
READ_ID();
|
READ_ID();
|
||||||
|
|
||||||
if(index_hole_count_ == 6) {
|
if(index_hole_count_ == 6) {
|
||||||
LOG("Nothing found to verify");
|
logger.info().append("Nothing found to verify");
|
||||||
update_status([] (Status &status) {
|
update_status([] (Status &status) {
|
||||||
status.seek_error = true;
|
status.seek_error = true;
|
||||||
});
|
});
|
||||||
@@ -357,7 +359,7 @@ void WD1770::posit_event(int new_event_type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(header_[0] == track_) {
|
if(header_[0] == track_) {
|
||||||
LOG("Reached track " << std::dec << int(track_));
|
logger.info().append("Reached track %d", track_);
|
||||||
update_status([] (Status &status) {
|
update_status([] (Status &status) {
|
||||||
status.crc_error = false;
|
status.crc_error = false;
|
||||||
});
|
});
|
||||||
@@ -430,7 +432,7 @@ void WD1770::posit_event(int new_event_type) {
|
|||||||
READ_ID();
|
READ_ID();
|
||||||
|
|
||||||
if(index_hole_count_ == 5) {
|
if(index_hole_count_ == 5) {
|
||||||
LOG("Failed to find sector " << std::dec << int(sector_));
|
logger.info().append("Failed to find sector %d", sector_);
|
||||||
update_status([] (Status &status) {
|
update_status([] (Status &status) {
|
||||||
status.record_not_found = true;
|
status.record_not_found = true;
|
||||||
});
|
});
|
||||||
@@ -440,12 +442,12 @@ void WD1770::posit_event(int new_event_type) {
|
|||||||
distance_into_section_ = 0;
|
distance_into_section_ = 0;
|
||||||
set_data_mode(DataMode::Scanning);
|
set_data_mode(DataMode::Scanning);
|
||||||
|
|
||||||
LOG("Considering " << std::dec << int(header_[0]) << "/" << int(header_[2]));
|
logger.info().append("Considering %d/%d", header_[0], header_[2]);
|
||||||
if( header_[0] == track_ && header_[2] == sector_ &&
|
if( header_[0] == track_ && header_[2] == sector_ &&
|
||||||
(has_motor_on_line() || !(command_&0x02) || ((command_&0x08) >> 3) == header_[1])) {
|
(has_motor_on_line() || !(command_&0x02) || ((command_&0x08) >> 3) == header_[1])) {
|
||||||
LOG("Found " << std::dec << int(header_[0]) << "/" << int(header_[2]));
|
logger.info().append("Found %d/%d", header_[0], header_[2]);
|
||||||
if(get_crc_generator().get_value()) {
|
if(get_crc_generator().get_value()) {
|
||||||
LOG("CRC error; back to searching");
|
logger.info().append("CRC error; back to searching");
|
||||||
update_status([] (Status &status) {
|
update_status([] (Status &status) {
|
||||||
status.crc_error = true;
|
status.crc_error = true;
|
||||||
});
|
});
|
||||||
@@ -503,18 +505,18 @@ void WD1770::posit_event(int new_event_type) {
|
|||||||
set_data_mode(DataMode::Scanning);
|
set_data_mode(DataMode::Scanning);
|
||||||
|
|
||||||
if(get_crc_generator().get_value()) {
|
if(get_crc_generator().get_value()) {
|
||||||
LOG("CRC error; terminating");
|
logger.info().append("CRC error; terminating");
|
||||||
update_status([] (Status &status) {
|
update_status([] (Status &status) {
|
||||||
status.crc_error = true;
|
status.crc_error = true;
|
||||||
});
|
});
|
||||||
goto wait_for_command;
|
goto wait_for_command;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("Finished reading sector " << std::dec << int(sector_));
|
logger.info().append("Finished reading sector %d", sector_);
|
||||||
|
|
||||||
if(command_ & 0x10) {
|
if(command_ & 0x10) {
|
||||||
sector_++;
|
sector_++;
|
||||||
LOG("Advancing to search for sector " << std::dec << int(sector_));
|
logger.info().append("Advancing to search for sector %d", sector_);
|
||||||
goto test_type2_write_protection;
|
goto test_type2_write_protection;
|
||||||
}
|
}
|
||||||
goto wait_for_command;
|
goto wait_for_command;
|
||||||
@@ -598,7 +600,7 @@ void WD1770::posit_event(int new_event_type) {
|
|||||||
sector_++;
|
sector_++;
|
||||||
goto test_type2_write_protection;
|
goto test_type2_write_protection;
|
||||||
}
|
}
|
||||||
LOG("Wrote sector " << std::dec << int(sector_));
|
logger.info().append("Wrote sector %d", sector_);
|
||||||
goto wait_for_command;
|
goto wait_for_command;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef _770_hpp
|
#pragma once
|
||||||
#define _770_hpp
|
|
||||||
|
|
||||||
#include "../../Storage/Disk/Controller/MFMDiskController.hpp"
|
#include "../../Storage/Disk/Controller/MFMDiskController.hpp"
|
||||||
|
|
||||||
@@ -141,5 +140,3 @@ class WD1770: public Storage::Disk::MFMController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _770_hpp */
|
|
||||||
|
@@ -8,13 +8,11 @@
|
|||||||
|
|
||||||
#include "ncr5380.hpp"
|
#include "ncr5380.hpp"
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
#define NDEBUG
|
|
||||||
#endif
|
|
||||||
#define LOG_PREFIX "[5380] "
|
|
||||||
|
|
||||||
#include "../../Outputs/Log.hpp"
|
#include "../../Outputs/Log.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Log::Logger<Log::Source::NCR5380> logger;
|
||||||
|
}
|
||||||
// TODO:
|
// TODO:
|
||||||
//
|
//
|
||||||
// end_of_dma_ should be set if: /EOP && /DACK && (/RD || /WR); for at least 100ns.
|
// end_of_dma_ should be set if: /EOP && /DACK && (/RD || /WR); for at least 100ns.
|
||||||
@@ -38,7 +36,7 @@ NCR5380::NCR5380(SCSI::Bus &bus, int clock_rate) :
|
|||||||
void NCR5380::write(int address, uint8_t value, bool) {
|
void NCR5380::write(int address, uint8_t value, bool) {
|
||||||
switch(address & 7) {
|
switch(address & 7) {
|
||||||
case 0:
|
case 0:
|
||||||
LOG("[0] Set current SCSI bus state to " << PADHEX(2) << int(value));
|
logger.info().append("[0] Set current SCSI bus state to %02x", value);
|
||||||
|
|
||||||
data_bus_ = value;
|
data_bus_ = value;
|
||||||
if(dma_request_ && dma_operation_ == DMAOperation::Send) {
|
if(dma_request_ && dma_operation_ == DMAOperation::Send) {
|
||||||
@@ -47,7 +45,7 @@ void NCR5380::write(int address, uint8_t value, bool) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: {
|
case 1: {
|
||||||
LOG("[1] Initiator command register set: " << PADHEX(2) << int(value));
|
logger.info().append("[1] Initiator command register set: %02x", value);
|
||||||
initiator_command_ = value;
|
initiator_command_ = value;
|
||||||
|
|
||||||
bus_output_ &= ~(Line::Reset | Line::Acknowledge | Line::Busy | Line::SelectTarget | Line::Attention);
|
bus_output_ &= ~(Line::Reset | Line::Acknowledge | Line::Busy | Line::SelectTarget | Line::Attention);
|
||||||
@@ -63,7 +61,7 @@ void NCR5380::write(int address, uint8_t value, bool) {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
LOG("[2] Set mode: " << PADHEX(2) << int(value));
|
logger.info().append("[2] Set mode: %02x", value);
|
||||||
mode_ = value;
|
mode_ = value;
|
||||||
|
|
||||||
// bit 7: 1 = use block mode DMA mode (if DMA mode is also enabled)
|
// bit 7: 1 = use block mode DMA mode (if DMA mode is also enabled)
|
||||||
@@ -104,27 +102,27 @@ void NCR5380::write(int address, uint8_t value, bool) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 3: {
|
case 3: {
|
||||||
LOG("[3] Set target command: " << PADHEX(2) << int(value));
|
logger.info().append("[3] Set target command: %02x", value);
|
||||||
target_command_ = value;
|
target_command_ = value;
|
||||||
update_control_output();
|
update_control_output();
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
LOG("[4] Set select enabled: " << PADHEX(2) << int(value));
|
logger.info().append("[4] Set select enabled: %02x", value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
LOG("[5] Start DMA send: " << PADHEX(2) << int(value));
|
logger.info().append("[5] Start DMA send: %02x", value);
|
||||||
dma_operation_ = DMAOperation::Send;
|
dma_operation_ = DMAOperation::Send;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
LOG("[6] Start DMA target receive: " << PADHEX(2) << int(value));
|
logger.info().append("[6] Start DMA target receive: %02x", value);
|
||||||
dma_operation_ = DMAOperation::TargetReceive;
|
dma_operation_ = DMAOperation::TargetReceive;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7:
|
case 7:
|
||||||
LOG("[7] Start DMA initiator receive: " << PADHEX(2) << int(value));
|
logger.info().append("[7] Start DMA initiator receive: %02x", value);
|
||||||
dma_operation_ = DMAOperation::InitiatorReceive;
|
dma_operation_ = DMAOperation::InitiatorReceive;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -148,7 +146,7 @@ void NCR5380::write(int address, uint8_t value, bool) {
|
|||||||
uint8_t NCR5380::read(int address, bool) {
|
uint8_t NCR5380::read(int address, bool) {
|
||||||
switch(address & 7) {
|
switch(address & 7) {
|
||||||
case 0:
|
case 0:
|
||||||
LOG("[0] Get current SCSI bus state: " << PADHEX(2) << (bus_.get_state() & 0xff));
|
logger.info().append("[0] Get current SCSI bus state: %02x", (bus_.get_state() & 0xff));
|
||||||
|
|
||||||
if(dma_request_ && dma_operation_ == DMAOperation::InitiatorReceive) {
|
if(dma_request_ && dma_operation_ == DMAOperation::InitiatorReceive) {
|
||||||
return dma_acknowledge();
|
return dma_acknowledge();
|
||||||
@@ -156,7 +154,7 @@ uint8_t NCR5380::read(int address, bool) {
|
|||||||
return uint8_t(bus_.get_state());
|
return uint8_t(bus_.get_state());
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
LOG("[1] Initiator command register get: " << (arbitration_in_progress_ ? 'p' : '-') << (lost_arbitration_ ? 'l' : '-'));
|
logger.info().append("[1] Initiator command register get: %c%c", arbitration_in_progress_ ? 'p' : '-', lost_arbitration_ ? 'l' : '-');
|
||||||
return
|
return
|
||||||
// Bits repeated as they were set.
|
// Bits repeated as they were set.
|
||||||
(initiator_command_ & ~0x60) |
|
(initiator_command_ & ~0x60) |
|
||||||
@@ -168,11 +166,11 @@ uint8_t NCR5380::read(int address, bool) {
|
|||||||
(lost_arbitration_ ? 0x20 : 0x00);
|
(lost_arbitration_ ? 0x20 : 0x00);
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
LOG("[2] Get mode");
|
logger.info().append("[2] Get mode");
|
||||||
return mode_;
|
return mode_;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
LOG("[3] Get target command");
|
logger.info().append("[3] Get target command");
|
||||||
return target_command_;
|
return target_command_;
|
||||||
|
|
||||||
case 4: {
|
case 4: {
|
||||||
@@ -186,7 +184,7 @@ uint8_t NCR5380::read(int address, bool) {
|
|||||||
((bus_state & Line::Input) ? 0x04 : 0x00) |
|
((bus_state & Line::Input) ? 0x04 : 0x00) |
|
||||||
((bus_state & Line::SelectTarget) ? 0x02 : 0x00) |
|
((bus_state & Line::SelectTarget) ? 0x02 : 0x00) |
|
||||||
((bus_state & Line::Parity) ? 0x01 : 0x00);
|
((bus_state & Line::Parity) ? 0x01 : 0x00);
|
||||||
LOG("[4] Get current bus state: " << PADHEX(2) << int(result));
|
logger.info().append("[4] Get current bus state: %02x", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,16 +199,16 @@ uint8_t NCR5380::read(int address, bool) {
|
|||||||
/* b2 = busy error */
|
/* b2 = busy error */
|
||||||
((bus_state & Line::Attention) ? 0x02 : 0x00) |
|
((bus_state & Line::Attention) ? 0x02 : 0x00) |
|
||||||
((bus_state & Line::Acknowledge) ? 0x01 : 0x00);
|
((bus_state & Line::Acknowledge) ? 0x01 : 0x00);
|
||||||
LOG("[5] Get bus and status: " << PADHEX(2) << int(result));
|
logger.info().append("[5] Get bus and status: %02x", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
LOG("[6] Get input data");
|
logger.info().append("[6] Get input data");
|
||||||
return 0xff;
|
return 0xff;
|
||||||
|
|
||||||
case 7:
|
case 7:
|
||||||
LOG("[7] Reset parity/interrupt");
|
logger.info().append("[7] Reset parity/interrupt");
|
||||||
irq_ = false;
|
irq_ = false;
|
||||||
return 0xff;
|
return 0xff;
|
||||||
}
|
}
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2019 Thomas Harte. All rights reserved.
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ncr5380_hpp
|
#pragma once
|
||||||
#define ncr5380_hpp
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@@ -86,5 +85,3 @@ class NCR5380 final: public SCSI::Bus::Observer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* ncr5380_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef _522_hpp
|
#pragma once
|
||||||
#define _522_hpp
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@@ -140,5 +139,3 @@ template <class BusHandlerT> class MOS6522: public MOS6522Storage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#include "Implementation/6522Implementation.hpp"
|
#include "Implementation/6522Implementation.hpp"
|
||||||
|
|
||||||
#endif /* _522_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef _522Storage_hpp
|
#pragma once
|
||||||
#define _522Storage_hpp
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@@ -107,5 +106,3 @@ class MOS6522Storage {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _522Storage_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef _526_h
|
#pragma once
|
||||||
#define _526_h
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@@ -88,5 +87,3 @@ template <typename PortHandlerT, Personality personality> class MOS6526:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#include "Implementation/6526Implementation.hpp"
|
#include "Implementation/6526Implementation.hpp"
|
||||||
|
|
||||||
#endif /* _526_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef _526Implementation_h
|
#pragma once
|
||||||
#define _526Implementation_h
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -238,5 +237,3 @@ bool MOS6526<BusHandlerT, personality>::serial_line_did_produce_bit(Serial::Line
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _526Implementation_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef _526Storage_h
|
#pragma once
|
||||||
#define _526Storage_h
|
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
@@ -322,8 +321,6 @@ struct MOS6526Storage {
|
|||||||
static constexpr int TestInputNow = 1 << 8;
|
static constexpr int TestInputNow = 1 << 8;
|
||||||
|
|
||||||
static constexpr int PendingClearMask = ~(ReloadNow | OneShotNow | ApplyClockNow);
|
static constexpr int PendingClearMask = ~(ReloadNow | OneShotNow | ApplyClockNow);
|
||||||
|
|
||||||
bool active_ = false;
|
|
||||||
} counter_[2];
|
} counter_[2];
|
||||||
|
|
||||||
static constexpr int InterruptInOne = 1 << 0;
|
static constexpr int InterruptInOne = 1 << 0;
|
||||||
@@ -333,5 +330,3 @@ struct MOS6526Storage {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _526Storage_h */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef _532_hpp
|
#pragma once
|
||||||
#define _532_hpp
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -188,5 +187,3 @@ template <class T> class MOS6532 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _532_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef _560_hpp
|
#pragma once
|
||||||
#define _560_hpp
|
|
||||||
|
|
||||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||||
#include "../../Concurrency/AsyncTaskQueue.hpp"
|
#include "../../Concurrency/AsyncTaskQueue.hpp"
|
||||||
@@ -278,7 +277,7 @@ template <class BusHandler> class MOS6560 {
|
|||||||
switch(output_state_) {
|
switch(output_state_) {
|
||||||
case State::Sync: crt_.output_sync(cycles_in_state_ * 4); break;
|
case State::Sync: crt_.output_sync(cycles_in_state_ * 4); break;
|
||||||
case State::ColourBurst: crt_.output_colour_burst(cycles_in_state_ * 4, (is_odd_frame_ || is_odd_line_) ? 128 : 0); break;
|
case State::ColourBurst: crt_.output_colour_burst(cycles_in_state_ * 4, (is_odd_frame_ || is_odd_line_) ? 128 : 0); break;
|
||||||
case State::Border: output_border(cycles_in_state_ * 4); break;
|
case State::Border: crt_.output_level<uint16_t>(cycles_in_state_ * 4, registers_.borderColour); break;
|
||||||
case State::Pixels: crt_.output_data(cycles_in_state_ * 4); break;
|
case State::Pixels: crt_.output_data(cycles_in_state_ * 4); break;
|
||||||
}
|
}
|
||||||
output_state_ = this_state_;
|
output_state_ = this_state_;
|
||||||
@@ -400,7 +399,7 @@ template <class BusHandler> class MOS6560 {
|
|||||||
case 0xf: {
|
case 0xf: {
|
||||||
uint16_t new_border_colour = colours_[value & 0x07];
|
uint16_t new_border_colour = colours_[value & 0x07];
|
||||||
if(this_state_ == State::Border && new_border_colour != registers_.borderColour) {
|
if(this_state_ == State::Border && new_border_colour != registers_.borderColour) {
|
||||||
output_border(cycles_in_state_ * 4);
|
crt_.output_level<uint16_t>(cycles_in_state_ * 4, registers_.borderColour);
|
||||||
cycles_in_state_ = 0;
|
cycles_in_state_ = 0;
|
||||||
}
|
}
|
||||||
registers_.invertedCells = !((value >> 3)&1);
|
registers_.invertedCells = !((value >> 3)&1);
|
||||||
@@ -503,11 +502,6 @@ template <class BusHandler> class MOS6560 {
|
|||||||
uint16_t colours_[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
uint16_t colours_[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
uint16_t *pixel_pointer = nullptr;
|
uint16_t *pixel_pointer = nullptr;
|
||||||
void output_border(int number_of_cycles) {
|
|
||||||
uint16_t *colour_pointer = reinterpret_cast<uint16_t *>(crt_.begin_data(1));
|
|
||||||
if(colour_pointer) *colour_pointer = registers_.borderColour;
|
|
||||||
crt_.output_level(number_of_cycles);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int cycles_per_line = 0;
|
int cycles_per_line = 0;
|
||||||
@@ -520,5 +514,3 @@ template <class BusHandler> class MOS6560 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _560_hpp */
|
|
||||||
|
@@ -6,8 +6,7 @@
|
|||||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef CRTC6845_hpp
|
#pragma once
|
||||||
#define CRTC6845_hpp
|
|
||||||
|
|
||||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||||
|
|
||||||
@@ -23,6 +22,10 @@ struct BusState {
|
|||||||
bool cursor = false;
|
bool cursor = false;
|
||||||
uint16_t refresh_address = 0;
|
uint16_t refresh_address = 0;
|
||||||
uint16_t row_address = 0;
|
uint16_t row_address = 0;
|
||||||
|
|
||||||
|
// Not strictly part of the bus state; provided because the partition between 6845 and bus handler
|
||||||
|
// doesn't quite hold up in some emulated systems where the two are integrated and share more state.
|
||||||
|
int field_count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BusHandler {
|
class BusHandler {
|
||||||
@@ -42,30 +45,48 @@ class BusHandler {
|
|||||||
void perform_bus_cycle_phase2(const BusState &) {}
|
void perform_bus_cycle_phase2(const BusState &) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Personality {
|
enum class Personality {
|
||||||
HD6845S, // Type 0 in CPC parlance. Zero-width HSYNC available, no status, programmable VSYNC length.
|
HD6845S, // Type 0 in CPC parlance. Zero-width HSYNC available, no status, programmable VSYNC length.
|
||||||
// Considered exactly identical to the UM6845, so this enum covers both.
|
// Considered exactly identical to the UM6845, so this enum covers both.
|
||||||
UM6845R, // Type 1 in CPC parlance. Status register, fixed-length VSYNC.
|
UM6845R, // Type 1 in CPC parlance. Status register, fixed-length VSYNC.
|
||||||
MC6845, // Type 2. No status register, fixed-length VSYNC, no zero-length HSYNC.
|
MC6845, // Type 2. No status register, fixed-length VSYNC, no zero-length HSYNC.
|
||||||
AMS40226 // Type 3. Status is get register, fixed-length VSYNC, no zero-length HSYNC.
|
AMS40226, // Type 3. Status is get register, fixed-length VSYNC, no zero-length HSYNC.
|
||||||
|
|
||||||
|
EGA, // Extended EGA-style CRTC; uses 16-bit addressing throughout.
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr bool is_egavga(Personality p) {
|
||||||
|
return p >= Personality::EGA;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.pcjs.org/blog/2018/03/20/ advises that "the behavior of bits 5 and 6 [of register 10, the cursor start
|
||||||
|
// register is really card specific".
|
||||||
|
//
|
||||||
|
// This enum captures those specifics.
|
||||||
|
enum class CursorType {
|
||||||
|
/// No cursor signal is generated.
|
||||||
|
None,
|
||||||
|
/// MDA style: 00 => symmetric blinking; 01 or 10 => no blinking; 11 => short on, long off.
|
||||||
|
MDA,
|
||||||
|
/// EGA style: ignore the bits completely.
|
||||||
|
EGA,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO UM6845R and R12/R13; see http://www.cpcwiki.eu/index.php/CRTC#CRTC_Differences
|
// TODO UM6845R and R12/R13; see http://www.cpcwiki.eu/index.php/CRTC#CRTC_Differences
|
||||||
|
|
||||||
template <class T> class CRTC6845 {
|
template <class BusHandlerT, Personality personality, CursorType cursor_type> class CRTC6845 {
|
||||||
public:
|
public:
|
||||||
|
CRTC6845(BusHandlerT &bus_handler) noexcept :
|
||||||
CRTC6845(Personality p, T &bus_handler) noexcept :
|
bus_handler_(bus_handler), status_(0) {}
|
||||||
personality_(p), bus_handler_(bus_handler), status_(0) {}
|
|
||||||
|
|
||||||
void select_register(uint8_t r) {
|
void select_register(uint8_t r) {
|
||||||
selected_register_ = r;
|
selected_register_ = r;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t get_status() {
|
uint8_t get_status() {
|
||||||
switch(personality_) {
|
switch(personality) {
|
||||||
case UM6845R: return status_ | (bus_state_.vsync ? 0x20 : 0x00);
|
case Personality::UM6845R: return status_ | (bus_state_.vsync ? 0x20 : 0x00);
|
||||||
case AMS40226: return get_register();
|
case Personality::AMS40226: return get_register();
|
||||||
default: return 0xff;
|
default: return 0xff;
|
||||||
}
|
}
|
||||||
return 0xff;
|
return 0xff;
|
||||||
@@ -75,30 +96,85 @@ template <class T> class CRTC6845 {
|
|||||||
if(selected_register_ == 31) status_ &= ~0x80;
|
if(selected_register_ == 31) status_ &= ~0x80;
|
||||||
if(selected_register_ == 16 || selected_register_ == 17) status_ &= ~0x40;
|
if(selected_register_ == 16 || selected_register_ == 17) status_ &= ~0x40;
|
||||||
|
|
||||||
if(personality_ == UM6845R && selected_register_ == 31) return dummy_register_;
|
if(personality == Personality::UM6845R && selected_register_ == 31) return dummy_register_;
|
||||||
if(selected_register_ < 12 || selected_register_ > 17) return 0xff;
|
if(selected_register_ < 12 || selected_register_ > 17) return 0xff;
|
||||||
return registers_[selected_register_];
|
return registers_[selected_register_];
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_register(uint8_t value) {
|
void set_register(uint8_t value) {
|
||||||
static uint8_t masks[] = {
|
static constexpr bool is_ega = is_egavga(personality);
|
||||||
0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f,
|
|
||||||
0xff, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff
|
auto load_low = [value](uint16_t &target) {
|
||||||
|
target = (target & 0xff00) | value;
|
||||||
|
};
|
||||||
|
auto load_high = [value](uint16_t &target) {
|
||||||
|
constexpr uint8_t mask = RefreshMask >> 8;
|
||||||
|
target = uint16_t((target & 0x00ff) | ((value & mask) << 8));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
switch(selected_register_) {
|
||||||
|
case 0: layout_.horizontal.total = value; break;
|
||||||
|
case 1: layout_.horizontal.displayed = value; break;
|
||||||
|
case 2: layout_.horizontal.start_sync = value; break;
|
||||||
|
case 3:
|
||||||
|
layout_.horizontal.sync_width = value & 0xf;
|
||||||
|
layout_.vertical.sync_lines = value >> 4;
|
||||||
|
// TODO: vertical sync lines:
|
||||||
|
// "(0 means 16 on some CRTC. Not present on all CRTCs, fixed to 16 lines on these)"
|
||||||
|
break;
|
||||||
|
case 4: layout_.vertical.total = value & 0x7f; break;
|
||||||
|
case 5: layout_.vertical.adjust = value & 0x1f; break;
|
||||||
|
case 6: layout_.vertical.displayed = value & 0x7f; break;
|
||||||
|
case 7: layout_.vertical.start_sync = value & 0x7f; break;
|
||||||
|
case 8:
|
||||||
|
switch(value & 3) {
|
||||||
|
default: layout_.interlace_mode_ = InterlaceMode::Off; break;
|
||||||
|
case 0b01: layout_.interlace_mode_ = InterlaceMode::InterlaceSync; break;
|
||||||
|
case 0b11: layout_.interlace_mode_ = InterlaceMode::InterlaceSyncAndVideo; break;
|
||||||
|
}
|
||||||
|
|
||||||
// Per CPC documentation, skew doesn't work on a "type 1 or 2", i.e. an MC6845 or a UM6845R.
|
// Per CPC documentation, skew doesn't work on a "type 1 or 2", i.e. an MC6845 or a UM6845R.
|
||||||
if(selected_register_ == 8 && personality_ != UM6845R && personality_ != MC6845) {
|
if(personality != Personality::UM6845R && personality != Personality::MC6845) {
|
||||||
switch((value >> 4)&3) {
|
switch((value >> 4)&3) {
|
||||||
default: display_skew_mask_ = 1; break;
|
default: display_skew_mask_ = 1; break;
|
||||||
case 1: display_skew_mask_ = 2; break;
|
case 1: display_skew_mask_ = 2; break;
|
||||||
case 2: display_skew_mask_ = 4; break;
|
case 2: display_skew_mask_ = 4; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case 9: layout_.vertical.end_row = value & 0x1f; break;
|
||||||
|
case 10:
|
||||||
|
layout_.vertical.start_cursor = value & 0x1f;
|
||||||
|
layout_.cursor_flags = (value >> 5) & 3;
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
layout_.vertical.end_cursor = value & 0x1f;
|
||||||
|
break;
|
||||||
|
case 12: load_high(layout_.start_address); break;
|
||||||
|
case 13: load_low(layout_.start_address); break;
|
||||||
|
case 14: load_high(layout_.cursor_address); break;
|
||||||
|
case 15: load_low(layout_.cursor_address); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr uint8_t masks[] = {
|
||||||
|
0xff, // Horizontal total.
|
||||||
|
0xff, // Horizontal display end.
|
||||||
|
0xff, // Start horizontal blank.
|
||||||
|
0xff, //
|
||||||
|
// EGA: b0–b4: end of horizontal blank;
|
||||||
|
// b5–b6: "Number of character clocks to delay start of display after Horizontal Total has been reached."
|
||||||
|
|
||||||
|
is_ega ? 0xff : 0x7f, // Start horizontal retrace.
|
||||||
|
0x1f, 0x7f, 0x7f,
|
||||||
|
0xff, 0x1f, 0x7f, 0x1f,
|
||||||
|
uint8_t(RefreshMask >> 8), uint8_t(RefreshMask),
|
||||||
|
uint8_t(RefreshMask >> 8), uint8_t(RefreshMask),
|
||||||
|
};
|
||||||
|
|
||||||
if(selected_register_ < 16) {
|
if(selected_register_ < 16) {
|
||||||
registers_[selected_register_] = value & masks[selected_register_];
|
registers_[selected_register_] = value & masks[selected_register_];
|
||||||
}
|
}
|
||||||
if(selected_register_ == 31 && personality_ == UM6845R) {
|
if(selected_register_ == 31 && personality == Personality::UM6845R) {
|
||||||
dummy_register_ = value;
|
dummy_register_ = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,45 +188,48 @@ template <class T> class CRTC6845 {
|
|||||||
void run_for(Cycles cycles) {
|
void run_for(Cycles cycles) {
|
||||||
auto cyles_remaining = cycles.as_integral();
|
auto cyles_remaining = cycles.as_integral();
|
||||||
while(cyles_remaining--) {
|
while(cyles_remaining--) {
|
||||||
// check for end of visible characters
|
// Check for end of visible characters.
|
||||||
if(character_counter_ == registers_[1]) {
|
if(character_counter_ == layout_.horizontal.displayed) {
|
||||||
// TODO: consider skew in character_is_visible_. Or maybe defer until perform_bus_cycle?
|
// TODO: consider skew in character_is_visible_. Or maybe defer until perform_bus_cycle?
|
||||||
character_is_visible_ = false;
|
character_is_visible_ = false;
|
||||||
end_of_line_address_ = bus_state_.refresh_address;
|
end_of_line_address_ = bus_state_.refresh_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
perform_bus_cycle_phase1();
|
perform_bus_cycle_phase1();
|
||||||
bus_state_.refresh_address = (bus_state_.refresh_address + 1) & 0x3fff;
|
bus_state_.refresh_address = (bus_state_.refresh_address + 1) & RefreshMask;
|
||||||
|
|
||||||
// check for end-of-line
|
bus_state_.cursor = is_cursor_line_ &&
|
||||||
if(character_counter_ == registers_[0]) {
|
bus_state_.refresh_address == layout_.cursor_address;
|
||||||
|
|
||||||
|
// Check for end-of-line.
|
||||||
|
if(character_counter_ == layout_.horizontal.total) {
|
||||||
character_counter_ = 0;
|
character_counter_ = 0;
|
||||||
do_end_of_line();
|
do_end_of_line();
|
||||||
character_is_visible_ = true;
|
character_is_visible_ = true;
|
||||||
} else {
|
} else {
|
||||||
// increment counter
|
// Increment counter.
|
||||||
character_counter_++;
|
character_counter_++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for start of horizontal sync
|
// Check for start of horizontal sync.
|
||||||
if(character_counter_ == registers_[2]) {
|
if(character_counter_ == layout_.horizontal.start_sync) {
|
||||||
hsync_counter_ = 0;
|
hsync_counter_ = 0;
|
||||||
bus_state_.hsync = true;
|
bus_state_.hsync = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for end of horizontal sync; note that a sync time of zero will result in an immediate
|
// Check for end of horizontal sync; note that a sync time of zero will result in an immediate
|
||||||
// cancellation of the plan to perform sync if this is an HD6845S or UM6845R; otherwise zero
|
// cancellation of the plan to perform sync if this is an HD6845S or UM6845R; otherwise zero
|
||||||
// will end up counting as 16 as it won't be checked until after overflow.
|
// will end up counting as 16 as it won't be checked until after overflow.
|
||||||
if(bus_state_.hsync) {
|
if(bus_state_.hsync) {
|
||||||
switch(personality_) {
|
switch(personality) {
|
||||||
case HD6845S:
|
case Personality::HD6845S:
|
||||||
case UM6845R:
|
case Personality::UM6845R:
|
||||||
bus_state_.hsync = hsync_counter_ != (registers_[3] & 15);
|
bus_state_.hsync = hsync_counter_ != layout_.horizontal.sync_width;
|
||||||
hsync_counter_ = (hsync_counter_ + 1) & 15;
|
hsync_counter_ = (hsync_counter_ + 1) & 15;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
hsync_counter_ = (hsync_counter_ + 1) & 15;
|
hsync_counter_ = (hsync_counter_ + 1) & 15;
|
||||||
bus_state_.hsync = hsync_counter_ != (registers_[3] & 15);
|
bus_state_.hsync = hsync_counter_ != layout_.horizontal.sync_width;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,6 +243,8 @@ template <class T> class CRTC6845 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr uint16_t RefreshMask = (personality >= Personality::EGA) ? 0xffff : 0x3fff;
|
||||||
|
|
||||||
inline void perform_bus_cycle_phase1() {
|
inline void perform_bus_cycle_phase1() {
|
||||||
// Skew theory of operation: keep a history of the last three states, and apply whichever is selected.
|
// Skew theory of operation: keep a history of the last three states, and apply whichever is selected.
|
||||||
character_is_visible_shifter_ = (character_is_visible_shifter_ << 1) | unsigned(character_is_visible_);
|
character_is_visible_shifter_ = (character_is_visible_shifter_ << 1) | unsigned(character_is_visible_);
|
||||||
@@ -176,15 +257,21 @@ template <class T> class CRTC6845 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void do_end_of_line() {
|
inline void do_end_of_line() {
|
||||||
// check for end of vertical sync
|
if constexpr (cursor_type != CursorType::None) {
|
||||||
|
// Check for cursor disable.
|
||||||
|
// TODO: this is handled differently on the EGA, should I ever implement that.
|
||||||
|
is_cursor_line_ &= bus_state_.row_address != layout_.vertical.end_cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for end of vertical sync.
|
||||||
if(bus_state_.vsync) {
|
if(bus_state_.vsync) {
|
||||||
vsync_counter_ = (vsync_counter_ + 1) & 15;
|
vsync_counter_ = (vsync_counter_ + 1) & 15;
|
||||||
// on the UM6845R and AMS40226, honour the programmed vertical sync time; on the other CRTCs
|
// On the UM6845R and AMS40226, honour the programmed vertical sync time; on the other CRTCs
|
||||||
// always use a vertical sync count of 16.
|
// always use a vertical sync count of 16.
|
||||||
switch(personality_) {
|
switch(personality) {
|
||||||
case HD6845S:
|
case Personality::HD6845S:
|
||||||
case AMS40226:
|
case Personality::AMS40226:
|
||||||
bus_state_.vsync = vsync_counter_ != (registers_[3] >> 4);
|
bus_state_.vsync = vsync_counter_ != layout_.vertical.sync_lines;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
bus_state_.vsync = vsync_counter_ != 0;
|
bus_state_.vsync = vsync_counter_ != 0;
|
||||||
@@ -194,19 +281,19 @@ template <class T> class CRTC6845 {
|
|||||||
|
|
||||||
if(is_in_adjustment_period_) {
|
if(is_in_adjustment_period_) {
|
||||||
line_counter_++;
|
line_counter_++;
|
||||||
if(line_counter_ == registers_[5]) {
|
if(line_counter_ == layout_.vertical.adjust) {
|
||||||
is_in_adjustment_period_ = false;
|
is_in_adjustment_period_ = false;
|
||||||
do_end_of_frame();
|
do_end_of_frame();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// advance vertical counter
|
// Advance vertical counter.
|
||||||
if(bus_state_.row_address == registers_[9]) {
|
if(bus_state_.row_address == layout_.vertical.end_row) {
|
||||||
bus_state_.row_address = 0;
|
bus_state_.row_address = 0;
|
||||||
line_address_ = end_of_line_address_;
|
line_address_ = end_of_line_address_;
|
||||||
|
|
||||||
// check for entry into the overflow area
|
// Check for entry into the overflow area.
|
||||||
if(line_counter_ == registers_[4]) {
|
if(line_counter_ == layout_.vertical.total) {
|
||||||
if(registers_[5]) {
|
if(layout_.vertical.adjust) {
|
||||||
line_counter_ = 0;
|
line_counter_ = 0;
|
||||||
is_in_adjustment_period_ = true;
|
is_in_adjustment_period_ = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -215,14 +302,14 @@ template <class T> class CRTC6845 {
|
|||||||
} else {
|
} else {
|
||||||
line_counter_ = (line_counter_ + 1) & 0x7f;
|
line_counter_ = (line_counter_ + 1) & 0x7f;
|
||||||
|
|
||||||
// check for start of vertical sync
|
// Check for start of vertical sync.
|
||||||
if(line_counter_ == registers_[7]) {
|
if(line_counter_ == layout_.vertical.start_sync) {
|
||||||
bus_state_.vsync = true;
|
bus_state_.vsync = true;
|
||||||
vsync_counter_ = 0;
|
vsync_counter_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for end of visible lines
|
// Check for end of visible lines.
|
||||||
if(line_counter_ == registers_[6]) {
|
if(line_counter_ == layout_.vertical.displayed) {
|
||||||
line_is_visible_ = false;
|
line_is_visible_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,21 +320,76 @@ template <class T> class CRTC6845 {
|
|||||||
|
|
||||||
bus_state_.refresh_address = line_address_;
|
bus_state_.refresh_address = line_address_;
|
||||||
character_counter_ = 0;
|
character_counter_ = 0;
|
||||||
character_is_visible_ = (registers_[1] != 0);
|
character_is_visible_ = (layout_.horizontal.displayed != 0);
|
||||||
|
|
||||||
|
if constexpr (cursor_type != CursorType::None) {
|
||||||
|
// Check for cursor enable.
|
||||||
|
is_cursor_line_ |= bus_state_.row_address == layout_.vertical.start_cursor;
|
||||||
|
|
||||||
|
switch(cursor_type) {
|
||||||
|
// MDA-style blinking.
|
||||||
|
// https://retrocomputing.stackexchange.com/questions/27803/what-are-the-blinking-rates-of-the-caret-and-of-blinking-text-on-pc-graphics-car
|
||||||
|
// gives an 8/8 pattern for regular blinking though mode 11 is then just a guess.
|
||||||
|
case CursorType::MDA:
|
||||||
|
switch(layout_.cursor_flags) {
|
||||||
|
case 0b11: is_cursor_line_ &= (bus_state_.field_count & 8) < 3; break;
|
||||||
|
case 0b00: is_cursor_line_ &= bool(bus_state_.field_count & 8); break;
|
||||||
|
case 0b01: is_cursor_line_ = false; break;
|
||||||
|
case 0b10: is_cursor_line_ = true; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void do_end_of_frame() {
|
inline void do_end_of_frame() {
|
||||||
line_counter_ = 0;
|
line_counter_ = 0;
|
||||||
line_is_visible_ = true;
|
line_is_visible_ = true;
|
||||||
line_address_ = uint16_t((registers_[12] << 8) | registers_[13]);
|
line_address_ = layout_.start_address;
|
||||||
bus_state_.refresh_address = line_address_;
|
bus_state_.refresh_address = line_address_;
|
||||||
|
++bus_state_.field_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
Personality personality_;
|
BusHandlerT &bus_handler_;
|
||||||
T &bus_handler_;
|
|
||||||
BusState bus_state_;
|
BusState bus_state_;
|
||||||
|
|
||||||
uint8_t registers_[18] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
enum class InterlaceMode {
|
||||||
|
Off,
|
||||||
|
InterlaceSync,
|
||||||
|
InterlaceSyncAndVideo,
|
||||||
|
};
|
||||||
|
enum class BlinkMode {
|
||||||
|
// TODO.
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
uint8_t total;
|
||||||
|
uint8_t displayed;
|
||||||
|
uint8_t start_sync;
|
||||||
|
uint8_t sync_width;
|
||||||
|
} horizontal;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint8_t total;
|
||||||
|
uint8_t displayed;
|
||||||
|
uint8_t start_sync;
|
||||||
|
uint8_t sync_lines;
|
||||||
|
uint8_t adjust;
|
||||||
|
|
||||||
|
uint8_t end_row;
|
||||||
|
uint8_t start_cursor;
|
||||||
|
uint8_t end_cursor;
|
||||||
|
} vertical;
|
||||||
|
|
||||||
|
InterlaceMode interlace_mode_ = InterlaceMode::Off;
|
||||||
|
uint16_t start_address;
|
||||||
|
uint16_t cursor_address;
|
||||||
|
uint16_t light_pen_address;
|
||||||
|
uint8_t cursor_flags;
|
||||||
|
} layout_;
|
||||||
|
|
||||||
|
uint8_t registers_[18]{};
|
||||||
uint8_t dummy_register_ = 0;
|
uint8_t dummy_register_ = 0;
|
||||||
int selected_register_ = 0;
|
int selected_register_ = 0;
|
||||||
|
|
||||||
@@ -266,8 +408,8 @@ template <class T> class CRTC6845 {
|
|||||||
|
|
||||||
int display_skew_mask_ = 1;
|
int display_skew_mask_ = 1;
|
||||||
unsigned int character_is_visible_shifter_ = 0;
|
unsigned int character_is_visible_shifter_ = 0;
|
||||||
|
|
||||||
|
bool is_cursor_line_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CRTC6845_hpp */
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user