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