summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/cpu/arm7/arm7core.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/cpu/arm7/arm7core.c')
-rw-r--r--trunk/src/emu/cpu/arm7/arm7core.c314
1 files changed, 314 insertions, 0 deletions
diff --git a/trunk/src/emu/cpu/arm7/arm7core.c b/trunk/src/emu/cpu/arm7/arm7core.c
new file mode 100644
index 00000000000..7bb484cda03
--- /dev/null
+++ b/trunk/src/emu/cpu/arm7/arm7core.c
@@ -0,0 +1,314 @@
+/*****************************************************************************
+ *
+ * arm7core.c
+ * Portable ARM7TDMI Core Emulator
+ *
+ * Copyright Steve Ellenoff, all rights reserved.
+ *
+ * - This source code is released as freeware for non-commercial purposes.
+ * - You are free to use and redistribute this code in modified or
+ * unmodified form, provided you list me in the credits.
+ * - If you modify this source code, you must add a notice to each modified
+ * source file that it has been changed. If you're a nice person, you
+ * will clearly mark each change too. :)
+ * - If you wish to use this for commercial purposes, please contact me at
+ * sellenoff@hotmail.com
+ * - The author of this copywritten work reserves the right to change the
+ * terms of its usage and license at any time, including retroactively
+ * - This entire notice must remain in the source code.
+ *
+ * This work is based on:
+ * #1) 'Atmel Corporation ARM7TDMI (Thumb) Datasheet - January 1999'
+ * #2) Arm 2/3/6 emulator By Bryan McPhail (bmcphail@tendril.co.uk) and Phil Stroffolino (MAME CORE 0.76)
+ * #3) Thumb support by Ryan Holtz
+ * #4) Additional Thumb support and bugfixes by R. Belmont
+ *
+ *****************************************************************************/
+
+/******************************************************************************
+ * Notes:
+
+ **This core comes from my AT91 cpu core contributed to PinMAME,
+ but with all the AT91 specific junk removed,
+ which leaves just the ARM7TDMI core itself. I further removed the CPU specific MAME stuff
+ so you just have the actual ARM7 core itself, since many cpu's incorporate an ARM7 core, but add on
+ many cpu specific functionality.
+
+ Therefore, to use the core, you simpy include this file along with the .h file into your own cpu specific
+ implementation, and therefore, this file shouldn't be compiled as part of your project directly.
+
+ For better or for worse, the code itself is very much intact from it's arm 2/3/6 origins from
+ Bryan & Phil's work. I contemplated merging it in, but thought the fact that the CPSR is
+ no longer part of the PC was enough of a change to make it annoying to merge.
+ **
+
+ Coprocessor functions are heavily implementation specific, so callback handlers are used to allow the
+ implementation to handle the functionality. Custom DASM handlers are included as well to allow the DASM
+ output to be tailored to the co-proc implementation details.
+
+ Todo:
+ 26 bit compatibility mode not implemented.
+ Data Processing opcodes need cycle count adjustments (see page 194 of ARM7TDMI manual for instruction timing summary)
+ Multi-emulated cpu support untested, but probably will not work too well, as no effort was made to code for more than 1.
+ Could not find info on what the TEQP opcode is from page 44..
+ I have no idea if user bank switching is right, as I don't fully understand it's use.
+ Search for Todo: tags for remaining items not done.
+
+
+ Differences from Arm 2/3 (6 also?)
+ -Thumb instruction support
+ -Full 32 bit address support
+ -PC no longer contains CPSR information, CPSR is own register now
+ -New register SPSR to store previous contents of CPSR (this register is banked in many modes)
+ -New opcodes for CPSR transfer, Long Multiplication, Co-Processor support, and some others
+ -User Bank Mode transfer using certain flags which were previously unallowed (LDM/STM with S Bit & R15)
+ -New operation modes? (unconfirmed)
+
+ Based heavily on arm core from MAME 0.76:
+ *****************************************
+ ARM 2/3/6 Emulation
+
+ Todo:
+ Software interrupts unverified (nothing uses them so far, but they should be ok)
+ Timing - Currently very approximated, nothing relies on proper timing so far.
+ IRQ timing not yet correct (again, nothing is affected by this so far).
+
+ By Bryan McPhail (bmcphail@tendril.co.uk) and Phil Stroffolino
+*****************************************************************************/
+
+
+/* Prototypes */
+
+
+extern UINT32 decodeShift(arm_state *cpustate, UINT32 insn, UINT32 *pCarry);
+
+void arm7_check_irq_state(arm_state *cpustate);
+
+
+/* Static Vars */
+// Note: for multi-cpu implementation, this approach won't work w/o modification
+write32_device_func arm7_coproc_do_callback; // holder for the co processor Data Operations Callback func.
+read32_device_func arm7_coproc_rt_r_callback; // holder for the co processor Register Transfer Read Callback func.
+write32_device_func arm7_coproc_rt_w_callback; // holder for the co processor Register Transfer Write Callback Callback func.
+
+#ifdef UNUSED_DEFINITION
+// custom dasm callback handlers for co-processor instructions
+char *(*arm7_dasm_cop_dt_callback)(arm_state *cpustate, char *pBuf, UINT32 opcode, char *pConditionCode, char *pBuf0);
+char *(*arm7_dasm_cop_rt_callback)(arm_state *cpustate, char *pBuf, UINT32 opcode, char *pConditionCode, char *pBuf0);
+char *(*arm7_dasm_cop_do_callback)(arm_state *cpustate, char *pBuf, UINT32 opcode, char *pConditionCode, char *pBuf0);
+#endif
+
+
+// convert cpsr mode num into to text
+static const char modetext[ARM7_NUM_MODES][5] = {
+ "USER", "FIRQ", "IRQ", "SVC", "ILL1", "ILL2", "ILL3", "ABT",
+ "ILL4", "ILL5", "ILL6", "UND", "ILL7", "ILL8", "ILL9", "SYS"
+};
+static const char *GetModeText(int cpsr)
+{
+ return modetext[cpsr & MODE_FLAG];
+}
+
+
+
+
+/***************************************************************************
+ * Main CPU Funcs
+ ***************************************************************************/
+
+// CPU INIT
+static void arm7_core_init(device_t *device, const char *cpuname)
+{
+ arm_state *cpustate = get_safe_token(device);
+
+ device->save_item(NAME(cpustate->sArmRegister));
+ device->save_item(NAME(cpustate->pendingIrq));
+ device->save_item(NAME(cpustate->pendingFiq));
+ device->save_item(NAME(cpustate->pendingAbtD));
+ device->save_item(NAME(cpustate->pendingAbtP));
+ device->save_item(NAME(cpustate->pendingUnd));
+ device->save_item(NAME(cpustate->pendingSwi));
+}
+
+// CPU RESET
+static void arm7_core_reset(legacy_cpu_device *device)
+{
+ arm_state *cpustate = get_safe_token(device);
+
+ device_irq_acknowledge_callback save_irqcallback = cpustate->irq_callback;
+
+ memset(cpustate, 0, sizeof(arm_state));
+ cpustate->irq_callback = save_irqcallback;
+ cpustate->device = device;
+ cpustate->program = device->space(AS_PROGRAM);
+ cpustate->endian = ENDIANNESS_LITTLE;
+ cpustate->direct = &cpustate->program->direct();
+
+ /* start up in SVC mode with interrupts disabled. */
+ ARM7REG(eCPSR) = I_MASK | F_MASK | 0x10;
+ SwitchMode(cpustate, eARM7_MODE_SVC);
+ R15 = 0;
+}
+
+// CPU CHECK IRQ STATE
+// Note: couldn't find any exact cycle counts for most of these exceptions
+void arm7_check_irq_state(arm_state *cpustate)
+{
+ UINT32 cpsr = GET_CPSR; /* save current CPSR */
+ UINT32 pc = R15 + 4; /* save old pc (already incremented in pipeline) */;
+
+ /* Exception priorities:
+
+ Reset
+ Data abort
+ FIRQ
+ IRQ
+ Prefetch abort
+ Undefined instruction
+ Software Interrupt
+ */
+
+ // Data Abort
+ if (cpustate->pendingAbtD) {
+ if (MODE26) fatalerror( "pendingAbtD (todo)");
+ SwitchMode(cpustate, eARM7_MODE_ABT); /* Set ABT mode so PC is saved to correct R14 bank */
+ SET_REGISTER(cpustate, 14, pc - 8 + 8); /* save PC to R14 */
+ SET_REGISTER(cpustate, SPSR, cpsr); /* Save current CPSR */
+ SET_CPSR(GET_CPSR | I_MASK); /* Mask IRQ */
+ SET_CPSR(GET_CPSR & ~T_MASK);
+ R15 = 0x10; /* IRQ Vector address */
+ if ((COPRO_CTRL & COPRO_CTRL_MMU_EN) && (COPRO_CTRL & COPRO_CTRL_INTVEC_ADJUST)) R15 |= 0xFFFF0000;
+ cpustate->pendingAbtD = 0;
+ return;
+ }
+
+ // FIQ
+ if (cpustate->pendingFiq && (cpsr & F_MASK) == 0) {
+ if (MODE26) fatalerror( "pendingFiq (todo)");
+ SwitchMode(cpustate, eARM7_MODE_FIQ); /* Set FIQ mode so PC is saved to correct R14 bank */
+ SET_REGISTER(cpustate, 14, pc - 4 + 4); /* save PC to R14 */
+ SET_REGISTER(cpustate, SPSR, cpsr); /* Save current CPSR */
+ SET_CPSR(GET_CPSR | I_MASK | F_MASK); /* Mask both IRQ & FIQ */
+ SET_CPSR(GET_CPSR & ~T_MASK);
+ R15 = 0x1c; /* IRQ Vector address */
+ if ((COPRO_CTRL & COPRO_CTRL_MMU_EN) && (COPRO_CTRL & COPRO_CTRL_INTVEC_ADJUST)) R15 |= 0xFFFF0000;
+ return;
+ }
+
+ // IRQ
+ if (cpustate->pendingIrq && (cpsr & I_MASK) == 0) {
+ SwitchMode(cpustate, eARM7_MODE_IRQ); /* Set IRQ mode so PC is saved to correct R14 bank */
+ SET_REGISTER(cpustate, 14, pc - 4 + 4); /* save PC to R14 */
+ if (MODE32)
+ {
+ SET_REGISTER(cpustate, SPSR, cpsr); /* Save current CPSR */
+ SET_CPSR(GET_CPSR | I_MASK); /* Mask IRQ */
+ SET_CPSR(GET_CPSR & ~T_MASK);
+ R15 = 0x18; /* IRQ Vector address */
+ }
+ else
+ {
+ UINT32 temp;
+ R15 = (pc & 0xF4000000) /* N Z C V F */ | 0x18 | 0x00000002 /* IRQ */ | 0x08000000 /* I */;
+ temp = (GET_CPSR & 0x0FFFFF3F) /* N Z C V I F */ | (R15 & 0xF0000000) /* N Z C V */ | ((R15 & 0x0C000000) >> (26 - 6)) /* I F */;
+ SET_CPSR(temp); /* Mask IRQ */
+ }
+ if ((COPRO_CTRL & COPRO_CTRL_MMU_EN) && (COPRO_CTRL & COPRO_CTRL_INTVEC_ADJUST)) R15 |= 0xFFFF0000;
+ return;
+ }
+
+ // Prefetch Abort
+ if (cpustate->pendingAbtP) {
+ if (MODE26) fatalerror( "pendingAbtP (todo)");
+ SwitchMode(cpustate, eARM7_MODE_ABT); /* Set ABT mode so PC is saved to correct R14 bank */
+ SET_REGISTER(cpustate, 14, pc - 4 + 4); /* save PC to R14 */
+ SET_REGISTER(cpustate, SPSR, cpsr); /* Save current CPSR */
+ SET_CPSR(GET_CPSR | I_MASK); /* Mask IRQ */
+ SET_CPSR(GET_CPSR & ~T_MASK);
+ R15 = 0x0c; /* IRQ Vector address */
+ if ((COPRO_CTRL & COPRO_CTRL_MMU_EN) && (COPRO_CTRL & COPRO_CTRL_INTVEC_ADJUST)) R15 |= 0xFFFF0000;
+ cpustate->pendingAbtP = 0;
+ return;
+ }
+
+ // Undefined instruction
+ if (cpustate->pendingUnd) {
+ if (MODE26) fatalerror( "pendingUnd (todo)");
+ SwitchMode(cpustate, eARM7_MODE_UND); /* Set UND mode so PC is saved to correct R14 bank */
+ // compensate for prefetch (should this also be done for normal IRQ?)
+ if (T_IS_SET(GET_CPSR))
+ {
+ SET_REGISTER(cpustate, 14, pc - 4 + 2); /* save PC to R14 */
+ }
+ else
+ {
+ SET_REGISTER(cpustate, 14, pc - 4 + 4 - 4); /* save PC to R14 */
+ }
+ SET_REGISTER(cpustate, SPSR, cpsr); /* Save current CPSR */
+ SET_CPSR(GET_CPSR | I_MASK); /* Mask IRQ */
+ SET_CPSR(GET_CPSR & ~T_MASK);
+ R15 = 0x04; /* IRQ Vector address */
+ if ((COPRO_CTRL & COPRO_CTRL_MMU_EN) && (COPRO_CTRL & COPRO_CTRL_INTVEC_ADJUST)) R15 |= 0xFFFF0000;
+ cpustate->pendingUnd = 0;
+ return;
+ }
+
+ // Software Interrupt
+ if (cpustate->pendingSwi) {
+ SwitchMode(cpustate, eARM7_MODE_SVC); /* Set SVC mode so PC is saved to correct R14 bank */
+ // compensate for prefetch (should this also be done for normal IRQ?)
+ if (T_IS_SET(GET_CPSR))
+ {
+ SET_REGISTER(cpustate, 14, pc - 4 + 2); /* save PC to R14 */
+ }
+ else
+ {
+ SET_REGISTER(cpustate, 14, pc - 4 + 4); /* save PC to R14 */
+ }
+ if (MODE32)
+ {
+ SET_REGISTER(cpustate, SPSR, cpsr); /* Save current CPSR */
+ SET_CPSR(GET_CPSR | I_MASK); /* Mask IRQ */
+ SET_CPSR(GET_CPSR & ~T_MASK); /* Go to ARM mode */
+ R15 = 0x08; /* Jump to the SWI vector */
+ }
+ else
+ {
+ UINT32 temp;
+ R15 = (pc & 0xF4000000) /* N Z C V F */ | 0x08 | 0x00000003 /* SVC */ | 0x08000000 /* I */;
+ temp = (GET_CPSR & 0x0FFFFF3F) /* N Z C V I F */ | (R15 & 0xF0000000) /* N Z C V */ | ((R15 & 0x0C000000) >> (26 - 6)) /* I F */;
+ SET_CPSR(temp); /* Mask IRQ */
+ }
+ if ((COPRO_CTRL & COPRO_CTRL_MMU_EN) && (COPRO_CTRL & COPRO_CTRL_INTVEC_ADJUST)) R15 |= 0xFFFF0000;
+ cpustate->pendingSwi = 0;
+ return;
+ }
+}
+
+// CPU - SET IRQ LINE
+static void arm7_core_set_irq_line(arm_state *cpustate, int irqline, int state)
+{
+ switch (irqline) {
+
+ case ARM7_IRQ_LINE: /* IRQ */
+ cpustate->pendingIrq = state & 1;
+ break;
+
+ case ARM7_FIRQ_LINE: /* FIRQ */
+ cpustate->pendingFiq = state & 1;
+ break;
+
+ case ARM7_ABORT_EXCEPTION:
+ cpustate->pendingAbtD = state & 1;
+ break;
+ case ARM7_ABORT_PREFETCH_EXCEPTION:
+ cpustate->pendingAbtP = state & 1;
+ break;
+
+ case ARM7_UNDEFINE_EXCEPTION:
+ cpustate->pendingUnd = state & 1;
+ break;
+ }
+
+ ARM7_CHECKIRQ;
+}