907 lines
21 KiB
C
907 lines
21 KiB
C
/*
|
|
** cpu.c PowerPC cpu-description file
|
|
** (c) in 2002-2017 by Frank Wille
|
|
*/
|
|
|
|
#include "vasm.h"
|
|
#include "operands.h"
|
|
|
|
mnemonic mnemonics[] = {
|
|
#include "opcodes.h"
|
|
};
|
|
|
|
int mnemonic_cnt=sizeof(mnemonics)/sizeof(mnemonics[0]);
|
|
|
|
char *cpu_copyright="vasm PowerPC cpu backend 3.0 (c) 2002-2017 Frank Wille";
|
|
char *cpuname = "PowerPC";
|
|
int bitsperbyte = 8;
|
|
int bytespertaddr = 4;
|
|
int ppc_endianess = 1;
|
|
|
|
static uint64_t cpu_type = CPU_TYPE_PPC | CPU_TYPE_ALTIVEC | CPU_TYPE_32 | CPU_TYPE_ANY;
|
|
static int regnames = 1;
|
|
static taddr sdreg = 13; /* this is default for V.4, PowerOpen = 2 */
|
|
static taddr sd2reg = 2;
|
|
static unsigned char opt_branch = 0;
|
|
|
|
|
|
|
|
int ppc_data_align(int n)
|
|
{
|
|
if (n<=8) return 1;
|
|
if (n<=16) return 2;
|
|
if (n<=32) return 4;
|
|
return 8;
|
|
}
|
|
|
|
|
|
int ppc_data_operand(int n)
|
|
{
|
|
if (n&OPSZ_FLOAT) return OPSZ_BITS(n)>32?OP_F64:OP_F32;
|
|
if (OPSZ_BITS(n)<=8) return OP_D8;
|
|
if (OPSZ_BITS(n)<=16) return OP_D16;
|
|
if (OPSZ_BITS(n)<=32) return OP_D32;
|
|
return OP_D64;
|
|
}
|
|
|
|
|
|
int ppc_operand_optional(operand *op,int type)
|
|
{
|
|
if (powerpc_operands[type].flags & OPER_OPTIONAL) {
|
|
op->attr = REL_NONE;
|
|
op->mode = OPM_NONE;
|
|
op->basereg = NULL;
|
|
op->value = number_expr(0); /* default value 0 */
|
|
|
|
if (powerpc_operands[type].flags & OPER_NEXT)
|
|
op->type = NEXT;
|
|
else
|
|
op->type = type;
|
|
return 1;
|
|
}
|
|
else if (powerpc_operands[type].flags & OPER_FAKE) {
|
|
op->type = type;
|
|
op->value = NULL;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int ppc_available(int idx)
|
|
/* Check if mnemonic is available for selected cpu_type. */
|
|
{
|
|
uint64_t avail = mnemonics[idx].ext.available;
|
|
uint64_t datawidth = CPU_TYPE_32 | CPU_TYPE_64;
|
|
|
|
if ((avail & cpu_type) != 0) {
|
|
if ((avail & cpu_type & ~datawidth)!=0 || (cpu_type & CPU_TYPE_ANY)!=0) {
|
|
if (avail & datawidth)
|
|
return (avail & datawidth) == (cpu_type & datawidth)
|
|
|| (cpu_type & CPU_TYPE_64_BRIDGE) != 0;
|
|
else
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static char *parse_reloc_attr(char *p,operand *op)
|
|
{
|
|
p = skip(p);
|
|
while (*p == '@') {
|
|
unsigned char chk;
|
|
|
|
p++;
|
|
chk = op->attr;
|
|
if (!strncmp(p,"got",3)) {
|
|
op->attr = REL_GOT;
|
|
p += 3;
|
|
}
|
|
else if (!strncmp(p,"plt",3)) {
|
|
op->attr = REL_PLT;
|
|
p += 3;
|
|
}
|
|
else if (!strncmp(p,"sdax",4)) {
|
|
op->attr = REL_SD;
|
|
p += 4;
|
|
}
|
|
else if (!strncmp(p,"sdarx",5)) {
|
|
op->attr = REL_SD;
|
|
p += 5;
|
|
}
|
|
else if (!strncmp(p,"sdarel",6)) {
|
|
op->attr = REL_SD;
|
|
p += 6;
|
|
}
|
|
else if (!strncmp(p,"sectoff",7)) {
|
|
op->attr = REL_SECOFF;
|
|
p += 7;
|
|
}
|
|
else if (!strncmp(p,"local",5)) {
|
|
op->attr = REL_LOCALPC;
|
|
p += 5;
|
|
}
|
|
else if (!strncmp(p,"globdat",7)) {
|
|
op->attr = REL_GLOBDAT;
|
|
p += 7;
|
|
}
|
|
else if (!strncmp(p,"sda2rel",7)) {
|
|
op->attr = REL_PPCEABI_SDA2;
|
|
p += 7;
|
|
}
|
|
else if (!strncmp(p,"sda21",5)) {
|
|
op->attr = REL_PPCEABI_SDA21;
|
|
p += 5;
|
|
}
|
|
else if (!strncmp(p,"sdai16",6)) {
|
|
op->attr = REL_PPCEABI_SDAI16;
|
|
p += 6;
|
|
}
|
|
else if (!strncmp(p,"sda2i16",7)) {
|
|
op->attr = REL_PPCEABI_SDA2I16;
|
|
p += 7;
|
|
}
|
|
else if (!strncmp(p,"drel",4)) {
|
|
op->attr = REL_MORPHOS_DREL;
|
|
p += 4;
|
|
}
|
|
else if (!strncmp(p,"brel",4)) {
|
|
op->attr = REL_AMIGAOS_BREL;
|
|
p += 4;
|
|
}
|
|
if (chk!=REL_NONE && chk!=op->attr)
|
|
cpu_error(7); /* multiple relocation attributes */
|
|
|
|
chk = op->mode;
|
|
if (!strncmp(p,"ha",2)) {
|
|
op->mode = OPM_HA;
|
|
p += 2;
|
|
}
|
|
if (*p == 'h') {
|
|
op->mode = OPM_HI;
|
|
p++;
|
|
}
|
|
if (*p == 'l') {
|
|
op->mode = OPM_LO;
|
|
p++;
|
|
}
|
|
if (chk!=OPM_NONE && chk!=op->mode)
|
|
cpu_error(8); /* multiple hi/lo modifiers */
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
int parse_operand(char *p,int len,operand *op,int optype)
|
|
/* Parses operands, reads expressions and assigns relocation types. */
|
|
{
|
|
char *start = p;
|
|
int rc = PO_MATCH;
|
|
|
|
op->attr = REL_NONE;
|
|
op->mode = OPM_NONE;
|
|
op->basereg = NULL;
|
|
|
|
p = skip(p);
|
|
op->value = OP_FLOAT(optype) ? parse_expr_float(&p) : parse_expr(&p);
|
|
|
|
if (!OP_DATA(optype)) {
|
|
p = parse_reloc_attr(p,op);
|
|
p = skip(p);
|
|
|
|
if (p-start < len && *p=='(') {
|
|
/* parse d(Rn) load/store addressing mode */
|
|
if (powerpc_operands[optype].flags & OPER_PARENS) {
|
|
p++;
|
|
op->basereg = parse_expr(&p);
|
|
p = skip(p);
|
|
if (*p == ')') {
|
|
p = skip(p+1);
|
|
rc = PO_SKIP;
|
|
}
|
|
else {
|
|
cpu_error(5); /* missing closing parenthesis */
|
|
rc = PO_CORRUPT;
|
|
goto leave;
|
|
}
|
|
}
|
|
else {
|
|
cpu_error(4); /* illegal operand type */
|
|
rc = PO_CORRUPT;
|
|
goto leave;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p-start < len)
|
|
cpu_error(3); /* trailing garbage in operand */
|
|
leave:
|
|
op->type = optype;
|
|
return rc;
|
|
}
|
|
|
|
|
|
static taddr read_sdreg(char **s,taddr def)
|
|
{
|
|
expr *tree;
|
|
taddr val = def;
|
|
|
|
*s = skip(*s);
|
|
tree = parse_expr(s);
|
|
simplify_expr(tree);
|
|
if (tree->type==NUM && tree->c.val>=0 && tree->c.val<=31)
|
|
val = tree->c.val;
|
|
else
|
|
cpu_error(13); /* not a valid register */
|
|
free_expr(tree);
|
|
return val;
|
|
}
|
|
|
|
|
|
char *parse_cpu_special(char *start)
|
|
/* parse cpu-specific directives; return pointer to end of
|
|
cpu-specific text */
|
|
{
|
|
char *name=start,*s=start;
|
|
|
|
if (ISIDSTART(*s)) {
|
|
s++;
|
|
while (ISIDCHAR(*s))
|
|
s++;
|
|
if (s-name==6 && !strncmp(name,".sdreg",6)) {
|
|
sdreg = read_sdreg(&s,sdreg);
|
|
return s;
|
|
}
|
|
else if (s-name==7 && !strncmp(name,".sd2reg",7)) {
|
|
sd2reg = read_sdreg(&s,sd2reg);
|
|
return s;
|
|
}
|
|
}
|
|
return start;
|
|
}
|
|
|
|
|
|
static int get_reloc_type(operand *op)
|
|
{
|
|
int rtype = REL_NONE;
|
|
|
|
if (OP_DATA(op->type)) { /* data relocs */
|
|
return REL_ABS;
|
|
}
|
|
|
|
else { /* handle instruction relocs */
|
|
const struct powerpc_operand *ppcop = &powerpc_operands[op->type];
|
|
|
|
if (ppcop->shift == 0) {
|
|
if (ppcop->bits == 16 || ppcop->bits == 26) {
|
|
|
|
if (ppcop->flags & OPER_RELATIVE) { /* a relative branch */
|
|
switch (op->attr) {
|
|
case REL_NONE:
|
|
rtype = REL_PC;
|
|
break;
|
|
case REL_PLT:
|
|
rtype = REL_PLTPC;
|
|
break;
|
|
case REL_LOCALPC:
|
|
rtype = REL_LOCALPC;
|
|
break;
|
|
default:
|
|
cpu_error(11); /* reloc attribute not supported by operand */
|
|
break;
|
|
}
|
|
}
|
|
|
|
else if (ppcop->flags & OPER_ABSOLUTE) { /* absolute branch */
|
|
switch (op->attr) {
|
|
case REL_NONE:
|
|
rtype = REL_ABS;
|
|
break;
|
|
case REL_PLT:
|
|
case REL_GLOBDAT:
|
|
case REL_SECOFF:
|
|
rtype = op->attr;
|
|
break;
|
|
default:
|
|
cpu_error(11); /* reloc attribute not supported by operand */
|
|
break;
|
|
}
|
|
}
|
|
|
|
else { /* immediate 16 bit or load/store d16(Rn) instruction */
|
|
switch (op->attr) {
|
|
case REL_NONE:
|
|
rtype = REL_ABS;
|
|
break;
|
|
case REL_GOT:
|
|
case REL_PLT:
|
|
case REL_SD:
|
|
case REL_PPCEABI_SDA2:
|
|
case REL_PPCEABI_SDA21:
|
|
case REL_PPCEABI_SDAI16:
|
|
case REL_PPCEABI_SDA2I16:
|
|
case REL_MORPHOS_DREL:
|
|
case REL_AMIGAOS_BREL:
|
|
rtype = op->attr;
|
|
break;
|
|
default:
|
|
cpu_error(11); /* reloc attribute not supported by operand */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rtype;
|
|
}
|
|
|
|
|
|
static int valid_hiloreloc(int type)
|
|
/* checks if this relocation type allows a @l/@h/@ha modifier */
|
|
{
|
|
switch (type) {
|
|
case REL_ABS:
|
|
case REL_GOT:
|
|
case REL_PLT:
|
|
case REL_MORPHOS_DREL:
|
|
case REL_AMIGAOS_BREL:
|
|
return 1;
|
|
}
|
|
cpu_error(6); /* relocation does not allow hi/lo modifier */
|
|
return 0;
|
|
}
|
|
|
|
|
|
static taddr make_reloc(int reloctype,operand *op,section *sec,
|
|
taddr pc,rlist **reloclist)
|
|
/* create a reloc-entry when operand contains a non-constant expression */
|
|
{
|
|
taddr val;
|
|
|
|
if (!eval_expr(op->value,&val,sec,pc)) {
|
|
/* non-constant expression requires a relocation entry */
|
|
symbol *base;
|
|
int btype,pos,size,offset;
|
|
taddr addend,mask;
|
|
|
|
btype = find_base(op->value,&base,sec,pc);
|
|
pos = offset = 0;
|
|
|
|
if (btype > BASE_ILLEGAL) {
|
|
if (btype == BASE_PCREL) {
|
|
if (reloctype == REL_ABS)
|
|
reloctype = REL_PC;
|
|
else
|
|
goto illreloc;
|
|
}
|
|
|
|
if (op->mode != OPM_NONE) {
|
|
/* check if reloc allows @ha/@h/@l */
|
|
if (!valid_hiloreloc(reloctype))
|
|
op->mode = OPM_NONE;
|
|
}
|
|
|
|
if (reloctype == REL_PC && !is_pc_reloc(base,sec)) {
|
|
/* a relative branch - reloc is only needed for external reference */
|
|
return val-pc;
|
|
}
|
|
|
|
/* determine reloc size, offset and mask */
|
|
if (OP_DATA(op->type)) { /* data operand */
|
|
switch (op->type) {
|
|
case OP_D8:
|
|
size = 8;
|
|
break;
|
|
case OP_D16:
|
|
size = 16;
|
|
break;
|
|
case OP_D32:
|
|
case OP_F32:
|
|
size = 32;
|
|
break;
|
|
case OP_D64:
|
|
case OP_F64:
|
|
size = 64;
|
|
break;
|
|
default:
|
|
ierror(0);
|
|
break;
|
|
}
|
|
addend = val;
|
|
mask = -1;
|
|
}
|
|
else { /* instruction operand */
|
|
const struct powerpc_operand *ppcop = &powerpc_operands[op->type];
|
|
|
|
if (ppcop->flags & (OPER_RELATIVE|OPER_ABSOLUTE)) {
|
|
/* branch instruction */
|
|
if (ppcop->bits == 26) {
|
|
size = 24;
|
|
pos = 6;
|
|
mask = 0x3fffffc;
|
|
}
|
|
else {
|
|
size = 14;
|
|
offset = 2;
|
|
mask = 0xfffc;
|
|
}
|
|
addend = (btype == BASE_PCREL) ? val + offset : val;
|
|
}
|
|
else {
|
|
/* load/store or immediate */
|
|
size = 16;
|
|
offset = 2;
|
|
addend = (btype == BASE_PCREL) ? val + offset : val;
|
|
switch (op->mode) {
|
|
case OPM_LO:
|
|
mask = 0xffff;
|
|
break;
|
|
case OPM_HI:
|
|
mask = 0xffff0000;
|
|
break;
|
|
case OPM_HA:
|
|
add_extnreloc_masked(reloclist,base,addend,reloctype,
|
|
pos,size,offset,0x8000);
|
|
mask = 0xffff0000;
|
|
break;
|
|
default:
|
|
mask = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
add_extnreloc_masked(reloclist,base,addend,reloctype,
|
|
pos,size,offset,mask);
|
|
}
|
|
else if (btype != BASE_NONE) {
|
|
illreloc:
|
|
general_error(38); /* illegal relocation */
|
|
}
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
|
|
static void fix_reloctype(dblock *db,int rtype)
|
|
{
|
|
rlist *rl;
|
|
|
|
for (rl=db->relocs; rl!=NULL; rl=rl->next)
|
|
rl->type = rtype;
|
|
}
|
|
|
|
|
|
static int cnt_insn_ops(instruction *p)
|
|
{
|
|
int cnt = 0;
|
|
|
|
while (cnt<MAX_OPERANDS && p->op[cnt]!=NULL)
|
|
cnt++;
|
|
return cnt;
|
|
}
|
|
|
|
|
|
static int cnt_mnemo_ops(mnemonic *p)
|
|
{
|
|
int cnt = 0;
|
|
|
|
while (cnt<MAX_OPERANDS && p->operand_type[cnt]!=UNUSED)
|
|
cnt++;
|
|
return cnt;
|
|
}
|
|
|
|
|
|
static void range_check(taddr val,const struct powerpc_operand *o,dblock *db)
|
|
/* checks if a value fits the allowed range for this operand field */
|
|
{
|
|
int32_t v = (int32_t)val;
|
|
int32_t minv = 0;
|
|
int32_t maxv = (1L << o->bits) - 1;
|
|
int force_signopt = 0;
|
|
|
|
if (db) {
|
|
if (db->relocs) {
|
|
switch (db->relocs->type) {
|
|
case REL_SD:
|
|
case REL_PPCEABI_SDA2:
|
|
case REL_PPCEABI_SDA21:
|
|
force_signopt = 1; /* relocation allows full positive range */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (o->flags & OPER_SIGNED) {
|
|
minv = ~(maxv >> 1);
|
|
|
|
/* @@@ Only recognize this flag in 32-bit mode! Don't care for now */
|
|
if (!(o->flags & OPER_SIGNOPT) && !force_signopt)
|
|
maxv >>= 1;
|
|
}
|
|
if (o->flags & OPER_NEGATIVE)
|
|
v = -v;
|
|
|
|
if (v<minv || v>maxv)
|
|
cpu_error(12,v,minv,maxv); /* operand out of range */
|
|
}
|
|
|
|
|
|
static void negate_bo_cond(uint32_t *p)
|
|
/* negates all conditions in a branch instruction's BO field */
|
|
{
|
|
if (!(*p & 0x02000000))
|
|
*p ^= 0x01000000;
|
|
if (!(*p & 0x00800000))
|
|
*p ^= 0x00400000;
|
|
}
|
|
|
|
|
|
static uint32_t insertcode(uint32_t i,taddr val,
|
|
const struct powerpc_operand *o)
|
|
{
|
|
if (o->insert) {
|
|
const char *errmsg = NULL;
|
|
|
|
i = (o->insert)(i,(int32_t)val,&errmsg);
|
|
if (errmsg)
|
|
cpu_error(0,errmsg);
|
|
}
|
|
else
|
|
i |= ((int32_t)val & ((1<<o->bits)-1)) << o->shift;
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
size_t eval_operands(instruction *ip,section *sec,taddr pc,
|
|
uint32_t *insn,dblock *db)
|
|
/* evaluate expressions and try to optimize instruction,
|
|
return size of instruction */
|
|
{
|
|
mnemonic *mnemo = &mnemonics[ip->code];
|
|
size_t isize = 4;
|
|
int i,j,omitted;
|
|
operand op;
|
|
|
|
if (insn != NULL)
|
|
*insn = mnemo->ext.opcode;
|
|
|
|
for (i=0; i<MAX_OPERANDS && ip->op[i]!=NULL; i++) {
|
|
const struct powerpc_operand *ppcop;
|
|
int reloctype;
|
|
taddr val;
|
|
|
|
op = *(ip->op[i]);
|
|
|
|
if (op.type == NEXT) {
|
|
/* special case: operand omitted and use this operand's type + 1
|
|
for the next operand */
|
|
op = *(ip->op[++i]);
|
|
op.type = mnemo->operand_type[i-1] + 1;
|
|
}
|
|
|
|
ppcop = &powerpc_operands[op.type];
|
|
|
|
if (ppcop->flags & OPER_FAKE) {
|
|
if (insn != NULL) {
|
|
if (op.value != NULL)
|
|
cpu_error(16); /* ignoring fake operand */
|
|
*insn = insertcode(*insn,0,ppcop);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if ((reloctype = get_reloc_type(&op)) != REL_NONE) {
|
|
if (db != NULL) {
|
|
val = make_reloc(reloctype,&op,sec,pc,&db->relocs);
|
|
}
|
|
else {
|
|
if (!eval_expr(op.value,&val,sec,pc)) {
|
|
if (reloctype == REL_PC)
|
|
val -= pc;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (!eval_expr(op.value,&val,sec,pc))
|
|
if (insn != NULL)
|
|
cpu_error(2); /* constant integer expression required */
|
|
}
|
|
|
|
/* execute modifier on val */
|
|
if (op.mode) {
|
|
switch (op.mode) {
|
|
case OPM_LO:
|
|
val &= 0xffff;
|
|
break;
|
|
case OPM_HI:
|
|
val = (val>>16) & 0xffff;
|
|
break;
|
|
case OPM_HA:
|
|
val = ((val>>16) + ((val & 0x8000) ? 1 : 0) & 0xffff);
|
|
break;
|
|
}
|
|
if ((ppcop->flags & OPER_SIGNED) && (val & 0x8000))
|
|
val -= 0x10000;
|
|
}
|
|
|
|
/* do optimizations here: */
|
|
|
|
if (opt_branch) {
|
|
if (reloctype==REL_PC &&
|
|
(op.type==BD || op.type==BDM || op.type==BDP)) {
|
|
if (val<-0x8000 || val>0x7fff) {
|
|
/* "B<cc>" branch destination out of range, convert into
|
|
a "B<!cc> ; B" combination */
|
|
if (insn != NULL) {
|
|
negate_bo_cond(insn);
|
|
*insn = insertcode(*insn,8,ppcop); /* B<!cc> $+8 */
|
|
insn++;
|
|
*insn = B(18,0,0); /* set B instruction opcode */
|
|
val -= 4;
|
|
}
|
|
ppcop = &powerpc_operands[LI]; /* set oper. for B instruction */
|
|
isize = 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ppcop->flags & OPER_PARENS) {
|
|
if (op.basereg) {
|
|
/* a load/store instruction d(Rn) carries basereg in current op */
|
|
taddr reg;
|
|
|
|
if (db!=NULL && op.mode==OPM_NONE && op.attr==REL_NONE) {
|
|
if (eval_expr(op.basereg,®,sec,pc)) {
|
|
if (reg == sdreg) /* is it a small data reference? */
|
|
fix_reloctype(db,REL_SD);
|
|
else if (reg == sd2reg) /* EABI small data 2 */
|
|
fix_reloctype(db,REL_PPCEABI_SDA2);
|
|
}
|
|
}
|
|
|
|
/* write displacement */
|
|
if (insn != NULL) {
|
|
range_check(val,ppcop,db);
|
|
*insn = insertcode(*insn,val,ppcop);
|
|
}
|
|
|
|
/* move to next operand type to handle base register */
|
|
op.type = mnemo->operand_type[++i];
|
|
ppcop = &powerpc_operands[op.type];
|
|
op.attr = REL_NONE;
|
|
op.mode = OPM_NONE;
|
|
op.value = op.basereg;
|
|
if (!eval_expr(op.value,&val,sec,pc))
|
|
if (insn != NULL)
|
|
cpu_error(2); /* constant integer expression required */
|
|
}
|
|
else if (insn != NULL)
|
|
cpu_error(14); /* missing base register */
|
|
}
|
|
|
|
/* write val (register, immediate, etc.) */
|
|
if (insn != NULL) {
|
|
range_check(val,ppcop,db);
|
|
*insn = insertcode(*insn,val,ppcop);
|
|
}
|
|
}
|
|
|
|
return isize;
|
|
}
|
|
|
|
|
|
size_t instruction_size(instruction *ip,section *sec,taddr pc)
|
|
/* Calculate the size of the current instruction; must be identical
|
|
to the data created by eval_instruction. */
|
|
{
|
|
/* determine optimized size, when needed */
|
|
if (opt_branch)
|
|
return eval_operands(ip,sec,pc,NULL,NULL);
|
|
|
|
/* otherwise an instruction is always 4 bytes */
|
|
return 4;
|
|
}
|
|
|
|
|
|
dblock *eval_instruction(instruction *ip,section *sec,taddr pc)
|
|
/* Convert an instruction into a DATA atom including relocations,
|
|
when necessary. */
|
|
{
|
|
dblock *db = new_dblock();
|
|
uint32_t insn[2];
|
|
|
|
if (db->size = eval_operands(ip,sec,pc,insn,db)) {
|
|
unsigned char *d = db->data = mymalloc(db->size);
|
|
int i;
|
|
|
|
for (i=0; i<db->size/4; i++)
|
|
d = setval(1,d,4,insn[i]);
|
|
}
|
|
|
|
return db;
|
|
}
|
|
|
|
|
|
dblock *eval_data(operand *op,size_t bitsize,section *sec,taddr pc)
|
|
/* Create a dblock (with relocs, if necessary) for size bits of data. */
|
|
{
|
|
dblock *db = new_dblock();
|
|
taddr val;
|
|
tfloat flt;
|
|
|
|
if ((bitsize & 7) || bitsize > 64)
|
|
cpu_error(9,bitsize); /* data size not supported */
|
|
if (!OP_DATA(op->type))
|
|
ierror(0);
|
|
|
|
db->size = bitsize >> 3;
|
|
db->data = mymalloc(db->size);
|
|
|
|
if (type_of_expr(op->value) == FLT) {
|
|
if (!eval_expr_float(op->value,&flt))
|
|
general_error(60); /* cannot evaluate floating point */
|
|
|
|
switch (bitsize) {
|
|
case 32:
|
|
conv2ieee32(1,db->data,flt);
|
|
break;
|
|
case 64:
|
|
conv2ieee64(1,db->data,flt);
|
|
break;
|
|
default:
|
|
cpu_error(10); /* data has illegal type */
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
val = make_reloc(get_reloc_type(op),op,sec,pc,&db->relocs);
|
|
|
|
switch (db->size) {
|
|
case 1:
|
|
db->data[0] = val & 0xff;
|
|
break;
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
setval(ppc_endianess,db->data,db->size,val);
|
|
break;
|
|
default:
|
|
ierror(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return db;
|
|
}
|
|
|
|
|
|
operand *new_operand()
|
|
{
|
|
operand *new = mymalloc(sizeof(*new));
|
|
new->type = -1;
|
|
new->mode = OPM_NONE;
|
|
return new;
|
|
}
|
|
|
|
|
|
static void define_regnames(void)
|
|
{
|
|
char r[4];
|
|
int i;
|
|
|
|
for (i=0; i<32; i++) {
|
|
sprintf(r,"r%d",i);
|
|
set_internal_abs(r,(taddr)i);
|
|
r[0] = 'f';
|
|
set_internal_abs(r,(taddr)i);
|
|
r[0] = 'v';
|
|
set_internal_abs(r,(taddr)i);
|
|
}
|
|
for (i=0; i<8; i++) {
|
|
sprintf(r,"cr%d",i);
|
|
set_internal_abs(r,(taddr)i);
|
|
}
|
|
set_internal_abs("vrsave",256);
|
|
set_internal_abs("lt",0);
|
|
set_internal_abs("gt",1);
|
|
set_internal_abs("eq",2);
|
|
set_internal_abs("so",3);
|
|
set_internal_abs("un",3);
|
|
set_internal_abs("sp",1);
|
|
set_internal_abs("rtoc",2);
|
|
set_internal_abs("fp",31);
|
|
set_internal_abs("fpscr",0);
|
|
set_internal_abs("xer",1);
|
|
set_internal_abs("lr",8);
|
|
set_internal_abs("ctr",9);
|
|
}
|
|
|
|
|
|
int init_cpu()
|
|
{
|
|
if (regnames)
|
|
define_regnames();
|
|
return 1;
|
|
}
|
|
|
|
|
|
int cpu_args(char *p)
|
|
{
|
|
int i;
|
|
|
|
if (!strncmp(p,"-m",2)) {
|
|
p += 2;
|
|
if (!strcmp(p,"pwrx") || !strcmp(p,"pwr2"))
|
|
cpu_type = CPU_TYPE_POWER | CPU_TYPE_POWER2 | CPU_TYPE_32;
|
|
else if (!strcmp(p,"pwr"))
|
|
cpu_type = CPU_TYPE_POWER | CPU_TYPE_32;
|
|
else if (!strcmp(p,"601"))
|
|
cpu_type = CPU_TYPE_601 | CPU_TYPE_PPC | CPU_TYPE_32;
|
|
else if (!strcmp(p,"ppc") || !strcmp(p,"ppc32") || !strncmp(p,"60",2) ||
|
|
!strncmp(p,"75",2) || !strncmp(p,"85",2))
|
|
cpu_type = CPU_TYPE_PPC | CPU_TYPE_32;
|
|
else if (!strcmp(p,"ppc64") || !strcmp(p,"620"))
|
|
cpu_type = CPU_TYPE_PPC | CPU_TYPE_64;
|
|
else if (!strcmp(p,"7450"))
|
|
cpu_type = CPU_TYPE_PPC | CPU_TYPE_7450 | CPU_TYPE_32 | CPU_TYPE_ALTIVEC;
|
|
else if (!strncmp(p,"74",2) || !strcmp(p,"avec") || !strcmp(p,"altivec"))
|
|
cpu_type = CPU_TYPE_PPC | CPU_TYPE_32 | CPU_TYPE_ALTIVEC;
|
|
else if (!strcmp(p,"403"))
|
|
cpu_type = CPU_TYPE_PPC | CPU_TYPE_403 | CPU_TYPE_32;
|
|
else if (!strcmp(p,"405"))
|
|
cpu_type = CPU_TYPE_PPC | CPU_TYPE_403 | CPU_TYPE_405 | CPU_TYPE_32;
|
|
else if (!strncmp(p,"44",2) || !strncmp(p,"46",2))
|
|
cpu_type = CPU_TYPE_PPC | CPU_TYPE_440 | CPU_TYPE_BOOKE | CPU_TYPE_ISEL
|
|
| CPU_TYPE_RFMCI | CPU_TYPE_32;
|
|
else if (!strcmp(p,"821") || !strcmp(p,"850") || !strcmp(p,"860"))
|
|
cpu_type = CPU_TYPE_PPC | CPU_TYPE_860 | CPU_TYPE_32;
|
|
else if (!strcmp(p,"e300"))
|
|
cpu_type = CPU_TYPE_PPC | CPU_TYPE_E300 | CPU_TYPE_32;
|
|
else if (!strcmp(p,"e500"))
|
|
cpu_type = CPU_TYPE_PPC | CPU_TYPE_E500 | CPU_TYPE_BOOKE | CPU_TYPE_ISEL
|
|
| CPU_TYPE_SPE | CPU_TYPE_EFS | CPU_TYPE_PMR | CPU_TYPE_RFMCI
|
|
| CPU_TYPE_32;
|
|
else if (!strcmp(p,"booke"))
|
|
cpu_type = CPU_TYPE_PPC | CPU_TYPE_BOOKE;
|
|
else if (!strcmp(p,"com"))
|
|
cpu_type = CPU_TYPE_COMMON | CPU_TYPE_32;
|
|
else if (!strcmp(p,"any"))
|
|
cpu_type |= CPU_TYPE_ANY;
|
|
else
|
|
return 0;
|
|
}
|
|
else if (!strcmp(p,"-no-regnames"))
|
|
regnames = 0;
|
|
else if (!strcmp(p,"-little"))
|
|
ppc_endianess = 0;
|
|
else if (!strcmp(p,"-big"))
|
|
ppc_endianess = 1;
|
|
else if (!strncmp(p,"-sdreg=",7)) {
|
|
i = atoi(p+7);
|
|
if (i>=0 && i<=31)
|
|
sdreg = i;
|
|
else
|
|
cpu_error(13); /* not a valid register */
|
|
}
|
|
else if (!strncmp(p,"-sd2reg=",8)) {
|
|
i = atoi(p+8);
|
|
if (i>=0 && i<=31)
|
|
sd2reg = i;
|
|
else
|
|
cpu_error(13); /* not a valid register */
|
|
}
|
|
else if (!strcmp(p,"-opt-branch"))
|
|
opt_branch = 1;
|
|
else
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|