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-2019 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 | #define NO_THUMB_STATS |
36 | #endif |
37 | |
38 | #define ROMADDMASK 0x7FFF |
39 | #define RAMADDMASK 0x1FFF |
40 | |
41 | #define ROMSIZE (ROMADDMASK+1) |
42 | #define RAMSIZE (RAMADDMASK+1) |
43 | |
44 | #define CPSR_N (1u<<31) |
45 | #define CPSR_Z (1u<<30) |
46 | #define CPSR_C (1u<<29) |
47 | #define CPSR_V (1u<<28) |
48 | |
49 | class Thumbulator |
50 | { |
51 | public: |
52 | // control cartridge specific features of the Thumbulator class, |
53 | // such as the start location for calling custom code |
54 | enum class ConfigureFor { |
55 | BUS, // cartridges of type BUS |
56 | CDF, // cartridges of type CDF |
57 | CDF1, // cartridges of type CDF version 1 |
58 | CDFJ, // cartrdiges of type CDFJ |
59 | DPCplus // cartridges of type DPC+ |
60 | }; |
61 | |
62 | Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, uInt16 rom_size, |
63 | bool traponfatal, Thumbulator::ConfigureFor configurefor, |
64 | Cartridge* cartridge); |
65 | |
66 | /** |
67 | Run the ARM code, and return when finished. A runtime_error exception is |
68 | thrown in case of any fatal errors/aborts (if enabled), containing the |
69 | actual error, and the contents of the registers at that point in time. |
70 | |
71 | @return The results of any debugging output (if enabled), |
72 | otherwise an empty string |
73 | */ |
74 | string run(); |
75 | string run(uInt32 cycles); |
76 | |
77 | #ifndef UNSAFE_OPTIMIZATIONS |
78 | /** |
79 | Normally when a fatal error is encountered, the ARM emulation |
80 | immediately throws an exception and exits. This method allows execution |
81 | to continue, and simply log the error. |
82 | |
83 | Note that this is meant for developers only, and should normally be |
84 | always enabled. It can be used to temporarily ignore illegal reads |
85 | and writes, but a ROM which consistently performs these operations |
86 | should be fixed, as it can cause crashes on real hardware. |
87 | |
88 | @param enable Enable (the default) or disable exceptions on fatal errors |
89 | */ |
90 | static void trapFatalErrors(bool enable) { trapOnFatal = enable; } |
91 | #endif |
92 | |
93 | /** |
94 | Inform the Thumbulator class about the console currently in use, |
95 | which is used to accurately determine how many 6507 cycles have |
96 | passed while ARM code is being executed. |
97 | */ |
98 | void setConsoleTiming(ConsoleTiming timing); |
99 | |
100 | private: |
101 | |
102 | enum class Op : uInt8 { |
103 | invalid, |
104 | adc, |
105 | add1, add2, add3, add4, add5, add6, add7, |
106 | and_, |
107 | asr1, asr2, |
108 | b1, b2, |
109 | bic, |
110 | bkpt, |
111 | blx1, blx2, |
112 | bx, |
113 | cmn, |
114 | cmp1, cmp2, cmp3, |
115 | cps, |
116 | cpy, |
117 | eor, |
118 | ldmia, |
119 | ldr1, ldr2, ldr3, ldr4, |
120 | ldrb1, ldrb2, |
121 | ldrh1, ldrh2, |
122 | ldrsb, |
123 | ldrsh, |
124 | lsl1, lsl2, |
125 | lsr1, lsr2, |
126 | mov1, mov2, mov3, |
127 | mul, |
128 | mvn, |
129 | neg, |
130 | orr, |
131 | pop, |
132 | push, |
133 | rev, |
134 | rev16, |
135 | revsh, |
136 | ror, |
137 | sbc, |
138 | setend, |
139 | stmia, |
140 | str1, str2, str3, |
141 | strb1, strb2, |
142 | strh1, strh2, |
143 | sub1, sub2, sub3, sub4, |
144 | swi, |
145 | sxtb, |
146 | sxth, |
147 | tst, |
148 | uxtb, |
149 | uxth |
150 | }; |
151 | |
152 | private: |
153 | uInt32 read_register(uInt32 reg); |
154 | void write_register(uInt32 reg, uInt32 data); |
155 | uInt32 fetch16(uInt32 addr); |
156 | uInt32 read16(uInt32 addr); |
157 | uInt32 read32(uInt32 addr); |
158 | #ifndef UNSAFE_OPTIMIZATIONS |
159 | bool isProtected(uInt32 addr); |
160 | #endif |
161 | void write16(uInt32 addr, uInt32 data); |
162 | void write32(uInt32 addr, uInt32 data); |
163 | void updateTimer(uInt32 cycles); |
164 | |
165 | static Op decodeInstructionWord(uint16_t inst); |
166 | |
167 | void do_zflag(uInt32 x); |
168 | void do_nflag(uInt32 x); |
169 | void do_cflag(uInt32 a, uInt32 b, uInt32 c); |
170 | void do_vflag(uInt32 a, uInt32 b, uInt32 c); |
171 | void do_cflag_bit(uInt32 x); |
172 | void do_vflag_bit(uInt32 x); |
173 | |
174 | #ifndef UNSAFE_OPTIMIZATIONS |
175 | // Throw a runtime_error exception containing an error referencing the |
176 | // given message and variables |
177 | // Note that the return value is never used in these methods |
178 | int fatalError(const char* opcode, uInt32 v1, const char* msg); |
179 | int fatalError(const char* opcode, uInt32 v1, uInt32 v2, const char* msg); |
180 | |
181 | void dump_counters(); |
182 | void dump_regs(); |
183 | #endif |
184 | int execute(); |
185 | int reset(); |
186 | |
187 | private: |
188 | const uInt16* rom; |
189 | uInt16 romSize; |
190 | const unique_ptr<Op[]> decodedRom; |
191 | uInt16* ram; |
192 | |
193 | uInt32 reg_norm[16]; // normal execution mode, do not have a thread mode |
194 | uInt32 cpsr, mamcr; |
195 | bool handler_mode; |
196 | uInt32 systick_ctrl, systick_reload, systick_count, systick_calibrate; |
197 | #ifndef UNSAFE_OPTIMIZATIONS |
198 | uInt64 instructions; |
199 | #endif |
200 | #ifndef NO_THUMB_STATS |
201 | uInt64 fetches, reads, writes; |
202 | #endif |
203 | |
204 | // For emulation of LPC2103's timer 1, used for NTSC/PAL/SECAM detection. |
205 | // Register names from documentation: |
206 | // http://www.nxp.com/documents/user_manual/UM10161.pdf |
207 | uInt32 T1TCR; // Timer 1 Timer Control Register |
208 | uInt32 T1TC; // Timer 1 Timer Counter |
209 | double timing_factor; |
210 | |
211 | #ifndef UNSAFE_OPTIMIZATIONS |
212 | ostringstream statusMsg; |
213 | |
214 | static bool trapOnFatal; |
215 | #endif |
216 | |
217 | ConfigureFor configuration; |
218 | |
219 | Cartridge* myCartridge; |
220 | |
221 | private: |
222 | // Following constructors and assignment operators not supported |
223 | Thumbulator() = delete; |
224 | Thumbulator(const Thumbulator&) = delete; |
225 | Thumbulator(Thumbulator&&) = delete; |
226 | Thumbulator& operator=(const Thumbulator&) = delete; |
227 | Thumbulator& operator=(Thumbulator&&) = delete; |
228 | }; |
229 | |
230 | #endif // THUMBULATOR_HXX |
231 | |