mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 08:49:37 +00:00
commit
9319f0525a
@ -59,6 +59,7 @@
|
|||||||
|
|
||||||
// State Snapshots
|
// State Snapshots
|
||||||
#include "../../Storage/State/SNA.hpp"
|
#include "../../Storage/State/SNA.hpp"
|
||||||
|
#include "../../Storage/State/SZX.hpp"
|
||||||
#include "../../Storage/State/Z80.hpp"
|
#include "../../Storage/State/Z80.hpp"
|
||||||
|
|
||||||
// Tapes
|
// Tapes
|
||||||
@ -226,6 +227,7 @@ TargetList Analyser::Static::GetTargets(const std::string &file_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Format("sna", SNA);
|
Format("sna", SNA);
|
||||||
|
Format("szx", SZX);
|
||||||
Format("z80", Z80);
|
Format("z80", Z80);
|
||||||
|
|
||||||
#undef TryInsert
|
#undef TryInsert
|
||||||
|
@ -198,12 +198,14 @@ struct Utility {
|
|||||||
|
|
||||||
struct State: public Reflection::StructImpl<State> {
|
struct State: public Reflection::StructImpl<State> {
|
||||||
uint8_t registers[16]{};
|
uint8_t registers[16]{};
|
||||||
|
uint8_t selected_register = 0;
|
||||||
|
|
||||||
// TODO: all audio-production thread state.
|
// TODO: all audio-production thread state.
|
||||||
|
|
||||||
State() {
|
State() {
|
||||||
if(needs_declare()) {
|
if(needs_declare()) {
|
||||||
DeclareField(registers);
|
DeclareField(registers);
|
||||||
|
DeclareField(selected_register);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,6 +215,7 @@ struct State: public Reflection::StructImpl<State> {
|
|||||||
target.select_register(c);
|
target.select_register(c);
|
||||||
target.set_register_value(registers[c]);
|
target.set_register_value(registers[c]);
|
||||||
}
|
}
|
||||||
|
target.select_register(selected_register);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ struct State: public Reflection::StructImpl<State> {
|
|||||||
|
|
||||||
// Meaningful for 128kb machines only.
|
// Meaningful for 128kb machines only.
|
||||||
uint8_t last_7ffd = 0;
|
uint8_t last_7ffd = 0;
|
||||||
uint8_t last_fffd = 0;
|
|
||||||
GI::AY38910::State ay;
|
GI::AY38910::State ay;
|
||||||
|
|
||||||
// Meaningful for the +2a and +3 only.
|
// Meaningful for the +2a and +3 only.
|
||||||
@ -42,7 +41,6 @@ struct State: public Reflection::StructImpl<State> {
|
|||||||
DeclareField(video);
|
DeclareField(video);
|
||||||
DeclareField(ram);
|
DeclareField(ram);
|
||||||
DeclareField(last_7ffd);
|
DeclareField(last_7ffd);
|
||||||
DeclareField(last_fffd);
|
|
||||||
DeclareField(last_1ffd);
|
DeclareField(last_1ffd);
|
||||||
DeclareField(ay);
|
DeclareField(ay);
|
||||||
}
|
}
|
||||||
|
@ -262,6 +262,39 @@ template <Timing timing> class Video {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr HalfCycles frame_duration() {
|
||||||
|
const auto timings = get_timings();
|
||||||
|
return HalfCycles(timings.cycles_per_line * timings.lines_per_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
HalfCycles time_since_interrupt() {
|
||||||
|
const auto timings = get_timings();
|
||||||
|
if(time_into_frame_ >= timings.interrupt_time) {
|
||||||
|
return HalfCycles(time_into_frame_ - timings.interrupt_time);
|
||||||
|
} else {
|
||||||
|
return HalfCycles(time_into_frame_) + frame_duration() - HalfCycles(timings.interrupt_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_time_since_interrupt(const HalfCycles time) {
|
||||||
|
// Advance using run_for to ensure that all proper CRT interactions occurred.
|
||||||
|
const auto timings = get_timings();
|
||||||
|
const auto target = (time + timings.interrupt_time) % frame_duration();
|
||||||
|
const auto now = HalfCycles(time_into_frame_);
|
||||||
|
|
||||||
|
// Maybe this is easy?
|
||||||
|
if(target == now) return;
|
||||||
|
|
||||||
|
// Is the time within this frame?
|
||||||
|
if(time > now) {
|
||||||
|
run_for(target - time);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then it's necessary to finish this frame and run into the next.
|
||||||
|
run_for(frame_duration() - now + time);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Video() :
|
Video() :
|
||||||
crt_(half_cycles_per_line(), 2, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Red2Green2Blue2)
|
crt_(half_cycles_per_line(), 2, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Red2Green2Blue2)
|
||||||
@ -427,7 +460,7 @@ template <Timing timing> class Video {
|
|||||||
|
|
||||||
struct State: public Reflection::StructImpl<State> {
|
struct State: public Reflection::StructImpl<State> {
|
||||||
uint8_t border_colour = 0;
|
uint8_t border_colour = 0;
|
||||||
int time_into_frame = 0;
|
int half_cycles_since_interrupt = 0;
|
||||||
bool flash = 0;
|
bool flash = 0;
|
||||||
int flash_counter = 0;
|
int flash_counter = 0;
|
||||||
bool is_alternate_line = false;
|
bool is_alternate_line = false;
|
||||||
@ -435,7 +468,7 @@ struct State: public Reflection::StructImpl<State> {
|
|||||||
State() {
|
State() {
|
||||||
if(needs_declare()) {
|
if(needs_declare()) {
|
||||||
DeclareField(border_colour);
|
DeclareField(border_colour);
|
||||||
DeclareField(time_into_frame);
|
DeclareField(half_cycles_since_interrupt);
|
||||||
DeclareField(flash);
|
DeclareField(flash);
|
||||||
DeclareField(flash_counter);
|
DeclareField(flash_counter);
|
||||||
DeclareField(is_alternate_line);
|
DeclareField(is_alternate_line);
|
||||||
@ -444,18 +477,18 @@ struct State: public Reflection::StructImpl<State> {
|
|||||||
|
|
||||||
template <typename Video> State(const Video &source) : State() {
|
template <typename Video> State(const Video &source) : State() {
|
||||||
border_colour = source.border_byte_;
|
border_colour = source.border_byte_;
|
||||||
time_into_frame = source.time_into_frame_;
|
|
||||||
flash = source.flash_mask_;
|
flash = source.flash_mask_;
|
||||||
flash_counter = source.flash_counter_;
|
flash_counter = source.flash_counter_;
|
||||||
is_alternate_line = source. is_alternate_line_;
|
is_alternate_line = source. is_alternate_line_;
|
||||||
|
half_cycles_since_interrupt = source.time_since_interrupt().template as<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Video> void apply(Video &target) {
|
template <typename Video> void apply(Video &target) {
|
||||||
target.set_border_colour(border_colour);
|
target.set_border_colour(border_colour);
|
||||||
target.time_into_frame_ = time_into_frame;
|
|
||||||
target.flash_mask_ = flash ? 0xff : 0x00;
|
target.flash_mask_ = flash ? 0xff : 0x00;
|
||||||
target.flash_counter_ = flash_counter;
|
target.flash_counter_ = flash_counter;
|
||||||
target.is_alternate_line_ = is_alternate_line;
|
target.is_alternate_line_ = is_alternate_line;
|
||||||
|
target.set_time_since_interrupt(HalfCycles(half_cycles_since_interrupt));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -143,8 +143,6 @@ template<Model model> class ConcreteMachine:
|
|||||||
port1ffd_ = state->last_1ffd;
|
port1ffd_ = state->last_1ffd;
|
||||||
port7ffd_ = state->last_7ffd;
|
port7ffd_ = state->last_7ffd;
|
||||||
update_memory_map();
|
update_memory_map();
|
||||||
|
|
||||||
GI::AY38910::Utility::select_register(ay_, state->last_fffd);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,8 @@
|
|||||||
4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53911D117D36003C6002 /* CSAudioQueue.m */; };
|
4B2A539F1D117D36003C6002 /* CSAudioQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2A53911D117D36003C6002 /* CSAudioQueue.m */; };
|
||||||
4B2B3A4B1F9B8FA70062DABF /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A471F9B8FA70062DABF /* Typer.cpp */; };
|
4B2B3A4B1F9B8FA70062DABF /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A471F9B8FA70062DABF /* Typer.cpp */; };
|
||||||
4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A481F9B8FA70062DABF /* MemoryFuzzer.cpp */; };
|
4B2B3A4C1F9B8FA70062DABF /* MemoryFuzzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A481F9B8FA70062DABF /* MemoryFuzzer.cpp */; };
|
||||||
|
4B2B946526377C0200E7097C /* SZX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B946326377C0200E7097C /* SZX.cpp */; };
|
||||||
|
4B2B946626377C0200E7097C /* SZX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B946326377C0200E7097C /* SZX.cpp */; };
|
||||||
4B2BF19123DCC6A200C3AD60 /* BD500.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA03523CEB86000B98D9E /* BD500.cpp */; };
|
4B2BF19123DCC6A200C3AD60 /* BD500.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA03523CEB86000B98D9E /* BD500.cpp */; };
|
||||||
4B2BF19223DCC6A800C3AD60 /* STX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA03323C58B1E00B98D9E /* STX.cpp */; };
|
4B2BF19223DCC6A800C3AD60 /* STX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA03323C58B1E00B98D9E /* STX.cpp */; };
|
||||||
4B2BF19623E10F0100C3AD60 /* CSHighPrecisionTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2BF19523E10F0000C3AD60 /* CSHighPrecisionTimer.m */; };
|
4B2BF19623E10F0100C3AD60 /* CSHighPrecisionTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B2BF19523E10F0000C3AD60 /* CSHighPrecisionTimer.m */; };
|
||||||
@ -1142,6 +1144,8 @@
|
|||||||
4B2B3A481F9B8FA70062DABF /* MemoryFuzzer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryFuzzer.cpp; sourceTree = "<group>"; };
|
4B2B3A481F9B8FA70062DABF /* MemoryFuzzer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryFuzzer.cpp; sourceTree = "<group>"; };
|
||||||
4B2B3A491F9B8FA70062DABF /* MemoryFuzzer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MemoryFuzzer.hpp; sourceTree = "<group>"; };
|
4B2B3A491F9B8FA70062DABF /* MemoryFuzzer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MemoryFuzzer.hpp; sourceTree = "<group>"; };
|
||||||
4B2B3A4A1F9B8FA70062DABF /* Typer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Typer.hpp; sourceTree = "<group>"; };
|
4B2B3A4A1F9B8FA70062DABF /* Typer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Typer.hpp; sourceTree = "<group>"; };
|
||||||
|
4B2B946326377C0200E7097C /* SZX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SZX.cpp; sourceTree = "<group>"; };
|
||||||
|
4B2B946426377C0200E7097C /* SZX.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SZX.hpp; sourceTree = "<group>"; };
|
||||||
4B2BF19423E10F0000C3AD60 /* CSHighPrecisionTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSHighPrecisionTimer.h; sourceTree = "<group>"; };
|
4B2BF19423E10F0000C3AD60 /* CSHighPrecisionTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSHighPrecisionTimer.h; sourceTree = "<group>"; };
|
||||||
4B2BF19523E10F0000C3AD60 /* CSHighPrecisionTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSHighPrecisionTimer.m; sourceTree = "<group>"; };
|
4B2BF19523E10F0000C3AD60 /* CSHighPrecisionTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSHighPrecisionTimer.m; sourceTree = "<group>"; };
|
||||||
4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TapePRG.cpp; sourceTree = "<group>"; };
|
4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TapePRG.cpp; sourceTree = "<group>"; };
|
||||||
@ -3263,6 +3267,8 @@
|
|||||||
4B8DD3832634D37E00B3C866 /* State */ = {
|
4B8DD3832634D37E00B3C866 /* State */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
4B2B946326377C0200E7097C /* SZX.cpp */,
|
||||||
|
4B2B946426377C0200E7097C /* SZX.hpp */,
|
||||||
4B8DD3842634D37E00B3C866 /* SNA.cpp */,
|
4B8DD3842634D37E00B3C866 /* SNA.cpp */,
|
||||||
4B8DD3852634D37E00B3C866 /* SNA.hpp */,
|
4B8DD3852634D37E00B3C866 /* SNA.hpp */,
|
||||||
4B8DD39526360DDF00B3C866 /* Z80.cpp */,
|
4B8DD39526360DDF00B3C866 /* Z80.cpp */,
|
||||||
@ -5237,6 +5243,7 @@
|
|||||||
4B1B58F7246CC4E8009C171E /* State.cpp in Sources */,
|
4B1B58F7246CC4E8009C171E /* State.cpp in Sources */,
|
||||||
4B0ACC03237756F6008902D0 /* Line.cpp in Sources */,
|
4B0ACC03237756F6008902D0 /* Line.cpp in Sources */,
|
||||||
4B055AB11FAE86070060FFFF /* Tape.cpp in Sources */,
|
4B055AB11FAE86070060FFFF /* Tape.cpp in Sources */,
|
||||||
|
4B2B946626377C0200E7097C /* SZX.cpp in Sources */,
|
||||||
4BEDA43225B3C700000C2DBD /* Executor.cpp in Sources */,
|
4BEDA43225B3C700000C2DBD /* Executor.cpp in Sources */,
|
||||||
4BC1317B2346DF2B00E4FF3D /* MSA.cpp in Sources */,
|
4BC1317B2346DF2B00E4FF3D /* MSA.cpp in Sources */,
|
||||||
4B894533201967B4007DE474 /* 6502.cpp in Sources */,
|
4B894533201967B4007DE474 /* 6502.cpp in Sources */,
|
||||||
@ -5531,6 +5538,7 @@
|
|||||||
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */,
|
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */,
|
||||||
4BB307BB235001C300457D33 /* 6850.cpp in Sources */,
|
4BB307BB235001C300457D33 /* 6850.cpp in Sources */,
|
||||||
4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */,
|
4BF437EE209D0F7E008CBD6B /* SegmentParser.cpp in Sources */,
|
||||||
|
4B2B946526377C0200E7097C /* SZX.cpp in Sources */,
|
||||||
4BC131762346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */,
|
4BC131762346DE9100E4FF3D /* StaticAnalyser.cpp in Sources */,
|
||||||
4B8334861F5DA3780097E338 /* 6502Storage.cpp in Sources */,
|
4B8334861F5DA3780097E338 /* 6502Storage.cpp in Sources */,
|
||||||
4B8FE2271DA1DE2D0090D3CE /* NSBundle+DataResource.m in Sources */,
|
4B8FE2271DA1DE2D0090D3CE /* NSBundle+DataResource.m in Sources */,
|
||||||
|
@ -632,6 +632,26 @@
|
|||||||
<key>NSDocumentClass</key>
|
<key>NSDocumentClass</key>
|
||||||
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeExtensions</key>
|
||||||
|
<array>
|
||||||
|
<string>szx</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>ZX Spectrum SZX snapshot</string>
|
||||||
|
<key>CFBundleTypeOSTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>????</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Owner</string>
|
||||||
|
<key>LSTypeIsPackage</key>
|
||||||
|
<false/>
|
||||||
|
<key>NSDocumentClass</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
|
||||||
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
196
Storage/State/SZX.cpp
Normal file
196
Storage/State/SZX.cpp
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
//
|
||||||
|
// SZX.cpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 26/04/2021.
|
||||||
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "SZX.hpp"
|
||||||
|
|
||||||
|
#include "../FileHolder.hpp"
|
||||||
|
|
||||||
|
#include "../../Analyser/Static/ZXSpectrum/Target.hpp"
|
||||||
|
#include "../../Machines/Sinclair/ZXSpectrum/State.hpp"
|
||||||
|
|
||||||
|
#define LOG_PREFIX "[SZX] "
|
||||||
|
#include "../../Outputs/Log.hpp"
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
using namespace Storage::State;
|
||||||
|
|
||||||
|
std::unique_ptr<Analyser::Static::Target> SZX::load(const std::string &file_name) {
|
||||||
|
FileHolder file(file_name);
|
||||||
|
|
||||||
|
// Construct a target with a Spectrum state.
|
||||||
|
using Target = Analyser::Static::ZXSpectrum::Target;
|
||||||
|
auto result = std::make_unique<Target>();
|
||||||
|
auto *const state = new Sinclair::ZXSpectrum::State();
|
||||||
|
result->state = std::unique_ptr<Reflection::Struct>(state);
|
||||||
|
|
||||||
|
// Check signature and major version number.
|
||||||
|
if(!file.check_signature("ZXST")) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const uint8_t major_version = file.get8();
|
||||||
|
[[maybe_unused]] const uint8_t minor_version = file.get8();
|
||||||
|
if(major_version > 1) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for a supported machine type.
|
||||||
|
const uint8_t machine_type = file.get8();
|
||||||
|
switch(machine_type) {
|
||||||
|
default: return nullptr;
|
||||||
|
|
||||||
|
case 0: result->model = Target::Model::SixteenK; break;
|
||||||
|
case 1: result->model = Target::Model::FortyEightK; break;
|
||||||
|
case 2: result->model = Target::Model::OneTwoEightK; break;
|
||||||
|
case 3: result->model = Target::Model::Plus2; break;
|
||||||
|
case 4: result->model = Target::Model::Plus2a; break;
|
||||||
|
case 5: result->model = Target::Model::Plus3; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consequential upon selected machine...
|
||||||
|
switch(result->model) {
|
||||||
|
case Target::Model::SixteenK: state->ram.resize(16 * 1024); break;
|
||||||
|
case Target::Model::FortyEightK: state->ram.resize(48 * 1024); break;
|
||||||
|
default:
|
||||||
|
state->ram.resize(128 * 1024);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t file_flags = file.get8();
|
||||||
|
[[maybe_unused]] const bool uses_late_timings = file_flags & 1;
|
||||||
|
|
||||||
|
// Now parse all included blocks.
|
||||||
|
while(true) {
|
||||||
|
const uint32_t blockID = file.get32le();
|
||||||
|
const uint32_t size = file.get32le();
|
||||||
|
if(file.eof()) break;
|
||||||
|
const auto location = file.tell();
|
||||||
|
|
||||||
|
#define BLOCK(str) str[0] | (str[1] << 8) | (str[2] << 16) | (str[3] << 24)
|
||||||
|
|
||||||
|
switch(blockID) {
|
||||||
|
default:
|
||||||
|
LOG("Unhandled block " << char(blockID) << char(blockID >> 8) << char(blockID >> 16) << char(blockID >> 24));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ZXSTZ80REGS
|
||||||
|
case BLOCK("Z80R"): {
|
||||||
|
state->z80.registers.flags = file.get8();
|
||||||
|
state->z80.registers.a = file.get8();
|
||||||
|
|
||||||
|
state->z80.registers.bc = file.get16le();
|
||||||
|
state->z80.registers.de = file.get16le();
|
||||||
|
state->z80.registers.hl = file.get16le();
|
||||||
|
|
||||||
|
state->z80.registers.af_dash = file.get16le();
|
||||||
|
state->z80.registers.bc_dash = file.get16le();
|
||||||
|
state->z80.registers.de_dash = file.get16le();
|
||||||
|
state->z80.registers.hl_dash = file.get16le();
|
||||||
|
|
||||||
|
state->z80.registers.ix = file.get16le();
|
||||||
|
state->z80.registers.iy = file.get16le();
|
||||||
|
state->z80.registers.stack_pointer = file.get16le();
|
||||||
|
state->z80.registers.program_counter = file.get16le();
|
||||||
|
|
||||||
|
const uint8_t i = file.get8();
|
||||||
|
const uint8_t r = file.get8();
|
||||||
|
state->z80.registers.ir = uint16_t((i << 8) | r);
|
||||||
|
|
||||||
|
state->z80.registers.iff1 = file.get8();
|
||||||
|
state->z80.registers.iff2 = file.get8();
|
||||||
|
state->z80.registers.interrupt_mode = file.get8();
|
||||||
|
|
||||||
|
state->video.half_cycles_since_interrupt = int(file.get32le()) * 2;
|
||||||
|
|
||||||
|
// SZX includes a count of remaining cycles that interrupt should be asserted for
|
||||||
|
// because it supports hardware that might cause an interrupt other than the display.
|
||||||
|
// This emulator doesn't, so this field can be ignored.
|
||||||
|
[[maybe_unused]] uint8_t remaining_interrupt_cycles = file.get8();
|
||||||
|
|
||||||
|
|
||||||
|
const uint8_t flags = file.get8();
|
||||||
|
state->z80.execution_state.is_halted = flags & 2;
|
||||||
|
// TODO: bit 0 indicates that the last instruction was an EI, or an invalid
|
||||||
|
// DD or FD. I assume I'm supposed to use that to conclude an interrupt
|
||||||
|
// verdict but I'm unclear what the effect of an invalid DD or FD is so
|
||||||
|
// have not yet implemented this.
|
||||||
|
|
||||||
|
state->z80.registers.memptr = file.get16le();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// ZXSTAYBLOCK
|
||||||
|
case BLOCK("AY\0\0"): {
|
||||||
|
// This applies to 48kb machines with AY boxes only. This emulator
|
||||||
|
// doesn't currently support those.
|
||||||
|
[[maybe_unused]] const uint8_t interface_type = file.get8();
|
||||||
|
|
||||||
|
state->ay.selected_register = file.get8();
|
||||||
|
file.read(state->ay.registers, 16);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// ZXSTRAMPAGE
|
||||||
|
case BLOCK("RAMP"): {
|
||||||
|
const uint16_t flags = file.get16le();
|
||||||
|
const uint8_t page = file.get8();
|
||||||
|
|
||||||
|
std::vector<uint8_t> contents;
|
||||||
|
if(flags & 1) {
|
||||||
|
// ZLib compression is applied.
|
||||||
|
contents.resize(16 * 1024);
|
||||||
|
const std::vector<uint8_t> source = file.read(size - 3);
|
||||||
|
|
||||||
|
uLongf output_length;
|
||||||
|
uncompress(contents.data(), &output_length, source.data(), source.size());
|
||||||
|
assert(output_length == contents.size());
|
||||||
|
} else {
|
||||||
|
// Data is raw.
|
||||||
|
contents = file.read(16 * 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(result->model) {
|
||||||
|
case Target::Model::SixteenK:
|
||||||
|
case Target::Model::FortyEightK: {
|
||||||
|
size_t address = 0;
|
||||||
|
switch(page) {
|
||||||
|
default: break;
|
||||||
|
case 5: address = 0x4000; break;
|
||||||
|
case 2: address = 0x8000; break;
|
||||||
|
case 0: address = 0xc000; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(address > 0 && (address - 0x4000) <= state->ram.size()) {
|
||||||
|
memcpy(&state->ram[address - 0x4000], contents.data(), 0x4000);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if(page < 8) {
|
||||||
|
memcpy(&state->ram[page * 0x4000], contents.data(), 0x4000);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// ZXSTSPECREGS
|
||||||
|
case BLOCK("SPCR"): {
|
||||||
|
state->video.border_colour = file.get8();
|
||||||
|
state->last_7ffd = file.get8();
|
||||||
|
state->last_1ffd = file.get8();
|
||||||
|
|
||||||
|
// TODO: use last write to FE, at least.
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef BLOCK
|
||||||
|
|
||||||
|
// Advance to the next block.
|
||||||
|
file.seek(location + size, SEEK_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
24
Storage/State/SZX.hpp
Normal file
24
Storage/State/SZX.hpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// SZX.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 26/04/2021.
|
||||||
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef Storage_State_SZX_hpp
|
||||||
|
#define Storage_State_SZX_hpp
|
||||||
|
|
||||||
|
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||||
|
|
||||||
|
namespace Storage {
|
||||||
|
namespace State {
|
||||||
|
|
||||||
|
struct SZX {
|
||||||
|
static std::unique_ptr<Analyser::Static::Target> load(const std::string &file_name);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* Storage_State_SZX_hpp */
|
@ -141,7 +141,7 @@ std::unique_ptr<Analyser::Static::Target> Z80::load(const std::string &file_name
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state->last_fffd = file.get8();
|
state->ay.selected_register = file.get8();
|
||||||
file.read(state->ay.registers, 16);
|
file.read(state->ay.registers, 16);
|
||||||
|
|
||||||
if(bonus_header_size != 23) {
|
if(bonus_header_size != 23) {
|
||||||
|
Loading…
Reference in New Issue
Block a user