Hi,
The emulation of memory accesses (LDR[B]/STR[B]/LDM/STM) in RPCEmu currently
mostly follows the base restored model, ie. StrongARM or ARM810 class.
This means that RISC OS 5 hangs when entering the desktop as it has detected
that the base restored model is in use and activated lazy task swapping,
when in fact the processor is an ARM610 (or 710 or 7500 or 7500FE).
Older versions of RISC OS get away with this as they have a fixed table
mapping from the processor type from CP15 to know which ones can do lazy
task swapping, rather than auto detecting it.
The following patch fixes the interpreter to emulate "base updated" and
"base restored" abort models (there looked to be partial support for this
with the stm_writeback_at_end flag already) and so allow RISC OS 5 to boot
properly in something other than StrongARM mode.
The patch also does the same for the dynamic recompiler, though the
emulation still isn't quite accurate enough in non-StrongARM mode. Some more
investigation would be needed to work out what's different between the
interpreter and recompiler, since they should behave the same.
It uses the following truth table
Abort model? Was there an abort? Base register
Base updated No Updated
Base restored No Updated
Base updated Yes Updated
Base restored Yes No
The patch is quite long but it's all a copy-and-paste job of the above
principle,
Sprow.
diff -r e0959b8f016a src/ArmDynarecOps.h
--- a/src/ArmDynarecOps.h Sun Jan 26 09:00:47 2014 +0000
+++ b/src/ArmDynarecOps.h Sun Jan 26 23:07:30 2014 +0000
@@ -941,7 +941,7 @@
memmode = templ;
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
return 1;
/* Writeback */
@@ -956,7 +956,7 @@
addr += addr2;
arm.reg[RN] = addr;
- return 0;
+ return (armirq & 0x40);
}
static int opLDRT(uint32_t opcode)
@@ -973,7 +973,7 @@
memmode = templ;
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
return 1;
/* Rotate if load is unaligned */
@@ -994,7 +994,7 @@
/* Write Rd */
LOADREG(RD, templ2);
- return 0;
+ return (armirq & 0x40);
}
static int opSTRBT(uint32_t opcode)
@@ -1011,7 +1011,7 @@
memmode = templ;
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
return 1;
/* Writeback */
@@ -1026,7 +1026,7 @@
addr += addr2;
arm.reg[RN] = addr;
- return 0;
+ return (armirq & 0x40);
}
static int opLDRBT(uint32_t opcode)
@@ -1043,7 +1043,7 @@
memmode = templ;
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
return 1;
/* Writeback */
@@ -1061,7 +1061,7 @@
/* Write Rd */
LOADREG(RD, templ2);
- return 0;
+ return (armirq & 0x40);
}
static int opSTR(uint32_t opcode)
@@ -1098,7 +1098,7 @@
writememl(addr & ~3, value);
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
return 1;
if (!(opcode & 0x1000000)) {
@@ -1110,7 +1110,7 @@
arm.reg[RN] = addr;
}
- return 0;
+ return (armirq & 0x40);
}
static int opLDR(uint32_t opcode)
@@ -1144,7 +1144,7 @@
templ = readmeml(addr & ~3);
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
return 1;
/* Rotate if load is unaligned */
@@ -1162,7 +1162,7 @@
/* Write Rd */
LOADREG(RD, templ);
- return 0;
+ return (armirq & 0x40);
}
static int opSTRB(uint32_t opcode)
@@ -1195,7 +1195,7 @@
writememb(addr, arm.reg[RD]);
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
return 1;
if (!(opcode & 0x1000000)) {
@@ -1207,7 +1207,7 @@
arm.reg[RN] = addr;
}
- return 0;
+ return (armirq & 0x40);
}
static int opLDRB(uint32_t opcode)
@@ -1241,7 +1241,7 @@
templ = readmemb(addr);
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
return 1;
if (!(opcode & 0x1000000)) {
@@ -1256,7 +1256,7 @@
/* Write Rd */
LOADREG(RD, templ);
- return 0;
+ return (armirq & 0x40);
}
static int opSTMD(uint32_t opcode)
diff -r e0959b8f016a src/arm.c
--- a/src/arm.c Sun Jan 26 09:00:47 2014 +0000
+++ b/src/arm.c Sun Jan 26 23:07:30 2014 +0000
@@ -291,10 +291,10 @@
pccache=0xFFFFFFFF;
if (cpu_model == CPUModel_SA110 || cpu_model == CPUModel_ARM810) {
r15diff = 0;
- arm.stm_writeback_at_end = 1;
+ arm.base = AbortModel_BaseRestored;
} else {
r15diff = 4;
- arm.stm_writeback_at_end = 0;
+ arm.base = AbortModel_BaseUpdated;
}
}
@@ -1373,7 +1373,7 @@
memmode = templ;
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
break;
/* Writeback */
@@ -1402,7 +1402,7 @@
memmode = templ;
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
break;
/* Rotate if load is unaligned */
@@ -1437,7 +1437,7 @@
memmode = templ;
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
break;
/* Writeback */
@@ -1466,7 +1466,7 @@
memmode = templ;
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
break;
/* Writeback */
@@ -1527,7 +1527,7 @@
writememl(addr & ~3, templ);
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
break;
if (!(opcode & 0x1000000)) {
@@ -1578,7 +1578,7 @@
templ = readmeml(addr & ~3);
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
break;
/* Rotate if load is unaligned */
@@ -1635,7 +1635,7 @@
writememb(addr, arm.reg[RD]);
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
break;
if (!(opcode & 0x1000000)) {
@@ -1686,7 +1686,7 @@
templ = readmemb(addr);
/* Check for Abort */
- if (armirq & 0x40)
+ if ((arm.base == AbortModel_BaseRestored) && (armirq & 0x40))
break;
if (!(opcode & 0x1000000)) {
diff -r e0959b8f016a src/arm.h
--- a/src/arm.h Sun Jan 26 09:00:47 2014 +0000
+++ b/src/arm.h Sun Jan 26 23:07:30 2014 +0000
@@ -3,9 +3,14 @@
#include "rpcemu.h"
+typedef enum {
+ AbortModel_BaseRestored,
+ AbortModel_BaseUpdated
+} AbortModel;
+
typedef struct {
uint32_t reg[18];
- uint8_t stm_writeback_at_end;
+ AbortModel base;
} ARMState;
typedef void (*OpFn)(uint32_t opcode);
diff -r e0959b8f016a src/arm_common.h
--- a/src/arm_common.h Sun Jan 26 09:00:47 2014 +0000
+++ b/src/arm_common.h Sun Jan 26 23:07:30 2014 +0000
@@ -342,11 +342,9 @@
static inline void
arm_store_multiple(uint32_t opcode, uint32_t address, uint32_t writeback)
{
- uint32_t orig_base, addr, mask, exception_flags;
+ uint32_t addr, mask, exception_flags;
int c;
- orig_base = arm.reg[RN];
-
addr = address & ~3;
/* Store first register */
@@ -365,7 +363,8 @@
c++;
/* Perform Writeback (if requested) at end of 2nd cycle */
- if (!arm.stm_writeback_at_end && (opcode & (1 << 21)) && (RN != 15)) {
+ if ((arm.base == AbortModel_BaseUpdated) &&
+ (opcode & (1 << 21)) && (RN != 15)) {
arm.reg[RN] = writeback;
}
@@ -385,16 +384,15 @@
exception_flags |= armirq;
}
- /* Perform Writeback (if requested) at end of instruction (SA110) */
- if (arm.stm_writeback_at_end && (opcode & (1 << 21)) && (RN != 15)) {
- arm.reg[RN] = writeback;
+ /* If a Data Abort occurred, update the Data Abort flag */
+ if (exception_flags & 0x40) {
+ armirq |= 0x40;
}
- /* If a Data Abort occurred, update the Data Abort flag and restore
- the Base Register to the value it had before the instruction */
- if (exception_flags & 0x40) {
- armirq |= 0x40;
- arm.reg[RN] = orig_base;
+ /* Perform Writeback (if requested) at end of instruction */
+ if ((arm.base == AbortModel_BaseRestored) && !(armirq & 0x40) &&
+ (opcode & (1 << 21)) && (RN != 15)) {
+ arm.reg[RN] = writeback;
}
}
@@ -412,11 +410,9 @@
static inline void
arm_store_multiple_s(uint32_t opcode, uint32_t address, uint32_t writeback)
{
- uint32_t orig_base, addr, mask, exception_flags;
+ uint32_t addr, mask, exception_flags;
int c;
- orig_base = arm.reg[RN];
-
addr = address & ~3;
/* Store first register */
@@ -435,7 +431,8 @@
c++;
/* Perform Writeback (if requested) at end of 2nd cycle */
- if (!arm.stm_writeback_at_end && (opcode & (1 << 21)) && (RN != 15)) {
+ if ((arm.base == AbortModel_BaseUpdated) &&
+ (opcode & (1 << 21)) && (RN != 15)) {
arm.reg[RN] = writeback;
}
@@ -455,16 +452,15 @@
exception_flags |= armirq;
}
- /* Perform Writeback (if requested) at end of instruction (SA110) */
- if (arm.stm_writeback_at_end && (opcode & (1 << 21)) && (RN != 15)) {
- arm.reg[RN] = writeback;
+ /* If a Data Abort occurred, update the Data Abort flag */
+ if (exception_flags & 0x40) {
+ armirq |= 0x40;
}
- /* If a Data Abort occurred, update the Data Abort flag and restore
- the Base Register to the value it had before the instruction */
- if (exception_flags & 0x40) {
- armirq |= 0x40;
- arm.reg[RN] = orig_base;
+ /* Perform Writeback (if requested) at end of instruction */
+ if ((arm.base == AbortModel_BaseRestored) && !(armirq & 0x40) &&
+ (opcode & (1 << 21)) && (RN != 15)) {
+ arm.reg[RN] = writeback;
}
}
@@ -523,7 +519,9 @@
/* A Data Abort occurred, restore the Base Register to the value it
had before the instruction */
data_abort:
- arm.reg[RN] = orig_base;
+ if (arm.base == AbortModel_BaseRestored) {
+ arm.reg[RN] = orig_base;
+ }
}
/**
@@ -599,7 +597,9 @@
/* A Data Abort occurred, restore the Base Register to the value it
had before the instruction */
data_abort:
- arm.reg[RN] = orig_base;
+ if (arm.base == AbortModel_BaseRestored) {
+ arm.reg[RN] = orig_base;
+ }
}
No comments:
Post a Comment