1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-08-05 08:26:28 +00:00

Introduce a linear-memory holder.

This commit is contained in:
Thomas Harte
2025-03-24 21:23:08 -04:00
parent 6ad88101f1
commit fac15f5539
6 changed files with 159 additions and 12 deletions

View File

@@ -8,7 +8,7 @@
#pragma once
#include "Memory.hpp"
#include "SegmentedMemory.hpp"
#include "ProcessorByModel.hpp"
#include "Registers.hpp"
#include "Segments.hpp"
@@ -24,7 +24,7 @@ public:
CPUControl(
Registers<processor_model(model)> &registers,
Segments<processor_model(model)> &segments,
Memory<model> &memory
SegmentedMemory<model> &memory
) : registers_(registers), segments_(segments), memory_(memory) {}
void reset() {
@@ -40,7 +40,7 @@ public:
private:
Registers<processor_model(model)> &registers_;
Segments<processor_model(model)> &segments_;
Memory<model> &memory_;
SegmentedMemory<model> &memory_;
Log::Logger<Log::Source::PCCompatible> log_;
};

View File

@@ -12,7 +12,7 @@
#include "Numeric/RegisterSizes.hpp"
#include "Outputs/Log.hpp"
#include "Memory.hpp"
#include "SegmentedMemory.hpp"
#include <array>
@@ -302,7 +302,7 @@ public:
DMAPages<has_second_dma> pages;
// Memory is set posthoc to resolve a startup time.
void set_memory(Memory<model> *const memory) {
void set_memory(SegmentedMemory<model> *const memory) {
memory_ = memory;
}
@@ -319,7 +319,7 @@ public:
}
private:
Memory<model> *memory_;
SegmentedMemory<model> *memory_;
};
}

View File

@@ -0,0 +1,143 @@
//
// LinearMemory.hpp
// Clock Signal
//
// Created by Thomas Harte on 24/03/2025.
// Copyright © 2025 Thomas Harte. All rights reserved.
//
#pragma once
#include "InstructionSets/x86/AccessType.hpp"
#include "InstructionSets/x86/Model.hpp"
#include <array>
#include <cassert>
#include <cstring>
#include <cstdint>
namespace PCCompatible {
/*!
Provides a mapping from linear addresses to underlying memory.
Prior to the 80286, linear addresses are presently physical addresses.
Some nuance might appear here if/when EGA and VGA and/or EMS are implemented.
On an 8086 and 80186, addresses are clamped to 20 bits.
On the 80286 they're clamped to 24 bits.
From the AT onwards, address line 20 can be enabled or disabled.
TODO: from the 80386 onwards, memory can be reordered and
exceptions might be raised.
TODO: remove assumption of a little-endian host.
TODO: allow for read-only areas of memory, paged areas of memory, etc.
*/
template <InstructionSet::x86::Model model>
struct LinearMemory {
using AccessType = InstructionSet::x86::AccessType;
static constexpr uint32_t MaxAddress =
1024 * 1024 * (model >= InstructionSet::x86::Model::i80286 ? 16 : 1);
/// @returns A suitable accessor for the @c IntT that starts at @c address, subject to address
/// wrapping within the bounds `[base, limit]`. There is a chance that some sort of behind-the-scenes
/// activity may be necessary to present something as a single @c IntT that is actually split across the
///
template <typename IntT, AccessType type>
typename InstructionSet::x86::Accessor<IntT, type>::type access(
const uint32_t address, /// Address within linear memory.
uint32_t base, /// Start address of wrapping window within linear memory.
const uint32_t limit /// Size of wrapping window.
) {
static_assert(
std::is_same_v<IntT, uint8_t> ||
std::is_same_v<IntT, uint16_t> ||
std::is_same_v<IntT, uint32_t> ||
!is_writeable(type)
);
assert(limit >= sizeof(IntT));
base &= MaxAddress - 1;
// Bytes: always safe.
if constexpr (std::is_same_v<IntT, uint8_t>) {
return memory[address];
}
// Larger quantities: split only if they won't fit.
const auto bytes_available = base + limit - address;
if(bytes_available >= sizeof(IntT)) {
return *reinterpret_cast<IntT *>(&memory[address]);
}
// This is a large quantity that straddles the limit,
// but if it's being read only then just assemble it and
// forget about things...
if constexpr (!is_writeable(type)) {
if constexpr (std::is_same_v<IntT, uint16_t>) {
return uint16_t(memory[address] | (memory[base] << 8));
}
IntT result;
auto buffer = reinterpret_cast<uint8_t *>(&result);
std::memcpy(buffer, &memory[address], bytes_available);
std::memcpy(buffer + bytes_available, &memory[base], sizeof(IntT) - bytes_available);
return result;
}
// The caller needs an atomic unit that looks like an IntT and will
// need to be written out eventually, so set up for that.
write_back_address_[0] = address;
write_back_address_[1] = base;
write_back_lead_size_ = bytes_available;
// Seed value only if this is a modify
if constexpr (type == AccessType::ReadModifyWrite) {
auto buffer = reinterpret_cast<uint8_t *>(&write_back_value_);
if constexpr (std::is_same_v<IntT, uint16_t>) {
buffer[0] = memory[address];
buffer[1] = memory[base];
} else {
std::memcpy(buffer, &memory[address], bytes_available);
std::memcpy(buffer + bytes_available, &memory[base], sizeof(IntT) - bytes_available);
}
}
return *reinterpret_cast<IntT *>(&write_back_value_);
}
template <typename IntT>
void write_back() {
if constexpr (std::is_same_v<IntT, uint8_t>) {
return;
}
if(write_back_address_[0] == NoWriteBack) {
return;
}
auto buffer = reinterpret_cast<uint8_t *>(&write_back_value_);
if constexpr (std::is_same_v<IntT, uint16_t>) {
memory[write_back_address_[0]] = buffer[0];
memory[write_back_address_[1]] = buffer[1];
} else {
std::memcpy(&memory[write_back_address_[0]], buffer, write_back_lead_size_);
std::memcpy(&memory[write_back_address_[1]], buffer + write_back_lead_size_, sizeof(IntT) - write_back_lead_size_);
}
write_back_address_[0] = NoWriteBack;
}
private:
std::array<uint8_t, MaxAddress> memory{0xff};
static constexpr uint32_t NoWriteBack = 0; // A low byte address of 0 can't require write-back.
uint32_t write_back_address_[2] = {NoWriteBack, NoWriteBack};
uint32_t write_back_lead_size_;
uint32_t write_back_value_;
};
}

View File

@@ -14,8 +14,9 @@
#include "FloppyController.hpp"
#include "KeyboardController.hpp"
#include "KeyboardMapper.hpp"
#include "LinearMemory.hpp"
#include "MDA.hpp"
#include "Memory.hpp"
#include "SegmentedMemory.hpp"
#include "PIC.hpp"
#include "PIT.hpp"
#include "ProcessorByModel.hpp"
@@ -933,8 +934,9 @@ private:
InstructionSet::x86::Flags flags;
Registers<x86_model> registers;
LinearMemory<x86_model> linear_memory;
Segments<x86_model> segments;
Memory<pc_model> memory;
SegmentedMemory<pc_model> memory;
FlowController<pc_model> flow_controller;
CPUControl<pc_model> cpu_control;
IO<pc_model, video> io;

View File

@@ -22,7 +22,7 @@ namespace PCCompatible {
// TODO: send writes to the ROM area off to nowhere.
template <Analyser::Static::PCCompatible::Model model>
class Memory {
class SegmentedMemory {
static constexpr auto x86_model = processor_model(model);
public:
@@ -30,7 +30,7 @@ public:
using Mode = InstructionSet::x86::Mode;
// Constructor.
Memory(Registers<x86_model> &registers, const Segments<x86_model> &segments) :
SegmentedMemory(Registers<x86_model> &registers, const Segments<x86_model> &segments) :
registers_(registers), segments_(segments) {}
//

View File

@@ -1252,7 +1252,7 @@
4238200E2B17CBC800964EFE /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
4238200F2B17CBC800964EFE /* Target.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
423820102B17CBC800964EFE /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
423820132B1A235200964EFE /* Memory.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Memory.hpp; sourceTree = "<group>"; };
423820132B1A235200964EFE /* SegmentedMemory.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SegmentedMemory.hpp; sourceTree = "<group>"; };
423820142B1A23C200964EFE /* Registers.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Registers.hpp; sourceTree = "<group>"; };
423820152B1A23E100964EFE /* Segments.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Segments.hpp; sourceTree = "<group>"; };
423820422B1A90BE00964EFE /* PCBooter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCBooter.cpp; sourceTree = "<group>"; };
@@ -1926,6 +1926,7 @@
4B9EC0E926B384080060A31F /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Keyboard.hpp; sourceTree = "<group>"; };
4B9F11C82272375400701480 /* qltrace.txt.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = qltrace.txt.gz; sourceTree = "<group>"; };
4B9F11CB22729B3500701480 /* OPCLOGR2.BIN */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = OPCLOGR2.BIN; path = "68000 Coverage/OPCLOGR2.BIN"; sourceTree = "<group>"; };
4BA094C82D92339F00BD78A1 /* LinearMemory.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LinearMemory.hpp; sourceTree = "<group>"; };
4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ZX8081.cpp; sourceTree = "<group>"; };
4BA0F68D1EEA0E8400E9489E /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ZX8081.hpp; sourceTree = "<group>"; };
4BA141C12073100800A31EC9 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
@@ -2568,14 +2569,15 @@
4B1FBE222D7B739700BAC888 /* FloppyController.hpp */,
4B1FBE242D7B753000BAC888 /* KeyboardController.hpp */,
4267A9CA2B111ED2008A59BB /* KeyboardMapper.hpp */,
4BA094C82D92339F00BD78A1 /* LinearMemory.hpp */,
429B13622B1FCA96006BB4CB /* MDA.hpp */,
423820132B1A235200964EFE /* Memory.hpp */,
425739362B051EA800B7D1E4 /* PCCompatible.hpp */,
4267A9C82B0D4EC2008A59BB /* PIC.hpp */,
4267A9C72B0C26FA008A59BB /* PIT.hpp */,
4B1FBE1F2D77AAC500BAC888 /* ProcessorByModel.hpp */,
423820142B1A23C200964EFE /* Registers.hpp */,
42EB81252B21788200429AF4 /* RTC.hpp */,
423820132B1A235200964EFE /* SegmentedMemory.hpp */,
423820152B1A23E100964EFE /* Segments.hpp */,
4B1FBE232D7B73C800BAC888 /* Speaker.hpp */,
);