1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-04-06 10:38:16 +00:00

Attempt to cleave by broad reason.

This commit is contained in:
Thomas Harte 2024-03-27 22:36:37 -04:00
parent 0e17f382a1
commit f4cf1e5313

View File

@ -8,7 +8,9 @@
#import <XCTest/XCTest.h>
#include "../../../InstructionSets/ARM/Disassembler.hpp"
#include "../../../InstructionSets/ARM/Executor.hpp"
#include "CSROMFetcher.hpp"
#include "NSData+dataWithContentsOfGZippedFile.h"
@ -363,7 +365,8 @@ struct MemoryLedger {
input >> std::hex;
using Exec = Executor<Model::ARMv2with32bitAddressing, MemoryLedger>;
static constexpr auto model = Model::ARMv2with32bitAddressing;
using Exec = Executor<model, MemoryLedger>;
std::unique_ptr<Exec> test;
struct FailureRecord {
@ -373,15 +376,76 @@ struct MemoryLedger {
};
std::map<uint32_t, FailureRecord> failures;
uint32_t instruction = 0;
uint32_t opcode = 0;
bool ignore_test = false;
uint32_t masks[16];
uint32_t test_pc_offset = 8;
int test_count = 0;
while(!input.eof()) {
std::string label;
input >> label;
if(label == "**") {
input >> instruction;
memset(masks, 0xff, sizeof(masks));
ignore_test = false;
test_pc_offset = 8;
input >> opcode;
test_count = 0;
// if(opcode == 0x01a0f000) {
// printf("");
// }
InstructionSet::ARM::Disassembler<model> disassembler;
InstructionSet::ARM::dispatch<model>(opcode, disassembler);
static constexpr uint32_t pc_address_mask = 0x03ff'fffc;
const auto instruction = disassembler.last();
switch(instruction.operation) {
case Instruction::Operation::BL:
// Tests don't multiplex flags into PC for storage to R14.
masks[14] = pc_address_mask;
break;
case Instruction::Operation::SWI:
// Tested CPU either doesn't switch into supervisor mode, or
// is sufficiently accurate in its pipeline that register
// changes haven't happened yet.
ignore_test = true;
break;
case Instruction::Operation::MOV:
// MOV from PC will pick up the address only in the test cases.
if(
instruction.operand2.type == Operand::Type::Register &&
instruction.operand2.value == 15
) {
masks[instruction.destination.value] = pc_address_mask;
}
// MOV to PC; there are both pipeline capture errors in the test
// set and its ARM won't change privilege level on a write to R15.
if(instruction.destination.value == 15) {
ignore_test = true;
}
break;
case Instruction::Operation::TEQ:
case Instruction::Operation::ORR:
case Instruction::Operation::BIC:
// Routinely used to change privilege level on an ARM2 but
// doesn't seem to have that effect on the ARM used to generate
// the test set.
if(instruction.destination.value == 15) {
ignore_test = true;
}
break;
default: break;
}
continue;
}
@ -392,23 +456,21 @@ struct MemoryLedger {
input >> regs[c];
}
uint32_t r15_mask = 0xffff'ffff;
bool ignore_test = false;
switch(instruction) {
switch(opcode) {
case 0xe090e00f:
// adds lr, r0, pc
// The test set comes from an ARM that doesn't multiplex flags
// and the PC.
r15_mask = 0;
masks[15] = 0;
regs[15] &= 0x03ff'fffc;
break;
case 0xe33ff3c3:
case 0xe33ff343:
case 0xe33ef000:
// TEQs to R15; sometimes these change privilege mode, and the captures then refill
// the ARM pipeline, which doesn't match this interpreter's behaviour.
continue;
case 0xee100f10:
case 0xee105f10:
case 0xee502110:
// MRCs; tests seem possibly to have a coprocessor?
ignore_test = true;
break;
// TODO:
// * adds to R15: e090f00e, e090f00f; possibly to do with non-multiplexing original?
@ -421,7 +483,21 @@ struct MemoryLedger {
auto &registers = test->registers();
if(label == "Before:") {
// This is the start of a new test.
// TODO: establish implicit register values?
// Establish implicit register values.
for(uint32_t c = 0; c < 15; c++) {
registers.set_mode(Mode::FIQ);
registers[c] = 0x200 | c;
registers.set_mode(Mode::Supervisor);
registers[c] = 0x300 | c;
registers.set_mode(Mode::IRQ);
registers[c] = 0x400 | c;
registers.set_mode(Mode::User);
registers[c] = 0x100 | c;
}
// Apply provided state.
registers.set_mode(Mode::Supervisor); // To make sure the actual mode is applied.
@ -437,31 +513,31 @@ struct MemoryLedger {
continue;
}
if(instruction == 0xe92d8001 && test_count == 1) {
if(opcode == 0xe5abb010 && test_count == 1) {
printf("");
}
execute(instruction, *test);
execute(opcode, *test);
NSMutableString *error = nil;
for(uint32_t c = 0; c < 15; c++) {
if(regs[c] != registers[c]) {
if((regs[c] & masks[c]) != (registers[c] & masks[c])) {
if(!error) error = [[NSMutableString alloc] init]; else [error appendString:@"; "];
[error appendFormat:@"R%d %08x v %08x", c, regs[c], registers[c]];
}
}
if((regs[15] & r15_mask) != (registers.pc_status(8) & r15_mask)) {
if((regs[15] & masks[15]) != (registers.pc_status(test_pc_offset) & masks[15])) {
if(!error) error = [[NSMutableString alloc] init]; else [error appendString:@"; "];
[error appendFormat:@"; PC/PSR %08x/%08x v %08x/%08x",
regs[15] & 0x03ff'fffc, regs[15] & ~0x03ff'fffc,
registers.pc(8), registers.status()];
registers.pc(test_pc_offset), registers.status()];
}
if(error) {
++failures[instruction].count;
if(failures[instruction].count == 1) {
failures[instruction].first = test_count;
failures[instruction].sample = error;
++failures[opcode].count;
if(failures[opcode].count == 1) {
failures[opcode].first = test_count;
failures[opcode].sample = error;
}
}
@ -509,6 +585,11 @@ struct MemoryLedger {
NSLog(@"%08x, %d total, test %d: %@", pair.first, pair.second.count, pair.second.first, pair.second.sample);
}
}
for(const auto &pair: failures) {
printf("%08x ", pair.first);
}
}
// TODO: turn the below into a trace-driven test case.