1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-28 07:29:45 +00:00

Clean up Memory.

This commit is contained in:
Thomas Harte 2023-11-02 14:25:13 -04:00
parent e4fdf09149
commit 8d0deeb20e

View File

@ -91,142 +91,149 @@ struct Registers {
} }
}; };
struct Memory { struct Memory {
using AccessType = InstructionSet::x86::AccessType; public:
using AccessType = InstructionSet::x86::AccessType;
template <typename IntT, AccessType type> struct ReturnType; template <typename IntT, AccessType type> struct ReturnType;
// Reads: return a value directly. // Reads: return a value directly.
template <typename IntT> struct ReturnType<IntT, AccessType::Read> { using type = IntT; }; template <typename IntT> struct ReturnType<IntT, AccessType::Read> { using type = IntT; };
template <typename IntT> struct ReturnType<IntT, AccessType::PreauthorisedRead> { using type = IntT; }; template <typename IntT> struct ReturnType<IntT, AccessType::PreauthorisedRead> { using type = IntT; };
// Writes: return a reference. // Writes: return a reference.
template <typename IntT> struct ReturnType<IntT, AccessType::Write> { using type = IntT &; }; template <typename IntT> struct ReturnType<IntT, AccessType::Write> { using type = IntT &; };
template <typename IntT> struct ReturnType<IntT, AccessType::ReadModifyWrite> { using type = IntT &; }; template <typename IntT> struct ReturnType<IntT, AccessType::ReadModifyWrite> { using type = IntT &; };
template <typename IntT> struct ReturnType<IntT, AccessType::PreauthorisedWrite> { using type = IntT &; }; template <typename IntT> struct ReturnType<IntT, AccessType::PreauthorisedWrite> { using type = IntT &; };
// Constructor.
enum class Tag { Memory(Registers &registers) : registers_(registers) {
Seeded, memory.resize(1024*1024);
AccessExpected,
Accessed,
};
std::unordered_map<uint32_t, Tag> tags;
std::vector<uint8_t> memory;
const Registers &registers_;
Memory(Registers &registers) : registers_(registers) {
memory.resize(1024*1024);
}
void clear() {
tags.clear();
}
void seed(uint32_t address, uint8_t value) {
memory[address] = value;
tags[address] = Tag::Seeded;
}
void touch(uint32_t address) {
tags[address] = Tag::AccessExpected;
}
uint32_t segment_base(InstructionSet::x86::Source segment) {
uint32_t physical_address;
using Source = InstructionSet::x86::Source;
switch(segment) {
default: physical_address = registers_.ds_; break;
case Source::ES: physical_address = registers_.es_; break;
case Source::CS: physical_address = registers_.cs_; break;
case Source::SS: physical_address = registers_.ss_; break;
} }
return physical_address << 4;
}
void preauthorise_stack_write(uint32_t) {} // Initialisation.
void preauthorise_stack_read(uint32_t) {} void clear() {
void preauthorise_read(InstructionSet::x86::Source, uint16_t, uint32_t) {} tags.clear();
void preauthorise_read(uint16_t, uint32_t) {} }
// Entry point used by the flow controller so that it can mark up locations at which the flags were written, void seed(uint32_t address, uint8_t value) {
// so that defined-flag-only masks can be applied while verifying RAM contents. memory[address] = value;
template <typename IntT, AccessType type> tags[address] = Tag::Seeded;
typename ReturnType<IntT, type>::type &access(InstructionSet::x86::Source segment, uint16_t address, Tag tag) { }
const uint32_t physical_address = (segment_base(segment) + address) & 0xf'ffff;
return access<IntT, type>(physical_address, tag);
}
// An additional entry point for the flow controller; on the original 8086 interrupt vectors aren't relative void touch(uint32_t address) {
// to a selector, they're just at an absolute location. tags[address] = Tag::AccessExpected;
template <typename IntT, AccessType type> }
typename ReturnType<IntT, type>::type &access(uint32_t address, Tag tag) {
// Check for address wraparound // Preauthorisation call-ins.
if(address >= 0x10'0001 - sizeof(IntT)) { void preauthorise_stack_write(uint32_t) {}
if constexpr (std::is_same_v<IntT, uint8_t>) { void preauthorise_stack_read(uint32_t) {}
address &= 0xf'ffff; void preauthorise_read(InstructionSet::x86::Source, uint16_t, uint32_t) {}
} else { void preauthorise_read(uint16_t, uint32_t) {}
if(address == 0xf'ffff) {
// This is a 16-bit access comprising the final byte in memory and the first. // Access call-ins.
write_back_address_[0] = address;
write_back_address_[1] = 0; // Accesses an address based on segment:offset.
template <typename IntT, AccessType type>
typename ReturnType<IntT, type>::type &access(InstructionSet::x86::Source segment, uint32_t address) {
if constexpr (std::is_same_v<IntT, uint16_t>) {
// If this is a 16-bit access that runs past the end of the segment, it'll wrap back
// to the start. So the 16-bit value will need to be a local cache.
if(address == 0xffff) {
write_back_address_[0] = (segment_base(segment) + address) & 0xf'ffff;
write_back_address_[1] = (write_back_address_[0] - 65535) & 0xf'ffff;
write_back_value_ = memory[write_back_address_[0]] | (memory[write_back_address_[1]] << 8); write_back_value_ = memory[write_back_address_[0]] | (memory[write_back_address_[1]] << 8);
return write_back_value_; return write_back_value_;
} else { }
address &= 0xf'ffff; }
auto &value = access<IntT, type>(segment, address, Tag::Accessed);
// If the CPU has indicated a write, it should be safe to fuzz the value now.
if(type == AccessType::Write) {
value = IntT(~0);
}
return value;
}
// Accesses an address based on physical location.
template <typename IntT, AccessType type>
typename ReturnType<IntT, type>::type &access(uint32_t address) {
return access<IntT, type>(address, Tag::Accessed);
}
template <typename IntT>
void write_back() {
if constexpr (std::is_same_v<IntT, uint16_t>) {
if(write_back_address_[0] != NoWriteBack) {
memory[write_back_address_[0]] = write_back_value_ & 0xff;
memory[write_back_address_[1]] = write_back_value_ >> 8;
write_back_address_[0] = 0;
} }
} }
} }
if(tags.find(address) == tags.end()) { private:
printf("Access to unexpected RAM address"); enum class Tag {
} Seeded,
tags[address] = tag; AccessExpected,
return *reinterpret_cast<IntT *>(&memory[address]); Accessed,
} };
// Entry point for the 8086; simply notes that memory was accessed. std::unordered_map<uint32_t, Tag> tags;
template <typename IntT, AccessType type> std::vector<uint8_t> memory;
typename ReturnType<IntT, type>::type &access(InstructionSet::x86::Source segment, uint32_t address) { const Registers &registers_;
if constexpr (std::is_same_v<IntT, uint16_t>) {
// If this is a 16-bit access that runs past the end of the segment, it'll wrap back uint32_t segment_base(InstructionSet::x86::Source segment) {
// to the start. So the 16-bit value will need to be a local cache. uint32_t physical_address;
if(address == 0xffff) { using Source = InstructionSet::x86::Source;
write_back_address_[0] = (segment_base(segment) + address) & 0xf'ffff; switch(segment) {
write_back_address_[1] = (write_back_address_[0] - 65535) & 0xf'ffff; default: physical_address = registers_.ds_; break;
write_back_value_ = memory[write_back_address_[0]] | (memory[write_back_address_[1]] << 8); case Source::ES: physical_address = registers_.es_; break;
return write_back_value_; case Source::CS: physical_address = registers_.cs_; break;
case Source::SS: physical_address = registers_.ss_; break;
} }
} return physical_address << 4;
auto &value = access<IntT, type>(segment, address, Tag::Accessed);
// If the CPU has indicated a write, it should be safe to fuzz the value now.
if(type == AccessType::Write) {
value = IntT(~0);
} }
return value;
}
template <typename IntT, AccessType type> // Entry point used by the flow controller so that it can mark up locations at which the flags were written,
typename ReturnType<IntT, type>::type &access(uint32_t address) { // so that defined-flag-only masks can be applied while verifying RAM contents.
return access<IntT, type>(address, Tag::Accessed); template <typename IntT, AccessType type>
} typename ReturnType<IntT, type>::type &access(InstructionSet::x86::Source segment, uint16_t address, Tag tag) {
const uint32_t physical_address = (segment_base(segment) + address) & 0xf'ffff;
return access<IntT, type>(physical_address, tag);
}
template <typename IntT> // An additional entry point for the flow controller; on the original 8086 interrupt vectors aren't relative
void write_back() { // to a selector, they're just at an absolute location.
if constexpr (std::is_same_v<IntT, uint16_t>) { template <typename IntT, AccessType type>
if(write_back_address_[0] != NoWriteBack) { typename ReturnType<IntT, type>::type &access(uint32_t address, Tag tag) {
memory[write_back_address_[0]] = write_back_value_ & 0xff; // Check for address wraparound
memory[write_back_address_[1]] = write_back_value_ >> 8; if(address > 0x10'0000 - sizeof(IntT)) {
write_back_address_[0] = 0; if constexpr (std::is_same_v<IntT, uint8_t>) {
address &= 0xf'ffff;
} else {
address &= 0xf'ffff;
if(address == 0xf'ffff) {
// This is a 16-bit access comprising the final byte in memory and the first.
write_back_address_[0] = address;
write_back_address_[1] = 0;
write_back_value_ = memory[write_back_address_[0]] | (memory[write_back_address_[1]] << 8);
return write_back_value_;
}
}
} }
}
}
static constexpr uint32_t NoWriteBack = 0; // A low byte address of 0 can't require write-back. if(tags.find(address) == tags.end()) {
uint32_t write_back_address_[2] = {NoWriteBack, NoWriteBack}; printf("Access to unexpected RAM address");
uint16_t write_back_value_; }
tags[address] = tag;
return *reinterpret_cast<IntT *>(&memory[address]);
}
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};
uint16_t write_back_value_;
}; };
struct IO { struct IO {
template <typename IntT> void out([[maybe_unused]] uint16_t port, [[maybe_unused]] IntT value) {} template <typename IntT> void out([[maybe_unused]] uint16_t port, [[maybe_unused]] IntT value) {}
@ -524,8 +531,9 @@ struct FailedExecution {
int mask_position = 0; int mask_position = 0;
for(NSArray<NSNumber *> *ram in final_state[@"ram"]) { for(NSArray<NSNumber *> *ram in final_state[@"ram"]) {
const uint32_t address = [ram[0] intValue]; const uint32_t address = [ram[0] intValue];
const auto value = execution_support.memory.access<uint8_t, Memory::AccessType::Read>(address);
if((mask_position != 1) && execution_support.memory.memory[address] == [ram[1] intValue]) { if((mask_position != 1) && value == [ram[1] intValue]) {
continue; continue;
} }
@ -535,7 +543,7 @@ struct FailedExecution {
while(mask_position < 2) { while(mask_position < 2) {
const uint8_t mask = mask_position ? (flags_mask >> 8) : (flags_mask & 0xff); const uint8_t mask = mask_position ? (flags_mask >> 8) : (flags_mask & 0xff);
++mask_position; ++mask_position;
if((execution_support.memory.memory[address] & mask) == ([ram[1] intValue] & mask)) { if((value & mask) == ([ram[1] intValue] & mask)) {
matched_with_mask = true; matched_with_mask = true;
break; break;
} }