1 /*****************************************************************************
2 *
3 * arm7core.c
4 * Portable ARM7TDMI Core Emulator
5 *
6 * Copyright Steve Ellenoff, all rights reserved.
7 *
8 * - This source code is released as freeware for non-commercial purposes.
9 * - You are free to use and redistribute this code in modified or
10 * unmodified form, provided you list me in the credits.
11 * - If you modify this source code, you must add a notice to each modified
12 * source file that it has been changed. If you're a nice person, you
13 * will clearly mark each change too. :)
14 * - If you wish to use this for commercial purposes, please contact me at
15 * sellenoff@hotmail.com
16 * - The author of this copywritten work reserves the right to change the
17 * terms of its usage and license at any time, including retroactively
18 * - This entire notice must remain in the source code.
19 *
20 * This work is based on:
21 * #1) 'Atmel Corporation ARM7TDMI (Thumb) Datasheet - January 1999'
22 * #2) Arm 2/3/6 emulator By Bryan McPhail (bmcphail@tendril.co.uk) and Phil Stroffolino (MAME CORE 0.76)
23 * #3) Thumb support by Ryan Holtz
24 * #4) Additional Thumb support and bugfixes by R. Belmont
25 *
26 *****************************************************************************/
27
28 /******************************************************************************
29 * Notes:
30
31 **This core comes from my AT91 cpu core contributed to PinMAME,
32 but with all the AT91 specific junk removed,
33 which leaves just the ARM7TDMI core itself. I further removed the CPU specific MAME stuff
34 so you just have the actual ARM7 core itself, since many cpu's incorporate an ARM7 core, but add on
35 many cpu specific functionality.
36
37 Therefore, to use the core, you simpy include this file along with the .h file into your own cpu specific
38 implementation, and therefore, this file shouldn't be compiled as part of your project directly.
39 Additionally, you will need to include arm7exec.c in your cpu's execute routine.
40
41 For better or for worse, the code itself is very much intact from it's arm 2/3/6 origins from
42 Bryan & Phil's work. I contemplated merging it in, but thought the fact that the CPSR is
43 no longer part of the PC was enough of a change to make it annoying to merge.
44 **
45
46 Coprocessor functions are heavily implementation specific, so callback handlers are used to allow the
47 implementation to handle the functionality. Custom DASM handlers are included as well to allow the DASM
48 output to be tailored to the co-proc implementation details.
49
50 Todo:
51 26 bit compatibility mode not implemented.
52 Data Processing opcodes need cycle count adjustments (see page 194 of ARM7TDMI manual for instruction timing summary)
53 Multi-emulated cpu support untested, but probably will not work too well, as no effort was made to code for more than 1.
54 Could not find info on what the TEQP opcode is from page 44..
55 I have no idea if user bank switching is right, as I don't fully understand it's use.
56 Search for Todo: tags for remaining items not done.
57
58
59 Differences from Arm 2/3 (6 also?)
60 -Thumb instruction support
61 -Full 32 bit address support
62 -PC no longer contains CPSR information, CPSR is own register now
63 -New register SPSR to store previous contents of CPSR (this register is banked in many modes)
64 -New opcodes for CPSR transfer, Long Multiplication, Co-Processor support, and some others
65 -User Bank Mode transfer using certain flags which were previously unallowed (LDM/STM with S Bit & R15)
66 -New operation modes? (unconfirmed)
67
68 Based heavily on arm core from MAME 0.76:
69 *****************************************
70 ARM 2/3/6 Emulation
71
72 Todo:
73 Software interrupts unverified (nothing uses them so far, but they should be ok)
74 Timing - Currently very approximated, nothing relies on proper timing so far.
75 IRQ timing not yet correct (again, nothing is affected by this so far).
76
77 By Bryan McPhail (bmcphail@tendril.co.uk) and Phil Stroffolino
78 *****************************************************************************/
79
80 #include <stdarg.h>
81 //#include "deprecat.h"
82
83 #define ARM7_DEBUG_CORE 0
84 #define logerror printf
85 #define fatalerror logerror
86
87 #if 0
88 #define LOG(x) mame_printf_debug x
89 #else
90 #define LOG(x) logerror x
91 #endif
92
93 #define ARM7_INLINE static inline
94
95 /* Prototypes */
96
97 // SJE: should these be inline? or are they too big to see any benefit?
98
99 static void HandleCoProcDO(UINT32 insn);
100 static void HandleCoProcRT(UINT32 insn);
101 static void HandleCoProcDT(UINT32 insn);
102 static void HandleHalfWordDT(UINT32 insn);
103 static void HandleSwap(UINT32 insn);
104 static void HandlePSRTransfer(UINT32 insn);
105 static void HandleALU(UINT32 insn);
106 static void HandleMul(UINT32 insn);
107 static void HandleUMulLong(UINT32 insn);
108 static void HandleSMulLong(UINT32 insn);
109 ARM7_INLINE void HandleBranch(UINT32 insn); // pretty short, so ARM7_INLINE should be ok
110 static void HandleMemSingle(UINT32 insn);
111 static void HandleMemBlock(UINT32 insn);
112 static UINT32 decodeShift(UINT32 insn, UINT32 *pCarry);
113 ARM7_INLINE void SwitchMode(int);
114 static void arm7_check_irq_state(void);
115
116 ARM7_INLINE void arm7_cpu_write32(UINT32 addr, UINT32 data);
117 ARM7_INLINE void arm7_cpu_write16(UINT32 addr, UINT16 data);
118 ARM7_INLINE void arm7_cpu_write8(UINT32 addr, UINT8 data);
119 ARM7_INLINE UINT32 arm7_cpu_read32(UINT32 addr);
120 ARM7_INLINE UINT16 arm7_cpu_read16(UINT32 addr);
121 ARM7_INLINE UINT8 arm7_cpu_read8(UINT32 addr);
122
123 /* Static Vars */
124 // Note: for multi-cpu implementation, this approach won't work w/o modification
125 void((*arm7_coproc_do_callback)(unsigned int, unsigned int)); // holder for the co processor Data Operations Callback func.
126 unsigned int((*arm7_coproc_rt_r_callback)(unsigned int)); // holder for the co processor Register Transfer Read Callback func.
127 void((*arm7_coproc_rt_w_callback)(unsigned int, unsigned int)); // holder for the co processor Register Transfer Write Callback Callback func.
128 // holder for the co processor Data Transfer Read & Write Callback funcs
129 void (*arm7_coproc_dt_r_callback)(UINT32 insn, UINT32 *prn, UINT32 (*read32)(UINT32 addr));
130 void (*arm7_coproc_dt_w_callback)(UINT32 insn, UINT32 *prn, void (*write32)(UINT32 addr, UINT32 data));
131
132
133 /***************************************************************************
134 * Default Memory Handlers
135 ***************************************************************************/
arm7_cpu_write32(UINT32 addr,UINT32 data)136 ARM7_INLINE void arm7_cpu_write32(UINT32 addr, UINT32 data)
137 {
138 addr &= ~3;
139 Arm7WriteLong(addr, data);
140 }
141
142
arm7_cpu_write16(UINT32 addr,UINT16 data)143 ARM7_INLINE void arm7_cpu_write16(UINT32 addr, UINT16 data)
144 {
145 addr &= ~1;
146 Arm7WriteWord(addr, data);
147 }
148
arm7_cpu_write8(UINT32 addr,UINT8 data)149 ARM7_INLINE void arm7_cpu_write8(UINT32 addr, UINT8 data)
150 {
151 Arm7WriteByte(addr, data);
152 }
153
arm7_cpu_read32(UINT32 addr)154 ARM7_INLINE UINT32 arm7_cpu_read32(UINT32 addr)
155 {
156 UINT32 result;
157
158 if (addr & 3)
159 {
160 result = Arm7ReadLong(addr & ~3);
161 result = (result >> (8 * (addr & 3))) | (result << (32 - (8 * (addr & 3))));
162 }
163 else
164 {
165 result = Arm7ReadLong(addr);
166 }
167
168 return result;
169 }
170
arm7_cpu_read16(UINT32 addr)171 ARM7_INLINE UINT16 arm7_cpu_read16(UINT32 addr)
172 {
173 UINT16 result;
174
175 result = Arm7ReadWord(addr & ~1);
176
177 if (addr & 1)
178 {
179 result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
180 }
181
182 return result;
183 }
184
arm7_cpu_read8(UINT32 addr)185 ARM7_INLINE UINT8 arm7_cpu_read8(UINT32 addr)
186 {
187 UINT8 result = Arm7ReadByte(addr);
188
189 // Handle through normal 8 bit handler (for 32 bit cpu)
190 return result;
191 }
192
cpu_readop32(UINT32 addr)193 ARM7_INLINE UINT32 cpu_readop32(UINT32 addr)
194 {
195 UINT32 result;
196
197 if (addr & 3)
198 {
199 result = Arm7FetchLong(addr & ~3);
200 result = (result >> (8 * (addr & 3))) | (result << (32 - (8 * (addr & 3))));
201 }
202 else
203 {
204 result = Arm7FetchLong(addr);
205 }
206
207 return result;
208 }
209
cpu_readop16(UINT32 addr)210 ARM7_INLINE UINT32 cpu_readop16(UINT32 addr)
211 {
212 UINT16 result;
213
214 result = Arm7FetchWord(addr & ~1);
215
216 if (addr & 1)
217 {
218 result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
219 }
220
221 return result;
222 }
223
224 /***************
225 * helper funcs
226 ***************/
227
228 // TODO LD:
229 // - SIGN_BITS_DIFFER = THUMB_SIGN_BITS_DIFFER
230 // - do while (0)
231 // - HandleALUAddFlags = HandleThumbALUAddFlags except for PC incr
232 // - HandleALUSubFlags = HandleThumbALUSubFlags except for PC incr
233
234 /* Set NZCV flags for ADDS / SUBS */
235 #define HandleALUAddFlags(rd, rn, op2) \
236 if (insn & INSN_S) \
237 SET_CPSR(((GET_CPSR & ~(N_MASK | Z_MASK | V_MASK | C_MASK)) \
238 | (((!SIGN_BITS_DIFFER(rn, op2)) && SIGN_BITS_DIFFER(rn, rd)) << V_BIT) \
239 | (((~(rn)) < (op2)) << C_BIT) \
240 | HandleALUNZFlags(rd))); \
241 R15 += 4;
242
243 #define HandleThumbALUAddFlags(rd, rn, op2) \
244 SET_CPSR(((GET_CPSR & ~(N_MASK | Z_MASK | V_MASK | C_MASK)) \
245 | (((!THUMB_SIGN_BITS_DIFFER(rn, op2)) && THUMB_SIGN_BITS_DIFFER(rn, rd)) << V_BIT) \
246 | (((~(rn)) < (op2)) << C_BIT) \
247 | HandleALUNZFlags(rd))); \
248 R15 += 2;
249
250 #define IsNeg(i) ((i) >> 31)
251 #define IsPos(i) ((~(i)) >> 31)
252
253 #define HandleALUSubFlags(rd, rn, op2) \
254 if (insn & INSN_S) \
255 SET_CPSR(((GET_CPSR & ~(N_MASK | Z_MASK | V_MASK | C_MASK)) \
256 | ((SIGN_BITS_DIFFER(rn, op2) && SIGN_BITS_DIFFER(rn, rd)) << V_BIT) \
257 | (((IsNeg(rn) & IsPos(op2)) | (IsNeg(rn) & IsPos(rd)) | (IsPos(op2) & IsPos(rd))) ? C_MASK : 0) \
258 | HandleALUNZFlags(rd))); \
259 R15 += 4;
260
261 #define HandleThumbALUSubFlags(rd, rn, op2) \
262 SET_CPSR(((GET_CPSR & ~(N_MASK | Z_MASK | V_MASK | C_MASK)) \
263 | ((THUMB_SIGN_BITS_DIFFER(rn, op2) && THUMB_SIGN_BITS_DIFFER(rn, rd)) << V_BIT) \
264 | (((IsNeg(rn) & IsPos(op2)) | (IsNeg(rn) & IsPos(rd)) | (IsPos(op2) & IsPos(rd))) ? C_MASK : 0) \
265 | HandleALUNZFlags(rd))); \
266 R15 += 2;
267
268 /* Set NZC flags for logical operations. */
269
270 // This macro (which I didn't write) - doesn't make it obvious that the SIGN BIT = 31, just as the N Bit does,
271 // therefore, N is set by default
272 #define HandleALUNZFlags(rd) \
273 (((rd) & SIGN_BIT) | ((!(rd)) << Z_BIT))
274
275
276 // Long ALU Functions use bit 63
277 #define HandleLongALUNZFlags(rd) \
278 ((((rd) & ((UINT64)1 << 63)) >> 32) | ((!(rd)) << Z_BIT))
279
280 #define HandleALULogicalFlags(rd, sc) \
281 if (insn & INSN_S) \
282 SET_CPSR(((GET_CPSR & ~(N_MASK | Z_MASK | C_MASK)) \
283 | HandleALUNZFlags(rd) \
284 | (((sc) != 0) << C_BIT))); \
285 R15 += 4;
286
287 // convert cpsr mode num into to text
288 static const char modetext[ARM7_NUM_MODES][5] = {
289 "USER", "FIRQ", "IRQ", "SVC", "ILL1", "ILL2", "ILL3", "ABT",
290 "ILL4", "ILL5", "ILL6", "UND", "ILL7", "ILL8", "ILL9", "SYS"
291 };
292
293 /*static const char *GetModeText(int cpsr)
294 {
295 return modetext[cpsr & MODE_FLAG];
296 }*/
297
298 // used to be functions, but no longer a need, so we'll use define for better speed.
299 #define GetRegister(rIndex) ARM7REG(sRegisterTable[GET_MODE][rIndex])
300 #define SetRegister(rIndex, value) ARM7REG(sRegisterTable[GET_MODE][rIndex]) = value
301
302 // I could prob. convert to macro, but Switchmode shouldn't occur that often in emulated code..
SwitchMode(int cpsr_mode_val)303 ARM7_INLINE void SwitchMode(int cpsr_mode_val)
304 {
305 UINT32 cspr = GET_CPSR & ~MODE_FLAG;
306 SET_CPSR(cspr | cpsr_mode_val);
307 }
308
309
310 /* Decodes an Op2-style shifted-register form. If @carry@ is non-zero the
311 * shifter carry output will manifest itself as @*carry == 0@ for carry clear
312 * and @*carry != 0@ for carry set.
313
314 SJE: Rules:
315 IF RC = 256, Result = no shift.
316 LSL 0 = Result = RM, Carry = Old Contents of CPSR C Bit
317 LSL(0,31) = Result shifted, least significant bit is in carry out
318 LSL 32 = Result of 0, Carry = Bit 0 of RM
319 LSL >32 = Result of 0, Carry out 0
320 LSR 0 = LSR 32 (see below)
321 LSR 32 = Result of 0, Carry = Bit 31 of RM
322 LSR >32 = Result of 0, Carry out 0
323 ASR >=32 = ENTIRE Result = bit 31 of RM
324 ROR 32 = Result = RM, Carry = Bit 31 of RM
325 ROR >32 = Same result as ROR n-32 until amount in range of 1-32 then follow rules
326 */
327
decodeShift(UINT32 insn,UINT32 * pCarry)328 static UINT32 decodeShift(UINT32 insn, UINT32 *pCarry)
329 {
330 UINT32 k = (insn & INSN_OP2_SHIFT) >> INSN_OP2_SHIFT_SHIFT; // Bits 11-7
331 UINT32 rm = GET_REGISTER(insn & INSN_OP2_RM);
332 UINT32 t = (insn & INSN_OP2_SHIFT_TYPE) >> INSN_OP2_SHIFT_TYPE_SHIFT;
333
334 if ((insn & INSN_OP2_RM) == 0xf) {
335 rm += 8;
336 }
337
338 /* All shift types ending in 1 are Rk, not #k */
339 if (t & 1)
340 {
341 // LOG(("%08x: RegShift %02x %02x\n", R15, k >> 1, GET_REGISTER(k >> 1)));
342 #if ARM7_DEBUG_CORE
343 if ((insn & 0x80) == 0x80)
344 LOG(("%08x: RegShift ERROR (p36)\n", R15));
345 #endif
346
347 // see p35 for check on this
348 //k = GET_REGISTER(k >> 1) & 0x1f;
349
350 // Keep only the bottom 8 bits for a Register Shift
351 k = GET_REGISTER(k >> 1) & 0xff;
352
353 if (k == 0) /* Register shift by 0 is a no-op */
354 {
355 // LOG(("%08x: NO-OP Regshift\n", R15));
356 /* TODO this is wrong for at least ROR by reg with lower
357 * 5 bits 0 but lower 8 bits non zero */
358 if (pCarry)
359 *pCarry = GET_CPSR & C_MASK;
360 return rm;
361 }
362 }
363 /* Decode the shift type and perform the shift */
364 switch (t >> 1)
365 {
366 case 0: /* LSL */
367 // LSL 32 = Result of 0, Carry = Bit 0 of RM
368 // LSL >32 = Result of 0, Carry out 0
369 if (k >= 32)
370 {
371 if (pCarry)
372 *pCarry = (k == 32) ? rm & 1 : 0;
373 return 0;
374 }
375 else
376 {
377 if (pCarry)
378 {
379 // LSL 0 = Result = RM, Carry = Old Contents of CPSR C Bit
380 // LSL (0,31) = Result shifted, least significant bit is in carry out
381 *pCarry = k ? (rm & (1 << (32 - k))) : (GET_CPSR & C_MASK);
382 }
383 return k ? LSL(rm, k) : rm;
384 }
385 break;
386
387 case 1: /* LSR */
388 if (k == 0 || k == 32)
389 {
390 if (pCarry)
391 *pCarry = rm & SIGN_BIT;
392 return 0;
393 }
394 else if (k > 32)
395 {
396 if (pCarry)
397 *pCarry = 0;
398 return 0;
399 }
400 else
401 {
402 if (pCarry)
403 *pCarry = (rm & (1 << (k - 1)));
404 return LSR(rm, k);
405 }
406 break;
407
408 case 2: /* ASR */
409 if (k == 0 || k > 32)
410 k = 32;
411
412 if (pCarry)
413 *pCarry = (rm & (1 << (k - 1)));
414 if (k >= 32)
415 return rm & SIGN_BIT ? 0xffffffffu : 0;
416 else
417 {
418 if (rm & SIGN_BIT)
419 return LSR(rm, k) | (0xffffffffu << (32 - k));
420 else
421 return LSR(rm, k);
422 }
423 break;
424
425 case 3: /* ROR and RRX */
426 if (k)
427 {
428 while (k > 32)
429 k -= 32;
430 if (pCarry)
431 *pCarry = rm & (1 << (k - 1));
432 return ROR(rm, k);
433 }
434 else
435 {
436 /* RRX */
437 if (pCarry)
438 *pCarry = (rm & 1);
439 return LSR(rm, 1) | ((GET_CPSR & C_MASK) << 2);
440 }
441 break;
442 }
443
444 LOG(("%08x: Decodeshift error\n", R15));
445 return 0;
446 } /* decodeShift */
447
448
loadInc(UINT32 pat,UINT32 rbv,UINT32 s)449 static int loadInc(UINT32 pat, UINT32 rbv, UINT32 s)
450 {
451 int i, result;
452
453 result = 0;
454 rbv &= ~3;
455 for (i = 0; i < 16; i++)
456 {
457 if ((pat >> i) & 1)
458 {
459 if (i == 15) {
460 if (s) /* Pull full contents from stack */
461 SET_REGISTER(15, READ32(rbv += 4));
462 else /* Pull only address, preserve mode & status flags */
463 SET_REGISTER(15, READ32(rbv += 4));
464 } else
465 SET_REGISTER(i, READ32(rbv += 4));
466
467 result++;
468 }
469 }
470 return result;
471 }
472
loadDec(UINT32 pat,UINT32 rbv,UINT32 s)473 static int loadDec(UINT32 pat, UINT32 rbv, UINT32 s)
474 {
475 int i, result;
476
477 result = 0;
478 rbv &= ~3;
479 for (i = 15; i >= 0; i--)
480 {
481 if ((pat >> i) & 1)
482 {
483 if (i == 15) {
484 if (s) /* Pull full contents from stack */
485 SET_REGISTER(15, READ32(rbv -= 4));
486 else /* Pull only address, preserve mode & status flags */
487 SET_REGISTER(15, READ32(rbv -= 4));
488 }
489 else
490 SET_REGISTER(i, READ32(rbv -= 4));
491 result++;
492 }
493 }
494 return result;
495 }
496
storeInc(UINT32 pat,UINT32 rbv)497 static int storeInc(UINT32 pat, UINT32 rbv)
498 {
499 int i, result;
500
501 result = 0;
502 for (i = 0; i < 16; i++)
503 {
504 if ((pat >> i) & 1)
505 {
506 #if ARM7_DEBUG_CORE
507 if (i == 15) /* R15 is plus 12 from address of STM */
508 LOG(("%08x: StoreInc on R15\n", R15));
509 #endif
510 WRITE32(rbv += 4, GET_REGISTER(i));
511 result++;
512 }
513 }
514 return result;
515 } /* storeInc */
516
storeDec(UINT32 pat,UINT32 rbv)517 static int storeDec(UINT32 pat, UINT32 rbv)
518 {
519 int i, result;
520
521 result = 0;
522 for (i = 15; i >= 0; i--)
523 {
524 if ((pat >> i) & 1)
525 {
526 #if ARM7_DEBUG_CORE
527 if (i == 15) /* R15 is plus 12 from address of STM */
528 LOG(("%08x: StoreDec on R15\n", R15));
529 #endif
530 WRITE32(rbv -= 4, GET_REGISTER(i));
531 result++;
532 }
533 }
534 return result;
535 } /* storeDec */
536
537 /***************************************************************************
538 * Main CPU Funcs
539 ***************************************************************************/
540
541 // CPU INIT
542 #if 0
arm7_core_init(const char * cpuname,int index)543 static void arm7_core_init(const char *cpuname, int index)
544 {
545 state_save_register_item_array(cpuname, index, ARM7.sArmRegister);
546 state_save_register_item(cpuname, index, ARM7.pendingIrq);
547 state_save_register_item(cpuname, index, ARM7.pendingFiq);
548 state_save_register_item(cpuname, index, ARM7.pendingAbtD);
549 state_save_register_item(cpuname, index, ARM7.pendingAbtP);
550 state_save_register_item(cpuname, index, ARM7.pendingUnd);
551 state_save_register_item(cpuname, index, ARM7.pendingSwi);
552 }
553 #endif
554
555 // CPU RESET
arm7_core_reset(void)556 static void arm7_core_reset(void)
557 {
558 memset(&ARM7, 0, sizeof(ARM7));
559
560 /* start up in SVC mode with interrupts disabled. */
561 SwitchMode(eARM7_MODE_SVC);
562 SET_CPSR(GET_CPSR | I_MASK | F_MASK | 0x10);
563 R15 = 0;
564 // change_pc(R15);
565 }
566
567 // Execute used to be here.. moved to separate file (arm7exec.c) to be included by cpu cores separately
568
569 // CPU CHECK IRQ STATE
570 // Note: couldn't find any exact cycle counts for most of these exceptions
arm7_check_irq_state(void)571 static void arm7_check_irq_state(void)
572 {
573 UINT32 cpsr = GET_CPSR; /* save current CPSR */
574 UINT32 pc = R15 + 4; /* save old pc (already incremented in pipeline) */;
575
576 /* Exception priorities:
577
578 Reset
579 Data abort
580 FIRQ
581 IRQ
582 Prefetch abort
583 Undefined instruction
584 Software Interrupt
585 */
586
587 // Data Abort
588 if (ARM7.pendingAbtD) {
589 SwitchMode(eARM7_MODE_ABT); /* Set ABT mode so PC is saved to correct R14 bank */
590 SET_REGISTER(14, pc); /* save PC to R14 */
591 SET_REGISTER(SPSR, cpsr); /* Save current CPSR */
592 SET_CPSR(GET_CPSR | I_MASK); /* Mask IRQ */
593 SET_CPSR(GET_CPSR & ~T_MASK);
594 R15 = 0x10; /* IRQ Vector address */
595 // change_pc(R15);
596 ARM7.pendingAbtD = 0;
597 return;
598 }
599
600 // FIQ
601 if (ARM7.pendingFiq && (cpsr & F_MASK) == 0) {
602 SwitchMode(eARM7_MODE_FIQ); /* Set FIQ mode so PC is saved to correct R14 bank */
603 SET_REGISTER(14, pc); /* save PC to R14 */
604 SET_REGISTER(SPSR, cpsr); /* Save current CPSR */
605 SET_CPSR(GET_CPSR | I_MASK | F_MASK); /* Mask both IRQ & FIQ */
606 SET_CPSR(GET_CPSR & ~T_MASK);
607 R15 = 0x1c; /* IRQ Vector address */
608 // change_pc(R15);
609 return;
610 }
611
612 // IRQ
613 if (ARM7.pendingIrq && (cpsr & I_MASK) == 0) {
614 SwitchMode(eARM7_MODE_IRQ); /* Set IRQ mode so PC is saved to correct R14 bank */
615 SET_REGISTER(14, pc); /* save PC to R14 */
616 SET_REGISTER(SPSR, cpsr); /* Save current CPSR */
617 SET_CPSR(GET_CPSR | I_MASK); /* Mask IRQ */
618 SET_CPSR(GET_CPSR & ~T_MASK);
619 R15 = 0x18; /* IRQ Vector address */
620 // change_pc(R15);
621 return;
622 }
623
624 // Prefetch Abort
625 if (ARM7.pendingAbtP) {
626 SwitchMode(eARM7_MODE_ABT); /* Set ABT mode so PC is saved to correct R14 bank */
627 SET_REGISTER(14, pc); /* save PC to R14 */
628 SET_REGISTER(SPSR, cpsr); /* Save current CPSR */
629 SET_CPSR(GET_CPSR | I_MASK); /* Mask IRQ */
630 SET_CPSR(GET_CPSR & ~T_MASK);
631 R15 = 0x0c; /* IRQ Vector address */
632 // change_pc(R15);
633 ARM7.pendingAbtP = 0;
634 return;
635 }
636
637 // Undefined instruction
638 if (ARM7.pendingUnd) {
639 SwitchMode(eARM7_MODE_UND); /* Set UND mode so PC is saved to correct R14 bank */
640 SET_REGISTER(14, pc); /* save PC to R14 */
641 SET_REGISTER(SPSR, cpsr); /* Save current CPSR */
642 SET_CPSR(GET_CPSR | I_MASK); /* Mask IRQ */
643 SET_CPSR(GET_CPSR & ~T_MASK);
644 R15 = 0x04; /* IRQ Vector address */
645 // change_pc(R15);
646 ARM7.pendingUnd = 0;
647 return;
648 }
649
650 // Software Interrupt
651 if (ARM7.pendingSwi) {
652 SwitchMode(eARM7_MODE_SVC); /* Set SVC mode so PC is saved to correct R14 bank */
653 // compensate for prefetch (should this also be done for normal IRQ?)
654 if (T_IS_SET(GET_CPSR))
655 {
656 SET_REGISTER(14, pc-2); /* save PC to R14 */
657 }
658 else
659 {
660 SET_REGISTER(14, pc); /* save PC to R14 */
661 }
662 SET_REGISTER(SPSR, cpsr); /* Save current CPSR */
663 SET_CPSR(GET_CPSR | I_MASK); /* Mask IRQ */
664 SET_CPSR(GET_CPSR & ~T_MASK); /* Go to ARM mode */
665 R15 = 0x08; /* Jump to the SWI vector */
666 // change_pc(R15);
667 ARM7.pendingSwi = 0;
668 return;
669 }
670 }
671
672 // CPU - SET IRQ LINE
arm7_core_set_irq_line(int irqline,int state)673 static void arm7_core_set_irq_line(int irqline, int state)
674 {
675 switch (irqline) {
676
677 case ARM7_IRQ_LINE: /* IRQ */
678 ARM7.pendingIrq = state & 1;
679 break;
680
681 case ARM7_FIRQ_LINE: /* FIRQ */
682 ARM7.pendingFiq = state & 1;
683 break;
684
685 case ARM7_ABORT_EXCEPTION:
686 ARM7.pendingAbtD = state & 1;
687 break;
688 case ARM7_ABORT_PREFETCH_EXCEPTION:
689 ARM7.pendingAbtP = state & 1;
690 break;
691
692 case ARM7_UNDEFINE_EXCEPTION:
693 ARM7.pendingUnd = state & 1;
694 break;
695 }
696
697 ARM7_CHECKIRQ;
698 }
699
700 /***************************************************************************
701 * OPCODE HANDLING
702 ***************************************************************************/
703
704 // Co-Processor Data Operation
HandleCoProcDO(UINT32 insn)705 static void HandleCoProcDO(UINT32 insn)
706 {
707 // This instruction simply instructs the co-processor to do something, no data is returned to ARM7 core
708 if (arm7_coproc_do_callback)
709 arm7_coproc_do_callback(0, insn); // simply pass entire opcode to callback - since data format is actually dependent on co-proc implementation
710 else
711 LOG(("%08x: Co-Processor Data Operation executed, but no callback defined!\n", R15));
712 }
713
714 // Co-Processor Register Transfer - To/From Arm to Co-Proc
HandleCoProcRT(UINT32 insn)715 static void HandleCoProcRT(UINT32 insn)
716 {
717
718 /* xxxx 1110 oooL nnnn dddd cccc ppp1 mmmm */
719
720 // Load (MRC) data from Co-Proc to ARM7 register
721 if (insn & 0x00100000) // Bit 20 = Load or Store
722 {
723 if (arm7_coproc_rt_r_callback)
724 {
725 UINT32 res = arm7_coproc_rt_r_callback(insn); // RT Read handler must parse opcode & return appropriate result
726 SET_REGISTER((insn >> 12) & 0xf, res);
727 }
728 else
729 LOG(("%08x: Co-Processor Register Transfer executed, but no RT Read callback defined!\n", R15));
730 }
731 // Store (MCR) data from ARM7 to Co-Proc register
732 else
733 {
734 if (arm7_coproc_rt_r_callback)
735 ; // arm7_coproc_rt_w_callback(insn, GET_REGISTER((insn >> 12) & 0xf), 0);
736 else
737 LOG(("%08x: Co-Processor Register Transfer executed, but no RT Write callback defined!\n", R15));
738 }
739 }
740
741 /* Data Transfer - To/From Arm to Co-Proc
742 Loading or Storing, the co-proc function is responsible to read/write from the base register supplied + offset
743 8 bit immediate value Base Offset address is << 2 to get the actual #
744
745 issues - #1 - the co-proc function, needs direct access to memory reads or writes (ie, so we must send a pointer to a func)
746 - #2 - the co-proc may adjust the base address (especially if it reads more than 1 word), so a pointer to the register must be used
747 but the old value of the register must be restored if write back is not set..
748 - #3 - when post incrementing is used, it's up to the co-proc func. to add the offset, since the transfer
749 address supplied in that case, is simply the base. I suppose this is irrelevant if write back not set
750 but if co-proc reads multiple address, it must handle the offset adjustment itself.
751 */
752 // todo: test with valid instructions
HandleCoProcDT(UINT32 insn)753 static void HandleCoProcDT(UINT32 insn)
754 {
755 UINT32 rn = (insn >> 16) & 0xf;
756 UINT32 rnv = GET_REGISTER(rn); // Get Address Value stored from Rn
757 UINT32 ornv = rnv; // Keep value of Rn
758 UINT32 off = (insn & 0xff) << 2; // Offset is << 2 according to manual
759 UINT32 *prn = &ARM7REG(rn); // Pointer to our register, so it can be changed in the callback
760
761 // Pointers to read32/write32 functions
762 void (*write32)(UINT32 addr, UINT32 data);
763 UINT32 (*read32)(UINT32 addr);
764 write32 = PTR_WRITE32;
765 read32 = PTR_READ32;
766
767 #if ARM7_DEBUG_CORE
768 if (((insn >> 16) & 0xf) == 15 && (insn & 0x200000))
769 LOG(("%08x: Illegal use of R15 as base for write back value!\n", R15));
770 #endif
771
772 // Pre-Increment base address (IF POST INCREMENT - CALL BACK FUNCTION MUST DO IT)
773 if ((insn & 0x1000000) && off)
774 {
775 // Up - Down bit
776 if (insn & 0x800000)
777 rnv += off;
778 else
779 rnv -= off;
780 }
781
782 // Load (LDC) data from ARM7 memory to Co-Proc memory
783 if (insn & 0x00100000)
784 {
785 if (arm7_coproc_dt_r_callback)
786 arm7_coproc_dt_r_callback(insn, prn, read32);
787 else
788 LOG(("%08x: Co-Processer Data Transfer executed, but no READ callback defined!\n", R15));
789 }
790 // Store (STC) data from Co-Proc to ARM7 memory
791 else
792 {
793 if (arm7_coproc_dt_w_callback)
794 arm7_coproc_dt_w_callback(insn, prn, write32);
795 else
796 LOG(("%08x: Co-Processer Data Transfer executed, but no WRITE callback defined!\n", R15));
797 }
798
799 // If writeback not used - ensure the original value of RN is restored in case co-proc callback changed value
800 if ((insn & 0x200000) == 0)
801 SET_REGISTER(rn, ornv);
802 }
803
HandleBranch(UINT32 insn)804 static void HandleBranch(UINT32 insn)
805 {
806 UINT32 off = (insn & INSN_BRANCH) << 2;
807
808 /* Save PC into LR if this is a branch with link */
809 if (insn & INSN_BL)
810 {
811 SET_REGISTER(14, R15 + 4);
812 }
813
814 /* Sign-extend the 24-bit offset in our calculations */
815 if (off & 0x2000000u)
816 {
817 R15 -= ((~(off | 0xfc000000u)) + 1) - 8;
818 }
819 else
820 {
821 R15 += off + 8;
822 }
823
824 // change_pc(R15);
825 }
826
HandleMemSingle(UINT32 insn)827 static void HandleMemSingle(UINT32 insn)
828 {
829 UINT32 rn, rnv, off, rd;
830
831 /* Fetch the offset */
832 if (insn & INSN_I)
833 {
834 /* Register Shift */
835 off = decodeShift(insn, NULL);
836 }
837 else
838 {
839 /* Immediate Value */
840 off = insn & INSN_SDT_IMM;
841 }
842
843 /* Calculate Rn, accounting for PC */
844 rn = (insn & INSN_RN) >> INSN_RN_SHIFT;
845
846 if (insn & INSN_SDT_P)
847 {
848 /* Pre-indexed addressing */
849 if (insn & INSN_SDT_U)
850 {
851 rnv = (GET_REGISTER(rn) + off);
852 }
853 else
854 {
855 rnv = (GET_REGISTER(rn) - off);
856 }
857
858 if (insn & INSN_SDT_W)
859 {
860 SET_REGISTER(rn, rnv);
861
862 // check writeback???
863 }
864 else if (rn == eR15)
865 {
866 rnv = rnv + 8;
867 }
868 }
869 else
870 {
871 /* Post-indexed addressing */
872 if (rn == eR15)
873 {
874 rnv = R15 + 8;
875 }
876 else
877 {
878 rnv = GET_REGISTER(rn);
879 }
880 }
881
882 /* Do the transfer */
883 rd = (insn & INSN_RD) >> INSN_RD_SHIFT;
884 if (insn & INSN_SDT_L)
885 {
886 /* Load */
887 if (insn & INSN_SDT_B)
888 {
889 SET_REGISTER(rd, (UINT32)READ8(rnv));
890 }
891 else
892 {
893 if (rd == eR15)
894 {
895 R15 = READ32(rnv);
896 R15 -= 4;
897 // change_pc(R15);
898 // LDR, PC takes 2S + 2N + 1I (5 total cycles)
899 ARM7_ICOUNT -= 2;
900 }
901 else
902 {
903 SET_REGISTER(rd, READ32(rnv));
904 }
905 }
906 }
907 else
908 {
909 /* Store */
910 if (insn & INSN_SDT_B)
911 {
912 #if ARM7_DEBUG_CORE
913 if (rd == eR15)
914 LOG(("Wrote R15 in byte mode\n"));
915 #endif
916
917 WRITE8(rnv, (UINT8) GET_REGISTER(rd) & 0xffu);
918 }
919 else
920 {
921 #if ARM7_DEBUG_CORE
922 if (rd == eR15)
923 LOG(("Wrote R15 in 32bit mode\n"));
924 #endif
925
926 //WRITE32(rnv, rd == eR15 ? R15 + 8 : GET_REGISTER(rd));
927 WRITE32(rnv, rd == eR15 ? R15 + 8 + 4 : GET_REGISTER(rd)); // manual says STR rd = PC, +12
928 }
929 // Store takes only 2 N Cycles, so add + 1
930 ARM7_ICOUNT += 1;
931 }
932
933 /* Do post-indexing writeback */
934 if (!(insn & INSN_SDT_P)/* && (insn & INSN_SDT_W)*/)
935 {
936 if (insn & INSN_SDT_U)
937 {
938 /* Writeback is applied in pipeline, before value is read from mem,
939 so writeback is effectively ignored */
940 if (rd == rn) {
941 SET_REGISTER(rn, GET_REGISTER(rd));
942 // todo: check for offs... ?
943 }
944 else {
945
946 if ((insn & INSN_SDT_W) != 0)
947 LOG(("%08x: RegisterWritebackIncrement %d %d %d\n", R15, (insn & INSN_SDT_P) != 0, (insn & INSN_SDT_W) != 0, (insn & INSN_SDT_U) != 0));
948
949 SET_REGISTER(rn, (rnv + off));
950 }
951 }
952 else
953 {
954 /* Writeback is applied in pipeline, before value is read from mem,
955 so writeback is effectively ignored */
956 if (rd == rn) {
957 SET_REGISTER(rn, GET_REGISTER(rd));
958 }
959 else {
960 SET_REGISTER(rn, (rnv - off));
961
962 if ((insn & INSN_SDT_W) != 0)
963 LOG(("%08x: RegisterWritebackDecrement %d %d %d\n", R15, (insn & INSN_SDT_P) != 0, (insn & INSN_SDT_W) != 0, (insn & INSN_SDT_U) != 0));
964 }
965 }
966 }
967
968 // ARM7_CHECKIRQ
969
970 } /* HandleMemSingle */
971
HandleHalfWordDT(UINT32 insn)972 static void HandleHalfWordDT(UINT32 insn)
973 {
974 UINT32 rn, rnv, off, rd;
975
976 // Immediate or Register Offset?
977 if (insn & 0x400000) { // Bit 22 - 1 = immediate, 0 = register
978 // imm. value in high nibble (bits 8-11) and lo nibble (bit 0-3)
979 off = (((insn >> 8) & 0x0f) << 4) | (insn & 0x0f);
980 }
981 else {
982 // register
983 off = GET_REGISTER(insn & 0x0f);
984 }
985
986 /* Calculate Rn, accounting for PC */
987 rn = (insn & INSN_RN) >> INSN_RN_SHIFT;
988
989 if (insn & INSN_SDT_P)
990 {
991 /* Pre-indexed addressing */
992 if (insn & INSN_SDT_U)
993 {
994 rnv = (GET_REGISTER(rn) + off);
995 }
996 else
997 {
998 rnv = (GET_REGISTER(rn) - off);
999 }
1000
1001 if (insn & INSN_SDT_W)
1002 {
1003 SET_REGISTER(rn, rnv);
1004
1005 // check writeback???
1006 }
1007 else if (rn == eR15)
1008 {
1009 rnv = (rnv) + 8;
1010 }
1011 }
1012 else
1013 {
1014 /* Post-indexed addressing */
1015 if (rn == eR15)
1016 {
1017 rnv = R15 + 8;
1018 }
1019 else
1020 {
1021 rnv = GET_REGISTER(rn);
1022 }
1023 }
1024
1025 /* Do the transfer */
1026 rd = (insn & INSN_RD) >> INSN_RD_SHIFT;
1027
1028 /* Load */
1029 if (insn & INSN_SDT_L)
1030 {
1031 // Signed?
1032 if (insn & 0x40)
1033 {
1034 UINT32 newval = 0;
1035
1036 // Signed Half Word?
1037 if (insn & 0x20) {
1038 UINT16 signbyte, databyte;
1039 databyte = READ16(rnv) & 0xFFFF;
1040 signbyte = (databyte & 0x8000) ? 0xffff : 0;
1041 newval = (UINT32)(signbyte << 16)|databyte;
1042 }
1043 // Signed Byte
1044 else {
1045 UINT8 databyte;
1046 UINT32 signbyte;
1047 databyte = READ8(rnv) & 0xff;
1048 signbyte = (databyte & 0x80) ? 0xffffff : 0;
1049 newval = (UINT32)(signbyte << 8)|databyte;
1050 }
1051
1052 // PC?
1053 if (rd == eR15)
1054 {
1055 R15 = newval + 8;
1056 // LDR(H,SH,SB) PC takes 2S + 2N + 1I (5 total cycles)
1057 ARM7_ICOUNT -= 2;
1058
1059 }
1060 else
1061 {
1062 SET_REGISTER(rd, newval);
1063 R15 += 4;
1064 }
1065 }
1066 // Unsigned Half Word
1067 else
1068 {
1069 if (rd == eR15)
1070 {
1071 R15 = READ16(rnv) + 8;
1072 }
1073 else
1074 {
1075 SET_REGISTER(rd, READ16(rnv));
1076 R15 += 4;
1077 }
1078 }
1079
1080
1081 }
1082 /* Store */
1083 else
1084 {
1085 // WRITE16(rnv, rd == eR15 ? R15 + 8 : GET_REGISTER(rd));
1086 WRITE16(rnv, rd == eR15 ? R15 + 8 + 4 : GET_REGISTER(rd)); // manual says STR RD=PC, +12 of address
1087 if (rn != eR15)
1088 R15 += 4;
1089 // STRH takes 2 cycles, so we add + 1
1090 ARM7_ICOUNT += 1;
1091 }
1092
1093
1094
1095 // SJE: No idea if this writeback code works or makes sense here..
1096
1097 /* Do post-indexing writeback */
1098 if (!(insn & INSN_SDT_P)/* && (insn & INSN_SDT_W)*/)
1099 {
1100 if (insn & INSN_SDT_U)
1101 {
1102 /* Writeback is applied in pipeline, before value is read from mem,
1103 so writeback is effectively ignored */
1104 if (rd == rn) {
1105 SET_REGISTER(rn, GET_REGISTER(rd));
1106 // todo: check for offs... ?
1107 }
1108 else {
1109
1110 if ((insn & INSN_SDT_W) != 0)
1111 LOG(("%08x: RegisterWritebackIncrement %d %d %d\n", R15, (insn & INSN_SDT_P) != 0, (insn & INSN_SDT_W) != 0, (insn & INSN_SDT_U) != 0));
1112
1113 SET_REGISTER(rn, (rnv + off));
1114 }
1115 }
1116 else
1117 {
1118 /* Writeback is applied in pipeline, before value is read from mem,
1119 so writeback is effectively ignored */
1120 if (rd == rn) {
1121 SET_REGISTER(rn, GET_REGISTER(rd));
1122 }
1123 else {
1124 SET_REGISTER(rn, (rnv - off));
1125
1126 if ((insn & INSN_SDT_W) != 0)
1127 LOG(("%08x: RegisterWritebackDecrement %d %d %d\n", R15, (insn & INSN_SDT_P) != 0, (insn & INSN_SDT_W) != 0, (insn & INSN_SDT_U) != 0));
1128 }
1129 }
1130 }
1131 // change_pc(R15);
1132 }
1133
HandleSwap(UINT32 insn)1134 static void HandleSwap(UINT32 insn)
1135 {
1136 UINT32 rn, rm, rd, tmp;
1137
1138 rn = GET_REGISTER((insn >> 16) & 0xf); // reg. w/read address
1139 rm = GET_REGISTER(insn & 0xf); // reg. w/write address
1140 rd = (insn >> 12) & 0xf; // dest reg
1141
1142 #if ARM7_DEBUG_CORE
1143 if (rn == 15 || rm == 15 || rd == 15)
1144 LOG(("%08x: Illegal use of R15 in Swap Instruction\n", R15));
1145 #endif
1146
1147 // can be byte or word
1148 if (insn & 0x400000)
1149 {
1150 tmp = READ8(rn);
1151 WRITE8(rn, rm);
1152 SET_REGISTER(rd, tmp);
1153 }
1154 else
1155 {
1156 tmp = READ32(rn);
1157 WRITE32(rn, rm);
1158 SET_REGISTER(rd, tmp);
1159 }
1160
1161 R15 += 4;
1162 // Instruction takes 1S+2N+1I cycles - so we subtract one more..
1163 ARM7_ICOUNT -= 1;
1164 }
1165
HandlePSRTransfer(UINT32 insn)1166 static void HandlePSRTransfer(UINT32 insn)
1167 {
1168 int reg = (insn & 0x400000) ? SPSR : eCPSR; // Either CPSR or SPSR
1169 UINT32 newval, val = 0;
1170 UINT32 oldmode = GET_CPSR & MODE_FLAG;
1171
1172 // get old value of CPSR/SPSR
1173 newval = GET_REGISTER(reg);
1174
1175 // MSR (bit 21 set) - Copy value to CPSR/SPSR
1176 if ((insn & 0x00200000))
1177 {
1178 // Immediate Value?
1179 if (insn & INSN_I) {
1180 // Value can be specified for a Right Rotate, 2x the value specified.
1181 int by = (insn & INSN_OP2_ROTATE) >> INSN_OP2_ROTATE_SHIFT;
1182 if (by)
1183 val = ROR(insn & INSN_OP2_IMM, by << 1);
1184 else
1185 val = insn & INSN_OP2_IMM;
1186 }
1187 // Value from Register
1188 else
1189 {
1190 val = GET_REGISTER(insn & 0x0f);
1191 }
1192
1193 // apply field code bits
1194 if (reg == eCPSR)
1195 {
1196 if (oldmode != eARM7_MODE_USER)
1197 {
1198 if (insn & 0x00010000)
1199 {
1200 newval = (newval & 0xffffff00) | (val & 0x000000ff);
1201 }
1202 if (insn & 0x00020000)
1203 {
1204 newval = (newval & 0xffff00ff) | (val & 0x0000ff00);
1205 }
1206 if (insn & 0x00040000)
1207 {
1208 newval = (newval & 0xff00ffff) | (val & 0x00ff0000);
1209 }
1210 }
1211
1212 // status flags can be modified regardless of mode
1213 if (insn & 0x00080000)
1214 {
1215 // TODO for non ARMv5E mask should be 0xf0000000 (ie mask Q bit)
1216 newval = (newval & 0x00ffffff) | (val & 0xf8000000);
1217 }
1218 }
1219 else // SPSR has stricter requirements
1220 {
1221 if (((GET_CPSR & 0x1f) > 0x10) && ((GET_CPSR & 0x1f) < 0x1f))
1222 {
1223 if (insn & 0x00010000)
1224 {
1225 newval = (newval & 0xffffff00) | (val & 0xff);
1226 }
1227 if (insn & 0x00020000)
1228 {
1229 newval = (newval & 0xffff00ff) | (val & 0xff00);
1230 }
1231 if (insn & 0x00040000)
1232 {
1233 newval = (newval & 0xff00ffff) | (val & 0xff0000);
1234 }
1235 if (insn & 0x00080000)
1236 {
1237 // TODO for non ARMv5E mask should be 0xf0000000 (ie mask Q bit)
1238 newval = (newval & 0x00ffffff) | (val & 0xf8000000);
1239 }
1240 }
1241 }
1242
1243 // force valid mode
1244 newval |= 0x10;
1245
1246 // Update the Register
1247 SET_REGISTER(reg, newval);
1248
1249 // Switch to new mode if changed
1250 if ((newval & MODE_FLAG) != oldmode)
1251 SwitchMode(GET_MODE);
1252
1253 }
1254 // MRS (bit 21 clear) - Copy CPSR or SPSR to specified Register
1255 else
1256 {
1257 SET_REGISTER((insn >> 12)& 0x0f, GET_REGISTER(reg));
1258 }
1259 }
1260
HandleALU(UINT32 insn)1261 static void HandleALU(UINT32 insn)
1262 {
1263 UINT32 op2, sc = 0, rd, rn, opcode;
1264 UINT32 by, rdn;
1265 // UINT32 oldR15 = R15;
1266
1267 opcode = (insn & INSN_OPCODE) >> INSN_OPCODE_SHIFT;
1268
1269 rd = 0;
1270 rn = 0;
1271
1272 /* --------------*/
1273 /* Construct Op2 */
1274 /* --------------*/
1275
1276 /* Immediate constant */
1277 if (insn & INSN_I)
1278 {
1279 by = (insn & INSN_OP2_ROTATE) >> INSN_OP2_ROTATE_SHIFT;
1280 if (by)
1281 {
1282 op2 = ROR(insn & INSN_OP2_IMM, by << 1);
1283 sc = op2 & SIGN_BIT;
1284 }
1285 else
1286 {
1287 op2 = insn & INSN_OP2; // SJE: Shouldn't this be INSN_OP2_IMM?
1288 sc = GET_CPSR & C_MASK;
1289 }
1290 }
1291 /* Op2 = Register Value */
1292 else
1293 {
1294 op2 = decodeShift(insn, (insn & INSN_S) ? &sc : NULL);
1295
1296 // LD TODO sc will always be 0 if this applies
1297 if (!(insn & INSN_S))
1298 sc = 0;
1299 }
1300
1301 // LD TODO this comment is wrong
1302 /* Calculate Rn to account for pipelining */
1303 if ((opcode & 0xd) != 0xd) /* No Rn in MOV */
1304 {
1305 if ((rn = (insn & INSN_RN) >> INSN_RN_SHIFT) == eR15)
1306 {
1307 #if ARM7_DEBUG_CORE
1308 LOG(("%08x: Pipelined R15 (Shift %d)\n", R15, (insn & INSN_I ? 8 : insn & 0x10u ? 12 : 12)));
1309 #endif
1310 rn = R15 + 8;
1311 }
1312 else
1313 {
1314 rn = GET_REGISTER(rn);
1315 }
1316 }
1317
1318 /* Perform the operation */
1319
1320 switch (opcode)
1321 {
1322 /* Arithmetic operations */
1323 case OPCODE_SBC:
1324 rd = (rn - op2 - (GET_CPSR & C_MASK ? 0 : 1));
1325 HandleALUSubFlags(rd, rn, op2);
1326 break;
1327 case OPCODE_CMP:
1328 case OPCODE_SUB:
1329 rd = (rn - op2);
1330 HandleALUSubFlags(rd, rn, op2);
1331 break;
1332 case OPCODE_RSC:
1333 rd = (op2 - rn - (GET_CPSR & C_MASK ? 0 : 1));
1334 HandleALUSubFlags(rd, op2, rn);
1335 break;
1336 case OPCODE_RSB:
1337 rd = (op2 - rn);
1338 HandleALUSubFlags(rd, op2, rn);
1339 break;
1340 case OPCODE_ADC:
1341 rd = (rn + op2 + ((GET_CPSR & C_MASK) >> C_BIT));
1342 HandleALUAddFlags(rd, rn, op2);
1343 break;
1344 case OPCODE_CMN:
1345 case OPCODE_ADD:
1346 rd = (rn + op2);
1347 HandleALUAddFlags(rd, rn, op2);
1348 break;
1349
1350 /* Logical operations */
1351 case OPCODE_AND:
1352 case OPCODE_TST:
1353 rd = rn & op2;
1354 HandleALULogicalFlags(rd, sc);
1355 break;
1356 case OPCODE_BIC:
1357 rd = rn & ~op2;
1358 HandleALULogicalFlags(rd, sc);
1359 break;
1360 case OPCODE_TEQ:
1361 case OPCODE_EOR:
1362 rd = rn ^ op2;
1363 HandleALULogicalFlags(rd, sc);
1364 break;
1365 case OPCODE_ORR:
1366 rd = rn | op2;
1367 HandleALULogicalFlags(rd, sc);
1368 break;
1369 case OPCODE_MOV:
1370 rd = op2;
1371 HandleALULogicalFlags(rd, sc);
1372 break;
1373 case OPCODE_MVN:
1374 rd = (~op2);
1375 HandleALULogicalFlags(rd, sc);
1376 break;
1377 }
1378
1379 /* Put the result in its register if not one of the test only opcodes (TST,TEQ,CMP,CMN) */
1380 rdn = (insn & INSN_RD) >> INSN_RD_SHIFT;
1381 if ((opcode & 0xc) != 0x8)
1382 {
1383 // If Rd = R15, but S Flag not set, Result is placed in R15, but CPSR is not affected (page 44)
1384 if (rdn == eR15 && !(insn & INSN_S))
1385 {
1386 R15 = rd;
1387 }
1388 else
1389 {
1390 // Rd = 15 and S Flag IS set, Result is placed in R15, and current mode SPSR moved to CPSR
1391 if (rdn == eR15) {
1392
1393 // Update CPSR from SPSR
1394 SET_CPSR(GET_REGISTER(SPSR));
1395 SwitchMode(GET_MODE);
1396
1397 R15 = rd;
1398
1399 /* IRQ masks may have changed in this instruction */
1400 // ARM7_CHECKIRQ;
1401 }
1402 else
1403 /* S Flag is set - Write results to register & update CPSR (which was already handled using HandleALU flag macros) */
1404 SET_REGISTER(rdn, rd);
1405 }
1406 }
1407 // SJE: Don't think this applies any more.. (see page 44 at bottom)
1408 /* TST & TEQ can affect R15 (the condition code register) with the S bit set */
1409 else if (rdn == eR15)
1410 {
1411 if (insn & INSN_S) {
1412 #if ARM7_DEBUG_CORE
1413 LOG(("%08x: TST class on R15 s bit set\n", R15));
1414 #endif
1415 R15 = rd;
1416
1417 /* IRQ masks may have changed in this instruction */
1418 // ARM7_CHECKIRQ;
1419 }
1420 else
1421 {
1422 #if ARM7_DEBUG_CORE
1423 LOG(("%08x: TST class on R15 no s bit set\n", R15));
1424 #endif
1425 }
1426 }
1427
1428 // if (oldR15 != R15)
1429 // change_pc(R15);
1430 }
1431
HandleMul(UINT32 insn)1432 static void HandleMul(UINT32 insn)
1433 {
1434 UINT32 r;
1435
1436 /* Do the basic multiply of Rm and Rs */
1437 r = GET_REGISTER(insn & INSN_MUL_RM) *
1438 GET_REGISTER((insn & INSN_MUL_RS) >> INSN_MUL_RS_SHIFT);
1439
1440 #if ARM7_DEBUG_CORE
1441 if ((insn & INSN_MUL_RM) == 0xf ||
1442 ((insn & INSN_MUL_RS) >> INSN_MUL_RS_SHIFT) == 0xf ||
1443 ((insn & INSN_MUL_RN) >> INSN_MUL_RN_SHIFT) == 0xf)
1444 LOG(("%08x: R15 used in mult\n", R15));
1445 #endif
1446
1447 /* Add on Rn if this is a MLA */
1448 if (insn & INSN_MUL_A)
1449 {
1450 r += GET_REGISTER((insn & INSN_MUL_RN) >> INSN_MUL_RN_SHIFT);
1451 }
1452
1453 /* Write the result */
1454 SET_REGISTER((insn & INSN_MUL_RD) >> INSN_MUL_RD_SHIFT, r);
1455
1456 /* Set N and Z if asked */
1457 if (insn & INSN_S)
1458 {
1459 SET_CPSR((GET_CPSR & ~(N_MASK | Z_MASK)) | HandleALUNZFlags(r));
1460 }
1461 }
1462
1463 // todo: add proper cycle counts
HandleSMulLong(UINT32 insn)1464 static void HandleSMulLong(UINT32 insn)
1465 {
1466 INT32 rm, rs;
1467 UINT32 rhi, rlo;
1468 INT64 res = 0;
1469
1470 rm = (INT32)GET_REGISTER(insn & 0xf);
1471 rs = (INT32)GET_REGISTER(((insn >> 8) & 0xf));
1472 rhi = (insn >> 16) & 0xf;
1473 rlo = (insn >> 12) & 0xf;
1474
1475 #if ARM7_DEBUG_CORE
1476 if ((insn & 0xf) == 15 || ((insn >> 8) & 0xf) == 15 || ((insn >> 16) & 0xf) == 15 || ((insn >> 12) & 0xf) == 15)
1477 LOG(("%08x: Illegal use of PC as a register in SMULL opcode\n", R15));
1478 #endif
1479
1480 /* Perform the multiplication */
1481 res = (INT64)rm * rs;
1482
1483 /* Add on Rn if this is a MLA */
1484 if (insn & INSN_MUL_A)
1485 {
1486 INT64 acum = (INT64)((((INT64)(GET_REGISTER(rhi))) << 32) | GET_REGISTER(rlo));
1487 res += acum;
1488 }
1489
1490 /* Write the result (upper dword goes to RHi, lower to RLo) */
1491 SET_REGISTER(rhi, res >> 32);
1492 SET_REGISTER(rlo, res & 0xFFFFFFFF);
1493
1494 /* Set N and Z if asked */
1495 if (insn & INSN_S)
1496 {
1497 SET_CPSR((GET_CPSR & ~(N_MASK | Z_MASK)) | HandleLongALUNZFlags(res));
1498 }
1499 }
1500
1501 // todo: add proper cycle counts
HandleUMulLong(UINT32 insn)1502 static void HandleUMulLong(UINT32 insn)
1503 {
1504 UINT32 rm, rs;
1505 UINT32 rhi, rlo;
1506 UINT64 res = 0;
1507
1508 rm = (INT32)GET_REGISTER(insn & 0xf);
1509 rs = (INT32)GET_REGISTER(((insn >> 8) & 0xf));
1510 rhi = (insn >> 16) & 0xf;
1511 rlo = (insn >> 12) & 0xf;
1512
1513 #if ARM7_DEBUG_CORE
1514 if (((insn & 0xf) == 15) || (((insn >> 8) & 0xf) == 15) || (((insn >> 16) & 0xf) == 15) || (((insn >> 12) & 0xf) == 15)
1515 LOG(("%08x: Illegal use of PC as a register in SMULL opcode\n", R15));
1516 #endif
1517
1518 /* Perform the multiplication */
1519 res = (UINT64)rm * rs;
1520
1521 /* Add on Rn if this is a MLA */
1522 if (insn & INSN_MUL_A)
1523 {
1524 UINT64 acum = (UINT64)((((UINT64)(GET_REGISTER(rhi))) << 32) | GET_REGISTER(rlo));
1525 res += acum;
1526 }
1527
1528 /* Write the result (upper dword goes to RHi, lower to RLo) */
1529 SET_REGISTER(rhi, res >> 32);
1530 SET_REGISTER(rlo, res & 0xFFFFFFFF);
1531
1532 /* Set N and Z if asked */
1533 if (insn & INSN_S)
1534 {
1535 SET_CPSR((GET_CPSR & ~(N_MASK | Z_MASK)) | HandleLongALUNZFlags(res));
1536 }
1537 }
1538
1539 static void HandleMemBlock(UINT32 insn)
1540 {
1541 UINT32 rb = (insn & INSN_RN) >> INSN_RN_SHIFT;
1542 UINT32 rbp = GET_REGISTER(rb);
1543 // UINT32 oldR15 = R15;
1544 int result;
1545
1546 #if ARM7_DEBUG_CORE
1547 if (rbp & 3)
1548 LOG(("%08x: Unaligned Mem Transfer @ %08x\n", R15, rbp));
1549 #endif
1550
1551 // We will specify the cycle count for each case, so remove the -3 that occurs at the end
1552 ARM7_ICOUNT += 3;
1553
1554 if (insn & INSN_BDT_L)
1555 {
1556 /* Loading */
1557 if (insn & INSN_BDT_U)
1558 {
1559 /* Incrementing */
1560 if (!(insn & INSN_BDT_P))
1561 {
1562 rbp = rbp + (- 4);
1563 }
1564
1565 // S Flag Set, but R15 not in list = User Bank Transfer
1566 if (insn & INSN_BDT_S && (insn & 0x8000) == 0)
1567 {
1568 // set to user mode - then do the transfer, and set back
1569 int curmode = GET_MODE;
1570 SwitchMode(eARM7_MODE_USER);
1571 LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15));
1572 result = loadInc(insn & 0xffff, rbp, insn & INSN_BDT_S);
1573 // todo - not sure if Writeback occurs on User registers also..
1574 SwitchMode(curmode);
1575 }
1576 else
1577 result = loadInc(insn & 0xffff, rbp, insn & INSN_BDT_S);
1578
1579 if (insn & INSN_BDT_W)
1580 {
1581 #if ARM7_DEBUG_CORE
1582 if (rb == 15)
1583 LOG(("%08x: Illegal LDRM writeback to r15\n", R15));
1584 #endif
1585 SET_REGISTER(rb, GET_REGISTER(rb) + result * 4);
1586 }
1587
1588 // R15 included? (NOTE: CPSR restore must occur LAST otherwise wrong registers restored!)
1589 if (insn & 0x8000) {
1590 R15 -= 4; // SJE: I forget why i did this?
1591 // S - Flag Set? Signals transfer of current mode SPSR->CPSR
1592 if (insn & INSN_BDT_S) {
1593 SET_CPSR(GET_REGISTER(SPSR));
1594 SwitchMode(GET_MODE);
1595 }
1596 }
1597 // LDM PC - takes 1 extra cycle
1598 ARM7_ICOUNT -= 1;
1599 }
1600 else
1601 {
1602 /* Decrementing */
1603 if (!(insn & INSN_BDT_P))
1604 {
1605 rbp = rbp - (- 4);
1606 }
1607
1608 // S Flag Set, but R15 not in list = User Bank Transfer
1609 if (insn & INSN_BDT_S && ((insn & 0x8000) == 0))
1610 {
1611 // set to user mode - then do the transfer, and set back
1612 int curmode = GET_MODE;
1613 SwitchMode(eARM7_MODE_USER);
1614 LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15));
1615 result = loadDec(insn & 0xffff, rbp, insn & INSN_BDT_S);
1616 // todo - not sure if Writeback occurs on User registers also..
1617 SwitchMode(curmode);
1618 }
1619 else
1620 result = loadDec(insn & 0xffff, rbp, insn & INSN_BDT_S);
1621
1622 if (insn & INSN_BDT_W)
1623 {
1624 if (rb == 0xf)
1625 LOG(("%08x: Illegal LDRM writeback to r15\n", R15));
1626 SET_REGISTER(rb, GET_REGISTER(rb)-result*4);
1627 }
1628
1629 // R15 included? (NOTE: CPSR restore must occur LAST otherwise wrong registers restored!)
1630 if (insn & 0x8000) {
1631 R15 -= 4; // SJE: I forget why i did this?
1632 // S - Flag Set? Signals transfer of current mode SPSR->CPSR
1633 if (insn & INSN_BDT_S) {
1634 SET_CPSR(GET_REGISTER(SPSR));
1635 SwitchMode(GET_MODE);
1636 }
1637 // LDM PC - takes 1 extra cycle
1638 ARM7_ICOUNT -= 1;
1639 }
1640
1641 // LDM (NO PC) takes nS + 1n + 1I cycles (n = # of register transfers)
1642 ARM7_ICOUNT -= result + 1 + 1;
1643 }
1644 } /* Loading */
1645 else
1646 {
1647 /* Storing */
1648 if (insn & (1 << eR15))
1649 {
1650 #if ARM7_DEBUG_CORE
1651 LOG(("%08x: Writing R15 in strm\n", R15));
1652 #endif
1653 /* special case handling if writing to PC */
1654 R15 += 12;
1655 }
1656 if (insn & INSN_BDT_U)
1657 {
1658 /* Incrementing */
1659 if (!(insn & INSN_BDT_P))
1660 {
1661 rbp = rbp + (- 4);
1662 }
1663
1664 // S Flag Set, but R15 not in list = User Bank Transfer
1665 if (insn & INSN_BDT_S && (insn & 0x8000) == 0)
1666 {
1667 // todo: needs to be tested..
1668
1669 // set to user mode - then do the transfer, and set back
1670 int curmode = GET_MODE;
1671 SwitchMode(eARM7_MODE_USER);
1672 LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15));
1673 result = storeInc(insn & 0xffff, rbp);
1674 // todo - not sure if Writeback occurs on User registers also..
1675 SwitchMode(curmode);
1676 }
1677 else
1678 result = storeInc(insn & 0xffff, rbp);
1679
1680 if (insn & INSN_BDT_W)
1681 {
1682 SET_REGISTER(rb, GET_REGISTER(rb) + result * 4);
1683 }
1684 }
1685 else
1686 {
1687 /* Decrementing */
1688 if (!(insn & INSN_BDT_P))
1689 {
1690 rbp = rbp - (-4);
1691 }
1692
1693 // S Flag Set, but R15 not in list = User Bank Transfer
1694 if (insn & INSN_BDT_S && (insn & 0x8000) == 0)
1695 {
1696 // set to user mode - then do the transfer, and set back
1697 int curmode = GET_MODE;
1698 SwitchMode(eARM7_MODE_USER);
1699 LOG(("%08x: User Bank Transfer not fully tested - please check if working properly!\n", R15));
1700 result = storeDec(insn & 0xffff, rbp);
1701 // todo - not sure if Writeback occurs on User registers also..
1702 SwitchMode(curmode);
1703 }
1704 else
1705 result = storeDec(insn & 0xffff, rbp);
1706
1707 if (insn & INSN_BDT_W)
1708 {
1709 SET_REGISTER(rb, GET_REGISTER(rb) - result * 4);
1710 }
1711 }
1712 if (insn & (1 << eR15))
1713 R15 -= 12;
1714
1715 // STM takes (n+1)S+2N+1I cycles (n = # of register transfers)
1716 ARM7_ICOUNT -= (result + 1) + 2 + 1;
1717 }
1718
1719 // if (oldR15 != R15)
1720 // change_pc(R15);
1721 } /* HandleMemBlock */
1722