+static unsigned
+NoCoPro3R (ARMul_State * state ATTRIBUTE_UNUSED,
+ unsigned a ATTRIBUTE_UNUSED,
+ ARMword b ATTRIBUTE_UNUSED)
+{
+ return ARMul_CANT;
+}
+
+static unsigned
+NoCoPro4R (ARMul_State * state ATTRIBUTE_UNUSED,
+ unsigned a ATTRIBUTE_UNUSED,
+ ARMword b ATTRIBUTE_UNUSED,
+ ARMword c ATTRIBUTE_UNUSED)
+{
+ return ARMul_CANT;
+}
+
+static unsigned
+NoCoPro4W (ARMul_State * state ATTRIBUTE_UNUSED,
+ unsigned a ATTRIBUTE_UNUSED,
+ ARMword b ATTRIBUTE_UNUSED,
+ ARMword * c ATTRIBUTE_UNUSED)
+{
+ return ARMul_CANT;
+}
+
+/* The XScale Co-processors. */
+
+/* Coprocessor 15: System Control. */
+static void write_cp14_reg (unsigned, ARMword);
+static ARMword read_cp14_reg (unsigned);
+
+/* There are two sets of registers for copro 15.
+ One set is available when opcode_2 is 0 and
+ the other set when opcode_2 >= 1. */
+static ARMword XScale_cp15_opcode_2_is_0_Regs[16];
+static ARMword XScale_cp15_opcode_2_is_not_0_Regs[16];
+/* There are also a set of breakpoint registers
+ which are accessed via CRm instead of opcode_2. */
+static ARMword XScale_cp15_DBR1;
+static ARMword XScale_cp15_DBCON;
+static ARMword XScale_cp15_IBCR0;
+static ARMword XScale_cp15_IBCR1;
+
+static unsigned
+XScale_cp15_init (ARMul_State * state ATTRIBUTE_UNUSED)
+{
+ int i;
+
+ for (i = 16; i--;)
+ {
+ XScale_cp15_opcode_2_is_0_Regs[i] = 0;
+ XScale_cp15_opcode_2_is_not_0_Regs[i] = 0;
+ }
+
+ /* Initialise the processor ID. */
+ XScale_cp15_opcode_2_is_0_Regs[0] = 0x69052000;
+
+ /* Initialise the cache type. */
+ XScale_cp15_opcode_2_is_not_0_Regs[0] = 0x0B1AA1AA;
+
+ /* Initialise the ARM Control Register. */
+ XScale_cp15_opcode_2_is_0_Regs[1] = 0x00000078;
+
+ return TRUE;
+}
+
+/* Check an access to a register. */
+
+static unsigned
+check_cp15_access (ARMul_State * state,
+ unsigned reg,
+ unsigned CRm,
+ unsigned opcode_1,
+ unsigned opcode_2)
+{
+ /* Do not allow access to these register in USER mode. */
+ if (state->Mode == USER26MODE || state->Mode == USER32MODE)
+ return ARMul_CANT;
+
+ /* Opcode_1should be zero. */
+ if (opcode_1 != 0)
+ return ARMul_CANT;
+
+ /* Different register have different access requirements. */
+ switch (reg)
+ {
+ case 0:
+ case 1:
+ /* CRm must be 0. Opcode_2 can be anything. */
+ if (CRm != 0)
+ return ARMul_CANT;
+ break;
+ case 2:
+ case 3:
+ /* CRm must be 0. Opcode_2 must be zero. */
+ if ((CRm != 0) || (opcode_2 != 0))
+ return ARMul_CANT;
+ break;
+ case 4:
+ /* Access not allowed. */
+ return ARMul_CANT;
+ case 5:
+ case 6:
+ /* Opcode_2 must be zero. CRm must be 0. */
+ if ((CRm != 0) || (opcode_2 != 0))
+ return ARMul_CANT;
+ break;
+ case 7:
+ /* Permissable combinations:
+ Opcode_2 CRm
+ 0 5
+ 0 6
+ 0 7
+ 1 5
+ 1 6
+ 1 10
+ 4 10
+ 5 2
+ 6 5 */
+ switch (opcode_2)
+ {
+ default: return ARMul_CANT;
+ case 6: if (CRm != 5) return ARMul_CANT; break;
+ case 5: if (CRm != 2) return ARMul_CANT; break;
+ case 4: if (CRm != 10) return ARMul_CANT; break;
+ case 1: if ((CRm != 5) && (CRm != 6) && (CRm != 10)) return ARMul_CANT; break;
+ case 0: if ((CRm < 5) || (CRm > 7)) return ARMul_CANT; break;
+ }
+ break;
+
+ case 8:
+ /* Permissable combinations:
+ Opcode_2 CRm
+ 0 5
+ 0 6
+ 0 7
+ 1 5
+ 1 6 */
+ if (opcode_2 > 1)
+ return ARMul_CANT;
+ if ((CRm < 5) || (CRm > 7))
+ return ARMul_CANT;
+ if (opcode_2 == 1 && CRm == 7)
+ return ARMul_CANT;
+ break;
+ case 9:
+ /* Opcode_2 must be zero or one. CRm must be 1 or 2. */
+ if ( ((CRm != 0) && (CRm != 1))
+ || ((opcode_2 != 1) && (opcode_2 != 2)))
+ return ARMul_CANT;
+ break;
+ case 10:
+ /* Opcode_2 must be zero or one. CRm must be 4 or 8. */
+ if ( ((CRm != 0) && (CRm != 1))
+ || ((opcode_2 != 4) && (opcode_2 != 8)))
+ return ARMul_CANT;
+ break;
+ case 11:
+ /* Access not allowed. */
+ return ARMul_CANT;
+ case 12:
+ /* Access not allowed. */
+ return ARMul_CANT;
+ case 13:
+ /* Opcode_2 must be zero. CRm must be 0. */
+ if ((CRm != 0) || (opcode_2 != 0))
+ return ARMul_CANT;
+ break;
+ case 14:
+ /* Opcode_2 must be 0. CRm must be 0, 3, 4, 8 or 9. */
+ if (opcode_2 != 0)
+ return ARMul_CANT;
+
+ if ((CRm != 0) && (CRm != 3) && (CRm != 4) && (CRm != 8) && (CRm != 9))
+ return ARMul_CANT;
+ break;
+ case 15:
+ /* Opcode_2 must be zero. CRm must be 1. */
+ if ((CRm != 1) || (opcode_2 != 0))
+ return ARMul_CANT;
+ break;
+ default:
+ /* Should never happen. */
+ return ARMul_CANT;
+ }
+
+ return ARMul_DONE;
+}
+
+/* Store a value into one of coprocessor 15's registers. */
+
+static void
+write_cp15_reg (ARMul_State * state,
+ unsigned reg,
+ unsigned opcode_2,
+ unsigned CRm,
+ ARMword value)
+{
+ if (opcode_2)
+ {
+ switch (reg)
+ {
+ case 0: /* Cache Type. */
+ /* Writes are not allowed. */
+ return;
+
+ case 1: /* Auxillary Control. */
+ /* Only BITS (5, 4) and BITS (1, 0) can be written. */
+ value &= 0x33;
+ break;
+
+ default:
+ return;
+ }
+
+ XScale_cp15_opcode_2_is_not_0_Regs [reg] = value;
+ }
+ else
+ {
+ switch (reg)
+ {
+ case 0: /* ID. */
+ /* Writes are not allowed. */
+ return;
+
+ case 1: /* ARM Control. */
+ /* Only BITS (13, 11), BITS (9, 7) and BITS (2, 0) can be written.
+ BITS (31, 14) and BIT (10) write as zero, BITS (6, 3) write as one. */
+ value &= 0x00003b87;
+ value |= 0x00000078;
+
+ /* Change the endianness if necessary. */
+ if ((value & ARMul_CP15_R1_ENDIAN) !=
+ (XScale_cp15_opcode_2_is_0_Regs [reg] & ARMul_CP15_R1_ENDIAN))
+ {
+ state->bigendSig = value & ARMul_CP15_R1_ENDIAN;
+ /* Force ARMulator to notice these now. */
+ state->Emulate = CHANGEMODE;
+ }
+ break;
+
+ case 2: /* Translation Table Base. */
+ /* Only BITS (31, 14) can be written. */
+ value &= 0xffffc000;
+ break;
+
+ case 3: /* Domain Access Control. */
+ /* All bits writable. */
+ break;
+
+ case 5: /* Fault Status Register. */
+ /* BITS (10, 9) and BITS (7, 0) can be written. */
+ value &= 0x000006ff;
+ break;
+
+ case 6: /* Fault Address Register. */
+ /* All bits writable. */
+ break;
+
+ case 7: /* Cache Functions. */
+ case 8: /* TLB Operations. */
+ case 10: /* TLB Lock Down. */
+ /* Ignore writes. */
+ return;
+
+ case 9: /* Data Cache Lock. */
+ /* Only BIT (0) can be written. */
+ value &= 0x1;
+ break;
+
+ case 13: /* Process ID. */
+ /* Only BITS (31, 25) are writable. */
+ value &= 0xfe000000;
+ break;
+
+ case 14: /* DBR0, DBR1, DBCON, IBCR0, IBCR1 */
+ /* All bits can be written. Which register is accessed is
+ dependent upon CRm. */
+ switch (CRm)
+ {
+ case 0: /* DBR0 */
+ break;
+ case 3: /* DBR1 */
+ XScale_cp15_DBR1 = value;
+ break;
+ case 4: /* DBCON */
+ XScale_cp15_DBCON = value;
+ break;
+ case 8: /* IBCR0 */
+ XScale_cp15_IBCR0 = value;
+ break;
+ case 9: /* IBCR1 */
+ XScale_cp15_IBCR1 = value;
+ break;
+ default:
+ return;
+ }
+ break;
+
+ case 15: /* Coprpcessor Access Register. */
+ /* Access is only valid if CRm == 1. */
+ if (CRm != 1)
+ return;
+
+ /* Only BITS (13, 0) may be written. */
+ value &= 0x00003fff;
+ break;
+
+ default:
+ return;
+ }
+
+ XScale_cp15_opcode_2_is_0_Regs [reg] = value;
+ }
+
+ return;
+}
+
+/* Return the value in a cp15 register. */
+
+ARMword
+read_cp15_reg (unsigned reg, unsigned opcode_2, unsigned CRm)
+{
+ if (opcode_2 == 0)
+ {
+ if (reg == 15 && CRm != 1)
+ return 0;
+
+ if (reg == 14)
+ {
+ switch (CRm)
+ {
+ case 3: return XScale_cp15_DBR1;
+ case 4: return XScale_cp15_DBCON;
+ case 8: return XScale_cp15_IBCR0;
+ case 9: return XScale_cp15_IBCR1;
+ default:
+ break;
+ }
+ }
+
+ return XScale_cp15_opcode_2_is_0_Regs [reg];
+ }
+ else
+ return XScale_cp15_opcode_2_is_not_0_Regs [reg];
+
+ return 0;
+}
+
+static unsigned
+XScale_cp15_MRC (ARMul_State * state,
+ unsigned type ATTRIBUTE_UNUSED,
+ ARMword instr,
+ ARMword * value)
+{
+ unsigned opcode_2 = BITS (5, 7);
+ unsigned CRm = BITS (0, 3);
+ unsigned reg = BITS (16, 19);
+ unsigned result;
+
+ result = check_cp15_access (state, reg, CRm, BITS (21, 23), opcode_2);
+
+ if (result == ARMul_DONE)
+ * value = read_cp15_reg (reg, opcode_2, CRm);
+
+ return result;
+}
+
+static unsigned
+XScale_cp15_MCR (ARMul_State * state,
+ unsigned type ATTRIBUTE_UNUSED,
+ ARMword instr,
+ ARMword value)
+{
+ unsigned opcode_2 = BITS (5, 7);
+ unsigned CRm = BITS (0, 3);
+ unsigned reg = BITS (16, 19);
+ unsigned result;
+
+ result = check_cp15_access (state, reg, CRm, BITS (21, 23), opcode_2);
+
+ if (result == ARMul_DONE)
+ write_cp15_reg (state, reg, opcode_2, CRm, value);
+
+ return result;
+}
+
+static unsigned
+XScale_cp15_read_reg (ARMul_State * state ATTRIBUTE_UNUSED,
+ unsigned reg,
+ ARMword * value)
+{
+ /* FIXME: Not sure what to do about the alternative register set
+ here. For now default to just accessing CRm == 0 registers. */
+ * value = read_cp15_reg (reg, 0, 0);
+
+ return TRUE;
+}
+
+static unsigned
+XScale_cp15_write_reg (ARMul_State * state ATTRIBUTE_UNUSED,
+ unsigned reg,
+ ARMword value)
+{
+ /* FIXME: Not sure what to do about the alternative register set
+ here. For now default to just accessing CRm == 0 registers. */
+ write_cp15_reg (state, reg, 0, 0, value);
+
+ return TRUE;
+}
+
+/* Check for special XScale memory access features. */
+
+void
+XScale_check_memacc (ARMul_State * state, ARMword * address, int store)
+{
+ ARMword dbcon, r0, r1;
+ int e1, e0;
+
+ if (!state->is_XScale)
+ return;
+
+ /* Check for PID-ification.
+ XXX BTB access support will require this test failing. */
+ r0 = (read_cp15_reg (13, 0, 0) & 0xfe000000);
+ if (r0 && (* address & 0xfe000000) == 0)
+ * address |= r0;
+
+ /* Check alignment fault enable/disable. */
+ if ((read_cp15_reg (1, 0, 0) & ARMul_CP15_R1_ALIGN) && (* address & 3))
+ {
+ /* Set the FSR and FAR.
+ Do not use XScale_set_fsr_far as this checks the DCSR register. */
+ write_cp15_reg (state, 5, 0, 0, ARMul_CP15_R5_MMU_EXCPT);
+ write_cp15_reg (state, 6, 0, 0, * address);
+
+ ARMul_Abort (state, ARMul_DataAbortV);
+ }
+
+ if (XScale_debug_moe (state, -1))
+ return;
+
+ /* Check the data breakpoint registers. */
+ dbcon = read_cp15_reg (14, 0, 4);
+ r0 = read_cp15_reg (14, 0, 0);
+ r1 = read_cp15_reg (14, 0, 3);
+ e0 = dbcon & ARMul_CP15_DBCON_E0;
+
+ if (dbcon & ARMul_CP15_DBCON_M)
+ {
+ /* r1 is a inverse mask. */
+ if (e0 != 0 && ((store && e0 != 3) || (!store && e0 != 1))
+ && ((* address & ~r1) == (r0 & ~r1)))
+ {
+ XScale_debug_moe (state, ARMul_CP14_R10_MOE_DB);
+ ARMul_OSHandleSWI (state, SWI_Breakpoint);
+ }
+ }
+ else
+ {
+ if (e0 != 0 && ((store && e0 != 3) || (!store && e0 != 1))
+ && ((* address & ~3) == (r0 & ~3)))
+ {
+ XScale_debug_moe (state, ARMul_CP14_R10_MOE_DB);
+ ARMul_OSHandleSWI (state, SWI_Breakpoint);
+ }
+
+ e1 = (dbcon & ARMul_CP15_DBCON_E1) >> 2;
+ if (e1 != 0 && ((store && e1 != 3) || (!store && e1 != 1))
+ && ((* address & ~3) == (r1 & ~3)))
+ {
+ XScale_debug_moe (state, ARMul_CP14_R10_MOE_DB);
+ ARMul_OSHandleSWI (state, SWI_Breakpoint);
+ }
+ }
+}
+
+/* Set the XScale FSR and FAR registers. */
+
+void
+XScale_set_fsr_far (ARMul_State * state, ARMword fsr, ARMword far)
+{
+ if (!state->is_XScale || (read_cp14_reg (10) & (1UL << 31)) == 0)
+ return;
+
+ write_cp15_reg (state, 5, 0, 0, fsr);
+ write_cp15_reg (state, 6, 0, 0, far);
+}
+
+/* Set the XScale debug `method of entry' if it is enabled. */
+
+int
+XScale_debug_moe (ARMul_State * state, int moe)
+{
+ ARMword value;
+
+ if (!state->is_XScale)
+ return 1;
+
+ value = read_cp14_reg (10);
+ if (value & (1UL << 31))
+ {
+ if (moe != -1)
+ {
+ value &= ~0x1c;
+ value |= moe;
+
+ write_cp14_reg (10, value);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/* Coprocessor 13: Interrupt Controller and Bus Controller. */