target: e2k: Basic instruction execution.

This commit is contained in:
Denis Drakhnia 2020-11-10 14:06:44 +02:00 committed by Denis Drakhnia
parent ed6b1618db
commit 38497f952b
7 changed files with 948 additions and 34 deletions

View File

@ -75,9 +75,28 @@ void e2k_cpu_dump_state(CPUState *cs, FILE *f, int flags)
{
E2KCPU *cpu = E2K_CPU(cs);
CPUE2KState *env = &cpu->env;
unsigned int i;
qemu_fprintf(f, "ip: " TARGET_FMT_lx " nip: " TARGET_FMT_lx "\n", env->ip,
env->nip);
for (i = 0; i < 192; i += 4) {
const char *s1 = i < 10 ? " " : (i < 100 ? " " : "");
const char *s2 = i + 2 < 10 ? " " : (i + 2 < 100 ? " " : "");
qemu_fprintf(f, "%s%%r%d = %16lx ", s1, i , env->wregs[i]);
qemu_fprintf(f, "%s%%r%d = %16lx ", s1, i + 1, env->wregs[i + 1]);
qemu_fprintf(f, "%s%%r%d = %16lx ", s2, i + 2, env->wregs[i + 2]);
qemu_fprintf(f, "%s%%r%d = %16lx\n", s2, i + 3, env->wregs[i + 3]);
}
for (i = 0; i < 32; i += 4) {
const char *s1 = i < 10 ? " " : (i < 100 ? " " : "");
const char *s2 = i + 2 < 10 ? " " : (i + 2 < 100 ? " " : "");
qemu_fprintf(f, "%s%%g%d = %16lx ", s1, i , env->gregs[i]);
qemu_fprintf(f, "%s%%g%d = %16lx ", s1, i + 1, env->gregs[i + 1]);
qemu_fprintf(f, "%s%%g%d = %16lx ", s2, i + 2, env->gregs[i + 2]);
qemu_fprintf(f, "%s%%g%d = %16lx\n", s2, i + 3, env->gregs[i + 3]);
}
}
static void e2k_cpu_set_pc(CPUState *cs, vaddr value)

View File

@ -9,6 +9,7 @@ void e2k_tcg_initialize(void);
#define MMU_USER_IDX 1
#define CPU_RESOLVING_TYPE TYPE_E2K_CPU
#define WREGS_SIZE 224
struct e2k_def_t {
const char *name;
@ -17,11 +18,18 @@ struct e2k_def_t {
typedef struct CPUArchState {
/* register file */
CPU_QuadU gregs[32]; /* general registers */
CPU_QuadU pregs[224]; /* procedure regs, access through wreg */
uint8_t greg_tags[32];
uint8_t preg_tags[224];
/* register file */
target_ulong gregs[32]; // global regs
target_ulong wregs[WREGS_SIZE]; // window regs
unsigned int wbs; // window regs offset (* 2)
unsigned int wsz; // window regs size (* 2)
unsigned int nfx; // TODO
unsigned int dbl; // TODO
unsigned int rbs; // based regs offset (* 2)
unsigned int rsz; // based regs window size (* 2 + 2)
unsigned int rcur; // based regs current index (* 2)
unsigned int psz; // pred regs window size
/* control registers */
target_ulong ctpr1; // Control Transfer Preparation Register (CTPR)
@ -29,8 +37,6 @@ typedef struct CPUArchState {
target_ulong ctpr3;
/* special registers */
target_ulong *wreg; /* pointer to current window */
uint32_t br; /* base rsegister offset, max 128 */
target_ulong ip, nip; /* instruction address, next instruction address */
uint32_t pfpfr; // Packed Floating Point Flag Register (PFPFR)

13
target/e2k/helper.c Normal file
View File

@ -0,0 +1,13 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
#include "qemu/host-utils.h"
#include "exec/helper-proto.h"
void helper_raise_exception(CPUE2KState *env, int tt)
{
CPUState *cs = env_cpu(env);
cs->exception_index = tt;
cpu_loop_exit(cs);
}

View File

@ -1 +1,3 @@
// include/exec/helper-proto.h needs this file
DEF_HELPER_2(raise_exception, noreturn, env, int)

0
target/e2k/helper_int.c Normal file
View File

View File

@ -1,6 +1,8 @@
e2k_ss = ss.source_set()
e2k_ss.add(files(
'cpu.c',
'helper.c',
'helper_int.c',
'translate.c',
))

View File

@ -4,11 +4,37 @@
#include "exec/translator.h"
#include "tcg/tcg-op.h"
#define TEMP_COUNT_32 16
#define TEMP_COUNT_64 16
static TCGv_i64 cpu_wregs[WREGS_SIZE];
static TCGv_i64 cpu_gregs[32];
static TCGv_i32 cpu_wbs;
static TCGv_i32 cpu_wsz;
static TCGv_i32 cpu_nfx;
static TCGv_i32 cpu_dbl;
static TCGv_i32 cpu_rbs;
static TCGv_i32 cpu_rsz;
static TCGv_i32 cpu_rcur;
static TCGv_i32 cpu_psz;
static TCGv cpu_pc, cpu_npc;
enum ResultType {
RESULT_NONE,
RESULT_BASED_REG,
RESULT_REGULAR_REG,
RESULT_GLOBAL_REG,
};
typedef struct {
DisasContextBase base;
target_ulong pc;
target_ulong npc;
} DisasContext;
enum ResultType tag;
union {
struct {
unsigned int i;
TCGv_i64 v;
} reg;
} u;
} Result;
static struct unpacked_instr {
unsigned int hs;
@ -44,12 +70,60 @@ static struct unpacked_instr {
unsigned int api_r[2];
} unpacked_instr;
typedef struct {
DisasContextBase base;
struct unpacked_instr instr;
target_ulong pc;
target_ulong npc;
// FIXME: I do not know if it is right place to store this values.
unsigned int wbs;
unsigned int wsz;
unsigned int nfx;
unsigned int dbl;
unsigned int rbs;
unsigned int rsz;
unsigned int rcur;
unsigned int psz;
// Temporary values.
TCGv_i32 t32[TEMP_COUNT_32];
TCGv_i64 t64[TEMP_COUNT_64];
// Allocated temporary values count.
int t32_len;
int t64_len;
target_ulong jump_pc;
Result alc[6];
} DisasContext;
#define GET_FIELD(v, start, end) \
(((v) >> (start)) & ((1 << ((end) - (start) + 1)) - 1))
#define IS_BASED(i) (((i) & 0x80) == 0)
#define IS_REGULAR(i) (((i) & 0xc0) == 0x80)
#define IS_IMM5(i) (((i) & 0xe0) == 0xc0)
#define IS_IMM4(i) (((i) & 0xf0) == 0xc0)
#define IS_LIT(i) (((i) & 0xf0) == 0xd0)
#define IS_LIT16_LO(i) (((i) & 0x0e) == 0x00)
#define IS_LIT16_HI(i) (((i) & 0x0e) == 0x04)
#define IS_LIT32(i) (((i) & 0x0c) == 0x08)
#define IS_LIT64(i) (((i) & 0x0c) == 0x0c)
#define IS_GLOBAL(i) (((i) & 0xe0) == 0xe0)
#define GET_BASED(i) ((i) & 0x7f)
#define GET_REGULAR(i) ((i) & 0x3f)
#define GET_IMM5(i) ((i) & 0x1f)
#define GET_IMM4(i) ((i) & 0x0f)
#define GET_LIT(i) ((i) & 0x03)
#define GET_GLOBAL(i) ((i) & 0x1f)
// TODO: return error on invalid instruction
static target_ulong unpack_instr(CPUE2KState *env, DisasContext *ctx, target_ulong pos,
static target_ulong unpack_instr(CPUE2KState *env, DisasContext *ctx, target_ulong pc,
struct unpacked_instr *instr)
{
unsigned int i;
target_ulong gap, next_pc = pos + 8;
target_ulong gap, pos = pc, next_pc = pos + 8;
int hsyll_cntr = 0;
unsigned int hs;
unsigned int mdl;
@ -109,7 +183,7 @@ static target_ulong unpack_instr(CPUE2KState *env, DisasContext *ctx, target_ulo
/* Calculate the size of f1 fragment in bytes. For a valid instruction it
should be equal to either of `pos', `pos + 4' or `pos + 8'. What should I
do if it's not? */
mdl = ((hs & 0xf) + 1) * 4;
mdl = pc + ((hs & 0xf) + 1) * 4;
/* The following condition means that ALES{2,5} are physically present within
the wide instruction. However, they should be probably taken into account
@ -217,7 +291,7 @@ static target_ulong unpack_instr(CPUE2KState *env, DisasContext *ctx, target_ulo
/* Set POS to point to the last syllable in the current wide instruction and
extract CDSj and PLSj syllables if any. */
pos = ((((hs & 0x70) >> 4) + 1) << 3) - 4;
pos = pc + ((((hs & 0x70) >> 4) + 1) << 3) - 4;
/* Check for CDSj syllables. */
for (i = 0; i < ((hs & 0x30000) >> 16); i++)
@ -269,18 +343,754 @@ static target_ulong unpack_instr(CPUE2KState *env, DisasContext *ctx, target_ulo
return next_pc;
}
static inline TCGv_i32 get_temp_i32(DisasContext *dc)
{
assert(dc->t32_len < ARRAY_SIZE(dc->t32));
return dc->t32[dc->t32_len++] = tcg_temp_new_i32();
}
static inline TCGv_i64 get_temp_i64(DisasContext *dc)
{
assert(dc->t64_len < ARRAY_SIZE(dc->t64));
return dc->t64[dc->t64_len++] = tcg_temp_new_i64();
}
static inline void save_npc(DisasContext *dc)
{
tcg_gen_movi_tl(cpu_npc, dc->npc);
}
static inline void save_state(DisasContext *dc)
{
tcg_gen_movi_tl(cpu_pc, dc->pc);
save_npc(dc);
}
static void gen_exception(DisasContext *dc, int which)
{
TCGv_i32 t;
save_state(dc);
t = tcg_const_i32(which);
gen_helper_raise_exception(cpu_env, t);
tcg_temp_free_i32(t);
dc->base.is_jmp = DISAS_NORETURN;
}
static inline unsigned int wreg_index(DisasContext *dc, int reg)
{
assert(reg < 64);
// TODO: exception
assert(reg < (dc->wsz * 2));
return (reg + dc->wbs * 2) % WREGS_SIZE;
}
static inline TCGv_i64 gen_load_wreg(DisasContext *dc, int reg)
{
return cpu_wregs[wreg_index(dc, reg)];
}
static inline void gen_store_wreg(DisasContext *dc, int reg, TCGv_i64 val)
{
unsigned int i = wreg_index(dc, reg);
tcg_gen_mov_i64(cpu_wregs[i], val);
}
static inline unsigned int breg_index(DisasContext *dc, int reg)
{
assert(reg < 128);
int rsz = dc->rsz * 2 + 2;
// TODO: exception
assert(reg < (dc->wsz * 2) && reg < rsz);
int i = (reg + (dc->rsz + 1 - dc->rcur) * 2) % rsz;
return (i + dc->wbs * 2 + dc->rbs * 2) % WREGS_SIZE;
}
static inline TCGv_i64 gen_load_breg(DisasContext *dc, int reg)
{
unsigned int i = breg_index(dc, reg);
return cpu_wregs[i];
}
static inline void gen_store_breg(DisasContext *dc, int reg, TCGv_i64 val)
{
unsigned int i = breg_index(dc, reg);
tcg_gen_mov_i64(cpu_wregs[i], val);
}
static inline TCGv_i64 gen_load_greg(DisasContext *dc, int reg)
{
// TODO: rotated gregs
assert(reg < 32);
return cpu_gregs[reg];
}
static inline void gen_store_greg(DisasContext *dc, int reg, TCGv_i64 val)
{
// TODO: rotated gregs
tcg_gen_mov_i64(reg, val);
}
static TCGv_i64 get_src1(DisasContext *dc, unsigned int als)
{
unsigned int src1 = GET_FIELD(als, 16, 27);
if (IS_BASED(src1)) {
unsigned int i = GET_BASED(src1);
return gen_load_breg(dc, i);
} else if (IS_REGULAR(src1)) {
unsigned int i = GET_REGULAR(src1);
return gen_load_wreg(dc, i);
} else if (IS_IMM5(src1)) {
unsigned int imm = GET_IMM5(src1);
TCGv_i64 t = get_temp_i64(dc);
tcg_gen_movi_i64(t, imm);
return t;
} else {
unsigned int i = GET_GLOBAL(src1);
return gen_load_greg(dc, i);
}
}
static TCGv_i64 get_src2(DisasContext *dc, unsigned int als)
{
unsigned int src2 = GET_FIELD(als, 8, 15);
if (IS_BASED(src2)) {
unsigned int i = GET_BASED(src2);
return gen_load_breg(dc, i);
} else if (IS_REGULAR(src2)) {
unsigned int i = GET_REGULAR(src2);
return gen_load_wreg(dc, i);
} else if (IS_IMM4(src2)) {
unsigned int imm = GET_IMM4(src2);
TCGv t = get_temp_i64(dc);
tcg_gen_movi_i64(t, imm);
return t;
} else if (IS_LIT(src2)) {
TCGv t = get_temp_i64(dc);
unsigned int i = GET_LIT(src2);
uint64_t lit = dc->instr.lts[i];
// TODO: exception
assert(dc->instr.lts_present[i]);
if (IS_LIT16_LO(src2) && i < 2) {
lit &= 0xffff;
} else if (IS_LIT16_HI(src2) && i < 2) {
lit >>= 16;
} else if (IS_LIT32(src2)) {
// nop
} else if (IS_LIT64(src2) && i < 3) {
// TODO: exception
assert(dc->instr.lts_present[i + 1]);
lit |= ((uint64_t) dc->instr.lts[i + 1]) << 32;
} else {
// TODO: exception
abort();
}
tcg_gen_movi_i64(t, lit);
return t;
} else {
unsigned int i = GET_GLOBAL(src2);
return gen_load_greg(dc, i);
}
}
static TCGv_i64 get_dst(DisasContext *dc, unsigned int als)
{
unsigned int dst = als & 0xff;
if (IS_BASED(dst)) {
unsigned int i = GET_BASED(dst);
return gen_load_breg(dc, i);
} else if (IS_REGULAR(dst)) {
unsigned int i = GET_REGULAR(dst);
return gen_load_wreg(dc, i);
} else if (IS_GLOBAL(dst)) {
unsigned int i = GET_GLOBAL(dst);
return gen_load_greg(dc, i);
} else {
// TODO: %empty, %ctpr, etc
abort();
}
}
static void gen_cs0(DisasContext *dc, CPUE2KState *env)
{
typedef enum {
NOTHING,
IBRANCH,
PREF,
PUTTSD,
DONE,
HRET,
GLAUNCH,
DISP,
SDISP,
GETTSD,
LDISP,
RETURN
} cs0_type;
static cs0_type cs0_ops[4][4] = {
{IBRANCH, PREF, PUTTSD, DONE},
{DISP, NOTHING, SDISP, GETTSD},
{DISP, LDISP, SDISP, GETTSD},
{DISP, NOTHING, SDISP, RETURN}
};
const struct unpacked_instr *instr = &dc->instr;
unsigned int cs0 = instr->cs0;
unsigned int ctpr = (cs0 & 0xc0000000) >> 30;
unsigned int ctp_opc = (cs0 & 0x30000000) >> 28;
unsigned int param_type = (cs0 & 0x00000007);
cs0_type type = cs0_ops[ctpr][ctp_opc];
if (type == RETURN && param_type == 1) {
type = GETTSD;
} else if (type == DONE) {
if (param_type == 3) {
type = HRET;
} else if (param_type == 4) {
type = GLAUNCH;
}
}
if (type == IBRANCH || type == DONE || type == HRET || type == GLAUNCH) {
/* IBRANCH, DONE, HRET and GLAUNCH are special because they require SS
to be properly encoded. */
if (! instr->ss_present
/* SS.ctop should be equal to zero for IBRANCH, DONE, HRET and
GLAUNCH (see C.17.1.1, note that they don't mention the latter two
instructions there which is probably an omission ). */
|| (instr->ss & 0x00000c00))
{
// TODO: invalid
abort();
}
/* Don't output either of the aforementioned instructions under "never"
condition. Don't disassemble CS0 being a part of HCALL. Unlike ldis
HCALL is currently disassembled on behalf of CS1. */
else if ((instr->ss & 0x1ff)
&& !(instr->cs1_present
/* CS1.opc == CALL */
&& (instr->cs1 & 0xf0000000) >> 28 == 5
/* CS1.param.ctopc == HCALL */
&& (instr->cs1 & 0x380) >> 7 == 2))
{
if (type == IBRANCH) {
/* C0F2 has `disp' field. In `C0F1' it's called `param'. Is this
the only difference between these two formats? Funnily enough,
DONE is also C0F2 and thus has `disp', though it obviously
makes no sense for it. */
unsigned int disp = (cs0 & 0x0fffffff);
/// Calculate a signed displacement in bytes.
int sdisp = ((int) (disp << 4)) >> 1;
dc->jump_pc = dc->base.pc_next + sdisp;
dc->base.is_jmp = DISAS_NORETURN;
}
// TODO: ctcond
// unsigned int ctcond = instr->ss & 0x1ff;
}
} else {
/* Note that according to Table B.4.1 it's possible to obtain
` gettsd %ctpr{1,2} with an invalid value for CS0.param.type. */
if (type == GETTSD && param_type != 1) {
// invalid
abort();
}
if (type == DISP
|| type == SDISP
|| type == LDISP
/* Note that RETURN is said to be COPF1. I can't understand what its
`CS0.param' is needed for: all of the bits except the three
lowermost ones are undefined, while the latter also known as "type"
field should be filled in with zeroes. */
|| type == RETURN
/* GETTSD has as meaningless `CS0.param' as RETURN. The only
difference is that its `CS0.param.type' should be equal to `1'. I
wonder if I should check for that and output something like
"invalid gettsd" if this turns out not to be the case . . . */
|| type == GETTSD)
{
// ctpr
}
if (type == SDISP) {
// my_printf (", 0x%x", cs0 & 0x1f);
} else if (type == DISP
|| type == LDISP
|| type == PUTTSD)
{
unsigned int disp = (cs0 & 0x0fffffff);
int sgnd_disp = ((int) (disp << 4)) >> 1;
/* PUTTSD obviously doesn't take %ctpr{j} parameter. TODO: beware of
an optional predicate which may control its execution which is
encoded via `SS.ctcond.psrc' and `SS.ts_opc == PUTTSDC{P,N}' in
case of `SS.type == 1' (see C.21.4). I wonder if `ct %ctpr<j>'
encoded in `SS.ctop' under the same `SS.ctcond' takes an effect in
such a case. */
// my_printf ("%s0x%llx", type == PUTTSD ? "" : ", ",
/* FIXME: this way I ensure that it'll work correctly
both on 32 and 64-bit hosts. */
// (unsigned long long) (instr_addr + sgnd_disp));
}
if (type == PREF) {
unsigned int pdisp = (instr->cs0 & 0x0ffffff0) >> 4;
unsigned int ipd = (instr->cs0 & 0x00000008) >> 3;
unsigned int prefr = instr->cs0 & 0x00000007;
}
}
}
static void gen_cs1(DisasContext *dc, CPUE2KState *env)
{
enum {
SETR0,
SETR1,
SETEI,
WAIT,
SETBR,
CALL,
MAS_OPC,
FLUSHR,
BG
};
const struct unpacked_instr *instr = &dc->instr;
unsigned int cs1 = instr->cs1;
unsigned int opc = (cs1 & 0xf0000000) >> 28;
if (opc == SETR0 || opc == SETR1 || opc == SETBR) {
unsigned int setbp = (cs1 & 0x08000000) >> 27;
unsigned int setbn = (cs1 & 0x04000000) >> 26;
/* Try to follow the same order of these instructions as in LDIS.
Presumably `vfrpsz' should come first, while `setbp' should be placed
between `setwd' and `setbn', but this is to be verified. I don't have
a binary with these commands by hand right now. */
if (opc == SETR1) {
if (! instr->lts_present[0]) {
// my_printf ("<bogus vfrpsz>");
} else {
/* Find out if VFRPSZ is always encoded together with SETWD. This
seems to be the case even if no SETWD has been explicitly
specified. */
unsigned int rpsz = (instr->lts[0] & 0x0001f000) >> 12;
// my_printf ("vfrpsz rpsz = 0x%x", rpsz);
}
}
if (opc == SETR0 || opc == SETR1) {
if (! instr->lts_present[0]) {
// TODO: <bogus setwd>
abort();
} else {
unsigned int lts0 = instr->lts[0];
dc->wsz = (lts0 & 0x00000fe0) >> 5;
dc->nfx = (lts0 & 0x00000010) >> 4;
if (env->version >= 3) {
// DBL parameter of SETWD was added only starting from
// elbrus-v3.
dc->dbl = (lts0 & 0x00000008) >> 3;
}
}
}
if (setbn) {
unsigned int rcur = (cs1 & 0x0003f000) >> 12;
unsigned int rsz = (cs1 & 0x00000fc0) >> 6;
unsigned int rbs = cs1 & 0x0000003f;
dc->rcur = rcur;
dc->rsz = rsz;
dc->rbs = rbs;
}
if (setbp) {
dc->psz = (cs1 & 0x007c0000) >> 18;
}
} else if (opc == SETEI) {
/* Verify that CS1.param.sft = CS1.param[27] is equal to zero as required
in C.14.3. */
unsigned int sft = (cs1 & 0x08000000) >> 27;
unsigned int eir = (cs1 & 0x000000ff);
if (sft) {
// my_printf ("%s", mcpu >= 2 ? "setsft" : "unimp");
} else {
// my_printf ("setei 0x%x", eir);
}
} else if (opc == WAIT) {
unsigned int ma_c = (cs1 & 0x00000020) >> 5;
unsigned int fl_c = (cs1 & 0x00000010) >> 4;
unsigned int ld_c = (cs1 & 0x00000008) >> 3;
unsigned int st_c = (cs1 & 0x00000004) >> 2;
unsigned int all_e = (cs1 & 0x00000002) >> 1;
unsigned int all_c = cs1 & 0x00000001;
if (env->version >= 5) {
/* `sa{l,s}' fields are `elbrus-v5'-specific. Each of them makes sense
only in the presence of `{ld,st}_c == 1' respectively. */
if (ld_c) {
unsigned int sal = (cs1 & 0x00000100) >> 8;
// my_printf ("sal = %d, ", sal);
}
if (st_c) {
unsigned int sas = (cs1 & 0x00000080) >> 7;
// my_printf ("sas = %d, ", sas);
}
}
if (env->version >= 2) {
/* `trap' field was introduced starting from `elbrus-v2'. */
unsigned int trap = (cs1 & 0x00000040) >> 6;
// my_printf ("trap = %d, ", trap);
}
// my_printf ("ma_c = %d, fl_c = %d, ld_c = %d, st_c = %d, all_e = %d, "
// "all_c = %d", ma_c, fl_c, ld_c, st_c, all_e, all_c);
} else if (opc == CALL) {
unsigned int ctop = (instr->ss & 0x00000c00) >> 10;
/* In C.17.4 it's said that other bits in CS1.param except for the
seven lowermost ones are ignored. */
unsigned int wbs = cs1 & 0x7f;
if (ctop) {
// my_printf ("call %%ctpr%d, wbs = 0x%x", ctop, wbs);
// print_ctcond (info, instr->ss & 0x1ff);
} else {
unsigned int cs1_ctopc = (cs1 & 0x380) >> 7;
/* CS1.param.ctpopc == HCALL. CS0 is required to encode HCALL. */
if (cs1_ctopc == 2 && instr->cs0_present) {
unsigned int cs0 = instr->cs0;
unsigned int cs0_opc = (cs0 & 0xf0000000) >> 28;
/* CS0.opc == HCALL, which means
CS0.opc.ctpr == CS0.opc.ctp_opc == 0 */
if (cs0_opc == 0) {
unsigned int hdisp = (cs0 & 0x1e) >> 1;
// my_printf ("hcall 0x%x, wbs = 0x%x", hdisp, wbs);
// print_ctcond (info, instr->ss & 0x1ff);
}
} else {
// my_printf ("<bogus call>");
}
}
} else if (opc == MAS_OPC) {
/* Note that LDIS doesn't print it out as a standalone instruction. */
unsigned int mas = cs1 & 0x0fffffff;
// my_printf ("mas 0x%x", mas);
} else if (opc == FLUSHR) {
/* . . . these stupid engineers off! FLUSHR seems to be responsible for
encoding both `flushr' and `flushc'. Moreover, according to their
logic it should be possible to encode them simultaneously. */
/* Check for `CS1.param.flr'. */
if (cs1 & 0x00000001) {
// my_printf ("flushr");
}
/* Check for `CS1.param.flc'. */
if (cs1 & 0x00000002) {
// my_printf ("flushc");
}
} else if (opc == BG) {
/* Hopefully, `vfbg' is the only instruction encoded by BG. I'm currently
unable to find other ones in `iset-v5.single' at least . . . */
unsigned int chkm4 = (cs1 & 0x00010000) >> 16;
unsigned int dmask = (cs1 & 0x0000ff00) >> 8;
unsigned int umsk = cs1 & 0x000000ff;
/* Print its fields in the order proposed in C.14.10. */
// my_printf ("vfbg umask = 0x%x, dmask = 0x%x, chkm4 = 0x%x",
// umsk, dmask, chkm4);
} else {
// my_printf ("unimp");
abort();
}
}
static void gen_op2_i32(TCGv_i64 ret, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 dst,
void (*op)(TCGv_i32, TCGv_i32, TCGv_i32))
{
TCGv_i32 lo1 = tcg_temp_new_i32();
TCGv_i32 lo2 = tcg_temp_new_i32();
TCGv_i32 dst_hi = tcg_temp_new_i32();
TCGv_i32 tmp = tcg_temp_new_i32();
tcg_gen_extrl_i64_i32(lo1, s1);
tcg_gen_extrl_i64_i32(lo2, s2);
tcg_gen_extrh_i64_i32(dst_hi, dst);
(*op)(tmp, lo1, lo2);
tcg_gen_concat_i32_i64(ret, tmp, dst_hi);
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(dst_hi);
tcg_temp_free_i32(lo2);
tcg_temp_free_i32(lo1);
}
static void gen_op21_i32(TCGv_i64 ret, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 dst,
void (*op1)(TCGv_i32, TCGv_i32, TCGv_i32),
void (*op2)(TCGv_i32, TCGv_i32))
{
TCGv_i32 lo1 = tcg_temp_new_i32();
TCGv_i32 lo2 = tcg_temp_new_i32();
TCGv_i32 dst_hi = tcg_temp_new_i32();
TCGv_i32 t0 = tcg_temp_new_i32();
TCGv_i32 t1 = tcg_temp_new_i32();
tcg_gen_extrl_i64_i32(lo1, s1);
tcg_gen_extrl_i64_i32(lo2, s2);
tcg_gen_extrh_i64_i32(dst_hi, dst);
(*op1)(t0, lo1, lo2);
(*op2)(t1, t0);
tcg_gen_concat_i32_i64(ret, t1, dst_hi);
tcg_temp_free_i32(t1);
tcg_temp_free_i32(t0);
tcg_temp_free_i32(dst_hi);
tcg_temp_free_i32(lo2);
tcg_temp_free_i32(lo1);
}
static Result gen_alc(DisasContext *dc, CPUE2KState *env, int chan)
{
const struct unpacked_instr *instr = &dc->instr;
unsigned int als = instr->als[chan];
int opc = als >> 24;
unsigned int dst = als & 0xff;
Result res = { 0 };
TCGv_i64 cpu_src1 = get_src1(dc, als);
TCGv_i64 cpu_src2 = get_src2(dc, als);
TCGv_i64 cpu_dst = get_dst(dc, als);
TCGv_i64 tmp_dst = get_temp_i64(dc);
TCGv_i64 t64 = get_temp_i64(dc);
switch(opc) {
case 0x00: // ands
gen_op2_i32(tmp_dst, cpu_src1, cpu_src2, cpu_dst, tcg_gen_and_i32);
break;
case 0x01: // andd
tcg_gen_and_i64(tmp_dst, cpu_src1, cpu_src2);
break;
case 0x02: // andns
gen_op21_i32(tmp_dst, cpu_src1, cpu_src2, cpu_dst, tcg_gen_add_i32, tcg_gen_not_i32);
break;
case 0x03: // andnd
tcg_gen_and_i64(t64, cpu_src1, cpu_src2);
tcg_gen_not_i64(tmp_dst, t64);
break;
case 0x04: // ors
gen_op2_i32(tmp_dst, cpu_src1, cpu_src2, cpu_dst, tcg_gen_or_i32);
break;
case 0x05: // ord
tcg_gen_or_i64(tmp_dst, cpu_src1, cpu_src2);
break;
case 0x06: // orns
gen_op21_i32(tmp_dst, cpu_src1, cpu_src2, cpu_dst, tcg_gen_or_i32, tcg_gen_not_i32);
break;
case 0x07: // ornd
tcg_gen_or_i64(t64, cpu_src1, cpu_src2);
tcg_gen_not_i64(tmp_dst, t64);
break;
case 0x08: // xors
gen_op2_i32(tmp_dst, cpu_src1, cpu_src2, cpu_dst, tcg_gen_xor_i32);
break;
case 0x09: // xord
tcg_gen_xor_i64(tmp_dst, cpu_src1, cpu_src2);
break;
case 0x0a: // xorns
gen_op21_i32(tmp_dst, cpu_src1, cpu_src2, cpu_dst, tcg_gen_xor_i32, tcg_gen_not_i32);
break;
case 0x0b: // xornd
tcg_gen_xor_i64(t64, cpu_src1, cpu_src2);
tcg_gen_not_i64(tmp_dst, t64);
break;
case 0x0c:
// TODO: sxt
abort();
break;
case 0x0e:
// TODO: merges
abort();
break;
case 0x0f:
// TODO: merged
abort();
break;
case 0x10: // adds
gen_op2_i32(tmp_dst, cpu_src1, cpu_src2, cpu_dst, tcg_gen_add_i32);
break;
case 0x11: // addd
tcg_gen_add_i64(tmp_dst, cpu_src1, cpu_src2);
break;
case 0x12: // subs
gen_op2_i32(tmp_dst, cpu_src1, cpu_src2, cpu_dst, tcg_gen_sub_i32);
break;
case 0x13: // subd
tcg_gen_sub_i64(tmp_dst, cpu_src1, cpu_src2);
break;
case 0x14: // scls
gen_op2_i32(tmp_dst, cpu_src1, cpu_src2, cpu_dst, tcg_gen_rotl_i32);
break;
case 0x15: // scld
tcg_gen_rotl_i64(tmp_dst, cpu_src1, cpu_src2);
break;
case 0x16: // scrs
gen_op2_i32(tmp_dst, cpu_src1, cpu_src2, cpu_dst, tcg_gen_rotr_i32);
break;
case 0x17: // scrd
tcg_gen_rotr_i64(tmp_dst, cpu_src1, cpu_src2);
break;
case 0x18: // shls
gen_op2_i32(tmp_dst, cpu_src1, cpu_src2, cpu_dst, tcg_gen_shl_i32);
break;
case 0x19: // shld
tcg_gen_shl_i64(tmp_dst, cpu_src1, cpu_src2);
break;
case 0x1a: // shrs
gen_op2_i32(tmp_dst, cpu_src1, cpu_src2, cpu_dst, tcg_gen_shr_i32);
break;
case 0x1b: // shrd
tcg_gen_shr_i64(tmp_dst, cpu_src1, cpu_src2);
break;
case 0x1c:
gen_op2_i32(tmp_dst, cpu_src1, cpu_src2, cpu_dst, tcg_gen_sar_i32);
break;
case 0x1d: // sard
tcg_gen_sar_i64(tmp_dst, cpu_src1, cpu_src2);
break;
case 0x1e:
// TODO: getfs
abort();
break;
case 0x1f:
// TODO: getfd
abort();
break;
case 0x40: // TODO: udivs used as temporary UD
gen_exception(dc, 1);
break;
default:
qemu_log_mask(LOG_UNIMP, "gen_alc: undefined instruction 0x%x\n", opc);
break;
}
if (IS_BASED(dst)) {
unsigned int i = GET_BASED(dst);
res.tag = RESULT_BASED_REG;
res.u.reg.i = i;
res.u.reg.v = tmp_dst;
} else if (IS_REGULAR(dst)) {
unsigned int i = GET_REGULAR(dst);
res.tag = RESULT_REGULAR_REG;
res.u.reg.i = i;
res.u.reg.v = tmp_dst;
} else if (IS_GLOBAL(dst)) {
unsigned int i = GET_GLOBAL(dst);
res.tag = RESULT_GLOBAL_REG;
res.u.reg.i = i;
res.u.reg.v = tmp_dst;
} else {
abort();
}
return res;
}
static target_ulong disas_e2k_insn(DisasContext *dc, CPUState *cs)
{
E2KCPU *cpu = E2K_CPU(cs);
CPUE2KState *env = &cpu->env;
struct unpacked_instr *instr = &unpacked_instr;
target_ulong next_pc = unpack_instr(env, dc, dc->base.pc_next, instr);
struct unpacked_instr *instr = &dc->instr;
unsigned int i;
target_ulong pc_next = unpack_instr(env, dc, dc->base.pc_next, instr);
// TODO: gen ops
qemu_log_mask(LOG_UNIMP, "disas_e2k_insn: not implemented, ip 0x%lx, HS 0x%x\n",
dc->base.pc_next, instr->hs);
qemu_log_mask(CPU_LOG_TB_IN_ASM, "Bundle %#lx\n", dc->base.pc_next);
return next_pc;
if (dc->instr.cs0_present) {
gen_cs0(dc, env);
}
if (dc->instr.cs1_present) {
gen_cs1(dc, env);
}
for (i = 0; i < 6; i++) {
if (!instr->als_present[i]) {
continue;
}
dc->alc[i] = gen_alc(dc, env, i);
}
for (i = 0; i < 6; i++) {
Result *res = &dc->alc[i];
if (!instr->als_present[i]) {
continue;
}
switch(res->tag) {
case RESULT_BASED_REG:
gen_store_breg(dc, res->u.reg.i, res->u.reg.v);
break;
case RESULT_REGULAR_REG:
gen_store_wreg(dc, res->u.reg.i, res->u.reg.v);
break;
case RESULT_GLOBAL_REG:
gen_store_greg(dc, res->u.reg.i, res->u.reg.v);
break;
default:
break;
}
}
unsigned int ss = dc->instr.ss;
unsigned int vfdi = (ss & 0x04000000) >> 26;
unsigned int abg = (ss & 0x01800000) >> 23;
unsigned int abn = (ss & 0x00600000) >> 21;
unsigned int abp = (ss & 0x000c0000) >> 18;
unsigned int alc = (ss & 0x00030000) >> 16;
// Change windowing registers
switch (dc->base.is_jmp) {
case DISAS_NEXT:
// move based if not branch
if (abn >> 1 != 0) {
dc->rcur = (dc->rcur + 1) % (dc->rsz + 1);
}
break;
case DISAS_NORETURN:
// move based if branch
if (abn & 0x1 != 0) {
dc->rcur = (dc->rcur + 1) % (dc->rsz + 1);
}
break;
default:
break;
}
// Control transfer
if (dc->base.is_jmp == DISAS_NORETURN) {
qemu_log_mask(CPU_LOG_TB_IN_ASM, "Jump %#lx\n", dc->jump_pc);
TCGv new_ip = tcg_const_tl(dc->jump_pc);
tcg_gen_st_tl(new_ip, cpu_env, offsetof(CPUE2KState, ip));
tcg_gen_exit_tb(NULL, 0);
}
// Free temporary values.
while(dc->t32_len) {
tcg_temp_free_i32(dc->t32[--dc->t32_len]);
}
while(dc->t64_len) {
tcg_temp_free_i64(dc->t64[--dc->t64_len]);
}
return pc_next;
}
static void e2k_tr_init_disas_context(DisasContextBase *db, CPUState *cs)
@ -291,17 +1101,10 @@ static void e2k_tr_init_disas_context(DisasContextBase *db, CPUState *cs)
dc->npc = dc->base.pc_next;
}
static void e2k_tr_tb_start(DisasContextBase *db, CPUState *cs)
{
// TODO
qemu_log_mask(LOG_UNIMP, "e2k_tr_tb_start: not implemented\n");
}
static void e2k_tr_insn_start(DisasContextBase *db, CPUState *cs)
{
DisasContext *dc = container_of(db, DisasContext, base);
tcg_gen_insn_start(dc->pc);
// FIXME: stop decoding on page boundary?
}
static bool e2k_tr_breakpoint_check(DisasContextBase *db, CPUState *cs,
@ -315,18 +1118,47 @@ static bool e2k_tr_breakpoint_check(DisasContextBase *db, CPUState *cs,
static void e2k_tr_translate_insn(DisasContextBase *db, CPUState *cs)
{
DisasContext *dc = container_of(db, DisasContext, base);
dc->base.pc_next = disas_e2k_insn(dc, cs);
target_ulong pc_next = disas_e2k_insn(dc, cs);
dc->base.pc_next = pc_next;
}
static void e2k_tr_tb_start(DisasContextBase *db, CPUState *cs)
{
DisasContext *dc = container_of(db, DisasContext, base);
E2KCPU *cpu = E2K_CPU(cs);
CPUE2KState *env = &cpu->env;
// restore window state
dc->wbs = env->wbs;
dc->wsz = env->wsz;
dc->nfx = env->nfx;
dc->dbl = env->dbl;
dc->rbs = env->rbs;
dc->rsz = env->rsz;
dc->rcur = env->rcur;
dc->psz = env->psz;
}
static void e2k_tr_tb_stop(DisasContextBase *db, CPUState *cs)
{
// TODO
qemu_log_mask(LOG_UNIMP, "e2k_tr_tb_stop: not implemented\n");
DisasContext *dc = container_of(db, DisasContext, base);
E2KCPU *cpu = E2K_CPU(cs);
CPUE2KState *env = &cpu->env;
// save window state
env->wbs = dc->wbs;
env->wsz = dc->wsz;
env->nfx = dc->nfx;
env->dbl = dc->dbl;
env->rbs = dc->rbs;
env->rsz = dc->rsz;
env->rcur = dc->rcur;
env->psz = dc->psz;
}
static void e2k_tr_disas_log(const DisasContextBase *db, CPUState *cpu)
{
// TODO
// TODO: e2k_tr_disas_log
qemu_log_mask(LOG_UNIMP, "e2k_tr_disas_log: not implemented\n");
}
@ -355,6 +1187,46 @@ void restore_state_to_opc(CPUE2KState *env, TranslationBlock *tb,
}
void e2k_tcg_initialize(void) {
// TODO
qemu_log_mask(LOG_UNIMP, "e2k_tcg_initialize: not implemented\n");
char buf[8] = { 0 };
static const struct { TCGv_i32 *ptr; int off; const char *name; } r32[] = {
{ &cpu_wbs, offsetof(CPUE2KState, wbs), "wbs" },
{ &cpu_wsz, offsetof(CPUE2KState, wsz), "wsz" },
{ &cpu_nfx, offsetof(CPUE2KState, nfx), "nfx" },
{ &cpu_dbl, offsetof(CPUE2KState, dbl), "dbl" },
{ &cpu_rbs, offsetof(CPUE2KState, rbs), "rbs" },
{ &cpu_rsz, offsetof(CPUE2KState, rsz), "rsz" },
{ &cpu_rcur, offsetof(CPUE2KState, rcur), "rcur" },
{ &cpu_psz, offsetof(CPUE2KState, psz), "psz" },
};
static const struct { TCGv *ptr; int off; const char *name; } rtl[] = {
{ &cpu_pc, offsetof(CPUE2KState, ip), "pc" },
{ &cpu_npc, offsetof(CPUE2KState, nip), "npc" },
};
unsigned int i;
for (i = 0; i < ARRAY_SIZE(r32); i++) {
*r32[i].ptr = tcg_global_mem_new_i32(cpu_env, r32[i].off, r32[i].name);
}
for (i = 0; i < ARRAY_SIZE(rtl); i++) {
*rtl[i].ptr = tcg_global_mem_new(cpu_env, rtl[i].off, rtl[i].name);
}
for (i = 0; i < WREGS_SIZE; i++) {
snprintf(buf, ARRAY_SIZE(buf), "%%r%d", i);
cpu_wregs[i] = tcg_global_mem_new(cpu_env,
offsetof(CPUE2KState, wregs[i]),
buf);
}
for (i = 0; i < 32; i++) {
snprintf(buf, ARRAY_SIZE(buf), "%%g%d", i);
cpu_gregs[i] = tcg_global_mem_new(cpu_env,
offsetof(CPUE2KState, gregs[i]),
buf);
}
}