libmicroemu 0.2.0
ARM Microcontroller Emulator Library
Loading...
Searching...
No Matches
processor.h
1#pragma once
2
4#include "libmicroemu/internal/logic/reg_access.h"
5#include "libmicroemu/internal/logic/spec_reg_access.h"
6#include "libmicroemu/internal/processor/step_flags.h"
8#include "libmicroemu/internal/trace/intstr_to_mnemonic.h"
9#include "libmicroemu/logger.h"
10#include "libmicroemu/machine.h"
11#include "libmicroemu/register_details.h"
13#include "libmicroemu/types.h"
14#include <assert.h>
15#include <cstddef>
16#include <cstdint>
17
18namespace libmicroemu::internal {
19
20template <typename TCpuAccessor, typename TBus, typename TProcessorOps, typename TFetcher,
21 typename TDecoder, typename TExecutor, typename TLogger = NullLogger>
22class Processor {
23public:
24 using It = typename TProcessorOps::It;
25 using Exc = typename TProcessorOps::Exc;
26 using Pc = typename TProcessorOps::Pc;
27 using ExcTrig = typename TProcessorOps::ExcTrig;
28
29 // Check if execution mode is thumb .. if not, raise usage fault and return true
30 static bool IsThumbModeOrRaise(TCpuAccessor &cpua) {
31 const bool is_thumb = Predicates::IsThumbMode(cpua);
32
33 // if EPSR.T == 0, a UsageFault(‘Invalid State’) is taken
34 if (!is_thumb) {
35 auto cfsr = cpua.template ReadSpecialRegister<SpecialRegisterId::kCfsr>();
36 cfsr |= CfsrUsageFault::kInvStateMsk;
37 cpua.template WriteSpecialRegister<SpecialRegisterId::kCfsr>(cfsr);
38 ExcTrig::SetPending(cpua, ExceptionType::kUsageFault);
39 return false;
40 }
41 return true;
42 }
43
44 template <typename TDelegates>
45 static Result<StepFlagsSet> Step(TCpuAccessor &cpua, TBus &bus, TDelegates &delegates) {
46 static constexpr u8 kRaw32BitMsk = static_cast<RawInstrFlagsSet>(RawInstrFlagsMsk::k32Bit);
47
48 StepFlagsSet step_flags{0U};
49
50 // *** FETCH ***
51 // The pc points to the current instruction +4 . Therefore decrement 4 to
52 // get the current address.
53
54 // Get the current program counter
55 const me_adr_t pc = static_cast<me_adr_t>(cpua.template ReadRegister<RegisterId::kPc>());
56 me_adr_t pc_this_instr = static_cast<me_adr_t>(pc - 4U);
57
58 // Check for exceptions raised by peripherals before fetching the instruction
59 const auto exc_ctx_pre_fetch = ExceptionContext{pc_this_instr};
60 TRY_ASSIGN(if_pre_fetch_exception, StepFlagsSet,
61 Exc::template CheckExceptions<ExceptionPreFetch>(cpua, bus, exc_ctx_pre_fetch));
62
63 // If an asynchronous exception should be executed, the pc must be updated
64 // to the new instruction address.
65 if (if_pre_fetch_exception) {
66 const me_adr_t pc = static_cast<me_adr_t>(cpua.template ReadRegister<RegisterId::kPc>());
67 pc_this_instr = static_cast<me_adr_t>(pc - 4U);
68 }
69
70 RawInstr raw_instr;
71
72 // Check if execution mode is thumb .. if not, raise usage fault and continue
73 if (IsThumbModeOrRaise(cpua)) {
74 auto r_raw_instr = TFetcher::Fetch(cpua, bus, pc_this_instr);
75 if (r_raw_instr.IsErr()) {
76 ErrorHandler(cpua, r_raw_instr, bus);
77 return Err<RawInstr, StepFlagsSet>(r_raw_instr);
78 }
79 raw_instr = r_raw_instr.content;
80 }
81
82 const auto exc_ctx_post_fetch = ExceptionContext{pc_this_instr};
83
84 // If an invalid state exception was raised, this will processed here
85 // Check for exceptions after fetching the instruction
86 TRY_ASSIGN(is_fetch_exception, StepFlagsSet,
87 Exc::template CheckExceptions<ExceptionPostFetch>(cpua, bus, exc_ctx_post_fetch));
88
89 if (is_fetch_exception) {
90 // special case: if the instruction fetch raises an exception, this cycle is
91 // considered as a NOP cycle.
92
93 // The pc was updated in the exception handler
94 step_flags |= static_cast<StepFlagsSet>(StepFlags::kStepOk);
95 return Ok<StepFlagsSet>(step_flags);
96 }
97
98 // *** DECODE ***
99 auto r_instr = TDecoder::Decode(cpua, raw_instr);
100 if (r_instr.IsErr()) {
101 ErrorHandler(cpua, r_instr, bus);
102 return Err<Instr, StepFlagsSet>(r_instr);
103 }
104 const auto &instr = r_instr.content;
105
106 // Decoder can not raise any exceptions
107#ifndef NDEBUG
108 static constexpr u8 k32BitMsk = static_cast<InstrFlagsSet>(InstrFlags::k32Bit);
109 // check if the raw instruction and the decoded instruction have the same width
110 assert(((((raw_instr.flags & kRaw32BitMsk) != 0U) && ((instr.nop.flags & k32BitMsk) != 0U)) ||
111 (((raw_instr.flags & kRaw32BitMsk) == 0U) && ((instr.nop.flags & k32BitMsk) == 0U))));
112#endif
113
114 // *** CALLBACK ***
115 if (delegates.IsPreExecSet()) {
116 const auto is_32bit = (raw_instr.flags & kRaw32BitMsk) == kRaw32BitMsk;
117 auto op_code = OpCode{raw_instr.low, raw_instr.high, is_32bit};
118 auto instr_to_mnemonic = InstrToMnemonic<TCpuAccessor, It>(cpua, instr);
119 auto reg_access = RegAccessor(cpua);
120 auto spec_reg_access = SpecialRegAccessor(cpua);
121
122 auto emu_ctx =
123 EmuContext(pc_this_instr, op_code, instr_to_mnemonic, reg_access, spec_reg_access);
124 delegates.PreExec(emu_ctx);
125 }
126
127 // *** EXECUTE ***
128 const auto r_execute = TExecutor::Execute(cpua, bus, instr, delegates);
129 if (r_execute.IsErr()) {
130 ErrorHandler(cpua, r_execute, bus);
131 return Err<InstrExecResult, StepFlagsSet>(r_execute);
132 }
133
134 if (delegates.IsPostExecSet()) {
135 const auto is_32bit = (raw_instr.flags & kRaw32BitMsk) == kRaw32BitMsk;
136 auto op_code = OpCode{raw_instr.low, raw_instr.high, is_32bit};
137 auto instr_to_mnemonic = InstrToMnemonic<TCpuAccessor, It>(cpua, instr);
138 auto reg_access = RegAccessor(cpua);
139 auto spec_reg_access = SpecialRegAccessor(cpua);
140 auto emu_ctx =
141 EmuContext(pc_this_instr, op_code, instr_to_mnemonic, reg_access, spec_reg_access);
142 delegates.PostExec(emu_ctx);
143 }
144
145 // *** Exit Conditions ***
146 const auto &eflags = r_execute.content.flags;
147
148 // all exit flags are mutually exclusive
149 if ((eflags & kExitFlagsMask) != 0U) {
150 if ((eflags & static_cast<InstrExecFlagsSet>(InstrExecFlags::kBkptReqExit)) != 0U) {
151 step_flags |= static_cast<StepFlagsSet>(StepFlags::kStepTerminationRequest);
152 return Ok<StepFlagsSet>(step_flags);
153 }
154 if ((eflags & static_cast<InstrExecFlagsSet>(InstrExecFlags::kSvcReqExit)) != 0U) {
155 step_flags |= static_cast<StepFlagsSet>(StepFlags::kStepTerminationRequest);
156 return Ok<StepFlagsSet>(step_flags);
157 }
158 if ((eflags & static_cast<InstrExecFlagsSet>(InstrExecFlags::kBkptReqErrorExit)) != 0U) {
159 return Err<StepFlagsSet>(StatusCode::kExecutorExitWithError);
160 }
161 if ((eflags & static_cast<InstrExecFlagsSet>(InstrExecFlags::kSvcReqErrorExit)) != 0U) {
162 return Err<StepFlagsSet>(StatusCode::kExecutorExitWithError);
163 }
164 }
165
166 // get the next instruction address
167 const auto exc_ctc_post_exec = ExceptionContext{pc_this_instr};
168
169 TRY(StepFlagsSet,
170 Exc::template CheckExceptions<ExceptionPostExecution>(cpua, bus, exc_ctc_post_exec));
171
172 step_flags |= static_cast<StepFlagsSet>(StepFlags::kStepOk);
173 return Ok<StepFlagsSet>(step_flags);
174 }
175
176 template <typename TResult>
177 static void ErrorHandler(TCpuAccessor &cpua, const TResult &res, const TBus &bus) {
178 printf("ERROR: Emulator panic - StatusCode: %s(%i)\n", res.ToString().data(),
179 static_cast<u32>(res.status_code));
180
181 // error return
182 const me_adr_t pc = static_cast<me_adr_t>(cpua.template ReadRegister<RegisterId::kPc>());
183 me_adr_t pc_this_instr = static_cast<me_adr_t>(pc - 0x4U);
184 printf(" # System state:\n");
185 printf(" Actual PC: 0x%x\n\n", pc_this_instr);
186 printf(" # Memory dump from PC:\n");
187
188 MemoryViewer<TCpuAccessor, TBus>::Print(cpua, bus, pc_this_instr, 32U, 3U);
189 }
190
191private:
192 Processor() = delete;
193 ~Processor() = delete;
194 Processor(Processor &r_src) = delete;
195 Processor &operator=(const Processor &r_src) = delete;
196 Processor(Processor &&r_src) = delete;
197 Processor &operator=(Processor &&r_src) = delete;
198};
199
200} // namespace libmicroemu::internal
Definition emu_context.h:79
Definition intstr_to_mnemonic.h:9
static void Print(TCpuAccessor &cpua, const TBus &mem, const me_offset_t vadr, const me_size_t size, const u32 indent=0U)
Print memory content to the console.
Definition memory_viewer.h:22
static bool IsThumbMode(TCpuAccessor &cpua)
Check if the cpu is in thumb mode.
Definition predicates.h:88
Provides access to processor registers.
Definition reg_access.h:19
Provides access to special registers.
Definition spec_reg_access.h:16
Contains the Machine class which is the main emulation machine for handling microcontroller emulation...
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
@ kExecutorExitWithError
Executor exited with an error.
Definition status_code.h:82
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 emu_context.h:11
Definition raw_instr.h:7
Definition exceptions_ops.h:18
Definition result.h:15