1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-16 15:29:34 +00:00
CLK/Storage/State/Z80.cpp
2023-05-12 14:14:45 -04:00

210 lines
6.3 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// Z80.cpp
// Clock Signal
//
// Created by Thomas Harte on 25/04/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#include "Z80.hpp"
#include "../FileHolder.hpp"
#include "../../Analyser/Static/ZXSpectrum/Target.hpp"
#include "../../Machines/Sinclair/ZXSpectrum/State.hpp"
using namespace Storage::State;
namespace {
std::vector<uint8_t> read_memory(Storage::FileHolder &file, size_t size, bool is_compressed) {
if(!is_compressed) {
return file.read(size);
}
std::vector<uint8_t> result(size);
size_t cursor = 0;
while(cursor != size) {
const uint8_t next = file.get8();
// If the next byte definitely doesn't, or can't,
// start an ED ED sequence then just take it.
if(next != 0xed || cursor == size - 1) {
result[cursor] = next;
++cursor;
continue;
}
// Grab the next byte. If it's not ED then write
// both and continue.
const uint8_t after = file.get8();
if(after != 0xed) {
result[cursor] = next;
result[cursor+1] = after;
cursor += 2;
continue;
}
// An ED ED has begun, so grab the RLE sequence.
const uint8_t count = file.get8();
const uint8_t value = file.get8();
memset(&result[cursor], value, count);
cursor += count;
}
return result;
}
}
std::unique_ptr<Analyser::Static::Target> Z80::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);
// Read version 1 header.
state->z80.registers.a = file.get8();
state->z80.registers.flags = file.get8();
state->z80.registers.bc = file.get16le();
state->z80.registers.hl = file.get16le();
state->z80.registers.program_counter = file.get16le();
state->z80.registers.stack_pointer = file.get16le();
state->z80.registers.ir = file.get16be(); // Stored I then R.
// Bit 7 of R is stored separately; likely this relates to an
// optimisation in the Z80 emulator that for some reason was
// exported into its file format.
const uint8_t raw_misc = file.get8();
const uint8_t misc = (raw_misc == 0xff) ? 1 : raw_misc;
state->z80.registers.ir = uint16_t((state->z80.registers.ir & ~0x80) | ((misc&1) << 7));
state->z80.registers.de = 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.af_dash = file.get16be(); // Stored A' then F'.
state->z80.registers.iy = file.get16le();
state->z80.registers.ix = file.get16le();
state->z80.registers.iff1 = bool(file.get8());
state->z80.registers.iff2 = bool(file.get8());
// Ignored from the next byte:
//
// bit 2 = 1 => issue 2 emulation
// bit 3 = 1 => double interrupt frequency (?)
// bit 45 => video synchronisation (to do with emulation hackery?)
// bit 67 => joystick type
state->z80.registers.interrupt_mode = file.get8() & 3;
// If the program counter is non-0 then this is a version 1 snapshot,
// which means it's definitely a 48k image.
if(state->z80.registers.program_counter) {
result->model = Target::Model::FortyEightK;
state->ram = read_memory(file, 48*1024, misc & 0x20);
return result;
}
// This was a version 1 or 2 snapshot, so keep going...
const uint16_t bonus_header_size = file.get16le();
if(bonus_header_size != 23 && bonus_header_size != 54 && bonus_header_size != 55) {
return nullptr;
}
state->z80.registers.program_counter = file.get16le();
const uint8_t model = file.get8();
switch(model) {
default: return nullptr;
case 0: result->model = Target::Model::FortyEightK; break;
case 3: result->model = Target::Model::OneTwoEightK; break;
case 7:
case 8: result->model = Target::Model::Plus3; break;
case 12: result->model = Target::Model::Plus2; break;
case 13: result->model = Target::Model::Plus2a; break;
}
state->last_7ffd = file.get8();
file.seek(1, SEEK_CUR);
if(file.get8() & 0x80) {
// The 'hardware modify' bit, which inexplicably does this:
switch(result->model) {
default: break;
case Target::Model::FortyEightK: result->model = Target::Model::SixteenK; break;
case Target::Model::OneTwoEightK: result->model = Target::Model::Plus2; break;
case Target::Model::Plus3: result->model = Target::Model::Plus2a; break;
}
}
state->ay.selected_register = file.get8();
file.read(state->ay.registers, 16);
if(bonus_header_size != 23) {
// More Z80, the emulator, lack of encapsulation to deal with here.
const uint16_t low_t_state = file.get16le();
const uint16_t high_t_state = file.get8();
switch(result->model) {
case Target::Model::SixteenK:
case Target::Model::FortyEightK:
state->video.half_cycles_since_interrupt = ((17471 - low_t_state) + (high_t_state * 17472)) * 2;
break;
default:
state->video.half_cycles_since_interrupt = ((17726 - low_t_state) + (high_t_state * 17727)) * 2;
break;
}
// Skip: Spectator flag, MGT, Multiface and other ROM flags.
file.seek(5, SEEK_CUR);
// Skip: highly Z80-the-emulator-specific stuff about user-defined joystick.
file.seek(20, SEEK_CUR);
// Skip: Disciple/Plus D stuff.
file.seek(3, SEEK_CUR);
if(bonus_header_size == 55) {
state->last_1ffd = file.get8();
}
}
// Grab RAM.
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;
}
while(true) {
const uint16_t block_size = file.get16le();
const uint8_t page = file.get8();
const auto location = file.tell();
if(file.eof()) break;
const auto data = read_memory(file, 16384, block_size != 0xffff);
if(result->model == Target::Model::SixteenK || result->model == Target::Model::FortyEightK) {
switch(page) {
default: break;
case 4: memcpy(&state->ram[0x4000], data.data(), 16384); break;
case 5: memcpy(&state->ram[0x8000], data.data(), 16384); break;
case 8: memcpy(&state->ram[0x0000], data.data(), 16384); break;
}
} else {
if(page >= 3 && page <= 10) {
memcpy(&state->ram[(page - 3) * 0x4000], data.data(), 16384);
}
}
assert(location + block_size == file.tell());
file.seek(location + block_size, SEEK_SET);
}
return result;
}