mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-21 18:37:11 +00:00
Compare commits
514 Commits
2024-10-19
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
53c1a322ed | ||
|
7fc16fa2c8 | ||
|
fe6a88c5df | ||
|
7b14df82e0 | ||
|
966b41313d | ||
|
91f1c3322c | ||
|
0b276c5a76 | ||
|
b654c2e170 | ||
|
32c88da6c4 | ||
|
3d19d0816b | ||
|
15da707324 | ||
|
387e8b04f9 | ||
|
4ca47be8a8 | ||
|
f9d9dc68b7 | ||
|
14a8f4511c | ||
|
97d237eed0 | ||
|
20d0406a24 | ||
|
73e843abd3 | ||
|
8d956da65b | ||
|
9b9cdfe937 | ||
|
3dcba9362c | ||
|
2d3a3ada57 | ||
|
143d1d5e35 | ||
|
13b32b269c | ||
|
592cea6a27 | ||
|
e76ca304e6 | ||
|
d77d8df1ac | ||
|
4f35d5dabd | ||
|
e927feb2d6 | ||
|
98f20c57c1 | ||
|
7a88d31fd9 | ||
|
96326411bf | ||
|
4e882e7d4d | ||
|
bff10c1714 | ||
|
cee2484108 | ||
|
93078abe87 | ||
|
8caa1a9664 | ||
|
d7b46ee03c | ||
|
e07b3da983 | ||
|
0f166cee48 | ||
|
08df42d05b | ||
|
2c165c3873 | ||
|
9135402d9e | ||
|
53135ec2c0 | ||
|
4eb8c8dea9 | ||
|
f318bec53c | ||
|
7d84d6909e | ||
|
9224645473 | ||
|
6717771f9a | ||
|
99e0902b74 | ||
|
c7f2805b05 | ||
|
6e1909647b | ||
|
0c7db56e15 | ||
|
3287ca449e | ||
|
53a8f65ecc | ||
|
113035b374 | ||
|
c6e64837c3 | ||
|
82419e6df1 | ||
|
faa76ee017 | ||
|
ffdefb4106 | ||
|
ba7b1c47b9 | ||
|
342b8105c4 | ||
|
367c2b568a | ||
|
0eef2c0d04 | ||
|
c9a065107b | ||
|
cacacc00f6 | ||
|
1b94cfc72c | ||
|
89fd41124f | ||
|
4e3b0ae3c1 | ||
|
9df6d535e2 | ||
|
d545cce276 | ||
|
71b481d3be | ||
|
2710acaae6 | ||
|
d79135ea01 | ||
|
1464011f6f | ||
|
409c8a6859 | ||
|
805ce36592 | ||
|
07fa56c53d | ||
|
28fca80023 | ||
|
08c0ee9ca8 | ||
|
2878ab1578 | ||
|
16f850cbcc | ||
|
b9177e50d3 | ||
|
c3258551d7 | ||
|
ea0799c546 | ||
|
4843b7f7b8 | ||
|
61694c625e | ||
|
64b4b99d48 | ||
|
dad966b551 | ||
|
e46f503641 | ||
|
fc4a1e6ace | ||
|
02b9a20c11 | ||
|
1db4e4caed | ||
|
991e176c85 | ||
|
4a41a6bb85 | ||
|
6229e1049b | ||
|
3cfd6ed109 | ||
|
ad70180edd | ||
|
efd4a83bd2 | ||
|
e13b4ab7c9 | ||
|
fee89cbaad | ||
|
2bdcba437c | ||
|
2c2216afae | ||
|
0823fc32fe | ||
|
091d19caf5 | ||
|
00a7381f08 | ||
|
d494d1e3ee | ||
|
b1c331a1df | ||
|
b1a4fd085b | ||
|
511ab2afe9 | ||
|
30387ad654 | ||
|
5d528ee1f3 | ||
|
96bb4d50ba | ||
|
45f850adae | ||
|
09341ddbe9 | ||
|
eab4274737 | ||
|
49fec1bc10 | ||
|
de164469c4 | ||
|
f595871cd9 | ||
|
ff86cbd48e | ||
|
47bd4dade5 | ||
|
ff8180920f | ||
|
d4f08f0006 | ||
|
1db756063b | ||
|
b44ea31bbf | ||
|
ddccb946ff | ||
|
1f6f30ae9e | ||
|
4b19a3f4ed | ||
|
c39d0ce2f7 | ||
|
cbdf1a941c | ||
|
a340635de5 | ||
|
d62362db1a | ||
|
a0aba69306 | ||
|
765683cd34 | ||
|
93ddf4f0ba | ||
|
8dcccf11bf | ||
|
43353ce892 | ||
|
a698fea078 | ||
|
390d9b0fe1 | ||
|
fd3ff05b17 | ||
|
a5e4c9dd7b | ||
|
37f07dcc5a | ||
|
75bae9a59c | ||
|
1684d88a3b | ||
|
50acbb70da | ||
|
4de1025468 | ||
|
c2506dd115 | ||
|
f8c9aa8e6c | ||
|
e19dc1f067 | ||
|
84776bced1 | ||
|
9162c86e21 | ||
|
88ffcbc62b | ||
|
6aff0b74cd | ||
|
79671890c5 | ||
|
edd4ed307f | ||
|
f786f8a970 | ||
|
a1d10adaa3 | ||
|
7f480e8e56 | ||
|
94b972aaf4 | ||
|
9470775292 | ||
|
93eb63d930 | ||
|
ea81096a43 | ||
|
594045b4e7 | ||
|
1449330ed3 | ||
|
07493a6b18 | ||
|
0310db5f24 | ||
|
ed6d5a7d38 | ||
|
ca7c1bc631 | ||
|
259070c658 | ||
|
e1a7dd9b24 | ||
|
e5945fbb3d | ||
|
247f636988 | ||
|
c58a2ee624 | ||
|
35a1b44c21 | ||
|
fd09f06507 | ||
|
d66b501a99 | ||
|
349f6766ac | ||
|
cd55ed1514 | ||
|
14d4c8accc | ||
|
37bca96bde | ||
|
b0e6ae58c4 | ||
|
0375e47359 | ||
|
8450ad2856 | ||
|
015a1fbd53 | ||
|
318b61b4d7 | ||
|
60640517ca | ||
|
051e38f034 | ||
|
300054b9f7 | ||
|
4f9e1e3a6b | ||
|
856bc27bf1 | ||
|
408b774b42 | ||
|
f82ef5aad4 | ||
|
a92df41eb5 | ||
|
605990929d | ||
|
925ca28659 | ||
|
41e3fa7aa7 | ||
|
5fa27448f8 | ||
|
c3428bdaed | ||
|
0539de9c4e | ||
|
eed87164b1 | ||
|
0fe726c503 | ||
|
f7f2113f1c | ||
|
49d931f5cc | ||
|
4dbd63de08 | ||
|
3a53c349a0 | ||
|
a9fe5d5c87 | ||
|
9cecccf5da | ||
|
6cb3bbaa2d | ||
|
0ff6a0bb53 | ||
|
8ba57dec03 | ||
|
d749c305ed | ||
|
f46ac2d0ed | ||
|
d7b7152315 | ||
|
da1d52033b | ||
|
01ddc24c02 | ||
|
53a3e88d16 | ||
|
bc8d1cc384 | ||
|
ed2ba63a5f | ||
|
8a2c009653 | ||
|
55690a4c7f | ||
|
161ad4b143 | ||
|
d701990df4 | ||
|
95a2d1013c | ||
|
5d4f3c0b3e | ||
|
f8e4023307 | ||
|
609aba7c73 | ||
|
bc7ab0eba1 | ||
|
56f271c8ad | ||
|
11190cff1d | ||
|
348a593dc1 | ||
|
b67bb50f4a | ||
|
1174f651ab | ||
|
dad9777c3e | ||
|
4f6285a8e7 | ||
|
20cecf4702 | ||
|
5763eabff4 | ||
|
0fc753949d | ||
|
083c1b7ca7 | ||
|
8f6b1b11e5 | ||
|
53b7d19c10 | ||
|
b0b4f5e51a | ||
|
b7414aa59c | ||
|
f449045118 | ||
|
1e9ddada37 | ||
|
55d59a1854 | ||
|
beb9f38514 | ||
|
00b1865fc8 | ||
|
0f545608c4 | ||
|
bde2047184 | ||
|
0a22d8fb9e | ||
|
1bef37d504 | ||
|
7f5d290b66 | ||
|
9461e6f285 | ||
|
3f59a03f29 | ||
|
062b581b55 | ||
|
58d3fdc1c2 | ||
|
2f546842a7 | ||
|
f089a85908 | ||
|
a6e453a452 | ||
|
3adf3dc547 | ||
|
1d0ea96ae9 | ||
|
a4cb17a1cb | ||
|
733da3161b | ||
|
1b1a0f553d | ||
|
972619c1fe | ||
|
61086d5360 | ||
|
37513d726c | ||
|
6510bee327 | ||
|
cd36f3f096 | ||
|
407a6f5e31 | ||
|
2b28df280e | ||
|
eb763ed82c | ||
|
755f53cce0 | ||
|
c190ab40b0 | ||
|
a3ad82de42 | ||
|
0f6cd6904d | ||
|
79c89af6ea | ||
|
56f10a9a52 | ||
|
c679e2c067 | ||
|
0677987320 | ||
|
5fb6e6780c | ||
|
58ef91a7b1 | ||
|
e1ae65b6d1 | ||
|
65307186dc | ||
|
6fa29c204b | ||
|
8219beeb1a | ||
|
ace7e24dfb | ||
|
828c2a6883 | ||
|
f195dc313d | ||
|
5b8a005f41 | ||
|
9feb75e645 | ||
|
7f8e90bd29 | ||
|
2fd34b649d | ||
|
104054ed1a | ||
|
457b28c22c | ||
|
095c8dcd0c | ||
|
8463e9ed94 | ||
|
b6278c6144 | ||
|
1c1e1eee47 | ||
|
b37ed9ec60 | ||
|
45f3ef6920 | ||
|
2cd6c4238b | ||
|
f6ed0b33eb | ||
|
c4f4ca3f90 | ||
|
9a6780616b | ||
|
db4eca0a42 | ||
|
6d674edb48 | ||
|
c0469a044b | ||
|
b9b64eba9a | ||
|
2d74387a00 | ||
|
f66b6fc20c | ||
|
f0711a9fbc | ||
|
83a8c7215a | ||
|
a86f966cb4 | ||
|
74db978b81 | ||
|
1300546a52 | ||
|
3aeb0bba71 | ||
|
03d3efa323 | ||
|
f9c220bee0 | ||
|
114c2e2636 | ||
|
75a0e622ad | ||
|
8e2de4ee30 | ||
|
b1602261cf | ||
|
e5ed11f8ec | ||
|
c1ecfd289e | ||
|
c5cca15b4e | ||
|
fa978315e6 | ||
|
c5bffc38f4 | ||
|
88b5f6b148 | ||
|
fc04742151 | ||
|
c618d18d46 | ||
|
33bc7c00df | ||
|
1ed550d7f9 | ||
|
18b87f2c80 | ||
|
fad503ca80 | ||
|
37ec3e4605 | ||
|
70e3d23f26 | ||
|
6ebf415a52 | ||
|
5c31104d0e | ||
|
aed8b65e2b | ||
|
906e8aa2b2 | ||
|
d0703f95af | ||
|
538b00797d | ||
|
985c555518 | ||
|
5ef26a25ee | ||
|
3db0e30d12 | ||
|
a666cabae9 | ||
|
2e8d9018ef | ||
|
ae49505e67 | ||
|
09bd5503b4 | ||
|
dbe733524c | ||
|
1625f5c0f9 | ||
|
7cd49d07f2 | ||
|
a542345456 | ||
|
0b98f21443 | ||
|
c42e231e99 | ||
|
ab653af4b3 | ||
|
8653f572c8 | ||
|
39b431fb19 | ||
|
e158c5bc30 | ||
|
d0fdfda4cb | ||
|
668a5ca041 | ||
|
5e3947b8bc | ||
|
233a627c9f | ||
|
6197486d49 | ||
|
60856b974b | ||
|
d1eca5dc21 | ||
|
24b281d625 | ||
|
b0d1dee38b | ||
|
0751c51803 | ||
|
0005229c1e | ||
|
33c2353107 | ||
|
cb98297bb5 | ||
|
0e663e1da8 | ||
|
918a8c5f8b | ||
|
34938e8c62 | ||
|
da6efe52ff | ||
|
570f1caa8f | ||
|
6f638805f7 | ||
|
c92b0dc886 | ||
|
b4b216de84 | ||
|
80f5d7c735 | ||
|
01aeb46664 | ||
|
2cfd2ff624 | ||
|
ff12bbbdb6 | ||
|
e19fe5d0e2 | ||
|
9670d5f4de | ||
|
863b09c39b | ||
|
6b90de539e | ||
|
4bb53ed6ba | ||
|
e6523f3ec1 | ||
|
c8ad8c79bd | ||
|
b08cc9cb49 | ||
|
4f93dc0adf | ||
|
096f48be33 | ||
|
81398d58a2 | ||
|
7466da5651 | ||
|
c8fdde4c5e | ||
|
15583e7975 | ||
|
5acdf39566 | ||
|
2240ada5db | ||
|
9d5c10d440 | ||
|
709f350d60 | ||
|
3d7e016b42 | ||
|
b76104d145 | ||
|
702b6d6567 | ||
|
3e93004db6 | ||
|
589903c43c | ||
|
f41b54de21 | ||
|
700b848f26 | ||
|
a1f6e93e22 | ||
|
1628af2ffc | ||
|
1b5d446635 | ||
|
83a9ef772a | ||
|
1d9c3fb827 | ||
|
ff92bdb324 | ||
|
363ad7342a | ||
|
663acd3810 | ||
|
c2fc26089e | ||
|
58b464bdfc | ||
|
ed766c74e6 | ||
|
1d07b8238c | ||
|
41c6ed7c5a | ||
|
f7750af3d0 | ||
|
8854ffddee | ||
|
a487619578 | ||
|
0eab6146fc | ||
|
389ba95e5a | ||
|
84d178c0ca | ||
|
aed8f8efa8 | ||
|
38325741de | ||
|
891d5c2066 | ||
|
6b7edac6e4 | ||
|
ab2a576e1b | ||
|
5a3e4dd47b | ||
|
064c4b4312 | ||
|
cbde504057 | ||
|
949cfcfa69 | ||
|
06a005321e | ||
|
d3587f595f | ||
|
b0158ed7ce | ||
|
e5f4300e54 | ||
|
7cc6f8604e | ||
|
657960e7d0 | ||
|
aecd7f9283 | ||
|
6f1b30cd24 | ||
|
a6ba549b67 | ||
|
84ea04f61d | ||
|
0d52cf5f97 | ||
|
b15a083a15 | ||
|
a128247ef5 | ||
|
590bd934c0 | ||
|
e9826d2e7e | ||
|
58ed63cd18 | ||
|
fd1bd3032f | ||
|
d7206096ea | ||
|
f43e594eca | ||
|
ea4fe5e809 | ||
|
9fcb634510 | ||
|
c14a4515ce | ||
|
8e71180cd2 | ||
|
08f98aa32f | ||
|
e8aa9b9eb2 | ||
|
19f815eeff | ||
|
a508f7a463 | ||
|
e58d3ee060 | ||
|
268842681a | ||
|
9b357a9fbf | ||
|
6f80018b6e | ||
|
7a1153be65 | ||
|
85d4c24aba | ||
|
48c0ae8fe4 | ||
|
e835b2c68c | ||
|
10d20f5d09 | ||
|
88b31ea940 | ||
|
9cb28d23a3 | ||
|
ce5aae3f7d | ||
|
e7f0eb6746 | ||
|
65a118d1f3 | ||
|
3d2eefc7e7 | ||
|
f804c32eee | ||
|
b89ecadc3a | ||
|
6d4ff0b89a | ||
|
598003ea39 | ||
|
6ef63790a9 | ||
|
3ffd986a1c | ||
|
0b5cd4c665 | ||
|
0371b0507a | ||
|
9fa71231c4 | ||
|
32beafc12d | ||
|
09e2ef334b | ||
|
c0ce62ed2b | ||
|
d3ed485e7a | ||
|
31c878b654 | ||
|
3a0f4a0bfc | ||
|
8b88d1294d | ||
|
43fcf46d69 | ||
|
394fe0f1f1 | ||
|
872921f635 | ||
|
7248470950 | ||
|
9d87296316 | ||
|
23c67f7e38 | ||
|
bd98d95dbf | ||
|
3addb8d72b | ||
|
5545906063 | ||
|
36edfe9715 | ||
|
030b54a2b6 | ||
|
f332613922 | ||
|
088bc14b11 | ||
|
86fa8da8c5 | ||
|
abfc73299e | ||
|
bd97fd5973 | ||
|
f00e7c4a80 | ||
|
49c811b5f5 |
53
.github/workflows/build.yml
vendored
53
.github/workflows/build.yml
vendored
@ -1,18 +1,24 @@
|
||||
name: Build
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
|
||||
build-mac-xcodebuild:
|
||||
name: Mac UI / xcodebuild / ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-12, macos-13, macos-14]
|
||||
os: [macos-latest] #[macos-13, macos-14, macos-15]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest-stable
|
||||
- name: Make
|
||||
working-directory: OSBindings/Mac
|
||||
run: xcodebuild CODE_SIGN_IDENTITY=-
|
||||
|
||||
build-sdl-cmake:
|
||||
name: SDL UI / cmake / ${{ matrix.os }}
|
||||
strategy:
|
||||
@ -49,11 +55,12 @@ jobs:
|
||||
esac
|
||||
cmake -S. -Bbuild -DCLK_UI=SDL -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build -v -j"$jobs"
|
||||
|
||||
build-sdl-scons:
|
||||
name: SDL UI / scons / ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-14, ubuntu-latest]
|
||||
os: [macos-latest, ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -85,3 +92,45 @@ jobs:
|
||||
jobs=1
|
||||
esac
|
||||
scons -j"$jobs"
|
||||
|
||||
build-qt5:
|
||||
name: Qt 5 / ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: '5.15.2'
|
||||
archives: 'qtbase qtmultimedia qtx11extras icu'
|
||||
- name: Make
|
||||
working-directory: OSBindings/Qt
|
||||
shell: bash
|
||||
run: |
|
||||
qmake -o Makefile clksignal.pro
|
||||
make
|
||||
|
||||
# build-qt6:
|
||||
# name: Qt 6 / ${{ matrix.os }}
|
||||
# strategy:
|
||||
# matrix:
|
||||
# os: [ubuntu-latest]
|
||||
# runs-on: ${{ matrix.os }}
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v4
|
||||
# - name: Install dependencies
|
||||
# uses: jurplel/install-qt-action@v4
|
||||
# with:
|
||||
# version: '6.8'
|
||||
# archives: qtbase
|
||||
# - name: Make
|
||||
# working-directory: OSBindings/Qt
|
||||
# shell: bash
|
||||
# run: |
|
||||
# qmake -o Makefile clksignal.pro
|
||||
# make
|
||||
|
@ -22,38 +22,38 @@ namespace Activity {
|
||||
and/or to show or unshow status indicators.
|
||||
*/
|
||||
class Observer {
|
||||
public:
|
||||
virtual ~Observer() = default;
|
||||
public:
|
||||
virtual ~Observer() = default;
|
||||
|
||||
/// Provides hints as to the sort of information presented on an LED.
|
||||
enum LEDPresentation: uint8_t {
|
||||
/// This LED informs the user of some sort of persistent state, e.g. scroll lock.
|
||||
/// If this flag is absent then the LED describes an ephemeral state, such as media access.
|
||||
Persistent = (1 << 0),
|
||||
};
|
||||
/// Provides hints as to the sort of information presented on an LED.
|
||||
enum LEDPresentation: uint8_t {
|
||||
/// This LED informs the user of some sort of persistent state, e.g. scroll lock.
|
||||
/// If this flag is absent then the LED describes an ephemeral state, such as media access.
|
||||
Persistent = (1 << 0),
|
||||
};
|
||||
|
||||
/// Announces to the receiver that there is an LED of name @c name.
|
||||
virtual void register_led([[maybe_unused]] const std::string &name, [[maybe_unused]] uint8_t presentation = 0) {}
|
||||
/// Announces to the receiver that there is an LED of name @c name.
|
||||
virtual void register_led([[maybe_unused]] const std::string &name, [[maybe_unused]] uint8_t presentation = 0) {}
|
||||
|
||||
/// Announces to the receiver that there is a drive of name @c name.
|
||||
///
|
||||
/// If a drive has the same name as an LED, that LED goes with this drive.
|
||||
virtual void register_drive([[maybe_unused]] const std::string &name) {}
|
||||
/// Announces to the receiver that there is a drive of name @c name.
|
||||
///
|
||||
/// If a drive has the same name as an LED, that LED goes with this drive.
|
||||
virtual void register_drive([[maybe_unused]] const std::string &name) {}
|
||||
|
||||
/// Informs the receiver of the new state of the LED with name @c name.
|
||||
virtual void set_led_status([[maybe_unused]] const std::string &name, [[maybe_unused]] bool lit) {}
|
||||
/// Informs the receiver of the new state of the LED with name @c name.
|
||||
virtual void set_led_status([[maybe_unused]] const std::string &name, [[maybe_unused]] bool lit) {}
|
||||
|
||||
enum class DriveEvent {
|
||||
StepNormal,
|
||||
StepBelowZero,
|
||||
StepBeyondMaximum
|
||||
};
|
||||
enum class DriveEvent {
|
||||
StepNormal,
|
||||
StepBelowZero,
|
||||
StepBeyondMaximum
|
||||
};
|
||||
|
||||
/// Informs the receiver that the named event just occurred for the drive with name @c name.
|
||||
virtual void announce_drive_event([[maybe_unused]] const std::string &name, [[maybe_unused]] DriveEvent event) {}
|
||||
/// Informs the receiver that the named event just occurred for the drive with name @c name.
|
||||
virtual void announce_drive_event([[maybe_unused]] const std::string &name, [[maybe_unused]] DriveEvent event) {}
|
||||
|
||||
/// Informs the receiver of the motor-on status of the drive with name @c name.
|
||||
virtual void set_drive_motor_status([[maybe_unused]] const std::string &name, [[maybe_unused]] bool is_on) {}
|
||||
/// Informs the receiver of the motor-on status of the drive with name @c name.
|
||||
virtual void set_drive_motor_status([[maybe_unused]] const std::string &name, [[maybe_unused]] bool is_on) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -13,8 +13,8 @@
|
||||
namespace Activity {
|
||||
|
||||
class Source {
|
||||
public:
|
||||
virtual void set_activity_observer(Observer *observer) = 0;
|
||||
public:
|
||||
virtual void set_activity_observer(Observer *observer) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -18,25 +18,25 @@ namespace Analyser::Dynamic {
|
||||
The initial value of the confidence counter is 0.5.
|
||||
*/
|
||||
class ConfidenceCounter: public ConfidenceSource {
|
||||
public:
|
||||
/*! @returns The computed probability, based on the history of events. */
|
||||
float get_confidence() final;
|
||||
public:
|
||||
/*! @returns The computed probability, based on the history of events. */
|
||||
float get_confidence() final;
|
||||
|
||||
/*! Records an event that implies this is the appropriate class: pushes probability up towards 1.0. */
|
||||
void add_hit();
|
||||
/*! Records an event that implies this is the appropriate class: pushes probability up towards 1.0. */
|
||||
void add_hit();
|
||||
|
||||
/*! Records an event that implies this is not the appropriate class: pushes probability down towards 0.0. */
|
||||
void add_miss();
|
||||
/*! Records an event that implies this is not the appropriate class: pushes probability down towards 0.0. */
|
||||
void add_miss();
|
||||
|
||||
/*!
|
||||
Records an event that could be correct but isn't necessarily so; which can push probability
|
||||
down towards 0.5, but will never push it upwards.
|
||||
*/
|
||||
void add_equivocal();
|
||||
/*!
|
||||
Records an event that could be correct but isn't necessarily so; which can push probability
|
||||
down towards 0.5, but will never push it upwards.
|
||||
*/
|
||||
void add_equivocal();
|
||||
|
||||
private:
|
||||
int hits_ = 1;
|
||||
int misses_ = 1;
|
||||
private:
|
||||
int hits_ = 1;
|
||||
int misses_ = 1;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,10 @@
|
||||
|
||||
using namespace Analyser::Dynamic;
|
||||
|
||||
ConfidenceSummary::ConfidenceSummary(const std::vector<ConfidenceSource *> &sources, const std::vector<float> &weights) :
|
||||
ConfidenceSummary::ConfidenceSummary(
|
||||
const std::vector<ConfidenceSource *> &sources,
|
||||
const std::vector<float> &weights
|
||||
) :
|
||||
sources_(sources), weights_(weights) {
|
||||
assert(weights.size() == sources.size());
|
||||
weight_sum_ = std::accumulate(weights.begin(), weights.end(), 0.0f);
|
||||
|
@ -18,24 +18,24 @@ namespace Analyser::Dynamic {
|
||||
Summaries a collection of confidence sources by calculating their weighted sum.
|
||||
*/
|
||||
class ConfidenceSummary: public ConfidenceSource {
|
||||
public:
|
||||
/*!
|
||||
Instantiates a summary that will produce the weighted sum of
|
||||
@c sources, each using the corresponding entry of @c weights.
|
||||
public:
|
||||
/*!
|
||||
Instantiates a summary that will produce the weighted sum of
|
||||
@c sources, each using the corresponding entry of @c weights.
|
||||
|
||||
Requires that @c sources and @c weights are of the same length.
|
||||
*/
|
||||
ConfidenceSummary(
|
||||
const std::vector<ConfidenceSource *> &sources,
|
||||
const std::vector<float> &weights);
|
||||
Requires that @c sources and @c weights are of the same length.
|
||||
*/
|
||||
ConfidenceSummary(
|
||||
const std::vector<ConfidenceSource *> &sources,
|
||||
const std::vector<float> &weights);
|
||||
|
||||
/*! @returns The weighted sum of all sources. */
|
||||
float get_confidence() final;
|
||||
/*! @returns The weighted sum of all sources. */
|
||||
float get_confidence() final;
|
||||
|
||||
private:
|
||||
const std::vector<ConfidenceSource *> sources_;
|
||||
const std::vector<float> weights_;
|
||||
float weight_sum_;
|
||||
private:
|
||||
const std::vector<ConfidenceSource *> sources_;
|
||||
const std::vector<float> weights_;
|
||||
float weight_sum_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -15,90 +15,90 @@ using namespace Analyser::Dynamic;
|
||||
namespace {
|
||||
|
||||
class MultiStruct: public Reflection::Struct {
|
||||
public:
|
||||
MultiStruct(const std::vector<Configurable::Device *> &devices) : devices_(devices) {
|
||||
for(auto device: devices) {
|
||||
options_.emplace_back(device->get_options());
|
||||
public:
|
||||
MultiStruct(const std::vector<Configurable::Device *> &devices) : devices_(devices) {
|
||||
for(auto device: devices) {
|
||||
options_.emplace_back(device->get_options());
|
||||
}
|
||||
}
|
||||
|
||||
void apply() {
|
||||
auto options = options_.begin();
|
||||
for(auto device: devices_) {
|
||||
device->set_options(*options);
|
||||
++options;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> all_keys() const final {
|
||||
std::set<std::string> keys;
|
||||
for(auto &options: options_) {
|
||||
const auto new_keys = options->all_keys();
|
||||
keys.insert(new_keys.begin(), new_keys.end());
|
||||
}
|
||||
return std::vector<std::string>(keys.begin(), keys.end());
|
||||
}
|
||||
|
||||
std::vector<std::string> values_for(const std::string &name) const final {
|
||||
std::set<std::string> values;
|
||||
for(auto &options: options_) {
|
||||
const auto new_values = options->values_for(name);
|
||||
values.insert(new_values.begin(), new_values.end());
|
||||
}
|
||||
return std::vector<std::string>(values.begin(), values.end());
|
||||
}
|
||||
|
||||
const std::type_info *type_of(const std::string &name) const final {
|
||||
for(auto &options: options_) {
|
||||
auto info = options->type_of(name);
|
||||
if(info) return info;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t count_of(const std::string &name) const final {
|
||||
for(auto &options: options_) {
|
||||
auto info = options->type_of(name);
|
||||
if(info) return options->count_of(name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const void *get(const std::string &name) const final {
|
||||
for(auto &options: options_) {
|
||||
auto value = options->get(name);
|
||||
if(value) return value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *get(const std::string &name) final {
|
||||
for(auto &options: options_) {
|
||||
auto value = options->get(name);
|
||||
if(value) return value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void set(const std::string &name, const void *value, const size_t offset) final {
|
||||
const auto safe_type = type_of(name);
|
||||
if(!safe_type) return;
|
||||
|
||||
// Set this property only where the child's type is the same as that
|
||||
// which was returned from here for type_of.
|
||||
for(auto &options: options_) {
|
||||
const auto type = options->type_of(name);
|
||||
if(!type) continue;
|
||||
|
||||
if(*type == *safe_type) {
|
||||
options->set(name, value, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void apply() {
|
||||
auto options = options_.begin();
|
||||
for(auto device: devices_) {
|
||||
device->set_options(*options);
|
||||
++options;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> all_keys() const final {
|
||||
std::set<std::string> keys;
|
||||
for(auto &options: options_) {
|
||||
const auto new_keys = options->all_keys();
|
||||
keys.insert(new_keys.begin(), new_keys.end());
|
||||
}
|
||||
return std::vector<std::string>(keys.begin(), keys.end());
|
||||
}
|
||||
|
||||
std::vector<std::string> values_for(const std::string &name) const final {
|
||||
std::set<std::string> values;
|
||||
for(auto &options: options_) {
|
||||
const auto new_values = options->values_for(name);
|
||||
values.insert(new_values.begin(), new_values.end());
|
||||
}
|
||||
return std::vector<std::string>(values.begin(), values.end());
|
||||
}
|
||||
|
||||
const std::type_info *type_of(const std::string &name) const final {
|
||||
for(auto &options: options_) {
|
||||
auto info = options->type_of(name);
|
||||
if(info) return info;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t count_of(const std::string &name) const final {
|
||||
for(auto &options: options_) {
|
||||
auto info = options->type_of(name);
|
||||
if(info) return options->count_of(name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const void *get(const std::string &name) const final {
|
||||
for(auto &options: options_) {
|
||||
auto value = options->get(name);
|
||||
if(value) return value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *get(const std::string &name) final {
|
||||
for(auto &options: options_) {
|
||||
auto value = options->get(name);
|
||||
if(value) return value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void set(const std::string &name, const void *value, size_t offset) final {
|
||||
const auto safe_type = type_of(name);
|
||||
if(!safe_type) return;
|
||||
|
||||
// Set this property only where the child's type is the same as that
|
||||
// which was returned from here for type_of.
|
||||
for(auto &options: options_) {
|
||||
const auto type = options->type_of(name);
|
||||
if(!type) continue;
|
||||
|
||||
if(*type == *safe_type) {
|
||||
options->set(name, value, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<Configurable::Device *> &devices_;
|
||||
std::vector<std::unique_ptr<Reflection::Struct>> options_;
|
||||
private:
|
||||
const std::vector<Configurable::Device *> &devices_;
|
||||
std::vector<std::unique_ptr<Reflection::Struct>> options_;
|
||||
};
|
||||
|
||||
}
|
||||
@ -115,6 +115,6 @@ void MultiConfigurable::set_options(const std::unique_ptr<Reflection::Struct> &s
|
||||
options->apply();
|
||||
}
|
||||
|
||||
std::unique_ptr<Reflection::Struct> MultiConfigurable::get_options() {
|
||||
std::unique_ptr<Reflection::Struct> MultiConfigurable::get_options() const {
|
||||
return std::make_unique<MultiStruct>(devices_);
|
||||
}
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
#include "../../../../Configurable/Configurable.hpp"
|
||||
#include "Machines/DynamicMachine.hpp"
|
||||
#include "Configurable/Configurable.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -23,15 +23,15 @@ namespace Analyser::Dynamic {
|
||||
order of delivered messages.
|
||||
*/
|
||||
class MultiConfigurable: public Configurable::Device {
|
||||
public:
|
||||
MultiConfigurable(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||
public:
|
||||
MultiConfigurable(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &);
|
||||
|
||||
// Below is the standard Configurable::Device interface; see there for documentation.
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &options) final;
|
||||
std::unique_ptr<Reflection::Struct> get_options() final;
|
||||
// Below is the standard Configurable::Device interface; see there for documentation.
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &) final;
|
||||
std::unique_ptr<Reflection::Struct> get_options() const final;
|
||||
|
||||
private:
|
||||
std::vector<Configurable::Device *> devices_;
|
||||
private:
|
||||
std::vector<Configurable::Device *> devices_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -15,52 +15,52 @@ using namespace Analyser::Dynamic;
|
||||
namespace {
|
||||
|
||||
class MultiJoystick: public Inputs::Joystick {
|
||||
public:
|
||||
MultiJoystick(std::vector<MachineTypes::JoystickMachine *> &machines, std::size_t index) {
|
||||
for(const auto &machine: machines) {
|
||||
const auto &joysticks = machine->get_joysticks();
|
||||
if(joysticks.size() >= index) {
|
||||
joysticks_.push_back(joysticks[index].get());
|
||||
}
|
||||
public:
|
||||
MultiJoystick(std::vector<MachineTypes::JoystickMachine *> &machines, const std::size_t index) {
|
||||
for(const auto &machine: machines) {
|
||||
const auto &joysticks = machine->get_joysticks();
|
||||
if(joysticks.size() > index) {
|
||||
joysticks_.push_back(joysticks[index].get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<Input> &get_inputs() final {
|
||||
if(inputs.empty()) {
|
||||
for(const auto &joystick: joysticks_) {
|
||||
std::vector<Input> joystick_inputs = joystick->get_inputs();
|
||||
for(const auto &input: joystick_inputs) {
|
||||
if(std::find(inputs.begin(), inputs.end(), input) != inputs.end()) {
|
||||
inputs.push_back(input);
|
||||
}
|
||||
const std::vector<Input> &get_inputs() final {
|
||||
if(inputs.empty()) {
|
||||
for(const auto &joystick: joysticks_) {
|
||||
std::vector<Input> joystick_inputs = joystick->get_inputs();
|
||||
for(const auto &input: joystick_inputs) {
|
||||
if(std::find(inputs.begin(), inputs.end(), input) != inputs.end()) {
|
||||
inputs.push_back(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inputs;
|
||||
}
|
||||
|
||||
void set_input(const Input &digital_input, bool is_active) final {
|
||||
for(const auto &joystick: joysticks_) {
|
||||
joystick->set_input(digital_input, is_active);
|
||||
}
|
||||
}
|
||||
return inputs;
|
||||
}
|
||||
|
||||
void set_input(const Input &digital_input, float value) final {
|
||||
for(const auto &joystick: joysticks_) {
|
||||
joystick->set_input(digital_input, value);
|
||||
}
|
||||
void set_input(const Input &digital_input, const bool is_active) final {
|
||||
for(const auto &joystick: joysticks_) {
|
||||
joystick->set_input(digital_input, is_active);
|
||||
}
|
||||
}
|
||||
|
||||
void reset_all_inputs() final {
|
||||
for(const auto &joystick: joysticks_) {
|
||||
joystick->reset_all_inputs();
|
||||
}
|
||||
void set_input(const Input &digital_input, const float value) final {
|
||||
for(const auto &joystick: joysticks_) {
|
||||
joystick->set_input(digital_input, value);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Input> inputs;
|
||||
std::vector<Inputs::Joystick *> joysticks_;
|
||||
void reset_all_inputs() final {
|
||||
for(const auto &joystick: joysticks_) {
|
||||
joystick->reset_all_inputs();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Input> inputs;
|
||||
std::vector<Inputs::Joystick *> joysticks_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
#include "Machines/DynamicMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -22,14 +22,14 @@ namespace Analyser::Dynamic {
|
||||
order of delivered messages.
|
||||
*/
|
||||
class MultiJoystickMachine: public MachineTypes::JoystickMachine {
|
||||
public:
|
||||
MultiJoystickMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||
public:
|
||||
MultiJoystickMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &);
|
||||
|
||||
// Below is the standard JoystickMachine::Machine interface; see there for documentation.
|
||||
const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() final;
|
||||
// Below is the standard JoystickMachine::Machine interface; see there for documentation.
|
||||
const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() final;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
|
||||
private:
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ void MultiKeyboardMachine::clear_all_keys() {
|
||||
}
|
||||
}
|
||||
|
||||
void MultiKeyboardMachine::set_key_state(uint16_t key, bool is_pressed) {
|
||||
void MultiKeyboardMachine::set_key_state(const uint16_t key, const bool is_pressed) {
|
||||
for(const auto &machine: machines_) {
|
||||
machine->set_key_state(key, is_pressed);
|
||||
}
|
||||
@ -36,7 +36,7 @@ void MultiKeyboardMachine::type_string(const std::string &string) {
|
||||
}
|
||||
}
|
||||
|
||||
bool MultiKeyboardMachine::can_type(char c) const {
|
||||
bool MultiKeyboardMachine::can_type(const char c) const {
|
||||
bool can_type = true;
|
||||
for(const auto &machine: machines_) {
|
||||
can_type &= machine->can_type(c);
|
||||
@ -51,12 +51,20 @@ Inputs::Keyboard &MultiKeyboardMachine::get_keyboard() {
|
||||
MultiKeyboardMachine::MultiKeyboard::MultiKeyboard(const std::vector<::MachineTypes::KeyboardMachine *> &machines)
|
||||
: machines_(machines) {
|
||||
for(const auto &machine: machines_) {
|
||||
observed_keys_.insert(machine->get_keyboard().observed_keys().begin(), machine->get_keyboard().observed_keys().end());
|
||||
observed_keys_.insert(
|
||||
machine->get_keyboard().observed_keys().begin(),
|
||||
machine->get_keyboard().observed_keys().end()
|
||||
);
|
||||
is_exclusive_ |= machine->get_keyboard().is_exclusive();
|
||||
}
|
||||
}
|
||||
|
||||
bool MultiKeyboardMachine::MultiKeyboard::set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat) {
|
||||
bool MultiKeyboardMachine::MultiKeyboard::set_key_pressed(
|
||||
const Key key,
|
||||
const char value,
|
||||
const bool is_pressed,
|
||||
const bool is_repeat
|
||||
) {
|
||||
bool was_consumed = false;
|
||||
for(const auto &machine: machines_) {
|
||||
was_consumed |= machine->get_keyboard().set_key_pressed(key, value, is_pressed, is_repeat);
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
#include "../../../../Machines/KeyboardMachine.hpp"
|
||||
#include "Machines/DynamicMachine.hpp"
|
||||
#include "Machines/KeyboardMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -23,34 +23,34 @@ namespace Analyser::Dynamic {
|
||||
order of delivered messages.
|
||||
*/
|
||||
class MultiKeyboardMachine: public MachineTypes::KeyboardMachine {
|
||||
private:
|
||||
std::vector<MachineTypes::KeyboardMachine *> machines_;
|
||||
|
||||
class MultiKeyboard: public Inputs::Keyboard {
|
||||
public:
|
||||
MultiKeyboard(const std::vector<MachineTypes::KeyboardMachine *> &machines);
|
||||
|
||||
bool set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat) final;
|
||||
void reset_all_keys() final;
|
||||
const std::set<Key> &observed_keys() const final;
|
||||
bool is_exclusive() const final;
|
||||
|
||||
private:
|
||||
const std::vector<MachineTypes::KeyboardMachine *> &machines_;
|
||||
std::set<Key> observed_keys_;
|
||||
bool is_exclusive_ = false;
|
||||
};
|
||||
std::unique_ptr<MultiKeyboard> keyboard_;
|
||||
private:
|
||||
std::vector<MachineTypes::KeyboardMachine *> machines_;
|
||||
|
||||
class MultiKeyboard: public Inputs::Keyboard {
|
||||
public:
|
||||
MultiKeyboardMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||
MultiKeyboard(const std::vector<MachineTypes::KeyboardMachine *> &);
|
||||
|
||||
// Below is the standard KeyboardMachine::Machine interface; see there for documentation.
|
||||
void clear_all_keys() final;
|
||||
void set_key_state(uint16_t key, bool is_pressed) final;
|
||||
void type_string(const std::string &) final;
|
||||
bool can_type(char c) const final;
|
||||
Inputs::Keyboard &get_keyboard() final;
|
||||
bool set_key_pressed(Key key, char value, bool is_pressed, bool is_repeat) final;
|
||||
void reset_all_keys() final;
|
||||
const std::set<Key> &observed_keys() const final;
|
||||
bool is_exclusive() const final;
|
||||
|
||||
private:
|
||||
const std::vector<MachineTypes::KeyboardMachine *> &machines_;
|
||||
std::set<Key> observed_keys_;
|
||||
bool is_exclusive_ = false;
|
||||
};
|
||||
std::unique_ptr<MultiKeyboard> keyboard_;
|
||||
|
||||
public:
|
||||
MultiKeyboardMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||
|
||||
// Below is the standard KeyboardMachine::Machine interface; see there for documentation.
|
||||
void clear_all_keys() final;
|
||||
void set_key_state(uint16_t key, bool is_pressed) final;
|
||||
void type_string(const std::string &) final;
|
||||
bool can_type(char c) const final;
|
||||
Inputs::Keyboard &get_keyboard() final;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
#include "MultiMediaTarget.hpp"
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace Analyser::Dynamic;
|
||||
|
||||
@ -18,9 +19,38 @@ MultiMediaTarget::MultiMediaTarget(const std::vector<std::unique_ptr<::Machine::
|
||||
}
|
||||
|
||||
bool MultiMediaTarget::insert_media(const Analyser::Static::Media &media) {
|
||||
// TODO: copy media afresh for each target machine; media
|
||||
// generally has mutable state.
|
||||
|
||||
bool inserted = false;
|
||||
for(const auto &target : targets_) {
|
||||
inserted |= target->insert_media(media);
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
|
||||
MultiMediaChangeObserver::MultiMediaChangeObserver(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
|
||||
for(const auto &machine: machines) {
|
||||
auto media_change_observer = machine->media_change_observer();
|
||||
if(media_change_observer) targets_.push_back(media_change_observer);
|
||||
}
|
||||
}
|
||||
|
||||
using ChangeEffect = MachineTypes::MediaChangeObserver::ChangeEffect;
|
||||
|
||||
ChangeEffect MultiMediaChangeObserver::effect_for_file_did_change(const std::string &name) const {
|
||||
if(targets_.empty()) {
|
||||
return ChangeEffect::None;
|
||||
}
|
||||
|
||||
std::unordered_set<ChangeEffect> effects;
|
||||
for(const auto &target: targets_) {
|
||||
effects.insert(target->effect_for_file_did_change(name));
|
||||
}
|
||||
|
||||
// No agreement => restart.
|
||||
if(effects.size() > 1) {
|
||||
return ChangeEffect::RestartMachine;
|
||||
}
|
||||
return *effects.begin();
|
||||
}
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../../Machines/MediaTarget.hpp"
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
#include "Machines/MediaTarget.hpp"
|
||||
#include "Machines/DynamicMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -23,14 +23,25 @@ namespace Analyser::Dynamic {
|
||||
order of delivered messages.
|
||||
*/
|
||||
struct MultiMediaTarget: public MachineTypes::MediaTarget {
|
||||
public:
|
||||
MultiMediaTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||
public:
|
||||
MultiMediaTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &);
|
||||
|
||||
// Below is the standard MediaTarget::Machine interface; see there for documentation.
|
||||
bool insert_media(const Analyser::Static::Media &media) final;
|
||||
// Below is the standard MediaTarget::Machine interface; see there for documentation.
|
||||
bool insert_media(const Analyser::Static::Media &) final;
|
||||
|
||||
private:
|
||||
std::vector<MachineTypes::MediaTarget *> targets_;
|
||||
private:
|
||||
std::vector<MachineTypes::MediaTarget *> targets_;
|
||||
};
|
||||
|
||||
struct MultiMediaChangeObserver: public MachineTypes::MediaChangeObserver {
|
||||
public:
|
||||
MultiMediaChangeObserver(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &);
|
||||
|
||||
// Below is the standard MediaTarget::Machine interface; see there for documentation.
|
||||
ChangeEffect effect_for_file_did_change(const std::string &) const final;
|
||||
|
||||
private:
|
||||
std::vector<MachineTypes::MediaChangeObserver *> targets_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ template <typename MachineType>
|
||||
void MultiInterface<MachineType>::perform_parallel(const std::function<void(MachineType *)> &function) {
|
||||
// Apply a blunt force parallelisation of the machines; each run_for is dispatched
|
||||
// to a separate queue and this queue will block until all are done.
|
||||
volatile std::size_t outstanding_machines;
|
||||
std::size_t outstanding_machines;
|
||||
std::condition_variable condition;
|
||||
std::mutex mutex;
|
||||
{
|
||||
@ -33,7 +33,7 @@ void MultiInterface<MachineType>::perform_parallel(const std::function<void(Mach
|
||||
if(machine) function(machine);
|
||||
|
||||
std::lock_guard lock(mutex);
|
||||
outstanding_machines--;
|
||||
--outstanding_machines;
|
||||
condition.notify_all();
|
||||
});
|
||||
}
|
||||
@ -53,7 +53,7 @@ void MultiInterface<MachineType>::perform_serial(const std::function<void(Machin
|
||||
}
|
||||
|
||||
// MARK: - MultiScanProducer
|
||||
void MultiScanProducer::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||
void MultiScanProducer::set_scan_target(Outputs::Display::ScanTarget *const scan_target) {
|
||||
scan_target_ = scan_target;
|
||||
|
||||
std::lock_guard machines_lock(machines_mutex_);
|
||||
@ -80,7 +80,12 @@ void MultiScanProducer::did_change_machine_order() {
|
||||
}
|
||||
|
||||
// MARK: - MultiAudioProducer
|
||||
MultiAudioProducer::MultiAudioProducer(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines, std::recursive_mutex &machines_mutex) : MultiInterface(machines, machines_mutex) {
|
||||
MultiAudioProducer::MultiAudioProducer(
|
||||
const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines,
|
||||
std::recursive_mutex &machines_mutex
|
||||
) :
|
||||
MultiInterface(machines, machines_mutex)
|
||||
{
|
||||
speaker_ = MultiSpeaker::create(machines);
|
||||
}
|
||||
|
||||
@ -96,7 +101,7 @@ void MultiAudioProducer::did_change_machine_order() {
|
||||
|
||||
// MARK: - MultiTimedMachine
|
||||
|
||||
void MultiTimedMachine::run_for(Time::Seconds duration) {
|
||||
void MultiTimedMachine::run_for(const Time::Seconds duration) {
|
||||
perform_parallel([duration](::MachineTypes::TimedMachine *machine) {
|
||||
if(machine->get_confidence() >= 0.01f) machine->run_for(duration);
|
||||
});
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../../Concurrency/AsyncTaskQueue.hpp"
|
||||
#include "../../../../Machines/MachineTypes.hpp"
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
#include "Concurrency/AsyncTaskQueue.hpp"
|
||||
#include "Machines/MachineTypes.hpp"
|
||||
#include "Machines/DynamicMachine.hpp"
|
||||
|
||||
#include "MultiSpeaker.hpp"
|
||||
|
||||
@ -21,88 +21,91 @@
|
||||
namespace Analyser::Dynamic {
|
||||
|
||||
template <typename MachineType> class MultiInterface {
|
||||
public:
|
||||
MultiInterface(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines, std::recursive_mutex &machines_mutex) :
|
||||
machines_(machines), machines_mutex_(machines_mutex), queues_(machines.size()) {}
|
||||
public:
|
||||
MultiInterface(
|
||||
const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines,
|
||||
std::recursive_mutex &machines_mutex
|
||||
) :
|
||||
machines_(machines), machines_mutex_(machines_mutex), queues_(machines.size()) {}
|
||||
|
||||
protected:
|
||||
/*!
|
||||
Performs a parallel for operation across all machines, performing the supplied
|
||||
function on each and returning only once all applications have completed.
|
||||
protected:
|
||||
/*!
|
||||
Performs a parallel for operation across all machines, performing the supplied
|
||||
function on each and returning only once all applications have completed.
|
||||
|
||||
No guarantees are extended as to which thread operations will occur on.
|
||||
*/
|
||||
void perform_parallel(const std::function<void(MachineType *)> &);
|
||||
No guarantees are extended as to which thread operations will occur on.
|
||||
*/
|
||||
void perform_parallel(const std::function<void(MachineType *)> &);
|
||||
|
||||
/*!
|
||||
Performs a serial for operation across all machines, performing the supplied
|
||||
function on each on the calling thread.
|
||||
*/
|
||||
void perform_serial(const std::function<void(MachineType *)> &);
|
||||
/*!
|
||||
Performs a serial for operation across all machines, performing the supplied
|
||||
function on each on the calling thread.
|
||||
*/
|
||||
void perform_serial(const std::function<void(MachineType *)> &);
|
||||
|
||||
protected:
|
||||
const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines_;
|
||||
std::recursive_mutex &machines_mutex_;
|
||||
protected:
|
||||
const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines_;
|
||||
std::recursive_mutex &machines_mutex_;
|
||||
|
||||
private:
|
||||
std::vector<Concurrency::AsyncTaskQueue<true>> queues_;
|
||||
private:
|
||||
std::vector<Concurrency::AsyncTaskQueue<true>> queues_;
|
||||
};
|
||||
|
||||
class MultiTimedMachine: public MultiInterface<MachineTypes::TimedMachine>, public MachineTypes::TimedMachine {
|
||||
public:
|
||||
using MultiInterface::MultiInterface;
|
||||
public:
|
||||
using MultiInterface::MultiInterface;
|
||||
|
||||
/*!
|
||||
Provides a mechanism by which a delegate can be informed each time a call to run_for has
|
||||
been received.
|
||||
*/
|
||||
struct Delegate {
|
||||
virtual void did_run_machines(MultiTimedMachine *) = 0;
|
||||
};
|
||||
/// Sets @c delegate as the receiver of delegate messages.
|
||||
void set_delegate(Delegate *delegate) {
|
||||
delegate_ = delegate;
|
||||
}
|
||||
/*!
|
||||
Provides a mechanism by which a delegate can be informed each time a call to run_for has
|
||||
been received.
|
||||
*/
|
||||
struct Delegate {
|
||||
virtual void did_run_machines(MultiTimedMachine *) = 0;
|
||||
};
|
||||
/// Sets @c delegate as the receiver of delegate messages.
|
||||
void set_delegate(Delegate *const delegate) {
|
||||
delegate_ = delegate;
|
||||
}
|
||||
|
||||
void run_for(Time::Seconds duration) final;
|
||||
void run_for(Time::Seconds duration) final;
|
||||
|
||||
private:
|
||||
void run_for(const Cycles) final {}
|
||||
Delegate *delegate_ = nullptr;
|
||||
private:
|
||||
void run_for(Cycles) final {}
|
||||
Delegate *delegate_ = nullptr;
|
||||
};
|
||||
|
||||
class MultiScanProducer: public MultiInterface<MachineTypes::ScanProducer>, public MachineTypes::ScanProducer {
|
||||
public:
|
||||
using MultiInterface::MultiInterface;
|
||||
public:
|
||||
using MultiInterface::MultiInterface;
|
||||
|
||||
/*!
|
||||
Informs the MultiScanProducer that the order of machines has changed; it
|
||||
uses this as an opportunity to synthesis any CRTMachine::Machine::Delegate messages that
|
||||
are necessary to bridge the gap between one machine and the next.
|
||||
*/
|
||||
void did_change_machine_order();
|
||||
/*!
|
||||
Informs the MultiScanProducer that the order of machines has changed; it
|
||||
uses this as an opportunity to synthesis any CRTMachine::Machine::Delegate messages that
|
||||
are necessary to bridge the gap between one machine and the next.
|
||||
*/
|
||||
void did_change_machine_order();
|
||||
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) final;
|
||||
Outputs::Display::ScanStatus get_scan_status() const final;
|
||||
void set_scan_target(Outputs::Display::ScanTarget *) final;
|
||||
Outputs::Display::ScanStatus get_scan_status() const final;
|
||||
|
||||
private:
|
||||
Outputs::Display::ScanTarget *scan_target_ = nullptr;
|
||||
private:
|
||||
Outputs::Display::ScanTarget *scan_target_ = nullptr;
|
||||
};
|
||||
|
||||
class MultiAudioProducer: public MultiInterface<MachineTypes::AudioProducer>, public MachineTypes::AudioProducer {
|
||||
public:
|
||||
MultiAudioProducer(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines, std::recursive_mutex &machines_mutex);
|
||||
public:
|
||||
MultiAudioProducer(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &, std::recursive_mutex &);
|
||||
|
||||
/*!
|
||||
Informs the MultiAudio that the order of machines has changed; it
|
||||
uses this as an opportunity to switch speaker delegates as appropriate.
|
||||
*/
|
||||
void did_change_machine_order();
|
||||
/*!
|
||||
Informs the MultiAudio that the order of machines has changed; it
|
||||
uses this as an opportunity to switch speaker delegates as appropriate.
|
||||
*/
|
||||
void did_change_machine_order();
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() final;
|
||||
Outputs::Speaker::Speaker *get_speaker() final;
|
||||
|
||||
private:
|
||||
MultiSpeaker *speaker_ = nullptr;
|
||||
private:
|
||||
MultiSpeaker *speaker_ = nullptr;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -28,7 +28,7 @@ MultiSpeaker::MultiSpeaker(const std::vector<Outputs::Speaker::Speaker *> &speak
|
||||
}
|
||||
}
|
||||
|
||||
float MultiSpeaker::get_ideal_clock_rate_in_range(float minimum, float maximum) {
|
||||
float MultiSpeaker::get_ideal_clock_rate_in_range(const float minimum, const float maximum) {
|
||||
float ideal = 0.0f;
|
||||
for(const auto &speaker: speakers_) {
|
||||
ideal += speaker->get_ideal_clock_rate_in_range(minimum, maximum);
|
||||
@ -37,7 +37,7 @@ float MultiSpeaker::get_ideal_clock_rate_in_range(float minimum, float maximum)
|
||||
return ideal / float(speakers_.size());
|
||||
}
|
||||
|
||||
void MultiSpeaker::set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) {
|
||||
void MultiSpeaker::set_computed_output_rate(const float cycles_per_second, const int buffer_size, const bool stereo) {
|
||||
stereo_output_ = stereo;
|
||||
for(const auto &speaker: speakers_) {
|
||||
speaker->set_computed_output_rate(cycles_per_second, buffer_size, stereo);
|
||||
@ -54,13 +54,13 @@ bool MultiSpeaker::get_is_stereo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void MultiSpeaker::set_output_volume(float volume) {
|
||||
void MultiSpeaker::set_output_volume(const float volume) {
|
||||
for(const auto &speaker: speakers_) {
|
||||
speaker->set_output_volume(volume);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiSpeaker::speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer) {
|
||||
void MultiSpeaker::speaker_did_complete_samples(Speaker *const speaker, const std::vector<int16_t> &buffer) {
|
||||
auto delegate = delegate_.load(std::memory_order_relaxed);
|
||||
if(!delegate) return;
|
||||
{
|
||||
@ -70,7 +70,7 @@ void MultiSpeaker::speaker_did_complete_samples(Speaker *speaker, const std::vec
|
||||
did_complete_samples(this, buffer, stereo_output_);
|
||||
}
|
||||
|
||||
void MultiSpeaker::speaker_did_change_input_clock(Speaker *speaker) {
|
||||
void MultiSpeaker::speaker_did_change_input_clock(Speaker *const speaker) {
|
||||
auto delegate = delegate_.load(std::memory_order_relaxed);
|
||||
if(!delegate) return;
|
||||
{
|
||||
@ -80,7 +80,7 @@ void MultiSpeaker::speaker_did_change_input_clock(Speaker *speaker) {
|
||||
delegate->speaker_did_change_input_clock(this);
|
||||
}
|
||||
|
||||
void MultiSpeaker::set_new_front_machine(::Machine::DynamicMachine *machine) {
|
||||
void MultiSpeaker::set_new_front_machine(::Machine::DynamicMachine *const machine) {
|
||||
{
|
||||
std::lock_guard lock_guard(front_speaker_mutex_);
|
||||
front_speaker_ = machine->audio_producer()->get_speaker();
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
#include "../../../../Outputs/Speaker/Speaker.hpp"
|
||||
#include "Machines/DynamicMachine.hpp"
|
||||
#include "Outputs/Speaker/Speaker.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@ -25,32 +25,32 @@ namespace Analyser::Dynamic {
|
||||
abreast of the current frontmost machine.
|
||||
*/
|
||||
class MultiSpeaker: public Outputs::Speaker::Speaker, Outputs::Speaker::Speaker::Delegate {
|
||||
public:
|
||||
/*!
|
||||
Provides a construction mechanism that may return nullptr, in the case that all included
|
||||
machines return nullptr as their speaker.
|
||||
*/
|
||||
static MultiSpeaker *create(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||
public:
|
||||
/*!
|
||||
Provides a construction mechanism that may return nullptr, in the case that all included
|
||||
machines return nullptr as their speaker.
|
||||
*/
|
||||
static MultiSpeaker *create(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &);
|
||||
|
||||
/// This class requires the caller to nominate changes in the frontmost machine.
|
||||
void set_new_front_machine(::Machine::DynamicMachine *machine);
|
||||
/// This class requires the caller to nominate changes in the frontmost machine.
|
||||
void set_new_front_machine(::Machine::DynamicMachine *);
|
||||
|
||||
// Below is the standard Outputs::Speaker::Speaker interface; see there for documentation.
|
||||
float get_ideal_clock_rate_in_range(float minimum, float maximum) override;
|
||||
void set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) override;
|
||||
bool get_is_stereo() override;
|
||||
void set_output_volume(float) override;
|
||||
// Below is the standard Outputs::Speaker::Speaker interface; see there for documentation.
|
||||
float get_ideal_clock_rate_in_range(float minimum, float maximum) override;
|
||||
void set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) override;
|
||||
bool get_is_stereo() override;
|
||||
void set_output_volume(float) override;
|
||||
|
||||
private:
|
||||
void speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer) final;
|
||||
void speaker_did_change_input_clock(Speaker *speaker) final;
|
||||
MultiSpeaker(const std::vector<Outputs::Speaker::Speaker *> &speakers);
|
||||
private:
|
||||
void speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer) final;
|
||||
void speaker_did_change_input_clock(Speaker *speaker) final;
|
||||
MultiSpeaker(const std::vector<Outputs::Speaker::Speaker *> &speakers);
|
||||
|
||||
std::vector<Outputs::Speaker::Speaker *> speakers_;
|
||||
Outputs::Speaker::Speaker *front_speaker_ = nullptr;
|
||||
std::mutex front_speaker_mutex_;
|
||||
std::vector<Outputs::Speaker::Speaker *> speakers_;
|
||||
Outputs::Speaker::Speaker *front_speaker_ = nullptr;
|
||||
std::mutex front_speaker_mutex_;
|
||||
|
||||
bool stereo_output_ = false;
|
||||
bool stereo_output_ = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
#include "MultiMachine.hpp"
|
||||
#include "../../../Outputs/Log.hpp"
|
||||
#include "Outputs/Log.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -27,7 +27,9 @@ MultiMachine::MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machin
|
||||
audio_producer_(machines_, machines_mutex_),
|
||||
joystick_machine_(machines_),
|
||||
keyboard_machine_(machines_),
|
||||
media_target_(machines_) {
|
||||
media_target_(machines_),
|
||||
media_change_observer_(machines_)
|
||||
{
|
||||
timed_machine_.set_delegate(this);
|
||||
}
|
||||
|
||||
@ -35,13 +37,13 @@ Activity::Source *MultiMachine::activity_source() {
|
||||
return nullptr; // TODO
|
||||
}
|
||||
|
||||
#define Provider(type, name, member) \
|
||||
type *MultiMachine::name() { \
|
||||
if(has_picked_) { \
|
||||
#define Provider(type, name, member) \
|
||||
type *MultiMachine::name() { \
|
||||
if(has_picked_) { \
|
||||
return machines_.front()->name(); \
|
||||
} else { \
|
||||
return &member; \
|
||||
} \
|
||||
} else { \
|
||||
return &member; \
|
||||
} \
|
||||
}
|
||||
|
||||
Provider(Configurable::Device, configurable_device, configurable_)
|
||||
@ -51,6 +53,7 @@ Provider(MachineTypes::AudioProducer, audio_producer, audio_producer_)
|
||||
Provider(MachineTypes::JoystickMachine, joystick_machine, joystick_machine_)
|
||||
Provider(MachineTypes::KeyboardMachine, keyboard_machine, keyboard_machine_)
|
||||
Provider(MachineTypes::MediaTarget, media_target, media_target_)
|
||||
Provider(MachineTypes::MediaChangeObserver, media_change_observer, media_change_observer_)
|
||||
|
||||
MachineTypes::MouseMachine *MultiMachine::mouse_machine() {
|
||||
// TODO.
|
||||
|
@ -8,14 +8,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Machines/DynamicMachine.hpp"
|
||||
#include "Machines/DynamicMachine.hpp"
|
||||
|
||||
#include "Implementation/MultiProducer.hpp"
|
||||
#include "Implementation/MultiConfigurable.hpp"
|
||||
#include "Implementation/MultiProducer.hpp"
|
||||
#include "Implementation/MultiJoystickMachine.hpp"
|
||||
#include "Implementation/MultiKeyboardMachine.hpp"
|
||||
#include "Implementation/MultiMediaTarget.hpp"
|
||||
#include "Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.hpp"
|
||||
#include "Analyser/Dynamic/MultiMachine/Implementation/MultiConfigurable.hpp"
|
||||
#include "Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.hpp"
|
||||
#include "Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.hpp"
|
||||
#include "Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp"
|
||||
#include "Analyser/Dynamic/MultiMachine/Implementation/MultiMediaTarget.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@ -38,44 +38,46 @@ namespace Analyser::Dynamic {
|
||||
the others in the set, that machine stops running.
|
||||
*/
|
||||
class MultiMachine: public ::Machine::DynamicMachine, public MultiTimedMachine::Delegate {
|
||||
public:
|
||||
/*!
|
||||
Allows a potential MultiMachine creator to enquire as to whether there's any benefit in
|
||||
requesting this class as a proxy.
|
||||
public:
|
||||
/*!
|
||||
Allows a potential MultiMachine creator to enquire as to whether there's any benefit in
|
||||
requesting this class as a proxy.
|
||||
|
||||
@returns @c true if the multimachine would discard all but the first machine in this list;
|
||||
@c false otherwise.
|
||||
*/
|
||||
static bool would_collapse(const std::vector<std::unique_ptr<DynamicMachine>> &machines);
|
||||
MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machines);
|
||||
@returns @c true if the multimachine would discard all but the first machine in this list;
|
||||
@c false otherwise.
|
||||
*/
|
||||
static bool would_collapse(const std::vector<std::unique_ptr<DynamicMachine>> &);
|
||||
MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&);
|
||||
|
||||
Activity::Source *activity_source() final;
|
||||
Configurable::Device *configurable_device() final;
|
||||
MachineTypes::TimedMachine *timed_machine() final;
|
||||
MachineTypes::ScanProducer *scan_producer() final;
|
||||
MachineTypes::AudioProducer *audio_producer() final;
|
||||
MachineTypes::JoystickMachine *joystick_machine() final;
|
||||
MachineTypes::KeyboardMachine *keyboard_machine() final;
|
||||
MachineTypes::MouseMachine *mouse_machine() final;
|
||||
MachineTypes::MediaTarget *media_target() final;
|
||||
void *raw_pointer() final;
|
||||
Activity::Source *activity_source() final;
|
||||
Configurable::Device *configurable_device() final;
|
||||
MachineTypes::TimedMachine *timed_machine() final;
|
||||
MachineTypes::ScanProducer *scan_producer() final;
|
||||
MachineTypes::AudioProducer *audio_producer() final;
|
||||
MachineTypes::JoystickMachine *joystick_machine() final;
|
||||
MachineTypes::KeyboardMachine *keyboard_machine() final;
|
||||
MachineTypes::MouseMachine *mouse_machine() final;
|
||||
MachineTypes::MediaTarget *media_target() final;
|
||||
MachineTypes::MediaChangeObserver *media_change_observer() final;
|
||||
void *raw_pointer() final;
|
||||
|
||||
private:
|
||||
void did_run_machines(MultiTimedMachine *) final;
|
||||
private:
|
||||
void did_run_machines(MultiTimedMachine *) final;
|
||||
|
||||
std::vector<std::unique_ptr<DynamicMachine>> machines_;
|
||||
std::recursive_mutex machines_mutex_;
|
||||
std::vector<std::unique_ptr<DynamicMachine>> machines_;
|
||||
std::recursive_mutex machines_mutex_;
|
||||
|
||||
MultiConfigurable configurable_;
|
||||
MultiTimedMachine timed_machine_;
|
||||
MultiScanProducer scan_producer_;
|
||||
MultiAudioProducer audio_producer_;
|
||||
MultiJoystickMachine joystick_machine_;
|
||||
MultiKeyboardMachine keyboard_machine_;
|
||||
MultiMediaTarget media_target_;
|
||||
MultiConfigurable configurable_;
|
||||
MultiTimedMachine timed_machine_;
|
||||
MultiScanProducer scan_producer_;
|
||||
MultiAudioProducer audio_producer_;
|
||||
MultiJoystickMachine joystick_machine_;
|
||||
MultiKeyboardMachine keyboard_machine_;
|
||||
MultiMediaTarget media_target_;
|
||||
MultiMediaChangeObserver media_change_observer_;
|
||||
|
||||
void pick_first();
|
||||
bool has_picked_ = false;
|
||||
void pick_first();
|
||||
bool has_picked_ = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ enum class Machine {
|
||||
MasterSystem,
|
||||
MSX,
|
||||
Oric,
|
||||
Plus4,
|
||||
PCCompatible,
|
||||
Vic20,
|
||||
ZX8081,
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#include "Disk.hpp"
|
||||
|
||||
#include "../../../Storage/Disk/Controller/DiskController.hpp"
|
||||
#include "../../../Storage/Disk/Encodings/MFM/Parser.hpp"
|
||||
#include "../../../Numeric/CRC.hpp"
|
||||
#include "Storage/Disk/Controller/DiskController.hpp"
|
||||
#include "Storage/Disk/Encodings/MFM/Parser.hpp"
|
||||
#include "Numeric/CRC.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
@ -49,27 +49,39 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetDFSCatalogue(const std::s
|
||||
char name[10];
|
||||
snprintf(name, 10, "%c.%.7s", names->samples[0][file_offset + 7] & 0x7f, &names->samples[0][file_offset]);
|
||||
new_file.name = name;
|
||||
new_file.load_address = uint32_t(details->samples[0][file_offset] | (details->samples[0][file_offset+1] << 8) | ((details->samples[0][file_offset+6]&0x0c) << 14));
|
||||
new_file.execution_address = uint32_t(details->samples[0][file_offset+2] | (details->samples[0][file_offset+3] << 8) | ((details->samples[0][file_offset+6]&0xc0) << 10));
|
||||
new_file.load_address = uint32_t(
|
||||
details->samples[0][file_offset] |
|
||||
(details->samples[0][file_offset+1] << 8) |
|
||||
((details->samples[0][file_offset+6]&0x0c) << 14)
|
||||
);
|
||||
new_file.execution_address = uint32_t(
|
||||
details->samples[0][file_offset+2] |
|
||||
(details->samples[0][file_offset+3] << 8) |
|
||||
((details->samples[0][file_offset+6]&0xc0) << 10)
|
||||
);
|
||||
if(names->samples[0][file_offset + 7] & 0x80) {
|
||||
// File is locked; it may not be altered or deleted.
|
||||
new_file.flags |= File::Flags::Locked;
|
||||
}
|
||||
|
||||
long data_length = long(details->samples[0][file_offset+4] | (details->samples[0][file_offset+5] << 8) | ((details->samples[0][file_offset+6]&0x30) << 12));
|
||||
auto data_length = long(
|
||||
details->samples[0][file_offset+4] |
|
||||
(details->samples[0][file_offset+5] << 8) |
|
||||
((details->samples[0][file_offset+6]&0x30) << 12)
|
||||
);
|
||||
int start_sector = details->samples[0][file_offset+7] | ((details->samples[0][file_offset+6]&0x03) << 8);
|
||||
new_file.data.reserve(size_t(data_length));
|
||||
|
||||
if(start_sector < 2) continue;
|
||||
while(data_length > 0) {
|
||||
uint8_t sector = uint8_t(start_sector % 10);
|
||||
uint8_t track = uint8_t(start_sector / 10);
|
||||
start_sector++;
|
||||
const uint8_t sector = uint8_t(start_sector % 10);
|
||||
const uint8_t track = uint8_t(start_sector / 10);
|
||||
++start_sector;
|
||||
|
||||
const Storage::Encodings::MFM::Sector *next_sector = parser.sector(0, track, sector);
|
||||
if(!next_sector) break;
|
||||
|
||||
long length_from_sector = std::min(data_length, 256l);
|
||||
const long length_from_sector = std::min(data_length, 256l);
|
||||
new_file.data.insert(new_file.data.end(), next_sector->samples[0].begin(), next_sector->samples[0].begin() + length_from_sector);
|
||||
data_length -= length_from_sector;
|
||||
}
|
||||
@ -133,7 +145,8 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetADFSCatalogue(const std::
|
||||
}
|
||||
|
||||
// Parse the root directory, at least.
|
||||
for(std::size_t file_offset = 0x005; file_offset < (catalogue->has_large_sectors ? 0x7d7 : 0x4cb); file_offset += 0x1a) {
|
||||
const std::size_t directory_extent = catalogue->has_large_sectors ? 0x7d7 : 0x4cb;
|
||||
for(std::size_t file_offset = 0x005; file_offset < directory_extent; file_offset += 0x1a) {
|
||||
// Obtain the name, which will be at most ten characters long, and will
|
||||
// be terminated by either a NULL character or a \r.
|
||||
char name[11]{};
|
||||
@ -190,11 +203,16 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetADFSCatalogue(const std::
|
||||
|
||||
new_file.data.reserve(size);
|
||||
while(new_file.data.size() < size) {
|
||||
const Storage::Encodings::MFM::Sector *const sector = parser.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;
|
||||
|
||||
const auto length_from_sector = std::min(size - new_file.data.size(), sector->samples[0].size());
|
||||
new_file.data.insert(new_file.data.end(), sector->samples[0].begin(), sector->samples[0].begin() + ssize_t(length_from_sector));
|
||||
new_file.data.insert(
|
||||
new_file.data.end(),
|
||||
sector->samples[0].begin(),
|
||||
sector->samples[0].begin() + ssize_t(length_from_sector)
|
||||
);
|
||||
++start_sector;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "File.hpp"
|
||||
#include "../../../Storage/Disk/Disk.hpp"
|
||||
#include "Storage/Disk/Disk.hpp"
|
||||
|
||||
namespace Analyser::Static::Acorn {
|
||||
|
||||
@ -27,7 +27,7 @@ struct Catalogue {
|
||||
} bootOption;
|
||||
};
|
||||
|
||||
std::unique_ptr<Catalogue> GetDFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk);
|
||||
std::unique_ptr<Catalogue> GetADFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk);
|
||||
std::unique_ptr<Catalogue> GetDFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &);
|
||||
std::unique_ptr<Catalogue> GetADFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &);
|
||||
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "Tape.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
#include "../../../Numeric/StringSimilarity.hpp"
|
||||
#include "Numeric/StringSimilarity.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
@ -20,20 +20,20 @@
|
||||
using namespace Analyser::Static::Acorn;
|
||||
|
||||
static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
AcornCartridgesFrom(const std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
|
||||
AcornCartridgesFrom(const std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
|
||||
std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> acorn_cartridges;
|
||||
|
||||
for(const auto &cartridge : cartridges) {
|
||||
const auto &segments = cartridge->get_segments();
|
||||
|
||||
// only one mapped item is allowed
|
||||
// Only one mapped item is allowed.
|
||||
if(segments.size() != 1) continue;
|
||||
|
||||
// which must be 8 or 16 kb in size
|
||||
// Cartridges must be 8 or 16 kb in size.
|
||||
const Storage::Cartridge::Cartridge::Segment &segment = segments.front();
|
||||
if(segment.data.size() != 0x4000 && segment.data.size() != 0x2000) continue;
|
||||
|
||||
// is a copyright string present?
|
||||
// Check copyright string.
|
||||
const uint8_t copyright_offset = segment.data[7];
|
||||
if(
|
||||
segment.data[copyright_offset] != 0x00 ||
|
||||
@ -42,16 +42,16 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
segment.data[copyright_offset+3] != 0x29
|
||||
) continue;
|
||||
|
||||
// is the language entry point valid?
|
||||
// Check language entry point.
|
||||
if(!(
|
||||
(segment.data[0] == 0x00 && segment.data[1] == 0x00 && segment.data[2] == 0x00) ||
|
||||
(segment.data[0] != 0x00 && segment.data[2] >= 0x80 && segment.data[2] < 0xc0)
|
||||
)) continue;
|
||||
|
||||
// is the service entry point valid?
|
||||
// Check service entry point.
|
||||
if(!(segment.data[5] >= 0x80 && segment.data[5] < 0xc0)) continue;
|
||||
|
||||
// probability of a random binary blob that isn't an Acorn ROM proceeding to here:
|
||||
// Probability of a random binary blob that isn't an Acorn ROM proceeding to here:
|
||||
// 1/(2^32) *
|
||||
// ( ((2^24)-1)/(2^24)*(1/4) + 1/(2^24) ) *
|
||||
// 1/4
|
||||
@ -62,44 +62,52 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
return acorn_cartridges;
|
||||
}
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &file_name,
|
||||
TargetPlatform::IntType,
|
||||
bool
|
||||
) {
|
||||
auto target8bit = std::make_unique<ElectronTarget>();
|
||||
auto targetArchimedes = std::make_unique<ArchimedesTarget>();
|
||||
|
||||
// Copy appropriate cartridges to the 8-bit target.
|
||||
target8bit->media.cartridges = AcornCartridgesFrom(media.cartridges);
|
||||
|
||||
// If there are any tapes, attempt to get data from the first.
|
||||
// If there are tapes, attempt to get data from the first.
|
||||
if(!media.tapes.empty()) {
|
||||
std::shared_ptr<Storage::Tape::Tape> tape = media.tapes.front();
|
||||
std::vector<File> files = GetFiles(tape);
|
||||
tape->reset();
|
||||
auto serialiser = tape->serialiser();
|
||||
std::vector<File> files = GetFiles(*serialiser);
|
||||
|
||||
// continue if there are any files
|
||||
if(!files.empty()) {
|
||||
bool is_basic = true;
|
||||
|
||||
// If a file is execute-only, that means *RUN.
|
||||
if(files.front().flags & File::Flags::ExecuteOnly) is_basic = false;
|
||||
if(files.front().flags & File::Flags::ExecuteOnly) {
|
||||
is_basic = false;
|
||||
}
|
||||
|
||||
// check also for a continuous threading of BASIC lines; if none then this probably isn't BASIC code,
|
||||
// so that's also justification to *RUN
|
||||
std::size_t pointer = 0;
|
||||
uint8_t *const data = &files.front().data[0];
|
||||
const std::size_t data_size = files.front().data.size();
|
||||
while(1) {
|
||||
if(pointer >= data_size-1 || data[pointer] != 13) {
|
||||
is_basic = false;
|
||||
break;
|
||||
// Check also for a continuous threading of BASIC lines; if none then this probably isn't BASIC code,
|
||||
// so that's also justification to *RUN.
|
||||
if(is_basic) {
|
||||
std::size_t pointer = 0;
|
||||
uint8_t *const data = &files.front().data[0];
|
||||
const std::size_t data_size = files.front().data.size();
|
||||
while(true) {
|
||||
if(pointer >= data_size-1 || data[pointer] != 0x0d) {
|
||||
is_basic = false;
|
||||
break;
|
||||
}
|
||||
if((data[pointer+1]&0x7f) == 0x7f) break;
|
||||
pointer += data[pointer+3];
|
||||
}
|
||||
if((data[pointer+1]&0x7f) == 0x7f) break;
|
||||
pointer += data[pointer+3];
|
||||
}
|
||||
|
||||
// Inspect first file. If it's protected or doesn't look like BASIC
|
||||
// then the loading command is *RUN. Otherwise it's CHAIN"".
|
||||
target8bit->loading_command = is_basic ? "CHAIN\"\"\n" : "*RUN\n";
|
||||
|
||||
target8bit->media.tapes = media.tapes;
|
||||
}
|
||||
}
|
||||
@ -121,7 +129,7 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me
|
||||
target8bit->has_pres_adfs = bool(adfs_catalogue);
|
||||
|
||||
// Check whether a simple shift+break will do for loading this disk.
|
||||
Catalogue::BootOption bootOption = (dfs_catalogue ?: adfs_catalogue)->bootOption;
|
||||
const auto bootOption = (dfs_catalogue ?: adfs_catalogue)->bootOption;
|
||||
if(bootOption != Catalogue::BootOption::None) {
|
||||
target8bit->should_shift_restart = true;
|
||||
} else {
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::Acorn {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -10,70 +10,68 @@
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "../../../Numeric/CRC.hpp"
|
||||
#include "../../../Storage/Tape/Parsers/Acorn.hpp"
|
||||
#include "Numeric/CRC.hpp"
|
||||
#include "Storage/Tape/Parsers/Acorn.hpp"
|
||||
|
||||
using namespace Analyser::Static::Acorn;
|
||||
|
||||
static std::unique_ptr<File::Chunk> GetNextChunk(const std::shared_ptr<Storage::Tape::Tape> &tape, Storage::Tape::Acorn::Parser &parser) {
|
||||
static std::unique_ptr<File::Chunk> GetNextChunk(
|
||||
Storage::Tape::TapeSerialiser &serialiser,
|
||||
Storage::Tape::Acorn::Parser &parser
|
||||
) {
|
||||
auto new_chunk = std::make_unique<File::Chunk>();
|
||||
int shift_register = 0;
|
||||
|
||||
// TODO: move this into the parser
|
||||
const auto shift = [&] {
|
||||
shift_register = (shift_register >> 1) | (parser.get_next_bit(tape) << 9);
|
||||
const auto find = [&](int target) {
|
||||
while(!serialiser.is_at_end() && (shift_register != target)) {
|
||||
shift_register = (shift_register >> 1) | (parser.get_next_bit(serialiser) << 9);
|
||||
}
|
||||
};
|
||||
|
||||
// find next area of high tone
|
||||
while(!tape->is_at_end() && (shift_register != 0x3ff)) {
|
||||
shift();
|
||||
}
|
||||
|
||||
// find next 0x2a (swallowing stop bit)
|
||||
while(!tape->is_at_end() && (shift_register != 0x254)) {
|
||||
shift();
|
||||
}
|
||||
|
||||
// Find first sync byte that follows high tone.
|
||||
find(0x3ff);
|
||||
find(0x254); // i.e. 0x2a wrapped in a 1 start bit and a 0 stop bit.
|
||||
parser.reset_crc();
|
||||
parser.reset_error_flag();
|
||||
|
||||
// read out name
|
||||
char name[11];
|
||||
// Read name.
|
||||
char name[11]{};
|
||||
std::size_t name_ptr = 0;
|
||||
while(!tape->is_at_end() && name_ptr < sizeof(name)) {
|
||||
name[name_ptr] = char(parser.get_next_byte(tape));
|
||||
while(!serialiser.is_at_end() && name_ptr < sizeof(name)) {
|
||||
name[name_ptr] = char(parser.get_next_byte(serialiser));
|
||||
if(!name[name_ptr]) break;
|
||||
++name_ptr;
|
||||
}
|
||||
name[sizeof(name)-1] = '\0';
|
||||
new_chunk->name = name;
|
||||
|
||||
// addresses
|
||||
new_chunk->load_address = uint32_t(parser.get_next_word(tape));
|
||||
new_chunk->execution_address = uint32_t(parser.get_next_word(tape));
|
||||
new_chunk->block_number = uint16_t(parser.get_next_short(tape));
|
||||
new_chunk->block_length = uint16_t(parser.get_next_short(tape));
|
||||
new_chunk->block_flag = uint8_t(parser.get_next_byte(tape));
|
||||
new_chunk->next_address = uint32_t(parser.get_next_word(tape));
|
||||
// Read rest of header fields.
|
||||
new_chunk->load_address = uint32_t(parser.get_next_word(serialiser));
|
||||
new_chunk->execution_address = uint32_t(parser.get_next_word(serialiser));
|
||||
new_chunk->block_number = uint16_t(parser.get_next_short(serialiser));
|
||||
new_chunk->block_length = uint16_t(parser.get_next_short(serialiser));
|
||||
new_chunk->block_flag = uint8_t(parser.get_next_byte(serialiser));
|
||||
new_chunk->next_address = uint32_t(parser.get_next_word(serialiser));
|
||||
|
||||
uint16_t calculated_header_crc = parser.get_crc();
|
||||
uint16_t stored_header_crc = uint16_t(parser.get_next_short(tape));
|
||||
stored_header_crc = uint16_t((stored_header_crc >> 8) | (stored_header_crc << 8));
|
||||
new_chunk->header_crc_matched = stored_header_crc == calculated_header_crc;
|
||||
const auto matched_crc = [&]() {
|
||||
const uint16_t calculated_crc = parser.get_crc();
|
||||
uint16_t stored_crc = uint16_t(parser.get_next_short(serialiser));
|
||||
stored_crc = uint16_t((stored_crc >> 8) | (stored_crc << 8));
|
||||
return stored_crc == calculated_crc;
|
||||
};
|
||||
|
||||
new_chunk->header_crc_matched = matched_crc();
|
||||
|
||||
if(!new_chunk->header_crc_matched) return nullptr;
|
||||
|
||||
parser.reset_crc();
|
||||
new_chunk->data.reserve(new_chunk->block_length);
|
||||
for(int c = 0; c < new_chunk->block_length; c++) {
|
||||
new_chunk->data.push_back(uint8_t(parser.get_next_byte(tape)));
|
||||
}
|
||||
|
||||
// Bit 6 of the block flag means 'empty block'; allow it to override declared block length.
|
||||
if(new_chunk->block_length && !(new_chunk->block_flag&0x40)) {
|
||||
uint16_t calculated_data_crc = parser.get_crc();
|
||||
uint16_t stored_data_crc = uint16_t(parser.get_next_short(tape));
|
||||
stored_data_crc = uint16_t((stored_data_crc >> 8) | (stored_data_crc << 8));
|
||||
new_chunk->data_crc_matched = stored_data_crc == calculated_data_crc;
|
||||
parser.reset_crc();
|
||||
new_chunk->data.reserve(new_chunk->block_length);
|
||||
for(int c = 0; c < new_chunk->block_length; c++) {
|
||||
new_chunk->data.push_back(uint8_t(parser.get_next_byte(serialiser)));
|
||||
}
|
||||
new_chunk->data_crc_matched = matched_crc();
|
||||
} else {
|
||||
new_chunk->data_crc_matched = true;
|
||||
}
|
||||
@ -82,67 +80,62 @@ static std::unique_ptr<File::Chunk> GetNextChunk(const std::shared_ptr<Storage::
|
||||
}
|
||||
|
||||
static std::unique_ptr<File> GetNextFile(std::deque<File::Chunk> &chunks) {
|
||||
// find next chunk with a block number of 0
|
||||
while(chunks.size() && chunks.front().block_number) {
|
||||
// Find next chunk with a block number of 0.
|
||||
while(!chunks.empty() && chunks.front().block_number) {
|
||||
chunks.pop_front();
|
||||
}
|
||||
if(chunks.empty()) return nullptr;
|
||||
|
||||
if(!chunks.size()) return nullptr;
|
||||
|
||||
// accumulate chunks for as long as block number is sequential and the end-of-file bit isn't set
|
||||
// Accumulate sequential blocks until end-of-file bit is set.
|
||||
auto file = std::make_unique<File>();
|
||||
|
||||
uint16_t block_number = 0;
|
||||
|
||||
while(chunks.size()) {
|
||||
while(!chunks.empty()) {
|
||||
if(chunks.front().block_number != block_number) return nullptr;
|
||||
|
||||
bool was_last = chunks.front().block_flag & 0x80;
|
||||
const bool was_last = chunks.front().block_flag & 0x80;
|
||||
file->chunks.push_back(chunks.front());
|
||||
chunks.pop_front();
|
||||
block_number++;
|
||||
++block_number;
|
||||
|
||||
if(was_last) break;
|
||||
}
|
||||
|
||||
// accumulate total data, copy flags appropriately
|
||||
// Grab metadata flags.
|
||||
file->name = file->chunks.front().name;
|
||||
file->load_address = file->chunks.front().load_address;
|
||||
file->execution_address = file->chunks.front().execution_address;
|
||||
// I think the final chunk's flags are the ones that count; TODO: check.
|
||||
if(file->chunks.back().block_flag & 0x01) {
|
||||
// File is locked, which in more generalised terms means it is
|
||||
// for execution only.
|
||||
// File is locked i.e. for execution only.
|
||||
file->flags |= File::Flags::ExecuteOnly;
|
||||
}
|
||||
|
||||
// copy all data into a single big block
|
||||
for(File::Chunk chunk : file->chunks) {
|
||||
// Copy data into a single big block.
|
||||
file->data.reserve(file->chunks.size() * 256);
|
||||
for(auto &chunk : file->chunks) {
|
||||
file->data.insert(file->data.end(), chunk.data.begin(), chunk.data.end());
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
std::vector<File> Analyser::Static::Acorn::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
std::vector<File> Analyser::Static::Acorn::GetFiles(Storage::Tape::TapeSerialiser &serialiser) {
|
||||
Storage::Tape::Acorn::Parser parser;
|
||||
|
||||
// populate chunk list
|
||||
// Read all chunks.
|
||||
std::deque<File::Chunk> chunk_list;
|
||||
while(!tape->is_at_end()) {
|
||||
std::unique_ptr<File::Chunk> chunk = GetNextChunk(tape, parser);
|
||||
while(!serialiser.is_at_end()) {
|
||||
const std::unique_ptr<File::Chunk> chunk = GetNextChunk(serialiser, parser);
|
||||
if(chunk) {
|
||||
chunk_list.push_back(*chunk);
|
||||
chunk_list.push_back(std::move(*chunk));
|
||||
}
|
||||
}
|
||||
|
||||
// decompose into file list
|
||||
// Convert to files.
|
||||
std::vector<File> file_list;
|
||||
|
||||
while(chunk_list.size()) {
|
||||
std::unique_ptr<File> next_file = GetNextFile(chunk_list);
|
||||
while(!chunk_list.empty()) {
|
||||
const std::unique_ptr<File> next_file = GetNextFile(chunk_list);
|
||||
if(next_file) {
|
||||
file_list.push_back(*next_file);
|
||||
file_list.push_back(std::move(*next_file));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,13 +8,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "File.hpp"
|
||||
#include "../../../Storage/Tape/Tape.hpp"
|
||||
#include "Storage/Tape/Tape.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Analyser::Static::Acorn {
|
||||
|
||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||
std::vector<File> GetFiles(Storage::Tape::TapeSerialiser &);
|
||||
|
||||
}
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::Acorn {
|
||||
@ -23,14 +23,16 @@ struct ElectronTarget: public ::Analyser::Static::Target, public Reflection::Str
|
||||
bool should_shift_restart = false;
|
||||
std::string loading_command;
|
||||
|
||||
ElectronTarget() : Analyser::Static::Target(Machine::Electron) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(has_pres_adfs);
|
||||
DeclareField(has_acorn_adfs);
|
||||
DeclareField(has_dfs);
|
||||
DeclareField(has_ap6_rom);
|
||||
DeclareField(has_sideways_ram);
|
||||
}
|
||||
ElectronTarget() : Analyser::Static::Target(Machine::Electron) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<ElectronTarget>;
|
||||
void declare_fields() {
|
||||
DeclareField(has_pres_adfs);
|
||||
DeclareField(has_acorn_adfs);
|
||||
DeclareField(has_dfs);
|
||||
DeclareField(has_ap6_rom);
|
||||
DeclareField(has_sideways_ram);
|
||||
}
|
||||
};
|
||||
|
||||
@ -38,6 +40,10 @@ struct ArchimedesTarget: public ::Analyser::Static::Target, public Reflection::S
|
||||
std::string main_program;
|
||||
|
||||
ArchimedesTarget() : Analyser::Static::Target(Machine::Archimedes) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<ArchimedesTarget>;
|
||||
void declare_fields() {}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -9,9 +9,14 @@
|
||||
#include "StaticAnalyser.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Amiga::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::Amiga::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
bool is_confident
|
||||
) {
|
||||
// This analyser can comprehend disks and mass-storage devices only.
|
||||
if(media.disks.empty()) return {};
|
||||
if(media.disks.empty() && !is_confident) return {};
|
||||
|
||||
// As there is at least one usable media image, wave it through.
|
||||
Analyser::Static::TargetList targets;
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::Amiga {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
|
||||
namespace Analyser::Static::Amiga {
|
||||
|
||||
@ -28,13 +28,15 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
ChipRAM chip_ram = ChipRAM::FiveHundredAndTwelveKilobytes;
|
||||
FastRAM fast_ram = FastRAM::EightMegabytes;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::Amiga) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(fast_ram);
|
||||
DeclareField(chip_ram);
|
||||
AnnounceEnum(FastRAM);
|
||||
AnnounceEnum(ChipRAM);
|
||||
}
|
||||
Target() : Analyser::Static::Target(Machine::Amiga) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<Target>;
|
||||
void declare_fields() {
|
||||
DeclareField(fast_ram);
|
||||
DeclareField(chip_ram);
|
||||
AnnounceEnum(FastRAM);
|
||||
AnnounceEnum(ChipRAM);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8,15 +8,15 @@
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "../../../Storage/Disk/Parsers/CPM.hpp"
|
||||
#include "../../../Storage/Disk/Encodings/MFM/Parser.hpp"
|
||||
#include "../../../Storage/Tape/Parsers/Spectrum.hpp"
|
||||
#include "Storage/Disk/Parsers/CPM.hpp"
|
||||
#include "Storage/Disk/Encodings/MFM/Parser.hpp"
|
||||
#include "Storage/Tape/Parsers/Spectrum.hpp"
|
||||
|
||||
#include "Target.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
|
||||
bool strcmp_insensitive(const char *a, const char *b) {
|
||||
@ -63,8 +63,8 @@ std::string RunCommandFor(const Storage::Disk::CPM::File &file) {
|
||||
|
||||
void InspectCatalogue(
|
||||
const Storage::Disk::CPM::Catalogue &catalogue,
|
||||
const std::unique_ptr<Analyser::Static::AmstradCPC::Target> &target) {
|
||||
|
||||
const std::unique_ptr<Analyser::Static::AmstradCPC::Target> &target
|
||||
) {
|
||||
std::vector<const Storage::Disk::CPM::File *> candidate_files;
|
||||
candidate_files.reserve(catalogue.files.size());
|
||||
for(const auto &file : catalogue.files) {
|
||||
@ -158,7 +158,10 @@ void InspectCatalogue(
|
||||
target->loading_command = "cat\n";
|
||||
}
|
||||
|
||||
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(Storage::Encodings::MFM::Density::Double, disk);
|
||||
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) {
|
||||
@ -182,7 +185,7 @@ bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &disk, const std
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsAmstradTape(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
bool IsAmstradTape(Storage::Tape::TapeSerialiser &serialiser) {
|
||||
// Limited sophistication here; look for a CPC-style file header, that is
|
||||
// any Spectrum-esque block with a synchronisation character of 0x2c.
|
||||
//
|
||||
@ -191,7 +194,7 @@ bool IsAmstradTape(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
Parser parser(Parser::MachineType::AmstradCPC);
|
||||
|
||||
while(true) {
|
||||
const auto block = parser.find_block(tape);
|
||||
const auto block = parser.find_block(serialiser);
|
||||
if(!block) break;
|
||||
|
||||
if(block->type == 0x2c) {
|
||||
@ -204,7 +207,12 @@ bool IsAmstradTape(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
|
||||
} // namespace
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
bool
|
||||
) {
|
||||
TargetList destination;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->confidence = 0.5;
|
||||
@ -214,7 +222,8 @@ Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets(const Medi
|
||||
if(!media.tapes.empty()) {
|
||||
bool has_cpc_tape = false;
|
||||
for(auto &tape: media.tapes) {
|
||||
has_cpc_tape |= IsAmstradTape(tape);
|
||||
const auto serialiser = tape->serialiser();
|
||||
has_cpc_tape |= IsAmstradTape(*serialiser);
|
||||
}
|
||||
|
||||
if(has_cpc_tape) {
|
||||
@ -233,7 +242,8 @@ Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets(const Medi
|
||||
|
||||
for(auto &disk: media.disks) {
|
||||
// Check for an ordinary catalogue, making sure this isn't actually a ZX Spectrum disk.
|
||||
std::unique_ptr<Storage::Disk::CPM::Catalogue> data_catalogue = Storage::Disk::CPM::GetCatalogue(disk, data_format, false);
|
||||
std::unique_ptr<Storage::Disk::CPM::Catalogue> data_catalogue =
|
||||
Storage::Disk::CPM::GetCatalogue(disk, data_format, false);
|
||||
if(data_catalogue && !data_catalogue->is_zx_spectrum_booter()) {
|
||||
InspectCatalogue(*data_catalogue, target);
|
||||
target->media.disks.push_back(disk);
|
||||
@ -247,7 +257,8 @@ Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets(const Medi
|
||||
}
|
||||
|
||||
// Failing that check for a system catalogue.
|
||||
std::unique_ptr<Storage::Disk::CPM::Catalogue> system_catalogue = Storage::Disk::CPM::GetCatalogue(disk, system_format, false);
|
||||
std::unique_ptr<Storage::Disk::CPM::Catalogue> system_catalogue =
|
||||
Storage::Disk::CPM::GetCatalogue(disk, system_format, false);
|
||||
if(system_catalogue && !system_catalogue->is_zx_spectrum_booter()) {
|
||||
InspectCatalogue(*system_catalogue, target);
|
||||
target->media.disks.push_back(disk);
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::AmstradCPC {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Reflection/Enum.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::AmstradCPC {
|
||||
@ -26,13 +26,15 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
// This is used internally for testing; it therefore isn't exposed reflectively.
|
||||
bool catch_ssm_codes = false;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::AmstradCPC) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(model);
|
||||
DeclareField(crtc_type);
|
||||
AnnounceEnum(Model);
|
||||
AnnounceEnum(CRTCType);
|
||||
}
|
||||
Target() : Analyser::Static::Target(Machine::AmstradCPC) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<Target>;
|
||||
void declare_fields() {
|
||||
DeclareField(model);
|
||||
DeclareField(crtc_type);
|
||||
AnnounceEnum(Model);
|
||||
AnnounceEnum(CRTCType);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9,7 +9,12 @@
|
||||
#include "StaticAnalyser.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::AppleII::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::AppleII::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
bool
|
||||
) {
|
||||
auto target = std::make_unique<Target>();
|
||||
target->media = media;
|
||||
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::AppleII {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Enum.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
|
||||
namespace Analyser::Static::AppleII {
|
||||
|
||||
@ -36,21 +36,23 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
SCSIController scsi_controller = SCSIController::None;
|
||||
bool has_mockingboard = true;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::AppleII) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(model);
|
||||
DeclareField(disk_controller);
|
||||
DeclareField(scsi_controller);
|
||||
DeclareField(has_mockingboard);
|
||||
Target() : Analyser::Static::Target(Machine::AppleII) {}
|
||||
|
||||
AnnounceEnum(Model);
|
||||
AnnounceEnum(DiskController);
|
||||
AnnounceEnum(SCSIController);
|
||||
}
|
||||
private:
|
||||
friend Reflection::StructImpl<Target>;
|
||||
void declare_fields() {
|
||||
DeclareField(model);
|
||||
DeclareField(disk_controller);
|
||||
DeclareField(scsi_controller);
|
||||
DeclareField(has_mockingboard);
|
||||
|
||||
AnnounceEnum(Model);
|
||||
AnnounceEnum(DiskController);
|
||||
AnnounceEnum(SCSIController);
|
||||
}
|
||||
};
|
||||
|
||||
constexpr bool is_iie(Target::Model model) {
|
||||
constexpr bool is_iie(const Target::Model model) {
|
||||
return model == Target::Model::IIe || model == Target::Model::EnhancedIIe;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,12 @@
|
||||
#include "StaticAnalyser.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::AppleIIgs::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::AppleIIgs::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
bool
|
||||
) {
|
||||
auto target = std::make_unique<Target>();
|
||||
target->media = media;
|
||||
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::AppleIIgs {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Enum.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
|
||||
namespace Analyser::Static::AppleIIgs {
|
||||
|
||||
@ -29,13 +29,15 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
Model model = Model::ROM01;
|
||||
MemoryModel memory_model = MemoryModel::EightMB;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::AppleIIgs) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(model);
|
||||
DeclareField(memory_model);
|
||||
AnnounceEnum(Model);
|
||||
AnnounceEnum(MemoryModel);
|
||||
}
|
||||
Target() : Analyser::Static::Target(Machine::AppleIIgs) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<Target>;
|
||||
void declare_fields() {
|
||||
DeclareField(model);
|
||||
DeclareField(memory_model);
|
||||
AnnounceEnum(Model);
|
||||
AnnounceEnum(MemoryModel);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
#include "Target.hpp"
|
||||
|
||||
#include "../Disassembler/6502.hpp"
|
||||
#include "Analyser/Static/Disassembler/6502.hpp"
|
||||
|
||||
using namespace Analyser::Static::Atari2600;
|
||||
using Target = Analyser::Static::Atari2600::Target;
|
||||
@ -33,11 +33,13 @@ static void DeterminePagingFor2kCartridge(Target &target, const Storage::Cartrid
|
||||
// Assume that any kind of store that looks likely to be intended for large amounts of memory implies
|
||||
// large amounts of memory.
|
||||
bool has_wide_area_store = false;
|
||||
for(std::map<uint16_t, Analyser::Static::MOS6502::Instruction>::value_type &entry : high_location_disassembly.instructions_by_address) {
|
||||
for(const auto &entry : high_location_disassembly.instructions_by_address) {
|
||||
using Instruction = Analyser::Static::MOS6502::Instruction;
|
||||
if(entry.second.operation == Analyser::Static::MOS6502::Instruction::STA) {
|
||||
has_wide_area_store |= entry.second.addressing_mode == Analyser::Static::MOS6502::Instruction::Indirect;
|
||||
has_wide_area_store |= entry.second.addressing_mode == Analyser::Static::MOS6502::Instruction::IndexedIndirectX;
|
||||
has_wide_area_store |= entry.second.addressing_mode == Analyser::Static::MOS6502::Instruction::IndirectIndexedY;
|
||||
has_wide_area_store |=
|
||||
entry.second.addressing_mode == Instruction::Indirect ||
|
||||
entry.second.addressing_mode == Instruction::IndexedIndirectX ||
|
||||
entry.second.addressing_mode == Instruction::IndirectIndexedY;
|
||||
|
||||
if(has_wide_area_store) break;
|
||||
}
|
||||
@ -50,13 +52,21 @@ static void DeterminePagingFor2kCartridge(Target &target, const Storage::Cartrid
|
||||
if(has_wide_area_store) target.paging_model = Target::PagingModel::CommaVid;
|
||||
}
|
||||
|
||||
static void DeterminePagingFor8kCartridge(Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
static void DeterminePagingFor8kCartridge(
|
||||
Target &target,
|
||||
const Storage::Cartridge::Cartridge::Segment &segment,
|
||||
const Analyser::Static::MOS6502::Disassembly &disassembly
|
||||
) {
|
||||
// Activision stack titles have their vectors at the top of the low 4k, not the top, and
|
||||
// always list 0xf000 as both vectors; they do not repeat them, and, inexplicably, they all
|
||||
// issue an SEI as their first instruction (maybe some sort of relic of the development environment?).
|
||||
if(
|
||||
segment.data[4095] == 0xf0 && segment.data[4093] == 0xf0 && segment.data[4094] == 0x00 && segment.data[4092] == 0x00 &&
|
||||
(segment.data[8191] != 0xf0 || segment.data[8189] != 0xf0 || segment.data[8190] != 0x00 || segment.data[8188] != 0x00) &&
|
||||
segment.data[4095] == 0xf0 && segment.data[4093] == 0xf0 &&
|
||||
segment.data[4094] == 0x00 && segment.data[4092] == 0x00 &&
|
||||
(
|
||||
segment.data[8191] != 0xf0 || segment.data[8189] != 0xf0 ||
|
||||
segment.data[8190] != 0x00 || segment.data[8188] != 0x00
|
||||
) &&
|
||||
segment.data[0] == 0x78
|
||||
) {
|
||||
target.paging_model = Target::PagingModel::ActivisionStack;
|
||||
@ -88,7 +98,11 @@ static void DeterminePagingFor8kCartridge(Target &target, const Storage::Cartrid
|
||||
else if(tigervision_access_count > atari_access_count) target.paging_model = Target::PagingModel::Tigervision;
|
||||
}
|
||||
|
||||
static void DeterminePagingFor16kCartridge(Target &target, const Storage::Cartridge::Cartridge::Segment &, const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
static void DeterminePagingFor16kCartridge(
|
||||
Target &target,
|
||||
const Storage::Cartridge::Cartridge::Segment &,
|
||||
const Analyser::Static::MOS6502::Disassembly &disassembly
|
||||
) {
|
||||
// Make an assumption that this is the Atari paging model.
|
||||
target.paging_model = Target::PagingModel::Atari16k;
|
||||
|
||||
@ -108,7 +122,11 @@ static void DeterminePagingFor16kCartridge(Target &target, const Storage::Cartri
|
||||
if(mnetwork_access_count > atari_access_count) target.paging_model = Target::PagingModel::MNetwork;
|
||||
}
|
||||
|
||||
static void DeterminePagingFor64kCartridge(Target &target, const Storage::Cartridge::Cartridge::Segment &, const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
static void DeterminePagingFor64kCartridge(
|
||||
Target &target,
|
||||
const Storage::Cartridge::Cartridge::Segment &,
|
||||
const Analyser::Static::MOS6502::Disassembly &disassembly
|
||||
) {
|
||||
// Make an assumption that this is a Tigervision if there is a write to 3F.
|
||||
target.paging_model =
|
||||
(disassembly.external_stores.find(0x3f) != disassembly.external_stores.end()) ?
|
||||
@ -121,8 +139,12 @@ static void DeterminePagingForCartridge(Target &target, const Storage::Cartridge
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t entry_address = uint16_t(segment.data[segment.data.size() - 4] | (segment.data[segment.data.size() - 3] << 8));
|
||||
const uint16_t break_address = uint16_t(segment.data[segment.data.size() - 2] | (segment.data[segment.data.size() - 1] << 8));
|
||||
const auto word = [](const uint8_t low, const uint8_t high) {
|
||||
return uint16_t(low | (high << 8));
|
||||
};
|
||||
|
||||
const auto entry_address = word(segment.data[segment.data.size() - 4], segment.data[segment.data.size() - 3]);
|
||||
const auto break_address = word(segment.data[segment.data.size() - 2], segment.data[segment.data.size() - 1]);
|
||||
|
||||
std::function<std::size_t(uint16_t address)> address_mapper = [](uint16_t address) {
|
||||
if(!(address & 0x1000)) return size_t(-1);
|
||||
@ -130,27 +152,16 @@ static void DeterminePagingForCartridge(Target &target, const Storage::Cartridge
|
||||
};
|
||||
|
||||
const std::vector<uint8_t> final_4k(segment.data.end() - 4096, segment.data.end());
|
||||
Analyser::Static::MOS6502::Disassembly disassembly = Analyser::Static::MOS6502::Disassemble(final_4k, address_mapper, {entry_address, break_address});
|
||||
const auto disassembly =
|
||||
Analyser::Static::MOS6502::Disassemble(final_4k, address_mapper, {entry_address, break_address});
|
||||
|
||||
switch(segment.data.size()) {
|
||||
case 8192:
|
||||
DeterminePagingFor8kCartridge(target, segment, disassembly);
|
||||
break;
|
||||
case 10495:
|
||||
target.paging_model = Target::PagingModel::Pitfall2;
|
||||
break;
|
||||
case 12288:
|
||||
target.paging_model = Target::PagingModel::CBSRamPlus;
|
||||
break;
|
||||
case 16384:
|
||||
DeterminePagingFor16kCartridge(target, segment, disassembly);
|
||||
break;
|
||||
case 32768:
|
||||
target.paging_model = Target::PagingModel::Atari32k;
|
||||
break;
|
||||
case 65536:
|
||||
DeterminePagingFor64kCartridge(target, segment, disassembly);
|
||||
break;
|
||||
case 8192: DeterminePagingFor8kCartridge(target, segment, disassembly); break;
|
||||
case 10495: target.paging_model = Target::PagingModel::Pitfall2; break;
|
||||
case 12288: target.paging_model = Target::PagingModel::CBSRamPlus; break;
|
||||
case 16384: DeterminePagingFor16kCartridge(target, segment, disassembly); break;
|
||||
case 32768: target.paging_model = Target::PagingModel::Atari32k; break;
|
||||
case 65536: DeterminePagingFor64kCartridge(target, segment, disassembly); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -177,7 +188,12 @@ static void DeterminePagingForCartridge(Target &target, const Storage::Cartridge
|
||||
}
|
||||
}
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Atari2600::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::Atari2600::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
bool
|
||||
) {
|
||||
// TODO: sanity checking; is this image really for an Atari 2600?
|
||||
auto target = std::make_unique<Target>();
|
||||
target->confidence = 0.5;
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::Atari2600 {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
|
||||
namespace Analyser::Static::Atari2600 {
|
||||
|
||||
|
@ -9,7 +9,12 @@
|
||||
#include "StaticAnalyser.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::AtariST::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::AtariST::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
bool
|
||||
) {
|
||||
// This analyser can comprehend disks and mass-storage devices only.
|
||||
if(media.disks.empty()) return {};
|
||||
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::AtariST {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
|
||||
namespace Analyser::Static::AtariST {
|
||||
|
||||
@ -20,11 +20,13 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
FourMegabytes);
|
||||
MemorySize memory_size = MemorySize::OneMegabyte;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::AtariST) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(memory_size);
|
||||
AnnounceEnum(MemorySize);
|
||||
}
|
||||
Target() : Analyser::Static::Target(Machine::AtariST) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<Target>;
|
||||
void declare_fields() {
|
||||
DeclareField(memory_size);
|
||||
AnnounceEnum(MemorySize);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "StaticAnalyser.hpp"
|
||||
|
||||
static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
ColecoCartridgesFrom(const std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
|
||||
ColecoCartridgesFrom(const std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
|
||||
std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> coleco_cartridges;
|
||||
|
||||
for(const auto &cartridge : cartridges) {
|
||||
@ -52,11 +52,20 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
return coleco_cartridges;
|
||||
}
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Coleco::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::Coleco::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
const bool is_confident
|
||||
) {
|
||||
TargetList targets;
|
||||
auto target = std::make_unique<Target>(Machine::ColecoVision);
|
||||
target->confidence = 1.0f - 1.0f / 32768.0f;
|
||||
target->media.cartridges = ColecoCartridgesFrom(media.cartridges);
|
||||
if(is_confident) {
|
||||
target->media = media;
|
||||
} else {
|
||||
target->media.cartridges = ColecoCartridgesFrom(media.cartridges);
|
||||
}
|
||||
if(!target->media.empty())
|
||||
targets.push_back(std::move(target));
|
||||
return targets;
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::Coleco {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -7,166 +7,168 @@
|
||||
//
|
||||
|
||||
#include "Disk.hpp"
|
||||
#include "../../../Storage/Disk/Controller/DiskController.hpp"
|
||||
#include "../../../Storage/Disk/Encodings/CommodoreGCR.hpp"
|
||||
#include "../../../Storage/Data/Commodore.hpp"
|
||||
#include "Storage/Disk/Controller/DiskController.hpp"
|
||||
#include "Storage/Disk/Encodings/CommodoreGCR.hpp"
|
||||
#include "Storage/Data/Commodore.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
using namespace Analyser::Static::Commodore;
|
||||
|
||||
class CommodoreGCRParser: public Storage::Disk::Controller {
|
||||
public:
|
||||
CommodoreGCRParser() : Storage::Disk::Controller(4000000), shift_register_(0), track_(1) {
|
||||
emplace_drive(4000000, 300, 2);
|
||||
set_drive(1);
|
||||
get_drive().set_motor_on(true);
|
||||
}
|
||||
public:
|
||||
CommodoreGCRParser() : Storage::Disk::Controller(4000000), shift_register_(0), track_(1) {
|
||||
emplace_drive(4000000, 300, 2);
|
||||
set_drive(1);
|
||||
get_drive().set_motor_on(true);
|
||||
}
|
||||
|
||||
struct Sector {
|
||||
uint8_t sector, track;
|
||||
std::array<uint8_t, 256> data;
|
||||
bool header_checksum_matched;
|
||||
bool data_checksum_matched;
|
||||
};
|
||||
struct Sector {
|
||||
uint8_t sector, track;
|
||||
std::array<uint8_t, 256> data;
|
||||
bool header_checksum_matched;
|
||||
bool data_checksum_matched;
|
||||
};
|
||||
|
||||
/*!
|
||||
Attempts to read the sector located at @c track and @c sector.
|
||||
/*!
|
||||
Attempts to read the sector located at @c track and @c sector.
|
||||
|
||||
@returns a sector if one was found; @c nullptr otherwise.
|
||||
*/
|
||||
std::shared_ptr<Sector> sector(uint8_t track, uint8_t sector) {
|
||||
int difference = int(track) - int(track_);
|
||||
track_ = track;
|
||||
@returns a sector if one was found; @c nullptr otherwise.
|
||||
*/
|
||||
const Sector *sector(const uint8_t track, const uint8_t sector) {
|
||||
int difference = int(track) - int(track_);
|
||||
track_ = track;
|
||||
|
||||
if(difference) {
|
||||
int direction = difference < 0 ? -1 : 1;
|
||||
difference *= direction;
|
||||
if(difference) {
|
||||
const int direction = difference < 0 ? -1 : 1;
|
||||
difference *= direction;
|
||||
|
||||
for(int c = 0; c < difference; c++) {
|
||||
get_drive().step(Storage::Disk::HeadPosition(direction));
|
||||
}
|
||||
|
||||
unsigned int zone = 3;
|
||||
if(track >= 18) zone = 2;
|
||||
else if(track >= 25) zone = 1;
|
||||
else if(track >= 31) zone = 0;
|
||||
set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(zone));
|
||||
for(int c = 0; c < difference; c++) {
|
||||
get_drive().step(Storage::Disk::HeadPosition(direction));
|
||||
}
|
||||
|
||||
return get_sector(sector);
|
||||
unsigned int zone = 3;
|
||||
if(track >= 18) zone = 2;
|
||||
else if(track >= 25) zone = 1;
|
||||
else if(track >= 31) zone = 0;
|
||||
set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(zone));
|
||||
}
|
||||
|
||||
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||
get_drive().set_disk(disk);
|
||||
return get_sector(sector);
|
||||
}
|
||||
|
||||
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||
get_drive().set_disk(disk);
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int shift_register_;
|
||||
int index_count_;
|
||||
int bit_count_;
|
||||
uint8_t track_;
|
||||
std::unordered_map<uint16_t, std::unique_ptr<Sector>> sector_cache_;
|
||||
|
||||
void process_input_bit(const int value) override {
|
||||
shift_register_ = ((shift_register_ << 1) | unsigned(value)) & 0x3ff;
|
||||
bit_count_++;
|
||||
}
|
||||
|
||||
unsigned int proceed_to_next_block(const int max_index_count) {
|
||||
// find GCR lead-in
|
||||
proceed_to_shift_value(0x3ff);
|
||||
if(shift_register_ != 0x3ff) return 0xff;
|
||||
|
||||
// find end of lead-in
|
||||
while(shift_register_ == 0x3ff && index_count_ < max_index_count) {
|
||||
run_for(Cycles(1));
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int shift_register_;
|
||||
int index_count_;
|
||||
int bit_count_;
|
||||
uint8_t track_;
|
||||
std::shared_ptr<Sector> sector_cache_[65536];
|
||||
|
||||
void process_input_bit(int value) {
|
||||
shift_register_ = ((shift_register_ << 1) | unsigned(value)) & 0x3ff;
|
||||
bit_count_++;
|
||||
// continue for a further nine bits
|
||||
bit_count_ = 0;
|
||||
while(bit_count_ < 9 && index_count_ < max_index_count) {
|
||||
run_for(Cycles(1));
|
||||
}
|
||||
|
||||
unsigned int proceed_to_next_block(int max_index_count) {
|
||||
// find GCR lead-in
|
||||
proceed_to_shift_value(0x3ff);
|
||||
if(shift_register_ != 0x3ff) return 0xff;
|
||||
return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_);
|
||||
}
|
||||
|
||||
// find end of lead-in
|
||||
while(shift_register_ == 0x3ff && index_count_ < max_index_count) {
|
||||
run_for(Cycles(1));
|
||||
}
|
||||
unsigned int get_next_byte() {
|
||||
bit_count_ = 0;
|
||||
while(bit_count_ < 10) run_for(Cycles(1));
|
||||
return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_);
|
||||
}
|
||||
|
||||
// continue for a further nine bits
|
||||
bit_count_ = 0;
|
||||
while(bit_count_ < 9 && index_count_ < max_index_count) {
|
||||
run_for(Cycles(1));
|
||||
}
|
||||
|
||||
return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_);
|
||||
void proceed_to_shift_value(const unsigned int shift_value) {
|
||||
const int max_index_count = index_count_ + 2;
|
||||
while(shift_register_ != shift_value && index_count_ < max_index_count) {
|
||||
run_for(Cycles(1));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int get_next_byte() {
|
||||
bit_count_ = 0;
|
||||
while(bit_count_ < 10) run_for(Cycles(1));
|
||||
return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_);
|
||||
void process_index_hole() override {
|
||||
index_count_++;
|
||||
}
|
||||
|
||||
const Sector *get_sector(const uint8_t sector) {
|
||||
const uint16_t sector_address = uint16_t((track_ << 8) | sector);
|
||||
auto existing = sector_cache_.find(sector_address);
|
||||
if(existing != sector_cache_.end()) return existing->second.get();
|
||||
|
||||
const auto first_sector = get_next_sector();
|
||||
if(!first_sector) return first_sector;
|
||||
if(first_sector->sector == sector) return first_sector;
|
||||
|
||||
while(true) {
|
||||
const auto next_sector = get_next_sector();
|
||||
if(next_sector->sector == first_sector->sector) return nullptr;
|
||||
if(next_sector->sector == sector) return next_sector;
|
||||
}
|
||||
}
|
||||
|
||||
void proceed_to_shift_value(unsigned int shift_value) {
|
||||
const int max_index_count = index_count_ + 2;
|
||||
while(shift_register_ != shift_value && index_count_ < max_index_count) {
|
||||
run_for(Cycles(1));
|
||||
}
|
||||
}
|
||||
|
||||
void process_index_hole() {
|
||||
index_count_++;
|
||||
}
|
||||
|
||||
std::shared_ptr<Sector> get_sector(uint8_t sector) {
|
||||
const uint16_t sector_address = uint16_t((track_ << 8) | sector);
|
||||
if(sector_cache_[sector_address]) return sector_cache_[sector_address];
|
||||
|
||||
const std::shared_ptr<Sector> first_sector = get_next_sector();
|
||||
if(!first_sector) return first_sector;
|
||||
if(first_sector->sector == sector) return first_sector;
|
||||
const Sector *get_next_sector() {
|
||||
auto sector = std::make_unique<Sector>();
|
||||
const int max_index_count = index_count_ + 2;
|
||||
|
||||
while(index_count_ < max_index_count) {
|
||||
// look for a sector header
|
||||
while(1) {
|
||||
const std::shared_ptr<Sector> next_sector = get_next_sector();
|
||||
if(next_sector->sector == first_sector->sector) return nullptr;
|
||||
if(next_sector->sector == sector) return next_sector;
|
||||
if(proceed_to_next_block(max_index_count) == 0x08) break;
|
||||
if(index_count_ >= max_index_count) return nullptr;
|
||||
}
|
||||
|
||||
// get sector details, skip if this looks malformed
|
||||
uint8_t checksum = uint8_t(get_next_byte());
|
||||
sector->sector = uint8_t(get_next_byte());
|
||||
sector->track = uint8_t(get_next_byte());
|
||||
uint8_t disk_id[2];
|
||||
disk_id[0] = uint8_t(get_next_byte());
|
||||
disk_id[1] = uint8_t(get_next_byte());
|
||||
if(checksum != (sector->sector ^ sector->track ^ disk_id[0] ^ disk_id[1])) continue;
|
||||
|
||||
// look for the following data
|
||||
while(1) {
|
||||
if(proceed_to_next_block(max_index_count) == 0x07) break;
|
||||
if(index_count_ >= max_index_count) return nullptr;
|
||||
}
|
||||
|
||||
checksum = 0;
|
||||
for(std::size_t c = 0; c < 256; c++) {
|
||||
sector->data[c] = uint8_t(get_next_byte());
|
||||
checksum ^= sector->data[c];
|
||||
}
|
||||
|
||||
if(checksum == get_next_byte()) {
|
||||
uint16_t sector_address = uint16_t((sector->track << 8) | sector->sector);
|
||||
auto pair = sector_cache_.emplace(sector_address, std::move(sector));
|
||||
return pair.first->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Sector> get_next_sector() {
|
||||
auto sector = std::make_shared<Sector>();
|
||||
const int max_index_count = index_count_ + 2;
|
||||
|
||||
while(index_count_ < max_index_count) {
|
||||
// look for a sector header
|
||||
while(1) {
|
||||
if(proceed_to_next_block(max_index_count) == 0x08) break;
|
||||
if(index_count_ >= max_index_count) return nullptr;
|
||||
}
|
||||
|
||||
// get sector details, skip if this looks malformed
|
||||
uint8_t checksum = uint8_t(get_next_byte());
|
||||
sector->sector = uint8_t(get_next_byte());
|
||||
sector->track = uint8_t(get_next_byte());
|
||||
uint8_t disk_id[2];
|
||||
disk_id[0] = uint8_t(get_next_byte());
|
||||
disk_id[1] = uint8_t(get_next_byte());
|
||||
if(checksum != (sector->sector ^ sector->track ^ disk_id[0] ^ disk_id[1])) continue;
|
||||
|
||||
// look for the following data
|
||||
while(1) {
|
||||
if(proceed_to_next_block(max_index_count) == 0x07) break;
|
||||
if(index_count_ >= max_index_count) return nullptr;
|
||||
}
|
||||
|
||||
checksum = 0;
|
||||
for(std::size_t c = 0; c < 256; c++) {
|
||||
sector->data[c] = uint8_t(get_next_byte());
|
||||
checksum ^= sector->data[c];
|
||||
}
|
||||
|
||||
if(checksum == get_next_byte()) {
|
||||
uint16_t sector_address = uint16_t((sector->track << 8) | sector->sector);
|
||||
sector_cache_[sector_address] = sector;
|
||||
return sector;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||
@ -174,20 +176,26 @@ std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<St
|
||||
CommodoreGCRParser parser;
|
||||
parser.set_disk(disk);
|
||||
|
||||
// find any sector whatsoever to establish the current track
|
||||
std::shared_ptr<CommodoreGCRParser::Sector> sector;
|
||||
|
||||
// assemble directory
|
||||
// Assemble directory.
|
||||
std::vector<uint8_t> directory;
|
||||
uint8_t next_track = 18;
|
||||
uint8_t next_sector = 1;
|
||||
while(1) {
|
||||
sector = parser.sector(next_track, next_sector);
|
||||
directory.reserve(20 * 1024); // Probably more than plenty.
|
||||
std::set<std::pair<uint8_t, uint8_t>> visited;
|
||||
while(true) {
|
||||
// Don't be fooled by disks that are encoded with a looping directory.
|
||||
const auto key = std::make_pair(next_track, next_sector);
|
||||
if(visited.find(key) != visited.end()) break;
|
||||
visited.insert(key);
|
||||
|
||||
// Append sector to directory and follow next link.
|
||||
const auto sector = parser.sector(next_track, next_sector);
|
||||
if(!sector) break;
|
||||
directory.insert(directory.end(), sector->data.begin(), sector->data.end());
|
||||
next_track = sector->data[0];
|
||||
next_sector = sector->data[1];
|
||||
|
||||
// Check for end-of-directory.
|
||||
if(!next_track) break;
|
||||
}
|
||||
|
||||
@ -216,24 +224,36 @@ std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<St
|
||||
}
|
||||
new_file.name = Storage::Data::Commodore::petscii_from_bytes(&new_file.raw_name[0], 16, false);
|
||||
|
||||
std::size_t number_of_sectors = size_t(directory[header_pointer + 0x1e]) + (size_t(directory[header_pointer + 0x1f]) << 8);
|
||||
new_file.data.reserve((number_of_sectors - 1) * 254 + 252);
|
||||
const std::size_t number_of_sectors =
|
||||
size_t(directory[header_pointer + 0x1e]) +
|
||||
(size_t(directory[header_pointer + 0x1f]) << 8);
|
||||
if(number_of_sectors) {
|
||||
new_file.data.reserve((number_of_sectors - 1) * 254 + 252);
|
||||
|
||||
bool is_first_sector = true;
|
||||
while(next_track) {
|
||||
sector = parser.sector(next_track, next_sector);
|
||||
if(!sector) break;
|
||||
bool is_first_sector = true;
|
||||
while(next_track) {
|
||||
const auto sector = parser.sector(next_track, next_sector);
|
||||
if(!sector) break;
|
||||
|
||||
next_track = sector->data[0];
|
||||
next_sector = sector->data[1];
|
||||
next_track = sector->data[0];
|
||||
next_sector = sector->data[1];
|
||||
|
||||
if(is_first_sector) new_file.starting_address = uint16_t(sector->data[2]) | uint16_t(sector->data[3] << 8);
|
||||
if(next_track)
|
||||
new_file.data.insert(new_file.data.end(), sector->data.begin() + (is_first_sector ? 4 : 2), sector->data.end());
|
||||
else
|
||||
new_file.data.insert(new_file.data.end(), sector->data.begin() + 2, sector->data.begin() + next_sector);
|
||||
if(is_first_sector) new_file.starting_address = uint16_t(sector->data[2]) | uint16_t(sector->data[3] << 8);
|
||||
if(next_track)
|
||||
new_file.data.insert(
|
||||
new_file.data.end(),
|
||||
sector->data.begin() + (is_first_sector ? 4 : 2),
|
||||
sector->data.end()
|
||||
);
|
||||
else
|
||||
new_file.data.insert(
|
||||
new_file.data.end(),
|
||||
sector->data.begin() + 2,
|
||||
sector->data.begin() + next_sector
|
||||
);
|
||||
|
||||
is_first_sector = false;
|
||||
is_first_sector = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!next_track) files.push_back(new_file);
|
||||
|
@ -8,13 +8,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Storage/Disk/Disk.hpp"
|
||||
#include "Storage/Disk/Disk.hpp"
|
||||
#include "File.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
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> &);
|
||||
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
//
|
||||
// File.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/09/2016.
|
||||
// Copyright 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "File.hpp"
|
||||
|
||||
bool Analyser::Static::Commodore::File::is_basic() {
|
||||
// BASIC files are always relocatable (?)
|
||||
if(type != File::RelocatableProgram) return false;
|
||||
|
||||
uint16_t line_address = starting_address;
|
||||
int line_number = -1;
|
||||
|
||||
// decide whether this is a BASIC file based on the proposition that:
|
||||
// (1) they're always relocatable; and
|
||||
// (2) they have a per-line structure of:
|
||||
// [4 bytes: address of start of next line]
|
||||
// [4 bytes: this line number]
|
||||
// ... null-terminated code ...
|
||||
// (with a next line address of 0000 indicating end of program)
|
||||
while(1) {
|
||||
if(size_t(line_address - starting_address) >= data.size() + 2) break;
|
||||
|
||||
uint16_t next_line_address = data[line_address - starting_address];
|
||||
next_line_address |= data[line_address - starting_address + 1] << 8;
|
||||
|
||||
if(!next_line_address) {
|
||||
return true;
|
||||
}
|
||||
if(next_line_address < line_address + 5) break;
|
||||
|
||||
if(size_t(line_address - starting_address) >= data.size() + 5) break;
|
||||
uint16_t next_line_number = data[line_address - starting_address + 2];
|
||||
next_line_number |= data[line_address - starting_address + 3] << 8;
|
||||
|
||||
if(next_line_number <= line_number) break;
|
||||
|
||||
line_number = uint16_t(next_line_number);
|
||||
line_address = next_line_address;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
@ -29,8 +29,6 @@ struct File {
|
||||
Relative
|
||||
} type;
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
bool is_basic();
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -12,26 +12,33 @@
|
||||
#include "File.hpp"
|
||||
#include "Tape.hpp"
|
||||
#include "Target.hpp"
|
||||
#include "../../../Storage/Cartridge/Encodings/CommodoreROM.hpp"
|
||||
#include "../../../Outputs/Log.hpp"
|
||||
#include "Storage/Cartridge/Encodings/CommodoreROM.hpp"
|
||||
#include "Outputs/Log.hpp"
|
||||
|
||||
#include "Analyser/Static/Disassembler/6502.hpp"
|
||||
#include "Analyser/Static/Disassembler/AddressMapper.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace Analyser::Static::Commodore;
|
||||
|
||||
static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
Vic20CartridgesFrom(const std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
|
||||
namespace {
|
||||
|
||||
std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
Vic20CartridgesFrom(const std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
|
||||
std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> vic20_cartridges;
|
||||
|
||||
for(const auto &cartridge : cartridges) {
|
||||
const auto &segments = cartridge->get_segments();
|
||||
|
||||
// only one mapped item is allowed
|
||||
// Only one mapped item is allowed ...
|
||||
if(segments.size() != 1) continue;
|
||||
|
||||
// which must be 16 kb in size
|
||||
// ... which must be 16 kb in size.
|
||||
Storage::Cartridge::Cartridge::Segment segment = segments.front();
|
||||
if(segment.start_address != 0xa000) continue;
|
||||
if(!Storage::Cartridge::Encodings::CommodoreROM::isROM(segment.data)) continue;
|
||||
@ -39,126 +46,312 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
vic20_cartridges.push_back(cartridge);
|
||||
}
|
||||
|
||||
// TODO: other machines?
|
||||
|
||||
return vic20_cartridges;
|
||||
}
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType) {
|
||||
TargetList destination;
|
||||
struct BASICAnalysis {
|
||||
enum class Version {
|
||||
NotBASIC,
|
||||
BASIC2,
|
||||
BASIC4,
|
||||
BASIC3_5,
|
||||
} minimum_version = Version::NotBASIC;
|
||||
std::vector<uint16_t> machine_code_addresses;
|
||||
};
|
||||
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Machine::Vic20; // TODO: machine estimation
|
||||
target->confidence = 0.5; // TODO: a proper estimation
|
||||
std::optional<BASICAnalysis> analyse(const File &file) {
|
||||
BASICAnalysis analysis;
|
||||
|
||||
switch(file.type) {
|
||||
// For 'program' types, proceed with analysis below.
|
||||
case File::RelocatableProgram:
|
||||
case File::NonRelocatableProgram:
|
||||
break;
|
||||
|
||||
// For sequential and relative data stop right now.
|
||||
case File::DataSequence:
|
||||
case File::Relative:
|
||||
return std::nullopt;
|
||||
|
||||
// For user data, try decoding from the starting point.
|
||||
case File::User:
|
||||
analysis.machine_code_addresses.push_back(file.starting_address);
|
||||
return analysis;
|
||||
}
|
||||
|
||||
// Don't form an opinion if file is empty.
|
||||
if(file.data.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
uint16_t line_address = file.starting_address;
|
||||
// int previous_line_number = -1;
|
||||
|
||||
const auto byte = [&](uint16_t address) {
|
||||
return file.data[address - file.starting_address];
|
||||
};
|
||||
const auto word = [&](uint16_t address) {
|
||||
return uint16_t(byte(address) | byte(address + 1) << 8);
|
||||
};
|
||||
|
||||
// BASIC programs have a per-line structure of:
|
||||
// [2 bytes: address of start of next line]
|
||||
// [2 bytes: this line number]
|
||||
// ... null-terminated code ...
|
||||
// (with a next line address of 0000 indicating end of program)
|
||||
//
|
||||
// If a SYS is encountered that jumps into the BASIC program then treat that as
|
||||
// a machine code entry point.
|
||||
|
||||
std::unordered_set<uint16_t> visited_lines;
|
||||
while(true) {
|
||||
// Analysis has failed if there isn't at least one complete BASIC line from here.
|
||||
// Fall back on guessing the start address as a machine code entrypoint.
|
||||
if(size_t(line_address - file.starting_address) + 5 >= file.data.size() || line_address < file.starting_address) {
|
||||
analysis.machine_code_addresses.push_back(file.starting_address);
|
||||
break;
|
||||
}
|
||||
|
||||
const auto next_line_address = word(line_address);
|
||||
// const auto line_number = word(line_address + 2);
|
||||
|
||||
uint16_t code = line_address + 4;
|
||||
const auto next = [&]() -> uint8_t {
|
||||
if(code >= file.starting_address + file.data.size()) {
|
||||
return 0;
|
||||
}
|
||||
return byte(code++);
|
||||
};
|
||||
|
||||
// TODO: sanity check on apparent line contents.
|
||||
// TODO: observe token set (and possibly parameters?) to guess BASIC version.
|
||||
while(true) {
|
||||
const auto token = next();
|
||||
if(!token || token == 0x8f) break;
|
||||
|
||||
switch(token) {
|
||||
case 0x9e: { // SYS; parse following ASCII argument.
|
||||
uint16_t address = 0;
|
||||
while(true) {
|
||||
const auto c = next();
|
||||
if(c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
address = (address * 10) + (c - '0');
|
||||
};
|
||||
analysis.machine_code_addresses.push_back(address);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// Exit if a formal end of the program has been declared or if, as some copy protections do,
|
||||
// the linked list of line contents has been made circular.
|
||||
visited_lines.insert(line_address);
|
||||
if(!next_line_address || visited_lines.find(next_line_address) != visited_lines.end()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// previous_line_number = line_number;
|
||||
line_address = next_line_address;
|
||||
}
|
||||
|
||||
return analysis;
|
||||
}
|
||||
|
||||
template <typename TargetT>
|
||||
void set_loading_command(TargetT &target) {
|
||||
if(target.media.disks.empty()) {
|
||||
target.loading_command = "LOAD\"\",1,1\nRUN\n";
|
||||
} else {
|
||||
target.loading_command = "LOAD\"*\",8,1\nRUN\n";
|
||||
}
|
||||
}
|
||||
|
||||
bool obviously_uses_ted(const File &file) {
|
||||
const auto analysis = analyse(file);
|
||||
if(!analysis) return false;
|
||||
|
||||
// Disassemble.
|
||||
const auto disassembly = Analyser::Static::MOS6502::Disassemble(
|
||||
file.data,
|
||||
Analyser::Static::Disassembler::OffsetMapper(file.starting_address),
|
||||
analysis->machine_code_addresses
|
||||
);
|
||||
|
||||
// Check for interrupt status and paging touches.
|
||||
for(const auto address: {0xff3e, 0xff3f, 0xff09}) {
|
||||
for(const auto &collection: {
|
||||
disassembly.external_loads,
|
||||
disassembly.external_stores,
|
||||
disassembly.external_modifies
|
||||
}) {
|
||||
if(collection.find(uint16_t(address)) != collection.end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct FileAnalysis {
|
||||
int device = 0;
|
||||
std::vector<File> files;
|
||||
bool is_disk = false;
|
||||
Analyser::Static::Media media;
|
||||
};
|
||||
|
||||
// strip out inappropriate cartridges
|
||||
target->media.cartridges = Vic20CartridgesFrom(media.cartridges);
|
||||
template <TargetPlatform::Type platform>
|
||||
FileAnalysis analyse_files(const Analyser::Static::Media &media) {
|
||||
FileAnalysis analysis;
|
||||
|
||||
// check disks
|
||||
// Find all valid Commodore files on disks.
|
||||
for(auto &disk : media.disks) {
|
||||
std::vector<File> disk_files = GetFiles(disk);
|
||||
if(!disk_files.empty()) {
|
||||
is_disk = true;
|
||||
files.insert(files.end(), disk_files.begin(), disk_files.end());
|
||||
target->media.disks.push_back(disk);
|
||||
if(!device) device = 8;
|
||||
analysis.is_disk = true;
|
||||
analysis.files.insert(
|
||||
analysis.files.end(),
|
||||
std::make_move_iterator(disk_files.begin()),
|
||||
std::make_move_iterator(disk_files.end())
|
||||
);
|
||||
analysis.media.disks.push_back(disk);
|
||||
if(!analysis.device) analysis.device = 8;
|
||||
}
|
||||
}
|
||||
|
||||
// check tapes
|
||||
// Find all valid Commodore files on tapes.
|
||||
for(auto &tape : media.tapes) {
|
||||
std::vector<File> tape_files = GetFiles(tape);
|
||||
tape->reset();
|
||||
auto serialiser = tape->serialiser();
|
||||
std::vector<File> tape_files = GetFiles(*serialiser, platform);
|
||||
if(!tape_files.empty()) {
|
||||
files.insert(files.end(), tape_files.begin(), tape_files.end());
|
||||
target->media.tapes.push_back(tape);
|
||||
if(!device) device = 1;
|
||||
analysis.files.insert(
|
||||
analysis.files.end(),
|
||||
std::make_move_iterator(tape_files.begin()),
|
||||
std::make_move_iterator(tape_files.end())
|
||||
);
|
||||
analysis.media.tapes.push_back(tape);
|
||||
if(!analysis.device) analysis.device = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(!files.empty()) {
|
||||
auto memory_model = Target::MemoryModel::Unexpanded;
|
||||
std::ostringstream string_stream;
|
||||
string_stream << "LOAD\"" << (is_disk ? "*" : "") << "\"," << device << ",";
|
||||
if(files.front().is_basic()) {
|
||||
string_stream << "0";
|
||||
} else {
|
||||
string_stream << "1";
|
||||
return analysis;
|
||||
}
|
||||
|
||||
std::string loading_command(const FileAnalysis &file_analysis) {
|
||||
std::ostringstream string_stream;
|
||||
string_stream << "LOAD\"" << (file_analysis.is_disk ? "*" : "") << "\"," << file_analysis.device;
|
||||
|
||||
const auto analysis = analyse(file_analysis.files[0]);
|
||||
if(analysis && !analysis->machine_code_addresses.empty()) {
|
||||
string_stream << ",1";
|
||||
}
|
||||
string_stream << "\nRUN\n";
|
||||
return string_stream.str();
|
||||
}
|
||||
|
||||
std::pair<TargetPlatform::IntType, std::optional<Vic20Target::MemoryModel>>
|
||||
analyse_starting_address(uint16_t starting_address) {
|
||||
switch(starting_address) {
|
||||
case 0x1c01:
|
||||
// TODO: assume C128.
|
||||
default:
|
||||
Log::Logger<Log::Source::CommodoreStaticAnalyser>().error().append(
|
||||
"Unrecognised loading address for Commodore program: %04x", starting_address);
|
||||
[[fallthrough]];
|
||||
case 0x1001:
|
||||
return std::make_pair(TargetPlatform::Vic20 | TargetPlatform::Plus4, Vic20Target::MemoryModel::Unexpanded);
|
||||
|
||||
case 0x1201: return std::make_pair(TargetPlatform::Vic20, Vic20Target::MemoryModel::ThirtyTwoKB);
|
||||
case 0x0401: return std::make_pair(TargetPlatform::Vic20, Vic20Target::MemoryModel::EightKB);
|
||||
case 0x0801: return std::make_pair(TargetPlatform::C64, std::nullopt);
|
||||
}
|
||||
}
|
||||
|
||||
template <TargetPlatform::IntType platform>
|
||||
std::unique_ptr<Analyser::Static::Target> get_target(
|
||||
const Analyser::Static::Media &media,
|
||||
const std::string &file_name,
|
||||
bool is_confident
|
||||
);
|
||||
|
||||
template<>
|
||||
std::unique_ptr<Analyser::Static::Target> get_target<TargetPlatform::Plus4>(
|
||||
const Analyser::Static::Media &media,
|
||||
const std::string &,
|
||||
bool is_confident
|
||||
) {
|
||||
auto target = std::make_unique<Plus4Target>();
|
||||
if(is_confident) {
|
||||
target->media = media;
|
||||
set_loading_command(*target);
|
||||
} else {
|
||||
const auto files = analyse_files<TargetPlatform::Plus4>(media);
|
||||
if(!files.files.empty()) {
|
||||
target->loading_command = loading_command(files);
|
||||
}
|
||||
string_stream << "\nRUN\n";
|
||||
target->loading_command = string_stream.str();
|
||||
|
||||
// make a first guess based on loading address
|
||||
switch(files.front().starting_address) {
|
||||
default:
|
||||
Log::Logger<Log::Source::CommodoreStaticAnalyser>().error().append("Unrecognised loading address for Commodore program: %04x", files.front().starting_address);
|
||||
[[fallthrough]];
|
||||
case 0x1001:
|
||||
memory_model = Target::MemoryModel::Unexpanded;
|
||||
break;
|
||||
case 0x1201:
|
||||
memory_model = Target::MemoryModel::ThirtyTwoKB;
|
||||
break;
|
||||
case 0x0401:
|
||||
memory_model = Target::MemoryModel::EightKB;
|
||||
break;
|
||||
}
|
||||
|
||||
target->set_memory_model(memory_model);
|
||||
|
||||
// General approach: increase memory size conservatively such that the largest file found will fit.
|
||||
// for(File &file : files) {
|
||||
// std::size_t file_size = file.data.size();
|
||||
// bool is_basic = file.is_basic();
|
||||
|
||||
/*if(is_basic)
|
||||
{
|
||||
// BASIC files may be relocated, so the only limit is size.
|
||||
//
|
||||
// An unexpanded machine has 3583 bytes free for BASIC;
|
||||
// a 3kb expanded machine has 6655 bytes free.
|
||||
if(file_size > 6655)
|
||||
target->vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB;
|
||||
else if(target->vic20.memory_model == Vic20MemoryModel::Unexpanded && file_size > 3583)
|
||||
target->vic20.memory_model = Vic20MemoryModel::EightKB;
|
||||
}
|
||||
else
|
||||
{*/
|
||||
// if(!file.type == File::NonRelocatableProgram)
|
||||
// {
|
||||
// Non-BASIC files may be relocatable but, if so, by what logic?
|
||||
// Given that this is unknown, take starting address as literal
|
||||
// and check against memory windows.
|
||||
//
|
||||
// (ignoring colour memory...)
|
||||
// An unexpanded Vic has memory between 0x0000 and 0x0400; and between 0x1000 and 0x2000.
|
||||
// A 3kb expanded Vic fills in the gap and has memory between 0x0000 and 0x2000.
|
||||
// A 32kb expanded Vic has memory in the entire low 32kb.
|
||||
// uint16_t starting_address = file.starting_address;
|
||||
|
||||
// If anything above the 8kb mark is touched, mark as a 32kb machine; otherwise if the
|
||||
// region 0x0400 to 0x1000 is touched and this is an unexpanded machine, mark as 3kb.
|
||||
// if(starting_address + file_size > 0x2000)
|
||||
// target->memory_model = Target::MemoryModel::ThirtyTwoKB;
|
||||
// else if(target->memory_model == Target::MemoryModel::Unexpanded && !(starting_address >= 0x1000 || starting_address+file_size < 0x0400))
|
||||
// target->memory_model = Target::MemoryModel::ThirtyTwoKB;
|
||||
// }
|
||||
// }
|
||||
target->media.disks = media.disks;
|
||||
target->media.tapes = media.tapes;
|
||||
}
|
||||
|
||||
// Attach a 1541 if there are any disks here.
|
||||
target->has_c1541 = !target->media.disks.empty();
|
||||
return target;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::unique_ptr<Analyser::Static::Target> get_target<TargetPlatform::Vic20>(
|
||||
const Analyser::Static::Media &media,
|
||||
const std::string &file_name,
|
||||
bool is_confident
|
||||
) {
|
||||
auto target = std::make_unique<Vic20Target>();
|
||||
const auto files = analyse_files<TargetPlatform::Vic20>(media);
|
||||
if(!files.files.empty()) {
|
||||
target->loading_command = loading_command(files);
|
||||
|
||||
const auto model = analyse_starting_address(files.files[0].starting_address);
|
||||
if(model.second.has_value()) {
|
||||
target->set_memory_model(*model.second);
|
||||
}
|
||||
}
|
||||
|
||||
if(is_confident) {
|
||||
target->media = media;
|
||||
set_loading_command(*target);
|
||||
} else {
|
||||
// Strip out inappropriate cartridges but retain all tapes and disks.
|
||||
target->media.cartridges = Vic20CartridgesFrom(media.cartridges);
|
||||
target->media.disks = media.disks;
|
||||
target->media.tapes = media.tapes;
|
||||
}
|
||||
|
||||
for(const auto &file : files.files) {
|
||||
// The Vic-20 never has RAM after 0x8000.
|
||||
if(file.ending_address >= 0x8000) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(obviously_uses_ted(file)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Inspect filename for configuration hints.
|
||||
if(!target->media.empty()) {
|
||||
// Inspect filename for configuration hints.
|
||||
using Region = Analyser::Static::Commodore::Vic20Target::Region;
|
||||
|
||||
std::string lowercase_name = file_name;
|
||||
std::transform(lowercase_name.begin(), lowercase_name.end(), lowercase_name.begin(), ::tolower);
|
||||
|
||||
// Hint 1: 'ntsc' anywhere in the name implies America.
|
||||
if(lowercase_name.find("ntsc") != std::string::npos) {
|
||||
target->region = Analyser::Static::Commodore::Target::Region::American;
|
||||
target->region = Region::American;
|
||||
}
|
||||
|
||||
// Potential additional hints: check for TheC64 tags.
|
||||
// Potential additional hints: check for TheC64 tags; these are Vic-20 exclusive.
|
||||
auto final_underscore = lowercase_name.find_last_of('_');
|
||||
if(final_underscore != std::string::npos) {
|
||||
auto iterator = lowercase_name.begin() + ssize_t(final_underscore) + 1;
|
||||
@ -180,10 +373,10 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media
|
||||
target->enabled_ram.bank3 |= !strcmp(next_tag, "b3");
|
||||
target->enabled_ram.bank5 |= !strcmp(next_tag, "b5");
|
||||
if(!strcmp(next_tag, "tn")) { // i.e. NTSC.
|
||||
target->region = Analyser::Static::Commodore::Target::Region::American;
|
||||
target->region = Region::American;
|
||||
}
|
||||
if(!strcmp(next_tag, "tp")) { // i.e. PAL.
|
||||
target->region = Analyser::Static::Commodore::Target::Region::European;
|
||||
target->region = Region::European;
|
||||
}
|
||||
|
||||
// Unhandled:
|
||||
@ -194,11 +387,36 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media
|
||||
// RO: this disk image should be treated as read-only.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attach a 1540 if there are any disks here.
|
||||
target->has_c1540 = !target->media.disks.empty();
|
||||
// Attach a 1540 if there are any disks here.
|
||||
target->has_c1540 = !target->media.disks.empty();
|
||||
return target;
|
||||
}
|
||||
|
||||
destination.push_back(std::move(target));
|
||||
}
|
||||
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &file_name,
|
||||
TargetPlatform::IntType platforms,
|
||||
bool is_confident
|
||||
) {
|
||||
TargetList destination;
|
||||
|
||||
if(platforms & TargetPlatform::Vic20) {
|
||||
auto vic20 = get_target<TargetPlatform::Vic20>(media, file_name, is_confident);
|
||||
if(vic20) {
|
||||
destination.push_back(std::move(vic20));
|
||||
}
|
||||
}
|
||||
|
||||
if(platforms & TargetPlatform::Plus4) {
|
||||
auto plus4 = get_target<TargetPlatform::Plus4>(media, file_name, is_confident);
|
||||
if(plus4) {
|
||||
destination.push_back(std::move(plus4));
|
||||
}
|
||||
}
|
||||
|
||||
return destination;
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::Commodore {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -7,26 +7,25 @@
|
||||
//
|
||||
|
||||
#include "Tape.hpp"
|
||||
|
||||
#include "../../../Storage/Tape/Parsers/Commodore.hpp"
|
||||
#include "Storage/Tape/Parsers/Commodore.hpp"
|
||||
|
||||
using namespace Analyser::Static::Commodore;
|
||||
|
||||
std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
Storage::Tape::Commodore::Parser parser;
|
||||
std::vector<File> Analyser::Static::Commodore::GetFiles(Storage::Tape::TapeSerialiser &serialiser, TargetPlatform::Type type) {
|
||||
Storage::Tape::Commodore::Parser parser(type);
|
||||
std::vector<File> file_list;
|
||||
|
||||
std::unique_ptr<Storage::Tape::Commodore::Header> header = parser.get_next_header(tape);
|
||||
std::unique_ptr<Storage::Tape::Commodore::Header> header = parser.get_next_header(serialiser);
|
||||
|
||||
while(!tape->is_at_end()) {
|
||||
while(!serialiser.is_at_end()) {
|
||||
if(!header) {
|
||||
header = parser.get_next_header(tape);
|
||||
header = parser.get_next_header(serialiser);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(header->type) {
|
||||
case Storage::Tape::Commodore::Header::DataSequenceHeader: {
|
||||
File new_file;
|
||||
File &new_file = file_list.emplace_back();
|
||||
new_file.name = header->name;
|
||||
new_file.raw_name = header->raw_name;
|
||||
new_file.starting_address = header->starting_address;
|
||||
@ -34,38 +33,36 @@ std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<St
|
||||
new_file.type = File::DataSequence;
|
||||
|
||||
new_file.data.swap(header->data);
|
||||
while(!tape->is_at_end()) {
|
||||
header = parser.get_next_header(tape);
|
||||
while(!serialiser.is_at_end()) {
|
||||
header = parser.get_next_header(serialiser);
|
||||
if(!header) continue;
|
||||
if(header->type != Storage::Tape::Commodore::Header::DataBlock) break;
|
||||
std::copy(header->data.begin(), header->data.end(), std::back_inserter(new_file.data));
|
||||
}
|
||||
|
||||
file_list.push_back(new_file);
|
||||
}
|
||||
break;
|
||||
|
||||
case Storage::Tape::Commodore::Header::RelocatableProgram:
|
||||
case Storage::Tape::Commodore::Header::NonRelocatableProgram: {
|
||||
std::unique_ptr<Storage::Tape::Commodore::Data> data = parser.get_next_data(tape);
|
||||
std::unique_ptr<Storage::Tape::Commodore::Data> data = parser.get_next_data(serialiser);
|
||||
if(data) {
|
||||
File new_file;
|
||||
File &new_file = file_list.emplace_back();
|
||||
new_file.name = header->name;
|
||||
new_file.raw_name = header->raw_name;
|
||||
new_file.starting_address = header->starting_address;
|
||||
new_file.ending_address = header->ending_address;
|
||||
new_file.data.swap(data->data);
|
||||
new_file.type = (header->type == Storage::Tape::Commodore::Header::RelocatableProgram) ? File::RelocatableProgram : File::NonRelocatableProgram;
|
||||
|
||||
file_list.push_back(new_file);
|
||||
new_file.type =
|
||||
header->type == Storage::Tape::Commodore::Header::RelocatableProgram
|
||||
? File::RelocatableProgram : File::NonRelocatableProgram;
|
||||
}
|
||||
|
||||
header = parser.get_next_header(tape);
|
||||
header = parser.get_next_header(serialiser);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
header = parser.get_next_header(tape);
|
||||
header = parser.get_next_header(serialiser);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Storage/Tape/Tape.hpp"
|
||||
#include "Storage/Tape/Tape.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include "File.hpp"
|
||||
|
||||
namespace Analyser::Static::Commodore {
|
||||
|
||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||
std::vector<File> GetFiles(Storage::Tape::TapeSerialiser &, TargetPlatform::Type);
|
||||
|
||||
}
|
||||
|
@ -8,14 +8,28 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Enum.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::Commodore {
|
||||
|
||||
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
struct Plus4Target: public Analyser::Static::Target, public Reflection::StructImpl<Plus4Target> {
|
||||
// TODO: region, etc.
|
||||
std::string loading_command;
|
||||
bool has_c1541 = false;
|
||||
|
||||
Plus4Target() : Analyser::Static::Target(Machine::Plus4) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<Plus4Target>;
|
||||
void declare_fields() {
|
||||
DeclareField(has_c1541);
|
||||
}
|
||||
};
|
||||
|
||||
struct Vic20Target: public Analyser::Static::Target, public Reflection::StructImpl<Vic20Target> {
|
||||
enum class MemoryModel {
|
||||
Unexpanded,
|
||||
EightKB,
|
||||
@ -54,17 +68,19 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
bool has_c1540 = false;
|
||||
std::string loading_command;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::Vic20) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(enabled_ram.bank0);
|
||||
DeclareField(enabled_ram.bank1);
|
||||
DeclareField(enabled_ram.bank2);
|
||||
DeclareField(enabled_ram.bank3);
|
||||
DeclareField(enabled_ram.bank5);
|
||||
DeclareField(region);
|
||||
DeclareField(has_c1540);
|
||||
AnnounceEnum(Region);
|
||||
}
|
||||
Vic20Target() : Analyser::Static::Target(Machine::Vic20) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<Vic20Target>;
|
||||
void declare_fields() {
|
||||
DeclareField(enabled_ram.bank0);
|
||||
DeclareField(enabled_ram.bank1);
|
||||
DeclareField(enabled_ram.bank2);
|
||||
DeclareField(enabled_ram.bank3);
|
||||
DeclareField(enabled_ram.bank5);
|
||||
DeclareField(region);
|
||||
DeclareField(has_c1540);
|
||||
AnnounceEnum(Region);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -17,7 +17,12 @@ using PartialDisassembly = Analyser::Static::Disassembly::PartialDisassembly<Dis
|
||||
|
||||
struct MOS6502Disassembler {
|
||||
|
||||
static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector<uint8_t> &memory, const std::function<std::size_t(uint16_t)> &address_mapper, uint16_t entry_point) {
|
||||
static void AddToDisassembly(
|
||||
PartialDisassembly &disassembly,
|
||||
const std::vector<uint8_t> &memory,
|
||||
const std::function<std::size_t(uint16_t)> &address_mapper,
|
||||
uint16_t entry_point
|
||||
) {
|
||||
disassembly.disassembly.internal_calls.insert(entry_point);
|
||||
uint16_t address = entry_point;
|
||||
while(true) {
|
||||
@ -75,23 +80,25 @@ static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector<
|
||||
}
|
||||
|
||||
// Decode operation.
|
||||
#define RM_INSTRUCTION(base, op) \
|
||||
case base+0x09: case base+0x05: case base+0x15: case base+0x01: case base+0x11: case base+0x0d: case base+0x1d: case base+0x19: \
|
||||
instruction.operation = op; \
|
||||
#define RM_INSTRUCTION(base, op) \
|
||||
case base+0x09: case base+0x05: case base+0x15: case base+0x01: \
|
||||
case base+0x11: case base+0x0d: case base+0x1d: case base+0x19: \
|
||||
instruction.operation = op; \
|
||||
break;
|
||||
|
||||
#define URM_INSTRUCTION(base, op) \
|
||||
#define URM_INSTRUCTION(base, op) \
|
||||
case base+0x07: case base+0x17: case base+0x03: case base+0x13: case base+0x0f: case base+0x1f: case base+0x1b: \
|
||||
instruction.operation = op; \
|
||||
instruction.operation = op; \
|
||||
break;
|
||||
|
||||
#define M_INSTRUCTION(base, op) \
|
||||
#define M_INSTRUCTION(base, op) \
|
||||
case base+0x0a: case base+0x06: case base+0x16: case base+0x0e: case base+0x1e: \
|
||||
instruction.operation = op; \
|
||||
instruction.operation = op; \
|
||||
break;
|
||||
|
||||
#define IM_INSTRUCTION(base, op) \
|
||||
#define IM_INSTRUCTION(base, op) \
|
||||
case base: instruction.operation = op; break;
|
||||
|
||||
switch(operation) {
|
||||
default:
|
||||
instruction.operation = Instruction::KIL;
|
||||
@ -259,7 +266,10 @@ static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector<
|
||||
disassembly.disassembly.instructions_by_address[instruction.address] = instruction;
|
||||
|
||||
// TODO: something wider-ranging than this
|
||||
if(instruction.addressing_mode == Instruction::Absolute || instruction.addressing_mode == Instruction::ZeroPage) {
|
||||
if(
|
||||
instruction.addressing_mode == Instruction::Absolute ||
|
||||
instruction.addressing_mode == Instruction::ZeroPage
|
||||
) {
|
||||
const size_t mapped_address = address_mapper(instruction.operand);
|
||||
const bool is_external = mapped_address >= memory.size();
|
||||
|
||||
@ -272,20 +282,23 @@ static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector<
|
||||
case Instruction::ADC: case Instruction::SBC:
|
||||
case Instruction::LAS:
|
||||
case Instruction::CMP: case Instruction::CPX: case Instruction::CPY:
|
||||
(is_external ? disassembly.disassembly.external_loads : disassembly.disassembly.internal_loads).insert(instruction.operand);
|
||||
(is_external ? disassembly.disassembly.external_loads : disassembly.disassembly.internal_loads)
|
||||
.insert(instruction.operand);
|
||||
break;
|
||||
|
||||
case Instruction::STY: case Instruction::STX: case Instruction::STA:
|
||||
case Instruction::AXS: case Instruction::AHX: case Instruction::SHX: case Instruction::SHY:
|
||||
case Instruction::TAS:
|
||||
(is_external ? disassembly.disassembly.external_stores : disassembly.disassembly.internal_stores).insert(instruction.operand);
|
||||
(is_external ? disassembly.disassembly.external_stores : disassembly.disassembly.internal_stores)
|
||||
.insert(instruction.operand);
|
||||
break;
|
||||
|
||||
case Instruction::SLO: case Instruction::RLA: case Instruction::SRE: case Instruction::RRA:
|
||||
case Instruction::DCP: case Instruction::ISC:
|
||||
case Instruction::INC: case Instruction::DEC:
|
||||
case Instruction::ASL: case Instruction::ROL: case Instruction::LSR: case Instruction::ROR:
|
||||
(is_external ? disassembly.disassembly.external_modifies : disassembly.disassembly.internal_modifies).insert(instruction.operand);
|
||||
(is_external ? disassembly.disassembly.external_modifies : disassembly.disassembly.internal_modifies)
|
||||
.insert(instruction.operand);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -330,5 +343,10 @@ Disassembly Analyser::Static::MOS6502::Disassemble(
|
||||
const std::vector<uint8_t> &memory,
|
||||
const std::function<std::size_t(uint16_t)> &address_mapper,
|
||||
std::vector<uint16_t> entry_points) {
|
||||
return Analyser::Static::Disassembly::Disassemble<Disassembly, uint16_t, MOS6502Disassembler>(memory, address_mapper, entry_points, false);
|
||||
return Analyser::Static::Disassembly::Disassemble<Disassembly, uint16_t, MOS6502Disassembler>(
|
||||
memory,
|
||||
address_mapper,
|
||||
entry_points,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ namespace Analyser::Static::Disassembler {
|
||||
Provides an address mapper that relocates a chunk of memory so that it starts at
|
||||
address @c start_address.
|
||||
*/
|
||||
template <typename T> std::function<std::size_t(T)> OffsetMapper(T start_address) {
|
||||
template <typename T> std::function<std::size_t(T)> OffsetMapper(const T start_address) {
|
||||
return [start_address](T argument) {
|
||||
return size_t(argument - start_address);
|
||||
};
|
||||
|
@ -16,44 +16,48 @@ namespace {
|
||||
using PartialDisassembly = Analyser::Static::Disassembly::PartialDisassembly<Disassembly, uint16_t>;
|
||||
|
||||
class Accessor {
|
||||
public:
|
||||
Accessor(const std::vector<uint8_t> &memory, const std::function<std::size_t(uint16_t)> &address_mapper, uint16_t address) :
|
||||
memory_(memory), address_mapper_(address_mapper), address_(address) {}
|
||||
public:
|
||||
Accessor(
|
||||
const std::vector<uint8_t> &memory,
|
||||
const std::function<std::size_t(uint16_t)> &address_mapper,
|
||||
uint16_t address
|
||||
) :
|
||||
memory_(memory), address_mapper_(address_mapper), address_(address) {}
|
||||
|
||||
uint8_t byte() {
|
||||
std::size_t mapped_address = address_mapper_(address_);
|
||||
address_++;
|
||||
if(mapped_address >= memory_.size()) {
|
||||
overrun_ = true;
|
||||
return 0xff;
|
||||
}
|
||||
return memory_[mapped_address];
|
||||
uint8_t byte() {
|
||||
std::size_t mapped_address = address_mapper_(address_);
|
||||
++address_;
|
||||
if(mapped_address >= memory_.size()) {
|
||||
overrun_ = true;
|
||||
return 0xff;
|
||||
}
|
||||
return memory_[mapped_address];
|
||||
}
|
||||
|
||||
uint16_t word() {
|
||||
uint8_t low = byte();
|
||||
uint8_t high = byte();
|
||||
return uint16_t(low | (high << 8));
|
||||
}
|
||||
uint16_t word() {
|
||||
uint8_t low = byte();
|
||||
uint8_t high = byte();
|
||||
return uint16_t(low | (high << 8));
|
||||
}
|
||||
|
||||
bool overrun() {
|
||||
return overrun_;
|
||||
}
|
||||
bool overrun() const {
|
||||
return overrun_;
|
||||
}
|
||||
|
||||
bool at_end() {
|
||||
std::size_t mapped_address = address_mapper_(address_);
|
||||
return mapped_address >= memory_.size();
|
||||
}
|
||||
bool at_end() const {
|
||||
std::size_t mapped_address = address_mapper_(address_);
|
||||
return mapped_address >= memory_.size();
|
||||
}
|
||||
|
||||
uint16_t address() {
|
||||
return address_;
|
||||
}
|
||||
uint16_t address() const {
|
||||
return address_;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<uint8_t> &memory_;
|
||||
const std::function<std::size_t(uint16_t)> &address_mapper_;
|
||||
uint16_t address_;
|
||||
bool overrun_ = false;
|
||||
private:
|
||||
const std::vector<uint8_t> &memory_;
|
||||
const std::function<std::size_t(uint16_t)> &address_mapper_;
|
||||
uint16_t address_;
|
||||
bool overrun_ = false;
|
||||
};
|
||||
|
||||
constexpr uint8_t x(uint8_t v) { return v >> 6; }
|
||||
@ -83,8 +87,12 @@ Instruction::Location register_pair_table2[] = {
|
||||
Instruction::Location::AF
|
||||
};
|
||||
|
||||
Instruction::Location RegisterTableEntry(int offset, Accessor &accessor, Instruction &instruction, bool needs_indirect_offset) {
|
||||
Instruction::Location register_table[] = {
|
||||
Instruction::Location RegisterTableEntry(
|
||||
const int offset, Accessor &accessor,
|
||||
Instruction &instruction,
|
||||
const bool needs_indirect_offset
|
||||
) {
|
||||
constexpr Instruction::Location register_table[] = {
|
||||
Instruction::Location::B, Instruction::Location::C,
|
||||
Instruction::Location::D, Instruction::Location::E,
|
||||
Instruction::Location::H, Instruction::Location::L,
|
||||
@ -92,7 +100,7 @@ Instruction::Location RegisterTableEntry(int offset, Accessor &accessor, Instruc
|
||||
Instruction::Location::A
|
||||
};
|
||||
|
||||
Instruction::Location location = register_table[offset];
|
||||
const Instruction::Location location = register_table[offset];
|
||||
if(location == Instruction::Location::HL_Indirect && needs_indirect_offset) {
|
||||
instruction.offset = accessor.byte() - 128;
|
||||
}
|
||||
@ -100,7 +108,7 @@ Instruction::Location RegisterTableEntry(int offset, Accessor &accessor, Instruc
|
||||
return location;
|
||||
}
|
||||
|
||||
Instruction::Operation alu_table[] = {
|
||||
constexpr Instruction::Operation alu_table[] = {
|
||||
Instruction::Operation::ADD,
|
||||
Instruction::Operation::ADC,
|
||||
Instruction::Operation::SUB,
|
||||
@ -111,7 +119,7 @@ Instruction::Operation alu_table[] = {
|
||||
Instruction::Operation::CP
|
||||
};
|
||||
|
||||
Instruction::Operation rotation_table[] = {
|
||||
constexpr Instruction::Operation rotation_table[] = {
|
||||
Instruction::Operation::RLC,
|
||||
Instruction::Operation::RRC,
|
||||
Instruction::Operation::RL,
|
||||
@ -122,19 +130,32 @@ Instruction::Operation rotation_table[] = {
|
||||
Instruction::Operation::SRL
|
||||
};
|
||||
|
||||
Instruction::Operation block_table[][4] = {
|
||||
{Instruction::Operation::LDI, Instruction::Operation::CPI, Instruction::Operation::INI, Instruction::Operation::OUTI},
|
||||
{Instruction::Operation::LDD, Instruction::Operation::CPD, Instruction::Operation::IND, Instruction::Operation::OUTD},
|
||||
{Instruction::Operation::LDIR, Instruction::Operation::CPIR, Instruction::Operation::INIR, Instruction::Operation::OTIR},
|
||||
{Instruction::Operation::LDDR, Instruction::Operation::CPDR, Instruction::Operation::INDR, Instruction::Operation::OTDR},
|
||||
constexpr Instruction::Operation block_table[][4] = {
|
||||
{
|
||||
Instruction::Operation::LDI, Instruction::Operation::CPI,
|
||||
Instruction::Operation::INI, Instruction::Operation::OUTI
|
||||
},
|
||||
{
|
||||
Instruction::Operation::LDD, Instruction::Operation::CPD,
|
||||
Instruction::Operation::IND, Instruction::Operation::OUTD
|
||||
},
|
||||
{
|
||||
Instruction::Operation::LDIR, Instruction::Operation::CPIR,
|
||||
Instruction::Operation::INIR, Instruction::Operation::OTIR
|
||||
},
|
||||
{
|
||||
Instruction::Operation::LDDR, Instruction::Operation::CPDR,
|
||||
Instruction::Operation::INDR, Instruction::Operation::OTDR
|
||||
},
|
||||
};
|
||||
|
||||
void DisassembleCBPage(Accessor &accessor, Instruction &instruction, bool needs_indirect_offset) {
|
||||
void DisassembleCBPage(Accessor &accessor, Instruction &instruction, const bool needs_indirect_offset) {
|
||||
const uint8_t operation = accessor.byte();
|
||||
|
||||
if(!x(operation)) {
|
||||
instruction.operation = rotation_table[y(operation)];
|
||||
instruction.source = instruction.destination = RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.source = instruction.destination =
|
||||
RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset);
|
||||
} else {
|
||||
instruction.destination = RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.source = Instruction::Location::Operand;
|
||||
@ -148,7 +169,7 @@ void DisassembleCBPage(Accessor &accessor, Instruction &instruction, bool needs_
|
||||
}
|
||||
}
|
||||
|
||||
void DisassembleEDPage(Accessor &accessor, Instruction &instruction, bool needs_indirect_offset) {
|
||||
void DisassembleEDPage(Accessor &accessor, Instruction &instruction, const bool needs_indirect_offset) {
|
||||
const uint8_t operation = accessor.byte();
|
||||
|
||||
switch(x(operation)) {
|
||||
@ -170,7 +191,8 @@ void DisassembleEDPage(Accessor &accessor, Instruction &instruction, bool needs_
|
||||
if(y(operation) == 6) {
|
||||
instruction.destination = Instruction::Location::None;
|
||||
} else {
|
||||
instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.destination =
|
||||
RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
@ -179,7 +201,8 @@ void DisassembleEDPage(Accessor &accessor, Instruction &instruction, bool needs_
|
||||
if(y(operation) == 6) {
|
||||
instruction.source = Instruction::Location::None;
|
||||
} else {
|
||||
instruction.source = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.source =
|
||||
RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
@ -190,11 +213,13 @@ void DisassembleEDPage(Accessor &accessor, Instruction &instruction, bool needs_
|
||||
case 3:
|
||||
instruction.operation = Instruction::Operation::LD;
|
||||
if(q(operation)) {
|
||||
instruction.destination = RegisterTableEntry(p(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.destination =
|
||||
RegisterTableEntry(p(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.source = Instruction::Location::Operand_Indirect;
|
||||
} else {
|
||||
instruction.destination = Instruction::Location::Operand_Indirect;
|
||||
instruction.source = RegisterTableEntry(p(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.source =
|
||||
RegisterTableEntry(p(operation), accessor, instruction, needs_indirect_offset);
|
||||
}
|
||||
instruction.operand = accessor.word();
|
||||
break;
|
||||
@ -202,7 +227,8 @@ void DisassembleEDPage(Accessor &accessor, Instruction &instruction, bool needs_
|
||||
instruction.operation = Instruction::Operation::NEG;
|
||||
break;
|
||||
case 5:
|
||||
instruction.operation = (y(operation) == 1) ? Instruction::Operation::RETI : Instruction::Operation::RETN;
|
||||
instruction.operation =
|
||||
y(operation) == 1 ? Instruction::Operation::RETI : Instruction::Operation::RETN;
|
||||
break;
|
||||
case 6:
|
||||
instruction.operation = Instruction::Operation::IM;
|
||||
@ -253,7 +279,7 @@ void DisassembleMainPage(Accessor &accessor, Instruction &instruction) {
|
||||
} hl_substitution = None;
|
||||
|
||||
while(true) {
|
||||
uint8_t operation = accessor.byte();
|
||||
const uint8_t operation = accessor.byte();
|
||||
|
||||
switch(x(operation)) {
|
||||
case 0:
|
||||
@ -343,15 +369,18 @@ void DisassembleMainPage(Accessor &accessor, Instruction &instruction) {
|
||||
break;
|
||||
case 4:
|
||||
instruction.operation = Instruction::Operation::INC;
|
||||
instruction.source = instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.source = instruction.destination =
|
||||
RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
|
||||
break;
|
||||
case 5:
|
||||
instruction.operation = Instruction::Operation::DEC;
|
||||
instruction.source = instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.source = instruction.destination =
|
||||
RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
|
||||
break;
|
||||
case 6:
|
||||
instruction.operation = Instruction::Operation::LD;
|
||||
instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.destination =
|
||||
RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.source = Instruction::Location::Operand;
|
||||
instruction.operand = accessor.byte();
|
||||
break;
|
||||
@ -374,8 +403,10 @@ void DisassembleMainPage(Accessor &accessor, Instruction &instruction) {
|
||||
instruction.operation = Instruction::Operation::HALT;
|
||||
} else {
|
||||
instruction.operation = Instruction::Operation::LD;
|
||||
instruction.source = RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.destination = RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.source =
|
||||
RegisterTableEntry(z(operation), accessor, instruction, needs_indirect_offset);
|
||||
instruction.destination =
|
||||
RegisterTableEntry(y(operation), accessor, instruction, needs_indirect_offset);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
@ -517,10 +548,14 @@ void DisassembleMainPage(Accessor &accessor, Instruction &instruction) {
|
||||
instruction.destination == Instruction::Location::HL_Indirect) {
|
||||
|
||||
if(instruction.source == Instruction::Location::HL_Indirect) {
|
||||
instruction.source = (hl_substitution == IX) ? Instruction::Location::IX_Indirect_Offset : Instruction::Location::IY_Indirect_Offset;
|
||||
instruction.source =
|
||||
hl_substitution == IX ?
|
||||
Instruction::Location::IX_Indirect_Offset : Instruction::Location::IY_Indirect_Offset;
|
||||
}
|
||||
if(instruction.destination == Instruction::Location::HL_Indirect) {
|
||||
instruction.destination = (hl_substitution == IX) ? Instruction::Location::IX_Indirect_Offset : Instruction::Location::IY_Indirect_Offset;
|
||||
instruction.destination =
|
||||
hl_substitution == IX ?
|
||||
Instruction::Location::IX_Indirect_Offset : Instruction::Location::IY_Indirect_Offset;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -542,7 +577,12 @@ void DisassembleMainPage(Accessor &accessor, Instruction &instruction) {
|
||||
}
|
||||
|
||||
struct Z80Disassembler {
|
||||
static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector<uint8_t> &memory, const std::function<std::size_t(uint16_t)> &address_mapper, uint16_t entry_point) {
|
||||
static void AddToDisassembly(
|
||||
PartialDisassembly &disassembly,
|
||||
const std::vector<uint8_t> &memory,
|
||||
const std::function<std::size_t(uint16_t)> &address_mapper,
|
||||
const uint16_t entry_point
|
||||
) {
|
||||
disassembly.disassembly.internal_calls.insert(entry_point);
|
||||
Accessor accessor(memory, address_mapper, entry_point);
|
||||
|
||||
|
@ -8,14 +8,14 @@
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
|
||||
#include "../AppleII/Target.hpp"
|
||||
#include "../AppleIIgs/Target.hpp"
|
||||
#include "../Oric/Target.hpp"
|
||||
#include "../Disassembler/6502.hpp"
|
||||
#include "../Disassembler/AddressMapper.hpp"
|
||||
#include "Analyser/Static/AppleII/Target.hpp"
|
||||
#include "Analyser/Static//AppleIIgs/Target.hpp"
|
||||
#include "Analyser/Static//Oric/Target.hpp"
|
||||
#include "Analyser/Static//Disassembler/6502.hpp"
|
||||
#include "Analyser/Static//Disassembler/AddressMapper.hpp"
|
||||
|
||||
#include "../../../Storage/Disk/Track/TrackSerialiser.hpp"
|
||||
#include "../../../Storage/Disk/Encodings/AppleGCR/SegmentParser.hpp"
|
||||
#include "Storage/Disk/Track/TrackSerialiser.hpp"
|
||||
#include "Storage/Disk/Encodings/AppleGCR/SegmentParser.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -47,7 +47,12 @@ Analyser::Static::Target *OricTarget(const Storage::Encodings::AppleGCR::Sector
|
||||
|
||||
}
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
bool
|
||||
) {
|
||||
// This analyser can comprehend disks only.
|
||||
if(media.disks.empty()) return {};
|
||||
|
||||
@ -55,14 +60,15 @@ Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets(const Media &m
|
||||
TargetList targets;
|
||||
|
||||
// If the disk image is too large for a 5.25" disk, map this to the IIgs.
|
||||
if(disk->get_maximum_head_position() > Storage::Disk::HeadPosition(40)) {
|
||||
if(disk->maximum_head_position() > Storage::Disk::HeadPosition(40)) {
|
||||
targets.push_back(std::unique_ptr<Analyser::Static::Target>(AppleIIgsTarget()));
|
||||
targets.back()->media = media;
|
||||
return targets;
|
||||
}
|
||||
|
||||
// Grab track 0, sector 0: the boot sector.
|
||||
const auto track_zero = disk->get_track_at_position(Storage::Disk::Track::Address(0, Storage::Disk::HeadPosition(0)));
|
||||
const auto track_zero =
|
||||
disk->track_at_position(Storage::Disk::Track::Address(0, Storage::Disk::HeadPosition(0)));
|
||||
const auto sector_map = Storage::Encodings::AppleGCR::sectors_from_segment(
|
||||
Storage::Disk::track_serialisation(*track_zero, Storage::Time(1, 50000)));
|
||||
|
||||
@ -89,7 +95,8 @@ Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets(const Media &m
|
||||
// If the boot sector looks like it's intended for the Oric, create an Oric.
|
||||
// Otherwise go with the Apple II.
|
||||
|
||||
const auto disassembly = Analyser::Static::MOS6502::Disassemble(sector_zero->data, Analyser::Static::Disassembler::OffsetMapper(0xb800), {0xb800});
|
||||
const auto disassembly = Analyser::Static::MOS6502::Disassemble(
|
||||
sector_zero->data, Analyser::Static::Disassembler::OffsetMapper(0xb800), {0xb800});
|
||||
|
||||
bool did_read_shift_register = false;
|
||||
bool is_oric = false;
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::DiskII {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "StaticAnalyser.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
#include "../../../Storage/Disk/Parsers/FAT.hpp"
|
||||
#include "Storage/Disk/Parsers/FAT.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -26,7 +26,12 @@ bool insensitive_equal(const std::string &lhs, const std::string &rhs) {
|
||||
|
||||
}
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
bool
|
||||
) {
|
||||
// This analyser can comprehend disks only.
|
||||
if(media.disks.empty()) return {};
|
||||
|
||||
@ -72,7 +77,8 @@ Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets(const Medi
|
||||
|
||||
if(!has_exdos_ini) {
|
||||
if(did_pick_file) {
|
||||
target->loading_command = std::string("run \"") + selected_file->name + "." + selected_file->extension + "\"\n";
|
||||
target->loading_command =
|
||||
std::string("run \"") + selected_file->name + "." + selected_file->extension + "\"\n";
|
||||
} else {
|
||||
target->loading_command = ":dir\n";
|
||||
}
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::Enterprise {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Enum.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
@ -30,20 +30,22 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
Speed speed = Speed::FourMHz;
|
||||
std::string loading_command;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::Enterprise) {
|
||||
if(needs_declare()) {
|
||||
AnnounceEnum(Model);
|
||||
AnnounceEnum(EXOSVersion);
|
||||
AnnounceEnum(BASICVersion);
|
||||
AnnounceEnum(DOS);
|
||||
AnnounceEnum(Speed);
|
||||
Target() : Analyser::Static::Target(Machine::Enterprise) {}
|
||||
|
||||
DeclareField(model);
|
||||
DeclareField(exos_version);
|
||||
DeclareField(basic_version);
|
||||
DeclareField(dos);
|
||||
DeclareField(speed);
|
||||
}
|
||||
private:
|
||||
friend Reflection::StructImpl<Target>;
|
||||
void declare_fields() {
|
||||
AnnounceEnum(Model);
|
||||
AnnounceEnum(EXOSVersion);
|
||||
AnnounceEnum(BASICVersion);
|
||||
AnnounceEnum(DOS);
|
||||
AnnounceEnum(Speed);
|
||||
|
||||
DeclareField(model);
|
||||
DeclareField(exos_version);
|
||||
DeclareField(basic_version);
|
||||
DeclareField(dos);
|
||||
DeclareField(speed);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8,15 +8,20 @@
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
|
||||
#include "../Enterprise/StaticAnalyser.hpp"
|
||||
#include "../PCCompatible/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/Enterprise/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/PCCompatible/StaticAnalyser.hpp"
|
||||
|
||||
#include "../../../Storage/Disk/Track/TrackSerialiser.hpp"
|
||||
#include "../../../Storage/Disk/Encodings/MFM/Constants.hpp"
|
||||
#include "../../../Storage/Disk/Encodings/MFM/SegmentParser.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) {
|
||||
Analyser::Static::TargetList Analyser::Static::FAT12::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &file_name,
|
||||
TargetPlatform::IntType platforms,
|
||||
bool
|
||||
) {
|
||||
// This analyser can comprehend disks only.
|
||||
if(media.disks.empty()) return {};
|
||||
|
||||
@ -34,12 +39,13 @@ Analyser::Static::TargetList Analyser::Static::FAT12::GetTargets(const Media &me
|
||||
|
||||
// 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);
|
||||
if(disk->maximum_head_position() <= Storage::Disk::HeadPosition(40)) {
|
||||
return Analyser::Static::PCCompatible::GetTargets(media, file_name, platforms, true);
|
||||
}
|
||||
|
||||
// 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 track_zero =
|
||||
disk->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,
|
||||
@ -48,7 +54,7 @@ Analyser::Static::TargetList Analyser::Static::FAT12::GetTargets(const Media &me
|
||||
|
||||
// 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);
|
||||
return Analyser::Static::PCCompatible::GetTargets(media, file_name, platforms, true);
|
||||
}
|
||||
|
||||
const Storage::Encodings::MFM::Sector *boot_sector = nullptr;
|
||||
@ -77,7 +83,7 @@ Analyser::Static::TargetList Analyser::Static::FAT12::GetTargets(const Media &me
|
||||
if(
|
||||
std::search(sample.begin(), sample.end(), string.begin(), string.end()) != sample.end()
|
||||
) {
|
||||
return Analyser::Static::PCCompatible::GetTargets(media, file_name, platforms);
|
||||
return Analyser::Static::PCCompatible::GetTargets(media, file_name, platforms, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,5 +102,5 @@ Analyser::Static::TargetList Analyser::Static::FAT12::GetTargets(const Media &me
|
||||
// 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);
|
||||
return Analyser::Static::Enterprise::GetTargets(media, file_name, platforms, false);
|
||||
}
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/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);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Storage/Cartridge/Cartridge.hpp"
|
||||
#include "Storage/Cartridge/Cartridge.hpp"
|
||||
|
||||
namespace Analyser::Static::MSX {
|
||||
|
||||
@ -26,7 +26,7 @@ struct Cartridge: public ::Storage::Cartridge::Cartridge {
|
||||
};
|
||||
const Type type;
|
||||
|
||||
Cartridge(const std::vector<Segment> &segments, Type type) :
|
||||
Cartridge(const std::vector<Segment> &segments, const Type type) :
|
||||
Storage::Cartridge::Cartridge(segments), type(type) {}
|
||||
};
|
||||
|
||||
|
@ -12,8 +12,8 @@
|
||||
#include "Tape.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
#include "../Disassembler/Z80.hpp"
|
||||
#include "../Disassembler/AddressMapper.hpp"
|
||||
#include "Analyser/Static/Disassembler/Z80.hpp"
|
||||
#include "Analyser/Static//Disassembler/AddressMapper.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -27,7 +27,8 @@ static std::unique_ptr<Analyser::Static::Target> CartridgeTarget(
|
||||
std::vector<Storage::Cartridge::Cartridge::Segment> output_segments;
|
||||
if(segment.data.size() & 0x1fff) {
|
||||
std::vector<uint8_t> truncated_data;
|
||||
std::vector<uint8_t>::difference_type truncated_size = std::vector<uint8_t>::difference_type(segment.data.size()) & ~0x1fff;
|
||||
const auto truncated_size =
|
||||
std::vector<uint8_t>::difference_type(segment.data.size()) & ~0x1fff;
|
||||
truncated_data.insert(truncated_data.begin(), segment.data.begin(), segment.data.begin() + truncated_size);
|
||||
output_segments.emplace_back(start_address, truncated_data);
|
||||
} else {
|
||||
@ -82,7 +83,7 @@ static Analyser::Static::TargetList CartridgeTargetsFrom(
|
||||
if(segments.size() != 1) continue;
|
||||
|
||||
// Which must be no more than 63 bytes larger than a multiple of 8 kb in size.
|
||||
Storage::Cartridge::Cartridge::Segment segment = segments.front();
|
||||
const Storage::Cartridge::Cartridge::Segment &segment = segments.front();
|
||||
const size_t data_size = segment.data.size();
|
||||
if(data_size < 0x2000 || (data_size & 0x1fff) > 64) continue;
|
||||
|
||||
@ -101,7 +102,7 @@ static Analyser::Static::TargetList CartridgeTargetsFrom(
|
||||
// Reject cartridge if the ROM header wasn't found.
|
||||
if(!found_start) continue;
|
||||
|
||||
uint16_t init_address = uint16_t(segment.data[2] | (segment.data[3] << 8));
|
||||
const uint16_t init_address = uint16_t(segment.data[2] | (segment.data[3] << 8));
|
||||
// TODO: check for a rational init address?
|
||||
|
||||
// If this ROM is less than 48kb in size then it's an ordinary ROM. Just emplace it and move on.
|
||||
@ -137,10 +138,12 @@ static Analyser::Static::TargetList CartridgeTargetsFrom(
|
||||
}
|
||||
|
||||
// Weight confidences by number of observed hits; if any is above 60% confidence, just use it.
|
||||
const auto ascii_8kb_total = address_counts[0x6000] + address_counts[0x6800] + address_counts[0x7000] + address_counts[0x7800];
|
||||
const auto ascii_8kb_total =
|
||||
address_counts[0x6000] + address_counts[0x6800] + address_counts[0x7000] + address_counts[0x7800];
|
||||
const auto ascii_16kb_total = address_counts[0x6000] + address_counts[0x7000] + address_counts[0x77ff];
|
||||
const auto konami_total = address_counts[0x6000] + address_counts[0x8000] + address_counts[0xa000];
|
||||
const auto konami_with_scc_total = address_counts[0x5000] + address_counts[0x7000] + address_counts[0x9000] + address_counts[0xb000];
|
||||
const auto konami_with_scc_total =
|
||||
address_counts[0x5000] + address_counts[0x7000] + address_counts[0x9000] + address_counts[0xb000];
|
||||
|
||||
const auto total_hits = ascii_8kb_total + ascii_16kb_total + konami_total + konami_with_scc_total;
|
||||
|
||||
@ -182,7 +185,12 @@ static Analyser::Static::TargetList CartridgeTargetsFrom(
|
||||
return targets;
|
||||
}
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::MSX::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::MSX::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
bool
|
||||
) {
|
||||
TargetList destination;
|
||||
|
||||
// Append targets for any cartridges that look correct.
|
||||
@ -194,7 +202,7 @@ Analyser::Static::TargetList Analyser::Static::MSX::GetTargets(const Media &medi
|
||||
|
||||
// Check tapes for loadable files.
|
||||
for(auto &tape : media.tapes) {
|
||||
std::vector<File> files_on_tape = GetFiles(tape);
|
||||
const std::vector<File> files_on_tape = GetFiles(tape);
|
||||
if(!files_on_tape.empty()) {
|
||||
switch(files_on_tape.front().type) {
|
||||
case File::Type::ASCII: target->loading_command = "RUN\"CAS:\r"; break;
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::MSX {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "Tape.hpp"
|
||||
|
||||
#include "../../../Storage/Tape/Parsers/MSX.hpp"
|
||||
#include "Storage/Tape/Parsers/MSX.hpp"
|
||||
|
||||
using namespace Analyser::Static::MSX;
|
||||
|
||||
@ -29,12 +29,12 @@ std::vector<File> Analyser::Static::MSX::GetFiles(const std::shared_ptr<Storage:
|
||||
|
||||
Storage::Tape::BinaryTapePlayer tape_player(1000000);
|
||||
tape_player.set_motor_control(true);
|
||||
tape_player.set_tape(tape);
|
||||
tape_player.set_tape(tape, TargetPlatform::MSX);
|
||||
|
||||
using Parser = Storage::Tape::MSX::Parser;
|
||||
|
||||
// Get all recognisable files from the tape.
|
||||
while(!tape->is_at_end()) {
|
||||
while(!tape_player.is_at_end()) {
|
||||
// Try to locate and measure a header.
|
||||
std::unique_ptr<Parser::FileSpeed> file_speed = Parser::find_header(tape_player);
|
||||
if(!file_speed) continue;
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Storage/Tape/Tape.hpp"
|
||||
#include "Storage/Tape/Tape.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -32,6 +32,6 @@ struct File {
|
||||
File();
|
||||
};
|
||||
|
||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &);
|
||||
|
||||
}
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Enum.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::MSX {
|
||||
@ -33,15 +33,17 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
|
||||
);
|
||||
Region region = Region::USA;
|
||||
|
||||
Target(): Analyser::Static::Target(Machine::MSX) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(has_disk_drive);
|
||||
DeclareField(has_msx_music);
|
||||
DeclareField(region);
|
||||
AnnounceEnum(Region);
|
||||
DeclareField(model);
|
||||
AnnounceEnum(Model);
|
||||
}
|
||||
Target(): Analyser::Static::Target(Machine::MSX) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<Target>;
|
||||
void declare_fields() {
|
||||
DeclareField(has_disk_drive);
|
||||
DeclareField(has_msx_music);
|
||||
DeclareField(region);
|
||||
AnnounceEnum(Region);
|
||||
DeclareField(model);
|
||||
AnnounceEnum(Model);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9,9 +9,14 @@
|
||||
#include "StaticAnalyser.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Macintosh::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::Macintosh::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
bool is_confident
|
||||
) {
|
||||
// This analyser can comprehend disks and mass-storage devices only.
|
||||
if(media.disks.empty() && media.mass_storage_devices.empty()) return {};
|
||||
if(media.disks.empty() && media.mass_storage_devices.empty() && !is_confident) return {};
|
||||
|
||||
// As there is at least one usable media image, wave it through.
|
||||
Analyser::Static::TargetList targets;
|
||||
@ -24,7 +29,7 @@ Analyser::Static::TargetList Analyser::Static::Macintosh::GetTargets(const Media
|
||||
if(media.mass_storage_devices.empty()) {
|
||||
bool has_800kb_disks = false;
|
||||
for(const auto &disk: media.disks) {
|
||||
has_800kb_disks |= disk->get_head_count() > 1;
|
||||
has_800kb_disks |= disk->head_count() > 1;
|
||||
}
|
||||
|
||||
if(!has_800kb_disks) {
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::Macintosh {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Enum.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
|
||||
namespace Analyser::Static::Macintosh {
|
||||
|
||||
@ -18,12 +18,13 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
ReflectableEnum(Model, Mac128k, Mac512k, Mac512ke, MacPlus);
|
||||
Model model = Model::MacPlus;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::Macintosh) {
|
||||
// Boilerplate for declaring fields and potential values.
|
||||
if(needs_declare()) {
|
||||
DeclareField(model);
|
||||
AnnounceEnum(Model);
|
||||
}
|
||||
Target() : Analyser::Static::Target(Machine::Macintosh) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<Target>;
|
||||
void declare_fields() {
|
||||
DeclareField(model);
|
||||
AnnounceEnum(Model);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11,10 +11,10 @@
|
||||
#include "Tape.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
#include "../Disassembler/6502.hpp"
|
||||
#include "../Disassembler/AddressMapper.hpp"
|
||||
#include "Analyser/Static/Disassembler/6502.hpp"
|
||||
#include "Analyser/Static/Disassembler/AddressMapper.hpp"
|
||||
|
||||
#include "../../../Storage/Disk/Encodings/MFM/Parser.hpp"
|
||||
#include "Storage/Disk/Encodings/MFM/Parser.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
@ -22,12 +22,22 @@ using namespace Analyser::Static::Oric;
|
||||
|
||||
namespace {
|
||||
|
||||
int score(const Analyser::Static::MOS6502::Disassembly &disassembly, const std::set<uint16_t> &rom_functions, const std::set<uint16_t> &variable_locations) {
|
||||
int score(
|
||||
const Analyser::Static::MOS6502::Disassembly &disassembly,
|
||||
const std::set<uint16_t> &rom_functions,
|
||||
const std::set<uint16_t> &variable_locations
|
||||
) {
|
||||
int score = 0;
|
||||
|
||||
for(const auto address : disassembly.outward_calls) score += (rom_functions.find(address) != rom_functions.end()) ? 1 : -1;
|
||||
for(const auto address : disassembly.external_stores) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
|
||||
for(const auto address : disassembly.external_loads) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
|
||||
for(const auto address : disassembly.outward_calls) {
|
||||
score += (rom_functions.find(address) != rom_functions.end()) ? 1 : -1;
|
||||
}
|
||||
for(const auto address : disassembly.external_stores) {
|
||||
score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
|
||||
}
|
||||
for(const auto address : disassembly.external_loads) {
|
||||
score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
@ -35,19 +45,32 @@ int score(const Analyser::Static::MOS6502::Disassembly &disassembly, const std::
|
||||
int basic10_score(const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
const std::set<uint16_t> rom_functions = {
|
||||
0x0228, 0x022b,
|
||||
0xc3ca, 0xc3f8, 0xc448, 0xc47c, 0xc4b5, 0xc4e3, 0xc4e0, 0xc524, 0xc56f, 0xc5a2, 0xc5f8, 0xc60a, 0xc6a5, 0xc6de, 0xc719, 0xc738,
|
||||
0xc773, 0xc824, 0xc832, 0xc841, 0xc8c1, 0xc8fe, 0xc91f, 0xc93f, 0xc941, 0xc91e, 0xc98b, 0xc996, 0xc9b3, 0xc9e0, 0xca0a, 0xca1c,
|
||||
0xca1f, 0xca3e, 0xca61, 0xca78, 0xca98, 0xcad2, 0xcb61, 0xcb9f, 0xcc59, 0xcbed, 0xcc0a, 0xcc8c, 0xcc8f, 0xccba, 0xccc9, 0xccfd,
|
||||
0xce0c, 0xce77, 0xce8b, 0xcfac, 0xcf74, 0xd03c, 0xd059, 0xcff0, 0xd087, 0xd0f2, 0xd0fc, 0xd361, 0xd3eb, 0xd47e, 0xd4a6, 0xd401,
|
||||
0xd593, 0xd5a3, 0xd4fa, 0xd595, 0xd730, 0xd767, 0xd816, 0xd82a, 0xd856, 0xd861, 0xd8a6, 0xd8b5, 0xd80a, 0xd867, 0xd938, 0xd894,
|
||||
0xd89d, 0xd8ac, 0xd983, 0xd993, 0xd9b5, 0xd93d, 0xd965, 0xda3f, 0xd9c6, 0xda16, 0xdaab, 0xdada, 0xda6b, 0xdb92, 0xdbb9, 0xdc79,
|
||||
0xdd4d, 0xdda3, 0xddbf, 0xd0d0, 0xde77, 0xdef4, 0xdf0b, 0xdf0f, 0xdf04, 0xdf12, 0xdf31, 0xdf4c, 0xdf8c, 0xdfa5, 0xdfcf, 0xe076,
|
||||
0xe0c1, 0xe22a, 0xe27c, 0xe2a6, 0xe313, 0xe34b, 0xe387, 0xe38e, 0xe3d7, 0xe407, 0xe43b, 0xe46f, 0xe4a8, 0xe4f2, 0xe554, 0xe57d,
|
||||
0xe585, 0xe58c, 0xe594, 0xe5a4, 0xe5ab, 0xe5b6, 0xe5ea, 0xe563, 0xe5c6, 0xe630, 0xe696, 0xe6ba, 0xe6ca, 0xe725, 0xe7aa, 0xe903,
|
||||
0xe7db, 0xe80d, 0xe987, 0xe9d1, 0xe87d, 0xe905, 0xe965, 0xe974, 0xe994, 0xe9a9, 0xe9bb, 0xec45, 0xeccc, 0xedc4, 0xecc7, 0xed01,
|
||||
0xed09, 0xed70, 0xed81, 0xed8f, 0xe0ad, 0xeee8, 0xeef8, 0xebdf, 0xebe2, 0xebe5, 0xebeb, 0xebee, 0xebf4, 0xebf7, 0xebfa, 0xebe8,
|
||||
0xf43c, 0xf4ef, 0xf523, 0xf561, 0xf535, 0xf57b, 0xf5d3, 0xf71a, 0xf73f, 0xf7e4, 0xf7e0, 0xf82f, 0xf88f, 0xf8af, 0xf8b5, 0xf920,
|
||||
0xf967, 0xf960, 0xf9c9, 0xfa14, 0xfa85, 0xfa9b, 0xfab1, 0xfac7, 0xfafa, 0xfb10, 0xfb26, 0xfbb6, 0xfbfe
|
||||
0xc3ca, 0xc3f8, 0xc448, 0xc47c, 0xc4b5, 0xc4e3, 0xc4e0, 0xc524,
|
||||
0xc56f, 0xc5a2, 0xc5f8, 0xc60a, 0xc6a5, 0xc6de, 0xc719, 0xc738,
|
||||
0xc773, 0xc824, 0xc832, 0xc841, 0xc8c1, 0xc8fe, 0xc91f, 0xc93f,
|
||||
0xc941, 0xc91e, 0xc98b, 0xc996, 0xc9b3, 0xc9e0, 0xca0a, 0xca1c,
|
||||
0xca1f, 0xca3e, 0xca61, 0xca78, 0xca98, 0xcad2, 0xcb61, 0xcb9f,
|
||||
0xcc59, 0xcbed, 0xcc0a, 0xcc8c, 0xcc8f, 0xccba, 0xccc9, 0xccfd,
|
||||
0xce0c, 0xce77, 0xce8b, 0xcfac, 0xcf74, 0xd03c, 0xd059, 0xcff0,
|
||||
0xd087, 0xd0f2, 0xd0fc, 0xd361, 0xd3eb, 0xd47e, 0xd4a6, 0xd401,
|
||||
0xd593, 0xd5a3, 0xd4fa, 0xd595, 0xd730, 0xd767, 0xd816, 0xd82a,
|
||||
0xd856, 0xd861, 0xd8a6, 0xd8b5, 0xd80a, 0xd867, 0xd938, 0xd894,
|
||||
0xd89d, 0xd8ac, 0xd983, 0xd993, 0xd9b5, 0xd93d, 0xd965, 0xda3f,
|
||||
0xd9c6, 0xda16, 0xdaab, 0xdada, 0xda6b, 0xdb92, 0xdbb9, 0xdc79,
|
||||
0xdd4d, 0xdda3, 0xddbf, 0xd0d0, 0xde77, 0xdef4, 0xdf0b, 0xdf0f,
|
||||
0xdf04, 0xdf12, 0xdf31, 0xdf4c, 0xdf8c, 0xdfa5, 0xdfcf, 0xe076,
|
||||
0xe0c1, 0xe22a, 0xe27c, 0xe2a6, 0xe313, 0xe34b, 0xe387, 0xe38e,
|
||||
0xe3d7, 0xe407, 0xe43b, 0xe46f, 0xe4a8, 0xe4f2, 0xe554, 0xe57d,
|
||||
0xe585, 0xe58c, 0xe594, 0xe5a4, 0xe5ab, 0xe5b6, 0xe5ea, 0xe563,
|
||||
0xe5c6, 0xe630, 0xe696, 0xe6ba, 0xe6ca, 0xe725, 0xe7aa, 0xe903,
|
||||
0xe7db, 0xe80d, 0xe987, 0xe9d1, 0xe87d, 0xe905, 0xe965, 0xe974,
|
||||
0xe994, 0xe9a9, 0xe9bb, 0xec45, 0xeccc, 0xedc4, 0xecc7, 0xed01,
|
||||
0xed09, 0xed70, 0xed81, 0xed8f, 0xe0ad, 0xeee8, 0xeef8, 0xebdf,
|
||||
0xebe2, 0xebe5, 0xebeb, 0xebee, 0xebf4, 0xebf7, 0xebfa, 0xebe8,
|
||||
0xf43c, 0xf4ef, 0xf523, 0xf561, 0xf535, 0xf57b, 0xf5d3, 0xf71a,
|
||||
0xf73f, 0xf7e4, 0xf7e0, 0xf82f, 0xf88f, 0xf8af, 0xf8b5, 0xf920,
|
||||
0xf967, 0xf960, 0xf9c9, 0xfa14, 0xfa85, 0xfa9b, 0xfab1, 0xfac7,
|
||||
0xfafa, 0xfb10, 0xfb26, 0xfbb6, 0xfbfe
|
||||
};
|
||||
const std::set<uint16_t> variable_locations = {
|
||||
0x0228, 0x0229, 0x022a, 0x022b, 0x022c, 0x022d, 0x0230
|
||||
@ -59,19 +82,32 @@ int basic10_score(const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
int basic11_score(const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
const std::set<uint16_t> rom_functions = {
|
||||
0x0238, 0x023b, 0x023e, 0x0241, 0x0244, 0x0247,
|
||||
0xc3c6, 0xc3f4, 0xc444, 0xc47c, 0xc4a8, 0xc4d3, 0xc4e0, 0xc524, 0xc55f, 0xc592, 0xc5e8, 0xc5fa, 0xc692, 0xc6b3, 0xc6ee, 0xc70d,
|
||||
0xc748, 0xc7fd, 0xc809, 0xc816, 0xc82f, 0xc855, 0xc8c1, 0xc915, 0xc952, 0xc971, 0xc973, 0xc9a0, 0xc9bd, 0xc9c8, 0xc9e5, 0xca12,
|
||||
0xca3c, 0xca4e, 0xca51, 0xca70, 0xca99, 0xcac2, 0xcae2, 0xcb1c, 0xcbab, 0xcbf0, 0xcc59, 0xccb0, 0xccce, 0xcd16, 0xcd19, 0xcd46,
|
||||
0xcd55, 0xcd89, 0xce98, 0xcf03, 0xcf17, 0xcfac, 0xd000, 0xd03c, 0xd059, 0xd07c, 0xd113, 0xd17e, 0xd188, 0xd361, 0xd3eb, 0xd47e,
|
||||
0xd4a6, 0xd4ba, 0xd593, 0xd5a3, 0xd5b5, 0xd650, 0xd730, 0xd767, 0xd816, 0xd82a, 0xd856, 0xd861, 0xd8a6, 0xd8b5, 0xd8c5, 0xd922,
|
||||
0xd938, 0xd94f, 0xd958, 0xd967, 0xd983, 0xd993, 0xd9b5, 0xd9de, 0xda0c, 0xda3f, 0xda51, 0xdaa1, 0xdaab, 0xdada, 0xdaf6, 0xdb92,
|
||||
0xdbb9, 0xdcaf, 0xdd51, 0xdda7, 0xddc3, 0xddd4, 0xde77, 0xdef4, 0xdf0b, 0xdf0f, 0xdf13, 0xdf21, 0xdf49, 0xdf4c, 0xdf8c, 0xdfbd,
|
||||
0xdfe7, 0xe076, 0xe0c5, 0xe22e, 0xe27c, 0xe2aa, 0xe313, 0xe34f, 0xe38b, 0xe392, 0xe3db, 0xe407, 0xe43f, 0xe46f, 0xe4ac, 0xe4e0,
|
||||
0xe4f2, 0xe56c, 0xe57d, 0xe585, 0xe58c, 0xe594, 0xe5a4, 0xe5ab, 0xe5b6, 0xe5ea, 0xe5f5, 0xe607, 0xe65e, 0xe6c9, 0xe735, 0xe75a,
|
||||
0xe76a, 0xe7b2, 0xe85b, 0xe903, 0xe909, 0xe946, 0xe987, 0xe9d1, 0xeaf0, 0xeb78, 0xebce, 0xebe7, 0xec0c, 0xec21, 0xec33, 0xec45,
|
||||
0xeccc, 0xedc4, 0xede0, 0xee1a, 0xee22, 0xee8c, 0xee9d, 0xeeab, 0xeec9, 0xeee8, 0xeef8, 0xf0c8, 0xf0fd, 0xf110, 0xf11d, 0xf12d,
|
||||
0xf204, 0xf210, 0xf268, 0xf37f, 0xf495, 0xf4ef, 0xf523, 0xf561, 0xf590, 0xf5c1, 0xf602, 0xf71a, 0xf77c, 0xf7e4, 0xf816, 0xf865,
|
||||
0xf88f, 0xf8af, 0xf8b5, 0xf920, 0xf967, 0xf9aa, 0xf9c9, 0xfa14, 0xfa9f, 0xfab5, 0xfacb, 0xfae1, 0xfb14, 0xfb2a, 0xfb40, 0xfbd0,
|
||||
0xc3c6, 0xc3f4, 0xc444, 0xc47c, 0xc4a8, 0xc4d3, 0xc4e0, 0xc524,
|
||||
0xc55f, 0xc592, 0xc5e8, 0xc5fa, 0xc692, 0xc6b3, 0xc6ee, 0xc70d,
|
||||
0xc748, 0xc7fd, 0xc809, 0xc816, 0xc82f, 0xc855, 0xc8c1, 0xc915,
|
||||
0xc952, 0xc971, 0xc973, 0xc9a0, 0xc9bd, 0xc9c8, 0xc9e5, 0xca12,
|
||||
0xca3c, 0xca4e, 0xca51, 0xca70, 0xca99, 0xcac2, 0xcae2, 0xcb1c,
|
||||
0xcbab, 0xcbf0, 0xcc59, 0xccb0, 0xccce, 0xcd16, 0xcd19, 0xcd46,
|
||||
0xcd55, 0xcd89, 0xce98, 0xcf03, 0xcf17, 0xcfac, 0xd000, 0xd03c,
|
||||
0xd059, 0xd07c, 0xd113, 0xd17e, 0xd188, 0xd361, 0xd3eb, 0xd47e,
|
||||
0xd4a6, 0xd4ba, 0xd593, 0xd5a3, 0xd5b5, 0xd650, 0xd730, 0xd767,
|
||||
0xd816, 0xd82a, 0xd856, 0xd861, 0xd8a6, 0xd8b5, 0xd8c5, 0xd922,
|
||||
0xd938, 0xd94f, 0xd958, 0xd967, 0xd983, 0xd993, 0xd9b5, 0xd9de,
|
||||
0xda0c, 0xda3f, 0xda51, 0xdaa1, 0xdaab, 0xdada, 0xdaf6, 0xdb92,
|
||||
0xdbb9, 0xdcaf, 0xdd51, 0xdda7, 0xddc3, 0xddd4, 0xde77, 0xdef4,
|
||||
0xdf0b, 0xdf0f, 0xdf13, 0xdf21, 0xdf49, 0xdf4c, 0xdf8c, 0xdfbd,
|
||||
0xdfe7, 0xe076, 0xe0c5, 0xe22e, 0xe27c, 0xe2aa, 0xe313, 0xe34f,
|
||||
0xe38b, 0xe392, 0xe3db, 0xe407, 0xe43f, 0xe46f, 0xe4ac, 0xe4e0,
|
||||
0xe4f2, 0xe56c, 0xe57d, 0xe585, 0xe58c, 0xe594, 0xe5a4, 0xe5ab,
|
||||
0xe5b6, 0xe5ea, 0xe5f5, 0xe607, 0xe65e, 0xe6c9, 0xe735, 0xe75a,
|
||||
0xe76a, 0xe7b2, 0xe85b, 0xe903, 0xe909, 0xe946, 0xe987, 0xe9d1,
|
||||
0xeaf0, 0xeb78, 0xebce, 0xebe7, 0xec0c, 0xec21, 0xec33, 0xec45,
|
||||
0xeccc, 0xedc4, 0xede0, 0xee1a, 0xee22, 0xee8c, 0xee9d, 0xeeab,
|
||||
0xeec9, 0xeee8, 0xeef8, 0xf0c8, 0xf0fd, 0xf110, 0xf11d, 0xf12d,
|
||||
0xf204, 0xf210, 0xf268, 0xf37f, 0xf495, 0xf4ef, 0xf523, 0xf561,
|
||||
0xf590, 0xf5c1, 0xf602, 0xf71a, 0xf77c, 0xf7e4, 0xf816, 0xf865,
|
||||
0xf88f, 0xf8af, 0xf8b5, 0xf920, 0xf967, 0xf9aa, 0xf9c9, 0xfa14,
|
||||
0xfa9f, 0xfab5, 0xfacb, 0xfae1, 0xfb14, 0xfb2a, 0xfb40, 0xfbd0,
|
||||
0xfc18
|
||||
};
|
||||
const std::set<uint16_t> variable_locations = {
|
||||
@ -102,7 +138,7 @@ bool is_microdisc(Storage::Encodings::MFM::Parser &parser) {
|
||||
return !std::memcmp(signature, first_sample.data(), sizeof(signature));
|
||||
}
|
||||
|
||||
bool is_400_loader(Storage::Encodings::MFM::Parser &parser, uint16_t range_start, uint16_t range_end) {
|
||||
bool is_400_loader(Storage::Encodings::MFM::Parser &parser, const uint16_t range_start, const uint16_t range_end) {
|
||||
/*
|
||||
Both the Jasmin and BD-DOS boot sectors are sector 1 of track 0 and are loaded at $400;
|
||||
use disassembly to test for likely matches.
|
||||
@ -120,8 +156,8 @@ bool is_400_loader(Storage::Encodings::MFM::Parser &parser, uint16_t range_start
|
||||
}
|
||||
|
||||
// Grab a disassembly.
|
||||
const auto disassembly =
|
||||
Analyser::Static::MOS6502::Disassemble(first_sample, Analyser::Static::Disassembler::OffsetMapper(0x400), {0x400});
|
||||
const auto disassembly = Analyser::Static::MOS6502::Disassemble(
|
||||
first_sample, Analyser::Static::Disassembler::OffsetMapper(0x400), {0x400});
|
||||
|
||||
// Check for references to the Jasmin registers.
|
||||
int register_hits = 0;
|
||||
@ -145,7 +181,12 @@ bool is_bd500(Storage::Encodings::MFM::Parser &parser) {
|
||||
|
||||
}
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
bool
|
||||
) {
|
||||
auto target = std::make_unique<Target>();
|
||||
target->confidence = 0.5;
|
||||
|
||||
@ -153,14 +194,18 @@ Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &med
|
||||
int basic11_votes = 0;
|
||||
|
||||
for(auto &tape : media.tapes) {
|
||||
std::vector<File> tape_files = GetFiles(tape);
|
||||
tape->reset();
|
||||
auto serialiser = tape->serialiser();
|
||||
std::vector<File> tape_files = GetFiles(*serialiser);
|
||||
if(!tape_files.empty()) {
|
||||
for(const auto &file : tape_files) {
|
||||
if(file.data_type == File::MachineCode) {
|
||||
std::vector<uint16_t> entry_points = {file.starting_address};
|
||||
const Analyser::Static::MOS6502::Disassembly disassembly =
|
||||
Analyser::Static::MOS6502::Disassemble(file.data, Analyser::Static::Disassembler::OffsetMapper(file.starting_address), entry_points);
|
||||
Analyser::Static::MOS6502::Disassemble(
|
||||
file.data,
|
||||
Analyser::Static::Disassembler::OffsetMapper(file.starting_address),
|
||||
entry_points
|
||||
);
|
||||
|
||||
if(basic10_score(disassembly) > basic11_score(disassembly)) ++basic10_votes; else ++basic11_votes;
|
||||
}
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::Oric {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -7,61 +7,61 @@
|
||||
//
|
||||
|
||||
#include "Tape.hpp"
|
||||
#include "../../../Storage/Tape/Parsers/Oric.hpp"
|
||||
#include "Storage/Tape/Parsers/Oric.hpp"
|
||||
|
||||
using namespace Analyser::Static::Oric;
|
||||
|
||||
std::vector<File> Analyser::Static::Oric::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
std::vector<File> Analyser::Static::Oric::GetFiles(Storage::Tape::TapeSerialiser &serialiser) {
|
||||
std::vector<File> files;
|
||||
Storage::Tape::Oric::Parser parser;
|
||||
|
||||
while(!tape->is_at_end()) {
|
||||
while(!serialiser.is_at_end()) {
|
||||
// sync to next lead-in, check that it's one of three 0x16s
|
||||
bool is_fast = parser.sync_and_get_encoding_speed(tape);
|
||||
bool is_fast = parser.sync_and_get_encoding_speed(serialiser);
|
||||
int next_bytes[2];
|
||||
next_bytes[0] = parser.get_next_byte(tape, is_fast);
|
||||
next_bytes[1] = parser.get_next_byte(tape, is_fast);
|
||||
next_bytes[0] = parser.get_next_byte(serialiser, is_fast);
|
||||
next_bytes[1] = parser.get_next_byte(serialiser, is_fast);
|
||||
|
||||
if(next_bytes[0] != 0x16 || next_bytes[1] != 0x16) continue;
|
||||
|
||||
// get the first byte that isn't a 0x16, check it was a 0x24
|
||||
int byte = 0x16;
|
||||
while(!tape->is_at_end() && byte == 0x16) {
|
||||
byte = parser.get_next_byte(tape, is_fast);
|
||||
while(!serialiser.is_at_end() && byte == 0x16) {
|
||||
byte = parser.get_next_byte(serialiser, is_fast);
|
||||
}
|
||||
if(byte != 0x24) continue;
|
||||
|
||||
// skip two empty bytes
|
||||
parser.get_next_byte(tape, is_fast);
|
||||
parser.get_next_byte(tape, is_fast);
|
||||
parser.get_next_byte(serialiser, is_fast);
|
||||
parser.get_next_byte(serialiser, is_fast);
|
||||
|
||||
// get data and launch types
|
||||
File new_file;
|
||||
switch(parser.get_next_byte(tape, is_fast)) {
|
||||
switch(parser.get_next_byte(serialiser, is_fast)) {
|
||||
case 0x00: new_file.data_type = File::ProgramType::BASIC; break;
|
||||
case 0x80: new_file.data_type = File::ProgramType::MachineCode; break;
|
||||
default: new_file.data_type = File::ProgramType::None; break;
|
||||
}
|
||||
switch(parser.get_next_byte(tape, is_fast)) {
|
||||
switch(parser.get_next_byte(serialiser, is_fast)) {
|
||||
case 0x80: new_file.launch_type = File::ProgramType::BASIC; break;
|
||||
case 0xc7: new_file.launch_type = File::ProgramType::MachineCode; break;
|
||||
default: new_file.launch_type = File::ProgramType::None; break;
|
||||
}
|
||||
|
||||
// read end and start addresses
|
||||
new_file.ending_address = uint16_t(parser.get_next_byte(tape, is_fast) << 8);
|
||||
new_file.ending_address |= uint16_t(parser.get_next_byte(tape, is_fast));
|
||||
new_file.starting_address = uint16_t(parser.get_next_byte(tape, is_fast) << 8);
|
||||
new_file.starting_address |= uint16_t(parser.get_next_byte(tape, is_fast));
|
||||
new_file.ending_address = uint16_t(parser.get_next_byte(serialiser, is_fast) << 8);
|
||||
new_file.ending_address |= uint16_t(parser.get_next_byte(serialiser, is_fast));
|
||||
new_file.starting_address = uint16_t(parser.get_next_byte(serialiser, is_fast) << 8);
|
||||
new_file.starting_address |= uint16_t(parser.get_next_byte(serialiser, is_fast));
|
||||
|
||||
// skip an empty byte
|
||||
parser.get_next_byte(tape, is_fast);
|
||||
parser.get_next_byte(serialiser, is_fast);
|
||||
|
||||
// read file name, up to 16 characters and null terminated
|
||||
char file_name[17];
|
||||
int name_pos = 0;
|
||||
while(name_pos < 16) {
|
||||
file_name[name_pos] = char(parser.get_next_byte(tape, is_fast));
|
||||
file_name[name_pos] = char(parser.get_next_byte(serialiser, is_fast));
|
||||
if(!file_name[name_pos]) break;
|
||||
name_pos++;
|
||||
}
|
||||
@ -72,11 +72,11 @@ std::vector<File> Analyser::Static::Oric::GetFiles(const std::shared_ptr<Storage
|
||||
std::size_t body_length = new_file.ending_address - new_file.starting_address + 1;
|
||||
new_file.data.reserve(body_length);
|
||||
for(std::size_t c = 0; c < body_length; c++) {
|
||||
new_file.data.push_back(uint8_t(parser.get_next_byte(tape, is_fast)));
|
||||
new_file.data.push_back(uint8_t(parser.get_next_byte(serialiser, is_fast)));
|
||||
}
|
||||
|
||||
// only one validation check: was there enough tape?
|
||||
if(!tape->is_at_end()) {
|
||||
if(!serialiser.is_at_end()) {
|
||||
files.push_back(new_file);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Storage/Tape/Tape.hpp"
|
||||
#include "Storage/Tape/Tape.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -28,6 +28,6 @@ struct File {
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||
std::vector<File> GetFiles(Storage::Tape::TapeSerialiser &);
|
||||
|
||||
}
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Enum.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::Oric {
|
||||
@ -41,15 +41,17 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
std::string loading_command;
|
||||
bool should_start_jasmin = false;
|
||||
|
||||
Target(): Analyser::Static::Target(Machine::Oric) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(rom);
|
||||
DeclareField(disk_interface);
|
||||
DeclareField(processor);
|
||||
AnnounceEnum(ROM);
|
||||
AnnounceEnum(DiskInterface);
|
||||
AnnounceEnum(Processor);
|
||||
}
|
||||
Target(): Analyser::Static::Target(Machine::Oric) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<Target>;
|
||||
void declare_fields() {
|
||||
DeclareField(rom);
|
||||
DeclareField(disk_interface);
|
||||
DeclareField(processor);
|
||||
AnnounceEnum(ROM);
|
||||
AnnounceEnum(DiskInterface);
|
||||
AnnounceEnum(Processor);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9,7 +9,12 @@
|
||||
#include "StaticAnalyser.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::PCCompatible::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::PCCompatible::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
bool
|
||||
) {
|
||||
// This analyser can comprehend disks only.
|
||||
if(media.disks.empty()) return {};
|
||||
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/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);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,29 +8,42 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
|
||||
namespace Analyser::Static::PCCompatible {
|
||||
|
||||
ReflectableEnum(Model,
|
||||
XT,
|
||||
TurboXT,
|
||||
AT
|
||||
);
|
||||
|
||||
constexpr bool is_xt(const Model model) {
|
||||
return model <= Model::TurboXT;
|
||||
}
|
||||
|
||||
constexpr bool is_at(const Model model) {
|
||||
return model >= Model::AT;
|
||||
}
|
||||
|
||||
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
ReflectableEnum(VideoAdaptor,
|
||||
MDA,
|
||||
CGA);
|
||||
CGA,
|
||||
);
|
||||
VideoAdaptor adaptor = VideoAdaptor::CGA;
|
||||
Model model = Model::TurboXT;
|
||||
|
||||
ReflectableEnum(Speed,
|
||||
ApproximatelyOriginal,
|
||||
Fast);
|
||||
Speed speed = Speed::Fast;
|
||||
Target() : Analyser::Static::Target(Machine::PCCompatible) {}
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::PCCompatible) {
|
||||
if(needs_declare()) {
|
||||
AnnounceEnum(VideoAdaptor);
|
||||
AnnounceEnum(Speed);
|
||||
DeclareField(adaptor);
|
||||
DeclareField(speed);
|
||||
}
|
||||
private:
|
||||
friend Reflection::StructImpl<Target>;
|
||||
void declare_fields() {
|
||||
AnnounceEnum(VideoAdaptor);
|
||||
AnnounceEnum(Model);
|
||||
DeclareField(adaptor);
|
||||
DeclareField(model);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -13,7 +13,12 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Sega::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::Sega::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &file_name,
|
||||
TargetPlatform::IntType,
|
||||
bool
|
||||
) {
|
||||
if(media.cartridges.empty())
|
||||
return {};
|
||||
|
||||
@ -54,7 +59,8 @@ Analyser::Static::TargetList Analyser::Static::Sega::GetTargets(const Media &med
|
||||
if(lowercase_name.find("(jp)") == std::string::npos) {
|
||||
target->region =
|
||||
(lowercase_name.find("(us)") == std::string::npos &&
|
||||
lowercase_name.find("(ntsc)") == std::string::npos) ? Target::Region::Europe : Target::Region::USA;
|
||||
lowercase_name.find("(ntsc)") == std::string::npos) ?
|
||||
Target::Region::Europe : Target::Region::USA;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
@ -63,9 +69,9 @@ Analyser::Static::TargetList Analyser::Static::Sega::GetTargets(const Media &med
|
||||
// If one is found, set the paging scheme appropriately.
|
||||
const uint16_t inverse_checksum = uint16_t(0x10000 - (data[0x7fe6] | (data[0x7fe7] << 8)));
|
||||
if(
|
||||
data[0x7fe3] >= 0x87 && data[0x7fe3] < 0x96 && // i.e. game is dated between 1987 and 1996
|
||||
data[0x7fe3] >= 0x87 && data[0x7fe3] < 0x96 && // i.e. game is dated between 1987 and 1996
|
||||
(inverse_checksum&0xff) == data[0x7fe8] &&
|
||||
(inverse_checksum >> 8) == data[0x7fe9] && // i.e. the standard checksum appears to be present
|
||||
(inverse_checksum >> 8) == data[0x7fe9] && // i.e. the standard checksum appears to be present.
|
||||
!data[0x7fea] && !data[0x7feb] && !data[0x7fec] && !data[0x7fed] && !data[0x7fee] && !data[0x7fef]
|
||||
) {
|
||||
target->paging_scheme = Target::PagingScheme::Codemasters;
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::Sega {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Enum.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
|
||||
namespace Analyser::Static::Sega {
|
||||
|
||||
@ -37,15 +37,17 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
Region region = Region::Japan;
|
||||
PagingScheme paging_scheme = PagingScheme::Sega;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::MasterSystem) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(region);
|
||||
AnnounceEnum(Region);
|
||||
}
|
||||
Target() : Analyser::Static::Target(Machine::MasterSystem) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<Target>;
|
||||
void declare_fields() {
|
||||
DeclareField(region);
|
||||
AnnounceEnum(Region);
|
||||
}
|
||||
};
|
||||
|
||||
constexpr bool is_master_system(Analyser::Static::Sega::Target::Model model) {
|
||||
constexpr bool is_master_system(const Analyser::Static::Sega::Target::Model model) {
|
||||
return model >= Analyser::Static::Sega::Target::Model::MasterSystem;
|
||||
}
|
||||
|
||||
|
@ -15,76 +15,76 @@
|
||||
#include <iterator>
|
||||
|
||||
// Analysers
|
||||
#include "Acorn/StaticAnalyser.hpp"
|
||||
#include "Amiga/StaticAnalyser.hpp"
|
||||
#include "AmstradCPC/StaticAnalyser.hpp"
|
||||
#include "AppleII/StaticAnalyser.hpp"
|
||||
#include "AppleIIgs/StaticAnalyser.hpp"
|
||||
#include "Atari2600/StaticAnalyser.hpp"
|
||||
#include "AtariST/StaticAnalyser.hpp"
|
||||
#include "Coleco/StaticAnalyser.hpp"
|
||||
#include "Commodore/StaticAnalyser.hpp"
|
||||
#include "DiskII/StaticAnalyser.hpp"
|
||||
#include "Enterprise/StaticAnalyser.hpp"
|
||||
#include "FAT12/StaticAnalyser.hpp"
|
||||
#include "Macintosh/StaticAnalyser.hpp"
|
||||
#include "MSX/StaticAnalyser.hpp"
|
||||
#include "Oric/StaticAnalyser.hpp"
|
||||
#include "PCCompatible/StaticAnalyser.hpp"
|
||||
#include "Sega/StaticAnalyser.hpp"
|
||||
#include "ZX8081/StaticAnalyser.hpp"
|
||||
#include "ZXSpectrum/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/Acorn/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/Amiga/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/AmstradCPC/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/AppleII/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/AppleIIgs/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/Atari2600/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/AtariST/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/Coleco/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/Commodore/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/DiskII/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/Enterprise/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/FAT12/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/Macintosh/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/MSX/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/Oric/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/PCCompatible/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/Sega/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/ZX8081/StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/ZXSpectrum/StaticAnalyser.hpp"
|
||||
|
||||
// Cartridges
|
||||
#include "../../Storage/Cartridge/Formats/BinaryDump.hpp"
|
||||
#include "../../Storage/Cartridge/Formats/PRG.hpp"
|
||||
#include "Storage/Cartridge/Formats/BinaryDump.hpp"
|
||||
#include "Storage/Cartridge/Formats/PRG.hpp"
|
||||
|
||||
// Disks
|
||||
#include "../../Storage/Disk/DiskImage/Formats/2MG.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/AcornADF.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/AmigaADF.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/AppleDSK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/CPCDSK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/D64.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/G64.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/DMK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/FAT12.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/HFE.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/MSA.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/NIB.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/STX.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/WOZ.hpp"
|
||||
#include "Storage/Disk/DiskImage/Formats/2MG.hpp"
|
||||
#include "Storage/Disk/DiskImage/Formats/AcornADF.hpp"
|
||||
#include "Storage/Disk/DiskImage/Formats/AmigaADF.hpp"
|
||||
#include "Storage/Disk/DiskImage/Formats/AppleDSK.hpp"
|
||||
#include "Storage/Disk/DiskImage/Formats/CPCDSK.hpp"
|
||||
#include "Storage/Disk/DiskImage/Formats/D64.hpp"
|
||||
#include "Storage/Disk/DiskImage/Formats/G64.hpp"
|
||||
#include "Storage/Disk/DiskImage/Formats/DMK.hpp"
|
||||
#include "Storage/Disk/DiskImage/Formats/FAT12.hpp"
|
||||
#include "Storage/Disk/DiskImage/Formats/HFE.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/MSA.hpp"
|
||||
#include "Storage/Disk/DiskImage/Formats/NIB.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/STX.hpp"
|
||||
#include "Storage/Disk/DiskImage/Formats/WOZ.hpp"
|
||||
|
||||
// Mass Storage Devices (i.e. usually, hard disks)
|
||||
#include "../../Storage/MassStorage/Formats/DAT.hpp"
|
||||
#include "../../Storage/MassStorage/Formats/DSK.hpp"
|
||||
#include "../../Storage/MassStorage/Formats/HDV.hpp"
|
||||
#include "../../Storage/MassStorage/Formats/HFV.hpp"
|
||||
#include "Storage/MassStorage/Formats/DAT.hpp"
|
||||
#include "Storage/MassStorage/Formats/DSK.hpp"
|
||||
#include "Storage/MassStorage/Formats/HDV.hpp"
|
||||
#include "Storage/MassStorage/Formats/HFV.hpp"
|
||||
|
||||
// State Snapshots
|
||||
#include "../../Storage/State/SNA.hpp"
|
||||
#include "../../Storage/State/SZX.hpp"
|
||||
#include "../../Storage/State/Z80.hpp"
|
||||
#include "Storage/State/SNA.hpp"
|
||||
#include "Storage/State/SZX.hpp"
|
||||
#include "Storage/State/Z80.hpp"
|
||||
|
||||
// Tapes
|
||||
#include "../../Storage/Tape/Formats/CAS.hpp"
|
||||
#include "../../Storage/Tape/Formats/CommodoreTAP.hpp"
|
||||
#include "../../Storage/Tape/Formats/CSW.hpp"
|
||||
#include "../../Storage/Tape/Formats/OricTAP.hpp"
|
||||
#include "../../Storage/Tape/Formats/TapePRG.hpp"
|
||||
#include "../../Storage/Tape/Formats/TapeUEF.hpp"
|
||||
#include "../../Storage/Tape/Formats/TZX.hpp"
|
||||
#include "../../Storage/Tape/Formats/ZX80O81P.hpp"
|
||||
#include "../../Storage/Tape/Formats/ZXSpectrumTAP.hpp"
|
||||
#include "Storage/Tape/Formats/CAS.hpp"
|
||||
#include "Storage/Tape/Formats/CommodoreTAP.hpp"
|
||||
#include "Storage/Tape/Formats/CSW.hpp"
|
||||
#include "Storage/Tape/Formats/OricTAP.hpp"
|
||||
#include "Storage/Tape/Formats/TapePRG.hpp"
|
||||
#include "Storage/Tape/Formats/TapeUEF.hpp"
|
||||
#include "Storage/Tape/Formats/TZX.hpp"
|
||||
#include "Storage/Tape/Formats/ZX80O81P.hpp"
|
||||
#include "Storage/Tape/Formats/ZXSpectrumTAP.hpp"
|
||||
|
||||
// Target Platform Types
|
||||
#include "../../Storage/TargetPlatforms.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
|
||||
template<class> inline constexpr bool always_false_v = false;
|
||||
|
||||
@ -104,14 +104,14 @@ std::string get_extension(const std::string &name) {
|
||||
}
|
||||
|
||||
class MediaAccumulator {
|
||||
public:
|
||||
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) {
|
||||
void insert(const 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>) {
|
||||
@ -127,20 +127,23 @@ class MediaAccumulator {
|
||||
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();
|
||||
TargetPlatform::Distinguisher *const distinguisher =
|
||||
dynamic_cast<TargetPlatform::Distinguisher *>(instance.get());
|
||||
if(distinguisher) {
|
||||
was_distinguished = true;
|
||||
potential_platforms_ &= distinguisher->target_platforms();
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
void insert(const 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) {
|
||||
void try_insert(const TargetPlatform::IntType platforms, Args &&... args) {
|
||||
try {
|
||||
insert<InstanceT>(platforms, std::forward<Args>(args)...);
|
||||
} catch(...) {}
|
||||
@ -149,22 +152,23 @@ class MediaAccumulator {
|
||||
/// 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) {
|
||||
void try_standard(const TargetPlatform::IntType platforms, const char *extension) {
|
||||
if(name_matches(extension)) {
|
||||
try_insert<InstanceT>(platforms, file_name_);
|
||||
}
|
||||
}
|
||||
|
||||
bool name_matches(const char *extension) {
|
||||
bool name_matches(const char *const extension) {
|
||||
return extension_ == extension;
|
||||
}
|
||||
|
||||
Media media;
|
||||
bool was_distinguished = false;
|
||||
|
||||
private:
|
||||
const std::string &file_name_;
|
||||
TargetPlatform::IntType &potential_platforms_;
|
||||
const std::string extension_;
|
||||
private:
|
||||
const std::string &file_name_;
|
||||
TargetPlatform::IntType &potential_platforms_;
|
||||
const std::string extension_;
|
||||
};
|
||||
|
||||
}
|
||||
@ -209,7 +213,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
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<Disk::DiskImageHolder<Disk::D64>>(TargetPlatform::Commodore8bit, "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");
|
||||
@ -223,11 +227,12 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
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<Disk::DiskImageHolder<Disk::G64>>(TargetPlatform::Commodore8bit, "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,
|
||||
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");
|
||||
@ -264,13 +269,14 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
|
||||
accumulator.try_standard<Tape::ZX80O81P>(TargetPlatform::ZX8081, "p81");
|
||||
|
||||
static constexpr auto PRGTargets = TargetPlatform::Vic20; //Commodore8bit; // Disabled until analysis improves.
|
||||
if(accumulator.name_matches("prg")) {
|
||||
// Try instantiating as a ROM; failing that accept as a tape.
|
||||
try {
|
||||
accumulator.insert<Cartridge::PRG>(TargetPlatform::Commodore, file_name);
|
||||
accumulator.insert<Cartridge::PRG>(PRGTargets, file_name);
|
||||
} catch(...) {
|
||||
try {
|
||||
accumulator.insert<Tape::PRG>(TargetPlatform::Commodore, file_name);
|
||||
accumulator.insert<Tape::PRG>(PRGTargets, file_name);
|
||||
} catch(...) {}
|
||||
}
|
||||
}
|
||||
@ -285,7 +291,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
accumulator.try_standard<Disk::DiskImageHolder<Disk::FAT12>>(TargetPlatform::AtariST, "st");
|
||||
accumulator.try_standard<Disk::DiskImageHolder<Disk::STX>>(TargetPlatform::AtariST, "stx");
|
||||
|
||||
accumulator.try_standard<Tape::CommodoreTAP>(TargetPlatform::Commodore, "tap");
|
||||
accumulator.try_standard<Tape::CommodoreTAP>(TargetPlatform::Commodore8bit, "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");
|
||||
@ -334,14 +340,30 @@ TargetList Analyser::Static::GetTargets(const std::string &file_name) {
|
||||
TargetPlatform::IntType potential_platforms = 0;
|
||||
Media media = GetMediaAndPlatforms(file_name, potential_platforms);
|
||||
|
||||
// TODO: std::popcount here.
|
||||
int total_options = 0;
|
||||
TargetPlatform::IntType mask = 1;
|
||||
while(mask) {
|
||||
total_options += bool(potential_platforms & mask);
|
||||
mask <<= 1;
|
||||
}
|
||||
const bool is_confident = total_options == 1;
|
||||
// i.e. This analyser `is_confident` if file analysis suggested only one potential target platform.
|
||||
// The machine-specific static analyser will still run in case it can provide meaningful annotations on
|
||||
// loading command, machine configuration, etc, but the flag will be passed onwards to mean "don't reject this".
|
||||
|
||||
// Hand off to platform-specific determination of whether these
|
||||
// things are actually compatible and, if so, how to load them.
|
||||
const auto append = [&](TargetPlatform::IntType platform, auto evaluator) {
|
||||
if(!(potential_platforms & platform)) {
|
||||
return;
|
||||
}
|
||||
auto new_targets = evaluator(media, file_name, potential_platforms);
|
||||
std::move(new_targets.begin(), new_targets.end(), std::back_inserter(targets));
|
||||
auto new_targets = evaluator(media, file_name, potential_platforms, is_confident);
|
||||
targets.insert(
|
||||
targets.end(),
|
||||
std::make_move_iterator(new_targets.begin()),
|
||||
std::make_move_iterator(new_targets.end())
|
||||
);
|
||||
};
|
||||
|
||||
append(TargetPlatform::Acorn, Acorn::GetTargets);
|
||||
@ -352,7 +374,7 @@ TargetList Analyser::Static::GetTargets(const std::string &file_name) {
|
||||
append(TargetPlatform::Atari2600, Atari2600::GetTargets);
|
||||
append(TargetPlatform::AtariST, AtariST::GetTargets);
|
||||
append(TargetPlatform::Coleco, Coleco::GetTargets);
|
||||
append(TargetPlatform::Commodore, Commodore::GetTargets);
|
||||
append(TargetPlatform::Commodore8bit, Commodore::GetTargets);
|
||||
append(TargetPlatform::DiskII, DiskII::GetTargets);
|
||||
append(TargetPlatform::Enterprise, Enterprise::GetTargets);
|
||||
append(TargetPlatform::FAT12, FAT12::GetTargets);
|
||||
@ -364,13 +386,6 @@ TargetList Analyser::Static::GetTargets(const std::string &file_name) {
|
||||
append(TargetPlatform::ZX8081, ZX8081::GetTargets);
|
||||
append(TargetPlatform::ZXSpectrum, ZXSpectrum::GetTargets);
|
||||
|
||||
// Reset any tapes to their initial position.
|
||||
for(const auto &target : targets) {
|
||||
for(auto &tape : target->media.tapes) {
|
||||
tape->reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by initial confidence. Use a stable sort in case any of the machine-specific analysers
|
||||
// picked their insertion order carefully.
|
||||
std::stable_sort(targets.begin(), targets.end(),
|
||||
|
@ -8,13 +8,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Machines.hpp"
|
||||
#include "Analyser/Machines.hpp"
|
||||
|
||||
#include "../../Storage/Cartridge/Cartridge.hpp"
|
||||
#include "../../Storage/Disk/Disk.hpp"
|
||||
#include "../../Storage/MassStorage/MassStorageDevice.hpp"
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
#include "../../Reflection/Struct.hpp"
|
||||
#include "Storage/Cartridge/Cartridge.hpp"
|
||||
#include "Storage/Disk/Disk.hpp"
|
||||
#include "Storage/MassStorage/MassStorageDevice.hpp"
|
||||
#include "Storage/Tape/Tape.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@ -64,9 +65,9 @@ struct Target {
|
||||
|
||||
Machine machine;
|
||||
Media media;
|
||||
float confidence = 0.0f;
|
||||
float confidence = 0.5f;
|
||||
};
|
||||
typedef std::vector<std::unique_ptr<Target>> TargetList;
|
||||
using TargetList = std::vector<std::unique_ptr<Target>>;
|
||||
|
||||
/*!
|
||||
Attempts, through any available means, to return a list of potential targets for the file with the given name.
|
||||
|
@ -12,27 +12,32 @@
|
||||
#include <vector>
|
||||
|
||||
#include "Target.hpp"
|
||||
#include "../../../Storage/Tape/Parsers/ZX8081.hpp"
|
||||
#include "Storage/Tape/Parsers/ZX8081.hpp"
|
||||
|
||||
static std::vector<Storage::Data::ZX8081::File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
static std::vector<Storage::Data::ZX8081::File> GetFiles(Storage::Tape::TapeSerialiser &serialiser) {
|
||||
std::vector<Storage::Data::ZX8081::File> files;
|
||||
Storage::Tape::ZX8081::Parser parser;
|
||||
|
||||
while(!tape->is_at_end()) {
|
||||
std::shared_ptr<Storage::Data::ZX8081::File> next_file = parser.get_next_file(tape);
|
||||
if(next_file != nullptr) {
|
||||
files.push_back(*next_file);
|
||||
while(!serialiser.is_at_end()) {
|
||||
const auto next_file = parser.get_next_file(serialiser);
|
||||
if(next_file) {
|
||||
files.push_back(std::move(*next_file));
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::ZX8081::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType potential_platforms) {
|
||||
Analyser::Static::TargetList Analyser::Static::ZX8081::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType potential_platforms,
|
||||
bool
|
||||
) {
|
||||
TargetList destination;
|
||||
if(!media.tapes.empty()) {
|
||||
std::vector<Storage::Data::ZX8081::File> files = GetFiles(media.tapes.front());
|
||||
media.tapes.front()->reset();
|
||||
const auto serialiser = media.tapes.front()->serialiser();
|
||||
std::vector<Storage::Data::ZX8081::File> files = GetFiles(*serialiser);
|
||||
if(!files.empty()) {
|
||||
Target *const target = new Target;
|
||||
destination.push_back(std::unique_ptr<::Analyser::Static::Target>(target));
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::ZX8081 {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Enum.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::ZX8081 {
|
||||
@ -27,13 +27,15 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
|
||||
bool ZX80_uses_ZX81_ROM = false;
|
||||
std::string loading_command;
|
||||
|
||||
Target(): Analyser::Static::Target(Machine::ZX8081) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(memory_model);
|
||||
DeclareField(is_ZX81);
|
||||
DeclareField(ZX80_uses_ZX81_ROM);
|
||||
AnnounceEnum(MemoryModel);
|
||||
}
|
||||
Target(): Analyser::Static::Target(Machine::ZX8081) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<Target>;
|
||||
void declare_fields() {
|
||||
DeclareField(memory_model);
|
||||
DeclareField(is_ZX81);
|
||||
DeclareField(ZX80_uses_ZX81_ROM);
|
||||
AnnounceEnum(MemoryModel);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
|
||||
#include "../../../Storage/Disk/Parsers/CPM.hpp"
|
||||
#include "../../../Storage/Disk/Encodings/MFM/Parser.hpp"
|
||||
#include "../../../Storage/Tape/Parsers/Spectrum.hpp"
|
||||
#include "Storage/Disk/Parsers/CPM.hpp"
|
||||
#include "Storage/Disk/Encodings/MFM/Parser.hpp"
|
||||
#include "Storage/Tape/Parsers/Spectrum.hpp"
|
||||
|
||||
#include "Target.hpp"
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsSpectrumTape(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
bool IsSpectrumTape(Storage::Tape::TapeSerialiser &tape) {
|
||||
using Parser = Storage::Tape::ZXSpectrum::Parser;
|
||||
Parser parser(Parser::MachineType::ZXSpectrum);
|
||||
|
||||
@ -103,7 +103,12 @@ bool IsSpectrumDisk(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||
|
||||
}
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::ZXSpectrum::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
Analyser::Static::TargetList Analyser::Static::ZXSpectrum::GetTargets(
|
||||
const Media &media,
|
||||
const std::string &,
|
||||
TargetPlatform::IntType,
|
||||
bool
|
||||
) {
|
||||
TargetList destination;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->confidence = 0.5;
|
||||
@ -111,7 +116,8 @@ Analyser::Static::TargetList Analyser::Static::ZXSpectrum::GetTargets(const Medi
|
||||
if(!media.tapes.empty()) {
|
||||
bool has_spectrum_tape = false;
|
||||
for(auto &tape: media.tapes) {
|
||||
has_spectrum_tape |= IsSpectrumTape(tape);
|
||||
auto serialiser = tape->serialiser();
|
||||
has_spectrum_tape |= IsSpectrumTape(*serialiser);
|
||||
}
|
||||
|
||||
if(has_spectrum_tape) {
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser::Static::ZXSpectrum {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
TargetList GetTargets(const Media &, const std::string &, TargetPlatform::IntType, bool);
|
||||
|
||||
}
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "Reflection/Enum.hpp"
|
||||
#include "Reflection/Struct.hpp"
|
||||
|
||||
namespace Analyser::Static::ZXSpectrum {
|
||||
|
||||
@ -27,11 +27,13 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
|
||||
Model model = Model::Plus2;
|
||||
bool should_hold_enter = false;
|
||||
|
||||
Target(): Analyser::Static::Target(Machine::ZXSpectrum) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(model);
|
||||
AnnounceEnum(Model);
|
||||
}
|
||||
Target(): Analyser::Static::Target(Machine::ZXSpectrum) {}
|
||||
|
||||
private:
|
||||
friend Reflection::StructImpl<Target>;
|
||||
void declare_fields() {
|
||||
DeclareField(model);
|
||||
AnnounceEnum(Model);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -29,6 +29,7 @@ endif()
|
||||
message(STATUS "Configuring for ${CLK_UI} UI")
|
||||
|
||||
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
include_directories(".")
|
||||
include("CLK_SOURCES")
|
||||
|
||||
add_executable(clksignal ${CLK_SOURCES})
|
||||
|
@ -56,218 +56,225 @@
|
||||
Boolean operators, but forcing callers and receivers to be explicit as to usage.
|
||||
*/
|
||||
template <class T> class WrappedInt {
|
||||
public:
|
||||
using IntType = int64_t;
|
||||
public:
|
||||
using IntType = int64_t;
|
||||
|
||||
forceinline constexpr WrappedInt(IntType l) noexcept : length_(l) {}
|
||||
forceinline constexpr WrappedInt() noexcept : length_(0) {}
|
||||
forceinline constexpr WrappedInt(IntType l) noexcept : length_(l) {}
|
||||
forceinline constexpr WrappedInt() noexcept : length_(0) {}
|
||||
|
||||
forceinline T &operator =(const T &rhs) {
|
||||
length_ = rhs.length_;
|
||||
return *this;
|
||||
}
|
||||
forceinline T &operator =(const T &rhs) {
|
||||
length_ = rhs.length_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
forceinline T &operator +=(const T &rhs) {
|
||||
length_ += rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
forceinline T &operator +=(const T &rhs) {
|
||||
length_ += rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
forceinline T &operator -=(const T &rhs) {
|
||||
length_ -= rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
forceinline T &operator -=(const T &rhs) {
|
||||
length_ -= rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
forceinline T &operator ++() {
|
||||
++ length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
forceinline T &operator ++() {
|
||||
++ length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
forceinline T &operator ++(int) {
|
||||
length_ ++;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
forceinline T &operator ++(int) {
|
||||
length_ ++;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
forceinline T &operator --() {
|
||||
-- length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
forceinline T &operator --() {
|
||||
-- length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
forceinline T &operator --(int) {
|
||||
length_ --;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
forceinline T &operator --(int) {
|
||||
length_ --;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
forceinline T &operator *=(const T &rhs) {
|
||||
length_ *= rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
forceinline T &operator *=(const T &rhs) {
|
||||
length_ *= rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
forceinline T &operator /=(const T &rhs) {
|
||||
length_ /= rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
forceinline T &operator /=(const T &rhs) {
|
||||
length_ /= rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
forceinline T &operator %=(const T &rhs) {
|
||||
length_ %= rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
forceinline T &operator %=(const T &rhs) {
|
||||
length_ %= rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
forceinline T &operator &=(const T &rhs) {
|
||||
length_ &= rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
forceinline T &operator &=(const T &rhs) {
|
||||
length_ &= rhs.length_;
|
||||
return *static_cast<T *>(this);
|
||||
}
|
||||
|
||||
forceinline constexpr T operator +(const T &rhs) const { return T(length_ + rhs.length_); }
|
||||
forceinline constexpr T operator -(const T &rhs) const { return T(length_ - rhs.length_); }
|
||||
forceinline constexpr T operator +(const T &rhs) const { return T(length_ + rhs.length_); }
|
||||
forceinline constexpr T operator -(const T &rhs) const { return T(length_ - rhs.length_); }
|
||||
|
||||
forceinline constexpr T operator *(const T &rhs) const { return T(length_ * rhs.length_); }
|
||||
forceinline constexpr T operator /(const T &rhs) const { return T(length_ / rhs.length_); }
|
||||
forceinline constexpr T operator *(const T &rhs) const { return T(length_ * rhs.length_); }
|
||||
forceinline constexpr T operator /(const T &rhs) const { return T(length_ / rhs.length_); }
|
||||
|
||||
forceinline constexpr T operator %(const T &rhs) const { return T(length_ % rhs.length_); }
|
||||
forceinline constexpr T operator &(const T &rhs) const { return T(length_ & rhs.length_); }
|
||||
forceinline constexpr T operator %(const T &rhs) const { return T(length_ % rhs.length_); }
|
||||
forceinline constexpr T operator &(const T &rhs) const { return T(length_ & rhs.length_); }
|
||||
|
||||
forceinline constexpr T operator -() const { return T(- length_); }
|
||||
forceinline constexpr T operator -() const { return T(- length_); }
|
||||
|
||||
forceinline constexpr bool operator <(const T &rhs) const { return length_ < rhs.length_; }
|
||||
forceinline constexpr bool operator >(const T &rhs) const { return length_ > rhs.length_; }
|
||||
forceinline constexpr bool operator <=(const T &rhs) const { return length_ <= rhs.length_; }
|
||||
forceinline constexpr bool operator >=(const T &rhs) const { return length_ >= rhs.length_; }
|
||||
forceinline constexpr bool operator ==(const T &rhs) const { return length_ == rhs.length_; }
|
||||
forceinline constexpr bool operator !=(const T &rhs) const { return length_ != rhs.length_; }
|
||||
forceinline constexpr bool operator <(const T &rhs) const { return length_ < rhs.length_; }
|
||||
forceinline constexpr bool operator >(const T &rhs) const { return length_ > rhs.length_; }
|
||||
forceinline constexpr bool operator <=(const T &rhs) const { return length_ <= rhs.length_; }
|
||||
forceinline constexpr bool operator >=(const T &rhs) const { return length_ >= rhs.length_; }
|
||||
forceinline constexpr bool operator ==(const T &rhs) const { return length_ == rhs.length_; }
|
||||
forceinline constexpr bool operator !=(const T &rhs) const { return length_ != rhs.length_; }
|
||||
|
||||
forceinline constexpr bool operator !() const { return !length_; }
|
||||
// bool operator () is not supported because it offers an implicit cast to int, which is prone silently to permit misuse
|
||||
forceinline constexpr bool operator !() const { return !length_; }
|
||||
// bool operator () is not supported because it offers an implicit cast to int,
|
||||
// which is prone silently to permit misuse.
|
||||
|
||||
/// @returns The underlying int, converted to an integral type of your choosing, clamped to that int's range.
|
||||
template<typename Type = IntType> forceinline constexpr Type as() const {
|
||||
if constexpr (sizeof(Type) == sizeof(IntType)) {
|
||||
if constexpr (std::is_same_v<Type, IntType>) {
|
||||
return length_;
|
||||
} else if constexpr (std::is_signed_v<Type>) {
|
||||
// Both integers are the same size, but a signed result is being asked for
|
||||
// from an unsigned original.
|
||||
return length_ > Type(std::numeric_limits<Type>::max()) ? Type(std::numeric_limits<Type>::max()) : Type(length_);
|
||||
} else {
|
||||
// An unsigned result is being asked for from a signed original.
|
||||
return length_ < 0 ? 0 : Type(length_);
|
||||
}
|
||||
/// @returns The underlying int, converted to an integral type of your choosing, clamped to that int's range.
|
||||
template<typename Type = IntType> forceinline constexpr Type as() const {
|
||||
if constexpr (sizeof(Type) == sizeof(IntType)) {
|
||||
if constexpr (std::is_same_v<Type, IntType>) {
|
||||
return length_;
|
||||
} else if constexpr (std::is_signed_v<Type>) {
|
||||
// Both integers are the same size, but a signed result is being asked for
|
||||
// from an unsigned original.
|
||||
return length_ > Type(std::numeric_limits<Type>::max()) ?
|
||||
Type(std::numeric_limits<Type>::max()) : Type(length_);
|
||||
} else {
|
||||
// An unsigned result is being asked for from a signed original.
|
||||
return length_ < 0 ? 0 : Type(length_);
|
||||
}
|
||||
|
||||
const auto clamped = std::clamp(length_, IntType(std::numeric_limits<Type>::min()), IntType(std::numeric_limits<Type>::max()));
|
||||
return Type(clamped);
|
||||
}
|
||||
|
||||
/// @returns The underlying int, in its native form.
|
||||
forceinline constexpr IntType as_integral() const { return length_; }
|
||||
const auto clamped = std::clamp(
|
||||
length_,
|
||||
IntType(std::numeric_limits<Type>::min()),
|
||||
IntType(std::numeric_limits<Type>::max())
|
||||
);
|
||||
return Type(clamped);
|
||||
}
|
||||
|
||||
/*!
|
||||
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
||||
the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
|
||||
*/
|
||||
template <typename Result = T> forceinline Result divide(const T &divisor) {
|
||||
Result r;
|
||||
static_cast<T *>(this)->fill(r, divisor);
|
||||
return r;
|
||||
}
|
||||
/// @returns The underlying int, in its native form.
|
||||
forceinline constexpr IntType as_integral() const { return length_; }
|
||||
|
||||
/*!
|
||||
Flushes the value in @c this. The current value is returned, and the internal value
|
||||
is reset to zero.
|
||||
*/
|
||||
template <typename Result> Result flush() {
|
||||
// Jiggery pokery here; switching to function overloading avoids
|
||||
// the namespace-level requirement for template specialisation.
|
||||
Result r;
|
||||
static_cast<T *>(this)->fill(r);
|
||||
return r;
|
||||
}
|
||||
/*!
|
||||
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
||||
the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
|
||||
*/
|
||||
template <typename Result = T> forceinline Result divide(const T &divisor) {
|
||||
Result r;
|
||||
static_cast<T *>(this)->fill(r, divisor);
|
||||
return r;
|
||||
}
|
||||
|
||||
// operator int() is deliberately not provided, to avoid accidental subtitution of
|
||||
// classes that use this template.
|
||||
/*!
|
||||
Flushes the value in @c this. The current value is returned, and the internal value
|
||||
is reset to zero.
|
||||
*/
|
||||
template <typename Result> Result flush() {
|
||||
// Jiggery pokery here; switching to function overloading avoids
|
||||
// the namespace-level requirement for template specialisation.
|
||||
Result r;
|
||||
static_cast<T *>(this)->fill(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
protected:
|
||||
IntType length_;
|
||||
// operator int() is deliberately not provided, to avoid accidental subtitution of
|
||||
// classes that use this template.
|
||||
|
||||
protected:
|
||||
IntType length_;
|
||||
};
|
||||
|
||||
/// Describes an integer number of whole cycles: pairs of clock signal transitions.
|
||||
class Cycles: public WrappedInt<Cycles> {
|
||||
public:
|
||||
forceinline constexpr Cycles(IntType l) noexcept : WrappedInt<Cycles>(l) {}
|
||||
forceinline constexpr Cycles() noexcept : WrappedInt<Cycles>() {}
|
||||
forceinline static constexpr Cycles max() {
|
||||
return Cycles(std::numeric_limits<IntType>::max());
|
||||
}
|
||||
public:
|
||||
forceinline constexpr Cycles(IntType l) noexcept : WrappedInt<Cycles>(l) {}
|
||||
forceinline constexpr Cycles() noexcept : WrappedInt<Cycles>() {}
|
||||
forceinline static constexpr Cycles max() {
|
||||
return Cycles(std::numeric_limits<IntType>::max());
|
||||
}
|
||||
|
||||
private:
|
||||
friend WrappedInt;
|
||||
void fill(Cycles &result) {
|
||||
result.length_ = length_;
|
||||
length_ = 0;
|
||||
}
|
||||
private:
|
||||
friend WrappedInt;
|
||||
void fill(Cycles &result) {
|
||||
result.length_ = length_;
|
||||
length_ = 0;
|
||||
}
|
||||
|
||||
void fill(Cycles &result, const Cycles &divisor) {
|
||||
result.length_ = length_ / divisor.length_;
|
||||
length_ %= divisor.length_;
|
||||
}
|
||||
void fill(Cycles &result, const Cycles &divisor) {
|
||||
result.length_ = length_ / divisor.length_;
|
||||
length_ %= divisor.length_;
|
||||
}
|
||||
};
|
||||
|
||||
/// Describes an integer number of half cycles: single clock signal transitions.
|
||||
class HalfCycles: public WrappedInt<HalfCycles> {
|
||||
public:
|
||||
forceinline constexpr HalfCycles(IntType l) noexcept : WrappedInt<HalfCycles>(l) {}
|
||||
forceinline constexpr HalfCycles() noexcept : WrappedInt<HalfCycles>() {}
|
||||
forceinline static constexpr HalfCycles max() {
|
||||
return HalfCycles(std::numeric_limits<IntType>::max());
|
||||
}
|
||||
public:
|
||||
forceinline constexpr HalfCycles(IntType l) noexcept : WrappedInt<HalfCycles>(l) {}
|
||||
forceinline constexpr HalfCycles() noexcept : WrappedInt<HalfCycles>() {}
|
||||
forceinline static constexpr HalfCycles max() {
|
||||
return HalfCycles(std::numeric_limits<IntType>::max());
|
||||
}
|
||||
|
||||
forceinline constexpr HalfCycles(const Cycles &cycles) noexcept : WrappedInt<HalfCycles>(cycles.as_integral() * 2) {}
|
||||
forceinline constexpr HalfCycles(const Cycles &cycles) noexcept :
|
||||
WrappedInt<HalfCycles>(cycles.as_integral() * 2) {}
|
||||
|
||||
/// @returns The number of whole cycles completely covered by this span of half cycles.
|
||||
forceinline constexpr Cycles cycles() const {
|
||||
return Cycles(length_ >> 1);
|
||||
}
|
||||
/// @returns The number of whole cycles completely covered by this span of half cycles.
|
||||
forceinline constexpr Cycles cycles() const {
|
||||
return Cycles(length_ >> 1);
|
||||
}
|
||||
|
||||
/*!
|
||||
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
||||
the value of @c this modulo @c divisor . @c this divided by @c divisor is returned.
|
||||
*/
|
||||
forceinline Cycles divide_cycles(const Cycles &divisor) {
|
||||
const HalfCycles half_divisor = HalfCycles(divisor);
|
||||
const Cycles result(length_ / half_divisor.length_);
|
||||
length_ %= half_divisor.length_;
|
||||
return result;
|
||||
}
|
||||
/*!
|
||||
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
||||
the value of @c this modulo @c divisor . @c this divided by @c divisor is returned.
|
||||
*/
|
||||
forceinline Cycles divide_cycles(const Cycles &divisor) {
|
||||
const HalfCycles half_divisor = HalfCycles(divisor);
|
||||
const Cycles result(length_ / half_divisor.length_);
|
||||
length_ %= half_divisor.length_;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
Equivalent to @c divide_cycles(Cycles(1)) but faster.
|
||||
*/
|
||||
forceinline Cycles divide_cycles() {
|
||||
const Cycles result(length_ >> 1);
|
||||
length_ &= 1;
|
||||
return result;
|
||||
}
|
||||
/*!
|
||||
Equivalent to @c divide_cycles(Cycles(1)) but faster.
|
||||
*/
|
||||
forceinline Cycles divide_cycles() {
|
||||
const Cycles result(length_ >> 1);
|
||||
length_ &= 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
friend WrappedInt;
|
||||
void fill(Cycles &result) {
|
||||
result = Cycles(length_ >> 1);
|
||||
length_ &= 1;
|
||||
}
|
||||
private:
|
||||
friend WrappedInt;
|
||||
void fill(Cycles &result) {
|
||||
result = Cycles(length_ >> 1);
|
||||
length_ &= 1;
|
||||
}
|
||||
|
||||
void fill(HalfCycles &result) {
|
||||
result.length_ = length_;
|
||||
length_ = 0;
|
||||
}
|
||||
void fill(HalfCycles &result) {
|
||||
result.length_ = length_;
|
||||
length_ = 0;
|
||||
}
|
||||
|
||||
void fill(Cycles &result, const HalfCycles &divisor) {
|
||||
result = Cycles(length_ / (divisor.length_ << 1));
|
||||
length_ %= (divisor.length_ << 1);
|
||||
}
|
||||
void fill(Cycles &result, const HalfCycles &divisor) {
|
||||
result = Cycles(length_ / (divisor.length_ << 1));
|
||||
length_ %= (divisor.length_ << 1);
|
||||
}
|
||||
|
||||
void fill(HalfCycles &result, const HalfCycles &divisor) {
|
||||
result.length_ = length_ / divisor.length_;
|
||||
length_ %= divisor.length_;
|
||||
}
|
||||
void fill(HalfCycles &result, const HalfCycles &divisor) {
|
||||
result.length_ = length_ / divisor.length_;
|
||||
length_ %= divisor.length_;
|
||||
}
|
||||
};
|
||||
|
||||
// Create a specialisation of WrappedInt::flush for converting HalfCycles to Cycles
|
||||
@ -278,14 +285,14 @@ class HalfCycles: public WrappedInt<HalfCycles> {
|
||||
automatically to gain run_for(HalfCycles).
|
||||
*/
|
||||
template <class T> class HalfClockReceiver: public T {
|
||||
public:
|
||||
using T::T;
|
||||
public:
|
||||
using T::T;
|
||||
|
||||
forceinline void run_for(const HalfCycles half_cycles) {
|
||||
half_cycles_ += half_cycles;
|
||||
T::run_for(half_cycles_.flush<Cycles>());
|
||||
}
|
||||
forceinline void run_for(const HalfCycles half_cycles) {
|
||||
half_cycles_ += half_cycles;
|
||||
T::run_for(half_cycles_.flush<Cycles>());
|
||||
}
|
||||
|
||||
private:
|
||||
HalfCycles half_cycles_;
|
||||
private:
|
||||
HalfCycles half_cycles_;
|
||||
};
|
||||
|
@ -58,28 +58,28 @@ struct Observer {
|
||||
The hint provided is just that: a hint. Owners may perform ::run_for at a greater frequency.
|
||||
*/
|
||||
class Source {
|
||||
public:
|
||||
/// Registers @c observer as the new clocking observer.
|
||||
void set_clocking_hint_observer(Observer *observer) {
|
||||
observer_ = observer;
|
||||
update_clocking_observer();
|
||||
}
|
||||
public:
|
||||
/// Registers @c observer as the new clocking observer.
|
||||
void set_clocking_hint_observer(Observer *observer) {
|
||||
observer_ = observer;
|
||||
update_clocking_observer();
|
||||
}
|
||||
|
||||
/// @returns the current preferred clocking strategy.
|
||||
virtual Preference preferred_clocking() const = 0;
|
||||
/// @returns the current preferred clocking strategy.
|
||||
virtual Preference preferred_clocking() const = 0;
|
||||
|
||||
private:
|
||||
Observer *observer_ = nullptr;
|
||||
private:
|
||||
Observer *observer_ = nullptr;
|
||||
|
||||
protected:
|
||||
/*!
|
||||
Provided for subclasses; call this whenever the clocking preference might have changed.
|
||||
This will notify the observer if there is one.
|
||||
*/
|
||||
void update_clocking_observer() {
|
||||
if(!observer_) return;
|
||||
observer_->set_component_prefers_clocking(this, preferred_clocking());
|
||||
}
|
||||
protected:
|
||||
/*!
|
||||
Provided for subclasses; call this whenever the clocking preference might have changed.
|
||||
This will notify the observer if there is one.
|
||||
*/
|
||||
void update_clocking_observer() {
|
||||
if(!observer_) return;
|
||||
observer_->set_component_prefers_clocking(this, preferred_clocking());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -15,78 +15,79 @@
|
||||
Provides the logic to insert into and traverse a list of future scheduled items.
|
||||
*/
|
||||
template <typename TimeUnit> class DeferredQueue {
|
||||
public:
|
||||
/*!
|
||||
Schedules @c action to occur in @c delay units of time.
|
||||
*/
|
||||
void defer(TimeUnit delay, const std::function<void(void)> &action) {
|
||||
// Apply immediately if there's no delay (or a negative delay).
|
||||
if(delay <= TimeUnit(0)) {
|
||||
action();
|
||||
return;
|
||||
public:
|
||||
/*!
|
||||
Schedules @c action to occur in @c delay units of time.
|
||||
*/
|
||||
void defer(TimeUnit delay, const std::function<void(void)> &action) {
|
||||
// Apply immediately if there's no delay (or a negative delay).
|
||||
if(delay <= TimeUnit(0)) {
|
||||
action();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!pending_actions_.empty()) {
|
||||
// Otherwise enqueue, having subtracted the delay for any preceding events,
|
||||
// and subtracting from the subsequent, if any.
|
||||
auto insertion_point = pending_actions_.begin();
|
||||
while(insertion_point != pending_actions_.end() && insertion_point->delay < delay) {
|
||||
delay -= insertion_point->delay;
|
||||
++insertion_point;
|
||||
}
|
||||
if(insertion_point != pending_actions_.end()) {
|
||||
insertion_point->delay -= delay;
|
||||
}
|
||||
|
||||
if(!pending_actions_.empty()) {
|
||||
// Otherwise enqueue, having subtracted the delay for any preceding events,
|
||||
// and subtracting from the subsequent, if any.
|
||||
auto insertion_point = pending_actions_.begin();
|
||||
while(insertion_point != pending_actions_.end() && insertion_point->delay < delay) {
|
||||
delay -= insertion_point->delay;
|
||||
++insertion_point;
|
||||
}
|
||||
if(insertion_point != pending_actions_.end()) {
|
||||
insertion_point->delay -= delay;
|
||||
}
|
||||
pending_actions_.emplace(insertion_point, delay, action);
|
||||
} else {
|
||||
pending_actions_.emplace_back(delay, action);
|
||||
}
|
||||
}
|
||||
|
||||
pending_actions_.emplace(insertion_point, delay, action);
|
||||
/*!
|
||||
@returns The amount of time until the next enqueued action will occur,
|
||||
or TimeUnit(-1) if the queue is empty.
|
||||
*/
|
||||
TimeUnit time_until_next_action() const {
|
||||
if(pending_actions_.empty()) return TimeUnit(-1);
|
||||
return pending_actions_.front().delay;
|
||||
}
|
||||
|
||||
/*!
|
||||
Advances the queue the specified amount of time, performing any actions it reaches.
|
||||
*/
|
||||
void advance(TimeUnit time) {
|
||||
auto erase_iterator = pending_actions_.begin();
|
||||
while(erase_iterator != pending_actions_.end()) {
|
||||
erase_iterator->delay -= time;
|
||||
if(erase_iterator->delay <= TimeUnit(0)) {
|
||||
time = -erase_iterator->delay;
|
||||
erase_iterator->action();
|
||||
++erase_iterator;
|
||||
} else {
|
||||
pending_actions_.emplace_back(delay, action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns The amount of time until the next enqueued action will occur,
|
||||
or TimeUnit(-1) if the queue is empty.
|
||||
*/
|
||||
TimeUnit time_until_next_action() const {
|
||||
if(pending_actions_.empty()) return TimeUnit(-1);
|
||||
return pending_actions_.front().delay;
|
||||
if(erase_iterator != pending_actions_.begin()) {
|
||||
pending_actions_.erase(pending_actions_.begin(), erase_iterator);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Advances the queue the specified amount of time, performing any actions it reaches.
|
||||
*/
|
||||
void advance(TimeUnit time) {
|
||||
auto erase_iterator = pending_actions_.begin();
|
||||
while(erase_iterator != pending_actions_.end()) {
|
||||
erase_iterator->delay -= time;
|
||||
if(erase_iterator->delay <= TimeUnit(0)) {
|
||||
time = -erase_iterator->delay;
|
||||
erase_iterator->action();
|
||||
++erase_iterator;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(erase_iterator != pending_actions_.begin()) {
|
||||
pending_actions_.erase(pending_actions_.begin(), erase_iterator);
|
||||
}
|
||||
}
|
||||
/// @returns @c true if no actions are enqueued; @c false otherwise.
|
||||
bool empty() const {
|
||||
return pending_actions_.empty();
|
||||
}
|
||||
|
||||
/// @returns @c true if no actions are enqueued; @c false otherwise.
|
||||
bool empty() const {
|
||||
return pending_actions_.empty();
|
||||
}
|
||||
private:
|
||||
// The list of deferred actions.
|
||||
struct DeferredAction {
|
||||
TimeUnit delay;
|
||||
std::function<void(void)> action;
|
||||
|
||||
private:
|
||||
// The list of deferred actions.
|
||||
struct DeferredAction {
|
||||
TimeUnit delay;
|
||||
std::function<void(void)> action;
|
||||
|
||||
DeferredAction(TimeUnit delay, const std::function<void(void)> &action) : delay(delay), action(std::move(action)) {}
|
||||
};
|
||||
std::vector<DeferredAction> pending_actions_;
|
||||
DeferredAction(TimeUnit delay, const std::function<void(void)> &action) :
|
||||
delay(delay), action(std::move(action)) {}
|
||||
};
|
||||
std::vector<DeferredAction> pending_actions_;
|
||||
};
|
||||
|
||||
/*!
|
||||
@ -97,30 +98,28 @@ template <typename TimeUnit> class DeferredQueue {
|
||||
This list is efficient only for short queues.
|
||||
*/
|
||||
template <typename TimeUnit> class DeferredQueuePerformer: public DeferredQueue<TimeUnit> {
|
||||
public:
|
||||
/// Constructs a DeferredQueue that will call target(period) in between deferred actions.
|
||||
constexpr DeferredQueuePerformer(std::function<void(TimeUnit)> &&target) : target_(std::move(target)) {}
|
||||
public:
|
||||
/// Constructs a DeferredQueue that will call target(period) in between deferred actions.
|
||||
constexpr DeferredQueuePerformer(std::function<void(TimeUnit)> &&target) : target_(std::move(target)) {}
|
||||
|
||||
/*!
|
||||
Runs for @c length units of time.
|
||||
/*!
|
||||
Runs for @c length units of time.
|
||||
|
||||
The constructor-supplied target will be called with one or more periods that add up to @c length;
|
||||
any scheduled actions will be called between periods.
|
||||
*/
|
||||
void run_for(TimeUnit length) {
|
||||
auto time_to_next = DeferredQueue<TimeUnit>::time_until_next_action();
|
||||
while(time_to_next != TimeUnit(-1) && time_to_next <= length) {
|
||||
target_(time_to_next);
|
||||
length -= time_to_next;
|
||||
DeferredQueue<TimeUnit>::advance(time_to_next);
|
||||
}
|
||||
|
||||
DeferredQueue<TimeUnit>::advance(length);
|
||||
target_(length);
|
||||
|
||||
// TODO: optimise this to avoid the multiple std::vector deletes. Find a neat way to expose that solution, maybe?
|
||||
The constructor-supplied target will be called with one or more periods that add up to @c length;
|
||||
any scheduled actions will be called between periods.
|
||||
*/
|
||||
void run_for(TimeUnit length) {
|
||||
auto time_to_next = DeferredQueue<TimeUnit>::time_until_next_action();
|
||||
while(time_to_next != TimeUnit(-1) && time_to_next <= length) {
|
||||
target_(time_to_next);
|
||||
length -= time_to_next;
|
||||
DeferredQueue<TimeUnit>::advance(time_to_next);
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(TimeUnit)> target_;
|
||||
DeferredQueue<TimeUnit>::advance(length);
|
||||
target_(length);
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(TimeUnit)> target_;
|
||||
};
|
||||
|
@ -13,33 +13,33 @@
|
||||
of future values.
|
||||
*/
|
||||
template <int DeferredDepth, typename ValueT> class DeferredValue {
|
||||
private:
|
||||
static_assert(sizeof(ValueT) <= 4);
|
||||
private:
|
||||
static_assert(sizeof(ValueT) <= 4);
|
||||
|
||||
constexpr int elements_per_uint32 = sizeof(uint32_t) / sizeof(ValueT);
|
||||
constexpr int unit_shift = sizeof(ValueT) * 8;
|
||||
constexpr int insert_shift = (DeferredDepth & (elements_per_uint32 - 1)) * unit_shift;
|
||||
constexpr uint32_t insert_mask = ~(0xffff'ffff << insert_shift);
|
||||
constexpr int elements_per_uint32 = sizeof(uint32_t) / sizeof(ValueT);
|
||||
constexpr int unit_shift = sizeof(ValueT) * 8;
|
||||
constexpr int insert_shift = (DeferredDepth & (elements_per_uint32 - 1)) * unit_shift;
|
||||
constexpr uint32_t insert_mask = ~(0xffff'ffff << insert_shift);
|
||||
|
||||
std::array<uint32_t, (DeferredDepth + elements_per_uint32 - 1) / elements_per_uint32> backlog;
|
||||
std::array<uint32_t, (DeferredDepth + elements_per_uint32 - 1) / elements_per_uint32> backlog;
|
||||
|
||||
public:
|
||||
/// @returns the current value.
|
||||
ValueT value() const {
|
||||
return uint8_t(backlog[0]);
|
||||
public:
|
||||
/// @returns the current value.
|
||||
ValueT value() const {
|
||||
return uint8_t(backlog[0]);
|
||||
}
|
||||
|
||||
/// Advances to the next enqueued value.
|
||||
void advance() {
|
||||
for(size_t c = 0; c < backlog.size() - 1; c--) {
|
||||
backlog[c] = (backlog[c] >> unit_shift) | (backlog[c+1] << (32 - unit_shift));
|
||||
}
|
||||
backlog[backlog.size() - 1] >>= unit_shift;
|
||||
}
|
||||
|
||||
/// Advances to the next enqueued value.
|
||||
void advance() {
|
||||
for(size_t c = 0; c < backlog.size() - 1; c--) {
|
||||
backlog[c] = (backlog[c] >> unit_shift) | (backlog[c+1] << (32 - unit_shift));
|
||||
}
|
||||
backlog[backlog.size() - 1] >>= unit_shift;
|
||||
}
|
||||
|
||||
/// Inserts a new value, replacing whatever is currently at the end of the queue.
|
||||
void insert(ValueT value) {
|
||||
backlog[DeferredDepth / elements_per_uint32] =
|
||||
(backlog[DeferredDepth / elements_per_uint32] & insert_mask) | (value << insert_shift);
|
||||
}
|
||||
/// Inserts a new value, replacing whatever is currently at the end of the queue.
|
||||
void insert(const ValueT value) {
|
||||
backlog[DeferredDepth / elements_per_uint32] =
|
||||
(backlog[DeferredDepth / elements_per_uint32] & insert_mask) | (value << insert_shift);
|
||||
}
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user