1 //============================================================================
2 //
3 //   SSSS    tt          lll  lll
4 //  SS  SS   tt           ll   ll
5 //  SS     tttttt  eeee   ll   ll   aaaa
6 //   SSSS    tt   ee  ee  ll   ll      aa
7 //      SS   tt   eeeeee  ll   ll   aaaaa  --  "An Atari 2600 VCS Emulator"
8 //  SS  SS   tt   ee      ll   ll  aa  aa
9 //   SSSS     ttt  eeeee llll llll  aaaaa
10 //
11 // Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony
12 // and the Stella Team
13 //
14 // See the file "License.txt" for information on usage and redistribution of
15 // this file, and for a DISCLAIMER OF ALL WARRANTIES.
16 //============================================================================
17 
18 //============================================================================
19 // This class provides Thumb emulation code ("Thumbulator")
20 //    by David Welch (dwelch@dwelch.com)
21 // Modified by Fred Quimby
22 // Code is public domain and used with the author's consent
23 //============================================================================
24 
25 #ifndef THUMBULATOR_HXX
26 #define THUMBULATOR_HXX
27 
28 class Cartridge;
29 
30 #include "bspf.hxx"
31 #include "Console.hxx"
32 
33 #ifdef RETRON77
34   #define UNSAFE_OPTIMIZATIONS
35 #endif
36 
37 #define ROMADDMASK 0x7FFFF
38 #define RAMADDMASK 0x7FFF
39 
40 #define ROMSIZE (ROMADDMASK+1)      // 512KB
41 #define RAMSIZE (RAMADDMASK+1)      // 32KB
42 
43 #define CPSR_N (1u<<31)
44 #define CPSR_Z (1u<<30)
45 #define CPSR_C (1u<<29)
46 #define CPSR_V (1u<<28)
47 
48 #ifdef DEBUGGER_SUPPORT
49   #define THUMB_CYCLE_COUNT
50   //#define COUNT_OPS
51   #define THUMB_STATS
52 #endif
53 
54 #ifdef THUMB_CYCLE_COUNT
55   //#define EMULATE_PIPELINE  // enable coarse ARM pipeline emulation (TODO)
56   #define TIMER_0           // enable timer 0 support (e.g. for measuring cycle count)
57 #endif
58 
59 class Thumbulator
60 {
61   public:
62     // control cartridge specific features of the Thumbulator class,
63     // such as the start location for calling custom code
64     enum class ConfigureFor {
65       BUS,      // cartridges of type BUS
66       CDF,      // cartridges of type CDF
67       CDF1,     // cartridges of type CDF version 1
68       CDFJ,     // cartridges of type CDFJ
69       CDFJplus, // cartridges of type CDFJ+
70       DPCplus   // cartridges of type DPC+
71     };
72     enum class ChipType {
73       LPC2101,    // Harmony (includes LPC2103)
74       LPC2104_OC, // Dev cart overclocked (includes LPC2105)
75       LPC2104,    // Dev cart (includes LPC2105)
76       LPC213x,    // future use (includes LPC2132)
77       numTypes
78     };
79     enum class MamModeType {
80       mode0, mode1, mode2, modeX
81     };
82     struct ChipPropsType {
83       double MHz;
84       uInt32 flashCycles;
85       uInt32 flashBanks;
86     };
87     struct Stats {
88       uInt32 instructions{0};
89     #ifdef THUMB_STATS
90       uInt32 reads{0}, writes{0};
91       uInt32 nCylces{0}, sCylces{0}, iCylces{0};
92       uInt32 branches{0}, taken{0};
93       uInt32 mamPrefetchHits{0}, mamPrefetchMisses{0};
94       uInt32 mamBranchHits{0}, mamBranchMisses{0};
95       uInt32 mamDataHits{0}, mamDataMisses{0};
96     #endif
97     };
98 
99     Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, uInt32 rom_size,
100                 const uInt32 c_base, const uInt32 c_start, const uInt32 c_stack,
101                 bool traponfatal, double cyclefactor,
102                 Thumbulator::ConfigureFor configurefor,
103                 Cartridge* cartridge);
104 
105     /**
106       Run the ARM code, and return when finished.  A runtime_error exception is
107       thrown in case of any fatal errors/aborts (if enabled), containing the
108       actual error, and the contents of the registers at that point in time.
109 
110       @return  The results of any debugging output (if enabled),
111                otherwise an empty string
112     */
113     string run(uInt32& cycles, bool irqDrivenAudio);
enableCycleCount(bool enable)114     void enableCycleCount(bool enable) { _countCycles = enable; }
stats() const115     const Stats& stats() const { return _stats; }
cycles() const116     uInt32 cycles() const { return _totalCycles; }
117     ChipPropsType setChipType(ChipType type);
setMamMode(MamModeType mode)118     void setMamMode(MamModeType mode) { mamcr = mode; }
lockMamMode(bool lock)119     void lockMamMode(bool lock) { _lockMamcr = lock; }
mamMode() const120     MamModeType mamMode() const { return static_cast<MamModeType>(mamcr); }
121 
122   #ifdef THUMB_CYCLE_COUNT
cycleFactor(double factor)123     void cycleFactor(double factor) { _armCyclesFactor = factor; }
cycleFactor() const124     double cycleFactor() const { return _armCyclesFactor; }
125   #else
cycleFactor(double)126     void cycleFactor(double) { }
cycleFactor() const127     double cycleFactor() const { return 1.0; }
128   #endif
129 
130   #ifndef UNSAFE_OPTIMIZATIONS
131     /**
132       Normally when a fatal error is encountered, the ARM emulation
133       immediately throws an exception and exits.  This method allows execution
134       to continue, and simply log the error.
135 
136       Note that this is meant for developers only, and should normally be
137       always enabled.  It can be used to temporarily ignore illegal reads
138       and writes, but a ROM which consistently performs these operations
139       should be fixed, as it can cause crashes on real hardware.
140 
141       @param enable  Enable (the default) or disable exceptions on fatal errors
142     */
trapFatalErrors(bool enable)143     void trapFatalErrors(bool enable) { trapOnFatal = enable; }
144   #endif
145 
146     /**
147       Inform the Thumbulator class about the console currently in use,
148       which is used to accurately determine how many 6507 cycles have
149       passed while ARM code is being executed.
150     */
151     void setConsoleTiming(ConsoleTiming timing);
152 
153   private:
154 
155     enum class Op : uInt8 {
156       invalid,
157       adc,
158       add1, add2, add3, add4, add5, add6, add7,
159       and_,
160       asr1, asr2,
161       b1, b2,
162       bic,
163       bkpt,
164       blx1, blx2,
165       bx,
166       cmn,
167       cmp1, cmp2, cmp3,
168       cps,
169       cpy,
170       eor,
171       ldmia,
172       ldr1, ldr2, ldr3, ldr4,
173       ldrb1, ldrb2,
174       ldrh1, ldrh2,
175       ldrsb,
176       ldrsh,
177       lsl1, lsl2,
178       lsr1, lsr2,
179       mov1, mov2, mov3,
180       mul,
181       mvn,
182       neg,
183       orr,
184       pop,
185       push,
186       rev,
187       rev16,
188       revsh,
189       ror,
190       sbc,
191       setend,
192       stmia,
193       str1, str2, str3,
194       strb1, strb2,
195       strh1, strh2,
196       sub1, sub2, sub3, sub4,
197       swi,
198       sxtb,
199       sxth,
200       tst,
201       uxtb,
202       uxth,
203       numOps
204     };
205   #ifdef THUMB_CYCLE_COUNT
206     enum class CycleType {
207       S, N, I // Sequential, Non-sequential, Internal
208     };
209     enum class AccessType {
210       prefetch, branch, data
211     };
212   #endif
213     const std::array<ChipPropsType, uInt32(ChipType::numTypes)> ChipProps =
214     {{
215       { 70.0, 4, 1 }, // LPC2101_02_03
216       { 70.0, 4, 2 }, // LPC2104_05_06 Overclocked
217       { 60.0, 3, 2 }, // LPC2104_05_06
218       { 60.0, 3, 1 }, // LPC2132..
219     }};
220 
221   private:
222     string doRun(uInt32& cycles, bool irqDrivenAudio);
223     uInt32 read_register(uInt32 reg);
224     void write_register(uInt32 reg, uInt32 data, bool isFlowBreak = true);
225     uInt32 fetch16(uInt32 addr);
226     uInt32 read16(uInt32 addr);
227     uInt32 read32(uInt32 addr);
228   #ifndef UNSAFE_OPTIMIZATIONS
229     bool isProtected(uInt32 addr);
230   #endif
231     void write16(uInt32 addr, uInt32 data);
232     void write32(uInt32 addr, uInt32 data);
233     void updateTimer(uInt32 cycles);
234 
235     static Op decodeInstructionWord(uint16_t inst);
236 
237     void do_zflag(uInt32 x);
238     void do_nflag(uInt32 x);
239     void do_cflag(uInt32 a, uInt32 b, uInt32 c);
240     void do_vflag(uInt32 a, uInt32 b, uInt32 c);
241     void do_cflag_bit(uInt32 x);
242     void do_vflag_bit(uInt32 x);
243 
244   #ifndef UNSAFE_OPTIMIZATIONS
245     // Throw a runtime_error exception containing an error referencing the
246     // given message and variables
247     // Note that the return value is never used in these methods
248     int fatalError(const char* opcode, uInt32 v1, const char* msg);
249     int fatalError(const char* opcode, uInt32 v1, uInt32 v2, const char* msg);
250 
251     void dump_counters();
252     void dump_regs();
253   #endif
254     int execute();
255     int reset();
256 
257   #ifdef THUMB_CYCLE_COUNT
258     bool isMamBuffered(uInt32 addr, AccessType = AccessType::data);
259     void incCycles(AccessType accessType, uInt32 cycles);
260     void incSCycles(uInt32 addr, AccessType = AccessType::data);
261     void incNCycles(uInt32 addr, AccessType = AccessType::data);
262     void incICycles(uInt32 m = 1);
263   #endif
264 
265   private:
266     const uInt16* rom{nullptr};
267     uInt32 romSize{0};
268     uInt32 cBase{0};
269     uInt32 cStart{0};
270     uInt32 cStack{0};
271     const unique_ptr<Op[]> decodedRom;  // NOLINT
272     uInt16* ram{nullptr};
273     std::array<uInt32, 16> reg_norm; // normal execution mode, do not have a thread mode
274     uInt32 cpsr{0};
275     MamModeType mamcr{MamModeType::mode0};
276     bool handler_mode{false};
277     uInt32 systick_ctrl{0}, systick_reload{0}, systick_count{0}, systick_calibrate{0};
278     ChipType _chipType{ChipType::LPC2101};
279     ConsoleTiming _consoleTiming{ConsoleTiming::ntsc};
280     double _MHz{70.0};
281     uInt32 _flashCycles{4};
282     uInt32 _flashBanks{1};
283     Stats _stats{0};
284     bool _irqDrivenAudio{false};
285     uInt32 _totalCycles{0};
286 
287     // For emulation of LPC2103's timer 1, used for NTSC/PAL/SECAM detection.
288     // Register names from documentation:
289     // http://www.nxp.com/documents/user_manual/UM10161.pdf
290   #ifdef TIMER_0
291     uInt32 T0TCR{0};      // Timer 0 Timer Control Register
292     uInt32 T0TC{0};       // Timer 0 Timer Counter
293     uInt32 tim0Start{0};  // _totalCycles when Timer 0 got started last time
294     uInt32 tim0Total{0};  // total cycles of Timer 0
295   #endif
296     uInt32 T1TCR{0};      // Timer 1 Timer Control Register
297     uInt32 T1TC{0};       // Timer 1 Timer Counter
298     uInt32 tim1Start{0};  // _totalCycles when Timer 1 got started last time
299     uInt32 tim1Total{0};  // total cycles of Timer 1
300     double timing_factor{0.0};
301 
302   #ifndef UNSAFE_OPTIMIZATIONS
303     ostringstream statusMsg;
304     bool trapOnFatal{true};
305   #endif
306     bool _countCycles{false};
307     bool _lockMamcr{false};
308 
309   #ifdef THUMB_CYCLE_COUNT
310     double _armCyclesFactor{1.05};
311     uInt32 _pipeIdx{0};
312     CycleType _prefetchCycleType[3]{CycleType::S};
313     CycleType _lastCycleType[3]{CycleType::S};
314 #if 0 // unused for now
315     AccessType _prefetchAccessType[3]{AccessType::data};
316 #endif
317    #ifdef EMULATE_PIPELINE
318     uInt32 _fetchPipeline{0}; // reserve fetch cycles resulting from pipelining (execution stage)
319     uInt32 _memory0Pipeline{0}, _memory1Pipeline{0};
320    #endif
321     uInt32 _prefetchBufferAddr[2]{0};
322     uInt32 _branchBufferAddr[2]{0};
323     uInt32 _dataBufferAddr{0};
324   #endif
325   #ifdef COUNT_OPS
326     uInt32 opCount[size_t(Op::numOps)]{0};
327   #endif
328 
329     ConfigureFor configuration;
330 
331     Cartridge* myCartridge;
332 
333   private:
334     // Following constructors and assignment operators not supported
335     Thumbulator() = delete;
336     Thumbulator(const Thumbulator&) = delete;
337     Thumbulator(Thumbulator&&) = delete;
338     Thumbulator& operator=(const Thumbulator&) = delete;
339     Thumbulator& operator=(Thumbulator&&) = delete;
340 };
341 
342 #endif  // THUMBULATOR_HXX
343