mirror of
https://github.com/TomHarte/CLK.git
synced 2025-10-25 09:27:01 +00:00
Compare commits
198 Commits
2021-06-13
...
2021-07-09
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5810a1a98e | ||
|
|
a4c011e3c0 | ||
|
|
337fd15dc0 | ||
|
|
9bc94f4536 | ||
|
|
3f4cf35384 | ||
|
|
4dd7f2cc09 | ||
|
|
1b29cc34c4 | ||
|
|
53c3c1f5ab | ||
|
|
6225abd751 | ||
|
|
c6fcd9a1eb | ||
|
|
30fbb6ea53 | ||
|
|
0e49258546 | ||
|
|
264b8dfb28 | ||
|
|
6a15b8f695 | ||
|
|
5167d256cc | ||
|
|
16bd826491 | ||
|
|
55af8fa5d9 | ||
|
|
1ec8ff20af | ||
|
|
99a65d3297 | ||
|
|
94907b51aa | ||
|
|
0085265d13 | ||
|
|
8e0893bd42 | ||
|
|
704dc9bdcb | ||
|
|
7a673a2448 | ||
|
|
33e2a4b21c | ||
|
|
3e6b804896 | ||
|
|
e98165a657 | ||
|
|
2a7727d12b | ||
|
|
c20e8f4062 | ||
|
|
4ca9db7d49 | ||
|
|
4add48cffb | ||
|
|
adbfb009f8 | ||
|
|
43ceca8711 | ||
|
|
3ef28a4f03 | ||
|
|
adcd580d5b | ||
|
|
5715c9183f | ||
|
|
ceb62ac7f9 | ||
|
|
bda0756620 | ||
|
|
6b47fb38c6 | ||
|
|
38bf8a06a7 | ||
|
|
196651d9aa | ||
|
|
6b46212a4e | ||
|
|
2a6fff2008 | ||
|
|
c5944efe50 | ||
|
|
f384370b18 | ||
|
|
0c09275a9f | ||
|
|
278671cdb9 | ||
|
|
964d2d4fa4 | ||
|
|
f371221dba | ||
|
|
27b0579ec6 | ||
|
|
283092cfbc | ||
|
|
614953a222 | ||
|
|
4fffb3cf19 | ||
|
|
850aa2b23a | ||
|
|
d715e5fd1d | ||
|
|
7826a26c7b | ||
|
|
dc0a82cf9a | ||
|
|
2e60c81bd6 | ||
|
|
763b9ba0ec | ||
|
|
bae8bb0c00 | ||
|
|
bcf483fb7e | ||
|
|
a5b7d819a7 | ||
|
|
fe07a0b1d8 | ||
|
|
d9231e5d4a | ||
|
|
b7aa1a1c84 | ||
|
|
32e144115d | ||
|
|
177cc96f49 | ||
|
|
51d98ef9ab | ||
|
|
2327c48cc4 | ||
|
|
742d44a532 | ||
|
|
52b96db2b9 | ||
|
|
0b9de78c38 | ||
|
|
2c28cb8c57 | ||
|
|
483fe82e9d | ||
|
|
29492d6138 | ||
|
|
19310e32c4 | ||
|
|
c04a395499 | ||
|
|
1c424833a9 | ||
|
|
a46ff5590d | ||
|
|
ab059b63fd | ||
|
|
3d8fc9952d | ||
|
|
8ce8fbd977 | ||
|
|
7f08218b28 | ||
|
|
2c139ad931 | ||
|
|
1119779c8b | ||
|
|
5351ac560f | ||
|
|
49f0ab0f15 | ||
|
|
a5c57e777e | ||
|
|
3c59042388 | ||
|
|
919e211bc4 | ||
|
|
daa0737ce4 | ||
|
|
36805cb120 | ||
|
|
7de69e9874 | ||
|
|
b93575bbcc | ||
|
|
116e0f0105 | ||
|
|
e4a650aaff | ||
|
|
b5312b9ba0 | ||
|
|
6afee7bb9b | ||
|
|
5729e6e13a | ||
|
|
2f53b105bb | ||
|
|
b698056f78 | ||
|
|
95c906f03d | ||
|
|
be19fa9dde | ||
|
|
81e9ba5608 | ||
|
|
f2d7b9f6a9 | ||
|
|
1ea034310a | ||
|
|
bdcab447f9 | ||
|
|
10bf6744aa | ||
|
|
895d98e266 | ||
|
|
903e343895 | ||
|
|
f8b7c59616 | ||
|
|
fcd267a3f9 | ||
|
|
f8bb66d2a0 | ||
|
|
90782d3c27 | ||
|
|
f2336d2efc | ||
|
|
c2d093fa3c | ||
|
|
1a97cc8a91 | ||
|
|
c34a548fa0 | ||
|
|
d1b89392a2 | ||
|
|
ed734754e5 | ||
|
|
520c3c9218 | ||
|
|
9230cf1726 | ||
|
|
6e616972a5 | ||
|
|
f98888824d | ||
|
|
6c8b23e708 | ||
|
|
2c2bb3765f | ||
|
|
0d165740ea | ||
|
|
88f0f2b623 | ||
|
|
0afa143375 | ||
|
|
8319aca351 | ||
|
|
a66734883a | ||
|
|
d2ab0dd839 | ||
|
|
2574407afb | ||
|
|
83a54fd6d2 | ||
|
|
e062780968 | ||
|
|
3acd0be1f7 | ||
|
|
69c0734975 | ||
|
|
c1678d7be7 | ||
|
|
117f9a9794 | ||
|
|
b49cc407c6 | ||
|
|
954386f1cc | ||
|
|
d7ff6bd04d | ||
|
|
6025516f9f | ||
|
|
d8b9cdf7a2 | ||
|
|
09dbff39f2 | ||
|
|
2fe15a6168 | ||
|
|
07dc26f8fa | ||
|
|
a08d65b1ff | ||
|
|
199621db08 | ||
|
|
0e1e8c7faa | ||
|
|
42a98e1676 | ||
|
|
23e26e0333 | ||
|
|
fadb04f3f3 | ||
|
|
4968ccf46d | ||
|
|
1dcac304d3 | ||
|
|
1651efe4fc | ||
|
|
8f24aed43e | ||
|
|
a381374e31 | ||
|
|
9411c37d23 | ||
|
|
6af6f21868 | ||
|
|
9a0022cfcb | ||
|
|
266310d9c2 | ||
|
|
fbf1adef05 | ||
|
|
311ddfb05a | ||
|
|
2fd8a8aa66 | ||
|
|
0c3e9dca28 | ||
|
|
c331d15429 | ||
|
|
4414e96710 | ||
|
|
7161783a4f | ||
|
|
cbac48da86 | ||
|
|
d9142d5427 | ||
|
|
e5e988b28f | ||
|
|
e94e051c87 | ||
|
|
5fc91effb5 | ||
|
|
6c9dacbe89 | ||
|
|
6a7eb832cc | ||
|
|
60cf8486bb | ||
|
|
90b8163d54 | ||
|
|
a1e4389f63 | ||
|
|
440b11708b | ||
|
|
f90dce5c54 | ||
|
|
606c7709cf | ||
|
|
1d1e6d1820 | ||
|
|
3eb4dd74a2 | ||
|
|
853914480c | ||
|
|
fe04410681 | ||
|
|
1f686c4e6b | ||
|
|
2a2ac1227b | ||
|
|
b5340c8f74 | ||
|
|
196c4dcdd9 | ||
|
|
c5a86f0ef7 | ||
|
|
88f2a2940b | ||
|
|
26b019a4d4 | ||
|
|
5f7b3ae313 | ||
|
|
61c127ed2e | ||
|
|
333981e2a7 | ||
|
|
8e0a6df03b | ||
|
|
2a6e9c5e8a |
@@ -19,6 +19,7 @@ enum class Machine {
|
||||
AtariST,
|
||||
ColecoVision,
|
||||
Electron,
|
||||
Enterprise,
|
||||
Macintosh,
|
||||
MasterSystem,
|
||||
MSX,
|
||||
|
||||
81
Analyser/Static/Enterprise/StaticAnalyser.cpp
Normal file
81
Analyser/Static/Enterprise/StaticAnalyser.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// StaticAnalyser.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 24/06/2021.
|
||||
// Copyright 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
#include "../../../Storage/Disk/Parsers/FAT.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
|
||||
bool insensitive_equal(const std::string &lhs, const std::string &rhs) {
|
||||
return std::equal(
|
||||
lhs.begin(), lhs.end(),
|
||||
rhs.begin(), rhs.end(),
|
||||
[] (char l, char r) {
|
||||
return tolower(l) == tolower(r);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Enterprise::GetTargets(const Media &media, const std::string &, TargetPlatform::IntType) {
|
||||
// This analyser can comprehend disks only.
|
||||
if(media.disks.empty()) return {};
|
||||
|
||||
// Otherwise, assume a return will happen.
|
||||
Analyser::Static::TargetList targets;
|
||||
using Target = Analyser::Static::Enterprise::Target;
|
||||
auto *const target = new Target;
|
||||
target->media = media;
|
||||
|
||||
// Always require a BASIC.
|
||||
target->basic_version = Target::BASICVersion::Any;
|
||||
|
||||
// Inspect any supplied disks.
|
||||
if(!media.disks.empty()) {
|
||||
// DOS will be needed.
|
||||
target->dos = Target::DOS::EXDOS;
|
||||
|
||||
// Grab the volume information, which includes the root directory.
|
||||
auto volume = Storage::Disk::FAT::GetVolume(media.disks.front());
|
||||
if(volume) {
|
||||
// If there's an EXDOS.INI then this disk should be able to boot itself.
|
||||
// If not but if there's only one .COM or .BAS, automatically load that.
|
||||
// Failing that, issue a :DIR and give the user a clue as to how to load.
|
||||
const Storage::Disk::FAT::File *selected_file = nullptr;
|
||||
bool has_exdos_ini = false;
|
||||
bool did_pick_file = false;
|
||||
for(const auto &file: (*volume).root_directory) {
|
||||
if(insensitive_equal(file.name, "exdos") && insensitive_equal(file.extension, "ini")) {
|
||||
has_exdos_ini = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(insensitive_equal(file.extension, "com") || insensitive_equal(file.extension, "bas")) {
|
||||
did_pick_file = !selected_file;
|
||||
selected_file = &file;
|
||||
}
|
||||
}
|
||||
|
||||
if(!has_exdos_ini) {
|
||||
if(did_pick_file) {
|
||||
target->loading_command = std::string("run \"") + selected_file->name + "." + selected_file->extension + "\"\n";
|
||||
} else {
|
||||
target->loading_command = ":dir\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
targets.push_back(std::unique_ptr<Analyser::Static::Target>(target));
|
||||
|
||||
return targets;
|
||||
}
|
||||
27
Analyser/Static/Enterprise/StaticAnalyser.hpp
Normal file
27
Analyser/Static/Enterprise/StaticAnalyser.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// StaticAnalyser.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 24/06/2021.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Analyser_Static_Enterprise_StaticAnalyser_hpp
|
||||
#define Analyser_Static_Enterprise_StaticAnalyser_hpp
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Enterprise {
|
||||
|
||||
TargetList GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* Analyser_Static_Enterprise_StaticAnalyser_hpp */
|
||||
57
Analyser/Static/Enterprise/Target.hpp
Normal file
57
Analyser/Static/Enterprise/Target.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Target.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/06/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Analyser_Static_Enterprise_Target_h
|
||||
#define Analyser_Static_Enterprise_Target_h
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Enterprise {
|
||||
|
||||
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
ReflectableEnum(Model, Enterprise64, Enterprise128, Enterprise256);
|
||||
ReflectableEnum(EXOSVersion, v10, v20, v21, v23, Any);
|
||||
ReflectableEnum(BASICVersion, v10, v11, v21, Any, None);
|
||||
ReflectableEnum(DOS, EXDOS, None);
|
||||
ReflectableEnum(Speed, FourMHz, SixMHz);
|
||||
|
||||
Model model = Model::Enterprise128;
|
||||
EXOSVersion exos_version = EXOSVersion::Any;
|
||||
BASICVersion basic_version = BASICVersion::None;
|
||||
DOS dos = DOS::None;
|
||||
Speed speed = Speed::FourMHz;
|
||||
std::string loading_command;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::Enterprise) {
|
||||
if(needs_declare()) {
|
||||
AnnounceEnum(Model);
|
||||
AnnounceEnum(EXOSVersion);
|
||||
AnnounceEnum(BASICVersion);
|
||||
AnnounceEnum(DOS);
|
||||
AnnounceEnum(Speed);
|
||||
|
||||
DeclareField(model);
|
||||
DeclareField(exos_version);
|
||||
DeclareField(basic_version);
|
||||
DeclareField(dos);
|
||||
DeclareField(speed);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Analyser_Static_Enterprise_Target_h */
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "Coleco/StaticAnalyser.hpp"
|
||||
#include "Commodore/StaticAnalyser.hpp"
|
||||
#include "DiskII/StaticAnalyser.hpp"
|
||||
#include "Enterprise/StaticAnalyser.hpp"
|
||||
#include "Macintosh/StaticAnalyser.hpp"
|
||||
#include "MSX/StaticAnalyser.hpp"
|
||||
#include "Oric/StaticAnalyser.hpp"
|
||||
@@ -43,9 +44,9 @@
|
||||
#include "../../Storage/Disk/DiskImage/Formats/MacintoshIMG.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/G64.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/DMK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/FAT12.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/HFE.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/MSA.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/MSXDSK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/NIB.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/SSD.hpp"
|
||||
@@ -147,7 +148,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::MacintoshIMG>, TargetPlatform::Macintosh) // DSK (Macintosh, floppy disk)
|
||||
Format("dsk", result.mass_storage_devices, MassStorage::HFV, TargetPlatform::Macintosh) // DSK (Macintosh, hard disk, single volume image)
|
||||
Format("dsk", result.mass_storage_devices, MassStorage::DSK, TargetPlatform::Macintosh) // DSK (Macintosh, hard disk, full device image)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::MSXDSK>, TargetPlatform::MSX) // DSK (MSX)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::FAT12>, TargetPlatform::MSX) // DSK (MSX)
|
||||
Format("dsk", result.disks, Disk::DiskImageHolder<Storage::Disk::OricMFMDSK>, TargetPlatform::Oric) // DSK (Oric)
|
||||
Format("g64", result.disks, Disk::DiskImageHolder<Storage::Disk::G64>, TargetPlatform::Commodore) // G64
|
||||
Format( "hfe",
|
||||
@@ -157,6 +158,7 @@ static Media GetMediaAndPlatforms(const std::string &file_name, TargetPlatform::
|
||||
// HFE (TODO: switch to AllDisk once the MSX stops being so greedy)
|
||||
Format("img", result.disks, Disk::DiskImageHolder<Storage::Disk::MacintoshIMG>, TargetPlatform::Macintosh) // IMG (DiskCopy 4.2)
|
||||
Format("image", result.disks, Disk::DiskImageHolder<Storage::Disk::MacintoshIMG>, TargetPlatform::Macintosh) // IMG (DiskCopy 4.2)
|
||||
Format("img", result.disks, Disk::DiskImageHolder<Storage::Disk::FAT12>, TargetPlatform::Enterprise) // IMG (Enterprise/MS-DOS style)
|
||||
Format("msa", result.disks, Disk::DiskImageHolder<Storage::Disk::MSA>, TargetPlatform::AtariST) // MSA
|
||||
Format("nib", result.disks, Disk::DiskImageHolder<Storage::Disk::NIB>, TargetPlatform::DiskII) // NIB
|
||||
Format("o", result.tapes, Tape::ZX80O81P, TargetPlatform::ZX8081) // O
|
||||
@@ -256,6 +258,7 @@ TargetList Analyser::Static::GetTargets(const std::string &file_name) {
|
||||
Append(Coleco);
|
||||
Append(Commodore);
|
||||
Append(DiskII);
|
||||
Append(Enterprise);
|
||||
Append(Macintosh);
|
||||
Append(MSX);
|
||||
Append(Oric);
|
||||
|
||||
@@ -213,7 +213,7 @@ class HalfCycles: public WrappedInt<HalfCycles> {
|
||||
|
||||
/*!
|
||||
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
||||
the value of @c this modulo @c divisor and @c divided by @c divisor is returned.
|
||||
the value of @c this modulo @c divisor . @c this divided by @c divisor is returned.
|
||||
*/
|
||||
forceinline Cycles divide_cycles(const Cycles &divisor) {
|
||||
const HalfCycles half_divisor = HalfCycles(divisor);
|
||||
|
||||
@@ -103,9 +103,9 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
|
||||
}
|
||||
|
||||
if constexpr (has_sequence_points<T>::value) {
|
||||
time_until_event_ -= rhs;
|
||||
time_until_event_ -= rhs * multiplier;
|
||||
if(time_until_event_ <= LocalTimeScale(0)) {
|
||||
time_overrun_ = time_until_event_;
|
||||
time_overrun_ = time_until_event_ / divider;
|
||||
flush();
|
||||
update_sequence_point();
|
||||
return true;
|
||||
@@ -145,13 +145,21 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
|
||||
|
||||
/// @returns the amount of time since the object was last flushed, in the target time scale.
|
||||
[[nodiscard]] forceinline TargetTimeScale time_since_flush() const {
|
||||
// TODO: does this handle conversions properly where TargetTimeScale != LocalTimeScale?
|
||||
if constexpr (divider == 1) {
|
||||
return time_since_update_;
|
||||
}
|
||||
return TargetTimeScale(time_since_update_.as_integral() / divider);
|
||||
}
|
||||
|
||||
/// @returns the amount of time since the object was last flushed, plus the local time scale @c offset,
|
||||
/// converted to the target time scale.
|
||||
[[nodiscard]] forceinline TargetTimeScale time_since_flush(LocalTimeScale offset) const {
|
||||
if constexpr (divider == 1) {
|
||||
return time_since_update_ + offset;
|
||||
}
|
||||
return TargetTimeScale((time_since_update_ + offset).as_integral() / divider);
|
||||
}
|
||||
|
||||
/// Flushes all accumulated time.
|
||||
///
|
||||
/// This does not affect this actor's record of when the next sequence point will occur.
|
||||
@@ -185,7 +193,7 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
|
||||
/// @returns the number of cycles until the next sequence-point-based flush, if the embedded object
|
||||
/// supports sequence points; @c LocalTimeScale() otherwise.
|
||||
[[nodiscard]] LocalTimeScale cycles_until_implicit_flush() const {
|
||||
return time_until_event_;
|
||||
return time_until_event_ / divider;
|
||||
}
|
||||
|
||||
/// Indicates whether a sequence-point-caused flush will occur if the specified period is added.
|
||||
@@ -196,10 +204,43 @@ template <class T, class LocalTimeScale = HalfCycles, int multiplier = 1, int di
|
||||
return rhs >= time_until_event_;
|
||||
}
|
||||
|
||||
/// Indicates the amount of time, in the local time scale, until the first local slot that falls wholly
|
||||
/// after @c duration, if that delay were to occur in @c offset units of time from now.
|
||||
[[nodiscard]] forceinline LocalTimeScale back_map(TargetTimeScale duration, TargetTimeScale offset) const {
|
||||
// A 1:1 mapping is easy.
|
||||
if constexpr (multiplier == 1 && divider == 1) {
|
||||
return duration;
|
||||
}
|
||||
|
||||
// Work out when this query is placed, and the time to which it relates
|
||||
const auto base = time_since_update_ + offset * divider;
|
||||
const auto target = base + duration * divider;
|
||||
|
||||
// Figure out the number of whole input steps that is required to get
|
||||
// past target, and subtract the number of whole input steps necessary
|
||||
// to get to base.
|
||||
const auto steps_to_base = base.as_integral() / multiplier;
|
||||
const auto steps_to_target = (target.as_integral() + divider - 1) / multiplier;
|
||||
|
||||
return LocalTimeScale(steps_to_target - steps_to_base);
|
||||
}
|
||||
|
||||
/// Updates this template's record of the next sequence point.
|
||||
void update_sequence_point() {
|
||||
if constexpr (has_sequence_points<T>::value) {
|
||||
// Keep a fast path where no conversions will be applied; if conversions are
|
||||
// going to be applied then do a direct max -> max translation rather than
|
||||
// allowing the arithmetic to overflow.
|
||||
if constexpr (divider == 1 && std::is_same_v<LocalTimeScale, TargetTimeScale>) {
|
||||
time_until_event_ = object_.get_next_sequence_point();
|
||||
} else {
|
||||
const auto time = object_.get_next_sequence_point();
|
||||
if(time == TargetTimeScale::max()) {
|
||||
time_until_event_ = LocalTimeScale::max();
|
||||
} else {
|
||||
time_until_event_ = time * divider;
|
||||
}
|
||||
}
|
||||
assert(time_until_event_ > LocalTimeScale(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +276,10 @@ void WD1770::posit_event(int new_event_type) {
|
||||
goto test_type1_type;
|
||||
|
||||
begin_type1_spin_up:
|
||||
if((command_&0x08) || get_drive().get_motor_on()) goto test_type1_type;
|
||||
if((command_&0x08) || get_drive().get_motor_on()) {
|
||||
set_motor_on(true);
|
||||
goto test_type1_type;
|
||||
}
|
||||
SPIN_UP();
|
||||
|
||||
test_type1_type:
|
||||
@@ -387,7 +390,10 @@ void WD1770::posit_event(int new_event_type) {
|
||||
distance_into_section_ = 0;
|
||||
|
||||
if((command_&0x08) && has_motor_on_line()) goto test_type2_delay;
|
||||
if(!has_motor_on_line() && !has_head_load_line()) goto test_type2_delay;
|
||||
if(!has_motor_on_line() && !has_head_load_line()) {
|
||||
if(has_motor_on_line()) set_motor_on(true);
|
||||
goto test_type2_delay;
|
||||
}
|
||||
|
||||
if(has_motor_on_line()) goto begin_type2_spin_up;
|
||||
goto begin_type2_load_head;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#ifndef Joystick_hpp
|
||||
#define Joystick_hpp
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace Inputs {
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "Keyboard.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
using namespace Inputs;
|
||||
|
||||
Keyboard::Keyboard(const std::set<Key> &essential_modifiers) : essential_modifiers_(essential_modifiers) {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#ifndef CachingExecutor_hpp
|
||||
#define CachingExecutor_hpp
|
||||
|
||||
#include "Sizes.hpp"
|
||||
#include "../Numeric/Sizes.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#ifndef Disassembler_hpp
|
||||
#define Disassembler_hpp
|
||||
|
||||
#include "Sizes.hpp"
|
||||
#include "../Numeric/Sizes.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
@@ -271,7 +271,7 @@ class Keyboard {
|
||||
/// so this may not be valid prior to Mode::PerformingCommand.
|
||||
int command_ = 0;
|
||||
/// Populated during PerformingCommand as the response to the most-recently-received command, this
|
||||
/// is then shifted out to teh host computer. So it is guaranteed valid at the beginning of Mode::SendingResponse,
|
||||
/// is then shifted out to the host computer. So it is guaranteed valid at the beginning of Mode::SendingResponse,
|
||||
/// but not afterwards.
|
||||
int response_ = 0;
|
||||
|
||||
|
||||
337
Machines/Enterprise/Dave.cpp
Normal file
337
Machines/Enterprise/Dave.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
//
|
||||
// Dave.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 22/06/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Dave.hpp"
|
||||
|
||||
using namespace Enterprise::Dave;
|
||||
|
||||
// MARK: - Audio generator
|
||||
|
||||
Audio::Audio(Concurrency::DeferringAsyncTaskQueue &audio_queue) :
|
||||
audio_queue_(audio_queue) {}
|
||||
|
||||
void Audio::write(uint16_t address, uint8_t value) {
|
||||
address &= 0x1f;
|
||||
audio_queue_.defer([address, value, this] {
|
||||
switch(address) {
|
||||
case 0: case 2: case 4:
|
||||
channels_[address >> 1].reload = (channels_[address >> 1].reload & 0xff00) | value;
|
||||
break;
|
||||
case 1: case 3: case 5:
|
||||
channels_[address >> 1].reload = uint16_t((channels_[address >> 1].reload & 0x00ff) | ((value & 0xf) << 8));
|
||||
channels_[address >> 1].distortion = Channel::Distortion((value >> 4)&3);
|
||||
channels_[address >> 1].high_pass = value & 0x40;
|
||||
channels_[address >> 1].ring_modulate = value & 0x80;
|
||||
break;
|
||||
case 6:
|
||||
noise_.frequency = Noise::Frequency(value&3);
|
||||
noise_.polynomial = Noise::Polynomial((value >> 2)&3);
|
||||
noise_.swap_polynomial = value & 0x10;
|
||||
noise_.low_pass = value & 0x20;
|
||||
noise_.high_pass = value & 0x40;
|
||||
noise_.ring_modulate = value & 0x80;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
channels_[0].sync = value & 0x01;
|
||||
channels_[1].sync = value & 0x02;
|
||||
channels_[2].sync = value & 0x04;
|
||||
use_direct_output_[0] = value & 0x08;
|
||||
use_direct_output_[1] = value & 0x10;
|
||||
// Interrupt bits are handled separately.
|
||||
break;
|
||||
|
||||
case 8: case 9: case 10:
|
||||
channels_[address - 8].amplitude[0] = value & 0x3f;
|
||||
break;
|
||||
case 12: case 13: case 14:
|
||||
channels_[address - 12].amplitude[1] = value & 0x3f;
|
||||
break;
|
||||
case 11: noise_.amplitude[0] = value & 0x3f; break;
|
||||
case 15: noise_.amplitude[1] = value & 0x3f; break;
|
||||
|
||||
case 31:
|
||||
global_divider_reload_ = 2 + ((value >> 1)&1);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::set_sample_volume_range(int16_t range) {
|
||||
audio_queue_.defer([range, this] {
|
||||
volume_ = range / (63*4);
|
||||
});
|
||||
}
|
||||
|
||||
void Audio::update_channel(int c) {
|
||||
auto output = channels_[c].output & 1;
|
||||
channels_[c].output <<= 1;
|
||||
if(channels_[c].sync) {
|
||||
channels_[c].count = channels_[c].reload;
|
||||
output = 0;
|
||||
} else {
|
||||
if(!channels_[c].count) {
|
||||
channels_[c].count = channels_[c].reload;
|
||||
|
||||
if(channels_[c].distortion == Channel::Distortion::None)
|
||||
output ^= 1;
|
||||
else
|
||||
output = poly_state_[int(channels_[c].distortion)];
|
||||
} else {
|
||||
--channels_[c].count;
|
||||
}
|
||||
|
||||
if(channels_[c].high_pass && (channels_[(c+1)%3].output&3) == 2) {
|
||||
output = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Ring modulation applies even when sync is enabled, per SIDBasic.
|
||||
if(channels_[c].ring_modulate) {
|
||||
output = ~(output ^ channels_[(c+2)%3].output) & 1;
|
||||
}
|
||||
|
||||
channels_[c].output |= output;
|
||||
}
|
||||
|
||||
void Audio::get_samples(std::size_t number_of_samples, int16_t *target) {
|
||||
int16_t output_level[2];
|
||||
size_t c = 0;
|
||||
while(c < number_of_samples) {
|
||||
// I'm unclear on the details of the time division multiplexing so,
|
||||
// for now, just sum the outputs.
|
||||
output_level[0] =
|
||||
volume_ *
|
||||
(use_direct_output_[0] ?
|
||||
channels_[0].amplitude[0]
|
||||
: (
|
||||
channels_[0].amplitude[0] * (channels_[0].output & 1) +
|
||||
channels_[1].amplitude[0] * (channels_[1].output & 1) +
|
||||
channels_[2].amplitude[0] * (channels_[2].output & 1) +
|
||||
noise_.amplitude[0] * noise_.final_output
|
||||
));
|
||||
|
||||
output_level[1] =
|
||||
volume_ *
|
||||
(use_direct_output_[1] ?
|
||||
channels_[0].amplitude[1]
|
||||
: (
|
||||
channels_[0].amplitude[1] * (channels_[0].output & 1) +
|
||||
channels_[1].amplitude[1] * (channels_[1].output & 1) +
|
||||
channels_[2].amplitude[1] * (channels_[2].output & 1) +
|
||||
noise_.amplitude[1] * noise_.final_output
|
||||
));
|
||||
|
||||
while(global_divider_ && c < number_of_samples) {
|
||||
--global_divider_;
|
||||
*reinterpret_cast<uint32_t *>(&target[c << 1]) = *reinterpret_cast<uint32_t *>(output_level);
|
||||
++c;
|
||||
}
|
||||
|
||||
global_divider_ = global_divider_reload_;
|
||||
if(!global_divider_) {
|
||||
global_divider_ = global_divider_reload_;
|
||||
}
|
||||
poly_state_[int(Channel::Distortion::FourBit)] = poly4_.next();
|
||||
poly_state_[int(Channel::Distortion::FiveBit)] = poly5_.next();
|
||||
poly_state_[int(Channel::Distortion::SevenBit)] = poly7_.next();
|
||||
if(noise_.swap_polynomial) {
|
||||
poly_state_[int(Channel::Distortion::SevenBit)] = poly_state_[int(Channel::Distortion::None)];
|
||||
}
|
||||
|
||||
// Update tone channels.
|
||||
update_channel(0);
|
||||
update_channel(1);
|
||||
update_channel(2);
|
||||
|
||||
// Update noise channel.
|
||||
|
||||
// Step 1: decide whether there is a tick to apply.
|
||||
bool noise_tick = false;
|
||||
if(noise_.frequency == Noise::Frequency::DivideByFour) {
|
||||
if(!noise_.count) {
|
||||
noise_tick = true;
|
||||
noise_.count = 3;
|
||||
} else {
|
||||
--noise_.count;
|
||||
}
|
||||
} else {
|
||||
noise_tick = (channels_[int(noise_.frequency) - 1].output&3) == 2;
|
||||
}
|
||||
|
||||
// Step 2: tick if necessary.
|
||||
int noise_output = noise_.output & 1;
|
||||
noise_.output <<= 1;
|
||||
if(noise_tick) {
|
||||
switch(noise_.polynomial) {
|
||||
case Noise::Polynomial::SeventeenBit:
|
||||
poly_state_[int(Channel::Distortion::None)] = uint8_t(poly17_.next());
|
||||
break;
|
||||
case Noise::Polynomial::FifteenBit:
|
||||
poly_state_[int(Channel::Distortion::None)] = uint8_t(poly15_.next());
|
||||
break;
|
||||
case Noise::Polynomial::ElevenBit:
|
||||
poly_state_[int(Channel::Distortion::None)] = uint8_t(poly11_.next());
|
||||
break;
|
||||
case Noise::Polynomial::NineBit:
|
||||
poly_state_[int(Channel::Distortion::None)] = uint8_t(poly9_.next());
|
||||
break;
|
||||
}
|
||||
|
||||
noise_output = poly_state_[int(Channel::Distortion::None)];
|
||||
}
|
||||
noise_.output |= noise_output;
|
||||
|
||||
// Low pass: sample channel 2 on downward transitions of the prima facie output.
|
||||
if(noise_.low_pass && (noise_.output & 3) == 2) {
|
||||
noise_.output = (noise_.output & ~1) | (channels_[2].output & 1);
|
||||
}
|
||||
|
||||
// Apply noise high-pass.
|
||||
if(noise_.high_pass && (channels_[0].output & 3) == 2) {
|
||||
noise_.output &= ~1;
|
||||
}
|
||||
|
||||
// Update noise ring modulation, if any.
|
||||
if(noise_.ring_modulate) {
|
||||
noise_.final_output = !((noise_.output ^ channels_[1].output) & 1);
|
||||
} else {
|
||||
noise_.final_output = noise_.output & 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Interrupt source
|
||||
|
||||
uint8_t TimedInterruptSource::get_new_interrupts() {
|
||||
const uint8_t result = interrupts_;
|
||||
interrupts_ = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void TimedInterruptSource::write(uint16_t address, uint8_t value) {
|
||||
address &= 0x1f;
|
||||
switch(address) {
|
||||
default: break;
|
||||
|
||||
case 0: case 2:
|
||||
channels_[address >> 1].reload = (channels_[address >> 1].reload & 0xff00) | value;
|
||||
break;
|
||||
case 1: case 3:
|
||||
channels_[address >> 1].reload = uint16_t((channels_[address >> 1].reload & 0x00ff) | ((value & 0xf) << 8));
|
||||
break;
|
||||
|
||||
case 7:
|
||||
channels_[0].sync = value & 0x01;
|
||||
channels_[1].sync = value & 0x02;
|
||||
rate_ = InterruptRate((value >> 5) & 3);
|
||||
break;
|
||||
|
||||
case 31:
|
||||
global_divider_ = Cycles(2 + ((value >> 1)&1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TimedInterruptSource::update_channel(int c, bool is_linked, int decrement) {
|
||||
if(channels_[c].sync) {
|
||||
channels_[c].value = channels_[c].reload;
|
||||
} else {
|
||||
if(decrement <= channels_[c].value) {
|
||||
channels_[c].value -= decrement;
|
||||
} else {
|
||||
// The decrement is greater than the current value, therefore
|
||||
// there'll be at least one flip.
|
||||
//
|
||||
// After decreasing the decrement by the current value + 1,
|
||||
// it'll be clear how many decrements are left after reload.
|
||||
//
|
||||
// Dividing that by the number of decrements necessary for a
|
||||
// flip will provide the total number of flips.
|
||||
const int decrements_after_flip = decrement - (channels_[c].value + 1);
|
||||
const int num_flips = 1 + decrements_after_flip / (channels_[c].reload + 1);
|
||||
|
||||
// If this is a linked channel, set the interrupt mask if a transition
|
||||
// from high to low is amongst the included flips.
|
||||
if(is_linked && num_flips + channels_[c].level >= 2) {
|
||||
interrupts_ |= uint8_t(Interrupt::VariableFrequency);
|
||||
programmable_level_ ^= true;
|
||||
}
|
||||
channels_[c].level ^= (num_flips & 1);
|
||||
|
||||
// Apply the modulo number of decrements to the reload value to
|
||||
// figure out where things stand now.
|
||||
channels_[c].value = channels_[c].reload - decrements_after_flip % (channels_[c].reload + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimedInterruptSource::run_for(Cycles duration) {
|
||||
// Determine total number of ticks.
|
||||
run_length_ += duration;
|
||||
const Cycles cycles = run_length_.divide(global_divider_);
|
||||
if(cycles == Cycles(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the two-second counter, from which the 1Hz, 50Hz and 1000Hz signals
|
||||
// are derived.
|
||||
const int previous_counter = two_second_counter_;
|
||||
two_second_counter_ = (two_second_counter_ + cycles.as<int>()) % 500'000;
|
||||
|
||||
// Check for a 1Hz rollover.
|
||||
if(previous_counter / 250'000 != two_second_counter_ / 250'000) {
|
||||
interrupts_ |= uint8_t(Interrupt::OneHz);
|
||||
}
|
||||
|
||||
// Check for 1kHz or 50Hz rollover;
|
||||
switch(rate_) {
|
||||
default: break;
|
||||
case InterruptRate::OnekHz:
|
||||
if(previous_counter / 250 != two_second_counter_ / 250) {
|
||||
interrupts_ |= uint8_t(Interrupt::VariableFrequency);
|
||||
programmable_level_ ^= true;
|
||||
}
|
||||
break;
|
||||
case InterruptRate::FiftyHz:
|
||||
if(previous_counter / 5'000 != two_second_counter_ / 5'000) {
|
||||
interrupts_ |= uint8_t(Interrupt::VariableFrequency);
|
||||
programmable_level_ ^= true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the two tone channels.
|
||||
update_channel(0, rate_ == InterruptRate::ToneGenerator0, cycles.as<int>());
|
||||
update_channel(1, rate_ == InterruptRate::ToneGenerator1, cycles.as<int>());
|
||||
}
|
||||
|
||||
Cycles TimedInterruptSource::get_next_sequence_point() const {
|
||||
// Since both the 1kHz and 50Hz timers are integer dividers of the 1Hz timer, there's no need
|
||||
// to factor that one in when determining the next sequence point for either of those.
|
||||
switch(rate_) {
|
||||
default:
|
||||
case InterruptRate::OnekHz: return Cycles(250 - (two_second_counter_ % 250));
|
||||
case InterruptRate::FiftyHz: return Cycles(5000 - (two_second_counter_ % 5000));
|
||||
|
||||
case InterruptRate::ToneGenerator0:
|
||||
case InterruptRate::ToneGenerator1: {
|
||||
const auto &channel = channels_[int(rate_) - int(InterruptRate::ToneGenerator0)];
|
||||
const int cycles_until_interrupt = channel.value + 1 + (!channel.level) * (channel.reload + 1);
|
||||
|
||||
return Cycles(std::min(
|
||||
250'000 - (two_second_counter_ % 250'000),
|
||||
cycles_until_interrupt
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t TimedInterruptSource::get_divider_state() {
|
||||
return uint8_t((two_second_counter_ / 250'000) * 4 | programmable_level_);
|
||||
}
|
||||
188
Machines/Enterprise/Dave.hpp
Normal file
188
Machines/Enterprise/Dave.hpp
Normal file
@@ -0,0 +1,188 @@
|
||||
//
|
||||
// Dave.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 22/06/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Dave_hpp
|
||||
#define Dave_hpp
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../../Concurrency/AsyncTaskQueue.hpp"
|
||||
#include "../../Numeric/LFSR.hpp"
|
||||
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
|
||||
|
||||
namespace Enterprise {
|
||||
namespace Dave {
|
||||
|
||||
enum class Interrupt: uint8_t {
|
||||
VariableFrequency = 0x02,
|
||||
OneHz = 0x08,
|
||||
Nick = 0x20,
|
||||
};
|
||||
|
||||
/*!
|
||||
Models the audio-production subset of Dave's behaviour.
|
||||
*/
|
||||
class Audio: public Outputs::Speaker::SampleSource {
|
||||
public:
|
||||
Audio(Concurrency::DeferringAsyncTaskQueue &audio_queue);
|
||||
|
||||
/// Modifies an register in the audio range; only the low 4 bits are
|
||||
/// used for register decoding so it's assumed that the caller has
|
||||
/// already identified this write as being to an audio register.
|
||||
void write(uint16_t address, uint8_t value);
|
||||
|
||||
// MARK: - SampleSource.
|
||||
void set_sample_volume_range(int16_t range);
|
||||
static constexpr bool get_is_stereo() { return true; } // Dave produces stereo sound.
|
||||
void get_samples(std::size_t number_of_samples, int16_t *target);
|
||||
|
||||
private:
|
||||
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
|
||||
|
||||
// Global divider (i.e. 8MHz/12Mhz switch).
|
||||
uint8_t global_divider_;
|
||||
uint8_t global_divider_reload_ = 2;
|
||||
|
||||
// Tone channels.
|
||||
struct Channel {
|
||||
// User-set values.
|
||||
uint16_t reload = 0;
|
||||
bool high_pass = false;
|
||||
bool ring_modulate = false;
|
||||
enum class Distortion {
|
||||
None = 0,
|
||||
FourBit = 1,
|
||||
FiveBit = 2,
|
||||
SevenBit = 3,
|
||||
} distortion = Distortion::None;
|
||||
uint8_t amplitude[2]{};
|
||||
bool sync = false;
|
||||
|
||||
// Current state.
|
||||
uint16_t count = 0;
|
||||
int output = 0;
|
||||
} channels_[3];
|
||||
void update_channel(int);
|
||||
|
||||
// Noise channel.
|
||||
struct Noise {
|
||||
// User-set values.
|
||||
uint8_t amplitude[2]{};
|
||||
enum class Frequency {
|
||||
DivideByFour,
|
||||
ToneChannel0,
|
||||
ToneChannel1,
|
||||
ToneChannel2,
|
||||
} frequency = Frequency::DivideByFour;
|
||||
enum class Polynomial {
|
||||
SeventeenBit,
|
||||
FifteenBit,
|
||||
ElevenBit,
|
||||
NineBit
|
||||
} polynomial = Polynomial::SeventeenBit;
|
||||
bool swap_polynomial = false;
|
||||
bool low_pass = false;
|
||||
bool high_pass = false;
|
||||
bool ring_modulate = false;
|
||||
|
||||
// Current state.
|
||||
int count = 0;
|
||||
int output = 0;
|
||||
bool final_output = false;
|
||||
} noise_;
|
||||
void update_noise();
|
||||
|
||||
bool use_direct_output_[2]{};
|
||||
|
||||
// Global volume, per SampleSource obligations.
|
||||
int16_t volume_ = 0;
|
||||
|
||||
// Polynomials that are always running.
|
||||
Numeric::LFSRv<0xc> poly4_;
|
||||
Numeric::LFSRv<0x14> poly5_;
|
||||
Numeric::LFSRv<0x60> poly7_;
|
||||
|
||||
// The selectable, noise-related polynomial.
|
||||
Numeric::LFSRv<0x110> poly9_;
|
||||
Numeric::LFSRv<0x500> poly11_;
|
||||
Numeric::LFSRv<0x6000> poly15_;
|
||||
Numeric::LFSRv<0x12000> poly17_;
|
||||
|
||||
// Current state of the active polynomials.
|
||||
uint8_t poly_state_[4];
|
||||
};
|
||||
|
||||
/*!
|
||||
Provides Dave's timed interrupts — those that are provided at 1 kHz,
|
||||
50 Hz or according to the rate of tone generators 0 or 1, plus the fixed
|
||||
1 Hz interrupt.
|
||||
*/
|
||||
class TimedInterruptSource {
|
||||
public:
|
||||
/// Modifies an register in the audio range; only the low 4 bits are
|
||||
/// used for register decoding so it's assumed that the caller has
|
||||
/// already identified this write as being to an audio register.
|
||||
void write(uint16_t address, uint8_t value);
|
||||
|
||||
/// Returns a bitmask of interrupts that have become active since
|
||||
/// the last time this method was called; flags are as defined in
|
||||
/// @c Enterprise::Dave::Interrupt
|
||||
uint8_t get_new_interrupts();
|
||||
|
||||
/// Returns the current high or low states of the inputs that trigger
|
||||
/// the interrupts modelled here, as a bit mask compatible with that
|
||||
/// exposed by Dave as the register at 0xb4.
|
||||
uint8_t get_divider_state();
|
||||
|
||||
/// Advances the interrupt source.
|
||||
void run_for(Cycles);
|
||||
|
||||
/// @returns The amount of time from now until the earliest that
|
||||
/// @c get_new_interrupts() _might_ have new interrupts to report.
|
||||
Cycles get_next_sequence_point() const;
|
||||
|
||||
private:
|
||||
static constexpr Cycles clock_rate{250000};
|
||||
static constexpr Cycles half_clock_rate{125000};
|
||||
|
||||
// Global divider (i.e. 8MHz/12Mhz switch).
|
||||
Cycles global_divider_ = Cycles(2);
|
||||
Cycles run_length_;
|
||||
|
||||
// Interrupts that have fired since get_new_interrupts()
|
||||
// was last called.
|
||||
uint8_t interrupts_ = 0;
|
||||
|
||||
// A counter for the 1Hz interrupt.
|
||||
int two_second_counter_ = 0;
|
||||
|
||||
// A counter specific to the 1kHz and 50Hz timers, if in use.
|
||||
enum class InterruptRate {
|
||||
OnekHz,
|
||||
FiftyHz,
|
||||
ToneGenerator0,
|
||||
ToneGenerator1,
|
||||
} rate_ = InterruptRate::OnekHz;
|
||||
bool programmable_level_ = false;
|
||||
|
||||
// A local duplicate of the counting state of the first two audio
|
||||
// channels, maintained in case either of those is used as an
|
||||
// interrupt source.
|
||||
struct Channel {
|
||||
int value = 100, reload = 100;
|
||||
bool sync = false;
|
||||
bool level = false;
|
||||
} channels_[2];
|
||||
void update_channel(int c, bool is_linked, int decrement);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Dave_hpp */
|
||||
78
Machines/Enterprise/EXDos.cpp
Normal file
78
Machines/Enterprise/EXDos.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// EXDos.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 20/06/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "EXDos.hpp"
|
||||
|
||||
// TODO: disk_did_change_ should be on the drive. Some drives report it.
|
||||
|
||||
using namespace Enterprise;
|
||||
|
||||
EXDos::EXDos() : WD1770(P1770) {
|
||||
emplace_drives(4, 8000000, 300, 2);
|
||||
set_control_register(0x00);
|
||||
}
|
||||
|
||||
void EXDos::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive) {
|
||||
get_drive(drive).set_disk(disk);
|
||||
disk_did_change_ = true;
|
||||
}
|
||||
|
||||
// Documentation for the control register:
|
||||
//
|
||||
// Write:
|
||||
// b7 in use (???)
|
||||
// b6 disk change reset
|
||||
// b5 0 = double density, 1 = single density
|
||||
// b4 side 1 select
|
||||
// b3, b3, b1, b0 select drive 3, 2, 1, 0
|
||||
//
|
||||
// Read:
|
||||
// b7 data request from WD1770
|
||||
// b6 disk change
|
||||
// b5, b4, b3, b2: not used
|
||||
// b1 interrupt request from WD1770
|
||||
// b0 drive ready
|
||||
|
||||
void EXDos::set_control_register(uint8_t control) {
|
||||
if(control & 0x40) disk_did_change_ = false;
|
||||
set_is_double_density(!(control & 0x20));
|
||||
|
||||
// Set side.
|
||||
const int head = (control >> 4) & 1;
|
||||
for(size_t c = 0; c < 4; c++) {
|
||||
get_drive(c).set_head(head);
|
||||
}
|
||||
|
||||
// Select drive, ensuring handover of the motor-on state.
|
||||
const bool motor_state = get_drive().get_motor_on();
|
||||
for_all_drives([] (Storage::Disk::Drive &drive, size_t) {
|
||||
drive.set_motor_on(false);
|
||||
});
|
||||
set_drive(control & 0xf);
|
||||
get_drive().set_motor_on(motor_state);
|
||||
}
|
||||
|
||||
uint8_t EXDos::get_control_register() {
|
||||
const uint8_t status =
|
||||
(get_data_request_line() ? 0x80 : 0x00) |
|
||||
(disk_did_change_ ? 0x40 : 0x00) |
|
||||
(get_interrupt_request_line() ? 0x02 : 0x00) |
|
||||
(get_drive().get_is_ready() ? 0x01 : 0x00);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void EXDos::set_motor_on(bool on) {
|
||||
get_drive().set_motor_on(on);
|
||||
}
|
||||
|
||||
void EXDos::set_activity_observer(Activity::Observer *observer) {
|
||||
for_all_drives([observer] (Storage::Disk::Drive &drive, size_t index) {
|
||||
drive.set_activity_observer(observer, "Drive " + std::to_string(index+1), true);
|
||||
});
|
||||
}
|
||||
36
Machines/Enterprise/EXDos.hpp
Normal file
36
Machines/Enterprise/EXDos.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// EXDos.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 20/06/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef EXDos_hpp
|
||||
#define EXDos_hpp
|
||||
|
||||
#include "../../Components/1770/1770.hpp"
|
||||
#include "../../Activity/Observer.hpp"
|
||||
|
||||
namespace Enterprise {
|
||||
|
||||
class EXDos final : public WD::WD1770 {
|
||||
public:
|
||||
EXDos();
|
||||
|
||||
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive);
|
||||
|
||||
void set_control_register(uint8_t control);
|
||||
uint8_t get_control_register();
|
||||
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
private:
|
||||
bool disk_did_change_ = false;
|
||||
|
||||
void set_motor_on(bool on) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* EXDos_hpp */
|
||||
765
Machines/Enterprise/Enterprise.cpp
Normal file
765
Machines/Enterprise/Enterprise.cpp
Normal file
@@ -0,0 +1,765 @@
|
||||
//
|
||||
// Enterprise.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/06/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Enterprise.hpp"
|
||||
|
||||
#include "Dave.hpp"
|
||||
#include "EXDos.hpp"
|
||||
#include "Keyboard.hpp"
|
||||
#include "Nick.hpp"
|
||||
|
||||
#include "../MachineTypes.hpp"
|
||||
#include "../Utility/Typer.hpp"
|
||||
|
||||
#include "../../Analyser/Static/Enterprise/Target.hpp"
|
||||
#include "../../ClockReceiver/JustInTime.hpp"
|
||||
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
|
||||
#include "../../Processors/Z80/Z80.hpp"
|
||||
|
||||
#define LOG_PREFIX "[Enterprise] "
|
||||
#include "../../Outputs/Log.hpp"
|
||||
|
||||
namespace Enterprise {
|
||||
|
||||
/*
|
||||
Notes to self on timing:
|
||||
|
||||
Nick divides each line into 57 windows; each window lasts 16 cycles and dedicates the
|
||||
first 10 of those to VRAM accesses, leaving the final six for a Z80 video RAM access
|
||||
if one has been requested.
|
||||
|
||||
The Z80 has a separate, asynchronous 4Mhz clock. That's that.
|
||||
|
||||
The documentation is also very forward in emphasising that Nick generates phaselocked
|
||||
(i.e. in-phase) PAL video.
|
||||
|
||||
So: 57*16 = 912 cycles/line.
|
||||
|
||||
A standard PAL line lasts 64µs and during that time outputs 283.7516 colour cycles.
|
||||
|
||||
I shall _guess_ that the Enterprise stretches each line to 284 colour cycles rather than
|
||||
reducing it to 283.
|
||||
|
||||
Therefore 912 cycles occurs in 284/283.7516 * 64 µs.
|
||||
|
||||
So one line = 181760000 / 2837516 µs = 45440000 / 709379 µs
|
||||
=> one cycle = 45440000 / 709379*912 = 45440000 / 646953648 = 2840000 / 40434603 µs
|
||||
=> clock rate of 40434603 / 2840000 Mhz
|
||||
|
||||
And, therefore, the ratio to a 4Mhz Z80 clock is:
|
||||
|
||||
40434603 / (2840000 * 4)
|
||||
= 40434603 / 11360000
|
||||
i.e. roughly 3.55 Nick cycles per Z80 cycle.
|
||||
|
||||
If that's true then the 6-cycle window is around 1.69 Z80 cycles long. Given that the Z80
|
||||
clock in an Enterprise can be stopped in half-cycle increments only, the Z80 can only be
|
||||
guaranteed to have around a 1.19 cycle minimum for its actual access. I'm therefore further
|
||||
postulating that the clock stoppage takes place so as to align the final cycle of a relevant
|
||||
access over the available window.
|
||||
|
||||
*/
|
||||
|
||||
template <bool has_disk_controller, bool is_6mhz> class ConcreteMachine:
|
||||
public Activity::Source,
|
||||
public Configurable::Device,
|
||||
public CPU::Z80::BusHandler,
|
||||
public Machine,
|
||||
public MachineTypes::AudioProducer,
|
||||
public MachineTypes::MappedKeyboardMachine,
|
||||
public MachineTypes::MediaTarget,
|
||||
public MachineTypes::ScanProducer,
|
||||
public MachineTypes::TimedMachine,
|
||||
public Utility::TypeRecipient<CharacterMapper> {
|
||||
private:
|
||||
constexpr uint8_t min_ram_slot(const Analyser::Static::Enterprise::Target &target) {
|
||||
size_t ram_size = 128*1024;
|
||||
switch(target.model) {
|
||||
case Analyser::Static::Enterprise::Target::Model::Enterprise64: ram_size = 64*1024; break;
|
||||
case Analyser::Static::Enterprise::Target::Model::Enterprise128: ram_size = 128*1024; break;
|
||||
case Analyser::Static::Enterprise::Target::Model::Enterprise256: ram_size = 256*1024; break;
|
||||
}
|
||||
|
||||
return uint8_t(0x100 - ram_size / 0x4000);
|
||||
}
|
||||
|
||||
static constexpr double clock_rate = is_6mhz ? 6'000'000.0 : 4'000'000.0;
|
||||
using NickType =
|
||||
std::conditional_t<is_6mhz,
|
||||
JustInTimeActor<Nick, HalfCycles, 13478201, 5680000>,
|
||||
JustInTimeActor<Nick, HalfCycles, 40434603, 11360000>>;
|
||||
|
||||
public:
|
||||
ConcreteMachine(const Analyser::Static::Enterprise::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||
min_ram_slot_(min_ram_slot(target)),
|
||||
z80_(*this),
|
||||
nick_(ram_.end() - 65536),
|
||||
dave_audio_(audio_queue_),
|
||||
speaker_(dave_audio_) {
|
||||
|
||||
// Request a clock of 4Mhz; this'll be mapped upwards for Nick and downwards for Dave elsewhere.
|
||||
set_clock_rate(clock_rate);
|
||||
speaker_.set_input_rate(float(clock_rate) / float(dave_divider));
|
||||
|
||||
ROM::Request request;
|
||||
using Target = Analyser::Static::Enterprise::Target;
|
||||
|
||||
// Pick one or more EXOS ROMs.
|
||||
switch(target.exos_version) {
|
||||
case Target::EXOSVersion::v10: request = request && ROM::Request(ROM::Name::EnterpriseEXOS10); break;
|
||||
case Target::EXOSVersion::v20: request = request && ROM::Request(ROM::Name::EnterpriseEXOS20); break;
|
||||
case Target::EXOSVersion::v21: request = request && ROM::Request(ROM::Name::EnterpriseEXOS21); break;
|
||||
case Target::EXOSVersion::v23: request = request && ROM::Request(ROM::Name::EnterpriseEXOS23); break;
|
||||
case Target::EXOSVersion::Any:
|
||||
request =
|
||||
request && (
|
||||
ROM::Request(ROM::Name::EnterpriseEXOS10) || ROM::Request(ROM::Name::EnterpriseEXOS20) ||
|
||||
ROM::Request(ROM::Name::EnterpriseEXOS21) || ROM::Request(ROM::Name::EnterpriseEXOS23)
|
||||
);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Similarly pick one or more BASIC ROMs.
|
||||
switch(target.basic_version) {
|
||||
case Target::BASICVersion::v10:
|
||||
request = request && (
|
||||
ROM::Request(ROM::Name::EnterpriseBASIC10) ||
|
||||
(ROM::Request(ROM::Name::EnterpriseBASIC10Part1) && ROM::Request(ROM::Name::EnterpriseBASIC10Part2))
|
||||
);
|
||||
break;
|
||||
case Target::BASICVersion::v11:
|
||||
request = request && (
|
||||
ROM::Request(ROM::Name::EnterpriseBASIC11) ||
|
||||
ROM::Request(ROM::Name::EnterpriseBASIC11Suffixed)
|
||||
);
|
||||
case Target::BASICVersion::v21:
|
||||
request = request && ROM::Request(ROM::Name::EnterpriseBASIC21);
|
||||
break;
|
||||
case Target::BASICVersion::Any:
|
||||
request =
|
||||
request && (
|
||||
ROM::Request(ROM::Name::EnterpriseBASIC10) ||
|
||||
(ROM::Request(ROM::Name::EnterpriseBASIC10Part1) && ROM::Request(ROM::Name::EnterpriseBASIC10Part2)) ||
|
||||
ROM::Request(ROM::Name::EnterpriseBASIC11) ||
|
||||
ROM::Request(ROM::Name::EnterpriseBASIC21)
|
||||
);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Possibly add in a DOS.
|
||||
switch(target.dos) {
|
||||
case Target::DOS::EXDOS: request = request && ROM::Request(ROM::Name::EnterpriseEXDOS); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Get and validate ROMs.
|
||||
auto roms = rom_fetcher(request);
|
||||
if(!request.validate(roms)) {
|
||||
throw ROMMachine::Error::MissingROMs;
|
||||
}
|
||||
|
||||
// Extract the appropriate EXOS ROM.
|
||||
exos_.fill(0xff);
|
||||
for(const auto rom_name: { ROM::Name::EnterpriseEXOS10, ROM::Name::EnterpriseEXOS20, ROM::Name::EnterpriseEXOS21, ROM::Name::EnterpriseEXOS23 }) {
|
||||
const auto exos = roms.find(rom_name);
|
||||
if(exos != roms.end()) {
|
||||
memcpy(exos_.data(), exos->second.data(), std::min(exos_.size(), exos->second.size()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the appropriate BASIC ROM[s] (if any).
|
||||
basic_.fill(0xff);
|
||||
bool has_basic = false;
|
||||
for(const auto rom_name: { ROM::Name::EnterpriseBASIC10, ROM::Name::EnterpriseBASIC11, ROM::Name::EnterpriseBASIC11Suffixed, ROM::Name::EnterpriseBASIC21 }) {
|
||||
const auto basic = roms.find(rom_name);
|
||||
if(basic != roms.end()) {
|
||||
memcpy(basic_.data(), basic->second.data(), std::min(basic_.size(), basic->second.size()));
|
||||
has_basic = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!has_basic) {
|
||||
const auto basic1 = roms.find(ROM::Name::EnterpriseBASIC10Part1);
|
||||
const auto basic2 = roms.find(ROM::Name::EnterpriseBASIC10Part2);
|
||||
if(basic1 != roms.end() && basic2 != roms.end()) {
|
||||
memcpy(&basic_[0x0000], basic1->second.data(), std::min(size_t(8192), basic1->second.size()));
|
||||
memcpy(&basic_[0x2000], basic2->second.data(), std::min(size_t(8192), basic2->second.size()));
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the appropriate DOS ROMs.
|
||||
epdos_rom_.fill(0xff);
|
||||
const auto epdos = roms.find(ROM::Name::EnterpriseEPDOS);
|
||||
if(epdos != roms.end()) {
|
||||
memcpy(epdos_rom_.data(), epdos->second.data(), std::min(epdos_rom_.size(), epdos->second.size()));
|
||||
}
|
||||
exdos_rom_.fill(0xff);
|
||||
const auto exdos = roms.find(ROM::Name::EnterpriseEXDOS);
|
||||
if(exdos != roms.end()) {
|
||||
memcpy(exdos_rom_.data(), exdos->second.data(), std::min(exdos_rom_.size(), exdos->second.size()));
|
||||
}
|
||||
|
||||
// Seed key state.
|
||||
clear_all_keys();
|
||||
|
||||
// Take a reasonable guess at the initial memory configuration:
|
||||
// put EXOS into the first bank since this is a Z80 and therefore
|
||||
// starts from address 0; the third instruction in EXOS is a jump
|
||||
// to $c02e so it's reasonable to assume EXOS is in the highest bank
|
||||
// too, and it appears to act correctly if it's the first 16kb that's
|
||||
// in the highest bank. From there I guess: all banks are initialised
|
||||
// to 0.
|
||||
page<0>(0x00);
|
||||
page<1>(0x00);
|
||||
page<2>(0x00);
|
||||
page<3>(0x00);
|
||||
|
||||
// Pass on any media.
|
||||
insert_media(target.media);
|
||||
if(!target.loading_command.empty()) {
|
||||
type_string(target.loading_command);
|
||||
}
|
||||
|
||||
// Ensure the splash screen is automatically skipped if any media has been provided.
|
||||
if(!target.media.empty()) {
|
||||
should_skip_splash_screen_ = !target.media.empty();
|
||||
typer_delay_ = 2;
|
||||
}
|
||||
}
|
||||
|
||||
~ConcreteMachine() {
|
||||
audio_queue_.flush();
|
||||
}
|
||||
|
||||
// MARK: - Z80::BusHandler.
|
||||
forceinline void advance_nick(HalfCycles duration) {
|
||||
if(nick_ += duration) {
|
||||
const auto nick = nick_.last_valid();
|
||||
const bool nick_interrupt_line = nick->get_interrupt_line();
|
||||
if(nick_interrupt_line && !previous_nick_interrupt_line_) {
|
||||
set_interrupts(uint8_t(Dave::Interrupt::Nick), nick_.last_sequence_point_overrun());
|
||||
}
|
||||
previous_nick_interrupt_line_ = nick_interrupt_line;
|
||||
}
|
||||
}
|
||||
|
||||
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||
using PartialMachineCycle = CPU::Z80::PartialMachineCycle;
|
||||
const uint16_t address = cycle.address ? *cycle.address : 0x0000;
|
||||
|
||||
// Calculate an access penalty, if applicable.
|
||||
//
|
||||
// Rule applied here, which is slightly inferred:
|
||||
//
|
||||
// Non-video reads and writes are delayed by exactly a cycle or not delayed at all,
|
||||
// depending on the programmer's configuration of Dave.
|
||||
//
|
||||
// Video reads and writes, and Nick port accesses, are delayed so that the last
|
||||
// clock cycle of the machine cycle falls wholly inside the designated Z80 access
|
||||
// window, per Nick.
|
||||
//
|
||||
// The switch statement below just attempts to implement that logic.
|
||||
//
|
||||
HalfCycles penalty;
|
||||
switch(cycle.operation) {
|
||||
default: break;
|
||||
|
||||
// For non-video pauses, insert during the initial part of the bus cycle.
|
||||
case PartialMachineCycle::ReadStart:
|
||||
case PartialMachineCycle::WriteStart:
|
||||
if(!is_video_[address >> 14] && wait_mode_ == WaitMode::OnAllAccesses) {
|
||||
penalty = dave_delay_;
|
||||
}
|
||||
break;
|
||||
case PartialMachineCycle::ReadOpcodeStart: {
|
||||
if(is_video_[address >> 14]) {
|
||||
// Query Nick for the amount of delay that would occur with one cycle left
|
||||
// in this read opcode.
|
||||
const auto delay_time = nick_.time_since_flush(HalfCycles(2));
|
||||
const auto delay = nick_.last_valid()->get_time_until_z80_slot(delay_time);
|
||||
penalty = nick_.back_map(delay, delay_time);
|
||||
} else if(wait_mode_ != WaitMode::None) {
|
||||
penalty = dave_delay_;
|
||||
}
|
||||
} break;
|
||||
|
||||
// Video pauses: insert right at the end of the bus cycle.
|
||||
case PartialMachineCycle::Write:
|
||||
// Ensure all video that should have been collected prior to
|
||||
// this write has been.
|
||||
if(is_video_[address >> 14]) {
|
||||
nick_.flush();
|
||||
}
|
||||
[[fallthrough]];
|
||||
|
||||
case PartialMachineCycle::Read:
|
||||
if(is_video_[address >> 14]) {
|
||||
// Get delay, in Nick cycles, for a Z80 access that occurs in 0.5
|
||||
// cycles from now (i.e. with one cycle left to run).
|
||||
const auto delay_time = nick_.time_since_flush(HalfCycles(1));
|
||||
const auto delay = nick_.last_valid()->get_time_until_z80_slot(delay_time);
|
||||
penalty = nick_.back_map(delay, delay_time);
|
||||
}
|
||||
break;
|
||||
|
||||
case PartialMachineCycle::Input:
|
||||
case PartialMachineCycle::Output: {
|
||||
if((address & 0xf0) == 0x80) {
|
||||
// Get delay, in Nick cycles, for a Z80 access that occurs in 0.5
|
||||
// cycles from now (i.e. with one cycle left to run).
|
||||
const auto delay_time = nick_.time_since_flush(HalfCycles(1));
|
||||
const auto delay = nick_.last_valid()->get_time_until_z80_slot(delay_time);
|
||||
penalty = nick_.back_map(delay, delay_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const HalfCycles full_length = cycle.length + penalty;
|
||||
time_since_audio_update_ += full_length;
|
||||
advance_nick(full_length);
|
||||
if(dave_timer_ += full_length) {
|
||||
set_interrupts(dave_timer_.last_valid()->get_new_interrupts(), dave_timer_.last_sequence_point_overrun());
|
||||
}
|
||||
|
||||
// The WD/etc runs at a nominal 8Mhz.
|
||||
if constexpr (has_disk_controller) {
|
||||
exdos_.run_for(Cycles(full_length.as_integral()));
|
||||
}
|
||||
|
||||
switch(cycle.operation) {
|
||||
default: break;
|
||||
|
||||
case PartialMachineCycle::Interrupt:
|
||||
*cycle.value = 0xff;
|
||||
break;
|
||||
|
||||
case PartialMachineCycle::Input:
|
||||
switch(address & 0xff) {
|
||||
default:
|
||||
LOG("Unhandled input from " << PADHEX(2) << (address & 0xff));
|
||||
*cycle.value = 0xff;
|
||||
break;
|
||||
|
||||
case 0x10: case 0x11: case 0x12: case 0x13:
|
||||
case 0x14: case 0x15: case 0x16: case 0x17:
|
||||
if constexpr (has_disk_controller) {
|
||||
*cycle.value = exdos_.read(address);
|
||||
} else {
|
||||
*cycle.value = 0xff;
|
||||
}
|
||||
break;
|
||||
case 0x18: case 0x19: case 0x1a: case 0x1b:
|
||||
case 0x1c: case 0x1d: case 0x1e: case 0x1f:
|
||||
if constexpr (has_disk_controller) {
|
||||
*cycle.value = exdos_.get_control_register();
|
||||
} else {
|
||||
*cycle.value = 0xff;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x80: case 0x81: case 0x82: case 0x83:
|
||||
case 0x84: case 0x85: case 0x86: case 0x87:
|
||||
case 0x88: case 0x89: case 0x8a: case 0x8b:
|
||||
case 0x8c: case 0x8d: case 0x8e: case 0x8f:
|
||||
*cycle.value = nick_->read();
|
||||
break;
|
||||
|
||||
case 0xb0: *cycle.value = pages_[0]; break;
|
||||
case 0xb1: *cycle.value = pages_[1]; break;
|
||||
case 0xb2: *cycle.value = pages_[2]; break;
|
||||
case 0xb3: *cycle.value = pages_[3]; break;
|
||||
|
||||
case 0xb4:
|
||||
*cycle.value =
|
||||
(nick_->get_interrupt_line() ? 0x10 : 0x00) |
|
||||
dave_timer_->get_divider_state() |
|
||||
interrupt_state_;
|
||||
break;
|
||||
case 0xb5:
|
||||
if(active_key_line_ < key_lines_.size()) {
|
||||
*cycle.value = key_lines_[active_key_line_];
|
||||
} else {
|
||||
*cycle.value = 0xff;
|
||||
}
|
||||
break;
|
||||
case 0xb6: {
|
||||
// TODO: selected keyboard row, 0 to 9, should return one bit of joystick
|
||||
// input. That being the case:
|
||||
//
|
||||
// b0: joystick input
|
||||
// b1, b2: unused (in theory read from control port, but not used by any hardware)
|
||||
// b3: 0 = printer ready; 1 = not ready
|
||||
// b4: serial, data in
|
||||
// b5: serial, status in
|
||||
// b6: tape input volume level, 0 = high, 1 = low
|
||||
// b7: tape data input
|
||||
*cycle.value = 0xff;
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
|
||||
case PartialMachineCycle::Output:
|
||||
switch(address & 0xff) {
|
||||
default:
|
||||
LOG("Unhandled output: " << PADHEX(2) << *cycle.value << " to " << PADHEX(2) << (address & 0xff));
|
||||
break;
|
||||
|
||||
case 0x10: case 0x11: case 0x12: case 0x13:
|
||||
case 0x14: case 0x15: case 0x16: case 0x17:
|
||||
if constexpr(has_disk_controller) {
|
||||
exdos_.write(address, *cycle.value);
|
||||
}
|
||||
break;
|
||||
case 0x18: case 0x19: case 0x1a: case 0x1b:
|
||||
case 0x1c: case 0x1d: case 0x1e: case 0x1f:
|
||||
if constexpr(has_disk_controller) {
|
||||
exdos_.set_control_register(*cycle.value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x80: case 0x81: case 0x82: case 0x83:
|
||||
case 0x84: case 0x85: case 0x86: case 0x87:
|
||||
case 0x88: case 0x89: case 0x8a: case 0x8b:
|
||||
case 0x8c: case 0x8d: case 0x8e: case 0x8f:
|
||||
nick_->write(address, *cycle.value);
|
||||
break;
|
||||
|
||||
case 0xb0: page<0>(*cycle.value); break;
|
||||
case 0xb1: page<1>(*cycle.value); break;
|
||||
case 0xb2: page<2>(*cycle.value); break;
|
||||
case 0xb3: page<3>(*cycle.value); break;
|
||||
|
||||
case 0xbf:
|
||||
switch((*cycle.value >> 2)&3) {
|
||||
default: wait_mode_ = WaitMode::None; break;
|
||||
case 0: wait_mode_ = WaitMode::OnAllAccesses; break;
|
||||
case 1: wait_mode_ = WaitMode::OnM1; break;
|
||||
}
|
||||
|
||||
// Dave delays (i.e. those affecting memory areas not associated with Nick)
|
||||
// are one cycle in 8Mhz mode, two cycles in 12Mhz mode.
|
||||
dave_delay_ = HalfCycles(2 + (*cycle.value)&2);
|
||||
|
||||
[[fallthrough]];
|
||||
|
||||
case 0xa0: case 0xa1: case 0xa2: case 0xa3:
|
||||
case 0xa4: case 0xa5: case 0xa6: case 0xa7:
|
||||
case 0xa8: case 0xa9: case 0xaa: case 0xab:
|
||||
case 0xac: case 0xad: case 0xae: case 0xaf:
|
||||
update_audio();
|
||||
dave_audio_.write(address, *cycle.value);
|
||||
dave_timer_->write(address, *cycle.value);
|
||||
break;
|
||||
|
||||
case 0xb4:
|
||||
interrupt_mask_ = *cycle.value & 0x55;
|
||||
interrupt_state_ &= ~*cycle.value;
|
||||
update_interrupts();
|
||||
break;
|
||||
case 0xb5:
|
||||
// Logic here: the ROM scans the keyboard by checking ascending
|
||||
// lines. It also seems to provide a line of 0 when using port B5
|
||||
// for non-keyboard uses.
|
||||
//
|
||||
// So: use the rollover from line 9 back to line 0 as a trigger to
|
||||
// spot that a scan of the keyboard just finished. Which makes it
|
||||
// time to enqueue the next keypress.
|
||||
//
|
||||
// Re: should_skip_splash_screen_ and typer_delay_, assume that a
|
||||
// single keypress is necessary to get past the Enterprise splash
|
||||
// screen, then a pause in keypressing while BASIC or whatever
|
||||
// starts up, then presses can resume.
|
||||
if(active_key_line_ == 9 && !(*cycle.value & 0xf) && (should_skip_splash_screen_ || typer_)) {
|
||||
if(should_skip_splash_screen_) {
|
||||
set_key_state(uint16_t(Key::Space), typer_delay_);
|
||||
if(typer_delay_) {
|
||||
--typer_delay_;
|
||||
} else {
|
||||
typer_delay_ = 60;
|
||||
should_skip_splash_screen_ = false;
|
||||
}
|
||||
} else {
|
||||
if(!typer_delay_) {
|
||||
if(!typer_->type_next_character()) {
|
||||
clear_all_keys();
|
||||
typer_ = nullptr;
|
||||
}
|
||||
} else {
|
||||
--typer_delay_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
active_key_line_ = *cycle.value & 0xf;
|
||||
// TODO:
|
||||
//
|
||||
// b4: strobe output for printer
|
||||
// b5: tape sound control (?)
|
||||
// b6: tape motor control 1, 1 = on
|
||||
// b7: tape motor control 2, 1 = on
|
||||
break;
|
||||
case 0xb6:
|
||||
// Just 8 bits of printer data.
|
||||
LOG("TODO: printer output " << PADHEX(2) << *cycle.value);
|
||||
break;
|
||||
case 0xb7:
|
||||
// b0 = serial data out
|
||||
// b1 = serial status out
|
||||
LOG("TODO: serial output " << PADHEX(2) << *cycle.value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case PartialMachineCycle::Read:
|
||||
case PartialMachineCycle::ReadOpcode:
|
||||
if(read_pointers_[address >> 14]) {
|
||||
*cycle.value = read_pointers_[address >> 14][address];
|
||||
} else {
|
||||
*cycle.value = 0xff;
|
||||
}
|
||||
break;
|
||||
|
||||
case PartialMachineCycle::Write:
|
||||
if(write_pointers_[address >> 14]) {
|
||||
write_pointers_[address >> 14][address] = *cycle.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return penalty;
|
||||
}
|
||||
|
||||
void flush() {
|
||||
nick_.flush();
|
||||
update_audio();
|
||||
audio_queue_.perform();
|
||||
}
|
||||
|
||||
private:
|
||||
// MARK: - Memory layout
|
||||
std::array<uint8_t, 256 * 1024> ram_{};
|
||||
std::array<uint8_t, 64 * 1024> exos_;
|
||||
std::array<uint8_t, 16 * 1024> basic_;
|
||||
std::array<uint8_t, 16 * 1024> exdos_rom_;
|
||||
std::array<uint8_t, 32 * 1024> epdos_rom_;
|
||||
const uint8_t min_ram_slot_;
|
||||
|
||||
const uint8_t *read_pointers_[4] = {nullptr, nullptr, nullptr, nullptr};
|
||||
uint8_t *write_pointers_[4] = {nullptr, nullptr, nullptr, nullptr};
|
||||
uint8_t pages_[4] = {0x80, 0x80, 0x80, 0x80};
|
||||
|
||||
template <size_t slot> void page(uint8_t offset) {
|
||||
pages_[slot] = offset;
|
||||
|
||||
#define Map(location, source) \
|
||||
if(offset >= location && offset < location + source.size() / 0x4000) { \
|
||||
page<slot>(&source[(offset - location) * 0x4000], nullptr); \
|
||||
is_video_[slot] = false; \
|
||||
return; \
|
||||
}
|
||||
|
||||
Map(0, exos_);
|
||||
Map(16, basic_);
|
||||
Map(32, exdos_rom_);
|
||||
Map(48, epdos_rom_);
|
||||
|
||||
#undef Map
|
||||
|
||||
// Of whatever size of RAM I've declared above, use only the final portion.
|
||||
// This correlated with Nick always having been handed the final 64kb and,
|
||||
// at least while the RAM is the first thing declared above, does a little
|
||||
// to benefit data locality. Albeit not in a useful sense.
|
||||
if(offset >= min_ram_slot_) {
|
||||
const auto ram_floor = 4194304 - ram_.size();
|
||||
const size_t address = offset * 0x4000 - ram_floor;
|
||||
is_video_[slot] = offset >= 0xfc; // TODO: this hard-codes a 64kb video assumption.
|
||||
page<slot>(&ram_[address], &ram_[address]);
|
||||
return;
|
||||
}
|
||||
|
||||
page<slot>(nullptr, nullptr);
|
||||
}
|
||||
|
||||
template <size_t slot> void page(const uint8_t *read, uint8_t *write) {
|
||||
read_pointers_[slot] = read ? read - (slot * 0x4000) : nullptr;
|
||||
write_pointers_[slot] = write ? write - (slot * 0x4000) : nullptr;
|
||||
}
|
||||
|
||||
// MARK: - Memory Timing
|
||||
|
||||
// The wait mode affects all memory accesses _outside of the video area_.
|
||||
enum class WaitMode {
|
||||
None,
|
||||
OnM1,
|
||||
OnAllAccesses
|
||||
} wait_mode_ = WaitMode::OnAllAccesses;
|
||||
bool is_video_[4]{};
|
||||
|
||||
// MARK: - ScanProducer
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||
nick_.last_valid()->set_scan_target(scan_target);
|
||||
}
|
||||
|
||||
Outputs::Display::ScanStatus get_scaled_scan_status() const override {
|
||||
return nick_.last_valid()->get_scaled_scan_status();
|
||||
}
|
||||
|
||||
void set_display_type(Outputs::Display::DisplayType display_type) final {
|
||||
nick_.last_valid()->set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() const final {
|
||||
return nick_.last_valid()->get_display_type();
|
||||
}
|
||||
|
||||
// MARK: - AudioProducer
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
return &speaker_;
|
||||
}
|
||||
|
||||
// MARK: - TimedMachine
|
||||
void run_for(const Cycles cycles) override {
|
||||
z80_.run_for(cycles);
|
||||
}
|
||||
|
||||
// MARK: - KeyboardMachine
|
||||
Enterprise::KeyboardMapper keyboard_mapper_;
|
||||
KeyboardMapper *get_keyboard_mapper() final {
|
||||
return &keyboard_mapper_;
|
||||
}
|
||||
|
||||
uint8_t active_key_line_ = 0;
|
||||
std::array<uint8_t, 10> key_lines_;
|
||||
void set_key_state(uint16_t key, bool is_pressed) final {
|
||||
if(is_pressed) {
|
||||
key_lines_[key >> 8] &= ~uint8_t(key);
|
||||
} else {
|
||||
key_lines_[key >> 8] |= uint8_t(key);
|
||||
}
|
||||
}
|
||||
|
||||
void clear_all_keys() final {
|
||||
key_lines_.fill(0xff);
|
||||
}
|
||||
|
||||
// MARK: - Utility::TypeRecipient
|
||||
void type_string(const std::string &string) final {
|
||||
Utility::TypeRecipient<CharacterMapper>::add_typer(string);
|
||||
|
||||
if(z80_.get_is_resetting()) {
|
||||
should_skip_splash_screen_ = true;
|
||||
typer_delay_ = 1;
|
||||
} else {
|
||||
should_skip_splash_screen_ = false;
|
||||
typer_delay_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool can_type(char c) const final {
|
||||
return Utility::TypeRecipient<CharacterMapper>::can_type(c);
|
||||
}
|
||||
|
||||
bool should_skip_splash_screen_ = false;
|
||||
int typer_delay_ = 30;
|
||||
|
||||
// MARK: - MediaTarget
|
||||
bool insert_media(const Analyser::Static::Media &media) final {
|
||||
if constexpr (has_disk_controller) {
|
||||
if(!media.disks.empty()) {
|
||||
exdos_.set_disk(media.disks.front(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// MARK: - Interrupts
|
||||
|
||||
uint8_t interrupt_mask_ = 0x00, interrupt_state_ = 0x00;
|
||||
void set_interrupts(uint8_t mask, HalfCycles offset = HalfCycles(0)) {
|
||||
interrupt_state_ |= uint8_t(mask);
|
||||
update_interrupts(offset);
|
||||
}
|
||||
void update_interrupts(HalfCycles offset = HalfCycles(0)) {
|
||||
z80_.set_interrupt_line((interrupt_state_ >> 1) & interrupt_mask_, offset);
|
||||
}
|
||||
|
||||
// MARK: - Chips.
|
||||
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
|
||||
NickType nick_;
|
||||
bool previous_nick_interrupt_line_ = false;
|
||||
// Cf. timing guesses above.
|
||||
|
||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||
Dave::Audio dave_audio_;
|
||||
Outputs::Speaker::LowpassSpeaker<Dave::Audio> speaker_;
|
||||
HalfCycles time_since_audio_update_;
|
||||
|
||||
HalfCycles dave_delay_ = HalfCycles(2);
|
||||
|
||||
// The divider supplied to the JustInTimeActor and the manual divider used in
|
||||
// update_audio() should match.
|
||||
static constexpr int dave_divider = 8;
|
||||
JustInTimeActor<Dave::TimedInterruptSource, HalfCycles, 1, dave_divider> dave_timer_;
|
||||
inline void update_audio() {
|
||||
speaker_.run_for(audio_queue_, time_since_audio_update_.divide_cycles(Cycles(dave_divider)));
|
||||
}
|
||||
|
||||
// MARK: - EXDos card.
|
||||
EXDos exdos_;
|
||||
|
||||
// MARK: - Activity Source
|
||||
void set_activity_observer(Activity::Observer *observer) final {
|
||||
if constexpr (has_disk_controller) {
|
||||
exdos_.set_activity_observer(observer);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
std::unique_ptr<Reflection::Struct> get_options() final {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||
options->output = get_video_signal_configurable();
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
set_video_signal_configurable(options->output);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using namespace Enterprise;
|
||||
|
||||
Machine *Machine::Enterprise(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
using Target = Analyser::Static::Enterprise::Target;
|
||||
const Target *const enterprise_target = dynamic_cast<const Target *>(target);
|
||||
|
||||
#define BuildMachine(exdos, sixmhz) \
|
||||
if((enterprise_target->dos == Target::DOS::None) != exdos && (enterprise_target->speed == Target::Speed::SixMHz) == sixmhz) { \
|
||||
return new Enterprise::ConcreteMachine<exdos, sixmhz>(*enterprise_target, rom_fetcher); \
|
||||
}
|
||||
|
||||
BuildMachine(false, false);
|
||||
BuildMachine(false, true);
|
||||
BuildMachine(true, false);
|
||||
BuildMachine(true, true);
|
||||
|
||||
#undef BuildMachine
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
47
Machines/Enterprise/Enterprise.hpp
Normal file
47
Machines/Enterprise/Enterprise.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// Enterprise.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 10/06/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Enterprise_hpp
|
||||
#define Enterprise_hpp
|
||||
|
||||
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
#include "../../Configurable/StandardOptions.hpp"
|
||||
#include "../ROMMachine.hpp"
|
||||
|
||||
namespace Enterprise {
|
||||
|
||||
/*!
|
||||
@abstract Represents an Elan Enterprise.
|
||||
|
||||
@discussion An instance of Enterprise::Machine represents the current state of an
|
||||
Elan Enterprise.
|
||||
*/
|
||||
class Machine {
|
||||
public:
|
||||
virtual ~Machine();
|
||||
|
||||
static Machine *Enterprise(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
|
||||
|
||||
/// Defines the runtime options available for an Enterprise.
|
||||
class Options: public Reflection::StructImpl<Options>, public Configurable::DisplayOption<Options> {
|
||||
friend Configurable::DisplayOption<Options>;
|
||||
public:
|
||||
Options(Configurable::OptionsType type) :
|
||||
Configurable::DisplayOption<Options>(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour) {
|
||||
if(needs_declare()) {
|
||||
declare_display_option();
|
||||
limit_enum(&output, Configurable::Display::RGB, Configurable::Display::CompositeColour, Configurable::Display::CompositeMonochrome, -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* Enterprise_hpp */
|
||||
151
Machines/Enterprise/Keyboard.cpp
Normal file
151
Machines/Enterprise/Keyboard.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// Keyboard.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 17/06/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Keyboard.hpp"
|
||||
|
||||
using namespace Enterprise;
|
||||
|
||||
uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) const {
|
||||
#define BIND(source, dest) case Inputs::Keyboard::Key::source: return uint16_t(Key::dest)
|
||||
switch(key) {
|
||||
default: break;
|
||||
|
||||
BIND(Backslash, Backslash);
|
||||
BIND(CapsLock, Lock);
|
||||
BIND(Tab, Tab);
|
||||
BIND(Escape, Escape);
|
||||
BIND(Hyphen, Hyphen);
|
||||
BIND(Equals, Caret);
|
||||
BIND(Backspace, Erase);
|
||||
BIND(Delete, Delete);
|
||||
BIND(Semicolon, Semicolon);
|
||||
BIND(Quote, Colon);
|
||||
BIND(OpenSquareBracket, OpenSquareBracket);
|
||||
BIND(CloseSquareBracket, CloseSquareBracket);
|
||||
|
||||
BIND(End, Stop);
|
||||
BIND(Insert, Insert);
|
||||
BIND(BackTick, At);
|
||||
|
||||
BIND(k1, k1); BIND(k2, k2); BIND(k3, k3); BIND(k4, k4); BIND(k5, k5);
|
||||
BIND(k6, k6); BIND(k7, k7); BIND(k8, k8); BIND(k9, k9); BIND(k0, k0);
|
||||
|
||||
BIND(F1, F1); BIND(F2, F2); BIND(F3, F3); BIND(F4, F4);
|
||||
BIND(F5, F5); BIND(F6, F6); BIND(F7, F7); BIND(F8, F8);
|
||||
|
||||
BIND(Keypad1, F1); BIND(Keypad2, F2); BIND(Keypad3, F3); BIND(Keypad4, F4);
|
||||
BIND(Keypad5, F5); BIND(Keypad6, F6); BIND(Keypad7, F7); BIND(Keypad8, F8);
|
||||
|
||||
BIND(Q, Q); BIND(W, W); BIND(E, E); BIND(R, R); BIND(T, T);
|
||||
BIND(Y, Y); BIND(U, U); BIND(I, I); BIND(O, O); BIND(P, P);
|
||||
|
||||
BIND(A, A); BIND(S, S); BIND(D, D); BIND(F, F); BIND(G, G);
|
||||
BIND(H, H); BIND(J, J); BIND(K, K); BIND(L, L);
|
||||
|
||||
BIND(Z, Z); BIND(X, X); BIND(C, C); BIND(V, V);
|
||||
BIND(B, B); BIND(N, N); BIND(M, M);
|
||||
|
||||
BIND(FullStop, FullStop);
|
||||
BIND(Comma, Comma);
|
||||
BIND(ForwardSlash, ForwardSlash);
|
||||
|
||||
BIND(Space, Space); BIND(Enter, Enter);
|
||||
|
||||
BIND(LeftShift, LeftShift);
|
||||
BIND(RightShift, RightShift);
|
||||
BIND(LeftOption, Alt);
|
||||
BIND(RightOption, Alt);
|
||||
BIND(LeftControl, Control);
|
||||
BIND(RightControl, Control);
|
||||
|
||||
BIND(Left, Left);
|
||||
BIND(Right, Right);
|
||||
BIND(Up, Up);
|
||||
BIND(Down, Down);
|
||||
}
|
||||
#undef BIND
|
||||
|
||||
return MachineTypes::MappedKeyboardMachine::KeyNotMapped;
|
||||
}
|
||||
|
||||
const uint16_t *CharacterMapper::sequence_for_character(char character) const {
|
||||
#define KEYS(x) {uint16_t(x), MachineTypes::MappedKeyboardMachine::KeyEndSequence}
|
||||
#define SHIFT(x) {uint16_t(Key::LeftShift), uint16_t(x), MachineTypes::MappedKeyboardMachine::KeyEndSequence}
|
||||
#define _ {MachineTypes::MappedKeyboardMachine::KeyNotMapped}
|
||||
static KeySequence key_sequences[] = {
|
||||
/* NUL */ _, /* SOH */ _,
|
||||
/* STX */ _, /* ETX */ _,
|
||||
/* EOT */ _, /* ENQ */ _,
|
||||
/* ACK */ _, /* BEL */ _,
|
||||
/* BS */ KEYS(Key::Erase), /* HT */ KEYS(Key::Tab),
|
||||
/* LF */ KEYS(Key::Enter), /* VT */ _,
|
||||
/* FF */ _, /* CR */ KEYS(Key::Enter),
|
||||
/* SO */ _, /* SI */ _,
|
||||
/* DLE */ _, /* DC1 */ _,
|
||||
/* DC2 */ _, /* DC3 */ _,
|
||||
/* DC4 */ _, /* NAK */ _,
|
||||
/* SYN */ _, /* ETB */ _,
|
||||
/* CAN */ _, /* EM */ _,
|
||||
/* SUB */ _, /* ESC */ KEYS(Key::Escape),
|
||||
/* FS */ _, /* GS */ _,
|
||||
/* RS */ _, /* US */ _,
|
||||
/* space */ KEYS(Key::Space), /* ! */ SHIFT(Key::k1),
|
||||
/* " */ SHIFT(Key::k2), /* # */ _,
|
||||
/* $ */ SHIFT(Key::k4), /* % */ SHIFT(Key::k5),
|
||||
/* & */ SHIFT(Key::k6), /* ' */ SHIFT(Key::k7),
|
||||
/* ( */ SHIFT(Key::k8), /* ) */ SHIFT(Key::k9),
|
||||
/* * */ SHIFT(Key::Colon), /* + */ SHIFT(Key::Semicolon),
|
||||
/* , */ KEYS(Key::Comma), /* - */ KEYS(Key::Hyphen),
|
||||
/* . */ KEYS(Key::FullStop), /* / */ KEYS(Key::ForwardSlash),
|
||||
/* 0 */ KEYS(Key::k0), /* 1 */ KEYS(Key::k1),
|
||||
/* 2 */ KEYS(Key::k2), /* 3 */ KEYS(Key::k3),
|
||||
/* 4 */ KEYS(Key::k4), /* 5 */ KEYS(Key::k5),
|
||||
/* 6 */ KEYS(Key::k6), /* 7 */ KEYS(Key::k7),
|
||||
/* 8 */ KEYS(Key::k8), /* 9 */ KEYS(Key::k9),
|
||||
/* : */ KEYS(Key::Colon), /* ; */ KEYS(Key::Semicolon),
|
||||
/* < */ SHIFT(Key::Comma), /* = */ SHIFT(Key::Hyphen),
|
||||
/* > */ SHIFT(Key::FullStop), /* ? */ SHIFT(Key::ForwardSlash),
|
||||
/* @ */ KEYS(Key::At), /* A */ SHIFT(Key::A),
|
||||
/* B */ SHIFT(Key::B), /* C */ SHIFT(Key::C),
|
||||
/* D */ SHIFT(Key::D), /* E */ SHIFT(Key::E),
|
||||
/* F */ SHIFT(Key::F), /* G */ SHIFT(Key::G),
|
||||
/* H */ SHIFT(Key::H), /* I */ SHIFT(Key::I),
|
||||
/* J */ SHIFT(Key::J), /* K */ SHIFT(Key::K),
|
||||
/* L */ SHIFT(Key::L), /* M */ SHIFT(Key::M),
|
||||
/* N */ SHIFT(Key::N), /* O */ SHIFT(Key::O),
|
||||
/* P */ SHIFT(Key::P), /* Q */ SHIFT(Key::Q),
|
||||
/* R */ SHIFT(Key::R), /* S */ SHIFT(Key::S),
|
||||
/* T */ SHIFT(Key::T), /* U */ SHIFT(Key::U),
|
||||
/* V */ SHIFT(Key::V), /* W */ SHIFT(Key::W),
|
||||
/* X */ SHIFT(Key::X), /* Y */ SHIFT(Key::Y),
|
||||
/* Z */ SHIFT(Key::Z), /* [ */ KEYS(Key::OpenSquareBracket),
|
||||
/* \ */ KEYS(Key::Backslash), /* ] */ KEYS(Key::CloseSquareBracket),
|
||||
/* ^ */ SHIFT(Key::Caret), /* _ */ SHIFT(Key::k0),
|
||||
/* ` */ SHIFT(Key::At), /* a */ KEYS(Key::A),
|
||||
/* b */ KEYS(Key::B), /* c */ KEYS(Key::C),
|
||||
/* d */ KEYS(Key::D), /* e */ KEYS(Key::E),
|
||||
/* f */ KEYS(Key::F), /* g */ KEYS(Key::G),
|
||||
/* h */ KEYS(Key::H), /* i */ KEYS(Key::I),
|
||||
/* j */ KEYS(Key::J), /* k */ KEYS(Key::K),
|
||||
/* l */ KEYS(Key::L), /* m */ KEYS(Key::M),
|
||||
/* n */ KEYS(Key::N), /* o */ KEYS(Key::O),
|
||||
/* p */ KEYS(Key::P), /* q */ KEYS(Key::Q),
|
||||
/* r */ KEYS(Key::R), /* s */ KEYS(Key::S),
|
||||
/* t */ KEYS(Key::T), /* u */ KEYS(Key::U),
|
||||
/* v */ KEYS(Key::V), /* w */ KEYS(Key::W),
|
||||
/* x */ KEYS(Key::X), /* y */ KEYS(Key::Y),
|
||||
/* z */ KEYS(Key::Z), /* { */ SHIFT(Key::OpenSquareBracket),
|
||||
/* | */ SHIFT(Key::Backslash), /* } */ SHIFT(Key::CloseSquareBracket),
|
||||
/* ~ */ SHIFT(Key::Caret)
|
||||
};
|
||||
#undef _
|
||||
#undef SHIFT
|
||||
#undef KEYS
|
||||
|
||||
return table_lookup_sequence_for_character(key_sequences, character);
|
||||
}
|
||||
66
Machines/Enterprise/Keyboard.hpp
Normal file
66
Machines/Enterprise/Keyboard.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// Keyboard.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 17/06/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Machines_Enterprise_Keyboard_hpp
|
||||
#define Machines_Enterprise_Keyboard_hpp
|
||||
|
||||
#include "../KeyboardMachine.hpp"
|
||||
#include "../Utility/Typer.hpp"
|
||||
|
||||
namespace Enterprise {
|
||||
|
||||
#define KeyCode(line, mask) (line << 8) | mask
|
||||
|
||||
enum class Key: uint16_t {
|
||||
N = 0x0000 | 0x01, Backslash = 0x0000 | 0x02, B = 0x0000 | 0x04, C = 0x0000 | 0x08,
|
||||
V = 0x0000 | 0x10, X = 0x0000 | 0x20, Z = 0x0000 | 0x40, LeftShift = 0x0000 | 0x80,
|
||||
|
||||
H = 0x0100 | 0x01, Lock = 0x0100 | 0x02, G = 0x0100 | 0x04, D = 0x0100 | 0x08,
|
||||
F = 0x0100 | 0x10, S = 0x0100 | 0x20, A = 0x0100 | 0x40, Control = 0x0100 | 0x80,
|
||||
|
||||
U = 0x0200 | 0x01, Q = 0x0200 | 0x02, Y = 0x0200 | 0x04, R = 0x0200 | 0x08,
|
||||
T = 0x0200 | 0x10, E = 0x0200 | 0x20, W = 0x0200 | 0x40, Tab = 0x0200 | 0x80,
|
||||
|
||||
k7 = 0x0300 | 0x01, k1 = 0x0300 | 0x02, k6 = 0x0300 | 0x04, k4 = 0x0300 | 0x08,
|
||||
k5 = 0x0300 | 0x10, k3 = 0x0300 | 0x20, k2 = 0x0300 | 0x40, Escape = 0x0300 | 0x80,
|
||||
|
||||
F4 = 0x0400 | 0x01, F8 = 0x0400 | 0x02, F3 = 0x0400 | 0x04, F6 = 0x0400 | 0x08,
|
||||
F5 = 0x0400 | 0x10, F7 = 0x0400 | 0x20, F2 = 0x0400 | 0x40, F1 = 0x0400 | 0x80,
|
||||
|
||||
k8 = 0x0500 | 0x01, k9 = 0x0500 | 0x04, Hyphen = 0x0500 | 0x08,
|
||||
k0 = 0x0500 | 0x10, Caret = 0x0500 | 0x20, Erase = 0x0500 | 0x40,
|
||||
|
||||
J = 0x0600 | 0x01, K = 0x0600 | 0x04, Semicolon = 0x0600 | 0x08,
|
||||
L = 0x0600 | 0x10, Colon = 0x0600 | 0x20, CloseSquareBracket = 0x0600 | 0x40,
|
||||
|
||||
Stop = 0x0700 | 0x01, Down = 0x0700 | 0x02, Right = 0x0700 | 0x04, Up = 0x0700 | 0x08,
|
||||
Hold = 0x0700 | 0x10, Left = 0x0700 | 0x20, Enter = 0x0700 | 0x40, Alt = 0x0700 | 0x80,
|
||||
|
||||
M = 0x0800 | 0x01, Delete = 0x0800 | 0x02, Comma = 0x0800 | 0x04,
|
||||
ForwardSlash = 0x0800 | 0x08,
|
||||
FullStop = 0x0800 | 0x10,
|
||||
RightShift = 0x0800 | 0x20, Space = 0x0800 | 0x40, Insert = 0x0800 | 0x80,
|
||||
|
||||
I = 0x0900 | 0x01, O = 0x0900 | 0x04, At = 0x0900 | 0x08,
|
||||
P = 0x0900 | 0x10,
|
||||
OpenSquareBracket = 0x0900 | 0x20
|
||||
};
|
||||
|
||||
#undef KeyCode
|
||||
|
||||
struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper {
|
||||
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) const final;
|
||||
};
|
||||
|
||||
struct CharacterMapper: public ::Utility::CharacterMapper {
|
||||
const uint16_t *sequence_for_character(char character) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* Keyboard_hpp */
|
||||
629
Machines/Enterprise/Nick.cpp
Normal file
629
Machines/Enterprise/Nick.cpp
Normal file
@@ -0,0 +1,629 @@
|
||||
//
|
||||
// Nick.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/06/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Nick.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace {
|
||||
|
||||
uint16_t mapped_colour(uint8_t source) {
|
||||
// On the Enterprise, red and green are 3-bit quantities; blue is a 2-bit quantity.
|
||||
int red = ((source&0x01) << 2) | ((source&0x08) >> 2) | ((source&0x40) >> 6);
|
||||
int green = ((source&0x02) << 1) | ((source&0x10) >> 3) | ((source&0x80) >> 7);
|
||||
int blue = ((source&0x04) >> 1) | ((source&0x20) >> 5);
|
||||
|
||||
assert(red <= 7);
|
||||
assert(green <= 7);
|
||||
assert(blue <= 3);
|
||||
|
||||
red = (red << 1) + (red >> 3);
|
||||
green = (green << 1) + (green >> 3);
|
||||
blue = (blue << 2) + blue;
|
||||
|
||||
assert(red <= 15);
|
||||
assert(green <= 15);
|
||||
assert(blue <= 15);
|
||||
|
||||
// Duplicate bits where necessary to map to a full 4-bit range per channel.
|
||||
const uint8_t parts[2] = {
|
||||
uint8_t(
|
||||
red
|
||||
),
|
||||
uint8_t(
|
||||
(green << 4) + blue
|
||||
)
|
||||
};
|
||||
return *reinterpret_cast<const uint16_t *>(parts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using namespace Enterprise;
|
||||
|
||||
Nick::Nick(const uint8_t *ram) :
|
||||
crt_(57*16, 16, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Red4Green4Blue4),
|
||||
ram_(ram) {
|
||||
|
||||
// Just use RGB for now.
|
||||
set_display_type(Outputs::Display::DisplayType::RGB);
|
||||
|
||||
// Crop to the centre 90% of the display.
|
||||
crt_.set_visible_area(Outputs::Display::Rect(0.05f, 0.05f, 0.9f, 0.9f));
|
||||
}
|
||||
|
||||
void Nick::write(uint16_t address, uint8_t value) {
|
||||
switch(address & 3) {
|
||||
case 0:
|
||||
// Ignored: everything to do with external colour.
|
||||
for(int c = 0; c < 8; c++) {
|
||||
palette_[c + 8] = mapped_colour(uint8_t(((value & 0x1f) << 3) + c));
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if(output_type_ == OutputType::Border) {
|
||||
set_output_type(OutputType::Border, true);
|
||||
}
|
||||
border_colour_ = mapped_colour(value);
|
||||
break;
|
||||
case 2:
|
||||
line_parameter_base_ = uint16_t((line_parameter_base_ & 0xf000) | (value << 4));
|
||||
break;
|
||||
case 3:
|
||||
line_parameter_base_ = uint16_t((line_parameter_base_ & 0x0ff0) | (value << 12));
|
||||
|
||||
// Still a mystery to me: the exact meaning of the top two bits here. For now
|
||||
// just treat a 0 -> 1 transition of the MSB as a forced frame restart.
|
||||
if((value^line_parameter_control_) & value & 0x80) {
|
||||
// For now: just force this to be the final line of this mode block.
|
||||
// I'm unclear whether I should also reset the horizontal counter
|
||||
// (i.e. completely abandon current video phase).
|
||||
lines_remaining_ = 0xff;
|
||||
should_reload_line_parameters_ = true;
|
||||
}
|
||||
line_parameter_control_ = value & 0xc0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Nick::read() {
|
||||
return last_read_;
|
||||
}
|
||||
|
||||
Cycles Nick::get_time_until_z80_slot(Cycles after_period) const {
|
||||
// Place Z80 accesses in the first six cycles in each sixteen-cycle window.
|
||||
// That models video accesses as being the final ten. Which has the net effect
|
||||
// of responding to the line parameter table interrupt flag as soon as it's
|
||||
// loaded.
|
||||
|
||||
// Assumed below: the Z80 can start its final cycle anywhere in the first three
|
||||
// of the permitted six.
|
||||
const int offset = (horizontal_counter_ + after_period.as<int>()) & 15;
|
||||
if(offset < 3) {
|
||||
return 0;
|
||||
} else {
|
||||
return 16 - offset;
|
||||
}
|
||||
}
|
||||
|
||||
void Nick::run_for(Cycles duration) {
|
||||
constexpr int line_length = 912;
|
||||
|
||||
#define add_window(x) \
|
||||
line_data_pointer_[0] += is_sync_or_pixels_ * line_data_per_column_increments_[0] * (x); \
|
||||
line_data_pointer_[1] += is_sync_or_pixels_ * line_data_per_column_increments_[1] * (x); \
|
||||
window += x; \
|
||||
if(window != 57 && window == left_margin_) is_sync_or_pixels_ = true; \
|
||||
if(window != 57 && window == right_margin_) is_sync_or_pixels_ = false;
|
||||
|
||||
int clocks_remaining = duration.as<int>();
|
||||
while(clocks_remaining) {
|
||||
// Determine how many cycles are left this line.
|
||||
const int clocks_this_line = std::min(clocks_remaining, line_length - horizontal_counter_);
|
||||
|
||||
// Convert that into a [start/current] and end window.
|
||||
int window = horizontal_counter_ >> 4;
|
||||
const int end_window = (horizontal_counter_ + clocks_this_line) >> 4;
|
||||
|
||||
// Advance the line counters.
|
||||
clocks_remaining -= clocks_this_line;
|
||||
horizontal_counter_ = (horizontal_counter_ + clocks_this_line) % line_length;
|
||||
|
||||
// Do nothing if a window boundary isn't crossed.
|
||||
if(window == end_window) continue;
|
||||
|
||||
// HSYNC is signalled for four windows at the start of the line.
|
||||
// I currently believe this happens regardless of Vsync mode.
|
||||
if(!window) {
|
||||
set_output_type(OutputType::Sync);
|
||||
|
||||
// There's no increment to get to 0, it happens when the horizontal_counter_
|
||||
// is reset. So test for active bit effect manually.
|
||||
if(!left_margin_) is_sync_or_pixels_ = true;
|
||||
if(!right_margin_) is_sync_or_pixels_ = false;
|
||||
}
|
||||
|
||||
// Default to noting read.
|
||||
last_read_ = 0xff;
|
||||
|
||||
while(window < 4 && window < end_window) {
|
||||
if(should_reload_line_parameters_) {
|
||||
switch(window) {
|
||||
// First slot: line count, mode and interrupt flag.
|
||||
case 0:
|
||||
// Byte 0: lines remaining.
|
||||
lines_remaining_ = ram_[line_parameter_pointer_];
|
||||
|
||||
// Byte 1: current interrupt output plus graphics modes...
|
||||
last_read_ = ram_[line_parameter_pointer_ + 1];
|
||||
|
||||
// Set the new interrupt line output.
|
||||
interrupt_line_ = ram_[line_parameter_pointer_ + 1] & 0x80;
|
||||
|
||||
// Determine the mode and depth, and hence the column size.
|
||||
mode_ = Mode((ram_[line_parameter_pointer_ + 1] >> 1)&7);
|
||||
bpp_ = 1 << ((ram_[line_parameter_pointer_ + 1] >> 5)&3);
|
||||
switch(mode_) {
|
||||
default:
|
||||
case Mode::Pixel:
|
||||
column_size_ = 16 / bpp_;
|
||||
line_data_per_column_increments_[0] = 2;
|
||||
line_data_per_column_increments_[1] = 0;
|
||||
break;
|
||||
|
||||
case Mode::LPixel:
|
||||
case Mode::CH64:
|
||||
case Mode::CH128:
|
||||
case Mode::CH256:
|
||||
column_size_ = 8 / bpp_;
|
||||
line_data_per_column_increments_[0] = 1;
|
||||
line_data_per_column_increments_[1] = 0;
|
||||
break;
|
||||
|
||||
case Mode::Attr:
|
||||
column_size_ = 8;
|
||||
line_data_per_column_increments_[0] = 1;
|
||||
line_data_per_column_increments_[1] = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
vres_ = ram_[line_parameter_pointer_ + 1] & 0x10;
|
||||
reload_line_parameter_pointer_ = ram_[line_parameter_pointer_ + 1] & 0x01;
|
||||
break;
|
||||
|
||||
// Second slot: margins and ALT/IND bits.
|
||||
case 1:
|
||||
// Determine the margins.
|
||||
left_margin_ = ram_[line_parameter_pointer_ + 2] & 0x3f;
|
||||
right_margin_ = ram_[line_parameter_pointer_ + 3] & 0x3f;
|
||||
last_read_ = ram_[line_parameter_pointer_ + 3];
|
||||
|
||||
// Set up the alternative palettes,
|
||||
switch(mode_) {
|
||||
default:
|
||||
break;
|
||||
|
||||
// NB: LSBALT/MSBALT and ALTIND0/ALTIND1 appear to have opposite effects on palette selection.
|
||||
|
||||
case Mode::Pixel:
|
||||
case Mode::LPixel: {
|
||||
const uint8_t flags = ram_[line_parameter_pointer_ + 2];
|
||||
|
||||
// Use MSBALT and LSBALT to pick the alt_ind_palettes.
|
||||
//
|
||||
// LSBALT = b6 of params[2], if set => character codes with bit 6 set should use palette indices 4... instead of 0... .
|
||||
// MSBALT = b7 of params[2], if set => character codes with bit 7 set should use palette indices 2 and 3.
|
||||
two_colour_mask_ = 0xff &~ (((flags&0x80) >> 7) | ((flags&0x40) << 1));
|
||||
|
||||
alt_ind_palettes[0] = palette_;
|
||||
alt_ind_palettes[2] = alt_ind_palettes[0] + ((flags & 0x80) ? 2 : 0);
|
||||
|
||||
alt_ind_palettes[1] = alt_ind_palettes[0] + ((flags & 0x40) ? 4 : 0);
|
||||
alt_ind_palettes[3] = alt_ind_palettes[2] + ((flags & 0x40) ? 4 : 0);
|
||||
} break;
|
||||
|
||||
case Mode::CH64:
|
||||
case Mode::CH128:
|
||||
case Mode::CH256: {
|
||||
const uint8_t flags = ram_[line_parameter_pointer_ + 3];
|
||||
|
||||
// Use ALTIND0 and ALTIND1 to pick the alt_ind_palettes.
|
||||
//
|
||||
// ALTIND1 = b6 of params[3], if set => character codes with bit 7 set should use palette indices 2 and 3.
|
||||
// ALTIND0 = b7 of params[3], if set => character codes with bit 6 set should use palette indices 4... instead of 0... .
|
||||
alt_ind_palettes[0] = palette_;
|
||||
alt_ind_palettes[2] = alt_ind_palettes[0] + ((flags & 0x40) ? 2 : 0);
|
||||
|
||||
alt_ind_palettes[1] = alt_ind_palettes[0] + ((flags & 0x80) ? 4 : 0);
|
||||
alt_ind_palettes[3] = alt_ind_palettes[2] + ((flags & 0x80) ? 4 : 0);
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
|
||||
// Third slot: Line data pointer 1.
|
||||
case 2:
|
||||
start_line_data_pointer_[0] = ram_[line_parameter_pointer_ + 4];
|
||||
start_line_data_pointer_[0] |= ram_[line_parameter_pointer_ + 5] << 8;
|
||||
|
||||
line_data_pointer_[0] = start_line_data_pointer_[0];
|
||||
last_read_ = ram_[line_parameter_pointer_ + 5];
|
||||
break;
|
||||
|
||||
// Fourth slot: Line data pointer 2.
|
||||
case 3:
|
||||
start_line_data_pointer_[1] = ram_[line_parameter_pointer_ + 6];
|
||||
start_line_data_pointer_[1] |= ram_[line_parameter_pointer_ + 7] << 8;
|
||||
|
||||
line_data_pointer_[1] = start_line_data_pointer_[1];
|
||||
last_read_ = ram_[line_parameter_pointer_ + 7];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++output_duration_;
|
||||
add_window(1);
|
||||
}
|
||||
if(window == 4) {
|
||||
if(mode_ == Mode::Vsync) {
|
||||
set_output_type(is_sync_or_pixels_ ? OutputType::Sync : OutputType::Blank);
|
||||
} else {
|
||||
set_output_type(OutputType::Blank);
|
||||
}
|
||||
}
|
||||
|
||||
// Deal with vsync mode out here.
|
||||
if(mode_ == Mode::Vsync) {
|
||||
if(window >= 4) {
|
||||
while(window < end_window) {
|
||||
// Skip straight to the next event.
|
||||
int next_event = end_window;
|
||||
if(window < left_margin_) next_event = std::min(next_event, left_margin_);
|
||||
if(window < right_margin_) next_event = std::min(next_event, right_margin_);
|
||||
|
||||
output_duration_ += next_event - window;
|
||||
add_window(next_event - window);
|
||||
set_output_type(is_sync_or_pixels_ ? OutputType::Sync : OutputType::Blank);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If present then the colour burst is output for the period from
|
||||
// the start of window 6 to the end of window 10.
|
||||
//
|
||||
// The first 8 palette entries also need to be fetched here.
|
||||
while(window < first_pixel_window_ && window < end_window) {
|
||||
if(window == 6) {
|
||||
set_output_type(OutputType::ColourBurst);
|
||||
}
|
||||
|
||||
if(should_reload_line_parameters_ && window < 8) {
|
||||
const int base = (window - 4) << 1;
|
||||
assert(base < 7);
|
||||
palette_[base] = mapped_colour(ram_[line_parameter_pointer_ + base + 8]);
|
||||
palette_[base + 1] = mapped_colour(ram_[line_parameter_pointer_ + base + 9]);
|
||||
last_read_ = ram_[line_parameter_pointer_ + base + 9];
|
||||
}
|
||||
|
||||
++output_duration_;
|
||||
add_window(1);
|
||||
}
|
||||
|
||||
if(window >= first_pixel_window_) {
|
||||
if(window == first_pixel_window_) {
|
||||
set_output_type(is_sync_or_pixels_ ? OutputType::Pixels : OutputType::Border);
|
||||
}
|
||||
|
||||
while(window < end_window) {
|
||||
int next_event = end_window;
|
||||
if(window < left_margin_) next_event = std::min(next_event, left_margin_);
|
||||
if(window < right_margin_) next_event = std::min(next_event, right_margin_);
|
||||
|
||||
if(is_sync_or_pixels_) {
|
||||
|
||||
#define DispatchBpp(func) \
|
||||
switch(bpp_) { \
|
||||
default: \
|
||||
case 1: func(1)(pixel_pointer_, output_duration); break; \
|
||||
case 2: func(2)(pixel_pointer_, output_duration); break; \
|
||||
case 4: func(4)(pixel_pointer_, output_duration); break; \
|
||||
case 8: func(8)(pixel_pointer_, output_duration); break; \
|
||||
}
|
||||
|
||||
#define pixel(x) output_pixel<x, false>
|
||||
#define lpixel(x) output_pixel<x, true>
|
||||
#define ch256(x) output_character<x, 8>
|
||||
#define ch128(x) output_character<x, 7>
|
||||
#define ch64(x) output_character<x, 6>
|
||||
#define attr(x) output_attributed<x>
|
||||
|
||||
int columns_remaining = next_event - window;
|
||||
while(columns_remaining) {
|
||||
if(!pixel_pointer_) {
|
||||
if(output_duration_) {
|
||||
set_output_type(OutputType::Pixels, true);
|
||||
}
|
||||
pixel_pointer_ = allocated_pointer_ = reinterpret_cast<uint16_t *>(crt_.begin_data(allocation_size));
|
||||
}
|
||||
|
||||
if(allocated_pointer_) {
|
||||
const int output_duration = std::min(columns_remaining, int(allocated_pointer_ + allocation_size - pixel_pointer_) / column_size_);
|
||||
|
||||
switch(mode_) {
|
||||
default:
|
||||
case Mode::Pixel: DispatchBpp(pixel); break;
|
||||
case Mode::LPixel: DispatchBpp(lpixel); break;
|
||||
case Mode::CH256: DispatchBpp(ch256); break;
|
||||
case Mode::CH128: DispatchBpp(ch128); break;
|
||||
case Mode::CH64: DispatchBpp(ch64); break;
|
||||
case Mode::Attr: DispatchBpp(attr); break;
|
||||
}
|
||||
|
||||
pixel_pointer_ += output_duration * column_size_;
|
||||
output_duration_ += output_duration;
|
||||
if(pixel_pointer_ - allocated_pointer_ == allocation_size) {
|
||||
set_output_type(OutputType::Pixels, true);
|
||||
}
|
||||
columns_remaining -= output_duration;
|
||||
add_window(output_duration);
|
||||
} else {
|
||||
output_duration_ += columns_remaining;
|
||||
add_window(columns_remaining);
|
||||
columns_remaining = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#undef attr
|
||||
#undef ch64
|
||||
#undef ch128
|
||||
#undef ch256
|
||||
#undef pixel
|
||||
#undef lpixel
|
||||
#undef DispatchBpp
|
||||
} else {
|
||||
output_duration_ += next_event - window;
|
||||
add_window(next_event - window);
|
||||
}
|
||||
|
||||
set_output_type(is_sync_or_pixels_ ? OutputType::Pixels : OutputType::Border);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for end of line.
|
||||
if(!horizontal_counter_) {
|
||||
assert(window == 57);
|
||||
|
||||
++lines_remaining_;
|
||||
if(!lines_remaining_) {
|
||||
should_reload_line_parameters_ = true;
|
||||
|
||||
// Check for end-of-frame.
|
||||
if(reload_line_parameter_pointer_) {
|
||||
line_parameter_pointer_ = line_parameter_base_;
|
||||
} else {
|
||||
line_parameter_pointer_ += 16;
|
||||
}
|
||||
} else {
|
||||
should_reload_line_parameters_ = false;
|
||||
}
|
||||
|
||||
// Deal with VRES and other address reloading, dependant upon mode.
|
||||
switch(mode_) {
|
||||
default: break;
|
||||
case Mode::CH64:
|
||||
case Mode::CH128:
|
||||
case Mode::CH256:
|
||||
line_data_pointer_[0] = start_line_data_pointer_[0];
|
||||
++line_data_pointer_[1];
|
||||
break;
|
||||
|
||||
case Mode::Pixel:
|
||||
case Mode::LPixel:
|
||||
case Mode::Attr:
|
||||
// Reload the pixel or attribute address if VRES is clear.
|
||||
if(!vres_) {
|
||||
line_data_pointer_[0] = start_line_data_pointer_[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef add_window
|
||||
|
||||
}
|
||||
|
||||
void Nick::set_output_type(OutputType type, bool force_flush) {
|
||||
if(type == output_type_ && !force_flush) {
|
||||
return;
|
||||
}
|
||||
if(output_duration_) {
|
||||
switch(output_type_) {
|
||||
case OutputType::Border: {
|
||||
uint16_t *const colour_pointer = reinterpret_cast<uint16_t *>(crt_.begin_data(1));
|
||||
if(colour_pointer) *colour_pointer = border_colour_;
|
||||
crt_.output_level(output_duration_*16);
|
||||
} break;
|
||||
|
||||
case OutputType::Pixels: {
|
||||
crt_.output_data(output_duration_*16, size_t(output_duration_*column_size_));
|
||||
pixel_pointer_ = nullptr;
|
||||
allocated_pointer_ = nullptr;
|
||||
} break;
|
||||
|
||||
case OutputType::Sync: crt_.output_sync(output_duration_*16); break;
|
||||
case OutputType::Blank: crt_.output_blank(output_duration_*16); break;
|
||||
case OutputType::ColourBurst: crt_.output_colour_burst(output_duration_*16, 0); break;
|
||||
}
|
||||
}
|
||||
|
||||
output_duration_ = 0;
|
||||
output_type_ = type;
|
||||
}
|
||||
|
||||
// MARK: - Sequence points.
|
||||
|
||||
Cycles Nick::get_next_sequence_point() const {
|
||||
constexpr int load_point = 16; // i.e. 16 cycles after the start of the line, the
|
||||
// interrupt line may change. That is, after the
|
||||
// second byte of the mode line has been read.
|
||||
|
||||
// Any mode line may cause a change in the interrupt output, so as a first blush
|
||||
// just always report the time until the end of the mode line.
|
||||
if(lines_remaining_ || horizontal_counter_ >= load_point) {
|
||||
return Cycles(load_point + (912 - horizontal_counter_) + (0xff - lines_remaining_) * 912);
|
||||
} else {
|
||||
return Cycles(load_point - horizontal_counter_);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CRT passthroughs.
|
||||
|
||||
void Nick::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||
crt_.set_scan_target(scan_target);
|
||||
}
|
||||
|
||||
Outputs::Display::ScanStatus Nick::get_scaled_scan_status() const {
|
||||
return crt_.get_scaled_scan_status();
|
||||
}
|
||||
|
||||
void Nick::set_display_type(Outputs::Display::DisplayType display_type) {
|
||||
first_pixel_window_ = display_type == Outputs::Display::DisplayType::RGB ? 8 : 10;
|
||||
crt_.set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType Nick::get_display_type() const {
|
||||
return crt_.get_display_type();
|
||||
}
|
||||
|
||||
// MARK: - Specific pixel outputters.
|
||||
|
||||
#define output1bpp(x) \
|
||||
target[0] = palette[(x & 0x80) >> 7]; \
|
||||
target[1] = palette[(x & 0x40) >> 6]; \
|
||||
target[2] = palette[(x & 0x20) >> 5]; \
|
||||
target[3] = palette[(x & 0x10) >> 4]; \
|
||||
target[4] = palette[(x & 0x08) >> 3]; \
|
||||
target[5] = palette[(x & 0x04) >> 2]; \
|
||||
target[6] = palette[(x & 0x02) >> 1]; \
|
||||
target[7] = palette[(x & 0x01) >> 0]; \
|
||||
target += 8
|
||||
|
||||
#define output2bpp(x) \
|
||||
target[0] = palette_[((x & 0x80) >> 7) | ((x & 0x08) >> 2)]; \
|
||||
target[1] = palette_[((x & 0x40) >> 6) | ((x & 0x04) >> 1)]; \
|
||||
target[2] = palette_[((x & 0x20) >> 5) | ((x & 0x02) >> 0)]; \
|
||||
target[3] = palette_[((x & 0x10) >> 4) | ((x & 0x01) << 1)]; \
|
||||
target += 4
|
||||
|
||||
#define output4bpp(x) \
|
||||
target[0] = palette_[((x & 0x02) << 2) | ((x & 0x20) >> 3) | ((x & 0x08) >> 2) | ((x & 0x80) >> 7)]; \
|
||||
target[1] = palette_[((x & 0x01) << 3) | ((x & 0x10) >> 2) | ((x & 0x04) >> 1) | ((x & 0x40) >> 6)]; \
|
||||
target += 2
|
||||
|
||||
#define output8bpp(x) \
|
||||
target[0] = mapped_colour(x); \
|
||||
++target
|
||||
|
||||
template <int bpp, bool is_lpixel> void Nick::output_pixel(uint16_t *target, int columns) const {
|
||||
static_assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8);
|
||||
|
||||
int index = 0;
|
||||
for(int c = 0; c < columns; c++) {
|
||||
uint8_t pixels[2] = {
|
||||
ram_[(line_data_pointer_[0] + index) & 0xffff],
|
||||
ram_[(line_data_pointer_[0] + index + 1) & 0xffff]
|
||||
};
|
||||
index += is_lpixel ? 1 : 2;
|
||||
last_read_ = pixels[1];
|
||||
|
||||
switch(bpp) {
|
||||
default:
|
||||
case 1: {
|
||||
const uint16_t *palette = alt_ind_palettes[((pixels[0] >> 6) & 0x02) | (pixels[0]&1)];
|
||||
pixels[0] &= two_colour_mask_;
|
||||
output1bpp(pixels[0]);
|
||||
|
||||
if constexpr (!is_lpixel) {
|
||||
palette = alt_ind_palettes[((pixels[1] >> 6) & 0x02) | (pixels[1]&1)];
|
||||
pixels[1] &= two_colour_mask_;
|
||||
output1bpp(pixels[1]);
|
||||
}
|
||||
} break;
|
||||
|
||||
case 2:
|
||||
output2bpp(pixels[0]);
|
||||
if constexpr (!is_lpixel) {
|
||||
output2bpp(pixels[1]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
output4bpp(pixels[0]);
|
||||
if constexpr (!is_lpixel) {
|
||||
output4bpp(pixels[1]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 8:
|
||||
output8bpp(pixels[0]);
|
||||
if constexpr (!is_lpixel) {
|
||||
output8bpp(pixels[1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <int bpp, int index_bits> void Nick::output_character(uint16_t *target, int columns) const {
|
||||
static_assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8);
|
||||
|
||||
for(int c = 0; c < columns; c++) {
|
||||
const uint8_t character = ram_[(line_data_pointer_[0] + c) & 0xffff];
|
||||
const uint8_t pixels = ram_[(
|
||||
(line_data_pointer_[1] << index_bits) +
|
||||
(character & ((1 << index_bits) - 1))
|
||||
) & 0xffff];
|
||||
last_read_ = pixels;
|
||||
|
||||
switch(bpp) {
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
|
||||
case 1: {
|
||||
// This applies ALTIND0 and ALTIND1.
|
||||
const uint16_t *palette = alt_ind_palettes[character >> 6];
|
||||
output1bpp(pixels);
|
||||
} break;
|
||||
|
||||
case 2: output2bpp(pixels); break;
|
||||
case 4: output4bpp(pixels); break;
|
||||
case 8: output8bpp(pixels); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <int bpp> void Nick::output_attributed(uint16_t *target, int columns) const {
|
||||
static_assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8);
|
||||
|
||||
for(int c = 0; c < columns; c++) {
|
||||
const uint8_t pixels = ram_[(line_data_pointer_[1] + c) & 0xffff];
|
||||
const uint8_t attributes = ram_[(line_data_pointer_[0] + c) & 0xffff];
|
||||
last_read_ = pixels;
|
||||
|
||||
const uint16_t palette[2] = {
|
||||
palette_[attributes >> 4], palette_[attributes & 0x0f]
|
||||
};
|
||||
output1bpp(pixels);
|
||||
}
|
||||
}
|
||||
|
||||
#undef output1bpp
|
||||
#undef output2bpp
|
||||
#undef output4bpp
|
||||
#undef output8bpp
|
||||
126
Machines/Enterprise/Nick.hpp
Normal file
126
Machines/Enterprise/Nick.hpp
Normal file
@@ -0,0 +1,126 @@
|
||||
//
|
||||
// Nick.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/06/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Nick_hpp
|
||||
#define Nick_hpp
|
||||
|
||||
#include <cstdint>
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../../Outputs/CRT/CRT.hpp"
|
||||
|
||||
namespace Enterprise {
|
||||
|
||||
class Nick {
|
||||
public:
|
||||
Nick(const uint8_t *ram);
|
||||
|
||||
/// Writes to a Nick register; only the low two bits are decoded.
|
||||
void write(uint16_t address, uint8_t value);
|
||||
|
||||
/// Reads from the Nick range. Nobody seems to be completely clear what
|
||||
/// this should return; I've set it up to return the last fetched video or mode
|
||||
/// line byte during periods when those things are being fetched, 0xff at all
|
||||
/// other times. Including during refresh, since I don't know what addresses
|
||||
/// are generated then.
|
||||
///
|
||||
/// This likely isn't accurate, but is the most accurate guess I could make.
|
||||
uint8_t read();
|
||||
|
||||
void run_for(Cycles);
|
||||
Cycles get_time_until_z80_slot(Cycles after_period) const;
|
||||
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target);
|
||||
Outputs::Display::ScanStatus get_scaled_scan_status() const;
|
||||
|
||||
/// @returns The amount of time until the next potential change in interrupt output.
|
||||
Cycles get_next_sequence_point() const;
|
||||
|
||||
/*!
|
||||
@returns The current state of the interrupt line — @c true for active;
|
||||
@c false for inactive.
|
||||
*/
|
||||
inline bool get_interrupt_line() const {
|
||||
return interrupt_line_;
|
||||
}
|
||||
|
||||
/// Sets the type of output.
|
||||
void set_display_type(Outputs::Display::DisplayType);
|
||||
|
||||
/// Gets the type of output.
|
||||
Outputs::Display::DisplayType get_display_type() const;
|
||||
|
||||
private:
|
||||
Outputs::CRT::CRT crt_;
|
||||
const uint8_t *const ram_;
|
||||
|
||||
// CPU-provided state.
|
||||
uint8_t line_parameter_control_ = 0xc0;
|
||||
uint16_t line_parameter_base_ = 0x0000;
|
||||
uint16_t border_colour_ = 0;
|
||||
|
||||
// Ephemerals, related to current video position.
|
||||
int horizontal_counter_ = 0;
|
||||
uint16_t line_parameter_pointer_ = 0x0000;
|
||||
bool should_reload_line_parameters_ = true;
|
||||
uint16_t line_data_pointer_[2];
|
||||
uint16_t start_line_data_pointer_[2];
|
||||
mutable uint8_t last_read_ = 0xff;
|
||||
|
||||
// Current mode line parameters.
|
||||
uint8_t lines_remaining_ = 0x00;
|
||||
uint8_t two_colour_mask_ = 0xff;
|
||||
int left_margin_ = 0, right_margin_ = 0;
|
||||
const uint16_t *alt_ind_palettes[4];
|
||||
enum class Mode {
|
||||
Vsync,
|
||||
Pixel,
|
||||
Attr,
|
||||
CH256,
|
||||
CH128,
|
||||
CH64,
|
||||
Unused,
|
||||
LPixel,
|
||||
} mode_ = Mode::Vsync;
|
||||
bool is_sync_or_pixels_ = false;
|
||||
int bpp_ = 0;
|
||||
int column_size_ = 0;
|
||||
bool interrupt_line_ = true;
|
||||
int line_data_per_column_increments_[2] = {0, 0};
|
||||
bool vres_ = false;
|
||||
bool reload_line_parameter_pointer_ = false;
|
||||
|
||||
// The destination for new pixels.
|
||||
static constexpr int allocation_size = 336;
|
||||
static_assert((allocation_size % 16) == 0, "Allocation size must be a multiple of 16");
|
||||
uint16_t *pixel_pointer_ = nullptr, *allocated_pointer_ = nullptr;
|
||||
|
||||
// Output transitions.
|
||||
enum class OutputType {
|
||||
Sync, Blank, Pixels, Border, ColourBurst
|
||||
};
|
||||
void set_output_type(OutputType, bool force_flush = false);
|
||||
int output_duration_ = 0;
|
||||
OutputType output_type_ = OutputType::Sync;
|
||||
|
||||
// Current palette.
|
||||
uint16_t palette_[16]{};
|
||||
|
||||
// The first column with pixels on it; will be either 8 or 10 depending
|
||||
// on whether the colour burst is meaningful to the current display type.
|
||||
int first_pixel_window_ = 10;
|
||||
|
||||
// Specific outputters.
|
||||
template <int bpp, bool is_lpixel> void output_pixel(uint16_t *target, int columns) const;
|
||||
template <int bpp, int index_bits> void output_character(uint16_t *target, int columns) const;
|
||||
template <int bpp> void output_attributed(uint16_t *target, int columns) const;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif /* Nick_hpp */
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "../ColecoVision/ColecoVision.hpp"
|
||||
#include "../Commodore/Vic-20/Vic20.hpp"
|
||||
#include "../Electron/Electron.hpp"
|
||||
#include "../Enterprise/Enterprise.hpp"
|
||||
#include "../MasterSystem/MasterSystem.hpp"
|
||||
#include "../MSX/MSX.hpp"
|
||||
#include "../Oric/Oric.hpp"
|
||||
@@ -34,6 +35,7 @@
|
||||
#include "../../Analyser/Static/Atari2600/Target.hpp"
|
||||
#include "../../Analyser/Static/AtariST/Target.hpp"
|
||||
#include "../../Analyser/Static/Commodore/Target.hpp"
|
||||
#include "../../Analyser/Static/Enterprise/Target.hpp"
|
||||
#include "../../Analyser/Static/Macintosh/Target.hpp"
|
||||
#include "../../Analyser/Static/MSX/Target.hpp"
|
||||
#include "../../Analyser/Static/Oric/Target.hpp"
|
||||
@@ -49,7 +51,7 @@ Machine::DynamicMachine *Machine::MachineForTarget(const Analyser::Static::Targe
|
||||
|
||||
Machine::DynamicMachine *machine = nullptr;
|
||||
try {
|
||||
#define BindD(name, m) case Analyser::Machine::m: machine = new Machine::TypedDynamicMachine<name::Machine>(name::Machine::m(target, rom_fetcher)); break;
|
||||
#define BindD(name, m) case Analyser::Machine::m: machine = new Machine::TypedDynamicMachine<::name::Machine>(name::Machine::m(target, rom_fetcher)); break;
|
||||
#define Bind(m) BindD(m, m)
|
||||
switch(target->machine) {
|
||||
Bind(AmstradCPC)
|
||||
@@ -61,6 +63,7 @@ Machine::DynamicMachine *Machine::MachineForTarget(const Analyser::Static::Targe
|
||||
BindD(Coleco::Vision, ColecoVision)
|
||||
BindD(Commodore::Vic20, Vic20)
|
||||
Bind(Electron)
|
||||
Bind(Enterprise)
|
||||
Bind(MSX)
|
||||
Bind(Oric)
|
||||
BindD(Sega::MasterSystem, MasterSystem)
|
||||
@@ -127,6 +130,7 @@ std::string Machine::ShortNameForTargetMachine(const Analyser::Machine machine)
|
||||
case Analyser::Machine::AtariST: return "AtariST";
|
||||
case Analyser::Machine::ColecoVision: return "ColecoVision";
|
||||
case Analyser::Machine::Electron: return "Electron";
|
||||
case Analyser::Machine::Enterprise: return "Enterprise";
|
||||
case Analyser::Machine::Macintosh: return "Macintosh";
|
||||
case Analyser::Machine::MasterSystem: return "MasterSystem";
|
||||
case Analyser::Machine::MSX: return "MSX";
|
||||
@@ -148,6 +152,7 @@ std::string Machine::LongNameForTargetMachine(Analyser::Machine machine) {
|
||||
case Analyser::Machine::AtariST: return "Atari ST";
|
||||
case Analyser::Machine::ColecoVision: return "ColecoVision";
|
||||
case Analyser::Machine::Electron: return "Acorn Electron";
|
||||
case Analyser::Machine::Enterprise: return "Enterprise";
|
||||
case Analyser::Machine::Macintosh: return "Apple Macintosh";
|
||||
case Analyser::Machine::MasterSystem: return "Sega Master System";
|
||||
case Analyser::Machine::MSX: return "MSX";
|
||||
@@ -177,6 +182,7 @@ std::vector<std::string> Machine::AllMachines(Type type, bool long_names) {
|
||||
AddName(AppleIIgs);
|
||||
AddName(AtariST);
|
||||
AddName(Electron);
|
||||
AddName(Enterprise);
|
||||
AddName(Macintosh);
|
||||
AddName(MSX);
|
||||
AddName(Oric);
|
||||
@@ -201,6 +207,7 @@ std::map<std::string, std::unique_ptr<Reflection::Struct>> Machine::AllOptionsBy
|
||||
Emplace(AtariST, Atari::ST::Machine);
|
||||
Emplace(ColecoVision, Coleco::Vision::Machine);
|
||||
Emplace(Electron, Electron::Machine);
|
||||
Emplace(Enterprise, Enterprise::Machine);
|
||||
Emplace(Macintosh, Apple::Macintosh::Machine);
|
||||
Emplace(MasterSystem, Sega::MasterSystem::Machine);
|
||||
Emplace(MSX, MSX::Machine);
|
||||
@@ -226,6 +233,7 @@ std::map<std::string, std::unique_ptr<Analyser::Static::Target>> Machine::Target
|
||||
Add(AppleIIgs);
|
||||
Add(AtariST);
|
||||
AddMapped(Electron, Acorn);
|
||||
Add(Enterprise);
|
||||
Add(Macintosh);
|
||||
Add(MSX);
|
||||
Add(Oric);
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "MemoryPacker.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
void Memory::PackBigEndian16(const std::vector<uint8_t> &source, uint16_t *target) {
|
||||
for(size_t c = 0; c < source.size(); c += 2) {
|
||||
target[c >> 1] = uint16_t(source[c] << 8) | uint16_t(source[c+1]);
|
||||
|
||||
@@ -27,8 +27,15 @@ Request::Request(Name name, bool optional) {
|
||||
}
|
||||
|
||||
Request Request::append(Node::Type type, const Request &rhs) {
|
||||
// Start with the easiest case: this is already an appropriate
|
||||
// request, and so is the new thing.
|
||||
// If either side is empty, act appropriately.
|
||||
if(node.empty() && !rhs.node.empty()) {
|
||||
return rhs;
|
||||
}
|
||||
if(rhs.node.empty()) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Just copy in the RHS child nodes if types match.
|
||||
if(node.type == type && rhs.node.type == type) {
|
||||
Request new_request = *this;
|
||||
new_request.node.children.insert(new_request.node.children.end(), rhs.node.children.begin(), rhs.node.children.end());
|
||||
@@ -404,6 +411,62 @@ Description::Description(Name name) {
|
||||
*this = Description(name, "DiskII", "the Disk II 13-sector state machine ROM", "state-machine-13.rom", 256, 0x62e22620u);
|
||||
break;
|
||||
|
||||
case Name::EnterpriseEXOS10: {
|
||||
const std::initializer_list<std::string> filenames = {"exos10.bin", "Exos (198x)(Enterprise).bin"};
|
||||
*this = Description(name, "Enterprise", "the Enterprise EXOS ROM v1.0", filenames, 32 * 1024, 0x30b26387u);
|
||||
} break;
|
||||
case Name::EnterpriseEXOS20: {
|
||||
const std::initializer_list<std::string> filenames = {"exos20.bin", "Expandible OS v2.0 (1984)(Intelligent Software).bin"};
|
||||
*this = Description(name, "Enterprise", "the Enterprise EXOS ROM v2.0", filenames, 32 * 1024, 0xd421795fu);
|
||||
} break;
|
||||
case Name::EnterpriseEXOS21: {
|
||||
const std::initializer_list<std::string> filenames = {"exos21.bin", "Expandible OS v2.1 (1985)(Intelligent Software).bin"};
|
||||
*this = Description(name, "Enterprise", "the Enterprise EXOS ROM v2.1", filenames, 32 * 1024, 0x982a3b44u);
|
||||
} break;
|
||||
case Name::EnterpriseEXOS23: {
|
||||
const std::initializer_list<std::string> filenames = {"exos23.bin", "Expandible OS v2.3 (1987)(Intelligent Software).bin"};
|
||||
*this = Description(name, "Enterprise", "the Enterprise EXOS ROM v2.1", filenames, 64 * 1024, 0x24838410u);
|
||||
} break;
|
||||
|
||||
case Name::EnterpriseBASIC10: {
|
||||
const std::initializer_list<std::string> filenames = {"basic10.bin"};
|
||||
*this = Description(name, "Enterprise", "the Enterprise BASIC ROM v1.0", filenames, 16 * 1024, 0xd62e4fb7u);
|
||||
} break;
|
||||
case Name::EnterpriseBASIC10Part1: {
|
||||
const std::initializer_list<std::string> filenames = {"BASIC 1.0 - EPROM 1-2 (198x)(Enterprise).bin"};
|
||||
*this = Description(name, "Enterprise", "the Enterprise BASIC ROM v1.0, Part 1", filenames, 8193, 0x37bf48e1u);
|
||||
} break;
|
||||
case Name::EnterpriseBASIC10Part2: {
|
||||
const std::initializer_list<std::string> filenames = {"BASIC 1.0 - EPROM 2-2 (198x)(Enterprise).bin"};
|
||||
*this = Description(name, "Enterprise", "the Enterprise BASIC ROM v1.0, Part 2", filenames, 8193, 0xc5298c79u);
|
||||
} break;
|
||||
case Name::EnterpriseBASIC11: {
|
||||
const std::initializer_list<std::string> filenames = {"basic11.bin"};
|
||||
*this = Description(name, "Enterprise", "the Enterprise BASIC ROM v1.1", filenames, 16 * 1024, 0x683cf455u);
|
||||
} break;
|
||||
case Name::EnterpriseBASIC11Suffixed: {
|
||||
const std::initializer_list<std::string> filenames = {"BASIC 1.1 - EPROM 1.1 (198x)(Enterprise).bin"};
|
||||
*this = Description(name, "Enterprise", "the Enterprise BASIC ROM v1.1, with trailing byte", filenames, 16385, 0xc96b7602u);
|
||||
} break;
|
||||
case Name::EnterpriseBASIC21: {
|
||||
const std::initializer_list<std::string> filenames = {
|
||||
"basic21.bin",
|
||||
"BASIC Interpreter v2.1 (1985)(Intelligent Software).bin",
|
||||
"BASIC Interpreter v2.1 (1985)(Intelligent Software)[a].bin"
|
||||
};
|
||||
const std::initializer_list<uint32_t> crcs = { 0x55f96251, 0x683cf455 };
|
||||
*this = Description(name, "Enterprise", "the Enterprise BASIC ROM v2.1", filenames, 16 * 1024, crcs);
|
||||
} break;
|
||||
|
||||
case Name::EnterpriseEPDOS: {
|
||||
const std::initializer_list<std::string> filenames = {"epdos.bin", "EPDOS v1.7 (19xx)(Haluska, Laszlo).bin"};
|
||||
*this = Description(name, "Enterprise", "the Enterprise EPDOS ROM", filenames, 32 * 1024, 0x201319ebu);
|
||||
} break;
|
||||
case Name::EnterpriseEXDOS: {
|
||||
const std::initializer_list<std::string> filenames = {"exdos.bin", "EX-DOS EPROM (198x)(Enterprise).bin"};
|
||||
*this = Description(name, "Enterprise", "the Enterprise EXDOS ROM", filenames, 16 * 1024, 0xe6daa0e9u);
|
||||
} break;
|
||||
|
||||
case Name::Macintosh128k: *this = Description(name, "Macintosh", "the Macintosh 128k ROM", "mac128k.rom", 64*1024, 0x6d0c8a28u); break;
|
||||
case Name::Macintosh512k: *this = Description(name, "Macintosh", "the Macintosh 512k ROM", "mac512k.rom", 64*1024, 0xcf759e0d); break;
|
||||
case Name::MacintoshPlus: {
|
||||
|
||||
@@ -71,6 +71,22 @@ enum Name {
|
||||
DiskIIStateMachine13Sector,
|
||||
DiskIIBoot13Sector,
|
||||
|
||||
// Enterprise.
|
||||
EnterpriseEXOS10,
|
||||
EnterpriseEXOS20,
|
||||
EnterpriseEXOS21,
|
||||
EnterpriseEXOS23,
|
||||
|
||||
EnterpriseBASIC10,
|
||||
EnterpriseBASIC10Part1,
|
||||
EnterpriseBASIC10Part2,
|
||||
EnterpriseBASIC11,
|
||||
EnterpriseBASIC11Suffixed,
|
||||
EnterpriseBASIC21,
|
||||
|
||||
EnterpriseEPDOS,
|
||||
EnterpriseEXDOS,
|
||||
|
||||
// Macintosh.
|
||||
Macintosh128k,
|
||||
Macintosh512k,
|
||||
@@ -237,6 +253,10 @@ struct Request {
|
||||
bool is_optional = false;
|
||||
std::vector<Node> children;
|
||||
|
||||
bool empty() const {
|
||||
return type == Type::One && name == Name::None;
|
||||
}
|
||||
|
||||
void add_descriptions(std::vector<Description> &) const;
|
||||
bool validate(Map &) const;
|
||||
void visit(
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "Sizes.hpp"
|
||||
|
||||
namespace Numeric {
|
||||
|
||||
template <typename IntType> struct LSFRPolynomial {};
|
||||
@@ -67,8 +69,8 @@ template <typename IntType = uint64_t, IntType polynomial = LSFRPolynomial<IntTy
|
||||
determining the bit that was just shifted out.
|
||||
*/
|
||||
IntType next() {
|
||||
const auto result = value_ & 1;
|
||||
value_ = (value_ >> 1) ^ (result * polynomial);
|
||||
const auto result = IntType(value_ & 1);
|
||||
value_ = IntType((value_ >> 1) ^ (result * polynomial));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -76,6 +78,8 @@ template <typename IntType = uint64_t, IntType polynomial = LSFRPolynomial<IntTy
|
||||
IntType value_ = 0;
|
||||
};
|
||||
|
||||
template <uint64_t polynomial> class LFSRv: public LFSR<typename MinIntTypeValue<polynomial>::type, polynomial> {};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LFSR_h */
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#define Sizes_h
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
/*!
|
||||
Maps to the smallest integral type that can contain max_value, from the following options:
|
||||
@@ -17,6 +17,17 @@
|
||||
4B051C93266D9D6900CA44E8 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; };
|
||||
4B051C95266EF50200CA44E8 /* AppleIIOptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C94266EF50200CA44E8 /* AppleIIOptionsPanel.swift */; };
|
||||
4B051C97266EF5F600CA44E8 /* CSAppleII.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C96266EF5F600CA44E8 /* CSAppleII.mm */; };
|
||||
4B051CA22676F52200CA44E8 /* Enterprise.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051CA12676F52200CA44E8 /* Enterprise.cpp */; };
|
||||
4B051CA32676F52200CA44E8 /* Enterprise.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051CA12676F52200CA44E8 /* Enterprise.cpp */; };
|
||||
4B051CA826781D6500CA44E8 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051CA726781D6500CA44E8 /* StaticAnalyser.cpp */; };
|
||||
4B051CA926781D6500CA44E8 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051CA726781D6500CA44E8 /* StaticAnalyser.cpp */; };
|
||||
4B051CAC26783E2000CA44E8 /* Nick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051CAA26783E2000CA44E8 /* Nick.cpp */; };
|
||||
4B051CAD26783E2000CA44E8 /* Nick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051CAA26783E2000CA44E8 /* Nick.cpp */; };
|
||||
4B051CB0267C1CA200CA44E8 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051CAE267C1CA200CA44E8 /* Keyboard.cpp */; };
|
||||
4B051CB1267C1CA200CA44E8 /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051CAE267C1CA200CA44E8 /* Keyboard.cpp */; };
|
||||
4B051CB3267D3FF800CA44E8 /* EnterpriseNickTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B051CB2267D3FF800CA44E8 /* EnterpriseNickTests.mm */; };
|
||||
4B051CB62680158600CA44E8 /* EXDos.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051CB42680158600CA44E8 /* EXDos.cpp */; };
|
||||
4B051CB72680158600CA44E8 /* EXDos.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051CB42680158600CA44E8 /* EXDos.cpp */; };
|
||||
4B05401E219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; };
|
||||
4B05401F219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; };
|
||||
4B055A7A1FAE78A00060FFFF /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B055A771FAE78210060FFFF /* SDL2.framework */; };
|
||||
@@ -230,6 +241,8 @@
|
||||
4B4518A31F75FD1C00926311 /* HFE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518951F75FD1B00926311 /* HFE.cpp */; };
|
||||
4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */; };
|
||||
4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518991F75FD1B00926311 /* SSD.cpp */; };
|
||||
4B47770B268FBE4D005C2340 /* FAT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B477709268FBE4D005C2340 /* FAT.cpp */; };
|
||||
4B47770D26900685005C2340 /* EnterpriseDaveTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B47770C26900685005C2340 /* EnterpriseDaveTests.mm */; };
|
||||
4B47F6C5241C87A100ED06F7 /* Struct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F6C4241C87A100ED06F7 /* Struct.cpp */; };
|
||||
4B47F6C6241C87A100ED06F7 /* Struct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F6C4241C87A100ED06F7 /* Struct.cpp */; };
|
||||
4B49F0A923346F7A0045E6A6 /* MacintoshOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B49F0A723346F7A0045E6A6 /* MacintoshOptions.xib */; };
|
||||
@@ -312,7 +325,7 @@
|
||||
4B778F0023A5EB990000D260 /* G64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518931F75FD1B00926311 /* G64.cpp */; };
|
||||
4B778F0123A5EBA00000D260 /* MacintoshIMG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB4BFAE22A42F290069048D /* MacintoshIMG.cpp */; };
|
||||
4B778F0223A5EBA40000D260 /* MFMSectorDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B58601C1F806AB200AEE2E3 /* MFMSectorDump.cpp */; };
|
||||
4B778F0323A5EBB00000D260 /* MSXDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */; };
|
||||
4B778F0323A5EBB00000D260 /* FAT12.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4B2002C4BF000708CC /* FAT12.cpp */; };
|
||||
4B778F0423A5EBB00000D260 /* OricMFMDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */; };
|
||||
4B778F0523A5EBB00000D260 /* ST.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE0A3EC237BB170002AB46F /* ST.cpp */; };
|
||||
4B778F0623A5EC150000D260 /* CAS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E04E81FC9E5DA00F43484 /* CAS.cpp */; };
|
||||
@@ -950,8 +963,8 @@
|
||||
4BE9A6B11EDE293000CBCB47 /* zexdoc.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BE9A6B01EDE293000CBCB47 /* zexdoc.com */; };
|
||||
4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA525D1DF33323007E74F2 /* Tape.cpp */; };
|
||||
4BEA52631DF339D7007E74F2 /* SoundGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA52611DF339D7007E74F2 /* SoundGenerator.cpp */; };
|
||||
4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */; };
|
||||
4BEBFB4E2002C4BF000708CC /* MSXDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */; };
|
||||
4BEBFB4D2002C4BF000708CC /* FAT12.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4B2002C4BF000708CC /* FAT12.cpp */; };
|
||||
4BEBFB4E2002C4BF000708CC /* FAT12.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4B2002C4BF000708CC /* FAT12.cpp */; };
|
||||
4BEBFB512002DB30000708CC /* DiskROM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4F2002DB30000708CC /* DiskROM.cpp */; };
|
||||
4BEBFB522002DB30000708CC /* DiskROM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEBFB4F2002DB30000708CC /* DiskROM.cpp */; };
|
||||
4BEDA3BA25B25563000C2DBD /* Decoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEDA3B425B25563000C2DBD /* Decoder.cpp */; };
|
||||
@@ -987,6 +1000,8 @@
|
||||
4BFCA1291ECBE7A700AC40C1 /* zexall.com in Resources */ = {isa = PBXBuildFile; fileRef = 4BFCA1281ECBE7A700AC40C1 /* zexall.com */; };
|
||||
4BFCA12B1ECBE7C400AC40C1 /* ZexallTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFCA12A1ECBE7C400AC40C1 /* ZexallTests.swift */; };
|
||||
4BFDD78C1F7F2DB4008579B9 /* ImplicitSectors.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFDD78B1F7F2DB4008579B9 /* ImplicitSectors.cpp */; };
|
||||
4BFEA2EF2682A7B900EBF94C /* Dave.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFEA2ED2682A7B900EBF94C /* Dave.cpp */; };
|
||||
4BFEA2F02682A7B900EBF94C /* Dave.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFEA2ED2682A7B900EBF94C /* Dave.cpp */; };
|
||||
4BFF1D3922337B0300838EA1 /* 68000Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFF1D3822337B0300838EA1 /* 68000Storage.cpp */; };
|
||||
4BFF1D3A22337B0300838EA1 /* 68000Storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFF1D3822337B0300838EA1 /* 68000Storage.cpp */; };
|
||||
4BFF1D3D2235C3C100838EA1 /* EmuTOSTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BFF1D3C2235C3C100838EA1 /* EmuTOSTests.mm */; };
|
||||
@@ -1028,6 +1043,18 @@
|
||||
4B051C94266EF50200CA44E8 /* AppleIIOptionsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleIIOptionsPanel.swift; sourceTree = "<group>"; };
|
||||
4B051C96266EF5F600CA44E8 /* CSAppleII.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CSAppleII.mm; sourceTree = "<group>"; };
|
||||
4B051C98266EF60500CA44E8 /* CSAppleII.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSAppleII.h; sourceTree = "<group>"; };
|
||||
4B051CA02676F52200CA44E8 /* Enterprise.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Enterprise.hpp; sourceTree = "<group>"; };
|
||||
4B051CA12676F52200CA44E8 /* Enterprise.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Enterprise.cpp; sourceTree = "<group>"; };
|
||||
4B051CA526781D6500CA44E8 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
|
||||
4B051CA626781D6500CA44E8 /* Target.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||
4B051CA726781D6500CA44E8 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
|
||||
4B051CAA26783E2000CA44E8 /* Nick.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Nick.cpp; sourceTree = "<group>"; };
|
||||
4B051CAB26783E2000CA44E8 /* Nick.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Nick.hpp; sourceTree = "<group>"; };
|
||||
4B051CAE267C1CA200CA44E8 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = "<group>"; };
|
||||
4B051CAF267C1CA200CA44E8 /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
|
||||
4B051CB2267D3FF800CA44E8 /* EnterpriseNickTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EnterpriseNickTests.mm; sourceTree = "<group>"; };
|
||||
4B051CB42680158600CA44E8 /* EXDos.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = EXDos.cpp; sourceTree = "<group>"; };
|
||||
4B051CB52680158600CA44E8 /* EXDos.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = EXDos.hpp; sourceTree = "<group>"; };
|
||||
4B05401D219D1618001BF69C /* ScanTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTarget.cpp; sourceTree = "<group>"; };
|
||||
4B055A6A1FAE763F0060FFFF /* Clock Signal Kiosk */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "Clock Signal Kiosk"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4B055A771FAE78210060FFFF /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../Library/Frameworks/SDL2.framework; sourceTree = SOURCE_ROOT; };
|
||||
@@ -1251,6 +1278,9 @@
|
||||
4B45189A1F75FD1B00926311 /* SSD.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SSD.hpp; sourceTree = "<group>"; };
|
||||
4B4518A71F76004200926311 /* TapeParser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TapeParser.hpp; path = Parsers/TapeParser.hpp; sourceTree = "<group>"; };
|
||||
4B4518A81F76022000926311 /* DiskImageImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskImageImplementation.hpp; sourceTree = "<group>"; };
|
||||
4B477709268FBE4D005C2340 /* FAT.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FAT.cpp; path = Parsers/FAT.cpp; sourceTree = "<group>"; };
|
||||
4B47770A268FBE4D005C2340 /* FAT.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = FAT.hpp; path = Parsers/FAT.hpp; sourceTree = "<group>"; };
|
||||
4B47770C26900685005C2340 /* EnterpriseDaveTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EnterpriseDaveTests.mm; sourceTree = "<group>"; };
|
||||
4B47F6C4241C87A100ED06F7 /* Struct.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Struct.cpp; sourceTree = "<group>"; };
|
||||
4B49F0A823346F7A0045E6A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/MacintoshOptions.xib"; sourceTree = SOURCE_ROOT; };
|
||||
4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AY38910.cpp; sourceTree = "<group>"; };
|
||||
@@ -1998,8 +2028,8 @@
|
||||
4BEA52601DF3343A007E74F2 /* Interrupts.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Interrupts.hpp; sourceTree = "<group>"; };
|
||||
4BEA52611DF339D7007E74F2 /* SoundGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SoundGenerator.cpp; sourceTree = "<group>"; };
|
||||
4BEA52621DF339D7007E74F2 /* SoundGenerator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SoundGenerator.hpp; sourceTree = "<group>"; };
|
||||
4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MSXDSK.cpp; sourceTree = "<group>"; };
|
||||
4BEBFB4C2002C4BF000708CC /* MSXDSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MSXDSK.hpp; sourceTree = "<group>"; };
|
||||
4BEBFB4B2002C4BF000708CC /* FAT12.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FAT12.cpp; sourceTree = "<group>"; };
|
||||
4BEBFB4C2002C4BF000708CC /* FAT12.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = FAT12.hpp; sourceTree = "<group>"; };
|
||||
4BEBFB4F2002DB30000708CC /* DiskROM.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DiskROM.cpp; sourceTree = "<group>"; };
|
||||
4BEBFB502002DB30000708CC /* DiskROM.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskROM.hpp; sourceTree = "<group>"; };
|
||||
4BEDA3B425B25563000C2DBD /* Decoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Decoder.cpp; sourceTree = "<group>"; };
|
||||
@@ -2049,6 +2079,9 @@
|
||||
4BFDD78A1F7F2DB4008579B9 /* ImplicitSectors.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ImplicitSectors.hpp; sourceTree = "<group>"; };
|
||||
4BFDD78B1F7F2DB4008579B9 /* ImplicitSectors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImplicitSectors.cpp; sourceTree = "<group>"; };
|
||||
4BFE7B861FC39BF100160B38 /* StandardOptions.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = StandardOptions.hpp; sourceTree = "<group>"; };
|
||||
4BFEA2ED2682A7B900EBF94C /* Dave.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Dave.cpp; sourceTree = "<group>"; };
|
||||
4BFEA2EE2682A7B900EBF94C /* Dave.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Dave.hpp; sourceTree = "<group>"; };
|
||||
4BFEA2F12682A90200EBF94C /* Sizes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Sizes.hpp; sourceTree = "<group>"; };
|
||||
4BFF1D342233778C00838EA1 /* 68000.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 68000.hpp; sourceTree = "<group>"; };
|
||||
4BFF1D37223379D500838EA1 /* 68000Storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 68000Storage.hpp; sourceTree = "<group>"; };
|
||||
4BFF1D3822337B0300838EA1 /* 68000Storage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = 68000Storage.cpp; sourceTree = "<group>"; };
|
||||
@@ -2090,6 +2123,33 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
4B051C9F2676F52200CA44E8 /* Enterprise */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BFEA2ED2682A7B900EBF94C /* Dave.cpp */,
|
||||
4B051CA12676F52200CA44E8 /* Enterprise.cpp */,
|
||||
4B051CB42680158600CA44E8 /* EXDos.cpp */,
|
||||
4B051CAE267C1CA200CA44E8 /* Keyboard.cpp */,
|
||||
4B051CAA26783E2000CA44E8 /* Nick.cpp */,
|
||||
4BFEA2EE2682A7B900EBF94C /* Dave.hpp */,
|
||||
4B051CA02676F52200CA44E8 /* Enterprise.hpp */,
|
||||
4B051CB52680158600CA44E8 /* EXDos.hpp */,
|
||||
4B051CAF267C1CA200CA44E8 /* Keyboard.hpp */,
|
||||
4B051CAB26783E2000CA44E8 /* Nick.hpp */,
|
||||
);
|
||||
path = Enterprise;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B051CA426781D6500CA44E8 /* Enterprise */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B051CA726781D6500CA44E8 /* StaticAnalyser.cpp */,
|
||||
4B051CA526781D6500CA44E8 /* StaticAnalyser.hpp */,
|
||||
4B051CA626781D6500CA44E8 /* Target.hpp */,
|
||||
);
|
||||
path = Enterprise;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B055A761FAE78210060FFFF /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2602,7 +2662,9 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B3FE75C1F3CF68B00448EE4 /* CPM.cpp */,
|
||||
4B477709268FBE4D005C2340 /* FAT.cpp */,
|
||||
4B3FE75D1F3CF68B00448EE4 /* CPM.hpp */,
|
||||
4B47770A268FBE4D005C2340 /* FAT.hpp */,
|
||||
);
|
||||
name = Parsers;
|
||||
sourceTree = "<group>";
|
||||
@@ -2655,34 +2717,36 @@
|
||||
4B45188C1F75FD1B00926311 /* Formats */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B80CD74256CA15E00176FCC /* 2MG.cpp */,
|
||||
4B45188D1F75FD1B00926311 /* AcornADF.cpp */,
|
||||
4B0333AD2094081A0050B93D /* AppleDSK.cpp */,
|
||||
4B45188F1F75FD1B00926311 /* CPCDSK.cpp */,
|
||||
4B4518911F75FD1B00926311 /* D64.cpp */,
|
||||
4BAF2B4C2004580C00480230 /* DMK.cpp */,
|
||||
4BEBFB4B2002C4BF000708CC /* FAT12.cpp */,
|
||||
4B4518931F75FD1B00926311 /* G64.cpp */,
|
||||
4B4518951F75FD1B00926311 /* HFE.cpp */,
|
||||
4BB4BFAE22A42F290069048D /* MacintoshIMG.cpp */,
|
||||
4B58601C1F806AB200AEE2E3 /* MFMSectorDump.cpp */,
|
||||
4BC131782346DF2B00E4FF3D /* MSA.cpp */,
|
||||
4BEBFB4B2002C4BF000708CC /* MSXDSK.cpp */,
|
||||
4B0F94FC208C1A1600FE41D9 /* NIB.cpp */,
|
||||
4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */,
|
||||
4B4518991F75FD1B00926311 /* SSD.cpp */,
|
||||
4BE0A3EC237BB170002AB46F /* ST.cpp */,
|
||||
4B7BA03323C58B1E00B98D9E /* STX.cpp */,
|
||||
4B6ED2EE208E2F8A0047B343 /* WOZ.cpp */,
|
||||
4B80CD75256CA15E00176FCC /* 2MG.hpp */,
|
||||
4B45188E1F75FD1B00926311 /* AcornADF.hpp */,
|
||||
4B0333AE2094081A0050B93D /* AppleDSK.hpp */,
|
||||
4B4518901F75FD1B00926311 /* CPCDSK.hpp */,
|
||||
4B4518921F75FD1B00926311 /* D64.hpp */,
|
||||
4BAF2B4D2004580C00480230 /* DMK.hpp */,
|
||||
4BEBFB4C2002C4BF000708CC /* FAT12.hpp */,
|
||||
4B4518941F75FD1B00926311 /* G64.hpp */,
|
||||
4B4518961F75FD1B00926311 /* HFE.hpp */,
|
||||
4BB4BFAF22A42F290069048D /* MacintoshIMG.hpp */,
|
||||
4B58601D1F806AB200AEE2E3 /* MFMSectorDump.hpp */,
|
||||
4BC131792346DF2B00E4FF3D /* MSA.hpp */,
|
||||
4BEBFB4C2002C4BF000708CC /* MSXDSK.hpp */,
|
||||
4B0F94FD208C1A1600FE41D9 /* NIB.hpp */,
|
||||
4B4518981F75FD1B00926311 /* OricMFMDSK.hpp */,
|
||||
4B45189A1F75FD1B00926311 /* SSD.hpp */,
|
||||
@@ -2690,8 +2754,6 @@
|
||||
4B7BA03223C58B1E00B98D9E /* STX.hpp */,
|
||||
4B6ED2EF208E2F8A0047B343 /* WOZ.hpp */,
|
||||
4BFDD7891F7F2DB4008579B9 /* Utility */,
|
||||
4B80CD74256CA15E00176FCC /* 2MG.cpp */,
|
||||
4B80CD75256CA15E00176FCC /* 2MG.hpp */,
|
||||
);
|
||||
path = Formats;
|
||||
sourceTree = "<group>";
|
||||
@@ -3048,6 +3110,7 @@
|
||||
children = (
|
||||
4B7BA03E23D55E7900B98D9E /* CRC.hpp */,
|
||||
4B7BA03F23D55E7900B98D9E /* LFSR.hpp */,
|
||||
4BFEA2F12682A90200EBF94C /* Sizes.hpp */,
|
||||
);
|
||||
name = Numeric;
|
||||
path = ../../Numeric;
|
||||
@@ -3185,6 +3248,7 @@
|
||||
4B8944FB201967B4007DE474 /* Commodore */,
|
||||
4B894507201967B4007DE474 /* Disassembler */,
|
||||
4BD67DC8209BE4D600AB2146 /* DiskII */,
|
||||
4B051CA426781D6500CA44E8 /* Enterprise */,
|
||||
4BB4BFB622A4372E0069048D /* Macintosh */,
|
||||
4B89450F201967B4007DE474 /* MSX */,
|
||||
4B8944F6201967B4007DE474 /* Oric */,
|
||||
@@ -3990,12 +4054,8 @@
|
||||
4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */,
|
||||
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
|
||||
4BC751B11D157E61006C31D9 /* 6522Tests.swift */,
|
||||
4B1E85801D176468001EF87D /* 6532Tests.swift */,
|
||||
4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */,
|
||||
4B8DF5132550D62900F3433C /* 65816kromTests.swift */,
|
||||
4B85322922778E4200F26553 /* Comparative68000.hpp */,
|
||||
4B90467222C6FA31000E2074 /* TestRunner68000.hpp */,
|
||||
4B90467522C6FD6E000E2074 /* 68000ArithmeticTests.mm */,
|
||||
4B9D0C4A22C7D70900DE1AD3 /* 68000BCDTests.mm */,
|
||||
4B90467322C6FADD000E2074 /* 68000BitwiseTests.mm */,
|
||||
@@ -4004,43 +4064,49 @@
|
||||
4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */,
|
||||
4B9D0C4E22C7E0CF00DE1AD3 /* 68000RollShiftTests.mm */,
|
||||
4BD388872239E198002D14B5 /* 68000Tests.mm */,
|
||||
4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */,
|
||||
4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */,
|
||||
4BE34437238389E10058E78F /* AtariSTVideoTests.mm */,
|
||||
4B049CDC1DA3C82F00322067 /* BCDTest.swift */,
|
||||
4B3BA0C41D318B44005DD7A7 /* Bridges */,
|
||||
4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */,
|
||||
4B85322922778E4200F26553 /* Comparative68000.hpp */,
|
||||
4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */,
|
||||
4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */,
|
||||
4BFF1D3C2235C3C100838EA1 /* EmuTOSTests.mm */,
|
||||
4BBF49AE1ED2880200AB3669 /* FUSETests.swift */,
|
||||
4B47770C26900685005C2340 /* EnterpriseDaveTests.mm */,
|
||||
4B051CB2267D3FF800CA44E8 /* EnterpriseNickTests.mm */,
|
||||
4B8DF4D725465B7500F3433C /* IIgsMemoryMapTests.mm */,
|
||||
4BB73EB81B587A5100552FC2 /* Info.plist */,
|
||||
4B4F477B253530B7004245B8 /* Jeek816Tests.swift */,
|
||||
4B1414611B58888700E04248 /* KlausDormannTests.swift */,
|
||||
4BEE1EBF22B5E236000A26A6 /* MacGCRTests.mm */,
|
||||
4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */,
|
||||
4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */,
|
||||
4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */,
|
||||
4BC0CB272446BC7B00A79DBB /* OPLTests.mm */,
|
||||
4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */,
|
||||
4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */,
|
||||
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */,
|
||||
4B3F76B825A1635300178AEC /* PowerPCDecoderTests.mm */,
|
||||
4BE76CF822641ED300ACD6FA /* QLTests.mm */,
|
||||
4B8DD3672633B2D400B3C866 /* SpectrumVideoContentionTests.mm */,
|
||||
4B1414631B588A1100E04248 /* Test Binaries */,
|
||||
4B90467222C6FA31000E2074 /* TestRunner68000.hpp */,
|
||||
4B2AF8681E513FC20027EE29 /* TIATests.mm */,
|
||||
4B1D08051E0F7A1100763741 /* TimeTests.mm */,
|
||||
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
|
||||
4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */,
|
||||
4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */,
|
||||
4BB73EB81B587A5100552FC2 /* Info.plist */,
|
||||
4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */,
|
||||
4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */,
|
||||
4BC751B11D157E61006C31D9 /* 6522Tests.swift */,
|
||||
4B1E85801D176468001EF87D /* 6532Tests.swift */,
|
||||
4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */,
|
||||
4B8DF5132550D62900F3433C /* 65816kromTests.swift */,
|
||||
4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */,
|
||||
4B049CDC1DA3C82F00322067 /* BCDTest.swift */,
|
||||
4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */,
|
||||
4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */,
|
||||
4BBF49AE1ED2880200AB3669 /* FUSETests.swift */,
|
||||
4B4F477B253530B7004245B8 /* Jeek816Tests.swift */,
|
||||
4B1414611B58888700E04248 /* KlausDormannTests.swift */,
|
||||
4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */,
|
||||
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */,
|
||||
4B08A2741EE35D56008B7065 /* Z80InterruptTests.swift */,
|
||||
4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */,
|
||||
4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */,
|
||||
4BFCA12A1ECBE7C400AC40C1 /* ZexallTests.swift */,
|
||||
4B3BA0C41D318B44005DD7A7 /* Bridges */,
|
||||
4B1414631B588A1100E04248 /* Test Binaries */,
|
||||
);
|
||||
path = "Clock SignalTests";
|
||||
sourceTree = "<group>";
|
||||
@@ -4075,6 +4141,7 @@
|
||||
4B7A90E22041097C008514A2 /* ColecoVision */,
|
||||
4B4DC81D1D2C2425003C5BF8 /* Commodore */,
|
||||
4B2E2D9E1C3A070900138695 /* Electron */,
|
||||
4B051C9F2676F52200CA44E8 /* Enterprise */,
|
||||
4B7F188B2154825D00388727 /* MasterSystem */,
|
||||
4B79A4FC1FC8FF9800EEDAD5 /* MSX */,
|
||||
4BCF1FA51DADC3E10039D2E7 /* Oric */,
|
||||
@@ -5177,6 +5244,7 @@
|
||||
4B055AB41FAE860F0060FFFF /* OricTAP.cpp in Sources */,
|
||||
4B055AB71FAE860F0060FFFF /* TZX.cpp in Sources */,
|
||||
4B2BF19123DCC6A200C3AD60 /* BD500.cpp in Sources */,
|
||||
4B051CA926781D6500CA44E8 /* StaticAnalyser.cpp in Sources */,
|
||||
4B055ADA1FAE9B460060FFFF /* 1770.cpp in Sources */,
|
||||
4B80CD77256CA16600176FCC /* 2MG.cpp in Sources */,
|
||||
4B0F1BE62602FF9D00B85C66 /* ZX8081.cpp in Sources */,
|
||||
@@ -5186,6 +5254,7 @@
|
||||
4B055AB61FAE860F0060FFFF /* TapeUEF.cpp in Sources */,
|
||||
4B055A9D1FAE85DA0060FFFF /* D64.cpp in Sources */,
|
||||
4B0ACC2B23775819008902D0 /* Video.cpp in Sources */,
|
||||
4B051CA32676F52200CA44E8 /* Enterprise.cpp in Sources */,
|
||||
4B055ABB1FAE86170060FFFF /* Oric.cpp in Sources */,
|
||||
4B12C0EE1FCFAD1A005BFD93 /* Keyboard.cpp in Sources */,
|
||||
4BCD634A22D6756400F567F1 /* MacintoshDoubleDensityDrive.cpp in Sources */,
|
||||
@@ -5214,6 +5283,7 @@
|
||||
4BB307BC235001C300457D33 /* 6850.cpp in Sources */,
|
||||
4B055AB31FAE860F0060FFFF /* CSW.cpp in Sources */,
|
||||
4B89451D201967B4007DE474 /* Disk.cpp in Sources */,
|
||||
4BFEA2F02682A7B900EBF94C /* Dave.cpp in Sources */,
|
||||
4BDACBED22FFA5D20045EF7E /* ncr5380.cpp in Sources */,
|
||||
4BC131772346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */,
|
||||
4B055ACF1FAE9B030060FFFF /* SoundGenerator.cpp in Sources */,
|
||||
@@ -5230,7 +5300,7 @@
|
||||
4BB4BFBA22A4372F0069048D /* StaticAnalyser.cpp in Sources */,
|
||||
4B055AA21FAE85DA0060FFFF /* SSD.cpp in Sources */,
|
||||
4B2E86E325DC95150024F1E9 /* Joystick.cpp in Sources */,
|
||||
4BEBFB4E2002C4BF000708CC /* MSXDSK.cpp in Sources */,
|
||||
4BEBFB4E2002C4BF000708CC /* FAT12.cpp in Sources */,
|
||||
4B055ADD1FAE9B460060FFFF /* i8272.cpp in Sources */,
|
||||
4B055A9C1FAE85DA0060FFFF /* CPCDSK.cpp in Sources */,
|
||||
4B2BF19223DCC6A800C3AD60 /* STX.cpp in Sources */,
|
||||
@@ -5248,6 +5318,7 @@
|
||||
4BC131712346DE5000E4FF3D /* StaticAnalyser.cpp in Sources */,
|
||||
4B055ACA1FAE9AFB0060FFFF /* Vic20.cpp in Sources */,
|
||||
4BE21200253FC80900435408 /* StaticAnalyser.cpp in Sources */,
|
||||
4B051CB72680158600CA44E8 /* EXDos.cpp in Sources */,
|
||||
4B8318B222D3E53C006DB630 /* Video.cpp in Sources */,
|
||||
4B055ABC1FAE86170060FFFF /* ZX8081.cpp in Sources */,
|
||||
4B055AC91FAE9AFB0060FFFF /* Keyboard.cpp in Sources */,
|
||||
@@ -5292,6 +5363,7 @@
|
||||
4B055AAD1FAE85FD0060FFFF /* PCMTrack.cpp in Sources */,
|
||||
4BD67DD1209BF27B00AB2146 /* Encoder.cpp in Sources */,
|
||||
4BE2121A253FCE9C00435408 /* AppleIIgs.cpp in Sources */,
|
||||
4B051CAD26783E2000CA44E8 /* Nick.cpp in Sources */,
|
||||
4B89451F201967B4007DE474 /* Tape.cpp in Sources */,
|
||||
4B055AA81FAE85EF0060FFFF /* Shifter.cpp in Sources */,
|
||||
4BEDA3BC25B25563000C2DBD /* Decoder.cpp in Sources */,
|
||||
@@ -5338,6 +5410,7 @@
|
||||
4B051C912669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */,
|
||||
4B055AB91FAE86170060FFFF /* Acorn.cpp in Sources */,
|
||||
4B302185208A550100773308 /* DiskII.cpp in Sources */,
|
||||
4B051CB1267C1CA200CA44E8 /* Keyboard.cpp in Sources */,
|
||||
4B0F1BB32602645900B85C66 /* StaticAnalyser.cpp in Sources */,
|
||||
4B055A931FAE85B50060FFFF /* BinaryDump.cpp in Sources */,
|
||||
4B89452D201967B4007DE474 /* Tape.cpp in Sources */,
|
||||
@@ -5461,6 +5534,7 @@
|
||||
4B7BA03023C2B19C00B98D9E /* Jasmin.cpp in Sources */,
|
||||
4B7136911F789C93008B8ED9 /* SegmentParser.cpp in Sources */,
|
||||
4BEDA3BA25B25563000C2DBD /* Decoder.cpp in Sources */,
|
||||
4BFEA2EF2682A7B900EBF94C /* Dave.cpp in Sources */,
|
||||
4B4518A21F75FD1C00926311 /* G64.cpp in Sources */,
|
||||
4B89452C201967B4007DE474 /* Tape.cpp in Sources */,
|
||||
4B448E811F1C45A00009ABD6 /* TZX.cpp in Sources */,
|
||||
@@ -5492,11 +5566,13 @@
|
||||
4B12C0ED1FCFA98D005BFD93 /* Keyboard.cpp in Sources */,
|
||||
4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */,
|
||||
4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */,
|
||||
4B051CA22676F52200CA44E8 /* Enterprise.cpp in Sources */,
|
||||
4B7F1897215486A200388727 /* StaticAnalyser.cpp in Sources */,
|
||||
4B47F6C5241C87A100ED06F7 /* Struct.cpp in Sources */,
|
||||
4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */,
|
||||
4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */,
|
||||
4B622AE5222E0AD5008B59F2 /* DisplayMetrics.cpp in Sources */,
|
||||
4B051CB0267C1CA200CA44E8 /* Keyboard.cpp in Sources */,
|
||||
4B1497881EE4A1DA00CE2596 /* ZX80O81P.cpp in Sources */,
|
||||
4B894520201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||
4BB4BFAD22A33DE50069048D /* DriveSpeedAccumulator.cpp in Sources */,
|
||||
@@ -5505,11 +5581,13 @@
|
||||
4B74CF812312FA9C00500CE8 /* HFV.cpp in Sources */,
|
||||
4B17B58B20A8A9D9007CCA8F /* StringSerialiser.cpp in Sources */,
|
||||
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */,
|
||||
4B051CA826781D6500CA44E8 /* StaticAnalyser.cpp in Sources */,
|
||||
4B3940E71DA83C8300427841 /* AsyncTaskQueue.cpp in Sources */,
|
||||
4B0E04FA1FC9FA3100F43484 /* 9918.cpp in Sources */,
|
||||
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */,
|
||||
4B4518841F75E91A00926311 /* UnformattedTrack.cpp in Sources */,
|
||||
4B65086022F4CF8D009C1100 /* Keyboard.cpp in Sources */,
|
||||
4B47770B268FBE4D005C2340 /* FAT.cpp in Sources */,
|
||||
4B894528201967B4007DE474 /* Disk.cpp in Sources */,
|
||||
4B2E86CF25D8D8C70024F1E9 /* Keyboard.cpp in Sources */,
|
||||
4BBB70A4202011C2002FE009 /* MultiMediaTarget.cpp in Sources */,
|
||||
@@ -5538,7 +5616,7 @@
|
||||
4B894536201967B4007DE474 /* Z80.cpp in Sources */,
|
||||
4BCA6CC81D9DD9F000C2D7B2 /* CommodoreROM.cpp in Sources */,
|
||||
4BC1317A2346DF2B00E4FF3D /* MSA.cpp in Sources */,
|
||||
4BEBFB4D2002C4BF000708CC /* MSXDSK.cpp in Sources */,
|
||||
4BEBFB4D2002C4BF000708CC /* FAT12.cpp in Sources */,
|
||||
4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */,
|
||||
4B83348A1F5DB94B0097E338 /* IRQDelegatePortHandler.cpp in Sources */,
|
||||
4B8DD3862634D37E00B3C866 /* SNA.cpp in Sources */,
|
||||
@@ -5549,9 +5627,11 @@
|
||||
4BFDD78C1F7F2DB4008579B9 /* ImplicitSectors.cpp in Sources */,
|
||||
4B894526201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||
4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */,
|
||||
4B051CB62680158600CA44E8 /* EXDos.cpp in Sources */,
|
||||
4B8805FB1DCFF807003085B1 /* Oric.cpp in Sources */,
|
||||
4B6ED2F0208E2F8A0047B343 /* WOZ.cpp in Sources */,
|
||||
4B15A9FC208249BB005E6C8D /* StaticAnalyser.cpp in Sources */,
|
||||
4B051CAC26783E2000CA44E8 /* Nick.cpp in Sources */,
|
||||
4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */,
|
||||
4B0F1BDA2602FF9800B85C66 /* Video.cpp in Sources */,
|
||||
4B051C922669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */,
|
||||
@@ -5684,7 +5764,7 @@
|
||||
4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */,
|
||||
4B778F6023A5F3460000D260 /* Disk.cpp in Sources */,
|
||||
4B778F5C23A5F3070000D260 /* MSX.cpp in Sources */,
|
||||
4B778F0323A5EBB00000D260 /* MSXDSK.cpp in Sources */,
|
||||
4B778F0323A5EBB00000D260 /* FAT12.cpp in Sources */,
|
||||
4B778F4023A5F1910000D260 /* z8530.cpp in Sources */,
|
||||
4B778EFD23A5EB8E0000D260 /* AppleDSK.cpp in Sources */,
|
||||
4B778EFB23A5EB7E0000D260 /* HFE.cpp in Sources */,
|
||||
@@ -5692,10 +5772,12 @@
|
||||
4BFCA12B1ECBE7C400AC40C1 /* ZexallTests.swift in Sources */,
|
||||
4B778F2223A5EDDD0000D260 /* PulseQueuedTape.cpp in Sources */,
|
||||
4B778EF123A5D6B50000D260 /* 9918.cpp in Sources */,
|
||||
4B051CB3267D3FF800CA44E8 /* EnterpriseNickTests.mm in Sources */,
|
||||
4B9D0C4D22C7DA1A00DE1AD3 /* 68000ControlFlowTests.mm in Sources */,
|
||||
4BB2A9AF1E13367E001A5C23 /* CRCTests.mm in Sources */,
|
||||
4B778F5623A5F2AF0000D260 /* CPM.cpp in Sources */,
|
||||
4B778F1C23A5ED3F0000D260 /* TimedEventLoop.cpp in Sources */,
|
||||
4B47770D26900685005C2340 /* EnterpriseDaveTests.mm in Sources */,
|
||||
4B3BA0D01D318B44005DD7A7 /* MOS6532Bridge.mm in Sources */,
|
||||
4B778F3823A5F11C0000D260 /* SegmentParser.cpp in Sources */,
|
||||
4B778F0723A5EC150000D260 /* CommodoreTAP.cpp in Sources */,
|
||||
|
||||
@@ -85,8 +85,12 @@
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--new=amstradcpc"
|
||||
isEnabled = "NO">
|
||||
argument = "--new=enterprise"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--basic-version=any"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/ColecoVision/Galaxian (1983)(Atari).col""
|
||||
@@ -126,15 +130,15 @@
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--model=mac512k"
|
||||
isEnabled = "YES">
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--new=macintosh"
|
||||
isEnabled = "YES">
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--use-square-pixels"
|
||||
isEnabled = "YES">
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
</LaunchAction>
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
debugDocumentVersioning = "YES"
|
||||
migratedStopOnEveryIssue = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
enableGPUShaderValidationMode = "2"
|
||||
allowLocationSimulation = "NO">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// AppleIIOptionsPanel.swift
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 07/06/2021.
|
||||
// Copyright 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
class AppleIIOptionsPanel: MachinePanel {
|
||||
var appleII: CSAppleII! {
|
||||
get {
|
||||
return self.machine.appleII
|
||||
}
|
||||
}
|
||||
var squarePixelsUserDefaultsKey: String {
|
||||
return prefixedUserDefaultsKey("useSquarePixels")
|
||||
}
|
||||
|
||||
@IBOutlet var squarePixelButton: NSButton!
|
||||
|
||||
@IBAction func optionDidChange(_ sender: AnyObject!) {
|
||||
let useSquarePixels = squarePixelButton.state == .on
|
||||
appleII.useSquarePixels = useSquarePixels
|
||||
|
||||
let standardUserDefaults = UserDefaults.standard
|
||||
standardUserDefaults.set(useSquarePixels, forKey: squarePixelsUserDefaultsKey)
|
||||
}
|
||||
|
||||
override func establishStoredOptions() {
|
||||
super.establishStoredOptions()
|
||||
|
||||
let standardUserDefaults = UserDefaults.standard
|
||||
let useSquarePixels = standardUserDefaults.bool(forKey: squarePixelsUserDefaultsKey)
|
||||
appleII.useSquarePixels = useSquarePixels
|
||||
squarePixelButton.state = useSquarePixels ? .on : .off
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,35 @@ typedef NS_ENUM(NSInteger, CSMachineCPCModel) {
|
||||
CSMachineCPCModel6128
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSMachineEnterpriseModel) {
|
||||
CSMachineEnterpriseModel64,
|
||||
CSMachineEnterpriseModel128,
|
||||
CSMachineEnterpriseModel256,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSMachineEnterpriseSpeed) {
|
||||
CSMachineEnterpriseSpeed4MHz,
|
||||
CSMachineEnterpriseSpeed6MHz
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSMachineEnterpriseEXOS) {
|
||||
CSMachineEnterpriseEXOSVersion21,
|
||||
CSMachineEnterpriseEXOSVersion20,
|
||||
CSMachineEnterpriseEXOSVersion10,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSMachineEnterpriseBASIC) {
|
||||
CSMachineEnterpriseBASICVersion21,
|
||||
CSMachineEnterpriseBASICVersion11,
|
||||
CSMachineEnterpriseBASICVersion10,
|
||||
CSMachineEnterpriseBASICNone,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSMachineEnterpriseDOS) {
|
||||
CSMachineEnterpriseDOSEXDOS,
|
||||
CSMachineEnterpriseDOSNone,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSMachineMacintoshModel) {
|
||||
CSMachineMacintoshModel128k,
|
||||
CSMachineMacintoshModel512k,
|
||||
@@ -96,6 +125,7 @@ typedef int Kilobytes;
|
||||
- (instancetype)initWithAppleIIgsModel:(CSMachineAppleIIgsModel)model memorySize:(Kilobytes)memorySize;
|
||||
- (instancetype)initWithAtariSTModel:(CSMachineAtariSTModel)model;
|
||||
- (instancetype)initWithElectronDFS:(BOOL)dfs adfs:(BOOL)adfs ap6:(BOOL)ap6 sidewaysRAM:(BOOL)sidewaysRAM;
|
||||
- (instancetype)initWithEnterpriseModel:(CSMachineEnterpriseModel)model speed:(CSMachineEnterpriseSpeed)speed exosVersion:(CSMachineEnterpriseEXOS)exosVersion basicVersion:(CSMachineEnterpriseBASIC)basicVersion dos:(CSMachineEnterpriseDOS)dos;
|
||||
- (instancetype)initWithMacintoshModel:(CSMachineMacintoshModel)model;
|
||||
- (instancetype)initWithMSXRegion:(CSMachineMSXRegion)region hasDiskDrive:(BOOL)hasDiskDrive;
|
||||
- (instancetype)initWithOricModel:(CSMachineOricModel)model diskInterface:(CSMachineOricDiskInterface)diskInterface;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "../../../../../Analyser/Static/AppleIIgs/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/AtariST/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/Commodore/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/Enterprise/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/Macintosh/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/MSX/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/Oric/Target.hpp"
|
||||
@@ -130,6 +131,51 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithEnterpriseModel:(CSMachineEnterpriseModel)model speed:(CSMachineEnterpriseSpeed)speed exosVersion:(CSMachineEnterpriseEXOS)exosVersion basicVersion:(CSMachineEnterpriseBASIC)basicVersion dos:(CSMachineEnterpriseDOS)dos {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
using Target = Analyser::Static::Enterprise::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
|
||||
switch(model) {
|
||||
case CSMachineEnterpriseModel64: target->model = Target::Model::Enterprise64; break;
|
||||
default:
|
||||
case CSMachineEnterpriseModel128: target->model = Target::Model::Enterprise128; break;
|
||||
case CSMachineEnterpriseModel256: target->model = Target::Model::Enterprise256; break;
|
||||
}
|
||||
|
||||
switch(speed) {
|
||||
case CSMachineEnterpriseSpeed6MHz: target->speed = Target::Speed::SixMHz; break;
|
||||
default:
|
||||
case CSMachineEnterpriseSpeed4MHz: target->speed = Target::Speed::FourMHz; break;
|
||||
}
|
||||
|
||||
switch(exosVersion) {
|
||||
case CSMachineEnterpriseEXOSVersion21: target->exos_version = Target::EXOSVersion::v21; break;
|
||||
default:
|
||||
case CSMachineEnterpriseEXOSVersion20: target->exos_version = Target::EXOSVersion::v20; break;
|
||||
case CSMachineEnterpriseEXOSVersion10: target->exos_version = Target::EXOSVersion::v10; break;
|
||||
}
|
||||
|
||||
switch(basicVersion) {
|
||||
case CSMachineEnterpriseBASICNone: target->basic_version = Target::BASICVersion::None; break;
|
||||
default:
|
||||
case CSMachineEnterpriseBASICVersion21: target->basic_version = Target::BASICVersion::v21; break;
|
||||
case CSMachineEnterpriseBASICVersion11: target->basic_version = Target::BASICVersion::v11; break;
|
||||
case CSMachineEnterpriseBASICVersion10: target->basic_version = Target::BASICVersion::v10; break;
|
||||
}
|
||||
|
||||
switch(dos) {
|
||||
case CSMachineEnterpriseDOSEXDOS: target->dos = Target::DOS::EXDOS; break;
|
||||
default:
|
||||
case CSMachineEnterpriseDOSNone: target->dos = Target::DOS::None; break;
|
||||
}
|
||||
|
||||
_targets.push_back(std::move(target));
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithMacintoshModel:(CSMachineMacintoshModel)model {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
@@ -276,6 +322,7 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
|
||||
case Analyser::Machine::AtariST: return @"CompositeOptions";
|
||||
case Analyser::Machine::ColecoVision: return @"CompositeOptions";
|
||||
case Analyser::Machine::Electron: return @"QuickLoadCompositeOptions";
|
||||
case Analyser::Machine::Enterprise: return @"CompositeOptions";
|
||||
case Analyser::Machine::Macintosh: return @"MacintoshOptions";
|
||||
case Analyser::Machine::MasterSystem: return @"CompositeOptions";
|
||||
case Analyser::Machine::MSX: return @"QuickLoadCompositeOptions";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17701"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@@ -17,10 +17,10 @@
|
||||
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" titleVisibility="hidden" id="QvC-M9-y7g">
|
||||
<windowStyleMask key="styleMask" titled="YES" documentModal="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="590" height="316"/>
|
||||
<rect key="contentRect" x="196" y="240" width="590" height="353"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1440"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="590" height="316"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="590" height="353"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hKn-1l-OSN">
|
||||
@@ -59,16 +59,16 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<tabView type="noTabsBezelBorder" translatesAutoresizingMaskIntoConstraints="NO" id="VUb-QG-x7c">
|
||||
<rect key="frame" x="154" y="56" width="420" height="206"/>
|
||||
<rect key="frame" x="154" y="56" width="420" height="243"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<tabViewItems>
|
||||
<tabViewItem label="Apple II" identifier="appleii" id="P59-QG-LOa">
|
||||
<view key="view" id="dHz-Yv-GNq">
|
||||
<rect key="frame" x="10" y="7" width="400" height="186"/>
|
||||
<rect key="frame" x="10" y="7" width="400" height="223"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="V5Z-dX-Ns4">
|
||||
<rect key="frame" x="18" y="163" width="46" height="16"/>
|
||||
<rect key="frame" x="18" y="185" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="qV3-2P-3JW">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -76,7 +76,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="WnO-ef-IC6">
|
||||
<rect key="frame" x="18" y="133" width="96" height="16"/>
|
||||
<rect key="frame" x="18" y="155" width="96" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disk controller:" id="kbf-rc-Y4M">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -84,7 +84,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jli-ac-Sij">
|
||||
<rect key="frame" x="67" y="157" width="117" height="25"/>
|
||||
<rect key="frame" x="67" y="179" width="117" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Apple II" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="axesIndependently" inset="2" selectedItem="VBQ-JG-AeM" id="U6V-us-O2F">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -99,7 +99,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LSB-WP-FMi">
|
||||
<rect key="frame" x="117" y="127" width="134" height="25"/>
|
||||
<rect key="frame" x="117" y="149" width="134" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Sixteen Sector" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="16" imageScaling="axesIndependently" inset="2" selectedItem="QaV-Yr-k9o" id="8BT-RV-2Nm">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -115,11 +115,11 @@ Gw
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="WnO-ef-IC6" firstAttribute="leading" secondItem="dHz-Yv-GNq" secondAttribute="leading" constant="20" symbolic="YES" id="5RF-1w-9HW"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="LSB-WP-FMi" secondAttribute="bottom" constant="5" id="865-cv-qVk"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="LSB-WP-FMi" secondAttribute="bottom" constant="20" symbolic="YES" id="865-cv-qVk"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="LSB-WP-FMi" secondAttribute="trailing" constant="20" symbolic="YES" id="9GL-al-1qi"/>
|
||||
<constraint firstItem="WnO-ef-IC6" firstAttribute="centerY" secondItem="LSB-WP-FMi" secondAttribute="centerY" id="Fuj-zT-MIm"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="jli-ac-Sij" secondAttribute="trailing" constant="20" symbolic="YES" id="I8d-OR-ICN"/>
|
||||
<constraint firstItem="jli-ac-Sij" firstAttribute="top" secondItem="dHz-Yv-GNq" secondAttribute="top" constant="5" id="Qi1-CV-A0c"/>
|
||||
<constraint firstItem="jli-ac-Sij" firstAttribute="top" secondItem="dHz-Yv-GNq" secondAttribute="top" constant="20" symbolic="YES" id="Qi1-CV-A0c"/>
|
||||
<constraint firstItem="V5Z-dX-Ns4" firstAttribute="leading" secondItem="dHz-Yv-GNq" secondAttribute="leading" constant="20" symbolic="YES" id="SWc-iX-1We"/>
|
||||
<constraint firstItem="LSB-WP-FMi" firstAttribute="leading" secondItem="WnO-ef-IC6" secondAttribute="trailing" constant="8" symbolic="YES" id="bte-XA-xNQ"/>
|
||||
<constraint firstItem="LSB-WP-FMi" firstAttribute="top" secondItem="jli-ac-Sij" secondAttribute="bottom" constant="10" symbolic="YES" id="ki5-JR-vRe"/>
|
||||
@@ -129,12 +129,12 @@ Gw
|
||||
</view>
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Apple IIgs" identifier="appleiigs" id="u5E-8n-ghF">
|
||||
<view key="view" ambiguous="YES" id="jOM-9f-vkk">
|
||||
<rect key="frame" x="10" y="7" width="400" height="76"/>
|
||||
<view key="view" id="jOM-9f-vkk">
|
||||
<rect key="frame" x="10" y="7" width="400" height="223"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0d9-IG-gKU">
|
||||
<rect key="frame" x="18" y="53" width="46" height="16"/>
|
||||
<rect key="frame" x="18" y="185" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="kiv-1P-FWc">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -142,7 +142,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LES-76-Ovz">
|
||||
<rect key="frame" x="18" y="23" width="85" height="16"/>
|
||||
<rect key="frame" x="18" y="155" width="85" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory size:" id="OLJ-nC-yyj">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -150,7 +150,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gcS-uy-mzl">
|
||||
<rect key="frame" x="67" y="47" width="89" height="25"/>
|
||||
<rect key="frame" x="67" y="179" width="89" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="ROM 03" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="2" imageScaling="axesIndependently" inset="2" selectedItem="0TS-DO-O9h" id="hjw-g8-e2f">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -164,7 +164,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nQa-YS-utT">
|
||||
<rect key="frame" x="106" y="17" width="82" height="25"/>
|
||||
<rect key="frame" x="106" y="149" width="82" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="8 mb" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="8192" imageScaling="axesIndependently" inset="2" selectedItem="UHg-gU-Xnn" id="dl3-cq-uWO">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -182,24 +182,24 @@ Gw
|
||||
<constraint firstItem="LES-76-Ovz" firstAttribute="leading" secondItem="jOM-9f-vkk" secondAttribute="leading" constant="20" symbolic="YES" id="2D3-Ve-6CN"/>
|
||||
<constraint firstItem="0d9-IG-gKU" firstAttribute="leading" secondItem="jOM-9f-vkk" secondAttribute="leading" constant="20" symbolic="YES" id="8Xz-86-tDf"/>
|
||||
<constraint firstItem="0d9-IG-gKU" firstAttribute="centerY" secondItem="gcS-uy-mzl" secondAttribute="centerY" id="Eww-Qq-eBT"/>
|
||||
<constraint firstItem="gcS-uy-mzl" firstAttribute="top" secondItem="jOM-9f-vkk" secondAttribute="top" constant="5" id="F6i-cP-7AR"/>
|
||||
<constraint firstItem="gcS-uy-mzl" firstAttribute="top" secondItem="jOM-9f-vkk" secondAttribute="top" constant="20" symbolic="YES" id="F6i-cP-7AR"/>
|
||||
<constraint firstItem="gcS-uy-mzl" firstAttribute="leading" secondItem="0d9-IG-gKU" secondAttribute="trailing" constant="8" symbolic="YES" id="LUm-rI-LYP"/>
|
||||
<constraint firstItem="LES-76-Ovz" firstAttribute="centerY" secondItem="nQa-YS-utT" secondAttribute="centerY" id="UdP-U6-OFE"/>
|
||||
<constraint firstItem="nQa-YS-utT" firstAttribute="leading" secondItem="LES-76-Ovz" secondAttribute="trailing" constant="8" symbolic="YES" id="Xm1-iG-Dgj"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="gcS-uy-mzl" secondAttribute="trailing" constant="20" symbolic="YES" id="a38-Cx-CEh"/>
|
||||
<constraint firstItem="nQa-YS-utT" firstAttribute="top" secondItem="gcS-uy-mzl" secondAttribute="bottom" constant="10" symbolic="YES" id="aM9-m8-s7z"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="nQa-YS-utT" secondAttribute="trailing" constant="20" symbolic="YES" id="cqx-Jc-rUb"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="nQa-YS-utT" secondAttribute="bottom" constant="5" id="sbT-If-NTU"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="nQa-YS-utT" secondAttribute="bottom" constant="20" symbolic="YES" id="sbT-If-NTU"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Amstrad CPC" identifier="cpc" id="JmB-OF-xcM">
|
||||
<view key="view" id="5zS-Nj-Ynx">
|
||||
<rect key="frame" x="10" y="7" width="400" height="76"/>
|
||||
<rect key="frame" x="10" y="7" width="400" height="223"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="00d-sg-Krh">
|
||||
<rect key="frame" x="67" y="47" width="96" height="25"/>
|
||||
<rect key="frame" x="67" y="179" width="96" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="CPC6128" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="6128" imageScaling="axesIndependently" inset="2" selectedItem="klh-ZE-Agp" id="hVJ-h6-iea">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -213,7 +213,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="q9q-sl-J0q">
|
||||
<rect key="frame" x="18" y="53" width="46" height="16"/>
|
||||
<rect key="frame" x="18" y="185" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="Cw3-q5-1bC">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -224,8 +224,8 @@ Gw
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="00d-sg-Krh" secondAttribute="trailing" constant="20" symbolic="YES" id="4AF-5C-2IF"/>
|
||||
<constraint firstItem="q9q-sl-J0q" firstAttribute="leading" secondItem="5zS-Nj-Ynx" secondAttribute="leading" constant="20" symbolic="YES" id="Wof-5h-gfD"/>
|
||||
<constraint firstItem="00d-sg-Krh" firstAttribute="top" secondItem="5zS-Nj-Ynx" secondAttribute="top" constant="5" id="c92-uU-NRr"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="00d-sg-Krh" secondAttribute="bottom" constant="5" id="enU-LN-Nep"/>
|
||||
<constraint firstItem="00d-sg-Krh" firstAttribute="top" secondItem="5zS-Nj-Ynx" secondAttribute="top" constant="20" symbolic="YES" id="c92-uU-NRr"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="00d-sg-Krh" secondAttribute="bottom" constant="20" symbolic="YES" id="enU-LN-Nep"/>
|
||||
<constraint firstItem="00d-sg-Krh" firstAttribute="leading" secondItem="q9q-sl-J0q" secondAttribute="trailing" constant="8" symbolic="YES" id="mA8-US-ndo"/>
|
||||
<constraint firstItem="q9q-sl-J0q" firstAttribute="centerY" secondItem="00d-sg-Krh" secondAttribute="centerY" id="vA8-IA-Uwf"/>
|
||||
</constraints>
|
||||
@@ -233,11 +233,11 @@ Gw
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Atari ST" identifier="atarist" id="a6Y-mx-yFn">
|
||||
<view key="view" id="nnv-Wi-7hc">
|
||||
<rect key="frame" x="10" y="7" width="400" height="76"/>
|
||||
<rect key="frame" x="10" y="7" width="400" height="223"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nLf-LI-nWO">
|
||||
<rect key="frame" x="18" y="57" width="728" height="16"/>
|
||||
<rect key="frame" x="18" y="204" width="364" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="At present only a 512k Atari ST is supported." id="gBA-ke-mur">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -254,32 +254,32 @@ Gw
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Electron" identifier="electron" id="muc-z9-Vqc">
|
||||
<view key="view" id="SRc-2D-95G">
|
||||
<rect key="frame" x="10" y="7" width="400" height="76"/>
|
||||
<rect key="frame" x="10" y="7" width="400" height="223"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JqM-IK-FMP">
|
||||
<rect key="frame" x="18" y="56" width="168" height="18"/>
|
||||
<rect key="frame" x="18" y="186" width="168" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="With Disk Filing System" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="tpW-5C-xKp">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="945-wU-JOH">
|
||||
<rect key="frame" x="18" y="34" width="232" height="18"/>
|
||||
<rect key="frame" x="18" y="164" width="232" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="With Advanced Disk Filing System" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="S0c-Jg-7Pu">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="cG2-Ph-S3Z">
|
||||
<rect key="frame" x="18" y="12" width="231" height="18"/>
|
||||
<rect key="frame" x="18" y="142" width="231" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="With Advanced Plus 6 Utility ROM" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="yjF-XS-zx6">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lzo-8g-o4S">
|
||||
<rect key="frame" x="18" y="-10" width="284" height="18"/>
|
||||
<rect key="frame" x="18" y="120" width="284" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Fill unused ROM banks with sideways RAM" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="JEz-eK-uWp">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -289,13 +289,13 @@ Gw
|
||||
<constraints>
|
||||
<constraint firstItem="945-wU-JOH" firstAttribute="leading" secondItem="SRc-2D-95G" secondAttribute="leading" constant="20" symbolic="YES" id="1iM-70-oZq"/>
|
||||
<constraint firstItem="cG2-Ph-S3Z" firstAttribute="top" secondItem="945-wU-JOH" secondAttribute="bottom" constant="6" symbolic="YES" id="E9b-RP-9vj"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="lzo-8g-o4S" secondAttribute="bottom" constant="5" id="FM6-AA-Vhf"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="lzo-8g-o4S" secondAttribute="bottom" constant="20" symbolic="YES" id="FM6-AA-Vhf"/>
|
||||
<constraint firstItem="JqM-IK-FMP" firstAttribute="leading" secondItem="SRc-2D-95G" secondAttribute="leading" constant="20" symbolic="YES" id="NfY-dE-aJw"/>
|
||||
<constraint firstItem="lzo-8g-o4S" firstAttribute="top" secondItem="cG2-Ph-S3Z" secondAttribute="bottom" constant="6" symbolic="YES" id="S45-42-Gtv"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="lzo-8g-o4S" secondAttribute="trailing" constant="20" symbolic="YES" id="X3p-qJ-ENH"/>
|
||||
<constraint firstItem="lzo-8g-o4S" firstAttribute="leading" secondItem="SRc-2D-95G" secondAttribute="leading" constant="20" symbolic="YES" id="b5a-SX-2ty"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="945-wU-JOH" secondAttribute="trailing" constant="20" symbolic="YES" id="dmY-PV-ap4"/>
|
||||
<constraint firstItem="JqM-IK-FMP" firstAttribute="top" secondItem="SRc-2D-95G" secondAttribute="top" constant="3" id="ggl-QH-mV4"/>
|
||||
<constraint firstItem="JqM-IK-FMP" firstAttribute="top" secondItem="SRc-2D-95G" secondAttribute="top" constant="20" symbolic="YES" id="ggl-QH-mV4"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="cG2-Ph-S3Z" secondAttribute="trailing" constant="20" symbolic="YES" id="m6t-dP-71d"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="JqM-IK-FMP" secondAttribute="trailing" constant="20" symbolic="YES" id="mvO-UZ-BtT"/>
|
||||
<constraint firstItem="cG2-Ph-S3Z" firstAttribute="leading" secondItem="SRc-2D-95G" secondAttribute="leading" constant="20" symbolic="YES" id="npw-IZ-6xU"/>
|
||||
@@ -303,13 +303,158 @@ Gw
|
||||
</constraints>
|
||||
</view>
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Enterprise" identifier="enterprise" id="zhO-EO-wUe">
|
||||
<view key="view" id="1cs-PX-RAH">
|
||||
<rect key="frame" x="10" y="7" width="400" height="223"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PhH-bu-pb5">
|
||||
<rect key="frame" x="80" y="179" width="129" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Enterprise 128" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="128" imageScaling="axesIndependently" inset="2" selectedItem="roH-nL-f8o" id="z9O-XC-XBv">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="ojI-Vb-iHz">
|
||||
<items>
|
||||
<menuItem title="Enterprise 256" tag="256" id="Al3-A0-tvw"/>
|
||||
<menuItem title="Enterprise 128" state="on" tag="128" id="roH-nL-f8o"/>
|
||||
<menuItem title="Enterprise 64" tag="64" id="vNG-Tv-bDI"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Lfl-5c-b8j">
|
||||
<rect key="frame" x="67" y="149" width="77" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="4 Mhz" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="4" imageScaling="axesIndependently" inset="2" selectedItem="5N6-tD-uN7" id="BU2-NJ-Kii">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="ANK-p0-M77">
|
||||
<items>
|
||||
<menuItem title="4 Mhz" state="on" tag="4" id="5N6-tD-uN7"/>
|
||||
<menuItem title="6 Mhz" tag="6" id="bVT-qO-U7n"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nen-Za-7zH">
|
||||
<rect key="frame" x="64" y="119" width="107" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Version 2.1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="21" imageScaling="axesIndependently" inset="2" selectedItem="Qja-xZ-wVM" id="xGG-ri-8Sb">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="uNC-hA-d5z">
|
||||
<items>
|
||||
<menuItem title="Version 2.1" state="on" tag="21" id="Qja-xZ-wVM"/>
|
||||
<menuItem title="Version 2.0" tag="20" id="XTj-l7-KX3"/>
|
||||
<menuItem title="Version 1.0" tag="10" id="Ky2-2D-wcY"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hIr-GH-7xi">
|
||||
<rect key="frame" x="67" y="89" width="105" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Version 2.1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="21" imageScaling="axesIndependently" inset="2" selectedItem="TME-cv-Jh1" id="9mQ-GW-lq9">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="VcH-Xm-1hY">
|
||||
<items>
|
||||
<menuItem title="Version 2.1" state="on" tag="21" id="TME-cv-Jh1"/>
|
||||
<menuItem title="Version 1.1" tag="11" id="7P2-aF-6fp"/>
|
||||
<menuItem title="Version 1.0" tag="10" id="j8p-uY-BhG"/>
|
||||
<menuItem title="None" id="eGk-Bj-IVT"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="syE-e7-TjU">
|
||||
<rect key="frame" x="57" y="59" width="83" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="EXDOS" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="1" imageScaling="axesIndependently" inset="2" selectedItem="8rP-2w-PdU" id="NvO-Zm-2Rq">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" title="DOS" id="sdr-al-7mi">
|
||||
<items>
|
||||
<menuItem title="EXDOS" state="on" tag="1" id="8rP-2w-PdU"/>
|
||||
<menuItem title="None" id="qoS-KO-iEZ"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ykc-W1-YaS">
|
||||
<rect key="frame" x="18" y="125" width="43" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="EXOS:" id="gUC-PN-zVL">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="frx-nk-c3P">
|
||||
<rect key="frame" x="18" y="185" width="59" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Machine:" id="uTv-hH-mIC">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dzd-tH-BjX">
|
||||
<rect key="frame" x="18" y="95" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="BASIC:" id="ai1-oR-X6Y">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pxr-Bq-yh0">
|
||||
<rect key="frame" x="18" y="65" width="36" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="DOS:" id="NFk-cp-DfS">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rHr-bh-QMV">
|
||||
<rect key="frame" x="17" y="155" width="47" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Speed:" id="sAw-C9-Sf7">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="dzd-tH-BjX" firstAttribute="centerY" secondItem="hIr-GH-7xi" secondAttribute="centerY" id="3TV-RU-Kgh"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="hIr-GH-7xi" secondAttribute="trailing" constant="20" symbolic="YES" id="44v-9O-y7L"/>
|
||||
<constraint firstItem="frx-nk-c3P" firstAttribute="centerY" secondItem="PhH-bu-pb5" secondAttribute="centerY" id="6Wc-aR-wuL"/>
|
||||
<constraint firstItem="dzd-tH-BjX" firstAttribute="leading" secondItem="1cs-PX-RAH" secondAttribute="leading" constant="20" symbolic="YES" id="7RZ-Om-TAa"/>
|
||||
<constraint firstItem="ykc-W1-YaS" firstAttribute="centerY" secondItem="nen-Za-7zH" secondAttribute="centerY" id="CLa-6E-8BB"/>
|
||||
<constraint firstItem="nen-Za-7zH" firstAttribute="top" secondItem="Lfl-5c-b8j" secondAttribute="bottom" constant="10" symbolic="YES" id="Df8-qv-3dY"/>
|
||||
<constraint firstItem="PhH-bu-pb5" firstAttribute="leading" secondItem="frx-nk-c3P" secondAttribute="trailing" constant="8" symbolic="YES" id="ENF-TY-TQ7"/>
|
||||
<constraint firstItem="nen-Za-7zH" firstAttribute="leading" secondItem="ykc-W1-YaS" secondAttribute="trailing" constant="8" symbolic="YES" id="GWR-VI-9PG"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="syE-e7-TjU" secondAttribute="bottom" constant="20" symbolic="YES" id="K3s-FA-zMB"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="PhH-bu-pb5" secondAttribute="trailing" constant="20" symbolic="YES" id="LwB-ef-uF4"/>
|
||||
<constraint firstItem="PhH-bu-pb5" firstAttribute="top" secondItem="1cs-PX-RAH" secondAttribute="top" constant="20" symbolic="YES" id="Myt-i4-jVq"/>
|
||||
<constraint firstItem="Lfl-5c-b8j" firstAttribute="top" secondItem="PhH-bu-pb5" secondAttribute="bottom" constant="10" symbolic="YES" id="Nl3-hL-jwg"/>
|
||||
<constraint firstItem="pxr-Bq-yh0" firstAttribute="leading" secondItem="1cs-PX-RAH" secondAttribute="leading" constant="20" symbolic="YES" id="Qzp-IY-Pa0"/>
|
||||
<constraint firstItem="PhH-bu-pb5" firstAttribute="leading" secondItem="frx-nk-c3P" secondAttribute="trailing" constant="8" symbolic="YES" id="T3e-u7-fiQ"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="syE-e7-TjU" secondAttribute="trailing" constant="20" symbolic="YES" id="TEX-Nw-y2K"/>
|
||||
<constraint firstItem="frx-nk-c3P" firstAttribute="leading" secondItem="1cs-PX-RAH" secondAttribute="leading" constant="20" symbolic="YES" id="TgR-RR-eA1"/>
|
||||
<constraint firstItem="pxr-Bq-yh0" firstAttribute="centerY" secondItem="syE-e7-TjU" secondAttribute="centerY" id="UYw-uz-Am0"/>
|
||||
<constraint firstItem="hIr-GH-7xi" firstAttribute="top" secondItem="nen-Za-7zH" secondAttribute="bottom" constant="10" symbolic="YES" id="VOc-2v-s3u"/>
|
||||
<constraint firstItem="syE-e7-TjU" firstAttribute="top" secondItem="hIr-GH-7xi" secondAttribute="bottom" constant="10" symbolic="YES" id="W9S-on-Heq"/>
|
||||
<constraint firstItem="rHr-bh-QMV" firstAttribute="leading" secondItem="1cs-PX-RAH" secondAttribute="leading" constant="19" id="XZf-lB-NnA"/>
|
||||
<constraint firstItem="ykc-W1-YaS" firstAttribute="leading" secondItem="1cs-PX-RAH" secondAttribute="leading" constant="20" symbolic="YES" id="bAP-lx-pdi"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="nen-Za-7zH" secondAttribute="trailing" constant="20" symbolic="YES" id="eEI-5Q-TnO"/>
|
||||
<constraint firstItem="syE-e7-TjU" firstAttribute="leading" secondItem="pxr-Bq-yh0" secondAttribute="trailing" constant="8" symbolic="YES" id="fmM-Ma-Jyu"/>
|
||||
<constraint firstItem="hIr-GH-7xi" firstAttribute="leading" secondItem="dzd-tH-BjX" secondAttribute="trailing" constant="8" symbolic="YES" id="jDQ-TF-Ogf"/>
|
||||
<constraint firstItem="rHr-bh-QMV" firstAttribute="centerY" secondItem="Lfl-5c-b8j" secondAttribute="centerY" id="mnp-JF-dVQ"/>
|
||||
<constraint firstItem="Lfl-5c-b8j" firstAttribute="leading" secondItem="rHr-bh-QMV" secondAttribute="trailing" constant="8" symbolic="YES" id="yfb-SL-v1H"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Macintosh" identifier="mac" id="lmR-z3-xSm">
|
||||
<view key="view" id="7Yf-vi-Q0W">
|
||||
<rect key="frame" x="10" y="7" width="400" height="76"/>
|
||||
<rect key="frame" x="10" y="7" width="400" height="223"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZOY-4E-Cfl">
|
||||
<rect key="frame" x="18" y="53" width="46" height="16"/>
|
||||
<rect key="frame" x="18" y="185" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="h9r-i6-66j">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -317,7 +462,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xa6-NA-JY5">
|
||||
<rect key="frame" x="67" y="47" width="75" height="25"/>
|
||||
<rect key="frame" x="67" y="179" width="75" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Plus" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="3" imageScaling="axesIndependently" inset="2" selectedItem="R6T-hg-rOF" id="1Kb-Q2-BGM">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -333,9 +478,9 @@ Gw
|
||||
</popUpButton>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="xa6-NA-JY5" firstAttribute="top" secondItem="7Yf-vi-Q0W" secondAttribute="top" constant="5" id="3hY-Ca-mnR"/>
|
||||
<constraint firstItem="xa6-NA-JY5" firstAttribute="top" secondItem="7Yf-vi-Q0W" secondAttribute="top" constant="20" symbolic="YES" id="3hY-Ca-mnR"/>
|
||||
<constraint firstItem="ZOY-4E-Cfl" firstAttribute="leading" secondItem="7Yf-vi-Q0W" secondAttribute="leading" constant="20" symbolic="YES" id="5s6-87-VT6"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="xa6-NA-JY5" secondAttribute="bottom" constant="5" id="KYf-GJ-Y7k"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="xa6-NA-JY5" secondAttribute="bottom" constant="20" symbolic="YES" id="KYf-GJ-Y7k"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="xa6-NA-JY5" secondAttribute="trailing" constant="20" symbolic="YES" id="LZ5-xH-fU0"/>
|
||||
<constraint firstItem="ZOY-4E-Cfl" firstAttribute="centerY" secondItem="xa6-NA-JY5" secondAttribute="centerY" id="Wa5-KX-3Me"/>
|
||||
<constraint firstItem="xa6-NA-JY5" firstAttribute="leading" secondItem="ZOY-4E-Cfl" secondAttribute="trailing" constant="8" symbolic="YES" id="ktS-sr-F8L"/>
|
||||
@@ -344,18 +489,18 @@ Gw
|
||||
</tabViewItem>
|
||||
<tabViewItem label="MSX" identifier="msx" id="6SR-DY-zdI">
|
||||
<view key="view" id="mWD-An-tR7">
|
||||
<rect key="frame" x="10" y="7" width="400" height="76"/>
|
||||
<rect key="frame" x="10" y="7" width="400" height="223"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8xT-Pr-8SE">
|
||||
<rect key="frame" x="18" y="26" width="128" height="18"/>
|
||||
<rect key="frame" x="18" y="158" width="128" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Attach disk drive" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="CB3-nA-VTM">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LG6-mP-SeG">
|
||||
<rect key="frame" x="71" y="47" width="146" height="25"/>
|
||||
<rect key="frame" x="71" y="179" width="146" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="European (PAL)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="axesIndependently" inset="2" selectedItem="xAh-Ch-tby" id="yR4-yv-Lvu">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -369,7 +514,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZaD-7v-rMS">
|
||||
<rect key="frame" x="18" y="53" width="50" height="16"/>
|
||||
<rect key="frame" x="18" y="185" width="50" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Region:" id="x4m-eh-Nif">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -380,9 +525,9 @@ Gw
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="LG6-mP-SeG" secondAttribute="trailing" constant="20" symbolic="YES" id="0Oc-n7-gaM"/>
|
||||
<constraint firstItem="8xT-Pr-8SE" firstAttribute="top" secondItem="LG6-mP-SeG" secondAttribute="bottom" constant="8" symbolic="YES" id="LBt-4m-GDc"/>
|
||||
<constraint firstItem="LG6-mP-SeG" firstAttribute="top" secondItem="mWD-An-tR7" secondAttribute="top" constant="5" id="bcb-ZZ-VpQ"/>
|
||||
<constraint firstItem="LG6-mP-SeG" firstAttribute="top" secondItem="mWD-An-tR7" secondAttribute="top" constant="20" symbolic="YES" id="bcb-ZZ-VpQ"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="8xT-Pr-8SE" secondAttribute="trailing" constant="20" symbolic="YES" id="l8P-UW-8ig"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="8xT-Pr-8SE" secondAttribute="bottom" constant="3" id="mga-YX-Bek"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="8xT-Pr-8SE" secondAttribute="bottom" constant="20" symbolic="YES" id="mga-YX-Bek"/>
|
||||
<constraint firstItem="8xT-Pr-8SE" firstAttribute="leading" secondItem="mWD-An-tR7" secondAttribute="leading" constant="20" symbolic="YES" id="q8Q-kh-Opj"/>
|
||||
<constraint firstItem="LG6-mP-SeG" firstAttribute="leading" secondItem="ZaD-7v-rMS" secondAttribute="trailing" constant="8" symbolic="YES" id="svb-nH-GlP"/>
|
||||
<constraint firstItem="ZaD-7v-rMS" firstAttribute="leading" secondItem="mWD-An-tR7" secondAttribute="leading" constant="20" symbolic="YES" id="zgh-a5-FNF"/>
|
||||
@@ -392,11 +537,11 @@ Gw
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Oric" identifier="oric" id="NSx-DC-p4M">
|
||||
<view key="view" id="sOR-e0-8iZ">
|
||||
<rect key="frame" x="10" y="7" width="400" height="76"/>
|
||||
<rect key="frame" x="10" y="7" width="400" height="223"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0ct-tf-uRH">
|
||||
<rect key="frame" x="18" y="53" width="46" height="16"/>
|
||||
<rect key="frame" x="18" y="185" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="Xm1-7x-YVl">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -404,7 +549,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ENP-hI-BVZ">
|
||||
<rect key="frame" x="67" y="47" width="107" height="25"/>
|
||||
<rect key="frame" x="67" y="179" width="107" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Oric-1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="axesIndependently" inset="2" selectedItem="jGN-1a-biF" id="Jll-EJ-cMr">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -418,7 +563,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fYL-p6-wyn">
|
||||
<rect key="frame" x="113" y="17" width="130" height="25"/>
|
||||
<rect key="frame" x="113" y="149" width="130" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="None" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="axesIndependently" inset="2" selectedItem="XhK-Jh-oTW" id="aYb-m1-H9X">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -434,7 +579,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="okM-ZI-NbF">
|
||||
<rect key="frame" x="18" y="23" width="92" height="16"/>
|
||||
<rect key="frame" x="18" y="155" width="92" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disk interface:" id="SFK-hS-tFC">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -443,7 +588,7 @@ Gw
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="ENP-hI-BVZ" firstAttribute="top" secondItem="sOR-e0-8iZ" secondAttribute="top" constant="5" id="Bgq-8R-8I4"/>
|
||||
<constraint firstItem="ENP-hI-BVZ" firstAttribute="top" secondItem="sOR-e0-8iZ" secondAttribute="top" constant="20" symbolic="YES" id="Bgq-8R-8I4"/>
|
||||
<constraint firstItem="fYL-p6-wyn" firstAttribute="leading" secondItem="okM-ZI-NbF" secondAttribute="trailing" constant="8" symbolic="YES" id="Pra-lC-JPB"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="ENP-hI-BVZ" secondAttribute="trailing" constant="20" symbolic="YES" id="Sr5-QZ-xU3"/>
|
||||
<constraint firstItem="ENP-hI-BVZ" firstAttribute="leading" secondItem="0ct-tf-uRH" secondAttribute="trailing" constant="8" symbolic="YES" id="Wcg-1P-z6l"/>
|
||||
@@ -453,17 +598,17 @@ Gw
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="fYL-p6-wyn" secondAttribute="trailing" constant="20" symbolic="YES" id="ewV-Dw-zj2"/>
|
||||
<constraint firstItem="0ct-tf-uRH" firstAttribute="leading" secondItem="sOR-e0-8iZ" secondAttribute="leading" constant="20" symbolic="YES" id="huH-9L-97Y"/>
|
||||
<constraint firstItem="fYL-p6-wyn" firstAttribute="top" secondItem="ENP-hI-BVZ" secondAttribute="bottom" constant="10" symbolic="YES" id="l3T-Ve-0Jw"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="fYL-p6-wyn" secondAttribute="bottom" constant="5" id="mN9-AZ-wSn"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="fYL-p6-wyn" secondAttribute="bottom" constant="20" symbolic="YES" id="mN9-AZ-wSn"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</tabViewItem>
|
||||
<tabViewItem label="Vic-20" identifier="vic20" id="cyO-PU-hSU">
|
||||
<view key="view" id="fLI-XB-QCr">
|
||||
<rect key="frame" x="10" y="7" width="400" height="76"/>
|
||||
<rect key="frame" x="10" y="7" width="400" height="223"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ueK-gq-gaF">
|
||||
<rect key="frame" x="71" y="47" width="146" height="25"/>
|
||||
<rect key="frame" x="71" y="179" width="146" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="European (PAL)" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="axesIndependently" inset="2" selectedItem="45i-0n-gau" id="yi7-eo-I0q">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -479,7 +624,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="2eV-Us-eEv">
|
||||
<rect key="frame" x="108" y="17" width="116" height="25"/>
|
||||
<rect key="frame" x="108" y="149" width="116" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Unexpanded" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="axesIndependently" inset="2" selectedItem="fOl-8Q-fsA" id="rH0-7T-pJE">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -493,7 +638,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MTh-9p-FqC">
|
||||
<rect key="frame" x="18" y="53" width="50" height="16"/>
|
||||
<rect key="frame" x="18" y="185" width="50" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Region:" id="F3g-Ya-ypU">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -501,7 +646,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gRS-DK-rIy">
|
||||
<rect key="frame" x="18" y="23" width="87" height="16"/>
|
||||
<rect key="frame" x="18" y="155" width="87" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="a4I-vG-yCp">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -509,7 +654,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Lrf-gL-6EI">
|
||||
<rect key="frame" x="18" y="-4" width="177" height="18"/>
|
||||
<rect key="frame" x="18" y="128" width="177" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Attach C-1540 disk drive" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="tsq-YD-xw8">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -521,7 +666,7 @@ Gw
|
||||
<constraint firstItem="MTh-9p-FqC" firstAttribute="centerY" secondItem="ueK-gq-gaF" secondAttribute="centerY" id="8KD-Bm-KRA"/>
|
||||
<constraint firstItem="ueK-gq-gaF" firstAttribute="leading" secondItem="MTh-9p-FqC" secondAttribute="trailing" constant="8" symbolic="YES" id="9m6-6j-8j2"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Lrf-gL-6EI" secondAttribute="trailing" constant="20" symbolic="YES" id="M08-mP-Plz"/>
|
||||
<constraint firstItem="ueK-gq-gaF" firstAttribute="top" secondItem="fLI-XB-QCr" secondAttribute="top" constant="5" id="XN3-GK-BX9"/>
|
||||
<constraint firstItem="ueK-gq-gaF" firstAttribute="top" secondItem="fLI-XB-QCr" secondAttribute="top" constant="20" symbolic="YES" id="XN3-GK-BX9"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="ueK-gq-gaF" secondAttribute="trailing" constant="20" symbolic="YES" id="YZ9-7N-ssA"/>
|
||||
<constraint firstItem="MTh-9p-FqC" firstAttribute="leading" secondItem="fLI-XB-QCr" secondAttribute="leading" constant="20" symbolic="YES" id="bgZ-k9-IQC"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="2eV-Us-eEv" secondAttribute="trailing" constant="20" symbolic="YES" id="eiB-vH-17d"/>
|
||||
@@ -529,18 +674,18 @@ Gw
|
||||
<constraint firstItem="2eV-Us-eEv" firstAttribute="leading" secondItem="gRS-DK-rIy" secondAttribute="trailing" constant="8" symbolic="YES" id="hQ9-uy-KHg"/>
|
||||
<constraint firstItem="gRS-DK-rIy" firstAttribute="leading" secondItem="fLI-XB-QCr" secondAttribute="leading" constant="20" symbolic="YES" id="iyp-1K-rdC"/>
|
||||
<constraint firstItem="gRS-DK-rIy" firstAttribute="centerY" secondItem="2eV-Us-eEv" secondAttribute="centerY" id="qWf-Nb-PfJ"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="Lrf-gL-6EI" secondAttribute="bottom" constant="3" id="vNb-Sw-Mxw"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="Lrf-gL-6EI" secondAttribute="bottom" constant="20" symbolic="YES" id="vNb-Sw-Mxw"/>
|
||||
<constraint firstItem="Lrf-gL-6EI" firstAttribute="top" secondItem="2eV-Us-eEv" secondAttribute="bottom" constant="8" symbolic="YES" id="zBX-Qq-j5f"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</tabViewItem>
|
||||
<tabViewItem label="ZX80" identifier="zx80" id="tMH-kF-GUz">
|
||||
<view key="view" id="8hL-Vn-Hg0">
|
||||
<rect key="frame" x="10" y="7" width="400" height="76"/>
|
||||
<rect key="frame" x="10" y="7" width="400" height="223"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="I1a-Eu-5UB">
|
||||
<rect key="frame" x="108" y="47" width="116" height="25"/>
|
||||
<rect key="frame" x="108" y="179" width="116" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Unexpanded" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="axesIndependently" inset="2" selectedItem="4Sa-jR-xOd" id="B8M-do-Yod">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -554,7 +699,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="NCX-4e-lSu">
|
||||
<rect key="frame" x="18" y="53" width="87" height="16"/>
|
||||
<rect key="frame" x="18" y="185" width="87" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="e6x-TE-OC5">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -562,7 +707,7 @@ Gw
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ReP-bV-Thu">
|
||||
<rect key="frame" x="18" y="26" width="118" height="18"/>
|
||||
<rect key="frame" x="18" y="158" width="118" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Use ZX81 ROM" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="VQH-nv-Pfm">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -574,8 +719,8 @@ Gw
|
||||
<constraint firstItem="NCX-4e-lSu" firstAttribute="centerY" secondItem="I1a-Eu-5UB" secondAttribute="centerY" id="1ve-sc-QwI"/>
|
||||
<constraint firstItem="I1a-Eu-5UB" firstAttribute="leading" secondItem="NCX-4e-lSu" secondAttribute="trailing" constant="8" symbolic="YES" id="Bu6-60-74x"/>
|
||||
<constraint firstItem="NCX-4e-lSu" firstAttribute="leading" secondItem="8hL-Vn-Hg0" secondAttribute="leading" constant="20" symbolic="YES" id="W09-iG-ARI"/>
|
||||
<constraint firstItem="I1a-Eu-5UB" firstAttribute="top" secondItem="8hL-Vn-Hg0" secondAttribute="top" constant="5" id="fkf-wO-Q8i"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="ReP-bV-Thu" secondAttribute="bottom" constant="3" id="fmT-7E-hUT"/>
|
||||
<constraint firstItem="I1a-Eu-5UB" firstAttribute="top" secondItem="8hL-Vn-Hg0" secondAttribute="top" constant="20" symbolic="YES" id="fkf-wO-Q8i"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="ReP-bV-Thu" secondAttribute="bottom" constant="20" symbolic="YES" id="fmT-7E-hUT"/>
|
||||
<constraint firstItem="ReP-bV-Thu" firstAttribute="top" secondItem="I1a-Eu-5UB" secondAttribute="bottom" constant="8" symbolic="YES" id="hYk-xC-63o"/>
|
||||
<constraint firstItem="ReP-bV-Thu" firstAttribute="leading" secondItem="8hL-Vn-Hg0" secondAttribute="leading" constant="20" symbolic="YES" id="qen-KS-rWi"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="ReP-bV-Thu" secondAttribute="trailing" constant="20" symbolic="YES" id="r7F-DT-oRc"/>
|
||||
@@ -584,11 +729,11 @@ Gw
|
||||
</tabViewItem>
|
||||
<tabViewItem label="ZX81" identifier="zx81" id="Wnn-nQ-gZ6">
|
||||
<view key="view" id="bmd-gL-gzT">
|
||||
<rect key="frame" x="10" y="7" width="400" height="186"/>
|
||||
<rect key="frame" x="10" y="7" width="400" height="223"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5aO-UX-HnX">
|
||||
<rect key="frame" x="108" y="157" width="116" height="25"/>
|
||||
<rect key="frame" x="108" y="179" width="116" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Unexpanded" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="axesIndependently" inset="2" selectedItem="7QC-Ij-hES" id="d3W-Gl-3Mf">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
@@ -602,7 +747,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8tU-73-XEE">
|
||||
<rect key="frame" x="18" y="163" width="87" height="16"/>
|
||||
<rect key="frame" x="18" y="185" width="87" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Memory Size:" id="z4b-oR-Yl2">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -613,37 +758,37 @@ Gw
|
||||
<constraints>
|
||||
<constraint firstItem="8tU-73-XEE" firstAttribute="centerY" secondItem="5aO-UX-HnX" secondAttribute="centerY" id="1Jm-YL-OKU"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="5aO-UX-HnX" secondAttribute="trailing" constant="20" symbolic="YES" id="ALj-0x-bFb"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="5aO-UX-HnX" secondAttribute="bottom" constant="5" id="LJ2-W9-fOf"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="5aO-UX-HnX" secondAttribute="bottom" constant="20" symbolic="YES" id="LJ2-W9-fOf"/>
|
||||
<constraint firstItem="8tU-73-XEE" firstAttribute="leading" secondItem="bmd-gL-gzT" secondAttribute="leading" constant="20" symbolic="YES" id="M6Y-jN-LAf"/>
|
||||
<constraint firstItem="5aO-UX-HnX" firstAttribute="top" secondItem="bmd-gL-gzT" secondAttribute="top" constant="5" id="PPD-Jz-qCL"/>
|
||||
<constraint firstItem="5aO-UX-HnX" firstAttribute="top" secondItem="bmd-gL-gzT" secondAttribute="top" constant="20" symbolic="YES" id="PPD-Jz-qCL"/>
|
||||
<constraint firstItem="5aO-UX-HnX" firstAttribute="leading" secondItem="8tU-73-XEE" secondAttribute="trailing" constant="8" symbolic="YES" id="j1u-n4-2IK"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</tabViewItem>
|
||||
<tabViewItem label="ZX Spectrum" identifier="spectrum" id="HQv-oF-k8b">
|
||||
<view key="view" id="bMx-F6-JUb">
|
||||
<rect key="frame" x="10" y="7" width="400" height="186"/>
|
||||
<rect key="frame" x="10" y="7" width="400" height="223"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gFZ-d4-WFv">
|
||||
<rect key="frame" x="67" y="157" width="76" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="16kb" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" tag="16" imageScaling="axesIndependently" inset="2" selectedItem="Fo7-NL-Kv5" id="tYs-sA-oek">
|
||||
<rect key="frame" x="67" y="179" width="76" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="16kb" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="16" imageScaling="axesIndependently" inset="2" selectedItem="Fo7-NL-Kv5" id="tYs-sA-oek">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="8lt-dk-zPr">
|
||||
<items>
|
||||
<menuItem title="16kb" tag="16" id="Fo7-NL-Kv5"/>
|
||||
<menuItem title="16kb" state="on" tag="16" id="Fo7-NL-Kv5"/>
|
||||
<menuItem title="48kb" tag="48" id="xks-Rv-Umd"/>
|
||||
<menuItem title="128kb" tag="128" id="w8h-lY-JLX"/>
|
||||
<menuItem title="+2" tag="2" id="Vvu-ua-pjg"/>
|
||||
<menuItem title="+2a" state="on" tag="21" id="bFk-nC-Txe"/>
|
||||
<menuItem title="+2a" tag="21" id="bFk-nC-Txe"/>
|
||||
<menuItem title="+3" tag="3" id="jwx-fZ-vXp"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fJ3-ma-Byy">
|
||||
<rect key="frame" x="18" y="163" width="46" height="16"/>
|
||||
<rect key="frame" x="18" y="185" width="46" height="16"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Model:" id="JId-Tp-LrE">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -655,23 +800,23 @@ Gw
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="gFZ-d4-WFv" secondAttribute="trailing" constant="20" symbolic="YES" id="90E-uI-MQg"/>
|
||||
<constraint firstItem="fJ3-ma-Byy" firstAttribute="leading" secondItem="bMx-F6-JUb" secondAttribute="leading" constant="20" symbolic="YES" id="9kE-iQ-dxd"/>
|
||||
<constraint firstItem="fJ3-ma-Byy" firstAttribute="centerY" secondItem="gFZ-d4-WFv" secondAttribute="centerY" id="LxG-5E-Q5Y"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="gFZ-d4-WFv" secondAttribute="bottom" constant="5" id="d8S-vX-B5e"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="gFZ-d4-WFv" secondAttribute="bottom" constant="20" symbolic="YES" id="d8S-vX-B5e"/>
|
||||
<constraint firstItem="gFZ-d4-WFv" firstAttribute="leading" secondItem="fJ3-ma-Byy" secondAttribute="trailing" constant="8" symbolic="YES" id="hKS-47-R2y"/>
|
||||
<constraint firstItem="gFZ-d4-WFv" firstAttribute="top" secondItem="bMx-F6-JUb" secondAttribute="top" constant="5" id="wsX-Wq-iPt"/>
|
||||
<constraint firstItem="gFZ-d4-WFv" firstAttribute="top" secondItem="bMx-F6-JUb" secondAttribute="top" constant="20" symbolic="YES" id="wsX-Wq-iPt"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</tabViewItem>
|
||||
</tabViewItems>
|
||||
</tabView>
|
||||
<scrollView borderType="line" autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="10" verticalLineScroll="24" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="z5Q-Bs-hJj">
|
||||
<rect key="frame" x="20" y="60" width="130" height="201"/>
|
||||
<rect key="frame" x="20" y="60" width="130" height="238"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" heightSizable="YES"/>
|
||||
<clipView key="contentView" id="O8s-Vw-9yQ">
|
||||
<rect key="frame" x="1" y="1" width="128" height="199"/>
|
||||
<rect key="frame" x="1" y="1" width="128" height="236"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="24" id="3go-Eb-GOy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="128" height="199"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="128" height="236"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="17" height="0.0"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -708,7 +853,7 @@ Gw
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="VAc-6N-O7q">
|
||||
<rect key="frame" x="18" y="269" width="554" height="27"/>
|
||||
<rect key="frame" x="18" y="306" width="554" height="27"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Choose a machine" id="32m-Vs-dPO">
|
||||
<font key="font" textStyle="title2" name=".SFNS-Regular"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -737,7 +882,7 @@ Gw
|
||||
<constraint firstItem="9YM-5x-pc0" firstAttribute="top" secondItem="z5Q-Bs-hJj" secondAttribute="bottom" constant="14" id="suf-rn-Bmy"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<point key="canvasLocation" x="-1" y="88"/>
|
||||
<point key="canvasLocation" x="-1" y="106.5"/>
|
||||
</window>
|
||||
<customObject id="192-Eb-Rpg" customClass="MachinePicker" customModule="Clock_Signal" customModuleProvider="target">
|
||||
<connections>
|
||||
@@ -750,6 +895,11 @@ Gw
|
||||
<outlet property="electronAP6Button" destination="cG2-Ph-S3Z" id="vkq-1J-KBG"/>
|
||||
<outlet property="electronDFSButton" destination="JqM-IK-FMP" id="C80-1k-TdQ"/>
|
||||
<outlet property="electronSidewaysRAMButton" destination="lzo-8g-o4S" id="LtS-wv-tMf"/>
|
||||
<outlet property="enterpriseBASICButton" destination="hIr-GH-7xi" id="fM6-It-9UO"/>
|
||||
<outlet property="enterpriseDOSButton" destination="syE-e7-TjU" id="sCW-Bj-ZTW"/>
|
||||
<outlet property="enterpriseEXOSButton" destination="nen-Za-7zH" id="NwS-ua-FdA"/>
|
||||
<outlet property="enterpriseModelButton" destination="PhH-bu-pb5" id="8wD-sW-aBw"/>
|
||||
<outlet property="enterpriseSpeedButton" destination="Lfl-5c-b8j" id="ClN-yN-Nbx"/>
|
||||
<outlet property="machineNameTable" destination="3go-Eb-GOy" id="Ppf-S0-IP1"/>
|
||||
<outlet property="machineSelector" destination="VUb-QG-x7c" id="crR-hB-jGd"/>
|
||||
<outlet property="macintoshModelTypeButton" destination="xa6-NA-JY5" id="2jX-PY-v2z"/>
|
||||
|
||||
@@ -27,14 +27,21 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
@IBOutlet var appleIIgsModelButton: NSPopUpButton!
|
||||
@IBOutlet var appleIIgsMemorySizeButton: NSPopUpButton!
|
||||
|
||||
// MARK: - CPC properties
|
||||
@IBOutlet var cpcModelTypeButton: NSPopUpButton!
|
||||
|
||||
// MARK: - Electron properties
|
||||
@IBOutlet var electronDFSButton: NSButton!
|
||||
@IBOutlet var electronADFSButton: NSButton!
|
||||
@IBOutlet var electronAP6Button: NSButton!
|
||||
@IBOutlet var electronSidewaysRAMButton: NSButton!
|
||||
|
||||
// MARK: - CPC properties
|
||||
@IBOutlet var cpcModelTypeButton: NSPopUpButton!
|
||||
// MARK: - Enterprise properties
|
||||
@IBOutlet var enterpriseModelButton: NSPopUpButton!
|
||||
@IBOutlet var enterpriseSpeedButton: NSPopUpButton!
|
||||
@IBOutlet var enterpriseEXOSButton: NSPopUpButton!
|
||||
@IBOutlet var enterpriseBASICButton: NSPopUpButton!
|
||||
@IBOutlet var enterpriseDOSButton: NSPopUpButton!
|
||||
|
||||
// MARK: - Macintosh properties
|
||||
@IBOutlet var macintoshModelTypeButton: NSPopUpButton!
|
||||
@@ -93,14 +100,21 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
appleIIgsModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.appleIIgsModel"))
|
||||
appleIIgsMemorySizeButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.appleIIgsMemorySize"))
|
||||
|
||||
// CPC settings
|
||||
cpcModelTypeButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.cpcModel"))
|
||||
|
||||
// Electron settings
|
||||
electronDFSButton.state = standardUserDefaults.bool(forKey: "new.electronDFS") ? .on : .off
|
||||
electronADFSButton.state = standardUserDefaults.bool(forKey: "new.electronADFS") ? .on : .off
|
||||
electronAP6Button.state = standardUserDefaults.bool(forKey: "new.electronAP6") ? .on : .off
|
||||
electronSidewaysRAMButton.state = standardUserDefaults.bool(forKey: "new.electronSidewaysRAM") ? .on : .off
|
||||
|
||||
// CPC settings
|
||||
cpcModelTypeButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.cpcModel"))
|
||||
// Enterprise settings
|
||||
enterpriseModelButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseModel"))
|
||||
enterpriseSpeedButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseSpeed"))
|
||||
enterpriseEXOSButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseEXOSVersion"))
|
||||
enterpriseBASICButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseBASICVersion"))
|
||||
enterpriseDOSButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.enterpriseDOS"))
|
||||
|
||||
// Macintosh settings
|
||||
macintoshModelTypeButton.selectItem(withTag: standardUserDefaults.integer(forKey: "new.macintoshModel"))
|
||||
@@ -143,14 +157,21 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
standardUserDefaults.set(appleIIgsModelButton.selectedTag(), forKey: "new.appleIIgsModel")
|
||||
standardUserDefaults.set(appleIIgsMemorySizeButton.selectedTag(), forKey: "new.appleIIgsMemorySize")
|
||||
|
||||
// CPC settings
|
||||
standardUserDefaults.set(cpcModelTypeButton.selectedTag(), forKey: "new.cpcModel")
|
||||
|
||||
// Electron settings
|
||||
standardUserDefaults.set(electronDFSButton.state == .on, forKey: "new.electronDFS")
|
||||
standardUserDefaults.set(electronADFSButton.state == .on, forKey: "new.electronADFS")
|
||||
standardUserDefaults.set(electronAP6Button.state == .on, forKey: "new.electronAP6")
|
||||
standardUserDefaults.set(electronSidewaysRAMButton.state == .on, forKey: "new.electronSidewaysRAM")
|
||||
|
||||
// CPC settings
|
||||
standardUserDefaults.set(cpcModelTypeButton.selectedTag(), forKey: "new.cpcModel")
|
||||
// Enterprise settings
|
||||
standardUserDefaults.set(enterpriseModelButton.selectedTag(), forKey: "new.enterpriseModel")
|
||||
standardUserDefaults.set(enterpriseSpeedButton.selectedTag(), forKey: "new.enterpriseSpeed")
|
||||
standardUserDefaults.set(enterpriseEXOSButton.selectedTag(), forKey: "new.enterpriseEXOSVersion")
|
||||
standardUserDefaults.set(enterpriseBASICButton.selectedTag(), forKey: "new.enterpriseBASICVersion")
|
||||
standardUserDefaults.set(enterpriseDOSButton.selectedTag(), forKey: "new.enterpriseDOS")
|
||||
|
||||
// Macintosh settings
|
||||
standardUserDefaults.set(macintoshModelTypeButton.selectedTag(), forKey: "new.macintoshModel")
|
||||
@@ -205,12 +226,6 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
storeOptions()
|
||||
|
||||
switch machineSelector.selectedTabViewItem!.identifier as! String {
|
||||
case "electron":
|
||||
return CSStaticAnalyser(
|
||||
electronDFS: electronDFSButton.state == .on,
|
||||
adfs: electronADFSButton.state == .on,
|
||||
ap6: electronAP6Button.state == .on,
|
||||
sidewaysRAM: electronSidewaysRAMButton.state == .on)
|
||||
|
||||
case "appleii":
|
||||
var model: CSMachineAppleIIModel = .appleII
|
||||
@@ -241,22 +256,71 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
default: model = .ROM00
|
||||
}
|
||||
|
||||
let memorySize = Kilobytes(appleIIgsMemorySizeButton.selectedItem!.tag)
|
||||
let memorySize = Kilobytes(appleIIgsMemorySizeButton.selectedTag())
|
||||
return CSStaticAnalyser(appleIIgsModel: model, memorySize: memorySize)
|
||||
|
||||
case "atarist":
|
||||
return CSStaticAnalyser(atariSTModel: .model512k)
|
||||
|
||||
case "cpc":
|
||||
switch cpcModelTypeButton.selectedItem!.tag {
|
||||
switch cpcModelTypeButton.selectedTag() {
|
||||
case 464: return CSStaticAnalyser(amstradCPCModel: .model464)
|
||||
case 664: return CSStaticAnalyser(amstradCPCModel: .model664)
|
||||
case 6128: fallthrough
|
||||
default: return CSStaticAnalyser(amstradCPCModel: .model6128)
|
||||
}
|
||||
|
||||
case "electron":
|
||||
return CSStaticAnalyser(
|
||||
electronDFS: electronDFSButton.state == .on,
|
||||
adfs: electronADFSButton.state == .on,
|
||||
ap6: electronAP6Button.state == .on,
|
||||
sidewaysRAM: electronSidewaysRAMButton.state == .on)
|
||||
|
||||
case "enterprise":
|
||||
var model: CSMachineEnterpriseModel = .model128
|
||||
switch enterpriseModelButton.selectedTag() {
|
||||
case 64: model = .model64
|
||||
case 256: model = .model256
|
||||
case 128: fallthrough
|
||||
default: model = .model128
|
||||
}
|
||||
|
||||
var speed: CSMachineEnterpriseSpeed = .speed4MHz
|
||||
switch enterpriseSpeedButton.selectedTag() {
|
||||
case 6: speed = .speed6MHz
|
||||
case 4: fallthrough
|
||||
default: speed = .speed4MHz
|
||||
}
|
||||
|
||||
var exos: CSMachineEnterpriseEXOS = .version21
|
||||
switch enterpriseEXOSButton.selectedTag() {
|
||||
case 10: exos = .version10
|
||||
case 20: exos = .version20
|
||||
case 21: fallthrough
|
||||
default: exos = .version21
|
||||
}
|
||||
|
||||
var basic: CSMachineEnterpriseBASIC = .version21
|
||||
switch enterpriseBASICButton.selectedTag() {
|
||||
case 0: basic = .none
|
||||
case 10: basic = .version10
|
||||
case 11: basic = .version11
|
||||
case 21: fallthrough
|
||||
default: basic = .version21
|
||||
}
|
||||
|
||||
var dos: CSMachineEnterpriseDOS = .dosNone
|
||||
switch enterpriseDOSButton.selectedTag() {
|
||||
case 1: dos = .DOSEXDOS
|
||||
case 0: fallthrough
|
||||
default: dos = .dosNone
|
||||
}
|
||||
|
||||
return CSStaticAnalyser(enterpriseModel: model, speed: speed, exosVersion: exos, basicVersion: basic, dos: dos)
|
||||
|
||||
case "mac":
|
||||
switch macintoshModelTypeButton.selectedItem!.tag {
|
||||
switch macintoshModelTypeButton.selectedTag() {
|
||||
case 0: return CSStaticAnalyser(macintoshModel: .model128k)
|
||||
case 1: return CSStaticAnalyser(macintoshModel: .model512k)
|
||||
case 2: return CSStaticAnalyser(macintoshModel: .model512ke)
|
||||
@@ -266,7 +330,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
|
||||
case "msx":
|
||||
let hasDiskDrive = msxHasDiskDriveButton.state == .on
|
||||
switch msxRegionButton.selectedItem!.tag {
|
||||
switch msxRegionButton.selectedTag() {
|
||||
case 2:
|
||||
return CSStaticAnalyser(msxRegion: .japanese, hasDiskDrive: hasDiskDrive)
|
||||
case 1:
|
||||
@@ -287,7 +351,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
|
||||
}
|
||||
var model: CSMachineOricModel = .oric1
|
||||
switch oricModelTypeButton.selectedItem!.tag {
|
||||
switch oricModelTypeButton.selectedTag() {
|
||||
case 1: model = .oricAtmos
|
||||
case 2: model = .pravetz
|
||||
default: break
|
||||
@@ -297,7 +361,7 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
|
||||
case "spectrum":
|
||||
var model: CSMachineSpectrumModel = .plus2a
|
||||
switch spectrumModelTypeButton.selectedItem!.tag {
|
||||
switch spectrumModelTypeButton.selectedTag() {
|
||||
case 16: model = .sixteenK
|
||||
case 48: model = .fortyEightK
|
||||
case 128: model = .oneTwoEightK
|
||||
@@ -310,9 +374,9 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
return CSStaticAnalyser(spectrumModel: model)
|
||||
|
||||
case "vic20":
|
||||
let memorySize = Kilobytes(vic20MemorySizeButton.selectedItem!.tag)
|
||||
let memorySize = Kilobytes(vic20MemorySizeButton.selectedTag())
|
||||
let hasC1540 = vic20HasC1540Button.state == .on
|
||||
switch vic20RegionButton.selectedItem!.tag {
|
||||
switch vic20RegionButton.selectedTag() {
|
||||
case 1:
|
||||
return CSStaticAnalyser(vic20Region: .american, memorySize: memorySize, hasC1540: hasC1540)
|
||||
case 2:
|
||||
@@ -327,10 +391,10 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
}
|
||||
|
||||
case "zx80":
|
||||
return CSStaticAnalyser(zx80MemorySize: Kilobytes(zx80MemorySizeButton.selectedItem!.tag), useZX81ROM: zx80UsesZX81ROMButton.state == .on)
|
||||
return CSStaticAnalyser(zx80MemorySize: Kilobytes(zx80MemorySizeButton.selectedTag()), useZX81ROM: zx80UsesZX81ROMButton.state == .on)
|
||||
|
||||
case "zx81":
|
||||
return CSStaticAnalyser(zx81MemorySize: Kilobytes(zx81MemorySizeButton.selectedItem!.tag))
|
||||
return CSStaticAnalyser(zx81MemorySize: Kilobytes(zx81MemorySizeButton.selectedTag()))
|
||||
|
||||
default: return CSStaticAnalyser()
|
||||
}
|
||||
|
||||
102
OSBindings/Mac/Clock SignalTests/EnterpriseDaveTests.mm
Normal file
102
OSBindings/Mac/Clock SignalTests/EnterpriseDaveTests.mm
Normal file
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// EnterpriseDaveTests.m
|
||||
// Clock SignalTests
|
||||
//
|
||||
// Created by Thomas Harte on 02/07/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#include "../../../Machines/Enterprise/Dave.hpp"
|
||||
#include <memory>
|
||||
|
||||
@interface EnterpriseDaveTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation EnterpriseDaveTests {
|
||||
std::unique_ptr<Enterprise::Dave::TimedInterruptSource> _interruptSource;
|
||||
}
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
_interruptSource = std::make_unique<Enterprise::Dave::TimedInterruptSource>();
|
||||
}
|
||||
|
||||
/// Tests that the programmable timer flag toggles and produces interrupts
|
||||
/// at the rate specified, and that the flag toggles when interrupts are signalled.
|
||||
- (void)performTestExpectedInterrupts:(double)expectedInterruptsPerSecond mode:(int)mode {
|
||||
// If a programmable timer mode is requested, synchronise both channels.
|
||||
if(mode >= 2) {
|
||||
_interruptSource->write(0xa7, 3);
|
||||
_interruptSource->run_for(Cycles(2));
|
||||
}
|
||||
|
||||
// Set mode (and disable sync, if it was applied).
|
||||
_interruptSource->write(0xa7, mode << 5);
|
||||
|
||||
int toggles = 0;
|
||||
int interrupts = 0;
|
||||
uint8_t dividerState = _interruptSource->get_divider_state() & 1;
|
||||
int nextSequencePoint = _interruptSource->get_next_sequence_point().as<int>();
|
||||
|
||||
for(int c = 0; c < 250000 * 5; c++) {
|
||||
// Advance one cycle. Clock is 500,000 Hz.
|
||||
_interruptSource->run_for(Cycles(2));
|
||||
--nextSequencePoint;
|
||||
|
||||
// Check for a status bit change.
|
||||
const uint8_t newDividerState = _interruptSource->get_divider_state();
|
||||
const bool didToggle = (dividerState^newDividerState)&0x1;
|
||||
dividerState = newDividerState;
|
||||
toggles += didToggle;
|
||||
|
||||
// Check for the relevant interrupt.
|
||||
const uint8_t newInterrupts = _interruptSource->get_new_interrupts();
|
||||
if(newInterrupts) {
|
||||
XCTAssertEqual(nextSequencePoint, 0);
|
||||
nextSequencePoint = _interruptSource->get_next_sequence_point().as<int>();
|
||||
|
||||
if(newInterrupts & 0x02) {
|
||||
++interrupts;
|
||||
XCTAssertTrue(didToggle);
|
||||
} else {
|
||||
// Failing that, confirm that the other interrupt happend.
|
||||
XCTAssertTrue(newInterrupts & 0x08);
|
||||
}
|
||||
}
|
||||
|
||||
XCTAssertEqual(nextSequencePoint, _interruptSource->get_next_sequence_point().as<int>(), @"At cycle %d", c);
|
||||
}
|
||||
|
||||
XCTAssertEqual(toggles, int(expectedInterruptsPerSecond * 5.0));
|
||||
XCTAssertEqual(interrupts, int(expectedInterruptsPerSecond * 5.0));
|
||||
}
|
||||
|
||||
- (void)test1kHzTimer {
|
||||
[self performTestExpectedInterrupts:1000.0 mode:0];
|
||||
}
|
||||
|
||||
- (void)test50HzTimer {
|
||||
[self performTestExpectedInterrupts:50.0 mode:1];
|
||||
}
|
||||
|
||||
- (void)testTone0Timer {
|
||||
// Set tone generator 0 as the interrupt source, with a divider of 137;
|
||||
// apply sync momentarily.
|
||||
_interruptSource->write(0, 137);
|
||||
_interruptSource->write(1, 0);
|
||||
|
||||
[self performTestExpectedInterrupts:250000.0/(138.0 * 2.0) mode:2];
|
||||
}
|
||||
|
||||
- (void)testTone1Timer {
|
||||
// Set tone generator 1 as the interrupt source, with a divider of 961;
|
||||
// apply sync momentarily.
|
||||
_interruptSource->write(2, 961 & 0xff);
|
||||
_interruptSource->write(3, (961 >> 8) & 0xff);
|
||||
|
||||
[self performTestExpectedInterrupts:250000.0/(962.0 * 2.0) mode:3];
|
||||
}
|
||||
|
||||
@end
|
||||
72
OSBindings/Mac/Clock SignalTests/EnterpriseNickTests.mm
Normal file
72
OSBindings/Mac/Clock SignalTests/EnterpriseNickTests.mm
Normal file
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// EnterpriseNickTests.m
|
||||
// Clock SignalTests
|
||||
//
|
||||
// Created by Thomas Harte on 18/06/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#include "../../../Machines/Enterprise/Nick.hpp"
|
||||
#include <memory>
|
||||
|
||||
@interface EnterpriseNickTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation EnterpriseNickTests {
|
||||
std::unique_ptr<Enterprise::Nick> _nick;
|
||||
uint8_t _ram[64*1024];
|
||||
int _totalLines;
|
||||
}
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
|
||||
// Create a Nick.
|
||||
_nick = std::make_unique<Enterprise::Nick>(_ram);
|
||||
|
||||
// Add a basic line table of blocks proceeding in length: 1, 2, 3, 4, etc and toggling the interrupt bit.
|
||||
_totalLines = 0;
|
||||
int nextLength = 0;
|
||||
int pointer = 0;
|
||||
uint8_t interruptFlag = 0x80;
|
||||
while(nextLength < 256) {
|
||||
_ram[pointer] = 0x100 - nextLength;
|
||||
_ram[pointer+1] = interruptFlag;
|
||||
|
||||
pointer += 16;
|
||||
++nextLength;
|
||||
interruptFlag ^= 0x80;
|
||||
_totalLines += nextLength;
|
||||
}
|
||||
|
||||
// For now: assume Nick starts at address 0 from creation.
|
||||
}
|
||||
|
||||
- (void)testInterruptPrediction {
|
||||
// Run for the number of cycles implied by the number of lines.
|
||||
int next_sequence_point = _nick->get_next_sequence_point().as<int>();
|
||||
bool last_interrupt_line = _nick->get_interrupt_line();
|
||||
|
||||
for(int c = 0; c < _totalLines*912; c++) {
|
||||
// Check that interrupt line transitions happen only on declared sequence points.
|
||||
_nick->run_for(Cycles(1));
|
||||
--next_sequence_point;
|
||||
const bool interrupt_line = _nick->get_interrupt_line();
|
||||
|
||||
if(interrupt_line != last_interrupt_line) {
|
||||
XCTAssertEqual(next_sequence_point, 0);
|
||||
}
|
||||
last_interrupt_line = interrupt_line;
|
||||
|
||||
if(!next_sequence_point) {
|
||||
next_sequence_point = _nick->get_next_sequence_point().as<int>();
|
||||
} else {
|
||||
const int expected_next_sequence_point = _nick->get_next_sequence_point().as<int>();
|
||||
XCTAssertEqual(next_sequence_point, expected_next_sequence_point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -47,6 +47,7 @@ SOURCES += \
|
||||
$$SRC/Analyser/Static/Commodore/*.cpp \
|
||||
$$SRC/Analyser/Static/Disassembler/*.cpp \
|
||||
$$SRC/Analyser/Static/DiskII/*.cpp \
|
||||
$$SRC/Analyser/Static/Enterprise/*.cpp \
|
||||
$$SRC/Analyser/Static/Macintosh/*.cpp \
|
||||
$$SRC/Analyser/Static/MSX/*.cpp \
|
||||
$$SRC/Analyser/Static/Oric/*.cpp \
|
||||
@@ -92,6 +93,7 @@ SOURCES += \
|
||||
$$SRC/Machines/Commodore/1540/Implementation/*.cpp \
|
||||
$$SRC/Machines/Commodore/Vic-20/*.cpp \
|
||||
$$SRC/Machines/Electron/*.cpp \
|
||||
$$SRC/Machines/Enterprise/*.cpp \
|
||||
$$SRC/Machines/MasterSystem/*.cpp \
|
||||
$$SRC/Machines/MSX/*.cpp \
|
||||
$$SRC/Machines/Oric/*.cpp \
|
||||
@@ -166,6 +168,7 @@ HEADERS += \
|
||||
$$SRC/Analyser/Static/Commodore/*.hpp \
|
||||
$$SRC/Analyser/Static/Disassembler/*.hpp \
|
||||
$$SRC/Analyser/Static/DiskII/*.hpp \
|
||||
$$SRC/Analyser/Static/Enterprise/*.hpp \
|
||||
$$SRC/Analyser/Static/Macintosh/*.hpp \
|
||||
$$SRC/Analyser/Static/MSX/*.hpp \
|
||||
$$SRC/Analyser/Static/Oric/*.hpp \
|
||||
@@ -221,6 +224,7 @@ HEADERS += \
|
||||
$$SRC/Machines/Commodore/1540/Implementation/*.hpp \
|
||||
$$SRC/Machines/Commodore/Vic-20/*.hpp \
|
||||
$$SRC/Machines/Electron/*.hpp \
|
||||
$$SRC/Machines/Enterprise/*.hpp \
|
||||
$$SRC/Machines/MasterSystem/*.hpp \
|
||||
$$SRC/Machines/MSX/*.hpp \
|
||||
$$SRC/Machines/Oric/*.hpp \
|
||||
|
||||
@@ -424,6 +424,10 @@ void MainWindow::launchMachine() {
|
||||
addEnhancementsMenu(settingsPrefix, true, false);
|
||||
break;
|
||||
|
||||
case Analyser::Machine::Enterprise:
|
||||
addDisplayMenu(settingsPrefix, "Composite", "", "", "RGB");
|
||||
break;
|
||||
|
||||
case Analyser::Machine::Macintosh:
|
||||
addEnhancementsMenu(settingsPrefix, false, true);
|
||||
break;
|
||||
@@ -953,6 +957,7 @@ void MainWindow::setButtonPressed(int index, bool isPressed) {
|
||||
#include "../../Analyser/Static/AppleIIgs/Target.hpp"
|
||||
#include "../../Analyser/Static/AtariST/Target.hpp"
|
||||
#include "../../Analyser/Static/Commodore/Target.hpp"
|
||||
#include "../../Analyser/Static/Enterprise/Target.hpp"
|
||||
#include "../../Analyser/Static/Macintosh/Target.hpp"
|
||||
#include "../../Analyser/Static/MSX/Target.hpp"
|
||||
#include "../../Analyser/Static/Oric/Target.hpp"
|
||||
@@ -973,6 +978,7 @@ void MainWindow::startMachine() {
|
||||
TEST(amstradCPC);
|
||||
TEST(atariST);
|
||||
TEST(electron);
|
||||
TEST(enterprise);
|
||||
TEST(macintosh);
|
||||
TEST(msx);
|
||||
TEST(oric);
|
||||
@@ -1057,6 +1063,43 @@ void MainWindow::start_electron() {
|
||||
launchTarget(std::move(target));
|
||||
}
|
||||
|
||||
void MainWindow::start_enterprise() {
|
||||
using Target = Analyser::Static::Enterprise::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
|
||||
switch(ui->enterpriseModelComboBox->currentIndex()) {
|
||||
default: target->model = Target::Model::Enterprise64; break;
|
||||
case 1: target->model = Target::Model::Enterprise128; break;
|
||||
case 2: target->model = Target::Model::Enterprise256; break;
|
||||
}
|
||||
|
||||
switch(ui->enterpriseSpeedComboBox->currentIndex()) {
|
||||
default: target->speed = Target::Speed::FourMHz; break;
|
||||
case 1: target->speed = Target::Speed::SixMHz; break;
|
||||
}
|
||||
|
||||
switch(ui->enterpriseEXOSComboBox->currentIndex()) {
|
||||
default: target->exos_version = Target::EXOSVersion::v10; break;
|
||||
case 1: target->exos_version = Target::EXOSVersion::v20; break;
|
||||
case 2: target->exos_version = Target::EXOSVersion::v21; break;
|
||||
case 3: target->exos_version = Target::EXOSVersion::v23; break;
|
||||
}
|
||||
|
||||
switch(ui->enterpriseBASICComboBox->currentIndex()) {
|
||||
default: target->basic_version = Target::BASICVersion::None; break;
|
||||
case 1: target->basic_version = Target::BASICVersion::v10; break;
|
||||
case 2: target->basic_version = Target::BASICVersion::v11; break;
|
||||
case 3: target->basic_version = Target::BASICVersion::v21; break;
|
||||
}
|
||||
|
||||
switch(ui->enterpriseDOSComboBox->currentIndex()) {
|
||||
default: target->dos = Target::DOS::None; break;
|
||||
case 1: target->dos = Target::DOS::EXDOS; break;
|
||||
}
|
||||
|
||||
launchTarget(std::move(target));
|
||||
}
|
||||
|
||||
void MainWindow::start_macintosh() {
|
||||
using Target = Analyser::Static::Macintosh::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
@@ -1212,6 +1255,13 @@ void MainWindow::launchTarget(std::unique_ptr<Analyser::Static::Target> &&target
|
||||
CheckBox(electronAP6CheckBox, "electron.hasAP6"); \
|
||||
CheckBox(electronSidewaysRAMCheckBox, "electron.fillSidewaysRAM"); \
|
||||
\
|
||||
/* Enterprise. */ \
|
||||
ComboBox(enterpriseModelComboBox, "enterprise.model"); \
|
||||
ComboBox(enterpriseSpeedComboBox, "enterprise.speed"); \
|
||||
ComboBox(enterpriseEXOSComboBox, "enterprise.exos"); \
|
||||
ComboBox(enterpriseBASICComboBox, "enterprise.basic"); \
|
||||
ComboBox(enterpriseDOSComboBox, "enterprise.dos"); \
|
||||
\
|
||||
/* Macintosh. */ \
|
||||
ComboBox(macintoshModelComboBox, "macintosh.model"); \
|
||||
\
|
||||
@@ -1265,6 +1315,7 @@ void MainWindow::restoreSelections() {
|
||||
// MARK: - Activity observation
|
||||
|
||||
void MainWindow::addActivityObserver() {
|
||||
ledStatuses.clear();
|
||||
auto activitySource = machine->activity_source();
|
||||
if(!activitySource) return;
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
|
||||
void start_amstradCPC();
|
||||
void start_atariST();
|
||||
void start_electron();
|
||||
void start_enterprise();
|
||||
void start_macintosh();
|
||||
void start_msx();
|
||||
void start_oric();
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<item>
|
||||
<widget class="QTabWidget" name="machineSelectionTabs">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>5</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="appleIITab">
|
||||
<attribute name="title">
|
||||
@@ -310,6 +310,159 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="enterpriseTab">
|
||||
<attribute name="title">
|
||||
<string>Enterprise</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="enterpriseLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="enterpriseHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="enterpriseFormLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="enterpriseModelLabel">
|
||||
<property name="text">
|
||||
<string>Model:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="enterpriseModelComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Enterprise 64</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Enterprise 128</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Enterprise 256</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="enterpriseSpeedLabel">
|
||||
<property name="text">
|
||||
<string>Speed:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="enterpriseSpeedComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4 MHz</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>6 MHz</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="enterpriseEXOSLabel">
|
||||
<property name="text">
|
||||
<string>EXOS:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="enterpriseEXOSComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Version 1.0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Version 2.0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Version 2.1</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="enterpriseBASICLabel">
|
||||
<property name="text">
|
||||
<string>BASIC:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="enterpriseBASICComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Version 1.0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Version 1.1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Version 2.1</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="enterpriseDOSLabel">
|
||||
<property name="text">
|
||||
<string>DOS:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="enterpriseDOSComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>EXDOS</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="enterpriseHSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="macintoshTab">
|
||||
<attribute name="title">
|
||||
<string>Macintosh</string>
|
||||
@@ -533,75 +686,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="spectrumTab">
|
||||
<attribute name="title">
|
||||
<string>Spectrum</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="spectrumLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="spectrumHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="spectrumFormLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="spectrumModelLabel">
|
||||
<property name="text">
|
||||
<string>Model:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="spectrumModelComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>16kb</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>48kb</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>128kb</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>+2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>+2a</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>+3</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spectrumHSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="vic20Tab">
|
||||
<attribute name="title">
|
||||
<string>Vic-20</string>
|
||||
@@ -830,6 +914,75 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="spectrumTab">
|
||||
<attribute name="title">
|
||||
<string>ZX Spectrum</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="spectrumLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="spectrumHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="spectrumFormLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="spectrumModelLabel">
|
||||
<property name="text">
|
||||
<string>Model:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="spectrumModelComboBox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>16kb</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>48kb</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>128kb</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>+2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>+2a</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>+3</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spectrumHSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignBottom">
|
||||
|
||||
@@ -31,6 +31,7 @@ SOURCES += glob.glob('../../Analyser/Static/Coleco/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/Commodore/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/Disassembler/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/DiskII/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/Enterprise/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/Macintosh/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/MSX/*.cpp')
|
||||
SOURCES += glob.glob('../../Analyser/Static/Oric/*.cpp')
|
||||
@@ -79,6 +80,7 @@ SOURCES += glob.glob('../../Machines/Commodore/*.cpp')
|
||||
SOURCES += glob.glob('../../Machines/Commodore/1540/Implementation/*.cpp')
|
||||
SOURCES += glob.glob('../../Machines/Commodore/Vic-20/*.cpp')
|
||||
SOURCES += glob.glob('../../Machines/Electron/*.cpp')
|
||||
SOURCES += glob.glob('../../Machines/Enterprise/*.cpp')
|
||||
SOURCES += glob.glob('../../Machines/MasterSystem/*.cpp')
|
||||
SOURCES += glob.glob('../../Machines/MSX/*.cpp')
|
||||
SOURCES += glob.glob('../../Machines/Oric/*.cpp')
|
||||
|
||||
@@ -832,11 +832,14 @@ int main(int argc, char *argv[]) {
|
||||
} else if(volume < 0.0 || volume > 1.0) {
|
||||
std::cerr << "Cannot run with volume " << volume_string << "; volumes must be between 0.0 and 1.0." << std::endl;
|
||||
} else {
|
||||
const auto audio_producer = machine->audio_producer();
|
||||
if(audio_producer) {
|
||||
const auto speaker = machine->audio_producer()->get_speaker();
|
||||
if(speaker) speaker->set_output_volume(volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether a 'logical' keyboard has been requested, or the machine would prefer one anyway.
|
||||
const bool logical_keyboard =
|
||||
@@ -907,7 +910,9 @@ int main(int argc, char *argv[]) {
|
||||
machine->scan_producer()->set_scan_target(&scan_target);
|
||||
|
||||
// For now, lie about audio output intentions.
|
||||
auto speaker = machine->audio_producer()->get_speaker();
|
||||
const auto audio_producer = machine->audio_producer();
|
||||
if(audio_producer) {
|
||||
auto speaker = audio_producer->get_speaker();
|
||||
if(speaker) {
|
||||
// Create an audio pipe.
|
||||
SDL_AudioSpec desired_audio_spec;
|
||||
@@ -928,6 +933,7 @@ int main(int argc, char *argv[]) {
|
||||
speaker->set_delegate(&speaker_delegate);
|
||||
SDL_PauseAudioDevice(speaker_delegate.audio_device, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
If the machine offers anything for activity observation,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#define WDC65816_hpp
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -145,6 +145,12 @@ struct PartialMachineCycle {
|
||||
forceinline bool is_wait() const {
|
||||
return operation >= Operation::ReadOpcodeWait && operation <= Operation::InterruptWait;
|
||||
}
|
||||
/*!
|
||||
@returns @c true if this partial machine cycle is a memory access; @c false otherwise.
|
||||
*/
|
||||
forceinline bool is_memory_access() const {
|
||||
return operation <= Operation::Write;
|
||||
}
|
||||
|
||||
enum Line {
|
||||
CLK = 1 << 0,
|
||||
|
||||
@@ -15,6 +15,7 @@ It currently contains emulations of the:
|
||||
* Atari ST;
|
||||
* ColecoVision;
|
||||
* Commodore Vic-20 (and Commodore 1540/1);
|
||||
* Enterprise 64/128;
|
||||
* Macintosh 512ke and Plus;
|
||||
* MSX 1;
|
||||
* Oric 1/Atmos;
|
||||
@@ -83,6 +84,7 @@ This emulator attempts cycle-accurate emulation of all supported machines. In so
|
||||
| | 
|
||||
| | 
|
||||
| | 
|
||||
| | 
|
||||
|
||||

|
||||

|
||||
@@ -91,4 +93,4 @@ This emulator attempts cycle-accurate emulation of all supported machines. In so
|
||||
|
||||
I've been asked several times whether it is possible to sponsor this project; I think that's a poor fit for this emulator's highly-malleable scope, and it makes me uncomfortable because as the author I primarily see only its defects.
|
||||
|
||||
An Amazon US wishlist is now attached in the hope of avoiding the question in future. A lot of it is old books now available only secondhand — I like to read about potential future additions well in advance of starting on them. Per the optimism of some book sellers, please don't purchase anything that is currnetly listed only at an absurd price; they were sorted by secondhand price when added to the list, with cheapest being $5.
|
||||
An Amazon US wishlist is now attached in the hope of avoiding the question in future. A lot of it is old books now available only secondhand — I like to read about potential future additions well in advance of starting on them. Despite the optimism of some book sellers, please don't purchase anything that is currently listed only at an absurd price; they were sorted by secondhand price when added to the list, with the cheapest being $5.
|
||||
|
||||
BIN
READMEImages/EnterpriseHERO.png
Normal file
BIN
READMEImages/EnterpriseHERO.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
BIN
READMEImages/EnterpriseStartup.png
Normal file
BIN
READMEImages/EnterpriseStartup.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
5
ROMImages/Enterprise/readme.txt
Normal file
5
ROMImages/Enterprise/readme.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
ROM files would ordinarily go here; the copyright status of these is uncertain so they have not been included in this repository.
|
||||
|
||||
Expected files:
|
||||
|
||||
exos.rom
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
#include "FIRFilter.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#ifndef M_PI
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#define USE_ACCELERATE
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace SignalProcessing {
|
||||
|
||||
@@ -49,8 +49,10 @@ class Controller:
|
||||
void run_for(const Cycles cycles);
|
||||
|
||||
/*!
|
||||
Sets the current drive(s). Normally this will be exactly one, but some machines allow
|
||||
zero or multiple drives to be attached, with useless results.
|
||||
Sets the current drive(s), by bit mask. Normally this will be exactly one, but some
|
||||
machines allow zero or multiple drives to be attached, with useless results.
|
||||
|
||||
E.g. supply 1 to select drive 0, 2 to select drive 1, 4 to select drive 2, etc.
|
||||
*/
|
||||
void set_drive(int index_mask);
|
||||
|
||||
|
||||
57
Storage/Disk/DiskImage/Formats/FAT12.cpp
Normal file
57
Storage/Disk/DiskImage/Formats/FAT12.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// FAT12.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 07/01/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "FAT12.hpp"
|
||||
|
||||
#include "Utility/ImplicitSectors.hpp"
|
||||
|
||||
using namespace Storage::Disk;
|
||||
|
||||
FAT12::FAT12(const std::string &file_name) :
|
||||
MFMSectorDump(file_name) {
|
||||
// The only sanity check here is whether a sensible
|
||||
// geometry is encoded in the first sector, or can be guessed.
|
||||
off_t file_size = file_.stats().st_size;
|
||||
|
||||
if(file_size < 512) throw Error::InvalidFormat;
|
||||
|
||||
// Inspect the FAT.
|
||||
file_.seek(11, SEEK_SET);
|
||||
sector_size_ = file_.get16le();
|
||||
file_.seek(19, SEEK_SET);
|
||||
const uint16_t total_sectors = file_.get16le();
|
||||
file_.seek(24, SEEK_SET);
|
||||
sector_count_ = file_.get16le();
|
||||
head_count_ = file_.get16le();
|
||||
|
||||
// Throw if there would seemingly be an incomplete track.
|
||||
if(file_size != total_sectors*sector_size_) throw Error::InvalidFormat;
|
||||
if(total_sectors % (head_count_ * sector_count_)) throw Error::InvalidFormat;
|
||||
track_count_ = int(total_sectors / (head_count_ * sector_count_));
|
||||
|
||||
// Check that there is a valid power-of-two sector size.
|
||||
uint8_t log_sector_size = 2;
|
||||
while(log_sector_size < 5 && (1 << (7+log_sector_size)) != sector_size_) {
|
||||
++log_sector_size;
|
||||
}
|
||||
if(log_sector_size >= 5) throw Error::InvalidFormat;
|
||||
|
||||
set_geometry(sector_count_, log_sector_size, 1, true);
|
||||
}
|
||||
|
||||
HeadPosition FAT12::get_maximum_head_position() {
|
||||
return HeadPosition(track_count_);
|
||||
}
|
||||
|
||||
int FAT12::get_head_count() {
|
||||
return head_count_;
|
||||
}
|
||||
|
||||
long FAT12::get_file_offset_for_position(Track::Address address) {
|
||||
return (address.position.as_int()*head_count_ + address.head) * sector_size_ * sector_count_;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// MSXDSK.hpp
|
||||
// FAT12.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 07/01/2018.
|
||||
@@ -17,12 +17,12 @@ namespace Storage {
|
||||
namespace Disk {
|
||||
|
||||
/*!
|
||||
Provides a @c DiskImage descriging an MSX-style disk image:
|
||||
Provides a @c DiskImage holding an MSDOS-style FAT12 disk image:
|
||||
a sector dump of appropriate proportions.
|
||||
*/
|
||||
class MSXDSK: public MFMSectorDump {
|
||||
class FAT12: public MFMSectorDump {
|
||||
public:
|
||||
MSXDSK(const std::string &file_name);
|
||||
FAT12(const std::string &file_name);
|
||||
HeadPosition get_maximum_head_position() final;
|
||||
int get_head_count() final;
|
||||
|
||||
@@ -31,6 +31,8 @@ class MSXDSK: public MFMSectorDump {
|
||||
|
||||
int head_count_;
|
||||
int track_count_;
|
||||
int sector_count_;
|
||||
int sector_size_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
//
|
||||
// MSXDSK.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 07/01/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MSXDSK.hpp"
|
||||
|
||||
#include "Utility/ImplicitSectors.hpp"
|
||||
|
||||
namespace {
|
||||
constexpr int sectors_per_track = 9;
|
||||
constexpr int sector_size = 2;
|
||||
constexpr off_t track_size = (128 << sector_size)*sectors_per_track;
|
||||
}
|
||||
|
||||
using namespace Storage::Disk;
|
||||
|
||||
MSXDSK::MSXDSK(const std::string &file_name) :
|
||||
MFMSectorDump(file_name) {
|
||||
// The only sanity check here is whether a sensible
|
||||
// geometry can be guessed.
|
||||
off_t file_size = file_.stats().st_size;
|
||||
|
||||
// Throw if there would seemingly be an incomplete track.
|
||||
if(file_size % track_size) throw Error::InvalidFormat;
|
||||
|
||||
track_count_ = int(file_size / track_size);
|
||||
head_count_ = 1;
|
||||
|
||||
// Throw if too large or too small or too large for single sided and
|
||||
// clearly not double sided.
|
||||
if(track_count_ < 40) throw Error::InvalidFormat;
|
||||
if(track_count_ > 82*2) throw Error::InvalidFormat;
|
||||
if(track_count_ > 82 && track_count_&1) throw Error::InvalidFormat;
|
||||
|
||||
// The below effectively prefers the idea of a single-sided 80-track disk
|
||||
// to a double-sided 40-track disk. Emulators have to guess.
|
||||
if(track_count_ > 82) {
|
||||
track_count_ /= 2;
|
||||
head_count_ = 2;
|
||||
}
|
||||
|
||||
set_geometry(sectors_per_track, sector_size, 1, true);
|
||||
}
|
||||
|
||||
HeadPosition MSXDSK::get_maximum_head_position() {
|
||||
return HeadPosition(track_count_);
|
||||
}
|
||||
|
||||
int MSXDSK::get_head_count() {
|
||||
return head_count_;
|
||||
}
|
||||
|
||||
long MSXDSK::get_file_offset_for_position(Track::Address address) {
|
||||
return (address.position.as_int()*head_count_ + address.head) * 512 * 9;
|
||||
}
|
||||
@@ -57,6 +57,6 @@ void Storage::Disk::decode_sectors(Track &track, uint8_t *const destination, uin
|
||||
if(pair.second.address.sector < first_sector) continue;
|
||||
if(pair.second.size != sector_size) continue;
|
||||
if(pair.second.samples.empty()) continue;
|
||||
std::memcpy(&destination[pair.second.address.sector * byte_size], pair.second.samples[0].data(), std::min(pair.second.samples[0].size(), byte_size));
|
||||
std::memcpy(&destination[(pair.second.address.sector - first_sector) * byte_size], pair.second.samples[0].data(), std::min(pair.second.samples[0].size(), byte_size));
|
||||
}
|
||||
}
|
||||
|
||||
179
Storage/Disk/Parsers/FAT.cpp
Normal file
179
Storage/Disk/Parsers/FAT.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
//
|
||||
// FAT.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 02/07/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "FAT.hpp"
|
||||
|
||||
#include "../Encodings/MFM/Parser.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace Storage::Disk;
|
||||
|
||||
FAT::Volume::CHS FAT::Volume::chs_for_sector(int sector) const {
|
||||
const auto track = sector / sectors_per_track;
|
||||
|
||||
// Sides are interleaved.
|
||||
return CHS{
|
||||
track / head_count,
|
||||
track % head_count,
|
||||
1 + (sector % sectors_per_track)
|
||||
};
|
||||
}
|
||||
|
||||
int FAT::Volume::sector_for_cluster(uint16_t cluster) const {
|
||||
// The first cluster in the data area is numbered as 2.
|
||||
return ((cluster - 2) * sectors_per_cluster) + first_data_sector;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename CharT> std::string trim(CharT start, CharT end) {
|
||||
std::string result(start, end);
|
||||
result.erase(std::find_if(result.rbegin(), result.rend(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), result.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
FAT::Directory directory_from(const std::vector<uint8_t> &contents) {
|
||||
FAT::Directory result;
|
||||
|
||||
// Worst case: parse until the amount of data supplied is fully consumed.
|
||||
for(size_t base = 0; base < contents.size(); base += 32) {
|
||||
// An entry starting with byte 0 indicates end-of-directory.
|
||||
if(!contents[base]) {
|
||||
break;
|
||||
}
|
||||
|
||||
// An entry starting in 0xe5 is merely deleted.
|
||||
if(contents[base] == 0xe5) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise create and populate a new entry.
|
||||
result.emplace_back();
|
||||
result.back().name = trim(&contents[base], &contents[base+8]);
|
||||
result.back().extension = trim(&contents[base+8], &contents[base+11]);
|
||||
result.back().attributes = contents[base + 11];
|
||||
result.back().time = uint16_t(contents[base+22] | (contents[base+23] << 8));
|
||||
result.back().date = uint16_t(contents[base+24] | (contents[base+25] << 8));
|
||||
result.back().starting_cluster = uint16_t(contents[base+26] | (contents[base+27] << 8));
|
||||
result.back().size = uint32_t(
|
||||
contents[base+28] |
|
||||
(contents[base+29] << 8) |
|
||||
(contents[base+30] << 16) |
|
||||
(contents[base+31] << 24)
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::optional<FAT::Volume> FAT::GetVolume(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||
Storage::Encodings::MFM::Parser parser(true, disk);
|
||||
|
||||
// Grab the boot sector; that'll be enough to establish the volume.
|
||||
Storage::Encodings::MFM::Sector *const boot_sector = parser.get_sector(0, 0, 1);
|
||||
if(!boot_sector || boot_sector->samples.empty() || boot_sector->samples[0].size() < 512) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Obtain volume details.
|
||||
const auto &data = boot_sector->samples[0];
|
||||
FAT::Volume volume;
|
||||
volume.bytes_per_sector = uint16_t(data[11] | (data[12] << 8));
|
||||
volume.sectors_per_cluster = data[13];
|
||||
volume.reserved_sectors = uint16_t(data[14] | (data[15] << 8));
|
||||
volume.fat_copies = data[16];
|
||||
const uint16_t root_directory_entries = uint16_t(data[17] | (data[18] << 8));
|
||||
volume.total_sectors = uint16_t(data[19] | (data[20] << 8));
|
||||
volume.sectors_per_fat = uint16_t(data[22] | (data[23] << 8));
|
||||
volume.sectors_per_track = uint16_t(data[24] | (data[25] << 8));
|
||||
volume.head_count = uint16_t(data[26] | (data[27] << 8));
|
||||
volume.correct_signature = data[510] == 0x55 && data[511] == 0xaa;
|
||||
|
||||
const size_t root_directory_sectors = (root_directory_entries*32 + volume.bytes_per_sector - 1) / volume.bytes_per_sector;
|
||||
volume.first_data_sector = int(volume.reserved_sectors + volume.sectors_per_fat*volume.fat_copies + root_directory_sectors);
|
||||
|
||||
// Grab the FAT.
|
||||
std::vector<uint8_t> source_fat;
|
||||
for(int c = 0; c < volume.sectors_per_fat; c++) {
|
||||
const int sector_number = volume.reserved_sectors + c;
|
||||
const auto address = volume.chs_for_sector(sector_number);
|
||||
|
||||
Storage::Encodings::MFM::Sector *const fat_sector =
|
||||
parser.get_sector(address.head, address.cylinder, uint8_t(address.sector));
|
||||
if(!fat_sector || fat_sector->samples.empty() || fat_sector->samples[0].size() != volume.bytes_per_sector) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::copy(fat_sector->samples[0].begin(), fat_sector->samples[0].end(), std::back_inserter(source_fat));
|
||||
}
|
||||
|
||||
// Decode the FAT.
|
||||
// TODO: stop assuming FAT12 here.
|
||||
for(size_t c = 0; c < source_fat.size(); c += 3) {
|
||||
const uint32_t double_cluster = uint32_t(source_fat[c] + (source_fat[c + 1] << 8) + (source_fat[c + 2] << 16));
|
||||
volume.fat.push_back(uint16_t(double_cluster & 0xfff));
|
||||
volume.fat.push_back(uint16_t(double_cluster >> 12));
|
||||
}
|
||||
|
||||
// Grab the root directory.
|
||||
std::vector<uint8_t> root_directory;
|
||||
for(size_t c = 0; c < root_directory_sectors; c++) {
|
||||
const auto sector_number = int(volume.reserved_sectors + c + volume.sectors_per_fat*volume.fat_copies);
|
||||
const auto address = volume.chs_for_sector(sector_number);
|
||||
|
||||
Storage::Encodings::MFM::Sector *const sector =
|
||||
parser.get_sector(address.head, address.cylinder, uint8_t(address.sector));
|
||||
if(!sector || sector->samples.empty() || sector->samples[0].size() != volume.bytes_per_sector) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::copy(sector->samples[0].begin(), sector->samples[0].end(), std::back_inserter(root_directory));
|
||||
}
|
||||
volume.root_directory = directory_from(root_directory);
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
std::optional<std::vector<uint8_t>> FAT::GetFile(const std::shared_ptr<Storage::Disk::Disk> &disk, const Volume &volume, const File &file) {
|
||||
Storage::Encodings::MFM::Parser parser(true, disk);
|
||||
|
||||
std::vector<uint8_t> contents;
|
||||
|
||||
// In FAT cluster numbers describe a linked list via the FAT table, with values above $FF0 being reserved
|
||||
// (relevantly: FF7 means bad cluster; FF8–FFF mean end-of-file).
|
||||
uint16_t cluster = file.starting_cluster;
|
||||
do {
|
||||
const int sector = volume.sector_for_cluster(cluster);
|
||||
|
||||
for(int c = 0; c < volume.sectors_per_cluster; c++) {
|
||||
const auto address = volume.chs_for_sector(sector + c);
|
||||
|
||||
Storage::Encodings::MFM::Sector *const sector_contents =
|
||||
parser.get_sector(address.head, address.cylinder, uint8_t(address.sector));
|
||||
if(!sector_contents || sector_contents->samples.empty() || sector_contents->samples[0].size() != volume.bytes_per_sector) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::copy(sector_contents->samples[0].begin(), sector_contents->samples[0].end(), std::back_inserter(contents));
|
||||
}
|
||||
|
||||
cluster = volume.fat[cluster];
|
||||
} while(cluster < 0xff0);
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
std::optional<FAT::Directory> FAT::GetDirectory(const std::shared_ptr<Storage::Disk::Disk> &disk, const Volume &volume, const File &file) {
|
||||
const auto contents = GetFile(disk, volume, file);
|
||||
if(!contents) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return directory_from(*contents);
|
||||
}
|
||||
79
Storage/Disk/Parsers/FAT.hpp
Normal file
79
Storage/Disk/Parsers/FAT.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// FAT.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 02/07/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Storage_Disk_Parsers_FAT_hpp
|
||||
#define Storage_Disk_Parsers_FAT_hpp
|
||||
|
||||
#include "../Disk.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Storage {
|
||||
namespace Disk {
|
||||
namespace FAT {
|
||||
|
||||
struct File {
|
||||
std::string name;
|
||||
std::string extension;
|
||||
uint8_t attributes = 0;
|
||||
uint16_t time = 0; // TODO: offer time/date decoders.
|
||||
uint16_t date = 0;
|
||||
uint16_t starting_cluster = 0;
|
||||
uint32_t size = 0;
|
||||
|
||||
enum Attribute: uint8_t {
|
||||
ReadOnly = (1 << 0),
|
||||
Hidden = (1 << 1),
|
||||
System = (1 << 2),
|
||||
VolumeLabel = (1 << 3),
|
||||
Directory = (1 << 4),
|
||||
Archive = (1 << 5),
|
||||
};
|
||||
};
|
||||
|
||||
using Directory = std::vector<File>;
|
||||
|
||||
struct Volume {
|
||||
uint16_t bytes_per_sector = 0;
|
||||
uint8_t sectors_per_cluster = 0;
|
||||
uint16_t reserved_sectors = 0;
|
||||
uint8_t fat_copies = 0;
|
||||
uint16_t total_sectors = 0;
|
||||
uint16_t sectors_per_fat = 0;
|
||||
uint16_t sectors_per_track = 0;
|
||||
uint16_t head_count = 0;
|
||||
uint16_t hidden_sectors = 0;
|
||||
bool correct_signature = false;
|
||||
int first_data_sector = 0;
|
||||
|
||||
std::vector<uint16_t> fat;
|
||||
Directory root_directory;
|
||||
|
||||
struct CHS {
|
||||
int cylinder;
|
||||
int head;
|
||||
int sector;
|
||||
};
|
||||
/// @returns a direct sector -> CHS address translation.
|
||||
CHS chs_for_sector(int sector) const;
|
||||
/// @returns the CHS address for the numbered cluster within the data area.
|
||||
int sector_for_cluster(uint16_t cluster) const;
|
||||
};
|
||||
|
||||
std::optional<Volume> GetVolume(const std::shared_ptr<Storage::Disk::Disk> &disk);
|
||||
std::optional<std::vector<uint8_t>> GetFile(const std::shared_ptr<Storage::Disk::Disk> &disk, const Volume &volume, const File &file);
|
||||
std::optional<Directory> GetDirectory(const std::shared_ptr<Storage::Disk::Disk> &disk, const Volume &volume, const File &file);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* FAT_hpp */
|
||||
@@ -29,13 +29,14 @@ enum Type: IntType {
|
||||
Coleco = 1 << 10,
|
||||
Commodore = 1 << 11,
|
||||
DiskII = 1 << 12,
|
||||
Sega = 1 << 13,
|
||||
Macintosh = 1 << 14,
|
||||
MSX = 1 << 15,
|
||||
Oric = 1 << 16,
|
||||
ZX80 = 1 << 17,
|
||||
ZX81 = 1 << 18,
|
||||
ZXSpectrum = 1 << 19,
|
||||
Enterprise = 1 << 13,
|
||||
Sega = 1 << 14,
|
||||
Macintosh = 1 << 15,
|
||||
MSX = 1 << 16,
|
||||
Oric = 1 << 17,
|
||||
ZX80 = 1 << 18,
|
||||
ZX81 = 1 << 19,
|
||||
ZXSpectrum = 1 << 20,
|
||||
|
||||
Acorn = AcornAtom | AcornElectron | BBCMaster | BBCModelA | BBCModelB,
|
||||
ZX8081 = ZX80 | ZX81,
|
||||
|
||||
Reference in New Issue
Block a user