[Rpcemu] Patch to abort model for ARM6/7

Sprow webpages at sprow.co.uk
Mon Jan 27 14:38:50 PST 2014


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;
+	}
 }
 
 #endif
diff -r e0959b8f016a src/codegen_amd64.c
--- a/src/codegen_amd64.c	Sun Jan 26 09:00:47 2014 +0000
+++ b/src/codegen_amd64.c	Mon Jan 27 09:06:43 2014 +0000
@@ -442,8 +442,10 @@
 	/* .notinbuffer */
 	gen_x86_jump_here(jump_notinbuffer);
 	gen_x86_call(readmemfl);
-	addbyte(0xf6); addbyte(0x04); addbyte(0x25); addlong(&armirq);
addbyte(0x40); /* TESTB $0x40,armirq */
-	gen_x86_jump(CC_NZ, 0);
+	if (arm.base == AbortModel_BaseRestored) {
+		addbyte(0xf6); addbyte(0x04); addbyte(0x25); addlong(&armirq);
addbyte(0x40); /* TESTB $0x40,armirq */
+		gen_x86_jump(CC_NZ, 0);
+	}
 	/* .nextbit */
 	gen_x86_jump_here(jump_nextbit);
 	/* Rotate if load is unaligned */
@@ -468,8 +470,10 @@
 	/* .notinbuffer */
 	gen_x86_jump_here(jump_notinbuffer);
 	gen_x86_call(readmemfb);
-	addbyte(0xf6); addbyte(0x04); addbyte(0x25); addlong(&armirq);
addbyte(0x40); /* TESTB $0x40,armirq */
-	gen_x86_jump(CC_NZ, 0);
+	if (arm.base == AbortModel_BaseRestored) {
+		addbyte(0xf6); addbyte(0x04); addbyte(0x25); addlong(&armirq);
addbyte(0x40); /* TESTB $0x40,armirq */
+		gen_x86_jump(CC_NZ, 0);
+	}
 	/* .nextbit */
 	gen_x86_jump_here(jump_nextbit);
 }
@@ -491,8 +495,10 @@
 	/* .notinbuffer */
 	gen_x86_jump_here(jump_notinbuffer);
 	gen_x86_call(writememfl);
-	addbyte(0xf6); addbyte(0x04); addbyte(0x25); addlong(&armirq);
addbyte(0x40); /* TESTB $0x40,armirq */
-	gen_x86_jump(CC_NZ, 0);
+	if (arm.base == AbortModel_BaseRestored) {
+		addbyte(0xf6); addbyte(0x04); addbyte(0x25); addlong(&armirq);
addbyte(0x40); /* TESTB $0x40,armirq */
+		gen_x86_jump(CC_NZ, 0);
+	}
 	/* .nextbit */
 	gen_x86_jump_here(jump_nextbit);
 }
@@ -513,8 +519,10 @@
 	/* .notinbuffer */
 	gen_x86_jump_here(jump_notinbuffer);
 	gen_x86_call(writememfb);
-	addbyte(0xf6); addbyte(0x04); addbyte(0x25); addlong(&armirq);
addbyte(0x40); /* TESTB $0x40,armirq */
-	gen_x86_jump(CC_NZ, 0);
+	if (arm.base == AbortModel_BaseRestored) {
+		addbyte(0xf6); addbyte(0x04); addbyte(0x25); addlong(&armirq);
addbyte(0x40); /* TESTB $0x40,armirq */
+		gen_x86_jump(CC_NZ, 0);
+	}
 	/* .nextbit */
 	gen_x86_jump_here(jump_nextbit);
 }
diff -r e0959b8f016a src/codegen_x86.c
--- a/src/codegen_x86.c	Sun Jan 26 09:00:47 2014 +0000
+++ b/src/codegen_x86.c	Mon Jan 27 09:06:53 2014 +0000
@@ -763,8 +763,10 @@
 	gen_x86_jump_here(jump_notinbuffer);
 	gen_x86_mov_reg32_stack(EDI, 0);
 	gen_x86_call(readmemfl);
-	addbyte(0xf6); addbyte(0x05); addlong(&armirq); addbyte(0x40); /* TESTB
$0x40,armirq */
-	gen_x86_jump(CC_NZ, 0);
+	if (arm.base == AbortModel_BaseRestored) {
+		addbyte(0xf6); addbyte(0x05); addlong(&armirq); addbyte(0x40); /* TESTB
$0x40,armirq */
+		gen_x86_jump(CC_NZ, 0);
+	}
 	/* .nextbit */
 	gen_x86_jump_here(jump_nextbit);
 	/* Rotate if load is unaligned */
@@ -789,8 +791,10 @@
 	gen_x86_jump_here(jump_notinbuffer);
 	gen_x86_mov_reg32_stack(EBX, 0);
 	gen_x86_call(readmemfb);
-	addbyte(0xf6); addbyte(0x05); addlong(&armirq); addbyte(0x40); /* TESTB
$0x40,armirq */
-	gen_x86_jump(CC_NZ, 0);
+	if (arm.base == AbortModel_BaseRestored) {
+		addbyte(0xf6); addbyte(0x05); addlong(&armirq); addbyte(0x40); /* TESTB
$0x40,armirq */
+		gen_x86_jump(CC_NZ, 0);
+	}
 	/* .nextbit */
 	gen_x86_jump_here(jump_nextbit);
 }
@@ -814,8 +818,10 @@
 	gen_x86_mov_reg32_stack(EDI, 0);
 	gen_x86_mov_reg32_stack(ECX, 4);
 	gen_x86_call(writememfl);
-	addbyte(0xf6); addbyte(0x05); addlong(&armirq); addbyte(0x40); /* TESTB
$0x40,armirq */
-	gen_x86_jump(CC_NZ, 0);
+	if (arm.base == AbortModel_BaseRestored) {
+		addbyte(0xf6); addbyte(0x05); addlong(&armirq); addbyte(0x40); /* TESTB
$0x40,armirq */
+		gen_x86_jump(CC_NZ, 0);
+	}
 	/* .nextbit */
 	gen_x86_jump_here(jump_nextbit);
 }
@@ -837,8 +843,10 @@
 	gen_x86_mov_reg32_stack(EBX, 0);
 	gen_x86_mov_reg32_stack(ECX, 4);
 	gen_x86_call(writememfb);
-	addbyte(0xf6); addbyte(0x05); addlong(&armirq); addbyte(0x40); /* TESTB
$0x40,armirq */
-	gen_x86_jump(CC_NZ, 0);
+	if (arm.base == AbortModel_BaseRestored) {
+		addbyte(0xf6); addbyte(0x05); addlong(&armirq); addbyte(0x40); /* TESTB
$0x40,armirq */
+		gen_x86_jump(CC_NZ, 0);
+	}
 	/* .nextbit */
 	gen_x86_jump_here(jump_nextbit);
 }




More information about the Rpcemu mailing list