From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1L0vJu-0006t6-MH for qemu-devel@nongnu.org; Fri, 14 Nov 2008 04:50:30 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1L0vJt-0006s3-7G for qemu-devel@nongnu.org; Fri, 14 Nov 2008 04:50:29 -0500 Received: from [199.232.76.173] (port=55381 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1L0vJr-0006rm-Vz for qemu-devel@nongnu.org; Fri, 14 Nov 2008 04:50:28 -0500 Received: from mailout07.t-online.de ([194.25.134.83]:42874) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1L0vJr-0001YH-6b for qemu-devel@nongnu.org; Fri, 14 Nov 2008 04:50:27 -0500 Date: Fri, 14 Nov 2008 10:52:39 +0100 From: Simon Willcocks Message-ID: <6a7a0afe4f.simonwillcocks@home.invalid> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="1702313630--1763874744--1645907910" Subject: [Qemu-devel] [PATCH] ARM CPSR and conditional instructions - revisited Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org This message is in MIME format which your mailer apparently does not support. You either require a newer version of your software which supports MIME, or a separate MIME decoding utility. Alternatively, ask the sender of this message to resend it in a different format. --1702313630--1763874744--1645907910 Content-Type: text/plain; charset=us-ascii Hello, I was having a problem emulating some ARM code that I don't have any right to modify, and which has its origins back in the early nineties. I tracked it down to incorrect flag handling and found Justin Fletcher's post of Thu, 23 Nov 2006 via Google. Since his fix doesn't completely correct the problem, I've developed the attached patch and a simple test program. I haven't tested whether the flags are set correctly other than by the MSR instruction, but the code I'm emulating gets a lot further than it used to, which is a reasonable smoke test, and the modifications to the flag setting routines are reasonably minor. I doubt the modifications will result in a significant change in speed since, while the _cc routines are a little more complicated, the op_test_ routines have only got simpler and ARM code only sets the flags when they will be checked, and they are checked at least as often as they are set. This is a very useful project, thanks to everyone who has worked on it. Regards, Simon Willcocks -- ROLF - The RISC OS Look and Feel on Linux. http://stoppers.drobe.co.uk http://ro-lookandfeel.blogspot.com/ --1702313630--1763874744--1645907910 Content-Type: text/plain; charset=iso-8859-1; name="flags_test1.c" Content-Disposition: attachment; filename="flags_test1.c" Content-Transfer-Encoding: quoted-printable /* QEMU ARM Flags test * * Copyright (c) 2008 Simon Willcocks * * Permission is hereby granted, free of charge, to any person obtaining = a copy * of this software and associated documentation files (the "Software"), = to deal * in the Software without restriction, including without limitation the = rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or s= ell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be include= d in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRES= S OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILIT= Y, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHAL= L * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR O= THER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISIN= G FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS= IN * THE SOFTWARE. */ /* This is a test of the op_test_* routines in target-arm/op.c. * * The unpatched qemu-0.9.1 doesn't allow the N and Z flags to be * set simultaneously, although ARM processors allow it. * * The code to be emulated is: * * mov r0, #16 ; 0x10 * subs r0, r0, #1 ; 0x1 * svcvs 0x00000000 * lsl r1, r0, #28 * msr CPSR_f, r1 * mov r2, #0 ; 0x0 * orreq r2, r2, #1 ; 0x1 * orrne r2, r2, #2 ; 0x2 * orrcs r2, r2, #16 ; 0x10 * orrcc r2, r2, #32 ; 0x20 * orrmi r2, r2, #256 ; 0x100 * orrpl r2, r2, #512 ; 0x200 * orrvs r2, r2, #4096 ; 0x1000 * orrvc r2, r2, #8192 ; 0x2000 * orrhi r2, r2, #65536 ; 0x10000 * orrls r2, r2, #131072 ; 0x20000 * orrge r2, r2, #1048576 ; 0x100000 * orrlt r2, r2, #2097152 ; 0x200000 * orrgt r2, r2, #16777216 ; 0x1000000 * orrle r2, r2, #33554432 ; 0x2000000 * svc 0x00000000 * b 0xbfe115c8 * * The cpu_arm_exec routine exits for the caller to implement the svc (SW= I) * instructions (when executed), and continues after them when the routin= e is * called again. * * The value of r2 on exit should be a leading zero and a string of 1 and= 2s. */ /* Build library with: * * ./configure --disable-kqemu --disable-system --target-list=3Darm-linux= -user \ * --disable-sdl --disable-gfx-check --disable-vnc-tls && mak= e * * Compile this file with: * * gcc flags_test1.c -o flags_test1 qemu-0.9.1/arm-linux-user/libqemu.a \= * -I qemu-0.9.1 -DNEED_CPU_H -Iqemu-0.9.1/fpu -I qemu-0.9.1/arm-linu= x-user/ \ * -Wall qemu-0.9.1/osdep.c -I qemu-0.9.1/target-arm/ -D_XOPEN_SOURCE= =3D600 * * The unpatched library outputs the following: * * bf86f878: 0000000f 01111112 NzCV * bf86f878: 0000000e 02212112 NzCv * bf86f878: 0000000d 01121122 NzcV * bf86f878: 0000000c 02222122 Nzcv * bf86f878: 0000000b 01111112 NzCV * bf86f878: 0000000a 02212112 NzCv * bf86f878: 00000009 01121122 NzcV * bf86f878: 00000008 02222122 Nzcv * bf86f878: 00000007 02221211 nZCV * bf86f878: 00000006 02122211 nZCv * bf86f878: 00000005 02221221 nZcV * bf86f878: 00000004 02122221 nZcv * bf86f878: 00000003 02211212 nzCV * bf86f878: 00000002 01112212 nzCv * bf86f878: 00000001 02221222 nzcV * bf86f878: 00000000 01122222 nzcv * * Note that the first four lines are incorrect (although the conditional= * instructions are correct according to the stored flags). * * The patched library: * * bfe11618: 0000000f 02121111 NZCV * bfe11618: 0000000e 02222111 NZCv * bfe11618: 0000000d 02121121 NZcV * bfe11618: 0000000c 02222121 NZcv * bfe11618: 0000000b 01111112 NzCV * bfe11618: 0000000a 02212112 NzCv * bfe11618: 00000009 01121122 NzcV * bfe11618: 00000008 02222122 Nzcv * bfe11618: 00000007 02221211 nZCV * bfe11618: 00000006 02122211 nZCv * bfe11618: 00000005 02221221 nZcV * bfe11618: 00000004 02122221 nZcv * bfe11618: 00000003 02211212 nzCV * bfe11618: 00000002 01112212 nzCv * bfe11618: 00000001 02221222 nzcV * bfe11618: 00000000 01122222 nzcv * */ #include #include #include "cpu.h" static const char *flag_string[16] =3D { "nzcv", "nzcV", "nzCv", "nzCV", "nZcv", "nZcV", "nZCv", "nZCV", "Nzcv", "NzcV", "NzCv", "NzCV", "NZcv", "NZcV", "NZCv", "NZCV" }; int main() { uint32_t code[] =3D { 0xE3A00010, 0xE2500001, 0x6F000000, 0xE1A01E00= ,=20 0xE128F001, 0xE3A02000, 0x3822001, 0x13822002, 0x23822010, 0x33822020, 0x43822C01, 0x53822C02, 0x63822A01, 0x73822A02, 0x83822801, 0x93822802, 0xA3822601, 0xB3822602, 0xC3822401, 0xD3822402, 0xef000000, 0xEAFFFFEA }; struct CPUARMState *state =3D cpu_arm_init( "any" ); state->user_mode_only =3D 1; cpu_set_log_filename("cpu_log.txt"); cpu_set_log(0xfff); //state->singlestep_enabled =3D 1; state->regs[15] =3D (uint32_t) code; while ((cpu_arm_exec( state ) =3D=3D EXCP_SWI && (state->regs[0] & 0x= 8000000) =3D=3D 0)) { printf( "%08x: %08x %08x %s\n", state->regs[15], state->regs[0], = state->regs[2], flag_string[(cpsr_read( state ) >> 28 )] ); } return 0; } --1702313630--1763874744--1645907910 Content-Type: text/plain; charset=iso-8859-1; name="qemu-0.9.1.patch" Content-Disposition: attachment; filename="qemu-0.9.1.patch" Content-Transfer-Encoding: quoted-printable diff -Naur qemu-0.9.1/target-arm/cpu.h qemu-0.9.1.patched/target-arm/cpu.= h --- qemu-0.9.1/target-arm/cpu.h 2008-01-06 20:38:44.000000000 +0100 +++ qemu-0.9.1.patched/target-arm/cpu.h 2008-11-12 10:35:29.000000000 +01= 00 @@ -84,9 +84,17 @@ uint32_t fiq_regs[5]; =20 /* cpsr flag cache for faster execution */ + /* All the NZCV flags may be set independently (MSR cpsr_f, #0xe0000= 000 works) */ + /* All the flags are zero or one; the flags are calculated less ofte= n than they are + * read, since ARM code only sets flags when it knows they will be r= ead and often + * they are read more than once. */ + /* I note an assumption that in boolean true is the value 1. i.e. (0= =3D=3D 0) =3D=3D 1. */ + /* TODO: Establish whether storing the flags as 8 bit values is more= efficient */ + uint32_t NF; /* 0 or 1 */ + uint32_t ZF; /* 0 or 1 */ uint32_t CF; /* 0 or 1 */ - uint32_t VF; /* V is the bit 31. All other bits are undefined */ - uint32_t NZF; /* N is bit 31. Z is computed from NZF */ + uint32_t VF; /* 0 or 1 */ + uint32_t QF; /* 0 or 1 */ uint32_t GE; /* cpsr[19:16] */ int thumb; /* cprs[5]. 0 =3D arm mode, 1 =3D thumb mode. */ @@ -255,10 +263,8 @@ /* Return the current xPSR value. */ static inline uint32_t xpsr_read(CPUARMState *env) { - int ZF; - ZF =3D (env->NZF =3D=3D 0); - return (env->NZF & 0x80000000) | (ZF << 30) - | (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << = 27) + return (env->NF << 31) | (env->ZF << 30) + | (env->CF << 29) | (env->VF << 28) | (env->QF << 27) | (env->thumb << 24) | ((env->condexec_bits & 3) << 25) | ((env->condexec_bits & 0xfc) << 8) | env->v7m.exception; @@ -267,11 +273,11 @@ /* Set the xPSR. Note that some bits of mask must be all-set or all-cle= ar. */ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t m= ask) { - /* NOTE: N =3D 1 and Z =3D 1 cannot be stored currently */ if (mask & CPSR_NZCV) { - env->NZF =3D (val & 0xc0000000) ^ 0x40000000; + env->NF =3D (val >> 31); + env->ZF =3D (val >> 30) & 1; env->CF =3D (val >> 29) & 1; - env->VF =3D (val << 3) & 0x80000000; + env->VF =3D (val >> 28) & 1; } if (mask & CPSR_Q) env->QF =3D ((val & CPSR_Q) !=3D 0); diff -Naur qemu-0.9.1/target-arm/helper.c qemu-0.9.1.patched/target-arm/h= elper.c --- qemu-0.9.1/target-arm/helper.c 2008-01-06 20:38:44.000000000 +0100 +++ qemu-0.9.1.patched/target-arm/helper.c 2008-11-12 10:37:26.000000000 = +0100 @@ -275,10 +275,8 @@ =20 uint32_t cpsr_read(CPUARMState *env) { - int ZF; - ZF =3D (env->NZF =3D=3D 0); - return env->uncached_cpsr | (env->NZF & 0x80000000) | (ZF << 30) | - (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27= ) + return env->uncached_cpsr | (env->NF << 31) | (env->ZF << 30) + | (env->CF << 29) | (env->VF << 28) | (env->QF << 27) | (env->thumb << 5) | ((env->condexec_bits & 3) << 25) | ((env->condexec_bits & 0xfc) << 8) | (env->GE << 16); @@ -286,11 +284,11 @@ =20 void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) { - /* NOTE: N =3D 1 and Z =3D 1 cannot be stored currently */ if (mask & CPSR_NZCV) { - env->NZF =3D (val & 0xc0000000) ^ 0x40000000; + env->NF =3D (val >> 31) & 1; + env->ZF =3D (val >> 30) & 1; env->CF =3D (val >> 29) & 1; - env->VF =3D (val << 3) & 0x80000000; + env->VF =3D (val >> 28) & 1; } if (mask & CPSR_Q) env->QF =3D ((val & CPSR_Q) !=3D 0); diff -Naur qemu-0.9.1/target-arm/op.c qemu-0.9.1.patched/target-arm/op.c --- qemu-0.9.1/target-arm/op.c 2008-01-06 20:38:44.000000000 +0100 +++ qemu-0.9.1.patched/target-arm/op.c 2008-11-12 10:14:42.000000000 +010= 0 @@ -141,9 +141,10 @@ unsigned int src1; src1 =3D T0; T0 +=3D T1; - env->NZF =3D T0; + env->NF =3D (T0 >> 31) & 1; + env->ZF =3D T0 =3D=3D 0; env->CF =3D T0 < src1; - env->VF =3D (src1 ^ T1 ^ -1) & (src1 ^ T0); + env->VF =3D (((src1 ^ T1 ^ -1) & (src1 ^ T0)) >> 31) & 1; } =20 void OPPROTO op_adcl_T0_T1(void) @@ -162,8 +163,9 @@ T0 +=3D T1 + 1; env->CF =3D T0 <=3D src1; } - env->VF =3D (src1 ^ T1 ^ -1) & (src1 ^ T0); - env->NZF =3D T0; + env->VF =3D (((src1 ^ T1 ^ -1) & (src1 ^ T0)) >> 31) & 1; + env->NF =3D (T0 >> 31) & 1; + env->ZF =3D T0 =3D=3D 0; FORCE_RET(); } =20 @@ -179,9 +181,10 @@ unsigned int src1; \ src1 =3D T0; \ T0 -=3D T1; \ - env->NZF =3D T0; \ + env->NF =3D (T0 >> 31) & 1; \ + env->ZF =3D T0 =3D=3D 0; \ env->CF =3D src1 >=3D T1; \ - env->VF =3D (src1 ^ T1) & (src1 ^ T0); \ + env->VF =3D (((src1 ^ T1) & (src1 ^ T0)) >> 31) & 1;\ res =3D T0; \ } \ \ @@ -201,8 +204,9 @@ T0 =3D T0 - T1; \ env->CF =3D src1 >=3D T1; \ } \ - env->VF =3D (src1 ^ T1) & (src1 ^ T0); \ - env->NZF =3D T0; \ + env->VF =3D (((src1 ^ T1) & (src1 ^ T0)) >> 31) & 1;\ + env->NF =3D (T0 >> 31) & 1; \ + env->ZF =3D T0 =3D=3D 0; \ res =3D T0; \ FORCE_RET(); \ } @@ -243,26 +247,28 @@ =20 void OPPROTO op_logic_T0_cc(void) { - env->NZF =3D T0; + env->NF =3D (T0 >> 31) & 1; + env->ZF =3D T0 =3D=3D 0; } =20 void OPPROTO op_logic_T1_cc(void) { - env->NZF =3D T1; + env->NF =3D (T1 >> 31) & 1; + env->ZF =3D T1 =3D=3D 0; } =20 #define EIP (env->regs[15]) =20 void OPPROTO op_test_eq(void) { - if (env->NZF =3D=3D 0) + if (env->ZF) GOTO_LABEL_PARAM(1);; FORCE_RET(); } =20 void OPPROTO op_test_ne(void) { - if (env->NZF !=3D 0) + if (!env->ZF) GOTO_LABEL_PARAM(1);; FORCE_RET(); } @@ -283,70 +289,70 @@ =20 void OPPROTO op_test_mi(void) { - if ((env->NZF & 0x80000000) !=3D 0) + if (env->NF) GOTO_LABEL_PARAM(1); FORCE_RET(); } =20 void OPPROTO op_test_pl(void) { - if ((env->NZF & 0x80000000) =3D=3D 0) + if (!env->NF) GOTO_LABEL_PARAM(1); FORCE_RET(); } =20 void OPPROTO op_test_vs(void) { - if ((env->VF & 0x80000000) !=3D 0) + if (env->VF) GOTO_LABEL_PARAM(1); FORCE_RET(); } =20 void OPPROTO op_test_vc(void) { - if ((env->VF & 0x80000000) =3D=3D 0) + if (!env->VF) GOTO_LABEL_PARAM(1); FORCE_RET(); } =20 void OPPROTO op_test_hi(void) { - if (env->CF !=3D 0 && env->NZF !=3D 0) + if (env->CF && !env->ZF) GOTO_LABEL_PARAM(1); FORCE_RET(); } =20 void OPPROTO op_test_ls(void) { - if (env->CF =3D=3D 0 || env->NZF =3D=3D 0) + if (!env->CF || env->ZF) GOTO_LABEL_PARAM(1); FORCE_RET(); } =20 void OPPROTO op_test_ge(void) { - if (((env->VF ^ env->NZF) & 0x80000000) =3D=3D 0) + if (env->VF =3D=3D env->NF) GOTO_LABEL_PARAM(1); FORCE_RET(); } =20 void OPPROTO op_test_lt(void) { - if (((env->VF ^ env->NZF) & 0x80000000) !=3D 0) + if (env->VF !=3D env->NF) GOTO_LABEL_PARAM(1); FORCE_RET(); } =20 void OPPROTO op_test_gt(void) { - if (env->NZF !=3D 0 && ((env->VF ^ env->NZF) & 0x80000000) =3D=3D 0)= + if (!env->ZF && (env->NF =3D=3D env->VF)) GOTO_LABEL_PARAM(1); FORCE_RET(); } =20 void OPPROTO op_test_le(void) { - if (env->NZF =3D=3D 0 || ((env->VF ^ env->NZF) & 0x80000000) !=3D 0)= + if (env->ZF || (env->NF !=3D env->VF)) GOTO_LABEL_PARAM(1); FORCE_RET(); } @@ -476,7 +482,8 @@ =20 void OPPROTO op_logicq_cc(void) { - env->NZF =3D (T1 & 0x80000000) | ((T0 | T1) !=3D 0); + env->ZF =3D ((T0 | T1) !=3D 0); + env->NF =3D (T1 >> 31); } =20 /* memory access */ @@ -902,7 +909,8 @@ env->CF =3D (T0 >> (32 - shift)) & 1; T0 =3D T0 << shift; } - env->NZF =3D T0; + env->NF =3D (T0 >> 31) & 1; + env->ZF =3D T0 =3D=3D 0; FORCE_RET(); } =20 @@ -924,7 +932,8 @@ env->CF =3D (T0 >> (shift - 1)) & 1; T0 =3D T0 >> shift; } - env->NZF =3D T0; + env->NF =3D (T0 >> 31) & 1; + env->ZF =3D T0 =3D=3D 0; FORCE_RET(); } =20 @@ -953,7 +962,8 @@ env->CF =3D (T0 >> (shift - 1)) & 1; T0 =3D ((int32_t)T0) >> shift; } - env->NZF =3D T0; + env->NF =3D (T0 >> 31) & 1; + env->ZF =3D T0 =3D=3D 0; FORCE_RET(); } =20 --1702313630--1763874744--1645907910--