Starts working in the 48kb and 128kb Spectrums.

This commit is contained in:
Thomas Harte 2021-04-14 21:37:10 -04:00
parent cf481effa6
commit 0af405aa46
11 changed files with 116 additions and 56 deletions

View File

@ -19,6 +19,10 @@ namespace ZXSpectrum {
struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<Target> {
ReflectableEnum(Model,
SixteenK,
FortyEightK,
OneTwoEightK,
Plus2,
Plus2a,
Plus3,
);

View File

@ -18,7 +18,9 @@ namespace Sinclair {
namespace ZXSpectrum {
enum class VideoTiming {
Plus3
FortyEightK,
OneTwoEightK,
Plus3,
};
/*

View File

@ -81,8 +81,29 @@ template<Model model> class ConcreteMachine:
// With only the +2a and +3 currently supported, the +3 ROM is always
// the one required.
const auto roms =
rom_fetcher({ {"ZXSpectrum", "the +2a/+3 ROM", "plus3.rom", 64 * 1024, 0x96e3c17a} });
std::vector<ROMMachine::ROM> rom_names;
const std::string machine = "ZXSpectrum";
switch(model) {
case Model::SixteenK:
case Model::FortyEightK:
rom_names.emplace_back(machine, "the 48kb ROM", "48.rom", 16 * 1024, 0xddee531f);
break;
case Model::OneTwoEightK:
rom_names.emplace_back(machine, "the 128kb ROM", "128.rom", 32 * 1024, 0x2cbe8995);
break;
case Model::Plus2:
rom_names.emplace_back(machine, "the +2 ROM", "plus2.rom", 32 * 1024, 0xe7a517dc);
break;
case Model::Plus2a:
case Model::Plus3: {
const std::initializer_list<uint32_t> crc32s = { 0x96e3c17a, 0xbe0d9ec4 };
rom_names.emplace_back(machine, "the +2a/+3 ROM", "plus3.rom", 64 * 1024, crc32s);
} break;
}
const auto roms = rom_fetcher(rom_names);
if(!roms[0]) throw ROMMachine::Error::MissingROMs;
memcpy(rom_.data(), roms[0]->data(), std::min(rom_.size(), roms[0]->size()));
@ -110,7 +131,7 @@ template<Model model> class ConcreteMachine:
}
static constexpr unsigned int clock_rate() {
// constexpr unsigned int ClockRate = 3'500'000;
constexpr unsigned int OriginalClockRate = 3'500'000;
constexpr unsigned int Plus3ClockRate = 3'546'875; // See notes below; this is a guess.
// Notes on timing for the +2a and +3:
@ -137,7 +158,7 @@ template<Model model> class ConcreteMachine:
// the Spectrum is a PAL machine with a fixed colour phase relationship. For
// this emulator's world, that's a first!
return Plus3ClockRate;
return model < Model::OneTwoEightK ? OriginalClockRate : Plus3ClockRate;
}
// MARK: - TimedMachine.
@ -189,18 +210,22 @@ template<Model model> class ConcreteMachine:
const uint16_t address = cycle.address ? *cycle.address : 0x0000;
// Apply contention if necessary.
if(
is_contended_[address >> 14] &&
cycle.operation >= PartialMachineCycle::ReadOpcodeStart &&
cycle.operation <= PartialMachineCycle::WriteStart) {
// Assumption here: the trigger for the ULA inserting a delay is the falling edge
// of MREQ, which is always half a cycle into a read or write.
//
// TODO: somehow provide that information in the PartialMachineCycle?
if constexpr (model >= Model::Plus2a) {
if(
is_contended_[address >> 14] &&
cycle.operation >= PartialMachineCycle::ReadOpcodeStart &&
cycle.operation <= PartialMachineCycle::WriteStart) {
// Assumption here: the trigger for the ULA inserting a delay is the falling edge
// of MREQ, which is always half a cycle into a read or write.
//
// TODO: somehow provide that information in the PartialMachineCycle?
const HalfCycles delay = video_.last_valid()->access_delay(video_.time_since_flush() + HalfCycles(1));
advance(cycle.length + delay);
return delay;
const HalfCycles delay = video_.last_valid()->access_delay(video_.time_since_flush() + HalfCycles(1));
advance(cycle.length + delay);
return delay;
}
} else {
// TODO.
}
// For all other machine cycles, model the action as happening at the end of the machine cycle;
@ -263,39 +288,45 @@ template<Model model> class ConcreteMachine:
}
// Test for classic 128kb paging register (i.e. port 7ffd).
if((address & 0xc002) == 0x4000) {
port7ffd_ = *cycle.value;
update_memory_map();
if constexpr (model >= Model::OneTwoEightK) {
if((address & 0xc002) == 0x4000) {
port7ffd_ = *cycle.value;
update_memory_map();
// Set the proper video base pointer.
set_video_address();
// Set the proper video base pointer.
set_video_address();
// Potentially lock paging, _after_ the current
// port values have taken effect.
disable_paging_ |= *cycle.value & 0x20;
}
// Test for +2a/+3 paging (i.e. port 1ffd).
if((address & 0xf002) == 0x1000) {
port1ffd_ = *cycle.value;
update_memory_map();
update_video_base();
if constexpr (model == Model::Plus3) {
fdc_->set_motor_on(*cycle.value & 0x08);
// Potentially lock paging, _after_ the current
// port values have taken effect.
disable_paging_ |= *cycle.value & 0x20;
}
}
if((address & 0xc002) == 0xc000) {
// Select AY register.
update_audio();
GI::AY38910::Utility::select_register(ay_, *cycle.value);
// Test for +2a/+3 paging (i.e. port 1ffd).
if constexpr (model >= Model::Plus2a) {
if((address & 0xf002) == 0x1000) {
port1ffd_ = *cycle.value;
update_memory_map();
update_video_base();
if constexpr (model == Model::Plus3) {
fdc_->set_motor_on(*cycle.value & 0x08);
}
}
}
if((address & 0xc002) == 0x8000) {
// Write to AY register.
update_audio();
GI::AY38910::Utility::write_data(ay_, *cycle.value);
if constexpr (model >= Model::OneTwoEightK) {
if((address & 0xc002) == 0xc000) {
// Select AY register.
update_audio();
GI::AY38910::Utility::select_register(ay_, *cycle.value);
}
if((address & 0xc002) == 0x8000) {
// Write to AY register.
update_audio();
GI::AY38910::Utility::write_data(ay_, *cycle.value);
}
}
if constexpr (model == Model::Plus3) {
@ -574,7 +605,7 @@ template<Model model> class ConcreteMachine:
is_contended_[bank] = (source >= 4 && source < 8);
pages_[bank] = source;
uint8_t *read = (source < 0x80) ? &ram_[source * 16384] : &rom_[(source & 0x7f) * 16384];
uint8_t *const read = (source < 0x80) ? &ram_[source * 16384] : &rom_[(source & 0x7f) * 16384];
const auto offset = bank*16384;
read_pointers_[bank] = read - offset;
@ -712,8 +743,12 @@ Machine *Machine::ZXSpectrum(const Analyser::Static::Target *target, const ROMMa
const auto zx_target = dynamic_cast<const Analyser::Static::ZXSpectrum::Target *>(target);
switch(zx_target->model) {
case Model::Plus2a: return new ConcreteMachine<Model::Plus2a>(*zx_target, rom_fetcher);
case Model::Plus3: return new ConcreteMachine<Model::Plus3>(*zx_target, rom_fetcher);
case Model::SixteenK: return new ConcreteMachine<Model::SixteenK>(*zx_target, rom_fetcher);
case Model::FortyEightK: return new ConcreteMachine<Model::FortyEightK>(*zx_target, rom_fetcher);
case Model::OneTwoEightK: return new ConcreteMachine<Model::OneTwoEightK>(*zx_target, rom_fetcher);
case Model::Plus2: return new ConcreteMachine<Model::Plus2>(*zx_target, rom_fetcher);
case Model::Plus2a: return new ConcreteMachine<Model::Plus2a>(*zx_target, rom_fetcher);
case Model::Plus3: return new ConcreteMachine<Model::Plus3>(*zx_target, rom_fetcher);
}
return nullptr;

View File

@ -63,6 +63,10 @@ typedef NS_ENUM(NSInteger, CSMachineOricDiskInterface) {
};
typedef NS_ENUM(NSInteger, CSMachineSpectrumModel) {
CSMachineSpectrumModelSixteenK,
CSMachineSpectrumModelFortyEightK,
CSMachineSpectrumModelOneTwoEightK,
CSMachineSpectrumModelPlus2,
CSMachineSpectrumModelPlus2a,
CSMachineSpectrumModelPlus3,
};

View File

@ -194,8 +194,12 @@
using Target = Analyser::Static::ZXSpectrum::Target;
auto target = std::make_unique<Target>();
switch(model) {
case CSMachineSpectrumModelPlus2a: target->model = Target::Model::Plus2a; break;
case CSMachineSpectrumModelPlus3: target->model = Target::Model::Plus3; break;
case CSMachineSpectrumModelSixteenK: target->model = Target::Model::SixteenK; break;
case CSMachineSpectrumModelFortyEightK: target->model = Target::Model::FortyEightK; break;
case CSMachineSpectrumModelOneTwoEightK: target->model = Target::Model::OneTwoEightK; break;
case CSMachineSpectrumModelPlus2: target->model = Target::Model::Plus2; break;
case CSMachineSpectrumModelPlus2a: target->model = Target::Model::Plus2a; break;
case CSMachineSpectrumModelPlus3: target->model = Target::Model::Plus3; break;
}
_targets.push_back(std::move(target));
}

View File

@ -18,7 +18,7 @@
<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="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
<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"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@ -584,11 +584,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="76"/>
<rect key="frame" x="10" y="7" width="400" height="186"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5aO-UX-HnX">
<rect key="frame" x="108" y="47" width="116" height="25"/>
<rect key="frame" x="108" y="157" 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 +602,7 @@ Gw
</popUpButtonCell>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8tU-73-XEE">
<rect key="frame" x="18" y="53" width="87" height="16"/>
<rect key="frame" x="18" y="163" 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"/>
@ -622,24 +622,28 @@ Gw
</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="76"/>
<rect key="frame" x="10" y="7" width="400" height="186"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gFZ-d4-WFv">
<rect key="frame" x="67" y="47" width="62" height="25"/>
<popUpButtonCell key="cell" type="push" title="+2a" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="21" imageScaling="axesIndependently" inset="2" selectedItem="Fo7-NL-Kv5" id="tYs-sA-oek">
<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">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="8lt-dk-zPr">
<items>
<menuItem title="+2a" state="on" tag="21" id="Fo7-NL-Kv5"/>
<menuItem title="16kb" 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="+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="53" width="46" height="16"/>
<rect key="frame" x="18" y="163" 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"/>

View File

@ -298,6 +298,10 @@ class MachinePicker: NSObject, NSTableViewDataSource, NSTableViewDelegate {
case "spectrum":
var model: CSMachineSpectrumModel = .plus2a
switch spectrumModelTypeButton.selectedItem!.tag {
case 16: model = .sixteenK
case 48: model = .fortyEightK
case 128: model = .oneTwoEightK
case 2: model = .plus2
case 21: model = .plus2a
case 3: model = .plus3
default: break

Binary file not shown.

BIN
ROMImages/ZXSpectrum/48.rom Normal file

Binary file not shown.

Binary file not shown.

View File

@ -9,3 +9,6 @@ material but retain that copyright"."
With that in mind, Amstrad have kindly given their permission for the redistribution of their copyrighted material but retain that copyright. Material expected here, copyright Amstrad:
plus3.rom — the +2a/+3 ROM file, 64kb in size.
plus2.rom the +2 ROM file, 32kb in size.
128.rom the 128kb ROM file, 32kb in size.
48.rom the 16/48kb ROM file, 16kb in size.