1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-10-25 09:27:01 +00:00

Compare commits

...

377 Commits

Author SHA1 Message Date
Thomas Harte
94359e9c75 Merge pull request #458 from TomHarte/ApplePhase
Corrects NTSC Q phase
2018-06-03 08:11:43 -04:00
Thomas Harte
076fa55651 Corrects: flux set is no-flux incoming.
This restores good sleeping behaviour.
2018-06-03 08:11:17 -04:00
Thomas Harte
d380595ad4 Unrolls the loops for slightly fewer conditionals. 2018-06-03 07:27:03 -04:00
Thomas Harte
d84b8700a3 Switches the Apple II to one byte per pixel.
Just trying to get it right for now; optimisation to come.
2018-06-02 22:03:45 -04:00
Thomas Harte
80b281d9f1 Switches back to whole bytes per pixel, owing to persistent precision problems at 1bpp.
Also fixes the inaccurately-named `cycles_since_update_`.
2018-06-02 18:25:00 -04:00
Thomas Harte
69dc3cc4d8 Switches to using the same varying for byte and subpixel selection. 2018-06-01 22:52:29 -04:00
Thomas Harte
1a9cea050e Minor: ensure AY registers *read* as 0 from reset, as well as being 0. 2018-06-01 19:48:42 -04:00
Thomas Harte
0833412df9 Corrects port for ZON-X reads. 2018-06-01 19:45:37 -04:00
Thomas Harte
35e84ff1a8 Corrects NTSC quadrature phase. 2018-05-31 21:40:46 -04:00
Thomas Harte
8dd7c6ef23 Eliminates 'reversed_c' as I no longer believe low-resolution colour numbers are reversed.
Also gets explicit about phase.
2018-05-29 22:30:45 -04:00
Thomas Harte
a26ab7086d Merge pull request #456 from TomHarte/TristateSleeper
Commutes `Sleeper` to `ClockingHint::Source`
2018-05-28 18:25:21 -04:00
Thomas Harte
b2464598d0 Forces the Apple II bus handler call inline. 2018-05-28 18:21:01 -04:00
Thomas Harte
6812a001d8 Teaches the Oric to apply a lighter Disk II touch when possible. 2018-05-28 18:20:43 -04:00
Thomas Harte
6c16754a6b Strips further improper constexprs. 2018-05-28 17:48:55 -04:00
Thomas Harte
75f9e3caeb Resolves incorrect bracketing. 2018-05-28 17:48:35 -04:00
Thomas Harte
ad5afe21ee Removes constexpr from things which are not const. Duh. 2018-05-28 17:28:57 -04:00
Thomas Harte
8a566cc1dd Experimentally goes to town on constexpr. 2018-05-28 17:20:11 -04:00
Thomas Harte
928aab13dc Introduces more granular clocking announcements to the Disk II.
As well as making it accept the clock rate it'll actually receive, to supply to the drives, so that they spin at the proper speed.
2018-05-28 17:19:29 -04:00
Thomas Harte
f3fe711542 Attempts to reduce FDC costs. 2018-05-27 23:55:04 -04:00
Thomas Harte
db8d8d8404 Commutes Sleeper to ClockingHint::Source, making state more granular. 2018-05-27 23:17:06 -04:00
Thomas Harte
6220ccb5d3 Merge pull request #455 from TomHarte/HumptyDumpty
Relaxes .p validation even further
2018-05-27 17:01:07 -04:00
Thomas Harte
20843305dd Removes unused calculation of vars. 2018-05-27 13:31:30 -04:00
Thomas Harte
8f6c0f6a8d Eliminates vars test.
At least Humpty Dumpty is a working .p that doesn't satisfy the test.
2018-05-26 19:05:35 -04:00
Thomas Harte
ede2df7e70 Merge pull request #452 from TomHarte/NIBWriting
Adds write support for NIBs
2018-05-25 18:40:35 -04:00
Thomas Harte
d45231c1a8 Introduces an additional validation test.
Thereby satisfying the TODO.
2018-05-25 18:40:15 -04:00
Thomas Harte
772812b35f Corrects improper textual reference to interface names. 2018-05-25 18:31:20 -04:00
Thomas Harte
f443fd44b5 Introduces support for writing NIBs. 2018-05-25 18:30:55 -04:00
Thomas Harte
79c60b8984 Adds necessary import for memcpy. 2018-05-24 21:58:50 -04:00
Thomas Harte
2dc2c2ce79 Merge pull request #449 from TomHarte/WOZWriting
Implements write support for WOZ files
2018-05-24 21:48:28 -04:00
Thomas Harte
523ca3264b Implements write support for WOZ files. 2018-05-24 21:44:31 -04:00
Thomas Harte
4036c60b45 Merge branch 'master' into WOZWriting 2018-05-24 19:01:04 -04:00
Thomas Harte
7d652e53e2 Merge pull request #450 from TomHarte/OricMicrodisc
Corrects meaning of the Microdisc's paging control.
2018-05-24 18:58:07 -04:00
Thomas Harte
7c3dd55e5c Corrects typo and improves exposition. 2018-05-24 18:57:35 -04:00
Thomas Harte
1b43381be0 Corrects meaning of the Microdisc's paging control. 2018-05-24 18:53:02 -04:00
Thomas Harte
8f78e5039e Factors out track seeking. 2018-05-24 18:45:00 -04:00
Thomas Harte
57ee6d4e41 Merge branch 'master' into WOZWriting 2018-05-23 22:32:30 -04:00
Thomas Harte
2868b1eca7 Adds missing import for memcpy. 2018-05-23 22:31:35 -04:00
Thomas Harte
a4d7703efd Adds missing #include. 2018-05-23 22:28:00 -04:00
Thomas Harte
ca4bc92c33 Adds WOZ CRC checking. 2018-05-23 22:22:17 -04:00
Thomas Harte
853261364e Generalised CRC generation and created specific subclasses for the CCITT CRC16 and CRC32. 2018-05-23 22:21:57 -04:00
Thomas Harte
d3c5e4267f Merge pull request #447 from TomHarte/DiskIIWriting
Substantially improves Disk II emulation, including write support
2018-05-22 21:54:16 -04:00
Thomas Harte
086b801c29 Mildly rearranges to avoid unnecessary call. 2018-05-22 21:50:07 -04:00
Thomas Harte
f9c25372c2 Ensures cards get messaged regardless of memory area. 2018-05-22 21:49:34 -04:00
Thomas Harte
ea92363e6c Attempts to get the Apple II to honour the AppleII::Card select constraints appropriately. 2018-05-22 20:34:59 -04:00
Thomas Harte
015f692bd3 The Disk II card now commutes Disk II sleep activity to select constraints. 2018-05-22 19:51:39 -04:00
Thomas Harte
80d34f5511 Specs out a new AppleII::Card interface.
Doesn't yet fully implement it on the Apple II side though.
2018-05-21 20:54:53 -04:00
Thomas Harte
e482929da8 Enhances the Disk II's ability to sleep.
Also enables Disk II sleep observation in the Oric.
2018-05-19 23:15:28 -04:00
Thomas Harte
4952657b31 Factors out physical to logical sector conversion. 2018-05-19 22:59:59 -04:00
Thomas Harte
46fae1a761 Corrected: now maps in the proper direction. 2018-05-19 22:50:33 -04:00
Thomas Harte
a09fb01d71 Makes an attempt at write support for Apple DSK files. 2018-05-19 22:30:52 -04:00
Thomas Harte
7cee3b7449 Resolves potential overflow / sign corruption. 2018-05-19 22:28:29 -04:00
Thomas Harte
8263c48a1d Added a guarantee that the TrackSerialiser won't modify tracks it receives. 2018-05-18 23:03:28 -04:00
Thomas Harte
ed06533e60 Implements write support out of the Disk II. 2018-05-18 22:07:58 -04:00
Thomas Harte
7b7beb13a3 Eliminates the fiction of setting and getting registers.
The Disk II seems lower level than that; it will read the data bus whenever it likes, it is the programmer's responsibility to keep up with that. It also reserves the right not to load the bus regardless of whether it receives a read or write access.
2018-05-17 21:39:11 -04:00
Thomas Harte
c46007332a Switches to returning the shift register contents on every even read. 2018-05-17 20:18:34 -04:00
Thomas Harte
908d3b0ee5 Slightly wrong as to the details, but gets the controller trying to output.
At an initial look, I think the shift register should end up on the data bus for all odd accesses. Need to investigate more thoroughly.
2018-05-16 22:37:22 -04:00
Thomas Harte
8a031b1137 Eliminates 'data' register as it doesn't exist; rejigs state machine command set. 2018-05-16 22:09:59 -04:00
Thomas Harte
1aba9f807e Ensures proper upward propagation of sleeping from first start. 2018-05-16 22:07:54 -04:00
Thomas Harte
4c49963988 Switches to proper handling of the motor control and write protection.
Per Understanding the Apple II the drive looks write protected  while phase 1 is enabled.
2018-05-16 21:44:09 -04:00
Thomas Harte
821d40fe74 Reinstitutes the cap on maximum updating time. 2018-05-16 21:42:05 -04:00
Thomas Harte
6ab1cf9325 Merge pull request #446 from TomHarte/MachinePickerLayout
Corrects various placement inconsistencies in the machine picker
2018-05-16 19:21:57 -04:00
Thomas Harte
076c0a48e9 Slightly tweaks initial size that this doesn't resize when switching to the Vic selection page. 2018-05-16 19:19:50 -04:00
Thomas Harte
fde613a5c4 Corrects various placement inconsistencies.
Hopefully to move into line with Apple's HID standards.
2018-05-16 19:15:49 -04:00
Thomas Harte
44ad0970be Merge pull request #445 from TomHarte/ColecoVisionDelay
Imposes a three-cycle penalty for SN76489 access.
2018-05-16 19:06:33 -04:00
Thomas Harte
b3f4d0ed8c Imposes a three-cycle penalty for SN76489 access.
This is my reading of (i) the SN76489 data sheet; plus (ii) the ColecoVision schematic.
2018-05-16 19:06:03 -04:00
Thomas Harte
bfdd3468ea Merge pull request #444 from TomHarte/AppleAudio
Introduces a low-pass filter for the Apple II
2018-05-15 21:33:52 -04:00
Thomas Harte
f7decd80b6 As an initial step, ensured latency doesn't pile up endlessly. 2018-05-15 21:12:43 -04:00
Thomas Harte
7c2721d54d Adjusted number again. But we'll see. 2018-05-15 20:43:13 -04:00
Thomas Harte
8907d0a9a7 Adds a low-pass filter to the Apple II's audio. 2018-05-14 21:56:14 -04:00
Thomas Harte
6c8e6e9303 Merge pull request #443 from TomHarte/DiskGainNoise
Ensures generation of random noise if too many zeroes exist on a disk.
2018-05-14 20:03:52 -04:00
Thomas Harte
85c4e009f3 Undoes reformatting error. 2018-05-14 20:03:32 -04:00
Thomas Harte
76802b5e38 Eliminates arc4random.
It seems not to be as portable as I'd hoped.
2018-05-14 20:01:20 -04:00
Thomas Harte
9f2f388e5a Ensures generation of random noise if too many zeroes exist on a disk surface. 2018-05-14 19:17:34 -04:00
Thomas Harte
729f53d84f Goes explicit with the Apple II. 2018-05-14 09:43:24 -04:00
Thomas Harte
d2d7ab5d04 Merge pull request #441 from TomHarte/AppleDSKFixes
Corrects various Apple DSK handling errors.
2018-05-13 22:42:21 -04:00
Thomas Harte
5107c7c23d Ensures all keypresses are entered as upper case. 2018-05-13 22:40:28 -04:00
Thomas Harte
4dbd1f1358 Corrects Disk II ROM visibility. 2018-05-13 22:36:02 -04:00
Thomas Harte
7996040f35 Rejigs segment conjugation to avoid potential accidental empty byte. 2018-05-13 22:30:44 -04:00
Thomas Harte
0055efb720 Corrects failure of expression in track size expansion. 2018-05-13 22:29:36 -04:00
Thomas Harte
dfa5eef20d Switches the command to issue to capitals; the Pravetz redefines lowercase as non-Latin. 2018-05-13 19:30:44 -04:00
Thomas Harte
3053acb4f3 Merge pull request #440 from TomHarte/VapourLock
Attempts to implement vapour lock bus behaviour.
2018-05-13 18:55:13 -04:00
Thomas Harte
dea9892a85 Attempts to implement vapour lock bus behaviour. 2018-05-13 18:53:32 -04:00
Thomas Harte
b9b6327707 Merge pull request #439 from TomHarte/ASCII
Eliminates all non-ASCII codes from all crossplatform code
2018-05-13 16:06:08 -04:00
Thomas Harte
84ae2964fd Switches to explicit unicode entry points. 2018-05-13 16:02:46 -04:00
Thomas Harte
149b940f84 Commutes the pound sign to a proper unicode value. 2018-05-13 15:50:56 -04:00
Thomas Harte
7226d8d4f7 Eliminates all instances of µ. 2018-05-13 15:46:14 -04:00
Thomas Harte
ad9b0cd4e3 Eliminates all endashes. 2018-05-13 15:43:03 -04:00
Thomas Harte
484e640d43 Removes stray non-ASCII typo. 2018-05-13 15:37:35 -04:00
Thomas Harte
5d6b5d9f10 Eliminates all emdashes in cross-platform code. 2018-05-13 15:34:31 -04:00
Thomas Harte
0b771ce61a Removes all instances of the copyright symbol. 2018-05-13 15:19:52 -04:00
Thomas Harte
72e07d4e83 Merge pull request #438 from TomHarte/OricTyper
Corrects Oric text pasting.
2018-05-13 14:14:13 -04:00
Thomas Harte
2252c29495 Switches the Oric to string insertion at the time of consumption.
To avoid issues around keyboard scanning being decoupled from consumption via IRQ, but not buffered. Keys pressed while BASIC is otherwise busy are just lost.
2018-05-13 14:02:46 -04:00
Thomas Harte
39c0bc6c47 Factors string serialisation with \n\r conversion out of the Apple II and reuses it with the Oric. 2018-05-13 13:57:19 -04:00
Thomas Harte
8f1a516a2c Merge pull request #437 from TomHarte/AppleIIPaste
Implements `type_string` for the Apple II.
2018-05-13 11:32:45 -04:00
Thomas Harte
a6b8e88406 Implements type_string for the Apple II. 2018-05-13 11:30:04 -04:00
Thomas Harte
c19b50619f Merge pull request #436 from TomHarte/MacPaste
Corrects Mac paste pathway.
2018-05-13 11:14:59 -04:00
Thomas Harte
3747d96b22 Corrects Mac paste pathway.
Also updates documentation around CSOpenGLView.
2018-05-13 11:12:03 -04:00
Thomas Harte
8b23a08fc4 Merge pull request #434 from TomHarte/RelaxedParsing
Removes requirement for correct sector epilogues.
2018-05-12 23:39:22 -04:00
Thomas Harte
3fdefb94e4 Removes requirement for correct sector epilogues.
It's now a length test that at present accepts 6-and-2 sectors only.
2018-05-12 23:03:08 -04:00
Thomas Harte
49592ebaf3 Ensures initialisation of scanner and that sectors overlapping the end of track are captured. 2018-05-12 18:42:07 -04:00
Thomas Harte
f410dcb3f3 Ensures proper test: not having a number of sectors that is a multiple of the track count. 2018-05-12 18:05:33 -04:00
Thomas Harte
bd27f61a03 Corrects various impossible-in-real-life compiler warnings. 2018-05-12 18:02:16 -04:00
Thomas Harte
d703328114 Adds missing #include for memcpy. 2018-05-12 17:54:13 -04:00
Thomas Harte
afe222cb16 Merge pull request #433 from TomHarte/ActivityReceiver
Introduces infrastructure for sending and receiving 'activity' notifications
2018-05-12 17:45:06 -04:00
Thomas Harte
d0fd4dd4db The MSX is now an activity source.
Completing the set.
2018-05-12 17:32:53 -04:00
Thomas Harte
3ba6b6f1ee Makes the Oric an event source. 2018-05-11 23:05:36 -04:00
Thomas Harte
bc464e247f The 1540 and, by extension, the Vic-20 are now activity sources. 2018-05-11 22:24:33 -04:00
Thomas Harte
c23f6d8d19 Corrects type for array accesses. 2018-05-11 21:46:30 -04:00
Thomas Harte
39d779edf0 Makes CPC an activity source. 2018-05-11 21:45:46 -04:00
Thomas Harte
0cb5362c6f Disambiguates whether Step will occur in addition to below zero/beyond maximum. 2018-05-11 21:44:08 -04:00
Thomas Harte
a43ca0db35 Makes the Apple II an activity source. 2018-05-10 22:17:13 -04:00
Thomas Harte
9089bf6535 Adds step events. 2018-05-10 21:58:14 -04:00
Thomas Harte
ef19a03efc Drives can now deliver activity events. 2018-05-10 21:54:10 -04:00
Thomas Harte
85e1610627 Merge branch 'master' into ActivityReceiver 2018-05-10 20:49:32 -04:00
Thomas Harte
d16ae84d0b Reduces number of Apple II video flushes, to reduce processing cost. 2018-05-10 20:48:57 -04:00
Thomas Harte
95f859cf5c Merge branch 'master' into ActivityReceiver 2018-05-10 20:26:50 -04:00
Thomas Harte
578a5b3e69 Ensures NDEBUG is set for release builds. 2018-05-09 22:27:57 -04:00
Thomas Harte
25f7e3af31 Removes dead debugging aid. What a klutz! 2018-05-09 22:24:20 -04:00
Thomas Harte
86192b18d1 Merge pull request #431 from TomHarte/DiskIIRemap
Shuffles the Disk II ROM at load time into B.A.P. form.
2018-05-09 22:10:29 -04:00
Thomas Harte
c3144382c5 Shuffles the Disk II ROM at load time into B.A.P. form.
Only if required. In order to support various potential forms of supplied ROM.
2018-05-09 22:03:59 -04:00
Thomas Harte
6bb9b7be04 Merge pull request #430 from TomHarte/PravetzPaging
Corrects Pravetz 8DOS startup.
2018-05-09 20:30:34 -04:00
Thomas Harte
8ee34fafa6 Switches default DSK volume to 254. That seems to resolve Pravetz booting issues. 2018-05-09 20:28:58 -04:00
Thomas Harte
312171fa59 Pulls out a couple of repeating constants. 2018-05-09 20:28:25 -04:00
Thomas Harte
a8dbfb0569 Adds direct link to 'releases' tab.
It has been explained to me that people who do not often come to Github are having difficulty finding the releases. This might help.
2018-05-09 10:32:16 -04:00
Thomas Harte
b09b4b4433 Merge pull request #429 from TomHarte/Pravetz
Makes attempt to implement support for the Pravetz 8D + 8DOS.
2018-05-08 22:53:51 -04:00
Thomas Harte
45bd24ada0 Corrects tags for Oric machine selection. 2018-05-08 22:53:27 -04:00
Thomas Harte
c3a2f7717b Makes attempt to implement support for the Pravetz 8D + 8DOS.
i.e. the Disk II wired up to the Oric, with some ROM swaps.
2018-05-08 22:05:43 -04:00
Thomas Harte
70e6c3b2f6 Introduces the ActivityObserver protocol for LEDs, drive events, etc.
The Electron's caps lock LED is the test case.
2018-05-07 21:57:54 -04:00
Thomas Harte
d1b889aa61 Merge pull request #424 from TomHarte/TrackDivision
Makes disk head position explicitly something with sub-integral precision.
2018-05-06 23:19:56 -04:00
Thomas Harte
f65c65569a Makes disk head position explicitly something with sub-integral precision.
Also as a drive-by fix, corrects accidental assumption of 10 sectors for all MFMSectorDump descendants.
2018-05-06 23:17:36 -04:00
Thomas Harte
1139caa83f Merge pull request #423 from TomHarte/LanguageCard
Implements the Apple II language card
2018-05-06 16:18:15 -04:00
Thomas Harte
d613c3c187 Adds an implementation of the language card. 2018-05-06 16:17:11 -04:00
Thomas Harte
36f8b165cf Makes the epilogue test a bit more thorough. 2018-05-05 20:52:42 -04:00
Thomas Harte
d6e8b34942 Ensures media is passed on from the Disk II analyser. 2018-05-05 20:32:47 -04:00
Thomas Harte
4c4ab25d0e Attempts to rationalise Apple II address decoding. 2018-05-05 20:24:03 -04:00
Thomas Harte
9ff34d90f4 Merge pull request #422 from TomHarte/DiskIIAnalyser
Introduces an analyser for Disk II-esque files.
2018-05-05 19:35:24 -04:00
Thomas Harte
9593e0f7fe Updates SContruct file for Disk II analysis. 2018-05-05 19:34:22 -04:00
Thomas Harte
1293d8b69e Corrects various indentation errors. 2018-05-05 19:32:20 -04:00
Thomas Harte
3e0055737e Adds a genuine attempt to discern Pravetz disks from Apple. 2018-05-05 19:32:08 -04:00
Thomas Harte
ba7fbc4032 Reroutes all Disk II types through the Disk II analyser and returns actual sector from the Apple GCR parser results. 2018-05-05 16:37:33 -04:00
Thomas Harte
c36d7b4972 Makes first attempt at 6 and 2 decoder. 2018-05-04 23:11:12 -04:00
Thomas Harte
1c0b5bb02b Corrects phoney switch of 'run' build to release. 2018-05-04 18:04:23 -04:00
Thomas Harte
0dece80b5d Improves documentation. 2018-05-04 18:02:55 -04:00
Thomas Harte
e3b4aebf1a Introduces the Disk II as a unique media target platform.
As it makes a little more sense to analyse Apple GCR images to determine target platform than it does to have the potential platforms vote over them.

Also starts on the parser that'll be necessary for making a decision.
2018-05-04 18:02:36 -04:00
Thomas Harte
2e20191c01 Relocates and cleans up what is currently written of Apple GCR handling as the encoder.
A decoder will be forthcoming.
2018-05-03 22:40:45 -04:00
Thomas Harte
59718e132b Fixes macOS 10.10 warning. 2018-05-03 22:39:34 -04:00
Thomas Harte
4d070fbfe3 Merge pull request #421 from TomHarte/AppleConfiguration
Introduces configuration options for the Apple II.
2018-05-03 19:38:29 -04:00
Thomas Harte
723ee88043 Introduces configuration options for the Apple II.
Specifically: II or II+? Disk II 13- or 16-sector? Or not at all?
2018-05-03 19:37:32 -04:00
Thomas Harte
65ba9ee6e7 Merge pull request #420 from TomHarte/DSKDos33
Corrects handling of Apple II DSK files.
2018-05-02 21:45:56 -04:00
Thomas Harte
fcc750784a Switches interleaving logic around: having inspected some NIBs sectors are numbered in order, but scatter read from the image. 2018-05-02 21:28:18 -04:00
Thomas Harte
3787d094ec Deals with potential precision pitfall. 2018-05-02 21:26:39 -04:00
Thomas Harte
4b4ea4a103 Corrects final two bytes of Apple GCR low nibble encoding. 2018-05-02 21:06:18 -04:00
Thomas Harte
af0cf0d40a Merge pull request #419 from TomHarte/ZXLineCounter
Simplifies the test for resetting the ZX80/81 line counter.
2018-05-01 22:04:39 -04:00
Thomas Harte
eecea93b3b Simplifies the test for resetting the ZX80/81 line counter. 2018-05-01 21:31:37 -04:00
Thomas Harte
ac4948c4b1 Merge pull request #417 from TomHarte/DiskII
Adds attempt at Disk II emulation.
2018-05-01 20:34:40 -04:00
Thomas Harte
5e34c1b6b8 Switches to producing a single segment for NIBs and DSKs.
I've now seemingly verified that the values read back by the CPU are those I'm intending to produce, so I'm at a loss.
2018-05-01 20:31:42 -04:00
Thomas Harte
05e31d7594 Mutates testComplicatedTrackSeek into an actual test.
Which frustratingly passes.
2018-05-01 19:52:12 -04:00
Thomas Harte
f4097290c2 Made various corrections following a quick for-loop constness audit. 2018-04-30 22:23:57 -04:00
Thomas Harte
9da481b060 Slightly simplifies syntax. 2018-04-30 22:08:51 -04:00
Thomas Harte
79002d6962 Adds an additional assert. 2018-04-30 22:07:54 -04:00
Thomas Harte
dbd9282efc Experimentally switches to doubles for TimedEventLoop time tracking. 2018-04-30 22:07:17 -04:00
Thomas Harte
b32538f3c8 Adds an additional test. 2018-04-30 22:05:44 -04:00
Thomas Harte
e7618bb32e Corrects types (/chickens out). 2018-04-30 22:04:05 -04:00
Thomas Harte
aacf26f05d Removed logged comment. 2018-04-30 22:03:09 -04:00
Thomas Harte
265bc80d44 Attempts to introduce sleeping to the Disk II. 2018-04-29 17:52:29 -04:00
Thomas Harte
10c0e687f5 Attempts to introduce sleeping for the Disk II. 2018-04-29 17:51:10 -04:00
Thomas Harte
a9d4fe0b41 Introduces filetype wiring for DO and PO files.
Also corrects sector numbering logic to ensure there is a sector 15.
2018-04-29 16:34:10 -04:00
Thomas Harte
5cd15147eb Introduces interleaving of sector numbers. 2018-04-29 16:18:14 -04:00
Thomas Harte
c62db6665a Corrects storage of lower two bit pairs.
It turns out the non-integral result of 256/3 is handled differently than my guess.
2018-04-29 11:20:23 -04:00
Thomas Harte
fabcb261dc Corrects data prologue usage and off-by-one error in checksum placement. 2018-04-28 23:17:06 -04:00
Thomas Harte
45cf28e0eb Corrects sync lengths. 2018-04-28 15:48:49 -04:00
Thomas Harte
5b35c88be2 Corrections: data segments now correctly announce their number of bits, and tracks aren't oversized. 2018-04-28 15:47:50 -04:00
Thomas Harte
7f03f5d02f Makes a first attempt at six-and-two encoding for DSKs. 2018-04-28 15:18:48 -04:00
Thomas Harte
b98d5b790a Finally unifies disk image file exceptions, and adds a placeholder for Apple DSK. 2018-04-27 23:18:45 -04:00
Thomas Harte
5c74044e62 Unifies constants. 2018-04-27 21:38:08 -04:00
Thomas Harte
992a99d792 Improves validation of suspected sync regions. 2018-04-27 19:53:35 -04:00
Thomas Harte
41075356e2 Makes a first attempt at NIB support. 2018-04-26 22:49:07 -04:00
Thomas Harte
850a394eb5 Corrects graphics 'carry' — the potential holdover into delayed bytes. 2018-04-26 19:26:43 -04:00
Thomas Harte
244721a6f8 Corrects graphics mode address generation. 2018-04-25 22:26:01 -04:00
Thomas Harte
d59db504a3 Adjusted stepper logic; some disks load now. 2018-04-25 21:59:18 -04:00
Thomas Harte
c90e122eb2 Switches casts around to avoid potential undefined behaviour of left-shifting signed numbers. 2018-04-25 19:59:32 -04:00
Thomas Harte
4c6dc597f4 Converts Time::get into a template, introduces a via-a-double fallback for the timed event loop. 2018-04-25 19:54:39 -04:00
Thomas Harte
b4f6dee954 Ensures the contextually-proper boot and state machine ROMs are requested. 2018-04-24 20:25:02 -07:00
Thomas Harte
2685e9087e Changes the default-assigned Disk II card slot from 7 to 6. 2018-04-24 20:24:44 -07:00
Thomas Harte
376b26c1e4 Simplifies the rotational multiplier upon construction, to mitigate against scale issues later. 2018-04-24 20:16:14 -07:00
Thomas Harte
7061537ff5 Makes joined-up attempt to run data through the Disk II. 2018-04-24 19:44:45 -07:00
Thomas Harte
2f2390b5aa Adds F12 as a reset key, triggers cards upon a flush. 2018-04-24 09:03:30 -07:00
Thomas Harte
99de8f1c5c Inverts the pulse strobe. 2018-04-24 09:03:03 -07:00
Thomas Harte
af61bbc3e2 Attempts actual performance of the state machine. 2018-04-24 08:29:05 -07:00
Thomas Harte
56d88f23ef Teeters closer and closer to trying actually to run the Disk II state machine. 2018-04-23 22:29:36 -07:00
Thomas Harte
4bff44377a Attempts to route Disk II requests to the thing itself. 2018-04-23 22:11:31 -07:00
Thomas Harte
7463edaa1b Attempts to bring card support to the Apple II, and adds a 'has disk' flag. 2018-04-23 21:14:45 -07:00
Thomas Harte
e92e06a5f4 Doubled down on the ROMMachine::ROMFetcher typedef. 2018-04-23 20:20:14 -07:00
Thomas Harte
4cbe5068a9 Works further towards NIB, but still isn't close. 2018-04-23 20:01:12 -07:00
Thomas Harte
38b2302b59 Corrects minor documentation errors. 2018-04-23 19:59:19 -07:00
Thomas Harte
bce0702745 Makes some faulty steps further towards providing Apple GCR assistance. 2018-04-23 19:59:03 -07:00
Thomas Harte
d447e81abd Adds provisional support for WOZ files. 2018-04-23 19:57:45 -07:00
Thomas Harte
6592745e53 Adds the bare minimum to respond to attempts to open NIB files with an Apple II. 2018-04-21 21:21:57 -07:00
Thomas Harte
e87a3cffd4 Merge branch 'master' into DiskII 2018-04-21 15:02:18 -07:00
Thomas Harte
fa0b6e8a08 Merge pull request #416 from TomHarte/AppleAudio
Corrects Apple II output audio.
2018-04-21 18:01:59 -04:00
Thomas Harte
074b4c3500 Eliminates repeating cause of misuse.
Raises the question as to whether an async task queue should be required at construction; let's see how things look as the project develops.
2018-04-21 15:01:18 -07:00
Thomas Harte
5968c9a391 Corrects Apple II output audio. 2018-04-21 14:56:50 -07:00
Thomas Harte
72bc5f8d7b Adds a class to contain the Disk II and begins Apple GCR conversion routines. 2018-04-21 14:33:42 -07:00
Thomas Harte
0a0d81cd5a Merge pull request #415 from TomHarte/SconsOmissions
Updates SConstruct file to include the Apple II.
2018-04-20 11:00:00 -04:00
Thomas Harte
75e9c3678b Adds the missing Apple II static analyser. 2018-04-20 10:58:57 -04:00
Thomas Harte
aebe8a64a2 Removes empty printf. 2018-04-20 10:58:23 -04:00
Thomas Harte
1aacf437b5 Adds omitted paths to SConstruct. 2018-04-20 10:56:59 -04:00
Thomas Harte
7e8e3fdd39 Merge pull request #414 from TomHarte/AppleII
Adds provisional Apple II emulation
2018-04-19 22:31:02 -04:00
Thomas Harte
b8ae283049 Implements correct text inverse/flashing. 2018-04-19 22:14:22 -04:00
Thomas Harte
6621e54952 Shortens the name for the Electron tab, owing to limited space. 2018-04-19 20:54:16 -04:00
Thomas Harte
e03a403a51 Adds exposition. 2018-04-19 20:41:09 -04:00
Thomas Harte
ba43b3e6b8 Reverses bit order of graphics stream; apparently the ROM is backwards. 2018-04-19 20:39:38 -04:00
Thomas Harte
b4a2d1395c Ensures left and right cursor keys work. 2018-04-18 22:23:31 -04:00
Thomas Harte
f5ae8d0f79 Attempts to be more rigorous about clock rates. 2018-04-18 21:52:22 -04:00
Thomas Harte
5f1c210746 Simplifies and corrects low-resolution colour generation.
Possibly disproving the premise for this whole experiment, all colours seem immediately to work correctly. Hmmm.
2018-04-18 21:41:11 -04:00
Thomas Harte
f6c2f6e896 Slightly adjusts colour burst logic to fix transition lines in mixed mode. 2018-04-18 20:39:12 -04:00
Thomas Harte
6547560e52 Gives the CRT the ability to move iCoordinate multiplication outside of the fragment loop.
That resolves precision issues, as were plaguing the Apple II.
2018-04-18 19:29:03 -04:00
Thomas Harte
a167e3849b Allows multiple ROMs to be inserted into the Electron. 2018-04-18 18:13:30 -04:00
Thomas Harte
f22c23cb4c Attempts to bring audio to the Apple II.
By factoring the audio toggle out from the MSX.
2018-04-17 22:28:13 -04:00
Thomas Harte
a07c99d778 Completes first draft of Apple II video hardware. 2018-04-17 22:04:02 -04:00
Thomas Harte
1c605d58e3 Removes the CRT requirement for an integral relationship between cycles and samples. 2018-04-16 20:00:56 -04:00
Thomas Harte
6a79ce9eb1 Adds enough to the Apple II's video that I can see what's going on with soft switches. 2018-04-15 21:55:26 -04:00
Thomas Harte
465c38f03c Extends the keyboard protocol and adds keyboard input to the Apple II. 2018-04-15 21:11:30 -04:00
Thomas Harte
be05d51e07 Now gives something a lot like the proper character output. 2018-04-15 20:31:04 -04:00
Thomas Harte
9bc470027e Put enough in place to get a visual representation of video memory.
Not the correct one, and so as to indicate that the machine isn't booting, surprisingly.
2018-04-15 19:35:08 -04:00
Thomas Harte
335c633884 Retrenches temporarily to full 8bpp output; introduces extra half a colour cycle of pause. 2018-04-15 18:54:05 -04:00
Thomas Harte
cd26f11818 Fixes documentation misstatement. 2018-04-15 18:00:51 -04:00
Thomas Harte
abe47b6ed8 Makes first attempt at a stable display area. Not entirely successful. 2018-04-15 18:00:40 -04:00
Thomas Harte
61659faeaa Adds the necessary call-outs to allow implementation of video generation. 2018-04-15 15:13:07 -04:00
Thomas Harte
71adb964e5 The Apple II now has a functioning processor, ROM and RAM. 2018-04-14 21:41:26 -04:00
Thomas Harte
e599e65087 Switches to use of the TargetList typedef wherever possible. 2018-04-14 19:46:38 -04:00
Thomas Harte
7efee9b52b Does the bare minimum to create a class skeleton for Apple II implementation. 2018-04-14 19:46:15 -04:00
Thomas Harte
079dc671e1 Rationalises per-machine static analyser call pattern, and adds Apple II as an option. 2018-04-14 12:12:12 -04:00
Thomas Harte
a32a7d1374 Merge pull request #413 from TomHarte/VicPAL
Adjusts PAL Vic timing.
2018-04-12 21:38:18 -04:00
Thomas Harte
467cd5450f Adjusts PAL Vic timing. 2018-04-12 21:12:09 -04:00
Thomas Harte
1580874a55 Merge pull request #412 from TomHarte/VideoRestriction
Reintroduces accessible memory restrictions on the VIC.
2018-04-11 22:07:42 -04:00
Thomas Harte
15f7cbe8c1 Corrects capitalisation. 2018-04-11 22:06:50 -04:00
Thomas Harte
428b6145fa Converts 6560 to more project normative templated form. 2018-04-11 22:00:42 -04:00
Thomas Harte
3ad0b31db8 Limits regions accessible to the 6560 to those built into the machine. 2018-04-11 21:35:23 -04:00
Thomas Harte
8d4d5d1f46 Merge pull request #410 from TomHarte/VicNTSC
Corrects NTSC VIC raster register timing.
2018-04-11 10:29:46 -04:00
Thomas Harte
4c8a68c6a4 Implements late-0 with proper timing, and NTSC interlaced raster count timing. 2018-04-11 08:00:37 -04:00
Thomas Harte
0b4b6f4aec Tweaks luminances and reintroduces late-to-zero line counts. 2018-04-10 23:05:18 -04:00
Thomas Harte
bb4db6b382 Ensures that 'choose' responds to enter. 2018-04-08 18:52:46 -04:00
Thomas Harte
94b1c37fb2 Slightly simplifies bus decoding. 2018-04-08 18:51:37 -04:00
Thomas Harte
cf6f6c5c15 Eliminates the full_frame_counter_ and slightly tweaks NTSC raster timing. 2018-04-08 18:51:20 -04:00
Thomas Harte
f541986333 Switches to more normative preincrement. 2018-04-08 18:50:42 -04:00
Thomas Harte
44513d6912 Ensures a 1540 is requested if any disks are present. 2018-04-08 17:37:39 -04:00
Thomas Harte
b20cbcd5fe Causes the Vic-20 to obey its own has_c1540 flag. 2018-04-08 17:35:02 -04:00
Thomas Harte
1c5972f7b0 Ensures NTSC raster count rollover; previously it was positing a line '261' for half of '0'. 2018-04-08 16:18:41 -04:00
Thomas Harte
28947bb3c4 Merge pull request #409 from TomHarte/BitShader
Switches ZX80/81 video bit unpacking to the GPU.
2018-04-08 10:35:43 -04:00
Thomas Harte
865c47a1ac Names the magic constants. 2018-04-08 10:35:07 -04:00
Thomas Harte
3821679efd Switches to bit unpacking on the GPU. 2018-04-07 22:17:47 -04:00
Thomas Harte
506b4da6c3 Merge pull request #408 from TomHarte/MixerBalance
Enhances the CompoundSource so that constituents can have different volumes.
2018-04-07 14:32:47 -04:00
Thomas Harte
10f637d2cf Enhances the CompoundSource so that constituents can have different volumes. 2018-04-07 14:30:02 -04:00
Thomas Harte
0bab7c88f0 Merge pull request #407 from TomHarte/NameImplications
Allows the Vic-20 analyser to act on 'NTSC' in a filename.
2018-04-06 20:10:56 -04:00
Thomas Harte
78c612ca17 Adds a missing import, removes a redundant conversion. 2018-04-06 20:07:10 -04:00
Thomas Harte
e1c4035812 Switches away from C strings and allows Vic-20 region inference from filenames. 2018-04-06 17:42:24 -04:00
Thomas Harte
eb6d6c8033 Merge pull request #406 from TomHarte/NewFixes
Tweaks the 'new machine' dialogue for ZX memory size
2018-04-05 22:02:10 -04:00
Thomas Harte
7bf88565ce Resizes to fit all options. 2018-04-05 21:59:19 -04:00
Thomas Harte
ee10155296 Adds advice and withdraws the ZX 64kb option. 2018-04-05 21:57:26 -04:00
Thomas Harte
cc49140f6f Merge pull request #405 from TomHarte/VicFraming
Introduces different clipping zones for NTSC and PAL output.
2018-04-05 21:26:07 -04:00
Thomas Harte
3e846f89a1 Introduces different clipping zones for NTSC and PAL output. 2018-04-05 21:25:19 -04:00
Thomas Harte
5782cab2a0 Minor whitespace fix. 2018-04-05 21:15:25 -04:00
Thomas Harte
8c511e2b76 Merge pull request #404 from TomHarte/ProperShaderSetup
Ensures the SVideo shader gets all proper `enable_vertex_attribute_with_pointer`s.
2018-04-05 21:13:26 -04:00
Thomas Harte
ec72fb3baf Ensures the SVideo shader gets all proper enable_vertex_attribute_with_pointers. 2018-04-05 21:12:28 -04:00
Thomas Harte
bab1440f5c Merge pull request #403 from TomHarte/VicRange
Causes the 6560 to obey `set_sample_volume_range`.
2018-04-05 21:06:09 -04:00
Thomas Harte
60c1da6a66 Causes the 6560 to obey set_sample_volume_range.
Thereby resolves a clipping issue.
2018-04-05 21:04:46 -04:00
Thomas Harte
a849b3f2e4 Merge pull request #402 from TomHarte/AudioCutoff
Ensures artificial audio frequency limits are honoured.
2018-04-05 19:05:48 -04:00
Thomas Harte
dbe3c5c3f8 Ensures artificial frequency limits are honoured. 2018-04-05 18:40:07 -04:00
Thomas Harte
60cf6b3cfd Merge pull request #401 from TomHarte/VideoQuirks
Corrects composite output of the ZX80/81 and the Oric
2018-04-04 19:23:45 -04:00
Thomas Harte
5044aac337 Sizes up default window size better to fit machine selector. 2018-04-04 19:18:22 -04:00
Thomas Harte
36e0cb29c0 Ensures proper propagation of video choice through the Oric. 2018-04-04 19:14:42 -04:00
Thomas Harte
c0b4dd65da Mades the expected video signal usage explicit. 2018-04-04 19:01:18 -04:00
Thomas Harte
d061ea232b Ensures no attempt to compile an SVideo shader without appropriate source. 2018-04-04 19:01:01 -04:00
Thomas Harte
49feca4ddf Merge pull request #400 from TomHarte/NewCrash
Introduces a rudimentary 'new' dialogue for the Mac
2018-04-03 23:24:00 -04:00
Thomas Harte
46b1c57bf4 Enables the titlebar, inexplicably allowing the sheet to obtain focus. 2018-04-03 23:22:26 -04:00
Thomas Harte
eaf1482182 Reverts the once-again-unused document controller. 2018-04-03 23:11:19 -04:00
Thomas Harte
d3418550eb Attempts explicitly to disable promise of saving. 2018-04-03 23:06:48 -04:00
Thomas Harte
3ffa9e2751 Ensures complete machine picker state is preserved. 2018-04-03 23:01:12 -04:00
Thomas Harte
c697dd78f0 Ensures a new machine starts as first responder. 2018-04-03 22:22:39 -04:00
Thomas Harte
7dac791290 Causes the machine picker to show as a sheet.
Albeit with some user experience issues lingering.
2018-04-03 18:47:07 -04:00
Thomas Harte
cde2faeda6 Makes an unsuccessful attempt to show the new machine dialogue as a sheet.
Also corrects the 'open' case versus recent changes.
2018-04-02 23:31:36 -04:00
Thomas Harte
69f520428d Makes a first, ugly attempt at a 'new machine' dialogue for the Mac.
Which has implied getting much more specific about MSX disk drive attachment, and has prompted an excuse to offer the ZX80 with the ZX81 ROM.
2018-04-02 22:42:41 -04:00
Thomas Harte
80c84ddd75 Merge pull request #398 from TomHarte/SVideoOption
Exposes S-Video as a user-selectable option
2018-04-01 13:30:41 -04:00
Thomas Harte
fca8a58b36 Exposes S-Video option in the Mac UI. 2018-04-01 13:29:04 -04:00
Thomas Harte
33084899d0 Provides s-video as a command-line option. 2018-03-31 22:14:34 -04:00
Thomas Harte
7b381a8b6b Merge pull request #397 from TomHarte/Vic20FastTape
Improves Vic-20 fast tape ownership and simplifies memory logic.
2018-03-31 21:05:22 -04:00
Thomas Harte
9c75689a8d Increased verbosity. 2018-03-31 20:58:16 -04:00
Thomas Harte
0ee40e8556 Reintroduces 90% crop for VIC output. 2018-03-31 20:57:45 -04:00
Thomas Harte
8b45377b89 Simplifies storage underlying Vic memory.
In the hope of avoiding non-obvious bugs.
2018-03-31 18:54:40 -04:00
Thomas Harte
f6fb368d88 Allows the fast-tape mechanism to take ownership of tape handling.
Any successful fast tape interaction will now permanently pause the tape until a failed interaction occurs. This may or may not be a good idea.
2018-03-30 21:22:52 -04:00
Thomas Harte
183a5379de Merge pull request #396 from TomHarte/SVideo
Adds support for s-video.
2018-03-30 18:25:28 -04:00
Thomas Harte
912791d3d4 Causes the s-video path correctly to function. 2018-03-30 18:24:18 -04:00
Thomas Harte
163a61dd63 Corrects SVideo-as-composite output; the Atari and Vic-20 now both supply svideo. 2018-03-30 13:16:18 -04:00
Thomas Harte
207d462dbf Attempts to provide an implementation of SVideo support. 2018-03-30 12:41:20 -04:00
Thomas Harte
33281b9d89 Introduces S-Video as a video signal type at the interface level. 2018-03-30 10:25:41 -04:00
Thomas Harte
389979923e Performs update to and satisfaction of Xcode 9.3's preferred warnings. 2018-03-30 10:25:01 -04:00
Thomas Harte
067174965e Merge pull request #395 from TomHarte/TEDEsqueColours
Introduces Vic luminances sourced from the TED manual.
2018-03-30 09:39:02 -04:00
Thomas Harte
286259c83b Adds missing 6560 update hooks. 2018-03-29 20:49:36 -04:00
Thomas Harte
e1aa3e5a7f Imports chrominances from the TED documentation. They seem to apply to the VIC-I also. 2018-03-29 20:04:37 -04:00
Thomas Harte
78e1c2851a Merge pull request #393 from TomHarte/Vic20Faster
Introduces some minor Vic-20 optimisations.
2018-03-27 22:04:40 -04:00
Thomas Harte
0869213c55 Cuts detritus. 2018-03-27 22:00:13 -04:00
Thomas Harte
f3fe16215a Reintroduces options for the Vic-20, now tape loading speed only. 2018-03-27 21:55:43 -04:00
Thomas Harte
ec353ce663 Makes minor Vic-20 optimisations.
Specifically: the 6560 is updated only upon writes (more nuance can arrive), and tape sleeps are observed.
2018-03-27 21:52:52 -04:00
Thomas Harte
b7ff5ef9dd Merge pull request #392 from TomHarte/VicPalette
Tweaks VIC palette, especially PAL.
2018-03-26 21:25:12 -04:00
Thomas Harte
3b26e0a7c5 Tweaks NTSC colour generation. 2018-03-26 21:22:06 -04:00
Thomas Harte
6d464557a0 Reintroduces a warm-up run for the C1540.
That simulates the normal real-life scenario of switching the drive on slightly before the computer, and causes it to function correctly from immediate fast typing on an American Vic.

Also switches a couple of casts within the C1540 to functional style.
2018-03-26 21:06:07 -04:00
Thomas Harte
a776bec46a Tweaks PAL colours for the 6560 to be closer to screenshots found online. 2018-03-26 19:02:16 -04:00
Thomas Harte
a2da51c30b Commutes Vic-20 machine configuration options to its Target. 2018-03-26 19:01:57 -04:00
Thomas Harte
8067bf548a Merge pull request #390 from TomHarte/VicOptions
Ensures the Vic-20 doesn't show the ZX80/81 options panel on macOS.
2018-03-25 16:07:13 -04:00
Thomas Harte
62b0645ed0 Ensures the Vic-20 doesn't show the ZX80/81 options panel on macOS. 2018-03-25 16:04:44 -04:00
Thomas Harte
39a94874ae Merge pull request #389 from TomHarte/VicAnalysis
Strips back Vic-20 static analysis to the bare minimum.
2018-03-25 13:42:59 -04:00
Thomas Harte
e15d6717a1 Strips back Vic-20 static analysis to the bare minimum.
Also corrects an unsafe assumption in fast loading.
2018-03-25 13:37:33 -04:00
Thomas Harte
37ef46e7bb Merge branch 'SDLTravis' of github.com:TomHarte/CLK into SDLTravis 2018-03-23 21:52:27 -04:00
Thomas Harte
70c09b3031 Attempted to draft a travis.yml for SDL. 2018-03-23 21:51:15 -04:00
Thomas Harte
9378fbb0df Attempted to draft a travis.yml for SDL. 2018-03-23 21:40:46 -04:00
Thomas Harte
2118b9c0cd Merge pull request #385 from TomHarte/OricHFE
Corrects nullptr references in the CPC static analyser.
2018-03-23 18:40:13 -04:00
Thomas Harte
d0c53de250 Corrects nullptr references in the CPC static analyser. 2018-03-23 18:39:37 -04:00
Thomas Harte
d98507eab0 Merge pull request #384 from TomHarte/PlentifulIcons
Fills out the application icon set.
2018-03-23 18:33:02 -04:00
Thomas Harte
760c75103e Fills out the application icon set. 2018-03-23 18:29:18 -04:00
Thomas Harte
4407fd2f1f Merge pull request #383 from TomHarte/D64Crash
Ensures the rom fetcher is properly provided to the C1540.
2018-03-23 18:22:37 -04:00
Thomas Harte
7fcd243be0 Ensures the rom fetcher is properly recorded for potential provision to the C1540. 2018-03-23 18:20:17 -04:00
Thomas Harte
3165e9d82e Merge pull request #382 from TomHarte/Headers
Introduces missing #includes.
2018-03-23 18:08:55 -04:00
Thomas Harte
6656a08c60 Introduces missing #includes. 2018-03-23 18:05:51 -04:00
Thomas Harte
76661c0b51 Merge pull request #375 from TomHarte/UndefinedBehaviour
Resolves various pieces of undefined behaviour.
2018-03-22 22:01:19 -04:00
Thomas Harte
3bb496f9ae Enforces a maximum sector size to avoid impossible sizes.
Such as 128 * 2^255.
2018-03-22 22:00:26 -04:00
Thomas Harte
45be1c19df Resolves undefined behaviour of a signed shift left. 2018-03-22 21:59:39 -04:00
Thomas Harte
a301964bd0 Ensures all audio queues are fully merged before machine destruction.
Thereby avoids a race condition.
2018-03-22 21:59:19 -04:00
Thomas Harte
eea6858121 Resolves undefined behaviour from uninitialised limited-range values. 2018-03-22 21:58:42 -04:00
Thomas Harte
2a320fdf56 Merge pull request #374 from TomHarte/HFEFixup
Corrects two errors in all-machine HFE offering.
2018-03-22 20:24:24 -04:00
Thomas Harte
4695296ef2 Corrects bit mask for offering HFE around. 2018-03-22 20:23:39 -04:00
Thomas Harte
0fdbbeca1d Ensures the Commodore parser properly rejects non-GCR disks. 2018-03-22 20:23:21 -04:00
Thomas Harte
34cc39ad65 Merge pull request #373 from TomHarte/SpeakerCritical
Moves all LowpassSpeaker delegate calls outside of critical sections.
2018-03-22 20:07:20 -04:00
Thomas Harte
3d0c832a21 Moves all LowpassSpeaker delegate calls outside of critical sections. 2018-03-22 19:01:20 -04:00
Thomas Harte
1acdab9448 Expanded potential HFE targets to everything other than the MSX.
The MSX does not yet perform any sanity checks on disks. That's TODO.
2018-03-22 18:55:52 -04:00
Thomas Harte
93e85c5c4a The CPC now accepts disks only if it can make sense of them. 2018-03-22 18:52:43 -04:00
Thomas Harte
ab98189d25 Merge pull request #372 from TomHarte/MultiJoystick
Implements multimachine joystick support.
2018-03-22 11:09:38 -04:00
Thomas Harte
cd0fb7624b Pulls delegate messages out of the critical sections. 2018-03-22 11:08:07 -04:00
Thomas Harte
bae38497bb Implements multitarget joysticks. 2018-03-22 11:07:52 -04:00
Thomas Harte
29921bfa8d Merge pull request #371 from TomHarte/NanosecondMachines
Devolves time -> clock rate mapping to machines.
2018-03-22 10:08:58 -04:00
Thomas Harte
2712702461 Makes get_clock_rate protected. It's now an implementation detail. 2018-03-22 10:01:18 -04:00
Thomas Harte
a3fa9440d1 Renames method better to communicate purpose. 2018-03-22 09:49:36 -04:00
Thomas Harte
6419b0e619 Reintroduces CSMachineDelegate, allowing the Mac port to switch output audio rate dynamically. 2018-03-22 09:48:19 -04:00
Thomas Harte
58e5b6e3f1 Updates SDL kiosk mode to the death of CRTMachineDelegate. 2018-03-22 09:23:27 -04:00
Thomas Harte
682c3d8079 Adds new hook for watching audio output rate changes. 2018-03-22 09:23:01 -04:00
Thomas Harte
da3d65c18f Devolves time to cycle conversion to machines.
Thereby avoids a whole bunch of complicated machinations that would otherwise have been required of the multimachine.
2018-03-21 22:18:13 -04:00
Thomas Harte
ece3a05504 Merge pull request #370 from TomHarte/OricDiskDetection
Causes the Oric properly to evaluate disks offered to it.
2018-03-21 20:51:12 -04:00
Thomas Harte
927697b0f0 Causes the Oric properly to evaluate disks offered to it. 2018-03-21 20:48:21 -04:00
Thomas Harte
74dfc80b0f Merge pull request #369 from TomHarte/AnalyserUnion
Encapsulates per-platform analyser result fields.
2018-03-09 16:13:17 -05:00
Thomas Harte
a7f229bc4b Adds missing files. 2018-03-09 16:10:17 -05:00
Thomas Harte
89bec2919f Encapsulates machine configuration properties for all remaining platforms. 2018-03-09 16:07:29 -05:00
Thomas Harte
78eaecb29e Provides the proper framework for encapsulation of analyser target specifics.
... while making them a safe container for objects too. Uses the ZX80/81 as the pilot platform.
2018-03-09 15:36:11 -05:00
Thomas Harte
d410aea856 Merge pull request #368 from TomHarte/DiamondInheritance
Eliminates diamond inheritance of KeyboardMachine::Machine by typers.
2018-03-09 15:19:54 -05:00
Thomas Harte
6b1eef572b Eliminates diamond inheritance of KeyboardMachine::Machine by typers.
Specifically by pulling the key action stuff into a purely abstract class [/interface]. Takes the opportunity to unpublish a bunch of machine details.
2018-03-09 15:19:02 -05:00
Thomas Harte
719f5d79c2 Merge pull request #367 from TomHarte/DynamicVolume
Introduces formal setting of the output volume to `SampleSource`.
2018-03-09 14:10:55 -05:00
Thomas Harte
48737a32a7 Introduces formal setting of the output volume to SampleSource.
Previously every output device was making its own decision. Which is increasingly less sustainable due to the CompoundSource.
2018-03-09 13:23:18 -05:00
Thomas Harte
53f05efb2d Merge pull request #366 from TomHarte/MoreMemptr
Improves Z80 memptr behaviour.
2018-03-09 10:05:57 -05:00
Thomas Harte
0e73ba4b3e Introduces proper 5/3 SCF/CCF behaviour for the Z80.
While also `const`ing a bunch of things.
2018-03-09 09:47:00 -05:00
Thomas Harte
f0f9d5a6af Corrects memptr leakage via BIT, and ld (de/bc/nn), A behaviour. 2018-03-08 20:30:22 -05:00
Thomas Harte
03501df9e5 Merge pull request #365 from TomHarte/CartridgeDetermination
Works towards eliminating the special cases for Atari 2600 ROM handling.
2018-03-08 18:40:58 -05:00
Thomas Harte
dd6f85d4db Merge pull request #364 from TomHarte/TimingUpfront
Ensures the Coleco & MSX account for instruction lengths prior to outward accesses.
2018-03-07 17:29:32 -05:00
Thomas Harte
1804ea6849 Ensures the ColecoVision and MSX account for instruction lengths in advance when timing secondary components. 2018-03-07 17:00:18 -05:00
Thomas Harte
c8657e08f4 Merge remote-tracking branch 'origin/master' into CartridgeDetermination 2018-03-07 16:42:16 -05:00
Thomas Harte
a942e1319b Merge pull request #363 from TomHarte/ZonX
Introduces ZonX emulation and corrects a minor ColecoVision AY timing issue.
2018-03-07 16:23:51 -05:00
Thomas Harte
9e0a56b4f0 Withdraws the 2600 from .rom consideration.
Will return when it is performing more sanity checks; for the time being I don't want it constantly forcing multimachines.
2018-03-07 16:21:17 -05:00
Thomas Harte
9abc020818 Corrects potential ColecoVision SGM AY timing issues. 2018-03-07 16:16:58 -05:00
Thomas Harte
2dade8d353 Introduces ZonX emulation for the ZX81. 2018-03-07 16:16:29 -05:00
Thomas Harte
1100dc6993 Opens up .bin and .rom to all cartridge platforms, and adds a confidence estimate to the Atari 2600. 2018-03-07 14:26:07 -05:00
Thomas Harte
f212b18511 Declares a confidence for the ColecoVision equal to the probability that the special bytes are wrong. 2018-03-07 14:25:25 -05:00
Thomas Harte
a6ca69550f Standardises machines that aren't making a real guess on reporting a confidence of 0.5. 2018-03-07 14:24:52 -05:00
Thomas Harte
2452641844 Introduces a fast workaround to avert a MultiMachine where it would instantly end. 2018-03-06 19:08:02 -05:00
Thomas Harte
c82af4b814 Introduces get_confidence for the ColecoVision.
Based almost entirely on joypad accesses for now.
2018-03-06 19:06:35 -05:00
Thomas Harte
fdef914137 Corrects test target regression. 2018-03-06 18:32:21 -05:00
Thomas Harte
dfcc502a88 Merge pull request #360 from TomHarte/SDLJoystick
Introduces keyboard-as-joystick fallback for the SDL target.
2018-03-04 17:28:05 -05:00
Thomas Harte
1c6faaae88 Introduces keyboard-as-joystick fallback for the SDL target. 2018-03-04 17:26:32 -05:00
Thomas Harte
35c8a0dd8c Merge pull request #359 from TomHarte/MentionColecoVision
Adds the ColecoVision to the declared list of machines.
2018-03-03 19:05:05 -05:00
Thomas Harte
38feedaf6a Adds the ColecoVision. 2018-03-03 19:03:54 -05:00
483 changed files with 8733 additions and 3002 deletions

View File

@@ -1,5 +1,13 @@
language: objective-c
osx_image: xcode8.2
xcode_project: OSBindings/Mac/Clock Signal.xcodeproj
xcode_scheme: Clock Signal
xcode_sdk: macosx10.12
# language: objective-c
# osx_image: xcode8.2
# xcode_project: OSBindings/Mac/Clock Signal.xcodeproj
# xcode_scheme: Clock Signal
# xcode_sdk: macosx10.12
language: cpp
before_install:
- sudo apt-get install libsdl2-dev
script: cd OSBindings/SDL && scons
compiler:
- clang
- gcc

51
Activity/Observer.hpp Normal file
View File

@@ -0,0 +1,51 @@
//
// ActivityObserver.h
// Clock Signal
//
// Created by Thomas Harte on 07/05/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef ActivityObserver_h
#define ActivityObserver_h
#include <string>
namespace Activity {
/*!
Provides a purely virtual base class for anybody that wants to receive notifications of
'activity': any feedback from an emulated system which a user could perceive other than
through the machine's native audio and video outputs.
So: status LEDs, drive activity, etc. A receiver may choose to make appropriate noises
and/or to show or unshow status indicators.
*/
class Observer {
public:
/// Announces to the receiver that there is an LED of name @c name.
virtual void register_led(const std::string &name) = 0;
/// Announces to the receiver that there is a drive of name @c name.
virtual void register_drive(const std::string &name) = 0;
/// Informs the receiver of the new state of the LED with name @c name.
virtual void set_led_status(const std::string &name, bool lit) = 0;
enum class DriveEvent {
StepNormal,
StepBelowZero,
StepBeyondMaximum
};
/// Informs the receiver that the named event just occurred for the drive with name @c name.
virtual void announce_drive_event(const std::string &name, DriveEvent event) = 0;
/// Informs the receiver of the motor-on status of the drive with name @c name.
virtual void set_drive_motor_status(const std::string &name, bool is_on) = 0;
};
}
#endif /* ActivityObserver_h */

24
Activity/Source.hpp Normal file
View File

@@ -0,0 +1,24 @@
//
// ActivitySource.h
// Clock Signal
//
// Created by Thomas Harte on 07/05/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef ActivitySource_h
#define ActivitySource_h
#include "Observer.hpp"
namespace Activity {
class Source {
public:
virtual void set_activity_observer(Observer *observer) = 0;
};
}
#endif /* ActivitySource_h */

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 21/01/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "ConfidenceCounter.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 21/01/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef ConfidenceCounter_hpp
@@ -24,10 +24,10 @@ class ConfidenceCounter: public ConfidenceSource {
/*! @returns The computed probability, based on the history of events. */
float get_confidence() override;
/*! Records an event that implies this is the appropriate class pushes probability up towards 1.0. */
/*! Records an event that implies this is the appropriate class: pushes probability up towards 1.0. */
void add_hit();
/*! Records an event that implies this is not the appropriate class pushes probability down towards 0.0. */
/*! Records an event that implies this is not the appropriate class: pushes probability down towards 0.0. */
void add_miss();
/*!

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 21/01/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef ConfidenceSource_hpp

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 21/01/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "ConfidenceSummary.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 21/01/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef ConfidenceSummary_hpp

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 29/01/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "MultiCRTMachine.hpp"
@@ -75,41 +75,16 @@ Outputs::Speaker::Speaker *MultiCRTMachine::get_speaker() {
return speaker_;
}
void MultiCRTMachine::run_for(const Cycles cycles) {
void MultiCRTMachine::run_for(Time::Seconds duration) {
perform_parallel([=](::CRTMachine::Machine *machine) {
if(machine->get_confidence() >= 0.01f) machine->run_for(cycles);
if(machine->get_confidence() >= 0.01f) machine->run_for(duration);
});
if(delegate_) delegate_->multi_crt_did_run_machines();
}
double MultiCRTMachine::get_clock_rate() {
// TODO: something smarter than this? Not all clock rates will necessarily be the same.
std::lock_guard<std::mutex> machines_lock(machines_mutex_);
CRTMachine::Machine *crt_machine = machines_.front()->crt_machine();
return crt_machine ? crt_machine->get_clock_rate() : 0.0;
}
bool MultiCRTMachine::get_clock_is_unlimited() {
std::lock_guard<std::mutex> machines_lock(machines_mutex_);
CRTMachine::Machine *crt_machine = machines_.front()->crt_machine();
return crt_machine ? crt_machine->get_clock_is_unlimited() : false;
}
void MultiCRTMachine::did_change_machine_order() {
if(speaker_) {
speaker_->set_new_front_machine(machines_.front().get());
}
}
void MultiCRTMachine::set_delegate(::CRTMachine::Machine::Delegate *delegate) {
// TODO:
}
void MultiCRTMachine::machine_did_change_clock_rate(Machine *machine) {
// TODO: consider passing along.
}
void MultiCRTMachine::machine_did_change_clock_is_unlimited(Machine *machine) {
// TODO: consider passing along.
}

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 29/01/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef MultiCRTMachine_hpp
@@ -29,7 +29,7 @@ namespace Dynamic {
acquiring a supplied mutex. The owner should also call did_change_machine_order()
if the order of machines changes.
*/
class MultiCRTMachine: public CRTMachine::Machine, public CRTMachine::Machine::Delegate {
class MultiCRTMachine: public CRTMachine::Machine {
public:
MultiCRTMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines, std::mutex &machines_mutex);
@@ -57,16 +57,10 @@ class MultiCRTMachine: public CRTMachine::Machine, public CRTMachine::Machine::D
void close_output() override;
Outputs::CRT::CRT *get_crt() override;
Outputs::Speaker::Speaker *get_speaker() override;
void run_for(const Cycles cycles) override;
double get_clock_rate() override;
bool get_clock_is_unlimited() override;
void set_delegate(::CRTMachine::Machine::Delegate *delegate) override;
void run_for(Time::Seconds duration) override;
private:
// CRTMachine::Machine::Delegate
void machine_did_change_clock_rate(Machine *machine) override;
void machine_did_change_clock_is_unlimited(Machine *machine) override;
void run_for(const Cycles cycles) override {}
const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines_;
std::mutex &machines_mutex_;
std::vector<Concurrency::AsyncTaskQueue> queues_;

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 09/02/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "MultiConfigurable.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 09/02/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef MultiConfigurable_hpp

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 29/01/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "MultiConfigurationTarget.hpp"
@@ -17,7 +17,7 @@ MultiConfigurationTarget::MultiConfigurationTarget(const std::vector<std::unique
}
}
void MultiConfigurationTarget::configure_as_target(const Analyser::Static::Target &target) {
void MultiConfigurationTarget::configure_as_target(const Analyser::Static::Target *target) {
}
bool MultiConfigurationTarget::insert_media(const Analyser::Static::Media &media) {

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 29/01/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef MultiConfigurationTarget_hpp
@@ -29,7 +29,7 @@ struct MultiConfigurationTarget: public ConfigurationTarget::Machine {
MultiConfigurationTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
// Below is the standard ConfigurationTarget::Machine interface; see there for documentation.
void configure_as_target(const Analyser::Static::Target &target) override;
void configure_as_target(const Analyser::Static::Target *target) override;
bool insert_media(const Analyser::Static::Media &media) override;
private:

View File

@@ -3,17 +3,73 @@
// Clock Signal
//
// Created by Thomas Harte on 09/02/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "MultiJoystickMachine.hpp"
#include <algorithm>
using namespace Analyser::Dynamic;
namespace {
class MultiJoystick: public Inputs::Joystick {
public:
MultiJoystick(std::vector<JoystickMachine::Machine *> &machines, std::size_t index) {
for(const auto &machine: machines) {
const auto &joysticks = machine->get_joysticks();
if(joysticks.size() >= index) {
joysticks_.push_back(joysticks[index].get());
}
}
}
std::vector<DigitalInput> get_inputs() override {
std::vector<DigitalInput> inputs;
for(const auto &joystick: joysticks_) {
std::vector<DigitalInput> joystick_inputs = joystick->get_inputs();
for(const auto &input: joystick_inputs) {
if(std::find(inputs.begin(), inputs.end(), input) != inputs.end()) {
inputs.push_back(input);
}
}
}
return inputs;
}
void set_digital_input(const DigitalInput &digital_input, bool is_active) override {
for(const auto &joystick: joysticks_) {
joystick->set_digital_input(digital_input, is_active);
}
}
void reset_all_inputs() override {
for(const auto &joystick: joysticks_) {
joystick->reset_all_inputs();
}
}
private:
std::vector<Inputs::Joystick *> joysticks_;
};
}
MultiJoystickMachine::MultiJoystickMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
std::size_t total_joysticks = 0;
std::vector<JoystickMachine::Machine *> joystick_machines;
for(const auto &machine: machines) {
JoystickMachine::Machine *joystick_machine = machine->joystick_machine();
if(joystick_machine) machines_.push_back(joystick_machine);
if(joystick_machine) {
joystick_machines.push_back(joystick_machine);
total_joysticks = std::max(total_joysticks, joystick_machine->get_joysticks().size());
}
}
for(std::size_t index = 0; index < total_joysticks; ++index) {
joysticks_.emplace_back(new MultiJoystick(joystick_machines, index));
}
}

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 09/02/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef MultiJoystickMachine_hpp
@@ -31,7 +31,6 @@ class MultiJoystickMachine: public JoystickMachine::Machine {
std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override;
private:
std::vector<JoystickMachine::Machine *> machines_;
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
};

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 09/02/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "MultiKeyboardMachine.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 09/02/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef MultiKeyboardMachine_hpp

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 18/02/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "MultiSpeaker.hpp"
@@ -48,13 +48,29 @@ void MultiSpeaker::set_delegate(Outputs::Speaker::Speaker::Delegate *delegate) {
}
void MultiSpeaker::speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer) {
if(!delegate_) return;
{
std::lock_guard<std::mutex> lock_guard(front_speaker_mutex_);
if(delegate_ && speaker == front_speaker_) {
if(speaker != front_speaker_) return;
}
delegate_->speaker_did_complete_samples(this, buffer);
}
void MultiSpeaker::speaker_did_change_input_clock(Speaker *speaker) {
if(!delegate_) return;
{
std::lock_guard<std::mutex> lock_guard(front_speaker_mutex_);
if(speaker != front_speaker_) return;
}
delegate_->speaker_did_change_input_clock(this);
}
void MultiSpeaker::set_new_front_machine(::Machine::DynamicMachine *machine) {
{
std::lock_guard<std::mutex> lock_guard(front_speaker_mutex_);
front_speaker_ = machine->crt_machine()->get_speaker();
}
if(delegate_) {
delegate_->speaker_did_change_input_clock(this);
}
}

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 18/02/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef MultiSpeaker_hpp
@@ -38,12 +38,13 @@ class MultiSpeaker: public Outputs::Speaker::Speaker, Outputs::Speaker::Speaker:
void set_new_front_machine(::Machine::DynamicMachine *machine);
// Below is the standard Outputs::Speaker::Speaker interface; see there for documentation.
float get_ideal_clock_rate_in_range(float minimum, float maximum);
void set_output_rate(float cycles_per_second, int buffer_size);
void set_delegate(Outputs::Speaker::Speaker::Delegate *delegate);
float get_ideal_clock_rate_in_range(float minimum, float maximum) override;
void set_output_rate(float cycles_per_second, int buffer_size) override;
void set_delegate(Outputs::Speaker::Speaker::Delegate *delegate) override;
private:
void speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer);
void speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer) override;
void speaker_did_change_input_clock(Speaker *speaker) override;
MultiSpeaker(const std::vector<Outputs::Speaker::Speaker *> &speakers);
std::vector<Outputs::Speaker::Speaker *> speakers_;

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 28/01/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "MultiMachine.hpp"
@@ -22,6 +22,10 @@ MultiMachine::MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machin
crt_machine_.set_delegate(this);
}
Activity::Source *MultiMachine::activity_source() {
return nullptr; // TODO
}
ConfigurationTarget::Machine *MultiMachine::configuration_target() {
if(has_picked_) {
return machines_.front()->configuration_target();
@@ -62,8 +66,15 @@ Configurable::Device *MultiMachine::configurable_device() {
}
}
bool MultiMachine::would_collapse(const std::vector<std::unique_ptr<DynamicMachine>> &machines) {
return
(machines.front()->crt_machine()->get_confidence() > 0.9f) ||
(machines.front()->crt_machine()->get_confidence() >= 2.0f * machines[1]->crt_machine()->get_confidence());
}
void MultiMachine::multi_crt_did_run_machines() {
std::lock_guard<std::mutex> machines_lock(machines_mutex_);
#ifdef DEBUG
for(const auto &machine: machines_) {
CRTMachine::Machine *crt = machine->crt_machine();
printf("%0.2f ", crt->get_confidence());
@@ -71,6 +82,7 @@ void MultiMachine::multi_crt_did_run_machines() {
printf("; ");
}
printf("\n");
#endif
DynamicMachine *front = machines_.front().get();
std::stable_sort(machines_.begin(), machines_.end(),
@@ -84,10 +96,7 @@ void MultiMachine::multi_crt_did_run_machines() {
crt_machine_.did_change_machine_order();
}
if(
(machines_.front()->crt_machine()->get_confidence() > 0.9f) ||
(machines_.front()->crt_machine()->get_confidence() >= 2.0f * machines_[1]->crt_machine()->get_confidence())
) {
if(would_collapse(machines_)) {
pick_first();
}
}

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 28/01/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef MultiMachine_hpp
@@ -40,8 +40,17 @@ namespace Dynamic {
*/
class MultiMachine: public ::Machine::DynamicMachine, public MultiCRTMachine::Delegate {
public:
/*!
Allows a potential MultiMachine creator to enquire as to whether there's any benefit in
requesting this class as a proxy.
@returns @c true if the multimachine would discard all but the first machine in this list;
@c false otherwise.
*/
static bool would_collapse(const std::vector<std::unique_ptr<DynamicMachine>> &machines);
MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machines);
Activity::Source *activity_source() override;
ConfigurationTarget::Machine *configuration_target() override;
CRTMachine::Machine *crt_machine() override;
JoystickMachine::Machine *joystick_machine() override;

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 24/01/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef Machines_h
@@ -13,6 +13,7 @@ namespace Analyser {
enum class Machine {
AmstradCPC,
AppleII,
Atari2600,
ColecoVision,
Electron,

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 18/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "Disk.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 18/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Acorn_Disk_hpp
@@ -16,7 +16,7 @@ namespace Analyser {
namespace Static {
namespace Acorn {
/// Describes a DFS- or ADFS-format catalogue(/directory) the list of files available and the catalogue's boot option.
/// Describes a DFS- or ADFS-format catalogue(/directory): the list of files available and the catalogue's boot option.
struct Catalogue {
std::string name;
std::vector<File> files;

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 18/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Acorn_File_hpp

View File

@@ -3,13 +3,14 @@
// Clock Signal
//
// Created by Thomas Harte on 29/08/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "StaticAnalyser.hpp"
#include "Disk.hpp"
#include "Tape.hpp"
#include "Target.hpp"
using namespace Analyser::Static::Acorn;
@@ -49,20 +50,20 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
// 1/(2^32) *
// ( ((2^24)-1)/(2^24)*(1/4) + 1/(2^24) ) *
// 1/4
// = something very improbable around 1/16th of 1 in 2^32, but not exactly.
// = something very improbable, around 1/16th of 1 in 2^32, but not exactly.
acorn_cartridges.push_back(cartridge);
}
return acorn_cartridges;
}
void Analyser::Static::Acorn::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
std::unique_ptr<Target> target(new Target);
target->machine = Machine::Electron;
target->confidence = 1.0; // TODO: a proper estimation
target->acorn.has_dfs = false;
target->acorn.has_adfs = false;
target->acorn.should_shift_restart = false;
target->confidence = 0.5; // TODO: a proper estimation
target->has_dfs = false;
target->has_adfs = false;
target->should_shift_restart = false;
// strip out inappropriate cartridges
target->media.cartridges = AcornCartridgesFrom(media.cartridges);
@@ -109,17 +110,20 @@ void Analyser::Static::Acorn::AddTargets(const Media &media, std::vector<std::un
if(dfs_catalogue == nullptr) adfs_catalogue = GetADFSCatalogue(disk);
if(dfs_catalogue || adfs_catalogue) {
target->media.disks = media.disks;
target->acorn.has_dfs = !!dfs_catalogue;
target->acorn.has_adfs = !!adfs_catalogue;
target->has_dfs = !!dfs_catalogue;
target->has_adfs = !!adfs_catalogue;
Catalogue::BootOption bootOption = (dfs_catalogue ?: adfs_catalogue)->bootOption;
if(bootOption != Catalogue::BootOption::None)
target->acorn.should_shift_restart = true;
target->should_shift_restart = true;
else
target->loading_command = "*CAT\n";
}
}
if(target->media.tapes.size() || target->media.disks.size() || target->media.cartridges.size())
destination.push_back(std::move(target));
TargetList targets;
if(!target->media.empty()) {
targets.push_back(std::move(target));
}
return targets;
}

View File

@@ -3,19 +3,21 @@
// Clock Signal
//
// Created by Thomas Harte on 29/08/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Acorn_StaticAnalyser_hpp
#define StaticAnalyser_Acorn_StaticAnalyser_hpp
#include "../StaticAnalyser.hpp"
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Acorn {
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 29/08/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "Tape.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 29/08/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Acorn_Tape_hpp

View File

@@ -0,0 +1,30 @@
//
// Target.hpp
// Clock Signal
//
// Created by Thomas Harte on 09/03/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef Analyser_Static_Acorn_Target_h
#define Analyser_Static_Acorn_Target_h
#include "../StaticAnalyser.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Acorn {
struct Target: public ::Analyser::Static::Target {
bool has_adfs = false;
bool has_dfs = false;
bool should_shift_restart = false;
std::string loading_command;
};
}
}
}
#endif /* Analyser_Static_Acorn_Target_h */

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 30/07/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#include "StaticAnalyser.hpp"
@@ -11,6 +11,8 @@
#include <algorithm>
#include <cstring>
#include "Target.hpp"
#include "../../../Storage/Disk/Parsers/CPM.hpp"
#include "../../../Storage/Disk/Encodings/MFM/Parser.hpp"
@@ -58,18 +60,18 @@ static std::string RunCommandFor(const Storage::Disk::CPM::File &file) {
static void InspectCatalogue(
const Storage::Disk::CPM::Catalogue &catalogue,
const std::unique_ptr<Analyser::Static::Target> &target) {
const std::unique_ptr<Analyser::Static::AmstradCPC::Target> &target) {
std::vector<const Storage::Disk::CPM::File *> candidate_files;
candidate_files.reserve(catalogue.files.size());
for(auto &file : catalogue.files) {
for(const auto &file : catalogue.files) {
candidate_files.push_back(&file);
}
// Remove all files with untypable characters.
candidate_files.erase(
std::remove_if(candidate_files.begin(), candidate_files.end(), [](const Storage::Disk::CPM::File *file) {
for(auto c : file->name + file->type) {
for(const auto c : file->name + file->type) {
if(c < 32) return true;
}
return false;
@@ -78,7 +80,7 @@ static void InspectCatalogue(
// If that leaves a mix of 'system' (i.e. hidden) and non-system files, remove the system files.
bool are_all_system = true;
for(auto file : candidate_files) {
for(const auto &file : candidate_files) {
if(!file->system) {
are_all_system = false;
break;
@@ -135,13 +137,13 @@ static void InspectCatalogue(
std::map<std::string, int> name_counts;
std::map<std::string, std::size_t> indices_by_name;
std::size_t index = 0;
for(auto file : candidate_files) {
for(const auto &file : candidate_files) {
name_counts[file->name]++;
indices_by_name[file->name] = index;
index++;
}
if(name_counts.size() == 2) {
for(auto &pair : name_counts) {
for(const auto &pair : name_counts) {
if(pair.second == 1) {
target->loading_command = RunCommandFor(*candidate_files[indices_by_name[pair.first]]);
return;
@@ -153,10 +155,10 @@ static void InspectCatalogue(
target->loading_command = "cat\n";
}
static bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &disk, const std::unique_ptr<Analyser::Static::Target> &target) {
static 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::Sector *boot_sector = parser.get_sector(0, 0, 0x41);
if(boot_sector != nullptr && !boot_sector->samples.empty()) {
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
// this disk was formatted and the filler byte never replaced.
bool matched = true;
@@ -177,24 +179,25 @@ static bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &disk, co
return false;
}
void Analyser::Static::AmstradCPC::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
TargetList destination;
std::unique_ptr<Target> target(new Target);
target->machine = Machine::AmstradCPC;
target->confidence = 1.0;
target->media.disks = media.disks;
target->confidence = 0.5;
target->model = Target::Model::CPC6128;
if(!media.tapes.empty()) {
// TODO: which of these are actually potentially CPC tapes?
target->media.tapes = media.tapes;
target->media.cartridges = media.cartridges;
target->amstradcpc.model = AmstradCPCModel::CPC6128;
if(!target->media.tapes.empty()) {
// Ugliness flows here: assume the CPC isn't smart enough to pause between pressing
// enter and responding to the follow-on prompt to press a key, so just type for
// a while. Yuck!
target->loading_command = "|tape\nrun\"\n1234567890";
}
if(!target->media.disks.empty()) {
if(!media.disks.empty()) {
Storage::Disk::CPM::ParameterBlock data_format;
data_format.sectors_per_track = 9;
data_format.tracks = 40;
@@ -203,11 +206,6 @@ void Analyser::Static::AmstradCPC::AddTargets(const Media &media, std::vector<st
data_format.catalogue_allocation_bitmap = 0xc000;
data_format.reserved_tracks = 0;
std::unique_ptr<Storage::Disk::CPM::Catalogue> data_catalogue = Storage::Disk::CPM::GetCatalogue(target->media.disks.front(), data_format);
if(data_catalogue) {
InspectCatalogue(*data_catalogue, target);
} else {
if(!CheckBootSector(target->media.disks.front(), target)) {
Storage::Disk::CPM::ParameterBlock system_format;
system_format.sectors_per_track = 9;
system_format.tracks = 40;
@@ -216,13 +214,34 @@ void Analyser::Static::AmstradCPC::AddTargets(const Media &media, std::vector<st
system_format.catalogue_allocation_bitmap = 0xc000;
system_format.reserved_tracks = 2;
std::unique_ptr<Storage::Disk::CPM::Catalogue> system_catalogue = Storage::Disk::CPM::GetCatalogue(target->media.disks.front(), system_format);
for(auto &disk: media.disks) {
// Check for an ordinary catalogue.
std::unique_ptr<Storage::Disk::CPM::Catalogue> data_catalogue = Storage::Disk::CPM::GetCatalogue(disk, data_format);
if(data_catalogue) {
InspectCatalogue(*data_catalogue, target);
target->media.disks.push_back(disk);
continue;
}
// Failing that check for a boot sector.
if(CheckBootSector(disk, target)) {
target->media.disks.push_back(disk);
continue;
}
// Failing that check for a system catalogue.
std::unique_ptr<Storage::Disk::CPM::Catalogue> system_catalogue = Storage::Disk::CPM::GetCatalogue(disk, system_format);
if(system_catalogue) {
InspectCatalogue(*system_catalogue, target);
}
target->media.disks.push_back(disk);
continue;
}
}
}
// If any media survived, add the target.
if(!target->media.empty())
destination.push_back(std::move(target));
return destination;
}

View File

@@ -3,22 +3,24 @@
// Clock Signal
//
// Created by Thomas Harte on 30/07/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_AmstradCPC_StaticAnalyser_hpp
#define StaticAnalyser_AmstradCPC_StaticAnalyser_hpp
#ifndef Analyser_Static_AmstradCPC_StaticAnalyser_hpp
#define Analyser_Static_AmstradCPC_StaticAnalyser_hpp
#include "../StaticAnalyser.hpp"
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace AmstradCPC {
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* StaticAnalyser_AmstradCPC_StaticAnalyser_hpp */
#endif /* Analyser_Static_AmstradCPC_StaticAnalyser_hpp */

View File

@@ -0,0 +1,35 @@
//
// Target.hpp
// Clock Signal
//
// Created by Thomas Harte on 09/03/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef Analyser_Static_AmstradCPC_Target_h
#define Analyser_Static_AmstradCPC_Target_h
#include "../StaticAnalyser.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace AmstradCPC {
struct Target: public ::Analyser::Static::Target {
enum class Model {
CPC464,
CPC664,
CPC6128
};
Model model = Model::CPC464;
std::string loading_command;
};
}
}
}
#endif /* Analyser_Static_AmstradCPC_Target_h */

View File

@@ -0,0 +1,23 @@
//
// StaticAnalyser.cpp
// Clock Signal
//
// Created by Thomas Harte on 14/04/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "StaticAnalyser.hpp"
#include "Target.hpp"
Analyser::Static::TargetList Analyser::Static::AppleII::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
auto target = std::unique_ptr<Target>(new Target);
target->machine = Machine::AppleII;
target->media = media;
if(!target->media.disks.empty())
target->disk_controller = Target::DiskController::SixteenSector;
TargetList targets;
targets.push_back(std::move(target));
return targets;
}

View File

@@ -0,0 +1,26 @@
//
// StaticAnalyser.hpp
// Clock Signal
//
// Created by Thomas Harte on 14/04/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef Analyser_Static_AppleII_StaticAnalyser_hpp
#define Analyser_Static_AppleII_StaticAnalyser_hpp
#include "../StaticAnalyser.hpp"
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace AppleII {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* Analyser_Static_AppleII_StaticAnalyser_hpp */

View File

@@ -0,0 +1,37 @@
//
// Target.hpp
// Clock Signal
//
// Created by Thomas Harte on 21/04/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef Target_h
#define Target_h
#include "../StaticAnalyser.hpp"
namespace Analyser {
namespace Static {
namespace AppleII {
struct Target: public ::Analyser::Static::Target {
enum class Model {
II,
IIplus
};
enum class DiskController {
None,
SixteenSector,
ThirteenSector
};
Model model = Model::IIplus;
DiskController disk_controller = DiskController::None;
};
}
}
}
#endif /* Target_h */

View File

@@ -3,16 +3,18 @@
// Clock Signal
//
// Created by Thomas Harte on 15/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "StaticAnalyser.hpp"
#include "Target.hpp"
#include "../Disassembler/6502.hpp"
using namespace Analyser::Static::Atari;
static void DeterminePagingFor2kCartridge(Analyser::Static::Target &target, const Storage::Cartridge::Cartridge::Segment &segment) {
static void DeterminePagingFor2kCartridge(Analyser::Static::Atari::Target &target, const Storage::Cartridge::Cartridge::Segment &segment) {
// if this is a 2kb cartridge then it's definitely either unpaged or a CommaVid
uint16_t entry_address, break_address;
@@ -46,10 +48,10 @@ static void DeterminePagingFor2kCartridge(Analyser::Static::Target &target, cons
// caveat: false positives aren't likely to be problematic; a false positive is a 2KB ROM that always addresses
// itself so as to land in ROM even if mapped as a CommaVid and this code is on the fence as to whether it
// attempts to modify itself but it probably doesn't
if(has_wide_area_store) target.atari.paging_model = Analyser::Static::Atari2600PagingModel::CommaVid;
if(has_wide_area_store) target.paging_model = Analyser::Static::Atari::Target::PagingModel::CommaVid;
}
static void DeterminePagingFor8kCartridge(Analyser::Static::Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const Analyser::Static::MOS6502::Disassembly &disassembly) {
static void DeterminePagingFor8kCartridge(Analyser::Static::Atari::Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const Analyser::Static::MOS6502::Disassembly &disassembly) {
// Activision stack titles have their vectors at the top of the low 4k, not the top, and
// always list 0xf000 as both vectors; they do not repeat them, and, inexplicably, they all
// issue an SEI as their first instruction (maybe some sort of relic of the development environment?)
@@ -58,12 +60,12 @@ static void DeterminePagingFor8kCartridge(Analyser::Static::Target &target, cons
(segment.data[8191] != 0xf0 || segment.data[8189] != 0xf0 || segment.data[8190] != 0x00 || segment.data[8188] != 0x00) &&
segment.data[0] == 0x78
) {
target.atari.paging_model = Analyser::Static::Atari2600PagingModel::ActivisionStack;
target.paging_model = Analyser::Static::Atari::Target::PagingModel::ActivisionStack;
return;
}
// make an assumption that this is the Atari paging model
target.atari.paging_model = Analyser::Static::Atari2600PagingModel::Atari8k;
target.paging_model = Analyser::Static::Atari::Target::PagingModel::Atari8k;
std::set<uint16_t> internal_accesses;
internal_accesses.insert(disassembly.internal_stores.begin(), disassembly.internal_stores.end());
@@ -83,13 +85,13 @@ static void DeterminePagingFor8kCartridge(Analyser::Static::Target &target, cons
tigervision_access_count += masked_address == 0x3f;
}
if(parker_access_count > atari_access_count) target.atari.paging_model = Analyser::Static::Atari2600PagingModel::ParkerBros;
else if(tigervision_access_count > atari_access_count) target.atari.paging_model = Analyser::Static::Atari2600PagingModel::Tigervision;
if(parker_access_count > atari_access_count) target.paging_model = Analyser::Static::Atari::Target::PagingModel::ParkerBros;
else if(tigervision_access_count > atari_access_count) target.paging_model = Analyser::Static::Atari::Target::PagingModel::Tigervision;
}
static void DeterminePagingFor16kCartridge(Analyser::Static::Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const Analyser::Static::MOS6502::Disassembly &disassembly) {
static void DeterminePagingFor16kCartridge(Analyser::Static::Atari::Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const Analyser::Static::MOS6502::Disassembly &disassembly) {
// make an assumption that this is the Atari paging model
target.atari.paging_model = Analyser::Static::Atari2600PagingModel::Atari16k;
target.paging_model = Analyser::Static::Atari::Target::PagingModel::Atari16k;
std::set<uint16_t> internal_accesses;
internal_accesses.insert(disassembly.internal_stores.begin(), disassembly.internal_stores.end());
@@ -104,17 +106,17 @@ static void DeterminePagingFor16kCartridge(Analyser::Static::Target &target, con
mnetwork_access_count += masked_address >= 0x1fe0 && masked_address < 0x1ffb;
}
if(mnetwork_access_count > atari_access_count) target.atari.paging_model = Analyser::Static::Atari2600PagingModel::MNetwork;
if(mnetwork_access_count > atari_access_count) target.paging_model = Analyser::Static::Atari::Target::PagingModel::MNetwork;
}
static void DeterminePagingFor64kCartridge(Analyser::Static::Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const Analyser::Static::MOS6502::Disassembly &disassembly) {
static void DeterminePagingFor64kCartridge(Analyser::Static::Atari::Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const Analyser::Static::MOS6502::Disassembly &disassembly) {
// make an assumption that this is a Tigervision if there is a write to 3F
target.atari.paging_model =
target.paging_model =
(disassembly.external_stores.find(0x3f) != disassembly.external_stores.end()) ?
Analyser::Static::Atari2600PagingModel::Tigervision : Analyser::Static::Atari2600PagingModel::MegaBoy;
Analyser::Static::Atari::Target::PagingModel::Tigervision : Analyser::Static::Atari::Target::PagingModel::MegaBoy;
}
static void DeterminePagingForCartridge(Analyser::Static::Target &target, const Storage::Cartridge::Cartridge::Segment &segment) {
static void DeterminePagingForCartridge(Analyser::Static::Atari::Target &target, const Storage::Cartridge::Cartridge::Segment &segment) {
if(segment.data.size() == 2048) {
DeterminePagingFor2kCartridge(target, segment);
return;
@@ -138,16 +140,16 @@ static void DeterminePagingForCartridge(Analyser::Static::Target &target, const
DeterminePagingFor8kCartridge(target, segment, disassembly);
break;
case 10495:
target.atari.paging_model = Analyser::Static::Atari2600PagingModel::Pitfall2;
target.paging_model = Analyser::Static::Atari::Target::PagingModel::Pitfall2;
break;
case 12288:
target.atari.paging_model = Analyser::Static::Atari2600PagingModel::CBSRamPlus;
target.paging_model = Analyser::Static::Atari::Target::PagingModel::CBSRamPlus;
break;
case 16384:
DeterminePagingFor16kCartridge(target, segment, disassembly);
break;
case 32768:
target.atari.paging_model = Analyser::Static::Atari2600PagingModel::Atari32k;
target.paging_model = Analyser::Static::Atari::Target::PagingModel::Atari32k;
break;
case 65536:
DeterminePagingFor64kCartridge(target, segment, disassembly);
@@ -159,8 +161,8 @@ static void DeterminePagingForCartridge(Analyser::Static::Target &target, const
// check for a Super Chip. Atari ROM images [almost] always have the same value stored over RAM
// regions; when they don't they at least seem to have the first 128 bytes be the same as the
// next 128 bytes. So check for that.
if( target.atari.paging_model != Analyser::Static::Atari2600PagingModel::CBSRamPlus &&
target.atari.paging_model != Analyser::Static::Atari2600PagingModel::MNetwork) {
if( target.paging_model != Analyser::Static::Atari::Target::PagingModel::CBSRamPlus &&
target.paging_model != Analyser::Static::Atari::Target::PagingModel::MNetwork) {
bool has_superchip = true;
for(std::size_t address = 0; address < 128; address++) {
if(segment.data[address] != segment.data[address+128]) {
@@ -168,24 +170,24 @@ static void DeterminePagingForCartridge(Analyser::Static::Target &target, const
break;
}
}
target.atari.uses_superchip = has_superchip;
target.uses_superchip = has_superchip;
}
// check for a Tigervision or Tigervision-esque scheme
if(target.atari.paging_model == Analyser::Static::Atari2600PagingModel::None && segment.data.size() > 4096) {
if(target.paging_model == Analyser::Static::Atari::Target::PagingModel::None && segment.data.size() > 4096) {
bool looks_like_tigervision = disassembly.external_stores.find(0x3f) != disassembly.external_stores.end();
if(looks_like_tigervision) target.atari.paging_model = Analyser::Static::Atari2600PagingModel::Tigervision;
if(looks_like_tigervision) target.paging_model = Analyser::Static::Atari::Target::PagingModel::Tigervision;
}
}
void Analyser::Static::Atari::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
// TODO: sanity checking; is this image really for an Atari 2600.
std::unique_ptr<Target> target(new Target);
Analyser::Static::TargetList Analyser::Static::Atari::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
// TODO: sanity checking; is this image really for an Atari 2600?
std::unique_ptr<Analyser::Static::Atari::Target> target(new Analyser::Static::Atari::Target);
target->machine = Machine::Atari2600;
target->confidence = 1.0;
target->confidence = 0.5;
target->media.cartridges = media.cartridges;
target->atari.paging_model = Atari2600PagingModel::None;
target->atari.uses_superchip = false;
target->paging_model = Analyser::Static::Atari::Target::PagingModel::None;
target->uses_superchip = false;
// try to figure out the paging scheme
if(!media.cartridges.empty()) {
@@ -196,6 +198,7 @@ void Analyser::Static::Atari::AddTargets(const Media &media, std::vector<std::un
DeterminePagingForCartridge(*target, segment);
}
}
destination.push_back(std::move(target));
TargetList destinations;
destinations.push_back(std::move(target));
return destinations;
}

View File

@@ -3,19 +3,21 @@
// Clock Signal
//
// Created by Thomas Harte on 15/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Atari_StaticAnalyser_hpp
#define StaticAnalyser_Atari_StaticAnalyser_hpp
#include "../StaticAnalyser.hpp"
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Atari {
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}

View File

@@ -0,0 +1,43 @@
//
// Target.hpp
// Clock Signal
//
// Created by Thomas Harte on 09/03/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef Analyser_Static_Atari_Target_h
#define Analyser_Static_Atari_Target_h
#include "../StaticAnalyser.hpp"
namespace Analyser {
namespace Static {
namespace Atari {
struct Target: public ::Analyser::Static::Target {
enum class PagingModel {
None,
CommaVid,
Atari8k,
Atari16k,
Atari32k,
ActivisionStack,
ParkerBros,
Tigervision,
CBSRamPlus,
MNetwork,
MegaBoy,
Pitfall2
};
// TODO: shouldn't these be properties of the cartridge?
PagingModel paging_model = PagingModel::None;
bool uses_superchip = false;
};
}
}
}
#endif /* Analyser_Static_Atari_Target_h */

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 23/02/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "StaticAnalyser.hpp"
@@ -52,11 +52,13 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
return coleco_cartridges;
}
void Analyser::Static::Coleco::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
Analyser::Static::TargetList Analyser::Static::Coleco::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
TargetList targets;
std::unique_ptr<Target> target(new Target);
target->machine = Machine::ColecoVision;
target->confidence = 0.5;
target->confidence = 1.0f - 1.0f / 32768.0f;
target->media.cartridges = ColecoCartridgesFrom(media.cartridges);
if(!target->media.empty())
destination.push_back(std::move(target));
targets.push_back(std::move(target));
return targets;
}

View File

@@ -3,19 +3,21 @@
// Clock Signal
//
// Created by Thomas Harte on 23/02/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Coleco_StaticAnalyser_hpp
#define StaticAnalyser_Coleco_StaticAnalyser_hpp
#include "../StaticAnalyser.hpp"
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Coleco {
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 13/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "Disk.hpp"
@@ -45,9 +45,11 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
if(difference) {
int direction = difference < 0 ? -1 : 1;
difference *= 2 * direction;
difference *= direction;
for(int c = 0; c < difference; c++) get_drive().step(direction);
for(int c = 0; c < difference; c++) {
get_drive().step(Storage::Disk::HeadPosition(direction));
}
unsigned int zone = 3;
if(track >= 18) zone = 2;
@@ -71,19 +73,19 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
bit_count_++;
}
unsigned int proceed_to_next_block() {
unsigned int proceed_to_next_block(int max_index_count) {
// find GCR lead-in
proceed_to_shift_value(0x3ff);
if(shift_register_ != 0x3ff) return 0xff;
// find end of lead-in
while(shift_register_ == 0x3ff && index_count_ < 2) {
while(shift_register_ == 0x3ff && index_count_ < max_index_count) {
run_for(Cycles(1));
}
// continue for a further nine bits
bit_count_ = 0;
while(bit_count_ < 9 && index_count_ < 2) {
while(bit_count_ < 9 && index_count_ < max_index_count) {
run_for(Cycles(1));
}
@@ -97,8 +99,8 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
}
void proceed_to_shift_value(unsigned int shift_value) {
index_count_ = 0;
while(shift_register_ != shift_value && index_count_ < 2) {
const int max_index_count = index_count_ + 2;
while(shift_register_ != shift_value && index_count_ < max_index_count) {
run_for(Cycles(1));
}
}
@@ -124,13 +126,13 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
std::shared_ptr<Sector> get_next_sector() {
std::shared_ptr<Sector> sector(new Sector);
index_count_ = 0;
const int max_index_count = index_count_ + 2;
while(index_count_ < 2) {
while(index_count_ < max_index_count) {
// look for a sector header
while(1) {
if(proceed_to_next_block() == 0x08) break;
if(index_count_ >= 2) return nullptr;
if(proceed_to_next_block(max_index_count) == 0x08) break;
if(index_count_ >= max_index_count) return nullptr;
}
// get sector details, skip if this looks malformed
@@ -144,8 +146,8 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
// look for the following data
while(1) {
if(proceed_to_next_block() == 0x07) break;
if(index_count_ >= 2) return nullptr;
if(proceed_to_next_block(max_index_count) == 0x07) break;
if(index_count_ >= max_index_count) return nullptr;
}
checksum = 0;

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 13/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Commodore_Disk_hpp

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 10/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "File.hpp"
@@ -21,7 +21,7 @@ bool Analyser::Static::Commodore::File::is_basic() {
// [4 bytes: address of start of next line]
// [4 bytes: this line number]
// ... null-terminated code ...
// (with a next line address of 0000 indicating end of program)ß
// (with a next line address of 0000 indicating end of program)
while(1) {
if(static_cast<size_t>(line_address - starting_address) >= data.size() + 2) break;

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 10/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef File_hpp

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 06/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "StaticAnalyser.hpp"
@@ -11,8 +11,10 @@
#include "Disk.hpp"
#include "File.hpp"
#include "Tape.hpp"
#include "Target.hpp"
#include "../../../Storage/Cartridge/Encodings/CommodoreROM.hpp"
#include <algorithm>
#include <sstream>
using namespace Analyser::Static::Commodore;
@@ -38,10 +40,12 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
return vic20_cartridges;
}
void Analyser::Static::Commodore::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
TargetList destination;
std::unique_ptr<Target> target(new Target);
target->machine = Machine::Vic20; // TODO: machine estimation
target->confidence = 1.0; // TODO: a proper estimation
target->confidence = 0.5; // TODO: a proper estimation
int device = 0;
std::vector<File> files;
@@ -73,7 +77,7 @@ void Analyser::Static::Commodore::AddTargets(const Media &media, std::vector<std
}
if(!files.empty()) {
target->vic20.memory_model = Vic20MemoryModel::Unexpanded;
target->memory_model = Target::MemoryModel::Unexpanded;
std::ostringstream string_stream;
string_stream << "LOAD\"" << (is_disk ? "*" : "") << "\"," << device << ",";
if(files.front().is_basic()) {
@@ -86,19 +90,22 @@ void Analyser::Static::Commodore::AddTargets(const Media &media, std::vector<std
// make a first guess based on loading address
switch(files.front().starting_address) {
default:
printf("Starting address %04x?\n", files.front().starting_address);
case 0x1001:
default: break;
target->memory_model = Target::MemoryModel::Unexpanded;
break;
case 0x1201:
target->vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB;
target->memory_model = Target::MemoryModel::ThirtyTwoKB;
break;
case 0x0401:
target->vic20.memory_model = Vic20MemoryModel::EightKB;
target->memory_model = Target::MemoryModel::EightKB;
break;
}
// General approach: increase memory size conservatively such that the largest file found will fit.
for(File &file : files) {
std::size_t file_size = file.data.size();
// for(File &file : files) {
// std::size_t file_size = file.data.size();
// bool is_basic = file.is_basic();
/*if(is_basic)
@@ -124,18 +131,31 @@ void Analyser::Static::Commodore::AddTargets(const Media &media, std::vector<std
// An unexpanded Vic has memory between 0x0000 and 0x0400; and between 0x1000 and 0x2000.
// A 3kb expanded Vic fills in the gap and has memory between 0x0000 and 0x2000.
// A 32kb expanded Vic has memory in the entire low 32kb.
uint16_t starting_address = file.starting_address;
// uint16_t starting_address = file.starting_address;
// If anything above the 8kb mark is touched, mark as a 32kb machine; otherwise if the
// region 0x0400 to 0x1000 is touched and this is an unexpanded machine, mark as 3kb.
if(starting_address + file_size > 0x2000)
target->vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB;
else if(target->vic20.memory_model == Vic20MemoryModel::Unexpanded && !(starting_address >= 0x1000 || starting_address+file_size < 0x0400))
target->vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB;
// if(starting_address + file_size > 0x2000)
// target->memory_model = Target::MemoryModel::ThirtyTwoKB;
// else if(target->memory_model == Target::MemoryModel::Unexpanded && !(starting_address >= 0x1000 || starting_address+file_size < 0x0400))
// target->memory_model = Target::MemoryModel::ThirtyTwoKB;
// }
// }
}
if(!target->media.empty()) {
// Inspect filename for a region hint.
std::string lowercase_name = file_name;
std::transform(lowercase_name.begin(), lowercase_name.end(), lowercase_name.begin(), ::tolower);
if(lowercase_name.find("ntsc") != std::string::npos) {
target->region = Analyser::Static::Commodore::Target::Region::American;
}
if(!target->media.empty())
// Attach a 1540 if there are any disks here.
target->has_c1540 = !target->media.disks.empty();
destination.push_back(std::move(target));
}
return destination;
}

View File

@@ -3,19 +3,21 @@
// Clock Signal
//
// Created by Thomas Harte on 06/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Commodore_StaticAnalyser_hpp
#define StaticAnalyser_Commodore_StaticAnalyser_hpp
#include "../StaticAnalyser.hpp"
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Commodore {
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 24/08/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "Tape.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 24/08/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Commodore_Tape_hpp

View File

@@ -0,0 +1,44 @@
//
// Target.hpp
// Clock Signal
//
// Created by Thomas Harte on 09/03/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef Analyser_Static_Commodore_Target_h
#define Analyser_Static_Commodore_Target_h
#include "../StaticAnalyser.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Commodore {
struct Target: public ::Analyser::Static::Target {
enum class MemoryModel {
Unexpanded,
EightKB,
ThirtyTwoKB
};
enum class Region {
American,
Danish,
Japanese,
European,
Swedish
};
MemoryModel memory_model = MemoryModel::Unexpanded;
Region region = Region::European;
bool has_c1540 = false;
std::string loading_command;
};
}
}
}
#endif /* Analyser_Static_Commodore_Target_h */

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 10/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "6502.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 10/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Disassembler_6502_hpp
@@ -21,7 +21,7 @@ namespace Static {
namespace MOS6502 {
/*!
Describes a 6502 instruciton its address, the operation it performs, its addressing mode
Describes a 6502 instruciton: its address, the operation it performs, its addressing mode
and its operand, if any.
*/
struct Instruction {

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 30/12/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#include "AddressMapper.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 30/12/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef AddressMapper_hpp

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 31/12/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef Kernel_hpp

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 30/12/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#include "Z80.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 30/12/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Disassembler_Z80_hpp

View File

@@ -0,0 +1,125 @@
//
// StaticAnalyser.cpp
// Clock Signal
//
// Created by Thomas Harte on 03/05/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "StaticAnalyser.hpp"
#include "../AppleII/Target.hpp"
#include "../Oric/Target.hpp"
#include "../Disassembler/6502.hpp"
#include "../Disassembler/AddressMapper.hpp"
#include "../../../Storage/Disk/Track/TrackSerialiser.hpp"
#include "../../../Storage/Disk/Encodings/AppleGCR/SegmentParser.hpp"
namespace {
Analyser::Static::Target *AppleTarget(const Storage::Encodings::AppleGCR::Sector *sector_zero) {
using Target = Analyser::Static::AppleII::Target;
auto *target = new Target;
target->machine = Analyser::Machine::AppleII;
if(sector_zero && sector_zero->encoding == Storage::Encodings::AppleGCR::Sector::Encoding::FiveAndThree) {
target->disk_controller = Target::DiskController::ThirteenSector;
} else {
target->disk_controller = Target::DiskController::SixteenSector;
}
return target;
}
Analyser::Static::Target *OricTarget(const Storage::Encodings::AppleGCR::Sector *sector_zero) {
using Target = Analyser::Static::Oric::Target;
auto *target = new Target;
target->machine = Analyser::Machine::Oric;
target->rom = Target::ROM::Pravetz;
target->disk_interface = Target::DiskInterface::Pravetz;
target->loading_command = "CALL 800\n";
return target;
}
}
Analyser::Static::TargetList Analyser::Static::DiskII::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
// This analyser can comprehend disks only.
if(media.disks.empty()) return {};
// Grab track 0, sector 0: the boot sector.
auto track_zero = media.disks.front()->get_track_at_position(Storage::Disk::Track::Address(0, Storage::Disk::HeadPosition(0)));
auto sector_map = Storage::Encodings::AppleGCR::sectors_from_segment(
Storage::Disk::track_serialisation(*track_zero, Storage::Time(1, 50000)));
const Storage::Encodings::AppleGCR::Sector *sector_zero = nullptr;
for(const auto &pair: sector_map) {
if(!pair.second.address.sector) {
sector_zero = &pair.second;
break;
}
}
// If there's no boot sector then if there are also no sectors at all,
// decline to nominate a machine. Otherwise go with an Apple as the default.
TargetList targets;
if(!sector_zero) {
if(sector_map.empty()) {
return targets;
} else {
targets.push_back(std::unique_ptr<Analyser::Static::Target>(AppleTarget(nullptr)));
targets.back()->media = media;
return targets;
}
}
// If the boot sector looks like it's intended for the Oric, create an Oric.
// Otherwise go with the Apple II.
auto disassembly = Analyser::Static::MOS6502::Disassemble(sector_zero->data, Analyser::Static::Disassembler::OffsetMapper(0xb800), {0xb800});
bool did_read_shift_register = false;
bool is_oric = false;
// Look for a tight BPL loop reading the Oric's shift register address of 0x31c. The Apple II just has RAM there,
// so the probability of such a loop is infinitesimal.
for(const auto &instruction: disassembly.instructions_by_address) {
// Is this a read of the shift register?
if(
(
(instruction.second.operation == Analyser::Static::MOS6502::Instruction::LDA) ||
(instruction.second.operation == Analyser::Static::MOS6502::Instruction::LDX) ||
(instruction.second.operation == Analyser::Static::MOS6502::Instruction::LDY)
) &&
instruction.second.addressing_mode == Analyser::Static::MOS6502::Instruction::Absolute &&
instruction.second.address == 0x031c) {
did_read_shift_register = true;
continue;
}
if(did_read_shift_register) {
if(
instruction.second.operation == Analyser::Static::MOS6502::Instruction::BPL &&
instruction.second.address == 0xfb) {
is_oric = true;
break;
}
did_read_shift_register = false;
}
}
// Check also for calls into the 0x3xx page above 0x320, as that's where the Oric's boot ROM is.
for(const auto address: disassembly.outward_calls) {
is_oric |= address >= 0x320 && address < 0x400;
}
if(is_oric) {
targets.push_back(std::unique_ptr<Analyser::Static::Target>(OricTarget(sector_zero)));
} else {
targets.push_back(std::unique_ptr<Analyser::Static::Target>(AppleTarget(sector_zero)));
}
targets.back()->media = media;
return targets;
}

View File

@@ -0,0 +1,27 @@
//
// StaticAnalyser.hpp
// Clock Signal
//
// Created by Thomas Harte on 03/05/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef Analyser_Static_DiskII_StaticAnalyser_hpp
#define Analyser_Static_DiskII_StaticAnalyser_hpp
#include "../StaticAnalyser.hpp"
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace DiskII {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* Analyser_Static_DiskII_StaticAnalyser_hpp */

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 25/01/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef Cartridge_hpp

View File

@@ -3,13 +3,15 @@
// Clock Signal
//
// Created by Thomas Harte on 25/11/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#include "StaticAnalyser.hpp"
#include "Cartridge.hpp"
#include "Tape.hpp"
#include "Target.hpp"
#include "../Disassembler/Z80.hpp"
#include "../Disassembler/AddressMapper.hpp"
@@ -32,7 +34,7 @@ static std::unique_ptr<Analyser::Static::Target> CartridgeTarget(
output_segments.emplace_back(start_address, segment.data);
}
std::unique_ptr<Analyser::Static::Target> target(new Analyser::Static::Target);
std::unique_ptr<Analyser::Static::MSX::Target> target(new Analyser::Static::MSX::Target);
target->machine = Analyser::Machine::MSX;
target->confidence = confidence;
@@ -61,14 +63,14 @@ static std::unique_ptr<Analyser::Static::Target> CartridgeTarget(
(additional audio hardware is also sometimes included, but it's implied by the banking hardware)
*/
static std::vector<std::unique_ptr<Analyser::Static::Target>> CartridgeTargetsFrom(
static Analyser::Static::TargetList CartridgeTargetsFrom(
const std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
// No cartridges implies no targets.
if(cartridges.empty()) {
return {};
}
std::vector<std::unique_ptr<Analyser::Static::Target>> targets;
Analyser::Static::TargetList targets;
for(const auto &cartridge : cartridges) {
const auto &segments = cartridge->get_segments();
@@ -259,16 +261,18 @@ static std::vector<std::unique_ptr<Analyser::Static::Target>> CartridgeTargetsFr
return targets;
}
void Analyser::Static::MSX::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
Analyser::Static::TargetList Analyser::Static::MSX::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
TargetList destination;
// Append targets for any cartridges that look correct.
std::vector<std::unique_ptr<Target>> cartridge_targets = CartridgeTargetsFrom(media.cartridges);
auto cartridge_targets = CartridgeTargetsFrom(media.cartridges);
std::move(cartridge_targets.begin(), cartridge_targets.end(), std::back_inserter(destination));
// Consider building a target for disks and/or tapes.
std::unique_ptr<Target> target(new Target);
// Check tapes for loadable files.
for(const auto &tape : media.tapes) {
for(auto &tape : media.tapes) {
std::vector<File> files_on_tape = GetFiles(tape);
if(!files_on_tape.empty()) {
switch(files_on_tape.front().type) {
@@ -283,10 +287,13 @@ void Analyser::Static::MSX::AddTargets(const Media &media, std::vector<std::uniq
// Blindly accept disks for now.
target->media.disks = media.disks;
target->has_disk_drive = !media.disks.empty();
if(!target->media.empty()) {
target->machine = Machine::MSX;
target->confidence = 1.0;
target->confidence = 0.5;
destination.push_back(std::move(target));
}
return destination;
}

View File

@@ -3,19 +3,21 @@
// Clock Signal
//
// Created by Thomas Harte on 25/11/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_MSX_StaticAnalyser_hpp
#define StaticAnalyser_MSX_StaticAnalyser_hpp
#include "../StaticAnalyser.hpp"
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace MSX {
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 25/12/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#include "Tape.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 25/12/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_MSX_Tape_hpp

View File

@@ -0,0 +1,28 @@
//
// Target.hpp
// Clock Signal
//
// Created by Thomas Harte on 02/04/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef Analyser_Static_MSX_Target_h
#define Analyser_Static_MSX_Target_h
#include "../StaticAnalyser.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace MSX {
struct Target: public ::Analyser::Static::Target {
bool has_disk_drive = false;
std::string loading_command;
};
}
}
}
#endif /* Analyser_Static_MSX_Target_h */

View File

@@ -3,29 +3,35 @@
// Clock Signal
//
// Created by Thomas Harte on 11/10/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "StaticAnalyser.hpp"
#include "Tape.hpp"
#include "Target.hpp"
#include "../Disassembler/6502.hpp"
#include "../Disassembler/AddressMapper.hpp"
#include "../../../Storage/Disk/Encodings/MFM/Parser.hpp"
#include <cstring>
using namespace Analyser::Static::Oric;
static int Score(const Analyser::Static::MOS6502::Disassembly &disassembly, const std::set<uint16_t> &rom_functions, const std::set<uint16_t> &variable_locations) {
int score = 0;
for(auto address : disassembly.outward_calls) score += (rom_functions.find(address) != rom_functions.end()) ? 1 : -1;
for(auto address : disassembly.external_stores) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
for(auto address : disassembly.external_loads) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
for(const auto address : disassembly.outward_calls) score += (rom_functions.find(address) != rom_functions.end()) ? 1 : -1;
for(const auto address : disassembly.external_stores) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
for(const auto address : disassembly.external_loads) score += (variable_locations.find(address) != variable_locations.end()) ? 1 : -1;
return score;
}
static int Basic10Score(const Analyser::Static::MOS6502::Disassembly &disassembly) {
std::set<uint16_t> rom_functions = {
const std::set<uint16_t> rom_functions = {
0x0228, 0x022b,
0xc3ca, 0xc3f8, 0xc448, 0xc47c, 0xc4b5, 0xc4e3, 0xc4e0, 0xc524, 0xc56f, 0xc5a2, 0xc5f8, 0xc60a, 0xc6a5, 0xc6de, 0xc719, 0xc738,
0xc773, 0xc824, 0xc832, 0xc841, 0xc8c1, 0xc8fe, 0xc91f, 0xc93f, 0xc941, 0xc91e, 0xc98b, 0xc996, 0xc9b3, 0xc9e0, 0xca0a, 0xca1c,
@@ -41,7 +47,7 @@ static int Basic10Score(const Analyser::Static::MOS6502::Disassembly &disassembl
0xf43c, 0xf4ef, 0xf523, 0xf561, 0xf535, 0xf57b, 0xf5d3, 0xf71a, 0xf73f, 0xf7e4, 0xf7e0, 0xf82f, 0xf88f, 0xf8af, 0xf8b5, 0xf920,
0xf967, 0xf960, 0xf9c9, 0xfa14, 0xfa85, 0xfa9b, 0xfab1, 0xfac7, 0xfafa, 0xfb10, 0xfb26, 0xfbb6, 0xfbfe
};
std::set<uint16_t> variable_locations = {
const std::set<uint16_t> variable_locations = {
0x0228, 0x0229, 0x022a, 0x022b, 0x022c, 0x022d, 0x0230
};
@@ -49,7 +55,7 @@ static int Basic10Score(const Analyser::Static::MOS6502::Disassembly &disassembl
}
static int Basic11Score(const Analyser::Static::MOS6502::Disassembly &disassembly) {
std::set<uint16_t> rom_functions = {
const std::set<uint16_t> rom_functions = {
0x0238, 0x023b, 0x023e, 0x0241, 0x0244, 0x0247,
0xc3c6, 0xc3f4, 0xc444, 0xc47c, 0xc4a8, 0xc4d3, 0xc4e0, 0xc524, 0xc55f, 0xc592, 0xc5e8, 0xc5fa, 0xc692, 0xc6b3, 0xc6ee, 0xc70d,
0xc748, 0xc7fd, 0xc809, 0xc816, 0xc82f, 0xc855, 0xc8c1, 0xc915, 0xc952, 0xc971, 0xc973, 0xc9a0, 0xc9bd, 0xc9c8, 0xc9e5, 0xca12,
@@ -66,17 +72,38 @@ static int Basic11Score(const Analyser::Static::MOS6502::Disassembly &disassembl
0xf88f, 0xf8af, 0xf8b5, 0xf920, 0xf967, 0xf9aa, 0xf9c9, 0xfa14, 0xfa9f, 0xfab5, 0xfacb, 0xfae1, 0xfb14, 0xfb2a, 0xfb40, 0xfbd0,
0xfc18
};
std::set<uint16_t> variable_locations = {
const std::set<uint16_t> variable_locations = {
0x0244, 0x0245, 0x0246, 0x0247, 0x0248, 0x0249, 0x024a, 0x024b, 0x024c
};
return Score(disassembly, rom_functions, variable_locations);
}
void Analyser::Static::Oric::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
static bool IsMicrodisc(Storage::Encodings::MFM::Parser &parser) {
/*
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);
if(!sector) return false;
if(sector->samples.empty()) return false;
const std::vector<uint8_t> &first_sample = sector->samples[0];
if(first_sample.size() != 256) return false;
const uint8_t signature[] = {
0x00, 0x00, 0xFF, 0x00, 0xD0, 0x9F, 0xD0,
0x9F, 0x02, 0xB9, 0x01, 0x00, 0xFF, 0x00,
0x00, 0xB9, 0xE4, 0xB9, 0x00, 0x00, 0xE6,
0x12, 0x00
};
return !std::memcmp(signature, first_sample.data(), sizeof(signature));
}
Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
std::unique_ptr<Target> target(new Target);
target->machine = Machine::Oric;
target->confidence = 1.0;
target->confidence = 0.5;
int basic10_votes = 0;
int basic11_votes = 0;
@@ -85,7 +112,7 @@ void Analyser::Static::Oric::AddTargets(const Media &media, std::vector<std::uni
std::vector<File> tape_files = GetFiles(tape);
tape->reset();
if(tape_files.size()) {
for(auto file : tape_files) {
for(const auto &file : tape_files) {
if(file.data_type == File::MachineCode) {
std::vector<uint16_t> entry_points = {file.starting_address};
Analyser::Static::MOS6502::Disassembly disassembly =
@@ -102,18 +129,27 @@ void Analyser::Static::Oric::AddTargets(const Media &media, std::vector<std::uni
}
}
// trust that any disk supplied can be handled by the Microdisc. TODO: check.
if(!media.disks.empty()) {
target->oric.has_microdisc = true;
target->media.disks = media.disks;
} else {
target->oric.has_microdisc = false;
// Only the Microdisc is emulated right now, so accept only disks that it can boot from.
for(auto &disk: media.disks) {
Storage::Encodings::MFM::Parser parser(true, disk);
if(IsMicrodisc(parser)) {
target->disk_interface = Target::DiskInterface::Microdisc;
target->media.disks.push_back(disk);
}
}
}
else
target->disk_interface = Target::DiskInterface::None;
// TODO: really this should add two targets if not all votes agree
target->oric.use_atmos_rom = basic11_votes >= basic10_votes;
if(target->oric.has_microdisc) target->oric.use_atmos_rom = true;
if(basic11_votes >= basic10_votes || target->disk_interface == Target::DiskInterface::Microdisc)
target->rom = Target::ROM::BASIC11;
else
target->rom = Target::ROM::BASIC10;
TargetList targets;
if(target->media.tapes.size() || target->media.disks.size() || target->media.cartridges.size())
destination.push_back(std::move(target));
targets.push_back(std::move(target));
return targets;
}

View File

@@ -3,19 +3,21 @@
// Clock Signal
//
// Created by Thomas Harte on 11/10/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Oric_StaticAnalyser_hpp
#define StaticAnalyser_Oric_StaticAnalyser_hpp
#include "../StaticAnalyser.hpp"
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Oric {
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 06/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "Tape.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 06/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_Oric_Tape_hpp

View File

@@ -0,0 +1,41 @@
//
// Target.hpp
// Clock Signal
//
// Created by Thomas Harte on 09/03/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef Analyser_Static_Oric_Target_h
#define Analyser_Static_Oric_Target_h
#include "../StaticAnalyser.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Oric {
struct Target: public ::Analyser::Static::Target {
enum class ROM {
BASIC10,
BASIC11,
Pravetz
};
enum class DiskInterface {
Microdisc,
Pravetz,
None
};
ROM rom = ROM::BASIC11;
DiskInterface disk_interface = DiskInterface::None;
std::string loading_command;
};
}
}
}
#endif /* Analyser_Static_Oric_Target_h */

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 23/08/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "StaticAnalyser.hpp"
@@ -11,13 +11,16 @@
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <iterator>
// Analysers
#include "Acorn/StaticAnalyser.hpp"
#include "AmstradCPC/StaticAnalyser.hpp"
#include "AppleII/StaticAnalyser.hpp"
#include "Atari/StaticAnalyser.hpp"
#include "Coleco/StaticAnalyser.hpp"
#include "Commodore/StaticAnalyser.hpp"
#include "DiskII/StaticAnalyser.hpp"
#include "MSX/StaticAnalyser.hpp"
#include "Oric/StaticAnalyser.hpp"
#include "ZX8081/StaticAnalyser.hpp"
@@ -28,14 +31,17 @@
// Disks
#include "../../Storage/Disk/DiskImage/Formats/AcornADF.hpp"
#include "../../Storage/Disk/DiskImage/Formats/AppleDSK.hpp"
#include "../../Storage/Disk/DiskImage/Formats/CPCDSK.hpp"
#include "../../Storage/Disk/DiskImage/Formats/D64.hpp"
#include "../../Storage/Disk/DiskImage/Formats/G64.hpp"
#include "../../Storage/Disk/DiskImage/Formats/DMK.hpp"
#include "../../Storage/Disk/DiskImage/Formats/HFE.hpp"
#include "../../Storage/Disk/DiskImage/Formats/MSXDSK.hpp"
#include "../../Storage/Disk/DiskImage/Formats/NIB.hpp"
#include "../../Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp"
#include "../../Storage/Disk/DiskImage/Formats/SSD.hpp"
#include "../../Storage/Disk/DiskImage/Formats/WOZ.hpp"
// Tapes
#include "../../Storage/Tape/Formats/CAS.hpp"
@@ -52,21 +58,16 @@
using namespace Analyser::Static;
static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType &potential_platforms) {
static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::IntType &potential_platforms) {
Media result;
// Get the extension, if any; it will be assumed that extensions are reliable, so an extension is a broad-phase
// test as to file format.
const char *mixed_case_extension = strrchr(file_name, '.');
char *lowercase_extension = nullptr;
if(mixed_case_extension) {
lowercase_extension = strdup(mixed_case_extension+1);
char *parser = lowercase_extension;
while(*parser) {
*parser = (char)tolower(*parser);
parser++;
}
}
std::string::size_type final_dot = file_name.find_last_of(".");
if(final_dot == std::string::npos) return result;
std::string extension = file_name.substr(final_dot + 1);
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
Media result;
#define Insert(list, class, platforms) \
list.emplace_back(new Storage::class(file_name));\
potential_platforms |= platforms;\
@@ -78,35 +79,42 @@ static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType
Insert(list, class, platforms) \
} catch(...) {}
#define Format(extension, list, class, platforms) \
if(!std::strcmp(lowercase_extension, extension)) { \
#define Format(ext, list, class, platforms) \
if(extension == ext) { \
TryInsert(list, class, platforms) \
}
if(lowercase_extension) {
Format("80", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // 80
Format("81", result.tapes, 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
Format("bin", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN
Format("bin", result.cartridges, Cartridge::BinaryDump, TargetPlatform::AllCartridge) // BIN
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::ColecoVision) // COL
Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW
Format("d64", result.disks, Disk::DiskImageHolder<Storage::Disk::D64>, TargetPlatform::Commodore) // D64
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) // DSK (Amstrad CPC)
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::AppleDSK>, TargetPlatform::DiskII) // DSK (Apple)
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::MSXDSK>, 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("hfe", result.disks, Disk::DiskImageHolder<Storage::Disk::HFE>, TargetPlatform::AmstradCPC) // HFE (TODO: plus other target platforms)
Format( "hfe",
result.disks,
Disk::DiskImageHolder<Storage::Disk::HFE>,
TargetPlatform::Acorn | TargetPlatform::AmstradCPC | TargetPlatform::Commodore | TargetPlatform::Oric)
// HFE (TODO: switch to AllDisk once the MSX stops being so greedy)
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
Format("p81", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P81
// PRG
if(!std::strcmp(lowercase_extension, "prg")) {
if(extension == "prg") {
// try instantiating as a ROM; failing that accept as a tape
try {
Insert(result.cartridges, Cartridge::PRG, TargetPlatform::Commodore)
@@ -120,33 +128,29 @@ static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType
Format( "rom",
result.cartridges,
Cartridge::BinaryDump,
TargetPlatform::Acorn | TargetPlatform::MSX | TargetPlatform::ColecoVision) // ROM
TargetPlatform::AcornElectron | TargetPlatform::ColecoVision | TargetPlatform::MSX) // ROM
Format("ssd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // SSD
Format("tap", result.tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP (Commodore)
Format("tap", result.tapes, Tape::OricTAP, TargetPlatform::Oric) // TAP (Oric)
Format("tsx", result.tapes, Tape::TZX, TargetPlatform::MSX) // TSX
Format("tzx", result.tapes, Tape::TZX, TargetPlatform::ZX8081) // 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
#undef Insert
#undef TryInsert
// Filter potential platforms as per file preferences, if any.
free(lowercase_extension);
}
return result;
}
Media Analyser::Static::GetMedia(const char *file_name) {
Media Analyser::Static::GetMedia(const std::string &file_name) {
TargetPlatform::IntType throwaway;
return GetMediaAndPlatforms(file_name, throwaway);
}
std::vector<std::unique_ptr<Target>> Analyser::Static::GetTargets(const char *file_name) {
std::vector<std::unique_ptr<Target>> targets;
TargetList Analyser::Static::GetTargets(const std::string &file_name) {
TargetList targets;
// Collect all disks, tapes and ROMs as can be extrapolated from this file, forming the
// union of all platforms this file might be a target for.
@@ -155,17 +159,24 @@ std::vector<std::unique_ptr<Target>> Analyser::Static::GetTargets(const char *fi
// Hand off to platform-specific determination of whether these things are actually compatible and,
// if so, how to load them.
if(potential_platforms & TargetPlatform::Acorn) Acorn::AddTargets(media, targets);
if(potential_platforms & TargetPlatform::AmstradCPC) AmstradCPC::AddTargets(media, targets);
if(potential_platforms & TargetPlatform::Atari2600) Atari::AddTargets(media, targets);
if(potential_platforms & TargetPlatform::ColecoVision) Coleco::AddTargets(media, targets);
if(potential_platforms & TargetPlatform::Commodore) Commodore::AddTargets(media, targets);
if(potential_platforms & TargetPlatform::MSX) MSX::AddTargets(media, targets);
if(potential_platforms & TargetPlatform::Oric) Oric::AddTargets(media, targets);
if(potential_platforms & TargetPlatform::ZX8081) ZX8081::AddTargets(media, targets, potential_platforms);
#define Append(x) {\
auto new_targets = x::GetTargets(media, file_name, potential_platforms);\
std::move(new_targets.begin(), new_targets.end(), std::back_inserter(targets));\
}
if(potential_platforms & TargetPlatform::Acorn) Append(Acorn);
if(potential_platforms & TargetPlatform::AmstradCPC) Append(AmstradCPC);
if(potential_platforms & TargetPlatform::AppleII) Append(AppleII);
if(potential_platforms & TargetPlatform::Atari2600) Append(Atari);
if(potential_platforms & TargetPlatform::ColecoVision) Append(Coleco);
if(potential_platforms & TargetPlatform::Commodore) Append(Commodore);
if(potential_platforms & TargetPlatform::DiskII) Append(DiskII);
if(potential_platforms & TargetPlatform::MSX) Append(MSX);
if(potential_platforms & TargetPlatform::Oric) Append(Oric);
if(potential_platforms & TargetPlatform::ZX8081) Append(ZX8081);
#undef Append
// Reset any tapes to their initial position
for(auto &target : targets) {
for(const auto &target : targets) {
for(auto &tape : target->media.tapes) {
tape->reset();
}

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 23/08/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_hpp
@@ -15,45 +15,13 @@
#include "../../Storage/Disk/Disk.hpp"
#include "../../Storage/Cartridge/Cartridge.hpp"
#include <memory>
#include <string>
#include <vector>
namespace Analyser {
namespace Static {
enum class Vic20MemoryModel {
Unexpanded,
EightKB,
ThirtyTwoKB
};
enum class Atari2600PagingModel {
None,
CommaVid,
Atari8k,
Atari16k,
Atari32k,
ActivisionStack,
ParkerBros,
Tigervision,
CBSRamPlus,
MNetwork,
MegaBoy,
Pitfall2
};
enum class ZX8081MemoryModel {
Unexpanded,
SixteenKB,
SixtyFourKB
};
enum class AmstradCPCModel {
CPC464,
CPC664,
CPC6128
};
/*!
A list of disks, tapes and cartridges.
*/
@@ -72,58 +40,25 @@ struct Media {
and instructions on how to launch the software attached, plus a measure of confidence in this target's correctness.
*/
struct Target {
virtual ~Target() {}
Machine machine;
Media media;
float confidence;
std::string loading_command;
// TODO: this is too C-like a solution; make Target a base class and
// turn the following into information held by more specific subclasses.
union {
struct {
bool has_adfs;
bool has_dfs;
bool should_shift_restart;
} acorn;
struct {
Atari2600PagingModel paging_model;
bool uses_superchip;
} atari;
struct {
bool use_atmos_rom;
bool has_microdisc;
} oric;
struct {
Vic20MemoryModel memory_model;
bool has_c1540;
} vic20;
struct {
ZX8081MemoryModel memory_model;
bool isZX81;
} zx8081;
struct {
AmstradCPCModel model;
} amstradcpc;
};
};
typedef std::vector<std::unique_ptr<Target>> TargetList;
/*!
Attempts, through any available means, to return a list of potential targets for the file with the given name.
@returns The list of potential targets, sorted from most to least probable.
*/
std::vector<std::unique_ptr<Target>> GetTargets(const char *file_name);
TargetList GetTargets(const std::string &file_name);
/*!
Inspects the supplied file and determines the media included.
*/
Media GetMedia(const char *file_name);
Media GetMedia(const std::string &file_name);
}
}

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 04/06/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#include "StaticAnalyser.hpp"
@@ -11,6 +11,7 @@
#include <string>
#include <vector>
#include "Target.hpp"
#include "../../../Storage/Tape/Parsers/ZX8081.hpp"
static std::vector<Storage::Data::ZX8081::File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
@@ -27,41 +28,43 @@ static std::vector<Storage::Data::ZX8081::File> GetFiles(const std::shared_ptr<S
return files;
}
void Analyser::Static::ZX8081::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination, TargetPlatform::IntType potential_platforms) {
Analyser::Static::TargetList Analyser::Static::ZX8081::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
TargetList destination;
if(!media.tapes.empty()) {
std::vector<Storage::Data::ZX8081::File> files = GetFiles(media.tapes.front());
media.tapes.front()->reset();
if(!files.empty()) {
std::unique_ptr<Target> target(new Target);
Target *target = new Target;
destination.push_back(std::unique_ptr<::Analyser::Static::Target>(target));
target->machine = Machine::ZX8081;
// Guess the machine type from the file only if it isn't already known.
switch(potential_platforms & (TargetPlatform::ZX80 | TargetPlatform::ZX81)) {
default:
target->zx8081.isZX81 = files.front().isZX81;
target->is_ZX81 = files.front().isZX81;
break;
case TargetPlatform::ZX80: target->zx8081.isZX81 = false; break;
case TargetPlatform::ZX81: target->zx8081.isZX81 = true; break;
case TargetPlatform::ZX80: target->is_ZX81 = false; break;
case TargetPlatform::ZX81: target->is_ZX81 = true; break;
}
/*if(files.front().data.size() > 16384) {
target->zx8081.memory_model = ZX8081MemoryModel::SixtyFourKB;
} else*/ if(files.front().data.size() > 1024) {
target->zx8081.memory_model = ZX8081MemoryModel::SixteenKB;
target->memory_model = Target::MemoryModel::SixteenKB;
} else {
target->zx8081.memory_model = ZX8081MemoryModel::Unexpanded;
target->memory_model = Target::MemoryModel::Unexpanded;
}
target->media.tapes = media.tapes;
// TODO: how to run software once loaded? Might require a BASIC detokeniser.
if(target->zx8081.isZX81) {
if(target->is_ZX81) {
target->loading_command = "J\"\"\n";
} else {
target->loading_command = "W\n";
}
destination.push_back(std::move(target));
}
}
return destination;
}

View File

@@ -3,20 +3,21 @@
// Clock Signal
//
// Created by Thomas Harte on 04/06/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef StaticAnalyser_ZX8081_StaticAnalyser_hpp
#define StaticAnalyser_ZX8081_StaticAnalyser_hpp
#ifndef Analyser_Static_ZX8081_StaticAnalyser_hpp
#define Analyser_Static_ZX8081_StaticAnalyser_hpp
#include "../StaticAnalyser.hpp"
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace ZX8081 {
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination, TargetPlatform::IntType potential_platforms);
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}

View File

@@ -0,0 +1,36 @@
//
// Target.hpp
// Clock Signal
//
// Created by Thomas Harte on 09/03/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef Analyser_Static_ZX8081_Target_h
#define Analyser_Static_ZX8081_Target_h
#include "../StaticAnalyser.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace ZX8081 {
struct Target: public ::Analyser::Static::Target {
enum class MemoryModel {
Unexpanded,
SixteenKB,
SixtyFourKB
};
MemoryModel memory_model = MemoryModel::Unexpanded;
bool is_ZX81 = false;
bool ZX80_uses_ZX81_ROM = false;
std::string loading_command;
};
}
}
}
#endif /* Analyser_Static_ZX8081_Target_h */

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 22/07/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef ClockReceiver_hpp
@@ -52,79 +52,79 @@
*/
template <class T> class WrappedInt {
public:
inline WrappedInt(int l) : length_(l) {}
inline WrappedInt() : length_(0) {}
constexpr WrappedInt(int l) : length_(l) {}
constexpr WrappedInt() : length_(0) {}
inline T &operator =(const T &rhs) {
T &operator =(const T &rhs) {
length_ = rhs.length_;
return *this;
}
inline T &operator +=(const T &rhs) {
T &operator +=(const T &rhs) {
length_ += rhs.length_;
return *static_cast<T *>(this);
}
inline T &operator -=(const T &rhs) {
T &operator -=(const T &rhs) {
length_ -= rhs.length_;
return *static_cast<T *>(this);
}
inline T &operator ++() {
T &operator ++() {
++ length_;
return *static_cast<T *>(this);
}
inline T &operator ++(int) {
T &operator ++(int) {
length_ ++;
return *static_cast<T *>(this);
}
inline T &operator --() {
T &operator --() {
-- length_;
return *static_cast<T *>(this);
}
inline T &operator --(int) {
T &operator --(int) {
length_ --;
return *static_cast<T *>(this);
}
inline T &operator %=(const T &rhs) {
T &operator %=(const T &rhs) {
length_ %= rhs.length_;
return *static_cast<T *>(this);
}
inline T &operator &=(const T &rhs) {
T &operator &=(const T &rhs) {
length_ &= rhs.length_;
return *static_cast<T *>(this);
}
inline T operator +(const T &rhs) const { return T(length_ + rhs.length_); }
inline T operator -(const T &rhs) const { return T(length_ - rhs.length_); }
constexpr T operator +(const T &rhs) const { return T(length_ + rhs.length_); }
constexpr T operator -(const T &rhs) const { return T(length_ - rhs.length_); }
inline T operator %(const T &rhs) const { return T(length_ % rhs.length_); }
inline T operator &(const T &rhs) const { return T(length_ & rhs.length_); }
constexpr T operator %(const T &rhs) const { return T(length_ % rhs.length_); }
constexpr T operator &(const T &rhs) const { return T(length_ & rhs.length_); }
inline T operator -() const { return T(- length_); }
constexpr T operator -() const { return T(- length_); }
inline bool operator <(const T &rhs) const { return length_ < rhs.length_; }
inline bool operator >(const T &rhs) const { return length_ > rhs.length_; }
inline bool operator <=(const T &rhs) const { return length_ <= rhs.length_; }
inline bool operator >=(const T &rhs) const { return length_ >= rhs.length_; }
inline bool operator ==(const T &rhs) const { return length_ == rhs.length_; }
inline bool operator !=(const T &rhs) const { return length_ != rhs.length_; }
constexpr bool operator <(const T &rhs) const { return length_ < rhs.length_; }
constexpr bool operator >(const T &rhs) const { return length_ > rhs.length_; }
constexpr bool operator <=(const T &rhs) const { return length_ <= rhs.length_; }
constexpr bool operator >=(const T &rhs) const { return length_ >= rhs.length_; }
constexpr bool operator ==(const T &rhs) const { return length_ == rhs.length_; }
constexpr bool operator !=(const T &rhs) const { return length_ != rhs.length_; }
inline bool operator !() const { return !length_; }
constexpr bool operator !() const { return !length_; }
// bool operator () is not supported because it offers an implicit cast to int, which is prone silently to permit misuse
inline int as_int() const { return length_; }
constexpr int as_int() const { return length_; }
/*!
Severs from @c this the effect of dividing by @c divisor @c this will end up with
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
*/
inline T divide(const T &divisor) {
T divide(const T &divisor) {
T result(length_ / divisor.length_);
length_ %= divisor.length_;
return result;
@@ -134,7 +134,7 @@ template <class T> class WrappedInt {
Flushes the value in @c this. The current value is returned, and the internal value
is reset to zero.
*/
inline T flush() {
T flush() {
T result(length_);
length_ = 0;
return result;
@@ -147,47 +147,47 @@ template <class T> class WrappedInt {
int length_;
};
/// Describes an integer number of whole cycles pairs of clock signal transitions.
/// Describes an integer number of whole cycles: pairs of clock signal transitions.
class Cycles: public WrappedInt<Cycles> {
public:
inline Cycles(int l) : WrappedInt<Cycles>(l) {}
inline Cycles() : WrappedInt<Cycles>() {}
inline Cycles(const Cycles &cycles) : WrappedInt<Cycles>(cycles.length_) {}
constexpr Cycles(int l) : WrappedInt<Cycles>(l) {}
constexpr Cycles() : WrappedInt<Cycles>() {}
constexpr Cycles(const Cycles &cycles) : WrappedInt<Cycles>(cycles.length_) {}
};
/// Describes an integer number of half cycles single clock signal transitions.
/// Describes an integer number of half cycles: single clock signal transitions.
class HalfCycles: public WrappedInt<HalfCycles> {
public:
inline HalfCycles(int l) : WrappedInt<HalfCycles>(l) {}
inline HalfCycles() : WrappedInt<HalfCycles>() {}
constexpr HalfCycles(int l) : WrappedInt<HalfCycles>(l) {}
constexpr HalfCycles() : WrappedInt<HalfCycles>() {}
inline HalfCycles(const Cycles cycles) : WrappedInt<HalfCycles>(cycles.as_int() * 2) {}
inline HalfCycles(const HalfCycles &half_cycles) : WrappedInt<HalfCycles>(half_cycles.length_) {}
constexpr HalfCycles(const Cycles cycles) : WrappedInt<HalfCycles>(cycles.as_int() * 2) {}
constexpr HalfCycles(const HalfCycles &half_cycles) : WrappedInt<HalfCycles>(half_cycles.length_) {}
/// @returns The number of whole cycles completely covered by this span of half cycles.
inline Cycles cycles() {
constexpr Cycles cycles() {
return Cycles(length_ >> 1);
}
/// Flushes the whole cycles in @c this, subtracting that many from the total stored here.
inline Cycles flush_cycles() {
Cycles flush_cycles() {
Cycles result(length_ >> 1);
length_ &= 1;
return result;
}
/// Flushes the half cycles in @c this, returning the number stored and setting this total to zero.
inline HalfCycles flush() {
HalfCycles flush() {
HalfCycles result(length_);
length_ = 0;
return result;
}
/*!
Severs from @c this the effect of dividing by @c divisor @c this will end up with
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
*/
inline Cycles divide_cycles(const Cycles &divisor) {
Cycles divide_cycles(const Cycles &divisor) {
HalfCycles half_divisor = HalfCycles(divisor);
Cycles result(length_ / half_divisor.length_);
length_ %= half_divisor.length_;
@@ -203,7 +203,6 @@ template <class T> class HalfClockReceiver: public T {
public:
using T::T;
using T::run_for;
inline void run_for(const HalfCycles half_cycles) {
half_cycles_ += half_cycles;
T::run_for(half_cycles_.flush_cycles());

View File

@@ -0,0 +1,88 @@
//
// ClockingHintSource.h
// Clock Signal
//
// Created by Thomas Harte on 20/08/2017.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef ClockingHintSource_hpp
#define ClockingHintSource_hpp
namespace ClockingHint {
enum class Preference {
/// The component doesn't currently require a clock signal.
None,
/// The component can be clocked only immediate prior to (explicit) accesses.
JustInTime,
/// The component require real-time clocking.
RealTime
};
class Source;
struct Observer {
/// Called to inform an observer that the component @c component has changed its clocking requirements.
virtual void set_component_prefers_clocking(Source *component, Preference clocking) = 0;
};
/*!
An clocking hint source is any component that can provide hints as to the type of
clocking required for accurate emulation. A disk controller is an archetypal example.
Types of clocking are:
- none:
a component that acts and reacts to direct contact but does not have a state that autonomously evolves.
E.g. a ROM, RAM, or some kinds of disk controller when not in the process of performing a command.
- just-in-time:
a component that has an evolving state but can receive clock updates only immediately before a
direct contact. This is possibly the most common kind of component.
- real-time:
a component that needs to be clocked in 'real time' (i.e. in terms of the emulated machine). For example
so that it can announce an interrupt at the proper moment, because it is monitoring some aspect of
the machine rather than waiting to be called upon, or because there's some other non-obvious relationship
at play.
A clocking hint source can signal changes in preferred clocking to an observer.
This is intended to allow for performance improvements to machines with components that can be messaged selectively.
The observer callout is virtual so the intended use case is that a machine holds a component that might go through
periods of different clocking requirements.
Transitions should be sufficiently infrequent that a virtual call to announce them costs little enough that
the saved or deferred ::run_fors add up to a substantial amount.
The hint provided is just that: a hint. Owners may perform ::run_for at a greater frequency.
*/
class Source {
public:
/// Registers @c observer as the new clocking observer.
void set_clocking_hint_observer(Observer *observer) {
observer_ = observer;
update_clocking_observer();
}
/// @returns the current preferred clocking strategy.
virtual Preference preferred_clocking() = 0;
private:
Observer *observer_ = nullptr;
protected:
/*!
Provided for subclasses; call this whenever the clocking preference might have changed.
This will notify the observer if there is one.
*/
void update_clocking_observer() {
if(!observer_) return;
observer_->set_component_prefers_clocking(this, preferred_clocking());
}
};
}
#endif /* ClockingHintSource_h */

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 01/08/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef ForceInline_hpp

View File

@@ -1,60 +0,0 @@
//
// Sleeper.h
// Clock Signal
//
// Created by Thomas Harte on 20/08/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef Sleeper_hpp
#define Sleeper_hpp
/*!
A sleeper is any component that sometimes requires a clock but at other times is 'asleep' — i.e. is not doing
any clock-derived work, so needn't receive a clock. A disk controller is an archetypal example.
A sleeper will signal sleeps and wakes to an observer.
This is intended to allow for performance improvements to machines with components that can sleep. The observer
callout is virtual so the intended use case is that a machine holds a component that might sleep. Its transitions
into and out of sleep are sufficiently infrequent that a virtual call to announce them costs sufficiently little that
the saved ::run_fors add up to a substantial amount.
By convention, sleeper components must be willing to accept ::run_for even after announcing sleep. It's a hint,
not a command.
*/
class Sleeper {
public:
Sleeper() : sleep_observer_(nullptr) {}
class SleepObserver {
public:
/// Called to inform an observer that the component @c component has either gone to sleep or become awake.
virtual void set_component_is_sleeping(void *component, bool is_sleeping) = 0;
};
/// Registers @c observer as the new sleep observer;
void set_sleep_observer(SleepObserver *observer) {
sleep_observer_ = observer;
}
/// @returns @c true if the component is currently sleeping; @c false otherwise.
virtual bool is_sleeping() = 0;
protected:
/// Provided for subclasses; send sleep announcements to the sleep_observer_.
SleepObserver *sleep_observer_;
/*!
Provided for subclasses; call this whenever is_sleeping might have changed, and the observer will be notified,
if one exists.
@c is_sleeping will be called only if there is an observer.
*/
void update_sleep_observer() {
if(!sleep_observer_) return;
sleep_observer_->set_component_is_sleeping(this, is_sleeping());
}
};
#endif /* Sleeper_h */

View File

@@ -0,0 +1,18 @@
//
// TimeTypes.hpp
// Clock Signal
//
// Created by Thomas Harte on 21/03/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#ifndef TimeTypes_h
#define TimeTypes_h
namespace Time {
typedef double Seconds;
}
#endif /* TimeTypes_h */

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 17/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "1770.hpp"
@@ -282,7 +282,7 @@ void WD1770::posit_event(int new_event_type) {
track_ = 0;
goto verify;
}
get_drive().step(step_direction_ ? 1 : -1);
get_drive().step(Storage::Disk::HeadPosition(step_direction_ ? 1 : -1));
unsigned int time_to_wait;
switch(command_ & 3) {
default:
@@ -522,7 +522,7 @@ void WD1770::posit_event(int new_event_type) {
type2_write_loop:
/*
This deviates from the data sheet slightly since that would prima facie request one more byte
of data than is actually written the last time around the loop it has transferred from the
of data than is actually written; the last time around the loop it has transferred from the
data register to the data shift register, set data request, written the byte, checked that data
request has been satified, then finally considers whether all bytes are done. Based on both
natural expectations and the way that emulated machines responded, I believe that to be a

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 17/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef _770_hpp
@@ -43,7 +43,6 @@ class WD1770: public Storage::Disk::MFMController {
/// Runs the controller for @c number_of_cycles cycles.
void run_for(const Cycles cycles);
using Storage::Disk::Controller::run_for;
enum Flag: uint8_t {
NotReady = 0x80,

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 06/06/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef _522_hpp
@@ -113,6 +113,9 @@ template <class T> class MOS6522: public MOS6522Base {
/*! Gets a register value. */
uint8_t get_register(int address);
/*! @returns the bus handler. */
T &bus_handler();
private:
T &bus_handler_;

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 04/09/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#include "../6522.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 04/09/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
template <typename T> void MOS6522<T>::set_register(int address, uint8_t value) {
@@ -145,6 +145,10 @@ template <typename T> uint8_t MOS6522<T>::get_port_input(Port port, uint8_t outp
return (input & ~output_mask) | (output & output_mask);
}
template <typename T> T &MOS6522<T>::bus_handler() {
return bus_handler_;
}
// Delegate and communications
template <typename T> void MOS6522<T>::reevaluate_interrupts() {
bool new_interrupt_status = get_interrupt_line();

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 04/09/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef _522Storage_hpp

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 04/09/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#include "../6522.hpp"

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 19/06/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef _532_hpp

View File

@@ -3,14 +3,14 @@
// Clock Signal
//
// Created by Thomas Harte on 05/06/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "6560.hpp"
#include <cstring>
using namespace MOS;
using namespace MOS::MOS6560;
AudioGenerator::AudioGenerator(Concurrency::DeferringAsyncTaskQueue &audio_queue) :
audio_queue_(audio_queue) {}
@@ -18,7 +18,7 @@ AudioGenerator::AudioGenerator(Concurrency::DeferringAsyncTaskQueue &audio_queue
void AudioGenerator::set_volume(uint8_t volume) {
audio_queue_.defer([=]() {
volume_ = volume;
volume_ = static_cast<int16_t>(volume) * range_multiplier_;
});
}
@@ -106,7 +106,7 @@ static uint8_t noise_pattern[] = {
// means every second cycle, etc.
void AudioGenerator::get_samples(std::size_t number_of_samples, int16_t *target) {
for(unsigned int c = 0; c < number_of_samples; c++) {
for(unsigned int c = 0; c < number_of_samples; ++c) {
update(0, 2, shift);
update(1, 1, shift);
update(2, 0, shift);
@@ -114,17 +114,17 @@ void AudioGenerator::get_samples(std::size_t number_of_samples, int16_t *target)
// this sums the output of all three sounds channels plus a DC offset for volume;
// TODO: what's the real ratio of this stuff?
target[c] = (
target[c] = static_cast<int16_t>(
(shift_registers_[0]&1) +
(shift_registers_[1]&1) +
(shift_registers_[2]&1) +
((noise_pattern[shift_registers_[3] >> 3] >> (shift_registers_[3]&7))&(control_registers_[3] >> 7)&1)
) * volume_ * 700 + volume_ * 44;
) * volume_ + (volume_ >> 4);
}
}
void AudioGenerator::skip_samples(std::size_t number_of_samples) {
for(unsigned int c = 0; c < number_of_samples; c++) {
for(unsigned int c = 0; c < number_of_samples; ++c) {
update(0, 2, shift);
update(1, 1, shift);
update(2, 0, shift);
@@ -132,6 +132,10 @@ void AudioGenerator::skip_samples(std::size_t number_of_samples) {
}
}
void AudioGenerator::set_sample_volume_range(std::int16_t range) {
range_multiplier_ = static_cast<int16_t>(range / 64);
}
#undef shift
#undef increment
#undef update

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 05/06/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef _560_hpp
@@ -16,6 +16,7 @@
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
namespace MOS {
namespace MOS6560 {
// audio state
class AudioGenerator: public ::Outputs::Speaker::SampleSource {
@@ -25,8 +26,10 @@ class AudioGenerator: public ::Outputs::Speaker::SampleSource {
void set_volume(uint8_t volume);
void set_control(int channel, uint8_t value);
// For ::SampleSource.
void get_samples(std::size_t number_of_samples, int16_t *target);
void skip_samples(std::size_t number_of_samples);
void set_sample_volume_range(std::int16_t range);
private:
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
@@ -34,7 +37,19 @@ class AudioGenerator: public ::Outputs::Speaker::SampleSource {
unsigned int counters_[4] = {2, 1, 0, 0}; // create a slight phase offset for the three channels
unsigned int shift_registers_[4] = {0, 0, 0, 0};
uint8_t control_registers_[4] = {0, 0, 0, 0};
uint8_t volume_ = 0;
int16_t volume_ = 0;
int16_t range_multiplier_ = 1;
};
struct BusHandler {
void perform_read(uint16_t address, uint8_t *pixel_data, uint8_t *colour_data) {
*pixel_data = 0xff;
*colour_data = 0xff;
}
};
enum class OutputMode {
PAL, NTSC
};
/*!
@@ -45,27 +60,36 @@ class AudioGenerator: public ::Outputs::Speaker::SampleSource {
@c set_register and @c get_register provide register access.
*/
template <class T> class MOS6560 {
template <class BusHandler> class MOS6560 {
public:
MOS6560() :
MOS6560(BusHandler &bus_handler) :
bus_handler_(bus_handler),
crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::CRT::DisplayType::NTSC60, 2)),
audio_generator_(audio_queue_),
speaker_(audio_generator_)
{
crt_->set_composite_sampling_function(
"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
crt_->set_svideo_sampling_function(
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
"{"
"vec2 yc = texture(texID, coordinate).rg / vec2(255.0);"
"float phaseOffset = 6.283185308 * 2.0 * yc.y;"
"float chroma = cos(phase + phaseOffset);"
"return mix(yc.x, step(yc.y, 0.75) * chroma, amplitude);"
"float phaseOffset = 6.283185308 * 2.0 * yc.y;"
"float chroma = step(yc.y, 0.75) * cos(phase + phaseOffset);"
"return vec2(yc.x, chroma);"
"}");
// default to s-video output
crt_->set_video_signal(Outputs::CRT::VideoSignal::SVideo);
// default to NTSC
set_output_mode(OutputMode::NTSC);
}
~MOS6560() {
audio_queue_.flush();
}
void set_clock_rate(double clock_rate) {
speaker_.set_input_rate(static_cast<float>(clock_rate / 4.0));
}
@@ -77,38 +101,34 @@ template <class T> class MOS6560 {
speaker_.set_high_frequency_cutoff(cutoff);
}
enum OutputMode {
PAL, NTSC
};
/*!
Sets the output mode to either PAL or NTSC.
*/
void set_output_mode(OutputMode output_mode) {
output_mode_ = output_mode;
// Lumunances are encoded trivially: on a 0255 scale.
// Luminances are encoded trivially: on a 0-255 scale.
const uint8_t luminances[16] = {
0, 255, 109, 189,
199, 144, 159, 161,
126, 227, 227, 207,
235, 173, 188, 196
0, 255, 64, 192,
128, 128, 64, 192,
128, 192, 128, 255,
192, 192, 128, 255
};
// Chrominances are encoded such that 0128 is a complete revolution of phase;
// Chrominances are encoded such that 0-128 is a complete revolution of phase;
// anything above 191 disables the colour subcarrier. Phase is relative to the
// colour burst, so 0 is green.
const uint8_t pal_chrominances[16] = {
255, 255, 40, 112,
8, 88, 120, 56,
40, 48, 40, 112,
8, 88, 120, 56,
255, 255, 37, 101,
19, 86, 123, 59,
46, 53, 37, 101,
19, 86, 123, 59,
};
const uint8_t ntsc_chrominances[16] = {
255, 255, 8, 72,
32, 88, 48, 112,
0, 0, 8, 72,
32, 88, 48, 112,
255, 255, 121, 57,
103, 42, 80, 16,
0, 9, 121, 57,
103, 42, 80, 16,
};
const uint8_t *chrominances;
Outputs::CRT::DisplayType display_type;
@@ -118,7 +138,8 @@ template <class T> class MOS6560 {
chrominances = pal_chrominances;
display_type = Outputs::CRT::DisplayType::PAL50;
timing_.cycles_per_line = 71;
timing_.line_counter_increment_offset = 0;
timing_.line_counter_increment_offset = 4;
timing_.final_line_increment_position = timing_.cycles_per_line - timing_.line_counter_increment_offset;
timing_.lines_per_progressive_field = 312;
timing_.supports_interlacing = false;
break;
@@ -127,23 +148,23 @@ template <class T> class MOS6560 {
chrominances = ntsc_chrominances;
display_type = Outputs::CRT::DisplayType::NTSC60;
timing_.cycles_per_line = 65;
timing_.line_counter_increment_offset = 65 - 33; // TODO: this is a bit of a hack; separate vertical and horizontal counting
timing_.line_counter_increment_offset = 40;
timing_.final_line_increment_position = 58;
timing_.lines_per_progressive_field = 261;
timing_.supports_interlacing = true;
break;
}
crt_->set_new_display_type(static_cast<unsigned int>(timing_.cycles_per_line*4), display_type);
crt_->set_visible_area(Outputs::CRT::Rect(0.05f, 0.05f, 0.9f, 0.9f));
// switch(output_mode) {
// case OutputMode::PAL:
// crt_->set_visible_area(crt_->get_rect_for_area(16, 237, 15*4, 55*4, 4.0f / 3.0f));
// break;
// case OutputMode::NTSC:
// crt_->set_visible_area(crt_->get_rect_for_area(16, 237, 11*4, 55*4, 4.0f / 3.0f));
// break;
// }
switch(output_mode) {
case OutputMode::PAL:
crt_->set_visible_area(Outputs::CRT::Rect(0.1f, 0.07f, 0.9f, 0.9f));
break;
case OutputMode::NTSC:
crt_->set_visible_area(Outputs::CRT::Rect(0.05f, 0.05f, 0.9f, 0.9f));
break;
}
for(int c = 0; c < 16; c++) {
uint8_t *colour = reinterpret_cast<uint8_t *>(&colours_[c]);
@@ -166,7 +187,6 @@ template <class T> class MOS6560 {
// keep track of internal time relative to this scanline
horizontal_counter_++;
full_frame_counter_++;
if(horizontal_counter_ == timing_.cycles_per_line) {
if(horizontal_drawing_latch_) {
current_character_row_++;
@@ -188,9 +208,8 @@ template <class T> class MOS6560 {
horizontal_drawing_latch_ = false;
vertical_counter_ ++;
if(vertical_counter_ == (registers_.interlaced ? (is_odd_frame_ ? 262 : 263) : timing_.lines_per_progressive_field)) {
if(vertical_counter_ == lines_this_field()) {
vertical_counter_ = 0;
full_frame_counter_ = 0;
if(output_mode_ == OutputMode::NTSC) is_odd_frame_ ^= true;
current_row_ = 0;
@@ -238,7 +257,7 @@ template <class T> class MOS6560 {
uint8_t pixel_data;
uint8_t colour_data;
static_cast<T *>(this)->perform_read(fetch_address, &pixel_data, &colour_data);
bus_handler_.perform_read(fetch_address, &pixel_data, &colour_data);
// TODO: there should be a further two-cycle delay on pixels being output; the reverse bit should
// divide the byte it is set for 3:1 and then continue as usual.
@@ -252,7 +271,7 @@ template <class T> class MOS6560 {
// apply vertical sync
if(
(vertical_counter_ < 3 && (is_odd_frame_ || !registers_.interlaced)) ||
(vertical_counter_ < 3 && is_odd_frame()) ||
(registers_.interlaced &&
(
(vertical_counter_ == 0 && horizontal_counter_ > 32) ||
@@ -268,7 +287,7 @@ template <class T> class MOS6560 {
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::Border: output_border(cycles_in_state_ * 4); break;
case State::Pixels: crt_->output_data(cycles_in_state_ * 4, 1); break;
case State::Pixels: crt_->output_data(cycles_in_state_ * 4); break;
}
output_state_ = this_state_;
cycles_in_state_ = 0;
@@ -281,6 +300,9 @@ template <class T> class MOS6560 {
cycles_in_state_++;
if(this_state_ == State::Pixels) {
// TODO: palette changes can happen within half-characters; the below needs to be divided.
// Also: a perfect opportunity to rearrange this inner loop for no longer needing to be
// two parts with a cooperative owner?
if(column_counter_&1) {
character_value_ = pixel_data;
@@ -321,7 +343,10 @@ template <class T> class MOS6560 {
character_code_ = pixel_data;
character_colour_ = colour_data;
}
}
// Keep counting columns even if sync or the colour burst have interceded.
if(column_counter_ >= 0 && column_counter_ < columns_this_line_*2) {
column_counter_++;
}
}
@@ -404,15 +429,15 @@ template <class T> class MOS6560 {
*/
uint8_t get_register(int address) {
address &= 0xf;
int current_line = (full_frame_counter_ + timing_.line_counter_increment_offset) / timing_.cycles_per_line;
switch(address) {
default: return registers_.direct_values[address];
case 0x03: return static_cast<uint8_t>(current_line << 7) | (registers_.direct_values[3] & 0x7f);
case 0x04: return (current_line >> 1) & 0xff;
case 0x03: return static_cast<uint8_t>(raster_value() << 7) | (registers_.direct_values[3] & 0x7f);
case 0x04: return (raster_value() >> 1) & 0xff;
}
}
private:
BusHandler &bus_handler_;
std::unique_ptr<Outputs::CRT::CRT> crt_;
Concurrency::DeferringAsyncTaskQueue audio_queue_;
@@ -443,7 +468,29 @@ template <class T> class MOS6560 {
unsigned int cycles_in_state_;
// counters that cover an entire field
int horizontal_counter_ = 0, vertical_counter_ = 0, full_frame_counter_;
int horizontal_counter_ = 0, vertical_counter_ = 0;
const int lines_this_field() {
// Necessary knowledge here: only the NTSC 6560 supports interlaced video.
return registers_.interlaced ? (is_odd_frame_ ? 262 : 263) : timing_.lines_per_progressive_field;
}
const int raster_value() {
const int bonus_line = (horizontal_counter_ + timing_.line_counter_increment_offset) / timing_.cycles_per_line;
const int line = vertical_counter_ + bonus_line;
const int final_line = lines_this_field();
if(line < final_line)
return line;
if(is_odd_frame()) {
return (horizontal_counter_ >= timing_.final_line_increment_position) ? 0 : final_line - 1;
} else {
return line % final_line;
}
// Cf. http://www.sleepingelephant.com/ipw-web/bulletin/bb/viewtopic.php?f=14&t=7237&start=15#p80737
}
bool is_odd_frame() {
return is_odd_frame_ || !registers_.interlaced;
}
// latches dictating start and length of drawing
bool vertical_drawing_latch_, horizontal_drawing_latch_;
@@ -473,12 +520,14 @@ template <class T> class MOS6560 {
struct {
int cycles_per_line;
int line_counter_increment_offset;
int final_line_increment_position;
int lines_per_progressive_field;
bool supports_interlacing;
} timing_;
OutputMode output_mode_;
};
}
}
#endif /* _560_hpp */

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 31/07/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef CRTC6845_hpp
@@ -35,7 +35,7 @@ class BusHandler {
void perform_bus_cycle_phase1(const BusState &) {}
/*!
Performs the second phase of a 6845 bus cycle. Some bus state including sync is updated
Performs the second phase of a 6845 bus cycle. Some bus state, including sync, is updated
directly after phase 1 and hence is visible to an observer during phase 2. Handlers may therefore
implement @c perform_bus_cycle_phase2 to be notified of the availability of that state without
having to wait until the next cycle has begun.

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 01/08/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef i8255_hpp

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 05/08/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#include "i8272.hpp"
@@ -83,8 +83,10 @@ i8272::i8272(BusHandler &bus_handler, Cycles clock_rate) :
posit_event(static_cast<int>(Event8272::CommandByte));
}
bool i8272::is_sleeping() {
return is_sleeping_ && Storage::Disk::MFMController::is_sleeping();
ClockingHint::Preference i8272::preferred_clocking() {
const auto mfm_controller_preferred_clocking = Storage::Disk::MFMController::preferred_clocking();
if(mfm_controller_preferred_clocking != ClockingHint::Preference::None) return mfm_controller_preferred_clocking;
return is_sleeping_ ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime;
}
void i8272::run_for(Cycles cycles) {
@@ -115,7 +117,7 @@ void i8272::run_for(Cycles cycles) {
int direction = (drives_[c].target_head_position < drives_[c].head_position) ? -1 : 1;
printf("Target %d versus believed %d\n", drives_[c].target_head_position, drives_[c].head_position);
select_drive(c);
get_drive().step(direction);
get_drive().step(Storage::Disk::HeadPosition(direction));
if(drives_[c].target_head_position >= 0) drives_[c].head_position += direction;
// Check for completion.
@@ -159,7 +161,7 @@ void i8272::run_for(Cycles cycles) {
}
is_sleeping_ = !delay_time_ && !drives_seeking_ && !head_timers_running_;
if(is_sleeping_) update_sleep_observer();
if(is_sleeping_) update_clocking_observer();
}
void i8272::set_register(int address, uint8_t value) {
@@ -198,7 +200,7 @@ uint8_t i8272::get_register(int address) {
#define MS_TO_CYCLES(x) x * 8000
#define WAIT_FOR_EVENT(mask) resume_point_ = __LINE__; interesting_event_mask_ = static_cast<int>(mask); return; case __LINE__:
#define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; interesting_event_mask_ = static_cast<int>(Event8272::Timer); delay_time_ = MS_TO_CYCLES(ms); is_sleeping_ = false; update_sleep_observer(); case __LINE__: if(delay_time_) return;
#define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; interesting_event_mask_ = static_cast<int>(Event8272::Timer); delay_time_ = MS_TO_CYCLES(ms); is_sleeping_ = false; update_clocking_observer(); case __LINE__: if(delay_time_) return;
#define PASTE(x, y) x##y
#define CONCAT(x, y) PASTE(x, y)
@@ -257,7 +259,7 @@ uint8_t i8272::get_register(int address) {
if(drives_[active_drive_].head_unload_delay[active_head_] == 0) { \
head_timers_running_++; \
is_sleeping_ = false; \
update_sleep_observer(); \
update_clocking_observer(); \
} \
drives_[active_drive_].head_unload_delay[active_head_] = MS_TO_CYCLES(head_unload_time_);\
}
@@ -434,7 +436,7 @@ void i8272::posit_event(int event_type) {
ClearControlMark();
if(event_type == static_cast<int>(Event::Token)) {
if(get_latest_token().type != Token::Data && get_latest_token().type != Token::DeletedData) {
// Something other than a data mark came next impliedly an ID or index mark.
// Something other than a data mark came next, impliedly an ID or index mark.
SetMissingAddressMark();
SetMissingDataAddressMark();
goto abort; // TODO: or read_next_data?
@@ -720,7 +722,7 @@ void i8272::posit_event(int event_type) {
if(drives_[drive].phase != Drive::Seeking) {
drives_seeking_++;
is_sleeping_ = false;
update_sleep_observer();
update_clocking_observer();
}
// Set currently seeking, with a step to occur right now (yes, it sounds like jamming these
@@ -828,7 +830,7 @@ void i8272::posit_event(int event_type) {
goto post_result;
// Posts whatever is in result_stack_ as a result phase. Be aware that it is a stack the
// Posts whatever is in result_stack_ as a result phase. Be aware that it is a stack, so the
// last thing in it will be returned first.
post_result:
printf("Result to %02x, main %02x: ", command_[0] & 0x1f, main_status_);

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 05/08/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef i8272_hpp
@@ -39,7 +39,7 @@ class i8272: public Storage::Disk::MFMController {
void set_dma_acknowledge(bool dack);
void set_terminal_count(bool tc);
bool is_sleeping();
ClockingHint::Preference preferred_clocking() override;
protected:
virtual void select_drive(int number) = 0;
@@ -67,7 +67,7 @@ class i8272: public Storage::Disk::MFMController {
ResultEmpty = (1 << 5),
NoLongerReady = (1 << 6)
};
void posit_event(int type);
void posit_event(int type) override;
int interesting_event_mask_ = static_cast<int>(Event8272::CommandByte);
int resume_point_ = 0;
bool is_access_command_ = false;

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 25/11/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#include "9918.hpp"
@@ -96,7 +96,7 @@ TMS9918::TMS9918(Personality p) {
"{"
"return texture(sampler, coordinate).rgb / vec3(255.0);"
"}");
crt_->set_output_device(Outputs::CRT::OutputDevice::Monitor);
crt_->set_video_signal(Outputs::CRT::VideoSignal::RGB);
crt_->set_visible_area(Outputs::CRT::Rect(0.055f, 0.025f, 0.9f, 0.9f));
crt_->set_input_gamma(2.8f);
@@ -536,7 +536,8 @@ void TMS9918::run_for(const HalfCycles cycles) {
}
if(output_column_ == first_right_border_column_) {
crt_->output_data(static_cast<unsigned int>(first_right_border_column_ - first_pixel_column_) * 4, 4);
const unsigned int data_length = static_cast<unsigned int>(first_right_border_column_ - first_pixel_column_);
crt_->output_data(data_length * 4, data_length);
pixel_target_ = nullptr;
}
}

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 25/11/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef TMS9918_hpp
@@ -56,7 +56,7 @@ class TMS9918: public TMS9918Base {
/*!
Runs the VCP for the number of cycles indicate; it is an implicit assumption of the code
that the input clock rate is 3579545 Hz the NTSC colour clock rate.
that the input clock rate is 3579545 Hz, the NTSC colour clock rate.
*/
void run_for(const HalfCycles cycles);

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 14/12/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#ifndef TMS9918Base_hpp

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 14/10/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#include "AY38910.hpp"
@@ -56,13 +56,18 @@ AY38910::AY38910(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_
}
}
set_sample_volume_range(0);
}
void AY38910::set_sample_volume_range(std::int16_t range) {
// set up volume lookup table
float max_volume = 8192;
float root_two = sqrtf(2.0f);
const float max_volume = static_cast<float>(range) / 3.0f; // As there are three channels.
const float root_two = sqrtf(2.0f);
for(int v = 0; v < 16; v++) {
volumes_[v] = static_cast<int>(max_volume / powf(root_two, static_cast<float>(v ^ 0xf)));
}
volumes_[0] = 0;
evaluate_output_volume();
}
void AY38910::get_samples(std::size_t number_of_samples, int16_t *target) {
@@ -98,7 +103,7 @@ void AY38910::get_samples(std::size_t number_of_samples, int16_t *target) {
noise_shift_register_ >>= 1;
}
// ... and the envelope generator. Table based for pattern lookup, with a 'refill' step a way of
// ... and the envelope generator. Table based for pattern lookup, with a 'refill' step: a way of
// implementing non-repeating patterns by locking them to table position 0x1f.
if(envelope_divider_) envelope_divider_--;
else {
@@ -125,7 +130,7 @@ void AY38910::evaluate_output_volume() {
// The output level for a channel is:
// 1 if neither tone nor noise is enabled;
// 0 if either tone or noise is enabled and its value is low.
// The tone/noise enable bits use inverse logic 0 = on, 1 = off permitting the OR logic below.
// The tone/noise enable bits use inverse logic; 0 = on, 1 = off; permitting the OR logic below.
#define tone_level(c, tone_bit) (tone_outputs_[c] | (output_registers_[7] >> tone_bit))
#define noise_level(c, noise_bit) (noise_output_ | (output_registers_[7] >> noise_bit))

View File

@@ -3,7 +3,7 @@
// Clock Signal
//
// Created by Thomas Harte on 14/10/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#ifndef AY_3_8910_hpp
@@ -86,12 +86,13 @@ class AY38910: public ::Outputs::Speaker::SampleSource {
// to satisfy ::Outputs::Speaker (included via ::Outputs::Filter; not for public consumption
void get_samples(std::size_t number_of_samples, int16_t *target);
bool is_zero_level();
void set_sample_volume_range(std::int16_t range);
private:
Concurrency::DeferringAsyncTaskQueue &task_queue_;
int selected_register_ = 0;
uint8_t registers_[16];
uint8_t registers_[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
uint8_t output_registers_[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
uint8_t port_inputs_[2];

Some files were not shown because too many files have changed in this diff Show More