libmicroemu 0.2.0
ARM Microcontroller Emulator Library
Loading...
Searching...
No Matches
reg_ops.h
1#pragma once
2
4#include "libmicroemu/logger.h"
5#include "libmicroemu/register_id.h"
7#include <array>
8#include <cstddef>
9#include <cstdint>
10#include <string_view>
11#include <type_traits>
12
13namespace libmicroemu::internal {
14
15template <typename TCpuStates, typename TSpecRegOps, typename TLogger = NullLogger> class RegOps {
16public:
17 using SReg = TSpecRegOps;
18
19 static std::string_view GetRegisterName(const RegisterId &id) {
20 using enum_type = std::underlying_type<RegisterId>::type;
21 const enum_type raw_id = static_cast<enum_type>(id);
22
23 switch (raw_id) {
24 case 0x0U:
25 return "R0";
26 case 0x1U:
27 return "R1";
28 case 0x2U:
29 return "R2";
30 case 0x3U:
31 return "R3";
32 case 0x4U:
33 return "R4";
34 case 0x5U:
35 return "R5";
36 case 0x6U:
37 return "R6";
38 case 0x7U:
39 return "R7";
40 case 0x8U:
41 return "R8";
42 case 0x9U:
43 return "R9";
44 case 0xAU: // stack limit or scratch register (r10)
45 return "SL";
46 case 0xBU: // frame pointer (r11)
47 return "FP";
48 case 0xCU: // intra-procedure call scratch register (r12)
49 return "IP";
50 case static_cast<u8>(libmicroemu::RegisterId::kSp): // stack-pointer (r13)
51 return "SP";
52 case 0xEU: // link-register (r14)
53 return "LR";
54 case static_cast<u8>(libmicroemu::RegisterId::kPc): // r15
55 return "PC";
56 default:
57 return "UNDEFINED";
58 }
59 }
60
61 static inline SpecialRegisterId LookUpSP(const TCpuStates &cpus) {
62 // see Armv7-M Architecture Reference Manual Issue E.e p.521
63 auto sys_ctrl = TSpecRegOps::template ReadRegister<SpecialRegisterId::kSysCtrl>(cpus);
64 auto spsel = sys_ctrl & SysCtrlRegister::kControlSpSelMsk;
65
66 // if CONTROL.SPSEL == '1' then
67 if (spsel == 1U) {
68 const auto exec_mode = sys_ctrl & SysCtrlRegister::kExecModeMsk;
69
70 // if CurrentMode==Mode_Thread then
71 if (exec_mode == SysCtrlRegister::kExecModeThread) {
72 // sp = RNameSP_process;
74 } else {
75 // UNPREDICTABLE;
76 // I see no clean way to handle this without loosing performance
77 // in release mode, so we'll just return the main stack pointer
78 assert(false && "LookUpSp UNPREDICTABLE");
80 }
81 }
82
84 }
85 static inline u32 ReadSP(const TCpuStates &cpus) {
86 // The actual value of R13 is determined by the current stack
87 // pointer selection bit in the CONTROL. The value in the
88 // register array is not used.
89 const auto sp_reg = LookUpSP(cpus);
90 const auto sp = SReg::ReadRegister(cpus, sp_reg);
91 return sp;
92 }
93
94 static inline void WriteSP(TCpuStates &cpus, const u32 &value) {
95 const auto sp_reg = LookUpSP(cpus);
96 SReg::WriteRegister(cpus, sp_reg, value);
97 }
98
99 static inline me_adr_t ReadPC(const TCpuStates &cpus) {
100 // see Armv7-M Architecture Reference Manual Issue E.e p.521
101 const auto &registers = cpus.GetRegisters();
102 const me_adr_t &pc =
103 static_cast<me_adr_t>(std::get<static_cast<u8>(RegisterId::kPc)>(registers));
104 return pc + 0x4U;
105 }
106
107 template <RegisterId Id> static inline u32 ReadRegister(const TCpuStates &cpus) {
108 static_assert(static_cast<u8>(Id) < CountRegisters(), "Invalid register id");
109
110 using enum_type = std::underlying_type<RegisterId>::type;
111
112 switch (Id) {
113 case RegisterId::kSp:
114 return ReadSP(cpus);
115 case RegisterId::kPc:
116 return ReadPC(cpus);
117 default:
118 // Retrieve the register array from the processor state
119 const auto &registers = cpus.GetRegisters();
120 return registers[static_cast<enum_type>(Id)];
121 }
122 // Should not happen, but returns 0U if somehow out of range
123 return 0;
124 }
125
126 static inline u32 ReadRegister(const TCpuStates &cpus, RegisterId id) {
127 using enum_type = std::underlying_type<RegisterId>::type;
128 assert(static_cast<enum_type>(id) < CountRegisters() && "Invalid register id");
129 switch (id) {
130 case RegisterId::kSp:
131 return ReadSP(cpus);
132 case RegisterId::kPc:
133 return ReadPC(cpus);
134 default:
135 // Retrieve the register array from the processor state
136 const auto &registers = cpus.GetRegisters();
137 return registers[static_cast<enum_type>(id)];
138 }
139
140 // Not reachable
141 return 0U;
142 }
143
144 template <RegisterId Id> static inline void WriteRegister(TCpuStates &cpus, const u32 &value) {
145 static_assert(static_cast<u8>(Id) < CountRegisters(), "Invalid register id");
146 static_assert(Id != RegisterId::kPc, "PC is not assignable by this function");
147
148 using enum_type = std::underlying_type<RegisterId>::type;
149
150 switch (Id) {
151 case RegisterId::kSp:
152 return WriteSP(cpus, value);
153 default:
154 // Retrieve the register array from the processor state
155 auto &registers = cpus.GetRegisters();
156 registers[static_cast<enum_type>(Id)] = value;
157 }
158 // No action needed for out-of-range or unhandled IDs
159 }
160
161 static inline void WriteRegister(TCpuStates &cpus, RegisterId id, u32 value) {
162 using enum_type = std::underlying_type<RegisterId>::type;
163
164 assert(static_cast<enum_type>(id) < CountRegisters() && "Invalid register id");
165
166 switch (id) {
167 case RegisterId::kSp:
168 return WriteSP(cpus, value);
169 case RegisterId::kPc:
170 assert(false && "PC is not assignable by this function");
171 break;
172 default:
173 // Retrieve the register array from the processor state
174 auto &registers = cpus.GetRegisters();
175 registers[static_cast<enum_type>(id)] = value;
176 }
177 // No action needed for out-of-range or unhandled IDs
178 }
179
180private:
181 RegOps() = delete;
182 ~RegOps() = delete;
183 RegOps &operator=(const RegOps &r_src) = delete;
184 RegOps(RegOps &&r_src) = delete;
185 RegOps &operator=(RegOps &&r_src) = delete;
186 RegOps(const RegOps &r_src) = delete;
187};
188
189} // namespace libmicroemu::internal
The libmicroemu::internal namespace contains all classes and functions of libmicroemu which are priva...
Definition bkpt_flags.h:6
static constexpr u32 CountRegisters() noexcept
Returns the number of general-purpose registers.
Definition register_id.h:45
SpecialRegisterId
Enumeration of special register IDs.
Definition special_register_id.h:18
@ kSpMain
Main Stack Pointer, MSP.
Definition special_register_id.h:33
@ kSpProcess
Process Stack Pointer, PSP.
Definition special_register_id.h:34
RegisterId
Enumeration of register IDs.
Definition register_id.h:13
@ kPc
Program-counter register.
Definition register_id.h:34
@ kSp
Stack-pointer register.
Definition register_id.h:28
Contains the Result class which is used to return the result of a function.
This file contains the declaration of the SpecialRegisterId enumeration and related functions.