summaryrefslogtreecommitdiff
path: root/src/core/arm/interpreter/thumbemu.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2014-04-08 19:25:03 -0400
committerGravatar bunnei2014-04-08 19:25:03 -0400
commit63e46abdb8764bc97e91bae862c8d461e61b1965 (patch)
treee73f4aa25d7b4015a265e7bbfb6004dab7561027 /src/core/arm/interpreter/thumbemu.cpp
parentfixed some license headers that I missed (diff)
downloadyuzu-63e46abdb8764bc97e91bae862c8d461e61b1965.tar.gz
yuzu-63e46abdb8764bc97e91bae862c8d461e61b1965.tar.xz
yuzu-63e46abdb8764bc97e91bae862c8d461e61b1965.zip
got rid of 'src' folders in each sub-project
Diffstat (limited to 'src/core/arm/interpreter/thumbemu.cpp')
-rw-r--r--src/core/arm/interpreter/thumbemu.cpp513
1 files changed, 513 insertions, 0 deletions
diff --git a/src/core/arm/interpreter/thumbemu.cpp b/src/core/arm/interpreter/thumbemu.cpp
new file mode 100644
index 000000000..032d84b65
--- /dev/null
+++ b/src/core/arm/interpreter/thumbemu.cpp
@@ -0,0 +1,513 @@
1/* thumbemu.c -- Thumb instruction emulation.
2 Copyright (C) 1996, Cygnus Software Technologies Ltd.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
17
18/* We can provide simple Thumb simulation by decoding the Thumb
19instruction into its corresponding ARM instruction, and using the
20existing ARM simulator. */
21
22#include "skyeye_defs.h"
23
24#ifndef MODET /* required for the Thumb instruction support */
25#if 1
26#error "MODET needs to be defined for the Thumb world to work"
27#else
28#define MODET (1)
29#endif
30#endif
31
32#include "armdefs.h"
33#include "armemu.h"
34#include "armos.h"
35
36
37/* Decode a 16bit Thumb instruction. The instruction is in the low
38 16-bits of the tinstr field, with the following Thumb instruction
39 held in the high 16-bits. Passing in two Thumb instructions allows
40 easier simulation of the special dual BL instruction. */
41
42tdstate
43ARMul_ThumbDecode (
44 ARMul_State *state,
45 ARMword pc,
46 ARMword tinstr,
47 ARMword *ainstr)
48{
49 tdstate valid = t_decoded; /* default assumes a valid instruction */
50 ARMword next_instr;
51
52 if (state->bigendSig) {
53 next_instr = tinstr & 0xFFFF;
54 tinstr >>= 16;
55 }
56 else {
57 next_instr = tinstr >> 16;
58 tinstr &= 0xFFFF;
59 }
60
61#if 1 /* debugging to catch non updates */
62 *ainstr = 0xDEADC0DE;
63#endif
64
65 switch ((tinstr & 0xF800) >> 11) {
66 case 0: /* LSL */
67 case 1: /* LSR */
68 case 2: /* ASR */
69 /* Format 1 */
70 *ainstr = 0xE1B00000 /* base opcode */
71 | ((tinstr & 0x1800) >> (11 - 5)) /* shift type */
72 |((tinstr & 0x07C0) << (7 - 6)) /* imm5 */
73 |((tinstr & 0x0038) >> 3) /* Rs */
74 |((tinstr & 0x0007) << 12); /* Rd */
75 break;
76 case 3: /* ADD/SUB */
77 /* Format 2 */
78 {
79 ARMword subset[4] = {
80 0xE0900000, /* ADDS Rd,Rs,Rn */
81 0xE0500000, /* SUBS Rd,Rs,Rn */
82 0xE2900000, /* ADDS Rd,Rs,#imm3 */
83 0xE2500000 /* SUBS Rd,Rs,#imm3 */
84 };
85 /* It is quicker indexing into a table, than performing switch
86 or conditionals: */
87 *ainstr = subset[(tinstr & 0x0600) >> 9] /* base opcode */
88 |((tinstr & 0x01C0) >> 6) /* Rn or imm3 */
89 |((tinstr & 0x0038) << (16 - 3)) /* Rs */
90 |((tinstr & 0x0007) << (12 - 0)); /* Rd */
91 }
92 break;
93 case 4: /* MOV */
94 case 5: /* CMP */
95 case 6: /* ADD */
96 case 7: /* SUB */
97 /* Format 3 */
98 {
99 ARMword subset[4] = {
100 0xE3B00000, /* MOVS Rd,#imm8 */
101 0xE3500000, /* CMP Rd,#imm8 */
102 0xE2900000, /* ADDS Rd,Rd,#imm8 */
103 0xE2500000, /* SUBS Rd,Rd,#imm8 */
104 };
105 *ainstr = subset[(tinstr & 0x1800) >> 11] /* base opcode */
106 |((tinstr & 0x00FF) >> 0) /* imm8 */
107 |((tinstr & 0x0700) << (16 - 8)) /* Rn */
108 |((tinstr & 0x0700) << (12 - 8)); /* Rd */
109 }
110 break;
111 case 8: /* Arithmetic and high register transfers */
112 /* TODO: Since the subsets for both Format 4 and Format 5
113 instructions are made up of different ARM encodings, we could
114 save the following conditional, and just have one large
115 subset. */
116 if ((tinstr & (1 << 10)) == 0) {
117 /* Format 4 */
118 enum OpcodeType { t_norm, t_shift, t_neg, t_mul };
119 struct ThumbOpcode {
120 ARMword opcode;
121 OpcodeType otype;
122 };
123
124 ThumbOpcode subset[16] = {
125 {
126 0xE0100000, t_norm}, /* ANDS Rd,Rd,Rs */
127 {
128 0xE0300000, t_norm}, /* EORS Rd,Rd,Rs */
129 {
130 0xE1B00010, t_shift}, /* MOVS Rd,Rd,LSL Rs */
131 {
132 0xE1B00030, t_shift}, /* MOVS Rd,Rd,LSR Rs */
133 {
134 0xE1B00050, t_shift}, /* MOVS Rd,Rd,ASR Rs */
135 {
136 0xE0B00000, t_norm}, /* ADCS Rd,Rd,Rs */
137 {
138 0xE0D00000, t_norm}, /* SBCS Rd,Rd,Rs */
139 {
140 0xE1B00070, t_shift}, /* MOVS Rd,Rd,ROR Rs */
141 {
142 0xE1100000, t_norm}, /* TST Rd,Rs */
143 {
144 0xE2700000, t_neg}, /* RSBS Rd,Rs,#0 */
145 {
146 0xE1500000, t_norm}, /* CMP Rd,Rs */
147 {
148 0xE1700000, t_norm}, /* CMN Rd,Rs */
149 {
150 0xE1900000, t_norm}, /* ORRS Rd,Rd,Rs */
151 {
152 0xE0100090, t_mul}, /* MULS Rd,Rd,Rs */
153 {
154 0xE1D00000, t_norm}, /* BICS Rd,Rd,Rs */
155 {
156 0xE1F00000, t_norm} /* MVNS Rd,Rs */
157 };
158 *ainstr = subset[(tinstr & 0x03C0) >> 6].opcode; /* base */
159 switch (subset[(tinstr & 0x03C0) >> 6].otype) {
160 case t_norm:
161 *ainstr |= ((tinstr & 0x0007) << 16) /* Rn */
162 |((tinstr & 0x0007) << 12) /* Rd */
163 |((tinstr & 0x0038) >> 3); /* Rs */
164 break;
165 case t_shift:
166 *ainstr |= ((tinstr & 0x0007) << 12) /* Rd */
167 |((tinstr & 0x0007) >> 0) /* Rm */
168 |((tinstr & 0x0038) << (8 - 3)); /* Rs */
169 break;
170 case t_neg:
171 *ainstr |= ((tinstr & 0x0007) << 12) /* Rd */
172 |((tinstr & 0x0038) << (16 - 3)); /* Rn */
173 break;
174 case t_mul:
175 *ainstr |= ((tinstr & 0x0007) << 16) /* Rd */
176 |((tinstr & 0x0007) << 8) /* Rs */
177 |((tinstr & 0x0038) >> 3); /* Rm */
178 break;
179 }
180 }
181 else {
182 /* Format 5 */
183 ARMword Rd = ((tinstr & 0x0007) >> 0);
184 ARMword Rs = ((tinstr & 0x0038) >> 3);
185 if (tinstr & (1 << 7))
186 Rd += 8;
187 if (tinstr & (1 << 6))
188 Rs += 8;
189 switch ((tinstr & 0x03C0) >> 6) {
190 case 0x1: /* ADD Rd,Rd,Hs */
191 case 0x2: /* ADD Hd,Hd,Rs */
192 case 0x3: /* ADD Hd,Hd,Hs */
193 *ainstr = 0xE0800000 /* base */
194 | (Rd << 16) /* Rn */
195 |(Rd << 12) /* Rd */
196 |(Rs << 0); /* Rm */
197 break;
198 case 0x5: /* CMP Rd,Hs */
199 case 0x6: /* CMP Hd,Rs */
200 case 0x7: /* CMP Hd,Hs */
201 *ainstr = 0xE1500000 /* base */
202 | (Rd << 16) /* Rn */
203 |(Rd << 12) /* Rd */
204 |(Rs << 0); /* Rm */
205 break;
206 case 0x9: /* MOV Rd,Hs */
207 case 0xA: /* MOV Hd,Rs */
208 case 0xB: /* MOV Hd,Hs */
209 *ainstr = 0xE1A00000 /* base */
210 | (Rd << 16) /* Rn */
211 |(Rd << 12) /* Rd */
212 |(Rs << 0); /* Rm */
213 break;
214 case 0xC: /* BX Rs */
215 case 0xD: /* BX Hs */
216 *ainstr = 0xE12FFF10 /* base */
217 | ((tinstr & 0x0078) >> 3); /* Rd */
218 break;
219 case 0x0: /* UNDEFINED */
220 case 0x4: /* UNDEFINED */
221 case 0x8: /* UNDEFINED */
222 valid = t_undefined;
223 break;
224 case 0xE: /* BLX */
225 case 0xF: /* BLX */
226 if (state->is_v5) {
227 *ainstr = 0xE1200030 /* base */
228 |(Rs << 0); /* Rm */
229 } else {
230 valid = t_undefined;
231 }
232 break;
233 }
234 }
235 break;
236 case 9: /* LDR Rd,[PC,#imm8] */
237 /* Format 6 */
238 *ainstr = 0xE59F0000 /* base */
239 | ((tinstr & 0x0700) << (12 - 8)) /* Rd */
240 |((tinstr & 0x00FF) << (2 - 0)); /* off8 */
241 break;
242 case 10:
243 case 11:
244 /* TODO: Format 7 and Format 8 perform the same ARM encoding, so
245 the following could be merged into a single subset, saving on
246 the following boolean: */
247 if ((tinstr & (1 << 9)) == 0) {
248 /* Format 7 */
249 ARMword subset[4] = {
250 0xE7800000, /* STR Rd,[Rb,Ro] */
251 0xE7C00000, /* STRB Rd,[Rb,Ro] */
252 0xE7900000, /* LDR Rd,[Rb,Ro] */
253 0xE7D00000 /* LDRB Rd,[Rb,Ro] */
254 };
255 *ainstr = subset[(tinstr & 0x0C00) >> 10] /* base */
256 |((tinstr & 0x0007) << (12 - 0)) /* Rd */
257 |((tinstr & 0x0038) << (16 - 3)) /* Rb */
258 |((tinstr & 0x01C0) >> 6); /* Ro */
259 }
260 else {
261 /* Format 8 */
262 ARMword subset[4] = {
263 0xE18000B0, /* STRH Rd,[Rb,Ro] */
264 0xE19000D0, /* LDRSB Rd,[Rb,Ro] */
265 0xE19000B0, /* LDRH Rd,[Rb,Ro] */
266 0xE19000F0 /* LDRSH Rd,[Rb,Ro] */
267 };
268 *ainstr = subset[(tinstr & 0x0C00) >> 10] /* base */
269 |((tinstr & 0x0007) << (12 - 0)) /* Rd */
270 |((tinstr & 0x0038) << (16 - 3)) /* Rb */
271 |((tinstr & 0x01C0) >> 6); /* Ro */
272 }
273 break;
274 case 12: /* STR Rd,[Rb,#imm5] */
275 case 13: /* LDR Rd,[Rb,#imm5] */
276 case 14: /* STRB Rd,[Rb,#imm5] */
277 case 15: /* LDRB Rd,[Rb,#imm5] */
278 /* Format 9 */
279 {
280 ARMword subset[4] = {
281 0xE5800000, /* STR Rd,[Rb,#imm5] */
282 0xE5900000, /* LDR Rd,[Rb,#imm5] */
283 0xE5C00000, /* STRB Rd,[Rb,#imm5] */
284 0xE5D00000 /* LDRB Rd,[Rb,#imm5] */
285 };
286 /* The offset range defends on whether we are transferring a
287 byte or word value: */
288 *ainstr = subset[(tinstr & 0x1800) >> 11] /* base */
289 |((tinstr & 0x0007) << (12 - 0)) /* Rd */
290 |((tinstr & 0x0038) << (16 - 3)) /* Rb */
291 |((tinstr & 0x07C0) >> (6 - ((tinstr & (1 << 12)) ? 0 : 2))); /* off5 */
292 }
293 break;
294 case 16: /* STRH Rd,[Rb,#imm5] */
295 case 17: /* LDRH Rd,[Rb,#imm5] */
296 /* Format 10 */
297 *ainstr = ((tinstr & (1 << 11)) /* base */
298 ? 0xE1D000B0 /* LDRH */
299 : 0xE1C000B0) /* STRH */
300 |((tinstr & 0x0007) << (12 - 0)) /* Rd */
301 |((tinstr & 0x0038) << (16 - 3)) /* Rb */
302 |((tinstr & 0x01C0) >> (6 - 1)) /* off5, low nibble */
303 |((tinstr & 0x0600) >> (9 - 8)); /* off5, high nibble */
304 break;
305 case 18: /* STR Rd,[SP,#imm8] */
306 case 19: /* LDR Rd,[SP,#imm8] */
307 /* Format 11 */
308 *ainstr = ((tinstr & (1 << 11)) /* base */
309 ? 0xE59D0000 /* LDR */
310 : 0xE58D0000) /* STR */
311 |((tinstr & 0x0700) << (12 - 8)) /* Rd */
312 |((tinstr & 0x00FF) << 2); /* off8 */
313 break;
314 case 20: /* ADD Rd,PC,#imm8 */
315 case 21: /* ADD Rd,SP,#imm8 */
316 /* Format 12 */
317 if ((tinstr & (1 << 11)) == 0) {
318 /* NOTE: The PC value used here should by word aligned */
319 /* We encode shift-left-by-2 in the rotate immediate field,
320 so no shift of off8 is needed. */
321 *ainstr = 0xE28F0F00 /* base */
322 | ((tinstr & 0x0700) << (12 - 8)) /* Rd */
323 |(tinstr & 0x00FF); /* off8 */
324 }
325 else {
326 /* We encode shift-left-by-2 in the rotate immediate field,
327 so no shift of off8 is needed. */
328 *ainstr = 0xE28D0F00 /* base */
329 | ((tinstr & 0x0700) << (12 - 8)) /* Rd */
330 |(tinstr & 0x00FF); /* off8 */
331 }
332 break;
333 case 22:
334 case 23:
335 if ((tinstr & 0x0F00) == 0x0000) {
336 /* Format 13 */
337 /* NOTE: The instruction contains a shift left of 2
338 equivalent (implemented as ROR #30): */
339 *ainstr = ((tinstr & (1 << 7)) /* base */
340 ? 0xE24DDF00 /* SUB */
341 : 0xE28DDF00) /* ADD */
342 |(tinstr & 0x007F); /* off7 */
343 }
344 else if ((tinstr & 0x0F00) == 0x0e00)
345 *ainstr = 0xEF000000 | SWI_Breakpoint;
346 else {
347 /* Format 14 */
348 ARMword subset[4] = {
349 0xE92D0000, /* STMDB sp!,{rlist} */
350 0xE92D4000, /* STMDB sp!,{rlist,lr} */
351 0xE8BD0000, /* LDMIA sp!,{rlist} */
352 0xE8BD8000 /* LDMIA sp!,{rlist,pc} */
353 };
354 *ainstr = subset[((tinstr & (1 << 11)) >> 10) | ((tinstr & (1 << 8)) >> 8)] /* base */
355 |(tinstr & 0x00FF); /* mask8 */
356 }
357 break;
358 case 24: /* STMIA */
359 case 25: /* LDMIA */
360 /* Format 15 */
361 *ainstr = ((tinstr & (1 << 11)) /* base */
362 ? 0xE8B00000 /* LDMIA */
363 : 0xE8A00000) /* STMIA */
364 |((tinstr & 0x0700) << (16 - 8)) /* Rb */
365 |(tinstr & 0x00FF); /* mask8 */
366 break;
367 case 26: /* Bcc */
368 case 27: /* Bcc/SWI */
369 if ((tinstr & 0x0F00) == 0x0F00) {
370 if (tinstr == (ARMul_ABORTWORD & 0xffff) &&
371 state->AbortAddr == pc) {
372 *ainstr = ARMul_ABORTWORD;
373 break;
374 }
375 /* Format 17 : SWI */
376 *ainstr = 0xEF000000;
377 /* Breakpoint must be handled specially. */
378 if ((tinstr & 0x00FF) == 0x18)
379 *ainstr |= ((tinstr & 0x00FF) << 16);
380 /* New breakpoint value. See gdb/arm-tdep.c */
381 else if ((tinstr & 0x00FF) == 0xFE)
382 *ainstr |= SWI_Breakpoint;
383 else
384 *ainstr |= (tinstr & 0x00FF);
385 }
386 else if ((tinstr & 0x0F00) != 0x0E00) {
387 /* Format 16 */
388 int doit = FALSE;
389 /* TODO: Since we are doing a switch here, we could just add
390 the SWI and undefined instruction checks into this
391 switch to same on a couple of conditionals: */
392 switch ((tinstr & 0x0F00) >> 8) {
393 case EQ:
394 doit = ZFLAG;
395 break;
396 case NE:
397 doit = !ZFLAG;
398 break;
399 case VS:
400 doit = VFLAG;
401 break;
402 case VC:
403 doit = !VFLAG;
404 break;
405 case MI:
406 doit = NFLAG;
407 break;
408 case PL:
409 doit = !NFLAG;
410 break;
411 case CS:
412 doit = CFLAG;
413 break;
414 case CC:
415 doit = !CFLAG;
416 break;
417 case HI:
418 doit = (CFLAG && !ZFLAG);
419 break;
420 case LS:
421 doit = (!CFLAG || ZFLAG);
422 break;
423 case GE:
424 doit = ((!NFLAG && !VFLAG)
425 || (NFLAG && VFLAG));
426 break;
427 case LT:
428 doit = ((NFLAG && !VFLAG)
429 || (!NFLAG && VFLAG));
430 break;
431 case GT:
432 doit = ((!NFLAG && !VFLAG && !ZFLAG)
433 || (NFLAG && VFLAG && !ZFLAG));
434 break;
435 case LE:
436 doit = ((NFLAG && !VFLAG)
437 || (!NFLAG && VFLAG)) || ZFLAG;
438 break;
439 }
440 if (doit) {
441 state->Reg[15] = (pc + 4
442 + (((tinstr & 0x7F) << 1)
443 | ((tinstr & (1 << 7)) ?
444 0xFFFFFF00 : 0)));
445 FLUSHPIPE;
446 }
447 valid = t_branch;
448 }
449 else /* UNDEFINED : cc=1110(AL) uses different format */
450 valid = t_undefined;
451 break;
452 case 28: /* B */
453 /* Format 18 */
454 state->Reg[15] = (pc + 4 + (((tinstr & 0x3FF) << 1)
455 | ((tinstr & (1 << 10)) ?
456 0xFFFFF800 : 0)));
457 FLUSHPIPE;
458 valid = t_branch;
459 break;
460 case 29:
461 if(tinstr & 0x1)
462 valid = t_undefined;
463 else{
464 /* BLX 1 for armv5t and above */
465 ARMword tmp = (pc + 2);
466 state->Reg[15] =
467 (state->Reg[14] + ((tinstr & 0x07FF) << 1)) & 0xFFFFFFFC;
468 state->Reg[14] = (tmp | 1);
469 CLEART;
470 DEBUG_LOG(ARM11, "In %s, After BLX(1),LR=0x%x,PC=0x%x, offset=0x%x\n", __FUNCTION__, state->Reg[14], state->Reg[15], (tinstr &0x7FF) << 1);
471 valid = t_branch;
472 FLUSHPIPE;
473 }
474 break;
475 case 30: /* BL instruction 1 */
476 /* Format 19 */
477 /* There is no single ARM instruction equivalent for this Thumb
478 instruction. To keep the simulation simple (from the user
479 perspective) we check if the following instruction is the
480 second half of this BL, and if it is we simulate it
481 immediately. */
482 state->Reg[14] = state->Reg[15]
483 + (((tinstr & 0x07FF) << 12)
484 | ((tinstr & (1 << 10)) ? 0xFF800000 : 0));
485 valid = t_branch; /* in-case we don't have the 2nd half */
486 //tinstr = next_instr; /* move the instruction down */
487 //if (((tinstr & 0xF800) >> 11) != 31)
488 // break; /* exit, since not correct instruction */
489 /* else we fall through to process the second half of the BL */
490 //pc += 2; /* point the pc at the 2nd half */
491 state->Reg[15] = pc + 2;
492 FLUSHPIPE;
493 break;
494 case 31: /* BL instruction 2 */
495 /* Format 19 */
496 /* There is no single ARM instruction equivalent for this
497 instruction. Also, it should only ever be matched with the
498 fmt19 "BL instruction 1" instruction. However, we do allow
499 the simulation of it on its own, with undefined results if
500 r14 is not suitably initialised. */
501 {
502 ARMword tmp = (pc + 2);
503 state->Reg[15] =
504 (state->Reg[14] + ((tinstr & 0x07FF) << 1));
505 state->Reg[14] = (tmp | 1);
506 valid = t_branch;
507 FLUSHPIPE;
508 }
509 break;
510 }
511
512 return valid;
513}