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

Compare commits

...

105 Commits

Author SHA1 Message Date
Thomas Harte
e7299c16f6 Merge branch 'master' into SeparateFetchClock 2023-08-01 14:48:21 -04:00
Thomas Harte
cdb86022a6 Merge pull request #1148 from TomHarte/NoEmulationStack
Use full 16-bit stack pointer for all 'new' instructions.
2023-07-31 20:41:10 -04:00
Thomas Harte
2262725010 Reveal 16-bit stack pointer when asked, regardless of mode. 2023-07-31 17:08:02 -04:00
Thomas Harte
e61a4eb5a9 Regularise PHD and PLD. 2023-07-30 16:36:29 -04:00
Thomas Harte
acd7f9f4cd Fix stack usage of JSL. 2023-07-30 16:34:42 -04:00
Thomas Harte
9f1a657cc4 Fix stack usage of PEA. 2023-07-30 16:33:44 -04:00
Thomas Harte
e52d1866ab Fix PEI stack usage. 2023-07-30 16:32:56 -04:00
Thomas Harte
a02b8222fa Fix stack usage of PER. 2023-07-30 16:29:56 -04:00
Thomas Harte
3762ee1a63 Fix stack usage of PHD. 2023-07-30 16:29:15 -04:00
Thomas Harte
3ec61e8770 Fix stack usage of RTL. 2023-07-30 16:27:13 -04:00
Thomas Harte
2f7dd0b01a Correct stack behaviour of PLD. 2023-07-30 16:26:29 -04:00
Thomas Harte
3a02c22072 Provide an always-16bit-address route to the stack. 2023-07-30 16:25:51 -04:00
Thomas Harte
6ae967de51 Merge pull request #1147 from TomHarte/ErrantDBR
Remove DBR reset upon COP/BRK/IRQ/NMI; fix (d, x) addressing.
2023-07-30 16:20:34 -04:00
Thomas Harte
5d45aa4a6a Fix seed per test. 2023-07-28 13:58:01 -04:00
Thomas Harte
0f1468adfd Correct wrapping behaviour for (d, x). 2023-07-28 13:39:21 -04:00
Thomas Harte
e9347168e6 Don't alter the data bank upon BRK, COP, IRQ, etc. 2023-07-28 10:53:02 -04:00
Thomas Harte
e2dcb0a8e2 Begin commutation of interrupts. 2023-07-28 10:34:15 -04:00
Thomas Harte
1797bab28f Explain logic. 2023-07-23 14:26:02 -04:00
Thomas Harte
7f48cd6d9d Fix fetch offset. Breaking interrupt placement. 2023-07-21 15:48:52 -04:00
Thomas Harte
8662f06ae5 Add documentation, to persuade myself. 2023-07-16 22:43:50 -04:00
Thomas Harte
3b67d48ebf With origin being its own thing, simplify. 2023-07-16 21:23:49 -04:00
Thomas Harte
2ab16867cb Require explicit origin. 2023-07-16 15:12:52 -04:00
Thomas Harte
3a93b8059a Make faulty attempt to introduce skew into fetching. 2023-07-10 22:24:13 -04:00
Thomas Harte
3e09afbb59 Remove errant square bracket. 2023-06-21 11:57:09 -04:00
Thomas Harte
9703fed9f8 Explain constant. 2023-06-18 14:56:44 -04:00
Thomas Harte
f30637a773 Merge pull request #1144 from TomHarte/Base144
Enhance mechanisms for display-style dispatch.
2023-06-15 21:42:59 -04:00
Thomas Harte
1d8bc41724 Shift back to original name. 2023-06-13 15:25:51 -04:00
Thomas Harte
d36a88dd11 Collect up different dispatches. 2023-06-13 15:22:53 -04:00
Thomas Harte
de5ee8f0d0 Mildly extend test. 2023-06-13 13:26:39 -04:00
Thomas Harte
6261ac24b4 Reformat SubrangeDispatcher; test. 2023-06-13 12:46:21 -04:00
Thomas Harte
b00eac4a34 Get to building. 2023-06-12 23:16:45 -04:00
Thomas Harte
6e35d84a96 Merge branch 'Base144' of github.com:TomHarte/CLK into Base144 2023-06-12 17:39:16 -04:00
Thomas Harte
d028555361 Get code up on feet, fix most obvious transgressions. 2023-06-12 16:09:02 -04:00
Thomas Harte
1aa953dd4d Consolidate RangeDispatcher under Dispatcher's umbrella. 2023-06-12 15:52:10 -04:00
Thomas Harte
77c67ab59d Build max into the sequencer. 2023-06-12 15:35:33 -04:00
Thomas Harte
05d2e78f80 Conversion can be a separate step. 2023-06-12 15:34:44 -04:00
Thomas Harte
837d8d29ca Merge branch 'master' into Base144 2023-06-10 16:00:57 -04:00
Thomas Harte
8a831b1409 Import sketch for a potential range dispatcher. 2023-06-10 15:58:30 -04:00
Thomas Harte
c0547f6e14 Tidy up; forward construction arguments. 2023-06-10 15:58:13 -04:00
Thomas Harte
81e475f052 Merge pull request #1142 from aperezdc/gcc13-cstdint
Add missing <cstdint> includes for GCC 13
2023-06-01 17:43:18 +01:00
Thomas Harte
4e12d5a70a Attempt to make switch sets even more obviously collapsible. 2023-05-30 16:43:22 +01:00
Thomas Harte
c630f86f33 Attempt to generalise out from the 9918's current sense of dispatching. 2023-05-29 22:56:36 +01:00
Adrian Perez de Castro
1de2631877 Add missing <cstdint> includes for GCC 13
Sprinkle includes of the <cstdint> header as needed to make the
build succeed with GCC 13, this fixes both with SDL and Qt builds.
2023-05-25 23:06:13 +03:00
Thomas Harte
dd3fc43bd3 Merge pull request #1141 from TomHarte/ConvertFromGrauw
Clean up further internal magic constants.
2023-05-19 19:52:40 -04:00
Thomas Harte
40d5bd4e58 Switch to purposive name. 2023-05-19 14:22:22 -04:00
Thomas Harte
c75efb7dac Also allow for a potential Grauw conversion in Yamaha land. 2023-05-19 13:43:28 -04:00
Thomas Harte
d117a44069 Allow for potential Grauw offset in TMS and SMS. 2023-05-19 11:46:49 -04:00
Thomas Harte
dc425a03d3 Partially resolve. 2023-05-18 16:55:17 -04:00
Thomas Harte
ce8bd011d7 Add commentary, and TODOs. 2023-05-18 16:50:46 -04:00
Thomas Harte
c76048bff9 Formalise the idea of Grauw as a separate clock. 2023-05-18 16:37:48 -04:00
Thomas Harte
4cb7abe13d Update old comment. 2023-05-18 16:28:05 -04:00
Thomas Harte
c445295411 Merge pull request #1140 from TomHarte/YamahaLineInterrupts
Move Yamaha interrupts to end of line; clean source.
2023-05-16 16:46:56 -04:00
Thomas Harte
5c51bae605 Remove unused variable. 2023-05-16 16:46:36 -04:00
Thomas Harte
8578dfbf22 Eliminate various other errant spaces. 2023-05-16 16:40:09 -04:00
Thomas Harte
f821b60430 Remove stray space. 2023-05-16 16:16:10 -04:00
Thomas Harte
8ca0d9e13a Add a hook for when I think mode latching should occur. 2023-05-16 16:14:37 -04:00
Thomas Harte
3014c957e7 Relocate Yamaha line interrupt. 2023-05-16 13:01:23 -04:00
Thomas Harte
747dc09a80 Merge pull request #1139 from TomHarte/OtherProjects
Clean up SDL and Qt projects, Qt build warnings.
2023-05-15 10:18:16 -04:00
Thomas Harte
7f8f1d7e61 Avoid BASIC 2.1 requirement when running 1.1. 2023-05-15 10:17:27 -04:00
Thomas Harte
a1a7c0e253 Apply maybe_unused judiciously. 2023-05-15 10:17:04 -04:00
Thomas Harte
9342c6005f Remove dead 68000 references. 2023-05-15 10:09:39 -04:00
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
400 changed files with 5328 additions and 12729 deletions

View File

@@ -9,6 +9,7 @@
#ifndef ActivityObserver_h
#define ActivityObserver_h
#include <cstdint>
#include <string>
namespace Activity {

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

@@ -20,7 +20,7 @@ static std::unique_ptr<File::Chunk> GetNextChunk(const std::shared_ptr<Storage::
int shift_register = 0;
// TODO: move this into the parser
#define shift() shift_register = (shift_register >> 1) | (parser.get_next_bit(tape) << 9)
#define shift() shift_register = (shift_register >> 1) | (parser.get_next_bit(tape) << 9)
// find next area of high tone
while(!tape->is_at_end() && (shift_register != 0x3ff)) {

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,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,
@@ -51,8 +49,6 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
}
};
}
}
}
#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,9 +12,7 @@
#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> {
ReflectableEnum(MemorySize,
@@ -31,8 +29,6 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
}
};
}
}
}
#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

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

View File

@@ -93,7 +93,7 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media
// make a first guess based on loading address
switch(files.front().starting_address) {
default:
LOG("Unrecognised loading address for Commodore program: " << PADHEX(4) << files.front().starting_address);
LOG("Unrecognised loading address for Commodore program: " << PADHEX(4) << files.front().starting_address);
[[fallthrough]];
case 0x1001:
memory_model = Target::MemoryModel::Unexpanded;
@@ -188,8 +188,8 @@ Analyser::Static::TargetList Analyser::Static::Commodore::GetTargets(const Media
// Unhandled:
//
// M6: this is a C64 file.
// MV: this is a Vic-20 file.
// M6: this is a C64 file.
// MV: this is a Vic-20 file.
// J1/J2: this C64 file should have the primary joystick in slot 1/2.
// RO: this disk image should be treated as read-only.
}

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

@@ -11,7 +11,7 @@
#include "Kernel.hpp"
using namespace Analyser::Static::MOS6502;
namespace {
namespace {
using PartialDisassembly = Analyser::Static::Disassembly::PartialDisassembly<Disassembly, uint16_t>;

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

@@ -11,7 +11,7 @@
#include "Kernel.hpp"
using namespace Analyser::Static::Z80;
namespace {
namespace {
using PartialDisassembly = Analyser::Static::Disassembly::PartialDisassembly<Disassembly, uint16_t>;

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

@@ -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,12 +14,11 @@
#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,
@@ -38,6 +37,7 @@ 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);
@@ -46,8 +46,6 @@ struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<
}
};
}
}
}
#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 {
@@ -52,8 +50,6 @@ 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

@@ -185,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
@@ -244,7 +245,7 @@ TargetList Analyser::Static::GetTargets(const std::string &file_name) {
const std::string extension = get_extension(file_name);
// Check whether the file directly identifies a target; if so then just return that.
#define Format(ext, class) \
#define Format(ext, class) \
if(extension == ext) { \
try { \
auto target = Storage::State::class::load(file_name); \

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

@@ -294,7 +294,7 @@ template <class T, class LocalTimeScale = HalfCycles, class TargetTimeScale = Lo
/// Constructs a new AsyncJustInTimeActor using the same construction arguments as the included object.
template<typename... Args> AsyncJustInTimeActor(TargetTimeScale threshold, Args&&... args) :
object_(std::forward<Args>(args)...),
threshold_(threshold) {}
threshold_(threshold) {}
/// Adds time to the actor.
inline void operator += (const LocalTimeScale &rhs) {

View File

@@ -66,7 +66,7 @@ uint8_t WD1770::read(int address) {
// Per Jean Louis-Guérin's documentation:
//
// * the write-protect bit is locked into place by a type 2 or type 3 command, but is
// * the write-protect bit is locked into place by a type 2 or type 3 command, but is
// read live after a type 1.
// * the track 0 bit is captured during a type 1 instruction and lost upon any other type,
// it is not live sampled.

View File

@@ -156,7 +156,7 @@ uint8_t NCR5380::read(int address, bool) {
return uint8_t(bus_.get_state());
case 1:
LOG("[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) |

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.
@@ -24,7 +23,7 @@ class NCR5380 final: public SCSI::Bus::Observer {
public:
NCR5380(SCSI::Bus &bus, int clock_rate);
/*! Writes @c value to @c address. */
/*! Writes @c value to @c address. */
void write(int address, uint8_t value, bool dma_acknowledge = false);
/*! Reads from @c address. */
@@ -86,7 +85,6 @@ class NCR5380 final: public SCSI::Bus::Observer {
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) {
@@ -272,7 +271,7 @@ template <typename T> void MOS6522<T>::set_control_line_input(Port port, Line li
// TODO: and at least one full clock since the shift register was written?
if(port == Port::B) {
switch(shift_mode()) {
default: break;
default: break;
case ShiftMode::InUnderCB1: if(value) shift_in(); break; // Shifts in are captured on a low-to-high transition.
case ShiftMode::OutUnderCB1: if(!value) shift_out(); break; // Shifts out are updated on a high-to-low transition.
}
@@ -330,7 +329,7 @@ template <typename T> void MOS6522<T>::do_phase2() {
// If the shift register is shifting according to the input clock, do a shift.
switch(shift_mode()) {
default: break;
default: break;
case ShiftMode::InUnderPhase2: shift_in(); break;
case ShiftMode::OutUnderPhase2: shift_out(); break;
}
@@ -346,9 +345,9 @@ template <typename T> void MOS6522<T>::do_phase1() {
// If the shift register is shifting according to this timer, do a shift.
// TODO: "shift register is driven by only the low order 8 bits of timer 2"?
switch(shift_mode()) {
default: break;
default: break;
case ShiftMode::InUnderT2: shift_in(); break;
case ShiftMode::OutUnderT2FreeRunning: shift_out(); break;
case ShiftMode::OutUnderT2FreeRunning: shift_out(); break;
case ShiftMode::OutUnderT2: shift_out(); break; // TODO: present a clock on CB1.
}
@@ -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

@@ -15,8 +15,7 @@
#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 {
@@ -84,11 +83,11 @@ template <class BusHandler> class MOS6560 {
speaker_.set_input_rate(float(clock_rate / 4.0));
}
void set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); }
void set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); }
Outputs::Display::ScanStatus get_scaled_scan_status() const { return crt_.get_scaled_scan_status() / 4.0f; }
void set_display_type(Outputs::Display::DisplayType display_type) { crt_.set_display_type(display_type); }
Outputs::Display::DisplayType get_display_type() const { return crt_.get_display_type(); }
Outputs::Speaker::Speaker *get_speaker() { return &speaker_; }
void set_display_type(Outputs::Display::DisplayType display_type) { crt_.set_display_type(display_type); }
Outputs::Display::DisplayType get_display_type() const { return crt_.get_display_type(); }
Outputs::Speaker::Speaker *get_speaker() { return &speaker_; }
void set_high_frequency_cutoff(float cutoff) {
speaker_.set_high_frequency_cutoff(cutoff);
@@ -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

@@ -14,8 +14,7 @@
#include <cstdint>
namespace Motorola {
namespace MFP68901 {
namespace Motorola::MFP68901 {
class PortHandler {
public:
@@ -182,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 */

View File

@@ -18,6 +18,8 @@ namespace TI::TMS {
enum Personality {
TMS9918A, // includes the 9928 and 9929; set TV standard and output device as desired.
// Yamaha extensions.
V9938,
V9958,

View File

@@ -30,27 +30,6 @@ Base<personality>::Base() :
// Unimaginatively, this class just passes RGB through to the shader. Investigation is needed
// into whether there's a more natural form. It feels unlikely given the diversity of chips modelled.
if constexpr (is_sega_vdp(personality)) {
// Cf. https://www.smspower.org/forums/8161-SMSDisplayTiming
// "For a line interrupt, /INT is pulled low 608 mclks into the appropriate scanline relative to pixel 0.
// This is 3 mclks before the rising edge of /HSYNC which starts the next scanline."
mode_timing_.line_interrupt_position = (LineLayout<personality>::EndOfLeftBorder + 304) % Timing<personality>::CyclesPerLine;
// For a frame interrupt, /INT is pulled low 607 mclks into scanline 192 (of scanlines 0 through 261) relative to pixel 0.
// This is 4 mclks before the rising edge of /HSYNC which starts the next scanline.
mode_timing_.end_of_frame_interrupt_position.column = mode_timing_.line_interrupt_position - 1;
mode_timing_.end_of_frame_interrupt_position.row = 193;
}
if constexpr (is_yamaha_vdp(personality)) {
// TODO: start of sync, or end of sync? Or elsewhere.
// Note that there's a bug elsewhere if the proper value of this is zero in the
// "if started before but reached this count" logic — that is boxed into considering
// a single line only so never sees starts before 0.
mode_timing_.line_interrupt_position = LineLayout<personality>::EndOfSync;
}
// Establish that output is delayed after reading by `output_lag` cycles,
// i.e. the fetch pointer is currently _ahead_ of the output pointer.
output_pointer_.row = output_pointer_.column = 0;
@@ -58,6 +37,22 @@ Base<personality>::Base() :
fetch_pointer_ = output_pointer_;
fetch_pointer_.column += output_lag;
// The fetch pointer is interpreted such that its zero is at the mode-latch cycle.
// Conversely the output pointer has zero be at start of sync. So the following
// is a mere change-of-origin.
//
// Logically, any mode latch time greater than 0 — i.e. beyond the start of sync — will
// cause fetch_pointer_ to **regress**. It will be set to the value it was at the start
// of sync, ready to overflow to 0 upon mode latch. When it overflows, the fetch row
// will be incremented.
//
// Therefore the nominal output row at instantiation needs to be one greater than the
// fetch row, as that's the first row that'll actually be fetched.
fetch_pointer_.column = to_internal<personality, Origin::ModeLatch>(output_pointer_.column);
if(LineLayout<personality>::ModeLatchCycle) {
++output_pointer_.row;
}
fetch_line_buffer_ = line_buffers_.begin();
draw_line_buffer_ = line_buffers_.begin();
fetch_sprite_buffer_ = sprite_buffers_.begin();
@@ -82,6 +77,7 @@ TMS9918<personality>::TMS9918() {
template <Personality personality>
void TMS9918<personality>::set_tv_standard(TVStandard standard) {
// TODO: the Yamaha is programmable on this at runtime.
this->tv_standard_ = standard;
switch(standard) {
case TVStandard::PAL:
@@ -197,7 +193,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
if(fetch_cycles_pool) {
// Determine how much writing to do; at the absolute most go to the end of this line.
const int fetch_cycles = std::min(
Timing<personality>::CyclesPerLine - this->fetch_pointer_.column,
LineLayout<personality>::CyclesPerLine - this->fetch_pointer_.column,
fetch_cycles_pool
);
const int end_column = this->fetch_pointer_.column + fetch_cycles;
@@ -216,8 +212,13 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
// Latch scrolling position, if necessary.
// ---------------------------------------
if constexpr (is_sega_vdp(personality)) {
if(this->fetch_pointer_.column < 61 && end_column >= 61) {
if(!this->fetch_pointer_.row) {
if(!this->fetch_pointer_.row) {
// TODO: where did this magic constant come from? https://www.smspower.org/forums/17970-RoadRashHow#111000 mentioned in passing
// that "the vertical scroll register is latched at the start of the active display" and this is two clocks before that, so it's
// not uncompelling. I can just no longer find my source.
constexpr auto latch_time = to_internal<personality, Origin::ModeLatch>(LineLayout<personality>::EndOfLeftBorder - 2);
static_assert(latch_time > 0);
if(this->fetch_pointer_.column < latch_time && end_column >= latch_time) {
Storage<personality>::latched_vertical_scroll_ = Storage<personality>::vertical_scroll_;
if(Storage<personality>::mode4_enable_) {
@@ -236,7 +237,6 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
}
// ------------------------
// Perform memory accesses.
// ------------------------
@@ -275,11 +275,16 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
// -------------------------------
// Check for interrupt conditions.
// -------------------------------
if(this->fetch_pointer_.column < this->mode_timing_.line_interrupt_position && end_column >= this->mode_timing_.line_interrupt_position) {
if constexpr (LineLayout<personality>::HasFixedLineInterrupt) {
// The Sega VDP offers a decrementing counter for triggering line interrupts;
// it is reloaded either when it overflows or upon every non-pixel line after the first.
// It is otherwise decremented.
if constexpr (is_sega_vdp(personality)) {
constexpr int FixedLineInterrupt = to_internal<personality, Origin::ModeLatch>(LineLayout<personality>::FixedLineInterrupt);
if(
this->fetch_pointer_.column < FixedLineInterrupt &&
end_column >= FixedLineInterrupt
) {
if(this->fetch_pointer_.row >= 0 && this->fetch_pointer_.row <= this->mode_timing_.pixel_lines) {
if(!this->line_interrupt_counter_) {
this->line_interrupt_pending_ = true;
@@ -291,14 +296,27 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
this->line_interrupt_counter_ = this->line_interrupt_target_;
}
}
}
if constexpr (is_yamaha_vdp(personality)) {
if(
this->vertical_active_ &&
this->fetch_pointer_.row == ((this->line_interrupt_target_ - Storage<personality>::vertical_offset_) & 0xff)
) {
this->line_interrupt_pending_ = true;
}
if constexpr (is_yamaha_vdp(personality)) {
// The Yamaha VDPs allow the user to specify which line an interrupt should occur on,
// which is relative to the current vertical base. Such an interrupt will occur immediately
// after pixels have ended.
if(
this->vertical_active_ &&
this->fetch_pointer_.column < Storage<personality>::mode_description_.end_cycle &&
end_column >= Storage<personality>::mode_description_.end_cycle &&
this->fetch_pointer_.row == ((this->line_interrupt_target_ - Storage<personality>::vertical_offset_) & 0xff)
) {
this->line_interrupt_pending_ = true;
Storage<personality>::line_matches_ = true;
}
if(
this->fetch_pointer_.column < Storage<personality>::mode_description_.start_cycle &&
end_column >= Storage<personality>::mode_description_.start_cycle
) {
Storage<personality>::line_matches_ = false;
}
}
@@ -319,7 +337,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
fetch_cycles_pool -= fetch_cycles;
// Check for end of line.
if(this->fetch_pointer_.column == Timing<personality>::CyclesPerLine) {
if(this->fetch_pointer_.column == LineLayout<personality>::CyclesPerLine) {
this->fetch_pointer_.column = 0;
this->fetch_pointer_.row = (this->fetch_pointer_.row + 1) % this->mode_timing_.total_lines;
@@ -341,13 +359,13 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
this->minimum_access_column_ =
std::max(
0,
this->minimum_access_column_ - Timing<personality>::CyclesPerLine
this->minimum_access_column_ - LineLayout<personality>::CyclesPerLine
);
if constexpr (is_yamaha_vdp(personality)) {
Storage<personality>::minimum_command_column_ =
std::max(
0,
Storage<personality>::minimum_command_column_ - Timing<personality>::CyclesPerLine
Storage<personality>::minimum_command_column_ - LineLayout<personality>::CyclesPerLine
);
}
@@ -369,6 +387,13 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
desc.pixels_per_byte = pixels_per_byte(this->underlying_mode_);
desc.width = width(this->underlying_mode_);
desc.rotate_address = interleaves_banks(this->underlying_mode_);
if(is_text(this->underlying_mode_)) {
desc.start_cycle = LineLayout<personality>::TextModeEndOfLeftBorder;
desc.end_cycle = LineLayout<personality>::TextModeEndOfPixels;
} else {
desc.start_cycle = LineLayout<personality>::EndOfLeftBorder;
desc.end_cycle = LineLayout<personality>::EndOfPixels;
}
}
// Based on the output mode, pick a line mode.
@@ -457,7 +482,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
if(output_cycles_pool) {
// Determine how much time has passed in the remainder of this line, and proceed.
const int target_output_cycles = std::min(
Timing<personality>::CyclesPerLine - this->output_pointer_.column,
LineLayout<personality>::CyclesPerLine - this->output_pointer_.column,
output_cycles_pool
);
int output_cycles_performed = 0;
@@ -522,8 +547,8 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
};
const auto right_blank = [&]() {
if(end_column == Timing<personality>::CyclesPerLine) {
output_blank(Timing<personality>::CyclesPerLine - LineLayout<personality>::EndOfRightBorder);
if(end_column == LineLayout<personality>::CyclesPerLine) {
output_blank(LineLayout<personality>::CyclesPerLine - LineLayout<personality>::EndOfRightBorder);
}
};
@@ -534,8 +559,8 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
) {
// Vertical sync.
// TODO: the Yamaha and Mega Drive both support interlaced video.
if(end_column == Timing<personality>::CyclesPerLine) {
output_sync(Timing<personality>::CyclesPerLine);
if(end_column == LineLayout<personality>::CyclesPerLine) {
output_sync(LineLayout<personality>::CyclesPerLine);
}
} else {
left_blank();
@@ -568,7 +593,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
if(this->pixel_target_) {
if constexpr (is_yamaha_vdp(personality)) {
draw(draw_yamaha(0, relative_start, relative_end), Clock::Internal); // TODO: what is the correct 'y'?
draw(draw_yamaha(0, relative_start, relative_end), Clock::Internal); // TODO: what is the correct 'y'?
} else {
switch(this->draw_line_buffer_->fetch_mode) {
case FetchMode::SMS: draw(draw_sms(relative_start, relative_end, cram_value), Clock::TMSPixel); break;
@@ -610,14 +635,14 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
// Advance time.
// -------------
this->output_pointer_.column = end_column;
if(end_column == Timing<personality>::CyclesPerLine) {
if(end_column == LineLayout<personality>::CyclesPerLine) {
// Advance line buffer.
this->advance(this->draw_line_buffer_);
}
}
output_cycles_pool -= target_output_cycles;
if(this->output_pointer_.column == Timing<personality>::CyclesPerLine) {
if(this->output_pointer_.column == LineLayout<personality>::CyclesPerLine) {
this->output_pointer_.column = 0;
this->output_pointer_.row = (this->output_pointer_.row + 1) % this->mode_timing_.total_lines;
}
@@ -685,7 +710,7 @@ void Base<personality>::write_vram(uint8_t value) {
// Enqueue the write to occur at the next available slot.
read_ahead_buffer_ = value;
queued_access_ = MemoryAccess::Write;
minimum_access_column_ = fetch_pointer_.column + Timing<personality>::VRAMAccessDelay;
minimum_access_column_ = fetch_pointer_.column + LineLayout<personality>::VRAMAccessDelay;
}
template <Personality personality>
@@ -957,7 +982,7 @@ void Base<personality>::commit_register(int reg, uint8_t value) {
switch(value >> 4) {
// All codes not listed below are invalid; treat them as STOP.
default:
case 0b0000: Storage<personality>::command_ = nullptr; break; // STOP.
case 0b0000: Storage<personality>::command_ = nullptr; break; // STOP.
case 0b0100: Begin(Point<true>); break; // POINT [read a pixel colour].
case 0b0101: Begin(Point<false>); break; // PSET [plot a pixel].
@@ -1034,7 +1059,7 @@ void Base<personality>::write_register(uint8_t value) {
// A read request is enqueued upon setting the address; conversely a write
// won't be enqueued unless and until some actual data is supplied.
queued_access_ = MemoryAccess::Read;
minimum_access_column_ = fetch_pointer_.column + Timing<personality>::VRAMAccessDelay;
minimum_access_column_ = fetch_pointer_.column + LineLayout<personality>::VRAMAccessDelay;
}
if constexpr (is_sega_vdp(personality)) {
@@ -1242,11 +1267,11 @@ HalfCycles TMS9918<personality>::get_next_sequence_point() const {
if(get_interrupt_line()) return HalfCycles::max();
// Calculate the amount of time until the next end-of-frame interrupt.
const int frame_length = Timing<personality>::CyclesPerLine * this->mode_timing_.total_lines;
const int frame_length = LineLayout<personality>::CyclesPerLine * this->mode_timing_.total_lines;
int time_until_frame_interrupt =
(
((this->mode_timing_.end_of_frame_interrupt_position.row * Timing<personality>::CyclesPerLine) + this->mode_timing_.end_of_frame_interrupt_position.column + frame_length) -
((this->fetch_pointer_.row * Timing<personality>::CyclesPerLine) + this->fetch_pointer_.column)
((this->mode_timing_.end_of_frame_interrupt_position.row * LineLayout<personality>::CyclesPerLine) + this->mode_timing_.end_of_frame_interrupt_position.column + frame_length) -
((this->fetch_pointer_.row * LineLayout<personality>::CyclesPerLine) + this->fetch_pointer_.column)
) % frame_length;
if(!time_until_frame_interrupt) time_until_frame_interrupt = frame_length;
@@ -1260,7 +1285,7 @@ HalfCycles TMS9918<personality>::get_next_sequence_point() const {
int cycles_to_next_interrupt_threshold = this->mode_timing_.line_interrupt_position - this->fetch_pointer_.column;
int line_of_next_interrupt_threshold = this->fetch_pointer_.row;
if(cycles_to_next_interrupt_threshold <= 0) {
cycles_to_next_interrupt_threshold += Timing<personality>::CyclesPerLine;
cycles_to_next_interrupt_threshold += LineLayout<personality>::CyclesPerLine;
++line_of_next_interrupt_threshold;
}
@@ -1291,7 +1316,7 @@ HalfCycles TMS9918<personality>::get_next_sequence_point() const {
// Figure out the number of internal cycles until the next line interrupt, which is the amount
// of time to the next tick over and then next_line_interrupt_row - row_ lines further.
const int lines_until_interrupt = (next_line_interrupt_row - line_of_next_interrupt_threshold + this->mode_timing_.total_lines) % this->mode_timing_.total_lines;
const int local_cycles_until_line_interrupt = cycles_to_next_interrupt_threshold + lines_until_interrupt * Timing<personality>::CyclesPerLine;
const int local_cycles_until_line_interrupt = cycles_to_next_interrupt_threshold + lines_until_interrupt * LineLayout<personality>::CyclesPerLine;
if(!this->generate_interrupts_) return this->clock_converter_.half_cycles_before_internal_cycles(local_cycles_until_line_interrupt);
// Return whichever interrupt is closer.
@@ -1305,7 +1330,7 @@ HalfCycles TMS9918<personality>::get_time_until_line(int line) {
int cycles_to_next_interrupt_threshold = this->mode_timing_.line_interrupt_position - this->fetch_pointer_.column;
int line_of_next_interrupt_threshold = this->fetch_pointer_.row;
if(cycles_to_next_interrupt_threshold <= 0) {
cycles_to_next_interrupt_threshold += Timing<personality>::CyclesPerLine;
cycles_to_next_interrupt_threshold += LineLayout<personality>::CyclesPerLine;
++line_of_next_interrupt_threshold;
}
@@ -1313,7 +1338,7 @@ HalfCycles TMS9918<personality>::get_time_until_line(int line) {
line += this->mode_timing_.total_lines;
}
return this->clock_converter_.half_cycles_before_internal_cycles(cycles_to_next_interrupt_threshold + (line - line_of_next_interrupt_threshold)*Timing<personality>::CyclesPerLine);
return this->clock_converter_.half_cycles_before_internal_cycles(cycles_to_next_interrupt_threshold + (line - line_of_next_interrupt_threshold)*LineLayout<personality>::CyclesPerLine);
}
template <Personality personality>
@@ -1329,7 +1354,7 @@ template <Personality personality>uint8_t TMS9918<personality>::get_latched_hori
// which counts the 256 pixels as items 0255, starts
// counting at -48, and returns only the top 8 bits of the number.
int public_counter = this->latched_column_ - LineLayout<personality>::EndOfLeftBorder;
if(public_counter < -46) public_counter += Timing<personality>::CyclesPerLine;
if(public_counter < -46) public_counter += LineLayout<personality>::CyclesPerLine;
return uint8_t(public_counter >> 1);
}

View File

@@ -170,16 +170,6 @@ template <Personality personality> struct Base: public Storage<personality> {
// then the appropriate status information will be set.
int maximum_visible_sprites = 4;
// Set the position, in cycles, of the two interrupts,
// within a line.
//
// TODO: redetermine where this number came from.
struct {
int column = 313;
int row = 192;
} end_of_frame_interrupt_position;
int line_interrupt_position = -1;
// Enables or disabled the recognition of the sprite
// list terminator, and sets the terminator value.
bool allow_sprite_terminator = true;
@@ -585,7 +575,7 @@ template <Personality personality> struct Base: public Storage<personality> {
/// Helper for TMS dispatches; contains a switch statement with cases 0 to 170, each of the form:
///
/// if constexpr (use_end && end == n) return; [[fallthrough]]; case n: fetcher.fetch<n>();
/// if constexpr (use_end && end == n) return; [[fallthrough]]; case n: fetcher.fetch<n>();
///
/// i.e. it provides standard glue to enter a fetch sequence at any point, while the fetches themselves are templated on the cycle
/// at which they appear for neater expression.
@@ -620,9 +610,9 @@ template <Personality personality> struct Base: public Storage<personality> {
template <SpriteMode mode, bool double_width> void draw_sprites(uint8_t y, int start, int end, const std::array<uint32_t, 16> &palette, int *colour_buffer = nullptr);
};
}
#include "Fetch.hpp"
#include "Draw.hpp"
}
#endif /* TMS9918Base_hpp */

View File

@@ -78,6 +78,9 @@ constexpr bool interleaves_banks(ScreenMode mode) {
return mode == ScreenMode::YamahaGraphics6 || mode == ScreenMode::YamahaGraphics7;
}
constexpr bool is_text(ScreenMode mode) {
return mode == ScreenMode::Text || mode == ScreenMode::YamahaText80;
}
enum class FetchMode {
Text,

View File

@@ -11,14 +11,28 @@
#include "../9918.hpp"
#include "PersonalityTraits.hpp"
#include "LineLayout.hpp"
namespace TI::TMS {
enum class Clock {
/// Whatever rate this VDP runs at, with location 0 being "the start" of the line per internal preference.
Internal,
/// A 342-cycle/line clock with the same start position as ::Internal.
TMSPixel,
/// A 171-cycle/line clock that begins at the memory window which starts straight after ::Internal = 0.
TMSMemoryWindow,
CRT
/// A fixed 1368-cycle/line clock that is used to count output to the CRT.
CRT,
};
enum class Origin {
///
ModeLatch,
/// Provides the same clock rate as ::Internal but is relocated so that 0 is the start of horizontal sync — very not coincidentally,
/// where Grauw puts 0 on his detailed TMS and Yamaha timing diagrams.
StartOfSync,
};
template <Personality personality, Clock clk> constexpr int clock_rate() {
@@ -43,31 +57,55 @@ template <Personality personality, Clock clk> constexpr int clock_rate() {
}
}
/// Scales @c length from @c clock to the internal clock rate.
template <Personality personality, Clock clock> constexpr int to_internal(int length) {
return length * clock_rate<personality, Clock::Internal>() / clock_rate<personality, clock>();
}
/// Moves @c position that is relative to @c Origin::StartOfSync so that it is relative to @c origin ;
/// i.e. can be thought of as "to [internal with origin as specified]".
template <Personality personality, Origin origin> constexpr int to_internal(int position) {
if constexpr (origin == Origin::ModeLatch) {
return (
position + LineLayout<personality>::CyclesPerLine - LineLayout<personality>::ModeLatchCycle
) % LineLayout<personality>::CyclesPerLine;
}
return position;
}
/// Converts @c position from one that is measured at the rate implied by @c clock and relative to @c Origin::StartOfSync
/// to one that is at the internal clock rate and relative to @c origin.
template <Personality personality, Origin origin, Clock clock> constexpr int to_internal(int position) {
position = to_internal<personality, clock>(position);
return to_internal<personality, origin>(position);
}
/// Scales @c length from the internal clock rate to @c clock.
template <Personality personality, Clock clock> constexpr int from_internal(int length) {
return length * clock_rate<personality, clock>() / clock_rate<personality, Clock::Internal>();
}
/// Provides default timing measurements that duplicate the layout of a TMS9928's line,
/// scaled to the clock rate specified.
template <Personality personality> struct StandardTiming {
/// The total number of internal cycles per line of output.
constexpr static int CyclesPerLine = clock_rate<personality, Clock::Internal>();
/// Moves @c position that is relative to @c origin so that it is relative to @c Origin::StartOfSync ;
/// i.e. can be thought of as "from [internal with origin as specified]".
template <Personality personality, Origin origin> constexpr int from_internal(int length) {
if constexpr (origin == Origin::ModeLatch) {
return (
length + LineLayout<personality>::ModeLatchCycle
) % LineLayout<personality>::CyclesPerLine;
}
return length;
}
/// 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;
};
/// Provides concrete, specific timing for the nominated personality.
template <Personality personality> struct Timing: public StandardTiming<personality> {};
/// Converts @c position from one that is measured at the internal clock rate and relative to @c origin
/// to one that is at the rate implied by @c clock and relative to @c Origin::StartOfSync
template <Personality personality, Origin origin, Clock clock> constexpr int from_internal(int position) {
position = from_internal<personality, origin>(position);
return from_internal<personality, clock>(position);
}
/*!
Provides a [potentially-]stateful conversion between the external and internal clocks.
Unlike the other clock conversions, this one may be non-integral, requiring that
Unlike the other clock conversions, this may be non-integral, requiring that
an error term be tracked.
*/
template <Personality personality> class ClockConverter {
@@ -144,54 +182,6 @@ template <Personality personality> class ClockConverter {
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 TextModeEndOfLeftBorder = 69;
constexpr static int TextModeEndOfPixels = 309;
};
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 TextModeEndOfLeftBorder = 294;
constexpr static int TextModeEndOfPixels = 1254;
};
}
#endif /* ClockConverter_hpp */

View File

@@ -9,11 +9,13 @@
#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) {
void Base<personality>::draw_sprites([[maybe_unused]] uint8_t y, int start, int end, const std::array<uint32_t, 16> &palette, int *colour_buffer) {
if(!draw_line_buffer_->sprites) {
return;
}
@@ -344,7 +346,7 @@ void Base<personality>::draw_tms_text(int start, int end) {
// MARK: - Master System
template <Personality personality>
void Base<personality>::draw_sms(int start, int end, uint32_t cram_dot) {
void Base<personality>::draw_sms([[maybe_unused]] int start, [[maybe_unused]] int end, [[maybe_unused]] uint32_t cram_dot) {
if constexpr (is_sega_vdp(personality)) {
int colour_buffer[256];
auto &line_buffer = *draw_line_buffer_;
@@ -449,7 +451,7 @@ void Base<personality>::draw_sms(int start, int end, uint32_t cram_dot) {
template <Personality personality>
template <ScreenMode mode>
void Base<personality>::draw_yamaha(uint8_t y, int start, int end) {
const auto active_palette = palette();
[[maybe_unused]] const auto active_palette = palette();
const int sprite_start = start >> 2;
const int sprite_end = end >> 2;
auto &line_buffer = *draw_line_buffer_;
@@ -470,7 +472,7 @@ void Base<personality>::draw_yamaha(uint8_t y, int start, int end) {
switch(offset) {
case 0:
do {
pixel_target_[column+0] = active_palette[line_buffer.bitmap[start] >> 4];
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;
@@ -490,9 +492,9 @@ void Base<personality>::draw_yamaha(uint8_t y, int start, int end) {
switch(offset) {
case 0:
do {
pixel_target_[column+0] = active_palette[line_buffer.bitmap[start] >> 6];
case 1: pixel_target_[column+1] = active_palette[(line_buffer.bitmap[start] >> 4) & 3];
case 2: pixel_target_[column+2] = active_palette[(line_buffer.bitmap[start] >> 2) & 3];
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;
@@ -565,4 +567,6 @@ void Base<personality>::draw_yamaha(uint8_t y, int start, int end) {
// TODO.
}
#endif /* Draw_hpp */

View File

@@ -9,43 +9,26 @@
#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.
2) times are measured relative to the an appropriate clock they directly
count access windows on the TMS and Master System, and cycles on a Yamaha.
3) within each sequencer, cycle are numbered as per Grauw's timing diagrams. The difference
between those and internal timing, if there is one, is handled by the dispatcher.
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.]
end is < [cycles per line], false otherwise. So functions can use it to eliminate
should-exit-now checks (which is likely to be the more usual path of execution).
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.
All functions should just spool data to intermediary storage. Fetching and drawing are decoupled.
*/
// MARK: - Address mask helpers.
@@ -71,7 +54,7 @@ template<bool use_end, typename SequencerT> void Base<personality>::dispatch(Seq
#define index(n) \
if(use_end && end == n) return; \
[[fallthrough]]; \
case n: fetcher.template fetch<n>();
case n: fetcher.template fetch<from_internal<personality, Origin::StartOfSync>(n)>();
switch(start) {
default: assert(false);
@@ -204,7 +187,7 @@ class SpriteFetcher {
// 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);
static constexpr AddressT AttributeAddressMask = (mode == SpriteMode::Mode2) ? AddressT(~0x180) : AddressT(~0x000);
SpriteFetcher(Base<personality> *base, uint8_t y) :
base(base),
@@ -390,7 +373,7 @@ struct RefreshSequencer {
template <int cycle> void fetch() {
if(cycle < 26 || (cycle & 1) || cycle >= 154) {
base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
base->do_external_slot(to_internal<personality, Origin::ModeLatch, Clock::TMSMemoryWindow>(cycle));
}
}
@@ -404,16 +387,22 @@ struct TextSequencer {
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));
fetcher.base->do_external_slot(to_internal<personality, Origin::ModeLatch, 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.
case 0: // (1) fetch tile name.
fetcher.fetch_name(column);
break;
case 1: // (2) external slot.
fetcher.base->do_external_slot(to_internal<personality, Origin::ModeLatch, Clock::TMSMemoryWindow>(cycle));
break;
case 2: // (3) fetch tile pattern.
fetcher.fetch_pattern(column);
break;
}
}
}
@@ -430,7 +419,7 @@ struct CharacterSequencer {
template <int cycle> void fetch() {
if(cycle < 5) {
character_fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
character_fetcher.base->do_external_slot(to_internal<personality, Origin::ModeLatch, Clock::TMSMemoryWindow>(cycle));
}
if(cycle == 5) {
@@ -441,7 +430,7 @@ struct CharacterSequencer {
}
if(cycle > 14 && cycle < 19) {
character_fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
character_fetcher.base->do_external_slot(to_internal<personality, Origin::ModeLatch, Clock::TMSMemoryWindow>(cycle));
}
// Fetch 8 new sprite Y coordinates, to begin selecting sprites for next line.
@@ -459,7 +448,7 @@ struct CharacterSequencer {
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));
character_fetcher.base->do_external_slot(to_internal<personality, Origin::ModeLatch, Clock::TMSMemoryWindow>(cycle));
} else {
constexpr int sprite = 8 + ((block >> 2) * 3) + ((block & 3) - 1);
sprite_fetcher.fetch_y(sprite);
@@ -474,7 +463,7 @@ struct CharacterSequencer {
}
if(cycle >= 155 && cycle < 157) {
character_fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
character_fetcher.base->do_external_slot(to_internal<personality, Origin::ModeLatch, Clock::TMSMemoryWindow>(cycle));
}
if(cycle == 157) {
@@ -522,7 +511,7 @@ struct SMSSequencer {
// window 0 to HSYNC low.
template <int cycle> void fetch() {
if(cycle < 3) {
fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
fetcher.base->do_external_slot(to_internal<personality, Origin::ModeLatch, Clock::TMSMemoryWindow>(cycle));
}
if(cycle == 3) {
@@ -533,7 +522,7 @@ struct SMSSequencer {
}
if(cycle == 15 || cycle == 16) {
fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
fetcher.base->do_external_slot(to_internal<personality, Origin::ModeLatch, Clock::TMSMemoryWindow>(cycle));
}
if(cycle == 17) {
@@ -554,7 +543,7 @@ struct SMSSequencer {
case 0: fetcher.fetch_tile_name(block); break;
case 1:
if(!(block & 3)) {
fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
fetcher.base->do_external_slot(to_internal<personality, Origin::ModeLatch, Clock::TMSMemoryWindow>(cycle));
} else {
constexpr int sprite = (8 + ((block >> 2) * 3) + ((block & 3) - 1)) << 1;
fetcher.posit_sprite(sprite);
@@ -566,7 +555,7 @@ struct SMSSequencer {
}
if(cycle >= 153 && cycle < 157) {
fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
fetcher.base->do_external_slot(to_internal<personality, Origin::ModeLatch, Clock::TMSMemoryWindow>(cycle));
}
if(cycle == 157) {
@@ -577,7 +566,7 @@ struct SMSSequencer {
}
if(cycle >= 169) {
fetcher.base->do_external_slot(to_internal<personality, Clock::TMSMemoryWindow>(cycle));
fetcher.base->do_external_slot(to_internal<personality, Origin::ModeLatch, Clock::TMSMemoryWindow>(cycle));
}
}
@@ -586,7 +575,7 @@ struct SMSSequencer {
};
template <Personality personality>
template<bool use_end> void Base<personality>::fetch_sms(uint8_t y, int start, int end) {
template<bool use_end> void Base<personality>::fetch_sms([[maybe_unused]] uint8_t y, [[maybe_unused]] int start, [[maybe_unused]] int end) {
if constexpr (is_sega_vdp(personality)) {
SMSSequencer<personality> sequencer(this, y);
dispatch<use_end>(sequencer, start, end);
@@ -802,4 +791,6 @@ template<bool use_end> void Base<personality>::fetch_yamaha(uint8_t y, int, int
// TODO.
}
#endif /* Fetch_hpp */

View File

@@ -0,0 +1,110 @@
//
// LineLayout.hpp
// Clock Signal
//
// Created by Thomas Harte on 18/05/2023.
// Copyright © 2023 Thomas Harte. All rights reserved.
//
#ifndef LineLayout_h
#define LineLayout_h
namespace TI::TMS {
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.
//
// ModeLaytchCycle is the cycle at which the video mode, blank disable/enable and
// sprite enable/disable are latched for the line.
template <Personality personality> struct LineLayout<personality, std::enable_if_t<is_classic_vdp(personality) && !is_sega_vdp(personality)>> {
constexpr static int StartOfSync = 0;
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;
constexpr static int ModeLatchCycle = 36; // Just a guess; correlates with the known 144 for the Yamaha VDPs,
// and falls into the collection gap between the final sprite
// graphics and the initial tiles or pixels.
constexpr static bool HasDynamicLineInterrupt = false;
constexpr static bool HasFixedLineInterrupt = false;
constexpr static int EndOfFrameInterrupt = 313;
/// 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_sega_vdp(personality)>> :
public LineLayout<Personality::TMS9918A> {
// Cf. https://www.smspower.org/forums/8161-SMSDisplayTiming
// "For a line interrupt, /INT is pulled low 608 mclks into the appropriate scanline relative to pixel 0.
// This is 3 mclks before the rising edge of /HSYNC which starts the next scanline."
//
// i.e. it's 304 internal clocks after the end of the left border.
constexpr static bool HasFixedLineInterrupt = false;
constexpr static int FixedLineInterrupt = (EndOfLeftBorder + 304) % CyclesPerLine;
// For a frame interrupt, /INT is pulled low 607 mclks into scanline 192 (of scanlines 0 through 261) relative to pixel 0.
// This is 4 mclks before the rising edge of /HSYNC which starts the next scanline.
//
// i.e. it's 1/2 cycle before the line interrupt position, which I have rounded. Ugh.
constexpr static int EndOfFrameInterrupt = 313;
};
template <Personality personality> struct LineLayout<personality, std::enable_if_t<is_yamaha_vdp(personality)>> {
constexpr static int StartOfSync = 0;
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;
constexpr static int ModeLatchCycle = 144;
constexpr static bool HasDynamicLineInterrupt = true;
constexpr static bool HasFixedLineInterrupt = false;
constexpr static int EndOfFrameInterrupt = 313;
/// 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 /* LineLayout_h */

View File

@@ -14,6 +14,7 @@
#include "YamahaCommands.hpp"
#include <optional>
#include <vector>
namespace TI::TMS {
@@ -27,113 +28,274 @@ template <> struct Storage<Personality::TMS9918A> {
void begin_line(ScreenMode, bool) {}
};
// Yamaha-specific storage.
template <Personality personality> struct Storage<personality, std::enable_if_t<is_yamaha_vdp(personality)>> {
using AddressT = uint32_t;
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,
std::array<uint8_t, 65536> expansion_ram_;
//
// Sprites.
//
SpriteY,
SpriteLocation,
SpritePattern,
int selected_status_ = 0;
//
// Backgrounds.
//
Name,
Colour,
Pattern,
} type = Type::External;
uint8_t id = 0;
int indirect_register_ = 0;
bool increment_indirect_register_ = false;
constexpr Event(Type type, uint8_t id = 0) noexcept :
type(type),
id(id) {}
int adjustment_[2]{};
constexpr Event() noexcept {}
};
std::array<uint32_t, 16> palette_{};
std::array<uint32_t, 16> background_palette_{};
bool solid_background_ = true;
// State that tracks fetching position within a line.
const Event *next_event_ = nullptr;
uint8_t new_colour_ = 0;
uint8_t palette_entry_ = 0;
bool palette_write_phase_ = false;
// Sprite collection state.
bool sprites_enabled_ = true;
uint8_t mode_ = 0;
uint8_t vertical_offset_ = 0;
uint8_t sprite_cache_[8][32]{};
/// 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;
// 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;
// Sprite collection state.
bool sprites_enabled_ = true;
// Additional status.
uint8_t colour_status_ = 0;
uint16_t colour_location_ = 0;
uint16_t collision_location_[2]{};
/// 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;
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;
}
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;
/// @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++) {
// Specific personality doesn't matter here; both Yamahas use the same internal timing.
const int mapped_location = from_internal<Personality::V9938, Origin::StartOfSync>(c);
const auto event = GeneratorT::event(mapped_location);
if(!event) {
continue;
}
result[index] = *event;
result[index].offset = uint16_t(c);
++index;
}
result[index] = Event();
return result;
}
}
// Command engine state.
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;
@@ -191,256 +353,96 @@ template <Personality personality> struct Storage<personality, std::enable_if_t<
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]{};
bool line_matches_ = false;
Storage() noexcept {
// Perform sanity checks on the event lists.
#ifndef NDEBUG
const Event *lists[] = { no_sprites_events.data(), sprites_events.data(), text_events.data(), character_events.data(), refresh_events.data(), nullptr };
const Event **list = lists;
while(*list) {
const Event *cursor = *list;
++list;
while(cursor[1].offset != 1368) {
assert(cursor[1].offset > cursor[0].offset);
++cursor;
}
}
#endif
// Seed to _something_ meaningful.
//
// TODO: this is a workaround [/hack], in effect, for the main TMS' habit of starting
// in a randomised position, which means that start-of-line isn't announced.
//
// Do I really want that behaviour?
// 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:
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;
}
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;
}
};
static constexpr auto refresh_events = events<RefreshGenerator>();
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;
}
};
static constexpr auto no_sprites_events = events<BitmapGenerator<false>>();
static constexpr auto sprites_events = events<BitmapGenerator<true>>();
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;
}
};
static constexpr auto text_events = events<TextGenerator>();
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;
}
};
static constexpr auto character_events = events<CharacterGenerator>();
};

View File

@@ -89,6 +89,8 @@ struct ModeDescription {
int width = 256;
int pixels_per_byte = 4;
bool rotate_address = false;
int start_cycle = 0;
int end_cycle = 0;
};
struct Command {

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
@@ -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.
@@ -196,7 +195,7 @@ class SerialClock: public ClockStorage {
Sets the current clock and data inputs to the clock.
*/
void set_input(bool clock, bool data) {
// The data line is valid when the clock transitions to level 0.
// The data line is valid when the clock transitions to level 0.
if(clock && !previous_clock_) {
// Shift into the command_ register, no matter what.
command_ = uint16_t((command_ << 1) | (data ? 1 : 0));
@@ -293,7 +292,6 @@ class ParallelClock: public ClockStorage {
uint8_t control_;
};
}
}
#endif /* Apple_RealTimeClock_hpp */

View File

@@ -13,7 +13,7 @@
using namespace Apple;
namespace {
namespace {
const uint8_t input_command = 0x4; // i.e. Q6
const uint8_t input_mode = 0x8; // i.e. Q7
const uint8_t input_flux = 0x1;
@@ -144,7 +144,7 @@ void DiskII::decide_clocking_preference() {
// none, given that drives are not running, the shift register has already emptied or stopped and there's no flux about to be received.
if(!(inputs_ & ~input_flux)) {
const bool is_stuck_at_nop =
!flux_duration_ && state_machine_[(state_ & 0xf0) | inputs_ | ((shift_register_&0x80) >> 6)] == state_ && (state_ &0xf) == 0x8;
!flux_duration_ && state_machine_[(state_ & 0xf0) | inputs_ | ((shift_register_&0x80) >> 6)] == state_ && (state_ &0xf) == 0x8;
clocking_preference_ =
(drive_is_sleeping_[0] && drive_is_sleeping_[1] && (!shift_register_ || is_stuck_at_nop) && (inputs_&input_flux))

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

@@ -17,7 +17,7 @@
using namespace Apple;
namespace {
namespace {
constexpr int CA0 = 1 << 0;
constexpr int CA1 = 1 << 1;
constexpr int CA2 = 1 << 2;
@@ -220,7 +220,7 @@ void IWM::set_select(bool enabled) {
}
void IWM::push_drive_state() {
if(drives_[active_drive_]) {
if(drives_[active_drive_]) {
const uint8_t drive_control_lines =
((state_ & CA0) ? IWMDrive::CA0 : 0) |
((state_ & CA1) ? IWMDrive::CA1 : 0) |

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

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

View File

@@ -9,8 +9,7 @@
#ifndef KeyLevelScaler_h
#define KeyLevelScaler_h
namespace Yamaha {
namespace OPL {
namespace Yamaha::OPL {
template <int frequency_precision> class KeyLevelScaler {
public:
@@ -51,8 +50,6 @@ template <int frequency_precision> class KeyLevelScaler {
int shift_ = 0;
};
}
}
#endif /* KeyLevelScaler_h */

View File

@@ -11,8 +11,7 @@
#include "../../../Numeric/LFSR.hpp"
namespace Yamaha {
namespace OPL {
namespace Yamaha::OPL {
/*!
Models the output of the OPL low-frequency oscillator, which provides a couple of optional fixed-frequency
@@ -54,7 +53,7 @@ class LowFrequencyOscillator {
/// Updartes the LFSR output. Should be called at the input clock rate.
void update_lfsr() {
lfsr = noise_source_.next();
lfsr = noise_source_.next();
}
private:
@@ -62,7 +61,6 @@ class LowFrequencyOscillator {
Numeric::LFSR<int, 0x800302> noise_source_;
};
}
}
#endif /* LowFrequencyOscillator_hpp */

View File

@@ -12,8 +12,7 @@
#include "../../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../../Concurrency/AsyncTaskQueue.hpp"
namespace Yamaha {
namespace OPL {
namespace Yamaha::OPL {
template <typename Child> class OPLBase: public ::Outputs::Speaker::SampleSource {
public:
@@ -34,7 +33,6 @@ template <typename Child> class OPLBase: public ::Outputs::Speaker::SampleSource
uint8_t selected_register_ = 0;
};
}
}
#endif /* OPLBase_h */

View File

@@ -13,8 +13,7 @@
#include "LowFrequencyOscillator.hpp"
#include "Tables.hpp"
namespace Yamaha {
namespace OPL {
namespace Yamaha::OPL {
/*!
Models an OPL-style phase generator of templated precision; having been told its period ('f-num'), octave ('block') and
@@ -109,7 +108,7 @@ template <int precision> class PhaseGenerator {
}
private:
static constexpr int precision_shift = 1 + precision;
static constexpr int precision_shift = 1 + precision;
int phase_ = 0;
@@ -119,7 +118,6 @@ template <int precision> class PhaseGenerator {
int enable_vibrato_ = 0;
};
}
}
#endif /* PhaseGenerator_h */

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