1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-16 18:30:32 +00:00

Hack on through to something that builds.

This commit is contained in:
Thomas Harte 2023-10-05 22:27:52 -04:00
parent f7d9116811
commit 6d392852d2
3 changed files with 187 additions and 104 deletions

View File

@ -10,6 +10,7 @@
#define PerformImplementation_h #define PerformImplementation_h
#include "../../../Numeric/Carry.hpp" #include "../../../Numeric/Carry.hpp"
#include "../../../Numeric/RegisterSizes.hpp"
namespace InstructionSet::x86 { namespace InstructionSet::x86 {
@ -79,6 +80,7 @@ void aaa(CPU::RegisterPair16 &ax, Status &status) {
} else { } else {
status.auxiliary_carry = status.carry = 0; status.auxiliary_carry = status.carry = 0;
} }
ax.halves.low &= 0x0f;
} }
void aad(CPU::RegisterPair16 &ax, uint8_t imm, Status &status) { void aad(CPU::RegisterPair16 &ax, uint8_t imm, Status &status) {
@ -199,73 +201,63 @@ template <
using IntT = typename DataSizeType<data_size>::type; using IntT = typename DataSizeType<data_size>::type;
using AddressT = typename AddressT<is_32bit(model)>::type; using AddressT = typename AddressT<is_32bit(model)>::type;
// Establish source() and destination() shorthand to fetch data if necessary. IntT zero = 0;
IntT fetched_data = 0, original_data = 0;
Source segment;
AddressT address;
static constexpr IntT zero = 0;
auto data = [&](DataPointer source) -> IntT& { auto data = [&](DataPointer source) -> IntT& {
// Rules: // Rules:
// //
// * if this is a memory access, set target_address and break; // * if this is a memory access, set target_address and break;
// * otherwise return the appropriate value. // * otherwise return the appropriate value.
AddressT address;
switch(source.source<false>()) { switch(source.source<false>()) {
case Source::eAX: case Source::eAX:
switch(data_size) { if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.eax(); }
default: return registers.al(); else if constexpr (data_size == DataSize::DWord) { return zero; }
case DataSize::Word: return registers.ax(); else if constexpr (data_size == DataSize::Word) { return registers.ax(); }
case DataSize::DWord: return registers.eax(); else { return registers.al(); }
}
case Source::eCX: case Source::eCX:
switch(data_size) { if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.ecx(); }
default: return registers.cl(); else if constexpr (data_size == DataSize::DWord) { return zero; }
case DataSize::Word: return registers.cx(); else if constexpr (data_size == DataSize::Word) { return registers.cx(); }
case DataSize::DWord: return registers.ecx(); else { return registers.cl(); }
}
case Source::eDX: case Source::eDX:
switch(data_size) { if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.edx(); }
default: return registers.dl(); else if constexpr (data_size == DataSize::DWord) { return zero; }
case DataSize::Word: return registers.dx(); else if constexpr (data_size == DataSize::Word) { return registers.dx(); }
case DataSize::DWord: return registers.edx(); else { return registers.dl(); }
}
case Source::eBX: case Source::eBX:
switch(data_size) { if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.ebx(); }
default: return registers.bl(); else if constexpr (data_size == DataSize::DWord) { return zero; }
case DataSize::Word: return registers.bx(); else if constexpr (data_size == DataSize::Word) { return registers.bx(); }
case DataSize::DWord: return registers.ebx(); else { return registers.bl(); }
}
case Source::eSPorAH: case Source::eSPorAH:
switch(data_size) { if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.esp(); }
default: return registers.ah(); else if constexpr (data_size == DataSize::DWord) { return zero; }
case DataSize::Word: return registers.sp(); else if constexpr (data_size == DataSize::Word) { return registers.sp(); }
case DataSize::DWord: return registers.esp(); else { return registers.ah(); }
}
case Source::eBPorCH: case Source::eBPorCH:
switch(data_size) { if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.ebp(); }
default: return registers.ch(); else if constexpr (data_size == DataSize::DWord) { return zero; }
case DataSize::Word: return registers.bp(); else if constexpr (data_size == DataSize::Word) { return registers.bp(); }
case DataSize::DWord: return registers.ebp(); else { return registers.ch(); }
}
case Source::eSIorDH: case Source::eSIorDH:
switch(data_size) { if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.esi(); }
default: return registers.dh(); else if constexpr (data_size == DataSize::DWord) { return zero; }
case DataSize::Word: return registers.si(); else if constexpr (data_size == DataSize::Word) { return registers.si(); }
case DataSize::DWord: return registers.esi(); else { return registers.dh(); }
}
case Source::eDIorBH: case Source::eDIorBH:
switch(data_size) { if constexpr (is_32bit(model) && data_size == DataSize::DWord) { return registers.edi(); }
default: return registers.bh(); else if constexpr (data_size == DataSize::DWord) { return zero; }
case DataSize::Word: return registers.di(); else if constexpr (data_size == DataSize::Word) { return registers.di(); }
case DataSize::DWord: return registers.edi(); else { return registers.bh(); }
}
case Source::ES: return registers.es(); // TODO: the below.
case Source::CS: return registers.cs(); default:
case Source::SS: return registers.ss(); // case Source::ES: return registers.es();
case Source::DS: return registers.ds(); // case Source::CS: return registers.cs();
case Source::FS: return registers.fs(); // case Source::SS: return registers.ss();
case Source::GS: return registers.gs(); // case Source::DS: return registers.ds();
// case Source::FS: return registers.fs();
// case Source::GS: return registers.gs();
case Source::Immediate: // TODO (here the use of a reference falls down?) case Source::Immediate: // TODO (here the use of a reference falls down?)
@ -281,11 +273,11 @@ template <
// If execution has reached here then a memory fetch is required. // If execution has reached here then a memory fetch is required.
// Do it and exit. // Do it and exit.
segment = source.segment(instruction.segment_override()); const Source segment = source.segment(instruction.segment_override());
fetched_data = original_data = memory.template read<IntT>(segment, address); return memory.template access<IntT>(segment, address);
return fetched_data;
}; };
// Establish source() and destination() shorthand to fetch data if necessary.
auto source = [&]() -> IntT& { return data(instruction.source()); }; auto source = [&]() -> IntT& { return data(instruction.source()); };
auto destination = [&]() -> IntT& { return data(instruction.destination()); }; auto destination = [&]() -> IntT& { return data(instruction.destination()); };
@ -295,19 +287,21 @@ template <
// * return directly if there is definitely no possible write back to RAM; // * return directly if there is definitely no possible write back to RAM;
// * otherwise use the source() and destination() lambdas, and break in order to allow a writeback if necessary. // * otherwise use the source() and destination() lambdas, and break in order to allow a writeback if necessary.
switch(instruction.operation) { switch(instruction.operation) {
case Operation::AAA: Primitive::aaa(registers.ax(), status); return; default: assert(false);
case Operation::AAD: Primitive::aad(registers.ax(), instruction.immediate(), status); return;
case Operation::AAM: Primitive::aam(registers.ax(), instruction.immediate(), status); return; case Operation::AAA: Primitive::aaa(registers.axp(), status); return;
case Operation::AAS: Primitive::aas(registers.ax(), status); return; case Operation::AAD: Primitive::aad(registers.axp(), instruction.operand(), status); return;
case Operation::AAM: Primitive::aam(registers.axp(), instruction.operand(), status); return;
case Operation::AAS: Primitive::aas(registers.axp(), status); return;
case Operation::ADC: Primitive::adc(destination(), source(), status); break; case Operation::ADC: Primitive::adc(destination(), source(), status); break;
case Operation::ADD: Primitive::add(destination(), source(), status); break; case Operation::ADD: Primitive::add(destination(), source(), status); break;
} }
// Write to memory if required to complete this operation. // Write to memory if required to complete this operation.
if(original_data != fetched_data) { // if(original_data != fetched_data) {
// TODO. // TODO.
} // }
} }
template < template <

View File

@ -12,7 +12,6 @@
#include "Instruction.hpp" #include "Instruction.hpp"
#include "Model.hpp" #include "Model.hpp"
#include "Status.hpp" #include "Status.hpp"
#include "../../Numeric/RegisterSizes.hpp"
namespace InstructionSet::x86 { namespace InstructionSet::x86 {

View File

@ -19,6 +19,7 @@
#include "../../../InstructionSets/x86/Decoder.hpp" #include "../../../InstructionSets/x86/Decoder.hpp"
#include "../../../InstructionSets/x86/Perform.hpp" #include "../../../InstructionSets/x86/Perform.hpp"
#include "../../../Numeric/RegisterSizes.hpp"
namespace { namespace {
@ -59,22 +60,30 @@ constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1"
return [fullPaths sortedArrayUsingSelector:@selector(compare:)]; return [fullPaths sortedArrayUsingSelector:@selector(compare:)];
} }
- (NSArray<NSDictionary *> *)testsInFile:(NSString *)file {
NSData *data = [NSData dataWithContentsOfGZippedFile:file];
return [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
}
- (NSString *)toString:(const InstructionSet::x86::Instruction<false> &)instruction offsetLength:(int)offsetLength immediateLength:(int)immediateLength { - (NSString *)toString:(const InstructionSet::x86::Instruction<false> &)instruction offsetLength:(int)offsetLength immediateLength:(int)immediateLength {
const auto operation = to_string(instruction, InstructionSet::x86::Model::i8086, offsetLength, immediateLength); const auto operation = to_string(instruction, InstructionSet::x86::Model::i8086, offsetLength, immediateLength);
return [[NSString stringWithUTF8String:operation.c_str()] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; return [[NSString stringWithUTF8String:operation.c_str()] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
} }
- (bool)applyDecodingTest:(NSDictionary *)test file:(NSString *)file assert:(BOOL)assert { - (std::vector<uint8_t>)bytes:(NSArray<NSNumber *> *)encoding {
using Decoder = InstructionSet::x86::Decoder<InstructionSet::x86::Model::i8086>;
Decoder decoder;
// Build a vector of the instruction bytes; this makes manual step debugging easier.
NSArray<NSNumber *> *encoding = test[@"bytes"];
std::vector<uint8_t> data; std::vector<uint8_t> data;
data.reserve(encoding.count); data.reserve(encoding.count);
for(NSNumber *number in encoding) { for(NSNumber *number in encoding) {
data.push_back([number intValue]); data.push_back([number intValue]);
} }
return data;
}
- (bool)applyDecodingTest:(NSDictionary *)test file:(NSString *)file assert:(BOOL)assert {
InstructionSet::x86::Decoder<InstructionSet::x86::Model::i8086> decoder;
// Build a vector of the instruction bytes; this makes manual step debugging easier.
const auto data = [self bytes:test[@"bytes"]];
auto hex_instruction = [&]() -> NSString * { auto hex_instruction = [&]() -> NSString * {
NSMutableString *hexInstruction = [[NSMutableString alloc] init]; NSMutableString *hexInstruction = [[NSMutableString alloc] init];
for(uint8_t byte: data) { for(uint8_t byte: data) {
@ -84,18 +93,19 @@ constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1"
}; };
const auto decoded = decoder.decode(data.data(), data.size()); const auto decoded = decoder.decode(data.data(), data.size());
const bool sizeMatched = decoded.first == data.size();
if(assert) { if(assert) {
XCTAssert( XCTAssert(
decoded.first == [encoding count], sizeMatched,
"Wrong length of instruction decoded for %@ — decoded %d rather than %lu from %@; file %@", "Wrong length of instruction decoded for %@ — decoded %d rather than %lu from %@; file %@",
test[@"name"], test[@"name"],
decoded.first, decoded.first,
(unsigned long)[encoding count], (unsigned long)data.size(),
hex_instruction(), hex_instruction(),
file file
); );
} }
if(decoded.first != [encoding count]) { if(!sizeMatched) {
return false; return false;
} }
@ -158,54 +168,134 @@ constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1"
return isEqual; return isEqual;
} }
- (void)testDecoding { - (bool)applyExecutionTest:(NSDictionary *)test file:(NSString *)file assert:(BOOL)assert {
NSMutableSet<NSString *> *failures = [[NSMutableSet alloc] init]; InstructionSet::x86::Decoder<InstructionSet::x86::Model::i8086> decoder;
NSArray<NSString *> *testFiles = [self testFiles]; const auto data = [self bytes:test[@"bytes"]];
const auto decoded = decoder.decode(data.data(), data.size());
for(NSString *file in testFiles) { struct Registers {
NSData *data = [NSData dataWithContentsOfGZippedFile:file]; CPU::RegisterPair16 ax_;
NSArray<NSDictionary *> *testsInFile = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; uint8_t &al() { return ax_.halves.low; }
NSUInteger successes = 0; uint8_t &ah() { return ax_.halves.high; }
for(NSDictionary *test in testsInFile) { uint16_t &ax() { return ax_.full; }
CPU::RegisterPair16 &axp() { return ax_; }
CPU::RegisterPair16 cx_;
uint8_t &cl() { return cx_.halves.low; }
uint8_t &ch() { return cx_.halves.high; }
uint16_t &cx() { return cx_.full; }
CPU::RegisterPair16 dx_;
uint8_t &dl() { return dx_.halves.low; }
uint8_t &dh() { return dx_.halves.high; }
uint16_t &dx() { return dx_.full; }
CPU::RegisterPair16 bx_;
uint8_t &bl() { return bx_.halves.low; }
uint8_t &bh() { return bx_.halves.high; }
uint16_t &bx() { return bx_.full; }
uint16_t sp_;
uint16_t &sp() { return sp_; }
uint16_t bp_;
uint16_t &bp() { return bp_; }
uint16_t si_;
uint16_t &si() { return si_; }
uint16_t di_;
uint16_t &di() { return di_; }
uint16_t es_, cs_, ds_, ss_;
};
struct Memory {
std::vector<uint8_t> memory;
const Registers &registers_;
Memory(Registers &registers) : registers_(registers) {
memory.resize(1024*1024);
}
template <typename IntT> IntT &access([[maybe_unused]] InstructionSet::x86::Source segment, uint16_t address) {
uint32_t physical_address;
using Source = InstructionSet::x86::Source;
switch(segment) {
default: address = registers_.ds_; break;
case Source::ES: address = registers_.es_; break;
case Source::CS: address = registers_.cs_; break;
case Source::DS: address = registers_.ds_; break;
}
physical_address = ((physical_address << 4) + address) & 0xf'ffff;
return *reinterpret_cast<IntT *>(&memory[physical_address]);
}
};
struct IO {
};
struct FlowController {
};
InstructionSet::x86::Status status;
FlowController flow_controller;
Registers registers;
Memory memory(registers);
IO io;
InstructionSet::x86::perform<InstructionSet::x86::Model::i8086>(
decoded.second,
status,
flow_controller,
registers,
memory,
io
);
return false;
}
- (void)printFailures:(NSArray<NSString *> *)failures {
NSLog(
@"%ld failures out of %ld tests: %@",
failures.count,
[self testFiles].count,
[failures sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]);
}
- (void)testDecoding {
NSMutableArray<NSString *> *failures = [[NSMutableArray alloc] init];
for(NSString *file in [self testFiles]) {
for(NSDictionary *test in [self testsInFile:file]) {
// A single failure per instruction is fine. // A single failure per instruction is fine.
if(![self applyDecodingTest:test file:file assert:YES]) { if(![self applyDecodingTest:test file:file assert:YES]) {
[failures addObject:file]; [failures addObject:file];
// Attempt a second decoding, to provide a debugger hook. // Attempt a second decoding, to provide a debugger hook.
[self applyDecodingTest:test file:file assert:NO]; [self applyDecodingTest:test file:file assert:NO];
break; break;
} }
++successes;
}
if(successes != [testsInFile count]) {
NSLog(@"Failed after %ld successes", successes);
} }
} }
NSLog(@"%ld failures out of %ld tests: %@", failures.count, testFiles.count, [[failures allObjects] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]); [self printFailures:failures];
} }
- (void)testExecution { - (void)testExecution {
// CPU::RegisterPair16 source, dest; NSMutableArray<NSString *> *failures = [[NSMutableArray alloc] init];
// InstructionSet::x86::Status status; for(NSString *file in [self testFiles]) {
// struct NoFlow { for(NSDictionary *test in [self testsInFile:file]) {
// } flow_controller; // A single failure per instruction is fine.
// if(![self applyExecutionTest:test file:file assert:YES]) {
// dest.full = 0xff; [failures addObject:file];
// source.full = 10;
// // Attempt a second decoding, to provide a debugger hook.
// InstructionSet::x86::perform< [self applyExecutionTest:test file:file assert:NO];
// InstructionSet::x86::Model::i8086, break;
// InstructionSet::x86::Operation::ADD, }
// InstructionSet::x86::DataSize::Byte }
// >( }
// dest,
// source, [self printFailures:failures];
// status,
// flow_controller
// );
//
} }
@end @end