1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Introduce disaster of an attempted test run.

This commit is contained in:
Thomas Harte 2024-03-02 22:40:12 -05:00
parent 37499d493a
commit 1663d3d9d1
3 changed files with 42 additions and 19 deletions

View File

@ -18,7 +18,7 @@ namespace InstructionSet::ARM {
/// A class compatible with the @c OperationMapper definition of a scheduler which applies all actions
/// immediately, updating either a set of @c Registers or using the templated @c MemoryT to access
/// memory. No hooks are currently provided for applying realistic timing.
template <typename MemoryT>
template <Model model, typename MemoryT>
struct Executor {
bool should_schedule(Condition condition) {
return registers_.test(condition);
@ -49,7 +49,7 @@ struct Executor {
// PC will be 8 bytes ahead when used as Rs."
shift_amount =
fields.shift_register() == 15 ?
registers_.pc(8) :
registers_.pc(4) :
registers_.active[fields.shift_register()];
// A register shift amount of 0 has a different meaning than an in-instruction
@ -84,7 +84,7 @@ struct Executor {
// when used as Rn or Rm."
const uint32_t operand1 =
(fields.operand1() == 15) ?
registers_.pc(shift_by_register ? 12 : 8) :
registers_.pc(shift_by_register ? 8 : 4) :
registers_.active[fields.operand1()];
uint32_t operand2;
@ -100,7 +100,7 @@ struct Executor {
shift<ShiftType::RotateRight, shift_sets_carry>(operand2, fields.rotate(), rotate_carry);
}
} else {
operand2 = decode_shift<true, shift_sets_carry>(fields, rotate_carry, shift_by_register ? 12 : 8);
operand2 = decode_shift<true, shift_sets_carry>(fields, rotate_carry, shift_by_register ? 8 : 4);
}
// Perform the data processing operation.
@ -216,11 +216,11 @@ struct Executor {
// * Rn: with PSR, 8 bytes ahead;
// * Rm: with PSR, 12 bytes ahead.
const uint32_t multiplicand = fields.multiplicand() == 15 ? registers_.pc(8) : registers_.active[fields.multiplicand()];
const uint32_t multiplier = fields.multiplier() == 15 ? registers_.pc_status(8) : registers_.active[fields.multiplier()];
const uint32_t multiplicand = fields.multiplicand() == 15 ? registers_.pc(4) : registers_.active[fields.multiplicand()];
const uint32_t multiplier = fields.multiplier() == 15 ? registers_.pc_status(4) : registers_.active[fields.multiplier()];
const uint32_t accumulator =
flags.operation() == MultiplyFlags::Operation::MUL ? 0 :
(fields.multiplicand() == 15 ? registers_.pc_status(12) : registers_.active[fields.accumulator()]);
(fields.multiplicand() == 15 ? registers_.pc_status(8) : registers_.active[fields.accumulator()]);
const uint32_t result = multiplicand * multiplier + accumulator;
@ -238,9 +238,9 @@ struct Executor {
constexpr BranchFlags flags(f);
if constexpr (flags.operation() == BranchFlags::Operation::BL) {
registers_.active[14] = registers_.pc(4);
registers_.active[14] = registers_.pc(0);
}
registers_.set_pc(registers_.pc(8) + branch.offset());
registers_.set_pc(registers_.pc(4) + branch.offset());
}
template <Flags f> void perform(SingleDataTransfer transfer) {
@ -255,13 +255,13 @@ struct Executor {
// the register specified shift amounts are not available
// in this instruction class.
uint32_t carry = registers_.c();
offset = decode_shift<false, false>(transfer, carry, 8);
offset = decode_shift<false, false>(transfer, carry, 4);
}
// Obtain base address.
uint32_t address =
transfer.base() == 15 ?
registers_.pc(8) :
registers_.pc(4) :
registers_.active[transfer.base()];
// Determine what the address will be after offsetting.
@ -287,7 +287,7 @@ struct Executor {
if constexpr (flags.operation() == SingleDataTransferFlags::Operation::STR) {
const uint32_t source =
transfer.source() == 15 ?
registers_.pc_status(12) :
registers_.pc_status(8) :
registers_.active[transfer.source()];
bool did_write;
@ -354,7 +354,7 @@ struct Executor {
// it has to be restored later, and to write that value rather than
// the final address if the base register is first in the write-out list.
uint32_t address = transfer.base() == 15 ?
registers_.pc_status(8) :
registers_.pc_status(4) :
registers_.active[transfer.base()];
const uint32_t initial_address = address;
@ -501,7 +501,7 @@ struct Executor {
if(list & (1 << 15)) {
uint32_t value;
if constexpr (flags.operation() == BlockDataTransferFlags::Operation::STM) {
value = registers_.pc_status(12);
value = registers_.pc_status(8);
access(value);
} else {
access(value);
@ -550,6 +550,10 @@ struct Executor {
registers_.set_pc(pc);
}
uint32_t pc() const {
return registers_.pc(0);
}
private:
Registers registers_;
};
@ -557,9 +561,11 @@ private:
/// Provides an analogue of the @c OperationMapper -affiliated @c dispatch that also updates the
/// program counter in an executor's register bank appropriately.
template <Model model, typename MemoryT>
void dispatch(uint32_t pc, uint32_t instruction, Executor<MemoryT> &executor) {
executor.set_pc(pc);
void dispatch(uint32_t &pc, uint32_t instruction, Executor<model, MemoryT> &executor) {
// TODO: avoid PC gymnastics below.
executor.set_pc(pc + 4);
dispatch<model>(instruction, executor);
pc = executor.pc();
}
}

View File

@ -39,8 +39,10 @@ enum class Mode {
/// Combines the ARM registers and status flags into a single whole, given that the architecture
/// doesn't have the same degree of separation as others.
///
/// The PC contained here is always taken to be **the address of the current instruction**,
/// i.e. disregarding pipeline differences. Appropriate prefetch offsets are left to other code to handle.
/// The PC contained here is always taken to be **the address of the current instruction + 4**,
/// i.e. whatever should be executed next, disregarding pipeline differences.
///
/// Appropriate prefetch offsets are left to other code to handle.
/// This is to try to keep this structure independent of a specific ARM implementation.
struct Registers {
public:

View File

@ -192,7 +192,22 @@ struct Memory {
ROM::Request request(rom_name);
const auto roms = CSROMFetcher()(request);
NSLog(@"");
const auto rom = roms.find(rom_name)->second;
uint32_t pc = 0;
Executor<Model::ARMv2, Memory> executor;
for(int c = 0; c < 100; c++) {
uint32_t instruction;
if(pc > 0x3800000) {
instruction = *reinterpret_cast<const uint32_t *>(&rom[pc - 0x3800000]);
} else {
instruction = *reinterpret_cast<const uint32_t *>(&rom[pc]);
}
printf("%08x: %08x\n", pc, instruction);
dispatch<Model::ARMv2>(pc, instruction, executor);
}
}
@end