libmicroemu 0.2.0
ARM Microcontroller Emulator Library
Loading...
Searching...
No Matches
special_instr.h
1#pragma once
2#include "libmicroemu/internal/decoder/decoder.h"
3#include "libmicroemu/internal/executor/instr_context.h"
4#include "libmicroemu/internal/executor/instr_exec_results.h"
5#include "libmicroemu/internal/i_breakpoint.h"
8#include "libmicroemu/internal/utils/rarg.h"
9#include "libmicroemu/logger.h"
10#include "libmicroemu/register_details.h"
12#include "libmicroemu/types.h"
13
14namespace libmicroemu::internal {
15
16template <typename TInstrContext, typename TLogger = NullLogger> class SpecialInstr {
17public:
18 using TCpuAccessor = decltype(TInstrContext::cpua);
19 using It = typename TInstrContext::It;
20 using Pc = typename TInstrContext::Pc;
21
22 using ExcTrig = typename TInstrContext::ExcTrig;
28 static Result<InstrExecResult> ItInstr(TInstrContext &ictx, const InstrFlagsSet &iflags,
29 const u32 &firstcond, const u32 &mask) {
30 const auto is_32bit = (iflags & static_cast<InstrFlagsSet>(InstrFlags::k32Bit)) != 0U;
31 auto istate = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kIstate>();
32 istate = firstcond << 4U | mask;
33
34 ictx.cpua.template WriteSpecialRegister<SpecialRegisterId::kIstate>(istate);
35 Pc::AdvanceInstr(ictx.cpua, is_32bit);
36 return Ok(InstrExecResult{kNoInstrExecFlags});
37 }
38
44 template <typename TDelegates>
45 static Result<InstrExecResult> Svc(TInstrContext &ictx, const InstrFlagsSet &iflags,
46 const u32 &imm32, TDelegates &delegates) {
47 TRY_ASSIGN(condition_passed, InstrExecResult, It::ConditionPassed(ictx.cpua));
48 if (!condition_passed) {
49 PostExecAdvancePcAndIt::Call(ictx, iflags);
50 return Ok(InstrExecResult{kNoInstrExecFlags});
51 }
52 InstrExecFlagsSet eflags{kNoInstrExecFlags};
53 if (delegates.IsSvcSet()) {
54 TRY_ASSIGN(svc_flags, InstrExecResult, delegates.Svc(imm32));
55
56 if ((svc_flags & static_cast<SvcFlagsSet>(SvcFlags::kRequestExit)) != 0U) {
57 eflags |= static_cast<InstrExecFlagsSet>(InstrExecFlags::kSvcReqExit);
58 } else if ((svc_flags & static_cast<SvcFlagsSet>(SvcFlags::kRequestErrorExit)) != 0U) {
59 eflags |= static_cast<InstrExecFlagsSet>(InstrExecFlags::kSvcReqErrorExit);
60 }
61
62 if ((svc_flags & static_cast<SvcFlagsSet>(SvcFlags::kOmitException)) == 0U) {
63 ExcTrig::SetPending(ictx.cpua, ExceptionType::kSVCall);
64 }
65 } else {
66 ExcTrig::SetPending(ictx.cpua, ExceptionType::kSVCall);
67 }
68 PostExecAdvancePcAndIt::Call(ictx, iflags);
69 return Ok(InstrExecResult{eflags});
70 }
71
77 template <typename TDelegates>
78 static Result<InstrExecResult> Bkpt(TInstrContext &ictx, const InstrFlagsSet &iflags,
79 const u32 &imm32, TDelegates &delegates) {
80 InstrExecFlagsSet eflags{kNoInstrExecFlags};
81 if (delegates.IsBkptSet()) {
82 TRY_ASSIGN(bkpt_flags, InstrExecResult, delegates.Bkpt(imm32));
83
84 if ((bkpt_flags & static_cast<BkptFlagsSet>(BkptFlags::kRequestExit)) != 0U) {
85 eflags |= static_cast<InstrExecFlagsSet>(InstrExecFlags::kBkptReqExit);
86 } else if ((bkpt_flags & static_cast<BkptFlagsSet>(BkptFlags::kRequestErrorExit)) != 0U) {
87 eflags |= static_cast<InstrExecFlagsSet>(InstrExecFlags::kBkptReqErrorExit);
88 }
89 if ((bkpt_flags & static_cast<BkptFlagsSet>(BkptFlags::kOmitException)) == 0U) {
90 ExcTrig::SetPending(ictx.cpua, ExceptionType::kHardFault);
91 }
92 } else {
93 ExcTrig::SetPending(ictx.cpua, ExceptionType::kHardFault);
94 }
95 PostExecAdvancePcAndIt::Call(ictx, iflags);
96 return Ok(InstrExecResult{eflags});
97 }
98
104 static Result<InstrExecResult> BCond(TInstrContext &ictx, const InstrFlagsSet &iflags,
105 const u32 &imm32, const u8 &cond) {
106 const auto condition_passed = It::ConditionPassed(ictx.cpua, cond);
107 if (!condition_passed) {
108 PostExecAdvancePcAndIt::Call(ictx, iflags);
109 return Ok(InstrExecResult{kNoInstrExecFlags});
110 }
111 const me_adr_t pc = static_cast<me_adr_t>(ictx.cpua.template ReadRegister<RegisterId::kPc>());
112 Pc::BranchWritePC(ictx.cpua, pc + imm32);
113 return Ok(InstrExecResult{kNoInstrExecFlags});
114 }
115
121 template <typename TDest, typename TArg0>
122 static Result<InstrExecResult> Bfi(TInstrContext &ictx, const InstrFlagsSet &iflags,
123 const TDest &rd, const TArg0 &rn, const u8 &lsbit,
124 const u8 &msbit) {
125 TRY_ASSIGN(condition_passed, InstrExecResult, It::ConditionPassed(ictx.cpua));
126 if (!condition_passed) {
127 PostExecAdvancePcAndIt::Call(ictx, iflags);
128 return Ok(InstrExecResult{kNoInstrExecFlags});
129 }
130 if (msbit >= lsbit) {
131 const auto n = ictx.cpua.ReadRegister(rn.Get());
132 const auto d = ictx.cpua.ReadRegister(rd.Get());
133
134 const auto src_bitmask = static_cast<u32>(static_cast<u32>(1U) << (msbit - lsbit + 1U)) - 1U;
135 const auto dest_bitmask = static_cast<u32>(
136 ((static_cast<u32>(1U) << (msbit - lsbit + 1U)) - static_cast<u32>(1U)) << lsbit);
137
138 const auto rn_slice = (n & src_bitmask) << lsbit;
139 const auto rd_result = (d & ~dest_bitmask) | rn_slice;
140
141 ictx.cpua.WriteRegister(rd.Get(), rd_result);
142 } else {
143 return Err<InstrExecResult>(StatusCode::kExecutorUnpredictable);
144 }
145
146 PostExecAdvancePcAndIt::Call(ictx, iflags);
147 return Ok(InstrExecResult{kNoInstrExecFlags});
148 }
149
155 template <typename TDest, typename TArg0>
156 static Result<InstrExecResult> Ubfx(TInstrContext &ictx, const InstrFlagsSet &iflags,
157 const TDest &rd, const TArg0 &rn, const u8 &lsbit,
158 const u8 &widthminus1) {
159 TRY_ASSIGN(condition_passed, InstrExecResult, It::ConditionPassed(ictx.cpua));
160 if (!condition_passed) {
161 PostExecAdvancePcAndIt::Call(ictx, iflags);
162 return Ok(InstrExecResult{kNoInstrExecFlags});
163 }
164
165 const u8 msbit = lsbit + widthminus1;
166 if (msbit > 31U) {
167 return Err<InstrExecResult>(StatusCode::kExecutorUnpredictable);
168 }
169
170 u32 msk = ((static_cast<u32>(1U) << (msbit - lsbit + 1U)) - static_cast<u32>(1U)) << lsbit;
171 const auto n = ictx.cpua.ReadRegister(rn.Get());
172 const auto result = (n & msk) >> lsbit;
173
174 PostExecWriteRegPcExcluded::Call(ictx, rd, result);
175 PostExecAdvancePcAndIt::Call(ictx, iflags);
176 return Ok(InstrExecResult{kNoInstrExecFlags});
177 }
178
184 template <typename TTgt0, typename TTgt1, typename TArg0>
185 static Result<InstrExecResult> Ldrd(TInstrContext &ictx, const InstrFlagsSet &iflags,
186 const TTgt0 &rt, const TTgt1 &rt2, const TArg0 &rn,
187 const u32 &imm32) {
188 const bool is_wback = (iflags & static_cast<InstrFlagsSet>(InstrFlags::kWBack)) != 0U;
189 const bool is_index = (iflags & static_cast<InstrFlagsSet>(InstrFlags::kIndex)) != 0U;
190 const bool is_add = (iflags & static_cast<InstrFlagsSet>(InstrFlags::kAdd)) != 0U;
191
192 TRY_ASSIGN(condition_passed, InstrExecResult, It::ConditionPassed(ictx.cpua));
193
194 if (!condition_passed) {
195 PostExecAdvancePcAndIt::Call(ictx, iflags);
196 return Ok(InstrExecResult{kNoInstrExecFlags});
197 }
198 const auto n = ictx.cpua.ReadRegister(rn.Get());
199 const me_adr_t offset_addr = is_add == true ? n + imm32 : n - imm32;
200 const me_adr_t address = is_index == true ? offset_addr : n;
201
202 // Read from address
204 ictx.bus.template ReadOrRaise<u32>(ictx.cpua, address,
205 BusExceptionType::kRaisePreciseDataBusError));
207 ictx.bus.template ReadOrRaise<u32>(ictx.cpua, address + 0x4U,
208 BusExceptionType::kRaisePreciseDataBusError));
209 if (is_wback) {
210 PostExecWriteRegPcExcluded::Call(ictx, rn, offset_addr);
211 }
212 PostExecWriteRegPcExcluded::Call(ictx, rt, data);
213 PostExecWriteRegPcExcluded::Call(ictx, rt2, data2);
214 PostExecAdvancePcAndIt::Call(ictx, iflags);
215
216 return Ok(InstrExecResult{kNoInstrExecFlags});
217 }
218
224 template <typename TArg0>
225 static Result<InstrExecResult> Msr(TInstrContext &ictx, const InstrFlagsSet &iflags,
226 const TArg0 &rn, const uint8_t mask, const uint8_t SYSm) {
227 TRY_ASSIGN(condition_passed, InstrExecResult, It::ConditionPassed(ictx.cpua));
228 if (!condition_passed) {
229 PostExecAdvancePcAndIt::Call(ictx, iflags);
230 return Ok(InstrExecResult{kNoInstrExecFlags});
231 }
232
233 const auto n = ictx.cpua.ReadRegister(rn.Get());
234 const auto SYSm_7_3 = Bm32::ExtractBits1R<7U, 3U>(SYSm);
235 switch (SYSm_7_3) {
236 case 0b00000U: {
237 if (mask & 0x1) {
238 // APSR_g Application Program Status Register
239 assert(false && "not implemented");
240 } else {
241 // APSR_nzcvq Application Program Status Register
242 assert(false && "not implemented");
243 }
244 break;
245 }
246 case 0b00001U: {
247 const auto SYSm_2_0 = Bm32::ExtractBits1R<2U, 0U>(SYSm);
248 switch (SYSm_2_0) {
249 case 0b000U: {
250 // MSP - Main Stack Pointer
251 ictx.cpua.template WriteSpecialRegister<SpecialRegisterId::kSpMain>(n);
252 LOG_TRACE(TLogger, "MSR Call - Write main stack pointer: 0x%08X", rn);
253 break;
254 }
255 case 0b001U: {
256 // PSP - Process Stack Pointer
257 ictx.cpua.template WriteSpecialRegister<SpecialRegisterId::kSpProcess>(n);
258 LOG_TRACE(TLogger, "MSR Call - Write process stack pointer: 0x%08X", rn);
259 break;
260 }
261 default:
262 // undefined
263 assert(false && "undefined");
264 break;
265 }
266 break;
267 }
268 case 0b00010U: {
269 const auto SYSm_2_0 = Bm32::ExtractBits1R<2U, 0U>(SYSm);
270 switch (SYSm_2_0) {
271 case 0b000U:
272 // PRIMASK - Priority Mask
273 assert(false && "not implemented");
274 break;
275 case 0b001U:
276 // BASEPRI - Base Priority
277 assert(false && "not implemented");
278 break;
279 case 0b010U:
280 // BASEPRI_MAX - Base Priority Max
281 assert(false && "not implemented");
282 break;
283 case 0b011U:
284 // FAULTMASK- Fault Mask
285 assert(false && "not implemented");
286 break;
287 case 0b100U: {
288 // CONTROL- Control
289 const auto is_privileged = Predicates::IsCurrentModePrivileged<TCpuAccessor>(ictx.cpua);
290
291 if (is_privileged) {
292 auto control = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kControl>();
293 // CONTROL.nPRIV = R[n]<0>;
294 control &= ~ControlRegister::kNPrivMsk;
295 control |= ((n & 0x1U) >> 0U) << ControlRegister::kNPrivPos;
296 if (Predicates::IsThreadMode(ictx.cpua)) {
297 // CONTROL.SPSEL = R[n]<1>;
298 control &= ~ControlRegister::kSpselMsk;
299 control |= ((n & 0x2U) >> 1U) << ControlRegister::kSpselPos;
300 }
301 LOG_TRACE(TLogger, "MSR Call - Write CONTROL: 0x%08X", control);
302
303 ictx.cpua.template WriteSpecialRegister<SpecialRegisterId::kControl>(control);
304 }
305
306 // if HaveFPExt() then CONTROL.FPCA = R[n]<2>;
307 break;
308 }
309 default:
310 // undefined
311 assert(false && "undefined");
312 break;
313 }
314 } break;
315 default:
316 // undefined
317 assert(false && "undefined");
318 break;
319 }
320
321 PostExecAdvancePcAndIt::Call(ictx, iflags);
322 return Ok(InstrExecResult{kNoInstrExecFlags});
323 }
324
330 template <typename TDest>
331 static Result<InstrExecResult> Mrs(TInstrContext &ictx, const InstrFlagsSet &iflags,
332 const TDest &rd, const uint8_t mask, const uint8_t SYSm) {
333 TRY_ASSIGN(condition_passed, InstrExecResult, It::ConditionPassed(ictx.cpua));
334 if (!condition_passed) {
335 PostExecAdvancePcAndIt::Call(ictx, iflags);
336 return Ok(InstrExecResult{kNoInstrExecFlags});
337 }
338
339 const auto SYSm_7_3 = Bm32::ExtractBits1R<7U, 3U>(SYSm);
340 u32 rd_val = 0U;
341 switch (SYSm_7_3) {
342 case 0b00000U: {
343 if (mask & 0x1) {
344 // APSR_g Application Program Status Register
345 assert(false && "not implemented");
346 } else {
347 // APSR_nzcvq Application Program Status Register
348 assert(false && "not implemented");
349 }
350 break;
351 }
352 case 0b00001U: {
353 const auto SYSm_2_0 = Bm32::ExtractBits1R<2U, 0U>(SYSm);
354 switch (SYSm_2_0) {
355 case 0b000U: {
356 // MSP - Main Stack Pointer
357 rd_val = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kSpMain>();
358 LOG_TRACE(TLogger, "MRS Call - Read MSP: 0x%08X", rd_val);
359 break;
360 }
361 case 0b001U: {
362 // PSP - Process Stack Pointer
363 rd_val = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kSpProcess>();
364 LOG_TRACE(TLogger, "MRS Call - Read PSP: 0x%08X", rd_val);
365 break;
366 }
367 default:
368 // undefined
369 assert(false && "undefined");
370 break;
371 }
372 break;
373 }
374 case 0b00010U: {
375 const auto SYSm_2_0 = Bm32::ExtractBits1R<2U, 0U>(SYSm);
376 switch (SYSm_2_0) {
377 case 0b000U:
378 // PRIMASK - Priority Mask
379 assert(false && "not implemented");
380 break;
381 case 0b001U:
382 // BASEPRI - Base Priority
383 assert(false && "not implemented");
384 break;
385 case 0b010U:
386 // BASEPRI_MAX - Base Priority Max
387 assert(false && "not implemented");
388 break;
389 case 0b011U:
390 // FAULTMASK- Fault Mask
391 assert(false && "not implemented");
392 break;
393 case 0b100U: {
394 // CONTROL- Control
395
396 const auto control = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kControl>();
397 // if HaveFPExt() then
398 if (false) {
399 // R[d]<2:0> = CONTROL<2:0>;
400 rd_val |= control & ControlRegister::kControlBit2toBit0Msk;
401 } else {
402
403 // R[d]<1:0> = CONTROL<1:0>;
404 rd_val |= control & ControlRegister::kControlBit1toBit0Msk;
405 }
406
407 LOG_TRACE(TLogger, "MRS Call - Read CONTROL: 0x%08X", rd_val);
408 break;
409 }
410 default:
411 // undefined
412 assert(false && "undefined");
413 break;
414 }
415 } break;
416 default:
417 // undefined
418 assert(false && "undefined");
419 break;
420 }
421
422 PostExecWriteRegPcExcluded::Call(ictx, rd, rd_val);
423 PostExecAdvancePcAndIt::Call(ictx, iflags);
424 return Ok(InstrExecResult{kNoInstrExecFlags});
425 }
426
432 template <typename TTgt, typename TArg0, typename TArg1>
433 static Result<InstrExecResult> Strd(TInstrContext &ictx, const InstrFlagsSet &iflags,
434 const TTgt &rt, const TArg0 &rt2, const TArg1 &rn,
435 const u32 &imm32) {
436
437 const bool is_wback = (iflags & static_cast<InstrFlagsSet>(InstrFlags::kWBack)) != 0U;
438 const bool is_index = (iflags & static_cast<InstrFlagsSet>(InstrFlags::kIndex)) != 0U;
439 const bool is_add = (iflags & static_cast<InstrFlagsSet>(InstrFlags::kAdd)) != 0U;
440
441 TRY_ASSIGN(condition_passed, InstrExecResult, It::ConditionPassed(ictx.cpua));
442 if (!condition_passed) {
443 PostExecAdvancePcAndIt::Call(ictx, iflags);
444 return Ok(InstrExecResult{kNoInstrExecFlags});
445 }
446 const auto n = ictx.cpua.ReadRegister(rn.Get());
447
448 const u32 offset_addr = is_add == true ? n + imm32 : n - imm32;
449 const u32 address = is_index == true ? offset_addr : n;
450
451 const auto t = ictx.cpua.ReadRegister(rt.Get());
452 TRY(InstrExecResult, ictx.bus.template WriteOrRaise<u32>(
453 ictx.cpua, address, t, BusExceptionType::kRaisePreciseDataBusError));
454
455 //---
456 const auto t2 = ictx.cpua.ReadRegister(rt2.Get());
458 ictx.bus.template WriteOrRaise<u32>(ictx.cpua, address + 0x4, t2,
459 BusExceptionType::kRaisePreciseDataBusError));
460
461 if (is_wback) {
462 PostExecWriteRegPcExcluded::Call(ictx, rn, offset_addr);
463 }
464 PostExecAdvancePcAndIt::Call(ictx, iflags);
465 return Ok(InstrExecResult{kNoInstrExecFlags});
466 }
467
468public:
469private:
473 SpecialInstr() = delete;
474
478 ~SpecialInstr() = delete;
479
484 SpecialInstr(const SpecialInstr &r_src) = delete;
485
490 SpecialInstr &operator=(const SpecialInstr &r_src) = delete;
491
496 SpecialInstr(SpecialInstr &&r_src) = delete;
497
502 SpecialInstr &operator=(SpecialInstr &&r_src) = delete;
503};
504
505} // namespace libmicroemu::internal
static constexpr u32 ExtractBits1R(const u32 &value)
Definition bit_manip.h:136
static bool IsCurrentModePrivileged(TCpuAccessor &cpua)
Check if the cpu is in privileged mode.
Definition predicates.h:73
static bool IsThreadMode(const TCpuAccessor &cpua)
Check if the cpu is in thread mode.
Definition predicates.h:27
Definition special_instr.h:16
static Result< InstrExecResult > Ubfx(TInstrContext &ictx, const InstrFlagsSet &iflags, const TDest &rd, const TArg0 &rn, const u8 &lsbit, const u8 &widthminus1)
Ubfx.
Definition special_instr.h:156
static Result< InstrExecResult > ItInstr(TInstrContext &ictx, const InstrFlagsSet &iflags, const u32 &firstcond, const u32 &mask)
It instruction.
Definition special_instr.h:28
static Result< InstrExecResult > Msr(TInstrContext &ictx, const InstrFlagsSet &iflags, const TArg0 &rn, const uint8_t mask, const uint8_t SYSm)
Msr.
Definition special_instr.h:225
static Result< InstrExecResult > Bkpt(TInstrContext &ictx, const InstrFlagsSet &iflags, const u32 &imm32, TDelegates &delegates)
Bkpt instruction.
Definition special_instr.h:78
static Result< InstrExecResult > Strd(TInstrContext &ictx, const InstrFlagsSet &iflags, const TTgt &rt, const TArg0 &rt2, const TArg1 &rn, const u32 &imm32)
Strd.
Definition special_instr.h:433
static Result< InstrExecResult > Bfi(TInstrContext &ictx, const InstrFlagsSet &iflags, const TDest &rd, const TArg0 &rn, const u8 &lsbit, const u8 &msbit)
Bfi.
Definition special_instr.h:122
static Result< InstrExecResult > BCond(TInstrContext &ictx, const InstrFlagsSet &iflags, const u32 &imm32, const u8 &cond)
Branch condition.
Definition special_instr.h:104
static Result< InstrExecResult > Svc(TInstrContext &ictx, const InstrFlagsSet &iflags, const u32 &imm32, TDelegates &delegates)
Svc instruction.
Definition special_instr.h:45
static Result< InstrExecResult > Mrs(TInstrContext &ictx, const InstrFlagsSet &iflags, const TDest &rd, const uint8_t mask, const uint8_t SYSm)
Mrs.
Definition special_instr.h:331
static Result< InstrExecResult > Ldrd(TInstrContext &ictx, const InstrFlagsSet &iflags, const TTgt0 &rt, const TTgt1 &rt2, const TArg0 &rn, const u32 &imm32)
Ldrd.
Definition special_instr.h:185
The libmicroemu::internal namespace contains all classes and functions of libmicroemu which are priva...
Definition bkpt_flags.h:6
@ kSVCall
Exception for system calls.
Definition exception_type.h:48
@ kHardFault
Hard fault exception, triggered by faults that cannot be handled by any other exception.
Definition exception_type.h:21
@ kExecutorUnpredictable
Executor encountered an unpredictable operation.
Definition status_code.h:76
This file contains the declaration of the Predicates class.
Contains the Result class which is used to return the result of a function.
#define TRY_ASSIGN(NAME, OUT_TYPE, CALL)
Try to assign a value to a variable and return an error if the assignment fails.
Definition result.h:123
#define TRY(OUT_TYPE, CALL)
Try to call a function and return an error if the call fails.
Definition result.h:138
This file contains the declaration of the SpecialRegisterId enumeration and related functions.
Definition register_details.h:273
Definition instr_exec_results.h:23
Definition result.h:15