mirror of
https://github.com/TomHarte/CLK.git
synced 2025-09-12 02:24:31 +00:00
Compare commits
105 Commits
2023-05-08
...
SeparateFe
Author | SHA1 | Date | |
---|---|---|---|
|
e7299c16f6 | ||
|
cdb86022a6 | ||
|
2262725010 | ||
|
e61a4eb5a9 | ||
|
acd7f9f4cd | ||
|
9f1a657cc4 | ||
|
e52d1866ab | ||
|
a02b8222fa | ||
|
3762ee1a63 | ||
|
3ec61e8770 | ||
|
2f7dd0b01a | ||
|
3a02c22072 | ||
|
6ae967de51 | ||
|
5d45aa4a6a | ||
|
0f1468adfd | ||
|
e9347168e6 | ||
|
e2dcb0a8e2 | ||
|
1797bab28f | ||
|
7f48cd6d9d | ||
|
8662f06ae5 | ||
|
3b67d48ebf | ||
|
2ab16867cb | ||
|
3a93b8059a | ||
|
3e09afbb59 | ||
|
9703fed9f8 | ||
|
f30637a773 | ||
|
1d8bc41724 | ||
|
d36a88dd11 | ||
|
de5ee8f0d0 | ||
|
6261ac24b4 | ||
|
b00eac4a34 | ||
|
6e35d84a96 | ||
|
d028555361 | ||
|
1aa953dd4d | ||
|
77c67ab59d | ||
|
05d2e78f80 | ||
|
837d8d29ca | ||
|
8a831b1409 | ||
|
c0547f6e14 | ||
|
81e475f052 | ||
|
4e12d5a70a | ||
|
c630f86f33 | ||
|
1de2631877 | ||
|
dd3fc43bd3 | ||
|
40d5bd4e58 | ||
|
c75efb7dac | ||
|
d117a44069 | ||
|
dc425a03d3 | ||
|
ce8bd011d7 | ||
|
c76048bff9 | ||
|
4cb7abe13d | ||
|
c445295411 | ||
|
5c51bae605 | ||
|
8578dfbf22 | ||
|
f821b60430 | ||
|
8ca0d9e13a | ||
|
3014c957e7 | ||
|
747dc09a80 | ||
|
7f8f1d7e61 | ||
|
a1a7c0e253 | ||
|
9342c6005f | ||
|
14ac4da813 | ||
|
b0e3bd85d6 | ||
|
9b6be2571a | ||
|
4ede538d36 | ||
|
8bf3d85e36 | ||
|
ec9abbe6a7 | ||
|
22ac13d3f2 | ||
|
413ab42b16 | ||
|
876fc6d1e0 | ||
|
b768e438b2 | ||
|
e1d671daf7 | ||
|
4989701de9 | ||
|
fed97b8d26 | ||
|
e7888497b7 | ||
|
0b53c73da8 | ||
|
a6ebfe2ce2 | ||
|
b89076cb72 | ||
|
50343dec43 | ||
|
28c79b2885 | ||
|
60bec3d4c0 | ||
|
56de9c418f | ||
|
5bcb5fb832 | ||
|
abeb361441 | ||
|
f9cc2013a8 | ||
|
e7c40eead9 | ||
|
c29d80006e | ||
|
596661bfbe | ||
|
7e319374b6 | ||
|
7f5d129b13 | ||
|
f6acee18cc | ||
|
3af30b1fec | ||
|
a8cc74f9fe | ||
|
10cd2a36cf | ||
|
ea50d5bda7 | ||
|
809cd7bca9 | ||
|
e56db3c4e5 | ||
|
2b56b7be0d | ||
|
25a245e35c | ||
|
882384b1f3 | ||
|
5cc19f436f | ||
|
fd0eaa5788 | ||
|
992a47c196 | ||
|
8540e7a953 | ||
|
6b19bfeab2 |
@@ -9,6 +9,7 @@
|
||||
#ifndef ActivityObserver_h
|
||||
#define ActivityObserver_h
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace Activity {
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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)) {
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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.
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -11,7 +11,7 @@
|
||||
#include "Kernel.hpp"
|
||||
|
||||
using namespace Analyser::Static::MOS6502;
|
||||
namespace {
|
||||
namespace {
|
||||
|
||||
using PartialDisassembly = Analyser::Static::Disassembly::PartialDisassembly<Disassembly, uint16_t>;
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -11,7 +11,7 @@
|
||||
#include "Kernel.hpp"
|
||||
|
||||
using namespace Analyser::Static::Z80;
|
||||
namespace {
|
||||
namespace {
|
||||
|
||||
using PartialDisassembly = Analyser::Static::Disassembly::PartialDisassembly<Disassembly, uint16_t>;
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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); \
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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) {
|
||||
|
@@ -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.
|
||||
|
@@ -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) |
|
||||
|
@@ -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 */
|
||||
|
@@ -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"
|
||||
|
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
@@ -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"
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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,
|
||||
|
||||
|
@@ -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 0–255, 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);
|
||||
}
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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,
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
110
Components/9918/Implementation/LineLayout.hpp
Normal file
110
Components/9918/Implementation/LineLayout.hpp
Normal 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 */
|
@@ -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>();
|
||||
};
|
||||
|
||||
|
@@ -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 {
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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))
|
||||
|
@@ -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 */
|
||||
|
@@ -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) |
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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
|
||||
@@ -62,7 +61,6 @@ class LowFrequencyOscillator {
|
||||
Numeric::LFSR<int, 0x800302> noise_source_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LowFrequencyOscillator_hpp */
|
||||
|
@@ -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 */
|
||||
|
@@ -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
Reference in New Issue
Block a user