/* Simulator for the moxie processor
Copyright (C) 2008-2023 Free Software Foundation, Inc.
Contributed by Anthony Green
This file is part of GDB, the GNU debugger.
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 . */
/* This must come before any other includes. */
#include "defs.h"
#include
#include
#include
#include
#include
#include
#include "bfd.h"
#include "libiberty.h"
#include "sim/sim.h"
#include "sim-main.h"
#include "sim-base.h"
#include "sim-options.h"
#include "sim-io.h"
#include "sim-signal.h"
#include "target-newlib-syscall.h"
#include "moxie-sim.h"
/* Extract the signed 10-bit offset from a 16-bit branch
instruction. */
#define INST2OFFSET(o) ((((signed short)((o & ((1<<10)-1))<<6))>>6)<<1)
#define EXTRACT_WORD(addr) \
((sim_core_read_aligned_1 (scpu, cia, read_map, addr) << 24) \
+ (sim_core_read_aligned_1 (scpu, cia, read_map, addr+1) << 16) \
+ (sim_core_read_aligned_1 (scpu, cia, read_map, addr+2) << 8) \
+ (sim_core_read_aligned_1 (scpu, cia, read_map, addr+3)))
#define EXTRACT_OFFSET(addr) \
(unsigned int) \
(((signed short) \
((sim_core_read_aligned_1 (scpu, cia, read_map, addr) << 8) \
+ (sim_core_read_aligned_1 (scpu, cia, read_map, addr+1))) << 16) >> 16)
static unsigned long
moxie_extract_unsigned_integer (const unsigned char *addr, int len)
{
unsigned long retval;
unsigned char * p;
unsigned char * startaddr = (unsigned char *)addr;
unsigned char * endaddr = startaddr + len;
if (len > (int) sizeof (unsigned long))
printf ("That operation is not available on integers of more than %zu bytes.",
sizeof (unsigned long));
/* Start at the most significant end of the integer, and work towards
the least significant. */
retval = 0;
for (p = endaddr; p > startaddr;)
retval = (retval << 8) | * -- p;
return retval;
}
static void
moxie_store_unsigned_integer (unsigned char *addr, int len, unsigned long val)
{
unsigned char * p;
unsigned char * startaddr = (unsigned char *)addr;
unsigned char * endaddr = startaddr + len;
for (p = endaddr; p > startaddr;)
{
* -- p = val & 0xff;
val >>= 8;
}
}
/* moxie register names. */
static const char *reg_names[16] =
{ "$fp", "$sp", "$r0", "$r1", "$r2", "$r3", "$r4", "$r5",
"$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13" };
/* The machine state.
This state is maintained in host byte order. The fetch/store
register functions must translate between host byte order and the
target processor byte order. Keeping this data in target byte
order simplifies the register read/write functions. Keeping this
data in native order improves the performance of the simulator.
Simulation speed is deemed more important. */
#define NUM_MOXIE_REGS 17 /* Including PC */
#define NUM_MOXIE_SREGS 256 /* The special registers */
#define PC_REGNO 16
/* The ordering of the moxie_regset structure is matched in the
gdb/config/moxie/tm-moxie.h file in the REGISTER_NAMES macro. */
/* TODO: This should be moved to sim-main.h:_sim_cpu. */
struct moxie_regset
{
int32_t regs[NUM_MOXIE_REGS + 1]; /* primary registers */
int32_t sregs[256]; /* special registers */
int32_t cc; /* the condition code reg */
unsigned long long insts; /* instruction counter */
};
#define CC_GT 1<<0
#define CC_LT 1<<1
#define CC_EQ 1<<2
#define CC_GTU 1<<3
#define CC_LTU 1<<4
/* TODO: This should be moved to sim-main.h:moxie_sim_cpu. */
union
{
struct moxie_regset asregs;
int32_t asints [1]; /* but accessed larger... */
} cpu;
static void
set_initial_gprs (void)
{
int i;
long space;
/* Set up machine just out of reset. */
cpu.asregs.regs[PC_REGNO] = 0;
/* Clean out the register contents. */
for (i = 0; i < NUM_MOXIE_REGS; i++)
cpu.asregs.regs[i] = 0;
for (i = 0; i < NUM_MOXIE_SREGS; i++)
cpu.asregs.sregs[i] = 0;
}
/* Write a 1 byte value to memory. */
static INLINE void
wbat (sim_cpu *scpu, int32_t pc, int32_t x, int32_t v)
{
address_word cia = CPU_PC_GET (scpu);
sim_core_write_aligned_1 (scpu, cia, write_map, x, v);
}
/* Write a 2 byte value to memory. */
static INLINE void
wsat (sim_cpu *scpu, int32_t pc, int32_t x, int32_t v)
{
address_word cia = CPU_PC_GET (scpu);
sim_core_write_aligned_2 (scpu, cia, write_map, x, v);
}
/* Write a 4 byte value to memory. */
static INLINE void
wlat (sim_cpu *scpu, int32_t pc, int32_t x, int32_t v)
{
address_word cia = CPU_PC_GET (scpu);
sim_core_write_aligned_4 (scpu, cia, write_map, x, v);
}
/* Read 2 bytes from memory. */
static INLINE int
rsat (sim_cpu *scpu, int32_t pc, int32_t x)
{
address_word cia = CPU_PC_GET (scpu);
return (sim_core_read_aligned_2 (scpu, cia, read_map, x));
}
/* Read 1 byte from memory. */
static INLINE int
rbat (sim_cpu *scpu, int32_t pc, int32_t x)
{
address_word cia = CPU_PC_GET (scpu);
return (sim_core_read_aligned_1 (scpu, cia, read_map, x));
}
/* Read 4 bytes from memory. */
static INLINE int
rlat (sim_cpu *scpu, int32_t pc, int32_t x)
{
address_word cia = CPU_PC_GET (scpu);
return (sim_core_read_aligned_4 (scpu, cia, read_map, x));
}
#define CHECK_FLAG(T,H) if (tflags & T) { hflags |= H; tflags ^= T; }
static unsigned int
convert_target_flags (unsigned int tflags)
{
unsigned int hflags = 0x0;
CHECK_FLAG(0x0001, O_WRONLY);
CHECK_FLAG(0x0002, O_RDWR);
CHECK_FLAG(0x0008, O_APPEND);
CHECK_FLAG(0x0200, O_CREAT);
CHECK_FLAG(0x0400, O_TRUNC);
CHECK_FLAG(0x0800, O_EXCL);
CHECK_FLAG(0x2000, O_SYNC);
if (tflags != 0x0)
fprintf (stderr,
"Simulator Error: problem converting target open flags for host. 0x%x\n",
tflags);
return hflags;
}
/* TODO: Split this up into finger trace levels than just insn. */
#define MOXIE_TRACE_INSN(str) \
TRACE_INSN (scpu, "0x%08x, %s, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x", \
opc, str, cpu.asregs.regs[0], cpu.asregs.regs[1], \
cpu.asregs.regs[2], cpu.asregs.regs[3], cpu.asregs.regs[4], \
cpu.asregs.regs[5], cpu.asregs.regs[6], cpu.asregs.regs[7], \
cpu.asregs.regs[8], cpu.asregs.regs[9], cpu.asregs.regs[10], \
cpu.asregs.regs[11], cpu.asregs.regs[12], cpu.asregs.regs[13], \
cpu.asregs.regs[14], cpu.asregs.regs[15])
void
sim_engine_run (SIM_DESC sd,
int next_cpu_nr, /* ignore */
int nr_cpus, /* ignore */
int siggnal) /* ignore */
{
int32_t pc, opc;
unsigned short inst;
sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */
address_word cia = CPU_PC_GET (scpu);
pc = cpu.asregs.regs[PC_REGNO];
/* Run instructions here. */
do
{
opc = pc;
/* Fetch the instruction at pc. */
inst = (sim_core_read_aligned_1 (scpu, cia, read_map, pc) << 8)
+ sim_core_read_aligned_1 (scpu, cia, read_map, pc+1);
/* Decode instruction. */
if (inst & (1 << 15))
{
if (inst & (1 << 14))
{
/* This is a Form 3 instruction. */
int opcode = (inst >> 10 & 0xf);
switch (opcode)
{
case 0x00: /* beq */
{
MOXIE_TRACE_INSN ("beq");
if (cpu.asregs.cc & CC_EQ)
pc += INST2OFFSET(inst);
}
break;
case 0x01: /* bne */
{
MOXIE_TRACE_INSN ("bne");
if (! (cpu.asregs.cc & CC_EQ))
pc += INST2OFFSET(inst);
}
break;
case 0x02: /* blt */
{
MOXIE_TRACE_INSN ("blt");
if (cpu.asregs.cc & CC_LT)
pc += INST2OFFSET(inst);
} break;
case 0x03: /* bgt */
{
MOXIE_TRACE_INSN ("bgt");
if (cpu.asregs.cc & CC_GT)
pc += INST2OFFSET(inst);
}
break;
case 0x04: /* bltu */
{
MOXIE_TRACE_INSN ("bltu");
if (cpu.asregs.cc & CC_LTU)
pc += INST2OFFSET(inst);
}
break;
case 0x05: /* bgtu */
{
MOXIE_TRACE_INSN ("bgtu");
if (cpu.asregs.cc & CC_GTU)
pc += INST2OFFSET(inst);
}
break;
case 0x06: /* bge */
{
MOXIE_TRACE_INSN ("bge");
if (cpu.asregs.cc & (CC_GT | CC_EQ))
pc += INST2OFFSET(inst);
}
break;
case 0x07: /* ble */
{
MOXIE_TRACE_INSN ("ble");
if (cpu.asregs.cc & (CC_LT | CC_EQ))
pc += INST2OFFSET(inst);
}
break;
case 0x08: /* bgeu */
{
MOXIE_TRACE_INSN ("bgeu");
if (cpu.asregs.cc & (CC_GTU | CC_EQ))
pc += INST2OFFSET(inst);
}
break;
case 0x09: /* bleu */
{
MOXIE_TRACE_INSN ("bleu");
if (cpu.asregs.cc & (CC_LTU | CC_EQ))
pc += INST2OFFSET(inst);
}
break;
default:
{
MOXIE_TRACE_INSN ("SIGILL3");
sim_engine_halt (sd, scpu, NULL, pc, sim_stopped, SIM_SIGILL);
break;
}
}
}
else
{
/* This is a Form 2 instruction. */
int opcode = (inst >> 12 & 0x3);
switch (opcode)
{
case 0x00: /* inc */
{
int a = (inst >> 8) & 0xf;
unsigned av = cpu.asregs.regs[a];
unsigned v = (inst & 0xff);
MOXIE_TRACE_INSN ("inc");
cpu.asregs.regs[a] = av + v;
}
break;
case 0x01: /* dec */
{
int a = (inst >> 8) & 0xf;
unsigned av = cpu.asregs.regs[a];
unsigned v = (inst & 0xff);
MOXIE_TRACE_INSN ("dec");
cpu.asregs.regs[a] = av - v;
}
break;
case 0x02: /* gsr */
{
int a = (inst >> 8) & 0xf;
unsigned v = (inst & 0xff);
MOXIE_TRACE_INSN ("gsr");
cpu.asregs.regs[a] = cpu.asregs.sregs[v];
}
break;
case 0x03: /* ssr */
{
int a = (inst >> 8) & 0xf;
unsigned v = (inst & 0xff);
MOXIE_TRACE_INSN ("ssr");
cpu.asregs.sregs[v] = cpu.asregs.regs[a];
}
break;
default:
MOXIE_TRACE_INSN ("SIGILL2");
sim_engine_halt (sd, scpu, NULL, pc, sim_stopped, SIM_SIGILL);
break;
}
}
}
else
{
/* This is a Form 1 instruction. */
int opcode = inst >> 8;
switch (opcode)
{
case 0x00: /* bad */
opc = opcode;
MOXIE_TRACE_INSN ("SIGILL0");
sim_engine_halt (sd, scpu, NULL, pc, sim_stopped, SIM_SIGILL);
break;
case 0x01: /* ldi.l (immediate) */
{
int reg = (inst >> 4) & 0xf;
unsigned int val = EXTRACT_WORD(pc+2);
MOXIE_TRACE_INSN ("ldi.l");
cpu.asregs.regs[reg] = val;
pc += 4;
}
break;
case 0x02: /* mov (register-to-register) */
{
int dest = (inst >> 4) & 0xf;
int src = (inst ) & 0xf;
MOXIE_TRACE_INSN ("mov");
cpu.asregs.regs[dest] = cpu.asregs.regs[src];
}
break;
case 0x03: /* jsra */
{
unsigned int fn = EXTRACT_WORD(pc+2);
unsigned int sp = cpu.asregs.regs[1];
MOXIE_TRACE_INSN ("jsra");
/* Save a slot for the static chain. */
sp -= 4;
/* Push the return address. */
sp -= 4;
wlat (scpu, opc, sp, pc + 6);
/* Push the current frame pointer. */
sp -= 4;
wlat (scpu, opc, sp, cpu.asregs.regs[0]);
/* Uncache the stack pointer and set the pc and $fp. */
cpu.asregs.regs[1] = sp;
cpu.asregs.regs[0] = sp;
pc = fn - 2;
}
break;
case 0x04: /* ret */
{
unsigned int sp = cpu.asregs.regs[0];
MOXIE_TRACE_INSN ("ret");
/* Pop the frame pointer. */
cpu.asregs.regs[0] = rlat (scpu, opc, sp);
sp += 4;
/* Pop the return address. */
pc = rlat (scpu, opc, sp) - 2;
sp += 4;
/* Skip over the static chain slot. */
sp += 4;
/* Uncache the stack pointer. */
cpu.asregs.regs[1] = sp;
}
break;
case 0x05: /* add.l */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
unsigned av = cpu.asregs.regs[a];
unsigned bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("add.l");
cpu.asregs.regs[a] = av + bv;
}
break;
case 0x06: /* push */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
int sp = cpu.asregs.regs[a] - 4;
MOXIE_TRACE_INSN ("push");
wlat (scpu, opc, sp, cpu.asregs.regs[b]);
cpu.asregs.regs[a] = sp;
}
break;
case 0x07: /* pop */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
int sp = cpu.asregs.regs[a];
MOXIE_TRACE_INSN ("pop");
cpu.asregs.regs[b] = rlat (scpu, opc, sp);
cpu.asregs.regs[a] = sp + 4;
}
break;
case 0x08: /* lda.l */
{
int reg = (inst >> 4) & 0xf;
unsigned int addr = EXTRACT_WORD(pc+2);
MOXIE_TRACE_INSN ("lda.l");
cpu.asregs.regs[reg] = rlat (scpu, opc, addr);
pc += 4;
}
break;
case 0x09: /* sta.l */
{
int reg = (inst >> 4) & 0xf;
unsigned int addr = EXTRACT_WORD(pc+2);
MOXIE_TRACE_INSN ("sta.l");
wlat (scpu, opc, addr, cpu.asregs.regs[reg]);
pc += 4;
}
break;
case 0x0a: /* ld.l (register indirect) */
{
int src = inst & 0xf;
int dest = (inst >> 4) & 0xf;
int xv;
MOXIE_TRACE_INSN ("ld.l");
xv = cpu.asregs.regs[src];
cpu.asregs.regs[dest] = rlat (scpu, opc, xv);
}
break;
case 0x0b: /* st.l */
{
int dest = (inst >> 4) & 0xf;
int val = inst & 0xf;
MOXIE_TRACE_INSN ("st.l");
wlat (scpu, opc, cpu.asregs.regs[dest], cpu.asregs.regs[val]);
}
break;
case 0x0c: /* ldo.l */
{
unsigned int addr = EXTRACT_OFFSET(pc+2);
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
MOXIE_TRACE_INSN ("ldo.l");
addr += cpu.asregs.regs[b];
cpu.asregs.regs[a] = rlat (scpu, opc, addr);
pc += 2;
}
break;
case 0x0d: /* sto.l */
{
unsigned int addr = EXTRACT_OFFSET(pc+2);
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
MOXIE_TRACE_INSN ("sto.l");
addr += cpu.asregs.regs[a];
wlat (scpu, opc, addr, cpu.asregs.regs[b]);
pc += 2;
}
break;
case 0x0e: /* cmp */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
int cc = 0;
int va = cpu.asregs.regs[a];
int vb = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("cmp");
if (va == vb)
cc = CC_EQ;
else
{
cc |= (va < vb ? CC_LT : 0);
cc |= (va > vb ? CC_GT : 0);
cc |= ((unsigned int) va < (unsigned int) vb ? CC_LTU : 0);
cc |= ((unsigned int) va > (unsigned int) vb ? CC_GTU : 0);
}
cpu.asregs.cc = cc;
}
break;
case 0x0f: /* nop */
break;
case 0x10: /* sex.b */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
signed char bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("sex.b");
cpu.asregs.regs[a] = (int) bv;
}
break;
case 0x11: /* sex.s */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
signed short bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("sex.s");
cpu.asregs.regs[a] = (int) bv;
}
break;
case 0x12: /* zex.b */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
signed char bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("zex.b");
cpu.asregs.regs[a] = (int) bv & 0xff;
}
break;
case 0x13: /* zex.s */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
signed short bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("zex.s");
cpu.asregs.regs[a] = (int) bv & 0xffff;
}
break;
case 0x14: /* umul.x */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
unsigned av = cpu.asregs.regs[a];
unsigned bv = cpu.asregs.regs[b];
unsigned long long r =
(unsigned long long) av * (unsigned long long) bv;
MOXIE_TRACE_INSN ("umul.x");
cpu.asregs.regs[a] = r >> 32;
}
break;
case 0x15: /* mul.x */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
unsigned av = cpu.asregs.regs[a];
unsigned bv = cpu.asregs.regs[b];
signed long long r =
(signed long long) av * (signed long long) bv;
MOXIE_TRACE_INSN ("mul.x");
cpu.asregs.regs[a] = r >> 32;
}
break;
case 0x16: /* bad */
case 0x17: /* bad */
case 0x18: /* bad */
{
opc = opcode;
MOXIE_TRACE_INSN ("SIGILL0");
sim_engine_halt (sd, scpu, NULL, pc, sim_stopped, SIM_SIGILL);
break;
}
case 0x19: /* jsr */
{
unsigned int fn = cpu.asregs.regs[(inst >> 4) & 0xf];
unsigned int sp = cpu.asregs.regs[1];
MOXIE_TRACE_INSN ("jsr");
/* Save a slot for the static chain. */
sp -= 4;
/* Push the return address. */
sp -= 4;
wlat (scpu, opc, sp, pc + 2);
/* Push the current frame pointer. */
sp -= 4;
wlat (scpu, opc, sp, cpu.asregs.regs[0]);
/* Uncache the stack pointer and set the fp & pc. */
cpu.asregs.regs[1] = sp;
cpu.asregs.regs[0] = sp;
pc = fn - 2;
}
break;
case 0x1a: /* jmpa */
{
unsigned int tgt = EXTRACT_WORD(pc+2);
MOXIE_TRACE_INSN ("jmpa");
pc = tgt - 2;
}
break;
case 0x1b: /* ldi.b (immediate) */
{
int reg = (inst >> 4) & 0xf;
unsigned int val = EXTRACT_WORD(pc+2);
MOXIE_TRACE_INSN ("ldi.b");
cpu.asregs.regs[reg] = val;
pc += 4;
}
break;
case 0x1c: /* ld.b (register indirect) */
{
int src = inst & 0xf;
int dest = (inst >> 4) & 0xf;
int xv;
MOXIE_TRACE_INSN ("ld.b");
xv = cpu.asregs.regs[src];
cpu.asregs.regs[dest] = rbat (scpu, opc, xv);
}
break;
case 0x1d: /* lda.b */
{
int reg = (inst >> 4) & 0xf;
unsigned int addr = EXTRACT_WORD(pc+2);
MOXIE_TRACE_INSN ("lda.b");
cpu.asregs.regs[reg] = rbat (scpu, opc, addr);
pc += 4;
}
break;
case 0x1e: /* st.b */
{
int dest = (inst >> 4) & 0xf;
int val = inst & 0xf;
MOXIE_TRACE_INSN ("st.b");
wbat (scpu, opc, cpu.asregs.regs[dest], cpu.asregs.regs[val]);
}
break;
case 0x1f: /* sta.b */
{
int reg = (inst >> 4) & 0xf;
unsigned int addr = EXTRACT_WORD(pc+2);
MOXIE_TRACE_INSN ("sta.b");
wbat (scpu, opc, addr, cpu.asregs.regs[reg]);
pc += 4;
}
break;
case 0x20: /* ldi.s (immediate) */
{
int reg = (inst >> 4) & 0xf;
unsigned int val = EXTRACT_WORD(pc+2);
MOXIE_TRACE_INSN ("ldi.s");
cpu.asregs.regs[reg] = val;
pc += 4;
}
break;
case 0x21: /* ld.s (register indirect) */
{
int src = inst & 0xf;
int dest = (inst >> 4) & 0xf;
int xv;
MOXIE_TRACE_INSN ("ld.s");
xv = cpu.asregs.regs[src];
cpu.asregs.regs[dest] = rsat (scpu, opc, xv);
}
break;
case 0x22: /* lda.s */
{
int reg = (inst >> 4) & 0xf;
unsigned int addr = EXTRACT_WORD(pc+2);
MOXIE_TRACE_INSN ("lda.s");
cpu.asregs.regs[reg] = rsat (scpu, opc, addr);
pc += 4;
}
break;
case 0x23: /* st.s */
{
int dest = (inst >> 4) & 0xf;
int val = inst & 0xf;
MOXIE_TRACE_INSN ("st.s");
wsat (scpu, opc, cpu.asregs.regs[dest], cpu.asregs.regs[val]);
}
break;
case 0x24: /* sta.s */
{
int reg = (inst >> 4) & 0xf;
unsigned int addr = EXTRACT_WORD(pc+2);
MOXIE_TRACE_INSN ("sta.s");
wsat (scpu, opc, addr, cpu.asregs.regs[reg]);
pc += 4;
}
break;
case 0x25: /* jmp */
{
int reg = (inst >> 4) & 0xf;
MOXIE_TRACE_INSN ("jmp");
pc = cpu.asregs.regs[reg] - 2;
}
break;
case 0x26: /* and */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
int av, bv;
MOXIE_TRACE_INSN ("and");
av = cpu.asregs.regs[a];
bv = cpu.asregs.regs[b];
cpu.asregs.regs[a] = av & bv;
}
break;
case 0x27: /* lshr */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
int av = cpu.asregs.regs[a];
int bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("lshr");
cpu.asregs.regs[a] = (unsigned) ((unsigned) av >> bv);
}
break;
case 0x28: /* ashl */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
int av = cpu.asregs.regs[a];
int bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("ashl");
cpu.asregs.regs[a] = av << bv;
}
break;
case 0x29: /* sub.l */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
unsigned av = cpu.asregs.regs[a];
unsigned bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("sub.l");
cpu.asregs.regs[a] = av - bv;
}
break;
case 0x2a: /* neg */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
int bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("neg");
cpu.asregs.regs[a] = - bv;
}
break;
case 0x2b: /* or */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
int av, bv;
MOXIE_TRACE_INSN ("or");
av = cpu.asregs.regs[a];
bv = cpu.asregs.regs[b];
cpu.asregs.regs[a] = av | bv;
}
break;
case 0x2c: /* not */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
int bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("not");
cpu.asregs.regs[a] = 0xffffffff ^ bv;
}
break;
case 0x2d: /* ashr */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
int av = cpu.asregs.regs[a];
int bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("ashr");
cpu.asregs.regs[a] = av >> bv;
}
break;
case 0x2e: /* xor */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
int av, bv;
MOXIE_TRACE_INSN ("xor");
av = cpu.asregs.regs[a];
bv = cpu.asregs.regs[b];
cpu.asregs.regs[a] = av ^ bv;
}
break;
case 0x2f: /* mul.l */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
unsigned av = cpu.asregs.regs[a];
unsigned bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("mul.l");
cpu.asregs.regs[a] = av * bv;
}
break;
case 0x30: /* swi */
{
unsigned int inum = EXTRACT_WORD(pc+2);
MOXIE_TRACE_INSN ("swi");
/* Set the special registers appropriately. */
cpu.asregs.sregs[2] = 3; /* MOXIE_EX_SWI */
cpu.asregs.sregs[3] = inum;
switch (inum)
{
case TARGET_NEWLIB_SYS_exit:
{
sim_engine_halt (sd, scpu, NULL, pc, sim_exited,
cpu.asregs.regs[2]);
break;
}
case TARGET_NEWLIB_SYS_open:
{
char fname[1024];
int mode = (int) convert_target_flags ((unsigned) cpu.asregs.regs[3]);
int perm = (int) cpu.asregs.regs[4];
int fd;
sim_core_read_buffer (sd, scpu, read_map, fname,
cpu.asregs.regs[2], 1024);
fd = sim_io_open (sd, fname, mode);
/* FIXME - set errno */
cpu.asregs.regs[2] = fd;
break;
}
case TARGET_NEWLIB_SYS_read:
{
int fd = cpu.asregs.regs[2];
unsigned len = (unsigned) cpu.asregs.regs[4];
char *buf = malloc (len);
cpu.asregs.regs[2] = sim_io_read (sd, fd, buf, len);
sim_core_write_buffer (sd, scpu, write_map, buf,
cpu.asregs.regs[3], len);
free (buf);
break;
}
case TARGET_NEWLIB_SYS_write:
{
char *str;
/* String length is at 0x12($fp) */
unsigned count, len = (unsigned) cpu.asregs.regs[4];
str = malloc (len);
sim_core_read_buffer (sd, scpu, read_map, str,
cpu.asregs.regs[3], len);
count = sim_io_write (sd, cpu.asregs.regs[2], str, len);
free (str);
cpu.asregs.regs[2] = count;
break;
}
case TARGET_NEWLIB_SYS_unlink:
{
char fname[1024];
int fd;
sim_core_read_buffer (sd, scpu, read_map, fname,
cpu.asregs.regs[2], 1024);
fd = sim_io_unlink (sd, fname);
/* FIXME - set errno */
cpu.asregs.regs[2] = fd;
break;
}
case 0xffffffff: /* Linux System Call */
{
unsigned int handler = cpu.asregs.sregs[1];
unsigned int sp = cpu.asregs.regs[1];
/* Save a slot for the static chain. */
sp -= 4;
/* Push the return address. */
sp -= 4;
wlat (scpu, opc, sp, pc + 6);
/* Push the current frame pointer. */
sp -= 4;
wlat (scpu, opc, sp, cpu.asregs.regs[0]);
/* Uncache the stack pointer and set the fp & pc. */
cpu.asregs.regs[1] = sp;
cpu.asregs.regs[0] = sp;
pc = handler - 6;
}
default:
break;
}
pc += 4;
}
break;
case 0x31: /* div.l */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
int av = cpu.asregs.regs[a];
int bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("div.l");
cpu.asregs.regs[a] = av / bv;
}
break;
case 0x32: /* udiv.l */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
unsigned int av = cpu.asregs.regs[a];
unsigned int bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("udiv.l");
cpu.asregs.regs[a] = (av / bv);
}
break;
case 0x33: /* mod.l */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
int av = cpu.asregs.regs[a];
int bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("mod.l");
cpu.asregs.regs[a] = av % bv;
}
break;
case 0x34: /* umod.l */
{
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
unsigned int av = cpu.asregs.regs[a];
unsigned int bv = cpu.asregs.regs[b];
MOXIE_TRACE_INSN ("umod.l");
cpu.asregs.regs[a] = (av % bv);
}
break;
case 0x35: /* brk */
MOXIE_TRACE_INSN ("brk");
sim_engine_halt (sd, scpu, NULL, pc, sim_stopped, SIM_SIGTRAP);
pc -= 2; /* Adjust pc */
break;
case 0x36: /* ldo.b */
{
unsigned int addr = EXTRACT_OFFSET(pc+2);
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
MOXIE_TRACE_INSN ("ldo.b");
addr += cpu.asregs.regs[b];
cpu.asregs.regs[a] = rbat (scpu, opc, addr);
pc += 2;
}
break;
case 0x37: /* sto.b */
{
unsigned int addr = EXTRACT_OFFSET(pc+2);
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
MOXIE_TRACE_INSN ("sto.b");
addr += cpu.asregs.regs[a];
wbat (scpu, opc, addr, cpu.asregs.regs[b]);
pc += 2;
}
break;
case 0x38: /* ldo.s */
{
unsigned int addr = EXTRACT_OFFSET(pc+2);
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
MOXIE_TRACE_INSN ("ldo.s");
addr += cpu.asregs.regs[b];
cpu.asregs.regs[a] = rsat (scpu, opc, addr);
pc += 2;
}
break;
case 0x39: /* sto.s */
{
unsigned int addr = EXTRACT_OFFSET(pc+2);
int a = (inst >> 4) & 0xf;
int b = inst & 0xf;
MOXIE_TRACE_INSN ("sto.s");
addr += cpu.asregs.regs[a];
wsat (scpu, opc, addr, cpu.asregs.regs[b]);
pc += 2;
}
break;
default:
opc = opcode;
MOXIE_TRACE_INSN ("SIGILL1");
sim_engine_halt (sd, scpu, NULL, pc, sim_stopped, SIM_SIGILL);
break;
}
}
cpu.asregs.insts++;
pc += 2;
cpu.asregs.regs[PC_REGNO] = pc;
if (sim_events_tick (sd))
sim_events_process (sd);
} while (1);
}
static int
moxie_reg_store (SIM_CPU *scpu, int rn, const void *memory, int length)
{
if (rn < NUM_MOXIE_REGS && rn >= 0)
{
if (length == 4)
{
long ival;
/* misalignment safe */
ival = moxie_extract_unsigned_integer (memory, 4);
cpu.asints[rn] = ival;
}
return 4;
}
else
return 0;
}
static int
moxie_reg_fetch (SIM_CPU *scpu, int rn, void *memory, int length)
{
if (rn < NUM_MOXIE_REGS && rn >= 0)
{
if (length == 4)
{
long ival = cpu.asints[rn];
/* misalignment-safe */
moxie_store_unsigned_integer (memory, 4, ival);
}
return 4;
}
else
return 0;
}
static sim_cia
moxie_pc_get (sim_cpu *cpu)
{
return MOXIE_SIM_CPU (cpu)->registers[PCIDX];
}
static void
moxie_pc_set (sim_cpu *cpu, sim_cia pc)
{
MOXIE_SIM_CPU (cpu)->registers[PCIDX] = pc;
}
static void
free_state (SIM_DESC sd)
{
if (STATE_MODULES (sd) != NULL)
sim_module_uninstall (sd);
sim_cpu_free_all (sd);
sim_state_free (sd);
}
SIM_DESC
sim_open (SIM_OPEN_KIND kind, host_callback *cb,
struct bfd *abfd, char * const *argv)
{
int i;
SIM_DESC sd = sim_state_alloc (kind, cb);
SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
/* Set default options before parsing user options. */
current_target_byte_order = BFD_ENDIAN_BIG;
/* The cpu data is kept in a separately allocated chunk of memory. */
if (sim_cpu_alloc_all_extra (sd, 0, sizeof (struct moxie_sim_cpu))
!= SIM_RC_OK)
{
free_state (sd);
return 0;
}
if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
{
free_state (sd);
return 0;
}
/* The parser will print an error message for us, so we silently return. */
if (sim_parse_args (sd, argv) != SIM_RC_OK)
{
free_state (sd);
return 0;
}
sim_do_command(sd," memory region 0x00000000,0x4000000") ;
sim_do_command(sd," memory region 0xE0000000,0x10000") ;
/* Check for/establish the a reference program image. */
if (sim_analyze_program (sd, STATE_PROG_FILE (sd), abfd) != SIM_RC_OK)
{
free_state (sd);
return 0;
}
/* Configure/verify the target byte order and other runtime
configuration options. */
if (sim_config (sd) != SIM_RC_OK)
{
sim_module_uninstall (sd);
return 0;
}
if (sim_post_argv_init (sd) != SIM_RC_OK)
{
/* Uninstall the modules to avoid memory leaks,
file descriptor leaks, etc. */
sim_module_uninstall (sd);
return 0;
}
/* CPU specific initialization. */
for (i = 0; i < MAX_NR_PROCESSORS; ++i)
{
SIM_CPU *cpu = STATE_CPU (sd, i);
CPU_REG_FETCH (cpu) = moxie_reg_fetch;
CPU_REG_STORE (cpu) = moxie_reg_store;
CPU_PC_FETCH (cpu) = moxie_pc_get;
CPU_PC_STORE (cpu) = moxie_pc_set;
set_initial_gprs (); /* Reset the GPR registers. */
}
return sd;
}
/* Load the device tree blob. */
static void
load_dtb (SIM_DESC sd, const char *filename)
{
int size = 0;
FILE *f = fopen (filename, "rb");
char *buf;
sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */
/* Don't warn as the sim works fine w/out a device tree. */
if (f == NULL)
return;
fseek (f, 0, SEEK_END);
size = ftell(f);
fseek (f, 0, SEEK_SET);
buf = alloca (size);
if (size != fread (buf, 1, size, f))
{
sim_io_eprintf (sd, "ERROR: error reading ``%s''.\n", filename);
fclose (f);
return;
}
sim_core_write_buffer (sd, scpu, write_map, buf, 0xE0000000, size);
cpu.asregs.sregs[9] = 0xE0000000;
fclose (f);
}
SIM_RC
sim_create_inferior (SIM_DESC sd, struct bfd *prog_bfd,
char * const *argv, char * const *env)
{
char * const *avp;
int l, argc, i, tp;
sim_cpu *scpu = STATE_CPU (sd, 0); /* FIXME */
if (prog_bfd != NULL)
cpu.asregs.regs[PC_REGNO] = bfd_get_start_address (prog_bfd);
/* Copy args into target memory. */
avp = argv;
for (argc = 0; avp && *avp; avp++)
argc++;
/* Target memory looks like this:
0x00000000 zero word
0x00000004 argc word
0x00000008 start of argv
.
0x0000???? end of argv
0x0000???? zero word
0x0000???? start of data pointed to by argv */
wlat (scpu, 0, 0, 0);
wlat (scpu, 0, 4, argc);
/* tp is the offset of our first argv data. */
tp = 4 + 4 + argc * 4 + 4;
for (i = 0; i < argc; i++)
{
/* Set the argv value. */
wlat (scpu, 0, 4 + 4 + i * 4, tp);
/* Store the string. */
sim_core_write_buffer (sd, scpu, write_map, argv[i],
tp, strlen(argv[i])+1);
tp += strlen (argv[i]) + 1;
}
wlat (scpu, 0, 4 + 4 + i * 4, 0);
load_dtb (sd, DTB);
return SIM_RC_OK;
}