1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-16 19:08:08 +00:00
CLK/InstructionSets/ARM/Registers.hpp

301 lines
9.1 KiB
C++
Raw Normal View History

2024-02-25 20:21:54 +00:00
//
// Status.hpp
// Clock Signal
//
// Created by Thomas Harte on 25/02/2024.
// Copyright © 2024 Thomas Harte. All rights reserved.
//
#pragma once
#include "OperationMapper.hpp"
2024-02-28 19:09:05 +00:00
#include <array>
2024-02-27 02:27:58 +00:00
#include <cstdint>
2024-02-25 20:21:54 +00:00
namespace InstructionSet::ARM {
namespace ConditionCode {
2024-03-04 18:53:46 +00:00
static constexpr uint32_t Negative = 1u << 31;
static constexpr uint32_t Zero = 1u << 30;
static constexpr uint32_t Carry = 1u << 29;
static constexpr uint32_t Overflow = 1u << 28;
static constexpr uint32_t IRQDisable = 1u << 27;
static constexpr uint32_t FIQDisable = 1u << 26;
static constexpr uint32_t Mode = (1u << 1) | (1u << 0);
2024-02-25 20:21:54 +00:00
static constexpr uint32_t Address = FIQDisable - Mode - 1;
}
2024-02-26 03:01:51 +00:00
enum class Mode {
User = 0b00,
FIQ = 0b01,
IRQ = 0b10,
Supervisor = 0b11,
};
/// 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.
2024-02-28 15:25:14 +00:00
///
/// 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.
2024-02-28 15:25:14 +00:00
/// This is to try to keep this structure independent of a specific ARM implementation.
struct Registers {
2024-02-26 03:01:51 +00:00
public:
/// Sets the N and Z flags according to the value of @c result.
void set_nz(uint32_t value) {
zero_result_ = negative_flag_ = value;
}
/// Sets C if @c value is non-zero; resets it otherwise.
void set_c(uint32_t value) {
carry_flag_ = value;
}
2024-02-27 02:27:58 +00:00
/// @returns @c 1 if carry is set; @c 0 otherwise.
uint32_t c() const {
return carry_flag_ ? 1 : 0;
}
2024-02-26 03:01:51 +00:00
/// Sets V if the highest bit of @c value is set; resets it otherwise.
void set_v(uint32_t value) {
overflow_flag_ = value;
}
2024-03-04 02:38:27 +00:00
/// @returns The current status bits, separate from the PC — mode, NVCZ and the two interrupt flags.
2024-03-04 02:29:53 +00:00
uint32_t status() const {
2024-02-26 03:01:51 +00:00
return
uint32_t(mode_) |
(negative_flag_ & ConditionCode::Negative) |
(zero_result_ ? 0 : ConditionCode::Zero) |
(carry_flag_ ? ConditionCode::Carry : 0) |
((overflow_flag_ >> 3) & ConditionCode::Overflow) |
interrupt_flags_;
}
2024-03-04 02:29:53 +00:00
/// @returns The full PC + status bits.
uint32_t pc_status(uint32_t offset) const {
return
2024-03-04 02:43:30 +00:00
((active_[15] + offset) & ConditionCode::Address) |
2024-03-04 02:29:53 +00:00
status();
}
2024-02-28 15:25:14 +00:00
/// Sets status bits only, subject to mode.
void set_status(uint32_t status) {
2024-02-28 15:15:23 +00:00
// ... in user mode the other flags (I, F, M1, M0) are protected from direct change
// but in non-user modes these will also be affected, accepting copies of bits 27, 26,
// 1 and 0 of the result respectively.
2024-02-28 15:25:14 +00:00
negative_flag_ = status;
overflow_flag_ = status << 3;
carry_flag_ = status & ConditionCode::Carry;
zero_result_ = ~status & ConditionCode::Zero;
if(mode_ != Mode::User) {
2024-02-28 19:09:05 +00:00
set_mode(Mode(status & 3));
2024-02-28 15:25:14 +00:00
interrupt_flags_ = status & (ConditionCode::IRQDisable | ConditionCode::FIQDisable);
}
2024-02-28 15:15:23 +00:00
}
2024-03-04 02:38:27 +00:00
/// @returns The current mode.
2024-03-03 04:15:17 +00:00
Mode mode() const {
return mode_;
}
2024-02-28 15:25:14 +00:00
/// Sets a new PC.
void set_pc(uint32_t value) {
2024-03-04 02:43:30 +00:00
active_[15] = value & ConditionCode::Address;
2024-02-28 15:25:14 +00:00
}
2024-02-28 15:15:23 +00:00
2024-03-04 02:38:27 +00:00
/// @returns The stored PC plus @c offset limited to 26 bits.
2024-02-28 14:33:05 +00:00
uint32_t pc(uint32_t offset) const {
2024-03-04 02:43:30 +00:00
return (active_[15] + offset) & ConditionCode::Address;
2024-02-28 14:33:05 +00:00
}
2024-02-28 19:25:12 +00:00
// MARK: - Exceptions.
enum class Exception {
/// Reset line went from high to low.
2024-02-28 19:25:12 +00:00
Reset = 0x00,
/// Either an undefined instruction or a coprocessor instruction for which no coprocessor was found.
2024-02-28 19:25:12 +00:00
UndefinedInstruction = 0x04,
/// Code executed a software interrupt.
2024-02-28 19:25:12 +00:00
SoftwareInterrupt = 0x08,
/// The memory subsystem indicated an abort during prefetch and that instruction has now come to the head of the queue.
2024-02-28 19:25:12 +00:00
PrefetchAbort = 0x0c,
/// The memory subsystem indicated an abort during an instruction; if it is an LDR or STR then this should be signalled
/// before any instruction execution. If it was an LDM then loading stops upon a data abort but both an LDM and STM
/// otherwise complete, including pointer writeback.
2024-02-28 19:25:12 +00:00
DataAbort = 0x10,
/// The first data transfer attempted within an instruction was above address 0x3ff'ffff.
2024-02-28 19:25:12 +00:00
Address = 0x14,
/// The IRQ line was low at the end of an instruction and ConditionCode::IRQDisable was not set.
2024-02-28 19:25:12 +00:00
IRQ = 0x18,
/// The FIQ went low at least one cycle ago and ConditionCode::FIQDisable was not set.
2024-02-28 19:25:12 +00:00
FIQ = 0x1c,
};
2024-03-04 02:38:27 +00:00
/// Updates the program counter, interupt flags and link register if applicable to begin @c exception.
template <Exception type>
2024-02-28 19:25:12 +00:00
void exception() {
interrupt_flags_ |= ConditionCode::IRQDisable;
if constexpr (type == Exception::Reset || type == Exception::FIQ) {
2024-02-28 19:25:12 +00:00
interrupt_flags_ |= ConditionCode::FIQDisable;
}
switch(type) {
2024-02-28 19:25:12 +00:00
case Exception::IRQ:
set_mode(Mode::IRQ);
2024-03-04 02:43:30 +00:00
active_[14] = pc(8);
2024-02-28 19:25:12 +00:00
break;
case Exception::FIQ:
set_mode(Mode::FIQ);
2024-03-04 02:43:30 +00:00
active_[14] = pc(8);
2024-02-28 19:25:12 +00:00
break;
default:
set_mode(Mode::Supervisor);
2024-03-04 02:43:30 +00:00
active_[14] = pc(4);
2024-02-28 19:25:12 +00:00
break;
}
2024-03-04 18:25:40 +00:00
set_pc(uint32_t(type));
2024-02-28 19:25:12 +00:00
}
// MARK: - Condition tests.
2024-03-04 02:38:27 +00:00
/// @returns @c true if @c condition tests as true; @c false otherwise.
2024-02-26 03:01:51 +00:00
bool test(Condition condition) {
const auto ne = [&]() -> bool {
return zero_result_;
};
const auto cs = [&]() -> bool {
return carry_flag_;
};
const auto mi = [&]() -> bool {
return negative_flag_ & ConditionCode::Negative;
};
const auto vs = [&]() -> bool {
return overflow_flag_ & ConditionCode::Negative;
};
const auto hi = [&]() -> bool {
return carry_flag_ && zero_result_;
};
const auto lt = [&]() -> bool {
return (negative_flag_ ^ overflow_flag_) & ConditionCode::Negative;
};
const auto le = [&]() -> bool {
return !zero_result_ || lt();
};
switch(condition) {
case Condition::EQ: return !ne();
case Condition::NE: return ne();
case Condition::CS: return cs();
case Condition::CC: return !cs();
case Condition::MI: return mi();
case Condition::PL: return !mi();
case Condition::VS: return vs();
case Condition::VC: return !vs();
case Condition::HI: return hi();
case Condition::LS: return !hi();
case Condition::GE: return !lt();
case Condition::LT: return lt();
case Condition::GT: return !le();
case Condition::LE: return le();
case Condition::AL: return true;
case Condition::NV: return false;
}
}
2024-03-04 02:38:27 +00:00
/// Sets current execution mode.
2024-02-28 19:09:05 +00:00
void set_mode(Mode target_mode) {
if(mode_ == target_mode) {
return;
}
// For outgoing modes other than FIQ, only save the final two registers for now;
// if the incoming mode is FIQ then the other five will be saved in the next switch.
// For FIQ, save all seven up front.
2024-02-28 19:09:05 +00:00
switch(mode_) {
// FIQ outgoing: save R8 to R14.
2024-02-28 19:09:05 +00:00
case Mode::FIQ:
2024-03-04 02:43:30 +00:00
std::copy(active_.begin() + 8, active_.begin() + 15, fiq_registers_.begin());
2024-02-28 19:09:05 +00:00
break;
// Non-FIQ outgoing: save R13 and R14. If saving to the user registers,
// use only the final two slots.
2024-02-28 19:09:05 +00:00
case Mode::User:
2024-03-04 02:43:30 +00:00
std::copy(active_.begin() + 13, active_.begin() + 15, user_registers_.begin() + 5);
2024-02-28 19:09:05 +00:00
break;
case Mode::Supervisor:
2024-03-04 02:43:30 +00:00
std::copy(active_.begin() + 13, active_.begin() + 15, supervisor_registers_.begin());
2024-02-28 19:09:05 +00:00
break;
case Mode::IRQ:
2024-03-04 02:43:30 +00:00
std::copy(active_.begin() + 13, active_.begin() + 15, irq_registers_.begin());
2024-02-28 19:09:05 +00:00
break;
}
// For all modes except FIQ: restore the final two registers to their appropriate values.
// For FIQ: save an additional five, then overwrite seven.
switch(target_mode) {
case Mode::FIQ:
// FIQ is incoming, so save registers 8 to 12 to the first five slots of the user registers.
2024-03-04 02:43:30 +00:00
std::copy(active_.begin() + 8, active_.begin() + 13, user_registers_.begin());
// Replace R8 to R14.
2024-03-04 02:43:30 +00:00
std::copy(fiq_registers_.begin(), fiq_registers_.end(), active_.begin() + 8);
2024-02-28 19:09:05 +00:00
break;
case Mode::User:
2024-03-04 02:43:30 +00:00
std::copy(user_registers_.begin() + 5, user_registers_.end(), active_.begin() + 13);
2024-02-28 19:09:05 +00:00
break;
case Mode::Supervisor:
2024-03-04 02:43:30 +00:00
std::copy(supervisor_registers_.begin(), supervisor_registers_.end(), active_.begin() + 13);
2024-02-28 19:09:05 +00:00
break;
case Mode::IRQ:
2024-03-04 02:43:30 +00:00
std::copy(irq_registers_.begin(), irq_registers_.end(), active_.begin() + 13);
2024-02-28 19:09:05 +00:00
break;
}
// If FIQ is outgoing then there's another five registers to restore.
if(mode_ == Mode::FIQ) {
2024-03-04 02:43:30 +00:00
std::copy(user_registers_.begin(), user_registers_.begin() + 5, active_.begin() + 8);
2024-02-28 19:09:05 +00:00
}
mode_ = target_mode;
2024-02-28 19:43:31 +00:00
}
2024-02-29 19:34:20 +00:00
2024-03-08 19:13:34 +00:00
uint32_t &operator[](uint32_t offset) {
return active_[static_cast<size_t>(offset)];
2024-03-04 02:43:30 +00:00
}
2024-03-08 19:13:34 +00:00
uint32_t operator[](uint32_t offset) const {
return active_[static_cast<size_t>(offset)];
2024-03-04 02:43:30 +00:00
}
2024-03-04 02:38:27 +00:00
2024-02-29 19:34:20 +00:00
private:
Mode mode_ = Mode::Supervisor;
2024-03-04 02:29:53 +00:00
uint32_t zero_result_ = 1;
2024-02-29 19:34:20 +00:00
uint32_t negative_flag_ = 0;
2024-03-04 02:29:53 +00:00
uint32_t interrupt_flags_ = ConditionCode::IRQDisable | ConditionCode::FIQDisable;
2024-02-29 19:34:20 +00:00
uint32_t carry_flag_ = 0;
uint32_t overflow_flag_ = 0;
// Various shadow registers.
2024-03-04 02:29:53 +00:00
std::array<uint32_t, 7> user_registers_{};
std::array<uint32_t, 7> fiq_registers_{};
std::array<uint32_t, 2> irq_registers_{};
std::array<uint32_t, 2> supervisor_registers_{};
2024-03-04 02:43:30 +00:00
// The active register set.
std::array<uint32_t, 16> active_{};
2024-02-26 03:01:51 +00:00
};
2024-02-25 20:21:54 +00:00
}