libmicroemu 0.2.0
ARM Microcontroller Emulator Library
Loading...
Searching...
No Matches
binary_instr.h
1#pragma once
2#include "libmicroemu/internal/cpu_accessor.h"
3#include "libmicroemu/internal/decoder/decoder.h"
4#include "libmicroemu/internal/executor/instr/post_exec.h"
5#include "libmicroemu/internal/executor/instr_context.h"
6#include "libmicroemu/internal/executor/instr_exec_results.h"
8#include "libmicroemu/internal/utils/rarg.h"
9#include "libmicroemu/register_details.h"
10#include "libmicroemu/types.h"
11
12namespace libmicroemu::internal {
13
19template <typename TInstrContext> class Lsr2Op {
20public:
21 template <typename TDest, typename TArg0, typename TArg1>
22 static Result<InstrExecFlagsSet> Call(TInstrContext &ictx, const InstrFlagsSet &iflags,
23 const TDest &rd, const TArg0 &rn, const TArg1 &rm) {
24 const auto n = ictx.cpua.ReadRegister(rn.Get());
25 const auto m = ictx.cpua.ReadRegister(rm.Get());
26 const auto shift_n = m & 0xFFU;
27
28 auto apsr = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kApsr>();
29 auto r_shift_c = Alu32::Shift_C(n, SRType::SRType_LSR, shift_n,
30 (apsr & ApsrRegister::kCMsk) == ApsrRegister::kCMsk);
31
32 const auto op_res = OpResult{r_shift_c.result, r_shift_c.carry_out,
33 (apsr & ApsrRegister::kVMsk) == ApsrRegister::kVMsk};
34
35 PostExecWriteRegPcExcluded::Call(ictx, rd, op_res.value);
36 PostExecOptionalSetFlags::Call(ictx, iflags, op_res);
37 PostExecAdvancePcAndIt::Call(ictx, iflags);
38
39 return Ok(kNoInstrExecFlags);
40 }
41};
42
48template <typename TInstrContext> class Asr2Op {
49public:
50 template <typename TDest, typename TArg0, typename TArg1>
51 static Result<InstrExecFlagsSet> Call(TInstrContext &ictx, const InstrFlagsSet &iflags,
52 const TDest &rd, const TArg0 &rn, const TArg1 &rm) {
53 const auto n = ictx.cpua.ReadRegister(rn.Get());
54 const auto m = ictx.cpua.ReadRegister(rm.Get());
55 const auto shift_n = m & 0xFFU;
56
57 auto apsr = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kApsr>();
58 auto r_shift_c = Alu32::Shift_C(n, SRType::SRType_ASR, shift_n,
59 (apsr & ApsrRegister::kCMsk) == ApsrRegister::kCMsk);
60
61 const auto op_res = OpResult{r_shift_c.result, r_shift_c.carry_out,
62 (apsr & ApsrRegister::kVMsk) == ApsrRegister::kVMsk};
63
64 PostExecWriteRegPcExcluded::Call(ictx, rd, op_res.value);
65 PostExecOptionalSetFlags::Call(ictx, iflags, op_res);
66 PostExecAdvancePcAndIt::Call(ictx, iflags);
67
68 return Ok(kNoInstrExecFlags);
69 }
70};
71
77template <typename TInstrContext> class Lsl2Op {
78public:
79 template <typename TDest, typename TArg0, typename TArg1>
80 static Result<InstrExecFlagsSet> Call(TInstrContext &ictx, const InstrFlagsSet &iflags,
81 const TDest &rd, const TArg0 &rn, const TArg1 &rm) {
82
83 const auto n = ictx.cpua.ReadRegister(rn.Get());
84 const auto m = ictx.cpua.ReadRegister(rm.Get());
85 const auto shift_n = m & 0xFFU;
86
87 auto apsr = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kApsr>();
88 auto r_shift_c = Alu32::Shift_C(n, SRType::SRType_LSL, shift_n,
89 (apsr & ApsrRegister::kCMsk) == ApsrRegister::kCMsk);
90
91 const auto op_res = OpResult{r_shift_c.result, r_shift_c.carry_out,
92 (apsr & ApsrRegister::kVMsk) == ApsrRegister::kVMsk};
93
94 PostExecWriteRegPcExcluded::Call(ictx, rd, op_res.value);
95 PostExecOptionalSetFlags::Call(ictx, iflags, op_res);
96 PostExecAdvancePcAndIt::Call(ictx, iflags);
97 return Ok(kNoInstrExecFlags);
98 }
99};
100
106template <typename TInstrContext> class Mul2Op {
107public:
108 template <typename TDest, typename TArg0, typename TArg1>
109 static Result<InstrExecFlagsSet> Call(TInstrContext &ictx, const InstrFlagsSet &iflags,
110 const TDest &rd, const TArg0 &rn, const TArg1 &rm) {
111 auto apsr = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kApsr>();
112 const auto n = ictx.cpua.ReadRegister(rn.Get());
113 const auto m = ictx.cpua.ReadRegister(rm.Get());
114 const auto result = static_cast<u32>(static_cast<i32>(n) * static_cast<i32>(m));
115
116 const auto op_res = OpResult{result, (apsr & ApsrRegister::kCMsk) == ApsrRegister::kCMsk,
117 (apsr & ApsrRegister::kVMsk) == ApsrRegister::kVMsk};
118
119 PostExecWriteRegPcExcluded::Call(ictx, rd, op_res.value);
120 PostExecOptionalSetFlags::Call(ictx, iflags, op_res);
121 PostExecAdvancePcAndIt::Call(ictx, iflags);
122 return Ok(kNoInstrExecFlags);
123 }
124};
125
131template <typename TInstrContext> class UDiv2Op {
132public:
133 using ExcTrig = typename TInstrContext::ExcTrig;
134 static inline bool IntegerZeroDivideTrappingEnabled(const TInstrContext &ictx) {
135 const auto ccr = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kCcr>();
136 return (ccr & CcrRegister::kDivByZeroTrapEnableMsk) != 0U;
137 }
138
139 static inline void GenerateIntegerZeroDivide(const TInstrContext &ictx) {
140 ExcTrig::SetPending(ictx.cpua, ExceptionType::kUsageFault);
141 auto cfsr = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kCfsr>();
142 cfsr |= CfsrUsageFault::kDivByZeroMsk;
143 ictx.cpua.template WriteSpecialRegister<SpecialRegisterId::kCfsr>(cfsr);
144 }
145
146 template <typename TDest, typename TArg0, typename TArg1>
147 static Result<InstrExecFlagsSet> Call(TInstrContext &ictx, const InstrFlagsSet &iflags,
148 const TDest &rd, const TArg0 &rn, const TArg1 &rm) {
149
150 const auto n = ictx.cpua.ReadRegister(rn.Get());
151 const auto m = ictx.cpua.ReadRegister(rm.Get());
152 auto apsr = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kApsr>();
153 u32 result = 0U;
154 if (m == 0U) {
155
156 if (IntegerZeroDivideTrappingEnabled(ictx)) {
157 GenerateIntegerZeroDivide(ictx);
158 } else {
159 result = 0U;
160 }
161 } else {
162 result = n / m;
163 }
164
165 const auto op_res = OpResult{result, (apsr & ApsrRegister::kCMsk) == ApsrRegister::kCMsk,
166 (apsr & ApsrRegister::kVMsk) == ApsrRegister::kVMsk};
167 PostExecWriteRegPcExcluded::Call(ictx, rd, op_res.value);
168 PostExecOptionalSetFlags::Call(ictx, iflags, op_res);
169 PostExecAdvancePcAndIt::Call(ictx, iflags);
170 return Ok(kNoInstrExecFlags);
171 }
172};
173
179template <typename TInstrContext> class SDiv2Op {
180public:
181 using ExcTrig = typename TInstrContext::ExcTrig;
182
183 static inline bool IntegerZeroDivideTrappingEnabled(const TInstrContext &ictx) {
184 const auto ccr = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kCcr>();
185 return (ccr & CcrRegister::kDivByZeroTrapEnableMsk) != 0U;
186 }
187
188 static inline void GenerateIntegerZeroDivide(const TInstrContext &ictx) {
189 ExcTrig::SetPending(ictx.cpua, ExceptionType::kUsageFault);
190 auto cfsr = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kCfsr>();
191 cfsr |= CfsrUsageFault::kDivByZeroMsk;
192 ictx.cpua.template WriteSpecialRegister<SpecialRegisterId::kCfsr>(cfsr);
193 }
194
195 template <typename TDest, typename TArg0, typename TArg1>
196 static Result<InstrExecFlagsSet> Call(TInstrContext &ictx, const InstrFlagsSet &iflags,
197 const TDest &rd, const TArg0 &rn, const TArg1 &rm) {
198
199 const auto n = ictx.cpua.ReadRegister(rn.Get());
200 const auto m = ictx.cpua.ReadRegister(rm.Get());
201 auto apsr = ictx.cpua.template ReadSpecialRegister<SpecialRegisterId::kApsr>();
202 u32 result = 0U;
203 if (m == 0U) {
204 if (IntegerZeroDivideTrappingEnabled(ictx)) {
205 GenerateIntegerZeroDivide(ictx);
206 } else {
207 result = 0U;
208 }
209 } else {
210 result = static_cast<u32>(static_cast<i32>(n) / static_cast<i32>(m));
211 }
212
213 const auto op_res = OpResult{result, (apsr & ApsrRegister::kCMsk) == ApsrRegister::kCMsk,
214 (apsr & ApsrRegister::kVMsk) == ApsrRegister::kVMsk};
215 PostExecWriteRegPcExcluded::Call(ictx, rd, op_res.value);
216
217 PostExecOptionalSetFlags::Call(ictx, iflags, op_res);
218 PostExecAdvancePcAndIt::Call(ictx, iflags);
219 return Ok(kNoInstrExecFlags);
220 }
221};
222template <typename TOp, typename TInstrContext> class BinaryInstr {
223public:
224 using It = typename TInstrContext::It;
225 using Pc = typename TInstrContext::Pc;
226
227 template <typename TDest, typename TArg0, typename TArg1>
228 static Result<InstrExecResult> Call(TInstrContext &ictx, const InstrFlagsSet &iflags,
229 const TDest &rd, const TArg0 &rn, const TArg1 &rm) {
230 TRY_ASSIGN(condition_passed, InstrExecResult, It::ConditionPassed(ictx.cpua));
231 if (!condition_passed) {
232 PostExecAdvancePcAndIt::Call(ictx, iflags);
233 return Ok(InstrExecResult{kNoInstrExecFlags});
234 }
235
236 TRY_ASSIGN(eflags, InstrExecResult, TOp::Call(ictx, iflags, rd, rn, rm));
237 return Ok(InstrExecResult{eflags});
238 }
239
240private:
244 BinaryInstr() = delete;
245
249 ~BinaryInstr() = delete;
250
255 BinaryInstr(const BinaryInstr &r_src) = delete;
256
261 BinaryInstr &operator=(const BinaryInstr &r_src) = delete;
262
267 BinaryInstr(BinaryInstr &&r_src) = delete;
268
273 BinaryInstr &operator=(BinaryInstr &&r_src) = delete;
274};
275
276} // namespace libmicroemu::internal
Asr.
Definition binary_instr.h:48
Lsl.
Definition binary_instr.h:77
Lsr.
Definition binary_instr.h:19
Mul.
Definition binary_instr.h:106
Sdiv.
Definition binary_instr.h:179
Udiv.
Definition binary_instr.h:131
The libmicroemu::internal namespace contains all classes and functions of libmicroemu which are priva...
Definition bkpt_flags.h:6
@ kUsageFault
Usage fault exception, triggered by errors in the usage of the processor.
Definition exception_type.h:30
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
Definition instr_exec_results.h:23
Definition post_exec.h:12
Definition result.h:15