1 |
/* |
/* |
2 |
* Copyright (C) 2005 Anders Gavare. All rights reserved. |
* Copyright (C) 2005-2006 Anders Gavare. All rights reserved. |
3 |
* |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
* modification, are permitted provided that the following conditions are met: |
25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: cpu_arm_instr.c,v 1.54 2005/11/19 18:53:07 debug Exp $ |
* $Id: cpu_arm_instr.c,v 1.60 2006/02/09 22:40:27 debug Exp $ |
29 |
* |
* |
30 |
* ARM instructions. |
* ARM instructions. |
31 |
* |
* |
36 |
*/ |
*/ |
37 |
|
|
38 |
|
|
|
#include "arm_quick_pc_to_pointers.h" |
|
|
|
|
39 |
/* #define GATHER_BDT_STATISTICS */ |
/* #define GATHER_BDT_STATISTICS */ |
40 |
|
|
41 |
|
|
585 |
|
|
586 |
|
|
587 |
/* |
/* |
588 |
|
* smulXY: 16-bit * 16-bit multiplication (32-bit result) |
589 |
|
* |
590 |
|
* arg[0] = ptr to rm |
591 |
|
* arg[1] = ptr to rs |
592 |
|
* arg[2] = ptr to rd |
593 |
|
*/ |
594 |
|
X(smulbb) |
595 |
|
{ |
596 |
|
reg(ic->arg[2]) = (int32_t)(int16_t)reg(ic->arg[0]) * |
597 |
|
(int32_t)(int16_t)reg(ic->arg[1]); |
598 |
|
} |
599 |
|
Y(smulbb) |
600 |
|
X(smultb) |
601 |
|
{ |
602 |
|
reg(ic->arg[2]) = (int32_t)(int16_t)(reg(ic->arg[0]) >> 16) * |
603 |
|
(int32_t)(int16_t)reg(ic->arg[1]); |
604 |
|
} |
605 |
|
Y(smultb) |
606 |
|
X(smulbt) |
607 |
|
{ |
608 |
|
reg(ic->arg[2]) = (int32_t)(int16_t)reg(ic->arg[0]) * |
609 |
|
(int32_t)(int16_t)(reg(ic->arg[1]) >> 16); |
610 |
|
} |
611 |
|
Y(smulbt) |
612 |
|
X(smultt) |
613 |
|
{ |
614 |
|
reg(ic->arg[2]) = (int32_t)(int16_t)(reg(ic->arg[0]) >> 16) * |
615 |
|
(int32_t)(int16_t)(reg(ic->arg[1]) >> 16); |
616 |
|
} |
617 |
|
Y(smultt) |
618 |
|
|
619 |
|
|
620 |
|
/* |
621 |
* mov_reg_reg: Move a register to another. |
* mov_reg_reg: Move a register to another. |
622 |
* |
* |
623 |
* arg[0] = ptr to source register |
* arg[0] = ptr to source register |
2022 |
|
|
2023 |
|
|
2024 |
/* |
/* |
2025 |
* arm_combine_netbsd_memset(): |
* Combine: netbsd_memset(): |
2026 |
* |
* |
2027 |
* Check for the core of a NetBSD/arm memset; large memsets use a sequence |
* Check for the core of a NetBSD/arm memset; large memsets use a sequence |
2028 |
* of 16 store-multiple instructions, each storing 2 registers at a time. |
* of 16 store-multiple instructions, each storing 2 registers at a time. |
2029 |
*/ |
*/ |
2030 |
void arm_combine_netbsd_memset(struct cpu *cpu, |
void COMBINE(netbsd_memset)(struct cpu *cpu, |
2031 |
struct arm_instr_call *ic, int low_addr) |
struct arm_instr_call *ic, int low_addr) |
2032 |
{ |
{ |
2033 |
#ifdef HOST_LITTLE_ENDIAN |
#ifdef HOST_LITTLE_ENDIAN |
2052 |
|
|
2053 |
|
|
2054 |
/* |
/* |
2055 |
* arm_combine_netbsd_memcpy(): |
* Combine: netbsd_memcpy(): |
2056 |
* |
* |
2057 |
* Check for the core of a NetBSD/arm memcpy; large memcpys use a |
* Check for the core of a NetBSD/arm memcpy; large memcpys use a |
2058 |
* sequence of ldmia instructions. |
* sequence of ldmia instructions. |
2059 |
*/ |
*/ |
2060 |
void arm_combine_netbsd_memcpy(struct cpu *cpu, |
void COMBINE(netbsd_memcpy)(struct cpu *cpu, struct arm_instr_call *ic, |
2061 |
struct arm_instr_call *ic, int low_addr) |
int low_addr) |
2062 |
{ |
{ |
2063 |
#ifdef HOST_LITTLE_ENDIAN |
#ifdef HOST_LITTLE_ENDIAN |
2064 |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
2082 |
|
|
2083 |
|
|
2084 |
/* |
/* |
2085 |
* arm_combine_netbsd_cacheclean(): |
* Combine: netbsd_cacheclean(): |
2086 |
* |
* |
2087 |
* Check for the core of a NetBSD/arm cache clean. (There are two variants.) |
* Check for the core of a NetBSD/arm cache clean. (There are two variants.) |
2088 |
*/ |
*/ |
2089 |
void arm_combine_netbsd_cacheclean(struct cpu *cpu, |
void COMBINE(netbsd_cacheclean)(struct cpu *cpu, |
2090 |
struct arm_instr_call *ic, int low_addr) |
struct arm_instr_call *ic, int low_addr) |
2091 |
{ |
{ |
2092 |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
2106 |
|
|
2107 |
|
|
2108 |
/* |
/* |
2109 |
* arm_combine_netbsd_cacheclean2(): |
* Combine: netbsd_cacheclean2(): |
2110 |
* |
* |
2111 |
* Check for the core of a NetBSD/arm cache clean. (Second variant.) |
* Check for the core of a NetBSD/arm cache clean. (Second variant.) |
2112 |
*/ |
*/ |
2113 |
void arm_combine_netbsd_cacheclean2(struct cpu *cpu, |
void COMBINE(netbsd_cacheclean2)(struct cpu *cpu, |
2114 |
struct arm_instr_call *ic, int low_addr) |
struct arm_instr_call *ic, int low_addr) |
2115 |
{ |
{ |
2116 |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
2131 |
|
|
2132 |
|
|
2133 |
/* |
/* |
2134 |
* arm_combine_netbsd_scanc(): |
* Combine: netbsd_scanc(): |
2135 |
*/ |
*/ |
2136 |
void arm_combine_netbsd_scanc(struct cpu *cpu, |
void COMBINE(netbsd_scanc)(struct cpu *cpu, |
2137 |
struct arm_instr_call *ic, int low_addr) |
struct arm_instr_call *ic, int low_addr) |
2138 |
{ |
{ |
2139 |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
2157 |
|
|
2158 |
|
|
2159 |
/* |
/* |
2160 |
* arm_combine_strlen(): |
* Combine: strlen(): |
2161 |
*/ |
*/ |
2162 |
void arm_combine_strlen(struct cpu *cpu, |
void COMBINE(strlen)(struct cpu *cpu, |
2163 |
struct arm_instr_call *ic, int low_addr) |
struct arm_instr_call *ic, int low_addr) |
2164 |
{ |
{ |
2165 |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
2181 |
|
|
2182 |
|
|
2183 |
/* |
/* |
2184 |
* arm_combine_xchg(): |
* Combine: xchg(): |
2185 |
*/ |
*/ |
2186 |
void arm_combine_xchg(struct cpu *cpu, |
void COMBINE(xchg)(struct cpu *cpu, |
2187 |
struct arm_instr_call *ic, int low_addr) |
struct arm_instr_call *ic, int low_addr) |
2188 |
{ |
{ |
2189 |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
2207 |
|
|
2208 |
|
|
2209 |
/* |
/* |
2210 |
* arm_combine_netbsd_copyin(): |
* Combine: netbsd_copyin(): |
2211 |
*/ |
*/ |
2212 |
void arm_combine_netbsd_copyin(struct cpu *cpu, |
void COMBINE(netbsd_copyin)(struct cpu *cpu, |
2213 |
struct arm_instr_call *ic, int low_addr) |
struct arm_instr_call *ic, int low_addr) |
2214 |
{ |
{ |
2215 |
#ifdef HOST_LITTLE_ENDIAN |
#ifdef HOST_LITTLE_ENDIAN |
2239 |
|
|
2240 |
|
|
2241 |
/* |
/* |
2242 |
* arm_combine_netbsd_copyout(): |
* Combine: netbsd_copyout(): |
2243 |
*/ |
*/ |
2244 |
void arm_combine_netbsd_copyout(struct cpu *cpu, |
void COMBINE(netbsd_copyout)(struct cpu *cpu, |
2245 |
struct arm_instr_call *ic, int low_addr) |
struct arm_instr_call *ic, int low_addr) |
2246 |
{ |
{ |
2247 |
#ifdef HOST_LITTLE_ENDIAN |
#ifdef HOST_LITTLE_ENDIAN |
2271 |
|
|
2272 |
|
|
2273 |
/* |
/* |
2274 |
* arm_combine_cmps_b(): |
* Combine: cmps_b(): |
2275 |
*/ |
*/ |
2276 |
void arm_combine_cmps_b(struct cpu *cpu, |
void COMBINE(cmps_b)(struct cpu *cpu, |
2277 |
struct arm_instr_call *ic, int low_addr) |
struct arm_instr_call *ic, int low_addr) |
2278 |
{ |
{ |
2279 |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
int n_back = (low_addr >> ARM_INSTR_ALIGNMENT_SHIFT) |
2455 |
X(to_be_translated) |
X(to_be_translated) |
2456 |
{ |
{ |
2457 |
uint32_t addr, low_pc, iword, imm = 0; |
uint32_t addr, low_pc, iword, imm = 0; |
2458 |
|
#ifdef DYNTRANS_BACKEND |
2459 |
|
int simple = 0; |
2460 |
|
#endif |
2461 |
unsigned char *page; |
unsigned char *page; |
2462 |
unsigned char ib[4]; |
unsigned char ib[4]; |
2463 |
int condition_code, main_opcode, secondary_opcode, s_bit, rn, rd, r8; |
int condition_code, main_opcode, secondary_opcode, s_bit, rn, rd, r8; |
2618 |
goto bad; |
goto bad; |
2619 |
} |
} |
2620 |
if ((iword & 0x0ff0f090) == 0x01600080) { |
if ((iword & 0x0ff0f090) == 0x01600080) { |
2621 |
/* TODO: smulXY */ |
/* smulXY (16-bit * 16-bit => 32-bit) */ |
2622 |
goto bad; |
switch (iword & 0x60) { |
2623 |
|
case 0x00: ic->f = cond_instr(smulbb); break; |
2624 |
|
case 0x20: ic->f = cond_instr(smultb); break; |
2625 |
|
case 0x40: ic->f = cond_instr(smulbt); break; |
2626 |
|
default: ic->f = cond_instr(smultt); break; |
2627 |
|
} |
2628 |
|
ic->arg[0] = (size_t)(&cpu->cd.arm.r[rm]); |
2629 |
|
ic->arg[1] = (size_t)(&cpu->cd.arm.r[r8]); |
2630 |
|
ic->arg[2] = (size_t)(&cpu->cd.arm.r[rn]); /* Rd */ |
2631 |
|
break; |
2632 |
} |
} |
2633 |
if ((iword & 0x0ff0f0b0) == 0x012000a0) { |
if ((iword & 0x0ff0f0b0) == 0x012000a0) { |
2634 |
/* TODO: smulwY */ |
/* TODO: smulwY */ |
2745 |
/* "mov reg,#0": */ |
/* "mov reg,#0": */ |
2746 |
if ((iword & 0x0fff0fff) == 0x03a00000 && rd != ARM_PC) { |
if ((iword & 0x0fff0fff) == 0x03a00000 && rd != ARM_PC) { |
2747 |
arm_switch_clear(ic, rd, condition_code); |
arm_switch_clear(ic, rd, condition_code); |
2748 |
|
#ifdef DYNTRANS_BACKEND |
2749 |
|
simple = 1; |
2750 |
|
#endif |
2751 |
break; |
break; |
2752 |
} |
} |
2753 |
|
|
2754 |
/* "mov reg,#1": */ |
/* "mov reg,#1": */ |
2755 |
if ((iword & 0x0fff0fff) == 0x03a00001 && rd != ARM_PC) { |
if ((iword & 0x0fff0fff) == 0x03a00001 && rd != ARM_PC) { |
2756 |
arm_switch_mov1(ic, rd, condition_code); |
arm_switch_mov1(ic, rd, condition_code); |
2757 |
|
#ifdef DYNTRANS_BACKEND |
2758 |
|
simple = 1; |
2759 |
|
#endif |
2760 |
break; |
break; |
2761 |
} |
} |
2762 |
|
|
2764 |
if ((iword & 0x0ff00fff) == 0x02800001 && rd != ARM_PC |
if ((iword & 0x0ff00fff) == 0x02800001 && rd != ARM_PC |
2765 |
&& rn == rd) { |
&& rn == rd) { |
2766 |
arm_switch_add1(ic, rd, condition_code); |
arm_switch_add1(ic, rd, condition_code); |
2767 |
|
#ifdef DYNTRANS_BACKEND |
2768 |
|
simple = 1; |
2769 |
|
#endif |
2770 |
break; |
break; |
2771 |
} |
} |
2772 |
|
|
2819 |
(any_pc_reg? 512 : 0) + (regform? 1024 : 0)]; |
(any_pc_reg? 512 : 0) + (regform? 1024 : 0)]; |
2820 |
|
|
2821 |
if (ic->f == instr(eor_regshort)) |
if (ic->f == instr(eor_regshort)) |
2822 |
cpu->cd.arm.combination_check = arm_combine_xchg; |
cpu->cd.arm.combination_check = COMBINE(xchg); |
2823 |
if (iword == 0xe113000c) |
if (iword == 0xe113000c) |
2824 |
cpu->cd.arm.combination_check = |
cpu->cd.arm.combination_check = COMBINE(netbsd_scanc); |
|
arm_combine_netbsd_scanc; |
|
2825 |
break; |
break; |
2826 |
|
|
2827 |
case 0x4: /* Load and store... */ |
case 0x4: /* Load and store... */ |
2893 |
} |
} |
2894 |
} |
} |
2895 |
if (iword == 0xe4b09004) |
if (iword == 0xe4b09004) |
2896 |
cpu->cd.arm.combination_check = |
cpu->cd.arm.combination_check = COMBINE(netbsd_copyin); |
|
arm_combine_netbsd_copyin; |
|
2897 |
if (iword == 0xe4a17004) |
if (iword == 0xe4a17004) |
2898 |
cpu->cd.arm.combination_check = |
cpu->cd.arm.combination_check = COMBINE(netbsd_copyout); |
|
arm_combine_netbsd_copyout; |
|
2899 |
break; |
break; |
2900 |
|
|
2901 |
case 0x8: /* Multiple load/store... (Block data transfer) */ |
case 0x8: /* Multiple load/store... (Block data transfer) */ |
2948 |
samepage_function = cond_instr(b_samepage); |
samepage_function = cond_instr(b_samepage); |
2949 |
if (iword == 0xcaffffed) |
if (iword == 0xcaffffed) |
2950 |
cpu->cd.arm.combination_check = |
cpu->cd.arm.combination_check = |
2951 |
arm_combine_netbsd_memset; |
COMBINE(netbsd_memset); |
2952 |
if (iword == 0xaafffff9) |
if (iword == 0xaafffff9) |
2953 |
cpu->cd.arm.combination_check = |
cpu->cd.arm.combination_check = |
2954 |
arm_combine_netbsd_memcpy; |
COMBINE(netbsd_memcpy); |
2955 |
} else { |
} else { |
2956 |
if (cpu->machine->show_trace_tree) { |
if (cpu->machine->show_trace_tree) { |
2957 |
ic->f = cond_instr(bl_trace); |
ic->f = cond_instr(bl_trace); |
3010 |
if (main_opcode == 0xa && (condition_code <= 1 |
if (main_opcode == 0xa && (condition_code <= 1 |
3011 |
|| condition_code == 3 || condition_code == 8 |
|| condition_code == 3 || condition_code == 8 |
3012 |
|| condition_code == 12 || condition_code == 13)) |
|| condition_code == 12 || condition_code == 13)) |
3013 |
cpu->cd.arm.combination_check = arm_combine_cmps_b; |
cpu->cd.arm.combination_check = COMBINE(cmps_b); |
3014 |
|
|
3015 |
if (iword == 0x1afffffc) |
if (iword == 0x1afffffc) |
3016 |
cpu->cd.arm.combination_check = arm_combine_strlen; |
cpu->cd.arm.combination_check = COMBINE(strlen); |
3017 |
|
|
3018 |
/* Hm. Does this really increase performance? */ |
/* Hm. Does this really increase performance? */ |
3019 |
if (iword == 0x8afffffa) |
if (iword == 0x8afffffa) |
3020 |
cpu->cd.arm.combination_check = |
cpu->cd.arm.combination_check = |
3021 |
arm_combine_netbsd_cacheclean2; |
COMBINE(netbsd_cacheclean2); |
3022 |
break; |
break; |
3023 |
|
|
3024 |
case 0xc: |
case 0xc: |
3027 |
* xxxx1100 0100nnnn ddddcccc oooommmm MCRR c,op,Rd,Rn,CRm |
* xxxx1100 0100nnnn ddddcccc oooommmm MCRR c,op,Rd,Rn,CRm |
3028 |
* xxxx1100 0101nnnn ddddcccc oooommmm MRRC c,op,Rd,Rn,CRm |
* xxxx1100 0101nnnn ddddcccc oooommmm MRRC c,op,Rd,Rn,CRm |
3029 |
*/ |
*/ |
3030 |
|
if ((iword & 0x0fe00fff) == 0x0c400000) { |
3031 |
|
/* Special case: mar/mra DSP instructions */ |
3032 |
|
fatal("TODO: mar/mra DSP instructions!\n"); |
3033 |
|
/* Perhaps these are actually identical to MCRR/MRRC */ |
3034 |
|
goto bad; |
3035 |
|
} |
3036 |
|
|
3037 |
if ((iword & 0x0fe00000) == 0x0c400000) { |
if ((iword & 0x0fe00000) == 0x0c400000) { |
3038 |
fatal("MCRR/MRRC: TODO\n"); |
fatal("MCRR/MRRC: TODO\n"); |
3039 |
goto bad; |
goto bad; |
3045 |
* For now, treat as Undefined instructions. This causes e.g. |
* For now, treat as Undefined instructions. This causes e.g. |
3046 |
* Linux/ARM to emulate these instructions (floating point). |
* Linux/ARM to emulate these instructions (floating point). |
3047 |
*/ |
*/ |
3048 |
|
#if 0 |
3049 |
ic->f = cond_instr(und); |
ic->f = cond_instr(und); |
3050 |
ic->arg[0] = addr & 0xfff; |
ic->arg[0] = addr & 0xfff; |
3051 |
|
#else |
3052 |
|
fatal("LDC/STC: TODO\n"); |
3053 |
|
goto bad; |
3054 |
|
#endif |
3055 |
break; |
break; |
3056 |
|
|
3057 |
case 0xe: |
case 0xe: |
3058 |
|
if ((iword & 0x0ff00ff0) == 0x0e200010) { |
3059 |
|
/* Special case: mia* DSP instructions */ |
3060 |
|
/* See Intel's 27343601.pdf, page 16-20 */ |
3061 |
|
fatal("TODO: mia* DSP instructions!\n"); |
3062 |
|
goto bad; |
3063 |
|
} |
3064 |
if (iword & 0x10) { |
if (iword & 0x10) { |
3065 |
/* xxxx1110 oooLNNNN ddddpppp qqq1MMMM MCR/MRC */ |
/* xxxx1110 oooLNNNN ddddpppp qqq1MMMM MCR/MRC */ |
3066 |
ic->arg[0] = iword; |
ic->arg[0] = iword; |
3072 |
} |
} |
3073 |
if (iword == 0xee070f9a) |
if (iword == 0xee070f9a) |
3074 |
cpu->cd.arm.combination_check = |
cpu->cd.arm.combination_check = |
3075 |
arm_combine_netbsd_cacheclean; |
COMBINE(netbsd_cacheclean); |
3076 |
break; |
break; |
3077 |
|
|
3078 |
case 0xf: |
case 0xf: |