034685f9ce
The CIA_{GET,SET} macros serve the same function as CPU_PC_{GET,SET} except the latter adds a layer of indirection via the sim state. This lets models set up different functions at runtime and doesn't reach so directly into the arch-specific cpu state. It also doesn't make sense to have two sets of macros that do exactly the same thing, so lets standardize on the one that gets us more.
1324 lines
30 KiB
C
1324 lines
30 KiB
C
/* Simulator for the moxie processor
|
|
Copyright (C) 2008-2015 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 <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/times.h>
|
|
#include <sys/param.h>
|
|
#include <unistd.h>
|
|
#include "bfd.h"
|
|
#include "libiberty.h"
|
|
#include "gdb/remote-sim.h"
|
|
|
|
#include "sim-main.h"
|
|
#include "sim-base.h"
|
|
#include "sim-options.h"
|
|
|
|
typedef int word;
|
|
typedef unsigned int uword;
|
|
|
|
/* 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 (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
|
|
{
|
|
word regs[NUM_MOXIE_REGS + 1]; /* primary registers */
|
|
word sregs[256]; /* special registers */
|
|
word 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:_sim_cpu. */
|
|
union
|
|
{
|
|
struct moxie_regset asregs;
|
|
word 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, word pc, word x, word 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, word pc, word x, word 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, word pc, word x, word 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, word pc, word 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, word pc, word 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, word pc, word 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: Move to sim-trace.h. */
|
|
static FILE *tracefile;
|
|
static const int tracing = 0;
|
|
#define TRACE(str) if (tracing) fprintf(tracefile,"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\n", 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 */
|
|
{
|
|
word 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 */
|
|
{
|
|
TRACE("beq");
|
|
if (cpu.asregs.cc & CC_EQ)
|
|
pc += INST2OFFSET(inst);
|
|
}
|
|
break;
|
|
case 0x01: /* bne */
|
|
{
|
|
TRACE("bne");
|
|
if (! (cpu.asregs.cc & CC_EQ))
|
|
pc += INST2OFFSET(inst);
|
|
}
|
|
break;
|
|
case 0x02: /* blt */
|
|
{
|
|
TRACE("blt");
|
|
if (cpu.asregs.cc & CC_LT)
|
|
pc += INST2OFFSET(inst);
|
|
} break;
|
|
case 0x03: /* bgt */
|
|
{
|
|
TRACE("bgt");
|
|
if (cpu.asregs.cc & CC_GT)
|
|
pc += INST2OFFSET(inst);
|
|
}
|
|
break;
|
|
case 0x04: /* bltu */
|
|
{
|
|
TRACE("bltu");
|
|
if (cpu.asregs.cc & CC_LTU)
|
|
pc += INST2OFFSET(inst);
|
|
}
|
|
break;
|
|
case 0x05: /* bgtu */
|
|
{
|
|
TRACE("bgtu");
|
|
if (cpu.asregs.cc & CC_GTU)
|
|
pc += INST2OFFSET(inst);
|
|
}
|
|
break;
|
|
case 0x06: /* bge */
|
|
{
|
|
TRACE("bge");
|
|
if (cpu.asregs.cc & (CC_GT | CC_EQ))
|
|
pc += INST2OFFSET(inst);
|
|
}
|
|
break;
|
|
case 0x07: /* ble */
|
|
{
|
|
TRACE("ble");
|
|
if (cpu.asregs.cc & (CC_LT | CC_EQ))
|
|
pc += INST2OFFSET(inst);
|
|
}
|
|
break;
|
|
case 0x08: /* bgeu */
|
|
{
|
|
TRACE("bgeu");
|
|
if (cpu.asregs.cc & (CC_GTU | CC_EQ))
|
|
pc += INST2OFFSET(inst);
|
|
}
|
|
break;
|
|
case 0x09: /* bleu */
|
|
{
|
|
TRACE("bleu");
|
|
if (cpu.asregs.cc & (CC_LTU | CC_EQ))
|
|
pc += INST2OFFSET(inst);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
TRACE("SIGILL3");
|
|
sim_engine_halt (sd, NULL, 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);
|
|
|
|
TRACE("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);
|
|
|
|
TRACE("dec");
|
|
cpu.asregs.regs[a] = av - v;
|
|
}
|
|
break;
|
|
case 0x02: /* gsr */
|
|
{
|
|
int a = (inst >> 8) & 0xf;
|
|
unsigned v = (inst & 0xff);
|
|
|
|
TRACE("gsr");
|
|
cpu.asregs.regs[a] = cpu.asregs.sregs[v];
|
|
}
|
|
break;
|
|
case 0x03: /* ssr */
|
|
{
|
|
int a = (inst >> 8) & 0xf;
|
|
unsigned v = (inst & 0xff);
|
|
|
|
TRACE("ssr");
|
|
cpu.asregs.sregs[v] = cpu.asregs.regs[a];
|
|
}
|
|
break;
|
|
default:
|
|
TRACE("SIGILL2");
|
|
sim_engine_halt (sd, NULL, 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;
|
|
TRACE("SIGILL0");
|
|
sim_engine_halt (sd, NULL, NULL, pc, sim_stopped, SIM_SIGILL);
|
|
break;
|
|
case 0x01: /* ldi.l (immediate) */
|
|
{
|
|
int reg = (inst >> 4) & 0xf;
|
|
unsigned int val = EXTRACT_WORD(pc+2);
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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);
|
|
|
|
TRACE("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);
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("mul.x");
|
|
cpu.asregs.regs[a] = r >> 32;
|
|
}
|
|
break;
|
|
case 0x16: /* bad */
|
|
case 0x17: /* bad */
|
|
case 0x18: /* bad */
|
|
{
|
|
opc = opcode;
|
|
TRACE("SIGILL0");
|
|
sim_engine_halt (sd, NULL, 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];
|
|
|
|
TRACE("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);
|
|
|
|
TRACE("jmpa");
|
|
pc = tgt - 2;
|
|
}
|
|
break;
|
|
case 0x1b: /* ldi.b (immediate) */
|
|
{
|
|
int reg = (inst >> 4) & 0xf;
|
|
unsigned int val = EXTRACT_WORD(pc+2);
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("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);
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("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);
|
|
|
|
TRACE("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);
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("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);
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("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);
|
|
|
|
TRACE("sta.s");
|
|
wsat (scpu, opc, addr, cpu.asregs.regs[reg]);
|
|
pc += 4;
|
|
}
|
|
break;
|
|
case 0x25: /* jmp */
|
|
{
|
|
int reg = (inst >> 4) & 0xf;
|
|
|
|
TRACE("jmp");
|
|
pc = cpu.asregs.regs[reg] - 2;
|
|
}
|
|
break;
|
|
case 0x26: /* and */
|
|
{
|
|
int a = (inst >> 4) & 0xf;
|
|
int b = inst & 0xf;
|
|
int av, bv;
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("neg");
|
|
cpu.asregs.regs[a] = - bv;
|
|
}
|
|
break;
|
|
case 0x2b: /* or */
|
|
{
|
|
int a = (inst >> 4) & 0xf;
|
|
int b = inst & 0xf;
|
|
int av, bv;
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("ashr");
|
|
cpu.asregs.regs[a] = av >> bv;
|
|
}
|
|
break;
|
|
case 0x2e: /* xor */
|
|
{
|
|
int a = (inst >> 4) & 0xf;
|
|
int b = inst & 0xf;
|
|
int av, bv;
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("mul.l");
|
|
cpu.asregs.regs[a] = av * bv;
|
|
}
|
|
break;
|
|
case 0x30: /* swi */
|
|
{
|
|
unsigned int inum = EXTRACT_WORD(pc+2);
|
|
|
|
TRACE("swi");
|
|
/* Set the special registers appropriately. */
|
|
cpu.asregs.sregs[2] = 3; /* MOXIE_EX_SWI */
|
|
cpu.asregs.sregs[3] = inum;
|
|
switch (inum)
|
|
{
|
|
case 0x1: /* SYS_exit */
|
|
{
|
|
sim_engine_halt (sd, NULL, NULL, pc, sim_exited,
|
|
cpu.asregs.regs[2]);
|
|
break;
|
|
}
|
|
case 0x2: /* 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 = open (fname, mode, perm);
|
|
sim_core_read_buffer (sd, scpu, read_map, fname,
|
|
cpu.asregs.regs[2], 1024);
|
|
/* FIXME - set errno */
|
|
cpu.asregs.regs[2] = fd;
|
|
break;
|
|
}
|
|
case 0x4: /* SYS_read */
|
|
{
|
|
int fd = cpu.asregs.regs[2];
|
|
unsigned len = (unsigned) cpu.asregs.regs[4];
|
|
char *buf = malloc (len);
|
|
cpu.asregs.regs[2] = read (fd, buf, len);
|
|
sim_core_write_buffer (sd, scpu, write_map, buf,
|
|
cpu.asregs.regs[3], len);
|
|
free (buf);
|
|
break;
|
|
}
|
|
case 0x5: /* 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 = write (cpu.asregs.regs[2], str, len);
|
|
free (str);
|
|
cpu.asregs.regs[2] = count;
|
|
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];
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("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];
|
|
|
|
TRACE("umod.l");
|
|
cpu.asregs.regs[a] = (av % bv);
|
|
}
|
|
break;
|
|
case 0x35: /* brk */
|
|
TRACE("brk");
|
|
sim_engine_halt (sd, NULL, 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;
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("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;
|
|
|
|
TRACE("sto.s");
|
|
addr += cpu.asregs.regs[a];
|
|
wsat (scpu, opc, addr, cpu.asregs.regs[b]);
|
|
pc += 2;
|
|
}
|
|
break;
|
|
default:
|
|
opc = opcode;
|
|
TRACE("SIGILL1");
|
|
sim_engine_halt (sd, NULL, NULL, pc, sim_stopped, SIM_SIGILL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
cpu.asregs.insts++;
|
|
pc += 2;
|
|
cpu.asregs.regs[PC_REGNO] = pc;
|
|
} while (1);
|
|
}
|
|
|
|
int
|
|
sim_store_register (SIM_DESC sd, int rn, unsigned char *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;
|
|
}
|
|
|
|
int
|
|
sim_fetch_register (SIM_DESC sd, int rn, unsigned char *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 cpu->registers[PCIDX];
|
|
}
|
|
|
|
static void
|
|
moxie_pc_set (sim_cpu *cpu, sim_cia pc)
|
|
{
|
|
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 **argv)
|
|
{
|
|
int i;
|
|
SIM_DESC sd = sim_state_alloc (kind, cb);
|
|
SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
|
|
|
|
/* The cpu data is kept in a separately allocated chunk of memory. */
|
|
if (sim_cpu_alloc_all (sd, 1, /*cgen_cpu_max_extra_bytes ()*/0) != SIM_RC_OK)
|
|
{
|
|
free_state (sd);
|
|
return 0;
|
|
}
|
|
|
|
STATE_WATCHPOINTS (sd)->pc = &cpu.asregs.regs[PC_REGNO];
|
|
STATE_WATCHPOINTS (sd)->sizeof_pc = sizeof (word);
|
|
|
|
if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
|
|
{
|
|
free_state (sd);
|
|
return 0;
|
|
}
|
|
|
|
/* getopt will print the error message so we just have to exit if this fails.
|
|
FIXME: Hmmm... in the case of gdb we need getopt to call
|
|
print_filtered. */
|
|
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_ARGV (sd) != NULL
|
|
? *STATE_PROG_ARGV (sd)
|
|
: NULL), 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_PC_FETCH (cpu) = moxie_pc_get;
|
|
CPU_PC_STORE (cpu) = moxie_pc_set;
|
|
|
|
set_initial_gprs (); /* Reset the GPR registers. */
|
|
}
|
|
|
|
return sd;
|
|
}
|
|
|
|
void
|
|
sim_close (SIM_DESC sd, int quitting)
|
|
{
|
|
/* nothing to do */
|
|
}
|
|
|
|
|
|
/* 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);
|
|
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 **argv, char **env)
|
|
{
|
|
char ** 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;
|
|
}
|