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