1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-06 13:31:55 +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 /// 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 /// 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. /// memory. No hooks are currently provided for applying realistic timing.
template <typename MemoryT> template <Model model, typename MemoryT>
struct Executor { struct Executor {
bool should_schedule(Condition condition) { bool should_schedule(Condition condition) {
return registers_.test(condition); return registers_.test(condition);
@ -49,7 +49,7 @@ struct Executor {
// PC will be 8 bytes ahead when used as Rs." // PC will be 8 bytes ahead when used as Rs."
shift_amount = shift_amount =
fields.shift_register() == 15 ? fields.shift_register() == 15 ?
registers_.pc(8) : registers_.pc(4) :
registers_.active[fields.shift_register()]; registers_.active[fields.shift_register()];
// A register shift amount of 0 has a different meaning than an in-instruction // 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." // when used as Rn or Rm."
const uint32_t operand1 = const uint32_t operand1 =
(fields.operand1() == 15) ? (fields.operand1() == 15) ?
registers_.pc(shift_by_register ? 12 : 8) : registers_.pc(shift_by_register ? 8 : 4) :
registers_.active[fields.operand1()]; registers_.active[fields.operand1()];
uint32_t operand2; uint32_t operand2;
@ -100,7 +100,7 @@ struct Executor {
shift<ShiftType::RotateRight, shift_sets_carry>(operand2, fields.rotate(), rotate_carry); shift<ShiftType::RotateRight, shift_sets_carry>(operand2, fields.rotate(), rotate_carry);
} }
} else { } 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. // Perform the data processing operation.
@ -216,11 +216,11 @@ struct Executor {
// * Rn: with PSR, 8 bytes ahead; // * Rn: with PSR, 8 bytes ahead;
// * Rm: with PSR, 12 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 multiplicand = fields.multiplicand() == 15 ? registers_.pc(4) : registers_.active[fields.multiplicand()];
const uint32_t multiplier = fields.multiplier() == 15 ? registers_.pc_status(8) : registers_.active[fields.multiplier()]; const uint32_t multiplier = fields.multiplier() == 15 ? registers_.pc_status(4) : registers_.active[fields.multiplier()];
const uint32_t accumulator = const uint32_t accumulator =
flags.operation() == MultiplyFlags::Operation::MUL ? 0 : 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; const uint32_t result = multiplicand * multiplier + accumulator;
@ -238,9 +238,9 @@ struct Executor {
constexpr BranchFlags flags(f); constexpr BranchFlags flags(f);
if constexpr (flags.operation() == BranchFlags::Operation::BL) { 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) { template <Flags f> void perform(SingleDataTransfer transfer) {
@ -255,13 +255,13 @@ struct Executor {
// the register specified shift amounts are not available // the register specified shift amounts are not available
// in this instruction class. // in this instruction class.
uint32_t carry = registers_.c(); uint32_t carry = registers_.c();
offset = decode_shift<false, false>(transfer, carry, 8); offset = decode_shift<false, false>(transfer, carry, 4);
} }
// Obtain base address. // Obtain base address.
uint32_t address = uint32_t address =
transfer.base() == 15 ? transfer.base() == 15 ?
registers_.pc(8) : registers_.pc(4) :
registers_.active[transfer.base()]; registers_.active[transfer.base()];
// Determine what the address will be after offsetting. // Determine what the address will be after offsetting.
@ -287,7 +287,7 @@ struct Executor {
if constexpr (flags.operation() == SingleDataTransferFlags::Operation::STR) { if constexpr (flags.operation() == SingleDataTransferFlags::Operation::STR) {
const uint32_t source = const uint32_t source =
transfer.source() == 15 ? transfer.source() == 15 ?
registers_.pc_status(12) : registers_.pc_status(8) :
registers_.active[transfer.source()]; registers_.active[transfer.source()];
bool did_write; bool did_write;
@ -354,7 +354,7 @@ struct Executor {
// it has to be restored later, and to write that value rather than // 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. // the final address if the base register is first in the write-out list.
uint32_t address = transfer.base() == 15 ? uint32_t address = transfer.base() == 15 ?
registers_.pc_status(8) : registers_.pc_status(4) :
registers_.active[transfer.base()]; registers_.active[transfer.base()];
const uint32_t initial_address = address; const uint32_t initial_address = address;
@ -501,7 +501,7 @@ struct Executor {
if(list & (1 << 15)) { if(list & (1 << 15)) {
uint32_t value; uint32_t value;
if constexpr (flags.operation() == BlockDataTransferFlags::Operation::STM) { if constexpr (flags.operation() == BlockDataTransferFlags::Operation::STM) {
value = registers_.pc_status(12); value = registers_.pc_status(8);
access(value); access(value);
} else { } else {
access(value); access(value);
@ -550,6 +550,10 @@ struct Executor {
registers_.set_pc(pc); registers_.set_pc(pc);
} }
uint32_t pc() const {
return registers_.pc(0);
}
private: private:
Registers registers_; Registers registers_;
}; };
@ -557,9 +561,11 @@ private:
/// Provides an analogue of the @c OperationMapper -affiliated @c dispatch that also updates the /// Provides an analogue of the @c OperationMapper -affiliated @c dispatch that also updates the
/// program counter in an executor's register bank appropriately. /// program counter in an executor's register bank appropriately.
template <Model model, typename MemoryT> template <Model model, typename MemoryT>
void dispatch(uint32_t pc, uint32_t instruction, Executor<MemoryT> &executor) { void dispatch(uint32_t &pc, uint32_t instruction, Executor<model, MemoryT> &executor) {
executor.set_pc(pc); // TODO: avoid PC gymnastics below.
executor.set_pc(pc + 4);
dispatch<model>(instruction, executor); 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 /// 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. /// 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**, /// The PC contained here is always taken to be **the address of the current instruction + 4**,
/// i.e. disregarding pipeline differences. Appropriate prefetch offsets are left to other code to handle. /// 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. /// This is to try to keep this structure independent of a specific ARM implementation.
struct Registers { struct Registers {
public: public:

View File

@ -192,7 +192,22 @@ struct Memory {
ROM::Request request(rom_name); ROM::Request request(rom_name);
const auto roms = CSROMFetcher()(request); 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 @end