1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-19 08:31:11 +00:00
CLK/InstructionSets/ARM/Status.hpp
2024-02-25 22:01:51 -05:00

135 lines
3.3 KiB
C++

//
// Status.hpp
// Clock Signal
//
// Created by Thomas Harte on 25/02/2024.
// Copyright © 2024 Thomas Harte. All rights reserved.
//
#pragma once
#include "OperationMapper.hpp"
namespace InstructionSet::ARM {
namespace ConditionCode {
static constexpr uint32_t Negative = 1 << 31;
static constexpr uint32_t Zero = 1 << 30;
static constexpr uint32_t Carry = 1 << 29;
static constexpr uint32_t Overflow = 1 << 28;
static constexpr uint32_t IRQDisable = 1 << 27;
static constexpr uint32_t FIQDisable = 1 << 26;
static constexpr uint32_t Mode = (1 << 1) | (1 << 0);
static constexpr uint32_t Address = FIQDisable - Mode - 1;
}
enum class Mode {
User = 0b00,
FIQ = 0b01,
IRQ = 0b10,
Supervisor = 0b11,
};
struct Status {
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;
}
/// Sets V if the highest bit of @c value is set; resets it otherwise.
void set_v(uint32_t value) {
overflow_flag_ = value;
}
/// @returns The program counter address only, optionally with a post-increment.
template <bool increment>
uint32_t pc() {
const uint32_t result = pc_;
if constexpr (increment) {
pc_ = (pc_ + 4) & ConditionCode::Address;
}
return result;
}
void begin_irq() { interrupt_flags_ |= ConditionCode::IRQDisable; }
void begin_fiq() { interrupt_flags_ |= ConditionCode::FIQDisable; }
/// @returns The full PC + status bits.
uint32_t get() const {
return
uint32_t(mode_) |
pc_ |
(negative_flag_ & ConditionCode::Negative) |
(zero_result_ ? 0 : ConditionCode::Zero) |
(carry_flag_ ? ConditionCode::Carry : 0) |
((overflow_flag_ >> 3) & ConditionCode::Overflow) |
interrupt_flags_;
}
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;
}
}
private:
uint32_t pc_ = 0;
Mode mode_ = Mode::Supervisor;
uint32_t zero_result_ = 0;
uint32_t negative_flag_ = 0;
uint32_t interrupt_flags_ = 0;
uint32_t carry_flag_ = 0;
uint32_t overflow_flag_ = 0;
};
}