1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-04-21 18:37:11 +00:00

Compare commits

...

514 Commits

Author SHA1 Message Date
Thomas Harte
53c1a322ed Increase version number. 2025-03-20 15:39:42 -04:00
Thomas Harte
7fc16fa2c8
Merge pull request #1495 from TomHarte/286StatusWord
Add extra 80286 registers.
2025-03-19 14:12:56 -04:00
Thomas Harte
fe6a88c5df Install [L/S][I/G]DT wiring. 2025-03-18 22:11:28 -04:00
Thomas Harte
7b14df82e0 Merge branch 'master' into 286StatusWord 2025-03-18 21:47:32 -04:00
Thomas Harte
966b41313d
Merge pull request #1497 from TomHarte/LargeWindowMousing
Correct potential SCSI crash; tweak macOS mouse behaviour.
2025-03-18 21:46:41 -04:00
Thomas Harte
91f1c3322c Ensure tracking areas are updated. 2025-03-18 21:12:48 -04:00
Thomas Harte
0b276c5a76 Remove focus rings. 2025-03-18 20:38:09 -04:00
Thomas Harte
b654c2e170 Avoid potential out-of-bounds access. 2025-03-18 20:23:31 -04:00
Thomas Harte
32c88da6c4 Iterate towards LGDT/LIDT.
Specifically: add a means to get just an indirect address; add an enum for descriptor tables; add an `ldt` function for the global and interrupt tables, which currently just authorises the access and then stops.
2025-03-18 18:22:04 -04:00
Thomas Harte
3d19d0816b Do something for SMSW. 2025-03-16 22:05:14 -04:00
Thomas Harte
15da707324
Merge pull request #1494 from TomHarte/MoreAT
AT: adjust reported RAM refresh timing.
2025-03-16 15:54:27 -04:00
Thomas Harte
387e8b04f9 Adjust reported refresh timing. 2025-03-16 15:44:05 -04:00
Thomas Harte
4ca47be8a8 Add const. 2025-03-15 22:21:35 -04:00
Thomas Harte
f9d9dc68b7 Factor out BIOS installation. 2025-03-15 22:19:26 -04:00
Thomas Harte
14a8f4511c
Merge pull request #1492 from TomHarte/ATBIOS
Support real IBM BIOS, either in odd/even or complete form.
2025-03-15 22:12:54 -04:00
Thomas Harte
97d237eed0 Support real IBM BIOS, either in odd/even or complete form. 2025-03-15 21:13:31 -04:00
Thomas Harte
20d0406a24
Merge pull request #1491 from TomHarte/PUSHA
Fix PUSHA register list; add illegal asserts.
2025-03-14 22:27:19 -04:00
Thomas Harte
73e843abd3 Assert on some hopefully unreachables. 2025-03-12 22:03:12 -04:00
Thomas Harte
8d956da65b Correct final thing written to stack by PUSHA. 2025-03-12 21:40:03 -04:00
Thomas Harte
9b9cdfe937
Merge pull request #1489 from TomHarte/MinorExtraStrings
Slightly improve x86 instruction to string lexicon.
2025-03-12 21:34:18 -04:00
Thomas Harte
3dcba9362c Add a couple of missing Operation:: mappings. 2025-03-12 13:51:38 -04:00
Thomas Harte
2d3a3ada57 Add AccessType to string conversion. 2025-03-12 13:51:17 -04:00
Thomas Harte
143d1d5e35
Merge pull request #1488 from TomHarte/ATKeyboard
Implement some proportion of the AT keyboard controller.
2025-03-12 13:19:15 -04:00
Thomas Harte
13b32b269c Force the AT in debug mode only. 2025-03-12 12:47:04 -04:00
Thomas Harte
592cea6a27 Tweak timing to once again pass BIOS controller test. 2025-03-12 12:31:29 -04:00
Thomas Harte
e76ca304e6 Attempt to support 60 and c0. 2025-03-11 22:54:54 -04:00
Thomas Harte
d77d8df1ac Add closer-to-real keyboard command loop. 2025-03-11 22:35:13 -04:00
Thomas Harte
4f35d5dabd Log A20 line. 2025-03-09 23:09:58 -04:00
Thomas Harte
e927feb2d6 Reintroduce is-tested flag. 2025-03-08 21:59:40 -05:00
Thomas Harte
98f20c57c1 Attempt to support reset-by-8042, resulting in boot loop. 2025-03-08 00:47:56 -05:00
Thomas Harte
7a88d31fd9 Add include for strlen. 2025-03-07 23:47:34 -05:00
Thomas Harte
96326411bf Move explicit specialization to namespace scope. 2025-03-07 23:41:56 -05:00
Thomas Harte
4e882e7d4d Accept uint8_ts only. 2025-03-07 23:36:47 -05:00
Thomas Harte
bff10c1714 Resolve newfound log ambiguity. 2025-03-07 23:34:12 -05:00
Thomas Harte
cee2484108 Flip input/output, perform commands instantly. 2025-03-07 23:32:07 -05:00
Thomas Harte
93078abe87 Buffer lines prior to output. 2025-03-07 23:25:22 -05:00
Thomas Harte
8caa1a9664 Experiment with dialogue. 2025-03-07 14:24:08 -05:00
Thomas Harte
d7b46ee03c Adopt compact form. 2025-03-07 14:23:50 -05:00
Thomas Harte
e07b3da983 Add commentary; start fleshing out AT keyboard controller. 2025-03-07 14:01:59 -05:00
Thomas Harte
0f166cee48
Merge pull request #1487 from TomHarte/PCSeparate
Separate the speaker, and keyboard and floppy controllers.
2025-03-07 14:01:19 -05:00
Thomas Harte
08df42d05b Just 'POST' is fine. 2025-03-07 13:46:07 -05:00
Thomas Harte
2c165c3873 Avoid repetition of 'PC'. 2025-03-07 13:44:49 -05:00
Thomas Harte
9135402d9e Extract keyboard controller. 2025-03-07 13:43:21 -05:00
Thomas Harte
53135ec2c0 Extract floppy controller, speaker. 2025-03-07 13:39:45 -05:00
Thomas Harte
4eb8c8dea9
Merge pull request #1485 from TomHarte/FurtherATDMA
Add second PIC and DMA controllers.
2025-03-06 22:46:55 -05:00
Thomas Harte
f318bec53c Reduce indentation, improve constness. 2025-03-06 22:33:58 -05:00
Thomas Harte
7d84d6909e Add TODO. 2025-03-06 22:16:51 -05:00
Thomas Harte
9224645473 Add second PIC. 2025-03-06 22:15:58 -05:00
Thomas Harte
6717771f9a Rejig doubling of DMA controllers. 2025-03-05 23:02:08 -05:00
Thomas Harte
99e0902b74 Reconnect speaker. 2025-03-05 21:51:50 -05:00
Thomas Harte
c7f2805b05 Install the AT keyboard controller. 2025-03-05 21:28:22 -05:00
Thomas Harte
6e1909647b Reformat; hatch separate AT keyboard controller; print POST codes. 2025-03-05 21:08:53 -05:00
Thomas Harte
0c7db56e15 Merge branch 'master' into FurtherATDMA 2025-03-05 16:34:50 -05:00
Thomas Harte
3287ca449e Update macOS version number. 2025-03-05 16:23:23 -05:00
Thomas Harte
53a8f65ecc Merge branch 'master' into FurtherATDMA 2025-03-05 16:03:46 -05:00
Thomas Harte
113035b374
Merge pull request #1486 from TomHarte/macOSCrashes
macOS: Don't crash if mouse exits window while picking machine.
2025-03-05 16:03:04 -05:00
Thomas Harte
c6e64837c3 Don't crash if mouse moves while picking machine. 2025-03-05 15:51:31 -05:00
Thomas Harte
82419e6df1 Revoke 'ForceAT', **again**. 2025-03-05 14:34:44 -05:00
Thomas Harte
faa76ee017 Not quite accurate, but segment out keyboard writes for the AT. 2025-03-05 14:33:55 -05:00
Thomas Harte
ffdefb4106 Don't crash if mouse moves while picking machine. 2025-03-05 14:27:33 -05:00
Thomas Harte
ba7b1c47b9 Improve constness, reduce trips to system clock. 2025-03-05 14:17:31 -05:00
Thomas Harte
342b8105c4 Improve constness. 2025-03-04 22:53:26 -05:00
Thomas Harte
367c2b568a Attempt to offer expanded DMA top bytes. 2025-03-04 22:51:17 -05:00
Thomas Harte
0eef2c0d04
Merge pull request #1484 from TomHarte/286Decoding
Repair 286 decoding and `perform`.
2025-03-04 21:21:16 -05:00
Thomas Harte
c9a065107b Further tweak ENTER. 2025-03-04 21:08:59 -05:00
Thomas Harte
cacacc00f6 Undo ForceAT. 2025-03-04 20:56:38 -05:00
Thomas Harte
1b94cfc72c Add nullptr backstop. 2025-03-04 20:46:10 -05:00
Thomas Harte
89fd41124f Template various bits of hardware on machine type. 2025-03-04 17:08:49 -05:00
Thomas Harte
4e3b0ae3c1 Resolve type warnings in ENTER, spurious new lines in PC. 2025-03-04 14:10:28 -05:00
Thomas Harte
9df6d535e2 Patch up enough to get an 80286 performer compilable. 2025-03-04 13:52:02 -05:00
Thomas Harte
d545cce276
Merge pull request #1483 from TomHarte/PC-AT
Tee up for an AT-class PC.
2025-03-04 11:57:09 -05:00
Thomas Harte
71b481d3be Bake PC model into template. 2025-03-04 11:45:56 -05:00
Thomas Harte
2710acaae6 Avoid repeating CPU model, normalise member names. 2025-03-04 11:35:11 -05:00
Thomas Harte
d79135ea01 Eliminate non-functional workaround. 2025-03-04 11:30:43 -05:00
Thomas Harte
1464011f6f Try throwing some externs at it. 2025-03-04 11:23:55 -05:00
Thomas Harte
409c8a6859 Keep poking at this. 2025-03-04 11:10:35 -05:00
Thomas Harte
805ce36592 Fix spelling, namespace. 2025-03-04 11:01:07 -05:00
Thomas Harte
07fa56c53d Get heavier with GCC workarounds. 2025-03-04 10:56:45 -05:00
Thomas Harte
28fca80023 Attempt lighter GCC workaround. 2025-03-04 10:46:14 -05:00
Thomas Harte
08c0ee9ca8 Fix further speed reference; eliminate Decoder8086 entirely. 2025-03-04 10:35:16 -05:00
Thomas Harte
2878ab1578 Update Qt UI. 2025-03-04 10:25:27 -05:00
Thomas Harte
16f850cbcc Attempt to eliminate Decoder8086. 2025-03-04 10:13:57 -05:00
Thomas Harte
b9177e50d3 Commute 'speed' to 'model approximation'. 2025-03-04 09:57:34 -05:00
Thomas Harte
c3258551d7
Merge pull request #1481 from TomHarte/x86Macros
Reduce macros in x86 decoder.
2025-03-03 23:27:39 -05:00
Thomas Harte
ea0799c546 Transition off printf. 2025-03-03 23:17:52 -05:00
Thomas Harte
4843b7f7b8 Update name expectations. 2025-03-01 22:35:15 -05:00
Thomas Harte
61694c625e Merge branch 'master' into x86Macros 2025-02-28 22:53:19 -05:00
Thomas Harte
64b4b99d48 Shave icon. 2025-02-28 22:24:06 -05:00
Thomas Harte
dad966b551
Merge pull request #1482 from TomHarte/IconAndReadme
Update README icon, mention Plus 4.
2025-02-28 22:10:51 -05:00
Thomas Harte
e46f503641 Try icon below title. 2025-02-28 22:09:21 -05:00
Thomas Harte
fc4a1e6ace Revert "See whether this affects title underlining."
This reverts commit 02b9a20c1199c87abb2b1a069e375e931b0e93c7.
2025-02-28 22:07:21 -05:00
Thomas Harte
02b9a20c11 See whether this affects title underlining. 2025-02-28 22:07:00 -05:00
Thomas Harte
1db4e4caed Try for on-left with wrapping. 2025-02-28 22:06:14 -05:00
Thomas Harte
991e176c85 Try as raw HTML. 2025-02-28 22:05:30 -05:00
Thomas Harte
4a41a6bb85 Try right alignment. 2025-02-28 22:04:30 -05:00
Thomas Harte
6229e1049b Move icon to other side of text. 2025-02-28 22:03:25 -05:00
Thomas Harte
3cfd6ed109 Experiment with top-line layout. 2025-02-28 22:02:41 -05:00
Thomas Harte
ad70180edd Update README icon, mention Plus 4. 2025-02-28 22:01:29 -05:00
Thomas Harte
efd4a83bd2 Remove dead #undefs. 2025-02-28 21:55:52 -05:00
Thomas Harte
e13b4ab7c9 Reduce reliance upon macros. 2025-02-28 21:51:44 -05:00
Thomas Harte
fee89cbaad
Switch to absolute include paths. 2025-02-28 14:53:48 -05:00
Thomas Harte
2bdcba437c Fix include path. 2025-02-28 13:27:36 -05:00
Thomas Harte
2c2216afae Further eliminate file-relative includes. 2025-02-28 13:18:48 -05:00
Thomas Harte
0823fc32fe Eliminate file-relative paths. 2025-02-28 12:30:25 -05:00
Thomas Harte
091d19caf5 Qt: overtly add project home as include path. 2025-02-28 11:34:27 -05:00
Thomas Harte
00a7381f08 Add top-level base for includes. 2025-02-28 11:32:44 -05:00
Thomas Harte
d494d1e3ee Add SConstruct top-level include path. 2025-02-28 11:30:08 -05:00
Thomas Harte
b1c331a1df Add a canary for include paths. 2025-02-28 11:26:43 -05:00
Thomas Harte
b1a4fd085b Permit project-relative includes. 2025-02-27 18:37:33 -05:00
Thomas Harte
511ab2afe9
Merge pull request #1478 from TomHarte/C++23SDL
Bump macOS SDL target to C++23; fix incompatibilities.
2025-02-27 18:36:23 -05:00
Thomas Harte
30387ad654 Merge branch 'master' into C++23SDL 2025-02-27 18:34:01 -05:00
Thomas Harte
5d528ee1f3
Merge pull request #1479 from TomHarte/x86Documentation
Improve x86 `const`ness.
2025-02-27 18:33:20 -05:00
Thomas Harte
96bb4d50ba Promote macOS SDL target to C++23 as a testing chamber; resolve issues. 2025-02-27 18:13:35 -05:00
Thomas Harte
45f850adae Improve constness. 2025-02-27 15:47:06 -05:00
Thomas Harte
09341ddbe9
Merge pull request #1477 from TomHarte/C++20Deprecations
Resolve two immediate warnings in a C++20 test build.
2025-02-27 15:29:41 -05:00
Thomas Harte
eab4274737 Capture 'this' by reference. 2025-02-27 11:56:51 -05:00
Thomas Harte
49fec1bc10 Remove meaningless 'volatile'. 2025-02-27 11:56:24 -05:00
Thomas Harte
de164469c4 Update version, copyright string. 2025-02-26 21:30:41 -05:00
Thomas Harte
f595871cd9
Merge pull request #1476 from TomHarte/MoreChangeEffects
Widen media change observation.
2025-02-26 21:23:19 -05:00
Thomas Harte
ff86cbd48e Remove more get_s. 2025-02-26 20:26:06 -05:00
Thomas Harte
47bd4dade5 Avoid direct cstdio where meaningful. 2025-02-26 18:04:13 -05:00
Thomas Harte
ff8180920f Simplify extension finder, 80/81 file grabbing. 2025-02-26 17:17:24 -05:00
Thomas Harte
d4f08f0006 Remove get_. 2025-02-26 17:09:47 -05:00
Thomas Harte
1db756063b Further remove type info from function naming. 2025-02-26 16:00:34 -05:00
Thomas Harte
b44ea31bbf Deal with default sizes delcaratively. 2025-02-26 11:33:38 -05:00
Thomas Harte
ddccb946ff Fix precedence error in put_le. 2025-02-25 23:24:11 -05:00
Thomas Harte
1f6f30ae9e Implement MediaChangeObserver for the consoles. 2025-02-25 23:03:45 -05:00
Thomas Harte
4b19a3f4ed Split interface, make const. 2025-02-25 22:58:36 -05:00
Thomas Harte
c39d0ce2f7 Add note to self. 2025-02-25 22:37:12 -05:00
Thomas Harte
cbdf1a941c
Merge pull request #1475 from TomHarte/FileDidChange
macOS: Reinsert media and/or restart machines upon underlying file changes.
2025-02-24 23:12:17 -05:00
Thomas Harte
a340635de5 Fix lost audio, race condition. 2025-02-24 23:04:41 -05:00
Thomas Harte
d62362db1a Reduce copy and paste. 2025-02-24 22:50:56 -05:00
Thomas Harte
a0aba69306 Resolve lost options. 2025-02-24 22:46:44 -05:00
Thomas Harte
765683cd34 Copy and paste to a mostly working substitution. 2025-02-24 22:44:10 -05:00
Thomas Harte
93ddf4f0ba Ensure ZX Spectrum, at least, returns correct indication. 2025-02-24 22:34:15 -05:00
Thomas Harte
8dcccf11bf Improve constness, remove unnecessary virtuals. 2025-02-24 18:18:12 -05:00
Thomas Harte
43353ce892 Confirm and wire through semantics. 2025-02-24 15:36:18 -05:00
Thomas Harte
a698fea078 Spell out some options. 2025-02-23 18:15:14 -05:00
Thomas Harte
390d9b0fe1 Add further note to self. 2025-02-22 23:12:41 -05:00
Thomas Harte
fd3ff05b17 Avoid undefined behaviour on left shift. 2025-02-22 23:06:40 -05:00
Thomas Harte
a5e4c9dd7b Add pure file-content change observer. 2025-02-22 22:40:15 -05:00
Thomas Harte
37f07dcc5a Add note to self on intentions. 2025-02-21 17:51:14 -05:00
Thomas Harte
75bae9a59c
Merge pull request #1474 from TomHarte/LighterMouseovers
Adjust macOS UI to show options/volume only when _directly_ mouseovered.
2025-02-21 12:42:15 -05:00
Thomas Harte
1684d88a3b Don't hide mouse cursor if over an interesting subview. 2025-02-21 11:43:47 -05:00
Thomas Harte
50acbb70da Rename protocol method. 2025-02-21 11:37:37 -05:00
Thomas Harte
4de1025468 Avoid potential MSX 2 crash at startup. 2025-02-21 11:37:24 -05:00
Thomas Harte
c2506dd115 Show controls immediately at startup. 2025-02-21 11:31:13 -05:00
Thomas Harte
f8c9aa8e6c Require mouseover near the volume/controls to activate. 2025-02-21 11:28:53 -05:00
Thomas Harte
e19dc1f067 Make scan target the window's native view. 2025-02-21 10:43:56 -05:00
Thomas Harte
84776bced1
Merge pull request #1473 from TomHarte/BetterBitReader
Slim FileHolder; improve and extract BitSerialiser.
2025-02-20 23:03:39 -05:00
Thomas Harte
9162c86e21 Test, improve BitStream. 2025-02-20 22:42:02 -05:00
Thomas Harte
88ffcbc62b Work in terms of the number of bits to be handled. 2025-02-19 22:04:51 -05:00
Thomas Harte
6aff0b74cd Reduce redundant types. 2025-02-19 00:09:57 -05:00
Thomas Harte
79671890c5 Generalise and improve BitStream. 2025-02-18 23:17:39 -05:00
Thomas Harte
edd4ed307f Template away repetition. 2025-02-18 22:48:47 -05:00
Thomas Harte
f786f8a970
Merge pull request #1472 from TomHarte/FastPRGs
Withdraw PRG support for the Plus 4.
2025-02-18 21:57:32 -05:00
Thomas Harte
a1d10adaa3 Support only the Vic-20 for PRGs for now. 2025-02-18 20:46:43 -05:00
Thomas Harte
7f480e8e56 Swing desperately at fast tape loading. 2025-02-11 21:49:58 -05:00
Thomas Harte
94b972aaf4 Improve style. 2025-02-11 21:48:56 -05:00
Thomas Harte
9470775292 Avoid race condition on input/output frequencies. 2025-02-11 21:48:23 -05:00
Thomas Harte
93eb63d930 Add interrupt register to TED check set. 2025-02-11 21:47:36 -05:00
Thomas Harte
ea81096a43 Reinstall debugging temporariness. 2025-02-07 18:09:33 -05:00
Thomas Harte
594045b4e7
Merge pull request #1471 from TomHarte/CRCInterface
Remove need for a CRC generator instance.
2025-02-05 22:18:59 -05:00
Thomas Harte
1449330ed3 Use updated interface. 2025-02-04 23:14:49 -05:00
Thomas Harte
07493a6b18 Remove need for a CRC generator instance. 2025-02-04 22:54:39 -05:00
Thomas Harte
0310db5f24
Merge pull request #1470 from TomHarte/SimplerReverse
Unify and simplify bit reversal functions.
2025-02-04 21:49:57 -05:00
Thomas Harte
ed6d5a7d38 Correct test target. 2025-02-04 00:05:09 -05:00
Thomas Harte
ca7c1bc631 Remove redundant inlines. 2025-02-04 00:00:12 -05:00
Thomas Harte
259070c658 Unify reverse functions. 2025-02-03 23:58:41 -05:00
Thomas Harte
e1a7dd9b24 Implement recursive reverse. 2025-02-03 23:50:15 -05:00
Thomas Harte
e5945fbb3d
Merge pull request #1469 from TomHarte/AllWarnings
Resolve all current compiler warnings.
2025-02-03 22:08:12 -05:00
Thomas Harte
247f636988 Reinstate ZRLE support. 2025-02-03 21:56:12 -05:00
Thomas Harte
c58a2ee624 Remove redundant moves. 2025-02-03 21:44:30 -05:00
Thomas Harte
35a1b44c21 Remove unused elements. 2025-02-03 21:42:07 -05:00
Thomas Harte
fd09f06507
Merge pull request #1468 from TomHarte/Qt6CI
Reinstate Xcode CI.
2025-02-03 21:34:36 -05:00
Thomas Harte
d66b501a99 Request latest Xcode. 2025-02-03 21:27:53 -05:00
Thomas Harte
349f6766ac Switch to array. 2025-02-03 21:24:05 -05:00
Thomas Harte
cd55ed1514 Persevere. 2025-02-03 21:22:50 -05:00
Thomas Harte
14d4c8accc Disable Qt 6; try to reenable Xcode. 2025-02-03 21:21:25 -05:00
Thomas Harte
37bca96bde Strip back. 2025-02-03 21:17:19 -05:00
Thomas Harte
b0e6ae58c4 Try 6.7. 2025-02-03 21:00:40 -05:00
Thomas Harte
0375e47359 Accept any 6.8. 2025-02-03 20:48:56 -05:00
Thomas Harte
8450ad2856 Add Qt 6 CI. 2025-02-03 20:28:46 -05:00
Thomas Harte
015a1fbd53 Merge branch 'master' into Plus4PRGs 2025-02-03 00:06:14 -05:00
Thomas Harte
318b61b4d7
Merge pull request #1467 from TomHarte/QtCI
Add Qt to CI set.
2025-02-03 00:05:54 -05:00
Thomas Harte
60640517ca Remove crutch. 2025-02-02 23:58:25 -05:00
Thomas Harte
051e38f034 Reformat. 2025-02-02 23:48:20 -05:00
Thomas Harte
300054b9f7 Attempt a full make. 2025-02-02 23:46:26 -05:00
Thomas Harte
4f9e1e3a6b Add 'icu'. 2025-02-02 23:41:47 -05:00
Thomas Harte
856bc27bf1 Must specify qtbase, at least. 2025-02-02 23:36:15 -05:00
Thomas Harte
408b774b42 Switch to archives. 2025-02-02 23:29:59 -05:00
Thomas Harte
f82ef5aad4 Apply one of those architectures. 2025-02-02 23:23:34 -05:00
Thomas Harte
a92df41eb5 List architectures, hopefully. 2025-02-02 22:58:32 -05:00
Thomas Harte
605990929d Hit and hope. 2025-02-02 22:47:23 -05:00
Thomas Harte
925ca28659 Try without architecture. 2025-02-02 22:38:31 -05:00
Thomas Harte
41e3fa7aa7 Retreat. 2025-02-02 22:24:37 -05:00
Thomas Harte
5fa27448f8 Try to coax module names. 2025-02-02 22:19:44 -05:00
Thomas Harte
c3428bdaed Try without prefixes. 2025-02-02 22:11:32 -05:00
Thomas Harte
0539de9c4e Take a guess at modules. 2025-02-02 22:05:36 -05:00
Thomas Harte
eed87164b1 Attempt a Qt build action. 2025-02-02 22:00:21 -05:00
Thomas Harte
0fe726c503 Avoid overlong line. 2025-02-02 21:50:30 -05:00
Thomas Harte
f7f2113f1c
Merge pull request #1465 from TomHarte/Plus4FastLoad
Edge towards fast loading in the Plus 4.
2025-02-02 21:49:22 -05:00
Thomas Harte
49d931f5cc Disable Mac job for now. 2025-02-02 21:41:37 -05:00
Thomas Harte
4dbd63de08 Attempt version rebump. 2025-01-31 12:33:21 -05:00
Thomas Harte
3a53c349a0 Abandon attempts to build on older macOS for now. 2025-01-31 11:45:43 -05:00
Thomas Harte
a9fe5d5c87 Manually revert Xcode project version. 2025-01-30 22:48:11 -05:00
Thomas Harte
9cecccf5da Correct TAP type check. 2025-01-30 21:04:36 -05:00
Thomas Harte
6cb3bbaa2d Ensure tape ending != infinite loop. 2025-01-29 23:30:16 -05:00
Thomas Harte
0ff6a0bb53 Slightly simplify template arguments. 2025-01-29 22:51:02 -05:00
Thomas Harte
8ba57dec03 Take another stab at read_dipole. 2025-01-29 22:07:17 -05:00
Thomas Harte
d749c305ed Merge branch 'master' into Plus4FastLoad 2025-01-28 20:20:21 -05:00
Thomas Harte
f46ac2d0ed
Merge pull request #1464 from TomHarte/AheadOfTimeCRCTables
Grab bag: calculate CRC tables ahead of time; improve carry typing.
2025-01-28 20:16:03 -05:00
Thomas Harte
d7b7152315 Apply const liberally. 2025-01-28 18:26:34 -05:00
Thomas Harte
da1d52033b Use contractions. 2025-01-28 18:19:31 -05:00
Thomas Harte
01ddc24c02 Require overt acknowledgement of meaning. 2025-01-28 17:42:26 -05:00
Thomas Harte
53a3e88d16 Shunt CRC XOR table generation to compile time. 2025-01-28 17:36:32 -05:00
Thomas Harte
bc8d1cc384
Merge pull request #1463 from TomHarte/AcornAnalyserStyle
Tweak Acorn analyser style.
2025-01-26 21:58:50 -05:00
Thomas Harte
ed2ba63a5f Adjust style. 2025-01-26 21:42:38 -05:00
Thomas Harte
8a2c009653 Reduce copies, size()s, code duplication. 2025-01-26 21:34:01 -05:00
Thomas Harte
55690a4c7f Merge branch 'master' into Plus4FastLoad 2025-01-22 19:49:23 -05:00
Thomas Harte
161ad4b143
Merge pull request #1462 from TomHarte/VIAPortTemplate
Reduce repetitive dynamic work in 6522 usages.
2025-01-22 19:48:24 -05:00
Thomas Harte
d701990df4 Simplify ownership of the shift value. 2025-01-22 16:12:45 -05:00
Thomas Harte
95a2d1013c Remove unused dynamic dispatcher. 2025-01-22 16:06:09 -05:00
Thomas Harte
5d4f3c0b3e Remove already-done TODO. 2025-01-22 16:04:17 -05:00
Thomas Harte
f8e4023307 Reduce repetitive dynamic work in 6522 usages. 2025-01-22 15:57:03 -05:00
Thomas Harte
609aba7c73 Made various additional style improvements. 2025-01-22 13:47:25 -05:00
Thomas Harte
bc7ab0eba1 Extend parser, accelerate headers. 2025-01-21 22:37:10 -05:00
Thomas Harte
56f271c8ad Continue failing. This is the story of my life. 2025-01-21 17:24:12 -05:00
Thomas Harte
11190cff1d Eject zero-cost execution in favour of faulty HLE. 2025-01-21 16:45:05 -05:00
Thomas Harte
348a593dc1 Flail in attempt to implement fast loading. 2025-01-21 14:13:42 -05:00
Thomas Harte
b67bb50f4a
Merge pull request #1461 from TomHarte/Vic20Confidence
Include VIC hits in Vic-20 confidence selection.
2025-01-20 23:21:19 -05:00
Thomas Harte
1174f651ab Switch to logger, ignore 0xfdfx. 2025-01-20 22:47:54 -05:00
Thomas Harte
dad9777c3e
Merge pull request #1460 from TomHarte/Plus4DriveSelection
Add C1541 button to mac UI; respect Target setting.
2025-01-20 22:45:49 -05:00
Thomas Harte
4f6285a8e7 Include VIC hits in Vic-20 confidence selection. 2025-01-20 22:25:24 -05:00
Thomas Harte
20cecf4702 Add C1541 button to mac UI; respect Target setting. 2025-01-20 22:02:35 -05:00
Thomas Harte
5763eabff4
Merge pull request #1459 from TomHarte/UnitTestUpdates
Repair lagging unit tests.
2025-01-20 21:41:15 -05:00
Thomas Harte
0fc753949d Repair lagging unit tests. 2025-01-20 21:36:25 -05:00
Thomas Harte
083c1b7ca7
Merge pull request #1458 from TomHarte/Plus4PRGs
Introdice alternative tape timings for the +4.
2025-01-20 20:59:33 -05:00
Thomas Harte
8f6b1b11e5 Fix member name. 2025-01-20 20:35:14 -05:00
Thomas Harte
53b7d19c10 Ensure tape images proper destruct. 2025-01-20 20:33:17 -05:00
Thomas Harte
b0b4f5e51a Add Plus 4 to Qt UI. 2025-01-20 20:31:12 -05:00
Thomas Harte
b7414aa59c Improve logging. 2025-01-20 16:19:02 -05:00
Thomas Harte
f449045118 Add some basic attempts at dynamic analysis. 2025-01-20 16:15:53 -05:00
Thomas Harte
1e9ddada37 Retain tapes and disks. 2025-01-20 15:51:01 -05:00
Thomas Harte
55d59a1854 Separate chunk parsing. 2025-01-19 18:16:33 -05:00
Thomas Harte
beb9f38514 Eliminate std::shared_ptr. 2025-01-18 23:25:08 -05:00
Thomas Harte
00b1865fc8 Fix boundary condition. 2025-01-17 21:46:43 -05:00
Thomas Harte
0f545608c4 Fix serialiser ownership, Commodore analyser. 2025-01-17 21:43:11 -05:00
Thomas Harte
bde2047184 Provide target platform where serialiser will accept it. 2025-01-17 17:09:47 -05:00
Thomas Harte
0a22d8fb9e Add TODO on final dangling issue. 2025-01-17 17:06:03 -05:00
Thomas Harte
1bef37d504 Test data only once. 2025-01-17 17:04:25 -05:00
Thomas Harte
7f5d290b66 Promote validation. 2025-01-17 17:01:09 -05:00
Thomas Harte
9461e6f285 Move validation up a level. 2025-01-17 16:59:30 -05:00
Thomas Harte
3f59a03f29 Parse Commodore .tap header only once. 2025-01-17 16:54:54 -05:00
Thomas Harte
062b581b55 Move .cas and ZX .tap initial parsing out of serialiser. 2025-01-17 16:45:09 -05:00
Thomas Harte
58d3fdc1c2 Separate stateful serialisation from tapes. 2025-01-17 16:39:21 -05:00
Thomas Harte
2f546842a7 Wire TAPs and similar directly to their targets. 2025-01-16 21:21:15 -05:00
Thomas Harte
f089a85908 Zoom out to a more likely TV cropping. 2025-01-15 22:52:20 -05:00
Thomas Harte
a6e453a452 Introdice alternative tape timings for the +4. 2025-01-15 22:11:26 -05:00
Thomas Harte
3adf3dc547
Merge pull request #1457 from TomHarte/BadlinePriority
Give priority to initial bad lines.
2025-01-13 20:40:17 -05:00
Thomas Harte
1d0ea96ae9 This hasn't been true for a while. 2025-01-13 17:38:29 -05:00
Thomas Harte
a4cb17a1cb First bad lines take priority over second ones. 2025-01-13 17:31:18 -05:00
Thomas Harte
733da3161b
Merge pull request #1456 from TomHarte/CounterSets
Correct shift on hcount write.
2025-01-12 22:32:46 -05:00
Thomas Harte
1b1a0f553d Keep three least bits. 2025-01-12 22:07:56 -05:00
Thomas Harte
972619c1fe Correct shift on hcount write. 2025-01-12 21:59:22 -05:00
Thomas Harte
61086d5360
Merge pull request #1455 from TomHarte/MatchBefore
Restructure loop to perform events AT time, not upon reaching it.
2025-01-12 19:18:32 -05:00
Thomas Harte
37513d726c Restructure loop to perform events AT time, not upon reaching it. 2025-01-12 19:06:42 -05:00
Thomas Harte
6510bee327
Merge pull request #1454 from TomHarte/VideoTweaks
Restrict counter sizes; invert written horizontal counter.
2025-01-11 23:42:58 -05:00
Thomas Harte
cd36f3f096 Restrict counter sizes; invert written horizontal counter. 2025-01-11 23:19:01 -05:00
Thomas Harte
407a6f5e31
Merge pull request #1453 from TomHarte/AbsentBits
Correct status unset-bit masks.
2025-01-11 22:58:42 -05:00
Thomas Harte
2b28df280e Correct status unset-bit masks. 2025-01-11 22:47:56 -05:00
Thomas Harte
eb763ed82c
Merge pull request #1452 from TomHarte/TapeMotorAgain
Ensure tape motor is a combination of programmed state and button.
2025-01-10 21:04:19 -05:00
Thomas Harte
755f53cce0 Ensure tape motor is a combination of programmed state and button. 2025-01-10 16:59:09 -05:00
Thomas Harte
c190ab40b0
Merge pull request #1451 from TomHarte/Plus4Joystick
Add joystick input.
2025-01-09 17:12:37 -05:00
Thomas Harte
a3ad82de42 Add joystick input. 2025-01-09 17:01:20 -05:00
Thomas Harte
0f6cd6904d Separate keyboard and joystick masks.
Based on aside in https://plus4world.powweb.com/forum/6867#6868 ; I don't yet know what the joystick bits are.
2025-01-09 16:41:21 -05:00
Thomas Harte
79c89af6ea
Merge pull request #1450 from TomHarte/LatestWarnings
Resolve GCC ubuntu-latest build warnings.
2025-01-09 16:32:50 -05:00
Thomas Harte
56f10a9a52 Adjust ownership to avoid passing reference to uninitialised object. 2025-01-09 16:27:19 -05:00
Thomas Harte
c679e2c067 line_number is now unused. 2025-01-09 16:14:16 -05:00
Thomas Harte
0677987320 Ensure all paths return a value. 2025-01-08 22:30:32 -05:00
Thomas Harte
5fb6e6780c Eliminate unused variable (at least temporarily). 2025-01-08 22:30:17 -05:00
Thomas Harte
58ef91a7b1
Merge pull request #1449 from TomHarte/FullerAudio
Switch to full-clock PWM audio implementation, with noise generator.
2025-01-08 22:28:01 -05:00
Thomas Harte
e1ae65b6d1 Switch to PWM implementation, with noise. 2025-01-08 22:09:42 -05:00
Thomas Harte
65307186dc Provide full clock to audio. 2025-01-08 21:17:48 -05:00
Thomas Harte
6fa29c204b Extend PAL/NTSC selection to audio. 2025-01-08 20:27:37 -05:00
Thomas Harte
8219beeb1a
Merge pull request #1448 from TomHarte/6502Macros
Eliminate macros from 6502 bus operation actions.
2025-01-08 20:19:09 -05:00
Thomas Harte
ace7e24dfb Eliminate Objective-C-style naming. 2025-01-07 22:55:19 -05:00
Thomas Harte
828c2a6883 Convert macros to functions. 2025-01-07 22:51:52 -05:00
Thomas Harte
f195dc313d Strongly type BusOperation. 2025-01-07 22:48:17 -05:00
Thomas Harte
5b8a005f41
Merge pull request #1447 from TomHarte/CounterAccess
Add programmatic access to video counters.
2025-01-07 22:33:28 -05:00
Thomas Harte
9feb75e645 Force high unwriteable bytes. 2025-01-07 22:21:42 -05:00
Thomas Harte
7f8e90bd29 Add video counter writes. 2025-01-07 21:56:04 -05:00
Thomas Harte
2fd34b649d Add missing video counter reads. 2025-01-07 21:40:20 -05:00
Thomas Harte
104054ed1a
Merge pull request #1446 from TomHarte/Plus4VideoOptions
Add selectable display type.
2025-01-07 20:15:21 -05:00
Thomas Harte
457b28c22c Merge branch 'master' into Plus4VideoOptions 2025-01-07 20:08:54 -05:00
Thomas Harte
095c8dcd0c
Merge pull request #1445 from TomHarte/Plus4Tapes
Improve support for C16 TAP files.
2025-01-07 20:07:38 -05:00
Thomas Harte
8463e9ed94 Add selectable display type. 2025-01-07 17:41:43 -05:00
Thomas Harte
b6278c6144 Remove debugging cruft. 2025-01-06 22:17:53 -05:00
Thomas Harte
1c1e1eee47 Double clock for all non-C16s. 2025-01-06 22:04:57 -05:00
Thomas Harte
b37ed9ec60 Take yet another stab at wave/half-waves. 2025-01-06 21:40:46 -05:00
Thomas Harte
45f3ef6920 Guess that all C16-style files are 'half wave'. 2025-01-06 17:26:51 -05:00
Thomas Harte
2cd6c4238b Quieten logging. 2025-01-05 22:52:40 -05:00
Thomas Harte
f6ed0b33eb Diagnose current scrolling colour fault; hack in graphics-mode fix. 2025-01-05 22:48:16 -05:00
Thomas Harte
c4f4ca3f90 Add asserts. 2025-01-05 22:45:09 -05:00
Thomas Harte
9a6780616b Close unpainted gaps. 2025-01-05 22:25:35 -05:00
Thomas Harte
db4eca0a42 Hack forward to a woring Mad Rally. 2025-01-05 22:08:20 -05:00
Thomas Harte
6d674edb48 Merge remote-tracking branch 'origin/master' into Plus4Tapes 2025-01-05 22:07:26 -05:00
Thomas Harte
c0469a044b Add missing address warnings. 2025-01-05 21:10:23 -05:00
Thomas Harte
b9b64eba9a Map all missing registers. 2025-01-05 21:06:46 -05:00
Thomas Harte
2d74387a00 Extend TAP support for C16. 2025-01-05 20:37:39 -05:00
Thomas Harte
f66b6fc20c Attempt support for C16 TAPs. 2025-01-05 08:51:20 -05:00
Thomas Harte
f0711a9fbc Use detection for play button; allow computer to set motor. 2025-01-04 22:54:13 -05:00
Thomas Harte
83a8c7215a
Merge pull request #1444 from TomHarte/AudioQueueTransients
Treat kAudioQueueErr_CannotStart as ephemeral.
2025-01-04 22:39:56 -05:00
Thomas Harte
a86f966cb4 Treat kAudioQueueErr_CannotStart as ephemeral. 2025-01-04 22:24:34 -05:00
Thomas Harte
74db978b81 Fix automatic tape motor control. 2025-01-04 22:23:46 -05:00
Thomas Harte
1300546a52
Merge pull request #1443 from TomHarte/TypingSpeed
Improve +4 typing speed.
2025-01-04 19:15:55 -05:00
Thomas Harte
3aeb0bba71 Improve +4 typing speed. 2025-01-04 15:14:25 -05:00
Thomas Harte
03d3efa323
Merge pull request #1442 from TomHarte/38columns
Progress shifter outside of painted pixels.
2025-01-04 07:20:56 -05:00
Thomas Harte
f9c220bee0 Progress shifter outside of painted pixels. 2025-01-04 07:15:20 -05:00
Thomas Harte
114c2e2636
Merge pull request #1441 from TomHarte/OSSGuardNoMacro
Remove macros from CoreAudio handler.
2025-01-03 23:38:35 -05:00
Thomas Harte
75a0e622ad Remove macros from CoreAudio handler. 2025-01-03 23:25:38 -05:00
Thomas Harte
8e2de4ee30
Merge pull request #1440 from TomHarte/MenuTweak
Improve menu wording.
2025-01-03 23:23:50 -05:00
Thomas Harte
b1602261cf Remove redundant adjective. 2025-01-03 23:23:02 -05:00
Thomas Harte
e5ed11f8ec Take another swing at menu item naming. 2025-01-03 23:21:55 -05:00
Thomas Harte
c1ecfd289e
Merge pull request #1438 from TomHarte/Plus4UI
Add Plus 4 to Mac UI.
2025-01-03 23:10:45 -05:00
Thomas Harte
c5cca15b4e Extend window size. 2025-01-03 23:01:33 -05:00
Thomas Harte
fa978315e6 Add Plus 4 option to Mac UI. 2025-01-03 22:59:39 -05:00
Thomas Harte
c5bffc38f4 Switch typedefs to usings. 2025-01-03 21:35:34 -05:00
Thomas Harte
88b5f6b148
Merge pull request #1437 from TomHarte/Plus4Typer
Add TED typer.
2025-01-03 21:31:56 -05:00
Thomas Harte
fc04742151 Pull input from the typer. 2025-01-03 21:03:56 -05:00
Thomas Harte
c618d18d46 Allow typers to be attached. 2025-01-03 20:29:19 -05:00
Thomas Harte
33bc7c00df Eliminate long-ago use of typedef. 2025-01-03 20:29:05 -05:00
Thomas Harte
1ed550d7f9
Merge pull request #1434 from TomHarte/Plus4Startup
Add a simulacrum of C16+4 emulation.
2025-01-03 20:11:08 -05:00
Thomas Harte
18b87f2c80 Keep a little more state outside the main loop. 2025-01-03 20:05:43 -05:00
Thomas Harte
fad503ca80 Use correct source for bitmap address. 2025-01-03 17:31:27 -05:00
Thomas Harte
37ec3e4605 Simplify flash/inversion handling. 2025-01-03 17:27:43 -05:00
Thomas Harte
70e3d23f26 Add note to self. 2025-01-02 23:10:17 -05:00
Thomas Harte
6ebf415a52 Improve invert and flash support. 2025-01-02 23:04:35 -05:00
Thomas Harte
5c31104d0e Simplify control flow, half-obey 256-character flag. 2025-01-02 22:39:23 -05:00
Thomas Harte
aed8b65e2b Mark extra constexprs. 2025-01-02 22:01:51 -05:00
Thomas Harte
906e8aa2b2 Move nullptr check to bottom of pipeline. 2025-01-02 21:02:11 -05:00
Thomas Harte
d0703f95af Normalise adjective/noun. 2025-01-02 16:01:47 -05:00
Thomas Harte
538b00797d Flatten structure. 2025-01-02 16:00:47 -05:00
Thomas Harte
985c555518 Support multicolour text. 2025-01-02 15:56:39 -05:00
Thomas Harte
5ef26a25ee Fix shift timing. 2025-01-02 15:52:20 -05:00
Thomas Harte
3db0e30d12 Factor out 1bpp and 2bpp pixel generation. 2025-01-02 15:39:45 -05:00
Thomas Harte
a666cabae9 Support extended colour text mode. 2025-01-02 15:32:44 -05:00
Thomas Harte
2e8d9018ef Add other address modes. 2025-01-02 15:09:37 -05:00
Thomas Harte
ae49505e67 Attempt multicolour bitmap mode. 2025-01-02 15:07:22 -05:00
Thomas Harte
09bd5503b4 Fetch pixels earlier for a mostly-working high-resolution output. 2025-01-02 14:42:49 -05:00
Thomas Harte
dbe733524c Reintroduce RDY control. 2025-01-02 13:49:24 -05:00
Thomas Harte
1625f5c0f9 Take a stab at high-resolution bitmap mode. 2025-01-02 13:46:35 -05:00
Thomas Harte
7cd49d07f2 Collapse to a single function. 2025-01-02 13:39:50 -05:00
Thomas Harte
a542345456 Iterate towards supporting all video modes. 2025-01-01 22:34:47 -05:00
Thomas Harte
0b98f21443 Enable extra functionality. 2025-01-01 22:34:30 -05:00
Thomas Harte
c42e231e99 Attempt fully to support x scroll. 2025-01-01 21:47:59 -05:00
Thomas Harte
ab653af4b3 Introduce pixel-level shifter. 2025-01-01 20:58:06 -05:00
Thomas Harte
8653f572c8 Move static_assert. 2025-01-01 15:06:00 -05:00
Thomas Harte
39b431fb19 Rejig marginally. 2024-12-31 22:14:02 -05:00
Thomas Harte
e158c5bc30 Extend shifting to final column of screen. 2024-12-31 20:33:27 -05:00
Thomas Harte
d0fdfda4cb Set volume peak, count up rather than down. 2024-12-31 11:45:32 -05:00
Thomas Harte
668a5ca041 Make a real attempt at some degree of audio. 2024-12-31 10:25:11 -05:00
Thomas Harte
5e3947b8bc Evaluate address lazily. 2024-12-31 08:28:42 -05:00
Thomas Harte
233a627c9f Correct longstanding confusion over character fetch. 2024-12-31 08:25:05 -05:00
Thomas Harte
6197486d49 Use increment_video_counter_ to trigger fetch. 2024-12-31 08:06:04 -05:00
Thomas Harte
60856b974b Add wiring for audio. 2024-12-30 22:56:29 -05:00
Thomas Harte
d1eca5dc21 Expand mask. 2024-12-30 21:29:36 -05:00
Thomas Harte
24b281d625 Use namespace; attempt to avoid false characters. 2024-12-30 09:14:27 -05:00
Thomas Harte
b0d1dee38b Add missing header. 2024-12-29 22:36:14 -05:00
Thomas Harte
0751c51803 Dispatch warning. 2024-12-29 22:34:32 -05:00
Thomas Harte
0005229c1e Improve header. 2024-12-29 22:21:38 -05:00
Thomas Harte
33c2353107 Alter motor control detection. 2024-12-29 22:21:02 -05:00
Thomas Harte
cb98297bb5 Add consts. 2024-12-29 22:14:48 -05:00
Thomas Harte
0e663e1da8 Add C1541 activity indicator. 2024-12-29 21:30:53 -05:00
Thomas Harte
918a8c5f8b Fix 1541 clocking, invert levels again. 2024-12-29 08:41:37 -05:00
Thomas Harte
34938e8c62 Fully connect serial port. 2024-12-28 23:07:01 -05:00
Thomas Harte
da6efe52ff Accept implicit bool, eliminate rom name repetition. 2024-12-28 22:22:28 -05:00
Thomas Harte
570f1caa8f Attempt also to integrate a C1541. 2024-12-28 21:49:04 -05:00
Thomas Harte
6f638805f7 Further eliminate std::shared_ptr connections. 2024-12-28 20:52:31 -05:00
Thomas Harte
c92b0dc886 Start normalising Commodore serial bus interface. 2024-12-28 20:11:27 -05:00
Thomas Harte
b4b216de84 Attempt press-play feedback. 2024-12-27 21:43:41 -05:00
Thomas Harte
80f5d7c735 Attempt full tape input. 2024-12-27 21:19:41 -05:00
Thomas Harte
01aeb46664 Bump version. 2024-12-27 20:59:24 -05:00
Thomas Harte
2cfd2ff624 Start adding tape player. 2024-12-27 20:59:07 -05:00
Thomas Harte
ff12bbbdb6 Simplify delay state. 2024-12-27 17:10:25 -05:00
Thomas Harte
e19fe5d0e2 Reintroduce colour burst. 2024-12-27 09:25:36 -05:00
Thomas Harte
9670d5f4de Add cursor, proving things generally to be off by one. 2024-12-27 09:20:12 -05:00
Thomas Harte
863b09c39b Increase key bindings. 2024-12-27 09:14:23 -05:00
Thomas Harte
6b90de539e Add data delays. 2024-12-26 22:39:16 -05:00
Thomas Harte
4bb53ed6ba Start trending towards an FPGA-inspired implementation. 2024-12-26 22:33:09 -05:00
Thomas Harte
e6523f3ec1 Shuffle colour conversion moment; move ownership of clock rate. 2024-12-20 09:17:40 -05:00
Thomas Harte
c8ad8c79bd Factor in x_scroll_. 2024-12-18 22:14:57 -05:00
Thomas Harte
b08cc9cb49 Use attributes, attempt real cursor. 2024-12-18 22:07:37 -05:00
Thomas Harte
4f93dc0adf Support attribute bytes. 2024-12-18 22:02:05 -05:00
Thomas Harte
096f48be33 Fix top line of cursor, add pretend cursor, page video separately. 2024-12-18 21:48:03 -05:00
Thomas Harte
81398d58a2 Improve get_rect_for_area, use in C16. 2024-12-18 20:53:03 -05:00
Thomas Harte
7466da5651 Add Keyboard. 2024-12-18 07:04:17 -05:00
Thomas Harte
c8fdde4c5e Clarify clock rates. 2024-12-17 07:08:04 -05:00
Thomas Harte
15583e7975 Avoid risk of unbounded memory consumption. 2024-12-16 22:29:23 -05:00
Thomas Harte
5acdf39566 Use a normative, unique_ptr-based cache. 2024-12-16 22:12:12 -05:00
Thomas Harte
2240ada5db Avoid going out of bounds below. 2024-12-16 22:11:49 -05:00
Thomas Harte
9d5c10d440 Edge towards realistic video collection. 2024-12-16 22:11:06 -05:00
Thomas Harte
709f350d60 Begin usage of RDY. 2024-12-16 07:18:07 -05:00
Thomas Harte
3d7e016b42 Name horizontal events. 2024-12-15 08:46:07 -05:00
Thomas Harte
b76104d145 Start edging towards proper video timing. 2024-12-14 22:06:18 -05:00
Thomas Harte
702b6d6567 Add extra note to self. 2024-12-14 11:56:13 -05:00
Thomas Harte
3e93004db6 Double nominal clock, to hit normative values. 2024-12-14 11:55:10 -05:00
Thomas Harte
589903c43c Add safety rail. 2024-12-13 23:14:00 -05:00
Thomas Harte
f41b54de21 Make a close-enough guess at chrominances. 2024-12-13 22:25:23 -05:00
Thomas Harte
700b848f26 Add overt nils to aid with debugging. 2024-12-13 21:25:01 -05:00
Thomas Harte
a1f6e93e22 Add most of the keyboard. 2024-12-13 21:24:11 -05:00
Thomas Harte
1628af2ffc Provide a stuck down key 'a'. 2024-12-13 17:56:47 -05:00
Thomas Harte
1b5d446635 Fix: writes to interrupt status _clear_. 2024-12-13 17:22:05 -05:00
Thomas Harte
83a9ef772a Add TODO explaining all currently-unhandled writes. 2024-12-13 14:02:33 -05:00
Thomas Harte
1d9c3fb827 Hack in some text output. 2024-12-13 13:56:12 -05:00
Thomas Harte
ff92bdb324 Add buffer for pixels, output _something_. 2024-12-13 13:31:15 -05:00
Thomas Harte
363ad7342a Add memory fuzzing, some text output. 2024-12-12 22:59:20 -05:00
Thomas Harte
663acd3810 Map initial border colour, white and black. 2024-12-12 22:35:50 -05:00
Thomas Harte
c2fc26089e Add background colour reading, fix writing. 2024-12-12 22:15:15 -05:00
Thomas Harte
58b464bdfc Attempt to add interrupts. 2024-12-12 22:07:51 -05:00
Thomas Harte
ed766c74e6 Add some paging. 2024-12-12 21:17:28 -05:00
Thomas Harte
1d07b8238c Add a crop rectangle. 2024-12-12 17:36:44 -05:00
Thomas Harte
41c6ed7c5a Further restrict 'active' area of the display. 2024-12-12 17:33:11 -05:00
Thomas Harte
f7750af3d0 Provide bus visibility to video; mark vertical portion of display. 2024-12-11 22:32:14 -05:00
Thomas Harte
8854ffddee Include possible clock divider. 2024-12-11 21:57:31 -05:00
Thomas Harte
a487619578 Track basic frame events. 2024-12-11 21:54:03 -05:00
Thomas Harte
0eab6146fc Introduce a CRT. 2024-12-11 21:38:32 -05:00
Thomas Harte
389ba95e5a Template out the usual repetitive stuff of segment finding. 2024-12-11 21:30:58 -05:00
Thomas Harte
84d178c0ca Transcribe event times into [mostly] non-action. 2024-12-11 17:32:51 -05:00
Thomas Harte
aed8f8efa8 Transcribe some timing numbers. 2024-12-10 22:56:14 -05:00
Thomas Harte
38325741de Forward address information to a video stub. 2024-12-10 21:29:17 -05:00
Thomas Harte
891d5c2066 Separate out TED calls, to aid with logging. 2024-12-10 18:07:07 -05:00
Thomas Harte
6b7edac6e4 Add timers. 2024-12-10 18:04:10 -05:00
Thomas Harte
ab2a576e1b Merge branch 'master' into Plus4Startup 2024-12-09 22:23:24 -05:00
Thomas Harte
5a3e4dd47b
Merge pull request #1431 from TomHarte/UniqueSectors
Eliminate use of std::shared_ptr for tracks on both sides of DiskImageHolder.
2024-12-09 22:23:01 -05:00
Thomas Harte
064c4b4312 Add some logging. 2024-12-09 22:22:20 -05:00
Thomas Harte
cbde504057 Add a memory map of sorts and a 6502. 2024-12-09 17:46:31 -05:00
Thomas Harte
949cfcfa69 Load ROMs. 2024-12-09 17:31:00 -05:00
Thomas Harte
06a005321e
Merge pull request #1433 from TomHarte/DeclareFieldsAfresh
Use `std::once` in preference to home-rolled solution.
2024-12-08 22:46:21 -05:00
Thomas Harte
d3587f595f
Merge pull request #1432 from TomHarte/Plus4ROMs
Add definitions for some of the Plus 4 ROMs.
2024-12-08 22:42:02 -05:00
Thomas Harte
b0158ed7ce Use std::once in preference to home-rolled solution. 2024-12-08 22:35:41 -05:00
Thomas Harte
e5f4300e54 Add definitions for some of the Plus 4 ROMs. 2024-12-08 22:03:44 -05:00
Thomas Harte
7cc6f8604e Eliminate std::shared_ptr outside of DiskImageHolder. 2024-12-08 21:49:34 -05:00
Thomas Harte
657960e7d0 Eliminate use of std::shared_ptr at DiskImage and below. 2024-12-08 21:26:03 -05:00
Thomas Harte
aecd7f9283
Merge pull request #1427 from TomHarte/CommodoreAnalyser
Improve analysis of Commodore BASIC
2024-12-08 18:59:03 -06:00
Thomas Harte
6f1b30cd24 Include Plus 4 in test target. 2024-12-07 11:56:16 -06:00
Thomas Harte
a6ba549b67 Don't repeat constant. 2024-12-07 11:55:38 -06:00
Thomas Harte
84ea04f61d Improve comments. 2024-12-07 11:54:55 -06:00
Thomas Harte
0d52cf5f97 Improve constiness. 2024-12-07 11:50:18 -06:00
Thomas Harte
b15a083a15 Switch to a non-macro route for startup declarations. 2024-12-07 10:15:38 -06:00
Thomas Harte
a128247ef5 Improve comments. 2024-12-07 09:32:45 -06:00
Thomas Harte
590bd934c0 Use built-in paralel for. 2024-12-07 09:32:32 -06:00
Thomas Harte
e9826d2e7e Use launch-time declarations. 2024-12-06 16:03:46 -05:00
Thomas Harte
58ed63cd18 Consciously uglify. These shouldn't look like functions. 2024-12-06 16:03:24 -05:00
Thomas Harte
fd1bd3032f Attempt to move towards at-launch field declaration.
This avoids any need for overt thread safety in mutations.
2024-12-06 16:00:03 -05:00
Thomas Harte
d7206096ea Attempt to parallelise. 2024-12-06 15:59:47 -05:00
Thomas Harte
f43e594eca Improve style: indentation, constness, names. 2024-12-06 15:29:49 -05:00
Thomas Harte
ea4fe5e809 Propagate Plus 4 to other project files. 2024-12-06 15:20:58 -05:00
Thomas Harte
9fcb634510 Route +4 software into a non-functional +4. 2024-12-06 15:17:49 -05:00
Thomas Harte
c14a4515ce Add consts widely. 2024-12-06 15:08:21 -05:00
Thomas Harte
8e71180cd2 Add an empty shell of a C16+4 class. 2024-12-06 13:53:08 -05:00
Thomas Harte
08f98aa32f Decrease indentation. 2024-12-06 13:52:42 -05:00
Thomas Harte
e8aa9b9eb2 Avoid overrun on empty file. 2024-12-06 13:37:06 -05:00
Thomas Harte
19f815eeff Infer more from first file starting address; reduce reallocations. 2024-12-06 13:34:35 -05:00
Thomas Harte
a508f7a463 Process all files if provided with a disk or tape. 2024-12-06 13:24:15 -05:00
Thomas Harte
e58d3ee060 Remove file. 2024-12-05 22:13:02 -05:00
Thomas Harte
268842681a Adjust move semantics. 2024-12-05 22:09:07 -05:00
Thomas Harte
9b357a9fbf Reduce copying. 2024-12-05 22:05:03 -05:00
Thomas Harte
6f80018b6e Reintroduce argument. 2024-12-05 22:04:39 -05:00
Thomas Harte
7a1153be65 Improve loop detection. 2024-12-05 17:30:30 -05:00
Thomas Harte
85d4c24aba Restore parameter name. 2024-12-05 17:29:05 -05:00
Thomas Harte
48c0ae8fe4 Avoid being thrown by looping BASIC. 2024-12-05 17:28:47 -05:00
Thomas Harte
e835b2c68c Merge branch 'master' into CommodoreAnalyser 2024-12-04 22:56:05 -05:00
Thomas Harte
10d20f5d09
Merge pull request #1429 from TomHarte/FarewellMacOS12
Remove deprecated macos-12 from CI; add macos-15.
2024-12-04 22:55:06 -05:00
Thomas Harte
88b31ea940
Merge pull request #1428 from TomHarte/MoreIndentation
Adjust more dangling indentation changes.
2024-12-04 22:40:30 -05:00
Thomas Harte
9cb28d23a3 Remove deprecated macos-12; add macos-15. 2024-12-04 22:39:29 -05:00
Thomas Harte
ce5aae3f7d Adjust more dangling indentation changes. 2024-12-04 22:29:08 -05:00
Thomas Harte
e7f0eb6746 Avoid invalid accesses. 2024-12-04 22:04:00 -05:00
Thomas Harte
65a118d1f3 Attempt to locate and disassemble machine code. 2024-12-04 21:41:05 -05:00
Thomas Harte
3d2eefc7e7
Merge pull request #1426 from TomHarte/C16Plus4Analysis
Begin Plus 4 analyser work, triggering clean-up of tape file classes.
2024-12-03 23:16:10 -05:00
Thomas Harte
f804c32eee Opportunistically const. 2024-12-03 22:57:38 -05:00
Thomas Harte
b89ecadc3a Improve interface. 2024-12-03 22:54:29 -05:00
Thomas Harte
6d4ff0b89a Finally eliminate all that virtual_ nonsense. 2024-12-03 22:28:57 -05:00
Thomas Harte
598003ea39 Continue marking override. 2024-12-03 21:18:26 -05:00
Thomas Harte
6ef63790a9 Mark overrides, improve constiness. 2024-12-03 17:33:09 -05:00
Thomas Harte
3ffd986a1c Start building Commodore analyser tests. 2024-12-03 09:25:58 -05:00
Thomas Harte
0b5cd4c665 Lock all tape classes down to read-only. 2024-12-03 09:21:13 -05:00
Thomas Harte
0371b0507a Avoid potential extending run-out-of-bounds. 2024-12-03 09:19:23 -05:00
Thomas Harte
9fa71231c4 Support zero-length files; further fix bounds checks. 2024-12-02 17:23:50 -05:00
Thomas Harte
32beafc12d Test Plus 4 detectionl; add shout for additional start address. 2024-12-02 15:27:37 -05:00
Thomas Harte
09e2ef334b Fix sign of bounds check. 2024-12-02 15:27:03 -05:00
Thomas Harte
c0ce62ed2b
Merge pull request #1425 from TomHarte/MoreIndentation
Take another big swing at indentation, some `const`s.
2024-12-01 22:02:25 -05:00
Thomas Harte
d3ed485e7a Take another big swing at indentation, some consts. 2024-12-01 21:44:14 -05:00
Thomas Harte
31c878b654
Merge pull request #1424 from TomHarte/InstructionSetFormatting
Improve formatting, `const`ness in instruction sets.
2024-12-01 20:24:55 -05:00
Thomas Harte
3a0f4a0bfc Improve constness, formatting. 2024-12-01 18:09:19 -05:00
Thomas Harte
8b88d1294d Remove errant spaces. 2024-12-01 09:04:32 -05:00
Thomas Harte
43fcf46d69 Limit line lengths. 2024-12-01 09:00:29 -05:00
Thomas Harte
394fe0f1f1 Improve formatting, constness in 68k and ARM instruction set implementations. 2024-12-01 08:20:24 -05:00
Thomas Harte
872921f635
Merge pull request #1423 from TomHarte/InputsFormatting
Roll formatting and `const` tweaks into Inputs.
2024-11-30 19:25:50 -05:00
Thomas Harte
7248470950 Roll formatting and const tweaks into Inputs. 2024-11-30 18:57:56 -05:00
Thomas Harte
9d87296316
Merge pull request #1422 from TomHarte/MutableLevels
Correct improper application of `const`.
2024-11-30 17:53:56 -05:00
Thomas Harte
23c67f7e38 Correct improper application of const. 2024-11-30 17:32:16 -05:00
Thomas Harte
bd98d95dbf
Merge pull request #1421 from TomHarte/FurtherFormatting
Further improve formatting.
2024-11-30 17:22:00 -05:00
Thomas Harte
3addb8d72b Finish updating components. 2024-11-30 17:21:00 -05:00
Thomas Harte
5545906063 Adopt new indentation, improve constness. 2024-11-30 15:53:58 -05:00
Thomas Harte
36edfe9715
Merge pull request #1420 from TomHarte/NewIcon
Import new icons.
2024-11-29 23:10:31 -05:00
Thomas Harte
030b54a2b6 Import new icons. 2024-11-29 23:04:28 -05:00
Thomas Harte
f332613922
Merge pull request #1419 from TomHarte/Reformatting
Begin a general reformatting.
2024-11-29 22:52:29 -05:00
Thomas Harte
088bc14b11 Begin a reformatting of components. 2024-11-29 22:43:54 -05:00
Thomas Harte
86fa8da8c5 Reformat ClockReceiver. 2024-11-29 22:12:57 -05:00
Thomas Harte
abfc73299e Update remainder of 'Analyser'. 2024-11-29 21:08:35 -05:00
Thomas Harte
bd97fd5973 Improve indentation, constness of 'Activity' and dynamic analyser. 2024-11-29 17:23:05 -05:00
Thomas Harte
f00e7c4a80
Merge pull request #1413 from TomHarte/ShakerScanTarget
Avoid taking an out-of-range pointer.
2024-10-19 10:15:30 -04:00
Thomas Harte
49c811b5f5 Avoid taking an out-of-range pointer.
(Even though it was safe)
2024-10-19 10:12:51 -04:00
699 changed files with 39039 additions and 32788 deletions

View File

@ -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

View File

@ -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) {}
};
}

View File

@ -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;
};
}

View File

@ -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;
};
}

View File

@ -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);

View File

@ -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_;
};
}

View File

@ -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_);
}

View File

@ -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_;
};
}

View File

@ -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_;
};
}

View File

@ -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_;
};
}

View File

@ -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);

View File

@ -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;
};
}

View File

@ -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();
}

View File

@ -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_;
};
}

View File

@ -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);
});

View File

@ -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;
};
/*!

View File

@ -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();

View File

@ -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;
};
}

View File

@ -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.

View File

@ -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;
};
}

View File

@ -25,6 +25,7 @@ enum class Machine {
MasterSystem,
MSX,
Oric,
Plus4,
PCCompatible,
Vic20,
ZX8081,

View File

@ -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;
}

View File

@ -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> &);
}

View File

@ -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 {

View File

@ -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);
}

View File

@ -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));
}
}

View 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 &);
}

View File

@ -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() {}
};
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}
};

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}
};

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}
};

View File

@ -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;

View File

@ -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);
}

View File

@ -8,7 +8,7 @@
#pragma once
#include "../StaticAnalyser.hpp"
#include "Analyser/Static/StaticAnalyser.hpp"
namespace Analyser::Static::Atari2600 {

View File

@ -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 {};

View File

@ -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);
}

View File

@ -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);
}
};

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View 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> &);
}

View File

@ -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;
}

View File

@ -29,8 +29,6 @@ struct File {
Relative
} type;
std::vector<uint8_t> data;
bool is_basic();
};
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}
};

View File

@ -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
);
}

View File

@ -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);
};

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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";
}

View File

@ -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);
}

View File

@ -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);
}
};

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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) {}
};

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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> &);
}

View File

@ -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);
}
};

View File

@ -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) {

View File

@ -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);
}

View File

@ -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);
}
};

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}
}

View 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 &);
}

View File

@ -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);
}
};

View File

@ -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 {};

View File

@ -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);
}

View File

@ -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);
}
};

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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(),

View File

@ -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.

View File

@ -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));

View File

@ -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);
}

View File

@ -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);
}
};

View File

@ -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) {

View File

@ -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);
}

View File

@ -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);
}
};

View File

@ -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})

View File

@ -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_;
};

View File

@ -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());
}
};
}

View File

@ -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_;
};

View File

@ -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