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

View File

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

View File

@ -19,6 +19,7 @@
#include "../../../InstructionSets/x86/Decoder.hpp"
#include "../../../InstructionSets/x86/Perform.hpp"
#include "../../../Numeric/RegisterSizes.hpp"
namespace {
@ -59,22 +60,30 @@ constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1"
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 {
const auto operation = to_string(instruction, InstructionSet::x86::Model::i8086, offsetLength, immediateLength);
return [[NSString stringWithUTF8String:operation.c_str()] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
- (bool)applyDecodingTest:(NSDictionary *)test file:(NSString *)file assert:(BOOL)assert {
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>)bytes:(NSArray<NSNumber *> *)encoding {
std::vector<uint8_t> data;
data.reserve(encoding.count);
for(NSNumber *number in encoding) {
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 * {
NSMutableString *hexInstruction = [[NSMutableString alloc] init];
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 bool sizeMatched = decoded.first == data.size();
if(assert) {
XCTAssert(
decoded.first == [encoding count],
sizeMatched,
"Wrong length of instruction decoded for %@ — decoded %d rather than %lu from %@; file %@",
test[@"name"],
decoded.first,
(unsigned long)[encoding count],
(unsigned long)data.size(),
hex_instruction(),
file
);
}
if(decoded.first != [encoding count]) {
if(!sizeMatched) {
return false;
}
@ -158,54 +168,134 @@ constexpr char TestSuiteHome[] = "/Users/tharte/Projects/ProcessorTests/8088/v1"
return isEqual;
}
- (void)testDecoding {
NSMutableSet<NSString *> *failures = [[NSMutableSet alloc] init];
NSArray<NSString *> *testFiles = [self testFiles];
- (bool)applyExecutionTest:(NSDictionary *)test file:(NSString *)file assert:(BOOL)assert {
InstructionSet::x86::Decoder<InstructionSet::x86::Model::i8086> decoder;
const auto data = [self bytes:test[@"bytes"]];
const auto decoded = decoder.decode(data.data(), data.size());
for(NSString *file in testFiles) {
NSData *data = [NSData dataWithContentsOfGZippedFile:file];
NSArray<NSDictionary *> *testsInFile = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSUInteger successes = 0;
for(NSDictionary *test in testsInFile) {
struct Registers {
CPU::RegisterPair16 ax_;
uint8_t &al() { return ax_.halves.low; }
uint8_t &ah() { return ax_.halves.high; }
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.
if(![self applyDecodingTest:test file:file assert:YES]) {
[failures addObject:file];
// Attempt a second decoding, to provide a debugger hook.
[self applyDecodingTest:test file:file assert:NO];
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 {
// CPU::RegisterPair16 source, dest;
// InstructionSet::x86::Status status;
// struct NoFlow {
// } flow_controller;
//
// dest.full = 0xff;
// source.full = 10;
//
// InstructionSet::x86::perform<
// InstructionSet::x86::Model::i8086,
// InstructionSet::x86::Operation::ADD,
// InstructionSet::x86::DataSize::Byte
// >(
// dest,
// source,
// status,
// flow_controller
// );
//
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.
if(![self applyExecutionTest:test file:file assert:YES]) {
[failures addObject:file];
// Attempt a second decoding, to provide a debugger hook.
[self applyExecutionTest:test file:file assert:NO];
break;
}
}
}
[self printFailures:failures];
}
@end