blob: e5ada18efee50be29c42a7758289ddea0a78eefa [file] [log] [blame]
/* 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 )) ? 1 : 0)
#define IS_CLEAR(_X) (TST (( _X )) ? 0 : 1)
/* Space saver macro. */
#define INSTR(HIGH, LOW) uimm (aarch64_get_instr (cpu), (HIGH), (LOW))
#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)); \
if (! TRACE_ANY_P (cpu)) \
sim_io_eprintf (CPU_STATE (cpu), "SIM Error: Unimplemented instruction: %#08x\n", \
aarch64_get_instr (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 (INSTR ((HI), (LO)) != (EXPECTED)) \
HALT_NYI; \
} \
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 = INSTR (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 = INSTR (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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_vec_u32 (cpu, rd, 0,
aarch64_get_mem_u32
(cpu, aarch64_get_PC (cpu) + offset * 4));
}
/* double pc-relative load */
static void
fldrd_pcrel (sim_cpu *cpu, int32_t offset)
{
unsigned int st = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_vec_u64 (cpu, st, 0,
aarch64_get_mem_u64
(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 = INSTR (4, 0);
uint64_t addr = aarch64_get_PC (cpu) + offset * 4;
FRegister a;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned st = INSTR (4, 0);
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
if (wb != Post)
address += offset;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_vec_u32 (cpu, st, 0, aarch64_get_mem_u32 (cpu, address));
if (wb == Post)
address += offset;
if (wb != NoWriteBack)
aarch64_set_reg_u64 (cpu, rn, SP_OK, address);
}
/* Load 8 bit with unsigned 12 bit offset. */
static void
fldrb_abs (sim_cpu *cpu, uint32_t offset)
{
unsigned rd = INSTR (4, 0);
unsigned rn = INSTR (9, 5);
uint64_t addr = aarch64_get_reg_u64 (cpu, rn, SP_OK) + offset;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_vec_u8 (cpu, rd, 0, aarch64_get_mem_u32 (cpu, addr));
}
/* Load 16 bit scaled unsigned 12 bit. */
static void
fldrh_abs (sim_cpu *cpu, uint32_t offset)
{
unsigned rd = INSTR (4, 0);
unsigned rn = INSTR (9, 5);
uint64_t addr = aarch64_get_reg_u64 (cpu, rn, SP_OK) + SCALE (offset, 16);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_vec_u16 (cpu, rd, 0, aarch64_get_mem_u16 (cpu, addr));
}
/* Load 32 bit scaled unsigned 12 bit. */
static void
fldrs_abs (sim_cpu *cpu, uint32_t offset)
{
unsigned rd = INSTR (4, 0);
unsigned rn = INSTR (9, 5);
uint64_t addr = aarch64_get_reg_u64 (cpu, rn, SP_OK) + SCALE (offset, 32);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_vec_u32 (cpu, rd, 0, aarch64_get_mem_u32 (cpu, addr));
}
/* Load 64 bit scaled unsigned 12 bit. */
static void
fldrd_abs (sim_cpu *cpu, uint32_t offset)
{
unsigned rd = INSTR (4, 0);
unsigned rn = INSTR (9, 5);
uint64_t addr = aarch64_get_reg_u64 (cpu, rn, SP_OK) + SCALE (offset, 64);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_vec_u64 (cpu, rd, 0, aarch64_get_mem_u64 (cpu, addr));
}
/* Load 128 bit scaled unsigned 12 bit. */
static void
fldrq_abs (sim_cpu *cpu, uint32_t offset)
{
unsigned rd = INSTR (4, 0);
unsigned rn = INSTR (9, 5);
uint64_t addr = aarch64_get_reg_u64 (cpu, rn, SP_OK) + SCALE (offset, 128);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_vec_u64 (cpu, rd, 0, aarch64_get_mem_u64 (cpu, addr));
aarch64_set_vec_u64 (cpu, rd, 1, aarch64_get_mem_u64 (cpu, addr + 8));
}
/* 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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned st = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_vec_u32 (cpu, st, 0, aarch64_get_mem_u32
(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 = INSTR (9, 5);
unsigned st = INSTR (4, 0);
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
if (wb != Post)
address += offset;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_vec_u64 (cpu, st, 0, aarch64_get_mem_u64 (cpu, address));
if (wb == Post)
address += offset;
if (wb != NoWriteBack)
aarch64_set_reg_u64 (cpu, rn, SP_OK, 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 = INSTR (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 = INSTR (9, 5);
unsigned st = INSTR (4, 0);
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
if (wb != Post)
address += offset;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 or unscaled zero- or sign-extended 32-bit register offset */
static void
fldrq_scale_ext (sim_cpu *cpu, Scaling scaling, Extension extension)
{
unsigned rm = INSTR (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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
/* 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 = INSTR (9, 5);
unsigned rt = INSTR (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;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
/* 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 = INSTR (9, 5);
unsigned rt = INSTR (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;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
/* 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 = INSTR (9, 5);
unsigned rt = INSTR (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;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
/* 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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
uint64_t address;
int64_t val;
if (rn == rt && wb != NoWriteBack)
HALT_UNALLOC;
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
if (wb != Post)
address += offset;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
val = aarch64_get_mem_s8 (cpu, address);
aarch64_set_reg_s64 (cpu, rt, NO_SP, val);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
/* There is no scaling required for a byte load. */
aarch64_set_reg_s64 (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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
uint32_t val;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
/* The target register may not be SP but the source may be. */
val = aarch64_get_mem_u16 (cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
+ SCALE (offset, 16));
aarch64_set_reg_u32 (cpu, rt, NO_SP, val);
}
/* 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 = INSTR (9, 5);
unsigned rt = INSTR (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;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_reg_u32 (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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_reg_u32 (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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
int32_t val;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
/* The target register may not be SP but the source may be. */
val = aarch64_get_mem_s16 (cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
+ SCALE (offset, 16));
aarch64_set_reg_s32 (cpu, rt, NO_SP, val);
}
/* 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 = INSTR (9, 5);
unsigned rt = INSTR (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;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_reg_s32 (cpu, rt, NO_SP,
(int32_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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
aarch64_set_reg_s32 (cpu, rt, NO_SP,
(int32_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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
int64_t val;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
/* The target register may not be SP but the source may be. */
val = aarch64_get_mem_s16 (cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
+ SCALE (offset, 16));
aarch64_set_reg_s64 (cpu, rt, NO_SP, val);
}
/* 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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
uint64_t address;
int64_t val;
if (rn == rt && wb != NoWriteBack)
HALT_UNALLOC;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
if (wb != Post)
address += offset;
val = aarch64_get_mem_s16 (cpu, address);
aarch64_set_reg_s64 (cpu, rt, NO_SP, val);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (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);
int64_t val;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
val = aarch64_get_mem_s16 (cpu, address + displacement);
aarch64_set_reg_s64 (cpu, rt, NO_SP, val);
}
/* 64 bit load sign-extended 32 bit scaled unsigned 12 bit. */
static void
ldrsw_abs (sim_cpu *cpu, uint32_t offset)
{
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
int64_t val;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
val = aarch64_get_mem_s32 (cpu, aarch64_get_reg_u64 (cpu, rn, SP_OK)
+ SCALE (offset, 32));
/* The target register may not be SP but the source may be. */
return aarch64_set_reg_s64 (cpu, rt, NO_SP, val);
}
/* 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 = INSTR (9, 5);
unsigned rt = INSTR (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;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
/* 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 = INSTR (9, 5);
unsigned rt = INSTR (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;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rt = INSTR (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;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
/* 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 = INSTR (9, 5);
unsigned rt = INSTR (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;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
/* 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 = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
/* 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 = INSTR (9, 5);
unsigned rt = INSTR (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;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 (instr, 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 (instr, 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 (instr, 4, 0);
uint64_t address = aarch64_get_PC (cpu) + offset. */
/* TODO : implement this */
}
/* Load-store exclusive. */
static void
ldxr (sim_cpu *cpu)
{
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
int size = INSTR (31, 30);
/* int ordered = INSTR (15, 15); */
/* int exclusive = ! INSTR (23, 23); */
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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;
}
}
static void
stxr (sim_cpu *cpu)
{
unsigned rn = INSTR (9, 5);
unsigned rt = INSTR (4, 0);
unsigned rs = INSTR (20, 16);
uint64_t address = aarch64_get_reg_u64 (cpu, rn, SP_OK);
int size = INSTR (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;
}
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (4, 0); */
uint32_t dispatch = (INSTR (31, 30) << 1) | INSTR (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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = 1U << 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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
/* TODO : do we need to worry about signs here? */
int32_t value1 = aarch64_get_reg_s32 (cpu, rn, SP_OK);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, SP_OK);
uint64_t value2 = aimm;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
uint32_t value1 = aarch64_get_reg_u64 (cpu, rn, SP_OK);
uint32_t value2 = aimm;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, SP_OK);
uint32_t value2 = aimm;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
uint32_t value1 = aarch64_get_reg_u32 (cpu, rn, SP_OK);
uint32_t value2 = extreg32 (cpu, rm, extension) << shift;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, SP_OK);
uint64_t value2 = extreg64 (cpu, rm, extension) << shift;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
uint32_t value1 = aarch64_get_reg_u32 (cpu, rn, SP_OK);
uint32_t value2 = extreg32 (cpu, rm, extension) << shift;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
uint64_t value1 = aarch64_get_reg_u64 (cpu, rn, SP_OK);
uint64_t value2 = extreg64 (cpu, rm, extension) << shift;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (23, 22);
uint32_t imm = INSTR (21, 10);
uint32_t dispatch = INSTR (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;
}
}
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 = INSTR (31, 31);
uint32_t count = INSTR (15, 10);
Shift shiftType = INSTR (23, 22);
NYI_assert (28, 24, 0x0B);
NYI_assert (21, 21, 0);
/* Shift encoded as ROR is unallocated. */
if (shiftType == ROR)
HALT_UNALLOC;
/* 32 bit operations must have count[5] = 0
or else we have an UNALLOC. */
if (size == 0 && uimm (count, 5, 5))
HALT_UNALLOC;
/* Dispatch on size:op i.e instr [31,29]. */
switch (INSTR (31, 29))
{
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;
}
}
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 = INSTR (15, 13);
uint32_t shift = INSTR (12, 10);
NYI_assert (28, 24, 0x0B);
NYI_assert (21, 21, 1);
/* Shift may not exceed 4. */
if (shift > 4)
HALT_UNALLOC;
/* Dispatch on size:op:set?. */
switch (INSTR (31, 29))
{
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;
}
}
/* 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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5); /* ngc iff rn == 31. */
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (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;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (20, 16);
unsigned rn = INSTR (9, 5);
unsigned rd = INSTR (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;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (15, 10);
NYI_assert (28, 21, 0xD0);
if (op2 != 0)
HALT_UNALLOC;
/* Dispatch on size:op:set?. */
switch (INSTR (31, 29))
{
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;
}
}
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 (1) or negative value (0)
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);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
if (! testConditionCode (cpu, INSTR (15, 12)))
{
aarch64_set_CPSR (cpu, INSTR (3, 0));
return;
}
negate = INSTR (30, 30) ? 1 : -1;
rm = INSTR (20, 16);
rn = INSTR ( 9, 5);
if (INSTR (31, 31))
{
if (INSTR (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 (INSTR (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 = INSTR (9, 5);
unsigned vd = INSTR (4, 0);
NYI_assert (29, 21, 0x075);
NYI_assert (15, 10, 0x07);
if (INSTR (20, 16) != vs)
HALT_NYI;
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
if (INSTR (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 = INSTR (9, 5);
unsigned rd = INSTR (4, 0);
NYI_assert (29, 21, 0x070);
NYI_assert (17, 10, 0x0F);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
switch (INSTR (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, INSTR (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 = INSTR (9, 5);
unsigned vd = INSTR (4, 0);
NYI_assert (31, 21, 0x270);
NYI_assert (15, 10, 0x07);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
if (INSTR (16, 16))
{
index = INSTR (20, 17);
aarch64_set_vec_u8 (cpu, vd, index,
aarch64_get_reg_u8 (cpu, rs, NO_SP));
}
else if (INSTR (17, 17))
{
index = INSTR (20, 18);
aarch64_set_vec_u16 (cpu, vd, index,
aarch64_get_reg_u16 (cpu, rs, NO_SP));
}
else if (INSTR (18, 18))
{
index = INSTR (20, 19);
aarch64_set_vec_u32 (cpu, vd, index,
aarch64_get_reg_u32 (cpu, rs, NO_SP));
}
else if (INSTR (19, 19))
{
index = INSTR (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 = INSTR (30, 30);
unsigned vs = INSTR (9, 5);
unsigned vd = INSTR (4, 0);
int i, index;
NYI_assert (29, 21, 0x070);
NYI_assert (15, 10, 0x01);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
if (INSTR (16, 16))
{
index = INSTR (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 (INSTR (17, 17))
{
index = INSTR (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 (INSTR (18, 18))
{
index = INSTR (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 (INSTR (19, 19) == 0)
HALT_UNALLOC;
if (! full)
HALT_UNALLOC;
index = INSTR (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 = INSTR (30, 30);
int len = INSTR (14, 13) + 1;
unsigned vm = INSTR (20, 16);
unsigned vn = INSTR (9, 5);
unsigned vd = INSTR (4, 0);
unsigned i;
NYI_assert (29, 21, 0x070);
NYI_assert (12, 10, 0);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
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 = INSTR (30, 30);
int second = INSTR (14, 14);
unsigned vm = INSTR (20, 16);
unsigned vn = INSTR (9, 5);
unsigned vd = INSTR (4, 0);
unsigned i;
NYI_assert (29, 24, 0x0E);
NYI_assert (13, 10, 0xA);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
switch (INSTR (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;
}
}
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 = INSTR (4, 0);
unsigned Rs = INSTR (9, 5);
int both = INSTR (30, 30);
NYI_assert (29, 20, 0x0E0);
NYI_assert (15, 10, 0x03);
TRACE_DECODE (cpu, "emulated at line %d", __LINE__);
switch (INSTR (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;
}