13057 lines
349 KiB
C
13057 lines
349 KiB
C
/* simulator.c -- Interface for the AArch64 simulator.
|
|
|
|
Copyright (C) 2015-2016 Free Software Foundation, Inc.
|
|
|
|
Contributed by Red Hat.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <math.h>
|
|
#include <time.h>
|
|
#include <limits.h>
|
|
|
|
#include "simulator.h"
|
|
#include "cpustate.h"
|
|
#include "memory.h"
|
|
|
|
#define NO_SP 0
|
|
#define SP_OK 1
|
|
|
|
#define TST(_flag) (aarch64_test_CPSR_bit (cpu, _flag))
|
|
#define IS_SET(_X) ( TST (( _X )))
|
|
#define IS_CLEAR(_X) (!TST (( _X )))
|
|
|
|
#define HALT_UNALLOC \
|
|
do \
|
|
{ \
|
|
TRACE_DISASM (cpu, aarch64_get_PC (cpu)); \
|
|
TRACE_INSN (cpu, \
|
|
"Unallocated instruction detected at sim line %d," \
|
|
" exe addr %" PRIx64, \
|
|
__LINE__, aarch64_get_PC (cpu)); \
|
|
sim_engine_halt (CPU_STATE (cpu), cpu, NULL, aarch64_get_PC (cpu),\
|
|
sim_stopped, SIM_SIGILL); \
|
|
} \
|
|
while (0)
|
|
|
|
#define HALT_NYI \
|
|
do \
|
|
{ \
|
|
TRACE_DISASM (cpu, aarch64_get_PC (cpu)); \
|
|
TRACE_INSN (cpu, \
|
|
"Unimplemented instruction detected at sim line %d," \
|
|
" exe addr %" PRIx64, \
|
|
__LINE__, aarch64_get_PC (cpu)); \
|
|
sim_engine_halt (CPU_STATE (cpu), cpu, NULL, aarch64_get_PC (cpu),\
|
|
sim_stopped, SIM_SIGABRT); \
|
|
} \
|
|
while (0)
|
|
|
|
#define NYI_assert(HI, LO, EXPECTED) \
|
|
do \
|
|
{ \
|
|
if (uimm (aarch64_get_instr (cpu), (HI), (LO)) != (EXPECTED)) \
|
|
HALT_NYI; \
|
|
} \
|
|
while (0)
|
|
|
|
#define HALT_UNREACHABLE \
|
|
do \
|
|
{ \
|
|
TRACE_EVENTS (cpu, "ISE: unreachable code point"); \
|
|
sim_engine_abort (NULL, cpu, aarch64_get_PC (cpu), "Internal Error"); \
|
|
} \
|
|
while (0)
|
|
|
|
/* Helper functions used by expandLogicalImmediate. */
|
|
|
|
/* for i = 1, ... N result<i-1> = 1 other bits are zero */
|
|
static inline uint64_t
|
|
ones (int N)
|
|
{
|
|
return (N == 64 ? (uint64_t)-1UL : ((1UL << N) - 1));
|
|
}
|
|
|
|
/* result<0> to val<N> */
|
|
static inline uint64_t
|
|
pickbit (uint64_t val, int N)
|
|
{
|
|
return pickbits64 (val, N, N);
|
|
}
|
|
|
|
static uint64_t
|
|
expand_logical_immediate (uint32_t S, uint32_t R, uint32_t N)
|
|
{
|
|
uint64_t mask;
|
|
uint64_t imm;
|
|
unsigned simd_size;
|
|
|
|
/* The immediate value is S+1 bits to 1, left rotated by SIMDsize - R
|
|
(in other words, right rotated by R), then replicated. */
|
|
if (N != 0)
|
|
{
|
|
simd_size = 64;
|
|
mask = 0xffffffffffffffffull;
|
|
}
|
|
else
|
|
{
|
|
switch (S)
|
|
{
|
|
case 0x00 ... 0x1f: /* 0xxxxx */ simd_size = 32; break;
|
|
case 0x20 ... 0x2f: /* 10xxxx */ simd_size = 16; S &= 0xf; break;
|
|
case 0x30 ... 0x37: /* 110xxx */ simd_size = 8; S &= 0x7; break;
|
|
case 0x38 ... 0x3b: /* 1110xx */ simd_size = 4; S &= 0x3; break;
|
|
case 0x3c ... 0x3d: /* 11110x */ simd_size = 2; S &= 0x1; break;
|
|
default: return 0;
|
|
}
|
|
mask = (1ull << simd_size) - 1;
|
|
/* Top bits are IGNORED. */
|
|
R &= simd_size - 1;
|
|
}
|
|
|
|
/* NOTE: if S = simd_size - 1 we get 0xf..f which is rejected. */
|
|
if (S == simd_size - 1)
|
|
return 0;
|
|
|
|
/* S+1 consecutive bits to 1. */
|
|
/* NOTE: S can't be 63 due to detection above. */
|
|
imm = (1ull << (S + 1)) - 1;
|
|
|
|
/* Rotate to the left by simd_size - R. */
|
|
if (R != 0)
|
|
imm = ((imm << (simd_size - R)) & mask) | (imm >> R);
|
|
|
|
/* Replicate the value according to SIMD size. */
|
|
switch (simd_size)
|
|
{
|
|
case 2: imm = (imm << 2) | imm;
|
|
case 4: imm = (imm << 4) | imm;
|
|
case 8: imm = (imm << 8) | imm;
|
|
case 16: imm = (imm << 16) | imm;
|
|
case 32: imm = (imm << 32) | imm;
|
|
case 64: break;
|
|
default: return 0;
|
|
}
|
|
|
|
return imm;
|
|
}
|
|
|
|
/* Instr[22,10] encodes N immr and imms. we want a lookup table
|
|
for each possible combination i.e. 13 bits worth of int entries. */
|
|
#define LI_TABLE_SIZE (1 << 13)
|
|
static uint64_t LITable[LI_TABLE_SIZE];
|
|
|
|
void
|
|
aarch64_init_LIT_table (void)
|
|
{
|
|
unsigned index;
|
|
|
|
for (index = 0; index < LI_TABLE_SIZE; index++)
|
|
{
|
|
uint32_t N = uimm (index, 12, 12);
|
|
uint32_t immr = uimm (index, 11, 6);
|
|
uint32_t imms = uimm (index, 5, 0);
|
|
|
|
LITable [index] = expand_logical_immediate (imms, immr, N);
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexNotify (sim_cpu *cpu)
|
|
{
|
|
/* instr[14,0] == type : 0 ==> method entry, 1 ==> method reentry
|
|
2 ==> exit Java, 3 ==> start next bytecode. */
|
|
uint32_t type = uimm (aarch64_get_instr (cpu), 14, 0);
|
|
|
|
TRACE_EVENTS (cpu, "Notify Insn encountered, type = 0x%x", type);
|
|
|
|
switch (type)
|
|
{
|
|
case 0:
|
|
/* aarch64_notifyMethodEntry (aarch64_get_reg_u64 (cpu, R23, 0),
|
|
aarch64_get_reg_u64 (cpu, R22, 0)); */
|
|
break;
|
|
case 1:
|
|
/* aarch64_notifyMethodReentry (aarch64_get_reg_u64 (cpu, R23, 0),
|
|
aarch64_get_reg_u64 (cpu, R22, 0)); */
|
|
break;
|
|
case 2:
|
|
/* aarch64_notifyMethodExit (); */
|
|
break;
|
|
case 3:
|
|
/* aarch64_notifyBCStart (aarch64_get_reg_u64 (cpu, R23, 0),
|
|
aarch64_get_reg_u64 (cpu, R22, 0)); */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* secondary decode within top level groups */
|
|
|
|
static void
|
|
dexPseudo (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[28,27] = 00
|
|
|
|
We provide 2 pseudo instructions:
|
|
|
|
HALT stops execution of the simulator causing an immediate
|
|
return to the x86 code which entered it.
|
|
|
|
CALLOUT initiates recursive entry into x86 code. A register
|
|
argument holds the address of the x86 routine. Immediate
|
|
values in the instruction identify the number of general
|
|
purpose and floating point register arguments to be passed
|
|
and the type of any value to be returned. */
|
|
|
|
uint32_t PSEUDO_HALT = 0xE0000000U;
|
|
uint32_t PSEUDO_CALLOUT = 0x00018000U;
|
|
uint32_t PSEUDO_CALLOUTR = 0x00018001U;
|
|
uint32_t PSEUDO_NOTIFY = 0x00014000U;
|
|
uint32_t dispatch;
|
|
|
|
if (aarch64_get_instr (cpu) == PSEUDO_HALT)
|
|
{
|
|
TRACE_EVENTS (cpu, " Pseudo Halt Instruction");
|
|
sim_engine_halt (CPU_STATE (cpu), cpu, NULL, aarch64_get_PC (cpu),
|
|
sim_stopped, SIM_SIGTRAP);
|
|
}
|
|
|
|
dispatch = uimm (aarch64_get_instr (cpu), 31, 15);
|
|
|
|
/* We do not handle callouts at the moment. */
|
|
if (dispatch == PSEUDO_CALLOUT || dispatch == PSEUDO_CALLOUTR)
|
|
{
|
|
TRACE_EVENTS (cpu, " Callout");
|
|
sim_engine_halt (CPU_STATE (cpu), cpu, NULL, aarch64_get_PC (cpu),
|
|
sim_stopped, SIM_SIGABRT);
|
|
}
|
|
|
|
else if (dispatch == PSEUDO_NOTIFY)
|
|
dexNotify (cpu);
|
|
|
|
else
|
|
HALT_UNALLOC;
|
|
}
|
|
|
|
/* Load-store single register (unscaled offset)
|
|
These instructions employ a base register plus an unscaled signed
|
|
9 bit offset.
|
|
|
|
N.B. the base register (source) can be Xn or SP. all other
|
|
registers may not be SP. */
|
|
|
|
/* 32 bit load 32 bit unscaled signed 9 bit. */
|
|
static void
|
|
ldur32 (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u32
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ offset));
|
|
}
|
|
|
|
/* 64 bit load 64 bit unscaled signed 9 bit. */
|
|
static void
|
|
ldur64 (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u64
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ offset));
|
|
}
|
|
|
|
/* 32 bit load zero-extended byte unscaled signed 9 bit. */
|
|
static void
|
|
ldurb32 (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u8
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ offset));
|
|
}
|
|
|
|
/* 32 bit load sign-extended byte unscaled signed 9 bit. */
|
|
static void
|
|
ldursb32 (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, (uint32_t) aarch64_get_mem_s8
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ offset));
|
|
}
|
|
|
|
/* 64 bit load sign-extended byte unscaled signed 9 bit. */
|
|
static void
|
|
ldursb64 (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_s64 (cpu, rt, NO_SP, aarch64_get_mem_s8
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ offset));
|
|
}
|
|
|
|
/* 32 bit load zero-extended short unscaled signed 9 bit */
|
|
static void
|
|
ldurh32 (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, aarch64_get_mem_u16
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ offset));
|
|
}
|
|
|
|
/* 32 bit load sign-extended short unscaled signed 9 bit */
|
|
static void
|
|
ldursh32 (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, (uint32_t) aarch64_get_mem_s16
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ offset));
|
|
}
|
|
|
|
/* 64 bit load sign-extended short unscaled signed 9 bit */
|
|
static void
|
|
ldursh64 (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_s64 (cpu, rt, NO_SP, aarch64_get_mem_s16
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ offset));
|
|
}
|
|
|
|
/* 64 bit load sign-extended word unscaled signed 9 bit */
|
|
static void
|
|
ldursw (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, (uint32_t) aarch64_get_mem_s32
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ offset));
|
|
}
|
|
|
|
/* N.B. with stores the value in source is written to the address
|
|
identified by source2 modified by offset. */
|
|
|
|
/* 32 bit store 32 bit unscaled signed 9 bit. */
|
|
static void
|
|
stur32 (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_mem_u32 (cpu,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK) + offset,
|
|
aarch64_get_reg_u32 (cpu, rd, NO_SP));
|
|
}
|
|
|
|
/* 64 bit store 64 bit unscaled signed 9 bit */
|
|
static void
|
|
stur64 (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_mem_u64 (cpu,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK) + offset,
|
|
aarch64_get_reg_u64 (cpu, rd, NO_SP));
|
|
}
|
|
|
|
/* 32 bit store byte unscaled signed 9 bit */
|
|
static void
|
|
sturb (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_mem_u8 (cpu,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK) + offset,
|
|
aarch64_get_reg_u8 (cpu, rd, NO_SP));
|
|
}
|
|
|
|
/* 32 bit store short unscaled signed 9 bit */
|
|
static void
|
|
sturh (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_mem_u16 (cpu,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK) + offset,
|
|
aarch64_get_reg_u16 (cpu, rd, NO_SP));
|
|
}
|
|
|
|
/* Load single register pc-relative label
|
|
Offset is a signed 19 bit immediate count in words
|
|
rt may not be SP. */
|
|
|
|
/* 32 bit pc-relative load */
|
|
static void
|
|
ldr32_pcrel (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_mem_u32
|
|
(cpu, aarch64_get_PC (cpu) + offset * 4));
|
|
}
|
|
|
|
/* 64 bit pc-relative load */
|
|
static void
|
|
ldr_pcrel (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_mem_u64
|
|
(cpu, aarch64_get_PC (cpu) + offset * 4));
|
|
}
|
|
|
|
/* sign extended 32 bit pc-relative load */
|
|
static void
|
|
ldrsw_pcrel (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_mem_s32
|
|
(cpu, aarch64_get_PC (cpu) + offset * 4));
|
|
}
|
|
|
|
/* float pc-relative load */
|
|
static void
|
|
fldrs_pcrel (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned int rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, rd,
|
|
aarch64_get_mem_float
|
|
(cpu, aarch64_get_PC (cpu) + offset * 4));
|
|
}
|
|
|
|
/* double pc-relative load */
|
|
static void
|
|
fldrd_pcrel (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, st,
|
|
aarch64_get_mem_double
|
|
(cpu, aarch64_get_PC (cpu) + offset * 4));
|
|
}
|
|
|
|
/* long double pc-relative load. */
|
|
static void
|
|
fldrq_pcrel (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t addr = aarch64_get_PC (cpu) + offset * 4;
|
|
FRegister a;
|
|
|
|
aarch64_get_mem_long_double (cpu, addr, & a);
|
|
aarch64_set_FP_long_double (cpu, st, a);
|
|
}
|
|
|
|
/* This can be used to scale an offset by applying
|
|
the requisite shift. the second argument is either
|
|
16, 32 or 64. */
|
|
|
|
#define SCALE(_offset, _elementSize) \
|
|
((_offset) << ScaleShift ## _elementSize)
|
|
|
|
/* This can be used to optionally scale a register derived offset
|
|
by applying the requisite shift as indicated by the Scaling
|
|
argument. the second argument is either Byte, Short, Word
|
|
or Long. The third argument is either Scaled or Unscaled.
|
|
N.B. when _Scaling is Scaled the shift gets ANDed with
|
|
all 1s while when it is Unscaled it gets ANDed with 0. */
|
|
|
|
#define OPT_SCALE(_offset, _elementType, _Scaling) \
|
|
((_offset) << (_Scaling ? ScaleShift ## _elementType : 0))
|
|
|
|
/* This can be used to zero or sign extend a 32 bit register derived
|
|
value to a 64 bit value. the first argument must be the value as
|
|
a uint32_t and the second must be either UXTW or SXTW. The result
|
|
is returned as an int64_t. */
|
|
|
|
static inline int64_t
|
|
extend (uint32_t value, Extension extension)
|
|
{
|
|
union
|
|
{
|
|
uint32_t u;
|
|
int32_t n;
|
|
} x;
|
|
|
|
/* A branchless variant of this ought to be possible. */
|
|
if (extension == UXTW || extension == NoExtension)
|
|
return value;
|
|
|
|
x.u = value;
|
|
return x.n;
|
|
}
|
|
|
|
/* Scalar Floating Point
|
|
|
|
FP load/store single register (4 addressing modes)
|
|
|
|
N.B. the base register (source) can be the stack pointer.
|
|
The secondary source register (source2) can only be an Xn register. */
|
|
|
|
/* Load 32 bit unscaled signed 9 bit with pre- or post-writeback. */
|
|
static void
|
|
fldrs_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_FP_float (cpu, st, aarch64_get_mem_float (cpu, address));
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* Load 32 bit scaled unsigned 12 bit. */
|
|
static void
|
|
fldrs_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
|
|
aarch64_set_FP_float (cpu, st,
|
|
aarch64_get_mem_float
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ SCALE (offset, 32)));
|
|
}
|
|
|
|
/* Load 32 bit scaled or unscaled zero- or sign-extended
|
|
32-bit register offset. */
|
|
static void
|
|
fldrs_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP), extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 32, scaling);
|
|
|
|
aarch64_set_FP_float (cpu, st,
|
|
aarch64_get_mem_float
|
|
(cpu, address + displacement));
|
|
}
|
|
|
|
/* Load 64 bit unscaled signed 9 bit with pre- or post-writeback. */
|
|
static void
|
|
fldrd_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_FP_double (cpu, st, aarch64_get_mem_double (cpu, address));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* Load 64 bit scaled unsigned 12 bit. */
|
|
static void
|
|
fldrd_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK) + SCALE (offset, 64);
|
|
|
|
aarch64_set_FP_double (cpu, st, aarch64_get_mem_double (cpu, address));
|
|
}
|
|
|
|
/* Load 64 bit scaled or unscaled zero- or sign-extended 32-bit register offset. */
|
|
static void
|
|
fldrd_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP), extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 64, scaling);
|
|
|
|
fldrd_wb (cpu, displacement, NoWriteBack);
|
|
}
|
|
|
|
/* Load 128 bit unscaled signed 9 bit with pre- or post-writeback. */
|
|
static void
|
|
fldrq_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
FRegister a;
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_get_mem_long_double (cpu, address, & a);
|
|
aarch64_set_FP_long_double (cpu, st, a);
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* Load 128 bit scaled unsigned 12 bit. */
|
|
static void
|
|
fldrq_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
FRegister a;
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK) + SCALE (offset, 128);
|
|
|
|
aarch64_get_mem_long_double (cpu, address, & a);
|
|
aarch64_set_FP_long_double (cpu, st, a);
|
|
}
|
|
|
|
/* Load 128 bit scaled or unscaled zero- or sign-extended 32-bit register offset */
|
|
static void
|
|
fldrq_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP), extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 128, scaling);
|
|
|
|
fldrq_wb (cpu, displacement, NoWriteBack);
|
|
}
|
|
|
|
/* Memory Access
|
|
|
|
load-store single register
|
|
There are four addressing modes available here which all employ a
|
|
64 bit source (base) register.
|
|
|
|
N.B. the base register (source) can be the stack pointer.
|
|
The secondary source register (source2)can only be an Xn register.
|
|
|
|
Scaled, 12-bit, unsigned immediate offset, without pre- and
|
|
post-index options.
|
|
Unscaled, 9-bit, signed immediate offset with pre- or post-index
|
|
writeback.
|
|
scaled or unscaled 64-bit register offset.
|
|
scaled or unscaled 32-bit extended register offset.
|
|
|
|
All offsets are assumed to be raw from the decode i.e. the
|
|
simulator is expected to adjust scaled offsets based on the
|
|
accessed data size with register or extended register offset
|
|
versions the same applies except that in the latter case the
|
|
operation may also require a sign extend.
|
|
|
|
A separate method is provided for each possible addressing mode. */
|
|
|
|
/* 32 bit load 32 bit scaled unsigned 12 bit */
|
|
static void
|
|
ldr32_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* The target register may not be SP but the source may be. */
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u32
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ SCALE (offset, 32)));
|
|
}
|
|
|
|
/* 32 bit load 32 bit unscaled signed 9 bit with pre- or post-writeback. */
|
|
static void
|
|
ldr32_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address;
|
|
|
|
if (rn == rt && wb != NoWriteBack)
|
|
HALT_UNALLOC;
|
|
|
|
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u32 (cpu, address));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 32 bit load 32 bit scaled or unscaled
|
|
zero- or sign-extended 32-bit register offset */
|
|
static void
|
|
ldr32_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* rn may reference SP, rm and rt must reference ZR */
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP), extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 32, scaling);
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP,
|
|
aarch64_get_mem_u32 (cpu, address + displacement));
|
|
}
|
|
|
|
/* 64 bit load 64 bit scaled unsigned 12 bit */
|
|
static void
|
|
ldr_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* The target register may not be SP but the source may be. */
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u64
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ SCALE (offset, 64)));
|
|
}
|
|
|
|
/* 64 bit load 64 bit unscaled signed 9 bit with pre- or post-writeback. */
|
|
static void
|
|
ldr_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address;
|
|
|
|
if (rn == rt && wb != NoWriteBack)
|
|
HALT_UNALLOC;
|
|
|
|
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u64 (cpu, address));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 64 bit load 64 bit scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
ldr_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* rn may reference SP, rm and rt must reference ZR */
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP), extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 64, scaling);
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP,
|
|
aarch64_get_mem_u64 (cpu, address + displacement));
|
|
}
|
|
|
|
/* 32 bit load zero-extended byte scaled unsigned 12 bit. */
|
|
static void
|
|
ldrb32_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* The target register may not be SP but the source may be
|
|
there is no scaling required for a byte load. */
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP,
|
|
aarch64_get_mem_u8
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK) + offset));
|
|
}
|
|
|
|
/* 32 bit load zero-extended byte unscaled signed 9 bit with pre- or post-writeback. */
|
|
static void
|
|
ldrb32_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address;
|
|
|
|
if (rn == rt && wb != NoWriteBack)
|
|
HALT_UNALLOC;
|
|
|
|
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u8 (cpu, address));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 32 bit load zero-extended byte scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
ldrb32_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* rn may reference SP, rm and rt must reference ZR */
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t displacement = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
extension);
|
|
|
|
/* There is no scaling required for a byte load. */
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP,
|
|
aarch64_get_mem_u8 (cpu, address + displacement));
|
|
}
|
|
|
|
/* 64 bit load sign-extended byte unscaled signed 9 bit
|
|
with pre- or post-writeback. */
|
|
static void
|
|
ldrsb_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address;
|
|
|
|
if (rn == rt && wb != NoWriteBack)
|
|
HALT_UNALLOC;
|
|
|
|
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_s8 (cpu, address));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 64 bit load sign-extended byte scaled unsigned 12 bit. */
|
|
static void
|
|
ldrsb_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
ldrsb_wb (cpu, offset, NoWriteBack);
|
|
}
|
|
|
|
/* 64 bit load sign-extended byte scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
ldrsb_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* rn may reference SP, rm and rt must reference ZR */
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t displacement = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
extension);
|
|
/* There is no scaling required for a byte load. */
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP,
|
|
aarch64_get_mem_s8 (cpu, address + displacement));
|
|
}
|
|
|
|
/* 32 bit load zero-extended short scaled unsigned 12 bit. */
|
|
static void
|
|
ldrh32_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* The target register may not be SP but the source may be. */
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u16
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ SCALE (offset, 16)));
|
|
}
|
|
|
|
/* 32 bit load zero-extended short unscaled signed 9 bit
|
|
with pre- or post-writeback. */
|
|
static void
|
|
ldrh32_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address;
|
|
|
|
if (rn == rt && wb != NoWriteBack)
|
|
HALT_UNALLOC;
|
|
|
|
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u16 (cpu, address));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 32 bit load zero-extended short scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
ldrh32_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* rn may reference SP, rm and rt must reference ZR */
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP), extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 16, scaling);
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP,
|
|
aarch64_get_mem_u16 (cpu, address + displacement));
|
|
}
|
|
|
|
/* 32 bit load sign-extended short scaled unsigned 12 bit. */
|
|
static void
|
|
ldrsh32_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* The target register may not be SP but the source may be. */
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, (uint32_t) aarch64_get_mem_s16
|
|
(cpu,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ SCALE (offset, 16)));
|
|
}
|
|
|
|
/* 32 bit load sign-extended short unscaled signed 9 bit
|
|
with pre- or post-writeback. */
|
|
static void
|
|
ldrsh32_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address;
|
|
|
|
if (rn == rt && wb != NoWriteBack)
|
|
HALT_UNALLOC;
|
|
|
|
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP,
|
|
(uint32_t) aarch64_get_mem_s16 (cpu, address));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 32 bit load sign-extended short scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
ldrsh32_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* rn may reference SP, rm and rt must reference ZR */
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP), extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 16, scaling);
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP,
|
|
(uint32_t) aarch64_get_mem_s16
|
|
(cpu, address + displacement));
|
|
}
|
|
|
|
/* 64 bit load sign-extended short scaled unsigned 12 bit. */
|
|
static void
|
|
ldrsh_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* The target register may not be SP but the source may be. */
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_s16
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ SCALE (offset, 16)));
|
|
}
|
|
|
|
/* 64 bit load sign-extended short unscaled signed 9 bit
|
|
with pre- or post-writeback. */
|
|
static void
|
|
ldrsh64_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address;
|
|
|
|
if (rn == rt && wb != NoWriteBack)
|
|
HALT_UNALLOC;
|
|
|
|
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_s16 (cpu, address));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 64 bit load sign-extended short scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
ldrsh_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* rn may reference SP, rm and rt must reference ZR */
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP), extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 16, scaling);
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP,
|
|
aarch64_get_mem_s16 (cpu, address + displacement));
|
|
}
|
|
|
|
/* 64 bit load sign-extended 32 bit scaled unsigned 12 bit. */
|
|
static void
|
|
ldrsw_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* The target register may not be SP but the source may be. */
|
|
return aarch64_set_reg_s64 (cpu, rt, NO_SP, aarch64_get_mem_s32
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ SCALE (offset, 32)));
|
|
}
|
|
|
|
/* 64 bit load sign-extended 32 bit unscaled signed 9 bit
|
|
with pre- or post-writeback. */
|
|
static void
|
|
ldrsw_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address;
|
|
|
|
if (rn == rt && wb != NoWriteBack)
|
|
HALT_UNALLOC;
|
|
|
|
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_reg_s64 (cpu, rt, NO_SP, aarch64_get_mem_s32 (cpu, address));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 64 bit load sign-extended 32 bit scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
ldrsw_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* rn may reference SP, rm and rt must reference ZR */
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP), extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 32, scaling);
|
|
|
|
aarch64_set_reg_s64 (cpu, rt, NO_SP,
|
|
aarch64_get_mem_s32 (cpu, address + displacement));
|
|
}
|
|
|
|
/* N.B. with stores the value in source is written to the
|
|
address identified by source2 modified by source3/offset. */
|
|
|
|
/* 32 bit store scaled unsigned 12 bit. */
|
|
static void
|
|
str32_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* The target register may not be SP but the source may be. */
|
|
aarch64_set_mem_u32 (cpu, (aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ SCALE (offset, 32)),
|
|
aarch64_get_reg_u32 (cpu, rt, NO_SP));
|
|
}
|
|
|
|
/* 32 bit store unscaled signed 9 bit with pre- or post-writeback. */
|
|
static void
|
|
str32_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address;
|
|
|
|
if (rn == rt && wb != NoWriteBack)
|
|
HALT_UNALLOC;
|
|
|
|
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_mem_u32 (cpu, address, aarch64_get_reg_u32 (cpu, rt, NO_SP));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 32 bit store scaled or unscaled zero- or
|
|
sign-extended 32-bit register offset. */
|
|
static void
|
|
str32_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP), extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 32, scaling);
|
|
|
|
aarch64_set_mem_u32 (cpu, address + displacement,
|
|
aarch64_get_reg_u64 (cpu, rt, NO_SP));
|
|
}
|
|
|
|
/* 64 bit store scaled unsigned 12 bit. */
|
|
static void
|
|
str_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_mem_u64 (cpu,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ SCALE (offset, 64),
|
|
aarch64_get_reg_u64 (cpu, rt, NO_SP));
|
|
}
|
|
|
|
/* 64 bit store unscaled signed 9 bit with pre- or post-writeback. */
|
|
static void
|
|
str_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address;
|
|
|
|
if (rn == rt && wb != NoWriteBack)
|
|
HALT_UNALLOC;
|
|
|
|
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_mem_u64 (cpu, address, aarch64_get_reg_u64 (cpu, rt, NO_SP));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 64 bit store scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
str_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* rn may reference SP, rm and rt must reference ZR */
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 64, scaling);
|
|
|
|
aarch64_set_mem_u64 (cpu, address + displacement,
|
|
aarch64_get_reg_u64 (cpu, rt, NO_SP));
|
|
}
|
|
|
|
/* 32 bit store byte scaled unsigned 12 bit. */
|
|
static void
|
|
strb_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* The target register may not be SP but the source may be.
|
|
There is no scaling required for a byte load. */
|
|
aarch64_set_mem_u8 (cpu,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK) + offset,
|
|
aarch64_get_reg_u8 (cpu, rt, NO_SP));
|
|
}
|
|
|
|
/* 32 bit store byte unscaled signed 9 bit with pre- or post-writeback. */
|
|
static void
|
|
strb_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address;
|
|
|
|
if (rn == rt && wb != NoWriteBack)
|
|
HALT_UNALLOC;
|
|
|
|
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_mem_u8 (cpu, address, aarch64_get_reg_u8 (cpu, rt, NO_SP));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 32 bit store byte scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
strb_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* rn may reference SP, rm and rt must reference ZR */
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t displacement = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
extension);
|
|
|
|
/* There is no scaling required for a byte load. */
|
|
aarch64_set_mem_u8 (cpu, address + displacement,
|
|
aarch64_get_reg_u8 (cpu, rt, NO_SP));
|
|
}
|
|
|
|
/* 32 bit store short scaled unsigned 12 bit. */
|
|
static void
|
|
strh_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* The target register may not be SP but the source may be. */
|
|
aarch64_set_mem_u16 (cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ SCALE (offset, 16),
|
|
aarch64_get_reg_u16 (cpu, rt, NO_SP));
|
|
}
|
|
|
|
/* 32 bit store short unscaled signed 9 bit with pre- or post-writeback. */
|
|
static void
|
|
strh_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address;
|
|
|
|
if (rn == rt && wb != NoWriteBack)
|
|
HALT_UNALLOC;
|
|
|
|
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_mem_u16 (cpu, address, aarch64_get_reg_u16 (cpu, rt, NO_SP));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 32 bit store short scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
strh_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* rn may reference SP, rm and rt must reference ZR */
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP), extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 16, scaling);
|
|
|
|
aarch64_set_mem_u16 (cpu, address + displacement,
|
|
aarch64_get_reg_u16 (cpu, rt, NO_SP));
|
|
}
|
|
|
|
/* Prefetch unsigned 12 bit. */
|
|
static void
|
|
prfm_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
/* instr[4,0] = prfop : 00000 ==> PLDL1KEEP, 00001 ==> PLDL1STRM,
|
|
00010 ==> PLDL2KEEP, 00001 ==> PLDL2STRM,
|
|
00100 ==> PLDL3KEEP, 00101 ==> PLDL3STRM,
|
|
10000 ==> PSTL1KEEP, 10001 ==> PSTL1STRM,
|
|
10010 ==> PSTL2KEEP, 10001 ==> PSTL2STRM,
|
|
10100 ==> PSTL3KEEP, 10101 ==> PSTL3STRM,
|
|
ow ==> UNALLOC
|
|
PrfOp prfop = prfop (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ SCALE (offset, 64). */
|
|
|
|
/* TODO : implement prefetch of address. */
|
|
}
|
|
|
|
/* Prefetch scaled or unscaled zero- or sign-extended 32-bit register offset. */
|
|
static void
|
|
prfm_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
/* instr[4,0] = prfop : 00000 ==> PLDL1KEEP, 00001 ==> PLDL1STRM,
|
|
00010 ==> PLDL2KEEP, 00001 ==> PLDL2STRM,
|
|
00100 ==> PLDL3KEEP, 00101 ==> PLDL3STRM,
|
|
10000 ==> PSTL1KEEP, 10001 ==> PSTL1STRM,
|
|
10010 ==> PSTL2KEEP, 10001 ==> PSTL2STRM,
|
|
10100 ==> PSTL3KEEP, 10101 ==> PSTL3STRM,
|
|
ow ==> UNALLOC
|
|
rn may reference SP, rm may only reference ZR
|
|
PrfOp prfop = prfop (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t base = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 64, scaling);
|
|
uint64_t address = base + displacement. */
|
|
|
|
/* TODO : implement prefetch of address */
|
|
}
|
|
|
|
/* 64 bit pc-relative prefetch. */
|
|
static void
|
|
prfm_pcrel (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
/* instr[4,0] = prfop : 00000 ==> PLDL1KEEP, 00001 ==> PLDL1STRM,
|
|
00010 ==> PLDL2KEEP, 00001 ==> PLDL2STRM,
|
|
00100 ==> PLDL3KEEP, 00101 ==> PLDL3STRM,
|
|
10000 ==> PSTL1KEEP, 10001 ==> PSTL1STRM,
|
|
10010 ==> PSTL2KEEP, 10001 ==> PSTL2STRM,
|
|
10100 ==> PSTL3KEEP, 10101 ==> PSTL3STRM,
|
|
ow ==> UNALLOC
|
|
PrfOp prfop = prfop (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_PC (cpu) + offset. */
|
|
|
|
/* TODO : implement this */
|
|
}
|
|
|
|
/* Load-store exclusive. */
|
|
|
|
static void
|
|
ldxr (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int size = uimm (aarch64_get_instr (cpu), 31, 30);
|
|
/* int ordered = uimm (aarch64_get_instr (cpu), 15, 15); */
|
|
/* int exclusive = ! uimm (aarch64_get_instr (cpu), 23, 23); */
|
|
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u8 (cpu, address));
|
|
break;
|
|
case 1:
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u16 (cpu, address));
|
|
break;
|
|
case 2:
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u32 (cpu, address));
|
|
break;
|
|
case 3:
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP, aarch64_get_mem_u64 (cpu, address));
|
|
break;
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
stxr (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned rs = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int size = uimm (aarch64_get_instr (cpu), 31, 30);
|
|
uint64_t data = aarch64_get_reg_u64 (cpu, rt, NO_SP);
|
|
|
|
switch (size)
|
|
{
|
|
case 0: aarch64_set_mem_u8 (cpu, address, data); break;
|
|
case 1: aarch64_set_mem_u16 (cpu, address, data); break;
|
|
case 2: aarch64_set_mem_u32 (cpu, address, data); break;
|
|
case 3: aarch64_set_mem_u64 (cpu, address, data); break;
|
|
default: HALT_UNALLOC;
|
|
}
|
|
|
|
aarch64_set_reg_u64 (cpu, rs, NO_SP, 0); /* Always exclusive... */
|
|
}
|
|
|
|
static void
|
|
dexLoadLiteral (sim_cpu *cpu)
|
|
{
|
|
/* instr[29,27] == 011
|
|
instr[25,24] == 00
|
|
instr[31,30:26] = opc: 000 ==> LDRW, 001 ==> FLDRS
|
|
010 ==> LDRX, 011 ==> FLDRD
|
|
100 ==> LDRSW, 101 ==> FLDRQ
|
|
110 ==> PRFM, 111 ==> UNALLOC
|
|
instr[26] ==> V : 0 ==> GReg, 1 ==> FReg
|
|
instr[23, 5] == simm19 */
|
|
|
|
/* unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0); */
|
|
uint32_t dispatch = ( (uimm (aarch64_get_instr (cpu), 31, 30) << 1)
|
|
| uimm (aarch64_get_instr (cpu), 26, 26));
|
|
int32_t imm = simm32 (aarch64_get_instr (cpu), 23, 5);
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 0: ldr32_pcrel (cpu, imm); break;
|
|
case 1: fldrs_pcrel (cpu, imm); break;
|
|
case 2: ldr_pcrel (cpu, imm); break;
|
|
case 3: fldrd_pcrel (cpu, imm); break;
|
|
case 4: ldrsw_pcrel (cpu, imm); break;
|
|
case 5: fldrq_pcrel (cpu, imm); break;
|
|
case 6: prfm_pcrel (cpu, imm); break;
|
|
case 7:
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
/* Immediate arithmetic
|
|
The aimm argument is a 12 bit unsigned value or a 12 bit unsigned
|
|
value left shifted by 12 bits (done at decode).
|
|
|
|
N.B. the register args (dest, source) can normally be Xn or SP.
|
|
the exception occurs for flag setting instructions which may
|
|
only use Xn for the output (dest). */
|
|
|
|
/* 32 bit add immediate. */
|
|
static void
|
|
add32 (sim_cpu *cpu, uint32_t aimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u32 (cpu, rn, SP_OK) + aimm);
|
|
}
|
|
|
|
/* 64 bit add immediate. */
|
|
static void
|
|
add64 (sim_cpu *cpu, uint32_t aimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK) + aimm);
|
|
}
|
|
|
|
static void
|
|
set_flags_for_add32 (sim_cpu *cpu, int32_t value1, int32_t value2)
|
|
{
|
|
int32_t result = value1 + value2;
|
|
int64_t sresult = (int64_t) value1 + (int64_t) value2;
|
|
uint64_t uresult = (uint64_t)(uint32_t) value1
|
|
+ (uint64_t)(uint32_t) value2;
|
|
uint32_t flags = 0;
|
|
|
|
if (result == 0)
|
|
flags |= Z;
|
|
|
|
if (result & (1 << 31))
|
|
flags |= N;
|
|
|
|
if (uresult != result)
|
|
flags |= C;
|
|
|
|
if (sresult != result)
|
|
flags |= V;
|
|
|
|
aarch64_set_CPSR (cpu, flags);
|
|
}
|
|
|
|
static void
|
|
set_flags_for_add64 (sim_cpu *cpu, uint64_t value1, uint64_t value2)
|
|
{
|
|
int64_t sval1 = value1;
|
|
int64_t sval2 = value2;
|
|
uint64_t result = value1 + value2;
|
|
int64_t sresult = sval1 + sval2;
|
|
uint32_t flags = 0;
|
|
|
|
if (result == 0)
|
|
flags |= Z;
|
|
|
|
if (result & (1ULL << 63))
|
|
flags |= N;
|
|
|
|
if (sval1 < 0)
|
|
{
|
|
if (sval2 < 0)
|
|
{
|
|
/* Negative plus a negative. Overflow happens if
|
|
the result is greater than either of the operands. */
|
|
if (sresult > sval1 || sresult > sval2)
|
|
flags |= V;
|
|
}
|
|
/* else Negative plus a positive. Overflow cannot happen. */
|
|
}
|
|
else /* value1 is +ve. */
|
|
{
|
|
if (sval2 < 0)
|
|
{
|
|
/* Overflow can only occur if we computed "0 - MININT". */
|
|
if (sval1 == 0 && sval2 == (1LL << 63))
|
|
flags |= V;
|
|
}
|
|
else
|
|
{
|
|
/* Postive plus positive - overflow has happened if the
|
|
result is smaller than either of the operands. */
|
|
if (result < value1 || result < value2)
|
|
flags |= V | C;
|
|
}
|
|
}
|
|
|
|
aarch64_set_CPSR (cpu, flags);
|
|
}
|
|
|
|
#define NEG(a) (((a) & signbit) == signbit)
|
|
#define POS(a) (((a) & signbit) == 0)
|
|
|
|
static void
|
|
set_flags_for_sub32 (sim_cpu *cpu, uint32_t value1, uint32_t value2)
|
|
{
|
|
uint32_t result = value1 - value2;
|
|
uint32_t flags = 0;
|
|
uint32_t signbit = 1ULL << 31;
|
|
|
|
if (result == 0)
|
|
flags |= Z;
|
|
|
|
if (NEG (result))
|
|
flags |= N;
|
|
|
|
if ( (NEG (value1) && POS (value2))
|
|
|| (NEG (value1) && POS (result))
|
|
|| (POS (value2) && POS (result)))
|
|
flags |= C;
|
|
|
|
if ( (NEG (value1) && POS (value2) && POS (result))
|
|
|| (POS (value1) && NEG (value2) && NEG (result)))
|
|
flags |= V;
|
|
|
|
aarch64_set_CPSR (cpu, flags);
|
|
}
|
|
|
|
static void
|
|
set_flags_for_sub64 (sim_cpu *cpu, uint64_t value1, uint64_t value2)
|
|
{
|
|
uint64_t result = value1 - value2;
|
|
uint32_t flags = 0;
|
|
uint64_t signbit = 1ULL << 63;
|
|
|
|
if (result == 0)
|
|
flags |= Z;
|
|
|
|
if (NEG (result))
|
|
flags |= N;
|
|
|
|
if ( (NEG (value1) && POS (value2))
|
|
|| (NEG (value1) && POS (result))
|
|
|| (POS (value2) && POS (result)))
|
|
flags |= C;
|
|
|
|
if ( (NEG (value1) && POS (value2) && POS (result))
|
|
|| (POS (value1) && NEG (value2) && NEG (result)))
|
|
flags |= V;
|
|
|
|
aarch64_set_CPSR (cpu, flags);
|
|
}
|
|
|
|
static void
|
|
set_flags_for_binop32 (sim_cpu *cpu, uint32_t result)
|
|
{
|
|
uint32_t flags = 0;
|
|
|
|
if (result == 0)
|
|
flags |= Z;
|
|
else
|
|
flags &= ~ Z;
|
|
|
|
if (result & (1 << 31))
|
|
flags |= N;
|
|
else
|
|
flags &= ~ N;
|
|
|
|
aarch64_set_CPSR (cpu, flags);
|
|
}
|
|
|
|
static void
|
|
set_flags_for_binop64 (sim_cpu *cpu, uint64_t result)
|
|
{
|
|
uint32_t flags = 0;
|
|
|
|
if (result == 0)
|
|
flags |= Z;
|
|
else
|
|
flags &= ~ Z;
|
|
|
|
if (result & (1ULL << 63))
|
|
flags |= N;
|
|
else
|
|
flags &= ~ N;
|
|
|
|
aarch64_set_CPSR (cpu, flags);
|
|
}
|
|
|
|
/* 32 bit add immediate set flags. */
|
|
static void
|
|
adds32 (sim_cpu *cpu, uint32_t aimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* TODO : do we need to worry about signs here? */
|
|
int32_t value1 = aarch64_get_reg_s32 (cpu, rn, SP_OK);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 + aimm);
|
|
set_flags_for_add32 (cpu, value1, aimm);
|
|
}
|
|
|
|
/* 64 bit add immediate set flags. */
|
|
static void
|
|
adds64 (sim_cpu *cpu, uint32_t aimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
uint64_t value2 = aimm;
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 + value2);
|
|
set_flags_for_add64 (cpu, value1, value2);
|
|
}
|
|
|
|
/* 32 bit sub immediate. */
|
|
static void
|
|
sub32 (sim_cpu *cpu, uint32_t aimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u32 (cpu, rn, SP_OK) - aimm);
|
|
}
|
|
|
|
/* 64 bit sub immediate. */
|
|
static void
|
|
sub64 (sim_cpu *cpu, uint32_t aimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK) - aimm);
|
|
}
|
|
|
|
/* 32 bit sub immediate set flags. */
|
|
static void
|
|
subs32 (sim_cpu *cpu, uint32_t aimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint32_t value1 = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
uint32_t value2 = aimm;
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 - value2);
|
|
set_flags_for_sub32 (cpu, value1, value2);
|
|
}
|
|
|
|
/* 64 bit sub immediate set flags. */
|
|
static void
|
|
subs64 (sim_cpu *cpu, uint32_t aimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
uint32_t value2 = aimm;
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 - value2);
|
|
set_flags_for_sub64 (cpu, value1, value2);
|
|
}
|
|
|
|
/* Data Processing Register. */
|
|
|
|
/* First two helpers to perform the shift operations. */
|
|
|
|
static inline uint32_t
|
|
shifted32 (uint32_t value, Shift shift, uint32_t count)
|
|
{
|
|
switch (shift)
|
|
{
|
|
default:
|
|
case LSL:
|
|
return (value << count);
|
|
case LSR:
|
|
return (value >> count);
|
|
case ASR:
|
|
{
|
|
int32_t svalue = value;
|
|
return (svalue >> count);
|
|
}
|
|
case ROR:
|
|
{
|
|
uint32_t top = value >> count;
|
|
uint32_t bottom = value << (32 - count);
|
|
return (bottom | top);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline uint64_t
|
|
shifted64 (uint64_t value, Shift shift, uint32_t count)
|
|
{
|
|
switch (shift)
|
|
{
|
|
default:
|
|
case LSL:
|
|
return (value << count);
|
|
case LSR:
|
|
return (value >> count);
|
|
case ASR:
|
|
{
|
|
int64_t svalue = value;
|
|
return (svalue >> count);
|
|
}
|
|
case ROR:
|
|
{
|
|
uint64_t top = value >> count;
|
|
uint64_t bottom = value << (64 - count);
|
|
return (bottom | top);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Arithmetic shifted register.
|
|
These allow an optional LSL, ASR or LSR to the second source
|
|
register with a count up to the register bit count.
|
|
|
|
N.B register args may not be SP. */
|
|
|
|
/* 32 bit ADD shifted register. */
|
|
static void
|
|
add32_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
+ shifted32 (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
shift, count));
|
|
}
|
|
|
|
/* 64 bit ADD shifted register. */
|
|
static void
|
|
add64_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
+ shifted64 (aarch64_get_reg_u64 (cpu, rm, NO_SP),
|
|
shift, count));
|
|
}
|
|
|
|
/* 32 bit ADD shifted register setting flags. */
|
|
static void
|
|
adds32_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint32_t value1 = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
uint32_t value2 = shifted32 (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
shift, count);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 + value2);
|
|
set_flags_for_add32 (cpu, value1, value2);
|
|
}
|
|
|
|
/* 64 bit ADD shifted register setting flags. */
|
|
static void
|
|
adds64_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
uint64_t value2 = shifted64 (aarch64_get_reg_u64 (cpu, rm, NO_SP),
|
|
shift, count);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 + value2);
|
|
set_flags_for_add64 (cpu, value1, value2);
|
|
}
|
|
|
|
/* 32 bit SUB shifted register. */
|
|
static void
|
|
sub32_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
- shifted32 (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
shift, count));
|
|
}
|
|
|
|
/* 64 bit SUB shifted register. */
|
|
static void
|
|
sub64_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
- shifted64 (aarch64_get_reg_u64 (cpu, rm, NO_SP),
|
|
shift, count));
|
|
}
|
|
|
|
/* 32 bit SUB shifted register setting flags. */
|
|
static void
|
|
subs32_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint32_t value1 = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
uint32_t value2 = shifted32 (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
shift, count);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 - value2);
|
|
set_flags_for_sub32 (cpu, value1, value2);
|
|
}
|
|
|
|
/* 64 bit SUB shifted register setting flags. */
|
|
static void
|
|
subs64_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
uint64_t value2 = shifted64 (aarch64_get_reg_u64 (cpu, rm, NO_SP),
|
|
shift, count);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 - value2);
|
|
set_flags_for_sub64 (cpu, value1, value2);
|
|
}
|
|
|
|
/* First a couple more helpers to fetch the
|
|
relevant source register element either
|
|
sign or zero extended as required by the
|
|
extension value. */
|
|
|
|
static uint32_t
|
|
extreg32 (sim_cpu *cpu, unsigned int lo, Extension extension)
|
|
{
|
|
switch (extension)
|
|
{
|
|
case UXTB: return aarch64_get_reg_u8 (cpu, lo, NO_SP);
|
|
case UXTH: return aarch64_get_reg_u16 (cpu, lo, NO_SP);
|
|
case UXTW: /* Fall through. */
|
|
case UXTX: return aarch64_get_reg_u32 (cpu, lo, NO_SP);
|
|
case SXTB: return aarch64_get_reg_s8 (cpu, lo, NO_SP);
|
|
case SXTH: return aarch64_get_reg_s16 (cpu, lo, NO_SP);
|
|
case SXTW: /* Fall through. */
|
|
case SXTX: /* Fall through. */
|
|
default: return aarch64_get_reg_s32 (cpu, lo, NO_SP);
|
|
}
|
|
}
|
|
|
|
static uint64_t
|
|
extreg64 (sim_cpu *cpu, unsigned int lo, Extension extension)
|
|
{
|
|
switch (extension)
|
|
{
|
|
case UXTB: return aarch64_get_reg_u8 (cpu, lo, NO_SP);
|
|
case UXTH: return aarch64_get_reg_u16 (cpu, lo, NO_SP);
|
|
case UXTW: return aarch64_get_reg_u32 (cpu, lo, NO_SP);
|
|
case UXTX: return aarch64_get_reg_u64 (cpu, lo, NO_SP);
|
|
case SXTB: return aarch64_get_reg_s8 (cpu, lo, NO_SP);
|
|
case SXTH: return aarch64_get_reg_s16 (cpu, lo, NO_SP);
|
|
case SXTW: return aarch64_get_reg_s32 (cpu, lo, NO_SP);
|
|
case SXTX:
|
|
default: return aarch64_get_reg_s64 (cpu, lo, NO_SP);
|
|
}
|
|
}
|
|
|
|
/* Arithmetic extending register
|
|
These allow an optional sign extension of some portion of the
|
|
second source register followed by an optional left shift of
|
|
between 1 and 4 bits (i.e. a shift of 0-4 bits???)
|
|
|
|
N.B output (dest) and first input arg (source) may normally be Xn
|
|
or SP. However, for flag setting operations dest can only be
|
|
Xn. Second input registers are always Xn. */
|
|
|
|
/* 32 bit ADD extending register. */
|
|
static void
|
|
add32_ext (sim_cpu *cpu, Extension extension, uint32_t shift)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u32 (cpu, rn, SP_OK)
|
|
+ (extreg32 (cpu, rm, extension) << shift));
|
|
}
|
|
|
|
/* 64 bit ADD extending register.
|
|
N.B. This subsumes the case with 64 bit source2 and UXTX #n or LSL #0. */
|
|
static void
|
|
add64_ext (sim_cpu *cpu, Extension extension, uint32_t shift)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
+ (extreg64 (cpu, rm, extension) << shift));
|
|
}
|
|
|
|
/* 32 bit ADD extending register setting flags. */
|
|
static void
|
|
adds32_ext (sim_cpu *cpu, Extension extension, uint32_t shift)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint32_t value1 = aarch64_get_reg_u32 (cpu, rn, SP_OK);
|
|
uint32_t value2 = extreg32 (cpu, rm, extension) << shift;
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 + value2);
|
|
set_flags_for_add32 (cpu, value1, value2);
|
|
}
|
|
|
|
/* 64 bit ADD extending register setting flags */
|
|
/* N.B. this subsumes the case with 64 bit source2 and UXTX #n or LSL #0 */
|
|
static void
|
|
adds64_ext (sim_cpu *cpu, Extension extension, uint32_t shift)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
uint64_t value2 = extreg64 (cpu, rm, extension) << shift;
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 + value2);
|
|
set_flags_for_add64 (cpu, value1, value2);
|
|
}
|
|
|
|
/* 32 bit SUB extending register. */
|
|
static void
|
|
sub32_ext (sim_cpu *cpu, Extension extension, uint32_t shift)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u32 (cpu, rn, SP_OK)
|
|
- (extreg32 (cpu, rm, extension) << shift));
|
|
}
|
|
|
|
/* 64 bit SUB extending register. */
|
|
/* N.B. this subsumes the case with 64 bit source2 and UXTX #n or LSL #0. */
|
|
static void
|
|
sub64_ext (sim_cpu *cpu, Extension extension, uint32_t shift)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK)
|
|
- (extreg64 (cpu, rm, extension) << shift));
|
|
}
|
|
|
|
/* 32 bit SUB extending register setting flags. */
|
|
static void
|
|
subs32_ext (sim_cpu *cpu, Extension extension, uint32_t shift)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint32_t value1 = aarch64_get_reg_u32 (cpu, rn, SP_OK);
|
|
uint32_t value2 = extreg32 (cpu, rm, extension) << shift;
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 - value2);
|
|
set_flags_for_sub32 (cpu, value1, value2);
|
|
}
|
|
|
|
/* 64 bit SUB extending register setting flags */
|
|
/* N.B. this subsumes the case with 64 bit source2 and UXTX #n or LSL #0 */
|
|
static void
|
|
subs64_ext (sim_cpu *cpu, Extension extension, uint32_t shift)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
uint64_t value2 = extreg64 (cpu, rm, extension) << shift;
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 - value2);
|
|
set_flags_for_sub64 (cpu, value1, value2);
|
|
}
|
|
|
|
static void
|
|
dexAddSubtractImmediate (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[30] = op : 0 ==> ADD, 1 ==> SUB
|
|
instr[29] = set : 0 ==> no flags, 1 ==> set flags
|
|
instr[28,24] = 10001
|
|
instr[23,22] = shift : 00 == LSL#0, 01 = LSL#12 1x = UNALLOC
|
|
instr[21,10] = uimm12
|
|
instr[9,5] = Rn
|
|
instr[4,0] = Rd */
|
|
|
|
/* N.B. the shift is applied at decode before calling the add/sub routine. */
|
|
uint32_t shift = uimm (aarch64_get_instr (cpu), 23, 22);
|
|
uint32_t imm = uimm (aarch64_get_instr (cpu), 21, 10);
|
|
uint32_t dispatch = uimm (aarch64_get_instr (cpu), 31, 29);
|
|
|
|
NYI_assert (28, 24, 0x11);
|
|
|
|
if (shift > 1)
|
|
HALT_UNALLOC;
|
|
|
|
if (shift)
|
|
imm <<= 12;
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 0: add32 (cpu, imm); break;
|
|
case 1: adds32 (cpu, imm); break;
|
|
case 2: sub32 (cpu, imm); break;
|
|
case 3: subs32 (cpu, imm); break;
|
|
case 4: add64 (cpu, imm); break;
|
|
case 5: adds64 (cpu, imm); break;
|
|
case 6: sub64 (cpu, imm); break;
|
|
case 7: subs64 (cpu, imm); break;
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexAddSubtractShiftedRegister (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[30,29] = op : 00 ==> ADD, 01 ==> ADDS, 10 ==> SUB, 11 ==> SUBS
|
|
instr[28,24] = 01011
|
|
instr[23,22] = shift : 0 ==> LSL, 1 ==> LSR, 2 ==> ASR, 3 ==> UNALLOC
|
|
instr[21] = 0
|
|
instr[20,16] = Rm
|
|
instr[15,10] = count : must be 0xxxxx for 32 bit
|
|
instr[9,5] = Rn
|
|
instr[4,0] = Rd */
|
|
|
|
uint32_t size = uimm (aarch64_get_instr (cpu), 31, 31);
|
|
/* 32 bit operations must have count[5] = 0
|
|
or else we have an UNALLOC. */
|
|
uint32_t count = uimm (aarch64_get_instr (cpu), 15, 10);
|
|
/* Shift encoded as ROR is unallocated. */
|
|
Shift shiftType = shift (aarch64_get_instr (cpu), 22);
|
|
/* Dispatch on size:op i.e aarch64_get_instr (cpu)[31,29]. */
|
|
uint32_t dispatch = uimm (aarch64_get_instr (cpu), 31, 29);
|
|
|
|
NYI_assert (28, 24, 0x0B);
|
|
NYI_assert (21, 21, 0);
|
|
|
|
if (shiftType == ROR)
|
|
HALT_UNALLOC;
|
|
|
|
if (!size && uimm (count, 5, 5))
|
|
HALT_UNALLOC;
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 0: add32_shift (cpu, shiftType, count); break;
|
|
case 1: adds32_shift (cpu, shiftType, count); break;
|
|
case 2: sub32_shift (cpu, shiftType, count); break;
|
|
case 3: subs32_shift (cpu, shiftType, count); break;
|
|
case 4: add64_shift (cpu, shiftType, count); break;
|
|
case 5: adds64_shift (cpu, shiftType, count); break;
|
|
case 6: sub64_shift (cpu, shiftType, count); break;
|
|
case 7: subs64_shift (cpu, shiftType, count); break;
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexAddSubtractExtendedRegister (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[30] = op : 0 ==> ADD, 1 ==> SUB
|
|
instr[29] = set? : 0 ==> no flags, 1 ==> set flags
|
|
instr[28,24] = 01011
|
|
instr[23,22] = opt : 0 ==> ok, 1,2,3 ==> UNALLOC
|
|
instr[21] = 1
|
|
instr[20,16] = Rm
|
|
instr[15,13] = option : 000 ==> UXTB, 001 ==> UXTH,
|
|
000 ==> LSL|UXTW, 001 ==> UXTZ,
|
|
000 ==> SXTB, 001 ==> SXTH,
|
|
000 ==> SXTW, 001 ==> SXTX,
|
|
instr[12,10] = shift : 0,1,2,3,4 ==> ok, 5,6,7 ==> UNALLOC
|
|
instr[9,5] = Rn
|
|
instr[4,0] = Rd */
|
|
|
|
Extension extensionType = extension (aarch64_get_instr (cpu), 13);
|
|
uint32_t shift = uimm (aarch64_get_instr (cpu), 12, 10);
|
|
/* dispatch on size:op:set? i.e aarch64_get_instr (cpu)[31,29] */
|
|
uint32_t dispatch = uimm (aarch64_get_instr (cpu), 31, 29);
|
|
|
|
NYI_assert (28, 24, 0x0B);
|
|
NYI_assert (21, 21, 1);
|
|
|
|
/* Shift may not exceed 4. */
|
|
if (shift > 4)
|
|
HALT_UNALLOC;
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 0: add32_ext (cpu, extensionType, shift); break;
|
|
case 1: adds32_ext (cpu, extensionType, shift); break;
|
|
case 2: sub32_ext (cpu, extensionType, shift); break;
|
|
case 3: subs32_ext (cpu, extensionType, shift); break;
|
|
case 4: add64_ext (cpu, extensionType, shift); break;
|
|
case 5: adds64_ext (cpu, extensionType, shift); break;
|
|
case 6: sub64_ext (cpu, extensionType, shift); break;
|
|
case 7: subs64_ext (cpu, extensionType, shift); break;
|
|
default: HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
/* Conditional data processing
|
|
Condition register is implicit 3rd source. */
|
|
|
|
/* 32 bit add with carry. */
|
|
/* N.B register args may not be SP. */
|
|
|
|
static void
|
|
adc32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
+ aarch64_get_reg_u32 (cpu, rm, NO_SP)
|
|
+ IS_SET (C));
|
|
}
|
|
|
|
/* 64 bit add with carry */
|
|
static void
|
|
adc64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
+ aarch64_get_reg_u64 (cpu, rm, NO_SP)
|
|
+ IS_SET (C));
|
|
}
|
|
|
|
/* 32 bit add with carry setting flags. */
|
|
static void
|
|
adcs32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint32_t value1 = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
uint32_t value2 = aarch64_get_reg_u32 (cpu, rm, NO_SP);
|
|
uint32_t carry = IS_SET (C);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 + value2 + carry);
|
|
set_flags_for_add32 (cpu, value1, value2 + carry);
|
|
}
|
|
|
|
/* 64 bit add with carry setting flags. */
|
|
static void
|
|
adcs64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
uint64_t value2 = aarch64_get_reg_u64 (cpu, rm, NO_SP);
|
|
uint64_t carry = IS_SET (C);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 + value2 + carry);
|
|
set_flags_for_add64 (cpu, value1, value2 + carry);
|
|
}
|
|
|
|
/* 32 bit sub with carry. */
|
|
static void
|
|
sbc32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
- aarch64_get_reg_u32 (cpu, rm, NO_SP)
|
|
- 1 + IS_SET (C));
|
|
}
|
|
|
|
/* 64 bit sub with carry */
|
|
static void
|
|
sbc64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
- aarch64_get_reg_u64 (cpu, rm, NO_SP)
|
|
- 1 + IS_SET (C));
|
|
}
|
|
|
|
/* 32 bit sub with carry setting flags */
|
|
static void
|
|
sbcs32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint32_t value1 = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
uint32_t value2 = aarch64_get_reg_u32 (cpu, rm, NO_SP);
|
|
uint32_t carry = IS_SET (C);
|
|
uint32_t result = value1 - value2 + 1 - carry;
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, result);
|
|
set_flags_for_sub32 (cpu, value1, value2 + 1 - carry);
|
|
}
|
|
|
|
/* 64 bit sub with carry setting flags */
|
|
static void
|
|
sbcs64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
uint64_t value2 = aarch64_get_reg_u64 (cpu, rm, NO_SP);
|
|
uint64_t carry = IS_SET (C);
|
|
uint64_t result = value1 - value2 + 1 - carry;
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, result);
|
|
set_flags_for_sub64 (cpu, value1, value2 + 1 - carry);
|
|
}
|
|
|
|
static void
|
|
dexAddSubtractWithCarry (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[30] = op : 0 ==> ADC, 1 ==> SBC
|
|
instr[29] = set? : 0 ==> no flags, 1 ==> set flags
|
|
instr[28,21] = 1 1010 000
|
|
instr[20,16] = Rm
|
|
instr[15,10] = op2 : 00000 ==> ok, ow ==> UNALLOC
|
|
instr[9,5] = Rn
|
|
instr[4,0] = Rd */
|
|
|
|
uint32_t op2 = uimm (aarch64_get_instr (cpu), 15, 10);
|
|
/* Dispatch on size:op:set? i.e aarch64_get_instr (cpu)[31,29] */
|
|
uint32_t dispatch = uimm (aarch64_get_instr (cpu), 31, 29);
|
|
|
|
NYI_assert (28, 21, 0xD0);
|
|
|
|
if (op2 != 0)
|
|
HALT_UNALLOC;
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 0: adc32 (cpu); break;
|
|
case 1: adcs32 (cpu); break;
|
|
case 2: sbc32 (cpu); break;
|
|
case 3: sbcs32 (cpu); break;
|
|
case 4: adc64 (cpu); break;
|
|
case 5: adcs64 (cpu); break;
|
|
case 6: sbc64 (cpu); break;
|
|
case 7: sbcs64 (cpu); break;
|
|
default: HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
testConditionCode (sim_cpu *cpu, CondCode cc)
|
|
{
|
|
/* This should be reduceable to branchless logic
|
|
by some careful testing of bits in CC followed
|
|
by the requisite masking and combining of bits
|
|
from the flag register.
|
|
|
|
For now we do it with a switch. */
|
|
int res;
|
|
|
|
switch (cc)
|
|
{
|
|
case EQ: res = IS_SET (Z); break;
|
|
case NE: res = IS_CLEAR (Z); break;
|
|
case CS: res = IS_SET (C); break;
|
|
case CC: res = IS_CLEAR (C); break;
|
|
case MI: res = IS_SET (N); break;
|
|
case PL: res = IS_CLEAR (N); break;
|
|
case VS: res = IS_SET (V); break;
|
|
case VC: res = IS_CLEAR (V); break;
|
|
case HI: res = IS_SET (C) && IS_CLEAR (Z); break;
|
|
case LS: res = IS_CLEAR (C) || IS_SET (Z); break;
|
|
case GE: res = IS_SET (N) == IS_SET (V); break;
|
|
case LT: res = IS_SET (N) != IS_SET (V); break;
|
|
case GT: res = IS_CLEAR (Z) && (IS_SET (N) == IS_SET (V)); break;
|
|
case LE: res = IS_SET (Z) || (IS_SET (N) != IS_SET (V)); break;
|
|
case AL:
|
|
case NV:
|
|
default:
|
|
res = 1;
|
|
break;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
CondCompare (sim_cpu *cpu) /* aka: ccmp and ccmn */
|
|
{
|
|
/* instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[30] = compare with positive (0) or negative value (1)
|
|
instr[29,21] = 1 1101 0010
|
|
instr[20,16] = Rm or const
|
|
instr[15,12] = cond
|
|
instr[11] = compare reg (0) or const (1)
|
|
instr[10] = 0
|
|
instr[9,5] = Rn
|
|
instr[4] = 0
|
|
instr[3,0] = value for CPSR bits if the comparison does not take place. */
|
|
signed int negate;
|
|
unsigned rm;
|
|
unsigned rn;
|
|
|
|
NYI_assert (29, 21, 0x1d2);
|
|
NYI_assert (10, 10, 0);
|
|
NYI_assert (4, 4, 0);
|
|
|
|
if (! testConditionCode (cpu, uimm (aarch64_get_instr (cpu), 15, 12)))
|
|
{
|
|
aarch64_set_CPSR (cpu, uimm (aarch64_get_instr (cpu), 3, 0));
|
|
return;
|
|
}
|
|
|
|
negate = uimm (aarch64_get_instr (cpu), 30, 30) ? -1 : 1;
|
|
rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 31, 31))
|
|
{
|
|
if (uimm (aarch64_get_instr (cpu), 11, 11))
|
|
set_flags_for_sub64 (cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK),
|
|
negate * (uint64_t) rm);
|
|
else
|
|
set_flags_for_sub64 (cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK),
|
|
negate * aarch64_get_reg_u64 (cpu, rm, SP_OK));
|
|
}
|
|
else
|
|
{
|
|
if (uimm (aarch64_get_instr (cpu), 11, 11))
|
|
set_flags_for_sub32 (cpu, aarch64_get_reg_u32 (cpu, rn, SP_OK),
|
|
negate * rm);
|
|
else
|
|
set_flags_for_sub32 (cpu, aarch64_get_reg_u32 (cpu, rn, SP_OK),
|
|
negate * aarch64_get_reg_u32 (cpu, rm, SP_OK));
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_MOV_whole_vector (sim_cpu *cpu)
|
|
{
|
|
/* MOV Vd.T, Vs.T (alias for ORR Vd.T, Vn.T, Vm.T where Vn == Vm)
|
|
|
|
instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29,21] = 001110101
|
|
instr[20,16] = Vs
|
|
instr[15,10] = 000111
|
|
instr[9,5] = Vs
|
|
instr[4,0] = Vd */
|
|
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (29, 21, 0x075);
|
|
NYI_assert (15, 10, 0x07);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 20, 16) != vs)
|
|
HALT_NYI;
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 30, 30))
|
|
aarch64_set_vec_u64 (cpu, vd, 1, aarch64_get_vec_u64 (cpu, vs, 1));
|
|
|
|
aarch64_set_vec_u64 (cpu, vd, 0, aarch64_get_vec_u64 (cpu, vs, 0));
|
|
}
|
|
|
|
static void
|
|
do_vec_MOV_into_scalar (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = word(0)/long(1)
|
|
instr[29,21] = 00 1110 000
|
|
instr[20,18] = element size and index
|
|
instr[17,10] = 00 0011 11
|
|
instr[9,5] = V source
|
|
instr[4,0] = R dest */
|
|
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (29, 21, 0x070);
|
|
NYI_assert (17, 10, 0x0F);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 20, 18))
|
|
{
|
|
case 0x2:
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, aarch64_get_vec_u64 (cpu, vs, 0));
|
|
break;
|
|
|
|
case 0x6:
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, aarch64_get_vec_u64 (cpu, vs, 1));
|
|
break;
|
|
|
|
case 0x1:
|
|
case 0x3:
|
|
case 0x5:
|
|
case 0x7:
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, aarch64_get_vec_u32
|
|
(cpu, vs, uimm (aarch64_get_instr (cpu), 20, 19)));
|
|
break;
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_INS (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,21] = 01001110000
|
|
instr[20,16] = element size and index
|
|
instr[15,10] = 000111
|
|
instr[9,5] = W source
|
|
instr[4,0] = V dest */
|
|
|
|
int index;
|
|
unsigned rs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (31, 21, 0x270);
|
|
NYI_assert (15, 10, 0x07);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 16, 16))
|
|
{
|
|
index = uimm (aarch64_get_instr (cpu), 20, 17);
|
|
aarch64_set_vec_u8 (cpu, vd, index,
|
|
aarch64_get_reg_u8 (cpu, rs, NO_SP));
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 17, 17))
|
|
{
|
|
index = uimm (aarch64_get_instr (cpu), 20, 18);
|
|
aarch64_set_vec_u16 (cpu, vd, index,
|
|
aarch64_get_reg_u16 (cpu, rs, NO_SP));
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 18, 18))
|
|
{
|
|
index = uimm (aarch64_get_instr (cpu), 20, 19);
|
|
aarch64_set_vec_u32 (cpu, vd, index,
|
|
aarch64_get_reg_u32 (cpu, rs, NO_SP));
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 19, 19))
|
|
{
|
|
index = uimm (aarch64_get_instr (cpu), 20, 20);
|
|
aarch64_set_vec_u64 (cpu, vd, index,
|
|
aarch64_get_reg_u64 (cpu, rs, NO_SP));
|
|
}
|
|
else
|
|
HALT_NYI;
|
|
}
|
|
|
|
static void
|
|
do_vec_DUP_vector_into_vector (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29,21] = 00 1110 000
|
|
instr[20,16] = element size and index
|
|
instr[15,10] = 0000 01
|
|
instr[9,5] = V source
|
|
instr[4,0] = V dest. */
|
|
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
int i, index;
|
|
|
|
NYI_assert (29, 21, 0x070);
|
|
NYI_assert (15, 10, 0x01);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 16, 16))
|
|
{
|
|
index = uimm (aarch64_get_instr (cpu), 20, 17);
|
|
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i, aarch64_get_vec_u8 (cpu, vs, index));
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 17, 17))
|
|
{
|
|
index = uimm (aarch64_get_instr (cpu), 20, 18);
|
|
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i, aarch64_get_vec_u16 (cpu, vs, index));
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 18, 18))
|
|
{
|
|
index = uimm (aarch64_get_instr (cpu), 20, 19);
|
|
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i, aarch64_get_vec_u32 (cpu, vs, index));
|
|
}
|
|
else
|
|
{
|
|
if (uimm (aarch64_get_instr (cpu), 19, 19) == 0)
|
|
HALT_UNALLOC;
|
|
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
|
|
index = uimm (aarch64_get_instr (cpu), 20, 20);
|
|
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_u64 (cpu, vd, i, aarch64_get_vec_u64 (cpu, vs, index));
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_TBL (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29,21] = 00 1110 000
|
|
instr[20,16] = Vm
|
|
instr[15] = 0
|
|
instr[14,13] = vec length
|
|
instr[12,10] = 000
|
|
instr[9,5] = V start
|
|
instr[4,0] = V dest */
|
|
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
int len = uimm (aarch64_get_instr (cpu), 14, 13) + 1;
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 21, 0x070);
|
|
NYI_assert (12, 10, 0);
|
|
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
{
|
|
unsigned int selector = aarch64_get_vec_u8 (cpu, vm, i);
|
|
uint8_t val;
|
|
|
|
if (selector < 16)
|
|
val = aarch64_get_vec_u8 (cpu, vn, selector);
|
|
else if (selector < 32)
|
|
val = len < 2 ? 0 : aarch64_get_vec_u8 (cpu, vn + 1, selector - 16);
|
|
else if (selector < 48)
|
|
val = len < 3 ? 0 : aarch64_get_vec_u8 (cpu, vn + 2, selector - 32);
|
|
else if (selector < 64)
|
|
val = len < 4 ? 0 : aarch64_get_vec_u8 (cpu, vn + 3, selector - 48);
|
|
else
|
|
val = 0;
|
|
|
|
aarch64_set_vec_u8 (cpu, vd, i, val);
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_TRN (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29,24] = 00 1110
|
|
instr[23,22] = size
|
|
instr[21] = 0
|
|
instr[20,16] = Vm
|
|
instr[15] = 0
|
|
instr[14] = TRN1 (0) / TRN2 (1)
|
|
instr[13,10] = 1010
|
|
instr[9,5] = V source
|
|
instr[4,0] = V dest. */
|
|
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
int second = uimm (aarch64_get_instr (cpu), 14, 14);
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 24, 0x0E);
|
|
NYI_assert (13, 10, 0xA);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
{
|
|
aarch64_set_vec_u8
|
|
(cpu, vd, i * 2,
|
|
aarch64_get_vec_u8 (cpu, second ? vm : vn, i * 2));
|
|
aarch64_set_vec_u8
|
|
(cpu, vd, 1 * 2 + 1,
|
|
aarch64_get_vec_u8 (cpu, second ? vn : vm, i * 2 + 1));
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
{
|
|
aarch64_set_vec_u16
|
|
(cpu, vd, i * 2,
|
|
aarch64_get_vec_u16 (cpu, second ? vm : vn, i * 2));
|
|
aarch64_set_vec_u16
|
|
(cpu, vd, 1 * 2 + 1,
|
|
aarch64_get_vec_u16 (cpu, second ? vn : vm, i * 2 + 1));
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
aarch64_set_vec_u32
|
|
(cpu, vd, 0, aarch64_get_vec_u32 (cpu, second ? vm : vn, 0));
|
|
aarch64_set_vec_u32
|
|
(cpu, vd, 1, aarch64_get_vec_u32 (cpu, second ? vn : vm, 1));
|
|
aarch64_set_vec_u32
|
|
(cpu, vd, 2, aarch64_get_vec_u32 (cpu, second ? vm : vn, 2));
|
|
aarch64_set_vec_u32
|
|
(cpu, vd, 3, aarch64_get_vec_u32 (cpu, second ? vn : vm, 3));
|
|
break;
|
|
|
|
case 3:
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
|
|
aarch64_set_vec_u64 (cpu, vd, 0,
|
|
aarch64_get_vec_u64 (cpu, second ? vm : vn, 0));
|
|
aarch64_set_vec_u64 (cpu, vd, 1,
|
|
aarch64_get_vec_u64 (cpu, second ? vn : vm, 1));
|
|
break;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_DUP_scalar_into_vector (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = 0=> zero top 64-bits, 1=> duplicate into top 64-bits
|
|
[must be 1 for 64-bit xfer]
|
|
instr[29,20] = 00 1110 0000
|
|
instr[19,16] = element size: 0001=> 8-bits, 0010=> 16-bits,
|
|
0100=> 32-bits. 1000=>64-bits
|
|
instr[15,10] = 0000 11
|
|
instr[9,5] = W source
|
|
instr[4,0] = V dest. */
|
|
|
|
unsigned i;
|
|
unsigned Vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned Rs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
int both = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 20, 0x0E0);
|
|
NYI_assert (15, 10, 0x03);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 19, 16))
|
|
{
|
|
case 1:
|
|
for (i = 0; i < (both ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, Vd, i, aarch64_get_reg_u8 (cpu, Rs, NO_SP));
|
|
break;
|
|
|
|
case 2:
|
|
for (i = 0; i < (both ? 8 : 4); i++)
|
|
aarch64_set_vec_u16 (cpu, Vd, i, aarch64_get_reg_u16 (cpu, Rs, NO_SP));
|
|
break;
|
|
|
|
case 4:
|
|
for (i = 0; i < (both ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, Vd, i, aarch64_get_reg_u32 (cpu, Rs, NO_SP));
|
|
break;
|
|
|
|
case 8:
|
|
if (!both)
|
|
HALT_NYI;
|
|
aarch64_set_vec_u64 (cpu, Vd, 0, aarch64_get_reg_u64 (cpu, Rs, NO_SP));
|
|
aarch64_set_vec_u64 (cpu, Vd, 1, aarch64_get_reg_u64 (cpu, Rs, NO_SP));
|
|
break;
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_UZP (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29,24] = 00 1110
|
|
instr[23,22] = size: byte(00), half(01), word (10), long (11)
|
|
instr[21] = 0
|
|
instr[20,16] = Vm
|
|
instr[15] = 0
|
|
instr[14] = lower (0) / upper (1)
|
|
instr[13,10] = 0110
|
|
instr[9,5] = Vn
|
|
instr[4,0] = Vd. */
|
|
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
int upper = uimm (aarch64_get_instr (cpu), 14, 14);
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t val_m1 = aarch64_get_vec_u64 (cpu, vm, 0);
|
|
uint64_t val_m2 = aarch64_get_vec_u64 (cpu, vm, 1);
|
|
uint64_t val_n1 = aarch64_get_vec_u64 (cpu, vn, 0);
|
|
uint64_t val_n2 = aarch64_get_vec_u64 (cpu, vn, 1);
|
|
|
|
uint64_t val1 = 0;
|
|
uint64_t val2 = 0;
|
|
|
|
uint64_t input1 = upper ? val_n1 : val_m1;
|
|
uint64_t input2 = upper ? val_n2 : val_m2;
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 24, 0x0E);
|
|
NYI_assert (21, 21, 0);
|
|
NYI_assert (15, 15, 0);
|
|
NYI_assert (13, 10, 6);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 23))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
val1 |= (input1 >> (i * 8)) & (0xFFULL << (i * 8));
|
|
val2 |= (input2 >> (i * 8)) & (0xFFULL << (i * 8));
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
val1 |= (input1 >> (i * 16)) & (0xFFFFULL << (i * 16));
|
|
val2 |= (input2 >> (i * 16)) & (0xFFFFULL << (i * 16));
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
val1 = ((input1 & 0xFFFFFFFF) | ((input1 >> 32) & 0xFFFFFFFF00000000ULL));
|
|
val2 = ((input2 & 0xFFFFFFFF) | ((input2 >> 32) & 0xFFFFFFFF00000000ULL));
|
|
|
|
case 3:
|
|
val1 = input1;
|
|
val2 = input2;
|
|
break;
|
|
}
|
|
|
|
aarch64_set_vec_u64 (cpu, vd, 0, val1);
|
|
if (full)
|
|
aarch64_set_vec_u64 (cpu, vd, 1, val2);
|
|
}
|
|
|
|
static void
|
|
do_vec_ZIP (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29,24] = 00 1110
|
|
instr[23,22] = size: byte(00), hald(01), word (10), long (11)
|
|
instr[21] = 0
|
|
instr[20,16] = Vm
|
|
instr[15] = 0
|
|
instr[14] = lower (0) / upper (1)
|
|
instr[13,10] = 1110
|
|
instr[9,5] = Vn
|
|
instr[4,0] = Vd. */
|
|
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
int upper = uimm (aarch64_get_instr (cpu), 14, 14);
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t val_m1 = aarch64_get_vec_u64 (cpu, vm, 0);
|
|
uint64_t val_m2 = aarch64_get_vec_u64 (cpu, vm, 1);
|
|
uint64_t val_n1 = aarch64_get_vec_u64 (cpu, vn, 0);
|
|
uint64_t val_n2 = aarch64_get_vec_u64 (cpu, vn, 1);
|
|
|
|
uint64_t val1 = 0;
|
|
uint64_t val2 = 0;
|
|
|
|
uint64_t input1 = upper ? val_n1 : val_m1;
|
|
uint64_t input2 = upper ? val_n2 : val_m2;
|
|
|
|
NYI_assert (29, 24, 0x0E);
|
|
NYI_assert (21, 21, 0);
|
|
NYI_assert (15, 15, 0);
|
|
NYI_assert (13, 10, 0xE);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 23))
|
|
{
|
|
case 0:
|
|
val1 =
|
|
((input1 << 0) & (0xFF << 0))
|
|
| ((input2 << 8) & (0xFF << 8))
|
|
| ((input1 << 8) & (0xFF << 16))
|
|
| ((input2 << 16) & (0xFF << 24))
|
|
| ((input1 << 16) & (0xFFULL << 32))
|
|
| ((input2 << 24) & (0xFFULL << 40))
|
|
| ((input1 << 24) & (0xFFULL << 48))
|
|
| ((input2 << 32) & (0xFFULL << 56));
|
|
|
|
val2 =
|
|
((input1 >> 32) & (0xFF << 0))
|
|
| ((input2 >> 24) & (0xFF << 8))
|
|
| ((input1 >> 24) & (0xFF << 16))
|
|
| ((input2 >> 16) & (0xFF << 24))
|
|
| ((input1 >> 16) & (0xFFULL << 32))
|
|
| ((input2 >> 8) & (0xFFULL << 40))
|
|
| ((input1 >> 8) & (0xFFULL << 48))
|
|
| ((input2 >> 0) & (0xFFULL << 56));
|
|
break;
|
|
|
|
case 1:
|
|
val1 =
|
|
((input1 << 0) & (0xFFFF << 0))
|
|
| ((input2 << 16) & (0xFFFF << 16))
|
|
| ((input1 << 16) & (0xFFFFULL << 32))
|
|
| ((input2 << 32) & (0xFFFFULL << 48));
|
|
|
|
val2 =
|
|
((input1 >> 32) & (0xFFFF << 0))
|
|
| ((input2 >> 16) & (0xFFFF << 16))
|
|
| ((input1 >> 16) & (0xFFFFULL << 32))
|
|
| ((input2 >> 0) & (0xFFFFULL << 48));
|
|
break;
|
|
|
|
case 2:
|
|
val1 = (input1 & 0xFFFFFFFFULL) | (input2 << 32);
|
|
val2 = (input2 & 0xFFFFFFFFULL) | (input1 << 32);
|
|
break;
|
|
|
|
case 3:
|
|
val1 = input1;
|
|
val2 = input2;
|
|
break;
|
|
}
|
|
|
|
aarch64_set_vec_u64 (cpu, vd, 0, val1);
|
|
if (full)
|
|
aarch64_set_vec_u64 (cpu, vd, 1, val2);
|
|
}
|
|
|
|
/* Floating point immediates are encoded in 8 bits.
|
|
fpimm[7] = sign bit.
|
|
fpimm[6:4] = signed exponent.
|
|
fpimm[3:0] = fraction (assuming leading 1).
|
|
i.e. F = s * 1.f * 2^(e - b). */
|
|
|
|
static float
|
|
fp_immediate_for_encoding_32 (uint32_t imm8)
|
|
{
|
|
float u;
|
|
uint32_t s, e, f, i;
|
|
|
|
s = (imm8 >> 7) & 0x1;
|
|
e = (imm8 >> 4) & 0x7;
|
|
f = imm8 & 0xf;
|
|
|
|
/* The fp value is s * n/16 * 2r where n is 16+e. */
|
|
u = (16.0 + f) / 16.0;
|
|
|
|
/* N.B. exponent is signed. */
|
|
if (e < 4)
|
|
{
|
|
int epos = e;
|
|
|
|
for (i = 0; i <= epos; i++)
|
|
u *= 2.0;
|
|
}
|
|
else
|
|
{
|
|
int eneg = 7 - e;
|
|
|
|
for (i = 0; i < eneg; i++)
|
|
u /= 2.0;
|
|
}
|
|
|
|
if (s)
|
|
u = - u;
|
|
|
|
return u;
|
|
}
|
|
|
|
static double
|
|
fp_immediate_for_encoding_64 (uint32_t imm8)
|
|
{
|
|
double u;
|
|
uint32_t s, e, f, i;
|
|
|
|
s = (imm8 >> 7) & 0x1;
|
|
e = (imm8 >> 4) & 0x7;
|
|
f = imm8 & 0xf;
|
|
|
|
/* The fp value is s * n/16 * 2r where n is 16+e. */
|
|
u = (16.0 + f) / 16.0;
|
|
|
|
/* N.B. exponent is signed. */
|
|
if (e < 4)
|
|
{
|
|
int epos = e;
|
|
|
|
for (i = 0; i <= epos; i++)
|
|
u *= 2.0;
|
|
}
|
|
else
|
|
{
|
|
int eneg = 7 - e;
|
|
|
|
for (i = 0; i < eneg; i++)
|
|
u /= 2.0;
|
|
}
|
|
|
|
if (s)
|
|
u = - u;
|
|
|
|
return u;
|
|
}
|
|
|
|
static void
|
|
do_vec_MOV_immediate (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = full/half selector
|
|
instr[29,19] = 00111100000
|
|
instr[18,16] = high 3 bits of uimm8
|
|
instr[15,12] = size & shift:
|
|
0000 => 32-bit
|
|
0010 => 32-bit + LSL#8
|
|
0100 => 32-bit + LSL#16
|
|
0110 => 32-bit + LSL#24
|
|
1010 => 16-bit + LSL#8
|
|
1000 => 16-bit
|
|
1101 => 32-bit + MSL#16
|
|
1100 => 32-bit + MSL#8
|
|
1110 => 8-bit
|
|
1111 => double
|
|
instr[11,10] = 01
|
|
instr[9,5] = low 5-bits of uimm8
|
|
instr[4,0] = Vd. */
|
|
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned val = uimm (aarch64_get_instr (cpu), 18, 16) << 5
|
|
| uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 19, 0x1E0);
|
|
NYI_assert (11, 10, 1);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 15, 12))
|
|
{
|
|
case 0x0: /* 32-bit, no shift. */
|
|
case 0x2: /* 32-bit, shift by 8. */
|
|
case 0x4: /* 32-bit, shift by 16. */
|
|
case 0x6: /* 32-bit, shift by 24. */
|
|
val <<= (8 * uimm (aarch64_get_instr (cpu), 14, 13));
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i, val);
|
|
break;
|
|
|
|
case 0xa: /* 16-bit, shift by 8. */
|
|
val <<= 8;
|
|
/* Fall through. */
|
|
case 0x8: /* 16-bit, no shift. */
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i, val);
|
|
/* Fall through. */
|
|
case 0xd: /* 32-bit, mask shift by 16. */
|
|
val <<= 8;
|
|
val |= 0xFF;
|
|
/* Fall through. */
|
|
case 0xc: /* 32-bit, mask shift by 8. */
|
|
val <<= 8;
|
|
val |= 0xFF;
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i, val);
|
|
break;
|
|
|
|
case 0xe: /* 8-bit, no shift. */
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i, val);
|
|
break;
|
|
|
|
case 0xf: /* FMOV Vs.{2|4}S, #fpimm. */
|
|
{
|
|
float u = fp_immediate_for_encoding_32 (val);
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_float (cpu, vd, i, u);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_MVNI (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = full/half selector
|
|
instr[29,19] = 10111100000
|
|
instr[18,16] = high 3 bits of uimm8
|
|
instr[15,12] = selector
|
|
instr[11,10] = 01
|
|
instr[9,5] = low 5-bits of uimm8
|
|
instr[4,0] = Vd. */
|
|
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned val = uimm (aarch64_get_instr (cpu), 18, 16) << 5
|
|
| uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 19, 0x5E0);
|
|
NYI_assert (11, 10, 1);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 15, 12))
|
|
{
|
|
case 0x0: /* 32-bit, no shift. */
|
|
case 0x2: /* 32-bit, shift by 8. */
|
|
case 0x4: /* 32-bit, shift by 16. */
|
|
case 0x6: /* 32-bit, shift by 24. */
|
|
val <<= (8 * uimm (aarch64_get_instr (cpu), 14, 13));
|
|
val = ~ val;
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i, val);
|
|
return;
|
|
|
|
case 0xa: /* 16-bit, 8 bit shift. */
|
|
val <<= 8;
|
|
case 0x8: /* 16-bit, no shift. */
|
|
val = ~ val;
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i, val);
|
|
return;
|
|
|
|
case 0xd: /* 32-bit, mask shift by 16. */
|
|
val <<= 8;
|
|
val |= 0xFF;
|
|
case 0xc: /* 32-bit, mask shift by 8. */
|
|
val <<= 8;
|
|
val |= 0xFF;
|
|
val = ~ val;
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i, val);
|
|
return;
|
|
|
|
case 0xE: /* MOVI Dn, #mask64 */
|
|
{
|
|
uint64_t mask = 0;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
if (val & (1 << i))
|
|
mask |= (0xF << (i * 4));
|
|
aarch64_set_vec_u64 (cpu, vd, 0, mask);
|
|
aarch64_set_vec_u64 (cpu, vd, 1, 0);
|
|
return;
|
|
}
|
|
|
|
case 0xf: /* FMOV Vd.2D, #fpimm. */
|
|
{
|
|
double u = fp_immediate_for_encoding_64 (val);
|
|
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
|
|
aarch64_set_vec_double (cpu, vd, 0, u);
|
|
aarch64_set_vec_double (cpu, vd, 1, u);
|
|
return;
|
|
}
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
#define ABS(A) ((A) < 0 ? - (A) : (A))
|
|
|
|
static void
|
|
do_vec_ABS (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29,24] = 00 1110
|
|
instr[23,22] = size: 00=> 8-bit, 01=> 16-bit, 10=> 32-bit, 11=> 64-bit
|
|
instr[21,10] = 10 0000 1011 10
|
|
instr[9,5] = Vn
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 24, 0x0E);
|
|
NYI_assert (21, 10, 0x82E);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_s8 (cpu, vd, i,
|
|
ABS (aarch64_get_vec_s8 (cpu, vn, i)));
|
|
break;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_s16 (cpu, vd, i,
|
|
ABS (aarch64_get_vec_s16 (cpu, vn, i)));
|
|
break;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_s32 (cpu, vd, i,
|
|
ABS (aarch64_get_vec_s32 (cpu, vn, i)));
|
|
break;
|
|
|
|
case 3:
|
|
if (! full)
|
|
HALT_NYI;
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_s64 (cpu, vd, i,
|
|
ABS (aarch64_get_vec_s64 (cpu, vn, i)));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_ADDV (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = full/half selector
|
|
instr[29,24] = 00 1110
|
|
instr[23,22] = size: 00=> 8-bit, 01=> 16-bit, 10=> 32-bit, 11=> 64-bit
|
|
instr[21,10] = 11 0001 1011 10
|
|
instr[9,5] = Vm
|
|
instr[4.0] = Rd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
uint64_t val = 0;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 24, 0x0E);
|
|
NYI_assert (21, 10, 0xC6E);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
val += aarch64_get_vec_u8 (cpu, vm, i);
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, val);
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
val += aarch64_get_vec_u16 (cpu, vm, i);
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, val);
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
val += aarch64_get_vec_u32 (cpu, vm, i);
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, val);
|
|
return;
|
|
|
|
case 3:
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
val = aarch64_get_vec_u64 (cpu, vm, 0);
|
|
val += aarch64_get_vec_u64 (cpu, vm, 1);
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, val);
|
|
return;
|
|
|
|
default:
|
|
HALT_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_ins_2 (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,21] = 01001110000
|
|
instr[20,18] = size & element selector
|
|
instr[17,14] = 0000
|
|
instr[13] = direction: to vec(0), from vec (1)
|
|
instr[12,10] = 111
|
|
instr[9,5] = Vm
|
|
instr[4,0] = Vd. */
|
|
|
|
unsigned elem;
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (31, 21, 0x270);
|
|
NYI_assert (17, 14, 0);
|
|
NYI_assert (12, 10, 7);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 13, 13) == 1)
|
|
{
|
|
if (uimm (aarch64_get_instr (cpu), 18, 18) == 1)
|
|
{
|
|
/* 32-bit moves. */
|
|
elem = uimm (aarch64_get_instr (cpu), 20, 19);
|
|
aarch64_set_reg_u64 (cpu, vd, NO_SP,
|
|
aarch64_get_vec_u32 (cpu, vm, elem));
|
|
}
|
|
else
|
|
{
|
|
/* 64-bit moves. */
|
|
if (uimm (aarch64_get_instr (cpu), 19, 19) != 1)
|
|
HALT_NYI;
|
|
|
|
elem = uimm (aarch64_get_instr (cpu), 20, 20);
|
|
aarch64_set_reg_u64 (cpu, vd, NO_SP,
|
|
aarch64_get_vec_u64 (cpu, vm, elem));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (uimm (aarch64_get_instr (cpu), 18, 18) == 1)
|
|
{
|
|
/* 32-bit moves. */
|
|
elem = uimm (aarch64_get_instr (cpu), 20, 19);
|
|
aarch64_set_vec_u32 (cpu, vd, elem,
|
|
aarch64_get_reg_u32 (cpu, vm, NO_SP));
|
|
}
|
|
else
|
|
{
|
|
/* 64-bit moves. */
|
|
if (uimm (aarch64_get_instr (cpu), 19, 19) != 1)
|
|
HALT_NYI;
|
|
|
|
elem = uimm (aarch64_get_instr (cpu), 20, 20);
|
|
aarch64_set_vec_u64 (cpu, vd, elem,
|
|
aarch64_get_reg_u64 (cpu, vm, NO_SP));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_mull (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = lower(0)/upper(1) selector
|
|
instr[29] = signed(0)/unsigned(1)
|
|
instr[28,24] = 0 1110
|
|
instr[23,22] = size: 8-bit (00), 16-bit (01), 32-bit (10)
|
|
instr[21] = 1
|
|
instr[20,16] = Vm
|
|
instr[15,10] = 11 0000
|
|
instr[9,5] = Vn
|
|
instr[4.0] = Vd. */
|
|
|
|
int unsign = uimm (aarch64_get_instr (cpu), 29, 29);
|
|
int bias = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (28, 24, 0x0E);
|
|
NYI_assert (15, 10, 0x30);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
if (bias)
|
|
bias = 8;
|
|
if (unsign)
|
|
for (i = 0; i < 8; i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i,
|
|
aarch64_get_vec_u8 (cpu, vn, i + bias)
|
|
* aarch64_get_vec_u8 (cpu, vm, i + bias));
|
|
else
|
|
for (i = 0; i < 8; i++)
|
|
aarch64_set_vec_s16 (cpu, vd, i,
|
|
aarch64_get_vec_s8 (cpu, vn, i + bias)
|
|
* aarch64_get_vec_s8 (cpu, vm, i + bias));
|
|
return;
|
|
|
|
case 1:
|
|
if (bias)
|
|
bias = 4;
|
|
if (unsign)
|
|
for (i = 0; i < 4; i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i,
|
|
aarch64_get_vec_u16 (cpu, vn, i + bias)
|
|
* aarch64_get_vec_u16 (cpu, vm, i + bias));
|
|
else
|
|
for (i = 0; i < 4; i++)
|
|
aarch64_set_vec_s32 (cpu, vd, i,
|
|
aarch64_get_vec_s16 (cpu, vn, i + bias)
|
|
* aarch64_get_vec_s16 (cpu, vm, i + bias));
|
|
return;
|
|
|
|
case 2:
|
|
if (bias)
|
|
bias = 2;
|
|
if (unsign)
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_u64 (cpu, vd, i,
|
|
(uint64_t) aarch64_get_vec_u32 (cpu, vn,
|
|
i + bias)
|
|
* (uint64_t) aarch64_get_vec_u32 (cpu, vm,
|
|
i + bias));
|
|
else
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_s64 (cpu, vd, i,
|
|
aarch64_get_vec_s32 (cpu, vn, i + bias)
|
|
* aarch64_get_vec_s32 (cpu, vm, i + bias));
|
|
return;
|
|
|
|
case 3:
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_fadd (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29,24] = 001110
|
|
instr[23] = FADD(0)/FSUB(1)
|
|
instr[22] = float (0)/double(1)
|
|
instr[21] = 1
|
|
instr[20,16] = Vm
|
|
instr[15,10] = 110101
|
|
instr[9,5] = Vn
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 24, 0x0E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x35);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 23, 23))
|
|
{
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
if (! full)
|
|
HALT_NYI;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_double (cpu, vd, i,
|
|
aarch64_get_vec_double (cpu, vn, i)
|
|
- aarch64_get_vec_double (cpu, vm, i));
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_float (cpu, vd, i,
|
|
aarch64_get_vec_float (cpu, vn, i)
|
|
- aarch64_get_vec_float (cpu, vm, i));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
if (! full)
|
|
HALT_NYI;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_double (cpu, vd, i,
|
|
aarch64_get_vec_double (cpu, vm, i)
|
|
+ aarch64_get_vec_double (cpu, vn, i));
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_float (cpu, vd, i,
|
|
aarch64_get_vec_float (cpu, vm, i)
|
|
+ aarch64_get_vec_float (cpu, vn, i));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_add (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = full/half selector
|
|
instr[29,24] = 001110
|
|
instr[23,22] = size: 00=> 8-bit, 01=> 16-bit, 10=> 32-bit, 11=> 64-bit
|
|
instr[21] = 1
|
|
instr[20,16] = Vn
|
|
instr[15,10] = 100001
|
|
instr[9,5] = Vm
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 24, 0x0E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x21);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i, aarch64_get_vec_u8 (cpu, vn, i)
|
|
+ aarch64_get_vec_u8 (cpu, vm, i));
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i, aarch64_get_vec_u16 (cpu, vn, i)
|
|
+ aarch64_get_vec_u16 (cpu, vm, i));
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i, aarch64_get_vec_u32 (cpu, vn, i)
|
|
+ aarch64_get_vec_u32 (cpu, vm, i));
|
|
return;
|
|
|
|
case 3:
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
aarch64_set_vec_u64 (cpu, vd, 0, aarch64_get_vec_u64 (cpu, vn, 0)
|
|
+ aarch64_get_vec_u64 (cpu, vm, 0));
|
|
aarch64_set_vec_u64 (cpu, vd, 1,
|
|
aarch64_get_vec_u64 (cpu, vn, 1)
|
|
+ aarch64_get_vec_u64 (cpu, vm, 1));
|
|
return;
|
|
|
|
default:
|
|
HALT_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_mul (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = full/half selector
|
|
instr[29,24] = 00 1110
|
|
instr[23,22] = size: 00=> 8-bit, 01=> 16-bit, 10=> 32-bit
|
|
instr[21] = 1
|
|
instr[20,16] = Vn
|
|
instr[15,10] = 10 0111
|
|
instr[9,5] = Vm
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 24, 0x0E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x27);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
{
|
|
uint16_t val = aarch64_get_vec_u8 (cpu, vn, i);
|
|
val *= aarch64_get_vec_u8 (cpu, vm, i);
|
|
|
|
aarch64_set_vec_u16 (cpu, vd, i, val);
|
|
}
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
{
|
|
uint32_t val = aarch64_get_vec_u16 (cpu, vn, i);
|
|
val *= aarch64_get_vec_u16 (cpu, vm, i);
|
|
|
|
aarch64_set_vec_u32 (cpu, vd, i, val);
|
|
}
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
{
|
|
uint64_t val = aarch64_get_vec_u32 (cpu, vn, i);
|
|
val *= aarch64_get_vec_u32 (cpu, vm, i);
|
|
|
|
aarch64_set_vec_u64 (cpu, vd, i, val);
|
|
}
|
|
return;
|
|
|
|
default:
|
|
case 3:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_MLA (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = full/half selector
|
|
instr[29,24] = 00 1110
|
|
instr[23,22] = size: 00=> 8-bit, 01=> 16-bit, 10=> 32-bit
|
|
instr[21] = 1
|
|
instr[20,16] = Vn
|
|
instr[15,10] = 1001 01
|
|
instr[9,5] = Vm
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 24, 0x0E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x25);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
{
|
|
uint16_t val = aarch64_get_vec_u8 (cpu, vn, i);
|
|
val *= aarch64_get_vec_u8 (cpu, vm, i);
|
|
val += aarch64_get_vec_u8 (cpu, vd, i);
|
|
|
|
aarch64_set_vec_u16 (cpu, vd, i, val);
|
|
}
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
{
|
|
uint32_t val = aarch64_get_vec_u16 (cpu, vn, i);
|
|
val *= aarch64_get_vec_u16 (cpu, vm, i);
|
|
val += aarch64_get_vec_u16 (cpu, vd, i);
|
|
|
|
aarch64_set_vec_u32 (cpu, vd, i, val);
|
|
}
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
{
|
|
uint64_t val = aarch64_get_vec_u32 (cpu, vn, i);
|
|
val *= aarch64_get_vec_u32 (cpu, vm, i);
|
|
val += aarch64_get_vec_u32 (cpu, vd, i);
|
|
|
|
aarch64_set_vec_u64 (cpu, vd, i, val);
|
|
}
|
|
return;
|
|
|
|
default:
|
|
case 3:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static float
|
|
fmaxnm (float a, float b)
|
|
{
|
|
if (fpclassify (a) == FP_NORMAL)
|
|
{
|
|
if (fpclassify (b) == FP_NORMAL)
|
|
return a > b ? a : b;
|
|
return a;
|
|
}
|
|
else if (fpclassify (b) == FP_NORMAL)
|
|
return b;
|
|
return a;
|
|
}
|
|
|
|
static float
|
|
fminnm (float a, float b)
|
|
{
|
|
if (fpclassify (a) == FP_NORMAL)
|
|
{
|
|
if (fpclassify (b) == FP_NORMAL)
|
|
return a < b ? a : b;
|
|
return a;
|
|
}
|
|
else if (fpclassify (b) == FP_NORMAL)
|
|
return b;
|
|
return a;
|
|
}
|
|
|
|
static double
|
|
dmaxnm (double a, double b)
|
|
{
|
|
if (fpclassify (a) == FP_NORMAL)
|
|
{
|
|
if (fpclassify (b) == FP_NORMAL)
|
|
return a > b ? a : b;
|
|
return a;
|
|
}
|
|
else if (fpclassify (b) == FP_NORMAL)
|
|
return b;
|
|
return a;
|
|
}
|
|
|
|
static double
|
|
dminnm (double a, double b)
|
|
{
|
|
if (fpclassify (a) == FP_NORMAL)
|
|
{
|
|
if (fpclassify (b) == FP_NORMAL)
|
|
return a < b ? a : b;
|
|
return a;
|
|
}
|
|
else if (fpclassify (b) == FP_NORMAL)
|
|
return b;
|
|
return a;
|
|
}
|
|
|
|
static void
|
|
do_vec_FminmaxNMP (sim_cpu *cpu)
|
|
{
|
|
/* aarch64_get_instr (cpu)[31] = 0
|
|
aarch64_get_instr (cpu)[30] = half (0)/full (1)
|
|
aarch64_get_instr (cpu)[29,24] = 10 1110
|
|
aarch64_get_instr (cpu)[23] = max(0)/min(1)
|
|
aarch64_get_instr (cpu)[22] = float (0)/double (1)
|
|
aarch64_get_instr (cpu)[21] = 1
|
|
aarch64_get_instr (cpu)[20,16] = Vn
|
|
aarch64_get_instr (cpu)[15,10] = 1100 01
|
|
aarch64_get_instr (cpu)[9,5] = Vm
|
|
aarch64_get_instr (cpu)[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 24, 0x2E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x31);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
double (* fn)(double, double) = uimm (aarch64_get_instr (cpu), 23, 23)
|
|
? dminnm : dmaxnm;
|
|
|
|
if (! full)
|
|
HALT_NYI;
|
|
aarch64_set_vec_double (cpu, vd, 0,
|
|
fn (aarch64_get_vec_double (cpu, vn, 0),
|
|
aarch64_get_vec_double (cpu, vn, 1)));
|
|
aarch64_set_vec_double (cpu, vd, 0,
|
|
fn (aarch64_get_vec_double (cpu, vm, 0),
|
|
aarch64_get_vec_double (cpu, vm, 1)));
|
|
}
|
|
else
|
|
{
|
|
float (* fn)(float, float) = uimm (aarch64_get_instr (cpu), 23, 23)
|
|
? fminnm : fmaxnm;
|
|
|
|
aarch64_set_vec_float (cpu, vd, 0,
|
|
fn (aarch64_get_vec_float (cpu, vn, 0),
|
|
aarch64_get_vec_float (cpu, vn, 1)));
|
|
if (full)
|
|
aarch64_set_vec_float (cpu, vd, 1,
|
|
fn (aarch64_get_vec_float (cpu, vn, 2),
|
|
aarch64_get_vec_float (cpu, vn, 3)));
|
|
|
|
aarch64_set_vec_float (cpu, vd, (full ? 2 : 1),
|
|
fn (aarch64_get_vec_float (cpu, vm, 0),
|
|
aarch64_get_vec_float (cpu, vm, 1)));
|
|
if (full)
|
|
aarch64_set_vec_float (cpu, vd, 3,
|
|
fn (aarch64_get_vec_float (cpu, vm, 2),
|
|
aarch64_get_vec_float (cpu, vm, 3)));
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_AND (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half (0)/full (1)
|
|
instr[29,21] = 001110001
|
|
instr[20,16] = Vm
|
|
instr[15,10] = 000111
|
|
instr[9,5] = Vn
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 21, 0x071);
|
|
NYI_assert (15, 10, 0x07);
|
|
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i,
|
|
aarch64_get_vec_u32 (cpu, vn, i)
|
|
& aarch64_get_vec_u32 (cpu, vm, i));
|
|
}
|
|
|
|
static void
|
|
do_vec_BSL (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half (0)/full (1)
|
|
instr[29,21] = 101110011
|
|
instr[20,16] = Vm
|
|
instr[15,10] = 000111
|
|
instr[9,5] = Vn
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 21, 0x173);
|
|
NYI_assert (15, 10, 0x07);
|
|
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i,
|
|
( aarch64_get_vec_u8 (cpu, vd, i)
|
|
& aarch64_get_vec_u8 (cpu, vn, i))
|
|
| ((~ aarch64_get_vec_u8 (cpu, vd, i))
|
|
& aarch64_get_vec_u8 (cpu, vm, i)));
|
|
}
|
|
|
|
static void
|
|
do_vec_EOR (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half (0)/full (1)
|
|
instr[29,21] = 10 1110 001
|
|
instr[20,16] = Vm
|
|
instr[15,10] = 000111
|
|
instr[9,5] = Vn
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 21, 0x171);
|
|
NYI_assert (15, 10, 0x07);
|
|
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i,
|
|
aarch64_get_vec_u32 (cpu, vn, i)
|
|
^ aarch64_get_vec_u32 (cpu, vm, i));
|
|
}
|
|
|
|
static void
|
|
do_vec_bit (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half (0)/full (1)
|
|
instr[29,23] = 10 1110 1
|
|
instr[22] = BIT (0) / BIF (1)
|
|
instr[21] = 1
|
|
instr[20,16] = Vm
|
|
instr[15,10] = 0001 11
|
|
instr[9,5] = Vn
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned test_false = uimm (aarch64_get_instr (cpu), 22, 22);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 23, 0x5D);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x07);
|
|
|
|
if (test_false)
|
|
{
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
if (aarch64_get_vec_u32 (cpu, vn, i) == 0)
|
|
aarch64_set_vec_u32 (cpu, vd, i, aarch64_get_vec_u32 (cpu, vm, i));
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
if (aarch64_get_vec_u32 (cpu, vn, i) != 0)
|
|
aarch64_set_vec_u32 (cpu, vd, i, aarch64_get_vec_u32 (cpu, vm, i));
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_ORN (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half (0)/full (1)
|
|
instr[29,21] = 00 1110 111
|
|
instr[20,16] = Vm
|
|
instr[15,10] = 00 0111
|
|
instr[9,5] = Vn
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 21, 0x077);
|
|
NYI_assert (15, 10, 0x07);
|
|
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i,
|
|
aarch64_get_vec_u8 (cpu, vn, i)
|
|
| ~ aarch64_get_vec_u8 (cpu, vm, i));
|
|
}
|
|
|
|
static void
|
|
do_vec_ORR (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half (0)/full (1)
|
|
instr[29,21] = 00 1110 101
|
|
instr[20,16] = Vm
|
|
instr[15,10] = 0001 11
|
|
instr[9,5] = Vn
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 21, 0x075);
|
|
NYI_assert (15, 10, 0x07);
|
|
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i,
|
|
aarch64_get_vec_u8 (cpu, vn, i)
|
|
| aarch64_get_vec_u8 (cpu, vm, i));
|
|
}
|
|
|
|
static void
|
|
do_vec_BIC (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half (0)/full (1)
|
|
instr[29,21] = 00 1110 011
|
|
instr[20,16] = Vm
|
|
instr[15,10] = 00 0111
|
|
instr[9,5] = Vn
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 21, 0x073);
|
|
NYI_assert (15, 10, 0x07);
|
|
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i,
|
|
aarch64_get_vec_u8 (cpu, vn, i)
|
|
& ~ aarch64_get_vec_u8 (cpu, vm, i));
|
|
}
|
|
|
|
static void
|
|
do_vec_XTN (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = first part (0)/ second part (1)
|
|
instr[29,24] = 00 1110
|
|
instr[23,22] = size: byte(00), half(01), word (10)
|
|
instr[21,10] = 1000 0100 1010
|
|
instr[9,5] = Vs
|
|
instr[4,0] = Vd. */
|
|
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned bias = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 24, 0x0E);
|
|
NYI_assert (21, 10, 0x84A);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
if (bias)
|
|
for (i = 0; i < 8; i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i + 8,
|
|
aarch64_get_vec_u16 (cpu, vs, i) >> 8);
|
|
else
|
|
for (i = 0; i < 8; i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i, aarch64_get_vec_u16 (cpu, vs, i));
|
|
return;
|
|
|
|
case 1:
|
|
if (bias)
|
|
for (i = 0; i < 4; i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i + 4,
|
|
aarch64_get_vec_u32 (cpu, vs, i) >> 16);
|
|
else
|
|
for (i = 0; i < 4; i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i, aarch64_get_vec_u32 (cpu, vs, i));
|
|
return;
|
|
|
|
case 2:
|
|
if (bias)
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i + 4,
|
|
aarch64_get_vec_u64 (cpu, vs, i) >> 32);
|
|
else
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i, aarch64_get_vec_u64 (cpu, vs, i));
|
|
return;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_maxv (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29] = signed (0)/unsigned(1)
|
|
instr[28,24] = 0 1110
|
|
instr[23,22] = size: byte(00), half(01), word (10)
|
|
instr[21] = 1
|
|
instr[20,17] = 1 000
|
|
instr[16] = max(0)/min(1)
|
|
instr[15,10] = 1010 10
|
|
instr[9,5] = V source
|
|
instr[4.0] = R dest. */
|
|
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned i;
|
|
|
|
NYI_assert (28, 24, 0x0E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (20, 17, 8);
|
|
NYI_assert (15, 10, 0x2A);
|
|
|
|
switch ((uimm (aarch64_get_instr (cpu), 29, 29) << 1)
|
|
| uimm (aarch64_get_instr (cpu), 16, 16))
|
|
{
|
|
case 0: /* SMAXV. */
|
|
{
|
|
int64_t smax;
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
smax = aarch64_get_vec_s8 (cpu, vs, 0);
|
|
for (i = 1; i < (full ? 16 : 8); i++)
|
|
smax = max (smax, aarch64_get_vec_s8 (cpu, vs, i));
|
|
break;
|
|
case 1:
|
|
smax = aarch64_get_vec_s16 (cpu, vs, 0);
|
|
for (i = 1; i < (full ? 8 : 4); i++)
|
|
smax = max (smax, aarch64_get_vec_s16 (cpu, vs, i));
|
|
break;
|
|
case 2:
|
|
smax = aarch64_get_vec_s32 (cpu, vs, 0);
|
|
for (i = 1; i < (full ? 4 : 2); i++)
|
|
smax = max (smax, aarch64_get_vec_s32 (cpu, vs, i));
|
|
break;
|
|
default:
|
|
case 3:
|
|
HALT_UNALLOC;
|
|
}
|
|
aarch64_set_reg_s64 (cpu, rd, NO_SP, smax);
|
|
return;
|
|
}
|
|
|
|
case 1: /* SMINV. */
|
|
{
|
|
int64_t smin;
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
smin = aarch64_get_vec_s8 (cpu, vs, 0);
|
|
for (i = 1; i < (full ? 16 : 8); i++)
|
|
smin = min (smin, aarch64_get_vec_s8 (cpu, vs, i));
|
|
break;
|
|
case 1:
|
|
smin = aarch64_get_vec_s16 (cpu, vs, 0);
|
|
for (i = 1; i < (full ? 8 : 4); i++)
|
|
smin = min (smin, aarch64_get_vec_s16 (cpu, vs, i));
|
|
break;
|
|
case 2:
|
|
smin = aarch64_get_vec_s32 (cpu, vs, 0);
|
|
for (i = 1; i < (full ? 4 : 2); i++)
|
|
smin = min (smin, aarch64_get_vec_s32 (cpu, vs, i));
|
|
break;
|
|
default:
|
|
case 3:
|
|
HALT_UNALLOC;
|
|
}
|
|
aarch64_set_reg_s64 (cpu, rd, NO_SP, smin);
|
|
return;
|
|
}
|
|
|
|
case 2: /* UMAXV. */
|
|
{
|
|
uint64_t umax;
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
umax = aarch64_get_vec_u8 (cpu, vs, 0);
|
|
for (i = 1; i < (full ? 16 : 8); i++)
|
|
umax = max (umax, aarch64_get_vec_u8 (cpu, vs, i));
|
|
break;
|
|
case 1:
|
|
umax = aarch64_get_vec_u16 (cpu, vs, 0);
|
|
for (i = 1; i < (full ? 8 : 4); i++)
|
|
umax = max (umax, aarch64_get_vec_u16 (cpu, vs, i));
|
|
break;
|
|
case 2:
|
|
umax = aarch64_get_vec_u32 (cpu, vs, 0);
|
|
for (i = 1; i < (full ? 4 : 2); i++)
|
|
umax = max (umax, aarch64_get_vec_u32 (cpu, vs, i));
|
|
break;
|
|
default:
|
|
case 3:
|
|
HALT_UNALLOC;
|
|
}
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, umax);
|
|
return;
|
|
}
|
|
|
|
case 3: /* UMINV. */
|
|
{
|
|
uint64_t umin;
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
umin = aarch64_get_vec_u8 (cpu, vs, 0);
|
|
for (i = 1; i < (full ? 16 : 8); i++)
|
|
umin = min (umin, aarch64_get_vec_u8 (cpu, vs, i));
|
|
break;
|
|
case 1:
|
|
umin = aarch64_get_vec_u16 (cpu, vs, 0);
|
|
for (i = 1; i < (full ? 8 : 4); i++)
|
|
umin = min (umin, aarch64_get_vec_u16 (cpu, vs, i));
|
|
break;
|
|
case 2:
|
|
umin = aarch64_get_vec_u32 (cpu, vs, 0);
|
|
for (i = 1; i < (full ? 4 : 2); i++)
|
|
umin = min (umin, aarch64_get_vec_u32 (cpu, vs, i));
|
|
break;
|
|
default:
|
|
case 3:
|
|
HALT_UNALLOC;
|
|
}
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, umin);
|
|
return;
|
|
}
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_fminmaxV (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,24] = 0110 1110
|
|
instr[23] = max(0)/min(1)
|
|
instr[22,14] = 011 0000 11
|
|
instr[13,12] = nm(00)/normal(11)
|
|
instr[11,10] = 10
|
|
instr[9,5] = V source
|
|
instr[4.0] = R dest. */
|
|
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
float res = aarch64_get_vec_float (cpu, vs, 0);
|
|
|
|
NYI_assert (31, 24, 0x6E);
|
|
NYI_assert (22, 14, 0x0C3);
|
|
NYI_assert (11, 10, 2);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 23, 23))
|
|
{
|
|
switch (uimm (aarch64_get_instr (cpu), 13, 12))
|
|
{
|
|
case 0: /* FMNINNMV. */
|
|
for (i = 1; i < 4; i++)
|
|
res = fminnm (res, aarch64_get_vec_float (cpu, vs, i));
|
|
break;
|
|
|
|
case 3: /* FMINV. */
|
|
for (i = 1; i < 4; i++)
|
|
res = min (res, aarch64_get_vec_float (cpu, vs, i));
|
|
break;
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (uimm (aarch64_get_instr (cpu), 13, 12))
|
|
{
|
|
case 0: /* FMNAXNMV. */
|
|
for (i = 1; i < 4; i++)
|
|
res = fmaxnm (res, aarch64_get_vec_float (cpu, vs, i));
|
|
break;
|
|
|
|
case 3: /* FMAXV. */
|
|
for (i = 1; i < 4; i++)
|
|
res = max (res, aarch64_get_vec_float (cpu, vs, i));
|
|
break;
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
aarch64_set_FP_float (cpu, rd, res);
|
|
}
|
|
|
|
static void
|
|
do_vec_Fminmax (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29,24] = 00 1110
|
|
instr[23] = max(0)/min(1)
|
|
instr[22] = float(0)/double(1)
|
|
instr[21] = 1
|
|
instr[20,16] = Vm
|
|
instr[15,14] = 11
|
|
instr[13,12] = nm(00)/normal(11)
|
|
instr[11,10] = 01
|
|
instr[9,5] = Vn
|
|
instr[4,0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned min = uimm (aarch64_get_instr (cpu), 23, 23);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 24, 0x0E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 14, 3);
|
|
NYI_assert (11, 10, 1);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
double (* func)(double, double);
|
|
|
|
if (! full)
|
|
HALT_NYI;
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 13, 12) == 0)
|
|
func = min ? dminnm : dmaxnm;
|
|
else if (uimm (aarch64_get_instr (cpu), 13, 12) == 3)
|
|
func = min ? fmin : fmax;
|
|
else
|
|
HALT_NYI;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_double (cpu, vd, i,
|
|
func (aarch64_get_vec_double (cpu, vn, i),
|
|
aarch64_get_vec_double (cpu, vm, i)));
|
|
}
|
|
else
|
|
{
|
|
float (* func)(float, float);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 13, 12) == 0)
|
|
func = min ? fminnm : fmaxnm;
|
|
else if (uimm (aarch64_get_instr (cpu), 13, 12) == 3)
|
|
func = min ? fminf : fmaxf;
|
|
else
|
|
HALT_NYI;
|
|
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_float (cpu, vd, i,
|
|
func (aarch64_get_vec_float (cpu, vn, i),
|
|
aarch64_get_vec_float (cpu, vm, i)));
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_SCVTF (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = Q
|
|
instr[29,23] = 00 1110 0
|
|
instr[22] = float(0)/double(1)
|
|
instr[21,10] = 10 0001 1101 10
|
|
instr[9,5] = Vn
|
|
instr[4,0] = Vd. */
|
|
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned size = uimm (aarch64_get_instr (cpu), 22, 22);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 23, 0x1C);
|
|
NYI_assert (21, 10, 0x876);
|
|
|
|
if (size)
|
|
{
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
double val = (double) aarch64_get_vec_u64 (cpu, vn, i);
|
|
aarch64_set_vec_double (cpu, vd, i, val);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
{
|
|
float val = (float) aarch64_get_vec_u32 (cpu, vn, i);
|
|
aarch64_set_vec_float (cpu, vd, i, val);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define VEC_CMP(SOURCE, CMP) \
|
|
do \
|
|
{ \
|
|
switch (size) \
|
|
{ \
|
|
case 0: \
|
|
for (i = 0; i < (full ? 16 : 8); i++) \
|
|
aarch64_set_vec_u8 (cpu, vd, i, \
|
|
aarch64_get_vec_##SOURCE##8 (cpu, vn, i) \
|
|
CMP \
|
|
aarch64_get_vec_##SOURCE##8 (cpu, vm, i) \
|
|
? -1 : 0); \
|
|
return; \
|
|
case 1: \
|
|
for (i = 0; i < (full ? 8 : 4); i++) \
|
|
aarch64_set_vec_u16 (cpu, vd, i, \
|
|
aarch64_get_vec_##SOURCE##16 (cpu, vn, i) \
|
|
CMP \
|
|
aarch64_get_vec_##SOURCE##16 (cpu, vm, i) \
|
|
? -1 : 0); \
|
|
return; \
|
|
case 2: \
|
|
for (i = 0; i < (full ? 4 : 2); i++) \
|
|
aarch64_set_vec_u32 (cpu, vd, i, \
|
|
aarch64_get_vec_##SOURCE##32 (cpu, vn, i) \
|
|
CMP \
|
|
aarch64_get_vec_##SOURCE##32 (cpu, vm, i) \
|
|
? -1 : 0); \
|
|
return; \
|
|
case 3: \
|
|
if (! full) \
|
|
HALT_UNALLOC; \
|
|
for (i = 0; i < 2; i++) \
|
|
aarch64_set_vec_u64 (cpu, vd, i, \
|
|
aarch64_get_vec_##SOURCE##64 (cpu, vn, i) \
|
|
CMP \
|
|
aarch64_get_vec_##SOURCE##64 (cpu, vm, i) \
|
|
? -1ULL : 0); \
|
|
return; \
|
|
default: \
|
|
HALT_UNALLOC; \
|
|
} \
|
|
} \
|
|
while (0)
|
|
|
|
#define VEC_CMP0(SOURCE, CMP) \
|
|
do \
|
|
{ \
|
|
switch (size) \
|
|
{ \
|
|
case 0: \
|
|
for (i = 0; i < (full ? 16 : 8); i++) \
|
|
aarch64_set_vec_u8 (cpu, vd, i, \
|
|
aarch64_get_vec_##SOURCE##8 (cpu, vn, i) \
|
|
CMP 0 ? -1 : 0); \
|
|
return; \
|
|
case 1: \
|
|
for (i = 0; i < (full ? 8 : 4); i++) \
|
|
aarch64_set_vec_u16 (cpu, vd, i, \
|
|
aarch64_get_vec_##SOURCE##16 (cpu, vn, i) \
|
|
CMP 0 ? -1 : 0); \
|
|
return; \
|
|
case 2: \
|
|
for (i = 0; i < (full ? 4 : 2); i++) \
|
|
aarch64_set_vec_u32 (cpu, vd, i, \
|
|
aarch64_get_vec_##SOURCE##32 (cpu, vn, i) \
|
|
CMP 0 ? -1 : 0); \
|
|
return; \
|
|
case 3: \
|
|
if (! full) \
|
|
HALT_UNALLOC; \
|
|
for (i = 0; i < 2; i++) \
|
|
aarch64_set_vec_u64 (cpu, vd, i, \
|
|
aarch64_get_vec_##SOURCE##64 (cpu, vn, i) \
|
|
CMP 0 ? -1ULL : 0); \
|
|
return; \
|
|
default: \
|
|
HALT_UNALLOC; \
|
|
} \
|
|
} \
|
|
while (0)
|
|
|
|
#define VEC_FCMP0(CMP) \
|
|
do \
|
|
{ \
|
|
if (vm != 0) \
|
|
HALT_NYI; \
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22)) \
|
|
{ \
|
|
if (! full) \
|
|
HALT_NYI; \
|
|
for (i = 0; i < 2; i++) \
|
|
aarch64_set_vec_u64 (cpu, vd, i, \
|
|
aarch64_get_vec_double (cpu, vn, i) \
|
|
CMP 0.0 ? -1 : 0); \
|
|
} \
|
|
else \
|
|
{ \
|
|
for (i = 0; i < (full ? 4 : 2); i++) \
|
|
aarch64_set_vec_u32 (cpu, vd, i, \
|
|
aarch64_get_vec_float (cpu, vn, i) \
|
|
CMP 0.0 ? -1 : 0); \
|
|
} \
|
|
return; \
|
|
} \
|
|
while (0)
|
|
|
|
#define VEC_FCMP(CMP) \
|
|
do \
|
|
{ \
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22)) \
|
|
{ \
|
|
if (! full) \
|
|
HALT_NYI; \
|
|
for (i = 0; i < 2; i++) \
|
|
aarch64_set_vec_u64 (cpu, vd, i, \
|
|
aarch64_get_vec_double (cpu, vn, i) \
|
|
CMP \
|
|
aarch64_get_vec_double (cpu, vm, i) \
|
|
? -1 : 0); \
|
|
} \
|
|
else \
|
|
{ \
|
|
for (i = 0; i < (full ? 4 : 2); i++) \
|
|
aarch64_set_vec_u32 (cpu, vd, i, \
|
|
aarch64_get_vec_float (cpu, vn, i) \
|
|
CMP \
|
|
aarch64_get_vec_float (cpu, vm, i) \
|
|
? -1 : 0); \
|
|
} \
|
|
return; \
|
|
} \
|
|
while (0)
|
|
|
|
static void
|
|
do_vec_compare (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29] = part-of-comparison-type
|
|
instr[28,24] = 0 1110
|
|
instr[23,22] = size of integer compares: byte(00), half(01), word (10), long (11)
|
|
type of float compares: single (-0) / double (-1)
|
|
instr[21] = 1
|
|
instr[20,16] = Vm or 00000 (compare vs 0)
|
|
instr[15,10] = part-of-comparison-type
|
|
instr[9,5] = Vn
|
|
instr[4.0] = Vd. */
|
|
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
int size = uimm (aarch64_get_instr (cpu), 23, 22);
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (28, 24, 0x0E);
|
|
NYI_assert (21, 21, 1);
|
|
|
|
if ((uimm (aarch64_get_instr (cpu), 11, 11)
|
|
&& uimm (aarch64_get_instr (cpu), 14, 14))
|
|
|| ((uimm (aarch64_get_instr (cpu), 11, 11) == 0
|
|
&& uimm (aarch64_get_instr (cpu), 10, 10) == 0)))
|
|
{
|
|
/* A compare vs 0. */
|
|
if (vm != 0)
|
|
{
|
|
if (uimm (aarch64_get_instr (cpu), 15, 10) == 0x2A)
|
|
do_vec_maxv (cpu);
|
|
else if (uimm (aarch64_get_instr (cpu), 15, 10) == 0x32
|
|
|| uimm (aarch64_get_instr (cpu), 15, 10) == 0x3E)
|
|
do_vec_fminmaxV (cpu);
|
|
else if (uimm (aarch64_get_instr (cpu), 29, 23) == 0x1C
|
|
&& uimm (aarch64_get_instr (cpu), 21, 10) == 0x876)
|
|
do_vec_SCVTF (cpu);
|
|
else
|
|
HALT_NYI;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 14, 14))
|
|
{
|
|
/* A floating point compare. */
|
|
unsigned decode = (uimm (aarch64_get_instr (cpu), 29, 29) << 5)
|
|
| (uimm (aarch64_get_instr (cpu), 23, 23) << 4)
|
|
| uimm (aarch64_get_instr (cpu), 13, 10);
|
|
|
|
NYI_assert (15, 15, 1);
|
|
|
|
switch (decode)
|
|
{
|
|
case /* 0b010010: GT#0 */ 0x12: VEC_FCMP0 (>);
|
|
case /* 0b110010: GE#0 */ 0x32: VEC_FCMP0 (>=);
|
|
case /* 0b010110: EQ#0 */ 0x16: VEC_FCMP0 (==);
|
|
case /* 0b110110: LE#0 */ 0x36: VEC_FCMP0 (<=);
|
|
case /* 0b011010: LT#0 */ 0x1A: VEC_FCMP0 (<);
|
|
case /* 0b111001: GT */ 0x39: VEC_FCMP (>);
|
|
case /* 0b101001: GE */ 0x29: VEC_FCMP (>=);
|
|
case /* 0b001001: EQ */ 0x09: VEC_FCMP (==);
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned decode = (uimm (aarch64_get_instr (cpu), 29, 29) << 6)
|
|
| uimm (aarch64_get_instr (cpu), 15, 10);
|
|
|
|
switch (decode)
|
|
{
|
|
case 0x0D: /* 0001101 GT */ VEC_CMP (s, > );
|
|
case 0x0F: /* 0001111 GE */ VEC_CMP (s, >= );
|
|
case 0x22: /* 0100010 GT #0 */ VEC_CMP0 (s, > );
|
|
case 0x26: /* 0100110 EQ #0 */ VEC_CMP0 (s, == );
|
|
case 0x2A: /* 0101010 LT #0 */ VEC_CMP0 (s, < );
|
|
case 0x4D: /* 1001101 HI */ VEC_CMP (u, > );
|
|
case 0x4F: /* 1001111 HS */ VEC_CMP (u, >= );
|
|
case 0x62: /* 1100010 GE #0 */ VEC_CMP0 (s, >= );
|
|
case 0x63: /* 1100011 EQ */ VEC_CMP (u, == );
|
|
case 0x66: /* 1100110 LE #0 */ VEC_CMP0 (s, <= );
|
|
default:
|
|
if (vm == 0)
|
|
HALT_NYI;
|
|
do_vec_maxv (cpu);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_SSHL (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = first part (0)/ second part (1)
|
|
instr[29,24] = 00 1110
|
|
instr[23,22] = size: byte(00), half(01), word (10), long (11)
|
|
instr[21] = 1
|
|
instr[20,16] = Vm
|
|
instr[15,10] = 0100 01
|
|
instr[9,5] = Vn
|
|
instr[4,0] = Vd. */
|
|
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 24, 0x0E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x11);
|
|
|
|
/* FIXME: What is a signed shift left in this context ?. */
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_s8 (cpu, vd, i, aarch64_get_vec_s8 (cpu, vn, i)
|
|
<< aarch64_get_vec_s8 (cpu, vm, i));
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_s16 (cpu, vd, i, aarch64_get_vec_s16 (cpu, vn, i)
|
|
<< aarch64_get_vec_s16 (cpu, vm, i));
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_s32 (cpu, vd, i, aarch64_get_vec_s32 (cpu, vn, i)
|
|
<< aarch64_get_vec_s32 (cpu, vm, i));
|
|
return;
|
|
|
|
case 3:
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_s64 (cpu, vd, i, aarch64_get_vec_s64 (cpu, vn, i)
|
|
<< aarch64_get_vec_s64 (cpu, vm, i));
|
|
return;
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_USHL (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = first part (0)/ second part (1)
|
|
instr[29,24] = 10 1110
|
|
instr[23,22] = size: byte(00), half(01), word (10), long (11)
|
|
instr[21] = 1
|
|
instr[20,16] = Vm
|
|
instr[15,10] = 0100 01
|
|
instr[9,5] = Vn
|
|
instr[4,0] = Vd */
|
|
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 24, 0x2E);
|
|
NYI_assert (15, 10, 0x11);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i, aarch64_get_vec_u8 (cpu, vn, i)
|
|
<< aarch64_get_vec_u8 (cpu, vm, i));
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i, aarch64_get_vec_u16 (cpu, vn, i)
|
|
<< aarch64_get_vec_u16 (cpu, vm, i));
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i, aarch64_get_vec_u32 (cpu, vn, i)
|
|
<< aarch64_get_vec_u32 (cpu, vm, i));
|
|
return;
|
|
|
|
case 3:
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_u64 (cpu, vd, i, aarch64_get_vec_u64 (cpu, vn, i)
|
|
<< aarch64_get_vec_u64 (cpu, vm, i));
|
|
return;
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_FMLA (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = full/half selector
|
|
instr[29,23] = 0011100
|
|
instr[22] = size: 0=>float, 1=>double
|
|
instr[21] = 1
|
|
instr[20,16] = Vn
|
|
instr[15,10] = 1100 11
|
|
instr[9,5] = Vm
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 23, 0x1C);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x33);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_double (cpu, vd, i,
|
|
aarch64_get_vec_double (cpu, vn, i) *
|
|
aarch64_get_vec_double (cpu, vm, i) +
|
|
aarch64_get_vec_double (cpu, vd, i));
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_float (cpu, vd, i,
|
|
aarch64_get_vec_float (cpu, vn, i) *
|
|
aarch64_get_vec_float (cpu, vm, i) +
|
|
aarch64_get_vec_float (cpu, vd, i));
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_max (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = full/half selector
|
|
instr[29] = SMAX (0) / UMAX (1)
|
|
instr[28,24] = 0 1110
|
|
instr[23,22] = size: 00=> 8-bit, 01=> 16-bit, 10=> 32-bit
|
|
instr[21] = 1
|
|
instr[20,16] = Vn
|
|
instr[15,10] = 0110 01
|
|
instr[9,5] = Vm
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (28, 24, 0x0E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x19);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 29, 29))
|
|
{
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i,
|
|
aarch64_get_vec_u8 (cpu, vn, i)
|
|
> aarch64_get_vec_u8 (cpu, vm, i)
|
|
? aarch64_get_vec_u8 (cpu, vn, i)
|
|
: aarch64_get_vec_u8 (cpu, vm, i));
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i,
|
|
aarch64_get_vec_u16 (cpu, vn, i)
|
|
> aarch64_get_vec_u16 (cpu, vm, i)
|
|
? aarch64_get_vec_u16 (cpu, vn, i)
|
|
: aarch64_get_vec_u16 (cpu, vm, i));
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i,
|
|
aarch64_get_vec_u32 (cpu, vn, i)
|
|
> aarch64_get_vec_u32 (cpu, vm, i)
|
|
? aarch64_get_vec_u32 (cpu, vn, i)
|
|
: aarch64_get_vec_u32 (cpu, vm, i));
|
|
return;
|
|
|
|
default:
|
|
case 3:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_s8 (cpu, vd, i,
|
|
aarch64_get_vec_s8 (cpu, vn, i)
|
|
> aarch64_get_vec_s8 (cpu, vm, i)
|
|
? aarch64_get_vec_s8 (cpu, vn, i)
|
|
: aarch64_get_vec_s8 (cpu, vm, i));
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_s16 (cpu, vd, i,
|
|
aarch64_get_vec_s16 (cpu, vn, i)
|
|
> aarch64_get_vec_s16 (cpu, vm, i)
|
|
? aarch64_get_vec_s16 (cpu, vn, i)
|
|
: aarch64_get_vec_s16 (cpu, vm, i));
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_s32 (cpu, vd, i,
|
|
aarch64_get_vec_s32 (cpu, vn, i)
|
|
> aarch64_get_vec_s32 (cpu, vm, i)
|
|
? aarch64_get_vec_s32 (cpu, vn, i)
|
|
: aarch64_get_vec_s32 (cpu, vm, i));
|
|
return;
|
|
|
|
default:
|
|
case 3:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_min (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = full/half selector
|
|
instr[29] = SMIN (0) / UMIN (1)
|
|
instr[28,24] = 0 1110
|
|
instr[23,22] = size: 00=> 8-bit, 01=> 16-bit, 10=> 32-bit
|
|
instr[21] = 1
|
|
instr[20,16] = Vn
|
|
instr[15,10] = 0110 11
|
|
instr[9,5] = Vm
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (28, 24, 0x0E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x1B);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 29, 29))
|
|
{
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i,
|
|
aarch64_get_vec_u8 (cpu, vn, i)
|
|
< aarch64_get_vec_u8 (cpu, vm, i)
|
|
? aarch64_get_vec_u8 (cpu, vn, i)
|
|
: aarch64_get_vec_u8 (cpu, vm, i));
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i,
|
|
aarch64_get_vec_u16 (cpu, vn, i)
|
|
< aarch64_get_vec_u16 (cpu, vm, i)
|
|
? aarch64_get_vec_u16 (cpu, vn, i)
|
|
: aarch64_get_vec_u16 (cpu, vm, i));
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i,
|
|
aarch64_get_vec_u32 (cpu, vn, i)
|
|
< aarch64_get_vec_u32 (cpu, vm, i)
|
|
? aarch64_get_vec_u32 (cpu, vn, i)
|
|
: aarch64_get_vec_u32 (cpu, vm, i));
|
|
return;
|
|
|
|
default:
|
|
case 3:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_s8 (cpu, vd, i,
|
|
aarch64_get_vec_s8 (cpu, vn, i)
|
|
< aarch64_get_vec_s8 (cpu, vm, i)
|
|
? aarch64_get_vec_s8 (cpu, vn, i)
|
|
: aarch64_get_vec_s8 (cpu, vm, i));
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_s16 (cpu, vd, i,
|
|
aarch64_get_vec_s16 (cpu, vn, i)
|
|
< aarch64_get_vec_s16 (cpu, vm, i)
|
|
? aarch64_get_vec_s16 (cpu, vn, i)
|
|
: aarch64_get_vec_s16 (cpu, vm, i));
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_s32 (cpu, vd, i,
|
|
aarch64_get_vec_s32 (cpu, vn, i)
|
|
< aarch64_get_vec_s32 (cpu, vm, i)
|
|
? aarch64_get_vec_s32 (cpu, vn, i)
|
|
: aarch64_get_vec_s32 (cpu, vm, i));
|
|
return;
|
|
|
|
default:
|
|
case 3:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_sub_long (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = lower (0) / upper (1)
|
|
instr[29] = signed (0) / unsigned (1)
|
|
instr[28,24] = 0 1110
|
|
instr[23,22] = size: bytes (00), half (01), word (10)
|
|
instr[21] = 1
|
|
insrt[20,16] = Vm
|
|
instr[15,10] = 0010 00
|
|
instr[9,5] = Vn
|
|
instr[4,0] = V dest. */
|
|
|
|
unsigned size = uimm (aarch64_get_instr (cpu), 23, 22);
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned bias = 0;
|
|
unsigned i;
|
|
|
|
NYI_assert (28, 24, 0x0E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x08);
|
|
|
|
if (size == 3)
|
|
HALT_UNALLOC;
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 30, 29))
|
|
{
|
|
case 2: /* SSUBL2. */
|
|
bias = 2;
|
|
case 0: /* SSUBL. */
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
bias *= 3;
|
|
for (i = 0; i < 8; i++)
|
|
aarch64_set_vec_s16 (cpu, vd, i,
|
|
aarch64_get_vec_s8 (cpu, vn, i + bias)
|
|
- aarch64_get_vec_s8 (cpu, vm, i + bias));
|
|
break;
|
|
|
|
case 1:
|
|
bias *= 2;
|
|
for (i = 0; i < 4; i++)
|
|
aarch64_set_vec_s32 (cpu, vd, i,
|
|
aarch64_get_vec_s16 (cpu, vn, i + bias)
|
|
- aarch64_get_vec_s16 (cpu, vm, i + bias));
|
|
break;
|
|
|
|
case 2:
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_s64 (cpu, vd, i,
|
|
aarch64_get_vec_s32 (cpu, vn, i + bias)
|
|
- aarch64_get_vec_s32 (cpu, vm, i + bias));
|
|
break;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
break;
|
|
|
|
case 3: /* USUBL2. */
|
|
bias = 2;
|
|
case 1: /* USUBL. */
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
bias *= 3;
|
|
for (i = 0; i < 8; i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i,
|
|
aarch64_get_vec_u8 (cpu, vn, i + bias)
|
|
- aarch64_get_vec_u8 (cpu, vm, i + bias));
|
|
break;
|
|
|
|
case 1:
|
|
bias *= 2;
|
|
for (i = 0; i < 4; i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i,
|
|
aarch64_get_vec_u16 (cpu, vn, i + bias)
|
|
- aarch64_get_vec_u16 (cpu, vm, i + bias));
|
|
break;
|
|
|
|
case 2:
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_u64 (cpu, vd, i,
|
|
aarch64_get_vec_u32 (cpu, vn, i + bias)
|
|
- aarch64_get_vec_u32 (cpu, vm, i + bias));
|
|
break;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define DO_ADDP(FN) \
|
|
do \
|
|
{ \
|
|
for (i = 0; i < range; i++) \
|
|
{ \
|
|
aarch64_set_vec_##FN (cpu, vd, i, \
|
|
aarch64_get_vec_##FN (cpu, vn, i * 2) \
|
|
+ aarch64_get_vec_##FN (cpu, vn, i * 2 + 1)); \
|
|
aarch64_set_vec_##FN (cpu, vd, i + range, \
|
|
aarch64_get_vec_##FN (cpu, vm, i * 2) \
|
|
+ aarch64_get_vec_##FN (cpu, vm, i * 2 + 1)); \
|
|
} \
|
|
} \
|
|
while (0)
|
|
|
|
static void
|
|
do_vec_ADDP (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29,24] = 00 1110
|
|
instr[23,22] = size: bytes (00), half (01), word (10), long (11)
|
|
instr[21] = 1
|
|
insrt[20,16] = Vm
|
|
instr[15,10] = 1011 11
|
|
instr[9,5] = Vn
|
|
instr[4,0] = V dest. */
|
|
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned size = uimm (aarch64_get_instr (cpu), 23, 22);
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i, range;
|
|
|
|
NYI_assert (29, 24, 0x0E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x2F);
|
|
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
range = full ? 8 : 4;
|
|
DO_ADDP (u8);
|
|
return;
|
|
|
|
case 1:
|
|
range = full ? 4 : 2;
|
|
DO_ADDP (u16);
|
|
return;
|
|
|
|
case 2:
|
|
range = full ? 2 : 1;
|
|
DO_ADDP (u32);
|
|
return;
|
|
|
|
case 3:
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
range = 1;
|
|
DO_ADDP (u64);
|
|
return;
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_UMOV (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = 32-bit(0)/64-bit(1)
|
|
instr[29,21] = 00 1110 000
|
|
insrt[20,16] = size & index
|
|
instr[15,10] = 0011 11
|
|
instr[9,5] = V source
|
|
instr[4,0] = R dest. */
|
|
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned index;
|
|
|
|
NYI_assert (29, 21, 0x070);
|
|
NYI_assert (15, 10, 0x0F);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 16, 16))
|
|
{
|
|
/* Byte transfer. */
|
|
index = uimm (aarch64_get_instr (cpu), 20, 17);
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_vec_u8 (cpu, vs, index));
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 17, 17))
|
|
{
|
|
index = uimm (aarch64_get_instr (cpu), 20, 18);
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_vec_u16 (cpu, vs, index));
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 18, 18))
|
|
{
|
|
index = uimm (aarch64_get_instr (cpu), 20, 19);
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_vec_u32 (cpu, vs, index));
|
|
}
|
|
else
|
|
{
|
|
if (uimm (aarch64_get_instr (cpu), 30, 30) != 1)
|
|
HALT_UNALLOC;
|
|
|
|
index = uimm (aarch64_get_instr (cpu), 20, 20);
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_vec_u64 (cpu, vs, index));
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_FABS (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29,23] = 00 1110 1
|
|
instr[22] = float(0)/double(1)
|
|
instr[21,16] = 10 0000
|
|
instr[15,10] = 1111 10
|
|
instr[9,5] = Vn
|
|
instr[4,0] = Vd. */
|
|
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 23, 0x1D);
|
|
NYI_assert (21, 10, 0x83E);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
if (! full)
|
|
HALT_NYI;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_double (cpu, vd, i,
|
|
fabs (aarch64_get_vec_double (cpu, vn, i)));
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_float (cpu, vd, i,
|
|
fabsf (aarch64_get_vec_float (cpu, vn, i)));
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_FCVTZS (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half (0) / all (1)
|
|
instr[29,23] = 00 1110 1
|
|
instr[22] = single (0) / double (1)
|
|
instr[21,10] = 10 0001 1011 10
|
|
instr[9,5] = Rn
|
|
instr[4,0] = Rd. */
|
|
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned i;
|
|
|
|
NYI_assert (31, 31, 0);
|
|
NYI_assert (29, 23, 0x1D);
|
|
NYI_assert (21, 10, 0x86E);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_s64 (cpu, rd, i,
|
|
(int64_t) aarch64_get_vec_double (cpu, rn, i));
|
|
}
|
|
else
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_s32 (cpu, rd, i,
|
|
(int32_t) aarch64_get_vec_float (cpu, rn, i));
|
|
}
|
|
|
|
static void
|
|
do_vec_op1 (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half/full
|
|
instr[29,24] = 00 1110
|
|
instr[23,21] = ???
|
|
instr[20,16] = Vm
|
|
instr[15,10] = sub-opcode
|
|
instr[9,5] = Vn
|
|
instr[4,0] = Vd */
|
|
NYI_assert (29, 24, 0x0E);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 21, 21) == 0)
|
|
{
|
|
if (uimm (aarch64_get_instr (cpu), 23, 22) == 0)
|
|
{
|
|
if (uimm (aarch64_get_instr (cpu), 30, 30) == 1
|
|
&& uimm (aarch64_get_instr (cpu), 17, 14) == 0
|
|
&& uimm (aarch64_get_instr (cpu), 12, 10) == 7)
|
|
return do_vec_ins_2 (cpu);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 15, 10))
|
|
{
|
|
case 0x01: do_vec_DUP_vector_into_vector (cpu); return;
|
|
case 0x03: do_vec_DUP_scalar_into_vector (cpu); return;
|
|
case 0x07: do_vec_INS (cpu); return;
|
|
case 0x0A: do_vec_TRN (cpu); return;
|
|
|
|
case 0x0F:
|
|
if (uimm (aarch64_get_instr (cpu), 17, 16) == 0)
|
|
{
|
|
do_vec_MOV_into_scalar (cpu);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case 0x00:
|
|
case 0x08:
|
|
case 0x10:
|
|
case 0x18:
|
|
do_vec_TBL (cpu); return;
|
|
|
|
case 0x06:
|
|
case 0x16:
|
|
do_vec_UZP (cpu); return;
|
|
|
|
case 0x0E:
|
|
case 0x1E:
|
|
do_vec_ZIP (cpu); return;
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 13, 10))
|
|
{
|
|
case 0x6: do_vec_UZP (cpu); return;
|
|
case 0xE: do_vec_ZIP (cpu); return;
|
|
case 0xA: do_vec_TRN (cpu); return;
|
|
case 0xF: do_vec_UMOV (cpu); return;
|
|
default: HALT_NYI;
|
|
}
|
|
}
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 15, 10))
|
|
{
|
|
case 0x07:
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 21))
|
|
{
|
|
case 1: do_vec_AND (cpu); return;
|
|
case 3: do_vec_BIC (cpu); return;
|
|
case 5: do_vec_ORR (cpu); return;
|
|
case 7: do_vec_ORN (cpu); return;
|
|
default: HALT_NYI;
|
|
}
|
|
|
|
case 0x08: do_vec_sub_long (cpu); return;
|
|
case 0x0a: do_vec_XTN (cpu); return;
|
|
case 0x11: do_vec_SSHL (cpu); return;
|
|
case 0x19: do_vec_max (cpu); return;
|
|
case 0x1B: do_vec_min (cpu); return;
|
|
case 0x21: do_vec_add (cpu); return;
|
|
case 0x25: do_vec_MLA (cpu); return;
|
|
case 0x27: do_vec_mul (cpu); return;
|
|
case 0x2F: do_vec_ADDP (cpu); return;
|
|
case 0x30: do_vec_mull (cpu); return;
|
|
case 0x33: do_vec_FMLA (cpu); return;
|
|
case 0x35: do_vec_fadd (cpu); return;
|
|
|
|
case 0x2E:
|
|
switch (uimm (aarch64_get_instr (cpu), 20, 16))
|
|
{
|
|
case 0x00: do_vec_ABS (cpu); return;
|
|
case 0x01: do_vec_FCVTZS (cpu); return;
|
|
case 0x11: do_vec_ADDV (cpu); return;
|
|
default: HALT_NYI;
|
|
}
|
|
|
|
case 0x31:
|
|
case 0x3B:
|
|
do_vec_Fminmax (cpu); return;
|
|
|
|
case 0x0D:
|
|
case 0x0F:
|
|
case 0x22:
|
|
case 0x23:
|
|
case 0x26:
|
|
case 0x2A:
|
|
case 0x32:
|
|
case 0x36:
|
|
case 0x39:
|
|
case 0x3A:
|
|
do_vec_compare (cpu); return;
|
|
|
|
case 0x3E:
|
|
do_vec_FABS (cpu); return;
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_xtl (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30,29] = SXTL (00), UXTL (01), SXTL2 (10), UXTL2 (11)
|
|
instr[28,22] = 0 1111 00
|
|
instr[21,16] = size & shift (USHLL, SSHLL, USHLL2, SSHLL2)
|
|
instr[15,10] = 1010 01
|
|
instr[9,5] = V source
|
|
instr[4,0] = V dest. */
|
|
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i, shift, bias = 0;
|
|
|
|
NYI_assert (28, 22, 0x3C);
|
|
NYI_assert (15, 10, 0x29);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 30, 29))
|
|
{
|
|
case 2: /* SXTL2, SSHLL2. */
|
|
bias = 2;
|
|
case 0: /* SXTL, SSHLL. */
|
|
if (uimm (aarch64_get_instr (cpu), 21, 21))
|
|
{
|
|
shift = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
aarch64_set_vec_s64
|
|
(cpu, vd, 0, aarch64_get_vec_s32 (cpu, vs, bias) << shift);
|
|
aarch64_set_vec_s64
|
|
(cpu, vd, 1, aarch64_get_vec_s32 (cpu, vs, bias + 1) << shift);
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 20, 20))
|
|
{
|
|
shift = uimm (aarch64_get_instr (cpu), 19, 16);
|
|
bias *= 2;
|
|
for (i = 0; i < 4; i++)
|
|
aarch64_set_vec_s32
|
|
(cpu, vd, i, aarch64_get_vec_s16 (cpu, vs, i + bias) << shift);
|
|
}
|
|
else
|
|
{
|
|
NYI_assert (19, 19, 1);
|
|
|
|
shift = uimm (aarch64_get_instr (cpu), 18, 16);
|
|
bias *= 3;
|
|
for (i = 0; i < 8; i++)
|
|
aarch64_set_vec_s16
|
|
(cpu, vd, i, aarch64_get_vec_s8 (cpu, vs, i + bias) << shift);
|
|
}
|
|
return;
|
|
|
|
case 3: /* UXTL2, USHLL2. */
|
|
bias = 2;
|
|
case 1: /* UXTL, USHLL. */
|
|
if (uimm (aarch64_get_instr (cpu), 21, 21))
|
|
{
|
|
shift = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
aarch64_set_vec_u64
|
|
(cpu, vd, 0, aarch64_get_vec_u32 (cpu, vs, bias) << shift);
|
|
aarch64_set_vec_u64
|
|
(cpu, vd, 1, aarch64_get_vec_u32 (cpu, vs, bias + 1) << shift);
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 20, 20))
|
|
{
|
|
shift = uimm (aarch64_get_instr (cpu), 19, 16);
|
|
bias *= 2;
|
|
for (i = 0; i < 4; i++)
|
|
aarch64_set_vec_u32
|
|
(cpu, vd, i, aarch64_get_vec_u16 (cpu, vs, i + bias) << shift);
|
|
}
|
|
else
|
|
{
|
|
NYI_assert (19, 19, 1);
|
|
|
|
shift = uimm (aarch64_get_instr (cpu), 18, 16);
|
|
bias *= 3;
|
|
for (i = 0; i < 8; i++)
|
|
aarch64_set_vec_u16
|
|
(cpu, vd, i, aarch64_get_vec_u8 (cpu, vs, i + bias) << shift);
|
|
}
|
|
return;
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_SHL (sim_cpu *cpu)
|
|
{
|
|
/* instr [31] = 0
|
|
instr [30] = half(0)/full(1)
|
|
instr [29,23] = 001 1110
|
|
instr [22,16] = size and shift amount
|
|
instr [15,10] = 01 0101
|
|
instr [9, 5] = Vs
|
|
instr [4, 0] = Vd. */
|
|
|
|
int shift;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 23, 0x1E);
|
|
NYI_assert (15, 10, 0x15);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
shift = uimm (aarch64_get_instr (cpu), 21, 16) - 1;
|
|
|
|
if (full == 0)
|
|
HALT_UNALLOC;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
uint64_t val = aarch64_get_vec_u64 (cpu, vs, i);
|
|
aarch64_set_vec_u64 (cpu, vd, i, val << shift);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 21, 21))
|
|
{
|
|
shift = uimm (aarch64_get_instr (cpu), 20, 16) - 1;
|
|
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
{
|
|
uint32_t val = aarch64_get_vec_u32 (cpu, vs, i);
|
|
aarch64_set_vec_u32 (cpu, vd, i, val << shift);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 20, 20))
|
|
{
|
|
shift = uimm (aarch64_get_instr (cpu), 19, 16) - 1;
|
|
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
{
|
|
uint16_t val = aarch64_get_vec_u16 (cpu, vs, i);
|
|
aarch64_set_vec_u16 (cpu, vd, i, val << shift);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 19, 19) == 0)
|
|
HALT_UNALLOC;
|
|
|
|
shift = uimm (aarch64_get_instr (cpu), 18, 16) - 1;
|
|
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
{
|
|
uint8_t val = aarch64_get_vec_u8 (cpu, vs, i);
|
|
aarch64_set_vec_u8 (cpu, vd, i, val << shift);
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_SSHR_USHR (sim_cpu *cpu)
|
|
{
|
|
/* instr [31] = 0
|
|
instr [30] = half(0)/full(1)
|
|
instr [29] = signed(0)/unsigned(1)
|
|
instr [28,23] = 01 1110
|
|
instr [22,16] = size and shift amount
|
|
instr [15,10] = 0000 01
|
|
instr [9, 5] = Vs
|
|
instr [4, 0] = Vd. */
|
|
|
|
int shift;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
int sign = uimm (aarch64_get_instr (cpu), 29, 29);
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (28, 23, 0x1E);
|
|
NYI_assert (15, 10, 0x01);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
shift = uimm (aarch64_get_instr (cpu), 21, 16);
|
|
|
|
if (full == 0)
|
|
HALT_UNALLOC;
|
|
|
|
if (sign)
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
int64_t val = aarch64_get_vec_s64 (cpu, vs, i);
|
|
aarch64_set_vec_s64 (cpu, vd, i, val >> shift);
|
|
}
|
|
else
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
uint64_t val = aarch64_get_vec_u64 (cpu, vs, i);
|
|
aarch64_set_vec_u64 (cpu, vd, i, val >> shift);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 21, 21))
|
|
{
|
|
shift = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
|
|
if (sign)
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
{
|
|
int32_t val = aarch64_get_vec_s32 (cpu, vs, i);
|
|
aarch64_set_vec_s32 (cpu, vd, i, val >> shift);
|
|
}
|
|
else
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
{
|
|
uint32_t val = aarch64_get_vec_u32 (cpu, vs, i);
|
|
aarch64_set_vec_u32 (cpu, vd, i, val >> shift);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 20, 20))
|
|
{
|
|
shift = uimm (aarch64_get_instr (cpu), 19, 16);
|
|
|
|
if (sign)
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
{
|
|
int16_t val = aarch64_get_vec_s16 (cpu, vs, i);
|
|
aarch64_set_vec_s16 (cpu, vd, i, val >> shift);
|
|
}
|
|
else
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
{
|
|
uint16_t val = aarch64_get_vec_u16 (cpu, vs, i);
|
|
aarch64_set_vec_u16 (cpu, vd, i, val >> shift);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 19, 19) == 0)
|
|
HALT_UNALLOC;
|
|
|
|
shift = uimm (aarch64_get_instr (cpu), 18, 16);
|
|
|
|
if (sign)
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
{
|
|
int8_t val = aarch64_get_vec_s8 (cpu, vs, i);
|
|
aarch64_set_vec_s8 (cpu, vd, i, val >> shift);
|
|
}
|
|
else
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
{
|
|
uint8_t val = aarch64_get_vec_u8 (cpu, vs, i);
|
|
aarch64_set_vec_u8 (cpu, vd, i, val >> shift);
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_op2 (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half/full
|
|
instr[29,24] = 00 1111
|
|
instr[23] = ?
|
|
instr[22,16] = element size & index
|
|
instr[15,10] = sub-opcode
|
|
instr[9,5] = Vm
|
|
instr[4.0] = Vd */
|
|
|
|
NYI_assert (29, 24, 0x0F);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 23, 23) != 0)
|
|
HALT_NYI;
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 15, 10))
|
|
{
|
|
case 0x01: do_vec_SSHR_USHR (cpu); return;
|
|
case 0x15: do_vec_SHL (cpu); return;
|
|
case 0x29: do_vec_xtl (cpu); return;
|
|
default: HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_neg (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = full(1)/half(0)
|
|
instr[29,24] = 10 1110
|
|
instr[23,22] = size: byte(00), half (01), word (10), long (11)
|
|
instr[21,10] = 1000 0010 1110
|
|
instr[9,5] = Vs
|
|
instr[4,0] = Vd */
|
|
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 24, 0x2E);
|
|
NYI_assert (21, 10, 0x82E);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_s8 (cpu, vd, i, - aarch64_get_vec_s8 (cpu, vs, i));
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_s16 (cpu, vd, i, - aarch64_get_vec_s16 (cpu, vs, i));
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_s32 (cpu, vd, i, - aarch64_get_vec_s32 (cpu, vs, i));
|
|
return;
|
|
|
|
case 3:
|
|
if (! full)
|
|
HALT_NYI;
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_s64 (cpu, vd, i, - aarch64_get_vec_s64 (cpu, vs, i));
|
|
return;
|
|
|
|
default:
|
|
HALT_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_sqrt (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = full(1)/half(0)
|
|
instr[29,23] = 101 1101
|
|
instr[22] = single(0)/double(1)
|
|
instr[21,10] = 1000 0111 1110
|
|
instr[9,5] = Vs
|
|
instr[4,0] = Vd. */
|
|
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 23, 0x5B);
|
|
NYI_assert (21, 10, 0x87E);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22) == 0)
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_float (cpu, vd, i,
|
|
sqrtf (aarch64_get_vec_float (cpu, vs, i)));
|
|
else
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_double (cpu, vd, i,
|
|
sqrt (aarch64_get_vec_double (cpu, vs, i)));
|
|
}
|
|
|
|
static void
|
|
do_vec_mls_indexed (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29,24] = 10 1111
|
|
instr[23,22] = 16-bit(01)/32-bit(10)
|
|
instr[21,20+11] = index (if 16-bit)
|
|
instr[21+11] = index (if 32-bit)
|
|
instr[20,16] = Vm
|
|
instr[15,12] = 0100
|
|
instr[11] = part of index
|
|
instr[10] = 0
|
|
instr[9,5] = Vs
|
|
instr[4,0] = Vd. */
|
|
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned i;
|
|
|
|
NYI_assert (15, 12, 4);
|
|
NYI_assert (10, 10, 0);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 1:
|
|
{
|
|
unsigned elem;
|
|
uint32_t val;
|
|
|
|
if (vm > 15)
|
|
HALT_NYI;
|
|
|
|
elem = (uimm (aarch64_get_instr (cpu), 21, 20) << 1)
|
|
| uimm (aarch64_get_instr (cpu), 11, 11);
|
|
val = aarch64_get_vec_u16 (cpu, vm, elem);
|
|
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i,
|
|
aarch64_get_vec_u32 (cpu, vd, i) -
|
|
(aarch64_get_vec_u32 (cpu, vs, i) * val));
|
|
return;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
unsigned elem = (uimm (aarch64_get_instr (cpu), 21, 21) << 1)
|
|
| uimm (aarch64_get_instr (cpu), 11, 11);
|
|
uint64_t val = aarch64_get_vec_u32 (cpu, vm, elem);
|
|
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u64 (cpu, vd, i,
|
|
aarch64_get_vec_u64 (cpu, vd, i) -
|
|
(aarch64_get_vec_u64 (cpu, vs, i) * val));
|
|
return;
|
|
}
|
|
|
|
case 0:
|
|
case 3:
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_SUB (sim_cpu *cpu)
|
|
{
|
|
/* instr [31] = 0
|
|
instr [30] = half(0)/full(1)
|
|
instr [29,24] = 10 1110
|
|
instr [23,22] = size: byte(00, half(01), word (10), long (11)
|
|
instr [21] = 1
|
|
instr [20,16] = Vm
|
|
instr [15,10] = 10 0001
|
|
instr [9, 5] = Vn
|
|
instr [4, 0] = Vd. */
|
|
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 24, 0x2E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x21);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_s8 (cpu, vd, i,
|
|
aarch64_get_vec_s8 (cpu, vn, i)
|
|
- aarch64_get_vec_s8 (cpu, vm, i));
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_s16 (cpu, vd, i,
|
|
aarch64_get_vec_s16 (cpu, vn, i)
|
|
- aarch64_get_vec_s16 (cpu, vm, i));
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_s32 (cpu, vd, i,
|
|
aarch64_get_vec_s32 (cpu, vn, i)
|
|
- aarch64_get_vec_s32 (cpu, vm, i));
|
|
return;
|
|
|
|
case 3:
|
|
if (full == 0)
|
|
HALT_UNALLOC;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_s64 (cpu, vd, i,
|
|
aarch64_get_vec_s64 (cpu, vn, i)
|
|
- aarch64_get_vec_s64 (cpu, vm, i));
|
|
return;
|
|
|
|
default:
|
|
HALT_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_MLS (sim_cpu *cpu)
|
|
{
|
|
/* instr [31] = 0
|
|
instr [30] = half(0)/full(1)
|
|
instr [29,24] = 10 1110
|
|
instr [23,22] = size: byte(00, half(01), word (10)
|
|
instr [21] = 1
|
|
instr [20,16] = Vm
|
|
instr [15,10] = 10 0101
|
|
instr [9, 5] = Vn
|
|
instr [4, 0] = Vd. */
|
|
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 24, 0x2E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x25);
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i,
|
|
(aarch64_get_vec_u8 (cpu, vn, i)
|
|
* aarch64_get_vec_u8 (cpu, vm, i))
|
|
- aarch64_get_vec_u8 (cpu, vd, i));
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i,
|
|
(aarch64_get_vec_u16 (cpu, vn, i)
|
|
* aarch64_get_vec_u16 (cpu, vm, i))
|
|
- aarch64_get_vec_u16 (cpu, vd, i));
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i,
|
|
(aarch64_get_vec_u32 (cpu, vn, i)
|
|
* aarch64_get_vec_u32 (cpu, vm, i))
|
|
- aarch64_get_vec_u32 (cpu, vd, i));
|
|
return;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_FDIV (sim_cpu *cpu)
|
|
{
|
|
/* instr [31] = 0
|
|
instr [30] = half(0)/full(1)
|
|
instr [29,23] = 10 1110 0
|
|
instr [22] = float()/double(1)
|
|
instr [21] = 1
|
|
instr [20,16] = Vm
|
|
instr [15,10] = 1111 11
|
|
instr [9, 5] = Vn
|
|
instr [4, 0] = Vd. */
|
|
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 23, 0x5C);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x3F);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_double (cpu, vd, i,
|
|
aarch64_get_vec_double (cpu, vn, i)
|
|
/ aarch64_get_vec_double (cpu, vm, i));
|
|
}
|
|
else
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_float (cpu, vd, i,
|
|
aarch64_get_vec_float (cpu, vn, i)
|
|
/ aarch64_get_vec_float (cpu, vm, i));
|
|
}
|
|
|
|
static void
|
|
do_vec_FMUL (sim_cpu *cpu)
|
|
{
|
|
/* instr [31] = 0
|
|
instr [30] = half(0)/full(1)
|
|
instr [29,23] = 10 1110 0
|
|
instr [22] = float(0)/double(1)
|
|
instr [21] = 1
|
|
instr [20,16] = Vm
|
|
instr [15,10] = 1101 11
|
|
instr [9, 5] = Vn
|
|
instr [4, 0] = Vd. */
|
|
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
NYI_assert (29, 23, 0x5C);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x37);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_double (cpu, vd, i,
|
|
aarch64_get_vec_double (cpu, vn, i)
|
|
* aarch64_get_vec_double (cpu, vm, i));
|
|
}
|
|
else
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_float (cpu, vd, i,
|
|
aarch64_get_vec_float (cpu, vn, i)
|
|
* aarch64_get_vec_float (cpu, vm, i));
|
|
}
|
|
|
|
static void
|
|
do_vec_FADDP (sim_cpu *cpu)
|
|
{
|
|
/* instr [31] = 0
|
|
instr [30] = half(0)/full(1)
|
|
instr [29,23] = 10 1110 0
|
|
instr [22] = float(0)/double(1)
|
|
instr [21] = 1
|
|
instr [20,16] = Vm
|
|
instr [15,10] = 1101 01
|
|
instr [9, 5] = Vn
|
|
instr [4, 0] = Vd. */
|
|
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (29, 23, 0x5C);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x35);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
|
|
aarch64_set_vec_double (cpu, vd, 0, aarch64_get_vec_double (cpu, vn, 0)
|
|
+ aarch64_get_vec_double (cpu, vn, 1));
|
|
aarch64_set_vec_double (cpu, vd, 1, aarch64_get_vec_double (cpu, vm, 0)
|
|
+ aarch64_get_vec_double (cpu, vm, 1));
|
|
}
|
|
else
|
|
{
|
|
aarch64_set_vec_float (cpu, vd, 0, aarch64_get_vec_float (cpu, vn, 0)
|
|
+ aarch64_get_vec_float (cpu, vn, 1));
|
|
if (full)
|
|
aarch64_set_vec_float (cpu, vd, 1, aarch64_get_vec_float (cpu, vn, 2)
|
|
+ aarch64_get_vec_float (cpu, vn, 3));
|
|
aarch64_set_vec_float (cpu, vd, full ? 2 : 1,
|
|
aarch64_get_vec_float (cpu, vm, 0)
|
|
+ aarch64_get_vec_float (cpu, vm, 1));
|
|
if (full)
|
|
aarch64_set_vec_float (cpu, vd, 3,
|
|
aarch64_get_vec_float (cpu, vm, 2)
|
|
+ aarch64_get_vec_float (cpu, vm, 3));
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_FSQRT (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half(0)/full(1)
|
|
instr[29,23] = 10 1110 1
|
|
instr[22] = single(0)/double(1)
|
|
instr[21,10] = 10 0001 1111 10
|
|
instr[9,5] = Vsrc
|
|
instr[4,0] = Vdest. */
|
|
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
int i;
|
|
|
|
NYI_assert (29, 23, 0x5D);
|
|
NYI_assert (21, 10, 0x87E);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_double (cpu, vd, i,
|
|
sqrt (aarch64_get_vec_double (cpu, vn, i)));
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_float (cpu, vd, i,
|
|
sqrtf (aarch64_get_vec_float (cpu, vn, i)));
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_FNEG (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half (0)/full (1)
|
|
instr[29,23] = 10 1110 1
|
|
instr[22] = single (0)/double (1)
|
|
instr[21,10] = 10 0000 1111 10
|
|
instr[9,5] = Vsrc
|
|
instr[4,0] = Vdest. */
|
|
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
int i;
|
|
|
|
NYI_assert (29, 23, 0x5D);
|
|
NYI_assert (21, 10, 0x83E);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
if (! full)
|
|
HALT_UNALLOC;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
aarch64_set_vec_double (cpu, vd, i,
|
|
- aarch64_get_vec_double (cpu, vn, i));
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_float (cpu, vd, i,
|
|
- aarch64_get_vec_float (cpu, vn, i));
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_NOT (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = half (0)/full (1)
|
|
instr[29,21] = 10 1110 001
|
|
instr[20,16] = 0 0000
|
|
instr[15,10] = 0101 10
|
|
instr[9,5] = Vn
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
int full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
|
|
NYI_assert (29, 10, 0xB8816);
|
|
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i, ~ aarch64_get_vec_u8 (cpu, vn, i));
|
|
}
|
|
|
|
static void
|
|
do_vec_MOV_element (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,21] = 0110 1110 000
|
|
instr[20,16] = size & dest index
|
|
instr[15] = 0
|
|
instr[14,11] = source index
|
|
instr[10] = 1
|
|
instr[9,5] = Vs
|
|
instr[4.0] = Vd. */
|
|
|
|
unsigned vs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned src_index;
|
|
unsigned dst_index;
|
|
|
|
NYI_assert (31, 21, 0x370);
|
|
NYI_assert (15, 15, 0);
|
|
NYI_assert (10, 10, 1);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 16, 16))
|
|
{
|
|
/* Move a byte. */
|
|
src_index = uimm (aarch64_get_instr (cpu), 14, 11);
|
|
dst_index = uimm (aarch64_get_instr (cpu), 20, 17);
|
|
aarch64_set_vec_u8 (cpu, vd, dst_index,
|
|
aarch64_get_vec_u8 (cpu, vs, src_index));
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 17, 17))
|
|
{
|
|
/* Move 16-bits. */
|
|
NYI_assert (11, 11, 0);
|
|
src_index = uimm (aarch64_get_instr (cpu), 14, 12);
|
|
dst_index = uimm (aarch64_get_instr (cpu), 20, 18);
|
|
aarch64_set_vec_u16 (cpu, vd, dst_index,
|
|
aarch64_get_vec_u16 (cpu, vs, src_index));
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 18, 18))
|
|
{
|
|
/* Move 32-bits. */
|
|
NYI_assert (12, 11, 0);
|
|
src_index = uimm (aarch64_get_instr (cpu), 14, 13);
|
|
dst_index = uimm (aarch64_get_instr (cpu), 20, 19);
|
|
aarch64_set_vec_u32 (cpu, vd, dst_index,
|
|
aarch64_get_vec_u32 (cpu, vs, src_index));
|
|
}
|
|
else
|
|
{
|
|
NYI_assert (19, 19, 1);
|
|
NYI_assert (13, 11, 0);
|
|
src_index = uimm (aarch64_get_instr (cpu), 14, 14);
|
|
dst_index = uimm (aarch64_get_instr (cpu), 20, 20);
|
|
aarch64_set_vec_u64 (cpu, vd, dst_index,
|
|
aarch64_get_vec_u64 (cpu, vs, src_index));
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexAdvSIMD0 (sim_cpu *cpu)
|
|
{
|
|
/* instr [28,25] = 0 111. */
|
|
if ( uimm (aarch64_get_instr (cpu), 15, 10) == 0x07
|
|
&& (uimm (aarch64_get_instr (cpu), 9, 5) ==
|
|
uimm (aarch64_get_instr (cpu), 20, 16)))
|
|
{
|
|
if (uimm (aarch64_get_instr (cpu), 31, 21) == 0x075
|
|
|| uimm (aarch64_get_instr (cpu), 31, 21) == 0x275)
|
|
{
|
|
do_vec_MOV_whole_vector (cpu);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 29, 19) == 0x1E0)
|
|
{
|
|
do_vec_MOV_immediate (cpu);
|
|
return;
|
|
}
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 29, 19) == 0x5E0)
|
|
{
|
|
do_vec_MVNI (cpu);
|
|
return;
|
|
}
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 29, 19) == 0x1C0
|
|
|| uimm (aarch64_get_instr (cpu), 29, 19) == 0x1C1)
|
|
{
|
|
if (uimm (aarch64_get_instr (cpu), 15, 10) == 0x03)
|
|
{
|
|
do_vec_DUP_scalar_into_vector (cpu);
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 29, 24))
|
|
{
|
|
case 0x0E: do_vec_op1 (cpu); return;
|
|
case 0x0F: do_vec_op2 (cpu); return;
|
|
|
|
case 0x2f:
|
|
switch (uimm (aarch64_get_instr (cpu), 15, 10))
|
|
{
|
|
case 0x01: do_vec_SSHR_USHR (cpu); return;
|
|
case 0x10:
|
|
case 0x12: do_vec_mls_indexed (cpu); return;
|
|
case 0x29: do_vec_xtl (cpu); return;
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
|
|
case 0x2E:
|
|
if (uimm (aarch64_get_instr (cpu), 21, 21) == 1)
|
|
{
|
|
switch (uimm (aarch64_get_instr (cpu), 15, 10))
|
|
{
|
|
case 0x07:
|
|
switch (uimm (aarch64_get_instr (cpu), 23, 22))
|
|
{
|
|
case 0: do_vec_EOR (cpu); return;
|
|
case 1: do_vec_BSL (cpu); return;
|
|
case 2:
|
|
case 3: do_vec_bit (cpu); return;
|
|
}
|
|
break;
|
|
|
|
case 0x08: do_vec_sub_long (cpu); return;
|
|
case 0x11: do_vec_USHL (cpu); return;
|
|
case 0x16: do_vec_NOT (cpu); return;
|
|
case 0x19: do_vec_max (cpu); return;
|
|
case 0x1B: do_vec_min (cpu); return;
|
|
case 0x21: do_vec_SUB (cpu); return;
|
|
case 0x25: do_vec_MLS (cpu); return;
|
|
case 0x31: do_vec_FminmaxNMP (cpu); return;
|
|
case 0x35: do_vec_FADDP (cpu); return;
|
|
case 0x37: do_vec_FMUL (cpu); return;
|
|
case 0x3F: do_vec_FDIV (cpu); return;
|
|
|
|
case 0x3E:
|
|
switch (uimm (aarch64_get_instr (cpu), 20, 16))
|
|
{
|
|
case 0x00: do_vec_FNEG (cpu); return;
|
|
case 0x01: do_vec_FSQRT (cpu); return;
|
|
default: HALT_NYI;
|
|
}
|
|
|
|
case 0x0D:
|
|
case 0x0F:
|
|
case 0x22:
|
|
case 0x23:
|
|
case 0x26:
|
|
case 0x2A:
|
|
case 0x32:
|
|
case 0x36:
|
|
case 0x39:
|
|
case 0x3A:
|
|
do_vec_compare (cpu); return;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 31, 21) == 0x370)
|
|
{
|
|
do_vec_MOV_element (cpu);
|
|
return;
|
|
}
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 21, 10))
|
|
{
|
|
case 0x82E: do_vec_neg (cpu); return;
|
|
case 0x87E: do_vec_sqrt (cpu); return;
|
|
default:
|
|
if (uimm (aarch64_get_instr (cpu), 15, 10) == 0x30)
|
|
{
|
|
do_vec_mull (cpu);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
HALT_NYI;
|
|
}
|
|
|
|
/* 3 sources. */
|
|
|
|
/* Float multiply add. */
|
|
static void
|
|
fmadds (sim_cpu *cpu)
|
|
{
|
|
unsigned sa = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, sd, aarch64_get_FP_float (cpu, sa)
|
|
+ aarch64_get_FP_float (cpu, sn)
|
|
* aarch64_get_FP_float (cpu, sm));
|
|
}
|
|
|
|
/* Double multiply add. */
|
|
static void
|
|
fmaddd (sim_cpu *cpu)
|
|
{
|
|
unsigned sa = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, sd, aarch64_get_FP_double (cpu, sa)
|
|
+ aarch64_get_FP_double (cpu, sn)
|
|
* aarch64_get_FP_double (cpu, sm));
|
|
}
|
|
|
|
/* Float multiply subtract. */
|
|
static void
|
|
fmsubs (sim_cpu *cpu)
|
|
{
|
|
unsigned sa = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, sd, aarch64_get_FP_float (cpu, sa)
|
|
- aarch64_get_FP_float (cpu, sn)
|
|
* aarch64_get_FP_float (cpu, sm));
|
|
}
|
|
|
|
/* Double multiply subtract. */
|
|
static void
|
|
fmsubd (sim_cpu *cpu)
|
|
{
|
|
unsigned sa = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, sd, aarch64_get_FP_double (cpu, sa)
|
|
- aarch64_get_FP_double (cpu, sn)
|
|
* aarch64_get_FP_double (cpu, sm));
|
|
}
|
|
|
|
/* Float negative multiply add. */
|
|
static void
|
|
fnmadds (sim_cpu *cpu)
|
|
{
|
|
unsigned sa = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, sd, - aarch64_get_FP_float (cpu, sa)
|
|
+ (- aarch64_get_FP_float (cpu, sn))
|
|
* aarch64_get_FP_float (cpu, sm));
|
|
}
|
|
|
|
/* Double negative multiply add. */
|
|
static void
|
|
fnmaddd (sim_cpu *cpu)
|
|
{
|
|
unsigned sa = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, sd, - aarch64_get_FP_double (cpu, sa)
|
|
+ (- aarch64_get_FP_double (cpu, sn))
|
|
* aarch64_get_FP_double (cpu, sm));
|
|
}
|
|
|
|
/* Float negative multiply subtract. */
|
|
static void
|
|
fnmsubs (sim_cpu *cpu)
|
|
{
|
|
unsigned sa = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, sd, - aarch64_get_FP_float (cpu, sa)
|
|
+ aarch64_get_FP_float (cpu, sn)
|
|
* aarch64_get_FP_float (cpu, sm));
|
|
}
|
|
|
|
/* Double negative multiply subtract. */
|
|
static void
|
|
fnmsubd (sim_cpu *cpu)
|
|
{
|
|
unsigned sa = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, sd, - aarch64_get_FP_double (cpu, sa)
|
|
+ aarch64_get_FP_double (cpu, sn)
|
|
* aarch64_get_FP_double (cpu, sm));
|
|
}
|
|
|
|
static void
|
|
dexSimpleFPDataProc3Source (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] ==> M : 0 ==> OK, 1 ==> UNALLOC
|
|
instr[30] = 0
|
|
instr[29] ==> S : 0 ==> OK, 1 ==> UNALLOC
|
|
instr[28,25] = 1111
|
|
instr[24] = 1
|
|
instr[23,22] ==> type : 0 ==> single, 01 ==> double, 1x ==> UNALLOC
|
|
instr[21] ==> o1 : 0 ==> unnegated, 1 ==> negated
|
|
instr[15] ==> o2 : 0 ==> ADD, 1 ==> SUB */
|
|
|
|
uint32_t M_S = (uimm (aarch64_get_instr (cpu), 31, 31) << 1)
|
|
| uimm (aarch64_get_instr (cpu), 29, 29);
|
|
/* dispatch on combined type:o1:o2. */
|
|
uint32_t dispatch = (uimm (aarch64_get_instr (cpu), 23, 21) << 1)
|
|
| uimm (aarch64_get_instr (cpu), 15, 15);
|
|
|
|
if (M_S != 0)
|
|
HALT_UNALLOC;
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 0: fmadds (cpu); return;
|
|
case 1: fmsubs (cpu); return;
|
|
case 2: fnmadds (cpu); return;
|
|
case 3: fnmsubs (cpu); return;
|
|
case 4: fmaddd (cpu); return;
|
|
case 5: fmsubd (cpu); return;
|
|
case 6: fnmaddd (cpu); return;
|
|
case 7: fnmsubd (cpu); return;
|
|
default:
|
|
/* type > 1 is currently unallocated. */
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexSimpleFPFixedConvert (sim_cpu *cpu)
|
|
{
|
|
HALT_NYI;
|
|
}
|
|
|
|
static void
|
|
dexSimpleFPCondCompare (sim_cpu *cpu)
|
|
{
|
|
HALT_NYI;
|
|
}
|
|
|
|
/* 2 sources. */
|
|
|
|
/* Float add. */
|
|
static void
|
|
fadds (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, sd, aarch64_get_FP_float (cpu, sn)
|
|
+ aarch64_get_FP_float (cpu, sm));
|
|
}
|
|
|
|
/* Double add. */
|
|
static void
|
|
faddd (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, sd, aarch64_get_FP_double (cpu, sn)
|
|
+ aarch64_get_FP_double (cpu, sm));
|
|
}
|
|
|
|
/* Float divide. */
|
|
static void
|
|
fdivs (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, sd, aarch64_get_FP_float (cpu, sn)
|
|
/ aarch64_get_FP_float (cpu, sm));
|
|
}
|
|
|
|
/* Double divide. */
|
|
static void
|
|
fdivd (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, sd, aarch64_get_FP_double (cpu, sn)
|
|
/ aarch64_get_FP_double (cpu, sm));
|
|
}
|
|
|
|
/* Float multiply. */
|
|
static void
|
|
fmuls (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, sd, aarch64_get_FP_float (cpu, sn)
|
|
* aarch64_get_FP_float (cpu, sm));
|
|
}
|
|
|
|
/* Double multiply. */
|
|
static void
|
|
fmuld (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, sd, aarch64_get_FP_double (cpu, sn)
|
|
* aarch64_get_FP_double (cpu, sm));
|
|
}
|
|
|
|
/* Float negate and multiply. */
|
|
static void
|
|
fnmuls (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, sd, - (aarch64_get_FP_float (cpu, sn)
|
|
* aarch64_get_FP_float (cpu, sm)));
|
|
}
|
|
|
|
/* Double negate and multiply. */
|
|
static void
|
|
fnmuld (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, sd, - (aarch64_get_FP_double (cpu, sn)
|
|
* aarch64_get_FP_double (cpu, sm)));
|
|
}
|
|
|
|
/* Float subtract. */
|
|
static void
|
|
fsubs (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, sd, aarch64_get_FP_float (cpu, sn)
|
|
- aarch64_get_FP_float (cpu, sm));
|
|
}
|
|
|
|
/* Double subtract. */
|
|
static void
|
|
fsubd (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, sd, aarch64_get_FP_double (cpu, sn)
|
|
- aarch64_get_FP_double (cpu, sm));
|
|
}
|
|
|
|
static void
|
|
do_FMINNM (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,23] = 0 0011 1100
|
|
instr[22] = float(0)/double(1)
|
|
instr[21] = 1
|
|
instr[20,16] = Sm
|
|
instr[15,10] = 01 1110
|
|
instr[9,5] = Sn
|
|
instr[4,0] = Cpu */
|
|
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (31, 23, 0x03C);
|
|
NYI_assert (15, 10, 0x1E);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
aarch64_set_FP_double (cpu, sd,
|
|
dminnm (aarch64_get_FP_double (cpu, sn),
|
|
aarch64_get_FP_double (cpu, sm)));
|
|
else
|
|
aarch64_set_FP_float (cpu, sd,
|
|
fminnm (aarch64_get_FP_float (cpu, sn),
|
|
aarch64_get_FP_float (cpu, sm)));
|
|
}
|
|
|
|
static void
|
|
do_FMAXNM (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,23] = 0 0011 1100
|
|
instr[22] = float(0)/double(1)
|
|
instr[21] = 1
|
|
instr[20,16] = Sm
|
|
instr[15,10] = 01 1010
|
|
instr[9,5] = Sn
|
|
instr[4,0] = Cpu */
|
|
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (31, 23, 0x03C);
|
|
NYI_assert (15, 10, 0x1A);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
aarch64_set_FP_double (cpu, sd,
|
|
dmaxnm (aarch64_get_FP_double (cpu, sn),
|
|
aarch64_get_FP_double (cpu, sm)));
|
|
else
|
|
aarch64_set_FP_float (cpu, sd,
|
|
fmaxnm (aarch64_get_FP_float (cpu, sn),
|
|
aarch64_get_FP_float (cpu, sm)));
|
|
}
|
|
|
|
static void
|
|
dexSimpleFPDataProc2Source (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] ==> M : 0 ==> OK, 1 ==> UNALLOC
|
|
instr[30] = 0
|
|
instr[29] ==> S : 0 ==> OK, 1 ==> UNALLOC
|
|
instr[28,25] = 1111
|
|
instr[24] = 0
|
|
instr[23,22] ==> type : 0 ==> single, 01 ==> double, 1x ==> UNALLOC
|
|
instr[21] = 1
|
|
instr[20,16] = Vm
|
|
instr[15,12] ==> opcode : 0000 ==> FMUL, 0001 ==> FDIV
|
|
0010 ==> FADD, 0011 ==> FSUB,
|
|
0100 ==> FMAX, 0101 ==> FMIN
|
|
0110 ==> FMAXNM, 0111 ==> FMINNM
|
|
1000 ==> FNMUL, ow ==> UNALLOC
|
|
instr[11,10] = 10
|
|
instr[9,5] = Vn
|
|
instr[4,0] = Vd */
|
|
|
|
uint32_t M_S = (uimm (aarch64_get_instr (cpu), 31, 31) << 1)
|
|
| uimm (aarch64_get_instr (cpu), 29, 29);
|
|
uint32_t type = uimm (aarch64_get_instr (cpu), 23, 22);
|
|
/* Dispatch on opcode. */
|
|
uint32_t dispatch = uimm (aarch64_get_instr (cpu), 15, 12);
|
|
|
|
if (type > 1)
|
|
HALT_UNALLOC;
|
|
|
|
if (M_S != 0)
|
|
HALT_UNALLOC;
|
|
|
|
if (type)
|
|
switch (dispatch)
|
|
{
|
|
case 0: fmuld (cpu); return;
|
|
case 1: fdivd (cpu); return;
|
|
case 2: faddd (cpu); return;
|
|
case 3: fsubd (cpu); return;
|
|
case 6: do_FMAXNM (cpu); return;
|
|
case 7: do_FMINNM (cpu); return;
|
|
case 8: fnmuld (cpu); return;
|
|
|
|
/* Have not yet implemented fmax and fmin. */
|
|
case 4:
|
|
case 5:
|
|
HALT_NYI;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
else /* type == 0 => floats. */
|
|
switch (dispatch)
|
|
{
|
|
case 0: fmuls (cpu); return;
|
|
case 1: fdivs (cpu); return;
|
|
case 2: fadds (cpu); return;
|
|
case 3: fsubs (cpu); return;
|
|
case 6: do_FMAXNM (cpu); return;
|
|
case 7: do_FMINNM (cpu); return;
|
|
case 8: fnmuls (cpu); return;
|
|
|
|
case 4:
|
|
case 5:
|
|
HALT_NYI;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexSimpleFPCondSelect (sim_cpu *cpu)
|
|
{
|
|
/* FCSEL
|
|
instr[31,23] = 0 0011 1100
|
|
instr[22] = 0=>single 1=>double
|
|
instr[21] = 1
|
|
instr[20,16] = Sm
|
|
instr[15,12] = cond
|
|
instr[11,10] = 11
|
|
instr[9,5] = Sn
|
|
instr[4,0] = Cpu */
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint32_t set = testConditionCode (cpu, uimm (aarch64_get_instr (cpu), 15, 12));
|
|
|
|
NYI_assert (31, 23, 0x03C);
|
|
NYI_assert (11, 10, 0x3);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
aarch64_set_FP_double (cpu, sd, set ? sn : sm);
|
|
else
|
|
aarch64_set_FP_float (cpu, sd, set ? sn : sm);
|
|
}
|
|
|
|
/* Store 32 bit unscaled signed 9 bit. */
|
|
static void
|
|
fsturs (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_mem_float (cpu, aarch64_get_reg_u64 (cpu, st, 1) + offset,
|
|
aarch64_get_FP_float (cpu, rn));
|
|
}
|
|
|
|
/* Store 64 bit unscaled signed 9 bit. */
|
|
static void
|
|
fsturd (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_mem_double (cpu, aarch64_get_reg_u64 (cpu, st, 1) + offset,
|
|
aarch64_get_FP_double (cpu, rn));
|
|
}
|
|
|
|
/* Store 128 bit unscaled signed 9 bit. */
|
|
static void
|
|
fsturq (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
FRegister a;
|
|
|
|
aarch64_get_FP_long_double (cpu, rn, & a);
|
|
aarch64_set_mem_long_double (cpu,
|
|
aarch64_get_reg_u64 (cpu, st, 1)
|
|
+ offset, a);
|
|
}
|
|
|
|
/* TODO FP move register. */
|
|
|
|
/* 32 bit fp to fp move register. */
|
|
static void
|
|
ffmovs (sim_cpu *cpu)
|
|
{
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, st, aarch64_get_FP_float (cpu, rn));
|
|
}
|
|
|
|
/* 64 bit fp to fp move register. */
|
|
static void
|
|
ffmovd (sim_cpu *cpu)
|
|
{
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, st, aarch64_get_FP_double (cpu, rn));
|
|
}
|
|
|
|
/* 32 bit GReg to Vec move register. */
|
|
static void
|
|
fgmovs (sim_cpu *cpu)
|
|
{
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_vec_u32 (cpu, st, 0, aarch64_get_reg_u32 (cpu, rn, NO_SP));
|
|
}
|
|
|
|
/* 64 bit g to fp move register. */
|
|
static void
|
|
fgmovd (sim_cpu *cpu)
|
|
{
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_vec_u64 (cpu, st, 0, aarch64_get_reg_u64 (cpu, rn, NO_SP));
|
|
}
|
|
|
|
/* 32 bit fp to g move register. */
|
|
static void
|
|
gfmovs (sim_cpu *cpu)
|
|
{
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, st, NO_SP, aarch64_get_vec_u32 (cpu, rn, 0));
|
|
}
|
|
|
|
/* 64 bit fp to g move register. */
|
|
static void
|
|
gfmovd (sim_cpu *cpu)
|
|
{
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, st, NO_SP, aarch64_get_vec_u64 (cpu, rn, 0));
|
|
}
|
|
|
|
/* FP move immediate
|
|
|
|
These install an immediate 8 bit value in the target register
|
|
where the 8 bits comprise 1 sign bit, 4 bits of fraction and a 3
|
|
bit exponent. */
|
|
|
|
static void
|
|
fmovs (sim_cpu *cpu)
|
|
{
|
|
unsigned int sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint32_t imm = uimm (aarch64_get_instr (cpu), 20, 13);
|
|
float f = fp_immediate_for_encoding_32 (imm);
|
|
|
|
aarch64_set_FP_float (cpu, sd, f);
|
|
}
|
|
|
|
static void
|
|
fmovd (sim_cpu *cpu)
|
|
{
|
|
unsigned int sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint32_t imm = uimm (aarch64_get_instr (cpu), 20, 13);
|
|
double d = fp_immediate_for_encoding_64 (imm);
|
|
|
|
aarch64_set_FP_double (cpu, sd, d);
|
|
}
|
|
|
|
static void
|
|
dexSimpleFPImmediate (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,23] == 00111100
|
|
instr[22] == type : single(0)/double(1)
|
|
instr[21] == 1
|
|
instr[20,13] == imm8
|
|
instr[12,10] == 100
|
|
instr[9,5] == imm5 : 00000 ==> PK, ow ==> UNALLOC
|
|
instr[4,0] == Rd */
|
|
uint32_t imm5 = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
|
|
NYI_assert (31, 23, 0x3C);
|
|
|
|
if (imm5 != 0)
|
|
HALT_UNALLOC;
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
fmovd (cpu);
|
|
else
|
|
fmovs (cpu);
|
|
}
|
|
|
|
/* TODO specific decode and execute for group Load Store. */
|
|
|
|
/* TODO FP load/store single register (unscaled offset). */
|
|
|
|
/* TODO load 8 bit unscaled signed 9 bit. */
|
|
/* TODO load 16 bit unscaled signed 9 bit. */
|
|
|
|
/* Load 32 bit unscaled signed 9 bit. */
|
|
static void
|
|
fldurs (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, st, aarch64_get_mem_float
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK) + offset));
|
|
}
|
|
|
|
/* Load 64 bit unscaled signed 9 bit. */
|
|
static void
|
|
fldurd (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, st, aarch64_get_mem_double
|
|
(cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK) + offset));
|
|
}
|
|
|
|
/* Load 128 bit unscaled signed 9 bit. */
|
|
static void
|
|
fldurq (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
FRegister a;
|
|
uint64_t addr = aarch64_get_reg_u64 (cpu, rn, SP_OK) + offset;
|
|
|
|
aarch64_get_mem_long_double (cpu, addr, & a);
|
|
aarch64_set_FP_long_double (cpu, st, a);
|
|
}
|
|
|
|
/* TODO store 8 bit unscaled signed 9 bit. */
|
|
/* TODO store 16 bit unscaled signed 9 bit. */
|
|
|
|
|
|
/* 1 source. */
|
|
|
|
/* Float absolute value. */
|
|
static void
|
|
fabss (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
float value = aarch64_get_FP_float (cpu, sn);
|
|
|
|
aarch64_set_FP_float (cpu, sd, fabsf (value));
|
|
}
|
|
|
|
/* Double absolute value. */
|
|
static void
|
|
fabcpu (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
double value = aarch64_get_FP_double (cpu, sn);
|
|
|
|
aarch64_set_FP_double (cpu, sd, fabs (value));
|
|
}
|
|
|
|
/* Float negative value. */
|
|
static void
|
|
fnegs (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, sd, - aarch64_get_FP_float (cpu, sn));
|
|
}
|
|
|
|
/* Double negative value. */
|
|
static void
|
|
fnegd (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, sd, - aarch64_get_FP_double (cpu, sn));
|
|
}
|
|
|
|
/* Float square root. */
|
|
static void
|
|
fsqrts (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, sd, sqrt (aarch64_get_FP_float (cpu, sn)));
|
|
}
|
|
|
|
/* Double square root. */
|
|
static void
|
|
fsqrtd (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, sd,
|
|
sqrt (aarch64_get_FP_double (cpu, sn)));
|
|
}
|
|
|
|
/* Convert double to float. */
|
|
static void
|
|
fcvtds (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float (cpu, sd, (float) aarch64_get_FP_double (cpu, sn));
|
|
}
|
|
|
|
/* Convert float to double. */
|
|
static void
|
|
fcvtcpu (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double (cpu, sd, (double) aarch64_get_FP_float (cpu, sn));
|
|
}
|
|
|
|
static void
|
|
do_FRINT (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,23] = 0001 1110 0
|
|
instr[22] = single(0)/double(1)
|
|
instr[21,18] = 1001
|
|
instr[17,15] = rounding mode
|
|
instr[14,10] = 10000
|
|
instr[9,5] = source
|
|
instr[4,0] = dest */
|
|
|
|
float val;
|
|
unsigned rs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned int rmode = uimm (aarch64_get_instr (cpu), 17, 15);
|
|
|
|
NYI_assert (31, 23, 0x03C);
|
|
NYI_assert (21, 18, 0x9);
|
|
NYI_assert (14, 10, 0x10);
|
|
|
|
if (rmode == 6 || rmode == 7)
|
|
/* FIXME: Add support for rmode == 6 exactness check. */
|
|
rmode = uimm (aarch64_get_FPSR (cpu), 23, 22);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
double val = aarch64_get_FP_double (cpu, rs);
|
|
|
|
switch (rmode)
|
|
{
|
|
case 0: /* mode N: nearest or even. */
|
|
{
|
|
double rval = round (val);
|
|
|
|
if (val - rval == 0.5)
|
|
{
|
|
if (((rval / 2.0) * 2.0) != rval)
|
|
rval += 1.0;
|
|
}
|
|
|
|
aarch64_set_FP_double (cpu, rd, round (val));
|
|
return;
|
|
}
|
|
|
|
case 1: /* mode P: towards +inf. */
|
|
if (val < 0.0)
|
|
aarch64_set_FP_double (cpu, rd, trunc (val));
|
|
else
|
|
aarch64_set_FP_double (cpu, rd, round (val));
|
|
return;
|
|
|
|
case 2: /* mode M: towards -inf. */
|
|
if (val < 0.0)
|
|
aarch64_set_FP_double (cpu, rd, round (val));
|
|
else
|
|
aarch64_set_FP_double (cpu, rd, trunc (val));
|
|
return;
|
|
|
|
case 3: /* mode Z: towards 0. */
|
|
aarch64_set_FP_double (cpu, rd, trunc (val));
|
|
return;
|
|
|
|
case 4: /* mode A: away from 0. */
|
|
aarch64_set_FP_double (cpu, rd, round (val));
|
|
return;
|
|
|
|
case 6: /* mode X: use FPCR with exactness check. */
|
|
case 7: /* mode I: use FPCR mode. */
|
|
HALT_NYI;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
val = aarch64_get_FP_float (cpu, rs);
|
|
|
|
switch (rmode)
|
|
{
|
|
case 0: /* mode N: nearest or even. */
|
|
{
|
|
float rval = roundf (val);
|
|
|
|
if (val - rval == 0.5)
|
|
{
|
|
if (((rval / 2.0) * 2.0) != rval)
|
|
rval += 1.0;
|
|
}
|
|
|
|
aarch64_set_FP_float (cpu, rd, rval);
|
|
return;
|
|
}
|
|
|
|
case 1: /* mode P: towards +inf. */
|
|
if (val < 0.0)
|
|
aarch64_set_FP_float (cpu, rd, truncf (val));
|
|
else
|
|
aarch64_set_FP_float (cpu, rd, roundf (val));
|
|
return;
|
|
|
|
case 2: /* mode M: towards -inf. */
|
|
if (val < 0.0)
|
|
aarch64_set_FP_float (cpu, rd, truncf (val));
|
|
else
|
|
aarch64_set_FP_float (cpu, rd, roundf (val));
|
|
return;
|
|
|
|
case 3: /* mode Z: towards 0. */
|
|
aarch64_set_FP_float (cpu, rd, truncf (val));
|
|
return;
|
|
|
|
case 4: /* mode A: away from 0. */
|
|
aarch64_set_FP_float (cpu, rd, roundf (val));
|
|
return;
|
|
|
|
case 6: /* mode X: use FPCR with exactness check. */
|
|
case 7: /* mode I: use FPCR mode. */
|
|
HALT_NYI;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexSimpleFPDataProc1Source (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] ==> M : 0 ==> OK, 1 ==> UNALLOC
|
|
instr[30] = 0
|
|
instr[29] ==> S : 0 ==> OK, 1 ==> UNALLOC
|
|
instr[28,25] = 1111
|
|
instr[24] = 0
|
|
instr[23,22] ==> type : 00 ==> source is single,
|
|
01 ==> source is double
|
|
10 ==> UNALLOC
|
|
11 ==> UNALLOC or source is half
|
|
instr[21] = 1
|
|
instr[20,15] ==> opcode : with type 00 or 01
|
|
000000 ==> FMOV, 000001 ==> FABS,
|
|
000010 ==> FNEG, 000011 ==> FSQRT,
|
|
000100 ==> UNALLOC, 000101 ==> FCVT,(to single/double)
|
|
000110 ==> UNALLOC, 000111 ==> FCVT (to half)
|
|
001000 ==> FRINTN, 001001 ==> FRINTP,
|
|
001010 ==> FRINTM, 001011 ==> FRINTZ,
|
|
001100 ==> FRINTA, 001101 ==> UNALLOC
|
|
001110 ==> FRINTX, 001111 ==> FRINTI
|
|
with type 11
|
|
000100 ==> FCVT (half-to-single)
|
|
000101 ==> FCVT (half-to-double)
|
|
instr[14,10] = 10000. */
|
|
|
|
uint32_t M_S = (uimm (aarch64_get_instr (cpu), 31, 31) << 1)
|
|
| uimm (aarch64_get_instr (cpu), 29, 29);
|
|
uint32_t type = uimm (aarch64_get_instr (cpu), 23, 22);
|
|
uint32_t opcode = uimm (aarch64_get_instr (cpu), 20, 15);
|
|
|
|
if (M_S != 0)
|
|
HALT_UNALLOC;
|
|
|
|
if (type == 3)
|
|
{
|
|
if (opcode == 4 || opcode == 5)
|
|
HALT_NYI;
|
|
else
|
|
HALT_UNALLOC;
|
|
}
|
|
|
|
if (type == 2)
|
|
HALT_UNALLOC;
|
|
|
|
switch (opcode)
|
|
{
|
|
case 0:
|
|
if (type)
|
|
ffmovd (cpu);
|
|
else
|
|
ffmovs (cpu);
|
|
return;
|
|
|
|
case 1:
|
|
if (type)
|
|
fabcpu (cpu);
|
|
else
|
|
fabss (cpu);
|
|
return;
|
|
|
|
case 2:
|
|
if (type)
|
|
fnegd (cpu);
|
|
else
|
|
fnegs (cpu);
|
|
return;
|
|
|
|
case 3:
|
|
if (type)
|
|
fsqrtd (cpu);
|
|
else
|
|
fsqrts (cpu);
|
|
return;
|
|
|
|
case 4:
|
|
if (type)
|
|
fcvtds (cpu);
|
|
else
|
|
HALT_UNALLOC;
|
|
return;
|
|
|
|
case 5:
|
|
if (type)
|
|
HALT_UNALLOC;
|
|
fcvtcpu (cpu);
|
|
return;
|
|
|
|
case 8: /* FRINTN etc. */
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
case 14:
|
|
case 15:
|
|
do_FRINT (cpu);
|
|
return;
|
|
|
|
case 7: /* FCVT double/single to half precision. */
|
|
case 13:
|
|
HALT_NYI;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
/* 32 bit signed int to float. */
|
|
static void
|
|
scvtf32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float
|
|
(cpu, sd, (float) aarch64_get_reg_s32 (cpu, rn, NO_SP));
|
|
}
|
|
|
|
/* signed int to float. */
|
|
static void
|
|
scvtf (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_float
|
|
(cpu, sd, (float) aarch64_get_reg_s64 (cpu, rn, NO_SP));
|
|
}
|
|
|
|
/* 32 bit signed int to double. */
|
|
static void
|
|
scvtd32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double
|
|
(cpu, sd, (double) aarch64_get_reg_s32 (cpu, rn, NO_SP));
|
|
}
|
|
|
|
/* signed int to double. */
|
|
static void
|
|
scvtd (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned sd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_FP_double
|
|
(cpu, sd, (double) aarch64_get_reg_s64 (cpu, rn, NO_SP));
|
|
}
|
|
|
|
static const float FLOAT_INT_MAX = (float) INT_MAX;
|
|
static const float FLOAT_INT_MIN = (float) INT_MIN;
|
|
static const double DOUBLE_INT_MAX = (double) INT_MAX;
|
|
static const double DOUBLE_INT_MIN = (double) INT_MIN;
|
|
static const float FLOAT_LONG_MAX = (float) LONG_MAX;
|
|
static const float FLOAT_LONG_MIN = (float) LONG_MIN;
|
|
static const double DOUBLE_LONG_MAX = (double) LONG_MAX;
|
|
static const double DOUBLE_LONG_MIN = (double) LONG_MIN;
|
|
|
|
/* Check for FP exception conditions:
|
|
NaN raises IO
|
|
Infinity raises IO
|
|
Out of Range raises IO and IX and saturates value
|
|
Denormal raises ID and IX and sets to zero. */
|
|
#define RAISE_EXCEPTIONS(F, VALUE, FTYPE, ITYPE) \
|
|
do \
|
|
{ \
|
|
switch (fpclassify (F)) \
|
|
{ \
|
|
case FP_INFINITE: \
|
|
case FP_NAN: \
|
|
aarch64_set_FPSR (cpu, IO); \
|
|
if (signbit (F)) \
|
|
VALUE = ITYPE##_MAX; \
|
|
else \
|
|
VALUE = ITYPE##_MIN; \
|
|
break; \
|
|
\
|
|
case FP_NORMAL: \
|
|
if (F >= FTYPE##_##ITYPE##_MAX) \
|
|
{ \
|
|
aarch64_set_FPSR_bits (cpu, IO | IX, IO | IX); \
|
|
VALUE = ITYPE##_MAX; \
|
|
} \
|
|
else if (F <= FTYPE##_##ITYPE##_MIN) \
|
|
{ \
|
|
aarch64_set_FPSR_bits (cpu, IO | IX, IO | IX); \
|
|
VALUE = ITYPE##_MIN; \
|
|
} \
|
|
break; \
|
|
\
|
|
case FP_SUBNORMAL: \
|
|
aarch64_set_FPSR_bits (cpu, IO | IX | ID, IX | ID); \
|
|
VALUE = 0; \
|
|
break; \
|
|
\
|
|
default: \
|
|
case FP_ZERO: \
|
|
VALUE = 0; \
|
|
break; \
|
|
} \
|
|
} \
|
|
while (0)
|
|
|
|
/* 32 bit convert float to signed int truncate towards zero. */
|
|
static void
|
|
fcvtszs32 (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* TODO : check that this rounds toward zero. */
|
|
float f = aarch64_get_FP_float (cpu, sn);
|
|
int32_t value = (int32_t) f;
|
|
|
|
RAISE_EXCEPTIONS (f, value, FLOAT, INT);
|
|
|
|
/* Avoid sign extension to 64 bit. */
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, (uint32_t) value);
|
|
}
|
|
|
|
/* 64 bit convert float to signed int truncate towards zero. */
|
|
static void
|
|
fcvtszs (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
float f = aarch64_get_FP_float (cpu, sn);
|
|
int64_t value = (int64_t) f;
|
|
|
|
RAISE_EXCEPTIONS (f, value, FLOAT, LONG);
|
|
|
|
aarch64_set_reg_s64 (cpu, rd, NO_SP, value);
|
|
}
|
|
|
|
/* 32 bit convert double to signed int truncate towards zero. */
|
|
static void
|
|
fcvtszd32 (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* TODO : check that this rounds toward zero. */
|
|
double d = aarch64_get_FP_double (cpu, sn);
|
|
int32_t value = (int32_t) d;
|
|
|
|
RAISE_EXCEPTIONS (d, value, DOUBLE, INT);
|
|
|
|
/* Avoid sign extension to 64 bit. */
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, (uint32_t) value);
|
|
}
|
|
|
|
/* 64 bit convert double to signed int truncate towards zero. */
|
|
static void
|
|
fcvtszd (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* TODO : check that this rounds toward zero. */
|
|
double d = aarch64_get_FP_double (cpu, sn);
|
|
int64_t value;
|
|
|
|
value = (int64_t) d;
|
|
|
|
RAISE_EXCEPTIONS (d, value, DOUBLE, LONG);
|
|
|
|
aarch64_set_reg_s64 (cpu, rd, NO_SP, value);
|
|
}
|
|
|
|
static void
|
|
do_fcvtzu (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = size: 32-bit (0), 64-bit (1)
|
|
instr[30,23] = 00111100
|
|
instr[22] = type: single (0)/ double (1)
|
|
instr[21] = enable (0)/disable(1) precision
|
|
instr[20,16] = 11001
|
|
instr[15,10] = precision
|
|
instr[9,5] = Rs
|
|
instr[4,0] = Rd. */
|
|
|
|
unsigned rs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (30, 23, 0x3C);
|
|
NYI_assert (20, 16, 0x19);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 21, 21) != 1)
|
|
/* Convert to fixed point. */
|
|
HALT_NYI;
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 31, 31))
|
|
{
|
|
/* Convert to unsigned 64-bit integer. */
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
double d = aarch64_get_FP_double (cpu, rs);
|
|
uint64_t value = (uint64_t) d;
|
|
|
|
/* Do not raise an exception if we have reached ULONG_MAX. */
|
|
if (value != (1UL << 63))
|
|
RAISE_EXCEPTIONS (d, value, DOUBLE, LONG);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value);
|
|
}
|
|
else
|
|
{
|
|
float f = aarch64_get_FP_float (cpu, rs);
|
|
uint64_t value = (uint64_t) f;
|
|
|
|
/* Do not raise an exception if we have reached ULONG_MAX. */
|
|
if (value != (1UL << 63))
|
|
RAISE_EXCEPTIONS (f, value, FLOAT, LONG);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint32_t value;
|
|
|
|
/* Convert to unsigned 32-bit integer. */
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
double d = aarch64_get_FP_double (cpu, rs);
|
|
|
|
value = (uint32_t) d;
|
|
/* Do not raise an exception if we have reached UINT_MAX. */
|
|
if (value != (1UL << 31))
|
|
RAISE_EXCEPTIONS (d, value, DOUBLE, INT);
|
|
}
|
|
else
|
|
{
|
|
float f = aarch64_get_FP_float (cpu, rs);
|
|
|
|
value = (uint32_t) f;
|
|
/* Do not raise an exception if we have reached UINT_MAX. */
|
|
if (value != (1UL << 31))
|
|
RAISE_EXCEPTIONS (f, value, FLOAT, INT);
|
|
}
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_UCVTF (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = size: 32-bit (0), 64-bit (1)
|
|
instr[30,23] = 001 1110 0
|
|
instr[22] = type: single (0)/ double (1)
|
|
instr[21] = enable (0)/disable(1) precision
|
|
instr[20,16] = 0 0011
|
|
instr[15,10] = precision
|
|
instr[9,5] = Rs
|
|
instr[4,0] = Rd. */
|
|
|
|
unsigned rs = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (30, 23, 0x3C);
|
|
NYI_assert (20, 16, 0x03);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 21, 21) != 1)
|
|
HALT_NYI;
|
|
|
|
/* FIXME: Add exception raising. */
|
|
if (uimm (aarch64_get_instr (cpu), 31, 31))
|
|
{
|
|
uint64_t value = aarch64_get_reg_u64 (cpu, rs, NO_SP);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
aarch64_set_FP_double (cpu, rd, (double) value);
|
|
else
|
|
aarch64_set_FP_float (cpu, rd, (float) value);
|
|
}
|
|
else
|
|
{
|
|
uint32_t value = aarch64_get_reg_u32 (cpu, rs, NO_SP);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
aarch64_set_FP_double (cpu, rd, (double) value);
|
|
else
|
|
aarch64_set_FP_float (cpu, rd, (float) value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
float_vector_move (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,17] == 100 1111 0101 0111
|
|
instr[16] ==> direction 0=> to GR, 1=> from GR
|
|
instr[15,10] => ???
|
|
instr[9,5] ==> source
|
|
instr[4,0] ==> dest. */
|
|
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (31, 17, 0x4F57);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 15, 10) != 0)
|
|
HALT_UNALLOC;
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 16, 16))
|
|
aarch64_set_vec_u64 (cpu, rd, 1, aarch64_get_reg_u64 (cpu, rn, NO_SP));
|
|
else
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, aarch64_get_vec_u64 (cpu, rn, 1));
|
|
}
|
|
|
|
static void
|
|
dexSimpleFPIntegerConvert (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[30 = 0
|
|
instr[29] = S : 0 ==> OK, 1 ==> UNALLOC
|
|
instr[28,25] = 1111
|
|
instr[24] = 0
|
|
instr[23,22] = type : 00 ==> single, 01 ==> double, 1x ==> UNALLOC
|
|
instr[21] = 1
|
|
instr[20,19] = rmode
|
|
instr[18,16] = opcode
|
|
instr[15,10] = 10 0000 */
|
|
|
|
uint32_t rmode_opcode;
|
|
uint32_t size_type;
|
|
uint32_t type;
|
|
uint32_t size;
|
|
uint32_t S;
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 31, 17) == 0x4F57)
|
|
{
|
|
float_vector_move (cpu);
|
|
return;
|
|
}
|
|
|
|
size = uimm (aarch64_get_instr (cpu), 31, 31);
|
|
S = uimm (aarch64_get_instr (cpu), 29, 29);
|
|
if (S != 0)
|
|
HALT_UNALLOC;
|
|
|
|
type = uimm (aarch64_get_instr (cpu), 23, 22);
|
|
if (type > 1)
|
|
HALT_UNALLOC;
|
|
|
|
rmode_opcode = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
size_type = (size << 1) | type; /* 0==32f, 1==32d, 2==64f, 3==64d. */
|
|
|
|
switch (rmode_opcode)
|
|
{
|
|
case 2: /* SCVTF. */
|
|
switch (size_type)
|
|
{
|
|
case 0: scvtf32 (cpu); return;
|
|
case 1: scvtd32 (cpu); return;
|
|
case 2: scvtf (cpu); return;
|
|
case 3: scvtd (cpu); return;
|
|
default:
|
|
HALT_UNREACHABLE;
|
|
}
|
|
|
|
case 6: /* FMOV GR, Vec. */
|
|
switch (size_type)
|
|
{
|
|
case 0: gfmovs (cpu); return;
|
|
case 3: gfmovd (cpu); return;
|
|
default: HALT_UNALLOC;
|
|
}
|
|
|
|
case 7: /* FMOV vec, GR. */
|
|
switch (size_type)
|
|
{
|
|
case 0: fgmovs (cpu); return;
|
|
case 3: fgmovd (cpu); return;
|
|
default: HALT_UNALLOC;
|
|
}
|
|
|
|
case 24: /* FCVTZS. */
|
|
switch (size_type)
|
|
{
|
|
case 0: fcvtszs32 (cpu); return;
|
|
case 1: fcvtszd32 (cpu); return;
|
|
case 2: fcvtszs (cpu); return;
|
|
case 3: fcvtszd (cpu); return;
|
|
default: HALT_UNREACHABLE;
|
|
}
|
|
|
|
case 25: do_fcvtzu (cpu); return;
|
|
case 3: do_UCVTF (cpu); return;
|
|
|
|
case 0: /* FCVTNS. */
|
|
case 1: /* FCVTNU. */
|
|
case 4: /* FCVTAS. */
|
|
case 5: /* FCVTAU. */
|
|
case 8: /* FCVPTS. */
|
|
case 9: /* FCVTPU. */
|
|
case 16: /* FCVTMS. */
|
|
case 17: /* FCVTMU. */
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_flags_for_float_compare (sim_cpu *cpu, float fvalue1, float fvalue2)
|
|
{
|
|
uint32_t flags;
|
|
|
|
if (isnan (fvalue1) || isnan (fvalue2))
|
|
flags = C|V;
|
|
else
|
|
{
|
|
float result = fvalue1 - fvalue2;
|
|
|
|
if (result == 0.0)
|
|
flags = Z|C;
|
|
else if (result < 0)
|
|
flags = N;
|
|
else /* (result > 0). */
|
|
flags = C;
|
|
}
|
|
|
|
aarch64_set_CPSR (cpu, flags);
|
|
}
|
|
|
|
static void
|
|
fcmps (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
|
|
float fvalue1 = aarch64_get_FP_float (cpu, sn);
|
|
float fvalue2 = aarch64_get_FP_float (cpu, sm);
|
|
|
|
set_flags_for_float_compare (cpu, fvalue1, fvalue2);
|
|
}
|
|
|
|
/* Float compare to zero -- Invalid Operation exception
|
|
only on signaling NaNs. */
|
|
static void
|
|
fcmpzs (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
float fvalue1 = aarch64_get_FP_float (cpu, sn);
|
|
|
|
set_flags_for_float_compare (cpu, fvalue1, 0.0f);
|
|
}
|
|
|
|
/* Float compare -- Invalid Operation exception on all NaNs. */
|
|
static void
|
|
fcmpes (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
|
|
float fvalue1 = aarch64_get_FP_float (cpu, sn);
|
|
float fvalue2 = aarch64_get_FP_float (cpu, sm);
|
|
|
|
set_flags_for_float_compare (cpu, fvalue1, fvalue2);
|
|
}
|
|
|
|
/* Float compare to zero -- Invalid Operation exception on all NaNs. */
|
|
static void
|
|
fcmpzes (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
float fvalue1 = aarch64_get_FP_float (cpu, sn);
|
|
|
|
set_flags_for_float_compare (cpu, fvalue1, 0.0f);
|
|
}
|
|
|
|
static void
|
|
set_flags_for_double_compare (sim_cpu *cpu, double dval1, double dval2)
|
|
{
|
|
uint32_t flags;
|
|
|
|
if (isnan (dval1) || isnan (dval2))
|
|
flags = C|V;
|
|
else
|
|
{
|
|
double result = dval1 - dval2;
|
|
|
|
if (result == 0.0)
|
|
flags = Z|C;
|
|
else if (result < 0)
|
|
flags = N;
|
|
else /* (result > 0). */
|
|
flags = C;
|
|
}
|
|
|
|
aarch64_set_CPSR (cpu, flags);
|
|
}
|
|
|
|
/* Double compare -- Invalid Operation exception only on signaling NaNs. */
|
|
static void
|
|
fcmpd (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
|
|
double dvalue1 = aarch64_get_FP_double (cpu, sn);
|
|
double dvalue2 = aarch64_get_FP_double (cpu, sm);
|
|
|
|
set_flags_for_double_compare (cpu, dvalue1, dvalue2);
|
|
}
|
|
|
|
/* Double compare to zero -- Invalid Operation exception
|
|
only on signaling NaNs. */
|
|
static void
|
|
fcmpzd (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
double dvalue1 = aarch64_get_FP_double (cpu, sn);
|
|
|
|
set_flags_for_double_compare (cpu, dvalue1, 0.0);
|
|
}
|
|
|
|
/* Double compare -- Invalid Operation exception on all NaNs. */
|
|
static void
|
|
fcmped (sim_cpu *cpu)
|
|
{
|
|
unsigned sm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
|
|
double dvalue1 = aarch64_get_FP_double (cpu, sn);
|
|
double dvalue2 = aarch64_get_FP_double (cpu, sm);
|
|
|
|
set_flags_for_double_compare (cpu, dvalue1, dvalue2);
|
|
}
|
|
|
|
/* Double compare to zero -- Invalid Operation exception on all NaNs. */
|
|
static void
|
|
fcmpzed (sim_cpu *cpu)
|
|
{
|
|
unsigned sn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
double dvalue1 = aarch64_get_FP_double (cpu, sn);
|
|
|
|
set_flags_for_double_compare (cpu, dvalue1, 0.0);
|
|
}
|
|
|
|
static void
|
|
dexSimpleFPCompare (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[28,25] == 1111
|
|
instr[30:24:21:13,10] = 0011000
|
|
instr[31] = M : 0 ==> OK, 1 ==> UNALLOC
|
|
instr[29] ==> S : 0 ==> OK, 1 ==> UNALLOC
|
|
instr[23,22] ==> type : 0 ==> single, 01 ==> double, 1x ==> UNALLOC
|
|
instr[15,14] ==> op : 00 ==> OK, ow ==> UNALLOC
|
|
instr[4,0] ==> opcode2 : 00000 ==> FCMP, 10000 ==> FCMPE,
|
|
01000 ==> FCMPZ, 11000 ==> FCMPEZ,
|
|
ow ==> UNALLOC */
|
|
uint32_t dispatch;
|
|
uint32_t M_S = (uimm (aarch64_get_instr (cpu), 31, 31) << 1)
|
|
| uimm (aarch64_get_instr (cpu), 29, 29);
|
|
uint32_t type = uimm (aarch64_get_instr (cpu), 23, 22);
|
|
uint32_t op = uimm (aarch64_get_instr (cpu), 15, 14);
|
|
uint32_t op2_2_0 = uimm (aarch64_get_instr (cpu), 2, 0);
|
|
|
|
if (op2_2_0 != 0)
|
|
HALT_UNALLOC;
|
|
|
|
if (M_S != 0)
|
|
HALT_UNALLOC;
|
|
|
|
if (type > 1)
|
|
HALT_UNALLOC;
|
|
|
|
if (op != 0)
|
|
HALT_UNALLOC;
|
|
|
|
/* dispatch on type and top 2 bits of opcode. */
|
|
dispatch = (type << 2) | uimm (aarch64_get_instr (cpu), 4, 3);
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 0: fcmps (cpu); return;
|
|
case 1: fcmpzs (cpu); return;
|
|
case 2: fcmpes (cpu); return;
|
|
case 3: fcmpzes (cpu); return;
|
|
case 4: fcmpd (cpu); return;
|
|
case 5: fcmpzd (cpu); return;
|
|
case 6: fcmped (cpu); return;
|
|
case 7: fcmpzed (cpu); return;
|
|
default: HALT_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_scalar_FADDP (sim_cpu *cpu)
|
|
{
|
|
/* instr [31,23] = 011111100
|
|
instr [22] = single(0)/double(1)
|
|
instr [21,10] = 1100 0011 0110
|
|
instr [9,5] = Fn
|
|
instr [4,0] = Fd. */
|
|
|
|
unsigned Fn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned Fd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (31, 23, 0x0FC);
|
|
NYI_assert (21, 10, 0xC36);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
double val1 = aarch64_get_vec_double (cpu, Fn, 0);
|
|
double val2 = aarch64_get_vec_double (cpu, Fn, 1);
|
|
|
|
aarch64_set_FP_double (cpu, Fd, val1 + val2);
|
|
}
|
|
else
|
|
{
|
|
float val1 = aarch64_get_vec_float (cpu, Fn, 0);
|
|
float val2 = aarch64_get_vec_float (cpu, Fn, 1);
|
|
|
|
aarch64_set_FP_float (cpu, Fd, val1 + val2);
|
|
}
|
|
}
|
|
|
|
/* Floating point absolute difference. */
|
|
|
|
static void
|
|
do_scalar_FABD (sim_cpu *cpu)
|
|
{
|
|
/* instr [31,23] = 0111 1110 1
|
|
instr [22] = float(0)/double(1)
|
|
instr [21] = 1
|
|
instr [20,16] = Rm
|
|
instr [15,10] = 1101 01
|
|
instr [9, 5] = Rn
|
|
instr [4, 0] = Rd. */
|
|
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (31, 23, 0x0FD);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 10, 0x35);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
aarch64_set_FP_double (cpu, rd,
|
|
fabs (aarch64_get_FP_double (cpu, rn)
|
|
- aarch64_get_FP_double (cpu, rm)));
|
|
else
|
|
aarch64_set_FP_float (cpu, rd,
|
|
fabsf (aarch64_get_FP_float (cpu, rn)
|
|
- aarch64_get_FP_float (cpu, rm)));
|
|
}
|
|
|
|
static void
|
|
do_scalar_CMGT (sim_cpu *cpu)
|
|
{
|
|
/* instr [31,21] = 0101 1110 111
|
|
instr [20,16] = Rm
|
|
instr [15,10] = 00 1101
|
|
instr [9, 5] = Rn
|
|
instr [4, 0] = Rd. */
|
|
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (31, 21, 0x2F7);
|
|
NYI_assert (15, 10, 0x0D);
|
|
|
|
aarch64_set_vec_u64 (cpu, rd, 0,
|
|
aarch64_get_vec_u64 (cpu, rn, 0) >
|
|
aarch64_get_vec_u64 (cpu, rm, 0) ? -1L : 0L);
|
|
}
|
|
|
|
static void
|
|
do_scalar_USHR (sim_cpu *cpu)
|
|
{
|
|
/* instr [31,23] = 0111 1111 0
|
|
instr [22,16] = shift amount
|
|
instr [15,10] = 0000 01
|
|
instr [9, 5] = Rn
|
|
instr [4, 0] = Rd. */
|
|
|
|
unsigned amount = 128 - uimm (aarch64_get_instr (cpu), 22, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (31, 23, 0x0FE);
|
|
NYI_assert (15, 10, 0x01);
|
|
|
|
aarch64_set_vec_u64 (cpu, rd, 0,
|
|
aarch64_get_vec_u64 (cpu, rn, 0) >> amount);
|
|
}
|
|
|
|
static void
|
|
do_scalar_SHL (sim_cpu *cpu)
|
|
{
|
|
/* instr [31,23] = 0111 1101 0
|
|
instr [22,16] = shift amount
|
|
instr [15,10] = 0101 01
|
|
instr [9, 5] = Rn
|
|
instr [4, 0] = Rd. */
|
|
|
|
unsigned amount = uimm (aarch64_get_instr (cpu), 22, 16) - 64;
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (31, 23, 0x0BE);
|
|
NYI_assert (15, 10, 0x15);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22) == 0)
|
|
HALT_UNALLOC;
|
|
|
|
aarch64_set_vec_u64 (cpu, rd, 0,
|
|
aarch64_get_vec_u64 (cpu, rn, 0) << amount);
|
|
}
|
|
|
|
/* FCMEQ FCMGT FCMGE. */
|
|
static void
|
|
do_scalar_FCM (sim_cpu *cpu)
|
|
{
|
|
/* instr [31,30] = 01
|
|
instr [29] = U
|
|
instr [28,24] = 1 1110
|
|
instr [23] = E
|
|
instr [22] = size
|
|
instr [21] = 1
|
|
instr [20,16] = Rm
|
|
instr [15,12] = 1110
|
|
instr [11] = AC
|
|
instr [10] = 1
|
|
instr [9, 5] = Rn
|
|
instr [4, 0] = Rd. */
|
|
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned EUac = (uimm (aarch64_get_instr (cpu), 23, 23) << 2)
|
|
| (uimm (aarch64_get_instr (cpu), 29, 29) << 1)
|
|
| uimm (aarch64_get_instr (cpu), 11, 11);
|
|
unsigned result;
|
|
float val1;
|
|
float val2;
|
|
|
|
NYI_assert (31, 30, 1);
|
|
NYI_assert (28, 24, 0x1E);
|
|
NYI_assert (21, 21, 1);
|
|
NYI_assert (15, 12, 0xE);
|
|
NYI_assert (10, 10, 1);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 22, 22))
|
|
{
|
|
double val1 = aarch64_get_FP_double (cpu, rn);
|
|
double val2 = aarch64_get_FP_double (cpu, rm);
|
|
|
|
switch (EUac)
|
|
{
|
|
case 0: /* 000 */
|
|
result = val1 == val2;
|
|
break;
|
|
|
|
case 3: /* 011 */
|
|
val1 = fabs (val1);
|
|
val2 = fabs (val2);
|
|
/* Fall through. */
|
|
case 2: /* 010 */
|
|
result = val1 >= val2;
|
|
break;
|
|
|
|
case 7: /* 111 */
|
|
val1 = fabs (val1);
|
|
val2 = fabs (val2);
|
|
/* Fall through. */
|
|
case 6: /* 110 */
|
|
result = val1 > val2;
|
|
break;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
|
|
aarch64_set_vec_u32 (cpu, rd, 0, result ? -1 : 0);
|
|
return;
|
|
}
|
|
|
|
val1 = aarch64_get_FP_float (cpu, rn);
|
|
val2 = aarch64_get_FP_float (cpu, rm);
|
|
|
|
switch (EUac)
|
|
{
|
|
case 0: /* 000 */
|
|
result = val1 == val2;
|
|
break;
|
|
|
|
case 3: /* 011 */
|
|
val1 = fabsf (val1);
|
|
val2 = fabsf (val2);
|
|
/* Fall through. */
|
|
case 2: /* 010 */
|
|
result = val1 >= val2;
|
|
break;
|
|
|
|
case 7: /* 111 */
|
|
val1 = fabsf (val1);
|
|
val2 = fabsf (val2);
|
|
/* Fall through. */
|
|
case 6: /* 110 */
|
|
result = val1 > val2;
|
|
break;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
|
|
aarch64_set_vec_u32 (cpu, rd, 0, result ? -1 : 0);
|
|
}
|
|
|
|
/* An alias of DUP. */
|
|
static void
|
|
do_scalar_MOV (sim_cpu *cpu)
|
|
{
|
|
/* instr [31,21] = 0101 1110 000
|
|
instr [20,16] = imm5
|
|
instr [15,10] = 0000 01
|
|
instr [9, 5] = Rn
|
|
instr [4, 0] = Rd. */
|
|
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned index;
|
|
|
|
NYI_assert (31, 21, 0x2F0);
|
|
NYI_assert (15, 10, 0x01);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 16, 16))
|
|
{
|
|
/* 8-bit. */
|
|
index = uimm (aarch64_get_instr (cpu), 20, 17);
|
|
aarch64_set_vec_u8
|
|
(cpu, rd, 0, aarch64_get_vec_u8 (cpu, rn, index));
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 17, 17))
|
|
{
|
|
/* 16-bit. */
|
|
index = uimm (aarch64_get_instr (cpu), 20, 18);
|
|
aarch64_set_vec_u16
|
|
(cpu, rd, 0, aarch64_get_vec_u16 (cpu, rn, index));
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 18, 18))
|
|
{
|
|
/* 32-bit. */
|
|
index = uimm (aarch64_get_instr (cpu), 20, 19);
|
|
aarch64_set_vec_u32
|
|
(cpu, rd, 0, aarch64_get_vec_u32 (cpu, rn, index));
|
|
}
|
|
else if (uimm (aarch64_get_instr (cpu), 19, 19))
|
|
{
|
|
/* 64-bit. */
|
|
index = uimm (aarch64_get_instr (cpu), 20, 20);
|
|
aarch64_set_vec_u64
|
|
(cpu, rd, 0, aarch64_get_vec_u64 (cpu, rn, index));
|
|
}
|
|
else
|
|
HALT_UNALLOC;
|
|
}
|
|
|
|
static void
|
|
do_double_add (sim_cpu *cpu)
|
|
{
|
|
/* instr [28,25] = 1111. */
|
|
unsigned Fd;
|
|
unsigned Fm;
|
|
unsigned Fn;
|
|
double val1;
|
|
double val2;
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 31, 23))
|
|
{
|
|
case 0xBC:
|
|
switch (uimm (aarch64_get_instr (cpu), 15, 10))
|
|
{
|
|
case 0x01: do_scalar_MOV (cpu); return;
|
|
case 0x39: do_scalar_FCM (cpu); return;
|
|
case 0x3B: do_scalar_FCM (cpu); return;
|
|
}
|
|
break;
|
|
|
|
case 0xBE: do_scalar_SHL (cpu); return;
|
|
|
|
case 0xFC:
|
|
switch (uimm (aarch64_get_instr (cpu), 15, 10))
|
|
{
|
|
case 0x36: do_scalar_FADDP (cpu); return;
|
|
case 0x39: do_scalar_FCM (cpu); return;
|
|
case 0x3B: do_scalar_FCM (cpu); return;
|
|
}
|
|
break;
|
|
|
|
case 0xFD:
|
|
switch (uimm (aarch64_get_instr (cpu), 15, 10))
|
|
{
|
|
case 0x0D: do_scalar_CMGT (cpu); return;
|
|
case 0x35: do_scalar_FABD (cpu); return;
|
|
case 0x39: do_scalar_FCM (cpu); return;
|
|
case 0x3B: do_scalar_FCM (cpu); return;
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
|
|
case 0xFE: do_scalar_USHR (cpu); return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* instr [31,21] = 0101 1110 111
|
|
instr [20,16] = Fn
|
|
instr [15,10] = 1000 01
|
|
instr [9,5] = Fm
|
|
instr [4,0] = Fd. */
|
|
if (uimm (aarch64_get_instr (cpu), 31, 21) != 0x2F7
|
|
|| uimm (aarch64_get_instr (cpu), 15, 10) != 0x21)
|
|
HALT_NYI;
|
|
|
|
Fd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
Fm = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
Fn = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
|
|
val1 = aarch64_get_FP_double (cpu, Fm);
|
|
val2 = aarch64_get_FP_double (cpu, Fn);
|
|
|
|
aarch64_set_FP_double (cpu, Fd, val1 + val2);
|
|
}
|
|
|
|
static void
|
|
dexAdvSIMD1 (sim_cpu *cpu)
|
|
{
|
|
/* instr [28,25] = 1 111. */
|
|
|
|
/* we are currently only interested in the basic
|
|
scalar fp routines which all have bit 30 = 0. */
|
|
if (uimm (aarch64_get_instr (cpu), 30, 30))
|
|
do_double_add (cpu);
|
|
|
|
/* instr[24] is set for FP data processing 3-source and clear for
|
|
all other basic scalar fp instruction groups. */
|
|
else if (uimm (aarch64_get_instr (cpu), 24, 24))
|
|
dexSimpleFPDataProc3Source (cpu);
|
|
|
|
/* instr[21] is clear for floating <-> fixed conversions and set for
|
|
all other basic scalar fp instruction groups. */
|
|
else if (!uimm (aarch64_get_instr (cpu), 21, 21))
|
|
dexSimpleFPFixedConvert (cpu);
|
|
|
|
/* instr[11,10] : 01 ==> cond compare, 10 ==> Data Proc 2 Source
|
|
11 ==> cond select, 00 ==> other. */
|
|
else
|
|
switch (uimm (aarch64_get_instr (cpu), 11, 10))
|
|
{
|
|
case 1: dexSimpleFPCondCompare (cpu); return;
|
|
case 2: dexSimpleFPDataProc2Source (cpu); return;
|
|
case 3: dexSimpleFPCondSelect (cpu); return;
|
|
|
|
default:
|
|
/* Now an ordered cascade of tests.
|
|
FP immediate has aarch64_get_instr (cpu)[12] == 1.
|
|
FP compare has aarch64_get_instr (cpu)[13] == 1.
|
|
FP Data Proc 1 Source has aarch64_get_instr (cpu)[14] == 1.
|
|
FP floating <--> integer conversions has aarch64_get_instr (cpu)[15] == 0. */
|
|
if (uimm (aarch64_get_instr (cpu), 12, 12))
|
|
dexSimpleFPImmediate (cpu);
|
|
|
|
else if (uimm (aarch64_get_instr (cpu), 13, 13))
|
|
dexSimpleFPCompare (cpu);
|
|
|
|
else if (uimm (aarch64_get_instr (cpu), 14, 14))
|
|
dexSimpleFPDataProc1Source (cpu);
|
|
|
|
else if (!uimm (aarch64_get_instr (cpu), 15, 15))
|
|
dexSimpleFPIntegerConvert (cpu);
|
|
|
|
else
|
|
/* If we get here then instr[15] == 1 which means UNALLOC. */
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
/* PC relative addressing. */
|
|
|
|
static void
|
|
pcadr (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = op : 0 ==> ADR, 1 ==> ADRP
|
|
instr[30,29] = immlo
|
|
instr[23,5] = immhi. */
|
|
uint64_t address;
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint32_t isPage = uimm (aarch64_get_instr (cpu), 31, 31);
|
|
union { int64_t u64; uint64_t s64; } imm;
|
|
uint64_t offset;
|
|
|
|
imm.s64 = simm64 (aarch64_get_instr (cpu), 23, 5);
|
|
offset = imm.u64;
|
|
offset = (offset << 2) | uimm (aarch64_get_instr (cpu), 30, 29);
|
|
|
|
address = aarch64_get_PC (cpu);
|
|
|
|
if (isPage)
|
|
{
|
|
offset <<= 12;
|
|
address &= ~0xfff;
|
|
}
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, address + offset);
|
|
}
|
|
|
|
/* Specific decode and execute for group Data Processing Immediate. */
|
|
|
|
static void
|
|
dexPCRelAddressing (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[28,24] = 10000. */
|
|
pcadr (cpu);
|
|
}
|
|
|
|
/* Immediate logical.
|
|
The bimm32/64 argument is constructed by replicating a 2, 4, 8,
|
|
16, 32 or 64 bit sequence pulled out at decode and possibly
|
|
inverting it..
|
|
|
|
N.B. the output register (dest) can normally be Xn or SP
|
|
the exception occurs for flag setting instructions which may
|
|
only use Xn for the output (dest). The input register can
|
|
never be SP. */
|
|
|
|
/* 32 bit and immediate. */
|
|
static void
|
|
and32 (sim_cpu *cpu, uint32_t bimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u32 (cpu, rn, NO_SP) & bimm);
|
|
}
|
|
|
|
/* 64 bit and immediate. */
|
|
static void
|
|
and64 (sim_cpu *cpu, uint64_t bimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u64 (cpu, rn, NO_SP) & bimm);
|
|
}
|
|
|
|
/* 32 bit and immediate set flags. */
|
|
static void
|
|
ands32 (sim_cpu *cpu, uint32_t bimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint32_t value1 = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
uint32_t value2 = bimm;
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 & value2);
|
|
set_flags_for_binop32 (cpu, value1 & value2);
|
|
}
|
|
|
|
/* 64 bit and immediate set flags. */
|
|
static void
|
|
ands64 (sim_cpu *cpu, uint64_t bimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
uint64_t value2 = bimm;
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 & value2);
|
|
set_flags_for_binop64 (cpu, value1 & value2);
|
|
}
|
|
|
|
/* 32 bit exclusive or immediate. */
|
|
static void
|
|
eor32 (sim_cpu *cpu, uint32_t bimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u32 (cpu, rn, NO_SP) ^ bimm);
|
|
}
|
|
|
|
/* 64 bit exclusive or immediate. */
|
|
static void
|
|
eor64 (sim_cpu *cpu, uint64_t bimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u64 (cpu, rn, NO_SP) ^ bimm);
|
|
}
|
|
|
|
/* 32 bit or immediate. */
|
|
static void
|
|
orr32 (sim_cpu *cpu, uint32_t bimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u32 (cpu, rn, NO_SP) | bimm);
|
|
}
|
|
|
|
/* 64 bit or immediate. */
|
|
static void
|
|
orr64 (sim_cpu *cpu, uint64_t bimm)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK,
|
|
aarch64_get_reg_u64 (cpu, rn, NO_SP) | bimm);
|
|
}
|
|
|
|
/* Logical shifted register.
|
|
These allow an optional LSL, ASR, LSR or ROR to the second source
|
|
register with a count up to the register bit count.
|
|
N.B register args may not be SP. */
|
|
|
|
/* 32 bit AND shifted register. */
|
|
static void
|
|
and32_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
& shifted32 (aarch64_get_reg_u32 (cpu, rm, NO_SP), shift, count));
|
|
}
|
|
|
|
/* 64 bit AND shifted register. */
|
|
static void
|
|
and64_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
& shifted64 (aarch64_get_reg_u64 (cpu, rm, NO_SP), shift, count));
|
|
}
|
|
|
|
/* 32 bit AND shifted register setting flags. */
|
|
static void
|
|
ands32_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint32_t value1 = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
uint32_t value2 = shifted32 (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
shift, count);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 & value2);
|
|
set_flags_for_binop32 (cpu, value1 & value2);
|
|
}
|
|
|
|
/* 64 bit AND shifted register setting flags. */
|
|
static void
|
|
ands64_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
uint64_t value2 = shifted64 (aarch64_get_reg_u64 (cpu, rm, NO_SP),
|
|
shift, count);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 & value2);
|
|
set_flags_for_binop64 (cpu, value1 & value2);
|
|
}
|
|
|
|
/* 32 bit BIC shifted register. */
|
|
static void
|
|
bic32_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
& ~ shifted32 (aarch64_get_reg_u32 (cpu, rm, NO_SP), shift, count));
|
|
}
|
|
|
|
/* 64 bit BIC shifted register. */
|
|
static void
|
|
bic64_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
& ~ shifted64 (aarch64_get_reg_u64 (cpu, rm, NO_SP), shift, count));
|
|
}
|
|
|
|
/* 32 bit BIC shifted register setting flags. */
|
|
static void
|
|
bics32_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint32_t value1 = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
uint32_t value2 = ~ shifted32 (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
shift, count);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 & value2);
|
|
set_flags_for_binop32 (cpu, value1 & value2);
|
|
}
|
|
|
|
/* 64 bit BIC shifted register setting flags. */
|
|
static void
|
|
bics64_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
uint64_t value2 = ~ shifted64 (aarch64_get_reg_u64 (cpu, rm, NO_SP),
|
|
shift, count);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value1 & value2);
|
|
set_flags_for_binop64 (cpu, value1 & value2);
|
|
}
|
|
|
|
/* 32 bit EON shifted register. */
|
|
static void
|
|
eon32_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
^ ~ shifted32 (aarch64_get_reg_u32 (cpu, rm, NO_SP), shift, count));
|
|
}
|
|
|
|
/* 64 bit EON shifted register. */
|
|
static void
|
|
eon64_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
^ ~ shifted64 (aarch64_get_reg_u64 (cpu, rm, NO_SP), shift, count));
|
|
}
|
|
|
|
/* 32 bit EOR shifted register. */
|
|
static void
|
|
eor32_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
^ shifted32 (aarch64_get_reg_u32 (cpu, rm, NO_SP), shift, count));
|
|
}
|
|
|
|
/* 64 bit EOR shifted register. */
|
|
static void
|
|
eor64_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
^ shifted64 (aarch64_get_reg_u64 (cpu, rm, NO_SP), shift, count));
|
|
}
|
|
|
|
/* 32 bit ORR shifted register. */
|
|
static void
|
|
orr32_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
| shifted32 (aarch64_get_reg_u32 (cpu, rm, NO_SP), shift, count));
|
|
}
|
|
|
|
/* 64 bit ORR shifted register. */
|
|
static void
|
|
orr64_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
| shifted64 (aarch64_get_reg_u64 (cpu, rm, NO_SP), shift, count));
|
|
}
|
|
|
|
/* 32 bit ORN shifted register. */
|
|
static void
|
|
orn32_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
| ~ shifted32 (aarch64_get_reg_u32 (cpu, rm, NO_SP), shift, count));
|
|
}
|
|
|
|
/* 64 bit ORN shifted register. */
|
|
static void
|
|
orn64_shift (sim_cpu *cpu, Shift shift, uint32_t count)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
| ~ shifted64 (aarch64_get_reg_u64 (cpu, rm, NO_SP), shift, count));
|
|
}
|
|
|
|
static void
|
|
dexLogicalImmediate (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[28,23] = 1001000
|
|
instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[30,29] = op : 0 ==> AND, 1 ==> ORR, 2 ==> EOR, 3 ==> ANDS
|
|
instr[22] = N : used to construct immediate mask
|
|
instr[21,16] = immr
|
|
instr[15,10] = imms
|
|
instr[9,5] = Rn
|
|
instr[4,0] = Rd */
|
|
|
|
/* 32 bit operations must have N = 0 or else we have an UNALLOC. */
|
|
uint32_t size = uimm (aarch64_get_instr (cpu), 31, 31);
|
|
uint32_t N = uimm (aarch64_get_instr (cpu), 22, 22);
|
|
/* uint32_t immr = uimm (aarch64_get_instr (cpu), 21, 16);. */
|
|
/* uint32_t imms = uimm (aarch64_get_instr (cpu), 15, 10);. */
|
|
uint32_t index = uimm (aarch64_get_instr (cpu), 22, 10);
|
|
uint64_t bimm64 = LITable [index];
|
|
uint32_t dispatch = uimm (aarch64_get_instr (cpu), 30, 29);
|
|
|
|
if (~size & N)
|
|
HALT_UNALLOC;
|
|
|
|
if (!bimm64)
|
|
HALT_UNALLOC;
|
|
|
|
if (size == 0)
|
|
{
|
|
uint32_t bimm = (uint32_t) bimm64;
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 0: and32 (cpu, bimm); return;
|
|
case 1: orr32 (cpu, bimm); return;
|
|
case 2: eor32 (cpu, bimm); return;
|
|
case 3: ands32 (cpu, bimm); return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (dispatch)
|
|
{
|
|
case 0: and64 (cpu, bimm64); return;
|
|
case 1: orr64 (cpu, bimm64); return;
|
|
case 2: eor64 (cpu, bimm64); return;
|
|
case 3: ands64 (cpu, bimm64); return;
|
|
}
|
|
}
|
|
HALT_UNALLOC;
|
|
}
|
|
|
|
/* Immediate move.
|
|
The uimm argument is a 16 bit value to be inserted into the
|
|
target register the pos argument locates the 16 bit word in the
|
|
dest register i.e. it is in {0, 1} for 32 bit and {0, 1, 2,
|
|
3} for 64 bit.
|
|
N.B register arg may not be SP so it should be.
|
|
accessed using the setGZRegisterXXX accessors. */
|
|
|
|
/* 32 bit move 16 bit immediate zero remaining shorts. */
|
|
static void
|
|
movz32 (sim_cpu *cpu, uint32_t val, uint32_t pos)
|
|
{
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, val << (pos * 16));
|
|
}
|
|
|
|
/* 64 bit move 16 bit immediate zero remaining shorts. */
|
|
static void
|
|
movz64 (sim_cpu *cpu, uint32_t val, uint32_t pos)
|
|
{
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, ((uint64_t) val) << (pos * 16));
|
|
}
|
|
|
|
/* 32 bit move 16 bit immediate negated. */
|
|
static void
|
|
movn32 (sim_cpu *cpu, uint32_t val, uint32_t pos)
|
|
{
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, ((val << (pos * 16)) ^ 0xffffffffU));
|
|
}
|
|
|
|
/* 64 bit move 16 bit immediate negated. */
|
|
static void
|
|
movn64 (sim_cpu *cpu, uint32_t val, uint32_t pos)
|
|
{
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, ((((uint64_t) val) << (pos * 16))
|
|
^ 0xffffffffffffffffULL));
|
|
}
|
|
|
|
/* 32 bit move 16 bit immediate keep remaining shorts. */
|
|
static void
|
|
movk32 (sim_cpu *cpu, uint32_t val, uint32_t pos)
|
|
{
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint32_t current = aarch64_get_reg_u32 (cpu, rd, NO_SP);
|
|
uint32_t value = val << (pos * 16);
|
|
uint32_t mask = ~(0xffffU << (pos * 16));
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, (value | (current & mask)));
|
|
}
|
|
|
|
/* 64 bit move 16 it immediate keep remaining shorts. */
|
|
static void
|
|
movk64 (sim_cpu *cpu, uint32_t val, uint32_t pos)
|
|
{
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t current = aarch64_get_reg_u64 (cpu, rd, NO_SP);
|
|
uint64_t value = (uint64_t) val << (pos * 16);
|
|
uint64_t mask = ~(0xffffULL << (pos * 16));
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, (value | (current & mask)));
|
|
}
|
|
|
|
static void
|
|
dexMoveWideImmediate (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[28:23] = 100101
|
|
instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[30,29] = op : 0 ==> MOVN, 1 ==> UNALLOC, 2 ==> MOVZ, 3 ==> MOVK
|
|
instr[22,21] = shift : 00 == LSL#0, 01 = LSL#16, 10 = LSL#32, 11 = LSL#48
|
|
instr[20,5] = uimm16
|
|
instr[4,0] = Rd */
|
|
|
|
/* N.B. the (multiple of 16) shift is applied by the called routine,
|
|
we just pass the multiplier. */
|
|
|
|
uint32_t imm;
|
|
uint32_t size = uimm (aarch64_get_instr (cpu), 31, 31);
|
|
uint32_t op = uimm (aarch64_get_instr (cpu), 30, 29);
|
|
uint32_t shift = uimm (aarch64_get_instr (cpu), 22, 21);
|
|
|
|
/* 32 bit can only shift 0 or 1 lot of 16.
|
|
anything else is an unallocated instruction. */
|
|
if (size == 0 && (shift > 1))
|
|
HALT_UNALLOC;
|
|
|
|
if (op == 1)
|
|
HALT_UNALLOC;
|
|
|
|
imm = uimm (aarch64_get_instr (cpu), 20, 5);
|
|
|
|
if (size == 0)
|
|
{
|
|
if (op == 0)
|
|
movn32 (cpu, imm, shift);
|
|
else if (op == 2)
|
|
movz32 (cpu, imm, shift);
|
|
else
|
|
movk32 (cpu, imm, shift);
|
|
}
|
|
else
|
|
{
|
|
if (op == 0)
|
|
movn64 (cpu, imm, shift);
|
|
else if (op == 2)
|
|
movz64 (cpu, imm, shift);
|
|
else
|
|
movk64 (cpu, imm, shift);
|
|
}
|
|
}
|
|
|
|
/* Bitfield operations.
|
|
These take a pair of bit positions r and s which are in {0..31}
|
|
or {0..63} depending on the instruction word size.
|
|
N.B register args may not be SP. */
|
|
|
|
/* OK, we start with ubfm which just needs to pick
|
|
some bits out of source zero the rest and write
|
|
the result to dest. Just need two logical shifts. */
|
|
|
|
/* 32 bit bitfield move, left and right of affected zeroed
|
|
if r <= s Wd<s-r:0> = Wn<s:r> else Wd<32+s-r,32-r> = Wn<s:0>. */
|
|
static void
|
|
ubfm32 (sim_cpu *cpu, uint32_t r, uint32_t s)
|
|
{
|
|
unsigned rd;
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
uint32_t value = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
|
|
/* Pick either s+1-r or s+1 consecutive bits out of the original word. */
|
|
if (r <= s)
|
|
{
|
|
/* 31:...:s:xxx:r:...:0 ==> 31:...:s-r:xxx:0.
|
|
We want only bits s:xxx:r at the bottom of the word
|
|
so we LSL bit s up to bit 31 i.e. by 31 - s
|
|
and then we LSR to bring bit 31 down to bit s - r
|
|
i.e. by 31 + r - s. */
|
|
value <<= 31 - s;
|
|
value >>= 31 + r - s;
|
|
}
|
|
else
|
|
{
|
|
/* 31:...:s:xxx:0 ==> 31:...:31-(r-1)+s:xxx:31-(r-1):...:0
|
|
We want only bits s:xxx:0 starting at it 31-(r-1)
|
|
so we LSL bit s up to bit 31 i.e. by 31 - s
|
|
and then we LSL to bring bit 31 down to 31-(r-1)+s
|
|
i.e. by r - (s + 1). */
|
|
value <<= 31 - s;
|
|
value >>= r - (s + 1);
|
|
}
|
|
|
|
rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value);
|
|
}
|
|
|
|
/* 64 bit bitfield move, left and right of affected zeroed
|
|
if r <= s Wd<s-r:0> = Wn<s:r> else Wd<64+s-r,64-r> = Wn<s:0>. */
|
|
static void
|
|
ubfm (sim_cpu *cpu, uint32_t r, uint32_t s)
|
|
{
|
|
unsigned rd;
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
uint64_t value = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
|
|
if (r <= s)
|
|
{
|
|
/* 63:...:s:xxx:r:...:0 ==> 63:...:s-r:xxx:0.
|
|
We want only bits s:xxx:r at the bottom of the word.
|
|
So we LSL bit s up to bit 63 i.e. by 63 - s
|
|
and then we LSR to bring bit 63 down to bit s - r
|
|
i.e. by 63 + r - s. */
|
|
value <<= 63 - s;
|
|
value >>= 63 + r - s;
|
|
}
|
|
else
|
|
{
|
|
/* 63:...:s:xxx:0 ==> 63:...:63-(r-1)+s:xxx:63-(r-1):...:0.
|
|
We want only bits s:xxx:0 starting at it 63-(r-1).
|
|
So we LSL bit s up to bit 63 i.e. by 63 - s
|
|
and then we LSL to bring bit 63 down to 63-(r-1)+s
|
|
i.e. by r - (s + 1). */
|
|
value <<= 63 - s;
|
|
value >>= r - (s + 1);
|
|
}
|
|
|
|
rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, value);
|
|
}
|
|
|
|
/* The signed versions need to insert sign bits
|
|
on the left of the inserted bit field. so we do
|
|
much the same as the unsigned version except we
|
|
use an arithmetic shift right -- this just means
|
|
we need to operate on signed values. */
|
|
|
|
/* 32 bit bitfield move, left of affected sign-extended, right zeroed. */
|
|
/* If r <= s Wd<s-r:0> = Wn<s:r> else Wd<32+s-r,32-r> = Wn<s:0>. */
|
|
static void
|
|
sbfm32 (sim_cpu *cpu, uint32_t r, uint32_t s)
|
|
{
|
|
unsigned rd;
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
/* as per ubfm32 but use an ASR instead of an LSR. */
|
|
int32_t value = aarch64_get_reg_s32 (cpu, rn, NO_SP);
|
|
|
|
if (r <= s)
|
|
{
|
|
value <<= 31 - s;
|
|
value >>= 31 + r - s;
|
|
}
|
|
else
|
|
{
|
|
value <<= 31 - s;
|
|
value >>= r - (s + 1);
|
|
}
|
|
|
|
rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, (uint32_t) value);
|
|
}
|
|
|
|
/* 64 bit bitfield move, left of affected sign-extended, right zeroed. */
|
|
/* If r <= s Wd<s-r:0> = Wn<s:r> else Wd<64+s-r,64-r> = Wn<s:0>. */
|
|
static void
|
|
sbfm (sim_cpu *cpu, uint32_t r, uint32_t s)
|
|
{
|
|
unsigned rd;
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
/* acpu per ubfm but use an ASR instead of an LSR. */
|
|
int64_t value = aarch64_get_reg_s64 (cpu, rn, NO_SP);
|
|
|
|
if (r <= s)
|
|
{
|
|
value <<= 63 - s;
|
|
value >>= 63 + r - s;
|
|
}
|
|
else
|
|
{
|
|
value <<= 63 - s;
|
|
value >>= r - (s + 1);
|
|
}
|
|
|
|
rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
aarch64_set_reg_s64 (cpu, rd, NO_SP, value);
|
|
}
|
|
|
|
/* Finally, these versions leave non-affected bits
|
|
as is. so we need to generate the bits as per
|
|
ubfm and also generate a mask to pick the
|
|
bits from the original and computed values. */
|
|
|
|
/* 32 bit bitfield move, non-affected bits left as is.
|
|
If r <= s Wd<s-r:0> = Wn<s:r> else Wd<32+s-r,32-r> = Wn<s:0>. */
|
|
static void
|
|
bfm32 (sim_cpu *cpu, uint32_t r, uint32_t s)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
uint32_t value = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
uint32_t mask = -1;
|
|
unsigned rd;
|
|
uint32_t value2;
|
|
|
|
/* Pick either s+1-r or s+1 consecutive bits out of the original word. */
|
|
if (r <= s)
|
|
{
|
|
/* 31:...:s:xxx:r:...:0 ==> 31:...:s-r:xxx:0.
|
|
We want only bits s:xxx:r at the bottom of the word
|
|
so we LSL bit s up to bit 31 i.e. by 31 - s
|
|
and then we LSR to bring bit 31 down to bit s - r
|
|
i.e. by 31 + r - s. */
|
|
value <<= 31 - s;
|
|
value >>= 31 + r - s;
|
|
/* the mask must include the same bits. */
|
|
mask <<= 31 - s;
|
|
mask >>= 31 + r - s;
|
|
}
|
|
else
|
|
{
|
|
/* 31:...:s:xxx:0 ==> 31:...:31-(r-1)+s:xxx:31-(r-1):...:0.
|
|
We want only bits s:xxx:0 starting at it 31-(r-1)
|
|
so we LSL bit s up to bit 31 i.e. by 31 - s
|
|
and then we LSL to bring bit 31 down to 31-(r-1)+s
|
|
i.e. by r - (s + 1). */
|
|
value <<= 31 - s;
|
|
value >>= r - (s + 1);
|
|
/* The mask must include the same bits. */
|
|
mask <<= 31 - s;
|
|
mask >>= r - (s + 1);
|
|
}
|
|
|
|
rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
value2 = aarch64_get_reg_u32 (cpu, rd, NO_SP);
|
|
|
|
value2 &= ~mask;
|
|
value2 |= value;
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, (aarch64_get_reg_u32 (cpu, rd, NO_SP) & ~mask) | value);
|
|
}
|
|
|
|
/* 64 bit bitfield move, non-affected bits left as is.
|
|
If r <= s Wd<s-r:0> = Wn<s:r> else Wd<64+s-r,64-r> = Wn<s:0>. */
|
|
static void
|
|
bfm (sim_cpu *cpu, uint32_t r, uint32_t s)
|
|
{
|
|
unsigned rd;
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
uint64_t value = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
uint64_t mask = 0xffffffffffffffffULL;
|
|
|
|
if (r <= s)
|
|
{
|
|
/* 63:...:s:xxx:r:...:0 ==> 63:...:s-r:xxx:0.
|
|
We want only bits s:xxx:r at the bottom of the word
|
|
so we LSL bit s up to bit 63 i.e. by 63 - s
|
|
and then we LSR to bring bit 63 down to bit s - r
|
|
i.e. by 63 + r - s. */
|
|
value <<= 63 - s;
|
|
value >>= 63 + r - s;
|
|
/* The mask must include the same bits. */
|
|
mask <<= 63 - s;
|
|
mask >>= 63 + r - s;
|
|
}
|
|
else
|
|
{
|
|
/* 63:...:s:xxx:0 ==> 63:...:63-(r-1)+s:xxx:63-(r-1):...:0
|
|
We want only bits s:xxx:0 starting at it 63-(r-1)
|
|
so we LSL bit s up to bit 63 i.e. by 63 - s
|
|
and then we LSL to bring bit 63 down to 63-(r-1)+s
|
|
i.e. by r - (s + 1). */
|
|
value <<= 63 - s;
|
|
value >>= r - (s + 1);
|
|
/* The mask must include the same bits. */
|
|
mask <<= 63 - s;
|
|
mask >>= r - (s + 1);
|
|
}
|
|
|
|
rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, (aarch64_get_reg_u64 (cpu, rd, NO_SP) & ~mask) | value);
|
|
}
|
|
|
|
static void
|
|
dexBitfieldImmediate (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[28:23] = 100110
|
|
instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[30,29] = op : 0 ==> SBFM, 1 ==> BFM, 2 ==> UBFM, 3 ==> UNALLOC
|
|
instr[22] = N : must be 0 for 32 bit, 1 for 64 bit ow UNALLOC
|
|
instr[21,16] = immr : 0xxxxx for 32 bit, xxxxxx for 64 bit
|
|
instr[15,10] = imms : 0xxxxx for 32 bit, xxxxxx for 64 bit
|
|
instr[9,5] = Rn
|
|
instr[4,0] = Rd */
|
|
|
|
/* 32 bit operations must have N = 0 or else we have an UNALLOC. */
|
|
uint32_t dispatch;
|
|
uint32_t imms;
|
|
uint32_t size = uimm (aarch64_get_instr (cpu), 31, 31);
|
|
uint32_t N = uimm (aarch64_get_instr (cpu), 22, 22);
|
|
/* 32 bit operations must have immr[5] = 0 and imms[5] = 0. */
|
|
/* or else we have an UNALLOC. */
|
|
uint32_t immr = uimm (aarch64_get_instr (cpu), 21, 16);
|
|
|
|
if (~size & N)
|
|
HALT_UNALLOC;
|
|
|
|
if (!size && uimm (immr, 5, 5))
|
|
HALT_UNALLOC;
|
|
|
|
imms = uimm (aarch64_get_instr (cpu), 15, 10);
|
|
if (!size && uimm (imms, 5, 5))
|
|
HALT_UNALLOC;
|
|
|
|
/* Switch on combined size and op. */
|
|
dispatch = uimm (aarch64_get_instr (cpu), 31, 29);
|
|
switch (dispatch)
|
|
{
|
|
case 0: sbfm32 (cpu, immr, imms); return;
|
|
case 1: bfm32 (cpu, immr, imms); return;
|
|
case 2: ubfm32 (cpu, immr, imms); return;
|
|
case 4: sbfm (cpu, immr, imms); return;
|
|
case 5: bfm (cpu, immr, imms); return;
|
|
case 6: ubfm (cpu, immr, imms); return;
|
|
default: HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_EXTR_32 (sim_cpu *cpu)
|
|
{
|
|
/* instr[31:21] = 00010011100
|
|
instr[20,16] = Rm
|
|
instr[15,10] = imms : 0xxxxx for 32 bit
|
|
instr[9,5] = Rn
|
|
instr[4,0] = Rd */
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned imms = uimm (aarch64_get_instr (cpu), 15, 10) & 31;
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t val1;
|
|
uint64_t val2;
|
|
|
|
val1 = aarch64_get_reg_u32 (cpu, rm, NO_SP);
|
|
val1 >>= imms;
|
|
val2 = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
val2 <<= (32 - imms);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, val1 | val2);
|
|
}
|
|
|
|
static void
|
|
do_EXTR_64 (sim_cpu *cpu)
|
|
{
|
|
/* instr[31:21] = 10010011100
|
|
instr[20,16] = Rm
|
|
instr[15,10] = imms
|
|
instr[9,5] = Rn
|
|
instr[4,0] = Rd */
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned imms = uimm (aarch64_get_instr (cpu), 15, 10) & 63;
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t val;
|
|
|
|
val = aarch64_get_reg_u64 (cpu, rm, NO_SP);
|
|
val >>= imms;
|
|
val |= (aarch64_get_reg_u64 (cpu, rn, NO_SP) << (64 - imms));
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, val);
|
|
}
|
|
|
|
static void
|
|
dexExtractImmediate (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[28:23] = 100111
|
|
instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[30,29] = op21 : 0 ==> EXTR, 1,2,3 ==> UNALLOC
|
|
instr[22] = N : must be 0 for 32 bit, 1 for 64 bit or UNALLOC
|
|
instr[21] = op0 : must be 0 or UNALLOC
|
|
instr[20,16] = Rm
|
|
instr[15,10] = imms : 0xxxxx for 32 bit, xxxxxx for 64 bit
|
|
instr[9,5] = Rn
|
|
instr[4,0] = Rd */
|
|
|
|
/* 32 bit operations must have N = 0 or else we have an UNALLOC. */
|
|
/* 64 bit operations must have N = 1 or else we have an UNALLOC. */
|
|
uint32_t dispatch;
|
|
uint32_t size = uimm (aarch64_get_instr (cpu), 31, 31);
|
|
uint32_t N = uimm (aarch64_get_instr (cpu), 22, 22);
|
|
/* 32 bit operations must have imms[5] = 0
|
|
or else we have an UNALLOC. */
|
|
uint32_t imms = uimm (aarch64_get_instr (cpu), 15, 10);
|
|
|
|
if (size ^ N)
|
|
HALT_UNALLOC;
|
|
|
|
if (!size && uimm (imms, 5, 5))
|
|
HALT_UNALLOC;
|
|
|
|
/* Switch on combined size and op. */
|
|
dispatch = uimm (aarch64_get_instr (cpu), 31, 29);
|
|
|
|
if (dispatch == 0)
|
|
do_EXTR_32 (cpu);
|
|
|
|
else if (dispatch == 4)
|
|
do_EXTR_64 (cpu);
|
|
|
|
else if (dispatch == 1)
|
|
HALT_NYI;
|
|
else
|
|
HALT_UNALLOC;
|
|
}
|
|
|
|
static void
|
|
dexDPImm (sim_cpu *cpu)
|
|
{
|
|
/* uint32_t group = dispatchGroup (aarch64_get_instr (cpu));
|
|
assert group == GROUP_DPIMM_1000 || grpoup == GROUP_DPIMM_1001
|
|
bits [25,23] of a DPImm are the secondary dispatch vector. */
|
|
uint32_t group2 = dispatchDPImm (aarch64_get_instr (cpu));
|
|
|
|
switch (group2)
|
|
{
|
|
case DPIMM_PCADR_000:
|
|
case DPIMM_PCADR_001:
|
|
dexPCRelAddressing (cpu);
|
|
return;
|
|
|
|
case DPIMM_ADDSUB_010:
|
|
case DPIMM_ADDSUB_011:
|
|
dexAddSubtractImmediate (cpu);
|
|
return;
|
|
|
|
case DPIMM_LOG_100:
|
|
dexLogicalImmediate (cpu);
|
|
return;
|
|
|
|
case DPIMM_MOV_101:
|
|
dexMoveWideImmediate (cpu);
|
|
return;
|
|
|
|
case DPIMM_BITF_110:
|
|
dexBitfieldImmediate (cpu);
|
|
return;
|
|
|
|
case DPIMM_EXTR_111:
|
|
dexExtractImmediate (cpu);
|
|
return;
|
|
|
|
default:
|
|
/* Should never reach here. */
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexLoadUnscaledImmediate (sim_cpu *cpu)
|
|
{
|
|
/* instr[29,24] == 111_00
|
|
instr[21] == 0
|
|
instr[11,10] == 00
|
|
instr[31,30] = size
|
|
instr[26] = V
|
|
instr[23,22] = opc
|
|
instr[20,12] = simm9
|
|
instr[9,5] = rn may be SP. */
|
|
/* unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0); */
|
|
uint32_t V = uimm (aarch64_get_instr (cpu), 26, 26);
|
|
uint32_t dispatch = ( (uimm (aarch64_get_instr (cpu), 31, 30) << 2)
|
|
| uimm (aarch64_get_instr (cpu), 23, 22));
|
|
int32_t imm = simm32 (aarch64_get_instr (cpu), 20, 12);
|
|
|
|
if (!V)
|
|
{
|
|
/* GReg operations. */
|
|
switch (dispatch)
|
|
{
|
|
case 0: sturb (cpu, imm); return;
|
|
case 1: ldurb32 (cpu, imm); return;
|
|
case 2: ldursb64 (cpu, imm); return;
|
|
case 3: ldursb32 (cpu, imm); return;
|
|
case 4: sturh (cpu, imm); return;
|
|
case 5: ldurh32 (cpu, imm); return;
|
|
case 6: ldursh64 (cpu, imm); return;
|
|
case 7: ldursh32 (cpu, imm); return;
|
|
case 8: stur32 (cpu, imm); return;
|
|
case 9: ldur32 (cpu, imm); return;
|
|
case 10: ldursw (cpu, imm); return;
|
|
case 12: stur64 (cpu, imm); return;
|
|
case 13: ldur64 (cpu, imm); return;
|
|
|
|
case 14:
|
|
/* PRFUM NYI. */
|
|
HALT_NYI;
|
|
|
|
default:
|
|
case 11:
|
|
case 15:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
/* FReg operations. */
|
|
switch (dispatch)
|
|
{
|
|
case 2: fsturq (cpu, imm); return;
|
|
case 3: fldurq (cpu, imm); return;
|
|
case 8: fsturs (cpu, imm); return;
|
|
case 9: fldurs (cpu, imm); return;
|
|
case 12: fsturd (cpu, imm); return;
|
|
case 13: fldurd (cpu, imm); return;
|
|
|
|
case 0: /* STUR 8 bit FP. */
|
|
case 1: /* LDUR 8 bit FP. */
|
|
case 4: /* STUR 16 bit FP. */
|
|
case 5: /* LDUR 8 bit FP. */
|
|
HALT_NYI;
|
|
|
|
default:
|
|
case 6:
|
|
case 7:
|
|
case 10:
|
|
case 11:
|
|
case 14:
|
|
case 15:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
/* N.B. A preliminary note regarding all the ldrs<x>32
|
|
instructions
|
|
|
|
The signed value loaded by these instructions is cast to unsigned
|
|
before being assigned to aarch64_get_reg_u64 (cpu, N) i.e. to the
|
|
64 bit element of the GReg union. this performs a 32 bit sign extension
|
|
(as required) but avoids 64 bit sign extension, thus ensuring that the
|
|
top half of the register word is zero. this is what the spec demands
|
|
when a 32 bit load occurs. */
|
|
|
|
/* 32 bit load sign-extended byte scaled unsigned 12 bit. */
|
|
static void
|
|
ldrsb32_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* The target register may not be SP but the source may be
|
|
there is no scaling required for a byte load. */
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK) + offset;
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP,
|
|
(int64_t) aarch64_get_mem_s8 (cpu, address));
|
|
}
|
|
|
|
/* 32 bit load sign-extended byte scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
ldrsb32_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned int rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* rn may reference SP, rm and rt must reference ZR. */
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t displacement = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
extension);
|
|
|
|
/* There is no scaling required for a byte load. */
|
|
aarch64_set_reg_u64
|
|
(cpu, rt, NO_SP, (int64_t) aarch64_get_mem_s8 (cpu, address
|
|
+ displacement));
|
|
}
|
|
|
|
/* 32 bit load sign-extended byte unscaled signed 9 bit with
|
|
pre- or post-writeback. */
|
|
static void
|
|
ldrsb32_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
uint64_t address;
|
|
unsigned int rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned int rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
if (rn == rt && wb != NoWriteBack)
|
|
HALT_UNALLOC;
|
|
|
|
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb == Pre)
|
|
address += offset;
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP,
|
|
(int64_t) aarch64_get_mem_s8 (cpu, address));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, NO_SP, address);
|
|
}
|
|
|
|
/* 8 bit store scaled. */
|
|
static void
|
|
fstrb_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
|
|
aarch64_set_mem_u8 (cpu,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK) + offset,
|
|
aarch64_get_vec_u8 (cpu, st, 0));
|
|
}
|
|
|
|
/* 8 bit store scaled or unscaled zero- or
|
|
sign-extended 8-bit register offset. */
|
|
static void
|
|
fstrb_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 32, scaling);
|
|
|
|
aarch64_set_mem_u8
|
|
(cpu, address + displacement, aarch64_get_vec_u8 (cpu, st, 0));
|
|
}
|
|
|
|
/* 16 bit store scaled. */
|
|
static void
|
|
fstrh_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
|
|
aarch64_set_mem_u16
|
|
(cpu,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK) + SCALE (offset, 16),
|
|
aarch64_get_vec_u16 (cpu, st, 0));
|
|
}
|
|
|
|
/* 16 bit store scaled or unscaled zero-
|
|
or sign-extended 16-bit register offset. */
|
|
static void
|
|
fstrh_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 32, scaling);
|
|
|
|
aarch64_set_mem_u16
|
|
(cpu, address + displacement, aarch64_get_vec_u16 (cpu, st, 0));
|
|
}
|
|
|
|
/* 32 bit store scaled unsigned 12 bit. */
|
|
static void
|
|
fstrs_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
|
|
aarch64_set_mem_float
|
|
(cpu,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK) + SCALE (offset, 32),
|
|
aarch64_get_FP_float (cpu, st));
|
|
}
|
|
|
|
/* 32 bit store unscaled signed 9 bit with pre- or post-writeback. */
|
|
static void
|
|
fstrs_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_mem_float (cpu, address, aarch64_get_FP_float (cpu, st));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 32 bit store scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
fstrs_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 32, scaling);
|
|
|
|
aarch64_set_mem_float
|
|
(cpu, address + displacement, aarch64_get_FP_float (cpu, st));
|
|
}
|
|
|
|
/* 64 bit store scaled unsigned 12 bit. */
|
|
static void
|
|
fstrd_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
|
|
aarch64_set_mem_double
|
|
(cpu,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK) + SCALE (offset, 64),
|
|
aarch64_get_FP_double (cpu, st));
|
|
}
|
|
|
|
/* 64 bit store unscaled signed 9 bit with pre- or post-writeback. */
|
|
static void
|
|
fstrd_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_mem_double (cpu, address, aarch64_get_FP_double (cpu, st));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 64 bit store scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
fstrd_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 64, scaling);
|
|
|
|
aarch64_set_mem_double
|
|
(cpu, address + displacement, aarch64_get_FP_double (cpu, st));
|
|
}
|
|
|
|
/* 128 bit store scaled unsigned 12 bit. */
|
|
static void
|
|
fstrq_abs (sim_cpu *cpu, uint32_t offset)
|
|
{
|
|
FRegister a;
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
uint64_t addr;
|
|
|
|
aarch64_get_FP_long_double (cpu, st, & a);
|
|
|
|
addr = aarch64_get_reg_u64 (cpu, rn, SP_OK) + SCALE (offset, 128);
|
|
aarch64_set_mem_long_double (cpu, addr, a);
|
|
}
|
|
|
|
/* 128 bit store unscaled signed 9 bit with pre- or post-writeback. */
|
|
static void
|
|
fstrq_wb (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
FRegister a;
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_get_FP_long_double (cpu, st, & a);
|
|
aarch64_set_mem_long_double (cpu, address, a);
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
|
|
}
|
|
|
|
/* 128 bit store scaled or unscaled zero-
|
|
or sign-extended 32-bit register offset. */
|
|
static void
|
|
fstrq_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned st = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
|
|
int64_t extended = extend (aarch64_get_reg_u32 (cpu, rm, NO_SP),
|
|
extension);
|
|
uint64_t displacement = OPT_SCALE (extended, 128, scaling);
|
|
|
|
FRegister a;
|
|
|
|
aarch64_get_FP_long_double (cpu, st, & a);
|
|
aarch64_set_mem_long_double (cpu, address + displacement, a);
|
|
}
|
|
|
|
static void
|
|
dexLoadImmediatePrePost (sim_cpu *cpu)
|
|
{
|
|
/* instr[29,24] == 111_00
|
|
instr[21] == 0
|
|
instr[11,10] == 00
|
|
instr[31,30] = size
|
|
instr[26] = V
|
|
instr[23,22] = opc
|
|
instr[20,12] = simm9
|
|
instr[11] = wb : 0 ==> Post, 1 ==> Pre
|
|
instr[9,5] = rn may be SP. */
|
|
/* unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0); */
|
|
uint32_t V = uimm (aarch64_get_instr (cpu), 26, 26);
|
|
uint32_t dispatch = ( (uimm (aarch64_get_instr (cpu), 31, 30) << 2)
|
|
| uimm (aarch64_get_instr (cpu), 23, 22));
|
|
int32_t imm = simm32 (aarch64_get_instr (cpu), 20, 12);
|
|
WriteBack wb = writeback (aarch64_get_instr (cpu), 11);
|
|
|
|
if (!V)
|
|
{
|
|
/* GReg operations. */
|
|
switch (dispatch)
|
|
{
|
|
case 0: strb_wb (cpu, imm, wb); return;
|
|
case 1: ldrb32_wb (cpu, imm, wb); return;
|
|
case 2: ldrsb_wb (cpu, imm, wb); return;
|
|
case 3: ldrsb32_wb (cpu, imm, wb); return;
|
|
case 4: strh_wb (cpu, imm, wb); return;
|
|
case 5: ldrh32_wb (cpu, imm, wb); return;
|
|
case 6: ldrsh64_wb (cpu, imm, wb); return;
|
|
case 7: ldrsh32_wb (cpu, imm, wb); return;
|
|
case 8: str32_wb (cpu, imm, wb); return;
|
|
case 9: ldr32_wb (cpu, imm, wb); return;
|
|
case 10: ldrsw_wb (cpu, imm, wb); return;
|
|
case 12: str_wb (cpu, imm, wb); return;
|
|
case 13: ldr_wb (cpu, imm, wb); return;
|
|
|
|
default:
|
|
case 11:
|
|
case 14:
|
|
case 15:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
/* FReg operations. */
|
|
switch (dispatch)
|
|
{
|
|
case 2: fstrq_wb (cpu, imm, wb); return;
|
|
case 3: fldrq_wb (cpu, imm, wb); return;
|
|
case 8: fstrs_wb (cpu, imm, wb); return;
|
|
case 9: fldrs_wb (cpu, imm, wb); return;
|
|
case 12: fstrd_wb (cpu, imm, wb); return;
|
|
case 13: fldrd_wb (cpu, imm, wb); return;
|
|
|
|
case 0: /* STUR 8 bit FP. */
|
|
case 1: /* LDUR 8 bit FP. */
|
|
case 4: /* STUR 16 bit FP. */
|
|
case 5: /* LDUR 8 bit FP. */
|
|
HALT_NYI;
|
|
|
|
default:
|
|
case 6:
|
|
case 7:
|
|
case 10:
|
|
case 11:
|
|
case 14:
|
|
case 15:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexLoadRegisterOffset (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,30] = size
|
|
instr[29,27] = 111
|
|
instr[26] = V
|
|
instr[25,24] = 00
|
|
instr[23,22] = opc
|
|
instr[21] = 1
|
|
instr[20,16] = rm
|
|
instr[15,13] = option : 010 ==> UXTW, 011 ==> UXTX/LSL,
|
|
110 ==> SXTW, 111 ==> SXTX,
|
|
ow ==> RESERVED
|
|
instr[12] = scaled
|
|
instr[11,10] = 10
|
|
instr[9,5] = rn
|
|
instr[4,0] = rt. */
|
|
|
|
uint32_t V = uimm (aarch64_get_instr (cpu), 26,26);
|
|
uint32_t dispatch = ( (uimm (aarch64_get_instr (cpu), 31, 30) << 2)
|
|
| uimm (aarch64_get_instr (cpu), 23, 22));
|
|
Scaling scale = scaling (aarch64_get_instr (cpu), 12);
|
|
Extension extensionType = extension (aarch64_get_instr (cpu), 13);
|
|
|
|
/* Check for illegal extension types. */
|
|
if (uimm (extensionType, 1, 1) == 0)
|
|
HALT_UNALLOC;
|
|
|
|
if (extensionType == UXTX || extensionType == SXTX)
|
|
extensionType = NoExtension;
|
|
|
|
if (!V)
|
|
{
|
|
/* GReg operations. */
|
|
switch (dispatch)
|
|
{
|
|
case 0: strb_scale_ext (cpu, scale, extensionType); return;
|
|
case 1: ldrb32_scale_ext (cpu, scale, extensionType); return;
|
|
case 2: ldrsb_scale_ext (cpu, scale, extensionType); return;
|
|
case 3: ldrsb32_scale_ext (cpu, scale, extensionType); return;
|
|
case 4: strh_scale_ext (cpu, scale, extensionType); return;
|
|
case 5: ldrh32_scale_ext (cpu, scale, extensionType); return;
|
|
case 6: ldrsh_scale_ext (cpu, scale, extensionType); return;
|
|
case 7: ldrsh32_scale_ext (cpu, scale, extensionType); return;
|
|
case 8: str32_scale_ext (cpu, scale, extensionType); return;
|
|
case 9: ldr32_scale_ext (cpu, scale, extensionType); return;
|
|
case 10: ldrsw_scale_ext (cpu, scale, extensionType); return;
|
|
case 12: str_scale_ext (cpu, scale, extensionType); return;
|
|
case 13: ldr_scale_ext (cpu, scale, extensionType); return;
|
|
case 14: prfm_scale_ext (cpu, scale, extensionType); return;
|
|
|
|
default:
|
|
case 11:
|
|
case 15:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
/* FReg operations. */
|
|
switch (dispatch)
|
|
{
|
|
case 1: /* LDUR 8 bit FP. */
|
|
HALT_NYI;
|
|
case 3: fldrq_scale_ext (cpu, scale, extensionType); return;
|
|
case 5: /* LDUR 8 bit FP. */
|
|
HALT_NYI;
|
|
case 9: fldrs_scale_ext (cpu, scale, extensionType); return;
|
|
case 13: fldrd_scale_ext (cpu, scale, extensionType); return;
|
|
|
|
case 0: fstrb_scale_ext (cpu, scale, extensionType); return;
|
|
case 2: fstrq_scale_ext (cpu, scale, extensionType); return;
|
|
case 4: fstrh_scale_ext (cpu, scale, extensionType); return;
|
|
case 8: fstrs_scale_ext (cpu, scale, extensionType); return;
|
|
case 12: fstrd_scale_ext (cpu, scale, extensionType); return;
|
|
|
|
default:
|
|
case 6:
|
|
case 7:
|
|
case 10:
|
|
case 11:
|
|
case 14:
|
|
case 15:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexLoadUnsignedImmediate (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[29,24] == 111_01
|
|
instr[31,30] = size
|
|
instr[26] = V
|
|
instr[23,22] = opc
|
|
instr[21,10] = uimm12 : unsigned immediate offset
|
|
instr[9,5] = rn may be SP. */
|
|
/* unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0); */
|
|
uint32_t V = uimm (aarch64_get_instr (cpu), 26,26);
|
|
uint32_t dispatch = ( (uimm (aarch64_get_instr (cpu), 31, 30) << 2)
|
|
| uimm (aarch64_get_instr (cpu), 23, 22));
|
|
uint32_t imm = uimm (aarch64_get_instr (cpu), 21, 10);
|
|
|
|
if (!V)
|
|
{
|
|
/* GReg operations. */
|
|
switch (dispatch)
|
|
{
|
|
case 0: strb_abs (cpu, imm); return;
|
|
case 1: ldrb32_abs (cpu, imm); return;
|
|
case 2: ldrsb_abs (cpu, imm); return;
|
|
case 3: ldrsb32_abs (cpu, imm); return;
|
|
case 4: strh_abs (cpu, imm); return;
|
|
case 5: ldrh32_abs (cpu, imm); return;
|
|
case 6: ldrsh_abs (cpu, imm); return;
|
|
case 7: ldrsh32_abs (cpu, imm); return;
|
|
case 8: str32_abs (cpu, imm); return;
|
|
case 9: ldr32_abs (cpu, imm); return;
|
|
case 10: ldrsw_abs (cpu, imm); return;
|
|
case 12: str_abs (cpu, imm); return;
|
|
case 13: ldr_abs (cpu, imm); return;
|
|
case 14: prfm_abs (cpu, imm); return;
|
|
|
|
default:
|
|
case 11:
|
|
case 15:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
/* FReg operations. */
|
|
switch (dispatch)
|
|
{
|
|
case 3: fldrq_abs (cpu, imm); return;
|
|
case 9: fldrs_abs (cpu, imm); return;
|
|
case 13: fldrd_abs (cpu, imm); return;
|
|
|
|
case 0: fstrb_abs (cpu, imm); return;
|
|
case 2: fstrq_abs (cpu, imm); return;
|
|
case 4: fstrh_abs (cpu, imm); return;
|
|
case 8: fstrs_abs (cpu, imm); return;
|
|
case 12: fstrd_abs (cpu, imm); return;
|
|
|
|
case 1: /* LDR 8 bit FP. */
|
|
case 5: /* LDR 8 bit FP. */
|
|
HALT_NYI;
|
|
|
|
default:
|
|
case 6:
|
|
case 7:
|
|
case 10:
|
|
case 11:
|
|
case 14:
|
|
case 15:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexLoadExclusive (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[29:24] = 001000;
|
|
instr[31,30] = size
|
|
instr[23] = 0 if exclusive
|
|
instr[22] = L : 1 if load, 0 if store
|
|
instr[21] = 1 if pair
|
|
instr[20,16] = Rs
|
|
instr[15] = o0 : 1 if ordered
|
|
instr[14,10] = Rt2
|
|
instr[9,5] = Rn
|
|
instr[4.0] = Rt. */
|
|
|
|
switch (uimm (aarch64_get_instr (cpu), 22, 21))
|
|
{
|
|
case 2: ldxr (cpu); return;
|
|
case 0: stxr (cpu); return;
|
|
default: HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexLoadOther (sim_cpu *cpu)
|
|
{
|
|
uint32_t dispatch;
|
|
|
|
/* instr[29,25] = 111_0
|
|
instr[24] == 0 ==> dispatch, 1 ==> ldst reg unsigned immediate
|
|
instr[21:11,10] is the secondary dispatch. */
|
|
if (uimm (aarch64_get_instr (cpu), 24, 24))
|
|
{
|
|
dexLoadUnsignedImmediate (cpu);
|
|
return;
|
|
}
|
|
|
|
dispatch = ( (uimm (aarch64_get_instr (cpu), 21, 21) << 2)
|
|
| uimm (aarch64_get_instr (cpu), 11, 10));
|
|
switch (dispatch)
|
|
{
|
|
case 0: dexLoadUnscaledImmediate (cpu); return;
|
|
case 1: dexLoadImmediatePrePost (cpu); return;
|
|
case 3: dexLoadImmediatePrePost (cpu); return;
|
|
case 6: dexLoadRegisterOffset (cpu); return;
|
|
|
|
default:
|
|
case 2:
|
|
case 4:
|
|
case 5:
|
|
case 7:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
store_pair_u32 (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rd, SP_OK);
|
|
|
|
if ((rn == rd || rm == rd) && wb != NoWriteBack)
|
|
HALT_UNALLOC; /* ??? */
|
|
|
|
offset <<= 2;
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_mem_u32 (cpu, address,
|
|
aarch64_get_reg_u32 (cpu, rm, NO_SP));
|
|
aarch64_set_mem_u32 (cpu, address + 4,
|
|
aarch64_get_reg_u32 (cpu, rn, NO_SP));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK, address);
|
|
}
|
|
|
|
static void
|
|
store_pair_u64 (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rd, SP_OK);
|
|
|
|
if ((rn == rd || rm == rd) && wb != NoWriteBack)
|
|
HALT_UNALLOC; /* ??? */
|
|
|
|
offset <<= 3;
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_mem_u64 (cpu, address,
|
|
aarch64_get_reg_u64 (cpu, rm, SP_OK));
|
|
aarch64_set_mem_u64 (cpu, address + 8,
|
|
aarch64_get_reg_u64 (cpu, rn, SP_OK));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK, address);
|
|
}
|
|
|
|
static void
|
|
load_pair_u32 (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rd, SP_OK);
|
|
|
|
/* treat this as unalloc to make sure we don't do it. */
|
|
if (rn == rm)
|
|
HALT_UNALLOC;
|
|
|
|
offset <<= 2;
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_reg_u64 (cpu, rm, SP_OK, aarch64_get_mem_u32 (cpu, address));
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, aarch64_get_mem_u32 (cpu, address + 4));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK, address);
|
|
}
|
|
|
|
static void
|
|
load_pair_s32 (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rd, SP_OK);
|
|
|
|
/* Treat this as unalloc to make sure we don't do it. */
|
|
if (rn == rm)
|
|
HALT_UNALLOC;
|
|
|
|
offset <<= 2;
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_reg_s64 (cpu, rm, SP_OK, aarch64_get_mem_s32 (cpu, address));
|
|
aarch64_set_reg_s64 (cpu, rn, SP_OK, aarch64_get_mem_s32 (cpu, address + 4));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK, address);
|
|
}
|
|
|
|
static void
|
|
load_pair_u64 (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rd, SP_OK);
|
|
|
|
/* Treat this as unalloc to make sure we don't do it. */
|
|
if (rn == rm)
|
|
HALT_UNALLOC;
|
|
|
|
offset <<= 3;
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_reg_u64 (cpu, rm, SP_OK, aarch64_get_mem_u64 (cpu, address));
|
|
aarch64_set_reg_u64 (cpu, rn, SP_OK, aarch64_get_mem_u64 (cpu, address + 8));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK, address);
|
|
}
|
|
|
|
static void
|
|
dex_load_store_pair_gr (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,30] = size (10=> 64-bit, 01=> signed 32-bit, 00=> 32-bit)
|
|
instr[29,25] = instruction encoding: 101_0
|
|
instr[26] = V : 1 if fp 0 if gp
|
|
instr[24,23] = addressing mode (10=> offset, 01=> post, 11=> pre)
|
|
instr[22] = load/store (1=> load)
|
|
instr[21,15] = signed, scaled, offset
|
|
instr[14,10] = Rn
|
|
instr[ 9, 5] = Rd
|
|
instr[ 4, 0] = Rm. */
|
|
|
|
uint32_t dispatch = ((uimm (aarch64_get_instr (cpu), 31, 30) << 3)
|
|
| uimm (aarch64_get_instr (cpu), 24, 22));
|
|
int32_t offset = simm32 (aarch64_get_instr (cpu), 21, 15);
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 2: store_pair_u32 (cpu, offset, Post); return;
|
|
case 3: load_pair_u32 (cpu, offset, Post); return;
|
|
case 4: store_pair_u32 (cpu, offset, NoWriteBack); return;
|
|
case 5: load_pair_u32 (cpu, offset, NoWriteBack); return;
|
|
case 6: store_pair_u32 (cpu, offset, Pre); return;
|
|
case 7: load_pair_u32 (cpu, offset, Pre); return;
|
|
|
|
case 11: load_pair_s32 (cpu, offset, Post); return;
|
|
case 13: load_pair_s32 (cpu, offset, NoWriteBack); return;
|
|
case 15: load_pair_s32 (cpu, offset, Pre); return;
|
|
|
|
case 18: store_pair_u64 (cpu, offset, Post); return;
|
|
case 19: load_pair_u64 (cpu, offset, Post); return;
|
|
case 20: store_pair_u64 (cpu, offset, NoWriteBack); return;
|
|
case 21: load_pair_u64 (cpu, offset, NoWriteBack); return;
|
|
case 22: store_pair_u64 (cpu, offset, Pre); return;
|
|
case 23: load_pair_u64 (cpu, offset, Pre); return;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
store_pair_float (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rd, SP_OK);
|
|
|
|
offset <<= 2;
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_mem_float (cpu, address, aarch64_get_FP_float (cpu, rm));
|
|
aarch64_set_mem_float (cpu, address + 4, aarch64_get_FP_float (cpu, rn));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK, address);
|
|
}
|
|
|
|
static void
|
|
store_pair_double (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rd, SP_OK);
|
|
|
|
offset <<= 3;
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_mem_double (cpu, address, aarch64_get_FP_double (cpu, rm));
|
|
aarch64_set_mem_double (cpu, address + 8, aarch64_get_FP_double (cpu, rn));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK, address);
|
|
}
|
|
|
|
static void
|
|
store_pair_long_double (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
FRegister a;
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rd, SP_OK);
|
|
|
|
offset <<= 4;
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_get_FP_long_double (cpu, rm, & a);
|
|
aarch64_set_mem_long_double (cpu, address, a);
|
|
aarch64_get_FP_long_double (cpu, rn, & a);
|
|
aarch64_set_mem_long_double (cpu, address + 16, a);
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK, address);
|
|
}
|
|
|
|
static void
|
|
load_pair_float (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rd, SP_OK);
|
|
|
|
if (rm == rn)
|
|
HALT_UNALLOC;
|
|
|
|
offset <<= 2;
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_FP_float (cpu, rm, aarch64_get_mem_float (cpu, address));
|
|
aarch64_set_FP_float (cpu, rn, aarch64_get_mem_float (cpu, address + 4));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK, address);
|
|
}
|
|
|
|
static void
|
|
load_pair_double (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rd, SP_OK);
|
|
|
|
if (rm == rn)
|
|
HALT_UNALLOC;
|
|
|
|
offset <<= 3;
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_set_FP_double (cpu, rm, aarch64_get_mem_double (cpu, address));
|
|
aarch64_set_FP_double (cpu, rn, aarch64_get_mem_double (cpu, address + 8));
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK, address);
|
|
}
|
|
|
|
static void
|
|
load_pair_long_double (sim_cpu *cpu, int32_t offset, WriteBack wb)
|
|
{
|
|
FRegister a;
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t address = aarch64_get_reg_u64 (cpu, rd, SP_OK);
|
|
|
|
if (rm == rn)
|
|
HALT_UNALLOC;
|
|
|
|
offset <<= 4;
|
|
|
|
if (wb != Post)
|
|
address += offset;
|
|
|
|
aarch64_get_mem_long_double (cpu, address, & a);
|
|
aarch64_set_FP_long_double (cpu, rm, a);
|
|
aarch64_get_mem_long_double (cpu, address + 16, & a);
|
|
aarch64_set_FP_long_double (cpu, rn, a);
|
|
|
|
if (wb == Post)
|
|
address += offset;
|
|
|
|
if (wb != NoWriteBack)
|
|
aarch64_set_reg_u64 (cpu, rd, SP_OK, address);
|
|
}
|
|
|
|
static void
|
|
dex_load_store_pair_fp (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,30] = size (10=> 128-bit, 01=> 64-bit, 00=> 32-bit)
|
|
instr[29,25] = instruction encoding
|
|
instr[24,23] = addressing mode (10=> offset, 01=> post, 11=> pre)
|
|
instr[22] = load/store (1=> load)
|
|
instr[21,15] = signed, scaled, offset
|
|
instr[14,10] = Rn
|
|
instr[ 9, 5] = Rd
|
|
instr[ 4, 0] = Rm */
|
|
|
|
uint32_t dispatch = ((uimm (aarch64_get_instr (cpu), 31, 30) << 3)
|
|
| uimm (aarch64_get_instr (cpu), 24, 22));
|
|
int32_t offset = simm32 (aarch64_get_instr (cpu), 21, 15);
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 2: store_pair_float (cpu, offset, Post); return;
|
|
case 3: load_pair_float (cpu, offset, Post); return;
|
|
case 4: store_pair_float (cpu, offset, NoWriteBack); return;
|
|
case 5: load_pair_float (cpu, offset, NoWriteBack); return;
|
|
case 6: store_pair_float (cpu, offset, Pre); return;
|
|
case 7: load_pair_float (cpu, offset, Pre); return;
|
|
|
|
case 10: store_pair_double (cpu, offset, Post); return;
|
|
case 11: load_pair_double (cpu, offset, Post); return;
|
|
case 12: store_pair_double (cpu, offset, NoWriteBack); return;
|
|
case 13: load_pair_double (cpu, offset, NoWriteBack); return;
|
|
case 14: store_pair_double (cpu, offset, Pre); return;
|
|
case 15: load_pair_double (cpu, offset, Pre); return;
|
|
|
|
case 18: store_pair_long_double (cpu, offset, Post); return;
|
|
case 19: load_pair_long_double (cpu, offset, Post); return;
|
|
case 20: store_pair_long_double (cpu, offset, NoWriteBack); return;
|
|
case 21: load_pair_long_double (cpu, offset, NoWriteBack); return;
|
|
case 22: store_pair_long_double (cpu, offset, Pre); return;
|
|
case 23: load_pair_long_double (cpu, offset, Pre); return;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static inline unsigned
|
|
vec_reg (unsigned v, unsigned o)
|
|
{
|
|
return (v + o) & 0x3F;
|
|
}
|
|
|
|
/* Load multiple N-element structures to N consecutive registers. */
|
|
static void
|
|
vec_load (sim_cpu *cpu, uint64_t address, unsigned N)
|
|
{
|
|
int all = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned size = uimm (aarch64_get_instr (cpu), 11, 10);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
switch (size)
|
|
{
|
|
case 0: /* 8-bit operations. */
|
|
if (all)
|
|
for (i = 0; i < (16 * N); i++)
|
|
aarch64_set_vec_u8 (cpu, vec_reg (vd, i >> 4), i & 15,
|
|
aarch64_get_mem_u8 (cpu, address + i));
|
|
else
|
|
for (i = 0; i < (8 * N); i++)
|
|
aarch64_set_vec_u8 (cpu, vec_reg (vd, i >> 3), i & 7,
|
|
aarch64_get_mem_u8 (cpu, address + i));
|
|
return;
|
|
|
|
case 1: /* 16-bit operations. */
|
|
if (all)
|
|
for (i = 0; i < (8 * N); i++)
|
|
aarch64_set_vec_u16 (cpu, vec_reg (vd, i >> 3), i & 7,
|
|
aarch64_get_mem_u16 (cpu, address + i * 2));
|
|
else
|
|
for (i = 0; i < (4 * N); i++)
|
|
aarch64_set_vec_u16 (cpu, vec_reg (vd, i >> 2), i & 3,
|
|
aarch64_get_mem_u16 (cpu, address + i * 2));
|
|
return;
|
|
|
|
case 2: /* 32-bit operations. */
|
|
if (all)
|
|
for (i = 0; i < (4 * N); i++)
|
|
aarch64_set_vec_u32 (cpu, vec_reg (vd, i >> 2), i & 3,
|
|
aarch64_get_mem_u32 (cpu, address + i * 4));
|
|
else
|
|
for (i = 0; i < (2 * N); i++)
|
|
aarch64_set_vec_u32 (cpu, vec_reg (vd, i >> 1), i & 1,
|
|
aarch64_get_mem_u32 (cpu, address + i * 4));
|
|
return;
|
|
|
|
case 3: /* 64-bit operations. */
|
|
if (all)
|
|
for (i = 0; i < (2 * N); i++)
|
|
aarch64_set_vec_u64 (cpu, vec_reg (vd, i >> 1), i & 1,
|
|
aarch64_get_mem_u64 (cpu, address + i * 8));
|
|
else
|
|
for (i = 0; i < N; i++)
|
|
aarch64_set_vec_u64 (cpu, vec_reg (vd, i), 0,
|
|
aarch64_get_mem_u64 (cpu, address + i * 8));
|
|
return;
|
|
|
|
default:
|
|
HALT_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
/* LD4: load multiple 4-element to four consecutive registers. */
|
|
static void
|
|
LD4 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
vec_load (cpu, address, 4);
|
|
}
|
|
|
|
/* LD3: load multiple 3-element structures to three consecutive registers. */
|
|
static void
|
|
LD3 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
vec_load (cpu, address, 3);
|
|
}
|
|
|
|
/* LD2: load multiple 2-element structures to two consecutive registers. */
|
|
static void
|
|
LD2 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
vec_load (cpu, address, 2);
|
|
}
|
|
|
|
/* Load multiple 1-element structures into one register. */
|
|
static void
|
|
LD1_1 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
int all = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned size = uimm (aarch64_get_instr (cpu), 11, 10);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
/* LD1 {Vd.16b}, addr, #16 */
|
|
/* LD1 {Vd.8b}, addr, #8 */
|
|
for (i = 0; i < (all ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i,
|
|
aarch64_get_mem_u8 (cpu, address + i));
|
|
return;
|
|
|
|
case 1:
|
|
/* LD1 {Vd.8h}, addr, #16 */
|
|
/* LD1 {Vd.4h}, addr, #8 */
|
|
for (i = 0; i < (all ? 8 : 4); i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i,
|
|
aarch64_get_mem_u16 (cpu, address + i * 2));
|
|
return;
|
|
|
|
case 2:
|
|
/* LD1 {Vd.4s}, addr, #16 */
|
|
/* LD1 {Vd.2s}, addr, #8 */
|
|
for (i = 0; i < (all ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i,
|
|
aarch64_get_mem_u32 (cpu, address + i * 4));
|
|
return;
|
|
|
|
case 3:
|
|
/* LD1 {Vd.2d}, addr, #16 */
|
|
/* LD1 {Vd.1d}, addr, #8 */
|
|
for (i = 0; i < (all ? 2 : 1); i++)
|
|
aarch64_set_vec_u64 (cpu, vd, i,
|
|
aarch64_get_mem_u64 (cpu, address + i * 8));
|
|
return;
|
|
|
|
default:
|
|
HALT_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
/* Load multiple 1-element structures into two registers. */
|
|
static void
|
|
LD1_2 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
/* FIXME: This algorithm is *exactly* the same as the LD2 version.
|
|
So why have two different instructions ? There must be something
|
|
wrong somewhere. */
|
|
vec_load (cpu, address, 2);
|
|
}
|
|
|
|
/* Load multiple 1-element structures into three registers. */
|
|
static void
|
|
LD1_3 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
/* FIXME: This algorithm is *exactly* the same as the LD3 version.
|
|
So why have two different instructions ? There must be something
|
|
wrong somewhere. */
|
|
vec_load (cpu, address, 3);
|
|
}
|
|
|
|
/* Load multiple 1-element structures into four registers. */
|
|
static void
|
|
LD1_4 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
/* FIXME: This algorithm is *exactly* the same as the LD4 version.
|
|
So why have two different instructions ? There must be something
|
|
wrong somewhere. */
|
|
vec_load (cpu, address, 4);
|
|
}
|
|
|
|
/* Store multiple N-element structures to N consecutive registers. */
|
|
static void
|
|
vec_store (sim_cpu *cpu, uint64_t address, unsigned N)
|
|
{
|
|
int all = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned size = uimm (aarch64_get_instr (cpu), 11, 10);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
switch (size)
|
|
{
|
|
case 0: /* 8-bit operations. */
|
|
if (all)
|
|
for (i = 0; i < (16 * N); i++)
|
|
aarch64_set_mem_u8
|
|
(cpu, address + i,
|
|
aarch64_get_vec_u8 (cpu, vec_reg (vd, i >> 4), i & 15));
|
|
else
|
|
for (i = 0; i < (8 * N); i++)
|
|
aarch64_set_mem_u8
|
|
(cpu, address + i,
|
|
aarch64_get_vec_u8 (cpu, vec_reg (vd, i >> 3), i & 7));
|
|
return;
|
|
|
|
case 1: /* 16-bit operations. */
|
|
if (all)
|
|
for (i = 0; i < (8 * N); i++)
|
|
aarch64_set_mem_u16
|
|
(cpu, address + i * 2,
|
|
aarch64_get_vec_u16 (cpu, vec_reg (vd, i >> 3), i & 7));
|
|
else
|
|
for (i = 0; i < (4 * N); i++)
|
|
aarch64_set_mem_u16
|
|
(cpu, address + i * 2,
|
|
aarch64_get_vec_u16 (cpu, vec_reg (vd, i >> 2), i & 3));
|
|
return;
|
|
|
|
case 2: /* 32-bit operations. */
|
|
if (all)
|
|
for (i = 0; i < (4 * N); i++)
|
|
aarch64_set_mem_u32
|
|
(cpu, address + i * 4,
|
|
aarch64_get_vec_u32 (cpu, vec_reg (vd, i >> 2), i & 3));
|
|
else
|
|
for (i = 0; i < (2 * N); i++)
|
|
aarch64_set_mem_u32
|
|
(cpu, address + i * 4,
|
|
aarch64_get_vec_u32 (cpu, vec_reg (vd, i >> 1), i & 1));
|
|
return;
|
|
|
|
case 3: /* 64-bit operations. */
|
|
if (all)
|
|
for (i = 0; i < (2 * N); i++)
|
|
aarch64_set_mem_u64
|
|
(cpu, address + i * 8,
|
|
aarch64_get_vec_u64 (cpu, vec_reg (vd, i >> 1), i & 1));
|
|
else
|
|
for (i = 0; i < N; i++)
|
|
aarch64_set_mem_u64
|
|
(cpu, address + i * 8,
|
|
aarch64_get_vec_u64 (cpu, vec_reg (vd, i), 0));
|
|
return;
|
|
|
|
default:
|
|
HALT_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
/* Store multiple 4-element structure to four consecutive registers. */
|
|
static void
|
|
ST4 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
vec_store (cpu, address, 4);
|
|
}
|
|
|
|
/* Store multiple 3-element structures to three consecutive registers. */
|
|
static void
|
|
ST3 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
vec_store (cpu, address, 3);
|
|
}
|
|
|
|
/* Store multiple 2-element structures to two consecutive registers. */
|
|
static void
|
|
ST2 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
vec_store (cpu, address, 2);
|
|
}
|
|
|
|
/* Store multiple 1-element structures into one register. */
|
|
static void
|
|
ST1_1 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
int all = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned size = uimm (aarch64_get_instr (cpu), 11, 10);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned i;
|
|
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
for (i = 0; i < (all ? 16 : 8); i++)
|
|
aarch64_set_mem_u8 (cpu, address + i,
|
|
aarch64_get_vec_u8 (cpu, vd, i));
|
|
return;
|
|
|
|
case 1:
|
|
for (i = 0; i < (all ? 8 : 4); i++)
|
|
aarch64_set_mem_u16 (cpu, address + i * 2,
|
|
aarch64_get_vec_u16 (cpu, vd, i));
|
|
return;
|
|
|
|
case 2:
|
|
for (i = 0; i < (all ? 4 : 2); i++)
|
|
aarch64_set_mem_u32 (cpu, address + i * 4,
|
|
aarch64_get_vec_u32 (cpu, vd, i));
|
|
return;
|
|
|
|
case 3:
|
|
for (i = 0; i < (all ? 2 : 1); i++)
|
|
aarch64_set_mem_u64 (cpu, address + i * 8,
|
|
aarch64_get_vec_u64 (cpu, vd, i));
|
|
return;
|
|
|
|
default:
|
|
HALT_UNREACHABLE;
|
|
}
|
|
}
|
|
|
|
/* Store multiple 1-element structures into two registers. */
|
|
static void
|
|
ST1_2 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
/* FIXME: This algorithm is *exactly* the same as the ST2 version.
|
|
So why have two different instructions ? There must be
|
|
something wrong somewhere. */
|
|
vec_store (cpu, address, 2);
|
|
}
|
|
|
|
/* Store multiple 1-element structures into three registers. */
|
|
static void
|
|
ST1_3 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
/* FIXME: This algorithm is *exactly* the same as the ST3 version.
|
|
So why have two different instructions ? There must be
|
|
something wrong somewhere. */
|
|
vec_store (cpu, address, 3);
|
|
}
|
|
|
|
/* Store multiple 1-element structures into four registers. */
|
|
static void
|
|
ST1_4 (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
/* FIXME: This algorithm is *exactly* the same as the ST4 version.
|
|
So why have two different instructions ? There must be
|
|
something wrong somewhere. */
|
|
vec_store (cpu, address, 4);
|
|
}
|
|
|
|
static void
|
|
do_vec_LDnR (sim_cpu *cpu, uint64_t address)
|
|
{
|
|
/* instr[31] = 0
|
|
instr[30] = element selector 0=>half, 1=>all elements
|
|
instr[29,24] = 00 1101
|
|
instr[23] = 0=>simple, 1=>post
|
|
instr[22] = 1
|
|
instr[21] = width: LD1R-or-LD3R (0) / LD2R-or-LD4R (1)
|
|
instr[20,16] = 0 0000 (simple), Vinc (reg-post-inc, no SP),
|
|
11111 (immediate post inc)
|
|
instr[15,14] = 11
|
|
instr[13] = width: LD1R-or-LD2R (0) / LD3R-or-LD4R (1)
|
|
instr[12] = 0
|
|
instr[11,10] = element size 00=> byte(b), 01=> half(h),
|
|
10=> word(s), 11=> double(d)
|
|
instr[9,5] = address
|
|
instr[4,0] = Vd */
|
|
|
|
unsigned full = uimm (aarch64_get_instr (cpu), 30, 30);
|
|
unsigned vd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
unsigned size = uimm (aarch64_get_instr (cpu), 11, 10);
|
|
int i;
|
|
|
|
NYI_assert (29, 24, 0x0D);
|
|
NYI_assert (22, 22, 1);
|
|
NYI_assert (15, 14, 3);
|
|
NYI_assert (12, 12, 0);
|
|
|
|
switch ((uimm (aarch64_get_instr (cpu), 13, 13) << 1)
|
|
| uimm (aarch64_get_instr (cpu), 21, 21))
|
|
{
|
|
case 0: /* LD1R. */
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
{
|
|
uint8_t val = aarch64_get_mem_u8 (cpu, address);
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
aarch64_set_vec_u8 (cpu, vd, i, val);
|
|
break;
|
|
}
|
|
|
|
case 1:
|
|
{
|
|
uint16_t val = aarch64_get_mem_u16 (cpu, address);
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
aarch64_set_vec_u16 (cpu, vd, i, val);
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
uint32_t val = aarch64_get_mem_u32 (cpu, address);
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
aarch64_set_vec_u32 (cpu, vd, i, val);
|
|
break;
|
|
}
|
|
|
|
case 3:
|
|
{
|
|
uint64_t val = aarch64_get_mem_u64 (cpu, address);
|
|
for (i = 0; i < (full ? 2 : 1); i++)
|
|
aarch64_set_vec_u64 (cpu, vd, i, val);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
break;
|
|
|
|
case 1: /* LD2R. */
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
{
|
|
uint8_t val1 = aarch64_get_mem_u8 (cpu, address);
|
|
uint8_t val2 = aarch64_get_mem_u8 (cpu, address + 1);
|
|
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
{
|
|
aarch64_set_vec_u8 (cpu, vd, 0, val1);
|
|
aarch64_set_vec_u8 (cpu, vd + 1, 0, val2);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 1:
|
|
{
|
|
uint16_t val1 = aarch64_get_mem_u16 (cpu, address);
|
|
uint16_t val2 = aarch64_get_mem_u16 (cpu, address + 2);
|
|
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
{
|
|
aarch64_set_vec_u16 (cpu, vd, 0, val1);
|
|
aarch64_set_vec_u16 (cpu, vd + 1, 0, val2);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
uint32_t val1 = aarch64_get_mem_u32 (cpu, address);
|
|
uint32_t val2 = aarch64_get_mem_u32 (cpu, address + 4);
|
|
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
{
|
|
aarch64_set_vec_u32 (cpu, vd, 0, val1);
|
|
aarch64_set_vec_u32 (cpu, vd + 1, 0, val2);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 3:
|
|
{
|
|
uint64_t val1 = aarch64_get_mem_u64 (cpu, address);
|
|
uint64_t val2 = aarch64_get_mem_u64 (cpu, address + 8);
|
|
|
|
for (i = 0; i < (full ? 2 : 1); i++)
|
|
{
|
|
aarch64_set_vec_u64 (cpu, vd, 0, val1);
|
|
aarch64_set_vec_u64 (cpu, vd + 1, 0, val2);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
break;
|
|
|
|
case 2: /* LD3R. */
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
{
|
|
uint8_t val1 = aarch64_get_mem_u8 (cpu, address);
|
|
uint8_t val2 = aarch64_get_mem_u8 (cpu, address + 1);
|
|
uint8_t val3 = aarch64_get_mem_u8 (cpu, address + 2);
|
|
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
{
|
|
aarch64_set_vec_u8 (cpu, vd, 0, val1);
|
|
aarch64_set_vec_u8 (cpu, vd + 1, 0, val2);
|
|
aarch64_set_vec_u8 (cpu, vd + 2, 0, val3);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
{
|
|
uint32_t val1 = aarch64_get_mem_u16 (cpu, address);
|
|
uint32_t val2 = aarch64_get_mem_u16 (cpu, address + 2);
|
|
uint32_t val3 = aarch64_get_mem_u16 (cpu, address + 4);
|
|
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
{
|
|
aarch64_set_vec_u16 (cpu, vd, 0, val1);
|
|
aarch64_set_vec_u16 (cpu, vd + 1, 0, val2);
|
|
aarch64_set_vec_u16 (cpu, vd + 2, 0, val3);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
{
|
|
uint32_t val1 = aarch64_get_mem_u32 (cpu, address);
|
|
uint32_t val2 = aarch64_get_mem_u32 (cpu, address + 4);
|
|
uint32_t val3 = aarch64_get_mem_u32 (cpu, address + 8);
|
|
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
{
|
|
aarch64_set_vec_u32 (cpu, vd, 0, val1);
|
|
aarch64_set_vec_u32 (cpu, vd + 1, 0, val2);
|
|
aarch64_set_vec_u32 (cpu, vd + 2, 0, val3);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
{
|
|
uint64_t val1 = aarch64_get_mem_u64 (cpu, address);
|
|
uint64_t val2 = aarch64_get_mem_u64 (cpu, address + 8);
|
|
uint64_t val3 = aarch64_get_mem_u64 (cpu, address + 16);
|
|
|
|
for (i = 0; i < (full ? 2 : 1); i++)
|
|
{
|
|
aarch64_set_vec_u64 (cpu, vd, 0, val1);
|
|
aarch64_set_vec_u64 (cpu, vd + 1, 0, val2);
|
|
aarch64_set_vec_u64 (cpu, vd + 2, 0, val3);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
break;
|
|
|
|
case 3: /* LD4R. */
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
{
|
|
uint8_t val1 = aarch64_get_mem_u8 (cpu, address);
|
|
uint8_t val2 = aarch64_get_mem_u8 (cpu, address + 1);
|
|
uint8_t val3 = aarch64_get_mem_u8 (cpu, address + 2);
|
|
uint8_t val4 = aarch64_get_mem_u8 (cpu, address + 3);
|
|
|
|
for (i = 0; i < (full ? 16 : 8); i++)
|
|
{
|
|
aarch64_set_vec_u8 (cpu, vd, 0, val1);
|
|
aarch64_set_vec_u8 (cpu, vd + 1, 0, val2);
|
|
aarch64_set_vec_u8 (cpu, vd + 2, 0, val3);
|
|
aarch64_set_vec_u8 (cpu, vd + 3, 0, val4);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
{
|
|
uint32_t val1 = aarch64_get_mem_u16 (cpu, address);
|
|
uint32_t val2 = aarch64_get_mem_u16 (cpu, address + 2);
|
|
uint32_t val3 = aarch64_get_mem_u16 (cpu, address + 4);
|
|
uint32_t val4 = aarch64_get_mem_u16 (cpu, address + 6);
|
|
|
|
for (i = 0; i < (full ? 8 : 4); i++)
|
|
{
|
|
aarch64_set_vec_u16 (cpu, vd, 0, val1);
|
|
aarch64_set_vec_u16 (cpu, vd + 1, 0, val2);
|
|
aarch64_set_vec_u16 (cpu, vd + 2, 0, val3);
|
|
aarch64_set_vec_u16 (cpu, vd + 3, 0, val4);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
{
|
|
uint32_t val1 = aarch64_get_mem_u32 (cpu, address);
|
|
uint32_t val2 = aarch64_get_mem_u32 (cpu, address + 4);
|
|
uint32_t val3 = aarch64_get_mem_u32 (cpu, address + 8);
|
|
uint32_t val4 = aarch64_get_mem_u32 (cpu, address + 12);
|
|
|
|
for (i = 0; i < (full ? 4 : 2); i++)
|
|
{
|
|
aarch64_set_vec_u32 (cpu, vd, 0, val1);
|
|
aarch64_set_vec_u32 (cpu, vd + 1, 0, val2);
|
|
aarch64_set_vec_u32 (cpu, vd + 2, 0, val3);
|
|
aarch64_set_vec_u32 (cpu, vd + 3, 0, val4);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
{
|
|
uint64_t val1 = aarch64_get_mem_u64 (cpu, address);
|
|
uint64_t val2 = aarch64_get_mem_u64 (cpu, address + 8);
|
|
uint64_t val3 = aarch64_get_mem_u64 (cpu, address + 16);
|
|
uint64_t val4 = aarch64_get_mem_u64 (cpu, address + 24);
|
|
|
|
for (i = 0; i < (full ? 2 : 1); i++)
|
|
{
|
|
aarch64_set_vec_u64 (cpu, vd, 0, val1);
|
|
aarch64_set_vec_u64 (cpu, vd + 1, 0, val2);
|
|
aarch64_set_vec_u64 (cpu, vd + 2, 0, val3);
|
|
aarch64_set_vec_u64 (cpu, vd + 3, 0, val4);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_vec_load_store (sim_cpu *cpu)
|
|
{
|
|
/* {LD|ST}<N> {Vd..Vd+N}, vaddr
|
|
|
|
instr[31] = 0
|
|
instr[30] = element selector 0=>half, 1=>all elements
|
|
instr[29,25] = 00110
|
|
instr[24] = ?
|
|
instr[23] = 0=>simple, 1=>post
|
|
instr[22] = 0=>store, 1=>load
|
|
instr[21] = 0 (LDn) / small(0)-large(1) selector (LDnR)
|
|
instr[20,16] = 00000 (simple), Vinc (reg-post-inc, no SP),
|
|
11111 (immediate post inc)
|
|
instr[15,12] = elements and destinations. eg for load:
|
|
0000=>LD4 => load multiple 4-element to
|
|
four consecutive registers
|
|
0100=>LD3 => load multiple 3-element to
|
|
three consecutive registers
|
|
1000=>LD2 => load multiple 2-element to
|
|
two consecutive registers
|
|
0010=>LD1 => load multiple 1-element to
|
|
four consecutive registers
|
|
0110=>LD1 => load multiple 1-element to
|
|
three consecutive registers
|
|
1010=>LD1 => load multiple 1-element to
|
|
two consecutive registers
|
|
0111=>LD1 => load multiple 1-element to
|
|
one register
|
|
1100=>LDR1,LDR2
|
|
1110=>LDR3,LDR4
|
|
instr[11,10] = element size 00=> byte(b), 01=> half(h),
|
|
10=> word(s), 11=> double(d)
|
|
instr[9,5] = Vn, can be SP
|
|
instr[4,0] = Vd */
|
|
|
|
int post;
|
|
int load;
|
|
unsigned vn;
|
|
uint64_t address;
|
|
int type;
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 31, 31) != 0
|
|
|| uimm (aarch64_get_instr (cpu), 29, 25) != 0x06)
|
|
HALT_NYI;
|
|
|
|
type = uimm (aarch64_get_instr (cpu), 15, 12);
|
|
if (type != 0xE && type != 0xE && uimm (aarch64_get_instr (cpu), 21, 21) != 0)
|
|
HALT_NYI;
|
|
|
|
post = uimm (aarch64_get_instr (cpu), 23, 23);
|
|
load = uimm (aarch64_get_instr (cpu), 22, 22);
|
|
vn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
address = aarch64_get_reg_u64 (cpu, vn, SP_OK);
|
|
|
|
if (post)
|
|
{
|
|
unsigned vm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
|
|
if (vm == R31)
|
|
{
|
|
unsigned sizeof_operation;
|
|
|
|
switch (type)
|
|
{
|
|
case 0: sizeof_operation = 32; break;
|
|
case 4: sizeof_operation = 24; break;
|
|
case 8: sizeof_operation = 16; break;
|
|
|
|
case 0xC:
|
|
sizeof_operation = uimm (aarch64_get_instr (cpu), 21, 21) ? 2 : 1;
|
|
sizeof_operation <<= uimm (aarch64_get_instr (cpu), 11, 10);
|
|
break;
|
|
|
|
case 0xE:
|
|
sizeof_operation = uimm (aarch64_get_instr (cpu), 21, 21) ? 8 : 4;
|
|
sizeof_operation <<= uimm (aarch64_get_instr (cpu), 11, 10);
|
|
break;
|
|
|
|
case 2:
|
|
case 6:
|
|
case 10:
|
|
case 7:
|
|
sizeof_operation = 2 << uimm (aarch64_get_instr (cpu), 11, 10);
|
|
break;
|
|
|
|
default:
|
|
HALT_UNALLOC;
|
|
}
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 30, 30))
|
|
sizeof_operation *= 2;
|
|
|
|
aarch64_set_reg_u64 (cpu, vn, SP_OK, address + sizeof_operation);
|
|
}
|
|
else
|
|
aarch64_set_reg_u64 (cpu, vn, SP_OK,
|
|
address + aarch64_get_reg_u64 (cpu, vm, NO_SP));
|
|
}
|
|
else
|
|
{
|
|
NYI_assert (20, 16, 0);
|
|
}
|
|
|
|
if (load)
|
|
{
|
|
switch (type)
|
|
{
|
|
case 0: LD4 (cpu, address); return;
|
|
case 4: LD3 (cpu, address); return;
|
|
case 8: LD2 (cpu, address); return;
|
|
case 2: LD1_4 (cpu, address); return;
|
|
case 6: LD1_3 (cpu, address); return;
|
|
case 10: LD1_2 (cpu, address); return;
|
|
case 7: LD1_1 (cpu, address); return;
|
|
|
|
case 0xE:
|
|
case 0xC: do_vec_LDnR (cpu, address); return;
|
|
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
/* Stores. */
|
|
switch (type)
|
|
{
|
|
case 0: ST4 (cpu, address); return;
|
|
case 4: ST3 (cpu, address); return;
|
|
case 8: ST2 (cpu, address); return;
|
|
case 2: ST1_4 (cpu, address); return;
|
|
case 6: ST1_3 (cpu, address); return;
|
|
case 10: ST1_2 (cpu, address); return;
|
|
case 7: ST1_1 (cpu, address); return;
|
|
default:
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexLdSt (sim_cpu *cpu)
|
|
{
|
|
/* uint32_t group = dispatchGroup (aarch64_get_instr (cpu));
|
|
assert group == GROUP_LDST_0100 || group == GROUP_LDST_0110 ||
|
|
group == GROUP_LDST_1100 || group == GROUP_LDST_1110
|
|
bits [29,28:26] of a LS are the secondary dispatch vector. */
|
|
uint32_t group2 = dispatchLS (aarch64_get_instr (cpu));
|
|
|
|
switch (group2)
|
|
{
|
|
case LS_EXCL_000:
|
|
dexLoadExclusive (cpu); return;
|
|
|
|
case LS_LIT_010:
|
|
case LS_LIT_011:
|
|
dexLoadLiteral (cpu); return;
|
|
|
|
case LS_OTHER_110:
|
|
case LS_OTHER_111:
|
|
dexLoadOther (cpu); return;
|
|
|
|
case LS_ADVSIMD_001:
|
|
do_vec_load_store (cpu); return;
|
|
|
|
case LS_PAIR_100:
|
|
dex_load_store_pair_gr (cpu); return;
|
|
|
|
case LS_PAIR_101:
|
|
dex_load_store_pair_fp (cpu); return;
|
|
|
|
default:
|
|
/* Should never reach here. */
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
/* Specific decode and execute for group Data Processing Register. */
|
|
|
|
static void
|
|
dexLogicalShiftedRegister (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[28:24] = 01010
|
|
instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[30,29:21] = op,N : 000 ==> AND, 001 ==> BIC,
|
|
010 ==> ORR, 011 ==> ORN
|
|
100 ==> EOR, 101 ==> EON,
|
|
110 ==> ANDS, 111 ==> BICS
|
|
instr[23,22] = shift : 0 ==> LSL, 1 ==> LSR, 2 ==> ASR, 3 ==> ROR
|
|
instr[15,10] = count : must be 0xxxxx for 32 bit
|
|
instr[9,5] = Rn
|
|
instr[4,0] = Rd */
|
|
|
|
/* unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16); */
|
|
uint32_t dispatch;
|
|
Shift shiftType;
|
|
uint32_t size = uimm (aarch64_get_instr (cpu), 31, 31);
|
|
|
|
/* 32 bit operations must have count[5] = 0. */
|
|
/* or else we have an UNALLOC. */
|
|
uint32_t count = uimm (aarch64_get_instr (cpu), 15, 10);
|
|
|
|
if (!size && uimm (count, 5, 5))
|
|
HALT_UNALLOC;
|
|
|
|
shiftType = shift (aarch64_get_instr (cpu), 22);
|
|
|
|
/* dispatch on size:op:N i.e aarch64_get_instr (cpu)[31,29:21]. */
|
|
dispatch = ( (uimm (aarch64_get_instr (cpu), 31, 29) << 1)
|
|
| uimm (aarch64_get_instr (cpu), 21, 21));
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 0: and32_shift (cpu, shiftType, count); return;
|
|
case 1: bic32_shift (cpu, shiftType, count); return;
|
|
case 2: orr32_shift (cpu, shiftType, count); return;
|
|
case 3: orn32_shift (cpu, shiftType, count); return;
|
|
case 4: eor32_shift (cpu, shiftType, count); return;
|
|
case 5: eon32_shift (cpu, shiftType, count); return;
|
|
case 6: ands32_shift (cpu, shiftType, count); return;
|
|
case 7: bics32_shift (cpu, shiftType, count); return;
|
|
case 8: and64_shift (cpu, shiftType, count); return;
|
|
case 9: bic64_shift (cpu, shiftType, count); return;
|
|
case 10:orr64_shift (cpu, shiftType, count); return;
|
|
case 11:orn64_shift (cpu, shiftType, count); return;
|
|
case 12:eor64_shift (cpu, shiftType, count); return;
|
|
case 13:eon64_shift (cpu, shiftType, count); return;
|
|
case 14:ands64_shift (cpu, shiftType, count); return;
|
|
case 15:bics64_shift (cpu, shiftType, count); return;
|
|
default: HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
/* 32 bit conditional select. */
|
|
static void
|
|
csel32 (sim_cpu *cpu, CondCode cc)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
testConditionCode (cpu, cc)
|
|
? aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
: aarch64_get_reg_u32 (cpu, rm, NO_SP));
|
|
}
|
|
|
|
/* 64 bit conditional select. */
|
|
static void
|
|
csel64 (sim_cpu *cpu, CondCode cc)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
testConditionCode (cpu, cc)
|
|
? aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
: aarch64_get_reg_u64 (cpu, rm, NO_SP));
|
|
}
|
|
|
|
/* 32 bit conditional increment. */
|
|
static void
|
|
csinc32 (sim_cpu *cpu, CondCode cc)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
testConditionCode (cpu, cc)
|
|
? aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
: aarch64_get_reg_u32 (cpu, rm, NO_SP) + 1);
|
|
}
|
|
|
|
/* 64 bit conditional increment. */
|
|
static void
|
|
csinc64 (sim_cpu *cpu, CondCode cc)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
testConditionCode (cpu, cc)
|
|
? aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
: aarch64_get_reg_u64 (cpu, rm, NO_SP) + 1);
|
|
}
|
|
|
|
/* 32 bit conditional invert. */
|
|
static void
|
|
csinv32 (sim_cpu *cpu, CondCode cc)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
testConditionCode (cpu, cc)
|
|
? aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
: ~ aarch64_get_reg_u32 (cpu, rm, NO_SP));
|
|
}
|
|
|
|
/* 64 bit conditional invert. */
|
|
static void
|
|
csinv64 (sim_cpu *cpu, CondCode cc)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
testConditionCode (cpu, cc)
|
|
? aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
: ~ aarch64_get_reg_u64 (cpu, rm, NO_SP));
|
|
}
|
|
|
|
/* 32 bit conditional negate. */
|
|
static void
|
|
csneg32 (sim_cpu *cpu, CondCode cc)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
testConditionCode (cpu, cc)
|
|
? aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
: - aarch64_get_reg_u32 (cpu, rm, NO_SP));
|
|
}
|
|
|
|
/* 64 bit conditional negate. */
|
|
static void
|
|
csneg64 (sim_cpu *cpu, CondCode cc)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
testConditionCode (cpu, cc)
|
|
? aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
: - aarch64_get_reg_u64 (cpu, rm, NO_SP));
|
|
}
|
|
|
|
static void
|
|
dexCondSelect (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[28,21] = 11011011
|
|
instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[30:11,10] = op : 000 ==> CSEL, 001 ==> CSINC,
|
|
100 ==> CSINV, 101 ==> CSNEG,
|
|
_1_ ==> UNALLOC
|
|
instr[29] = S : 0 ==> ok, 1 ==> UNALLOC
|
|
instr[15,12] = cond
|
|
instr[29] = S : 0 ==> ok, 1 ==> UNALLOC */
|
|
|
|
CondCode cc;
|
|
uint32_t dispatch;
|
|
uint32_t S = uimm (aarch64_get_instr (cpu), 29, 29);
|
|
uint32_t op2 = uimm (aarch64_get_instr (cpu), 11, 10);
|
|
|
|
if (S == 1)
|
|
HALT_UNALLOC;
|
|
|
|
if (op2 & 0x2)
|
|
HALT_UNALLOC;
|
|
|
|
cc = condcode (aarch64_get_instr (cpu), 12);
|
|
dispatch = ((uimm (aarch64_get_instr (cpu), 31, 30) << 1) | op2);
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 0: csel32 (cpu, cc); return;
|
|
case 1: csinc32 (cpu, cc); return;
|
|
case 2: csinv32 (cpu, cc); return;
|
|
case 3: csneg32 (cpu, cc); return;
|
|
case 4: csel64 (cpu, cc); return;
|
|
case 5: csinc64 (cpu, cc); return;
|
|
case 6: csinv64 (cpu, cc); return;
|
|
case 7: csneg64 (cpu, cc); return;
|
|
default: HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
/* Some helpers for counting leading 1 or 0 bits. */
|
|
|
|
/* Counts the number of leading bits which are the same
|
|
in a 32 bit value in the range 1 to 32. */
|
|
static uint32_t
|
|
leading32 (uint32_t value)
|
|
{
|
|
int32_t mask= 0xffff0000;
|
|
uint32_t count= 16; /* Counts number of bits set in mask. */
|
|
uint32_t lo = 1; /* Lower bound for number of sign bits. */
|
|
uint32_t hi = 32; /* Upper bound for number of sign bits. */
|
|
|
|
while (lo + 1 < hi)
|
|
{
|
|
int32_t test = (value & mask);
|
|
|
|
if (test == 0 || test == mask)
|
|
{
|
|
lo = count;
|
|
count = (lo + hi) / 2;
|
|
mask >>= (count - lo);
|
|
}
|
|
else
|
|
{
|
|
hi = count;
|
|
count = (lo + hi) / 2;
|
|
mask <<= hi - count;
|
|
}
|
|
}
|
|
|
|
if (lo != hi)
|
|
{
|
|
int32_t test;
|
|
|
|
mask >>= 1;
|
|
test = (value & mask);
|
|
|
|
if (test == 0 || test == mask)
|
|
count = hi;
|
|
else
|
|
count = lo;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/* Counts the number of leading bits which are the same
|
|
in a 64 bit value in the range 1 to 64. */
|
|
static uint64_t
|
|
leading64 (uint64_t value)
|
|
{
|
|
int64_t mask= 0xffffffff00000000LL;
|
|
uint64_t count = 32; /* Counts number of bits set in mask. */
|
|
uint64_t lo = 1; /* Lower bound for number of sign bits. */
|
|
uint64_t hi = 64; /* Upper bound for number of sign bits. */
|
|
|
|
while (lo + 1 < hi)
|
|
{
|
|
int64_t test = (value & mask);
|
|
|
|
if (test == 0 || test == mask)
|
|
{
|
|
lo = count;
|
|
count = (lo + hi) / 2;
|
|
mask >>= (count - lo);
|
|
}
|
|
else
|
|
{
|
|
hi = count;
|
|
count = (lo + hi) / 2;
|
|
mask <<= hi - count;
|
|
}
|
|
}
|
|
|
|
if (lo != hi)
|
|
{
|
|
int64_t test;
|
|
|
|
mask >>= 1;
|
|
test = (value & mask);
|
|
|
|
if (test == 0 || test == mask)
|
|
count = hi;
|
|
else
|
|
count = lo;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/* Bit operations. */
|
|
/* N.B register args may not be SP. */
|
|
|
|
/* 32 bit count leading sign bits. */
|
|
static void
|
|
cls32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* N.B. the result needs to exclude the leading bit. */
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, leading32 (aarch64_get_reg_u32 (cpu, rn, NO_SP)) - 1);
|
|
}
|
|
|
|
/* 64 bit count leading sign bits. */
|
|
static void
|
|
cls64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* N.B. the result needs to exclude the leading bit. */
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP, leading64 (aarch64_get_reg_u64 (cpu, rn, NO_SP)) - 1);
|
|
}
|
|
|
|
/* 32 bit count leading zero bits. */
|
|
static void
|
|
clz32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint32_t value = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
|
|
/* if the sign (top) bit is set then the count is 0. */
|
|
if (pick32 (value, 31, 31))
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, 0L);
|
|
else
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, leading32 (value));
|
|
}
|
|
|
|
/* 64 bit count leading zero bits. */
|
|
static void
|
|
clz64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t value = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
|
|
/* if the sign (top) bit is set then the count is 0. */
|
|
if (pick64 (value, 63, 63))
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, 0L);
|
|
else
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, leading64 (value));
|
|
}
|
|
|
|
/* 32 bit reverse bits. */
|
|
static void
|
|
rbit32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint32_t value = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
uint32_t result = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < 32; i++)
|
|
{
|
|
result <<= 1;
|
|
result |= (value & 1);
|
|
value >>= 1;
|
|
}
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, result);
|
|
}
|
|
|
|
/* 64 bit reverse bits. */
|
|
static void
|
|
rbit64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t value = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
uint64_t result = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < 64; i++)
|
|
{
|
|
result <<= 1;
|
|
result |= (value & 1L);
|
|
value >>= 1;
|
|
}
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, result);
|
|
}
|
|
|
|
/* 32 bit reverse bytes. */
|
|
static void
|
|
rev32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint32_t value = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
uint32_t result = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
result <<= 8;
|
|
result |= (value & 0xff);
|
|
value >>= 8;
|
|
}
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, result);
|
|
}
|
|
|
|
/* 64 bit reverse bytes. */
|
|
static void
|
|
rev64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t value = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
uint64_t result = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
result <<= 8;
|
|
result |= (value & 0xffULL);
|
|
value >>= 8;
|
|
}
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, result);
|
|
}
|
|
|
|
/* 32 bit reverse shorts. */
|
|
/* N.B.this reverses the order of the bytes in each half word. */
|
|
static void
|
|
revh32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint32_t value = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
uint32_t result = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
result <<= 8;
|
|
result |= (value & 0x00ff00ff);
|
|
value >>= 8;
|
|
}
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, result);
|
|
}
|
|
|
|
/* 64 bit reverse shorts. */
|
|
/* N.B.this reverses the order of the bytes in each half word. */
|
|
static void
|
|
revh64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint64_t value = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
uint64_t result = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
result <<= 8;
|
|
result |= (value & 0x00ff00ff00ff00ffULL);
|
|
value >>= 8;
|
|
}
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP, result);
|
|
}
|
|
|
|
static void
|
|
dexDataProc1Source (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[30] == 1
|
|
aarch64_get_instr (cpu)[28,21] == 111010110
|
|
instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[29] = S : 0 ==> ok, 1 ==> UNALLOC
|
|
instr[20,16] = opcode2 : 00000 ==> ok, ow ==> UNALLOC
|
|
instr[15,10] = opcode : 000000 ==> RBIT, 000001 ==> REV16,
|
|
000010 ==> REV, 000011 ==> UNALLOC
|
|
000100 ==> CLZ, 000101 ==> CLS
|
|
ow ==> UNALLOC
|
|
instr[9,5] = rn : may not be SP
|
|
instr[4,0] = rd : may not be SP. */
|
|
|
|
uint32_t S = uimm (aarch64_get_instr (cpu), 29, 29);
|
|
uint32_t opcode2 = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
uint32_t opcode = uimm (aarch64_get_instr (cpu), 15, 10);
|
|
uint32_t dispatch = ((uimm (aarch64_get_instr (cpu), 31, 31) << 3) | opcode);
|
|
|
|
if (S == 1)
|
|
HALT_UNALLOC;
|
|
|
|
if (opcode2 != 0)
|
|
HALT_UNALLOC;
|
|
|
|
if (opcode & 0x38)
|
|
HALT_UNALLOC;
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 0: rbit32 (cpu); return;
|
|
case 1: revh32 (cpu); return;
|
|
case 2: rev32 (cpu); return;
|
|
case 4: clz32 (cpu); return;
|
|
case 5: cls32 (cpu); return;
|
|
case 8: rbit64 (cpu); return;
|
|
case 9: revh64 (cpu); return;
|
|
case 10:rev32 (cpu); return;
|
|
case 11:rev64 (cpu); return;
|
|
case 12:clz64 (cpu); return;
|
|
case 13:cls64 (cpu); return;
|
|
default: HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
/* Variable shift.
|
|
Shifts by count supplied in register.
|
|
N.B register args may not be SP.
|
|
These all use the shifted auxiliary function for
|
|
simplicity and clarity. Writing the actual shift
|
|
inline would avoid a branch and so be faster but
|
|
would also necessitate getting signs right. */
|
|
|
|
/* 32 bit arithmetic shift right. */
|
|
static void
|
|
asrv32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP,
|
|
shifted32 (aarch64_get_reg_u32 (cpu, rn, NO_SP), ASR,
|
|
(aarch64_get_reg_u32 (cpu, rm, NO_SP) & 0x1f)));
|
|
}
|
|
|
|
/* 64 bit arithmetic shift right. */
|
|
static void
|
|
asrv64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP,
|
|
shifted64 (aarch64_get_reg_u64 (cpu, rn, NO_SP), ASR,
|
|
(aarch64_get_reg_u64 (cpu, rm, NO_SP) & 0x3f)));
|
|
}
|
|
|
|
/* 32 bit logical shift left. */
|
|
static void
|
|
lslv32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP,
|
|
shifted32 (aarch64_get_reg_u32 (cpu, rn, NO_SP), LSL,
|
|
(aarch64_get_reg_u32 (cpu, rm, NO_SP) & 0x1f)));
|
|
}
|
|
|
|
/* 64 bit arithmetic shift left. */
|
|
static void
|
|
lslv64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP,
|
|
shifted64 (aarch64_get_reg_u64 (cpu, rn, NO_SP), LSL,
|
|
(aarch64_get_reg_u64 (cpu, rm, NO_SP) & 0x3f)));
|
|
}
|
|
|
|
/* 32 bit logical shift right. */
|
|
static void
|
|
lsrv32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP,
|
|
shifted32 (aarch64_get_reg_u32 (cpu, rn, NO_SP), LSR,
|
|
(aarch64_get_reg_u32 (cpu, rm, NO_SP) & 0x1f)));
|
|
}
|
|
|
|
/* 64 bit logical shift right. */
|
|
static void
|
|
lsrv64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP,
|
|
shifted64 (aarch64_get_reg_u64 (cpu, rn, NO_SP), LSR,
|
|
(aarch64_get_reg_u64 (cpu, rm, NO_SP) & 0x3f)));
|
|
}
|
|
|
|
/* 32 bit rotate right. */
|
|
static void
|
|
rorv32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP,
|
|
shifted32 (aarch64_get_reg_u32 (cpu, rn, NO_SP), ROR,
|
|
(aarch64_get_reg_u32 (cpu, rm, NO_SP) & 0x1f)));
|
|
}
|
|
|
|
/* 64 bit rotate right. */
|
|
static void
|
|
rorv64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP,
|
|
shifted64 (aarch64_get_reg_u64 (cpu, rn, NO_SP), ROR,
|
|
(aarch64_get_reg_u64 (cpu, rm, NO_SP) & 0x3f)));
|
|
}
|
|
|
|
|
|
/* divide. */
|
|
|
|
/* 32 bit signed divide. */
|
|
static void
|
|
cpuiv32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
/* N.B. the pseudo-code does the divide using 64 bit data. */
|
|
/* TODO : check that this rounds towards zero as required. */
|
|
int64_t dividend = aarch64_get_reg_s32 (cpu, rn, NO_SP);
|
|
int64_t divisor = aarch64_get_reg_s32 (cpu, rm, NO_SP);
|
|
|
|
aarch64_set_reg_s64 (cpu, rd, NO_SP,
|
|
divisor ? ((int32_t) (dividend / divisor)) : 0);
|
|
}
|
|
|
|
/* 64 bit signed divide. */
|
|
static void
|
|
cpuiv64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* TODO : check that this rounds towards zero as required. */
|
|
int64_t divisor = aarch64_get_reg_s64 (cpu, rm, NO_SP);
|
|
|
|
aarch64_set_reg_s64
|
|
(cpu, rd, NO_SP,
|
|
divisor ? (aarch64_get_reg_s64 (cpu, rn, NO_SP) / divisor) : 0);
|
|
}
|
|
|
|
/* 32 bit unsigned divide. */
|
|
static void
|
|
udiv32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* N.B. the pseudo-code does the divide using 64 bit data. */
|
|
uint64_t dividend = aarch64_get_reg_u32 (cpu, rn, NO_SP);
|
|
uint64_t divisor = aarch64_get_reg_u32 (cpu, rm, NO_SP);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
divisor ? (uint32_t) (dividend / divisor) : 0);
|
|
}
|
|
|
|
/* 64 bit unsigned divide. */
|
|
static void
|
|
udiv64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* TODO : check that this rounds towards zero as required. */
|
|
uint64_t divisor = aarch64_get_reg_u64 (cpu, rm, NO_SP);
|
|
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP,
|
|
divisor ? (aarch64_get_reg_u64 (cpu, rn, NO_SP) / divisor) : 0);
|
|
}
|
|
|
|
static void
|
|
dexDataProc2Source (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[30] == 0
|
|
instr[28,21] == 11010110
|
|
instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit
|
|
instr[29] = S : 0 ==> ok, 1 ==> UNALLOC
|
|
instr[15,10] = opcode : 000010 ==> UDIV, 000011 ==> CPUIV,
|
|
001000 ==> LSLV, 001001 ==> LSRV
|
|
001010 ==> ASRV, 001011 ==> RORV
|
|
ow ==> UNALLOC. */
|
|
|
|
uint32_t dispatch;
|
|
uint32_t S = uimm (aarch64_get_instr (cpu), 29, 29);
|
|
uint32_t opcode = uimm (aarch64_get_instr (cpu), 15, 10);
|
|
|
|
if (S == 1)
|
|
HALT_UNALLOC;
|
|
|
|
if (opcode & 0x34)
|
|
HALT_UNALLOC;
|
|
|
|
dispatch = ( (uimm (aarch64_get_instr (cpu), 31, 31) << 3)
|
|
| (uimm (opcode, 3, 3) << 2)
|
|
| uimm (opcode, 1, 0));
|
|
switch (dispatch)
|
|
{
|
|
case 2: udiv32 (cpu); return;
|
|
case 3: cpuiv32 (cpu); return;
|
|
case 4: lslv32 (cpu); return;
|
|
case 5: lsrv32 (cpu); return;
|
|
case 6: asrv32 (cpu); return;
|
|
case 7: rorv32 (cpu); return;
|
|
case 10: udiv64 (cpu); return;
|
|
case 11: cpuiv64 (cpu); return;
|
|
case 12: lslv64 (cpu); return;
|
|
case 13: lsrv64 (cpu); return;
|
|
case 14: asrv64 (cpu); return;
|
|
case 15: rorv64 (cpu); return;
|
|
default: HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
|
|
/* Multiply. */
|
|
|
|
/* 32 bit multiply and add. */
|
|
static void
|
|
madd32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned ra = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_reg_u32 (cpu, ra, NO_SP)
|
|
+ aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
* aarch64_get_reg_u32 (cpu, rm, NO_SP));
|
|
}
|
|
|
|
/* 64 bit multiply and add. */
|
|
static void
|
|
madd64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned ra = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_reg_u64 (cpu, ra, NO_SP)
|
|
+ aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
* aarch64_get_reg_u64 (cpu, rm, NO_SP));
|
|
}
|
|
|
|
/* 32 bit multiply and sub. */
|
|
static void
|
|
msub32 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned ra = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_reg_u32 (cpu, ra, NO_SP)
|
|
- aarch64_get_reg_u32 (cpu, rn, NO_SP)
|
|
* aarch64_get_reg_u32 (cpu, rm, NO_SP));
|
|
}
|
|
|
|
/* 64 bit multiply and sub. */
|
|
static void
|
|
msub64 (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned ra = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
aarch64_get_reg_u64 (cpu, ra, NO_SP)
|
|
- aarch64_get_reg_u64 (cpu, rn, NO_SP)
|
|
* aarch64_get_reg_u64 (cpu, rm, NO_SP));
|
|
}
|
|
|
|
/* Signed multiply add long -- source, source2 : 32 bit, source3 : 64 bit. */
|
|
static void
|
|
smaddl (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned ra = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* N.B. we need to multiply the signed 32 bit values in rn, rm to
|
|
obtain a 64 bit product. */
|
|
aarch64_set_reg_s64
|
|
(cpu, rd, NO_SP,
|
|
aarch64_get_reg_s64 (cpu, ra, NO_SP)
|
|
+ ((int64_t) aarch64_get_reg_s32 (cpu, rn, NO_SP))
|
|
* ((int64_t) aarch64_get_reg_s32 (cpu, rm, NO_SP)));
|
|
}
|
|
|
|
/* Signed multiply sub long -- source, source2 : 32 bit, source3 : 64 bit. */
|
|
static void
|
|
smsubl (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned ra = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* N.B. we need to multiply the signed 32 bit values in rn, rm to
|
|
obtain a 64 bit product. */
|
|
aarch64_set_reg_s64
|
|
(cpu, rd, NO_SP,
|
|
aarch64_get_reg_s64 (cpu, ra, NO_SP)
|
|
- ((int64_t) aarch64_get_reg_s32 (cpu, rn, NO_SP))
|
|
* ((int64_t) aarch64_get_reg_s32 (cpu, rm, NO_SP)));
|
|
}
|
|
|
|
/* Integer Multiply/Divide. */
|
|
|
|
/* First some macros and a helper function. */
|
|
/* Macros to test or access elements of 64 bit words. */
|
|
|
|
/* Mask used to access lo 32 bits of 64 bit unsigned int. */
|
|
#define LOW_WORD_MASK ((1ULL << 32) - 1)
|
|
/* Return the lo 32 bit word of a 64 bit unsigned int as a 64 bit unsigned int. */
|
|
#define lowWordToU64(_value_u64) ((_value_u64) & LOW_WORD_MASK)
|
|
/* Return the hi 32 bit word of a 64 bit unsigned int as a 64 bit unsigned int. */
|
|
#define highWordToU64(_value_u64) ((_value_u64) >> 32)
|
|
|
|
/* Offset of sign bit in 64 bit signed integger. */
|
|
#define SIGN_SHIFT_U64 63
|
|
/* The sign bit itself -- also identifies the minimum negative int value. */
|
|
#define SIGN_BIT_U64 (1UL << SIGN_SHIFT_U64)
|
|
/* Return true if a 64 bit signed int presented as an unsigned int is the
|
|
most negative value. */
|
|
#define isMinimumU64(_value_u64) ((_value_u64) == SIGN_BIT_U64)
|
|
/* Return true (non-zero) if a 64 bit signed int presented as an unsigned
|
|
int has its sign bit set to false. */
|
|
#define isSignSetU64(_value_u64) ((_value_u64) & SIGN_BIT_U64)
|
|
/* Return 1L or -1L according to whether a 64 bit signed int presented as
|
|
an unsigned int has its sign bit set or not. */
|
|
#define signOfU64(_value_u64) (1L + (((value_u64) >> SIGN_SHIFT_U64) * -2L)
|
|
/* Clear the sign bit of a 64 bit signed int presented as an unsigned int. */
|
|
#define clearSignU64(_value_u64) ((_value_u64) &= ~SIGN_BIT_U64)
|
|
|
|
/* Multiply two 64 bit ints and return.
|
|
the hi 64 bits of the 128 bit product. */
|
|
|
|
static uint64_t
|
|
mul64hi (uint64_t value1, uint64_t value2)
|
|
{
|
|
uint64_t resultmid1;
|
|
uint64_t result;
|
|
uint64_t value1_lo = lowWordToU64 (value1);
|
|
uint64_t value1_hi = highWordToU64 (value1) ;
|
|
uint64_t value2_lo = lowWordToU64 (value2);
|
|
uint64_t value2_hi = highWordToU64 (value2);
|
|
|
|
/* Cross-multiply and collect results. */
|
|
|
|
uint64_t xproductlo = value1_lo * value2_lo;
|
|
uint64_t xproductmid1 = value1_lo * value2_hi;
|
|
uint64_t xproductmid2 = value1_hi * value2_lo;
|
|
uint64_t xproducthi = value1_hi * value2_hi;
|
|
uint64_t carry = 0;
|
|
/* Start accumulating 64 bit results. */
|
|
/* Drop bottom half of lowest cross-product. */
|
|
uint64_t resultmid = xproductlo >> 32;
|
|
/* Add in middle products. */
|
|
resultmid = resultmid + xproductmid1;
|
|
|
|
/* Check for overflow. */
|
|
if (resultmid < xproductmid1)
|
|
/* Carry over 1 into top cross-product. */
|
|
carry++;
|
|
|
|
resultmid1 = resultmid + xproductmid2;
|
|
|
|
/* Check for overflow. */
|
|
if (resultmid1 < xproductmid2)
|
|
/* Carry over 1 into top cross-product. */
|
|
carry++;
|
|
|
|
/* Drop lowest 32 bits of middle cross-product. */
|
|
result = resultmid1 >> 32;
|
|
|
|
/* Add top cross-product plus and any carry. */
|
|
result += xproducthi + carry;
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Signed multiply high, source, source2 :
|
|
64 bit, dest <-- high 64-bit of result. */
|
|
static void
|
|
smulh (sim_cpu *cpu)
|
|
{
|
|
uint64_t uresult;
|
|
int64_t result;
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
GReg ra = greg (aarch64_get_instr (cpu), 10);
|
|
int64_t value1 = aarch64_get_reg_u64 (cpu, rn, NO_SP);
|
|
int64_t value2 = aarch64_get_reg_u64 (cpu, rm, NO_SP);
|
|
uint64_t uvalue1;
|
|
uint64_t uvalue2;
|
|
int64_t signum = 1;
|
|
|
|
if (ra != R31)
|
|
HALT_UNALLOC;
|
|
|
|
/* Convert to unsigned and use the unsigned mul64hi routine
|
|
the fix the sign up afterwards. */
|
|
if (value1 < 0)
|
|
{
|
|
signum *= -1L;
|
|
uvalue1 = -value1;
|
|
}
|
|
else
|
|
{
|
|
uvalue1 = value1;
|
|
}
|
|
|
|
if (value2 < 0)
|
|
{
|
|
signum *= -1L;
|
|
uvalue2 = -value2;
|
|
}
|
|
else
|
|
{
|
|
uvalue2 = value2;
|
|
}
|
|
|
|
uresult = mul64hi (uvalue1, uvalue2);
|
|
result = uresult;
|
|
result *= signum;
|
|
|
|
aarch64_set_reg_s64 (cpu, rd, NO_SP, result);
|
|
}
|
|
|
|
/* Unsigned multiply add long -- source, source2 :
|
|
32 bit, source3 : 64 bit. */
|
|
static void
|
|
umaddl (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned ra = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* N.B. we need to multiply the signed 32 bit values in rn, rm to
|
|
obtain a 64 bit product. */
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP,
|
|
aarch64_get_reg_u64 (cpu, ra, NO_SP)
|
|
+ ((uint64_t) aarch64_get_reg_u32 (cpu, rn, NO_SP))
|
|
* ((uint64_t) aarch64_get_reg_u32 (cpu, rm, NO_SP)));
|
|
}
|
|
|
|
/* Unsigned multiply sub long -- source, source2 : 32 bit, source3 : 64 bit. */
|
|
static void
|
|
umsubl (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned ra = uimm (aarch64_get_instr (cpu), 14, 10);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
/* N.B. we need to multiply the signed 32 bit values in rn, rm to
|
|
obtain a 64 bit product. */
|
|
aarch64_set_reg_u64
|
|
(cpu, rd, NO_SP,
|
|
aarch64_get_reg_u64 (cpu, ra, NO_SP)
|
|
- ((uint64_t) aarch64_get_reg_u32 (cpu, rn, NO_SP))
|
|
* ((uint64_t) aarch64_get_reg_u32 (cpu, rm, NO_SP)));
|
|
}
|
|
|
|
/* Unsigned multiply high, source, source2 :
|
|
64 bit, dest <-- high 64-bit of result. */
|
|
static void
|
|
umulh (sim_cpu *cpu)
|
|
{
|
|
unsigned rm = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
unsigned rd = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
GReg ra = greg (aarch64_get_instr (cpu), 10);
|
|
|
|
if (ra != R31)
|
|
HALT_UNALLOC;
|
|
|
|
aarch64_set_reg_u64 (cpu, rd, NO_SP,
|
|
mul64hi (aarch64_get_reg_u64 (cpu, rn, NO_SP),
|
|
aarch64_get_reg_u64 (cpu, rm, NO_SP)));
|
|
}
|
|
|
|
static void
|
|
dexDataProc3Source (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[28,24] == 11011. */
|
|
/* instr[31] = size : 0 ==> 32 bit, 1 ==> 64 bit (for rd at least)
|
|
instr[30,29] = op54 : 00 ==> ok, ow ==> UNALLOC
|
|
instr[23,21] = op31 : 111 ==> UNALLOC, o2 ==> ok
|
|
instr[15] = o0 : 0/1 ==> ok
|
|
instr[23,21:15] ==> op : 0000 ==> MADD, 0001 ==> MSUB, (32/64 bit)
|
|
0010 ==> SMADDL, 0011 ==> SMSUBL, (64 bit only)
|
|
0100 ==> SMULH, (64 bit only)
|
|
1010 ==> UMADDL, 1011 ==> UNSUBL, (64 bit only)
|
|
1100 ==> UMULH (64 bit only)
|
|
ow ==> UNALLOC. */
|
|
|
|
uint32_t dispatch;
|
|
uint32_t size = uimm (aarch64_get_instr (cpu), 31, 31);
|
|
uint32_t op54 = uimm (aarch64_get_instr (cpu), 30, 29);
|
|
uint32_t op31 = uimm (aarch64_get_instr (cpu), 23, 21);
|
|
uint32_t o0 = uimm (aarch64_get_instr (cpu), 15, 15);
|
|
|
|
if (op54 != 0)
|
|
HALT_UNALLOC;
|
|
|
|
if (size == 0)
|
|
{
|
|
if (op31 != 0)
|
|
HALT_UNALLOC;
|
|
|
|
if (o0 == 0)
|
|
madd32 (cpu);
|
|
else
|
|
msub32 (cpu);
|
|
return;
|
|
}
|
|
|
|
dispatch = (op31 << 1) | o0;
|
|
|
|
switch (dispatch)
|
|
{
|
|
case 0: madd64 (cpu); return;
|
|
case 1: msub64 (cpu); return;
|
|
case 2: smaddl (cpu); return;
|
|
case 3: smsubl (cpu); return;
|
|
case 4: smulh (cpu); return;
|
|
case 10: umaddl (cpu); return;
|
|
case 11: umsubl (cpu); return;
|
|
case 12: umulh (cpu); return;
|
|
default: HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexDPReg (sim_cpu *cpu)
|
|
{
|
|
/* uint32_t group = dispatchGroup (aarch64_get_instr (cpu));
|
|
assert group == GROUP_DPREG_0101 || group == GROUP_DPREG_1101
|
|
bits [28:24:21] of a DPReg are the secondary dispatch vector. */
|
|
uint32_t group2 = dispatchDPReg (aarch64_get_instr (cpu));
|
|
|
|
switch (group2)
|
|
{
|
|
case DPREG_LOG_000:
|
|
case DPREG_LOG_001:
|
|
dexLogicalShiftedRegister (cpu); return;
|
|
|
|
case DPREG_ADDSHF_010:
|
|
dexAddSubtractShiftedRegister (cpu); return;
|
|
|
|
case DPREG_ADDEXT_011:
|
|
dexAddSubtractExtendedRegister (cpu); return;
|
|
|
|
case DPREG_ADDCOND_100:
|
|
{
|
|
/* This set bundles a variety of different operations. */
|
|
/* Check for. */
|
|
/* 1) add/sub w carry. */
|
|
uint32_t mask1 = 0x1FE00000U;
|
|
uint32_t val1 = 0x1A000000U;
|
|
/* 2) cond compare register/immediate. */
|
|
uint32_t mask2 = 0x1FE00000U;
|
|
uint32_t val2 = 0x1A400000U;
|
|
/* 3) cond select. */
|
|
uint32_t mask3 = 0x1FE00000U;
|
|
uint32_t val3 = 0x1A800000U;
|
|
/* 4) data proc 1/2 source. */
|
|
uint32_t mask4 = 0x1FE00000U;
|
|
uint32_t val4 = 0x1AC00000U;
|
|
|
|
if ((aarch64_get_instr (cpu) & mask1) == val1)
|
|
dexAddSubtractWithCarry (cpu);
|
|
|
|
else if ((aarch64_get_instr (cpu) & mask2) == val2)
|
|
CondCompare (cpu);
|
|
|
|
else if ((aarch64_get_instr (cpu) & mask3) == val3)
|
|
dexCondSelect (cpu);
|
|
|
|
else if ((aarch64_get_instr (cpu) & mask4) == val4)
|
|
{
|
|
/* Bit 30 is clear for data proc 2 source
|
|
and set for data proc 1 source. */
|
|
if (aarch64_get_instr (cpu) & (1U << 30))
|
|
dexDataProc1Source (cpu);
|
|
else
|
|
dexDataProc2Source (cpu);
|
|
}
|
|
|
|
else
|
|
/* Should not reach here. */
|
|
HALT_NYI;
|
|
|
|
return;
|
|
}
|
|
|
|
case DPREG_3SRC_110:
|
|
dexDataProc3Source (cpu); return;
|
|
|
|
case DPREG_UNALLOC_101:
|
|
HALT_UNALLOC;
|
|
|
|
case DPREG_3SRC_111:
|
|
dexDataProc3Source (cpu); return;
|
|
|
|
default:
|
|
/* Should never reach here. */
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
/* Unconditional Branch immediate.
|
|
Offset is a PC-relative byte offset in the range +/- 128MiB.
|
|
The offset is assumed to be raw from the decode i.e. the
|
|
simulator is expected to scale them from word offsets to byte. */
|
|
|
|
/* Unconditional branch. */
|
|
static void
|
|
buc (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
aarch64_set_next_PC_by_offset (cpu, offset);
|
|
}
|
|
|
|
static unsigned stack_depth = 0;
|
|
|
|
/* Unconditional branch and link -- writes return PC to LR. */
|
|
static void
|
|
bl (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
aarch64_save_LR (cpu);
|
|
aarch64_set_next_PC_by_offset (cpu, offset);
|
|
|
|
if (TRACE_BRANCH_P (cpu))
|
|
{
|
|
++ stack_depth;
|
|
TRACE_BRANCH (cpu,
|
|
" %*scall %" PRIx64 " [%s]"
|
|
" [args: %" PRIx64 " %" PRIx64 " %" PRIx64 "]",
|
|
stack_depth, " ", aarch64_get_next_PC (cpu),
|
|
aarch64_get_func (aarch64_get_next_PC (cpu)),
|
|
aarch64_get_reg_u64 (cpu, 0, NO_SP),
|
|
aarch64_get_reg_u64 (cpu, 1, NO_SP),
|
|
aarch64_get_reg_u64 (cpu, 2, NO_SP)
|
|
);
|
|
}
|
|
}
|
|
|
|
/* Unconditional Branch register.
|
|
Branch/return address is in source register. */
|
|
|
|
/* Unconditional branch. */
|
|
static void
|
|
br (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
aarch64_set_next_PC (cpu, aarch64_get_reg_u64 (cpu, rn, NO_SP));
|
|
}
|
|
|
|
/* Unconditional branch and link -- writes return PC to LR. */
|
|
static void
|
|
blr (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
|
|
/* The pseudo code in the spec says we update LR before fetching.
|
|
the value from the rn. */
|
|
aarch64_save_LR (cpu);
|
|
aarch64_set_next_PC (cpu, aarch64_get_reg_u64 (cpu, rn, NO_SP));
|
|
|
|
if (TRACE_BRANCH_P (cpu))
|
|
{
|
|
++ stack_depth;
|
|
TRACE_BRANCH (cpu,
|
|
" %*scall %" PRIx64 " [%s]"
|
|
" [args: %" PRIx64 " %" PRIx64 " %" PRIx64 "]",
|
|
stack_depth, " ", aarch64_get_next_PC (cpu),
|
|
aarch64_get_func (aarch64_get_next_PC (cpu)),
|
|
aarch64_get_reg_u64 (cpu, 0, NO_SP),
|
|
aarch64_get_reg_u64 (cpu, 1, NO_SP),
|
|
aarch64_get_reg_u64 (cpu, 2, NO_SP)
|
|
);
|
|
}
|
|
}
|
|
|
|
/* Return -- assembler will default source to LR this is functionally
|
|
equivalent to br but, presumably, unlike br it side effects the
|
|
branch predictor. */
|
|
static void
|
|
ret (sim_cpu *cpu)
|
|
{
|
|
unsigned rn = uimm (aarch64_get_instr (cpu), 9, 5);
|
|
aarch64_set_next_PC (cpu, aarch64_get_reg_u64 (cpu, rn, NO_SP));
|
|
|
|
if (TRACE_BRANCH_P (cpu))
|
|
{
|
|
TRACE_BRANCH (cpu,
|
|
" %*sreturn [result: %" PRIx64 "]",
|
|
stack_depth, " ", aarch64_get_reg_u64 (cpu, 0, NO_SP));
|
|
-- stack_depth;
|
|
}
|
|
}
|
|
|
|
/* NOP -- we implement this and call it from the decode in case we
|
|
want to intercept it later. */
|
|
|
|
static void
|
|
nop (sim_cpu *cpu)
|
|
{
|
|
}
|
|
|
|
/* Data synchronization barrier. */
|
|
|
|
static void
|
|
dsb (sim_cpu *cpu)
|
|
{
|
|
}
|
|
|
|
/* Data memory barrier. */
|
|
|
|
static void
|
|
dmb (sim_cpu *cpu)
|
|
{
|
|
}
|
|
|
|
/* Instruction synchronization barrier. */
|
|
|
|
static void
|
|
isb (sim_cpu *cpu)
|
|
{
|
|
}
|
|
|
|
static void
|
|
dexBranchImmediate (sim_cpu *cpu)
|
|
{
|
|
/* assert instr[30,26] == 00101
|
|
instr[31] ==> 0 == B, 1 == BL
|
|
instr[25,0] == imm26 branch offset counted in words. */
|
|
|
|
uint32_t top = uimm (aarch64_get_instr (cpu), 31, 31);
|
|
/* We have a 26 byte signed word offset which we need to pass to the
|
|
execute routine as a signed byte offset. */
|
|
int32_t offset = simm32 (aarch64_get_instr (cpu), 25, 0) << 2;
|
|
|
|
if (top)
|
|
bl (cpu, offset);
|
|
else
|
|
buc (cpu, offset);
|
|
}
|
|
|
|
/* Control Flow. */
|
|
|
|
/* Conditional branch
|
|
|
|
Offset is a PC-relative byte offset in the range +/- 1MiB pos is
|
|
a bit position in the range 0 .. 63
|
|
|
|
cc is a CondCode enum value as pulled out of the decode
|
|
|
|
N.B. any offset register (source) can only be Xn or Wn. */
|
|
|
|
static void
|
|
bcc (sim_cpu *cpu, int32_t offset, CondCode cc)
|
|
{
|
|
/* the test returns TRUE if CC is met. */
|
|
if (testConditionCode (cpu, cc))
|
|
aarch64_set_next_PC_by_offset (cpu, offset);
|
|
}
|
|
|
|
/* 32 bit branch on register non-zero. */
|
|
static void
|
|
cbnz32 (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
if (aarch64_get_reg_u32 (cpu, rt, NO_SP) != 0)
|
|
aarch64_set_next_PC_by_offset (cpu, offset);
|
|
}
|
|
|
|
/* 64 bit branch on register zero. */
|
|
static void
|
|
cbnz (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
if (aarch64_get_reg_u64 (cpu, rt, NO_SP) != 0)
|
|
aarch64_set_next_PC_by_offset (cpu, offset);
|
|
}
|
|
|
|
/* 32 bit branch on register non-zero. */
|
|
static void
|
|
cbz32 (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
if (aarch64_get_reg_u32 (cpu, rt, NO_SP) == 0)
|
|
aarch64_set_next_PC_by_offset (cpu, offset);
|
|
}
|
|
|
|
/* 64 bit branch on register zero. */
|
|
static void
|
|
cbz (sim_cpu *cpu, int32_t offset)
|
|
{
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
if (aarch64_get_reg_u64 (cpu, rt, NO_SP) == 0)
|
|
aarch64_set_next_PC_by_offset (cpu, offset);
|
|
}
|
|
|
|
/* Branch on register bit test non-zero -- one size fits all. */
|
|
static void
|
|
tbnz (sim_cpu *cpu, uint32_t pos, int32_t offset)
|
|
{
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
if (aarch64_get_reg_u64 (cpu, rt, NO_SP) & (1 << pos))
|
|
aarch64_set_next_PC_by_offset (cpu, offset);
|
|
}
|
|
|
|
/* branch on register bit test zero -- one size fits all. */
|
|
static void
|
|
tbz (sim_cpu *cpu, uint32_t pos, int32_t offset)
|
|
{
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
if (!(aarch64_get_reg_u64 (cpu, rt, NO_SP) & (1 << pos)))
|
|
aarch64_set_next_PC_by_offset (cpu, offset);
|
|
}
|
|
|
|
static void
|
|
dexCompareBranchImmediate (sim_cpu *cpu)
|
|
{
|
|
/* instr[30,25] = 01 1010
|
|
instr[31] = size : 0 ==> 32, 1 ==> 64
|
|
instr[24] = op : 0 ==> CBZ, 1 ==> CBNZ
|
|
instr[23,5] = simm19 branch offset counted in words
|
|
instr[4,0] = rt */
|
|
|
|
uint32_t size = uimm (aarch64_get_instr (cpu), 31, 31);
|
|
uint32_t op = uimm (aarch64_get_instr (cpu), 24, 24);
|
|
int32_t offset = simm32 (aarch64_get_instr (cpu), 23, 5) << 2;
|
|
|
|
if (size == 0)
|
|
{
|
|
if (op == 0)
|
|
cbz32 (cpu, offset);
|
|
else
|
|
cbnz32 (cpu, offset);
|
|
}
|
|
else
|
|
{
|
|
if (op == 0)
|
|
cbz (cpu, offset);
|
|
else
|
|
cbnz (cpu, offset);
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexTestBranchImmediate (sim_cpu *cpu)
|
|
{
|
|
/* instr[31] = b5 : bit 5 of test bit idx
|
|
instr[30,25] = 01 1011
|
|
instr[24] = op : 0 ==> TBZ, 1 == TBNZ
|
|
instr[23,19] = b40 : bits 4 to 0 of test bit idx
|
|
instr[18,5] = simm14 : signed offset counted in words
|
|
instr[4,0] = uimm5 */
|
|
|
|
uint32_t pos = ((uimm (aarch64_get_instr (cpu), 31, 31) << 4)
|
|
| uimm (aarch64_get_instr (cpu), 23,19));
|
|
int32_t offset = simm32 (aarch64_get_instr (cpu), 18, 5) << 2;
|
|
|
|
NYI_assert (30, 25, 0x1b);
|
|
|
|
if (uimm (aarch64_get_instr (cpu), 24, 24) == 0)
|
|
tbz (cpu, pos, offset);
|
|
else
|
|
tbnz (cpu, pos, offset);
|
|
}
|
|
|
|
static void
|
|
dexCondBranchImmediate (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,25] = 010 1010
|
|
instr[24] = op1; op => 00 ==> B.cond
|
|
instr[23,5] = simm19 : signed offset counted in words
|
|
instr[4] = op0
|
|
instr[3,0] = cond */
|
|
|
|
int32_t offset;
|
|
CondCode cc;
|
|
uint32_t op = ((uimm (aarch64_get_instr (cpu), 24, 24) << 1)
|
|
| uimm (aarch64_get_instr (cpu), 4, 4));
|
|
|
|
NYI_assert (31, 25, 0x2a);
|
|
|
|
if (op != 0)
|
|
HALT_UNALLOC;
|
|
|
|
offset = simm32 (aarch64_get_instr (cpu), 23, 5) << 2;
|
|
cc = condcode (aarch64_get_instr (cpu), 0);
|
|
|
|
bcc (cpu, offset, cc);
|
|
}
|
|
|
|
static void
|
|
dexBranchRegister (sim_cpu *cpu)
|
|
{
|
|
/* instr[31,25] = 110 1011
|
|
instr[24,21] = op : 0 ==> BR, 1 => BLR, 2 => RET, 3 => ERET, 4 => DRPS
|
|
instr[20,16] = op2 : must be 11111
|
|
instr[15,10] = op3 : must be 000000
|
|
instr[4,0] = op2 : must be 11111. */
|
|
|
|
uint32_t op = uimm (aarch64_get_instr (cpu), 24, 21);
|
|
uint32_t op2 = uimm (aarch64_get_instr (cpu), 20, 16);
|
|
uint32_t op3 = uimm (aarch64_get_instr (cpu), 15, 10);
|
|
uint32_t op4 = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
NYI_assert (31, 25, 0x6b);
|
|
|
|
if (op2 != 0x1F || op3 != 0 || op4 != 0)
|
|
HALT_UNALLOC;
|
|
|
|
if (op == 0)
|
|
br (cpu);
|
|
|
|
else if (op == 1)
|
|
blr (cpu);
|
|
|
|
else if (op == 2)
|
|
ret (cpu);
|
|
|
|
else
|
|
{
|
|
/* ERET and DRPS accept 0b11111 for rn = aarch64_get_instr (cpu)[4,0]. */
|
|
/* anything else is unallocated. */
|
|
uint32_t rn = greg (aarch64_get_instr (cpu), 0);
|
|
|
|
if (rn != 0x1f)
|
|
HALT_UNALLOC;
|
|
|
|
if (op == 4 || op == 5)
|
|
HALT_NYI;
|
|
|
|
HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
/* FIXME: We should get the Angel SWI values from ../../libgloss/aarch64/svc.h
|
|
but this may not be available. So instead we define the values we need
|
|
here. */
|
|
#define AngelSVC_Reason_Open 0x01
|
|
#define AngelSVC_Reason_Close 0x02
|
|
#define AngelSVC_Reason_Write 0x05
|
|
#define AngelSVC_Reason_Read 0x06
|
|
#define AngelSVC_Reason_IsTTY 0x09
|
|
#define AngelSVC_Reason_Seek 0x0A
|
|
#define AngelSVC_Reason_FLen 0x0C
|
|
#define AngelSVC_Reason_Remove 0x0E
|
|
#define AngelSVC_Reason_Rename 0x0F
|
|
#define AngelSVC_Reason_Clock 0x10
|
|
#define AngelSVC_Reason_Time 0x11
|
|
#define AngelSVC_Reason_System 0x12
|
|
#define AngelSVC_Reason_Errno 0x13
|
|
#define AngelSVC_Reason_GetCmdLine 0x15
|
|
#define AngelSVC_Reason_HeapInfo 0x16
|
|
#define AngelSVC_Reason_ReportException 0x18
|
|
#define AngelSVC_Reason_Elapsed 0x30
|
|
|
|
|
|
static void
|
|
handle_halt (sim_cpu *cpu, uint32_t val)
|
|
{
|
|
uint64_t result = 0;
|
|
|
|
if (val != 0xf000)
|
|
{
|
|
TRACE_SYSCALL (cpu, " HLT [0x%x]", val);
|
|
sim_engine_halt (CPU_STATE (cpu), cpu, NULL, aarch64_get_PC (cpu),
|
|
sim_stopped, SIM_SIGTRAP);
|
|
}
|
|
|
|
/* We have encountered an Angel SVC call. See if we can process it. */
|
|
switch (aarch64_get_reg_u32 (cpu, 0, NO_SP))
|
|
{
|
|
case AngelSVC_Reason_HeapInfo:
|
|
{
|
|
/* Get the values. */
|
|
uint64_t stack_top = aarch64_get_stack_start (cpu);
|
|
uint64_t heap_base = aarch64_get_heap_start (cpu);
|
|
|
|
/* Get the pointer */
|
|
uint64_t ptr = aarch64_get_reg_u64 (cpu, 1, SP_OK);
|
|
ptr = aarch64_get_mem_u64 (cpu, ptr);
|
|
|
|
/* Fill in the memory block. */
|
|
/* Start addr of heap. */
|
|
aarch64_set_mem_u64 (cpu, ptr + 0, heap_base);
|
|
/* End addr of heap. */
|
|
aarch64_set_mem_u64 (cpu, ptr + 8, stack_top);
|
|
/* Lowest stack addr. */
|
|
aarch64_set_mem_u64 (cpu, ptr + 16, heap_base);
|
|
/* Initial stack addr. */
|
|
aarch64_set_mem_u64 (cpu, ptr + 24, stack_top);
|
|
|
|
TRACE_SYSCALL (cpu, " AngelSVC: Get Heap Info");
|
|
}
|
|
break;
|
|
|
|
case AngelSVC_Reason_Open:
|
|
{
|
|
/* Get the pointer */
|
|
/* uint64_t ptr = aarch64_get_reg_u64 (cpu, 1, SP_OK);. */
|
|
/* FIXME: For now we just assume that we will only be asked
|
|
to open the standard file descriptors. */
|
|
static int fd = 0;
|
|
result = fd ++;
|
|
|
|
TRACE_SYSCALL (cpu, " AngelSVC: Open file %d", fd - 1);
|
|
}
|
|
break;
|
|
|
|
case AngelSVC_Reason_Close:
|
|
{
|
|
uint64_t fh = aarch64_get_reg_u64 (cpu, 1, SP_OK);
|
|
TRACE_SYSCALL (cpu, " AngelSVC: Close file %d", (int) fh);
|
|
result = 0;
|
|
}
|
|
break;
|
|
|
|
case AngelSVC_Reason_Errno:
|
|
result = 0;
|
|
TRACE_SYSCALL (cpu, " AngelSVC: Get Errno");
|
|
break;
|
|
|
|
case AngelSVC_Reason_Clock:
|
|
result =
|
|
#ifdef CLOCKS_PER_SEC
|
|
(CLOCKS_PER_SEC >= 100)
|
|
? (clock () / (CLOCKS_PER_SEC / 100))
|
|
: ((clock () * 100) / CLOCKS_PER_SEC)
|
|
#else
|
|
/* Presume unix... clock() returns microseconds. */
|
|
(clock () / 10000)
|
|
#endif
|
|
;
|
|
TRACE_SYSCALL (cpu, " AngelSVC: Get Clock");
|
|
break;
|
|
|
|
case AngelSVC_Reason_GetCmdLine:
|
|
{
|
|
/* Get the pointer */
|
|
uint64_t ptr = aarch64_get_reg_u64 (cpu, 1, SP_OK);
|
|
ptr = aarch64_get_mem_u64 (cpu, ptr);
|
|
|
|
/* FIXME: No command line for now. */
|
|
aarch64_set_mem_u64 (cpu, ptr, 0);
|
|
TRACE_SYSCALL (cpu, " AngelSVC: Get Command Line");
|
|
}
|
|
break;
|
|
|
|
case AngelSVC_Reason_IsTTY:
|
|
result = 1;
|
|
TRACE_SYSCALL (cpu, " AngelSVC: IsTTY ?");
|
|
break;
|
|
|
|
case AngelSVC_Reason_Write:
|
|
{
|
|
/* Get the pointer */
|
|
uint64_t ptr = aarch64_get_reg_u64 (cpu, 1, SP_OK);
|
|
/* Get the write control block. */
|
|
uint64_t fd = aarch64_get_mem_u64 (cpu, ptr);
|
|
uint64_t buf = aarch64_get_mem_u64 (cpu, ptr + 8);
|
|
uint64_t len = aarch64_get_mem_u64 (cpu, ptr + 16);
|
|
|
|
TRACE_SYSCALL (cpu, "write of %" PRIx64 " bytes from %"
|
|
PRIx64 " on descriptor %" PRIx64,
|
|
len, buf, fd);
|
|
|
|
if (len > 1280)
|
|
{
|
|
TRACE_SYSCALL (cpu,
|
|
" AngelSVC: Write: Suspiciously long write: %ld",
|
|
(long) len);
|
|
sim_engine_halt (CPU_STATE (cpu), cpu, NULL, aarch64_get_PC (cpu),
|
|
sim_stopped, SIM_SIGBUS);
|
|
}
|
|
else if (fd == 1)
|
|
{
|
|
printf ("%.*s", (int) len, aarch64_get_mem_ptr (cpu, buf));
|
|
}
|
|
else if (fd == 2)
|
|
{
|
|
TRACE (cpu, 0, "\n");
|
|
sim_io_eprintf (CPU_STATE (cpu), "%.*s",
|
|
(int) len, aarch64_get_mem_ptr (cpu, buf));
|
|
TRACE (cpu, 0, "\n");
|
|
}
|
|
else
|
|
{
|
|
TRACE_SYSCALL (cpu,
|
|
" AngelSVC: Write: Unexpected file handle: %d",
|
|
(int) fd);
|
|
sim_engine_halt (CPU_STATE (cpu), cpu, NULL, aarch64_get_PC (cpu),
|
|
sim_stopped, SIM_SIGABRT);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AngelSVC_Reason_ReportException:
|
|
{
|
|
/* Get the pointer */
|
|
uint64_t ptr = aarch64_get_reg_u64 (cpu, 1, SP_OK);
|
|
/*ptr = aarch64_get_mem_u64 (cpu, ptr);. */
|
|
uint64_t type = aarch64_get_mem_u64 (cpu, ptr);
|
|
uint64_t state = aarch64_get_mem_u64 (cpu, ptr + 8);
|
|
|
|
TRACE_SYSCALL (cpu,
|
|
"Angel Exception: type 0x%" PRIx64 " state %" PRIx64,
|
|
type, state);
|
|
|
|
if (type == 0x20026)
|
|
sim_engine_halt (CPU_STATE (cpu), cpu, NULL, aarch64_get_PC (cpu),
|
|
sim_exited, state);
|
|
else
|
|
sim_engine_halt (CPU_STATE (cpu), cpu, NULL, aarch64_get_PC (cpu),
|
|
sim_stopped, SIM_SIGINT);
|
|
}
|
|
break;
|
|
|
|
case AngelSVC_Reason_Read:
|
|
case AngelSVC_Reason_FLen:
|
|
case AngelSVC_Reason_Seek:
|
|
case AngelSVC_Reason_Remove:
|
|
case AngelSVC_Reason_Time:
|
|
case AngelSVC_Reason_System:
|
|
case AngelSVC_Reason_Rename:
|
|
case AngelSVC_Reason_Elapsed:
|
|
default:
|
|
TRACE_SYSCALL (cpu, " HLT [Unknown angel %x]",
|
|
aarch64_get_reg_u32 (cpu, 0, NO_SP));
|
|
sim_engine_halt (CPU_STATE (cpu), cpu, NULL, aarch64_get_PC (cpu),
|
|
sim_stopped, SIM_SIGTRAP);
|
|
}
|
|
|
|
aarch64_set_reg_u64 (cpu, 0, NO_SP, result);
|
|
}
|
|
|
|
static void
|
|
dexExcpnGen (sim_cpu *cpu)
|
|
{
|
|
/* instr[31:24] = 11010100
|
|
instr[23,21] = opc : 000 ==> GEN EXCPN, 001 ==> BRK
|
|
010 ==> HLT, 101 ==> DBG GEN EXCPN
|
|
instr[20,5] = imm16
|
|
instr[4,2] = opc2 000 ==> OK, ow ==> UNALLOC
|
|
instr[1,0] = LL : discriminates opc */
|
|
|
|
uint32_t opc = uimm (aarch64_get_instr (cpu), 23, 21);
|
|
uint32_t imm16 = uimm (aarch64_get_instr (cpu), 20, 5);
|
|
uint32_t opc2 = uimm (aarch64_get_instr (cpu), 4, 2);
|
|
uint32_t LL;
|
|
|
|
NYI_assert (31, 24, 0xd4);
|
|
|
|
if (opc2 != 0)
|
|
HALT_UNALLOC;
|
|
|
|
LL = uimm (aarch64_get_instr (cpu), 1, 0);
|
|
|
|
/* We only implement HLT and BRK for now. */
|
|
if (opc == 1 && LL == 0)
|
|
{
|
|
TRACE_EVENTS (cpu, " BRK [0x%x]", imm16);
|
|
sim_engine_halt (CPU_STATE (cpu), cpu, NULL, aarch64_get_PC (cpu),
|
|
sim_exited, aarch64_get_reg_s32 (cpu, R0, SP_OK));
|
|
}
|
|
|
|
if (opc == 2 && LL == 0)
|
|
handle_halt (cpu, imm16);
|
|
|
|
else if (opc == 0 || opc == 5)
|
|
HALT_NYI;
|
|
|
|
else
|
|
HALT_UNALLOC;
|
|
}
|
|
|
|
/* Stub for accessing system registers.
|
|
We implement support for the DCZID register since this is used
|
|
by the C library's memset function. */
|
|
|
|
static uint64_t
|
|
system_get (sim_cpu *cpu, unsigned op0, unsigned op1, unsigned crn,
|
|
unsigned crm, unsigned op2)
|
|
{
|
|
if (crn == 0 && op1 == 3 && crm == 0 && op2 == 7)
|
|
/* DCZID_EL0 - the Data Cache Zero ID register.
|
|
We do not support DC ZVA at the moment, so
|
|
we return a value with the disable bit set. */
|
|
return ((uint64_t) 1) << 4;
|
|
|
|
HALT_NYI;
|
|
}
|
|
|
|
static void
|
|
do_mrs (sim_cpu *cpu)
|
|
{
|
|
/* instr[31:20] = 1101 01010 0011
|
|
instr[19] = op0
|
|
instr[18,16] = op1
|
|
instr[15,12] = CRn
|
|
instr[11,8] = CRm
|
|
instr[7,5] = op2
|
|
instr[4,0] = Rt */
|
|
unsigned sys_op0 = uimm (aarch64_get_instr (cpu), 19, 19) + 2;
|
|
unsigned sys_op1 = uimm (aarch64_get_instr (cpu), 18, 16);
|
|
unsigned sys_crn = uimm (aarch64_get_instr (cpu), 15, 12);
|
|
unsigned sys_crm = uimm (aarch64_get_instr (cpu), 11, 8);
|
|
unsigned sys_op2 = uimm (aarch64_get_instr (cpu), 7, 5);
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
|
|
aarch64_set_reg_u64 (cpu, rt, NO_SP,
|
|
system_get (cpu, sys_op0, sys_op1, sys_crn, sys_crm, sys_op2));
|
|
}
|
|
|
|
static void
|
|
dexSystem (sim_cpu *cpu)
|
|
{
|
|
/* instr[31:22] = 1101 01010 0
|
|
instr[21] = L
|
|
instr[20,19] = op0
|
|
instr[18,16] = op1
|
|
instr[15,12] = CRn
|
|
instr[11,8] = CRm
|
|
instr[7,5] = op2
|
|
instr[4,0] = uimm5 */
|
|
|
|
/* We are interested in HINT, DSB, DMB and ISB
|
|
|
|
Hint #0 encodes NOOP (this is the only hint we care about)
|
|
L == 0, op0 == 0, op1 = 011, CRn = 0010, Rt = 11111,
|
|
CRm op2 != 0000 000 OR CRm op2 == 0000 000 || CRm op > 0000 101
|
|
|
|
DSB, DMB, ISB are data store barrier, data memory barrier and
|
|
instruction store barrier, respectively, where
|
|
|
|
L == 0, op0 == 0, op1 = 011, CRn = 0011, Rt = 11111,
|
|
op2 : DSB ==> 100, DMB ==> 101, ISB ==> 110
|
|
CRm<3:2> ==> domain, CRm<1:0> ==> types,
|
|
domain : 00 ==> OuterShareable, 01 ==> Nonshareable,
|
|
10 ==> InerShareable, 11 ==> FullSystem
|
|
types : 01 ==> Reads, 10 ==> Writes,
|
|
11 ==> All, 00 ==> All (domain == FullSystem). */
|
|
|
|
unsigned rt = uimm (aarch64_get_instr (cpu), 4, 0);
|
|
uint32_t l_op0_op1_crn = uimm (aarch64_get_instr (cpu), 21, 12);
|
|
|
|
NYI_assert (31, 22, 0x354);
|
|
|
|
switch (l_op0_op1_crn)
|
|
{
|
|
case 0x032:
|
|
if (rt == 0x1F)
|
|
{
|
|
/* NOP has CRm != 0000 OR. */
|
|
/* (CRm == 0000 AND (op2 == 000 OR op2 > 101)). */
|
|
uint32_t crm = uimm (aarch64_get_instr (cpu), 11, 8);
|
|
uint32_t op2 = uimm (aarch64_get_instr (cpu), 7, 5);
|
|
|
|
if (crm != 0 || (op2 == 0 || op2 > 5))
|
|
{
|
|
/* Actually call nop method so we can reimplement it later. */
|
|
nop (cpu);
|
|
return;
|
|
}
|
|
}
|
|
HALT_NYI;
|
|
|
|
case 0x033:
|
|
{
|
|
uint32_t op2 = uimm (aarch64_get_instr (cpu), 7, 5);
|
|
|
|
switch (op2)
|
|
{
|
|
case 2: HALT_NYI;
|
|
case 4: dsb (cpu); return;
|
|
case 5: dmb (cpu); return;
|
|
case 6: isb (cpu); return;
|
|
case 7:
|
|
default: HALT_UNALLOC;
|
|
}
|
|
}
|
|
|
|
case 0x3B0:
|
|
/* MRS Wt, sys-reg. */
|
|
do_mrs (cpu);
|
|
return;
|
|
|
|
case 0x3B4:
|
|
case 0x3BD:
|
|
/* MRS Xt, sys-reg. */
|
|
do_mrs (cpu);
|
|
return;
|
|
|
|
case 0x0B7:
|
|
/* DC <type>, x<n>. */
|
|
HALT_NYI;
|
|
return;
|
|
|
|
default:
|
|
/* if (uimm (aarch64_get_instr (cpu), 21, 20) == 0x1)
|
|
MRS Xt, sys-reg. */
|
|
HALT_NYI;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dexBr (sim_cpu *cpu)
|
|
{
|
|
/* uint32_t group = dispatchGroup (aarch64_get_instr (cpu));
|
|
assert group == GROUP_BREXSYS_1010 || group == GROUP_BREXSYS_1011
|
|
bits [31,29] of a BrExSys are the secondary dispatch vector. */
|
|
uint32_t group2 = dispatchBrExSys (aarch64_get_instr (cpu));
|
|
|
|
switch (group2)
|
|
{
|
|
case BR_IMM_000:
|
|
return dexBranchImmediate (cpu);
|
|
|
|
case BR_IMMCMP_001:
|
|
/* Compare has bit 25 clear while test has it set. */
|
|
if (!uimm (aarch64_get_instr (cpu), 25, 25))
|
|
dexCompareBranchImmediate (cpu);
|
|
else
|
|
dexTestBranchImmediate (cpu);
|
|
return;
|
|
|
|
case BR_IMMCOND_010:
|
|
/* This is a conditional branch if bit 25 is clear otherwise
|
|
unallocated. */
|
|
if (!uimm (aarch64_get_instr (cpu), 25, 25))
|
|
dexCondBranchImmediate (cpu);
|
|
else
|
|
HALT_UNALLOC;
|
|
return;
|
|
|
|
case BR_UNALLOC_011:
|
|
HALT_UNALLOC;
|
|
|
|
case BR_IMM_100:
|
|
dexBranchImmediate (cpu);
|
|
return;
|
|
|
|
case BR_IMMCMP_101:
|
|
/* Compare has bit 25 clear while test has it set. */
|
|
if (!uimm (aarch64_get_instr (cpu), 25, 25))
|
|
dexCompareBranchImmediate (cpu);
|
|
else
|
|
dexTestBranchImmediate (cpu);
|
|
return;
|
|
|
|
case BR_REG_110:
|
|
/* Unconditional branch reg has bit 25 set. */
|
|
if (uimm (aarch64_get_instr (cpu), 25, 25))
|
|
dexBranchRegister (cpu);
|
|
|
|
/* This includes both Excpn Gen, System and unalloc operations.
|
|
We need to decode the Excpn Gen operation BRK so we can plant
|
|
debugger entry points.
|
|
Excpn Gen operations have aarch64_get_instr (cpu)[24] = 0.
|
|
we need to decode at least one of the System operations NOP
|
|
which is an alias for HINT #0.
|
|
System operations have aarch64_get_instr (cpu)[24,22] = 100. */
|
|
else if (uimm (aarch64_get_instr (cpu), 24, 24) == 0)
|
|
dexExcpnGen (cpu);
|
|
|
|
else if (uimm (aarch64_get_instr (cpu), 24, 22) == 4)
|
|
dexSystem (cpu);
|
|
|
|
else
|
|
HALT_UNALLOC;
|
|
|
|
return;
|
|
|
|
case BR_UNALLOC_111:
|
|
HALT_UNALLOC;
|
|
|
|
default:
|
|
/* Should never reach here. */
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static void
|
|
aarch64_decode_and_execute (sim_cpu *cpu, uint64_t pc)
|
|
{
|
|
/* We need to check if gdb wants an in here. */
|
|
/* checkBreak (cpu);. */
|
|
|
|
uint64_t group = dispatchGroup (aarch64_get_instr (cpu));
|
|
|
|
switch (group)
|
|
{
|
|
case GROUP_PSEUDO_0000: dexPseudo (cpu); break;
|
|
case GROUP_LDST_0100: dexLdSt (cpu); break;
|
|
case GROUP_DPREG_0101: dexDPReg (cpu); break;
|
|
case GROUP_LDST_0110: dexLdSt (cpu); break;
|
|
case GROUP_ADVSIMD_0111: dexAdvSIMD0 (cpu); break;
|
|
case GROUP_DPIMM_1000: dexDPImm (cpu); break;
|
|
case GROUP_DPIMM_1001: dexDPImm (cpu); break;
|
|
case GROUP_BREXSYS_1010: dexBr (cpu); break;
|
|
case GROUP_BREXSYS_1011: dexBr (cpu); break;
|
|
case GROUP_LDST_1100: dexLdSt (cpu); break;
|
|
case GROUP_DPREG_1101: dexDPReg (cpu); break;
|
|
case GROUP_LDST_1110: dexLdSt (cpu); break;
|
|
case GROUP_ADVSIMD_1111: dexAdvSIMD1 (cpu); break;
|
|
|
|
case GROUP_UNALLOC_0001:
|
|
case GROUP_UNALLOC_0010:
|
|
case GROUP_UNALLOC_0011:
|
|
HALT_UNALLOC;
|
|
|
|
default:
|
|
/* Should never reach here. */
|
|
HALT_NYI;
|
|
}
|
|
}
|
|
|
|
static bfd_boolean
|
|
aarch64_step (sim_cpu *cpu)
|
|
{
|
|
uint64_t pc = aarch64_get_PC (cpu);
|
|
|
|
if (pc == TOP_LEVEL_RETURN_PC)
|
|
return FALSE;
|
|
|
|
aarch64_set_next_PC (cpu, pc + 4);
|
|
aarch64_get_instr (cpu) = aarch64_get_mem_u32 (cpu, pc);
|
|
|
|
TRACE_INSN (cpu, " pc = %" PRIx64 " instr = %x", pc,
|
|
aarch64_get_instr (cpu));
|
|
TRACE_DISASM (cpu, pc);
|
|
|
|
aarch64_decode_and_execute (cpu, pc);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
aarch64_run (SIM_DESC sd)
|
|
{
|
|
sim_cpu *cpu = STATE_CPU (sd, 0);
|
|
|
|
while (aarch64_step (cpu))
|
|
aarch64_update_PC (cpu);
|
|
|
|
sim_engine_halt (sd, NULL, NULL, aarch64_get_PC (cpu),
|
|
sim_exited, aarch64_get_reg_s32 (cpu, R0, SP_OK));
|
|
}
|
|
|
|
void
|
|
aarch64_init (sim_cpu *cpu, uint64_t pc)
|
|
{
|
|
uint64_t sp = aarch64_get_stack_start (cpu);
|
|
|
|
/* Install SP, FP and PC and set LR to -20
|
|
so we can detect a top-level return. */
|
|
aarch64_set_reg_u64 (cpu, SP, SP_OK, sp);
|
|
aarch64_set_reg_u64 (cpu, FP, SP_OK, sp);
|
|
aarch64_set_reg_u64 (cpu, LR, SP_OK, TOP_LEVEL_RETURN_PC);
|
|
aarch64_set_next_PC (cpu, pc);
|
|
aarch64_update_PC (cpu);
|
|
aarch64_init_LIT_table ();
|
|
}
|