simplesim-3.0/target-alpha/alpha.c

609 lines
16 KiB
C

/* alpha.c - Alpha ISA definition routines */
/* SimpleScalar(TM) Tool Suite
* Copyright (C) 1994-2003 by Todd M. Austin, Ph.D. and SimpleScalar, LLC.
* All Rights Reserved.
*
* THIS IS A LEGAL DOCUMENT, BY USING SIMPLESCALAR,
* YOU ARE AGREEING TO THESE TERMS AND CONDITIONS.
*
* No portion of this work may be used by any commercial entity, or for any
* commercial purpose, without the prior, written permission of SimpleScalar,
* LLC (info@simplescalar.com). Nonprofit and noncommercial use is permitted
* as described below.
*
* 1. SimpleScalar is provided AS IS, with no warranty of any kind, express
* or implied. The user of the program accepts full responsibility for the
* application of the program and the use of any results.
*
* 2. Nonprofit and noncommercial use is encouraged. SimpleScalar may be
* downloaded, compiled, executed, copied, and modified solely for nonprofit,
* educational, noncommercial research, and noncommercial scholarship
* purposes provided that this notice in its entirety accompanies all copies.
* Copies of the modified software can be delivered to persons who use it
* solely for nonprofit, educational, noncommercial research, and
* noncommercial scholarship purposes provided that this notice in its
* entirety accompanies all copies.
*
* 3. ALL COMMERCIAL USE, AND ALL USE BY FOR PROFIT ENTITIES, IS EXPRESSLY
* PROHIBITED WITHOUT A LICENSE FROM SIMPLESCALAR, LLC (info@simplescalar.com).
*
* 4. No nonprofit user may place any restrictions on the use of this software,
* including as modified by the user, by any other authorized user.
*
* 5. Noncommercial and nonprofit users may distribute copies of SimpleScalar
* in compiled or executable form as set forth in Section 2, provided that
* either: (A) it is accompanied by the corresponding machine-readable source
* code, or (B) it is accompanied by a written offer, with no time limit, to
* give anyone a machine-readable copy of the corresponding source code in
* return for reimbursement of the cost of distribution. This written offer
* must permit verbatim duplication by anyone, or (C) it is distributed by
* someone who received only the executable form, and is accompanied by a
* copy of the written offer of source code.
*
* 6. SimpleScalar was developed by Todd M. Austin, Ph.D. The tool suite is
* currently maintained by SimpleScalar LLC (info@simplescalar.com). US Mail:
* 2395 Timbercrest Court, Ann Arbor, MI 48105.
*
* Copyright (C) 1994-2003 by Todd M. Austin, Ph.D. and SimpleScalar, LLC.
*/
#include <stdio.h>
#include <stdlib.h>
#include "host.h"
#include "misc.h"
#include "machine.h"
#include "eval.h"
#include "regs.h"
#if 0 /* cross-endian execution now works with EIO trace files only... */
/* FIXME: currently SimpleScalar/AXP only builds on little-endian... */
#if !defined(BYTES_LITTLE_ENDIAN) || !defined(WORDS_LITTLE_ENDIAN)
#error SimpleScalar/AXP only builds on little-endian machines...
#endif
#endif
/* FIXME: currently SimpleScalar/AXP only builds with qword support... */
#if !defined(HOST_HAS_QWORD)
#error SimpleScalar/AXP only builds on hosts with builtin qword support...
#error Try building with GNU GCC, as it supports qwords on most machines.
#endif
/* preferred nop instruction definition */
md_inst_t MD_NOP_INST = 0x43ff041f; /* addq r31,r31,r31 */
/* opcode mask -> enum md_opcodem, used by decoder (MD_OP_ENUM()) */
enum md_opcode md_mask2op[MD_MAX_MASK+1];
unsigned int md_opoffset[OP_MAX];
/* enum md_opcode -> mask for decoding next level */
unsigned int md_opmask[OP_MAX] = {
0, /* NA */
#define DEFINST(OP,MSK,NAME,OPFORM,RES,FLAGS,O1,O2,I1,I2,I3) 0,
#define DEFLINK(OP,MSK,NAME,SHIFT,MASK) MASK,
#define CONNECT(OP)
#include "machine.def"
};
/* enum md_opcode -> shift for decoding next level */
unsigned int md_opshift[OP_MAX] = {
0, /* NA */
#define DEFINST(OP,MSK,NAME,OPFORM,RES,FLAGS,O1,O2,I1,I2,I3) 0,
#define DEFLINK(OP,MSK,NAME,SHIFT,MASK) SHIFT,
#define CONNECT(OP)
#include "machine.def"
};
/* enum md_opcode -> description string */
char *md_op2name[OP_MAX] = {
NULL, /* NA */
#define DEFINST(OP,MSK,NAME,OPFORM,RES,FLAGS,O1,O2,I1,I2,I3) NAME,
#define DEFLINK(OP,MSK,NAME,MASK,SHIFT) NAME,
#define CONNECT(OP)
#include "machine.def"
};
/* enum md_opcode -> opcode operand format, used by disassembler */
char *md_op2format[OP_MAX] = {
NULL, /* NA */
#define DEFINST(OP,MSK,NAME,OPFORM,RES,FLAGS,O1,O2,I1,I2,I3) OPFORM,
#define DEFLINK(OP,MSK,NAME,MASK,SHIFT) NULL,
#define CONNECT(OP)
#include "machine.def"
};
/* enum md_opcode -> enum md_fu_class, used by performance simulators */
enum md_fu_class md_op2fu[OP_MAX] = {
FUClamd_NA, /* NA */
#define DEFINST(OP,MSK,NAME,OPFORM,RES,FLAGS,O1,O2,I1,I2,I3) RES,
#define DEFLINK(OP,MSK,NAME,MASK,SHIFT) FUClamd_NA,
#define CONNECT(OP)
#include "machine.def"
};
/* enum md_fu_class -> description string */
char *md_fu2name[NUM_FU_CLASSES] = {
NULL, /* NA */
"fu-int-ALU",
"fu-int-multiply",
"fu-int-divide",
"fu-FP-add/sub",
"fu-FP-comparison",
"fu-FP-conversion",
"fu-FP-multiply",
"fu-FP-divide",
"fu-FP-sqrt",
"rd-port",
"wr-port"
};
char *md_amode_str[md_amode_NUM] =
{
"(const)", /* immediate addressing mode */
"(gp + const)", /* global data access through global pointer */
"(sp + const)", /* stack access through stack pointer */
"(fp + const)", /* stack access through frame pointer */
"(reg + const)", /* (reg + const) addressing */
"(reg + reg)" /* (reg + reg) addressing */
};
/* symbolic register names, parser is case-insensitive */
struct md_reg_names_t md_reg_names[] =
{
/* name */ /* file */ /* reg */
/* integer register file */
{ "$r0", rt_gpr, 0 },
{ "$v0", rt_gpr, 0 },
{ "$r1", rt_gpr, 1 },
{ "$r2", rt_gpr, 2 },
{ "$r3", rt_gpr, 3 },
{ "$r4", rt_gpr, 4 },
{ "$r5", rt_gpr, 5 },
{ "$r6", rt_gpr, 6 },
{ "$r7", rt_gpr, 7 },
{ "$err", rt_gpr, 7 },
{ "$r8", rt_gpr, 8 },
{ "$r9", rt_gpr, 9 },
{ "$r10", rt_gpr, 10 },
{ "$r11", rt_gpr, 11 },
{ "$r12", rt_gpr, 12 },
{ "$r13", rt_gpr, 13 },
{ "$r14", rt_gpr, 14 },
{ "$r15", rt_gpr, 15 },
{ "$fp", rt_gpr, 15 },
{ "$r16", rt_gpr, 16 },
{ "$a0", rt_gpr, 16 },
{ "$r17", rt_gpr, 17 },
{ "$a1", rt_gpr, 17 },
{ "$r18", rt_gpr, 18 },
{ "$a2", rt_gpr, 18 },
{ "$r19", rt_gpr, 19 },
{ "$a3", rt_gpr, 19 },
{ "$r20", rt_gpr, 20 },
{ "$a4", rt_gpr, 20 },
{ "$r21", rt_gpr, 21 },
{ "$a5", rt_gpr, 21 },
{ "$r22", rt_gpr, 22 },
{ "$r23", rt_gpr, 23 },
{ "$r24", rt_gpr, 24 },
{ "$r25", rt_gpr, 25 },
{ "$r26", rt_gpr, 26 },
{ "$ra", rt_gpr, 26 },
{ "$r27", rt_gpr, 27 },
{ "$r28", rt_gpr, 28 },
{ "$r29", rt_gpr, 29 },
{ "$gp", rt_gpr, 29 },
{ "$r30", rt_gpr, 30 },
{ "$sp", rt_gpr, 30 },
{ "$r31", rt_gpr, 31 },
{ "$zero", rt_gpr, 31 },
/* floating point register file - double precision */
{ "$f0", rt_fpr, 0 },
{ "$f1", rt_fpr, 1 },
{ "$f2", rt_fpr, 2 },
{ "$f3", rt_fpr, 3 },
{ "$f4", rt_fpr, 4 },
{ "$f5", rt_fpr, 5 },
{ "$f6", rt_fpr, 6 },
{ "$f7", rt_fpr, 7 },
{ "$f8", rt_fpr, 8 },
{ "$f9", rt_fpr, 9 },
{ "$f10", rt_fpr, 10 },
{ "$f11", rt_fpr, 11 },
{ "$f12", rt_fpr, 12 },
{ "$f13", rt_fpr, 13 },
{ "$f14", rt_fpr, 14 },
{ "$f15", rt_fpr, 15 },
{ "$f16", rt_fpr, 16 },
{ "$f17", rt_fpr, 17 },
{ "$f18", rt_fpr, 18 },
{ "$f19", rt_fpr, 19 },
{ "$f20", rt_fpr, 20 },
{ "$f21", rt_fpr, 21 },
{ "$f22", rt_fpr, 22 },
{ "$f23", rt_fpr, 23 },
{ "$f24", rt_fpr, 24 },
{ "$f25", rt_fpr, 25 },
{ "$f26", rt_fpr, 26 },
{ "$f27", rt_fpr, 27 },
{ "$f28", rt_fpr, 28 },
{ "$f29", rt_fpr, 29 },
{ "$f30", rt_fpr, 30 },
{ "$f31", rt_fpr, 31 },
/* floating point register file - integer precision */
{ "$l0", rt_lpr, 0 },
{ "$l1", rt_lpr, 1 },
{ "$l2", rt_lpr, 2 },
{ "$l3", rt_lpr, 3 },
{ "$l4", rt_lpr, 4 },
{ "$l5", rt_lpr, 5 },
{ "$l6", rt_lpr, 6 },
{ "$l7", rt_lpr, 7 },
{ "$l8", rt_lpr, 8 },
{ "$l9", rt_lpr, 9 },
{ "$l10", rt_lpr, 10 },
{ "$l11", rt_lpr, 11 },
{ "$l12", rt_lpr, 12 },
{ "$l13", rt_lpr, 13 },
{ "$l14", rt_lpr, 14 },
{ "$l15", rt_lpr, 15 },
{ "$l16", rt_lpr, 16 },
{ "$l17", rt_lpr, 17 },
{ "$l18", rt_lpr, 18 },
{ "$l19", rt_lpr, 19 },
{ "$l20", rt_lpr, 20 },
{ "$l21", rt_lpr, 21 },
{ "$l22", rt_lpr, 22 },
{ "$l23", rt_lpr, 23 },
{ "$l24", rt_lpr, 24 },
{ "$l25", rt_lpr, 25 },
{ "$l26", rt_lpr, 26 },
{ "$l27", rt_lpr, 27 },
{ "$l28", rt_lpr, 28 },
{ "$l29", rt_lpr, 29 },
{ "$l30", rt_lpr, 30 },
{ "$l31", rt_lpr, 31 },
/* miscellaneous registers */
{ "$fpcr", rt_ctrl, 0 },
{ "$uniq", rt_ctrl, 1 },
/* program counters */
{ "$pc", rt_PC, 0 },
{ "$npc", rt_NPC, 0 },
/* sentinel */
{ NULL, rt_gpr, 0 }
};
/* returns a register name string */
char *
md_reg_name(enum md_reg_type rt, int reg)
{
int i;
for (i=0; md_reg_names[i].str != NULL; i++)
{
if (md_reg_names[i].file == rt && md_reg_names[i].reg == reg)
return md_reg_names[i].str;
}
/* no found... */
return NULL;
}
char * /* err str, NULL for no err */
md_reg_obj(struct regs_t *regs, /* registers to access */
int is_write, /* access type */
enum md_reg_type rt, /* reg bank to probe */
int reg, /* register number */
struct eval_value_t *val) /* input, output */
{
switch (rt)
{
case rt_gpr:
if (reg < 0 || reg >= MD_NUM_IREGS)
return "register number out of range";
if (!is_write)
{
val->type = et_qword;
val->value.as_qword = regs->regs_R[reg];
}
else
regs->regs_R[reg] = eval_as_qword(*val);
break;
case rt_lpr:
if (reg < 0 || reg >= MD_NUM_FREGS)
return "register number out of range";
if (!is_write)
{
val->type = et_qword;
val->value.as_qword = regs->regs_F.q[reg];
}
else
regs->regs_F.q[reg] = eval_as_qword(*val);
break;
case rt_fpr:
if (reg < 0 || reg >= MD_NUM_FREGS)
return "register number out of range";
if (!is_write)
{
val->type = et_double;
val->value.as_double = regs->regs_F.d[reg];
}
else
regs->regs_F.d[reg] = eval_as_double(*val);
break;
case rt_ctrl:
switch (reg)
{
case /* FPCR */0:
if (!is_write)
{
val->type = et_qword;
val->value.as_qword = regs->regs_C.fpcr;
}
else
regs->regs_C.fpcr = eval_as_qword(*val);
break;
case /* UNIQ */1:
if (!is_write)
{
val->type = et_qword;
val->value.as_qword = regs->regs_C.uniq;
}
else
regs->regs_C.uniq = eval_as_qword(*val);
break;
default:
return "register number out of range";
}
break;
case rt_PC:
if (!is_write)
{
val->type = et_addr;
val->value.as_addr = regs->regs_PC;
}
else
regs->regs_PC = eval_as_addr(*val);
break;
case rt_NPC:
if (!is_write)
{
val->type = et_addr;
val->value.as_addr = regs->regs_NPC;
}
else
regs->regs_NPC = eval_as_addr(*val);
break;
default:
panic("bogus register bank");
}
/* no error */
return NULL;
}
/* print integer REG(S) to STREAM */
void
md_print_ireg(md_gpr_t regs, int reg, FILE *stream)
{
myfprintf(stream, "%4s: %16ld/0x%012lx",
md_reg_name(rt_gpr, reg), regs[reg], regs[reg]);
}
void
md_print_iregs(md_gpr_t regs, FILE *stream)
{
int i;
for (i=0; i < MD_NUM_IREGS; i += 2)
{
md_print_ireg(regs, i, stream);
fprintf(stream, " ");
md_print_ireg(regs, i+1, stream);
fprintf(stream, "\n");
}
}
/* print floating point REGS to STREAM */
void
md_print_fpreg(md_fpr_t regs, int reg, FILE *stream)
{
myfprintf(stream, "%4s: %16ld/0x%012lx/%f",
md_reg_name(rt_fpr, reg), regs.q[reg], regs.q[reg], regs.d[reg]);
}
void
md_print_fpregs(md_fpr_t regs, FILE *stream)
{
int i;
/* floating point registers */
for (i=0; i < MD_NUM_FREGS; i += 2)
{
md_print_fpreg(regs, i, stream);
fprintf(stream, "\n");
md_print_fpreg(regs, i+1, stream);
fprintf(stream, "\n");
}
}
void
md_print_creg(md_ctrl_t regs, int reg, FILE *stream)
{
/* index is only used to iterate over these registers, hence no enums... */
switch (reg)
{
case 0:
myfprintf(stream, "FPCR: 0x%012lx", regs.fpcr);
break;
case 1:
myfprintf(stream, "UNIQ: 0x%012lx", regs.uniq);
break;
default:
panic("bogus control register index");
}
}
void
md_print_cregs(md_ctrl_t regs, FILE *stream)
{
md_print_creg(regs, 0, stream);
fprintf(stream, " ");
md_print_creg(regs, 1, stream);
fprintf(stream, "\n");
}
/* xor checksum registers */
word_t
md_xor_regs(struct regs_t *regs)
{
int i;
qword_t checksum = 0;
for (i=0; i < (MD_NUM_IREGS-1); i++)
checksum ^= regs->regs_R[i];
for (i=0; i < (MD_NUM_FREGS-1); i++)
checksum ^= regs->regs_F.q[i];
checksum ^= regs->regs_C.fpcr;
checksum ^= regs->regs_C.uniq;
checksum ^= regs->regs_PC;
checksum ^= regs->regs_NPC;
return (word_t)((checksum >> 32) ^ checksum);
}
/* enum md_opcode -> opcode flags, used by simulators */
unsigned int md_op2flags[OP_MAX] = {
NA, /* NA */
#define DEFINST(OP,MSK,NAME,OPFORM,RES,FLAGS,O1,O2,I1,I2,I3) FLAGS,
#define DEFLINK(OP,MSK,NAME,MASK,SHIFT) NA,
#define CONNECT(OP)
#include "machine.def"
};
/* intialize the inst decoder, this function builds the ISA decode tables */
void
md_init_decoder(void)
{
unsigned long max_offset = 0;
unsigned long offset = 0;
#define DEFINST(OP,MSK,NAME,OPFORM,RES,FLAGS,O1,O2,I1,I2,I3) \
if ((MSK)+offset >= MD_MAX_MASK) \
panic("MASK_MAX is too small, index==%d", (MSK)+offset); \
if (md_mask2op[(MSK)+offset]) \
fatal("doubly defined opcode, index==%d", (MSK)+offset); \
md_mask2op[(MSK)+offset]=(OP); max_offset=MAX(max_offset,(MSK)+offset);
#define DEFLINK(OP,MSK,NAME,MASK,SHIFT) \
if ((MSK)+offset >= MD_MAX_MASK) \
panic("MASK_MAX is too small, index==%d", (MSK)+offset); \
if (md_mask2op[(MSK)+offset]) \
fatal("doubly defined opcode, index==%d", (MSK)+offset); \
md_mask2op[(MSK)+offset]=(OP); max_offset=MAX(max_offset,(MSK)+offset);
#define CONNECT(OP) \
offset = max_offset+1; md_opoffset[OP] = offset;
#include "machine.def"
if (max_offset >= MD_MAX_MASK)
panic("MASK_MAX is too small, index==%d", max_offset);
}
/* disassemble an Alpha instruction */
void
md_print_insn(md_inst_t inst, /* instruction to disassemble */
md_addr_t pc, /* addr of inst, used for PC-rels */
FILE *stream) /* output stream */
{
enum md_opcode op;
/* use stderr as default output stream */
if (!stream)
stream = stderr;
/* decode the instruction, assumes predecoded text segment */
MD_SET_OPCODE(op, inst);
/* disassemble the instruction */
if (op <= OP_NA || op >= OP_MAX)
{
/* bogus instruction */
fprintf(stream, "<invalid inst: 0x%08x>", inst);
}
else
{
char *s;
/* FIXME: %-10s crashes on Suns!!! */
fprintf(stream, "%s ", MD_OP_NAME(op));
s = MD_OP_FORMAT(op);
while (*s) {
switch (*s) {
case 'a':
fprintf(stream, "r%d", RA);
break;
case 'b':
fprintf(stream, "r%d", RB);
break;
case 'c':
fprintf(stream, "r%d", RC);
break;
case 'A':
fprintf(stream, "f%d", RA);
break;
case 'B':
fprintf(stream, "f%d", RB);
break;
case 'C':
fprintf(stream, "f%d", RC);
break;
case 'o':
fprintf(stream, "%d", (sword_t)SEXT(OFS));
break;
case 'j':
myfprintf(stream, "0x%p", pc + (SEXT(OFS) << 2) + 4);
break;
case 'J':
myfprintf(stream, "0x%p", pc + (SEXT21(TARG) << 2) + 4);
break;
case 'i':
fprintf(stream, "%d", (word_t)IMM);
break;
default:
/* anything unrecognized, e.g., '.' is just passed through */
fputc(*s, stream);
}
s++;
}
}
}