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:
parent
f7d9116811
commit
6d392852d2
@ -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 <
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "Instruction.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "Status.hpp"
|
||||
#include "../../Numeric/RegisterSizes.hpp"
|
||||
|
||||
namespace InstructionSet::x86 {
|
||||
|
||||
|
@ -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 ®isters_;
|
||||
|
||||
Memory(Registers ®isters) : 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
|
||||
|
Loading…
Reference in New Issue
Block a user