1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-09-12 17:24:42 +00:00

Compare commits

..

769 Commits

Author SHA1 Message Date
Thomas Harte
14ac4da813 Accept version number. 2023-05-15 10:01:38 -04:00
Thomas Harte
b0e3bd85d6 Merge pull request #1138 from TomHarte/QtMSXUI
Introduce Qt options for MSX model, MSX-MUSIC.
2023-05-15 09:54:25 -04:00
Thomas Harte
9b6be2571a Introduce Qt options for MSX model, MSX-MUSIC. 2023-05-15 09:50:22 -04:00
Thomas Harte
4ede538d36 Merge pull request #1137 from TomHarte/MX2
Add .MX2 as an MSX synonym of .ROM
2023-05-14 23:46:37 -04:00
Thomas Harte
8bf3d85e36 Add .MX2 as an MSX synonym of .ROM 2023-05-14 23:42:08 -04:00
Thomas Harte
ec9abbe6a7 Merge pull request #1136 from TomHarte/MSX-MUSIC
Add MSX-MUSIC (/FM-PAC) emulation.
2023-05-13 22:34:31 -04:00
Thomas Harte
22ac13d3f2 Set proper number of volumes. 2023-05-13 22:29:09 -04:00
Thomas Harte
413ab42b16 Add MSX-MUSIC option for macOS. 2023-05-13 22:25:50 -04:00
Thomas Harte
876fc6d1e0 Eliminate redundant line break. 2023-05-13 22:18:40 -04:00
Thomas Harte
b768e438b2 Enable MSX-MUSIC by default. 2023-05-13 22:17:09 -04:00
Thomas Harte
e1d671daf7 Avoid paying for an OPLL if not connected. 2023-05-13 22:16:42 -04:00
Thomas Harte
4989701de9 Install MSX-MUSIC ROM. 2023-05-12 23:50:43 -04:00
Thomas Harte
fed97b8d26 Add MSX-MUSIC entry. 2023-05-12 23:33:28 -04:00
Thomas Harte
e7888497b7 Add an OPLL. 2023-05-12 23:30:03 -04:00
Thomas Harte
0b53c73da8 Add additional consts. 2023-05-12 22:13:55 -04:00
Thomas Harte
a6ebfe2ce2 Add has_msx_music flag. 2023-05-12 22:09:15 -04:00
Thomas Harte
b89076cb72 Merge pull request #1135 from TomHarte/9918Cleanup
Adds yet more clenliness
2023-05-12 21:32:33 -04:00
Thomas Harte
50343dec43 Eliminate all whitespace-only lines. 2023-05-12 14:16:39 -04:00
Thomas Harte
28c79b2885 Eliminate redundant [space][tab] pairs. 2023-05-12 14:14:45 -04:00
Thomas Harte
60bec3d4c0 Eliminate trailing whitespace, fix tabs. 2023-05-12 14:03:38 -04:00
Thomas Harte
56de9c418f Improve comments. 2023-05-12 13:59:52 -04:00
Thomas Harte
5bcb5fb832 Also sever command-engine state. 2023-05-12 13:57:50 -04:00
Thomas Harte
abeb361441 Gift all generators to YamahaFetcher. 2023-05-12 13:54:07 -04:00
Thomas Harte
f9cc2013a8 Start to cleave off Yamaha fetch tables. 2023-05-12 13:49:53 -04:00
Thomas Harte
e7c40eead9 Have Fetch and Draw be overt about namespaces. 2023-05-12 13:46:35 -04:00
Thomas Harte
c29d80006e Start to organise. 2023-05-12 13:33:02 -04:00
Thomas Harte
596661bfbe Remove errant newline. 2023-05-12 13:25:11 -04:00
Thomas Harte
7e319374b6 Consolidate StandardTiming into LineLayout. 2023-05-11 23:49:12 -04:00
Thomas Harte
7f5d129b13 Merge pull request #1134 from TomHarte/WorkingTests
Clean up various long-dangling issues.
2023-05-11 08:21:53 -05:00
Thomas Harte
f6acee18cc Eliminate type-in-function-name from 6502-world. 2023-05-10 18:53:38 -05:00
Thomas Harte
3af30b1fec Update documentation. 2023-05-10 18:46:46 -05:00
Thomas Harte
a8cc74f9fe Further eliminate naming. 2023-05-10 18:46:21 -05:00
Thomas Harte
10cd2a36cf Avoid type-in-function-name, Z80 edition. 2023-05-10 18:42:19 -05:00
Thomas Harte
ea50d5bda7 Eliminate dead bit reverser. 2023-05-10 17:14:39 -05:00
Thomas Harte
809cd7bca9 Remove the 68000's Mk2 suffix. 2023-05-10 17:13:01 -05:00
Thomas Harte
e56db3c4e5 Eliminate the old 68000 implementation. 2023-05-10 17:06:27 -05:00
Thomas Harte
2b56b7be0d Simplify namespace syntax. 2023-05-10 16:02:18 -05:00
Thomas Harte
25a245e35c Flailingly switch things until tests run. 2023-05-10 15:17:00 -05:00
Thomas Harte
882384b1f3 Disambiguate Storage.hpp. 2023-05-10 15:06:39 -05:00
Thomas Harte
5cc19f436f Permit command-line parallel builds. 2023-05-10 15:02:07 -05:00
Thomas Harte
fd0eaa5788 Decline signing of all but release builds. 2023-05-10 15:01:02 -05:00
Thomas Harte
992a47c196 Add fallthrough annotations to Duff-esque loops. 2023-05-10 09:38:42 -05:00
Thomas Harte
8540e7a953 Add missing #include. 2023-05-10 09:37:21 -05:00
Thomas Harte
6b19bfeab2 No .cpp files remain in Components/9918 2023-05-09 17:40:14 -05:00
Thomas Harte
066e42145e Merge branch 'master' of github.com:TomHarte/CLK 2023-05-08 21:14:14 -04:00
Thomas Harte
efe1425e8e Accept new version number. 2023-05-08 21:14:03 -04:00
Thomas Harte
55e0742232 Merge pull request #1132 from TomHarte/MSX2Screenshot
Mention MSX 2, include screenshot.
2023-05-08 10:14:53 -04:00
Thomas Harte
abf92cd09e Platform name is conventionally included. 2023-05-08 10:14:40 -04:00
Thomas Harte
73a214913a Withdraw failed div experiment. 2023-05-08 10:13:47 -04:00
Thomas Harte
5453840d8c Attempt to equalise column widths. 2023-05-08 10:12:35 -04:00
Thomas Harte
77078e2b7a Fix Chromatrons reference. 2023-05-08 10:10:22 -04:00
Thomas Harte
3f1ae986cb Cut down on borders. 2023-05-08 10:08:33 -04:00
Thomas Harte
d39d2a88f8 Futher resort. 2023-05-08 10:02:29 -04:00
Thomas Harte
6556865615 Shuffle screenshot order. 2023-05-08 10:00:40 -04:00
Thomas Harte
44779e68ee Mention MSX 2, include screenshot. 2023-05-08 09:58:35 -04:00
Thomas Harte
54f5bae59e Merge pull request #1131 from TomHarte/MSXLineInterrupts
Restrict disabled MSX line interrupt signalling.
2023-05-08 09:55:52 -04:00
Thomas Harte
e94b9f695a Move interrupt away from buggy position.
Since I don't know where it's supposed to go anyway.
2023-05-08 09:50:05 -04:00
Thomas Harte
3797968870 Expose line interrupt status only when enabled. 2023-05-08 09:45:54 -04:00
Thomas Harte
9c079a6c50 Merge pull request #1130 from TomHarte/ImplicitPosition
Unify numbered and named slots.
2023-04-30 17:30:59 -04:00
Thomas Harte
561e2b774e Unify numbered and named slots. 2023-04-30 17:24:14 -04:00
Thomas Harte
0cb4fec504 Merge pull request #1129 from TomHarte/FarewellCodecvt
Eliminate use of deprecated codecvt.
2023-04-30 17:21:43 -04:00
Thomas Harte
ec81cdd388 Eliminate codecvt. 2023-04-30 17:17:40 -04:00
Thomas Harte
8f0dc9d9a2 Eliminate use of deprecated codecvt. 2023-04-30 16:55:55 -04:00
Thomas Harte
4ebc3344cb Merge pull request #1128 from TomHarte/SMSFetch
Fix base for Master System fetching.
2023-04-30 16:48:41 -04:00
Thomas Harte
6552d962ab Fix base for Master System fetching. 2023-04-30 16:43:03 -04:00
Thomas Harte
5f151c07ea Merge pull request #1127 from TomHarte/MapperReadback
Permit MSX RAM mapper readback.
2023-04-29 23:54:35 -04:00
Thomas Harte
1f4d526ea5 Permit MSX RAM mapper readback. 2023-04-29 23:48:22 -04:00
Thomas Harte
19d03dd4fd Merge pull request #1117 from TomHarte/MSX2
Flesh out the MSX 2.
2023-04-27 10:00:16 -04:00
Thomas Harte
6c0feeedb4 Update Master System horizontal counter. 2023-04-26 22:49:46 -04:00
Thomas Harte
b5d9586362 Clean up some dangling timing changes. 2023-04-25 23:16:21 -04:00
Thomas Harte
8cd38094fc Merge pull request #1126 from TomHarte/NewLineLayout
Definitively switch 9918 to counting cycle 0 as start-of-sync.
2023-04-25 11:26:28 -04:00
Thomas Harte
e49e98d309 Support horizontal offsets. 2023-04-24 22:43:11 -04:00
Thomas Harte
1b4df01a28 Fix missing right blank. 2023-04-24 22:23:09 -04:00
Thomas Harte
dbddcd109c Add mention of text mode. 2023-04-23 22:38:42 -04:00
Thomas Harte
efa7d659bc Subsume right erase. 2023-04-23 22:21:22 -04:00
Thomas Harte
5daec050dd Adopt proper pixel-content placement. 2023-04-23 22:18:36 -04:00
Thomas Harte
f5c8eba843 Reduce duplication. 2023-04-23 22:02:41 -04:00
Thomas Harte
e5b0e666cc Realign fetching. 2023-04-23 21:16:04 -04:00
Thomas Harte
96896f838c Adjust layout inner loop. 2023-04-23 12:17:55 -04:00
Thomas Harte
f22aa6eb36 Simplify all namespace usages.:wq 2023-04-23 12:08:07 -04:00
Thomas Harte
6d092e0633 Restore missing semicolon. 2023-04-23 12:06:07 -04:00
Thomas Harte
6651a9c323 Use established test. 2023-04-23 12:01:33 -04:00
Thomas Harte
d40bc58e8b Merge branch 'master' into MSX2 2023-04-23 11:58:34 -04:00
Thomas Harte
4b53082774 Merge pull request #1125 from TomHarte/CheckoutV3
Update checkout action.
2023-04-23 11:58:08 -04:00
Thomas Harte
8a5b7e9f47 Update checkout action. 2023-04-23 11:38:04 -04:00
Thomas Harte
12bcc2dee7 Make reasonable guesses at colour burst placement. 2023-04-22 23:11:01 -04:00
Thomas Harte
d587d80616 Transcribe Grauw's line timings. 2023-04-22 22:58:23 -04:00
Thomas Harte
0070a271f8 Merge branch 'master' into MSX2 2023-04-16 21:36:14 -04:00
Thomas Harte
d641a9c2b1 Merge pull request #1124 from TomHarte/65816Branches
65816: Fix test (and commentary) for shortened emulated branches.
2023-04-15 23:39:12 -04:00
Thomas Harte
ed2d4ebb0c Fix test (and commentary) for shortened emulated branches. 2023-04-15 23:30:30 -04:00
Thomas Harte
32597b4e95 Merge branch 'master' into MSX2 2023-04-14 23:26:19 -04:00
Thomas Harte
9f198f6392 Merge pull request #1123 from TomHarte/StackRelative65816
65816: Fix perceived S in emulated stack-relative mode.
2023-04-14 23:26:00 -04:00
Thomas Harte
107cb18df4 Fix perceives S in emulated stack-relative mode. 2023-04-14 00:04:44 -04:00
Thomas Harte
e66c015b43 Eliminate regress for now. 2023-04-13 23:30:52 -04:00
Thomas Harte
9d99cc6115 Fix external slot placement. 2023-04-12 22:35:01 -04:00
Thomas Harte
383770515e Avoid null dereference. 2023-04-10 23:13:36 -04:00
Thomas Harte
024b7960cb Overtly link line and sprite buffers. 2023-04-10 23:03:39 -04:00
Thomas Harte
e0a5d9f31c Reorient sequencers around HSYNC. 2023-04-08 15:28:49 -04:00
Thomas Harte
224c79c492 Move state. 2023-04-06 00:05:19 -04:00
Thomas Harte
278e7ba9b0 Take ownership of test choice. 2023-04-05 23:33:42 -04:00
Thomas Harte
20c1c6fdcd Add sanity check on sprite fetches versus draws. 2023-04-03 22:46:49 -04:00
Thomas Harte
514022204e Attempt to avoid lingering sprite elements. 2023-04-02 22:45:02 -04:00
Thomas Harte
564ee1a5cb Fix sprites on first line of display. 2023-03-30 23:45:19 -04:00
Thomas Harte
f3c2c0ffa9 Synchronise fetch and draw sprite buffer usage. 2023-03-30 19:11:00 -04:00
Thomas Harte
931d2373a4 Attempt to make outer loop sole owner of line/sprite buffer selection. 2023-03-30 00:20:03 -04:00
Thomas Harte
de3cd9c286 Simplify namespace declaration. 2023-03-25 23:22:34 -04:00
Thomas Harte
655638656f Elide the two fills; fix address masking. 2023-03-21 20:05:34 -04:00
Thomas Harte
7d63a50f3e Add MSX 2 to macOS UI. 2023-03-21 20:05:10 -04:00
Thomas Harte
2bf2abf4b2 Be more overt about masking. 2023-03-19 23:00:41 -04:00
Thomas Harte
235d54bb67 Attempt but retreat from proper treatment of width. 2023-03-18 23:13:06 -04:00
Thomas Harte
e66a92d6cb Fill in and use some parts of mode description. 2023-03-18 23:07:33 -04:00
Thomas Harte
a6251f436a Provide commands with [unpopulated] mode parameters. 2023-03-18 13:39:47 -04:00
Thomas Harte
6d49b2e66b Merge branch 'master' into MSX2 2023-03-17 21:25:51 -04:00
Thomas Harte
363fd0f781 Add 6809 to Xcode project. 2023-03-17 21:25:31 -04:00
Thomas Harte
345e519e6a Merge pull request #1122 from TomHarte/6809
Add experimental 6809 opcode decoder.
2023-03-17 21:25:08 -04:00
Thomas Harte
315e0b4545 Add experimental 6809 opcode decoder.
Just a pleasant distraction, for now.
2023-03-17 21:20:35 -04:00
Thomas Harte
4a5b2fd9ba Eliminate logged TODOs that I don't intend to action soon. 2023-03-16 22:00:47 -04:00
Thomas Harte
a5a36cb08e Add missing status storage; capture mode 2 sprite collision locations. 2023-03-15 23:06:32 -04:00
Thomas Harte
aa4582956f Add TODO. 2023-03-15 22:37:47 -04:00
Thomas Harte
c9543d0b36 Merge branch 'MSX2' of github.com:TomHarte/CLK into MSX2 2023-03-14 22:28:01 -04:00
Thomas Harte
d36c8df0c9 Eliminate redundant init. 2023-03-14 22:27:46 -04:00
Thomas Harte
f26dee16bf Update comment. 2023-03-13 23:21:19 -04:00
Thomas Harte
131784d007 Generalise PointSet to read or write. 2023-03-13 22:51:01 -04:00
Thomas Harte
e703fa9cf8 Fetch colours in TMS character mode. 2023-03-12 23:33:29 -04:00
Thomas Harte
201a7c17ae Avoid VDP race condition. 2023-03-12 23:20:48 -04:00
Thomas Harte
e0125e0177 Add MSX 1 diversion. 2023-03-12 23:14:24 -04:00
Thomas Harte
cc04349618 Reestablish relationship between fetch and output. 2023-03-11 22:24:11 -05:00
Thomas Harte
d46f869276 Minor style improvement. 2023-03-10 21:14:52 -05:00
Thomas Harte
9836a108da Avoid VDP access races. 2023-03-10 21:04:55 -05:00
Thomas Harte
da944fde92 Eliminate data-type assumption. 2023-03-10 21:04:35 -05:00
Thomas Harte
c9124f13cd Add suggested brackets. 2023-03-09 22:25:09 -05:00
Thomas Harte
ca7d34ad04 Fix ambiguous using. 2023-03-09 22:24:53 -05:00
Thomas Harte
2913368a06 Attempt YMMM. 2023-03-08 23:12:02 -05:00
Thomas Harte
82659e7924 Unify existing moves. 2023-03-08 22:36:06 -05:00
Thomas Harte
f3a84021ed Merge branch 'MSX2' of github.com:TomHarte/CLK into MSX2 2023-03-08 18:28:17 -05:00
Thomas Harte
555d883227 Yamaha fetches don't require an outer switch. 2023-03-08 18:28:13 -05:00
Thomas Harte
020d9604c6 Better judge when to rotate addresses. 2023-03-08 18:27:59 -05:00
Thomas Harte
6845008fd4 Place end-of-frame interrupt appropriately. 2023-03-07 22:12:06 -05:00
Thomas Harte
cc7b209e1a Adjust visible Y9938 area; clamp scrolled y; use proper mode 2 terminator. 2023-03-07 18:19:08 -05:00
Thomas Harte
e8404bdcc0 TODO is done, probably. 2023-03-06 22:57:53 -05:00
Thomas Harte
f8eb2199c2 Fix relative offset. 2023-03-06 22:49:21 -05:00
Thomas Harte
a13905acf9 Attempt to incorporate scroll offset into line interrupt. 2023-03-06 21:41:43 -05:00
Thomas Harte
5471979f8d Eliminate stale TODOs. 2023-03-05 21:34:10 -05:00
Thomas Harte
5b8a5755f0 Use correct mode-7 sprite palette. 2023-03-05 21:29:04 -05:00
Thomas Harte
caaba836ba Correct GR7 rasterisation and 6/7 collection. 2023-03-05 13:43:53 -05:00
Thomas Harte
8fc043247c Fix column addressing in modes 6 and 7. 2023-03-04 21:39:00 -05:00
Thomas Harte
e58a488add Rotate command-engine addresses in modes 6 and 7. 2023-03-03 23:06:52 -05:00
Thomas Harte
0ea1da10d6 Attempt to generalise moving from CPU. 2023-03-03 21:40:48 -05:00
Thomas Harte
3381e6b5aa Switch to Duff's device. 2023-03-02 22:16:18 -05:00
Thomas Harte
4c1973adc8 Fill in missing rasterisers. 2023-03-01 23:19:33 -05:00
Thomas Harte
318cfab67d Attempt ongoing state for vertical on/off. 2023-02-28 22:28:14 -05:00
Thomas Harte
1ef34b1f18 Clarify meaning of STOP. 2023-02-26 14:28:24 -05:00
Thomas Harte
12bd71cfe1 Allow that the next interrupt line might be in the next frame. 2023-02-26 14:23:38 -05:00
Thomas Harte
c7128f4206 Reduce logging. 2023-02-26 14:01:23 -05:00
Thomas Harte
95fd5a52ba Use redirected palette in bitmap modes. 2023-02-26 13:59:17 -05:00
Thomas Harte
bfa167fcdf Make attempt at the TP background-colour bit. 2023-02-26 13:42:59 -05:00
Thomas Harte
b948761f30 Detect mode 2 collisions, albeit without proper reporting. 2023-02-25 10:42:44 -06:00
Thomas Harte
dd65074bbf Add named getters, resolving composition bug. 2023-02-25 10:32:47 -06:00
Thomas Harte
9debed25e8 Fix meaning of 'origin'. 2023-02-25 08:53:00 -06:00
Thomas Harte
1bdf9a50e6 Take a shot at Mode 2 sprite output. 2023-02-25 08:48:16 -06:00
Thomas Harte
2592dfd78f Pile on the TODOs. 2023-02-21 22:33:47 -05:00
Thomas Harte
ef5d05db53 Scale down new-mode coordinates; add note to self. 2023-02-21 22:28:39 -05:00
Thomas Harte
6a2cda7074 Pull out and partially generalise sprite output.
This also provides the intended route to supporting Mode 2.
2023-02-21 22:00:00 -05:00
Thomas Harte
62d381065e Change 9938 identification number. 2023-02-21 19:35:11 -05:00
Thomas Harte
091c1b0f45 Correct sprite attribute and colour table addresses. 2023-02-21 19:34:26 -05:00
Thomas Harte
a28b5bdeea Only on the Sega are line interrupts disabled by a read of S#0. 2023-02-20 23:43:23 -05:00
Thomas Harte
2e45422b03 Make a further attempt at sprite colour. 2023-02-20 22:39:34 -05:00
Thomas Harte
3606f5befe Add expansion RAM to command engine, as far as it goes. 2023-02-20 22:32:36 -05:00
Thomas Harte
44ac948bb2 Add expansion RAM, albeit not yet into the command engine. 2023-02-20 22:27:30 -05:00
Thomas Harte
4bac782121 One final (?) time with colour. 2023-02-19 22:15:59 -05:00
Thomas Harte
318109b7d5 Try again with colour. 2023-02-19 22:14:18 -05:00
Thomas Harte
6990ba9242 Fetch colour from before attribute table. 2023-02-19 22:10:22 -05:00
Thomas Harte
54be424159 Include sprite index in SpriteY event. 2023-02-19 22:04:38 -05:00
Thomas Harte
8d900cf636 Attempt a full collection of Mode 2 sprite properties. 2023-02-19 21:52:10 -05:00
Thomas Harte
6db939f48f Create a dedicated sprite fetcher. 2023-02-19 21:37:24 -05:00
Thomas Harte
5eae11434a Sprite mode 2: select sprites, fetch locations and names. 2023-02-18 22:56:05 -05:00
Thomas Harte
7aa8728b39 Reimagine G3 for sprite mode 2. 2023-02-18 21:50:12 -05:00
Thomas Harte
270c5dfe85 Eliminate data_block_ entirely. 2023-02-18 21:41:22 -05:00
Thomas Harte
d6e4f12fdc Merge DataBlock and Pattern. 2023-02-18 21:34:09 -05:00
Thomas Harte
4e3e4806b9 Eliminate assert; events have overtaken this. 2023-02-17 23:01:42 -05:00
Thomas Harte
ebc596820e Obtain the background graphics, at least, for G3. 2023-02-17 22:47:15 -05:00
Thomas Harte
b62e899039 Avoid need for shortcuts. 2023-02-17 22:34:52 -05:00
Thomas Harte
a123ef151c Eliminate further magic ORs. 2023-02-17 22:31:21 -05:00
Thomas Harte
5b31db700b Deduplicate 40-column text fetching. 2023-02-17 22:23:10 -05:00
Thomas Harte
8af0a2313c Formally distinguish fetchers and sequencing. 2023-02-17 22:20:38 -05:00
Thomas Harte
88eaa4ff02 [Mostly] avoid magic address constants; avoid duplication of TMS fetching logic. 2023-02-17 21:59:39 -05:00
Thomas Harte
c140f370fe Attempt to copy and paste my way to working type-1 sprites. 2023-02-16 22:46:19 -05:00
Thomas Harte
023da1970a Label all character events with IDs. 2023-02-16 22:21:24 -05:00
Thomas Harte
211e145230 Make room for an ID field on Event. 2023-02-16 22:10:16 -05:00
Thomas Harte
bbccc5d6d6 Route other holdover TMS modes through the Yamaha logic. 2023-02-16 22:07:18 -05:00
Thomas Harte
dbfc9a14aa Introduce SMS fetcher, eliminating all macros. 2023-02-16 22:01:20 -05:00
Thomas Harte
9630a1bc39 Use a fetcher for character modes. 2023-02-16 13:15:18 -05:00
Thomas Harte
3847c9c281 Add missing underscore. 2023-02-15 21:22:51 -05:00
Thomas Harte
3f2a5929a3 Consolidate text output and support blinking; add sprites-enabled flag. 2023-02-15 20:18:56 -05:00
Thomas Harte
9b71f42375 Collect colours. 2023-02-14 21:18:10 -05:00
Thomas Harte
9c43776392 Fix 80-column address generation. 2023-02-14 21:14:35 -05:00
Thomas Harte
35a0a1447e Further clarify different usages of storage. 2023-02-14 20:23:17 -05:00
Thomas Harte
bf0ed2813c Make faulty attempt at 80-column text. 2023-02-14 20:13:51 -05:00
Thomas Harte
1edf747f9f Avoid flushes for video output changes. 2023-02-14 20:13:34 -05:00
Thomas Harte
f38cf91ea7 Add attempt to detect improper usage. 2023-02-14 20:13:16 -05:00
Thomas Harte
5c7367b262 Route Yamaha 40-column text mode appropriately. 2023-02-13 22:24:39 -05:00
Thomas Harte
c1457cc5e0 Attempt text mode data collection. 2023-02-13 22:20:47 -05:00
Thomas Harte
169d7a7418 Fix[/revert]: the fetch pointer should be _ahead_. 2023-02-13 21:10:14 -05:00
Thomas Harte
5143960970 Add notes to self on how to collect text. 2023-02-13 21:09:31 -05:00
Thomas Harte
40894964bc Move VerticalState to live with ScreenMode and FetchMode. 2023-02-13 09:54:29 -05:00
Thomas Harte
927e61484f Map all events lists appropriately. 2023-02-12 23:02:51 -05:00
Thomas Harte
dce04e7219 Add a generator for character modes. 2023-02-12 22:54:08 -05:00
Thomas Harte
f5814b4c75 Add text-mode events list. 2023-02-12 22:45:11 -05:00
Thomas Harte
815a75d9b6 Extend generator to handle sprite collection. 2023-02-12 22:28:34 -05:00
Thomas Harte
4ad84e5047 Use generator for no-sprite events list. 2023-02-12 22:12:23 -05:00
Thomas Harte
8665dca7f0 Permit generator-based event-table generation. 2023-02-12 21:58:50 -05:00
Thomas Harte
41d57e03a6 Split out LineBuffer and Storage to make 9918Base more manageable. 2023-02-12 12:58:46 -05:00
Thomas Harte
914a9e0c84 Yamaha: don't touch address at all unless a RAM access. 2023-02-11 22:43:29 -05:00
Thomas Harte
8768ee1504 Merge branch 'MSX2' of github.com:TomHarte/CLK into MSX2 2023-02-11 22:40:03 -05:00
Thomas Harte
8e6c36bb15 Yamaha: don't part-modify address. 2023-02-11 10:35:42 -05:00
Thomas Harte
c6401f0444 Add TODO. 2023-02-07 22:28:18 -05:00
Thomas Harte
3c3efe3e22 Don't be so fussy. 2023-02-06 22:16:42 -05:00
Thomas Harte
7028bdd05d Limit to 14 bits in old modes. 2023-02-06 22:16:31 -05:00
Thomas Harte
7cb51c021b Observation: offset is needed only ephemerally. 2023-02-06 21:45:35 -05:00
Thomas Harte
a3df106f92 Reset write phase only upon traditional register accesses. 2023-02-06 20:32:24 -05:00
Thomas Harte
b538407386 Introduce separate state for palette entries. 2023-02-06 19:12:02 -05:00
Thomas Harte
c04d292c8e Make this more obviously correct, albeit arbitrarily.
Comparing just a single bit would do.
2023-02-05 22:53:08 -05:00
Thomas Harte
f9c88fd598 Fix memory mask; mildly improve commentary. 2023-02-05 22:51:16 -05:00
Thomas Harte
7fcb1b29dd Keep source within rectangle.
This gives something vaguely recognisable, sort of, for the test program I'm using.
2023-02-04 21:37:08 -05:00
Thomas Harte
6786e3e78c Initialise to zero, for completeness. 2023-02-04 21:32:31 -05:00
Thomas Harte
67755c3811 Attempt [H/L]MMM. 2023-02-04 21:29:44 -05:00
Thomas Harte
c6372295c5 Complete ReadSourcePixel & ReadSourceByte paths. 2023-02-04 21:23:41 -05:00
Thomas Harte
a2786e6266 Invest Colour with its own logic, including potential emptiness. 2023-02-04 21:14:20 -05:00
Thomas Harte
a892523c09 Advance to the next breaking point. 2023-02-04 11:43:06 -05:00
Thomas Harte
ab595f5e8d Merge branch 'MSX2' of github.com:TomHarte/CLK into MSX2 2023-02-04 11:02:10 -05:00
Thomas Harte
38950fe241 Sketch out remaining necessary @c AccessTypes. 2023-02-04 11:02:02 -05:00
Thomas Harte
46d009f27b Add logical fill. 2023-02-04 10:31:41 -05:00
Thomas Harte
34722bae89 Start pivoting to a more natural expression of TMS patterns. 2023-02-03 23:06:27 -05:00
Thomas Harte
d41081c59f Fix sections. 2023-02-02 21:55:00 -05:00
Thomas Harte
ec227ce021 Generalise rectangular operations. 2023-02-02 21:49:05 -05:00
Thomas Harte
83f6d1cda3 Prepare for source/destination operations. 2023-02-02 21:16:24 -05:00
Thomas Harte
6d315b4660 Switch to specifying number of bits, to reduce potential error. 2023-02-02 12:12:11 -05:00
Thomas Harte
debdad350d Don't allow a disabled screen to interfere with Yamaha addressing. 2023-02-02 12:03:33 -05:00
Thomas Harte
0d4dc214fb Merge branch 'MSX2' of github.com:TomHarte/CLK into MSX2 2023-02-02 11:38:45 -05:00
Thomas Harte
5d4c49c913 Attempt to enable high-speed fill. 2023-02-01 22:57:33 -05:00
Thomas Harte
8f5c7fcabc Merge branch 'MSX2' of github.com:TomHarte/CLK into MSX2 2023-02-01 22:25:12 -05:00
Thomas Harte
115acf835e Vertical state is actually tristate. 2023-02-01 22:25:00 -05:00
Thomas Harte
c0fec8db15 Clean up macro. 2023-02-01 15:05:21 -05:00
Thomas Harte
f0e70f18fd Fix seeding of output_pointer_. 2023-02-01 14:59:42 -05:00
Thomas Harte
9d2841bf6a Reenable full ram pointer capture. Thanks @PatrickvL ! 2023-02-01 14:55:33 -05:00
Thomas Harte
ce6dd188a4 Double up on alignas. 2023-02-01 14:31:40 -05:00
Thomas Harte
4cc34fd557 Resolve GCC complaint. 2023-02-01 14:28:19 -05:00
Thomas Harte
3636383b1f Silence abstract/non-virtual-destructor warning. 2023-02-01 14:20:11 -05:00
Thomas Harte
1264600bab Shorten long lines. 2023-02-01 14:18:36 -05:00
Thomas Harte
002d27d9c2 Resolve various type conversion errors, and reduce duplication. 2023-02-01 14:17:49 -05:00
Thomas Harte
90e8ce3253 Fix lines.
TODO: determine whether I really need `location` as distinct from `.destination`.
2023-01-31 21:33:57 -05:00
Thomas Harte
a315384e30 Provide context for byte-by-byte commands. 2023-01-31 21:29:55 -05:00
Thomas Harte
c5c722ae56 Generalise axis steps; begin HMMV. 2023-01-31 13:35:39 -05:00
Thomas Harte
872b9e5021 Predict Yamaha horizontal retrace interrupts. 2023-01-30 21:33:47 -05:00
Thomas Harte
492a170b20 Implement much of Yamaha line interrupts. 2023-01-30 21:24:53 -05:00
Thomas Harte
30a2b1611f Merge branch 'MSX2' of github.com:TomHarte/CLK into MSX2 2023-01-30 21:06:23 -05:00
Thomas Harte
29af5542f8 Make an effort at doing _something_ for G4. 2023-01-30 21:06:04 -05:00
Thomas Harte
bc4c54800e Type out just a little of status register 1. 2023-01-30 20:20:33 -05:00
Thomas Harte
4c93d01fe2 Reduce logging. 2023-01-29 21:30:57 -05:00
Thomas Harte
8f20cb93e9 Note missed status accesses. 2023-01-29 21:20:50 -05:00
Thomas Harte
73e79b14ea Use Yamaha palette pervasively. 2023-01-29 21:17:00 -05:00
Thomas Harte
3142f5c21d Be overt about what replaces LineMode. 2023-01-29 21:04:15 -05:00
Thomas Harte
6d7f189ce7 Attempt the full panoply of logical pixel modes, across all graphics modes. 2023-01-29 18:28:49 -05:00
Thomas Harte
a91a5b8d07 Refer to actual field. 2023-01-29 18:02:09 -05:00
Thomas Harte
4cdcd3ac7d Retain logical operation, take colour combination outside the loop. 2023-01-29 13:29:19 -05:00
Thomas Harte
0576451102 Be overt about colour direction. 2023-01-29 13:22:56 -05:00
Thomas Harte
3f12a28f4f Locate first pixel correctly. 2023-01-28 22:50:04 -05:00
Thomas Harte
41ba883fb6 Honour direction, start transfer immediately. 2023-01-28 22:47:27 -05:00
Thomas Harte
1e646eb57b Improve transfer flag for LMMC. 2023-01-28 21:45:05 -05:00
Thomas Harte
2d6afe1013 Reduce repetition, tidy slightly. 2023-01-28 21:43:14 -05:00
Thomas Harte
d3c446d91b Take a shot at LMMC. 2023-01-28 21:30:45 -05:00
Thomas Harte
975ead5d01 Edge towards not assuming graphics mode. Much more to do here. 2023-01-28 13:54:26 -05:00
Thomas Harte
c6cda7c401 Switch to absolute placement of deferred events; properly serialise commands. 2023-01-28 11:55:12 -05:00
Thomas Harte
7e69e33ec2 Use paletted Yamaha border colour. 2023-01-27 22:42:02 -05:00
Thomas Harte
95e00dd958 Take slightly wrong-headed steps towards proper command timing. 2023-01-27 22:20:36 -05:00
Thomas Harte
8a673a697b Further adapt internal terminology. 2023-01-27 12:30:09 -05:00
Thomas Harte
75ad4cdb67 Fix line semantics. 2023-01-27 11:57:40 -05:00
Thomas Harte
d42b6df570 Retain extra pattern name address bits. 2023-01-27 06:44:11 -05:00
Thomas Harte
81b00c6d97 Add notes. 2023-01-26 22:25:10 -05:00
Thomas Harte
1e5f751bc0 Require STOP in order to stop. 2023-01-26 22:08:36 -05:00
Thomas Harte
9a65fffe16 That's PSET, not POINT. 2023-01-26 22:02:40 -05:00
Thomas Harte
515fa22bfe Implement point. 2023-01-26 21:52:41 -05:00
Thomas Harte
66ac089cc2 Deallocate. 2023-01-26 21:49:32 -05:00
Thomas Harte
5d5098acb2 Hack in vertical scrolling. 2023-01-26 21:38:51 -05:00
Thomas Harte
1bf8406e7e Correct deserialisation order. 2023-01-26 21:34:56 -05:00
Thomas Harte
75acbd2d6c A quick hack shows some part of the MSX logo. 2023-01-26 21:31:49 -05:00
Thomas Harte
baa6f9b3cd Implements the Command side of the line command. 2023-01-26 21:17:11 -05:00
Thomas Harte
1c6a0ad3f7 Clean up repetition. 2023-01-26 19:51:56 -05:00
Thomas Harte
fbfa26ad5e Minor steps towards implementing Line. 2023-01-26 12:55:08 -05:00
Thomas Harte
b12fd00145 Generate an appropriate instance for line drawing. 2023-01-26 12:09:06 -05:00
Thomas Harte
0c8815d6a0 Retain command-engine context. 2023-01-26 11:59:27 -05:00
Thomas Harte
700470915a Add pixel serialisation for Yamaha graphics mode 5. 2023-01-24 23:07:29 -05:00
Thomas Harte
f8b42d4107 While being lazy with types, implement 4/5/6/7 fetching. 2023-01-24 13:15:00 -05:00
Thomas Harte
63bd0f918d Be overt about buffer target and vertical position. 2023-01-24 12:46:09 -05:00
Thomas Harte
445b34933a Edge further towards actual fetching. 2023-01-23 23:19:04 -05:00
Thomas Harte
6b85ee9607 Months seem to start at 1; also fix seeded year for MSX. 2023-01-23 22:52:26 -05:00
Thomas Harte
bbd8b011ba Remove temporarily-faulty check. 2023-01-23 22:52:03 -05:00
Thomas Harte
15fbf4cb7f Ensure Yamaha-style refresh is used in all modes. 2023-01-23 22:39:34 -05:00
Thomas Harte
7293f9dc10 Remove extraneous brackets, add comment to self. 2023-01-23 22:25:56 -05:00
Thomas Harte
8567c934b1 Ensure Yamaha refresh program is used. 2023-01-22 22:11:01 -05:00
Thomas Harte
2744a9b6b0 Tidy up. 2023-01-22 22:02:39 -05:00
Thomas Harte
91047e5b3a Start attempting to use table-based Yamaha fetch. 2023-01-22 22:00:28 -05:00
Thomas Harte
c6dd7d4726 Transcribe no-sprite event list. 2023-01-22 20:19:21 -05:00
Thomas Harte
b7d80f5ed1 Copy in some notes, expand line buffer. 2023-01-21 23:04:48 -05:00
Thomas Harte
a5765abbad Route into the Yamaha fetcher.
Albeit that it doesn't yet fetch.
2023-01-21 22:47:16 -05:00
Thomas Harte
696ec12516 Add address rotation for applicable modes. 2023-01-21 22:33:26 -05:00
Thomas Harte
c9734df65c Implement extended colour, sprite and RAM pointers. 2023-01-21 20:45:23 -05:00
Thomas Harte
13e490e7d7 Log selected screen mode. 2023-01-21 14:48:55 -05:00
Thomas Harte
cefcc1d443 Expand Yamaha graphics mode recognition. 2023-01-21 14:35:26 -05:00
Thomas Harte
d1f929e6f7 Just do a multiply and divide. Easy. 2023-01-21 14:19:52 -05:00
Thomas Harte
c9643c4145 Log memory control meaningfully. 2023-01-21 14:13:02 -05:00
Thomas Harte
e289e6e757 Catch and map Yamaha palette entries.
It's one less thing in the uncaptured log.
2023-01-21 14:12:46 -05:00
Thomas Harte
a726c9d97a Enable indirect register writes. 2023-01-20 23:14:57 -05:00
Thomas Harte
c77e7c268f 1 = disable, 0 = enable. 2023-01-20 23:08:41 -05:00
Thomas Harte
9c57bfd58d Attempt to log dropped indirect writes. 2023-01-20 23:07:14 -05:00
Thomas Harte
4efda108c6 Transcribe the Yamaha 9938 register meanings. 2023-01-20 23:00:33 -05:00
Thomas Harte
191cf4829b Attempt real blank reporting. 2023-01-20 22:29:49 -05:00
Thomas Harte
9b7a925816 Give clearer names to the two pointers. 2023-01-20 20:29:15 -05:00
Thomas Harte
392b0acb58 Pull everything out of master_system_ struct.
Now that it's inherently collected in the relevant `Storage`.
2023-01-19 15:09:16 -05:00
Thomas Harte
4b7606894e Move Master System state, and start simplifying. 2023-01-19 14:09:31 -05:00
Thomas Harte
1fb94d15ab No need for this-> ugliness in Base methods. 2023-01-19 12:32:42 -05:00
Thomas Harte
348c42bdea Start trying to bluff my way through extended status. 2023-01-18 22:23:19 -05:00
Thomas Harte
e450e53c4e Temporarily copy and paste my way to further logging. 2023-01-18 14:59:30 -05:00
Thomas Harte
355ee7fbc7 Adjust factoring of read and write per expanded V9938 scope. 2023-01-18 12:36:57 -05:00
Thomas Harte
339086d597 The Yamaha chips have more ports. 2023-01-17 22:29:17 -05:00
Thomas Harte
f0b1c34db2 Merge pull request #1116 from TomHarte/RP5C01
More fully implement the RP-5C01.
2023-01-17 22:25:17 -05:00
Thomas Harte
7b25fe5f61 Make read consistent. 2023-01-17 21:18:56 -05:00
Thomas Harte
194b5bc36a Attempt to deal with hours correctly. 2023-01-17 21:12:00 -05:00
Thomas Harte
0951c50e40 Further explain. 2023-01-17 20:14:32 -05:00
Thomas Harte
9588c9bee2 Merge branch 'RP5C01' of github.com:TomHarte/CLK into RP5C01 2023-01-17 18:53:30 -05:00
Thomas Harte
6f973fc605 Attempt some use of NumericCoder. 2023-01-17 18:53:26 -05:00
Thomas Harte
eb51ed9ae8 Shift ownership of initial values. 2023-01-17 17:36:15 -05:00
Thomas Harte
83cf4497dd Split encode and decode for clearer naming. 2023-01-17 17:33:52 -05:00
Thomas Harte
f6e601daff Introduce a template for numeric coding. 2023-01-17 13:26:11 -05:00
Thomas Harte
bb6ceafe0e Implement the easy writes. 2023-01-16 22:31:03 -05:00
Thomas Harte
55e73cb812 Implement most of reading. 2023-01-16 22:25:20 -05:00
Thomas Harte
f0db676a10 Be consistent in use of C parts. 2023-01-16 20:29:32 -05:00
Thomas Harte
32b29bd63b Transcribe all missing registers. 2023-01-16 20:26:27 -05:00
Thomas Harte
bfe94eb268 Seed date and time with current. 2023-01-16 20:11:42 -05:00
Thomas Harte
20ec192129 Merge pull request #1114 from TomHarte/SecondarySlots
Add support for secondary MSX slots.
2023-01-16 20:01:21 -05:00
Thomas Harte
055e9cdf8d Differentiate unmapped and mapped-for-handler. 2023-01-16 19:52:40 -05:00
Thomas Harte
a5b9bdc18c Eliminate speculative apply_mapping. 2023-01-16 11:53:04 -05:00
Thomas Harte
eb51ff5cdf Add RAM paging. 2023-01-16 11:52:08 -05:00
Thomas Harte
1769c24531 Avoid ambiguous naming. 2023-01-16 11:43:43 -05:00
Thomas Harte
1a58ddaa67 Increase notes for future self. 2023-01-15 23:12:36 -05:00
Thomas Harte
183cb519e7 Give autonomy to secondary slots. 2023-01-15 22:51:17 -05:00
Thomas Harte
68361913ee Substitute VDP for the MSX 2. 2023-01-14 22:05:59 -05:00
Thomas Harte
ced002125e Make a basic attempt at RAM. 2023-01-14 14:58:12 -05:00
Thomas Harte
1e17fc71ab Add an RP-5C01 to the MSX 2. 2023-01-14 14:52:07 -05:00
Thomas Harte
f57c2a961f Add to further project files. 2023-01-14 14:20:29 -05:00
Thomas Harte
48a4355592 Start sketching out an RP5C01. 2023-01-14 14:17:28 -05:00
Thomas Harte
3bc38d35c9 Fix include order. 2023-01-14 14:16:56 -05:00
Thomas Harte
4d67360702 Merge branch 'master' into SecondarySlots 2023-01-13 22:23:54 -05:00
Thomas Harte
18def0c97d Correct extension ROM visibility. 2023-01-13 22:22:58 -05:00
Thomas Harte
84cb7df1be Merge pull request #1115 from TomHarte/DynamicCull
Restore repeated lookup of timed machine under macOS.
2023-01-13 22:08:29 -05:00
Thomas Harte
97d93ad55c Restore repeated lookup of timed machine.
This restores culling of abandoned parallel machines during dynamic analysis.
2023-01-13 22:02:56 -05:00
Thomas Harte
5f85074caa Restore repeated lookup of timed machine.
This restores culling of abandoned parallel machines during dynamic analysis.
2023-01-13 22:02:15 -05:00
Thomas Harte
f0a4d1d8ec Wire up did-page notifications. 2023-01-13 21:54:59 -05:00
Thomas Harte
fb0241cf6e Be overt about alignment. 2023-01-13 14:30:17 -05:00
Thomas Harte
50b5122969 For an MSX 2, the extension ROM is obligatory. 2023-01-13 14:18:39 -05:00
Thomas Harte
9f450b3ccb Expose the extension ROM to an MSX 2. 2023-01-13 14:16:12 -05:00
Thomas Harte
4190d25698 Ensure RAM is properly sized and available. 2023-01-13 14:07:54 -05:00
Thomas Harte
befc81743a Fix base RAM mapping. 2023-01-13 09:31:56 -05:00
Thomas Harte
23ff3fc366 Ensure all routes go somewhere. 2023-01-13 08:05:12 -05:00
Thomas Harte
78ce439b9b Add missing header; correct type. 2023-01-12 23:08:01 -05:00
Thomas Harte
ce440d52b3 Standardise name. 2023-01-12 23:02:24 -05:00
Thomas Harte
2e7e5ea12b Fleshes out most of a cleaner memory slot layout. 2023-01-12 23:01:11 -05:00
Thomas Harte
0d8c014099 Secondary slot selections are per primary slot. 2023-01-11 13:15:00 -05:00
Thomas Harte
fee82d3baa Fix typo. 2023-01-11 13:14:42 -05:00
Thomas Harte
76ad465030 Also seek the extension ROM for the MSX 2. 2023-01-11 12:56:09 -05:00
Thomas Harte
483ee8a74f Add a catch for the secondary paging register. 2023-01-10 22:24:40 -05:00
Thomas Harte
520ae7f2b2 Pick generic BIOS based on machine type. 2023-01-10 22:15:01 -05:00
Thomas Harte
ae5b81c0ab Add MSX 2 to the ROM catalogue. 2023-01-10 18:17:17 -05:00
Thomas Harte
6bd261b222 Add storage for secondary paging. 2023-01-10 18:07:31 -05:00
Thomas Harte
53bb17c848 Use model as a compile-time MSX configurator. 2023-01-10 14:55:57 -05:00
Thomas Harte
6e0f260478 Add a model field. 2023-01-10 14:52:09 -05:00
Thomas Harte
19e333d117 Merge branch 'master' into SecondarySlots 2023-01-10 14:40:49 -05:00
Thomas Harte
3352feb21b Merge pull request #1113 from TomHarte/VDPs
TMS9918 &c: Eliminate hard-coded assumption of 16kb.
2023-01-10 14:40:38 -05:00
Thomas Harte
73549eb38c Document quite a bit more, to refresh my memory. 2023-01-10 14:40:03 -05:00
Thomas Harte
dbff7592f5 Merge pull request #1112 from TomHarte/VDPs
Clean up TMS9918-related code.
2023-01-10 13:40:47 -05:00
Thomas Harte
4d96122884 Eliminate hard-coded assumption of 16kb.
Clearly I'll have to do something else to support 128k+, probably move the ram pointer?
2023-01-10 12:38:19 -05:00
Thomas Harte
9085ba4081 Update SMS VDP tests. 2023-01-09 22:58:12 -05:00
Thomas Harte
f1f16d1f9a Clarify and simplify half_cycles_before_internal_cycles. 2023-01-09 22:55:46 -05:00
Thomas Harte
fd14829992 Avoid hand-writing all the various conversions. 2023-01-09 22:34:56 -05:00
Thomas Harte
c0fe88a5bb Apply clock conversion to existing usages of do_external_slot. 2023-01-09 13:54:49 -05:00
Thomas Harte
4d9d684618 Add TODO on dangling hard-coded conversion. 2023-01-08 21:44:25 -05:00
Thomas Harte
a0a835cf10 Export memory size into traits. 2023-01-08 21:37:20 -05:00
Thomas Harte
ef67205ce8 Set pixel count per mode. 2023-01-08 21:31:00 -05:00
Thomas Harte
794adf470b Break assumption that cycles = pixels; fix pixel clocking. 2023-01-08 21:25:22 -05:00
Thomas Harte
8cc20844a9 Clock convert for draw_ calls. 2023-01-08 17:31:08 -05:00
Thomas Harte
b522d65c50 Fix border lengths. 2023-01-08 17:04:19 -05:00
Thomas Harte
cb19c2ffb0 Honour internal-clocked timing constants. 2023-01-08 14:10:06 -05:00
Thomas Harte
5f6ddf8557 Avoid expressing the same thing at different clock rates. 2023-01-08 13:58:12 -05:00
Thomas Harte
72e0bfecc1 Edge towards clock-independent line composition. 2023-01-07 14:57:32 -05:00
Thomas Harte
cdf547ac82 Decline to provide synthetic text mode timing on the Mega Drive. 2023-01-07 14:37:06 -05:00
Thomas Harte
dd5b4b484a Avoid double responsibility for state. 2023-01-07 14:34:33 -05:00
Thomas Harte
56831e02fc Expand fixed timing constants. 2023-01-07 13:10:51 -05:00
Thomas Harte
5d2d3944ef Make VRAM access delay a timing property. 2023-01-07 12:48:43 -05:00
Thomas Harte
f9e21df701 Avoid further hard-coded 342s. 2023-01-07 09:13:34 -05:00
Thomas Harte
bb436204f6 Merge branch 'VDPs' of github.com:TomHarte/CLK into VDPs 2023-01-07 09:10:50 -05:00
Thomas Harte
de45536b5c Elucidate a magic constant, add an extra constexpr. 2023-01-07 09:10:41 -05:00
Thomas Harte
ebc1264c2c Create a common home for timing information. 2023-01-06 22:39:46 -05:00
Thomas Harte
4875148617 Fill in Mega Drive numbers. 2023-01-05 14:22:51 -05:00
Thomas Harte
7a82b76911 Ensure visibility of memset. 2023-01-05 13:21:03 -05:00
Thomas Harte
27d37f71ec Generalise and better factor bit reversal and TMS drawing. 2023-01-05 13:18:10 -05:00
Thomas Harte
c4a5a9763e Minor indentation improvement. 2023-01-02 15:04:50 -05:00
Thomas Harte
a9f97ac871 Fix nothing-to-do test. 2023-01-02 15:04:08 -05:00
Thomas Harte
475440dc70 Update ClockConverter for potential alternative clocks. 2023-01-02 14:59:36 -05:00
Thomas Harte
dc3f8f5e42 These are the three fetchers to implement.
They'll look fairly different from the TMS and SMS fetchers, I think, owing to the greater irregularity that comes with the smarter RAM accesses. I might need to play around for a while.
2023-01-01 22:44:06 -05:00
Thomas Harte
459ef39b08 constexpr the TMS palette. 2023-01-01 22:34:07 -05:00
Thomas Harte
27812fd0e2 Separate fetchers into their own header. 2023-01-01 22:26:50 -05:00
Thomas Harte
38eb4d36de Better explain cumulative nature of @c to_internal. 2023-01-01 22:18:39 -05:00
Thomas Harte
2bd20a0cf8 Add further exposition. 2023-01-01 22:17:21 -05:00
Thomas Harte
da61909ec5 Explain the purpose here. 2023-01-01 21:20:30 -05:00
Thomas Harte
5729ece7bb Incompletely transitions towards more flexible clock ratios. 2023-01-01 14:20:45 -05:00
Thomas Harte
151f60958e Relocate the 9918 implementation file. 2023-01-01 14:01:19 -05:00
Thomas Harte
180045ada6 Convert vram_access_delay into a free-standing function. 2023-01-01 13:51:52 -05:00
Thomas Harte
11542e7a7f Improve const correctness, simplify inheritance. 2023-01-01 13:49:11 -05:00
Thomas Harte
71598250ea Improve commentary. 2023-01-01 13:41:51 -05:00
Thomas Harte
e8aab1fd2a Restore proper VDP selection. 2022-12-31 21:54:14 -05:00
Thomas Harte
ffb0b2ce0b Eliminate runtime duplication of personality. 2022-12-31 21:50:57 -05:00
Thomas Harte
b7c315058f Also template Base. 2022-12-31 21:47:05 -05:00
Thomas Harte
7d6eac2895 Template the TMS on its personality.
Template parameter currently unused, but preparatory to other improvements.
2022-12-31 15:08:33 -05:00
Thomas Harte
d79aac3081 Shuffle the personality enum into the 'public' header. 2022-12-31 15:01:11 -05:00
Thomas Harte
8d5547dc9e Minor further style improvements.
... as I refamiliarise myself.
2022-12-29 22:09:14 -05:00
Thomas Harte
5d89293c92 Improve constness, primarily of reverse_table. 2022-12-29 11:29:19 -05:00
Thomas Harte
5ba97da6cd Avoid macro. 2022-12-29 11:28:47 -05:00
Thomas Harte
37f07f349e Merge branch 'master' into VDPs 2022-12-27 22:50:53 -05:00
Thomas Harte
711f7b2d75 C++17 makes this a single step. 2022-12-27 22:50:12 -05:00
Thomas Harte
dca8c51384 Prefer to avoid a macro. 2022-12-27 22:36:27 -05:00
Thomas Harte
462b7dcbfa Add Mega Drive VRAM size. 2022-12-27 22:28:43 -05:00
Thomas Harte
2ab4b351ca Extend enum. 2022-12-27 22:20:47 -05:00
Thomas Harte
4eedec50fb Merge pull request #1111 from TomHarte/LessDynamic
Add note to future self.
2022-12-27 20:24:28 -05:00
Thomas Harte
ee22a98c17 Add note to future self. 2022-12-27 20:23:25 -05:00
Thomas Harte
99ced5476f Add quick clock-rate notes. 2022-12-26 22:56:45 -05:00
Thomas Harte
cdbd821913 Merge pull request #1109 from TomHarte/68020Addressing
Support 68020+ long extensions.
2022-12-19 11:16:35 -05:00
Thomas Harte
8808014a85 Add AddressingMode::ExtensionWord to the executor. 2022-12-19 11:07:58 -05:00
Thomas Harte
6832cbeb31 Attempt fully to tie together 68020+ addressing. 2022-12-19 10:38:51 -05:00
Thomas Harte
08b3c42a5c Edge further towards supporting full extension words. 2022-12-10 16:22:16 -05:00
Thomas Harte
95c526d957 Start arrangements for full extension words. 2022-11-30 16:21:35 -05:00
Thomas Harte
3f3c9f7491 Update version number. 2022-11-25 15:45:44 -05:00
Thomas Harte
6aa8400996 Merge pull request #1106 from TomHarte/JoystickModifiers
Ensure no dangling modifiers upon shortcut keyboard switch.
2022-11-24 16:05:10 -05:00
Thomas Harte
8ccb803b08 Ensure no dangling modifiers upon shortcut keyboard switch. 2022-11-24 15:33:30 -05:00
Thomas Harte
7ce8326c8c Merge pull request #1105 from TomHarte/AppleIIActivity
Add an Apple II SCSI activity indicator.
2022-11-17 11:11:28 -05:00
Thomas Harte
28b4f51cb3 Add a SCSI activity indicator. 2022-11-16 11:31:10 -05:00
Thomas Harte
2fe6253ca8 Merge pull request #1104 from TomHarte/HDV
Add support for Apple II .HDV files.
2022-11-15 16:11:49 -05:00
Thomas Harte
e6ae35638b Add HDV to Info.plist. 2022-11-15 15:20:12 -05:00
Thomas Harte
812234f695 Route HDV files appropriately. 2022-11-15 15:10:04 -05:00
Thomas Harte
b921e893a2 Redirect relevant 2MG images to HDV. 2022-11-15 15:06:24 -05:00
Thomas Harte
9b235a8f64 Create a specific container for HDV files. 2022-11-15 13:18:54 -05:00
Thomas Harte
c8a82933bc Merge pull request #1099 from TomHarte/68020
Expand 68k decoder to the 68020.
2022-11-14 16:08:52 -05:00
Thomas Harte
5813e2b6c6 Round out the list of operand flags. 2022-11-14 15:58:58 -05:00
Thomas Harte
005f38dbff Merge branch 'master' into 68020 2022-11-11 20:35:35 -05:00
Thomas Harte
b77fb50e89 Merge pull request #1103 from TomHarte/snprintf
Remove usage of `sprintf`.
2022-11-11 20:35:22 -05:00
Thomas Harte
ae8f0d339e Remove usage of sprintf. 2022-11-11 20:29:59 -05:00
Thomas Harte
ccadf69630 Add test of operand_flags and operand_size; add entries for missing 68000 and 68010 instructions. 2022-10-31 15:15:05 -04:00
Thomas Harte
bbd2cd47ea Decode [MUL/DIV][U/S].l. 2022-10-30 11:32:36 -04:00
Thomas Harte
63ad2e8263 Decode EXTB.l. 2022-10-30 11:20:43 -04:00
Thomas Harte
23e4a47f8b Accept CHK.l and LINK.l decodings. 2022-10-30 11:16:32 -04:00
Thomas Harte
255d2f3486 Attempt LINK.l and CHK.l. 2022-10-29 21:42:53 -04:00
Thomas Harte
6ad1d74ddd Parse and record duality of CHK2/CMP2. 2022-10-29 21:32:48 -04:00
Thomas Harte
12ca79e645 Decode CAS2. 2022-10-28 14:02:49 -04:00
Thomas Harte
85df54ee7d Decode CAS. 2022-10-28 13:57:00 -04:00
Thomas Harte
2b220659dd Incorporate PACK and UNPK. 2022-10-28 13:37:30 -04:00
Thomas Harte
8a8c044976 Support up to 15 extension words on a Preinstruction; use that to describe PACK/UNPK.
TODO: reconcile when to use that field versus the ExtensionWord operand. Probably only when operands are full?
2022-10-28 13:36:40 -04:00
Thomas Harte
e79388fc02 Codify RTM, TST, TRAPcc, Bcc, BF*. 2022-10-28 13:17:35 -04:00
Thomas Harte
f6a72dc2b4 Switch BFEXTU and BFFFO. 2022-10-27 12:13:13 -04:00
Thomas Harte
041eb79bf8 Move 68010 up into the verified area. 2022-10-27 10:52:26 -04:00
Thomas Harte
7d82b2ad12 Fix PACK operation code. 2022-10-27 10:52:07 -04:00
Thomas Harte
c2b8cbfefc Add text conversions. 2022-10-27 10:12:52 -04:00
Thomas Harte
53140c016e Disable bitcodes for operations that aren't otherwise yet present. 2022-10-27 10:09:16 -04:00
Thomas Harte
adbd23eaea Having verified manually, lock in 68010 instruction set. 2022-10-27 09:55:02 -04:00
Thomas Harte
3f80df1feb Additional TST modes become available on the 68020. 2022-10-27 09:49:20 -04:00
Thomas Harte
cabf1a052c Fill in operand sizes and flags for the 68010 extensions. 2022-10-27 09:39:00 -04:00
Thomas Harte
8ff9f27b91 Decode MOVES. 2022-10-26 13:34:01 -04:00
Thomas Harte
ae2419e283 Decode MOVEC. 2022-10-26 12:50:15 -04:00
Thomas Harte
c1f0eed0a3 Decode MOVE from CCR. 2022-10-26 12:39:40 -04:00
Thomas Harte
4e5a80e23a Fix model tests. 2022-10-25 22:36:00 -04:00
Thomas Harte
46fee9c53a Add BKPT and RTD. 2022-10-25 22:35:44 -04:00
Thomas Harte
8ddf20b36a Provide cleaner output. 2022-10-25 22:33:25 -04:00
Thomas Harte
7ba6c78d14 MOVE from CCR, MOVEC and MOVES are on the 68010. 2022-10-25 21:27:23 -04:00
Thomas Harte
fd20323c25 Refactor to permit newer-chip testing. 2022-10-25 21:27:01 -04:00
Thomas Harte
83b9fc3318 Declare TRAPcc operand size. 2022-10-25 12:20:40 -04:00
Thomas Harte
1ceabb30b0 Fully decode TRAPcc. 2022-10-25 12:19:03 -04:00
Thomas Harte
f8cb3ca8b5 Resolve transient GCC warning. 2022-10-25 10:20:06 -04:00
Thomas Harte
d8a11eaba7 Avoid explicit specialisation in non-namespace scope. 2022-10-25 10:13:12 -04:00
Thomas Harte
ab37b00356 Add model constraint to DIVS.l. 2022-10-25 10:04:36 -04:00
Thomas Harte
b4fcf92a62 Output extension words as if immediates. 2022-10-25 09:58:01 -04:00
Thomas Harte
38c531fd5a Accept that a uint8_t isn't always going to be large enough; split decoding by minimum processor. 2022-10-25 09:50:19 -04:00
Thomas Harte
8c670d2105 Add decodes for TRAPcc and PACK, discovering it's three operand (sort of). 2022-10-23 11:46:47 -04:00
Thomas Harte
9a56d053f8 Introduce/extend 68k enums to cover 68020 instruction set. 2022-10-22 15:20:30 -04:00
Thomas Harte
79224d9383 Merge pull request #1098 from TomHarte/XcodeVersioning
Xcode: use built-in install build trigger for version script.
2022-10-21 15:29:54 -04:00
Thomas Harte
7c328edd4a Use built-in install build trigger. 2022-10-21 15:28:50 -04:00
Thomas Harte
cb0e259339 Start the process of decoding 68020 operations. 2022-10-21 15:28:29 -04:00
Thomas Harte
9be9e1ab0c Use built-in install build trigger. 2022-10-21 15:28:11 -04:00
Thomas Harte
149c940a29 Merge pull request #1097 from TomHarte/Templates
Tidy up 68000, primarily switching from macros to templates.
2022-10-19 22:31:28 -04:00
Thomas Harte
ec728ad573 Fix ADD/SUBX carry. 2022-10-19 22:17:51 -04:00
Thomas Harte
df7f94f362 Include MacintoshVolume in test build. 2022-10-19 14:41:08 -04:00
Thomas Harte
bc9ddacb8d Improve commentary. 2022-10-19 14:40:29 -04:00
Thomas Harte
979bf42541 Fix ASL overflow test. 2022-10-18 22:43:17 -04:00
Thomas Harte
d09473b66f Move common negative and zero logic into Status. 2022-10-18 14:51:51 -04:00
Thomas Harte
b31b4a5d10 Reformulate NOT in terms of EOR, and clean up elsewhere. 2022-10-18 12:17:55 -04:00
Thomas Harte
5560a0ed39 Fix overflow test for ASL. 2022-10-18 11:47:36 -04:00
Thomas Harte
a1ae7c28b2 Add various insurances against undefined behaviour. 2022-10-18 11:30:40 -04:00
Thomas Harte
a364499d17 Revert inadvertent commits. 2022-10-17 23:15:45 -04:00
Thomas Harte
fb2b7969a2 Add TODO to self on undefined behaviour. 2022-10-17 23:14:14 -04:00
Thomas Harte
abb19e6670 Populate carry whenever count != 0, regardless of modulo. 2022-10-17 22:57:21 -04:00
Thomas Harte
555250dbd9 Don't trample on X before use. 2022-10-17 22:19:35 -04:00
Thomas Harte
8148397f62 Fill in comments, eliminate u/s_extend16 macros. 2022-10-17 15:37:13 -04:00
Thomas Harte
f095bba1ca Eliminate bitwise macros. 2022-10-17 15:21:54 -04:00
Thomas Harte
ee3a3df0b5 Eliminate SBCD macro. 2022-10-17 15:12:38 -04:00
Thomas Harte
aff1caed15 Clean up formatting. 2022-10-17 15:05:23 -04:00
Thomas Harte
da03cd58c1 Add overt casting. 2022-10-17 15:04:28 -04:00
Thomas Harte
ce98ca4bdd Pull RO[L/R][X]m out of their macro stupor. 2022-10-17 11:27:04 -04:00
Thomas Harte
cc55f0586d Clean up ASL/ASR/LSL/LSRm. 2022-10-17 11:18:10 -04:00
Thomas Harte
47e8f3c0f1 Collapse [A/L]S[L/R].[bwl] into a template. 2022-10-16 22:21:20 -04:00
Thomas Harte
d5ceb934d2 Fix overflow flags, avoid bigger-word usage. 2022-10-16 21:52:00 -04:00
Thomas Harte
17c1e51231 Commute ROL/ROR to templates. 2022-10-16 12:19:09 -04:00
Thomas Harte
fee072b404 Commute ROXL and ROXR into a template. 2022-10-16 12:06:28 -04:00
Thomas Harte
0a9c392371 Remove unused bit_count. 2022-10-13 15:01:06 -04:00
Thomas Harte
06dbb7167b Unify TST. 2022-10-11 21:31:14 -04:00
Thomas Harte
eff9a09b9f Collapse MOVE and NEG[X] similarities. 2022-10-11 21:27:18 -04:00
Thomas Harte
1f19141746 Eliminate BiggerInt. 2022-10-11 16:19:47 -04:00
Thomas Harte
28093196b9 Convert DIVU/DIVS logic to a template. 2022-10-11 16:16:53 -04:00
Thomas Harte
eb206a08d9 Templatise MULU/MULS. 2022-10-11 16:02:20 -04:00
Thomas Harte
b2f005da1b Collapse SR/CCR bitwise operations into a template. 2022-10-11 15:53:11 -04:00
Thomas Harte
8305a3b46a Consolidate compare logic. 2022-10-11 12:57:02 -04:00
Thomas Harte
f3f23f90a3 Consolidate repetition in CLR. 2022-10-11 11:22:34 -04:00
Thomas Harte
77bc60bf86 Consolidate BCLR, BCHG and BSET into a macro. 2022-10-11 10:47:55 -04:00
Thomas Harte
ec5d57fefe Eliminate 64-bit work. 2022-10-11 10:33:28 -04:00
Thomas Harte
58396f0c52 Perform a prima facie conversion of ADD/SUB[/X] from macros to templates. 2022-10-10 22:21:13 -04:00
Thomas Harte
c0377f074f Merge pull request #1095 from TomHarte/XcodeUpdate
macOS: enable dead code stripping; allow Xcode 14 to tag.
2022-09-16 22:05:16 -04:00
Thomas Harte
4d3221fc55 Enable dead code stripping; allow Xcode 14 to tag. 2022-09-16 19:53:36 -04:00
Thomas Harte
59388230a6 Record new macOS version number. 2022-09-16 16:14:59 -04:00
Thomas Harte
1ba4363802 Merge pull request #1094 from TomHarte/AppleIIDecoding
Resolve off-by-one error in Apple II sector decoding.
2022-09-16 16:04:00 -04:00
Thomas Harte
d17fadbe0b Avoid off-by-one error in sector decoding.
Specific issue: retaining the 256 bytes up to _and including_ the checksum, rather than excluding.
2022-09-16 15:47:38 -04:00
Thomas Harte
9cba56237d Upgrade to constexpr. 2022-09-16 15:46:09 -04:00
Thomas Harte
ea9411b21c Breakup line, for easier debugging. 2022-09-16 15:43:23 -04:00
Thomas Harte
38e85a340a Merge pull request #1085 from TomHarte/AppleIISCSI
Support SCSI drives on the Apple II
2022-09-15 16:52:45 -04:00
Thomas Harte
fea8fecf11 Continue DMA requests if writing, even after a phase mismatch. 2022-09-15 16:46:22 -04:00
Thomas Harte
c4091a4cdb Fix address mapping, implement write. 2022-09-15 16:39:19 -04:00
Thomas Harte
d826532031 Read proper file contents. 2022-09-15 16:34:20 -04:00
Thomas Harte
beca7a01c2 Treat a phase mismatch as ending DMA. 2022-09-15 16:34:06 -04:00
Thomas Harte
2d8e260671 Take a shot at the phase mismatch IRQ. 2022-09-15 16:24:06 -04:00
Thomas Harte
04f5d29ed9 Improve logging, factor out phase_matches per TODO comment. 2022-09-15 16:14:14 -04:00
Thomas Harte
5ed60f9153 Mark get_state as const. 2022-09-15 16:13:54 -04:00
Thomas Harte
2f78a1c7af Add SCSI controller inclusion logic. 2022-09-15 12:17:50 -04:00
Thomas Harte
dc35ec8fa0 Merge branch 'master' into AppleIISCSI 2022-09-15 12:05:58 -04:00
Thomas Harte
7e3dbbbf0a Merge pull request #1093 from TomHarte/IIgsFill
Apple IIgs: better spell out shadowing logic.
2022-09-13 16:45:49 -04:00
Thomas Harte
0f017302ce Fix tests. 2022-09-13 16:33:44 -04:00
Thomas Harte
36c3cb1f70 Deal with pre-ROM03 case, now that it's easy. 2022-09-13 16:31:06 -04:00
Thomas Harte
6773a321c1 Switch to portable direct bitwise logic. 2022-09-13 16:02:49 -04:00
Thomas Harte
ffdf44ad4f Switch to overt use of std::fill. 2022-09-13 15:39:17 -04:00
Thomas Harte
cb7f1e42ff Merge pull request #1091 from TomHarte/65816DxDy
Fix 65816 direct, [x/y] addressing when E=1, DL != 0.
2022-09-09 16:57:10 -04:00
Thomas Harte
84a6c89a92 Merge pull request #1092 from TomHarte/68kWarning
Avoid returning without value in release builds.
2022-09-09 16:54:46 -04:00
Thomas Harte
451b730c8e Avoid returning without value in release builds. 2022-09-09 16:48:12 -04:00
Thomas Harte
98d3da62b5 Apply E mode wrap for d,x and d,y only when DL = 0. 2022-09-09 16:02:35 -04:00
Thomas Harte
45dc99fb9d Further improve exposition. 2022-09-09 15:48:25 -04:00
Thomas Harte
2edbbfbe37 Merge pull request #1090 from TomHarte/68000Tests
Add 68000 regression test generator.
2022-09-08 20:10:56 -04:00
Thomas Harte
dad1d7744e Disable test generation. 2022-09-08 16:41:10 -04:00
Thomas Harte
de8ce3380c Record only 8 bits for byte accesses. 2022-09-06 20:49:45 -04:00
Thomas Harte
b848b1389a Include gaps in captured transactions, better collect final RAM state. 2022-09-06 15:08:35 -04:00
Thomas Harte
2c44ddfa95 Better bucket, and attempt to cover exceptions. 2022-09-06 11:26:38 -04:00
Thomas Harte
72b6ab4389 Provide a route to operation that factors in addressing mode. 2022-09-06 11:26:16 -04:00
Thomas Harte
1a7509e860 Properly announce ::SameAddress. 2022-09-05 22:26:45 -04:00
Thomas Harte
0fe94b2e6d Capture ::SameAddress versus ::NewAddress, for TAS recognition. 2022-09-05 22:26:30 -04:00
Thomas Harte
93c1f7fc90 Include prefetch in 68000 state. 2022-09-05 22:00:04 -04:00
Thomas Harte
b6da1019bd Bucket tests by operation, aim for ~1,000,000 total. 2022-09-05 21:52:48 -04:00
Thomas Harte
effe8c102d Provide a direct to_string on Operation. 2022-09-05 21:52:20 -04:00
Thomas Harte
cee3f78059 Attempt to output only relevant RAM. 2022-09-03 15:45:06 -04:00
Thomas Harte
68f810883d Begin process of creating on-disk tests. 2022-09-02 16:52:27 -04:00
Thomas Harte
cbfd8e18e8 Eliminate repetitive magic constants. 2022-09-02 15:54:16 -04:00
Thomas Harte
8dc1aca67c Add TODO shout-outs. 2022-08-31 21:20:08 -04:00
Thomas Harte
acc82546c4 Further avoid use of null pointer. 2022-08-31 16:03:01 -04:00
Thomas Harte
df29a50738 Attempt to support the DMA interface. 2022-08-31 15:33:48 -04:00
Thomas Harte
d460f40b13 Improve comment.
Status: this now seems to be blocked at unimplemented 5380 functionality.
2022-08-30 16:44:03 -04:00
Thomas Harte
7996fe6dab 'Clock' the SCSI bus (i.e. make it aware of passing time). 2022-08-30 16:40:25 -04:00
Thomas Harte
f50ce7f137 Upgrade to an Enhanced IIe if hard drives are present. 2022-08-30 16:33:43 -04:00
Thomas Harte
6fa4e379d2 Make a hacky and blunt offer of drive data. 2022-08-30 16:07:44 -04:00
Thomas Harte
3c954e76ed Extend to allow vending of only portions of files. 2022-08-30 15:51:29 -04:00
Thomas Harte
cd7671e8fa Merge branch 'master' into AppleIISCSI 2022-08-29 11:47:48 -04:00
Thomas Harte
54ca168db5 Merge pull request #1089 from TomHarte/MetalPadding
Incorporate new additional padding.
2022-08-29 11:47:29 -04:00
Thomas Harte
330d852686 Adopt same format as the master. 2022-08-29 11:46:53 -04:00
Thomas Harte
303ea496f1 Incorporate new additional padding. 2022-08-29 11:45:11 -04:00
Thomas Harte
e7b213604e Add comments. 2022-08-29 11:40:18 -04:00
Thomas Harte
c3007bffc9 Merge pull request #1088 from icecream95/vertex-align
Align Scan to be a multiple of four bytes
2022-08-29 11:29:45 -04:00
Icecream95
0499dbd4cf Align Scan to be a multiple of four bytes
Some GPUs (e.g. r600) require the stride of vertex attributes to be a
multiple of four bytes, add two bytes of padding to the Scan struct to
meet this alignment requirement and reduce driver CPU overhead.
2022-08-29 16:32:25 +12:00
Thomas Harte
20d685ec5c Permit a mass-storage device to be returned, in theory. 2022-08-26 16:38:10 -04:00
Thomas Harte
4df2a29a1f Add storage to the bus. 2022-08-24 15:23:50 -04:00
Thomas Harte
722e3a141d Fix types, introduce Apple II mapper. 2022-08-24 12:00:03 -04:00
Thomas Harte
91e9248ecc Allow VolumeProviders to opt out of drivers completely. 2022-08-23 20:56:27 -04:00
Thomas Harte
22a3f4de2c Merge branch 'master' into AppleIISCSI 2022-08-23 20:00:02 -04:00
Thomas Harte
d57464a02a Merge pull request #1087 from TomHarte/RestoreCopyrightSymbol
macOS: tweak info box copyright text to include symbol and newline.
2022-08-23 19:59:39 -04:00
Thomas Harte
1346bf6fff Add include for strlen. 2022-08-23 19:58:48 -04:00
Thomas Harte
4ff2e7f546 Tweak info box copyright text: include symbol and newline. 2022-08-23 19:45:03 -04:00
Thomas Harte
cf356c59aa Switch the Macintosh mapper to use Apple::PartitionMap. 2022-08-23 19:39:47 -04:00
Thomas Harte
1555b51d99 Begin a stumbling effort to generalise my implementation of the Apple Partition Map. 2022-08-23 16:46:47 -04:00
Thomas Harte
64c5b84b8b Acknowledge that HFS is assumed. 2022-08-23 16:19:19 -04:00
Thomas Harte
017f55390a Better represent on-disk structure. 2022-08-23 16:19:04 -04:00
Thomas Harte
6010c971a1 Provide a volume to the SCSI card if one is received. 2022-08-23 15:11:56 -04:00
Thomas Harte
ea4bf5f31a Provide card's SCSI ID. 2022-08-23 15:05:36 -04:00
Thomas Harte
f4c242d5e9 Attempt to offer centralised C8 region decoding. 2022-08-23 14:50:44 -04:00
Thomas Harte
0595773355 Invents a new virtual select line for extended handling card ROM areas. 2022-08-23 14:41:45 -04:00
Thomas Harte
f89ca84902 Add missing include. 2022-08-22 21:44:33 -04:00
Thomas Harte
e9771ce540 Merge branch 'master' into AppleIISCSI 2022-08-22 21:43:39 -04:00
Thomas Harte
02f65cb7db Commit new macOS version number. 2022-08-22 21:39:45 -04:00
Thomas Harte
246bd5a6ac Merge branch 'master' into AppleIISCSI 2022-08-22 17:09:57 -04:00
Thomas Harte
1a68df6765 Merge pull request #1086 from TomHarte/AmigaCrash
Resolve crash of machines that require the ROM requester under macOS.
2022-08-22 17:07:59 -04:00
Thomas Harte
1b197d0bb2 Resolve crash of machines that require the ROM requester. 2022-08-22 17:02:09 -04:00
Thomas Harte
3c2d01451a Remove dead comment. 2022-08-22 17:01:52 -04:00
Thomas Harte
4c38fa8ad3 Resolve crash of machines that require the ROM requester. 2022-08-22 17:01:41 -04:00
Thomas Harte
c2c81162a1 Sketch out some of the easy stuff. 2022-08-22 16:48:51 -04:00
Thomas Harte
3d234147a6 Add in collected specs. 2022-08-22 10:22:19 -04:00
Thomas Harte
38a509bc20 Merge pull request #1082 from TomHarte/BlitterBugSearch
Partially enable serialised blitter.
2022-08-22 10:08:44 -04:00
Thomas Harte
f30f13f0bc Add overt include. 2022-08-22 10:03:24 -04:00
Thomas Harte
8e7f53751d Add Apple II SCSI ROM to the catalogue. 2022-08-21 22:03:52 -04:00
Thomas Harte
bfc77f1606 Add workaround that further isolates whatever bug Spindizzy reveals. 2022-08-19 16:38:42 -04:00
Thomas Harte
a6b8285d9c Factor out the blitter sequencer. 2022-08-19 16:38:15 -04:00
Thomas Harte
a675dd8c24 Merge branch 'master' into BlitterBugSearch 2022-08-19 16:18:23 -04:00
Thomas Harte
7d13768d51 Merge pull request #1083 from TomHarte/OptimisationFlags
Accept whatever Apple thinks is an appropriate optimisation level.
2022-08-19 16:17:55 -04:00
Thomas Harte
ce46ec4d3e Clean up, marginally. 2022-08-19 16:12:39 -04:00
Thomas Harte
1ffd65b7af Remove stray tab. 2022-08-19 16:12:20 -04:00
Thomas Harte
43c6db3610 Remove various other redundancies. 2022-08-19 16:12:05 -04:00
Thomas Harte
175314cd16 Accept whatever Apple thinks is an appropriate optimisation level. 2022-08-19 15:58:14 -04:00
Thomas Harte
837acdcf60 Experimentally decline immediate blits. 2022-08-16 21:51:13 -04:00
Thomas Harte
7289192130 Fix refresh slots: they're taken, not open. 2022-08-16 21:51:02 -04:00
Thomas Harte
e84e94ef61 Merge pull request #1078 from TomHarte/SerialisedBlitter
Moves towards proper serialisation of the Amiga Blitter.
2022-08-15 11:16:20 -04:00
Thomas Harte
bb54ac14b8 Prove that new output errors are [probably] external to the Blitter. 2022-08-15 11:10:17 -04:00
Thomas Harte
dcd66b93fd Merge branch 'SerialisedBlitter' of github.com:TomHarte/CLK into SerialisedBlitter 2022-08-15 10:54:43 -04:00
Thomas Harte
856e3d97bf Merge branch 'master' into SerialisedBlitter 2022-08-15 10:54:36 -04:00
Thomas Harte
c6aa83e5f2 Merge branch 'master' into SerialisedBlitter 2022-08-14 11:23:29 -04:00
Thomas Harte
1b19f93965 Merge pull request #1081 from TomHarte/STFAT12
Overtly treat .ST images as FAT12.
2022-08-13 10:20:08 -04:00
Thomas Harte
c7373a5d3e Overtly treat .ST images as FAT12. 2022-08-13 10:09:34 -04:00
Thomas Harte
fb83603133 Merge pull request #1080 from TomHarte/AtariSTRAM
Provide 1mb and 4mb options for Atari ST memory size.
2022-08-10 21:39:52 -04:00
Thomas Harte
94231ca3e3 Put word-sizing responsibility on the caller. 2022-08-10 16:41:45 -04:00
Thomas Harte
e2a8b26b57 Display properly from greater RAM sizes. 2022-08-10 16:36:11 -04:00
Thomas Harte
b6f45d9a90 Fix struct/class confusion. 2022-08-10 15:40:46 -04:00
Thomas Harte
69f92963f9 Add Atari ST RAM size to Qt UI. 2022-08-10 15:39:55 -04:00
Thomas Harte
6b001e3106 Add ST RAM size selection to the macOS UI. 2022-08-10 14:58:19 -04:00
Thomas Harte
6d1c954623 Make ST RAM size selectable, default to 1MB. 2022-08-10 12:00:06 -04:00
Thomas Harte
bdb35b6191 Add an easier hook for debugging. 2022-08-08 21:00:28 -04:00
Thomas Harte
892580c183 Clarify test. 2022-08-08 15:57:36 -04:00
Thomas Harte
4c90a4ec93 Remove 'Faulty peek' JSON breakages. 2022-08-08 15:22:18 -04:00
Thomas Harte
f58f7102f7 Provide more context when JSON decoding fails. 2022-08-08 15:18:03 -04:00
Thomas Harte
adf3405e6b Be overt about performance side effect. 2022-08-08 15:17:04 -04:00
Thomas Harte
8d34d9a06a Add missing paramter. 2022-08-08 11:01:07 -04:00
Thomas Harte
0d540fd211 Merge branch 'SerialisedBlitter' of github.com:TomHarte/CLK into SerialisedBlitter 2022-08-08 10:59:50 -04:00
Thomas Harte
025c79ca65 Factor out GZip shenanigans. 2022-08-08 10:52:55 -04:00
Thomas Harte
868d179132 Compress all Blitter logs. 2022-08-07 21:55:33 -04:00
Thomas Harte
cfccfd48e5 Allow for GZipped tests. 2022-08-07 21:53:19 -04:00
Thomas Harte
2f3dfdcc67 Add Spindizzy test. 2022-08-07 21:27:11 -04:00
Thomas Harte
d4b7d73fc4 Further reduces lines to one access per slot, max. 2022-08-07 19:19:00 -04:00
Thomas Harte
867769f6e7 Reduces line drawing to two accesses per slot.
Still a fiction, but a better one.
2022-08-07 19:15:03 -04:00
Thomas Harte
7f423e39ed Resolve type warning. 2022-08-07 19:03:56 -04:00
Thomas Harte
e6505dc985 Recognise that some of these traces don't capture all bus transactions. 2022-08-07 19:03:14 -04:00
Thomas Harte
bcdb2d135d Remove partially-captured head. 2022-08-06 22:35:18 -04:00
Thomas Harte
c5d1cffad2 Include bus activity. 2022-08-06 22:21:02 -04:00
Thomas Harte
54b4a0771d Provide better exposition. 2022-08-06 21:52:26 -04:00
Thomas Harte
85f75ab1f3 Introduce Addams Family test case. 2022-08-06 21:47:36 -04:00
Thomas Harte
668332f6c7 Any one failure will do. 2022-08-06 14:59:13 -04:00
Thomas Harte
021ddb3565 Ensure pipeline is fully flushed before registers are accessed. 2022-08-06 14:55:31 -04:00
Thomas Harte
6981bc8a82 Add yet more context. 2022-08-06 14:47:24 -04:00
Thomas Harte
7030646671 Avoid infinite loop. 2022-08-06 14:42:09 -04:00
Thomas Harte
3781b5eb0e Provide further context. 2022-08-06 14:40:12 -04:00
Thomas Harte
e897cd99f9 Fix transcription of write. 2022-08-06 10:11:26 -04:00
Thomas Harte
cc9b6bbc61 Stop after a first mismatch. 2022-08-06 10:10:19 -04:00
Thomas Harte
318cea4ccd Attempt a full bus-transaction comparison. 2022-08-06 10:06:49 -04:00
Thomas Harte
45892f3584 Add optional transaction records to the Blitter. 2022-08-06 09:51:20 -04:00
Thomas Harte
612413cb1c Remove redundant state. 2022-08-04 10:06:14 -04:00
Thomas Harte
511ec5a736 Apply modulos at end of final line.
Possibly I need to rethink the sequence logic?
2022-07-30 21:35:26 -04:00
Thomas Harte
4fb9dec381 Fix use of bool. 2022-07-30 21:02:44 -04:00
Thomas Harte
82476bdabe Avoid 'complete' repetition. 2022-07-30 21:02:04 -04:00
Thomas Harte
58ee8e2460 Minor tidy-up. No fixes. 2022-07-30 21:00:50 -04:00
Thomas Harte
94a90b7a89 Attempt a real slot-by-slot blit. 2022-07-30 20:34:37 -04:00
Thomas Harte
5d992758f8 Ensure blitter with all flags disabled terminates. 2022-07-30 20:13:37 -04:00
Thomas Harte
27b8c29096 Apply modulos at end of line, not beginning. 2022-07-30 10:27:53 -04:00
Thomas Harte
93d2a612ee Add an explicit flush-pipeline step; some tests now pass. 2022-07-29 16:33:46 -04:00
Thomas Harte
03d4960a03 Begin a full-synchronous usage of the sequencer, at least exposing poor handling of the pipeline. 2022-07-29 16:15:18 -04:00
Thomas Harte
1ac0a4e924 Provide a loop count directly from the sequencer.
This avoids the caller having to take a guess at iterations.
2022-07-29 12:14:59 -04:00
Thomas Harte
d85d70a133 Add documentation, formal begin function. 2022-07-26 22:01:43 -04:00
Thomas Harte
76979c8059 Add missing tests. 2022-07-26 21:47:02 -04:00
Thomas Harte
86246e4f45 Introduce partial Blitter sequencer test. 2022-07-26 21:28:12 -04:00
Thomas Harte
2c95dea4db Introduce putative blitter sequencer. 2022-07-26 17:05:05 -04:00
Thomas Harte
804c12034c Apply blitter priority bit. 2022-07-26 16:07:26 -04:00
Thomas Harte
ce7f57f251 Switch to regular integer types for flags. 2022-07-26 09:22:05 -04:00
Thomas Harte
af7c56d313 Merge pull request #1077 from TomHarte/BroaderChroma
macOS: Use softer-edged luminance.
2022-07-25 13:24:42 -04:00
Thomas Harte
3e4044c7a0 Use softer-edged luminance. 2022-07-25 13:24:08 -04:00
Thomas Harte
88a22fdbf8 Merge pull request #1076 from TomHarte/DatedVersioning
Ensure macOS releases contain an updated version number.
2022-07-25 13:18:17 -04:00
Thomas Harte
146e739390 $ACTION seems to be the thing outside of Xcode Cloud. 2022-07-25 13:16:28 -04:00
Thomas Harte
f204162986 Use valid version numbers, only for archive builds. 2022-07-25 10:33:15 -04:00
Thomas Harte
8679854c91 Update copyright year, use valid version numbers. 2022-07-25 10:21:25 -04:00
Thomas Harte
0383d0333e Add build date (i.e. version) into Info.plist. 2022-07-25 10:15:48 -04:00
Thomas Harte
eb0b6e9df9 Merge pull request #1075 from TomHarte/PlayfieldMasking
Add comments, fix playfield sprite masking.
2022-07-22 21:20:50 -04:00
Thomas Harte
426eb0f79b Add comments, fix playfield sprite masking. 2022-07-22 17:01:38 -04:00
Thomas Harte
0b2d92048d Merge pull request #1074 from TomHarte/SpriteContinuity
Reinstate assumption of no Amiga sprite fetches in vertical blank.
2022-07-21 08:48:17 -04:00
Thomas Harte
6beca141d5 Reinstate assumption of no sprites in vertical blank. 2022-07-21 08:41:50 -04:00
Thomas Harte
b67790df7d Merge pull request #1073 from TomHarte/AmigaSprites
Improve Amiga sprite emulation.
2022-07-20 13:53:50 -04:00
Thomas Harte
f29d305597 Add missing #include. 2022-07-19 21:40:16 -04:00
Thomas Harte
89abf7faeb Take a guess at reintroducing a special case for end-of-blank. 2022-07-19 21:25:34 -04:00
Thomas Harte
57186c3c14 Don't limit sprite fetch area; add further commentary. 2022-07-19 16:37:13 -04:00
Thomas Harte
feee6afe0f Improve documentation. 2022-07-19 16:19:19 -04:00
Thomas Harte
cb42ee3ade Eliminate DMAState; it sounds like VSTOP solves this problem. 2022-07-19 16:11:29 -04:00
Thomas Harte
830704b4a9 Clarify and slightly improve state machine.
No more using the visible flag to permit a DMA control fetch.
2022-07-19 15:39:57 -04:00
Thomas Harte
0c6d7e07ee Merge pull request #1072 from TomHarte/BetterAppDelegate
Eliminate purposeless AppDelegate instance storage.
2022-07-18 10:15:25 -04:00
Thomas Harte
b28a3ebb4d Eliminate purposeless instance storage. 2022-07-18 09:35:38 -04:00
Thomas Harte
6579c12053 Merge pull request #1071 from TomHarte/EverSharper
macOS: Accept and embrace limits of composite sharpening.
2022-07-18 09:25:30 -04:00
Thomas Harte
28a7dc194c Increase saturation. 2022-07-17 22:01:30 -04:00
Thomas Harte
a943a0b59a Make sharpening slightly more aggressive. 2022-07-17 19:22:09 -04:00
Thomas Harte
80bc530d17 Merge pull request #1070 from TomHarte/ConcurrencyProjectFiles
Remove concurrency/*.cpp from various project files.
2022-07-17 14:46:27 -04:00
Thomas Harte
68480530fe Remove refernce to .cpp Concurrency files from Qt. 2022-07-17 14:39:15 -04:00
Thomas Harte
eadfa71b49 Remove refernce to .cpp Concurrency files from SDL. 2022-07-17 14:38:42 -04:00
Thomas Harte
9c43470c43 Merge pull request #1069 from TomHarte/AsyncTaskQueueRename
Switch name back to emphasise _async_.
2022-07-16 14:50:59 -04:00
Thomas Harte
8f2e94a1d8 Switch name back to emphasise _async_. 2022-07-16 14:41:04 -04:00
Thomas Harte
52c3e0592a Merge pull request #1068 from TomHarte/HAM
HAM: correct red/blue confusion.
2022-07-15 16:51:09 -04:00
Thomas Harte
637161157c Switch to slightly more sensical 'none' type. 2022-07-15 16:29:29 -04:00
Thomas Harte
76d5e53094 Fix red/blue confusion. 2022-07-15 16:24:07 -04:00
Thomas Harte
b6f40fdcc7 Merge pull request #1067 from TomHarte/MachineLeak
macOS: Avoid likely leak of machines.
2022-07-15 15:36:21 -04:00
Thomas Harte
3de1e762b7 Avoid retain cycles. 2022-07-15 15:22:12 -04:00
Thomas Harte
ee7ef81054 Avoid potential attempt to free enqueued buffers at dealloc. 2022-07-15 15:21:58 -04:00
Thomas Harte
bae47fca20 Free buffers before disposing of queue. 2022-07-15 15:13:21 -04:00
Thomas Harte
41af76bed8 Fix variable name. 2022-07-15 15:13:03 -04:00
Thomas Harte
a7515fe156 Merge pull request #1066 from TomHarte/AudioAssert
macOS: Fix stereo buffering, various audio asserts.
2022-07-15 14:44:55 -04:00
Thomas Harte
60f997a52c Fix stereo buffering, various audio asserts. 2022-07-14 21:59:40 -04:00
Thomas Harte
f465fe65f4 Merge pull request #1061 from TomHarte/MacintoshPixels
Microtweak: simplify Macintosh pixel serialisation.
2022-07-14 18:54:10 -04:00
Thomas Harte
3d6ce6c13f Merge pull request #1065 from TomHarte/QueueShakeup
Consolidate/simplify queue classes.
2022-07-14 18:53:59 -04:00
Thomas Harte
bf03bda314 Generalise AsyncTaskQueue, DeferringAsyncTaskQueue and AsyncUpdater into a single template. 2022-07-14 16:39:26 -04:00
Thomas Harte
126838e7c7 Thanks to std::swap and move semantics, there's no need for indirection here. 2022-07-14 15:52:31 -04:00
Thomas Harte
8310b40812 Merge pull request #1064 from TomHarte/FewerAudioAllocations
macOS: perform audio buffer allocations ahead of time.
2022-07-14 14:58:51 -04:00
Thomas Harte
9133e25a7b Allocate buffers once, ahead of time, and reuse. 2022-07-14 14:44:10 -04:00
Thomas Harte
ddfc2e4ca4 Provide sample length ahead of time. 2022-07-14 14:34:11 -04:00
Thomas Harte
5aa129fbd3 Merge pull request #1063 from TomHarte/EventDriven
Switch macOS to an event-driven emulation.
2022-07-14 11:37:20 -04:00
Thomas Harte
18f01bcd48 Switch back to std::list as a kneejerk fix for AsyncTaskQueue. 2022-07-13 22:26:59 -04:00
Thomas Harte
4c031bd335 Don't use kAudioQueueProperty_IsRunning as it seems not to be trustworthy. 2022-07-13 22:22:19 -04:00
Thomas Harte
79f8cab5e2 Attempt to reduce memory allocations. 2022-07-13 21:41:04 -04:00
Thomas Harte
92efad4970 Switch to std::vector. 2022-07-13 21:36:01 -04:00
Thomas Harte
6a509c1280 Improve comments, marginally reduce dynamic_casting. 2022-07-13 18:36:40 -04:00
Thomas Harte
dcb68c16fe Eliminate AudioQueueBufferMaxLength. 2022-07-13 15:24:43 -04:00
Thomas Harte
75f3f1a77f Avoid the whole thread hop for a zero-length run_for. 2022-07-13 15:05:34 -04:00
Thomas Harte
10108303e7 Eliminate AudioQueueStop, which is very slow, use AudioQueueStart only as required. 2022-07-13 15:04:58 -04:00
Thomas Harte
b7ad94c676 Attempt to get a bit more rigorous in diagnosing queue stoppages. 2022-07-12 21:43:33 -04:00
Thomas Harte
1c537a877e Remove unnecessary lock. 2022-07-12 16:22:19 -04:00
Thomas Harte
0270997acd Add insurance against calls before setup. 2022-07-12 16:03:09 -04:00
Thomas Harte
4b9d92929a Tweak logic. 2022-07-12 16:02:30 -04:00
Thomas Harte
5b69324ee9 Tidy up comments. 2022-07-12 15:58:16 -04:00
Thomas Harte
cce449ba8f Merge branch 'master' into EventDriven 2022-07-12 15:06:52 -04:00
Thomas Harte
df15d60b9e Switch to AudioQueueNewOutputWithDispatchQueue, reducing runloop contention. 2022-07-12 15:03:35 -04:00
Thomas Harte
a0e01d4c34 Add overt flushes to the SDL target. 2022-07-12 11:03:58 -04:00
Thomas Harte
59da143e6a Add overt flushes to the SDL target. 2022-07-12 10:57:22 -04:00
Thomas Harte
4ddbf095f3 Fully banish flush from the processors. 2022-07-12 10:49:53 -04:00
Thomas Harte
4e9ae65459 Reintroduce sync matching. 2022-07-12 09:56:13 -04:00
Thomas Harte
d16dc3a5d7 Move limit up to 20fps. 2022-07-12 07:45:07 -04:00
Thomas Harte
a1544f3033 Do a better job of keeping the queue populated. 2022-07-11 20:50:02 -04:00
Thomas Harte
f2fb9cf596 Avoid unnecessary queue jump. 2022-07-10 21:35:05 -04:00
Thomas Harte
6dabdaca45 Switch to int; attempt to do a better job of initial audio filling. 2022-07-09 13:33:46 -04:00
Thomas Harte
51ed3f2ed0 Reduce modal-related thread hopping. 2022-07-09 13:03:45 -04:00
Thomas Harte
b097b1296b Adopt granular flushing widely. 2022-07-08 16:04:32 -04:00
Thomas Harte
b03d91d5dd Permit granular specification of what to flush. 2022-07-08 15:38:29 -04:00
Thomas Harte
cf5c6c2144 Merge pull request #1062 from TomHarte/6502BRK
Correct 6502 for switched BRK presumption.
2022-07-08 11:24:23 -04:00
Thomas Harte
3a2d27a636 Correct for switched BRK presumption. 2022-07-08 11:15:48 -04:00
Thomas Harte
5c3084c37c Fix construction order. 2022-07-07 20:09:37 -04:00
Thomas Harte
07ce0f0133 Attempt safe shutdown. 2022-07-07 16:56:10 -04:00
Thomas Harte
96189bde4b Loop the Master System into the experiment. 2022-07-07 16:46:08 -04:00
Thomas Harte
fc0dc4e5e2 Amiga only, temporarily: attempt to reduce audio maintenance costs. 2022-07-07 16:41:49 -04:00
Thomas Harte
3e2a6ef3f4 Hacks up an [unsafe] return to something best-effort-updater-esque.
For profiling, mainly.
2022-07-07 16:35:45 -04:00
Thomas Harte
01a309909b Elide actions when running behind. 2022-07-07 11:10:54 -04:00
Thomas Harte
7886c2df7a Start experimenting with a more event-based approach to timing. 2022-07-07 10:48:42 -04:00
Thomas Harte
18735ee571 Merge pull request #1060 from TomHarte/QtErrors
Resolve invalid use of `constexpr` in IPF.cpp.
2022-07-05 17:09:05 -04:00
Thomas Harte
1ce07e2ee8 This reads the file, so it can't be constexpr. 2022-07-05 17:01:38 -04:00
Thomas Harte
5495f30329 Microtweak: simplify Macintosh pixel serialisation. 2022-06-06 08:34:58 -04:00
495 changed files with 147021 additions and 2840315 deletions

View File

@@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install dependencies
run: sudo apt-get --allow-releaseinfo-change update && sudo apt-get --fix-missing install libsdl2-dev scons
- name: Make

View File

@@ -11,8 +11,7 @@
#include "ConfidenceSource.hpp"
namespace Analyser {
namespace Dynamic {
namespace Analyser::Dynamic {
/*!
Provides a confidence source that calculates its probability by virtual of a history of events.
@@ -41,7 +40,6 @@ class ConfidenceCounter: public ConfidenceSource {
int misses_ = 1;
};
}
}
#endif /* ConfidenceCounter_hpp */

View File

@@ -9,8 +9,7 @@
#ifndef ConfidenceSource_hpp
#define ConfidenceSource_hpp
namespace Analyser {
namespace Dynamic {
namespace Analyser::Dynamic {
/*!
Provides an abstract interface through which objects can declare the probability
@@ -22,7 +21,6 @@ struct ConfidenceSource {
virtual float get_confidence() = 0;
};
}
}
#endif /* ConfidenceSource_hpp */

View File

@@ -13,8 +13,7 @@
#include <vector>
namespace Analyser {
namespace Dynamic {
namespace Analyser::Dynamic {
/*!
Summaries a collection of confidence sources by calculating their weighted sum.
@@ -40,7 +39,6 @@ class ConfidenceSummary: public ConfidenceSource {
float weight_sum_;
};
}
}
#endif /* ConfidenceSummary_hpp */

View File

@@ -15,8 +15,7 @@
#include <memory>
#include <vector>
namespace Analyser {
namespace Dynamic {
namespace Analyser::Dynamic {
/*!
Provides a class that multiplexes the configurable interface to multiple machines.
@@ -36,7 +35,6 @@ class MultiConfigurable: public Configurable::Device {
std::vector<Configurable::Device *> devices_;
};
}
}
#endif /* MultiConfigurable_hpp */

View File

@@ -14,8 +14,7 @@
#include <memory>
#include <vector>
namespace Analyser {
namespace Dynamic {
namespace Analyser::Dynamic {
/*!
Provides a class that multiplexes the joystick machine interface to multiple machines.
@@ -34,7 +33,6 @@ class MultiJoystickMachine: public MachineTypes::JoystickMachine {
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
};
}
}
#endif /* MultiJoystickMachine_hpp */

View File

@@ -15,8 +15,7 @@
#include <memory>
#include <vector>
namespace Analyser {
namespace Dynamic {
namespace Analyser::Dynamic {
/*!
Provides a class that multiplexes the keyboard machine interface to multiple machines.
@@ -55,7 +54,6 @@ class MultiKeyboardMachine: public MachineTypes::KeyboardMachine {
Inputs::Keyboard &get_keyboard() final;
};
}
}
#endif /* MultiKeyboardMachine_hpp */

View File

@@ -15,8 +15,7 @@
#include <memory>
#include <vector>
namespace Analyser {
namespace Dynamic {
namespace Analyser::Dynamic {
/*!
Provides a class that multiplexes the media target interface to multiple machines.
@@ -35,7 +34,6 @@ struct MultiMediaTarget: public MachineTypes::MediaTarget {
std::vector<MachineTypes::MediaTarget *> targets_;
};
}
}
#endif /* MultiMediaTarget_hpp */

View File

@@ -19,8 +19,7 @@
#include <mutex>
#include <vector>
namespace Analyser {
namespace Dynamic {
namespace Analyser::Dynamic {
template <typename MachineType> class MultiInterface {
public:
@@ -47,7 +46,7 @@ template <typename MachineType> class MultiInterface {
std::recursive_mutex &machines_mutex_;
private:
std::vector<Concurrency::AsyncTaskQueue> queues_;
std::vector<Concurrency::AsyncTaskQueue<true>> queues_;
};
class MultiTimedMachine: public MultiInterface<MachineTypes::TimedMachine>, public MachineTypes::TimedMachine {
@@ -116,7 +115,5 @@ class MultiAudioProducer: public MultiInterface<MachineTypes::AudioProducer>, pu
*/
}
}
#endif /* MultiProducer_hpp */

View File

@@ -16,8 +16,7 @@
#include <mutex>
#include <vector>
namespace Analyser {
namespace Dynamic {
namespace Analyser::Dynamic {
/*!
Provides a class that multiplexes calls to and from Outputs::Speaker::Speaker in order
@@ -55,7 +54,6 @@ class MultiSpeaker: public Outputs::Speaker::Speaker, Outputs::Speaker::Speaker:
bool stereo_output_ = false;
};
}
}
#endif /* MultiSpeaker_hpp */

View File

@@ -22,8 +22,7 @@
#include <mutex>
#include <vector>
namespace Analyser {
namespace Dynamic {
namespace Analyser::Dynamic {
/*!
Provides the same interface as to a single machine, while multiplexing all
@@ -80,7 +79,6 @@ class MultiMachine: public ::Machine::DynamicMachine, public MultiTimedMachine::
bool has_picked_ = false;
};
}
}
#endif /* MultiMachine_hpp */

View File

@@ -12,9 +12,7 @@
#include "File.hpp"
#include "../../../Storage/Disk/Disk.hpp"
namespace Analyser {
namespace Static {
namespace Acorn {
namespace Analyser::Static::Acorn {
/// Describes a DFS- or ADFS-format catalogue(/directory): the list of files available and the catalogue's boot option.
struct Catalogue {
@@ -31,8 +29,6 @@ struct Catalogue {
std::unique_ptr<Catalogue> GetDFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk);
std::unique_ptr<Catalogue> GetADFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk);
}
}
}
#endif /* Disk_hpp */

View File

@@ -13,9 +13,7 @@
#include <string>
#include <vector>
namespace Analyser {
namespace Static {
namespace Acorn {
namespace Analyser::Static::Acorn {
struct File {
std::string name;
@@ -60,8 +58,6 @@ struct File {
std::vector<Chunk> chunks;
};
}
}
}
#endif /* File_hpp */

View File

@@ -13,14 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Acorn {
namespace Analyser::Static::Acorn {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* AcornAnalyser_hpp */

View File

@@ -14,14 +14,10 @@
#include "File.hpp"
#include "../../../Storage/Tape/Tape.hpp"
namespace Analyser {
namespace Static {
namespace Acorn {
namespace Analyser::Static::Acorn {
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
}
}
}
#endif /* Tape_hpp */

View File

@@ -13,9 +13,7 @@
#include "../StaticAnalyser.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Acorn {
namespace Analyser::Static::Acorn {
struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<Target> {
bool has_acorn_adfs = false;
@@ -37,8 +35,6 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
}
};
}
}
}
#endif /* Analyser_Static_Acorn_Target_h */

View File

@@ -13,15 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Amiga {
namespace Analyser::Static::Amiga {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* Analyser_Static_Amiga_StaticAnalyser_hpp */

View File

@@ -12,9 +12,7 @@
#include "../../../Reflection/Struct.hpp"
#include "../StaticAnalyser.hpp"
namespace Analyser {
namespace Static {
namespace Amiga {
namespace Analyser::Static::Amiga {
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
ReflectableEnum(ChipRAM,
@@ -41,8 +39,6 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
}
};
}
}
}
#endif /* Analyser_Static_Amiga_Target_h */

View File

@@ -13,14 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace AmstradCPC {
namespace Analyser::Static::AmstradCPC {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* Analyser_Static_AmstradCPC_StaticAnalyser_hpp */

View File

@@ -14,9 +14,7 @@
#include "../StaticAnalyser.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace AmstradCPC {
namespace Analyser::Static::AmstradCPC {
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
ReflectableEnum(Model, CPC464, CPC664, CPC6128);
@@ -32,8 +30,5 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
};
}
}
}
#endif /* Analyser_Static_AmstradCPC_Target_h */

View File

@@ -13,8 +13,17 @@ Analyser::Static::TargetList Analyser::Static::AppleII::GetTargets(const Media &
auto target = std::make_unique<Target>();
target->media = media;
if(!target->media.disks.empty())
// If any disks are present, attach a Disk II.
if(!target->media.disks.empty()) {
target->disk_controller = Target::DiskController::SixteenSector;
}
// The emulated SCSI card requires a IIe, so upgrade to that if
// any mass storage is present.
if(!target->media.mass_storage_devices.empty()) {
target->model = Target::Model::EnhancedIIe;
target->scsi_controller = Target::SCSIController::AppleSCSI;
}
TargetList targets;
targets.push_back(std::move(target));

View File

@@ -13,14 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace AppleII {
namespace Analyser::Static::AppleII {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* Analyser_Static_AppleII_StaticAnalyser_hpp */

View File

@@ -13,9 +13,7 @@
#include "../../../Reflection/Struct.hpp"
#include "../StaticAnalyser.hpp"
namespace Analyser {
namespace Static {
namespace AppleII {
namespace Analyser::Static::AppleII {
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
ReflectableEnum(Model,
@@ -29,22 +27,28 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
SixteenSector,
ThirteenSector
);
ReflectableEnum(SCSIController,
None,
AppleSCSI
);
Model model = Model::IIe;
DiskController disk_controller = DiskController::None;
SCSIController scsi_controller = SCSIController::None;
Target() : Analyser::Static::Target(Machine::AppleII) {
if(needs_declare()) {
DeclareField(model);
DeclareField(disk_controller);
DeclareField(scsi_controller);
AnnounceEnum(Model);
AnnounceEnum(DiskController);
AnnounceEnum(SCSIController);
}
}
};
}
}
}
#endif /* Analyser_Static_AppleII_Target_h */

View File

@@ -13,14 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace AppleIIgs {
namespace Analyser::Static::AppleIIgs {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* Analyser_Static_AppleIIgs_StaticAnalyser_hpp */

View File

@@ -13,9 +13,7 @@
#include "../../../Reflection/Struct.hpp"
#include "../StaticAnalyser.hpp"
namespace Analyser {
namespace Static {
namespace AppleIIgs {
namespace Analyser::Static::AppleIIgs {
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
ReflectableEnum(Model,
@@ -42,8 +40,6 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
}
};
}
}
}
#endif /* Analyser_Static_AppleIIgs_Target_h */

View File

@@ -13,14 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Atari2600 {
namespace Analyser::Static::Atari2600 {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* StaticAnalyser_hpp */

View File

@@ -11,9 +11,7 @@
#include "../StaticAnalyser.hpp"
namespace Analyser {
namespace Static {
namespace Atari2600 {
namespace Analyser::Static::Atari2600 {
struct Target: public ::Analyser::Static::Target {
enum class PagingModel {
@@ -38,8 +36,6 @@ struct Target: public ::Analyser::Static::Target {
Target() : Analyser::Static::Target(Machine::Atari2600) {}
};
}
}
}
#endif /* Analyser_Static_Atari_Target_h */

View File

@@ -13,15 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace AtariST {
namespace Analyser::Static::AtariST {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* Analyser_Static_AtariST_StaticAnalyser_hpp */

View File

@@ -12,16 +12,23 @@
#include "../../../Reflection/Struct.hpp"
#include "../StaticAnalyser.hpp"
namespace Analyser {
namespace Static {
namespace AtariST {
namespace Analyser::Static::AtariST {
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
Target() : Analyser::Static::Target(Machine::AtariST) {}
ReflectableEnum(MemorySize,
FiveHundredAndTwelveKilobytes,
OneMegabyte,
FourMegabytes);
MemorySize memory_size = MemorySize::OneMegabyte;
Target() : Analyser::Static::Target(Machine::AtariST) {
if(needs_declare()) {
DeclareField(memory_size);
AnnounceEnum(MemorySize);
}
}
};
}
}
}
#endif /* Analyser_Static_AtariST_Target_h */

View File

@@ -13,14 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Coleco {
namespace Analyser::Static::Coleco {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* StaticAnalyser_hpp */

View File

@@ -14,14 +14,10 @@
#include <vector>
namespace Analyser {
namespace Static {
namespace Commodore {
namespace Analyser::Static::Commodore {
std::vector<File> GetFiles(const std::shared_ptr<Storage::Disk::Disk> &disk);
}
}
}
#endif /* Disk_hpp */

View File

@@ -12,9 +12,7 @@
#include <string>
#include <vector>
namespace Analyser {
namespace Static {
namespace Commodore {
namespace Analyser::Static::Commodore {
struct File {
std::wstring name;
@@ -35,8 +33,6 @@ struct File {
bool is_basic();
};
}
}
}
#endif /* File_hpp */

View File

@@ -13,14 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Commodore {
namespace Analyser::Static::Commodore {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* CommodoreAnalyser_hpp */

View File

@@ -12,14 +12,10 @@
#include "../../../Storage/Tape/Tape.hpp"
#include "File.hpp"
namespace Analyser {
namespace Static {
namespace Commodore {
namespace Analyser::Static::Commodore {
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
}
}
}
#endif /* Tape_hpp */

View File

@@ -14,9 +14,7 @@
#include "../StaticAnalyser.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Commodore {
namespace Analyser::Static::Commodore {
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
enum class MemoryModel {
@@ -71,8 +69,6 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
}
};
}
}
}
#endif /* Analyser_Static_Commodore_Target_h */

View File

@@ -16,9 +16,7 @@
#include <set>
#include <vector>
namespace Analyser {
namespace Static {
namespace MOS6502 {
namespace Analyser::Static::MOS6502 {
/*!
Describes a 6502 instruciton: its address, the operation it performs, its addressing mode
@@ -95,7 +93,5 @@ Disassembly Disassemble(
std::vector<uint16_t> entry_points);
}
}
}
#endif /* Disassembler6502_hpp */

View File

@@ -11,9 +11,7 @@
#include <functional>
namespace Analyser {
namespace Static {
namespace Disassembler {
namespace Analyser::Static::Disassembler {
/*!
Provides an address mapper that relocates a chunk of memory so that it starts at
@@ -25,8 +23,6 @@ template <typename T> std::function<std::size_t(T)> OffsetMapper(T start_address
};
}
}
}
}
#endif /* AddressMapper_hpp */

View File

@@ -9,9 +9,7 @@
#ifndef Kernel_hpp
#define Kernel_hpp
namespace Analyser {
namespace Static {
namespace Disassembly {
namespace Analyser::Static::Disassembly {
template <typename D, typename S> struct PartialDisassembly {
D disassembly;
@@ -45,8 +43,6 @@ template <typename D, typename S, typename Disassembler> D Disassemble(
return partial_disassembly.disassembly;
}
}
}
}
#endif /* Kernel_hpp */

View File

@@ -15,9 +15,7 @@
#include <set>
#include <vector>
namespace Analyser {
namespace Static {
namespace Z80 {
namespace Analyser::Static::Z80 {
struct Instruction {
/*! The address this instruction starts at. This is a mapped address. */
@@ -84,7 +82,5 @@ Disassembly Disassemble(
std::vector<uint16_t> entry_points);
}
}
}
#endif /* StaticAnalyser_Disassembler_Z80_hpp */

View File

@@ -13,15 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace DiskII {
namespace Analyser::Static::DiskII {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* Analyser_Static_DiskII_StaticAnalyser_hpp */

View File

@@ -13,15 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Enterprise {
namespace Analyser::Static::Enterprise {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* Analyser_Static_Enterprise_StaticAnalyser_hpp */

View File

@@ -15,9 +15,7 @@
#include <string>
namespace Analyser {
namespace Static {
namespace Enterprise {
namespace Analyser::Static::Enterprise {
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
ReflectableEnum(Model, Enterprise64, Enterprise128, Enterprise256);
@@ -50,8 +48,6 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
}
};
}
}
}
#endif /* Analyser_Static_Enterprise_Target_h */

View File

@@ -11,9 +11,7 @@
#include "../../../Storage/Cartridge/Cartridge.hpp"
namespace Analyser {
namespace Static {
namespace MSX {
namespace Analyser::Static::MSX {
/*!
Extends the base cartridge class by adding a (guess at) the banking scheme.
@@ -33,8 +31,6 @@ struct Cartridge: public ::Storage::Cartridge::Cartridge {
Storage::Cartridge::Cartridge(segments), type(type) {}
};
}
}
}
#endif /* Cartridge_hpp */

View File

@@ -37,6 +37,11 @@ static std::unique_ptr<Analyser::Static::Target> CartridgeTarget(
auto target = std::make_unique<Analyser::Static::MSX::Target>();
target->confidence = confidence;
// Observation: all ROMs of 48kb or less are from the MSX 1 era.
if(segment.data.size() < 48*1024) {
target->model = Analyser::Static::MSX::Target::Model::MSX1;
}
if(type == Analyser::Static::MSX::Cartridge::Type::None) {
target->media.cartridges.emplace_back(new Storage::Cartridge::Cartridge(output_segments));
} else {
@@ -100,6 +105,7 @@ static Analyser::Static::TargetList CartridgeTargetsFrom(
// TODO: check for a rational init address?
// If this ROM is less than 48kb in size then it's an ordinary ROM. Just emplace it and move on.
// Bonus observation: all such ROMs are from the MSX 1 era.
if(data_size <= 0xc000) {
targets.emplace_back(CartridgeTarget(segment, start_address, Analyser::Static::MSX::Cartridge::Type::None, 1.0));
continue;

View File

@@ -13,14 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace MSX {
namespace Analyser::Static::MSX {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* StaticAnalyser_MSX_StaticAnalyser_hpp */

View File

@@ -14,9 +14,7 @@
#include <string>
#include <vector>
namespace Analyser {
namespace Static {
namespace MSX {
namespace Analyser::Static::MSX {
struct File {
std::string name;
@@ -37,8 +35,6 @@ struct File {
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
}
}
}
#endif /* StaticAnalyser_MSX_Tape_hpp */

View File

@@ -14,14 +14,19 @@
#include "../StaticAnalyser.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace MSX {
namespace Analyser::Static::MSX {
struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<Target> {
bool has_disk_drive = false;
bool has_msx_music = true;
std::string loading_command;
ReflectableEnum(Model,
MSX1,
MSX2
);
Model model = Model::MSX2;
ReflectableEnum(Region,
Japan,
USA,
@@ -32,14 +37,15 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
Target(): Analyser::Static::Target(Machine::MSX) {
if(needs_declare()) {
DeclareField(has_disk_drive);
DeclareField(has_msx_music);
DeclareField(region);
AnnounceEnum(Region);
DeclareField(model);
AnnounceEnum(Model);
}
}
};
}
}
}
#endif /* Analyser_Static_MSX_Target_h */

View File

@@ -13,15 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Macintosh {
namespace Analyser::Static::Macintosh {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* Analyser_Static_Macintosh_StaticAnalyser_hpp */

View File

@@ -13,9 +13,7 @@
#include "../../../Reflection/Struct.hpp"
#include "../StaticAnalyser.hpp"
namespace Analyser {
namespace Static {
namespace Macintosh {
namespace Analyser::Static::Macintosh {
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
ReflectableEnum(Model, Mac128k, Mac512k, Mac512ke, MacPlus);
@@ -30,8 +28,6 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
}
};
}
}
}
#endif /* Analyser_Static_Macintosh_Target_h */

View File

@@ -13,14 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Oric {
namespace Analyser::Static::Oric {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* StaticAnalyser_hpp */

View File

@@ -14,9 +14,7 @@
#include <string>
#include <vector>
namespace Analyser {
namespace Static {
namespace Oric {
namespace Analyser::Static::Oric {
struct File {
std::string name;
@@ -33,8 +31,6 @@ struct File {
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
}
}
}
#endif /* Tape_hpp */

View File

@@ -14,9 +14,7 @@
#include "../StaticAnalyser.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Oric {
namespace Analyser::Static::Oric {
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
ReflectableEnum(ROM,
@@ -56,8 +54,6 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
}
};
}
}
}
#endif /* Analyser_Static_Oric_Target_h */

View File

@@ -13,14 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace Sega {
namespace Analyser::Static::Sega {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* StaticAnalyser_hpp */

View File

@@ -13,9 +13,7 @@
#include "../../../Reflection/Struct.hpp"
#include "../StaticAnalyser.hpp"
namespace Analyser {
namespace Static {
namespace Sega {
namespace Analyser::Static::Sega {
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
enum class Model {
@@ -48,10 +46,10 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
}
};
#define is_master_system(v) v >= Analyser::Static::Sega::Target::Model::MasterSystem
constexpr bool is_master_system(Analyser::Static::Sega::Target::Model model) {
return model >= Analyser::Static::Sega::Target::Model::MasterSystem;
}
}
}
}
#endif /* Analyser_Static_Sega_Target_h */

View File

@@ -53,13 +53,13 @@
#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/ST.hpp"
#include "../../Storage/Disk/DiskImage/Formats/STX.hpp"
#include "../../Storage/Disk/DiskImage/Formats/WOZ.hpp"
// Mass Storage Devices (i.e. usually, hard disks)
#include "../../Storage/MassStorage/Formats/DAT.hpp"
#include "../../Storage/MassStorage/Formats/DSK.hpp"
#include "../../Storage/MassStorage/Formats/HDV.hpp"
#include "../../Storage/MassStorage/Formats/HFV.hpp"
// State Snapshots
@@ -81,6 +81,8 @@
// Target Platform Types
#include "../../Storage/TargetPlatforms.hpp"
template<class> inline constexpr bool always_false_v = false;
using namespace Analyser::Static;
namespace {
@@ -124,7 +126,21 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
if(extension == "2mg") {
// 2MG uses a factory method; defer to it.
try {
InsertInstance(result.disks, Storage::Disk::Disk2MG::open(file_name), TargetPlatform::DiskII)
const auto media = Storage::Disk::Disk2MG::open(file_name);
std::visit([&result, &potential_platforms](auto &&arg) {
using Type = typename std::decay<decltype(arg)>::type;
if constexpr (std::is_same<Type, nullptr_t>::value) {
// It's valid for no media to be returned.
} else if constexpr (std::is_same<Type, Storage::Disk::DiskImageHolderBase *>::value) {
InsertInstance(result.disks, arg, TargetPlatform::DiskII);
} else if constexpr (std::is_same<Type, Storage::MassStorage::MassStorageDevice *>::value) {
// TODO: or is it Apple IIgs?
InsertInstance(result.mass_storage_devices, arg, TargetPlatform::AppleII);
} else {
static_assert(always_false_v<Type>, "Unexpected type encountered.");
}
}, media);
} catch(...) {}
}
@@ -155,6 +171,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::FAT12>, TargetPlatform::MSX) // DSK (MSX)
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::OricMFMDSK>, TargetPlatform::Oric) // DSK (Oric)
Format("g64", result.disks, Disk::DiskImageHolder<Storage::Disk::G64>, TargetPlatform::Commodore) // G64
Format("hdv", result.mass_storage_devices, MassStorage::HDV, TargetPlatform::AppleII) // HDV (Apple II, hard disk, single volume image)
Format( "hfe",
result.disks,
Disk::DiskImageHolder<Storage::Disk::HFE>,
@@ -168,6 +185,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
Disk::DiskImageHolder<Storage::Disk::IPF>,
TargetPlatform::Amiga | TargetPlatform::AtariST | TargetPlatform::AmstradCPC | TargetPlatform::ZXSpectrum) // IPF
Format("msa", result.disks, Disk::DiskImageHolder<Storage::Disk::MSA>, TargetPlatform::AtariST) // MSA
Format("mx2", result.cartridges, Cartridge::BinaryDump, TargetPlatform::MSX) // MX2
Format("nib", result.disks, Disk::DiskImageHolder<Storage::Disk::NIB>, TargetPlatform::DiskII) // NIB
Format("o", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // O
Format("p", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // P
@@ -199,7 +217,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
Format("sg", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Sega) // SG
Format("sms", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Sega) // SMS
Format("ssd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // SSD
Format("st", result.disks, Disk::DiskImageHolder<Storage::Disk::ST>, TargetPlatform::AtariST) // ST
Format("st", result.disks, Disk::DiskImageHolder<Storage::Disk::FAT12>, TargetPlatform::AtariST) // ST
Format("stx", result.disks, Disk::DiskImageHolder<Storage::Disk::STX>, TargetPlatform::AtariST) // STX
Format("tap", result.tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP (Commodore)
Format("tap", result.tapes, Tape::OricTAP, TargetPlatform::Oric) // TAP (Oric)

View File

@@ -21,8 +21,7 @@
#include <string>
#include <vector>
namespace Analyser {
namespace Static {
namespace Analyser::Static {
struct State;
@@ -79,7 +78,6 @@ TargetList GetTargets(const std::string &file_name);
*/
Media GetMedia(const std::string &file_name);
}
}
#endif /* StaticAnalyser_hpp */

View File

@@ -13,14 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace ZX8081 {
namespace Analyser::Static::ZX8081 {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* StaticAnalyser_hpp */

View File

@@ -14,9 +14,7 @@
#include "../StaticAnalyser.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace ZX8081 {
namespace Analyser::Static::ZX8081 {
struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<Target> {
ReflectableEnum(MemoryModel,
@@ -40,8 +38,6 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
}
};
}
}
}
#endif /* Analyser_Static_ZX8081_Target_h */

View File

@@ -13,14 +13,10 @@
#include "../../../Storage/TargetPlatforms.hpp"
#include <string>
namespace Analyser {
namespace Static {
namespace ZXSpectrum {
namespace Analyser::Static::ZXSpectrum {
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
}
}
}
#endif /* StaticAnalyser_hpp */

View File

@@ -13,9 +13,7 @@
#include "../../../Reflection/Struct.hpp"
#include "../StaticAnalyser.hpp"
namespace Analyser {
namespace Static {
namespace ZXSpectrum {
namespace Analyser::Static::ZXSpectrum {
struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<Target> {
ReflectableEnum(Model,
@@ -38,8 +36,6 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
}
};
}
}
}
#endif /* Target_h */

View File

@@ -14,6 +14,8 @@
#include "ClockingHintSource.hpp"
#include "ForceInline.hpp"
#include <atomic>
/*!
A JustInTimeActor holds (i) an embedded object with a run_for method; and (ii) an amount
of time since run_for was last called.
@@ -121,7 +123,13 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
/// If this object provides sequence points, checks for changes to the next
/// sequence point upon deletion of the pointer.
[[nodiscard]] forceinline auto operator->() {
#ifndef NDEBUG
assert(!flush_concurrency_check_.test_and_set());
#endif
flush();
#ifndef NDEBUG
flush_concurrency_check_.clear();
#endif
return std::unique_ptr<T, SequencePointAwareDeleter>(&object_, SequencePointAwareDeleter(this));
}
@@ -130,7 +138,13 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
/// Despite being const, this will flush the object and, if relevant, update the next sequence point.
[[nodiscard]] forceinline auto operator -> () const {
auto non_const_this = const_cast<JustInTimeActor<T, LocalTimeScale, multiplier, divider> *>(this);
#ifndef NDEBUG
assert(!non_const_this->flush_concurrency_check_.test_and_set());
#endif
non_const_this->flush();
#ifndef NDEBUG
non_const_this->flush_concurrency_check_.clear();
#endif
return std::unique_ptr<const T, SequencePointAwareDeleter>(&object_, SequencePointAwareDeleter(non_const_this));
}
@@ -264,6 +278,10 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
void set_component_prefers_clocking(ClockingHint::Source *, ClockingHint::Preference clocking) {
clocking_preference_ = clocking;
}
#ifndef NDEBUG
std::atomic_flag flush_concurrency_check_{};
#endif
};
/*!
@@ -315,7 +333,7 @@ template <class T, class LocalTimeScale = HalfCycles, class TargetTimeScale = Lo
LocalTimeScale time_since_update_;
TargetTimeScale threshold_;
bool is_flushed_ = true;
Concurrency::AsyncTaskQueue task_queue_;
Concurrency::AsyncTaskQueue<true> task_queue_;
};
#endif /* JustInTime_h */

View File

@@ -20,6 +20,11 @@ inline Nanos nanos_now() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
}
inline Seconds seconds(Nanos nanos) {
return double(nanos) / 1e9;
}
}
#endif /* TimeTypes_h */

View File

@@ -8,8 +8,18 @@
#include "ncr5380.hpp"
#ifndef NDEBUG
#define NDEBUG
#endif
#define LOG_PREFIX "[5380] "
#include "../../Outputs/Log.hpp"
// TODO:
//
// end_of_dma_ should be set if: /EOP && /DACK && (/RD || /WR); for at least 100ns.
using namespace NCR::NCR5380;
using SCSI::Line;
@@ -28,20 +38,16 @@ NCR5380::NCR5380(SCSI::Bus &bus, int clock_rate) :
void NCR5380::write(int address, uint8_t value, bool) {
switch(address & 7) {
case 0:
// LOG("[SCSI 0] Set current SCSI bus state to " << PADHEX(2) << int(value));
data_bus_ = value;
LOG("[0] Set current SCSI bus state to " << PADHEX(2) << int(value));
data_bus_ = value;
if(dma_request_ && dma_operation_ == DMAOperation::Send) {
// printf("w %02x\n", value);
dma_acknowledge_ = true;
dma_request_ = false;
update_control_output();
bus_.set_device_output(device_id_, bus_output_);
dma_acknowledge(value);
}
break;
case 1: {
// LOG("[SCSI 1] Initiator command register set: " << PADHEX(2) << int(value));
LOG("[1] Initiator command register set: " << PADHEX(2) << int(value));
initiator_command_ = value;
bus_output_ &= ~(Line::Reset | Line::Acknowledge | Line::Busy | Line::SelectTarget | Line::Attention);
@@ -57,7 +63,7 @@ void NCR5380::write(int address, uint8_t value, bool) {
} break;
case 2:
// LOG("[SCSI 2] Set mode: " << PADHEX(2) << int(value));
LOG("[2] Set mode: " << PADHEX(2) << int(value));
mode_ = value;
// bit 7: 1 = use block mode DMA mode (if DMA mode is also enabled)
@@ -69,6 +75,7 @@ void NCR5380::write(int address, uint8_t value, bool) {
// bit 1: 1 = use DMA mode
// bit 0: 1 = begin arbitration mode (device ID should be in register 0)
arbitration_in_progress_ = false;
phase_mismatch_ = false;
switch(mode_ & 0x3) {
case 0x0:
bus_output_ &= ~SCSI::Line::Busy;
@@ -88,31 +95,36 @@ void NCR5380::write(int address, uint8_t value, bool) {
bus_.update_observers();
break;
}
// "[The End of DMA Transfer] bit is reset when the DMA MODE bit
// is reset (0) in the Mode Register".
end_of_dma_ &= bool(value & 0x2);
update_control_output();
break;
case 3: {
// LOG("[SCSI 3] Set target command: " << PADHEX(2) << int(value));
LOG("[3] Set target command: " << PADHEX(2) << int(value));
target_command_ = value;
update_control_output();
} break;
case 4:
// LOG("[SCSI 4] Set select enabled: " << PADHEX(2) << int(value));
LOG("[4] Set select enabled: " << PADHEX(2) << int(value));
break;
case 5:
// LOG("[SCSI 5] Start DMA send: " << PADHEX(2) << int(value));
LOG("[5] Start DMA send: " << PADHEX(2) << int(value));
dma_operation_ = DMAOperation::Send;
break;
case 6:
// LOG("[SCSI 6] Start DMA target receive: " << PADHEX(2) << int(value));
LOG("[6] Start DMA target receive: " << PADHEX(2) << int(value));
dma_operation_ = DMAOperation::TargetReceive;
break;
case 7:
// LOG("[SCSI 7] Start DMA initiator receive: " << PADHEX(2) << int(value));
LOG("[7] Start DMA initiator receive: " << PADHEX(2) << int(value));
dma_operation_ = DMAOperation::InitiatorReceive;
break;
}
@@ -136,18 +148,15 @@ void NCR5380::write(int address, uint8_t value, bool) {
uint8_t NCR5380::read(int address, bool) {
switch(address & 7) {
case 0:
// LOG("[SCSI 0] Get current SCSI bus state: " << PADHEX(2) << (bus_.get_state() & 0xff));
LOG("[0] Get current SCSI bus state: " << PADHEX(2) << (bus_.get_state() & 0xff));
if(dma_request_ && dma_operation_ == DMAOperation::InitiatorReceive) {
dma_acknowledge_ = true;
dma_request_ = false;
update_control_output();
bus_.set_device_output(device_id_, bus_output_);
return dma_acknowledge();
}
return uint8_t(bus_.get_state());
case 1:
// LOG("[SCSI 1] Initiator command register get: " << (arbitration_in_progress_ ? 'p' : '-') << (lost_arbitration_ ? 'l' : '-'));
LOG("[1] Initiator command register get: " << (arbitration_in_progress_ ? 'p' : '-') << (lost_arbitration_ ? 'l' : '-'));
return
// Bits repeated as they were set.
(initiator_command_ & ~0x60) |
@@ -159,11 +168,11 @@ uint8_t NCR5380::read(int address, bool) {
(lost_arbitration_ ? 0x20 : 0x00);
case 2:
// LOG("[SCSI 2] Get mode");
LOG("[2] Get mode");
return mode_;
case 3:
// LOG("[SCSI 3] Get target command");
LOG("[3] Get target command");
return target_command_;
case 4: {
@@ -177,41 +186,38 @@ uint8_t NCR5380::read(int address, bool) {
((bus_state & Line::Input) ? 0x04 : 0x00) |
((bus_state & Line::SelectTarget) ? 0x02 : 0x00) |
((bus_state & Line::Parity) ? 0x01 : 0x00);
// LOG("[SCSI 4] Get current bus state: " << PADHEX(2) << int(result));
LOG("[4] Get current bus state: " << PADHEX(2) << int(result));
return result;
}
case 5: {
const auto bus_state = bus_.get_state();
const bool phase_matches =
(target_output() & (Line::Message | Line::Control | Line::Input)) ==
(bus_state & (Line::Message | Line::Control | Line::Input));
const uint8_t result =
/* b7 = end of DMA */
(end_of_dma_ ? 0x80 : 0x00) |
((dma_request_ && state_ == ExecutionState::PerformingDMA) ? 0x40 : 0x00) |
/* b5 = parity error */
/* b4 = IRQ active */
(phase_matches ? 0x08 : 0x00) |
(irq_ ? 0x10 : 0x00) |
(phase_matches() ? 0x08 : 0x00) |
/* b2 = busy error */
((bus_state & Line::Attention) ? 0x02 : 0x00) |
((bus_state & Line::Acknowledge) ? 0x01 : 0x00);
// LOG("[SCSI 5] Get bus and status: " << PADHEX(2) << int(result));
LOG("[5] Get bus and status: " << PADHEX(2) << int(result));
return result;
}
case 6:
// LOG("[SCSI 6] Get input data");
LOG("[6] Get input data");
return 0xff;
case 7:
// LOG("[SCSI 7] Reset parity/interrupt");
LOG("[7] Reset parity/interrupt");
irq_ = false;
return 0xff;
}
return 0;
}
SCSI::BusState NCR5380::target_output() {
SCSI::BusState NCR5380::target_output() const {
SCSI::BusState output = SCSI::DefaultBusState;
if(target_command_ & 0x08) output |= Line::Request;
if(target_command_ & 0x04) output |= Line::Message;
@@ -236,6 +242,17 @@ void NCR5380::update_control_output() {
}
void NCR5380::scsi_bus_did_change(SCSI::Bus *, SCSI::BusState new_state, double time_since_change) {
/*
When connected as an Initiator with DMA Mode True,
if the phase lines I//O, C//D, and /MSG do not match the
phase bits in the Target Command Register, a phase mismatch
interrupt is generated when /REQ goes active.
*/
if((mode_ & 0x42) == 0x02 && new_state & SCSI::Line::Request && !phase_matches()) {
irq_ = true;
phase_mismatch_ = true;
}
switch(state_) {
default: break;
@@ -296,7 +313,13 @@ void NCR5380::scsi_bus_did_change(SCSI::Bus *, SCSI::BusState new_state, double
dma_request_ = false;
break;
case SCSI::Line::Request:
dma_request_ = true;
// Don't issue a new DMA request if a phase mismatch has
// been detected and this is an intiator receiving.
// This is a bit of reading between the lines.
// (i.e. guesswork, partly)
dma_request_ =
!phase_mismatch_ ||
(dma_operation_ != DMAOperation::InitiatorReceive);
break;
case SCSI::Line::Request | SCSI::Line::Acknowledge:
dma_request_ = false;
@@ -316,3 +339,38 @@ void NCR5380::set_execution_state(ExecutionState state) {
state_ = state;
if(state != ExecutionState::PerformingDMA) dma_operation_ = DMAOperation::Ready;
}
size_t NCR5380::scsi_id() {
return device_id_;
}
bool NCR5380::dma_request() {
return dma_request_;
}
uint8_t NCR5380::dma_acknowledge() {
const uint8_t bus_state = uint8_t(bus_.get_state());
dma_acknowledge_ = true;
dma_request_ = false;
update_control_output();
bus_.set_device_output(device_id_, bus_output_);
return bus_state;
}
void NCR5380::dma_acknowledge(uint8_t value) {
data_bus_ = value;
dma_acknowledge_ = true;
dma_request_ = false;
update_control_output();
bus_.set_device_output(device_id_, bus_output_);
}
bool NCR5380::phase_matches() const {
const auto bus_state = bus_.get_state();
return
(target_output() & (Line::Message | Line::Control | Line::Input)) ==
(bus_state & (Line::Message | Line::Control | Line::Input));
}

View File

@@ -14,8 +14,7 @@
#include "../../Storage/MassStorage/SCSI/SCSI.hpp"
namespace NCR {
namespace NCR5380 {
namespace NCR::NCR5380 {
/*!
Models the NCR 5380, a SCSI interface chip.
@@ -30,6 +29,18 @@ class NCR5380 final: public SCSI::Bus::Observer {
/*! Reads from @c address. */
uint8_t read(int address, bool dma_acknowledge = false);
/*! @returns The SCSI ID assigned to this device. */
size_t scsi_id();
/*! @return @c true if DMA request is active; @c false otherwise. */
bool dma_request();
/*! Signals DMA acknowledge with a simultaneous read. */
uint8_t dma_acknowledge();
/*! Signals DMA acknowledge with a simultaneous write. */
void dma_acknowledge(uint8_t);
private:
SCSI::Bus &bus_;
@@ -46,6 +57,10 @@ class NCR5380 final: public SCSI::Bus::Observer {
bool assert_data_bus_ = false;
bool dma_request_ = false;
bool dma_acknowledge_ = false;
bool end_of_dma_ = false;
bool irq_ = false;
bool phase_mismatch_ = false;
enum class ExecutionState {
None,
@@ -63,13 +78,13 @@ class NCR5380 final: public SCSI::Bus::Observer {
void set_execution_state(ExecutionState state);
SCSI::BusState target_output();
SCSI::BusState target_output() const;
void update_control_output();
void scsi_bus_did_change(SCSI::Bus *, SCSI::BusState new_state, double time_since_change) final;
bool phase_matches() const;
};
}
}
#endif /* ncr5380_hpp */

View File

@@ -15,8 +15,7 @@
#include "../../ClockReceiver/ClockReceiver.hpp"
namespace MOS {
namespace MOS6522 {
namespace MOS::MOS6522 {
enum Port {
A = 0,
@@ -138,7 +137,6 @@ template <class BusHandlerT> class MOS6522: public MOS6522Storage {
void evaluate_port_b_output();
};
}
}
#include "Implementation/6522Implementation.hpp"

View File

@@ -12,8 +12,7 @@
//
// PB6 count-down mode for timer 2.
namespace MOS {
namespace MOS6522 {
namespace MOS::MOS6522 {
template <typename T> void MOS6522<T>::access(int address) {
switch(address) {
@@ -494,4 +493,3 @@ template <typename T> void MOS6522<T>::shift_out() {
}
}
}

View File

@@ -11,8 +11,7 @@
#include <cstdint>
namespace MOS {
namespace MOS6522 {
namespace MOS::MOS6522 {
class MOS6522Storage {
protected:
@@ -107,7 +106,6 @@ class MOS6522Storage {
}
};
}
}
#endif /* _522Storage_hpp */

View File

@@ -14,8 +14,7 @@
#include "Implementation/6526Storage.hpp"
#include "../Serial/Line.hpp"
namespace MOS {
namespace MOS6526 {
namespace MOS::MOS6526 {
enum Port {
A = 0,
@@ -86,7 +85,6 @@ template <typename PortHandlerT, Personality personality> class MOS6526:
bool serial_line_did_produce_bit(Serial::Line<true> *line, int bit) final;
};
}
}
#include "Implementation/6526Implementation.hpp"

View File

@@ -12,8 +12,7 @@
#include <cassert>
#include <cstdio>
namespace MOS {
namespace MOS6526 {
namespace MOS::MOS6526 {
enum Interrupts: uint8_t {
TimerA = 1 << 0,
@@ -238,7 +237,6 @@ bool MOS6526<BusHandlerT, personality>::serial_line_did_produce_bit(Serial::Line
return true;
}
}
}
#endif /* _526Implementation_h */

View File

@@ -13,8 +13,7 @@
#include "../../../ClockReceiver/ClockReceiver.hpp"
namespace MOS {
namespace MOS6526 {
namespace MOS::MOS6526 {
class TODBase {
public:
@@ -333,7 +332,6 @@ struct MOS6526Storage {
int pending_ = 0;
};
}
}
#endif /* _526Storage_h */

View File

@@ -12,18 +12,18 @@
using namespace MOS::MOS6560;
AudioGenerator::AudioGenerator(Concurrency::DeferringAsyncTaskQueue &audio_queue) :
AudioGenerator::AudioGenerator(Concurrency::AsyncTaskQueue<false> &audio_queue) :
audio_queue_(audio_queue) {}
void AudioGenerator::set_volume(uint8_t volume) {
audio_queue_.defer([this, volume]() {
audio_queue_.enqueue([this, volume]() {
volume_ = int16_t(volume) * range_multiplier_;
});
}
void AudioGenerator::set_control(int channel, uint8_t value) {
audio_queue_.defer([this, channel, value]() {
audio_queue_.enqueue([this, channel, value]() {
control_registers_[channel] = value;
});
}

View File

@@ -15,13 +15,12 @@
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
namespace MOS {
namespace MOS6560 {
namespace MOS::MOS6560 {
// audio state
class AudioGenerator: public ::Outputs::Speaker::SampleSource {
public:
AudioGenerator(Concurrency::DeferringAsyncTaskQueue &audio_queue);
AudioGenerator(Concurrency::AsyncTaskQueue<false> &audio_queue);
void set_volume(uint8_t volume);
void set_control(int channel, uint8_t value);
@@ -33,7 +32,7 @@ class AudioGenerator: public ::Outputs::Speaker::SampleSource {
static constexpr bool get_is_stereo() { return false; }
private:
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
Concurrency::AsyncTaskQueue<false> &audio_queue_;
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};
@@ -433,7 +432,7 @@ template <class BusHandler> class MOS6560 {
BusHandler &bus_handler_;
Outputs::CRT::CRT crt_;
Concurrency::DeferringAsyncTaskQueue audio_queue_;
Concurrency::AsyncTaskQueue<false> audio_queue_;
AudioGenerator audio_generator_;
Outputs::Speaker::PullLowpass<AudioGenerator> speaker_;
@@ -520,7 +519,6 @@ template <class BusHandler> class MOS6560 {
OutputMode output_mode_ = OutputMode::NTSC;
};
}
}
#endif /* _560_hpp */

View File

@@ -14,8 +14,7 @@
#include <cstdint>
#include <cstdio>
namespace Motorola {
namespace CRTC {
namespace Motorola::CRTC {
struct BusState {
bool display_enable = false;
@@ -269,7 +268,6 @@ template <class T> class CRTC6845 {
unsigned int character_is_visible_shifter_ = 0;
};
}
}
#endif /* CRTC6845_hpp */

View File

@@ -15,8 +15,7 @@
#include "../../ClockReceiver/ClockingHintSource.hpp"
#include "../Serial/Line.hpp"
namespace Motorola {
namespace ACIA {
namespace Motorola::ACIA {
class ACIA: public ClockingHint::Source, private Serial::Line<false>::ReadDelegate {
public:
@@ -126,7 +125,6 @@ class ACIA: public ClockingHint::Source, private Serial::Line<false>::ReadDelega
uint8_t get_status();
};
}
}
#endif /* Motorola_ACIA_6850_hpp */

View File

@@ -9,12 +9,12 @@
#ifndef MFP68901_hpp
#define MFP68901_hpp
#include <cstdint>
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "../../ClockReceiver/ClockingHintSource.hpp"
namespace Motorola {
namespace MFP68901 {
#include <cstdint>
namespace Motorola::MFP68901 {
class PortHandler {
public:
@@ -181,7 +181,6 @@ class MFP68901: public ClockingHint::Source {
}
};
}
}
#endif /* MFP68901_hpp */

View File

@@ -11,8 +11,7 @@
#include <cstdint>
namespace Intel {
namespace i8255 {
namespace Intel::i8255 {
class PortHandler {
public:
@@ -88,7 +87,6 @@ template <class T> class i8255 {
T &port_handler_;
};
}
}
#endif /* i8255_hpp */

View File

@@ -15,8 +15,7 @@
#include <memory>
#include <vector>
namespace Intel {
namespace i8272 {
namespace Intel::i8272 {
class BusHandler {
public:
@@ -130,7 +129,6 @@ class i8272 : public Storage::Disk::MFMController {
bool is_sleeping_ = false;
};
}
}
#endif /* i8272_hpp */

View File

@@ -11,8 +11,7 @@
#include <cstdint>
namespace Zilog {
namespace SCC {
namespace Zilog::SCC {
/*!
Models the Zilog 8530 SCC, a serial adaptor.
@@ -110,7 +109,5 @@ class z8530 {
};
}
}
#endif /* z8530_hpp */

File diff suppressed because it is too large Load Diff

View File

@@ -12,12 +12,36 @@
#include "../../Outputs/CRT/CRT.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "Implementation/9918Base.hpp"
#include <cstdint>
namespace TI {
namespace TMS {
namespace TI::TMS {
enum Personality {
TMS9918A, // includes the 9928 and 9929; set TV standard and output device as desired.
// Yamaha extensions.
V9938,
V9958,
// Sega extensions.
SMSVDP,
SMS2VDP,
GGVDP,
MDVDP,
};
enum class TVStandard {
/*! i.e. 50Hz output at around 312.5 lines/field */
PAL,
/*! i.e. 60Hz output at around 262.5 lines/field */
NTSC
};
}
#include "Implementation/9918Base.hpp"
namespace TI::TMS {
/*!
Provides emulation of the TMS9918a, TMS9928 and TMS9929. Likely in the future to be the
@@ -30,13 +54,10 @@ namespace TMS {
These chips have only one non-on-demand interaction with the outside world: an interrupt line.
See get_time_until_interrupt and get_interrupt_line for asynchronous operation options.
*/
class TMS9918: public Base {
template <Personality personality> class TMS9918: private Base<personality> {
public:
/*!
Constructs an instance of the drive controller that behaves according to personality @c p.
@param p The type of controller to emulate.
*/
TMS9918(Personality p);
/*! Constructs an instance of the VDP that behaves according to the templated personality. */
TMS9918();
/*! Sets the TV standard for this TMS, if that is hard-coded in hardware. */
void set_tv_standard(TVStandard standard);
@@ -44,18 +65,24 @@ class TMS9918: public Base {
/*! Sets the scan target this TMS will post content to. */
void set_scan_target(Outputs::Display::ScanTarget *);
/// Gets the current scan status.
/*! Gets the current scan status. */
Outputs::Display::ScanStatus get_scaled_scan_status() const;
/*! Sets the type of display the CRT will request. */
/*! Sets the type of CRT display. */
void set_display_type(Outputs::Display::DisplayType);
/*! Gets the type of display the CRT will request. */
/*! Gets the type of CRT display. */
Outputs::Display::DisplayType get_display_type() const;
/*!
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.
Runs the VDP for the number of cycles indicate; the input clock rate is implicitly assumed.
For everything except the Mega Drive VDP:
* the input clock rate should be 3579545 Hz, the NTSC colour clock rate.
For the Mega Drive:
* the input clock rate should be around 7.6MHz; 15/7ths of the NTSC colour
clock rate for NTSC output and 12/7ths of the PAL colour clock rate for PAL output.
*/
void run_for(const HalfCycles cycles);
@@ -65,11 +92,11 @@ class TMS9918: public Base {
/*! Gets a register value. */
uint8_t read(int address);
/*! Gets the current scan line; provided by the Master System only. */
uint8_t get_current_line();
/*! Gets the current scan line; provided by the Sega VDPs only. */
uint8_t get_current_line() const;
/*! Gets the current latched horizontal counter; provided by the Master System only. */
uint8_t get_latched_horizontal_counter();
/*! Gets the current latched horizontal counter; provided by the Sega VDPs only. */
uint8_t get_latched_horizontal_counter() const;
/*! Latches the current horizontal counter. */
void latch_horizontal_counter();
@@ -81,7 +108,7 @@ class TMS9918: public Base {
If get_interrupt_line is true now of if get_interrupt_line would
never return true, returns HalfCycles::max().
*/
HalfCycles get_next_sequence_point();
HalfCycles get_next_sequence_point() const;
/*!
Returns the amount of time until the nominated line interrupt position is
@@ -96,10 +123,9 @@ class TMS9918: public Base {
/*!
@returns @c true if the interrupt line is currently active; @c false otherwise.
*/
bool get_interrupt_line();
bool get_interrupt_line() const;
};
}
}
#endif /* TMS9918_hpp */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,111 @@
//
// AccessEnums.hpp
// Clock Signal
//
// Created by Thomas Harte on 26/01/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#ifndef AccessEnums_hpp
#define AccessEnums_hpp
namespace TI::TMS {
// The screen mode is a necessary predecessor to picking the line mode,
// which is the thing latched per line.
enum class ScreenMode {
// Original TMS modes.
Blank,
Text,
MultiColour,
ColouredText,
Graphics,
// 8-bit Sega modes.
SMSMode4,
// New Yamaha V9938 modes.
YamahaText80,
YamahaGraphics3,
YamahaGraphics4,
YamahaGraphics5,
YamahaGraphics6,
YamahaGraphics7,
// Rebranded Yamaha V9938 modes.
YamahaGraphics1 = ColouredText,
YamahaGraphics2 = Graphics,
};
constexpr int pixels_per_byte(ScreenMode mode) {
switch(mode) {
default:
case ScreenMode::Blank: return 1;
case ScreenMode::Text: return 6;
case ScreenMode::MultiColour: return 2;
case ScreenMode::ColouredText: return 8;
case ScreenMode::Graphics: return 8;
case ScreenMode::SMSMode4: return 2;
case ScreenMode::YamahaText80: return 6;
case ScreenMode::YamahaGraphics3: return 8;
case ScreenMode::YamahaGraphics4: return 2;
case ScreenMode::YamahaGraphics5: return 4;
case ScreenMode::YamahaGraphics6: return 2;
case ScreenMode::YamahaGraphics7: return 1;
}
}
constexpr int width(ScreenMode mode) {
switch(mode) {
default:
case ScreenMode::Blank: return 0;
case ScreenMode::Text: return 240;
case ScreenMode::MultiColour: return 256;
case ScreenMode::ColouredText: return 256;
case ScreenMode::Graphics: return 256;
case ScreenMode::SMSMode4: return 256;
case ScreenMode::YamahaText80: return 480;
case ScreenMode::YamahaGraphics3: return 256;
case ScreenMode::YamahaGraphics4: return 256;
case ScreenMode::YamahaGraphics5: return 512;
case ScreenMode::YamahaGraphics6: return 512;
case ScreenMode::YamahaGraphics7: return 256;
}
}
constexpr bool interleaves_banks(ScreenMode mode) {
return mode == ScreenMode::YamahaGraphics6 || mode == ScreenMode::YamahaGraphics7;
}
enum class FetchMode {
Text,
Character,
Refresh,
SMS,
Yamaha,
};
enum class MemoryAccess {
Read, Write, None
};
enum class VerticalState {
/// Describes any line on which pixels do not appear and no fetching occurs, including
/// the border, blanking and sync.
Blank,
/// A line on which pixels do not appear but fetching occurs.
Prefetch,
/// A line on which pixels appear and fetching occurs.
Pixels,
};
enum class SpriteMode {
Mode1,
Mode2,
MasterSystem,
};
}
#endif /* AccessEnums_hpp */

View File

@@ -0,0 +1,195 @@
//
// ClockConverter.hpp
// Clock Signal
//
// Created by Thomas Harte on 01/01/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#ifndef ClockConverter_hpp
#define ClockConverter_hpp
#include "../9918.hpp"
#include "PersonalityTraits.hpp"
namespace TI::TMS {
enum class Clock {
Internal,
TMSPixel,
TMSMemoryWindow,
CRT
};
template <Personality personality, Clock clk> constexpr int clock_rate() {
static_assert(
is_classic_vdp(personality) ||
is_yamaha_vdp(personality) ||
(personality == Personality::MDVDP)
);
switch(clk) {
case Clock::TMSPixel: return 342;
case Clock::TMSMemoryWindow: return 171;
case Clock::CRT: return 1368;
case Clock::Internal:
if constexpr (is_classic_vdp(personality)) {
return 342;
} else if constexpr (is_yamaha_vdp(personality)) {
return 1368;
} else if constexpr (personality == Personality::MDVDP) {
return 3420;
}
}
}
template <Personality personality, Clock clock> constexpr int to_internal(int length) {
return length * clock_rate<personality, Clock::Internal>() / clock_rate<personality, clock>();
}
template <Personality personality, Clock clock> constexpr int from_internal(int length) {
return length * clock_rate<personality, clock>() / clock_rate<personality, Clock::Internal>();
}
/*!
Provides a [potentially-]stateful conversion between the external and internal clocks.
Unlike the other clock conversions, this one may be non-integral, requiring that
an error term be tracked.
*/
template <Personality personality> class ClockConverter {
public:
/*!
Given that another @c source external **half-cycles** has occurred,
indicates how many complete internal **cycles** have additionally elapsed
since the last call to @c to_internal.
E.g. for the TMS, @c source will count 456 ticks per line, and the internal clock
runs at 342 ticks per line, so the proper conversion is to multiply by 3/4.
*/
int to_internal(int source) {
switch(personality) {
// Default behaviour is to apply a multiplication by 3/4;
// this is correct for the TMS and Sega VDPs other than the Mega Drive.
default: {
const int result = source * 3 + cycles_error_;
cycles_error_ = result & 3;
return result >> 2;
}
// The two Yamaha chips have an internal clock that is four times
// as fast as the TMS, therefore a stateless translation is possible.
case Personality::V9938:
case Personality::V9958:
return source * 3;
// The Mega Drive runs at 3420 master clocks per line, which is then
// divided by 4 or 5 depending on other state. That's 7 times the
// rate provided to the CPU; given that the input is in half-cycles
// the proper multiplier is therefore 3.5.
case Personality::MDVDP: {
const int result = source * 7 + cycles_error_;
cycles_error_ = result & 1;
return result >> 1;
}
}
}
/*!
Provides the number of external cycles that need to begin from now in order to
get at least @c internal_cycles into the future.
*/
HalfCycles half_cycles_before_internal_cycles(int internal_cycles) const {
// Logic here correlates with multipliers as per @c to_internal.
switch(personality) {
default:
// Relative to the external clock multiplied by 3, it will definitely take this
// many cycles to complete a further (internal_cycles - 1) after the current one.
internal_cycles = (internal_cycles - 1) << 2;
// It will also be necessary to complete the current one.
internal_cycles += 4 - cycles_error_;
// Round up to get the first external cycle after
// the number of internal_cycles has elapsed.
return HalfCycles((internal_cycles + 2) / 3);
case Personality::V9938:
case Personality::V9958:
return HalfCycles((internal_cycles + 2) / 3);
case Personality::MDVDP:
internal_cycles = (internal_cycles - 1) << 1;
internal_cycles += 2 - cycles_error_;
return HalfCycles((internal_cycles + 6) / 7);
}
}
private:
// Holds current residue in conversion from the external to
// internal clock.
int cycles_error_ = 0;
};
//
//
//
template <Personality personality, typename Enable = void> struct LineLayout;
// Line layout is:
//
// [0, EndOfSync] sync
// (EndOfSync, StartOfColourBurst] blank
// (StartOfColourBurst, EndOfColourBurst] colour burst
// (EndOfColourBurst, EndOfLeftErase] blank
// (EndOfLeftErase, EndOfLeftBorder] border colour
// (EndOfLeftBorder, EndOfPixels] pixel content
// (EndOfPixels, EndOfRightBorder] border colour
// [EndOfRightBorder, <end of line>] blank
//
// ... with minor caveats:
// * horizontal adjust on the Yamaha VDPs is applied to EndOfLeftBorder and EndOfPixels;
// * the Sega VDPs may programatically extend the left border; and
// * text mode on all VDPs adjusts border width.
template <Personality personality> struct LineLayout<personality, std::enable_if_t<is_classic_vdp(personality)>> {
constexpr static int EndOfSync = 26;
constexpr static int StartOfColourBurst = 29;
constexpr static int EndOfColourBurst = 43;
constexpr static int EndOfLeftErase = 50;
constexpr static int EndOfLeftBorder = 63;
constexpr static int EndOfPixels = 319;
constexpr static int EndOfRightBorder = 334;
constexpr static int CyclesPerLine = 342;
constexpr static int TextModeEndOfLeftBorder = 69;
constexpr static int TextModeEndOfPixels = 309;
/// The number of internal cycles that must elapse between a request to read or write and
/// it becoming a candidate for action.
constexpr static int VRAMAccessDelay = 6;
};
template <Personality personality> struct LineLayout<personality, std::enable_if_t<is_yamaha_vdp(personality)>> {
constexpr static int EndOfSync = 100;
constexpr static int StartOfColourBurst = 113;
constexpr static int EndOfColourBurst = 167;
constexpr static int EndOfLeftErase = 202;
constexpr static int EndOfLeftBorder = 258;
constexpr static int EndOfPixels = 1282;
constexpr static int EndOfRightBorder = 1341;
constexpr static int CyclesPerLine = 1368;
constexpr static int TextModeEndOfLeftBorder = 294;
constexpr static int TextModeEndOfPixels = 1254;
/// The number of internal cycles that must elapse between a request to read or write and
/// it becoming a candidate for action.
constexpr static int VRAMAccessDelay = 16;
};
}
#endif /* ClockConverter_hpp */

View File

@@ -0,0 +1,572 @@
//
// Draw.hpp
// Clock Signal
//
// Created by Thomas Harte on 05/01/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#ifndef Draw_hpp
#define Draw_hpp
namespace TI::TMS {
// MARK: - Sprites, as generalised.
template <Personality personality>
template <SpriteMode mode, bool double_width>
void Base<personality>::draw_sprites(uint8_t y, int start, int end, const std::array<uint32_t, 16> &palette, int *colour_buffer) {
if(!draw_line_buffer_->sprites) {
return;
}
auto &buffer = *draw_line_buffer_->sprites;
if(!buffer.active_sprite_slot) {
return;
}
const int shift_advance = sprites_magnified_ ? 1 : 2;
// If this is the start of the line clip any part of any sprites that is off to the left.
if(!start) {
for(int index = 0; index < buffer.active_sprite_slot; ++index) {
auto &sprite = buffer.active_sprites[index];
if(sprite.x < 0) sprite.shift_position -= shift_advance * sprite.x;
}
}
int sprite_buffer[256];
int sprite_collision = 0;
memset(&sprite_buffer[start], 0, size_t(end - start)*sizeof(sprite_buffer[0]));
if constexpr (mode == SpriteMode::MasterSystem) {
// Draw all sprites into the sprite buffer.
for(int index = buffer.active_sprite_slot - 1; index >= 0; --index) {
auto &sprite = buffer.active_sprites[index];
if(sprite.shift_position >= 16) {
continue;
}
const int pixel_start = std::max(start, sprite.x);
// TODO: it feels like the work below should be simplifiable;
// the double shift in particular, and hopefully the variable shift.
for(int c = pixel_start; c < end && sprite.shift_position < 16; ++c) {
const int shift = (sprite.shift_position >> 1);
const int sprite_colour =
(((sprite.image[3] << shift) & 0x80) >> 4) |
(((sprite.image[2] << shift) & 0x80) >> 5) |
(((sprite.image[1] << shift) & 0x80) >> 6) |
(((sprite.image[0] << shift) & 0x80) >> 7);
if(sprite_colour) {
sprite_collision |= sprite_buffer[c];
sprite_buffer[c] = sprite_colour | 0x10;
}
sprite.shift_position += shift_advance;
}
}
// Draw the sprite buffer onto the colour buffer, wherever the tile map doesn't have
// priority (or is transparent).
for(int c = start; c < end; ++c) {
if(
sprite_buffer[c] &&
(!(colour_buffer[c]&0x20) || !(colour_buffer[c]&0xf))
) colour_buffer[c] = sprite_buffer[c];
}
if(sprite_collision) {
status_ |= StatusSpriteCollision;
}
return;
}
if constexpr (SpriteBuffer::test_is_filling) {
assert(!buffer.is_filling);
}
constexpr uint32_t sprite_colour_selection_masks[2] = {0x00000000, 0xffffffff};
constexpr int colour_masks[16] = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
const int sprite_width = sprites_16x16_ ? 16 : 8;
const int shifter_target = sprite_width << 1;
const int pixel_width = sprites_magnified_ ? sprite_width << 1 : sprite_width;
int min_sprite = 0;
//
// Approach taken for Mode 2 sprites:
//
// (1) precompute full sprite images, at up to 32 pixels wide;
// (2) for each sprite that is marked as CC, walk backwards until the
// first sprite that is not marked CC, ORing it into the precomputed
// image at each step;
// (3) subsequently, just draw each sprite image independently.
//
if constexpr (mode == SpriteMode::Mode2) {
// Determine the lowest visible sprite; exit early if that leaves no sprites visible.
for(; min_sprite < buffer.active_sprite_slot; min_sprite++) {
auto &sprite = buffer.active_sprites[min_sprite];
if(sprite.opaque()) {
break;
}
}
if(min_sprite == buffer.active_sprite_slot) {
return;
}
if(!start) {
// Pre-rasterise the sprites one-by-one.
if(sprites_magnified_) {
for(int index = min_sprite; index < buffer.active_sprite_slot; index++) {
auto &sprite = buffer.active_sprites[index];
for(int c = 0; c < 32; c+= 2) {
const int shift = (c >> 1) ^ 7;
const int bit = 1 & (sprite.image[shift >> 3] >> (shift & 7));
Storage<personality>::sprite_cache_[index][c] =
Storage<personality>::sprite_cache_[index][c + 1] =
(sprite.image[2] & 0xf & sprite_colour_selection_masks[bit]) |
uint8_t((bit << StatusSpriteCollisionShift) & sprite.collision_bit());
}
}
} else {
for(int index = min_sprite; index < buffer.active_sprite_slot; index++) {
auto &sprite = buffer.active_sprites[index];
for(int c = 0; c < 16; c++) {
const int shift = c ^ 7;
const int bit = 1 & (sprite.image[shift >> 3] >> (shift & 7));
Storage<personality>::sprite_cache_[index][c] =
(sprite.image[2] & 0xf & sprite_colour_selection_masks[bit]) |
uint8_t((bit << StatusSpriteCollisionShift) & sprite.collision_bit());
}
}
}
// Go backwards compositing any sprites that are set as OR masks onto their parents.
for(int index = buffer.active_sprite_slot - 1; index >= min_sprite + 1; --index) {
auto &sprite = buffer.active_sprites[index];
if(sprite.opaque()) {
continue;
}
// Sprite may affect all previous up to and cindlugin the next one that is opaque.
for(int previous_index = index - 1; previous_index >= min_sprite; --previous_index) {
// Determine region of overlap (if any).
auto &previous = buffer.active_sprites[previous_index];
const int origin = sprite.x - previous.x;
const int x1 = std::max(0, -origin);
const int x2 = std::min(pixel_width - origin, pixel_width);
// Composite sprites.
for(int x = x1; x < x2; x++) {
Storage<personality>::sprite_cache_[previous_index][x + origin]
|= Storage<personality>::sprite_cache_[index][x];
}
// If a previous opaque sprite has been found, stop.
if(previous.opaque()) {
break;
}
}
}
}
// Draw.
for(int index = buffer.active_sprite_slot - 1; index >= min_sprite; --index) {
auto &sprite = buffer.active_sprites[index];
const int x1 = std::max(0, start - sprite.x);
const int x2 = std::min(end - sprite.x, pixel_width);
for(int x = x1; x < x2; x++) {
const uint8_t colour = Storage<personality>::sprite_cache_[index][x];
// Plot colour, if visible.
if(colour) {
pixel_origin_[sprite.x + x] = palette[colour & 0xf];
}
// TODO: is collision location recorded in mode 1?
// Check for a new collision.
if(!(status_ & StatusSpriteCollision)) {
sprite_collision |= sprite_buffer[sprite.x + x];
sprite_buffer[sprite.x + x] |= colour;
status_ |= sprite_collision & StatusSpriteCollision;
if(status_ & StatusSpriteCollision) {
Storage<personality>::collision_location_[0] = uint16_t(x);
Storage<personality>::collision_location_[1] = uint16_t(y);
}
}
}
}
return;
}
if constexpr (mode == SpriteMode::Mode1) {
for(int index = buffer.active_sprite_slot - 1; index >= min_sprite; --index) {
auto &sprite = buffer.active_sprites[index];
if(sprite.shift_position >= shifter_target) {
continue;
}
const int pixel_start = std::max(start, sprite.x);
for(int c = pixel_start; c < end && sprite.shift_position < shifter_target; ++c) {
const int shift = (sprite.shift_position >> 1) ^ 7;
int sprite_colour = (sprite.image[shift >> 3] >> (shift & 7)) & 1;
// A colision is detected regardless of sprite colour ...
sprite_collision |= sprite_buffer[c] & sprite_colour;
sprite_buffer[c] |= sprite_colour;
// ... but a sprite with the transparent colour won't actually be visible.
sprite_colour &= colour_masks[sprite.image[2] & 0xf];
pixel_origin_[c] =
(pixel_origin_[c] & sprite_colour_selection_masks[sprite_colour^1]) |
(palette[sprite.image[2] & 0xf] & sprite_colour_selection_masks[sprite_colour]);
sprite.shift_position += shift_advance;
}
}
status_ |= sprite_collision << StatusSpriteCollisionShift;
return;
}
}
// Mode 2 logic, as I currently understand it, as a note for my future self:
//
// If a sprite is marked as 'CC' then it doesn't collide, but its colour value is
// ORd with those of all lower-numbered sprites down to the next one that is visible on
// that line and not marked CC.
//
// If no previous sprite meets that criteria, no pixels are displayed. But if one does
// then pixels are displayed even where they don't overlap with the earlier sprites.
//
// ... so in terms of my loop above, I guess I need temporary storage to accumulate
// an OR mask up until I hit a non-CC sprite, at which point I composite everything out?
// I'm not immediately sure whether I can appropriately reuse sprite_buffer, but possibly?
// MARK: - TMS9918
template <Personality personality>
template <SpriteMode sprite_mode>
void Base<personality>::draw_tms_character(int start, int end) {
auto &line_buffer = *draw_line_buffer_;
// Paint the background tiles.
const int pixels_left = end - start;
if(this->screen_mode_ == ScreenMode::MultiColour) {
for(int c = start; c < end; ++c) {
pixel_target_[c] = palette()[
(line_buffer.tiles.patterns[c >> 3][0] >> (((c & 4)^4))) & 15
];
}
} else {
const int shift = start & 7;
int byte_column = start >> 3;
int length = std::min(pixels_left, 8 - shift);
int pattern = Numeric::bit_reverse(line_buffer.tiles.patterns[byte_column][0]) >> shift;
uint8_t colour = line_buffer.tiles.patterns[byte_column][1];
uint32_t colours[2] = {
palette()[(colour & 15) ? (colour & 15) : background_colour_],
palette()[(colour >> 4) ? (colour >> 4) : background_colour_]
};
int background_pixels_left = pixels_left;
while(true) {
background_pixels_left -= length;
for(int c = 0; c < length; ++c) {
pixel_target_[c] = colours[pattern&0x01];
pattern >>= 1;
}
pixel_target_ += length;
if(!background_pixels_left) break;
length = std::min(8, background_pixels_left);
byte_column++;
pattern = Numeric::bit_reverse(line_buffer.tiles.patterns[byte_column][0]);
colour = line_buffer.tiles.patterns[byte_column][1];
colours[0] = palette()[(colour & 15) ? (colour & 15) : background_colour_];
colours[1] = palette()[(colour >> 4) ? (colour >> 4) : background_colour_];
}
}
draw_sprites<sprite_mode, false>(0, start, end, palette()); // TODO: propagate a real 'y' into here.
}
template <Personality personality>
template <bool apply_blink>
void Base<personality>::draw_tms_text(int start, int end) {
auto &line_buffer = *draw_line_buffer_;
uint32_t colours[2][2] = {
{palette()[background_colour_], palette()[text_colour_]},
{0, 0}
};
if constexpr (apply_blink) {
colours[1][0] = palette()[Storage<personality>::blink_background_colour_];
colours[1][1] = palette()[Storage<personality>::blink_text_colour_];
}
const int shift = start % 6;
int byte_column = start / 6;
int pattern = Numeric::bit_reverse(line_buffer.characters.shapes[byte_column]) >> shift;
int pixels_left = end - start;
int length = std::min(pixels_left, 6 - shift);
int flag = 0;
if constexpr (apply_blink) {
flag = (line_buffer.characters.flags[byte_column >> 3] >> ((byte_column & 7) ^ 7)) & Storage<personality>::in_blink_;
}
while(true) {
pixels_left -= length;
for(int c = 0; c < length; ++c) {
pixel_target_[c] = colours[flag][(pattern&0x01)];
pattern >>= 1;
}
pixel_target_ += length;
if(!pixels_left) break;
length = std::min(6, pixels_left);
byte_column++;
pattern = Numeric::bit_reverse(line_buffer.characters.shapes[byte_column]);
if constexpr (apply_blink) {
flag = (line_buffer.characters.flags[byte_column >> 3] >> ((byte_column & 7) ^ 7)) & Storage<personality>::in_blink_;
}
}
}
// MARK: - Master System
template <Personality personality>
void Base<personality>::draw_sms(int start, int end, uint32_t cram_dot) {
if constexpr (is_sega_vdp(personality)) {
int colour_buffer[256];
auto &line_buffer = *draw_line_buffer_;
/*
Add extra border for any pixels that fall before the fine scroll.
*/
int tile_start = start, tile_end = end;
int tile_offset = start;
if(output_pointer_.row >= 16 || !Storage<personality>::horizontal_scroll_lock_) {
for(int c = start; c < (line_buffer.latched_horizontal_scroll & 7); ++c) {
colour_buffer[c] = 16 + background_colour_;
++tile_offset;
}
// Remove the border area from that to which tiles will be drawn.
tile_start = std::max(start - (line_buffer.latched_horizontal_scroll & 7), 0);
tile_end = std::max(end - (line_buffer.latched_horizontal_scroll & 7), 0);
}
uint32_t pattern;
uint8_t *const pattern_index = reinterpret_cast<uint8_t *>(&pattern);
/*
Add background tiles; these will fill the colour_buffer with values in which
the low five bits are a palette index, and bit six is set if this tile has
priority over sprites.
*/
if(tile_start < end) {
const int shift = tile_start & 7;
int byte_column = tile_start >> 3;
int pixels_left = tile_end - tile_start;
int length = std::min(pixels_left, 8 - shift);
pattern = *reinterpret_cast<const uint32_t *>(line_buffer.tiles.patterns[byte_column]);
if(line_buffer.tiles.flags[byte_column]&2)
pattern >>= shift;
else
pattern <<= shift;
while(true) {
const int palette_offset = (line_buffer.tiles.flags[byte_column]&0x18) << 1;
if(line_buffer.tiles.flags[byte_column]&2) {
for(int c = 0; c < length; ++c) {
colour_buffer[tile_offset] =
((pattern_index[3] & 0x01) << 3) |
((pattern_index[2] & 0x01) << 2) |
((pattern_index[1] & 0x01) << 1) |
((pattern_index[0] & 0x01) << 0) |
palette_offset;
++tile_offset;
pattern >>= 1;
}
} else {
for(int c = 0; c < length; ++c) {
colour_buffer[tile_offset] =
((pattern_index[3] & 0x80) >> 4) |
((pattern_index[2] & 0x80) >> 5) |
((pattern_index[1] & 0x80) >> 6) |
((pattern_index[0] & 0x80) >> 7) |
palette_offset;
++tile_offset;
pattern <<= 1;
}
}
pixels_left -= length;
if(!pixels_left) break;
length = std::min(8, pixels_left);
byte_column++;
pattern = *reinterpret_cast<const uint32_t *>(line_buffer.tiles.patterns[byte_column]);
}
}
/*
Apply sprites (if any).
*/
draw_sprites<SpriteMode::MasterSystem, false>(0, start, end, palette(), colour_buffer); // TODO provide good y, as per elsewhere.
// Map from the 32-colour buffer to real output pixels, applying the specific CRAM dot if any.
pixel_target_[start] = Storage<personality>::colour_ram_[colour_buffer[start] & 0x1f] | cram_dot;
for(int c = start+1; c < end; ++c) {
pixel_target_[c] = Storage<personality>::colour_ram_[colour_buffer[c] & 0x1f];
}
// If the VDP is set to hide the left column and this is the final call that'll come
// this line, hide it.
if(end == 256) {
if(Storage<personality>::hide_left_column_) {
pixel_origin_[0] = pixel_origin_[1] = pixel_origin_[2] = pixel_origin_[3] =
pixel_origin_[4] = pixel_origin_[5] = pixel_origin_[6] = pixel_origin_[7] =
Storage<personality>::colour_ram_[16 + background_colour_];
}
}
}
}
// MARK: - Yamaha
template <Personality personality>
template <ScreenMode mode>
void Base<personality>::draw_yamaha(uint8_t y, int start, int end) {
const auto active_palette = palette();
const int sprite_start = start >> 2;
const int sprite_end = end >> 2;
auto &line_buffer = *draw_line_buffer_;
// Observation justifying Duff's device below: it's acceptable to paint too many pixels — to paint
// beyond `end` — provided that the overpainting is within normal bitmap bounds, because any
// mispainted pixels will be replaced before becoming visible to the user.
if constexpr (mode == ScreenMode::YamahaGraphics4 || mode == ScreenMode::YamahaGraphics6) {
start >>= (mode == ScreenMode::YamahaGraphics4) ? 2 : 1;
end >>= (mode == ScreenMode::YamahaGraphics4) ? 2 : 1;
int column = start & ~1;
const int offset = start & 1;
start >>= 1;
end = (end + 1) >> 1;
switch(offset) {
case 0:
do {
pixel_target_[column+0] = active_palette[line_buffer.bitmap[start] >> 4]; [[fallthrough]];
case 1: pixel_target_[column+1] = active_palette[line_buffer.bitmap[start] & 0xf];
++start;
column += 2;
} while(start < end);
}
}
if constexpr (mode == ScreenMode::YamahaGraphics5) {
start >>= 1;
end >>= 1;
int column = start & ~3;
const int offset = start & 3;
start >>= 2;
end = (end + 3) >> 2;
switch(offset) {
case 0:
do {
pixel_target_[column+0] = active_palette[line_buffer.bitmap[start] >> 6]; [[fallthrough]];
case 1: pixel_target_[column+1] = active_palette[(line_buffer.bitmap[start] >> 4) & 3]; [[fallthrough]];
case 2: pixel_target_[column+2] = active_palette[(line_buffer.bitmap[start] >> 2) & 3]; [[fallthrough]];
case 3: pixel_target_[column+3] = active_palette[line_buffer.bitmap[start] & 3];
++start;
column += 4;
} while(start < end);
}
}
if constexpr (mode == ScreenMode::YamahaGraphics7) {
start >>= 2;
end >>= 2;
while(start < end) {
pixel_target_[start] =
palette_pack(
uint8_t((line_buffer.bitmap[start] & 0x1c) + ((line_buffer.bitmap[start] & 0x1c) << 3) + ((line_buffer.bitmap[start] & 0x1c) >> 3)),
uint8_t((line_buffer.bitmap[start] & 0xe0) + ((line_buffer.bitmap[start] & 0xe0) >> 3) + ((line_buffer.bitmap[start] & 0xe0) >> 6)),
uint8_t((line_buffer.bitmap[start] & 0x03) + ((line_buffer.bitmap[start] & 0x03) << 2) + ((line_buffer.bitmap[start] & 0x03) << 4) + ((line_buffer.bitmap[start] & 0x03) << 6))
);
++start;
}
}
constexpr std::array<uint32_t, 16> graphics7_sprite_palette = {
palette_pack(0b00000000, 0b00000000, 0b00000000), palette_pack(0b00000000, 0b00000000, 0b01001001),
palette_pack(0b00000000, 0b01101101, 0b00000000), palette_pack(0b00000000, 0b01101101, 0b01001001),
palette_pack(0b01101101, 0b00000000, 0b00000000), palette_pack(0b01101101, 0b00000000, 0b01001001),
palette_pack(0b01101101, 0b01101101, 0b00000000), palette_pack(0b01101101, 0b01101101, 0b01001001),
palette_pack(0b10010010, 0b11111111, 0b01001001), palette_pack(0b00000000, 0b00000000, 0b11111111),
palette_pack(0b00000000, 0b11111111, 0b00000000), palette_pack(0b00000000, 0b11111111, 0b11111111),
palette_pack(0b11111111, 0b00000000, 0b00000000), palette_pack(0b11111111, 0b00000000, 0b11111111),
palette_pack(0b11111111, 0b11111111, 0b00000000), palette_pack(0b11111111, 0b11111111, 0b11111111),
};
// Possibly TODO: is the data-sheet trying to allege some sort of colour mixing for sprites in Mode 6?
draw_sprites<
SpriteMode::Mode2,
mode == ScreenMode::YamahaGraphics5 || mode == ScreenMode::YamahaGraphics6
>(y, sprite_start, sprite_end, mode == ScreenMode::YamahaGraphics7 ? graphics7_sprite_palette : palette());
}
template <Personality personality>
void Base<personality>::draw_yamaha(uint8_t y, int start, int end) {
if constexpr (is_yamaha_vdp(personality)) {
switch(draw_line_buffer_->screen_mode) {
// Modes that are the same (or close enough) to those on the TMS.
case ScreenMode::Text: draw_tms_text<false>(start >> 2, end >> 2); break;
case ScreenMode::YamahaText80: draw_tms_text<true>(start >> 1, end >> 1); break;
case ScreenMode::MultiColour:
case ScreenMode::ColouredText:
case ScreenMode::Graphics: draw_tms_character(start >> 2, end >> 2); break;
case ScreenMode::YamahaGraphics3:
draw_tms_character<SpriteMode::Mode2>(start >> 2, end >> 2);
break;
#define Dispatch(x) case ScreenMode::x: draw_yamaha<ScreenMode::x>(y, start, end); break;
Dispatch(YamahaGraphics4);
Dispatch(YamahaGraphics5);
Dispatch(YamahaGraphics6);
Dispatch(YamahaGraphics7);
#undef Dispatch
default: break;
}
}
}
// MARK: - Mega Drive
// TODO.
}
#endif /* Draw_hpp */

View File

@@ -0,0 +1,809 @@
//
// Fetch.hpp
// Clock Signal
//
// Created by Thomas Harte on 01/01/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#ifndef Fetch_hpp
#define Fetch_hpp
namespace TI::TMS {
/*
Fetching routines follow below; they obey the following rules:
1) input is a start position and an end position; they should perform the proper
operations for the period: start <= time < end.
2) times are measured relative to a 172-cycles-per-line clock (so: they directly
count access windows on the TMS and Master System).
3) within each sequencer, time 0 is the access window that straddles the beginning of
horizontal sync. Which, conveniently, is the place to which Grauw's timing diagrams
are aligned.
4) all of these functions are templated with a `use_end` parameter. That will be true if
end is < 172, false otherwise. So functions can use it to eliminate should-exit-not checks,
for the more usual path of execution.
[Historically:
position 0 was the beginning of the access window immediately after the last pattern/data
block fetch that would contribute to this line, in a normal 32-column mode. So:
* it's cycle 309 on Mattias' TMS diagram;
* it's cycle 1238 on his V9938 diagram;
* it's after the last background render block in Mask of Destiny's Master System timing diagram.
That division point was selected, albeit arbitrarily, because it puts all the tile
fetches for a single line into the same [0, 171] period.
I'm moving away from this per the desire not to have V9938 output straddle two lines if horizontally-adjusted,
amongst other concerns.]
Provided for the benefit of the methods below:
* the function external_slot(), which will perform any pending VRAM read/write.
* the macros slot(n) and external_slot(n) which can be used to schedule those things inside a
switch(start)-based implementation.
All functions should just spool data to intermediary storage. This is because for most VDPs there is
a decoupling between fetch pattern and output pattern, and it's neater to keep the same division
for the exceptions.
*/
// MARK: - Address mask helpers.
/// @returns An instance of @c AddressT with all top bits set down to and including
/// bit @c end and all others clear.
///
/// So e.g. if @c AddressT is @c uint16_t and this VDP has a 15-bit address space then
/// @c top_bits<10> will be the address with bits 15 to 10 (inclusive) set and the rest clear.
template <typename AddressT, int end> constexpr AddressT top_bits() {
return AddressT(~0) - AddressT((1 << end) - 1);
}
/// Modifies and returns @c source so that all bits above position @c n are set; the others are unmodified.
template <int n, typename AddressT> constexpr AddressT bits(AddressT source = 0) {
return AddressT(source | top_bits<AddressT, n>());
}
// MARK: - 171-window Dispatcher.
template <Personality personality>
template<bool use_end, typename SequencerT> void Base<personality>::dispatch(SequencerT &fetcher, int start, int end) {
#define index(n) \
if(use_end && end == n) return; \
[[fallthrough]]; \
case n: fetcher.template fetch<n>();
switch(start) {
default: assert(false);
index(0); index(1); index(2); index(3); index(4); index(5); index(6); index(7); index(8); index(9);
index(10); index(11); index(12); index(13); index(14); index(15); index(16); index(17); index(18); index(19);
index(20); index(21); index(22); index(23); index(24); index(25); index(26); index(27); index(28); index(29);
index(30); index(31); index(32); index(33); index(34); index(35); index(36); index(37); index(38); index(39);
index(40); index(41); index(42); index(43); index(44); index(45); index(46); index(47); index(48); index(49);
index(50); index(51); index(52); index(53); index(54); index(55); index(56); index(57); index(58); index(59);
index(60); index(61); index(62); index(63); index(64); index(65); index(66); index(67); index(68); index(69);
index(70); index(71); index(72); index(73); index(74); index(75); index(76); index(77); index(78); index(79);
index(80); index(81); index(82); index(83); index(84); index(85); index(86); index(87); index(88); index(89);
index(90); index(91); index(92); index(93); index(94); index(95); index(96); index(97); index(98); index(99);
index(100); index(101); index(102); index(103); index(104); index(105); index(106); index(107); index(108); index(109);
index(110); index(111); index(112); index(113); index(114); index(115); index(116); index(117); index(118); index(119);
index(120); index(121); index(122); index(123); index(124); index(125); index(126); index(127); index(128); index(129);
index(130); index(131); index(132); index(133); index(134); index(135); index(136); index(137); index(138); index(139);
index(140); index(141); index(142); index(143); index(144); index(145); index(146); index(147); index(148); index(149);
index(150); index(151); index(152); index(153); index(154); index(155); index(156); index(157); index(158); index(159);
index(160); index(161); index(162); index(163); index(164); index(165); index(166); index(167); index(168); index(169);
index(170);
}
#undef index
}
// MARK: - Fetchers.
template <Personality personality>
struct TextFetcher {
using AddressT = typename Base<personality>::AddressT;
TextFetcher(Base<personality> *base, uint8_t y) :
base(base),
row_base(base->pattern_name_address_ & bits<10>(AddressT((y >> 3) * 40))),
row_offset(base->pattern_generator_table_address_ & bits<11>(AddressT(y & 7))) {}
void fetch_name(AddressT column, int slot = 0) {
base->name_[slot] = base->ram_[row_base + column];
}
void fetch_pattern(AddressT column, int slot = 0) {
base->fetch_line_buffer_->characters.shapes[column] = base->ram_[row_offset + size_t(base->name_[slot] << 3)];
}
Base<personality> *const base;
const AddressT row_base;
const AddressT row_offset;
};
template <Personality personality>
struct CharacterFetcher {
using AddressT = typename Base<personality>::AddressT;
CharacterFetcher(Base<personality> *base, uint8_t y) :
base(base),
y(y),
row_base(base->pattern_name_address_ & bits<10>(AddressT((y << 2)&~31)))
{
pattern_base = base->pattern_generator_table_address_;
colour_base = base->colour_table_address_;
colour_name_shift = 6;
const ScreenMode mode = base->fetch_line_buffer_->screen_mode;
if(mode == ScreenMode::Graphics || mode == ScreenMode::YamahaGraphics3) {
// If this is high resolution mode, allow the row number to affect the pattern and colour addresses.
pattern_base &= bits<13>(AddressT(((y & 0xc0) << 5)));
colour_base &= bits<13>(AddressT(((y & 0xc0) << 5)));
colour_base += AddressT(y & 7);
colour_name_shift = 0;
} else {
colour_base &= bits<6, AddressT>();
pattern_base &= bits<11, AddressT>();
}
if(mode == ScreenMode::MultiColour) {
pattern_base += AddressT((y >> 2) & 7);
} else {
pattern_base += AddressT(y & 7);
}
}
void fetch_name(int column) {
base->tile_offset_ = base->ram_[row_base + AddressT(column)];
}
void fetch_pattern(int column) {
base->fetch_line_buffer_->tiles.patterns[column][0] = base->ram_[pattern_base + AddressT(base->tile_offset_ << 3)];
}
void fetch_colour(int column) {
base->fetch_line_buffer_->tiles.patterns[column][1] = base->ram_[colour_base + AddressT((base->tile_offset_ << 3) >> colour_name_shift)];
}
Base<personality> *const base;
const uint8_t y;
const AddressT row_base;
AddressT pattern_base;
AddressT colour_base;
int colour_name_shift;
};
constexpr SpriteMode sprite_mode(ScreenMode screen_mode) {
switch(screen_mode) {
default:
return SpriteMode::Mode2;
case ScreenMode::MultiColour:
case ScreenMode::ColouredText:
case ScreenMode::Graphics:
return SpriteMode::Mode1;
case ScreenMode::SMSMode4:
return SpriteMode::MasterSystem;
}
}
// TODO: should this be extended to include Master System sprites?
template <Personality personality, SpriteMode mode>
class SpriteFetcher {
public:
using AddressT = typename Base<personality>::AddressT;
// The Yamaha VDP adds an additional table when in Sprite Mode 2, the sprite colour
// table, which is intended to fill the 512 bytes before the programmer-located sprite
// attribute table.
//
// It partially enforces this proximity by forcing bits 7 and 8 to 0 in the address of
// the attribute table, and forcing them to 1 but masking out bit 9 for the colour table.
//
// AttributeAddressMask is used to enable or disable that behaviour.
static constexpr AddressT AttributeAddressMask = (mode == SpriteMode::Mode2) ? AddressT(~0x180) : AddressT(~0x000);
SpriteFetcher(Base<personality> *base, uint8_t y) :
base(base),
y(y) {}
void fetch_location(int slot) {
fetch_xy(slot);
if constexpr (mode == SpriteMode::Mode2) {
fetch_xy(slot + 1);
base->name_[0] = name(slot);
base->name_[1] = name(slot + 1);
}
}
void fetch_pattern(int slot) {
switch(mode) {
case SpriteMode::Mode1:
fetch_image(slot, name(slot));
break;
case SpriteMode::Mode2:
fetch_image(slot, base->name_[0]);
fetch_image(slot + 1, base->name_[1]);
break;
}
}
void fetch_y(int sprite) {
const AddressT address = base->sprite_attribute_table_address_ & AttributeAddressMask & bits<7>(AddressT(sprite << 2));
const uint8_t sprite_y = base->ram_[address];
base->posit_sprite(sprite, sprite_y, y);
}
private:
void fetch_xy(int slot) {
auto &buffer = *base->fetch_sprite_buffer_;
buffer.active_sprites[slot].x =
base->ram_[
base->sprite_attribute_table_address_ & AttributeAddressMask & bits<7>(AddressT((buffer.active_sprites[slot].index << 2) | 1))
];
}
uint8_t name(int slot) {
auto &buffer = *base->fetch_sprite_buffer_;
const AddressT address =
base->sprite_attribute_table_address_ &
AttributeAddressMask &
bits<7>(AddressT((buffer.active_sprites[slot].index << 2) | 2));
const uint8_t name = base->ram_[address] & (base->sprites_16x16_ ? ~3 : ~0);
return name;
}
void fetch_image(int slot, uint8_t name) {
uint8_t colour = 0;
auto &sprite = base->fetch_sprite_buffer_->active_sprites[slot];
switch(mode) {
case SpriteMode::Mode1:
// Fetch colour from the attribute table, per this sprite's slot.
colour = base->ram_[
base->sprite_attribute_table_address_ & bits<7>(AddressT((sprite.index << 2) | 3))
];
break;
case SpriteMode::Mode2: {
// Fetch colour from the colour table, per this sprite's slot and row.
const AddressT colour_table_address = (base->sprite_attribute_table_address_ | ~AttributeAddressMask) & AddressT(~0x200);
colour = base->ram_[
colour_table_address &
bits<9>(
AddressT(sprite.index << 4) |
AddressT(sprite.row)
)
];
} break;
}
sprite.image[2] = colour;
sprite.x -= sprite.early_clock();
const AddressT graphic_location = base->sprite_generator_table_address_ & bits<11>(AddressT((name << 3) | sprite.row));
sprite.image[0] = base->ram_[graphic_location];
sprite.image[1] = base->ram_[graphic_location+16];
if constexpr (SpriteBuffer::test_is_filling) {
if(slot == ((mode == SpriteMode::Mode2) ? 7 : 3)) {
base->fetch_sprite_buffer_->is_filling = false;
}
}
}
Base<personality> *const base;
const uint8_t y;
};
template <Personality personality>
struct SMSFetcher {
using AddressT = typename Base<personality>::AddressT;
struct RowInfo {
AddressT pattern_address_base;
AddressT sub_row[2];
};
SMSFetcher(Base<personality> *base, uint8_t y) :
base(base),
storage(static_cast<Storage<personality> *>(base)),
y(y),
horizontal_offset((y >= 16 || !storage->horizontal_scroll_lock_) ? (base->fetch_line_buffer_->latched_horizontal_scroll >> 3) : 0)
{
// Limit address bits in use if this is a SMS2 mode.
const bool is_tall_mode = base->mode_timing_.pixel_lines != 192;
const AddressT pattern_name_address = storage->pattern_name_address_ | (is_tall_mode ? 0x800 : 0);
const AddressT pattern_name_offset = is_tall_mode ? 0x100 : 0;
// Determine row info for the screen both (i) if vertical scrolling is applied; and (ii) if it isn't.
// The programmer can opt out of applying vertical scrolling to the right-hand portion of the display.
const int scrolled_row = (y + storage->latched_vertical_scroll_) % (is_tall_mode ? 256 : 224);
scrolled_row_info.pattern_address_base = (pattern_name_address & bits<11>(AddressT((scrolled_row & ~7) << 3))) - pattern_name_offset;
scrolled_row_info.sub_row[0] = AddressT((scrolled_row & 7) << 2);
scrolled_row_info.sub_row[1] = AddressT(28 ^ ((scrolled_row & 7) << 2));
if(storage->vertical_scroll_lock_) {
static_row_info.pattern_address_base = bits<11>(AddressT(pattern_name_address & ((y & ~7) << 3))) - pattern_name_offset;
static_row_info.sub_row[0] = AddressT((y & 7) << 2);
static_row_info.sub_row[1] = 28 ^ AddressT((y & 7) << 2);
} else static_row_info = scrolled_row_info;
}
void fetch_sprite(int sprite) {
auto &sprite_buffer = *base->fetch_sprite_buffer_;
sprite_buffer.active_sprites[sprite].x =
base->ram_[
storage->sprite_attribute_table_address_ & bits<7>((sprite_buffer.active_sprites[sprite].index << 1) | 0)
] - (storage->shift_sprites_8px_left_ ? 8 : 0);
const uint8_t name = base->ram_[
storage->sprite_attribute_table_address_ & bits<7>((sprite_buffer.active_sprites[sprite].index << 1) | 1)
] & (base->sprites_16x16_ ? ~1 : ~0);
const AddressT graphic_location =
storage->sprite_generator_table_address_ &
bits<13>(AddressT((name << 5) | (sprite_buffer.active_sprites[sprite].row << 2)));
sprite_buffer.active_sprites[sprite].image[0] = base->ram_[graphic_location];
sprite_buffer.active_sprites[sprite].image[1] = base->ram_[graphic_location+1];
sprite_buffer.active_sprites[sprite].image[2] = base->ram_[graphic_location+2];
sprite_buffer.active_sprites[sprite].image[3] = base->ram_[graphic_location+3];
}
void fetch_tile_name(int column) {
const RowInfo &row_info = column < 24 ? scrolled_row_info : static_row_info;
const size_t scrolled_column = (column - horizontal_offset) & 0x1f;
const size_t address = row_info.pattern_address_base + (scrolled_column << 1);
auto &line_buffer = *base->fetch_line_buffer_;
line_buffer.tiles.flags[column] = base->ram_[address+1];
base->tile_offset_ = AddressT(
(((line_buffer.tiles.flags[column]&1) << 8) | base->ram_[address]) << 5
) + row_info.sub_row[(line_buffer.tiles.flags[column]&4) >> 2];
}
void fetch_tile_pattern(int column) {
auto &line_buffer = *base->fetch_line_buffer_;
line_buffer.tiles.patterns[column][0] = base->ram_[base->tile_offset_];
line_buffer.tiles.patterns[column][1] = base->ram_[base->tile_offset_+1];
line_buffer.tiles.patterns[column][2] = base->ram_[base->tile_offset_+2];
line_buffer.tiles.patterns[column][3] = base->ram_[base->tile_offset_+3];
}
void posit_sprite(int sprite) {
base->posit_sprite(sprite, base->ram_[storage->sprite_attribute_table_address_ & bits<8>(AddressT(sprite))], y);
}
Base<personality> *const base;
const Storage<personality> *const storage;
const uint8_t y;
const int horizontal_offset;
RowInfo scrolled_row_info, static_row_info;
};
// MARK: - TMS Sequencers.
template <Personality personality>
struct RefreshSequencer {
RefreshSequencer(Base<personality> *base) : base(base) {}
template <int cycle> void fetch() {
if(cycle < 26 || (cycle & 1) || cycle >= 154) {
base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
}
}
Base<personality> *const base;
};
template <Personality personality>
struct TextSequencer {
template <typename... Args> TextSequencer(Args&&... args) : fetcher(std::forward<Args>(args)...) {}
template <int cycle> void fetch() {
// The first 30 and the final 4 slots are external.
if constexpr (cycle < 30 || cycle >= 150) {
fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
return;
} else {
// For the 120 slots in between follow a three-step pattern of:
constexpr int offset = cycle - 30;
constexpr auto column = AddressT(offset / 3);
switch(offset % 3) {
case 0: fetcher.fetch_name(column); break; // (1) fetch tile name.
case 1: fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle)); break; // (2) external slot.
case 2: fetcher.fetch_pattern(column); break; // (3) fetch tile pattern.
}
}
}
using AddressT = typename Base<personality>::AddressT;
TextFetcher<personality> fetcher;
};
template <Personality personality>
struct CharacterSequencer {
template <typename... Args> CharacterSequencer(Args&&... args) :
character_fetcher(std::forward<Args>(args)...),
sprite_fetcher(std::forward<Args>(args)...) {}
template <int cycle> void fetch() {
if(cycle < 5) {
character_fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
}
if(cycle == 5) {
// Fetch: n1, c2, pat2a, pat2b, y3, x3, n3, c3, pat3a, pat3b.
sprite_fetcher.fetch_pattern(2);
sprite_fetcher.fetch_location(3);
sprite_fetcher.fetch_pattern(3);
}
if(cycle > 14 && cycle < 19) {
character_fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
}
// Fetch 8 new sprite Y coordinates, to begin selecting sprites for next line.
if(cycle == 19) {
sprite_fetcher.fetch_y(0); sprite_fetcher.fetch_y(1); sprite_fetcher.fetch_y(2); sprite_fetcher.fetch_y(3);
sprite_fetcher.fetch_y(4); sprite_fetcher.fetch_y(5); sprite_fetcher.fetch_y(6); sprite_fetcher.fetch_y(7);
}
// Body of line: tiles themselves, plus some additional potential sprites.
if(cycle >= 27 && cycle < 155) {
constexpr int offset = cycle - 27;
constexpr int block = offset >> 2;
constexpr int sub_block = offset & 3;
switch(sub_block) {
case 0: character_fetcher.fetch_name(block); break;
case 1:
if(!(block & 3)) {
character_fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
} else {
constexpr int sprite = 8 + ((block >> 2) * 3) + ((block & 3) - 1);
sprite_fetcher.fetch_y(sprite);
}
break;
case 2:
character_fetcher.fetch_pattern(block);
character_fetcher.fetch_colour(block);
break;
default: break;
}
}
if(cycle >= 155 && cycle < 157) {
character_fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
}
if(cycle == 157) {
// Fetch: y0, x0, n0, c0, pat0a, pat0b, y1, x1, n1, c1, pat1a, pat1b, y2, x2.
sprite_fetcher.fetch_location(0);
sprite_fetcher.fetch_pattern(0);
sprite_fetcher.fetch_location(1);
sprite_fetcher.fetch_pattern(1);
sprite_fetcher.fetch_location(2);
}
}
using AddressT = typename Base<personality>::AddressT;
CharacterFetcher<personality> character_fetcher;
SpriteFetcher<personality, SpriteMode::Mode1> sprite_fetcher;
};
// MARK: - TMS fetch routines.
template <Personality personality>
template<bool use_end> void Base<personality>::fetch_tms_refresh(uint8_t, int start, int end) {
RefreshSequencer sequencer(this);
dispatch<use_end>(sequencer, start, end);
}
template <Personality personality>
template<bool use_end> void Base<personality>::fetch_tms_text(uint8_t y, int start, int end) {
TextSequencer<personality> sequencer(this, y);
dispatch<use_end>(sequencer, start, end);
}
template <Personality personality>
template<bool use_end> void Base<personality>::fetch_tms_character(uint8_t y, int start, int end) {
CharacterSequencer<personality> sequencer(this, y);
dispatch<use_end>(sequencer, start, end);
}
// MARK: - Master System
template <Personality personality>
struct SMSSequencer {
template <typename... Args> SMSSequencer(Args&&... args) : fetcher(std::forward<Args>(args)...) {}
// Cf. https://www.smspower.org/forums/16485-GenesisMode4VRAMTiming with this implementation pegging
// window 0 to HSYNC low.
template <int cycle> void fetch() {
if(cycle < 3) {
fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
}
if(cycle == 3) {
fetcher.fetch_sprite(4);
fetcher.fetch_sprite(5);
fetcher.fetch_sprite(6);
fetcher.fetch_sprite(7);
}
if(cycle == 15 || cycle == 16) {
fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
}
if(cycle == 17) {
fetcher.posit_sprite(0); fetcher.posit_sprite(1); fetcher.posit_sprite(2); fetcher.posit_sprite(3);
fetcher.posit_sprite(4); fetcher.posit_sprite(5); fetcher.posit_sprite(6); fetcher.posit_sprite(7);
fetcher.posit_sprite(8); fetcher.posit_sprite(9); fetcher.posit_sprite(10); fetcher.posit_sprite(11);
fetcher.posit_sprite(12); fetcher.posit_sprite(13); fetcher.posit_sprite(14); fetcher.posit_sprite(15);
}
if(cycle >= 25 && cycle < 153) {
constexpr int offset = cycle - 25;
constexpr int block = offset >> 2;
constexpr int sub_block = offset & 3;
switch(sub_block) {
default: break;
case 0: fetcher.fetch_tile_name(block); break;
case 1:
if(!(block & 3)) {
fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
} else {
constexpr int sprite = (8 + ((block >> 2) * 3) + ((block & 3) - 1)) << 1;
fetcher.posit_sprite(sprite);
fetcher.posit_sprite(sprite+1);
}
break;
case 2: fetcher.fetch_tile_pattern(block); break;
}
}
if(cycle >= 153 && cycle < 157) {
fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
}
if(cycle == 157) {
fetcher.fetch_sprite(0);
fetcher.fetch_sprite(1);
fetcher.fetch_sprite(2);
fetcher.fetch_sprite(3);
}
if(cycle >= 169) {
fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
}
}
using AddressT = typename Base<personality>::AddressT;
SMSFetcher<personality> fetcher;
};
template <Personality personality>
template<bool use_end> void Base<personality>::fetch_sms(uint8_t y, int start, int end) {
if constexpr (is_sega_vdp(personality)) {
SMSSequencer<personality> sequencer(this, y);
dispatch<use_end>(sequencer, start, end);
}
}
// MARK: - Yamaha
template <Personality personality>
template<ScreenMode mode> void Base<personality>::fetch_yamaha(uint8_t y, int end) {
CharacterFetcher character_fetcher(this, y);
TextFetcher text_fetcher(this, y);
SpriteFetcher<personality, sprite_mode(mode)> sprite_fetcher(this, y);
using Type = typename Storage<personality>::Event::Type;
while(Storage<personality>::next_event_->offset < end) {
switch(Storage<personality>::next_event_->type) {
case Type::External:
do_external_slot(Storage<personality>::next_event_->offset);
break;
case Type::Name:
switch(mode) {
case ScreenMode::Text: {
const auto column = AddressT(Storage<personality>::next_event_->id << 1);
text_fetcher.fetch_name(column, 0);
text_fetcher.fetch_name(column + 1, 1);
} break;
case ScreenMode::YamahaText80: {
const auto column = AddressT(Storage<personality>::next_event_->id << 2);
const auto start = pattern_name_address_ & bits<12>(AddressT((y >> 3) * 80));
name_[0] = ram_[start + column + 0];
name_[1] = ram_[start + column + 1];
name_[2] = ram_[start + column + 2];
name_[3] = ram_[start + column + 3];
} break;
case ScreenMode::Graphics:
case ScreenMode::MultiColour:
case ScreenMode::ColouredText:
character_fetcher.fetch_name(Storage<personality>::next_event_->id);
break;
default: break;
}
break;
case Type::Colour:
switch(mode) {
case ScreenMode::YamahaText80: {
const auto column = AddressT(Storage<personality>::next_event_->id);
const auto address = colour_table_address_ & bits<9>(AddressT((y >> 3) * 10));
auto &line_buffer = *fetch_line_buffer_;
line_buffer.characters.flags[column] = ram_[address + column];
} break;
case ScreenMode::Graphics:
case ScreenMode::MultiColour:
case ScreenMode::ColouredText:
character_fetcher.fetch_colour(Storage<personality>::next_event_->id);
break;
default: break;
}
break;
case Type::Pattern:
switch(mode) {
case ScreenMode::Text: {
const auto column = AddressT(Storage<personality>::next_event_->id << 1);
text_fetcher.fetch_pattern(column, 0);
text_fetcher.fetch_pattern(column + 1, 1);
} break;
case ScreenMode::YamahaText80: {
const auto column = Storage<personality>::next_event_->id << 2;
const auto start = pattern_generator_table_address_ & bits<11>(AddressT(y & 7));
auto &line_buffer = *fetch_line_buffer_;
line_buffer.characters.shapes[column + 0] = ram_[start + AddressT(name_[0] << 3)];
line_buffer.characters.shapes[column + 1] = ram_[start + AddressT(name_[1] << 3)];
line_buffer.characters.shapes[column + 2] = ram_[start + AddressT(name_[2] << 3)];
line_buffer.characters.shapes[column + 3] = ram_[start + AddressT(name_[3] << 3)];
} break;
case ScreenMode::Graphics:
case ScreenMode::MultiColour:
case ScreenMode::ColouredText:
character_fetcher.fetch_pattern(Storage<personality>::next_event_->id);
break;
case ScreenMode::YamahaGraphics3:
// As per comment elsewhere; my _guess_ is that G3 is slotted as if it were
// a bitmap mode, with the three bytes that describe each column fitting into
// the relevant windows.
character_fetcher.fetch_name(Storage<personality>::next_event_->id);
character_fetcher.fetch_colour(Storage<personality>::next_event_->id);
character_fetcher.fetch_pattern(Storage<personality>::next_event_->id);
break;
case ScreenMode::YamahaGraphics4:
case ScreenMode::YamahaGraphics5: {
const int column = Storage<personality>::next_event_->id << 2;
const auto start = bits<15>((y << 7) | column);
auto &line_buffer = *fetch_line_buffer_;
line_buffer.bitmap[column + 0] = ram_[pattern_name_address_ & AddressT(start + 0)];
line_buffer.bitmap[column + 1] = ram_[pattern_name_address_ & AddressT(start + 1)];
line_buffer.bitmap[column + 2] = ram_[pattern_name_address_ & AddressT(start + 2)];
line_buffer.bitmap[column + 3] = ram_[pattern_name_address_ & AddressT(start + 3)];
} break;
case ScreenMode::YamahaGraphics6:
case ScreenMode::YamahaGraphics7: {
const uint8_t *const ram2 = &ram_[65536];
const int column = Storage<personality>::next_event_->id << 3;
const auto start = bits<15>((y << 7) | (column >> 1));
auto &line_buffer = *fetch_line_buffer_;
// Fetch from alternate banks.
line_buffer.bitmap[column + 0] = ram_[pattern_name_address_ & AddressT(start + 0) & 0xffff];
line_buffer.bitmap[column + 1] = ram2[pattern_name_address_ & AddressT(start + 0) & 0xffff];
line_buffer.bitmap[column + 2] = ram_[pattern_name_address_ & AddressT(start + 1) & 0xffff];
line_buffer.bitmap[column + 3] = ram2[pattern_name_address_ & AddressT(start + 1) & 0xffff];
line_buffer.bitmap[column + 4] = ram_[pattern_name_address_ & AddressT(start + 2) & 0xffff];
line_buffer.bitmap[column + 5] = ram2[pattern_name_address_ & AddressT(start + 2) & 0xffff];
line_buffer.bitmap[column + 6] = ram_[pattern_name_address_ & AddressT(start + 3) & 0xffff];
line_buffer.bitmap[column + 7] = ram2[pattern_name_address_ & AddressT(start + 3) & 0xffff];
} break;
default: break;
}
break;
case Type::SpriteY:
switch(mode) {
case ScreenMode::Blank:
case ScreenMode::Text:
case ScreenMode::YamahaText80:
// Ensure the compiler can discard character_fetcher in these modes.
break;
default:
sprite_fetcher.fetch_y(Storage<personality>::next_event_->id);
break;
}
break;
case Type::SpriteLocation:
switch(mode) {
case ScreenMode::Blank:
case ScreenMode::Text:
case ScreenMode::YamahaText80:
// Ensure the compiler can discard character_fetcher in these modes.
break;
default:
sprite_fetcher.fetch_location(Storage<personality>::next_event_->id);
break;
}
break;
case Type::SpritePattern:
switch(mode) {
case ScreenMode::Blank:
case ScreenMode::Text:
case ScreenMode::YamahaText80:
// Ensure the compiler can discard character_fetcher in these modes.
break;
default:
sprite_fetcher.fetch_pattern(Storage<personality>::next_event_->id);
break;
}
break;
default: break;
}
++Storage<personality>::next_event_;
}
}
template <Personality personality>
template<bool use_end> void Base<personality>::fetch_yamaha(uint8_t y, int, int end) {
if constexpr (is_yamaha_vdp(personality)) {
// Dispatch according to [supported] screen mode.
#define Dispatch(mode) case mode: fetch_yamaha<mode>(y, end); break;
switch(fetch_line_buffer_->screen_mode) {
default: break;
Dispatch(ScreenMode::Blank);
Dispatch(ScreenMode::Text);
Dispatch(ScreenMode::MultiColour);
Dispatch(ScreenMode::ColouredText);
Dispatch(ScreenMode::Graphics);
Dispatch(ScreenMode::YamahaText80);
Dispatch(ScreenMode::YamahaGraphics3);
Dispatch(ScreenMode::YamahaGraphics4);
Dispatch(ScreenMode::YamahaGraphics5);
Dispatch(ScreenMode::YamahaGraphics6);
Dispatch(ScreenMode::YamahaGraphics7);
}
#undef Dispatch
}
}
// MARK: - Mega Drive
// TODO.
}
#endif /* Fetch_hpp */

View File

@@ -0,0 +1,132 @@
//
// LineBuffer.hpp
// Clock Signal
//
// Created by Thomas Harte on 12/02/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#ifndef LineBuffer_hpp
#define LineBuffer_hpp
#include "AccessEnums.hpp"
namespace TI::TMS {
// Temporary buffers collect a representation of each line prior to pixel serialisation.
struct SpriteBuffer {
// An active sprite is one that has been selected for composition onto
// _this_ line.
struct ActiveSprite {
int index = 0; // The original in-table index of this sprite.
int row = 0; // The row of the sprite that should be drawn.
int x = 0; // The sprite's x position on screen.
uint8_t image[4]; // Up to four bytes of image information.
//
// In practice:
//
// Master System mode: the four bytes of this 8x8 sprite;
// TMS and Yamaha: [0] = the left half of this sprite; [1] = the right side (if 16x16 sprites enabled); [2] = colour, early-clock bit, etc.
int shift_position = 0; // An offset representing how much of the image information has already been drawn.
// Yamaha helpers.
bool opaque() const {
return !(image[2] & 0x40);
}
/// @returns @c 0x20 if this sprite should generate collisions; @c 0x00 otherwise.
int collision_bit() const {
return ((image[2] & 0x20) | ((image[2] & 0x40) >> 1)) ^ 0x20;
}
// Yamaha and TMS helpers.
int early_clock() const {
return (image[2] & 0x80) >> 2;
}
} active_sprites[8];
int active_sprite_slot = 0; // A pointer to the slot into which a new active sprite will be deposited, if required.
bool sprites_stopped = false; // A special TMS feature is that a sentinel value can be used to prevent any further sprites
// being evaluated for display. This flag determines whether the sentinel has yet been reached.
uint8_t sprite_terminator = 0;
#ifndef NDEBUG
static constexpr bool test_is_filling = true;
#else
static constexpr bool test_is_filling = false;
#endif
bool is_filling = false;
void reset_sprite_collection();
};
struct LineBuffer {
LineBuffer() {}
// The fetch mode describes the proper timing diagram for this line;
// screen mode captures proper output mode.
FetchMode fetch_mode = FetchMode::Text;
ScreenMode screen_mode = ScreenMode::Text;
VerticalState vertical_state = VerticalState::Blank;
SpriteBuffer *sprites = nullptr;
// Holds the horizontal scroll position to apply to this line;
// of those VDPs currently implemented, affects the Master System only.
uint8_t latched_horizontal_scroll = 0;
// The names array holds pattern names, as an offset into memory, and
// potentially flags also.
union {
// This struct captures maximal potential detail across the TMS9918
// and Sega VDP for tiled modes (plus multicolour).
struct {
uint8_t flags[32]{};
// The patterns array holds tile patterns, corresponding 1:1 with names.
// Four bytes per pattern is the maximum required by any
// currently-implemented VDP.
uint8_t patterns[32][4]{};
} tiles;
// The Yamaha and TMS both have text modes, with the former going up to
// 80 columns plus 10 bytes of colour-esque flags.
struct {
uint8_t shapes[80];
uint8_t flags[10];
} characters;
// The Yamaha VDP also has a variety of bitmap modes,
// the widest of which is 512px @ 4bpp.
uint8_t bitmap[256];
};
/*
Horizontal layout (on a 342-cycle clock):
15 cycles right border
58 cycles blanking & sync
13 cycles left border
... i.e. to cycle 86, then:
border up to first_pixel_output_column;
pixels up to next_border_column;
border up to the end.
e.g. standard 256-pixel modes will want to set
first_pixel_output_column = 86, next_border_column = 342.
*/
int first_pixel_output_column = 94;
int next_border_column = 334;
int pixel_count = 256;
};
struct LineBufferPointer {
int row = 0, column = 0;
};
}
#endif /* LineBuffer_hpp */

View File

@@ -0,0 +1,50 @@
//
// PersonalityTraits.hpp
// Clock Signal
//
// Created by Thomas Harte on 06/01/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#ifndef PersonalityTraits_hpp
#define PersonalityTraits_hpp
namespace TI::TMS {
// Genus determinants for the various personalityes.
constexpr bool is_sega_vdp(Personality p) {
return p >= Personality::SMSVDP;
}
constexpr bool is_yamaha_vdp(Personality p) {
return p == Personality::V9938 || p == Personality::V9958;
}
// i.e. one with the original internal timings.
constexpr bool is_classic_vdp(Personality p) {
return
p == Personality::TMS9918A ||
p == Personality::SMSVDP ||
p == Personality::SMS2VDP ||
p == Personality::GGVDP;
}
constexpr size_t memory_size(Personality p) {
switch(p) {
case TI::TMS::TMS9918A:
case TI::TMS::SMSVDP:
case TI::TMS::SMS2VDP:
case TI::TMS::GGVDP: return 16 * 1024;
case TI::TMS::MDVDP: return 64 * 1024;
case TI::TMS::V9938: return 128 * 1024;
case TI::TMS::V9958: return 192 * 1024;
}
}
constexpr size_t memory_mask(Personality p) {
return memory_size(p) - 1;
}
}
#endif /* PersonalityTraits_hpp */

View File

@@ -0,0 +1,488 @@
//
//
// Storage.hpp
// Clock Signal
//
// Created by Thomas Harte on 12/02/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#ifndef Storage_h
#define Storage_h
#include "LineBuffer.hpp"
#include "YamahaCommands.hpp"
#include <optional>
#include <vector>
namespace TI::TMS {
/// A container for personality-specific storage; see specific instances below.
template <Personality personality, typename Enable = void> struct Storage {
};
template <> struct Storage<Personality::TMS9918A> {
using AddressT = uint16_t;
void begin_line(ScreenMode, bool) {}
};
struct YamahaFetcher {
public:
/// Describes an _observable_ memory access event. i.e. anything that it is safe
/// (and convenient) to treat as atomic in between external slots.
struct Event {
/// Offset of the _beginning_ of the event. Not completely arbitrarily: this is when
/// external data must be ready by in order to take part in those slots.
uint16_t offset = 1368;
enum class Type: uint8_t {
/// A slot for reading or writing data on behalf of the CPU or the command engine.
External,
//
// Sprites.
//
SpriteY,
SpriteLocation,
SpritePattern,
//
// Backgrounds.
//
Name,
Colour,
Pattern,
} type = Type::External;
uint8_t id = 0;
constexpr Event(Type type, uint8_t id = 0) noexcept :
type(type),
id(id) {}
constexpr Event() noexcept {}
};
// State that tracks fetching position within a line.
const Event *next_event_ = nullptr;
// Sprite collection state.
bool sprites_enabled_ = true;
protected:
/// @return 1 + the number of times within a line that @c GeneratorT produces an event.
template <typename GeneratorT> static constexpr size_t events_size() {
size_t size = 0;
for(int c = 0; c < 1368; c++) {
const auto event_type = GeneratorT::event(c);
size += event_type.has_value();
}
return size + 1;
}
/// @return An array of all events generated by @c GeneratorT in line order.
template <typename GeneratorT, size_t size = events_size<GeneratorT>()>
static constexpr std::array<Event, size> events() {
std::array<Event, size> result{};
size_t index = 0;
for(int c = 0; c < 1368; c++) {
const auto event = GeneratorT::event(c);
if(!event) {
continue;
}
result[index] = *event;
result[index].offset = uint16_t(c);
++index;
}
result[index] = Event();
return result;
}
struct StandardGenerators {
static constexpr std::optional<Event> external_every_eight(int index) {
if(index & 7) return std::nullopt;
return Event::Type::External;
}
};
struct RefreshGenerator {
static constexpr std::optional<Event> event(int grauw_index) {
// From 0 to 126: CPU/CMD slots at every cycle divisible by 8.
if(grauw_index < 126) {
return StandardGenerators::external_every_eight(grauw_index - 0);
}
// From 164 to 1234: eight-cycle windows, the first 15 of each 16 being
// CPU/CMD and the final being refresh.
if(grauw_index >= 164 && grauw_index < 1234) {
const int offset = grauw_index - 164;
if(offset & 7) return std::nullopt;
if(((offset >> 3) & 15) == 15) return std::nullopt;
return Event::Type::External;
}
// From 1268 to 1330: CPU/CMD slots at every cycle divisible by 8.
if(grauw_index >= 1268 && grauw_index < 1330) {
return StandardGenerators::external_every_eight(grauw_index - 1268);
}
// A CPU/CMD at 1334.
if(grauw_index == 1334) {
return Event::Type::External;
}
// From 1344 to 1366: CPU/CMD slots every cycle divisible by 8.
if(grauw_index >= 1344 && grauw_index < 1366) {
return StandardGenerators::external_every_eight(grauw_index - 1344);
}
// Otherwise: nothing.
return std::nullopt;
}
};
template <bool include_sprites> struct BitmapGenerator {
static constexpr std::optional<Event> event(int grauw_index) {
if(!include_sprites) {
// Various standard zones of one-every-eight external slots.
if(grauw_index < 124) {
return StandardGenerators::external_every_eight(grauw_index + 2);
}
if(grauw_index > 1266) {
return StandardGenerators::external_every_eight(grauw_index - 1266);
}
} else {
// This records collection points for all data for selected sprites.
// There's only four of them (each site covering two sprites),
// so it's clearer just to be explicit.
//
// There's also a corresponding number of extra external slots to spell out.
switch(grauw_index) {
default: break;
case 1238: return Event(Event::Type::SpriteLocation, 0);
case 1302: return Event(Event::Type::SpriteLocation, 2);
case 2: return Event(Event::Type::SpriteLocation, 4);
case 66: return Event(Event::Type::SpriteLocation, 6);
case 1270: return Event(Event::Type::SpritePattern, 0);
case 1338: return Event(Event::Type::SpritePattern, 2);
case 34: return Event(Event::Type::SpritePattern, 4);
case 98: return Event(Event::Type::SpritePattern, 6);
case 1264: case 1330: case 28: case 92:
return Event::Type::External;
}
}
if(grauw_index >= 162 && grauw_index < 176) {
return StandardGenerators::external_every_eight(grauw_index - 162);
}
// Everywhere else the pattern is:
//
// external or sprite y, external, data block
//
// Subject to caveats:
//
// 1) the first data block is just a dummy fetch with no side effects,
// so this emulator declines to record it; and
// 2) every fourth block, the second external is actually a refresh.
//
if(grauw_index >= 182 && grauw_index < 1238) {
const int offset = grauw_index - 182;
const int block = offset / 32;
const int sub_block = offset & 31;
switch(sub_block) {
default: return std::nullopt;
case 0:
if(include_sprites) {
// Don't include the sprite post-amble (i.e. a spurious read with no side effects).
if(block < 32) {
return Event(Event::Type::SpriteY, uint8_t(block));
}
} else {
return Event::Type::External;
}
case 6:
if((block & 3) != 3) {
return Event::Type::External;
}
break;
case 12:
if(block) {
return Event(Event::Type::Pattern, uint8_t(block - 1));
}
break;
}
}
return std::nullopt;
}
};
struct TextGenerator {
static constexpr std::optional<Event> event(int grauw_index) {
// Capture various one-in-eight zones.
if(grauw_index < 72) {
return StandardGenerators::external_every_eight(grauw_index - 2);
}
if(grauw_index >= 166 && grauw_index < 228) {
return StandardGenerators::external_every_eight(grauw_index - 166);
}
if(grauw_index >= 1206 && grauw_index < 1332) {
return StandardGenerators::external_every_eight(grauw_index - 1206);
}
if(grauw_index == 1336) {
return Event::Type::External;
}
if(grauw_index >= 1346) {
return StandardGenerators::external_every_eight(grauw_index - 1346);
}
// Elsewhere...
if(grauw_index >= 246) {
const int offset = grauw_index - 246;
const int block = offset / 48;
const int sub_block = offset % 48;
switch(sub_block) {
default: break;
case 0: return Event(Event::Type::Name, uint8_t(block));
case 18: return (block & 1) ? Event::Type::External : Event(Event::Type::Colour, uint8_t(block >> 1));
case 24: return Event(Event::Type::Pattern, uint8_t(block));
}
}
return std::nullopt;
}
};
struct CharacterGenerator {
static constexpr std::optional<Event> event(int grauw_index) {
// Grab sprite events.
switch(grauw_index) {
default: break;
case 1242: return Event(Event::Type::SpriteLocation, 0);
case 1306: return Event(Event::Type::SpriteLocation, 1);
case 6: return Event(Event::Type::SpriteLocation, 2);
case 70: return Event(Event::Type::SpriteLocation, 3);
case 1274: return Event(Event::Type::SpritePattern, 0);
case 1342: return Event(Event::Type::SpritePattern, 1);
case 38: return Event(Event::Type::SpritePattern, 2);
case 102: return Event(Event::Type::SpritePattern, 3);
case 1268: case 1334: case 32: case 96: return Event::Type::External;
}
if(grauw_index >= 166 && grauw_index < 180) {
return StandardGenerators::external_every_eight(grauw_index - 166);
}
if(grauw_index >= 182 && grauw_index < 1238) {
const int offset = grauw_index - 182;
const int block = offset / 32;
const int sub_block = offset & 31;
switch(sub_block) {
case 0: if(block > 0) return Event(Event::Type::Name, uint8_t(block - 1));
case 6: if((sub_block & 3) != 3) return Event::Type::External;
case 12: if(block < 32) return Event(Event::Type::SpriteY, uint8_t(block));
case 18: if(block > 0) return Event(Event::Type::Pattern, uint8_t(block - 1));
case 24: if(block > 0) return Event(Event::Type::Colour, uint8_t(block - 1));
}
}
return std::nullopt;
}
};
};
struct YamahaCommandState {
CommandContext command_context_;
ModeDescription mode_description_;
std::unique_ptr<Command> command_ = nullptr;
enum class CommandStep {
None,
CopySourcePixelToStatus,
ReadSourcePixel,
ReadDestinationPixel,
WritePixel,
ReadSourceByte,
WriteByte,
};
CommandStep next_command_step_ = CommandStep::None;
int minimum_command_column_ = 0;
uint8_t command_latch_ = 0;
void update_command_step(int current_column) {
if(!command_) {
next_command_step_ = CommandStep::None;
return;
}
if(command_->done()) {
command_ = nullptr;
next_command_step_ = CommandStep::None;
return;
}
minimum_command_column_ = current_column + command_->cycles;
switch(command_->access) {
case Command::AccessType::ReadPoint:
next_command_step_ = CommandStep::CopySourcePixelToStatus;
break;
case Command::AccessType::CopyPoint:
next_command_step_ = CommandStep::ReadSourcePixel;
break;
case Command::AccessType::PlotPoint:
next_command_step_ = CommandStep::ReadDestinationPixel;
break;
case Command::AccessType::WaitForColourReceipt:
// i.e. nothing to do until a colour is received.
next_command_step_ = CommandStep::None;
break;
case Command::AccessType::CopyByte:
next_command_step_ = CommandStep::ReadSourceByte;
break;
case Command::AccessType::WriteByte:
next_command_step_ = CommandStep::WriteByte;
break;
}
}
};
// Yamaha-specific storage.
template <Personality personality> struct Storage<personality, std::enable_if_t<is_yamaha_vdp(personality)>>: public YamahaFetcher, public YamahaCommandState {
using AddressT = uint32_t;
// The Yamaha's (optional in real hardware) additional 64kb of expansion RAM.
// This is a valid target and source for the command engine, but can't be used as a source for current video data.
std::array<uint8_t, 65536> expansion_ram_;
// Register indirections.
int selected_status_ = 0;
int indirect_register_ = 0;
bool increment_indirect_register_ = false;
// Output horizontal and vertical adjustment, plus the selected vertical offset (i.e. hardware scroll).
int adjustment_[2]{};
uint8_t vertical_offset_ = 0;
// The palette, plus a shadow copy in which colour 0 is not the current palette colour 0,
// but is rather the current global background colour. This simplifies flow when colour 0
// is set as transparent.
std::array<uint32_t, 16> palette_{};
std::array<uint32_t, 16> background_palette_{};
bool solid_background_ = true;
// Transient state for palette setting.
uint8_t new_colour_ = 0;
uint8_t palette_entry_ = 0;
bool palette_write_phase_ = false;
// Recepticle for all five bits of the current screen mode.
uint8_t mode_ = 0;
// Used ephemerally during drawing to compound sprites with the 'CC'
// (compound colour?) bit set.
uint8_t sprite_cache_[8][32]{};
// Text blink colours.
uint8_t blink_text_colour_ = 0;
uint8_t blink_background_colour_ = 0;
// Blink state (which is also affects even/odd page display in applicable modes).
int in_blink_ = 1;
uint8_t blink_periods_ = 0;
uint8_t blink_counter_ = 0;
// Additional things exposed by status registers.
uint8_t colour_status_ = 0;
uint16_t colour_location_ = 0;
uint16_t collision_location_[2]{};
Storage() noexcept {
// Seed to something valid.
next_event_ = refresh_events.data();
}
/// Resets line-ephemeral state for a new line.
void begin_line(ScreenMode mode, bool is_refresh) {
if(is_refresh) {
next_event_ = refresh_events.data();
return;
}
switch(mode) {
case ScreenMode::YamahaText80:
case ScreenMode::Text:
next_event_ = text_events.data();
break;
case ScreenMode::MultiColour:
case ScreenMode::YamahaGraphics1:
case ScreenMode::YamahaGraphics2:
next_event_ = character_events.data();
break;
case ScreenMode::YamahaGraphics3: // TODO: verify; my guess is that G3 is timed like a bitmap mode
// in order to fit the pattern for sprite mode 2. Just a guess.
default:
next_event_ = sprites_enabled_ ? sprites_events.data() : no_sprites_events.data();
break;
}
}
private:
static constexpr auto refresh_events = events<RefreshGenerator>();
static constexpr auto no_sprites_events = events<BitmapGenerator<false>>();
static constexpr auto sprites_events = events<BitmapGenerator<true>>();
static constexpr auto text_events = events<TextGenerator>();
static constexpr auto character_events = events<CharacterGenerator>();
};
// Master System-specific storage.
template <Personality personality> struct Storage<personality, std::enable_if_t<is_sega_vdp(personality)>> {
using AddressT = uint16_t;
// The SMS VDP has a programmer-set colour palette, with a dedicated patch of RAM. But the RAM is only exactly
// fast enough for the pixel clock. So when the programmer writes to it, that causes a one-pixel glitch; there
// isn't the bandwidth for the read both write to occur simultaneously. The following buffer therefore keeps
// track of pending collisions, for visual reproduction.
struct CRAMDot {
LineBufferPointer location;
uint32_t value;
};
std::vector<CRAMDot> upcoming_cram_dots_;
// The Master System's additional colour RAM.
uint32_t colour_ram_[32];
bool cram_is_selected_ = false;
// Programmer-set flags.
bool vertical_scroll_lock_ = false;
bool horizontal_scroll_lock_ = false;
bool hide_left_column_ = false;
bool shift_sprites_8px_left_ = false;
bool mode4_enable_ = false;
uint8_t horizontal_scroll_ = 0;
uint8_t vertical_scroll_ = 0;
// Holds the vertical scroll position for this frame; this is latched
// once and cannot dynamically be changed until the next frame.
uint8_t latched_vertical_scroll_ = 0;
// Various resource addresses with VDP-version-specific modifications
// built in.
AddressT pattern_name_address_;
AddressT sprite_attribute_table_address_;
AddressT sprite_generator_table_address_;
void begin_line(ScreenMode, bool) {}
};
}
#endif /* Storage_h */

View File

@@ -0,0 +1,382 @@
//
// YamahaCommands.hpp
// Clock Signal
//
// Created by Thomas Harte on 26/01/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#ifndef YamahaCommands_hpp
#define YamahaCommands_hpp
#include "AccessEnums.hpp"
namespace TI::TMS {
// MARK: - Generics.
struct Vector {
int v[2]{};
template <int offset, bool high> void set(uint8_t value) {
constexpr uint8_t mask = high ? (offset ? 0x3 : 0x1) : 0xff;
constexpr int shift = high ? 8 : 0;
v[offset] = (v[offset] & ~(mask << shift)) | ((value & mask) << shift);
}
template <int offset> void add(int amount) {
v[offset] += amount;
if constexpr (offset == 1) {
v[offset] &= 0x3ff;
} else {
v[offset] &= 0x1ff;
}
}
Vector & operator += (const Vector &rhs) {
add<0>(rhs.v[0]);
add<1>(rhs.v[1]);
return *this;
}
};
struct Colour {
void set(uint8_t value) {
colour = value;
colour4bpp = uint8_t((value & 0xf) | (value << 4));
colour2bpp = uint8_t((colour4bpp & 0x33) | ((colour4bpp & 0x33) << 2));
}
void reset() {
colour = 0x00;
colour4bpp = 0xff;
}
bool has_value() const {
return (colour & 0xf) == (colour4bpp & 0xf);
}
/// Colour as written by the CPU.
uint8_t colour = 0x00;
/// The low four bits of the CPU-written colour, repeated twice.
uint8_t colour4bpp = 0xff;
/// The low two bits of the CPU-written colour, repeated four times.
uint8_t colour2bpp = 0xff;
};
struct CommandContext {
Vector source;
Vector destination;
Vector size;
uint8_t arguments = 0;
Colour colour;
Colour latched_colour;
enum class LogicalOperation {
Copy = 0b0000,
And = 0b0001,
Or = 0b0010,
Xor = 0b0011,
Not = 0b0100,
};
LogicalOperation pixel_operation;
bool test_source;
};
struct ModeDescription {
int width = 256;
int pixels_per_byte = 4;
bool rotate_address = false;
};
struct Command {
// In net:
//
// This command is blocked until @c access has been performed, reading
// from or writing to @c value. It should not be performed until at least
// @c cycles have passed.
enum class AccessType {
/// Plots a single pixel of the current contextual colour at @c destination,
/// which occurs as a read, then a 24-cycle gap, then a write.
PlotPoint,
/// Blocks until the next CPU write to the colour register.
WaitForColourReceipt,
/// Writes an entire byte to the address containing the current @c destination.
WriteByte,
/// Copies a single pixel from @c source location to @c destination,
/// being a read, a 32-cycle gap, then a PlotPoint.
CopyPoint,
/// Copies a complete byte from @c source location to @c destination,
/// being a read, a 24-cycle gap, then a write.
CopyByte,
/// Copies a single pixel from @c source to the colour status register.
ReadPoint,
// ReadByte,
// WaitForColourSend,
};
AccessType access = AccessType::PlotPoint;
int cycles = 0;
bool is_cpu_transfer = false;
bool y_only = false;
/// Current command parameters.
CommandContext &context;
ModeDescription &mode_description;
Command(CommandContext &context, ModeDescription &mode_description) : context(context), mode_description(mode_description) {}
virtual ~Command() {}
/// @returns @c true if all output from this command is done; @c false otherwise.
virtual bool done() = 0;
/// Repopulates the fields above with the next action to take, being provided with the
/// number of pixels per byte in the current screen mode.
virtual void advance() = 0;
protected:
template <int axis, bool include_source> void advance_axis(int offset = 1) {
context.destination.add<axis>(context.arguments & (0x4 << axis) ? -offset : offset);
if constexpr (include_source) {
context.source.add<axis>(context.arguments & (0x4 << axis) ? -offset : offset);
}
}
};
namespace Commands {
// MARK: - Line drawing.
/// Implements the LINE command, which is plain-old Bresenham.
///
/// Per Grauw timing is:
///
/// * 88 cycles between every pixel plot;
/// * plus an additional 32 cycles if a step along the minor axis is taken.
struct Line: public Command {
public:
Line(CommandContext &context, ModeDescription &mode_description) : Command(context, mode_description) {
// context.destination = start position;
// context.size.v[0] = long side dots;
// context.size.v[1] = short side dots;
// context.arguments => direction
position_ = context.size.v[1];
numerator_ = position_ << 1;
denominator_ = context.size.v[0] << 1;
cycles = 32;
access = AccessType::PlotPoint;
}
bool done() final {
return !context.size.v[0];
}
void advance() final {
--context.size.v[0];
cycles = 88;
// b0: 1 => long direction is y;
// 0 => long direction is x.
//
// b2: 1 => x direction is left;
// 0 => x direction is right.
//
// b3: 1 => y direction is up;
// 0 => y direction is down.
if(context.arguments & 0x1) {
advance_axis<1, false>();
} else {
advance_axis<0, false>();
}
position_ -= numerator_;
if(position_ < 0) {
position_ += denominator_;
cycles += 32;
if(context.arguments & 0x1) {
advance_axis<0, false>();
} else {
advance_axis<1, false>();
}
}
}
private:
int position_, numerator_, denominator_, duration_;
};
// MARK: - Single pixel manipulation.
/// Implements the PSET command, which plots a single pixel and POINT, which reads one.
///
/// No timings are documented, so this'll output or input as quickly as possible.
template <bool is_read> struct Point: public Command {
public:
Point(CommandContext &context, ModeDescription &mode_description) : Command(context, mode_description) {
cycles = 0; // TODO.
access = is_read ? AccessType::ReadPoint : AccessType::PlotPoint;
}
bool done() final {
return done_;
}
void advance() final {
done_ = true;
}
private:
bool done_ = false;
};
// MARK: - Rectangular base.
/// Useful base class for anything that does logical work in a rectangle.
template <bool logical, bool include_source> struct Rectangle: public Command {
public:
Rectangle(CommandContext &context, ModeDescription &mode_description) : Command(context, mode_description) {
if constexpr (include_source) {
start_x_[0] = context.source.v[0];
}
start_x_[1] = context.destination.v[0];
width_ = context.size.v[0];
if(!width_) {
// Width = 0 => maximal width for this mode.
// (aside: it's still unclear to me whether commands are
// automatically clipped to the display; I think so but
// don't want to spend any time on it until I'm certain)
// context.size.v[0] = width_ = mode_description.width;
}
}
/// Advances the current destination and, if @c include_source is @c true also the source;
/// @returns @c true if a new row was started; @c false otherwise.
bool advance_pixel() {
if constexpr (logical) {
advance_axis<0, include_source>();
--context.size.v[0];
if(context.size.v[0]) {
return false;
}
} else {
advance_axis<0, include_source>(mode_description.pixels_per_byte);
context.size.v[0] -= mode_description.pixels_per_byte;
if(context.size.v[0] & ~(mode_description.pixels_per_byte - 1)) {
return false;
}
}
context.size.v[0] = width_;
if constexpr (include_source) {
context.source.v[0] = start_x_[0];
}
context.destination.v[0] = start_x_[1];
advance_axis<1, include_source>();
--context.size.v[1];
return true;
}
bool done() final {
return !context.size.v[1] || !width_;
}
private:
int start_x_[2]{}, width_ = 0;
};
// MARK: - Rectangular moves to/from CPU.
template <bool logical> struct MoveFromCPU: public Rectangle<logical, false> {
MoveFromCPU(CommandContext &context, ModeDescription &mode_description) : Rectangle<logical, false>(context, mode_description) {
Command::is_cpu_transfer = true;
// This command is started with the first colour ready to transfer.
Command::cycles = 32;
Command::access = logical ? Command::AccessType::PlotPoint : Command::AccessType::WriteByte;
}
void advance() final {
switch(Command::access) {
default: break;
case Command::AccessType::WaitForColourReceipt:
Command::cycles = 32;
Command::access = logical ? Command::AccessType::PlotPoint : Command::AccessType::WriteByte;
break;
case Command::AccessType::WriteByte:
case Command::AccessType::PlotPoint:
Command::cycles = 0;
Command::access = Command::AccessType::WaitForColourReceipt;
if(Rectangle<logical, false>::advance_pixel()) {
Command::cycles = 64;
// TODO: I'm not sure this will be honoured per the outer wrapping.
}
break;
}
}
};
// MARK: - Rectangular moves within VRAM.
enum class MoveType {
Logical,
HighSpeed,
YOnly,
};
template <MoveType type> struct Move: public Rectangle<type == MoveType::Logical, true> {
static constexpr bool is_logical = type == MoveType::Logical;
static constexpr bool is_y_only = type == MoveType::YOnly;
using RectangleBase = Rectangle<is_logical, true>;
Move(CommandContext &context, ModeDescription &mode_description) : RectangleBase(context, mode_description) {
Command::access = is_logical ? Command::AccessType::CopyPoint : Command::AccessType::CopyByte;
Command::cycles = is_y_only ? 0 : 64;
Command::y_only = is_y_only;
}
void advance() final {
Command::cycles = is_y_only ? 40 : 64;
if(RectangleBase::advance_pixel()) {
Command::cycles += is_y_only ? 0 : 64;
}
}
};
// MARK: - Rectangular fills.
template <bool logical> struct Fill: public Rectangle<logical, false> {
using RectangleBase = Rectangle<logical, false>;
Fill(CommandContext &context, ModeDescription &mode_description) : RectangleBase(context, mode_description) {
Command::cycles = logical ? 64 : 56;
Command::access = logical ? Command::AccessType::PlotPoint : Command::AccessType::WriteByte;
}
void advance() final {
Command::cycles = logical ? 72 : 48;
if(RectangleBase::advance_pixel()) {
Command::cycles += logical ? 64 : 56;
}
}
};
}
}
#endif /* YamahaCommands_hpp */

View File

@@ -16,7 +16,7 @@
using namespace GI::AY38910;
template <bool is_stereo>
AY38910<is_stereo>::AY38910(Personality personality, Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_(task_queue) {
AY38910<is_stereo>::AY38910(Personality personality, Concurrency::AsyncTaskQueue<false> &task_queue) : task_queue_(task_queue) {
// Don't use the low bit of the envelope position if this is an AY.
envelope_position_mask_ |= personality == Personality::AY38910;
@@ -252,7 +252,7 @@ template <bool is_stereo> void AY38910<is_stereo>::set_register_value(uint8_t va
// If this is a register that affects audio output, enqueue a mutation onto the
// audio generation thread.
if(selected_register_ < 14) {
task_queue_.defer([this, selected_register = selected_register_, value] () {
task_queue_.enqueue([this, selected_register = selected_register_, value] () {
// Perform any register-specific mutation to output generation.
uint8_t masked_value = value;
switch(selected_register) {

View File

@@ -14,8 +14,7 @@
#include "../../Reflection/Struct.hpp"
namespace GI {
namespace AY38910 {
namespace GI::AY38910 {
/*!
A port handler provides all input for an AY's two 8-bit ports, and may optionally receive
@@ -71,7 +70,7 @@ enum class Personality {
template <bool is_stereo> class AY38910: public ::Outputs::Speaker::SampleSource {
public:
/// Creates a new AY38910.
AY38910(Personality, Concurrency::DeferringAsyncTaskQueue &);
AY38910(Personality, Concurrency::AsyncTaskQueue<false> &);
/// Sets the value the AY would read from its data lines if it were not outputting.
void set_data_input(uint8_t r);
@@ -114,7 +113,7 @@ template <bool is_stereo> class AY38910: public ::Outputs::Speaker::SampleSource
static constexpr bool get_is_stereo() { return is_stereo; }
private:
Concurrency::DeferringAsyncTaskQueue &task_queue_;
Concurrency::AsyncTaskQueue<false> &task_queue_;
int selected_register_ = 0;
uint8_t registers_[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
@@ -219,7 +218,6 @@ struct State: public Reflection::StructImpl<State> {
}
};
}
}
#endif /* AY_3_8910_hpp */

View File

@@ -11,8 +11,7 @@
#include <array>
namespace Apple {
namespace Clock {
namespace Apple::Clock {
/*!
Models Apple's real-time clocks, as contained in the Macintosh and IIgs.
@@ -293,7 +292,6 @@ class ParallelClock: public ClockStorage {
uint8_t control_;
};
}
}
#endif /* Apple_RealTimeClock_hpp */

View File

@@ -10,7 +10,7 @@
using namespace Audio;
Audio::Toggle::Toggle(Concurrency::DeferringAsyncTaskQueue &audio_queue) :
Audio::Toggle::Toggle(Concurrency::AsyncTaskQueue<false> &audio_queue) :
audio_queue_(audio_queue) {}
void Toggle::get_samples(std::size_t number_of_samples, std::int16_t *target) {
@@ -28,7 +28,7 @@ void Toggle::skip_samples(std::size_t) {}
void Toggle::set_output(bool enabled) {
if(is_enabled_ == enabled) return;
is_enabled_ = enabled;
audio_queue_.defer([this, enabled] {
audio_queue_.enqueue([this, enabled] {
level_ = enabled ? volume_ : 0;
});
}

View File

@@ -19,7 +19,7 @@ namespace Audio {
*/
class Toggle: public Outputs::Speaker::SampleSource {
public:
Toggle(Concurrency::DeferringAsyncTaskQueue &audio_queue);
Toggle(Concurrency::AsyncTaskQueue<false> &audio_queue);
void get_samples(std::size_t number_of_samples, std::int16_t *target);
void set_sample_volume_range(std::int16_t range);
@@ -31,7 +31,7 @@ class Toggle: public Outputs::Speaker::SampleSource {
private:
// Accessed on the calling thread.
bool is_enabled_ = false;
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
Concurrency::AsyncTaskQueue<false> &audio_queue_;
// Accessed on the audio thread.
int16_t level_ = 0, volume_ = 0;

View File

@@ -11,8 +11,7 @@
#include "IWM.hpp"
namespace Apple {
namespace Disk {
namespace Apple::Disk {
class DiskIIDrive: public IWMDrive {
public:
@@ -27,7 +26,6 @@ class DiskIIDrive: public IWMDrive {
int stepper_position_ = 0;
};
}
}
#endif /* DiskIIDrive_hpp */

View File

@@ -11,8 +11,7 @@
#include "IWM.hpp"
namespace Apple {
namespace Macintosh {
namespace Apple::Macintosh {
class DoubleDensityDrive: public IWMDrive {
public:
@@ -47,7 +46,6 @@ class DoubleDensityDrive: public IWMDrive {
int step_direction_ = 1;
};
}
}
#endif /* MacintoshDoubleDensityDrive_hpp */

View File

@@ -12,7 +12,7 @@
using namespace Konami;
SCC::SCC(Concurrency::DeferringAsyncTaskQueue &task_queue) :
SCC::SCC(Concurrency::AsyncTaskQueue<false> &task_queue) :
task_queue_(task_queue) {}
bool SCC::is_zero_level() const {
@@ -55,7 +55,7 @@ void SCC::write(uint16_t address, uint8_t value) {
address &= 0xff;
if(address < 0x80) ram_[address] = value;
task_queue_.defer([this, address, value] {
task_queue_.enqueue([this, address, value] {
// Check for a write into waveform memory.
if(address < 0x80) {
waves_[address >> 5].samples[address & 0x1f] = value;

View File

@@ -24,7 +24,7 @@ namespace Konami {
class SCC: public ::Outputs::Speaker::SampleSource {
public:
/// Creates a new SCC.
SCC(Concurrency::DeferringAsyncTaskQueue &task_queue);
SCC(Concurrency::AsyncTaskQueue<false> &task_queue);
/// As per ::SampleSource; provides a broadphase test for silence.
bool is_zero_level() const;
@@ -41,7 +41,7 @@ class SCC: public ::Outputs::Speaker::SampleSource {
uint8_t read(uint16_t address);
private:
Concurrency::DeferringAsyncTaskQueue &task_queue_;
Concurrency::AsyncTaskQueue<false> &task_queue_;
// State from here on down is accessed ony from the audio thread.
int master_divider_ = 0;

View File

@@ -13,8 +13,7 @@
#include <functional>
#include "LowFrequencyOscillator.hpp"
namespace Yamaha {
namespace OPL {
namespace Yamaha::OPL {
/*!
Models an OPL-style envelope generator.
@@ -258,7 +257,6 @@ template <int envelope_precision, int period_precision> class EnvelopeGenerator
}
};
}
}
#endif /* EnvelopeGenerator_h */

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