25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: cpu_arm_instr.c,v 1.29 2005/10/11 03:31:28 debug Exp $ |
* $Id: cpu_arm_instr.c,v 1.39 2005/10/27 14:01:13 debug Exp $ |
29 |
* |
* |
30 |
* ARM instructions. |
* ARM instructions. |
31 |
* |
* |
36 |
*/ |
*/ |
37 |
|
|
38 |
|
|
39 |
|
#include "arm_quick_pc_to_pointers.h" |
40 |
|
|
41 |
|
/* #define GATHER_BDT_STATISTICS */ |
42 |
|
|
43 |
|
|
44 |
|
#ifdef GATHER_BDT_STATISTICS |
45 |
|
/* |
46 |
|
* update_bdt_statistics(): |
47 |
|
* |
48 |
|
* Gathers statistics about load/store multiple instructions. |
49 |
|
* |
50 |
|
* NOTE/TODO: Perhaps it would be more memory efficient to swap the high |
51 |
|
* and low parts of the instruction word, so that the lllllll bits become |
52 |
|
* the high bits; this would cause fewer host pages to be used. Anyway, the |
53 |
|
* current implementation works on hosts with lots of RAM. |
54 |
|
* |
55 |
|
* The resulting file, bdt_statistics.txt, should then be processed like |
56 |
|
* this to give a new cpu_arm_multi.txt: |
57 |
|
* |
58 |
|
* uniq -c bdt_statistics.txt|sort -nr|head -256|cut -f 2 > cpu_arm_multi.txt |
59 |
|
*/ |
60 |
|
static void update_bdt_statistics(uint32_t iw) |
61 |
|
{ |
62 |
|
static FILE *f = NULL; |
63 |
|
static long long *counts; |
64 |
|
static char *counts_used; |
65 |
|
static long long n = 0; |
66 |
|
|
67 |
|
if (f == NULL) { |
68 |
|
size_t s = (1 << 24) * sizeof(long long); |
69 |
|
f = fopen("bdt_statistics.txt", "w"); |
70 |
|
if (f == NULL) { |
71 |
|
fprintf(stderr, "update_bdt_statistics(): :-(\n"); |
72 |
|
exit(1); |
73 |
|
} |
74 |
|
counts = zeroed_alloc(s); |
75 |
|
counts_used = zeroed_alloc(65536); |
76 |
|
} |
77 |
|
|
78 |
|
/* Drop the s-bit: xxxx100P USWLnnnn llllllll llllllll */ |
79 |
|
iw = ((iw & 0x01800000) >> 1) | (iw & 0x003fffff); |
80 |
|
|
81 |
|
counts_used[iw & 0xffff] = 1; |
82 |
|
counts[iw] ++; |
83 |
|
|
84 |
|
n ++; |
85 |
|
if ((n % 500000) == 0) { |
86 |
|
int i; |
87 |
|
long long j; |
88 |
|
fatal("[ update_bdt_statistics(): n = %lli ]\n", (long long) n); |
89 |
|
fseek(f, 0, SEEK_SET); |
90 |
|
for (i=0; i<0x1000000; i++) |
91 |
|
if (counts_used[i & 0xffff] && counts[i] != 0) { |
92 |
|
/* Recreate the opcode: */ |
93 |
|
uint32_t opcode = ((i & 0x00c00000) << 1) |
94 |
|
| (i & 0x003fffff) | 0x08000000; |
95 |
|
for (j=0; j<counts[i]; j++) |
96 |
|
fprintf(f, "0x%08x\n", opcode); |
97 |
|
} |
98 |
|
fflush(f); |
99 |
|
} |
100 |
|
} |
101 |
|
#endif |
102 |
|
|
103 |
|
|
104 |
|
/*****************************************************************************/ |
105 |
|
|
106 |
|
|
107 |
/* |
/* |
108 |
* Helper definitions: |
* Helper definitions: |
109 |
* |
* |
119 |
* condition code. (The NV condition code is not included, and the AL code |
* condition code. (The NV condition code is not included, and the AL code |
120 |
* uses the main foo function.) Y also defines an array with pointers to |
* uses the main foo function.) Y also defines an array with pointers to |
121 |
* all of these functions. |
* all of these functions. |
122 |
|
* |
123 |
|
* If the compiler is good enough (i.e. allows long enough code sequences |
124 |
|
* to be inlined), then the Y functions will be compiled as full (inlined) |
125 |
|
* functions, otherwise they will simply call the X function. |
126 |
*/ |
*/ |
127 |
|
|
128 |
#define Y(n) void arm_instr_ ## n ## __eq(struct cpu *cpu, \ |
#define Y(n) void arm_instr_ ## n ## __eq(struct cpu *cpu, \ |
248 |
cpu->pc = cpu->cd.arm.r[ARM_PC]; |
cpu->pc = cpu->cd.arm.r[ARM_PC]; |
249 |
|
|
250 |
/* Find the new physical page and update the translation pointers: */ |
/* Find the new physical page and update the translation pointers: */ |
251 |
arm_pc_to_pointers(cpu); |
quick_pc_to_pointers(cpu); |
252 |
} |
} |
253 |
Y(b) |
Y(b) |
254 |
|
|
280 |
cpu->pc &= ~3; |
cpu->pc &= ~3; |
281 |
|
|
282 |
/* Find the new physical page and update the translation pointers: */ |
/* Find the new physical page and update the translation pointers: */ |
283 |
arm_pc_to_pointers(cpu); |
quick_pc_to_pointers(cpu); |
284 |
} |
} |
285 |
Y(bx) |
Y(bx) |
286 |
|
|
302 |
cpu_functioncall_trace_return(cpu); |
cpu_functioncall_trace_return(cpu); |
303 |
|
|
304 |
/* Find the new physical page and update the translation pointers: */ |
/* Find the new physical page and update the translation pointers: */ |
305 |
arm_pc_to_pointers(cpu); |
quick_pc_to_pointers(cpu); |
306 |
} |
} |
307 |
Y(bx_trace) |
Y(bx_trace) |
308 |
|
|
330 |
cpu->pc = cpu->cd.arm.r[ARM_PC] = lr - 4 + (int32_t)ic->arg[0]; |
cpu->pc = cpu->cd.arm.r[ARM_PC] = lr - 4 + (int32_t)ic->arg[0]; |
331 |
|
|
332 |
/* Find the new physical page and update the translation pointers: */ |
/* Find the new physical page and update the translation pointers: */ |
333 |
arm_pc_to_pointers(cpu); |
quick_pc_to_pointers(cpu); |
334 |
} |
} |
335 |
Y(bl) |
Y(bl) |
336 |
|
|
362 |
cpu->pc &= ~3; |
cpu->pc &= ~3; |
363 |
|
|
364 |
/* Find the new physical page and update the translation pointers: */ |
/* Find the new physical page and update the translation pointers: */ |
365 |
arm_pc_to_pointers(cpu); |
quick_pc_to_pointers(cpu); |
366 |
} |
} |
367 |
Y(blx) |
Y(blx) |
368 |
|
|
392 |
cpu_functioncall_trace(cpu, cpu->pc); |
cpu_functioncall_trace(cpu, cpu->pc); |
393 |
|
|
394 |
/* Find the new physical page and update the translation pointers: */ |
/* Find the new physical page and update the translation pointers: */ |
395 |
arm_pc_to_pointers(cpu); |
quick_pc_to_pointers(cpu); |
396 |
} |
} |
397 |
Y(bl_trace) |
Y(bl_trace) |
398 |
|
|
455 |
Y(bl_samepage_trace) |
Y(bl_samepage_trace) |
456 |
|
|
457 |
|
|
458 |
|
#include "cpu_arm_instr_misc.c" |
459 |
|
|
460 |
|
|
461 |
/* |
/* |
462 |
* mul: Multiplication |
* mul: Multiplication |
463 |
* |
* |
472 |
Y(mul) |
Y(mul) |
473 |
X(muls) |
X(muls) |
474 |
{ |
{ |
475 |
uint32_t result = reg(ic->arg[1]) * reg(ic->arg[2]); |
uint32_t result; |
476 |
|
result = reg(ic->arg[1]) * reg(ic->arg[2]); |
477 |
cpu->cd.arm.cpsr &= ~(ARM_FLAG_Z | ARM_FLAG_N); |
cpu->cd.arm.cpsr &= ~(ARM_FLAG_Z | ARM_FLAG_N); |
478 |
if (result == 0) |
if (result == 0) |
479 |
cpu->cd.arm.cpsr |= ARM_FLAG_Z; |
cpu->cd.arm.cpsr |= ARM_FLAG_Z; |
493 |
{ |
{ |
494 |
/* xxxx0000 00ASdddd nnnnssss 1001mmmm (Rd,Rm,Rs[,Rn]) */ |
/* xxxx0000 00ASdddd nnnnssss 1001mmmm (Rd,Rm,Rs[,Rn]) */ |
495 |
uint32_t iw = ic->arg[0]; |
uint32_t iw = ic->arg[0]; |
496 |
int rd = (iw >> 16) & 15, rn = (iw >> 12) & 15, |
int rd, rs, rn, rm; |
497 |
rs = (iw >> 8) & 15, rm = iw & 15; |
rd = (iw >> 16) & 15; rn = (iw >> 12) & 15, |
498 |
|
rs = (iw >> 8) & 15; rm = iw & 15; |
499 |
cpu->cd.arm.r[rd] = cpu->cd.arm.r[rm] * cpu->cd.arm.r[rs] |
cpu->cd.arm.r[rd] = cpu->cd.arm.r[rm] * cpu->cd.arm.r[rs] |
500 |
+ cpu->cd.arm.r[rn]; |
+ cpu->cd.arm.r[rn]; |
501 |
} |
} |
504 |
{ |
{ |
505 |
/* xxxx0000 00ASdddd nnnnssss 1001mmmm (Rd,Rm,Rs[,Rn]) */ |
/* xxxx0000 00ASdddd nnnnssss 1001mmmm (Rd,Rm,Rs[,Rn]) */ |
506 |
uint32_t iw = ic->arg[0]; |
uint32_t iw = ic->arg[0]; |
507 |
int rd = (iw >> 16) & 15, rn = (iw >> 12) & 15, |
int rd, rs, rn, rm; |
508 |
rs = (iw >> 8) & 15, rm = iw & 15; |
rd = (iw >> 16) & 15; rn = (iw >> 12) & 15, |
509 |
|
rs = (iw >> 8) & 15; rm = iw & 15; |
510 |
cpu->cd.arm.r[rd] = cpu->cd.arm.r[rm] * cpu->cd.arm.r[rs] |
cpu->cd.arm.r[rd] = cpu->cd.arm.r[rm] * cpu->cd.arm.r[rs] |
511 |
+ cpu->cd.arm.r[rn]; |
+ cpu->cd.arm.r[rn]; |
512 |
cpu->cd.arm.cpsr &= ~(ARM_FLAG_Z | ARM_FLAG_N); |
cpu->cd.arm.cpsr &= ~(ARM_FLAG_Z | ARM_FLAG_N); |
526 |
X(mull) |
X(mull) |
527 |
{ |
{ |
528 |
/* xxxx0000 1UAShhhh llllssss 1001mmmm */ |
/* xxxx0000 1UAShhhh llllssss 1001mmmm */ |
529 |
uint32_t iw = ic->arg[0]; |
uint32_t iw; uint64_t tmp; int u_bit, a_bit; |
530 |
int u_bit = (iw >> 22) & 1, a_bit = (iw >> 21) & 1; |
iw = ic->arg[0]; |
531 |
uint64_t tmp = cpu->cd.arm.r[iw & 15]; |
u_bit = (iw >> 22) & 1; a_bit = (iw >> 21) & 1; |
532 |
|
tmp = cpu->cd.arm.r[iw & 15]; |
533 |
if (u_bit) |
if (u_bit) |
534 |
tmp = (int64_t)(int32_t)tmp |
tmp = (int64_t)(int32_t)tmp |
535 |
* (int64_t)(int32_t)cpu->cd.arm.r[(iw >> 8) & 15]; |
* (int64_t)(int32_t)cpu->cd.arm.r[(iw >> 8) & 15]; |
564 |
|
|
565 |
/* |
/* |
566 |
* ret_trace: "mov pc,lr" with trace enabled |
* ret_trace: "mov pc,lr" with trace enabled |
567 |
|
* ret: "mov pc,lr" without trace enabled |
568 |
* |
* |
569 |
* arg[0] = ignored |
* arg[0] = ignored |
570 |
*/ |
*/ |
571 |
X(ret_trace) |
X(ret_trace) |
572 |
{ |
{ |
573 |
uint32_t old_pc = cpu->cd.arm.r[ARM_PC]; |
uint32_t old_pc, mask_within_page; |
574 |
uint32_t mask_within_page = ((ARM_IC_ENTRIES_PER_PAGE-1) |
old_pc = cpu->cd.arm.r[ARM_PC]; |
575 |
|
mask_within_page = ((ARM_IC_ENTRIES_PER_PAGE-1) |
576 |
<< ARM_INSTR_ALIGNMENT_SHIFT) | |
<< ARM_INSTR_ALIGNMENT_SHIFT) | |
577 |
((1 << ARM_INSTR_ALIGNMENT_SHIFT) - 1); |
((1 << ARM_INSTR_ALIGNMENT_SHIFT) - 1); |
578 |
|
|
590 |
((cpu->pc & mask_within_page) >> ARM_INSTR_ALIGNMENT_SHIFT); |
((cpu->pc & mask_within_page) >> ARM_INSTR_ALIGNMENT_SHIFT); |
591 |
} else { |
} else { |
592 |
/* Find the new physical page and update pointers: */ |
/* Find the new physical page and update pointers: */ |
593 |
arm_pc_to_pointers(cpu); |
quick_pc_to_pointers(cpu); |
594 |
} |
} |
595 |
} |
} |
596 |
Y(ret_trace) |
Y(ret_trace) |
597 |
|
X(ret) |
598 |
|
{ |
599 |
|
cpu->pc = cpu->cd.arm.r[ARM_PC] = cpu->cd.arm.r[ARM_LR]; |
600 |
|
quick_pc_to_pointers(cpu); |
601 |
|
} |
602 |
|
Y(ret) |
603 |
|
|
604 |
|
|
605 |
/* |
/* |
759 |
cpu->pc = cpu->cd.arm.r[ARM_PC] = cpu->cd.arm.r[ARM_LR]; |
cpu->pc = cpu->cd.arm.r[ARM_PC] = cpu->cd.arm.r[ARM_LR]; |
760 |
if (cpu->machine->show_trace_tree) |
if (cpu->machine->show_trace_tree) |
761 |
cpu_functioncall_trace_return(cpu); |
cpu_functioncall_trace_return(cpu); |
762 |
arm_pc_to_pointers(cpu); |
quick_pc_to_pointers(cpu); |
763 |
} |
} |
764 |
|
|
765 |
|
|
787 |
} else if (cpu->pc != old_pc) { |
} else if (cpu->pc != old_pc) { |
788 |
/* PC was changed by the SWI call. Find the new physical |
/* PC was changed by the SWI call. Find the new physical |
789 |
page and update the translation pointers: */ |
page and update the translation pointers: */ |
790 |
arm_pc_to_pointers(cpu); |
quick_pc_to_pointers(cpu); |
791 |
} |
} |
792 |
} |
} |
793 |
Y(swi_useremul) |
Y(swi_useremul) |
879 |
struct arm_instr_call *); |
struct arm_instr_call *); |
880 |
X(store_w0_byte_u1_p0_imm); |
X(store_w0_byte_u1_p0_imm); |
881 |
X(store_w0_word_u1_p0_imm); |
X(store_w0_word_u1_p0_imm); |
882 |
|
X(load_w0_word_u1_p0_imm); |
883 |
|
X(load_w0_byte_u1_p1_imm); |
884 |
|
X(load_w0_byte_u1_p1_reg); |
885 |
|
|
886 |
extern void (*arm_load_store_instr_pc[1024])(struct cpu *, |
extern void (*arm_load_store_instr_pc[1024])(struct cpu *, |
887 |
struct arm_instr_call *); |
struct arm_instr_call *); |
898 |
struct arm_instr_call *); |
struct arm_instr_call *); |
899 |
X(cmps); |
X(cmps); |
900 |
X(sub); |
X(sub); |
901 |
|
X(add); |
902 |
X(subs); |
X(subs); |
903 |
|
|
904 |
|
|
923 |
int i, return_flag = 0; |
int i, return_flag = 0; |
924 |
uint32_t new_values[16]; |
uint32_t new_values[16]; |
925 |
|
|
926 |
|
#ifdef GATHER_BDT_STATISTICS |
927 |
|
if (!s_bit) |
928 |
|
update_bdt_statistics(iw); |
929 |
|
#endif |
930 |
|
|
931 |
/* Synchronize the program counter: */ |
/* Synchronize the program counter: */ |
932 |
low_pc = ((size_t)ic - (size_t) |
low_pc = ((size_t)ic - (size_t) |
933 |
cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call); |
cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call); |
1086 |
same page! */ |
same page! */ |
1087 |
/* Find the new physical page and update the |
/* Find the new physical page and update the |
1088 |
translation pointers: */ |
translation pointers: */ |
1089 |
arm_pc_to_pointers(cpu); |
quick_pc_to_pointers(cpu); |
1090 |
} |
} |
1091 |
} |
} |
1092 |
Y(bdt_load) |
Y(bdt_load) |
1111 |
int w_bit = iw & 0x00200000; |
int w_bit = iw & 0x00200000; |
1112 |
int i; |
int i; |
1113 |
|
|
1114 |
|
#ifdef GATHER_BDT_STATISTICS |
1115 |
|
if (!s_bit) |
1116 |
|
update_bdt_statistics(iw); |
1117 |
|
#endif |
1118 |
|
|
1119 |
/* Synchronize the program counter: */ |
/* Synchronize the program counter: */ |
1120 |
low_pc = ((size_t)ic - (size_t) |
low_pc = ((size_t)ic - (size_t) |
1121 |
cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call); |
cpu->cd.arm.cur_ic_page) / sizeof(struct arm_instr_call); |
1209 |
Y(bdt_store) |
Y(bdt_store) |
1210 |
|
|
1211 |
|
|
1212 |
|
/* Various load/store multiple instructions: */ |
1213 |
|
#include "tmp_arm_multi.c" |
1214 |
|
|
1215 |
|
|
1216 |
/*****************************************************************************/ |
/*****************************************************************************/ |
1217 |
|
|
1218 |
|
|
1361 |
} |
} |
1362 |
|
|
1363 |
|
|
1364 |
|
/* |
1365 |
|
* netbsd_memset: |
1366 |
|
* |
1367 |
|
* The core of a NetBSD/arm memset. |
1368 |
|
* |
1369 |
|
* f01bc420: e25XX080 subs rX,rX,#0x80 |
1370 |
|
* f01bc424: a8ac000c stmgeia ip!,{r2,r3} (16 of these) |
1371 |
|
* .. |
1372 |
|
* f01bc464: caffffed bgt 0xf01bc420 <memset+0x38> |
1373 |
|
*/ |
1374 |
|
X(netbsd_memset) |
1375 |
|
{ |
1376 |
|
unsigned char *page; |
1377 |
|
uint32_t addr; |
1378 |
|
|
1379 |
|
do { |
1380 |
|
addr = cpu->cd.arm.r[ARM_IP]; |
1381 |
|
|
1382 |
|
instr(subs)(cpu, ic); |
1383 |
|
|
1384 |
|
if (((cpu->cd.arm.cpsr & ARM_FLAG_N)?1:0) != |
1385 |
|
((cpu->cd.arm.cpsr & ARM_FLAG_V)?1:0)) { |
1386 |
|
cpu->n_translated_instrs += 16; |
1387 |
|
/* Skip the store multiples: */ |
1388 |
|
cpu->cd.arm.next_ic = &ic[17]; |
1389 |
|
return; |
1390 |
|
} |
1391 |
|
|
1392 |
|
/* Crossing a page boundary? Then continue non-combined. */ |
1393 |
|
if ((addr & 0xfff) + 128 > 0x1000) |
1394 |
|
return; |
1395 |
|
|
1396 |
|
/* R2/R3 non-zero? Not allowed here. */ |
1397 |
|
if (cpu->cd.arm.r[2] != 0 || cpu->cd.arm.r[3] != 0) |
1398 |
|
return; |
1399 |
|
|
1400 |
|
/* printf("addr = 0x%08x\n", addr); */ |
1401 |
|
|
1402 |
|
page = cpu->cd.arm.host_store[addr >> 12]; |
1403 |
|
/* No page translation? Continue non-combined. */ |
1404 |
|
if (page == NULL) |
1405 |
|
return; |
1406 |
|
|
1407 |
|
/* Clear: */ |
1408 |
|
memset(page + (addr & 0xfff), 0, 128); |
1409 |
|
cpu->cd.arm.r[ARM_IP] = addr + 128; |
1410 |
|
cpu->n_translated_instrs += 16; |
1411 |
|
|
1412 |
|
/* Branch back if greater: */ |
1413 |
|
cpu->n_translated_instrs += 1; |
1414 |
|
} while (((cpu->cd.arm.cpsr & ARM_FLAG_N)?1:0) == |
1415 |
|
((cpu->cd.arm.cpsr & ARM_FLAG_V)?1:0) && |
1416 |
|
!(cpu->cd.arm.cpsr & ARM_FLAG_Z)); |
1417 |
|
|
1418 |
|
/* Continue at the instruction after the bgt: */ |
1419 |
|
cpu->cd.arm.next_ic = &ic[18]; |
1420 |
|
} |
1421 |
|
|
1422 |
|
|
1423 |
|
/* |
1424 |
|
* netbsd_memcpy: |
1425 |
|
* |
1426 |
|
* The core of a NetBSD/arm memcpy. |
1427 |
|
* |
1428 |
|
* f01bc530: e8b15018 ldmia r1!,{r3,r4,ip,lr} |
1429 |
|
* f01bc534: e8a05018 stmia r0!,{r3,r4,ip,lr} |
1430 |
|
* f01bc538: e8b15018 ldmia r1!,{r3,r4,ip,lr} |
1431 |
|
* f01bc53c: e8a05018 stmia r0!,{r3,r4,ip,lr} |
1432 |
|
* f01bc540: e2522020 subs r2,r2,#0x20 |
1433 |
|
* f01bc544: aafffff9 bge 0xf01bc530 |
1434 |
|
*/ |
1435 |
|
X(netbsd_memcpy) |
1436 |
|
{ |
1437 |
|
unsigned char *page_0, *page_1; |
1438 |
|
uint32_t addr_r0, addr_r1; |
1439 |
|
|
1440 |
|
do { |
1441 |
|
addr_r0 = cpu->cd.arm.r[0]; |
1442 |
|
addr_r1 = cpu->cd.arm.r[1]; |
1443 |
|
|
1444 |
|
/* printf("addr_r0 = %08x r1 = %08x\n", addr_r0, addr_r1); */ |
1445 |
|
|
1446 |
|
/* Crossing a page boundary? Then continue non-combined. */ |
1447 |
|
if ((addr_r0 & 0xfff) + 32 > 0x1000 || |
1448 |
|
(addr_r1 & 0xfff) + 32 > 0x1000) { |
1449 |
|
instr(multi_0x08b15018)(cpu, ic); |
1450 |
|
return; |
1451 |
|
} |
1452 |
|
|
1453 |
|
page_0 = cpu->cd.arm.host_store[addr_r0 >> 12]; |
1454 |
|
page_1 = cpu->cd.arm.host_store[addr_r1 >> 12]; |
1455 |
|
|
1456 |
|
/* No page translations? Continue non-combined. */ |
1457 |
|
if (page_0 == NULL || page_1 == NULL) { |
1458 |
|
instr(multi_0x08b15018)(cpu, ic); |
1459 |
|
return; |
1460 |
|
} |
1461 |
|
|
1462 |
|
memcpy(page_0 + (addr_r0 & 0xfff), |
1463 |
|
page_1 + (addr_r1 & 0xfff), 32); |
1464 |
|
cpu->cd.arm.r[0] = addr_r0 + 32; |
1465 |
|
cpu->cd.arm.r[1] = addr_r1 + 32; |
1466 |
|
|
1467 |
|
cpu->n_translated_instrs += 4; |
1468 |
|
|
1469 |
|
instr(subs)(cpu, ic + 4); |
1470 |
|
cpu->n_translated_instrs ++; |
1471 |
|
|
1472 |
|
/* Loop while greater or equal: */ |
1473 |
|
cpu->n_translated_instrs ++; |
1474 |
|
} while (((cpu->cd.arm.cpsr & ARM_FLAG_N)?1:0) == |
1475 |
|
((cpu->cd.arm.cpsr & ARM_FLAG_V)?1:0)); |
1476 |
|
|
1477 |
|
/* Continue at the instruction after the bge: */ |
1478 |
|
cpu->cd.arm.next_ic = &ic[6]; |
1479 |
|
cpu->n_translated_instrs --; |
1480 |
|
} |
1481 |
|
|
1482 |
|
|
1483 |
|
/* |
1484 |
|
* netbsd_cacheclean: |
1485 |
|
* |
1486 |
|
* The core of a NetBSD/arm cache clean routine, variant 1: |
1487 |
|
* |
1488 |
|
* f015f88c: e4902020 ldr r2,[r0],#32 |
1489 |
|
* f015f890: e2511020 subs r1,r1,#0x20 |
1490 |
|
* f015f894: 1afffffc bne 0xf015f88c |
1491 |
|
* f015f898: ee070f9a mcr 15,0,r0,cr7,cr10,4 |
1492 |
|
*/ |
1493 |
|
X(netbsd_cacheclean) |
1494 |
|
{ |
1495 |
|
uint32_t r1 = cpu->cd.arm.r[1]; |
1496 |
|
cpu->n_translated_instrs += ((r1 >> 5) * 3); |
1497 |
|
cpu->cd.arm.next_ic = &ic[4]; |
1498 |
|
} |
1499 |
|
|
1500 |
|
|
1501 |
|
/* |
1502 |
|
* netbsd_cacheclean2: |
1503 |
|
* |
1504 |
|
* The core of a NetBSD/arm cache clean routine, variant 2: |
1505 |
|
* |
1506 |
|
* f015f93c: ee070f3a mcr 15,0,r0,cr7,cr10,1 |
1507 |
|
* f015f940: ee070f36 mcr 15,0,r0,cr7,cr6,1 |
1508 |
|
* f015f944: e2800020 add r0,r0,#0x20 |
1509 |
|
* f015f948: e2511020 subs r1,r1,#0x20 |
1510 |
|
* f015f94c: 8afffffa bhi 0xf015f93c |
1511 |
|
*/ |
1512 |
|
X(netbsd_cacheclean2) |
1513 |
|
{ |
1514 |
|
cpu->n_translated_instrs += ((cpu->cd.arm.r[1] >> 5) * 5) - 1; |
1515 |
|
cpu->cd.arm.next_ic = &ic[5]; |
1516 |
|
} |
1517 |
|
|
1518 |
|
|
1519 |
|
/* |
1520 |
|
* netbsd_scanc: |
1521 |
|
* |
1522 |
|
* f01bccbc: e5d13000 ldrb r3,[r1] |
1523 |
|
* f01bccc0: e7d23003 ldrb r3,[r2,r3] |
1524 |
|
* f01bccc4: e113000c tsts r3,ip |
1525 |
|
*/ |
1526 |
|
X(netbsd_scanc) |
1527 |
|
{ |
1528 |
|
unsigned char *page = cpu->cd.arm.host_load[cpu->cd.arm.r[1] >> 12]; |
1529 |
|
uint32_t t; |
1530 |
|
|
1531 |
|
if (page == NULL) { |
1532 |
|
instr(load_w0_byte_u1_p1_imm)(cpu, ic); |
1533 |
|
return; |
1534 |
|
} |
1535 |
|
|
1536 |
|
t = page[cpu->cd.arm.r[1] & 0xfff]; |
1537 |
|
t += cpu->cd.arm.r[2]; |
1538 |
|
page = cpu->cd.arm.host_load[t >> 12]; |
1539 |
|
|
1540 |
|
if (page == NULL) { |
1541 |
|
instr(load_w0_byte_u1_p1_imm)(cpu, ic); |
1542 |
|
return; |
1543 |
|
} |
1544 |
|
|
1545 |
|
cpu->cd.arm.r[3] = page[t & 0xfff]; |
1546 |
|
|
1547 |
|
t = cpu->cd.arm.r[3] & cpu->cd.arm.r[ARM_IP]; |
1548 |
|
cpu->cd.arm.cpsr &= ~(ARM_FLAG_Z | ARM_FLAG_N); |
1549 |
|
if (t == 0) |
1550 |
|
cpu->cd.arm.cpsr |= ARM_FLAG_Z; |
1551 |
|
|
1552 |
|
cpu->n_translated_instrs += 2; |
1553 |
|
cpu->cd.arm.next_ic = &ic[3]; |
1554 |
|
} |
1555 |
|
|
1556 |
|
|
1557 |
/*****************************************************************************/ |
/*****************************************************************************/ |
1558 |
|
|
1559 |
|
|
1567 |
cpu->pc = cpu->cd.arm.r[ARM_PC]; |
cpu->pc = cpu->cd.arm.r[ARM_PC]; |
1568 |
|
|
1569 |
/* Find the new physical page and update the translation pointers: */ |
/* Find the new physical page and update the translation pointers: */ |
1570 |
arm_pc_to_pointers(cpu); |
quick_pc_to_pointers(cpu); |
1571 |
|
|
1572 |
/* end_of_page doesn't count as an executed instruction: */ |
/* end_of_page doesn't count as an executed instruction: */ |
1573 |
cpu->n_translated_instrs --; |
cpu->n_translated_instrs --; |
1578 |
|
|
1579 |
|
|
1580 |
/* |
/* |
1581 |
* arm_combine_instructions(): |
* arm_combine_netbsd_memset(): |
1582 |
|
* |
1583 |
|
* Check for the core of a NetBSD/arm memset; large memsets use a sequence |
1584 |
|
* of 16 store-multiple instructions, each storing 2 registers at a time. |
1585 |
|
*/ |
1586 |
|
void arm_combine_netbsd_memset(struct cpu *cpu, struct arm_instr_call *ic, |
1587 |
|
int low_addr) |
1588 |
|
{ |
1589 |
|
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
1590 |
|
& (ARM_IC_ENTRIES_PER_PAGE-1); |
1591 |
|
|
1592 |
|
if (n_back >= 17) { |
1593 |
|
int i; |
1594 |
|
for (i=-16; i<=-1; i++) |
1595 |
|
if (ic[i].f != instr(multi_0x08ac000c__ge)) |
1596 |
|
return; |
1597 |
|
if (ic[-17].f == instr(subs) && |
1598 |
|
ic[-17].arg[0]==ic[-17].arg[2] && ic[-17].arg[1] == 128 && |
1599 |
|
ic[ 0].f == instr(b_samepage__gt) && |
1600 |
|
ic[ 0].arg[0] == (size_t)&ic[-17]) { |
1601 |
|
ic[-17].f = instr(netbsd_memset); |
1602 |
|
combined; |
1603 |
|
} |
1604 |
|
} |
1605 |
|
} |
1606 |
|
|
1607 |
|
|
1608 |
|
/* |
1609 |
|
* arm_combine_netbsd_memcpy(): |
1610 |
|
* |
1611 |
|
* Check for the core of a NetBSD/arm memcpy; large memcpys use a |
1612 |
|
* sequence of ldmia instructions. |
1613 |
|
*/ |
1614 |
|
void arm_combine_netbsd_memcpy(struct cpu *cpu, struct arm_instr_call *ic, |
1615 |
|
int low_addr) |
1616 |
|
{ |
1617 |
|
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
1618 |
|
& (ARM_IC_ENTRIES_PER_PAGE-1); |
1619 |
|
|
1620 |
|
if (n_back >= 5) { |
1621 |
|
if (ic[-5].f==instr(multi_0x08b15018) && |
1622 |
|
ic[-4].f==instr(multi_0x08a05018) && |
1623 |
|
ic[-3].f==instr(multi_0x08b15018) && |
1624 |
|
ic[-2].f==instr(multi_0x08a05018) && |
1625 |
|
ic[-1].f == instr(subs) && |
1626 |
|
ic[-1].arg[0]==ic[-1].arg[2] && ic[-1].arg[1] == 0x20 && |
1627 |
|
ic[ 0].f == instr(b_samepage__ge) && |
1628 |
|
ic[ 0].arg[0] == (size_t)&ic[-5]) { |
1629 |
|
ic[-5].f = instr(netbsd_memcpy); |
1630 |
|
combined; |
1631 |
|
} |
1632 |
|
} |
1633 |
|
} |
1634 |
|
|
1635 |
|
|
1636 |
|
/* |
1637 |
|
* arm_combine_netbsd_cacheclean(): |
1638 |
|
* |
1639 |
|
* Check for the core of a NetBSD/arm cache clean. (There are two variants.) |
1640 |
|
*/ |
1641 |
|
void arm_combine_netbsd_cacheclean(struct cpu *cpu, struct arm_instr_call *ic, |
1642 |
|
int low_addr) |
1643 |
|
{ |
1644 |
|
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
1645 |
|
& (ARM_IC_ENTRIES_PER_PAGE-1); |
1646 |
|
|
1647 |
|
if (n_back >= 3) { |
1648 |
|
if (ic[-3].f==instr(load_w0_word_u1_p0_imm) && |
1649 |
|
ic[-2].f == instr(subs) && |
1650 |
|
ic[-2].arg[0]==ic[-2].arg[2] && ic[-2].arg[1] == 0x20 && |
1651 |
|
ic[-1].f == instr(b_samepage__ne) && |
1652 |
|
ic[-1].arg[0] == (size_t)&ic[-3]) { |
1653 |
|
ic[-3].f = instr(netbsd_cacheclean); |
1654 |
|
combined; |
1655 |
|
} |
1656 |
|
} |
1657 |
|
} |
1658 |
|
|
1659 |
|
|
1660 |
|
/* |
1661 |
|
* arm_combine_netbsd_cacheclean2(): |
1662 |
* |
* |
1663 |
* Combine two or more instructions, if possible, into a single function call. |
* Check for the core of a NetBSD/arm cache clean. (Second variant.) |
1664 |
*/ |
*/ |
1665 |
void arm_combine_instructions(struct cpu *cpu, struct arm_instr_call *ic, |
void arm_combine_netbsd_cacheclean2(struct cpu *cpu, struct arm_instr_call *ic, |
1666 |
uint32_t addr) |
int low_addr) |
1667 |
{ |
{ |
1668 |
int n_back; |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
1669 |
n_back = (addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
& (ARM_IC_ENTRIES_PER_PAGE-1); |
1670 |
|
|
1671 |
|
if (n_back >= 4) { |
1672 |
|
if (ic[-4].f == instr(mcr_mrc) && ic[-4].arg[0] == 0xee070f3a && |
1673 |
|
ic[-3].f == instr(mcr_mrc) && ic[-3].arg[0] == 0xee070f36 && |
1674 |
|
ic[-2].f == instr(add) && |
1675 |
|
ic[-2].arg[0]==ic[-2].arg[2] && ic[-2].arg[1] == 0x20 && |
1676 |
|
ic[-1].f == instr(subs) && |
1677 |
|
ic[-1].arg[0]==ic[-1].arg[2] && ic[-1].arg[1] == 0x20) { |
1678 |
|
ic[-4].f = instr(netbsd_cacheclean2); |
1679 |
|
combined; |
1680 |
|
} |
1681 |
|
} |
1682 |
|
} |
1683 |
|
|
1684 |
|
|
1685 |
|
/* |
1686 |
|
* arm_combine_netbsd_scanc(): |
1687 |
|
*/ |
1688 |
|
void arm_combine_netbsd_scanc(struct cpu *cpu, struct arm_instr_call *ic, |
1689 |
|
int low_addr) |
1690 |
|
{ |
1691 |
|
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
1692 |
|
& (ARM_IC_ENTRIES_PER_PAGE-1); |
1693 |
|
|
1694 |
|
if (n_back >= 2) { |
1695 |
|
if (ic[-2].f == instr(load_w0_byte_u1_p1_imm) && |
1696 |
|
ic[-1].f == instr(load_w0_byte_u1_p1_reg)) { |
1697 |
|
ic[-2].f = instr(netbsd_scanc); |
1698 |
|
combined; |
1699 |
|
} |
1700 |
|
} |
1701 |
|
} |
1702 |
|
|
1703 |
|
|
1704 |
|
/* |
1705 |
|
* arm_combine_test2(): |
1706 |
|
*/ |
1707 |
|
void arm_combine_test2(struct cpu *cpu, struct arm_instr_call *ic, int low_addr) |
1708 |
|
{ |
1709 |
|
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
1710 |
& (ARM_IC_ENTRIES_PER_PAGE-1); |
& (ARM_IC_ENTRIES_PER_PAGE-1); |
1711 |
|
|
1712 |
if (n_back >= 2) { |
if (n_back >= 2) { |
1717 |
ic[ 0].f == instr(b_samepage__gt) && |
ic[ 0].f == instr(b_samepage__gt) && |
1718 |
ic[ 0].arg[0] == (size_t)&ic[-2]) { |
ic[ 0].arg[0] == (size_t)&ic[-2]) { |
1719 |
ic[-2].f = instr(fill_loop_test2); |
ic[-2].f = instr(fill_loop_test2); |
1720 |
|
printf("YO test2\n"); |
1721 |
combined; |
combined; |
1722 |
} |
} |
1723 |
} |
} |
1724 |
|
} |
1725 |
|
|
1726 |
|
|
1727 |
|
#if 0 |
1728 |
|
/* TODO: This is another test hack. */ |
1729 |
|
|
1730 |
if (n_back >= 3) { |
if (n_back >= 3) { |
1731 |
if (ic[-3].f == instr(cmps) && |
if (ic[-3].f == instr(cmps) && |
1741 |
combined; |
combined; |
1742 |
} |
} |
1743 |
} |
} |
|
|
|
1744 |
/* TODO: Combine forward as well */ |
/* TODO: Combine forward as well */ |
1745 |
} |
#endif |
1746 |
|
|
1747 |
|
|
1748 |
/*****************************************************************************/ |
/*****************************************************************************/ |
1762 |
unsigned char *page; |
unsigned char *page; |
1763 |
unsigned char ib[4]; |
unsigned char ib[4]; |
1764 |
int condition_code, main_opcode, secondary_opcode, s_bit, rn, rd, r8; |
int condition_code, main_opcode, secondary_opcode, s_bit, rn, rd, r8; |
1765 |
int p_bit, u_bit, b_bit, w_bit, l_bit, regform, rm, c, t; |
int p_bit, u_bit, b_bit, w_bit, l_bit, regform, rm, c, t, any_pc_reg; |
|
int any_pc_reg; |
|
1766 |
void (*samepage_function)(struct cpu *, struct arm_instr_call *); |
void (*samepage_function)(struct cpu *, struct arm_instr_call *); |
1767 |
|
|
1768 |
/* Figure out the address of the instruction: */ |
/* Figure out the address of the instruction: */ |
1785 |
sizeof(ib), MEM_READ, CACHE_INSTRUCTION)) { |
sizeof(ib), MEM_READ, CACHE_INSTRUCTION)) { |
1786 |
fatal("to_be_translated(): " |
fatal("to_be_translated(): " |
1787 |
"read failed: TODO\n"); |
"read failed: TODO\n"); |
1788 |
goto bad; |
return; |
1789 |
} |
} |
1790 |
} |
} |
1791 |
|
|
1983 |
goto bad; |
goto bad; |
1984 |
} |
} |
1985 |
|
|
1986 |
/* "mov pc,lr" with trace enabled: */ |
/* "mov pc,lr": */ |
1987 |
if ((iword & 0x0fffffff) == 0x01a0f00e && |
if ((iword & 0x0fffffff) == 0x01a0f00e) { |
1988 |
cpu->machine->show_trace_tree) { |
if (cpu->machine->show_trace_tree) |
1989 |
ic->f = cond_instr(ret_trace); |
ic->f = cond_instr(ret_trace); |
1990 |
|
else |
1991 |
|
ic->f = cond_instr(ret); |
1992 |
break; |
break; |
1993 |
} |
} |
1994 |
|
|
2001 |
break; |
break; |
2002 |
} |
} |
2003 |
|
|
2004 |
|
/* "mov reg,#0": */ |
2005 |
|
if ((iword & 0x0fff0fff) == 0x03a03000 && rd != ARM_PC) { |
2006 |
|
switch (rd) { |
2007 |
|
case 0: ic->f = cond_instr(clear_r0); break; |
2008 |
|
case 1: ic->f = cond_instr(clear_r1); break; |
2009 |
|
case 2: ic->f = cond_instr(clear_r2); break; |
2010 |
|
case 3: ic->f = cond_instr(clear_r3); break; |
2011 |
|
case 4: ic->f = cond_instr(clear_r4); break; |
2012 |
|
case 5: ic->f = cond_instr(clear_r5); break; |
2013 |
|
case 6: ic->f = cond_instr(clear_r6); break; |
2014 |
|
case 7: ic->f = cond_instr(clear_r7); break; |
2015 |
|
case 8: ic->f = cond_instr(clear_r8); break; |
2016 |
|
case 9: ic->f = cond_instr(clear_r9); break; |
2017 |
|
case 10: ic->f = cond_instr(clear_r10); break; |
2018 |
|
case 11: ic->f = cond_instr(clear_r11); break; |
2019 |
|
case 12: ic->f = cond_instr(clear_r12); break; |
2020 |
|
case 13: ic->f = cond_instr(clear_r13); break; |
2021 |
|
case 14: ic->f = cond_instr(clear_r14); break; |
2022 |
|
} |
2023 |
|
break; |
2024 |
|
} |
2025 |
|
|
2026 |
|
/* "mov reg,#1": */ |
2027 |
|
if ((iword & 0x0fff0fff) == 0x03a03001 && rd != ARM_PC) { |
2028 |
|
switch (rd) { |
2029 |
|
case 0: ic->f = cond_instr(mov1_r0); break; |
2030 |
|
case 1: ic->f = cond_instr(mov1_r1); break; |
2031 |
|
case 2: ic->f = cond_instr(mov1_r2); break; |
2032 |
|
case 3: ic->f = cond_instr(mov1_r3); break; |
2033 |
|
case 4: ic->f = cond_instr(mov1_r4); break; |
2034 |
|
case 5: ic->f = cond_instr(mov1_r5); break; |
2035 |
|
case 6: ic->f = cond_instr(mov1_r6); break; |
2036 |
|
case 7: ic->f = cond_instr(mov1_r7); break; |
2037 |
|
case 8: ic->f = cond_instr(mov1_r8); break; |
2038 |
|
case 9: ic->f = cond_instr(mov1_r9); break; |
2039 |
|
case 10: ic->f = cond_instr(mov1_r10); break; |
2040 |
|
case 11: ic->f = cond_instr(mov1_r11); break; |
2041 |
|
case 12: ic->f = cond_instr(mov1_r12); break; |
2042 |
|
case 13: ic->f = cond_instr(mov1_r13); break; |
2043 |
|
case 14: ic->f = cond_instr(mov1_r14); break; |
2044 |
|
} |
2045 |
|
break; |
2046 |
|
} |
2047 |
|
|
2048 |
/* |
/* |
2049 |
* Generic Data Processing Instructions: |
* Generic Data Processing Instructions: |
2050 |
*/ |
*/ |
2081 |
ic->f = arm_dpi_instr[condition_code + |
ic->f = arm_dpi_instr[condition_code + |
2082 |
16 * secondary_opcode + (s_bit? 256 : 0) + |
16 * secondary_opcode + (s_bit? 256 : 0) + |
2083 |
(any_pc_reg? 512 : 0) + (regform? 1024 : 0)]; |
(any_pc_reg? 512 : 0) + (regform? 1024 : 0)]; |
2084 |
|
|
2085 |
|
if (iword == 0xe113000c) |
2086 |
|
cpu->combination_check = arm_combine_netbsd_scanc; |
2087 |
break; |
break; |
2088 |
|
|
2089 |
case 0x4: /* Load and store... */ |
case 0x4: /* Load and store... */ |
2116 |
|
|
2117 |
case 0x8: /* Multiple load/store... (Block data transfer) */ |
case 0x8: /* Multiple load/store... (Block data transfer) */ |
2118 |
case 0x9: /* xxxx100P USWLnnnn llllllll llllllll */ |
case 0x9: /* xxxx100P USWLnnnn llllllll llllllll */ |
2119 |
|
ic->arg[0] = (size_t)(&cpu->cd.arm.r[rn]); |
2120 |
|
ic->arg[1] = (size_t)iword; |
2121 |
|
/* Generic case: */ |
2122 |
if (l_bit) |
if (l_bit) |
2123 |
ic->f = cond_instr(bdt_load); |
ic->f = cond_instr(bdt_load); |
2124 |
else |
else |
2125 |
ic->f = cond_instr(bdt_store); |
ic->f = cond_instr(bdt_store); |
2126 |
ic->arg[0] = (size_t)(&cpu->cd.arm.r[rn]); |
#if defined(HOST_LITTLE_ENDIAN) && !defined(GATHER_BDT_STATISTICS) |
2127 |
ic->arg[1] = (size_t)iword; |
/* |
2128 |
|
* Check for availability of optimized implementation: |
2129 |
|
* xxxx100P USWLnnnn llllllll llllllll |
2130 |
|
* ^ ^ ^ ^ ^ ^ ^ ^ (0x00950154) |
2131 |
|
* These bits are used to select which list to scan, and then |
2132 |
|
* the list is scanned linearly. |
2133 |
|
* |
2134 |
|
* The optimized functions do not support show_trace_tree, |
2135 |
|
* but it's ok to use the unoptimized version in that case. |
2136 |
|
*/ |
2137 |
|
if (!cpu->machine->show_trace_tree) { |
2138 |
|
int i = 0, j = iword; |
2139 |
|
j = ((j & 0x00800000) >> 16) | ((j & 0x00100000) >> 14) |
2140 |
|
| ((j & 0x00040000) >> 13) | ((j & 0x00010000) >> 12) |
2141 |
|
| ((j & 0x00000100) >> 5) | ((j & 0x00000040) >> 4) |
2142 |
|
| ((j & 0x00000010) >> 3) | ((j & 0x00000004) >> 2); |
2143 |
|
while (multi_opcode[j][i] != 0) { |
2144 |
|
if ((iword & 0x0fffffff) == |
2145 |
|
multi_opcode[j][i]) { |
2146 |
|
ic->f = multi_opcode_f[j] |
2147 |
|
[i*16 + condition_code]; |
2148 |
|
break; |
2149 |
|
} |
2150 |
|
i ++; |
2151 |
|
} |
2152 |
|
} |
2153 |
|
#endif |
2154 |
if (rn == ARM_PC) { |
if (rn == ARM_PC) { |
2155 |
fatal("TODO: bdt with PC as base\n"); |
fatal("TODO: bdt with PC as base\n"); |
2156 |
goto bad; |
goto bad; |
2162 |
if (main_opcode == 0x0a) { |
if (main_opcode == 0x0a) { |
2163 |
ic->f = cond_instr(b); |
ic->f = cond_instr(b); |
2164 |
samepage_function = cond_instr(b_samepage); |
samepage_function = cond_instr(b_samepage); |
2165 |
|
/* if (iword == 0xcafffffc) |
2166 |
|
cpu->combination_check = arm_combine_test2; */ |
2167 |
|
if (iword == 0xcaffffed) |
2168 |
|
cpu->combination_check = |
2169 |
|
arm_combine_netbsd_memset; |
2170 |
|
if (iword == 0xaafffff9) |
2171 |
|
cpu->combination_check = |
2172 |
|
arm_combine_netbsd_memcpy; |
2173 |
} else { |
} else { |
2174 |
if (cpu->machine->show_trace_tree) { |
if (cpu->machine->show_trace_tree) { |
2175 |
ic->f = cond_instr(bl_trace); |
ic->f = cond_instr(bl_trace); |
2207 |
ARM_INSTR_ALIGNMENT_SHIFT)); |
ARM_INSTR_ALIGNMENT_SHIFT)); |
2208 |
} |
} |
2209 |
} |
} |
2210 |
|
|
2211 |
|
#if 0 |
2212 |
|
/* Hm. This doesn't really increase performance. */ |
2213 |
|
if (iword == 0x8afffffa) |
2214 |
|
cpu->combination_check = arm_combine_netbsd_cacheclean2; |
2215 |
|
#endif |
2216 |
break; |
break; |
2217 |
|
|
2218 |
case 0xe: |
case 0xe: |
2225 |
ic->arg[0] = iword; |
ic->arg[0] = iword; |
2226 |
ic->f = cond_instr(cdp); |
ic->f = cond_instr(cdp); |
2227 |
} |
} |
2228 |
|
if (iword == 0xee070f9a) |
2229 |
|
cpu->combination_check = arm_combine_netbsd_cacheclean; |
2230 |
break; |
break; |
2231 |
|
|
2232 |
case 0xf: |
case 0xf: |