5819 lines
174 KiB
C
5819 lines
174 KiB
C
/*
|
|
** cpu.c Motorola M68k, CPU32 and ColdFire cpu-description file
|
|
** (c) in 2002-2019 by Frank Wille
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include "vasm.h"
|
|
#include "error.h"
|
|
|
|
#include "operands.h"
|
|
|
|
struct specreg SpecRegs[] = {
|
|
#include "specregs.h"
|
|
};
|
|
static int specreg_cnt = sizeof(SpecRegs)/sizeof(SpecRegs[0]);
|
|
|
|
mnemonic mnemonics[] = {
|
|
#include "opcodes.h"
|
|
};
|
|
int mnemonic_cnt = sizeof(mnemonics)/sizeof(mnemonics[0]);
|
|
|
|
struct cpu_models models[] = {
|
|
#include "cpu_models.h"
|
|
};
|
|
int model_cnt = sizeof(models)/sizeof(models[0]);
|
|
|
|
|
|
char *cpu_copyright="vasm M68k/CPU32/ColdFire cpu backend 2.3f (c) 2002-2019 Frank Wille";
|
|
char *cpuname = "M68k";
|
|
int bitsperbyte = 8;
|
|
int bytespertaddr = 4;
|
|
|
|
int m68k_mid = 1; /* default a.out MID: 68000/68010 */
|
|
|
|
static uint32_t cpu_type = m68000;
|
|
static expr *baseexp[7]; /* basereg: expression loaded to reg. */
|
|
static signed char sdreg = -1; /* current small-data base register */
|
|
static signed char last_sdreg = -1;
|
|
static unsigned char gas = 0; /* true enables GNU-as mnemonics */
|
|
static unsigned char sgs = 0; /* true enables & as immediate prefix */
|
|
static unsigned char no_fpu = 0; /* true: FPU code/direct. disallowed */
|
|
static unsigned char elfregs = 0; /* true: %Rn instead of Rn reg. names */
|
|
static unsigned char fpu_id = 1; /* default coprocessor id for FPU */
|
|
static unsigned char opt_gen = 1; /* generic optimizations (not Devpac) */
|
|
static unsigned char opt_movem = 0; /* MOVEM Rn -> MOVE Rn */
|
|
static unsigned char opt_pea = 0; /* MOVE.L #x,-(sp) -> PEA x */
|
|
static unsigned char opt_clr = 0; /* MOVE #0,<ea> -> CLR <ea> */
|
|
static unsigned char opt_st = 0; /* MOVE.B #-1,<ea> -> ST <ea> */
|
|
static unsigned char opt_lsl = 0; /* LSL #1,Dn -> ADD Dn,Dn */
|
|
static unsigned char opt_mul = 0; /* MULU/MULS #n,Dn -> LSL/ASL #n,Dn */
|
|
static unsigned char opt_div = 0; /* DIVU/DIVS.L #n,Dn -> LSR/ASR #n,Dn */
|
|
static unsigned char opt_fconst = 1; /* Fxxx.D #m,FPn -> Fxxx.S #m,FPn */
|
|
static unsigned char opt_brajmp = 0; /* branch to different sect. into jump */
|
|
static unsigned char opt_pc = 1; /* <label> -> (<label>,PC) */
|
|
static unsigned char opt_bra = 1; /* B<cc>.L -> B<cc>.W -> B<cc>.B */
|
|
static unsigned char opt_allbra = 0; /* also optimizes sized branches */
|
|
static unsigned char opt_jbra = 0; /* JMP/JSR <ext> -> BRA.L/BSR.L (020+) */
|
|
static unsigned char opt_disp = 1; /* (0,An) -> (An), etc. */
|
|
static unsigned char opt_abs = 1; /* optimize absolute addreses to 16bit */
|
|
static unsigned char opt_moveq = 1; /* MOVE.L #x,Dn -> MOVEQ #x,Dn */
|
|
static unsigned char opt_quick = 1; /* ADD/SUB #x,Rn -> ADDQ/SUBQ #x,Rn */
|
|
static unsigned char opt_branop = 1; /* BRA.B *+2 -> NOP */
|
|
static unsigned char opt_bdisp = 1; /* base displacement optimization */
|
|
static unsigned char opt_odisp = 1; /* outer displacement optimization */
|
|
static unsigned char opt_lea = 1; /* ADD/SUB #x,An -> LEA (x,An),An */
|
|
static unsigned char opt_lquick = 1; /* LEA (x,An),An -> ADDQ/SUBQ #x,An */
|
|
static unsigned char opt_immaddr = 1; /* <op>.L #x,An -> <op>.W #x,An */
|
|
static unsigned char opt_speed = 0; /* optimize for speed, not for size */
|
|
static unsigned char opt_sc = 0; /* external JMP/JSR are 16-bit PC-rel. */
|
|
static unsigned char opt_sd = 0; /* small data opts: abs.L -> (d16,An) */
|
|
static unsigned char no_opt = 0; /* don't optimize at all! */
|
|
static unsigned char warn_opts = 0; /* warn on optimizations/translations */
|
|
static unsigned char convert_brackets = 0; /* convert [ into ( for <020 */
|
|
static unsigned char typechk = 1; /* check value types and ranges */
|
|
static unsigned char ign_unambig_ext = 0; /* don't check unambig. size ext. */
|
|
static unsigned char regsymredef = 0; /* allow redefinition of reg. symbols */
|
|
static unsigned char phxass_compat = 0;
|
|
static unsigned char devpac_compat = 0;
|
|
static unsigned char kick1hunks = 0;
|
|
static char current_ext; /* extension of current parsed inst. */
|
|
|
|
static char b_str[] = "b";
|
|
static char w_str[] = "w";
|
|
static char l_str[] = "l";
|
|
static char s_str[] = "s";
|
|
static char d_str[] = "d";
|
|
static char x_str[] = "x";
|
|
static char p_str[] = "p";
|
|
|
|
static char optc_name[] = "__OPTC";
|
|
static char cpu_name[] = "__CPU";
|
|
static char mmu_name[] = "__MMU";
|
|
static char fpu_name[] = "__FPU";
|
|
static char g2_name[] = "__G2";
|
|
static char lk_name[] = "__LK";
|
|
|
|
static int OC_JMP,OC_JSR,OC_MOVEQ,OC_MOV3Q,OC_LEA,OC_PEA,OC_SUBA,OC_CLR;
|
|
static int OC_ST,OC_ADDQ,OC_SUBQ,OC_ADDA,OC_ADD,OC_BRA,OC_BSR,OC_TST;
|
|
static int OC_NOT,OC_NOOP,OC_FNOP,OC_MOVEA,OC_EXT,OC_MVZ,OC_MOVE;
|
|
static int OC_ASRI,OC_LSRI,OC_ASLI,OC_LSLI,OC_NEG;
|
|
static int OC_FMOVEMTOLIST,OC_FMOVEMTOSPEC,OC_FMOVEMFROMSPEC;
|
|
static int OC_FMUL,OC_FSMUL,OC_FDMUL,OC_FSGLMUL,OC_LOAD;
|
|
|
|
static struct {
|
|
int *var;
|
|
const char *name;
|
|
int16_t optype[2];
|
|
} code_tab[] = {
|
|
/* Note: keep same order as in mnemonics table! */
|
|
&OC_ADD, "add", DA,0,
|
|
&OC_ADDA, "adda", 0,0,
|
|
&OC_ADDQ, "addq", 0,AD,
|
|
&OC_ASLI, "asl", QI,0,
|
|
&OC_ASRI, "asr", QI,0,
|
|
&OC_BRA, "bra", 0,0,
|
|
&OC_BSR, "bsr", 0,0,
|
|
&OC_CLR, "clr", 0,0,
|
|
&OC_EXT, "ext", 0,0,
|
|
&OC_FMOVEMTOLIST, "fmovem", MR,FL,
|
|
&OC_FMOVEMFROMSPEC, "fmovem", FS,AM,
|
|
&OC_FMOVEMTOSPEC, "fmovem", MA,FS,
|
|
&OC_FMUL, "fmul", FA,F_,
|
|
&OC_FSMUL, "fsmul", FA,F_,
|
|
&OC_FDMUL, "fdmul", FA,F_,
|
|
&OC_FNOP, "fnop", 0,0,
|
|
&OC_FSGLMUL, "fsglmul",FA,F_,
|
|
&OC_JMP, "jmp", 0,0,
|
|
&OC_JSR, "jsr", 0,0,
|
|
&OC_LEA, "lea", 0,0,
|
|
&OC_LOAD, "load", 0,0,
|
|
&OC_LSLI, "lsl", QI,0,
|
|
&OC_LSRI, "lsr", QI,0,
|
|
&OC_MOV3Q, "mov3q", 0,0,
|
|
&OC_MOVE, "move", DA,AD,
|
|
&OC_MOVEA, "movea", 0,0,
|
|
&OC_MOVEQ, "moveq", 0,0,
|
|
&OC_MVZ, "mvz", 0,0,
|
|
&OC_NEG, "neg", D_,0,
|
|
&OC_NOT, "not", 0,0,
|
|
&OC_PEA, "pea", 0,0,
|
|
&OC_ST, "st", AD,0,
|
|
&OC_SUBA, "suba", 0,0,
|
|
&OC_SUBQ, "subq", 0,AD,
|
|
&OC_TST, "tst", 0,0,
|
|
&OC_NOOP, " no-op", 0,0
|
|
};
|
|
|
|
/* Serveral instruction copies allow optimizations to generate
|
|
additional instructions.
|
|
The ipslot has to be reset to 0, before using copy_instruction(),
|
|
ip_singleop() and ip_doubleop(). */
|
|
#define MAX_IP_COPIES 4
|
|
static int ipslot;
|
|
static instruction newip[MAX_IP_COPIES];
|
|
static operand newop[MAX_IP_COPIES][MAX_OPERANDS];
|
|
|
|
|
|
operand *new_operand(void)
|
|
{
|
|
return mycalloc(sizeof(operand));
|
|
}
|
|
|
|
|
|
static void free_op_exp(operand *op)
|
|
{
|
|
if (op) {
|
|
if (op->value[0])
|
|
free_expr(op->value[0]);
|
|
if (op->value[1])
|
|
free_expr(op->value[1]);
|
|
op->value[0] = op->value[1] = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static void free_operand(operand *op)
|
|
{
|
|
if (op) {
|
|
free_op_exp(op);
|
|
myfree(op);
|
|
}
|
|
}
|
|
|
|
|
|
static operand *clr_operand(operand *op)
|
|
{
|
|
memset(op,0,sizeof(operand));
|
|
return op;
|
|
}
|
|
|
|
|
|
void init_instruction_ext(instruction_ext *ixp)
|
|
{
|
|
ixp->un.real.flags = 0;
|
|
ixp->un.real.last_size = -1;
|
|
ixp->un.real.orig_ext = -1;
|
|
}
|
|
|
|
|
|
static instruction *clr_instruction(instruction *ip)
|
|
{
|
|
memset(ip,0,sizeof(instruction));
|
|
return ip;
|
|
}
|
|
|
|
|
|
static instruction *copy_instruction(instruction *sip)
|
|
/* copy an instruction and its operands */
|
|
{
|
|
instruction *dip = &newip[ipslot];
|
|
operand *dop = newop[ipslot];
|
|
int i;
|
|
|
|
if (ipslot >= MAX_IP_COPIES)
|
|
ierror(0);
|
|
dip->code = sip->code;
|
|
dip->qualifiers[0] = sip->qualifiers[0];
|
|
for (i=0; i<MAX_OPERANDS; i++) {
|
|
if (sip->op[i] != NULL) {
|
|
dip->op[i] = &dop[i];
|
|
*dip->op[i] = *sip->op[i];
|
|
}
|
|
else
|
|
dip->op[i] = NULL;
|
|
}
|
|
dip->ext = sip->ext;
|
|
++ipslot;
|
|
return dip;
|
|
}
|
|
|
|
|
|
int m68k_data_operand(int bits)
|
|
/* return data operand type for these number of bits */
|
|
{
|
|
switch (bits) {
|
|
case 8: return OP_D8;
|
|
case 16: return OP_D16;
|
|
case 32: return OP_D32;
|
|
case 64: return OP_D64;
|
|
case OPSZ_FLOAT|32: return OP_F32;
|
|
case OPSZ_FLOAT|64: return OP_F64;
|
|
case OPSZ_FLOAT|96: return OP_F96;
|
|
}
|
|
cpu_error(38,OPSZ_BITS(bits)); /* data obj. with n bits size are not supp. */
|
|
return 0;
|
|
}
|
|
|
|
|
|
int m68k_available(int idx)
|
|
/* Check if mnemonic is available for selected cpu_type. */
|
|
{
|
|
return (mnemonics[idx].ext.available & cpu_type) != 0;
|
|
}
|
|
|
|
|
|
static int phxass_cpu_num(uint32_t type)
|
|
{
|
|
static int cpus[] = {
|
|
68000,68010,68020,68030,68040,68060
|
|
};
|
|
int i;
|
|
|
|
for (i=5; i>=0; i--)
|
|
if (type & (1L<<i))
|
|
return cpus[i];
|
|
|
|
return 0; /* not a cpu known to PhxAss, like for example ColdFire */
|
|
}
|
|
|
|
|
|
static void set_optc_symbol(void)
|
|
{
|
|
/* set PhxAss __OPTC symbol from current optimization flags */
|
|
taddr optc = 0;
|
|
|
|
if (!no_opt) {
|
|
if (opt_disp && opt_abs && opt_moveq && opt_lea && opt_immaddr)
|
|
optc |= 0x001;
|
|
if (opt_pc)
|
|
optc |= 0x002;
|
|
if (opt_quick && opt_lquick)
|
|
optc |= 0x004;
|
|
if (opt_bra && opt_brajmp)
|
|
optc |= 0x108; /* T is always set together with B */
|
|
if (opt_lsl)
|
|
optc |= 0x010;
|
|
if (opt_pea)
|
|
optc |= 0x020;
|
|
if (opt_clr && opt_st && opt_fconst)
|
|
optc |= 0x040;
|
|
if (opt_gen)
|
|
optc |= 0x080;
|
|
if (opt_movem)
|
|
optc |= 0x200;
|
|
}
|
|
|
|
set_internal_abs(optc_name,optc);
|
|
}
|
|
|
|
|
|
static void set_g2_symbol(void)
|
|
{
|
|
/* Devpac __G2 symbol
|
|
* Bits 0-7: version and features (e.g. 43) - we don't use it!
|
|
* Bits 8-15: cpu type - 68000
|
|
* Bits 16-23: host system - we set all bits to indicate an unknown host
|
|
*/
|
|
set_internal_abs(g2_name,0xff0000|((phxass_cpu_num(cpu_type)-68000)<<8));
|
|
}
|
|
|
|
|
|
static void check_apollo_conflicts(void)
|
|
{
|
|
static int apollo_checks_done = 0;
|
|
|
|
if (!apollo_checks_done) {
|
|
/* When the Apollo cpu type is enabled for the first time, we have
|
|
to make sure that "load" is disabled as a directive. */
|
|
hashdata data;
|
|
|
|
if (find_name_nc(dirhash,mnemonics[OC_LOAD].name,&data)) {
|
|
rem_hashentry(dirhash,mnemonics[OC_LOAD].name,nocase);
|
|
/*cpu_error(63,mnemonics[OC_LOAD].name);*/
|
|
}
|
|
apollo_checks_done = 1;
|
|
}
|
|
}
|
|
|
|
|
|
void cpu_opts(void *opts)
|
|
/* set cpu options for following atoms */
|
|
{
|
|
int cmd = ((optcmd *)opts)->cmd;
|
|
int arg = ((optcmd *)opts)->arg;
|
|
|
|
if (cmd>OCMD_NOOPT && cmd<OCMD_OPTWARN && arg!=0)
|
|
no_opt = 0;
|
|
|
|
switch (cmd) {
|
|
case OCMD_NOP:
|
|
break;
|
|
case OCMD_CPU:
|
|
cpu_type = arg;
|
|
if (phxass_compat) {
|
|
set_internal_abs(cpu_name,phxass_cpu_num(cpu_type));
|
|
set_internal_abs(mmu_name,(cpu_type & mmmu)!=0);
|
|
}
|
|
if (devpac_compat)
|
|
set_g2_symbol();
|
|
set_internal_abs(vasmsym_name,cpu_type&CPUMASK);
|
|
if (cpu_type & apollo)
|
|
check_apollo_conflicts();
|
|
break;
|
|
case OCMD_FPU:
|
|
fpu_id = arg;
|
|
if (phxass_compat)
|
|
set_internal_abs(fpu_name,(cpu_type & mfloat)?fpu_id:0);
|
|
break;
|
|
case OCMD_SDREG:
|
|
sdreg = arg;
|
|
break;
|
|
|
|
case OCMD_NOOPT: no_opt=arg; break;
|
|
case OCMD_OPTGEN: opt_gen=arg; break;
|
|
case OCMD_OPTMOVEM: opt_movem=arg; break;
|
|
case OCMD_OPTPEA: opt_pea=arg; break;
|
|
case OCMD_OPTCLR: opt_clr=arg; break;
|
|
case OCMD_OPTST: opt_st=arg; break;
|
|
case OCMD_OPTLSL: opt_lsl=arg; break;
|
|
case OCMD_OPTMUL: opt_mul=arg; break;
|
|
case OCMD_OPTDIV: opt_div=arg; break;
|
|
case OCMD_OPTFCONST: opt_fconst=arg; break;
|
|
case OCMD_OPTBRAJMP: opt_brajmp=arg; break;
|
|
case OCMD_OPTJBRA: opt_jbra=arg; break;
|
|
case OCMD_OPTPC: opt_pc=arg; break;
|
|
case OCMD_OPTBRA: opt_bra=arg; break;
|
|
case OCMD_OPTDISP: opt_disp=arg; break;
|
|
case OCMD_OPTABS: opt_abs=arg; break;
|
|
case OCMD_OPTMOVEQ: opt_moveq=arg; break;
|
|
case OCMD_OPTQUICK: opt_quick=arg; break;
|
|
case OCMD_OPTBRANOP: opt_branop=arg; break;
|
|
case OCMD_OPTBDISP: opt_bdisp=arg; break;
|
|
case OCMD_OPTODISP: opt_odisp=arg; break;
|
|
case OCMD_OPTLEA: opt_lea=arg; break;
|
|
case OCMD_OPTLQUICK: opt_lquick=arg; break;
|
|
case OCMD_OPTIMMADDR: opt_immaddr=arg; break;
|
|
case OCMD_OPTSPEED: opt_speed=arg; break;
|
|
case OCMD_SMALLCODE: opt_sc=arg; break;
|
|
case OCMD_SMALLDATA: opt_sd=arg; break;
|
|
|
|
case OCMD_OPTWARN: warn_opts=arg; break;
|
|
case OCMD_CHKPIC: pic_check=arg; break;
|
|
case OCMD_CHKTYPE: typechk=arg; break;
|
|
case OCMD_NOWARN: no_warn=arg; break;
|
|
|
|
default: ierror(0); break;
|
|
}
|
|
}
|
|
|
|
|
|
static void add_cpu_opt(section *s,int cmd,int arg)
|
|
{
|
|
if (s || current_section) {
|
|
optcmd *new = mymalloc(sizeof(optcmd));
|
|
|
|
new->cmd = cmd;
|
|
new->arg = arg;
|
|
add_atom(s,new_opts_atom(new));
|
|
cpu_opts(new);
|
|
}
|
|
else {
|
|
/* no section known at this point, so set the option immediately: it will
|
|
automatically become the initial option of the first section */
|
|
optcmd o;
|
|
|
|
o.cmd = cmd;
|
|
o.arg = arg;
|
|
cpu_opts(&o);
|
|
}
|
|
}
|
|
|
|
|
|
static void cpu_opts_optinit(section *s)
|
|
/* create initial optimization atoms */
|
|
{
|
|
add_cpu_opt(s,OCMD_NOOPT,no_opt);
|
|
add_cpu_opt(s,OCMD_OPTGEN,opt_gen);
|
|
add_cpu_opt(s,OCMD_OPTMOVEM,opt_movem);
|
|
add_cpu_opt(s,OCMD_OPTPEA,opt_pea);
|
|
add_cpu_opt(s,OCMD_OPTCLR,opt_clr);
|
|
add_cpu_opt(s,OCMD_OPTST,opt_st);
|
|
add_cpu_opt(s,OCMD_OPTLSL,opt_lsl);
|
|
add_cpu_opt(s,OCMD_OPTMUL,opt_mul);
|
|
add_cpu_opt(s,OCMD_OPTDIV,opt_div);
|
|
add_cpu_opt(s,OCMD_OPTFCONST,opt_fconst);
|
|
add_cpu_opt(s,OCMD_OPTBRAJMP,opt_brajmp);
|
|
add_cpu_opt(s,OCMD_OPTJBRA,opt_jbra);
|
|
add_cpu_opt(s,OCMD_OPTPC,opt_pc);
|
|
add_cpu_opt(s,OCMD_OPTBRA,opt_bra);
|
|
add_cpu_opt(s,OCMD_OPTDISP,opt_disp);
|
|
add_cpu_opt(s,OCMD_OPTABS,opt_abs);
|
|
add_cpu_opt(s,OCMD_OPTMOVEQ,opt_moveq);
|
|
add_cpu_opt(s,OCMD_OPTQUICK,opt_quick);
|
|
add_cpu_opt(s,OCMD_OPTBRANOP,opt_branop);
|
|
add_cpu_opt(s,OCMD_OPTBDISP,opt_bdisp);
|
|
add_cpu_opt(s,OCMD_OPTODISP,opt_odisp);
|
|
add_cpu_opt(s,OCMD_OPTLEA,opt_lea);
|
|
add_cpu_opt(s,OCMD_OPTLQUICK,opt_lquick);
|
|
add_cpu_opt(s,OCMD_OPTIMMADDR,opt_immaddr);
|
|
add_cpu_opt(s,OCMD_OPTSPEED,opt_speed);
|
|
add_cpu_opt(s,OCMD_SMALLCODE,opt_sc);
|
|
add_cpu_opt(s,OCMD_SMALLDATA,opt_sd);
|
|
|
|
if (phxass_compat)
|
|
set_optc_symbol();
|
|
}
|
|
|
|
|
|
void cpu_opts_init(section *s)
|
|
/* set initial cpu opts */
|
|
{
|
|
add_cpu_opt(s,OCMD_CPU,cpu_type);
|
|
add_cpu_opt(s,OCMD_FPU,fpu_id);
|
|
add_cpu_opt(s,OCMD_SDREG,sdreg);
|
|
cpu_opts_optinit(s);
|
|
add_cpu_opt(s,OCMD_OPTWARN,warn_opts);
|
|
add_cpu_opt(s,OCMD_CHKPIC,pic_check);
|
|
add_cpu_opt(s,OCMD_CHKTYPE,typechk);
|
|
add_cpu_opt(s,OCMD_NOWARN,no_warn);
|
|
}
|
|
|
|
|
|
void print_cpu_opts(FILE *f,void *opts)
|
|
{
|
|
static const char *ocmds[] = {
|
|
"opt generic","opt movem","opt pea","opt clr","opt st","opt lsl",
|
|
"opt mul","opt div","opt float const","opt branch to jump",
|
|
"opt jump to longbranch","opt pc-relative","opt branch",
|
|
"opt displacement","opt absolute","opt moveq","opt quick",
|
|
"opt branch to nop","opt base disp","opt outer disp",
|
|
"opt adda/subq to lea","opt lea to addq/subq","opt immediate areg",
|
|
"opt for speed","opt small code","opt small data",
|
|
"warn about optimizations","PIC check","type and range checks",
|
|
"hide all warnings"
|
|
};
|
|
static const char *cpus[32] = {
|
|
"m68000","m68010","m68020","m68030","m68040","m68060",
|
|
"m6888x","m68851","cpu32",
|
|
"CF-ISA_A","CF-ISA_A+","CF-ISA_B","CF-ISA_C",
|
|
"CF-hwdiv","CF-MAC","CF-EMAC","CF-USP","CF-FPU","CF-MMU",NULL,
|
|
"ac68080",
|
|
NULL
|
|
};
|
|
int cmd = ((optcmd *)opts)->cmd;
|
|
int arg = ((optcmd *)opts)->arg;
|
|
|
|
fprintf(f,"opts: ");
|
|
if (cmd == OCMD_CPU) {
|
|
int i;
|
|
|
|
arg &= CPUMASK;
|
|
fprintf(f,"cpu types:");
|
|
for (i=0; i<32; i++) {
|
|
if ((arg & (1<<i)) && cpus[i])
|
|
fprintf(f," %s",cpus[i]);
|
|
}
|
|
}
|
|
else if (cmd == OCMD_NOP)
|
|
fprintf(f,"none");
|
|
else if (cmd == OCMD_FPU)
|
|
fprintf(f,"fpu id %d (f%xxx)",arg,(unsigned)arg<<1);
|
|
else if (cmd == OCMD_SDREG) {
|
|
if (arg >= 0)
|
|
fprintf(f,"small data base reg is a%d",arg);
|
|
else
|
|
fprintf(f,"small data is disabled");
|
|
}
|
|
else if (cmd == OCMD_NOOPT)
|
|
fprintf(f,"optimizations %sabled",arg?"dis":"en");
|
|
else
|
|
fprintf(f,"%s (%d)",ocmds[cmd-OCMD_OPTGEN],arg);
|
|
}
|
|
|
|
|
|
static void conv2packed(unsigned char *buf,long double f)
|
|
{
|
|
/* @@@@@@@ to be implemented */
|
|
}
|
|
|
|
|
|
static taddr reverse(uint32_t v,int size)
|
|
/* reverse bit-order in v */
|
|
{
|
|
int i;
|
|
uint32_t r = 0;
|
|
|
|
for (i=0; i<size; i++) {
|
|
r <<= 1;
|
|
if (v & (1L<<i))
|
|
r++;
|
|
}
|
|
return (taddr)r;
|
|
}
|
|
|
|
|
|
static int cntones(taddr v,int bits)
|
|
/* count number of bits set */
|
|
{
|
|
int i,r;
|
|
|
|
for (i=0,r=0; i<bits; i++) {
|
|
r += v & 1;
|
|
v >>= 1;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
static int bfffo(taddr v,int start,int bits)
|
|
/* return index of first bit set */
|
|
{
|
|
int i;
|
|
|
|
for (i=start; i<bits; i++) {
|
|
if (v & (1<<i))
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
static int getextcode(char c)
|
|
{
|
|
switch (tolower((unsigned char)c)) {
|
|
case 'b':
|
|
return EXT_BYTE;
|
|
case 'w':
|
|
return EXT_WORD;
|
|
case 'l':
|
|
return EXT_LONG;
|
|
case 's':
|
|
return EXT_SINGLE;
|
|
case 'd':
|
|
return EXT_DOUBLE;
|
|
case 'x':
|
|
return EXT_EXTENDED;
|
|
case 'p':
|
|
return EXT_PACKED;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int getmacextcode(char c)
|
|
{
|
|
switch (tolower((unsigned char)c)) {
|
|
case 'l':
|
|
return EXT_LOWER;
|
|
case 'u':
|
|
return EXT_UPPER;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int read_extension(char **s,int extcode)
|
|
{
|
|
char *p = *s;
|
|
|
|
if (*p++ == '.') {
|
|
int x = getextcode(*p++);
|
|
|
|
if (x) {
|
|
extcode = x;
|
|
*s = p;
|
|
}
|
|
}
|
|
return extcode;
|
|
}
|
|
|
|
|
|
static int is_float_ext(void)
|
|
{
|
|
switch (current_ext) {
|
|
case 's':
|
|
case 'd':
|
|
case 'x':
|
|
case 'p':
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static uint16_t lc_ext_to_size(char ext)
|
|
/* convert lower-case extension character to a SIZE_xxx code */
|
|
{
|
|
switch (ext) {
|
|
case 'b': return SIZE_BYTE;
|
|
case 'w': return SIZE_WORD;
|
|
case 'l': return SIZE_LONG;
|
|
case 's': return SIZE_SINGLE;
|
|
case 'd': return SIZE_DOUBLE;
|
|
case 'x': return SIZE_EXTENDED;
|
|
case 'p': return SIZE_PACKED;
|
|
case 'q': return SIZE_DOUBLE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int branch_size(char ext)
|
|
/* branch size for each size-extension code */
|
|
{
|
|
switch (ext) {
|
|
case 'b':
|
|
case 's':
|
|
return 0;
|
|
case 'l':
|
|
return 4;
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
|
|
static signed char getreg(char **start,int indexreg)
|
|
/* checks stream for a data or address register (might include extension)
|
|
return -1 on failure, otherwise return a bit-field containing
|
|
the following information:
|
|
Bit7 6 5 4 3 2 1 0
|
|
---------------------------------------------
|
|
| 0 | extension (1-7) | An reg. | reg. number |
|
|
--------------------------------------------- */
|
|
{
|
|
char *s = *start;
|
|
signed char reg = -1;
|
|
|
|
if (ISIDSTART(*s) || (elfregs && *s=='%')) {
|
|
char *p = s++;
|
|
regsym *sym;
|
|
|
|
while (ISIDCHAR(*s) && *s!='.')
|
|
s++;
|
|
|
|
if ((sym = find_regsym(p,s-p)) != NULL) {
|
|
/* register symbol found */
|
|
if (sym->reg_type==RSTYPE_Dn || sym->reg_type==RSTYPE_An)
|
|
reg = ((sym->reg_type==RSTYPE_An) ? REGAn : 0)
|
|
| (signed char)sym->reg_num;
|
|
}
|
|
else {
|
|
if (elfregs) {
|
|
if (*p == '%')
|
|
p++;
|
|
else
|
|
return -1;
|
|
}
|
|
if (s-p == 2) {
|
|
if ((*p=='D' || *p=='d' || *p=='A' || *p=='a') &&
|
|
(*(p+1)>='0' && *(p+1)<='7'))
|
|
reg = ((*p=='A' || *p=='a') ? REGAn : 0) | (*(p+1) - '0');
|
|
else if ((*p=='S' || *p=='s') && (*(p+1)=='P' || *(p+1)=='p'))
|
|
reg = REGAn + 7;
|
|
}
|
|
}
|
|
|
|
if (reg >= 0) {
|
|
if (*s == '.') {
|
|
int extcode;
|
|
|
|
if (!indexreg) {
|
|
/* ColdFire MAC register extension? */
|
|
extcode = getmacextcode(*(s+1));
|
|
}
|
|
else {
|
|
/* index register extension */
|
|
extcode = getextcode(*(s+1));
|
|
}
|
|
if (extcode) {
|
|
reg |= extcode << 4;
|
|
*start = s + 2;
|
|
}
|
|
}
|
|
else
|
|
*start = s;
|
|
}
|
|
}
|
|
return reg;
|
|
}
|
|
|
|
|
|
static uint16_t scan_Rnlist(char **start)
|
|
/* returns bit field for a Dn/An register list
|
|
returns 0 otherwise */
|
|
{
|
|
char *p = *start;
|
|
|
|
if (getreg(&p,0) >= 0) {
|
|
uint16_t list = 0;
|
|
signed char lastreg=-1,reg,rx;
|
|
int rangemode = 0;
|
|
|
|
for (p=*start; *p; p++) {
|
|
p = skip(p);
|
|
if (rangemode && (*p>='0' && *p<='7')) { /* allows d0-7 */
|
|
reg = (lastreg & REGAn) | (*p - '0');
|
|
p++;
|
|
}
|
|
else if ((reg = getreg(&p,0)) < 0) {
|
|
cpu_error(2); /* invalid register list */
|
|
return 0;
|
|
}
|
|
|
|
if (rangemode) {
|
|
/* lastreg...reg describes a range of registers */
|
|
list &= ~(1<<lastreg);
|
|
if (lastreg > reg) {
|
|
rx = reg;
|
|
reg = lastreg;
|
|
lastreg = rx;
|
|
}
|
|
else if (lastreg == reg)
|
|
cpu_error(17); /* Rn-Rn */
|
|
for (rx=lastreg; rx<=reg; rx++) {
|
|
if (list & (1<<rx))
|
|
cpu_error(17); /* double register in list */
|
|
else
|
|
list |= 1<<rx;
|
|
}
|
|
rangemode = 0;
|
|
}
|
|
else {
|
|
if (list & (1<<reg))
|
|
cpu_error(17); /* double register in list */
|
|
else
|
|
list |= 1<<reg;
|
|
}
|
|
|
|
lastreg = reg;
|
|
p = skip(p);
|
|
if (*p == '-')
|
|
rangemode = 1;
|
|
else if (*p != '/')
|
|
break;
|
|
}
|
|
*start = p;
|
|
return list;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static signed char getbreg(char **start)
|
|
{
|
|
char *s = *start;
|
|
signed char reg = -1;
|
|
|
|
if ((cpu_type & apollo) && (ISIDSTART(*s) || (elfregs && *s=='%'))) {
|
|
char *p = s++;
|
|
regsym *sym;
|
|
|
|
while (ISIDCHAR(*s) && *s!='.')
|
|
s++;
|
|
|
|
if ((sym = find_regsym(p,s-p)) != NULL) {
|
|
/* register symbol found */
|
|
if (sym->reg_type==RSTYPE_Bn)
|
|
reg = (signed char)sym->reg_num;
|
|
}
|
|
else {
|
|
if (elfregs) {
|
|
if (*p == '%')
|
|
p++;
|
|
else
|
|
return -1;
|
|
}
|
|
if (s-p == 2 && (*p=='B' || *p=='b') && (*(p+1)>='0' && *(p+1)<='7'))
|
|
reg = *(p+1) - '0';
|
|
}
|
|
if (reg >= 0)
|
|
*start = s;
|
|
}
|
|
return reg;
|
|
}
|
|
|
|
|
|
static signed char getfreg(char **start)
|
|
/* checks stream for an FPU register
|
|
return -1 on failure, 0-7 for FP0-FP7 and
|
|
10 for FPIAR, 11 for FPSR and 12 for FPCR */
|
|
{
|
|
char *s = *start;
|
|
signed char reg = -1;
|
|
|
|
if ((cpu_type & (mfloat|mcffpu)) && (ISIDSTART(*s) || (elfregs && *s=='%'))) {
|
|
char *p = s++;
|
|
regsym *sym;
|
|
|
|
while (ISIDCHAR(*s) && *s!='.')
|
|
s++;
|
|
|
|
if ((sym = find_regsym(p,s-p)) != NULL) {
|
|
/* register symbol found */
|
|
if (sym->reg_type==RSTYPE_FPn)
|
|
reg = (signed char)sym->reg_num;
|
|
}
|
|
else {
|
|
if (elfregs) {
|
|
if (*p == '%')
|
|
p++;
|
|
else
|
|
return -1;
|
|
}
|
|
if (s-p==3 && !strnicmp(p,"FP",2) && (*(p+2)>='0' && *(p+2)<='7'))
|
|
reg = *(p+2) - '0';
|
|
else if (s-p==5 && !strnicmp(p,"FPIAR",5))
|
|
reg = 10;
|
|
else if (s-p==4 && !strnicmp(p,"FPSR",4))
|
|
reg = 11;
|
|
else if (s-p==4 && !strnicmp(p,"FPCR",4))
|
|
reg = 12;
|
|
}
|
|
if (reg >= 0)
|
|
*start = s;
|
|
}
|
|
return reg;
|
|
}
|
|
|
|
|
|
static uint16_t scan_FPnlist(char **start)
|
|
/* returns bit field for a FPn or FPIAR/FPSR/FPCR register list
|
|
returns 0 otherwise */
|
|
{
|
|
char *p = *start;
|
|
|
|
if (getfreg(&p) >= 0) {
|
|
signed char lastreg=-1,reg,rx;
|
|
uint16_t list = 0;
|
|
int fpnmode = -1;
|
|
int rangemode = 0;
|
|
|
|
for (p=*start; *p; p++) {
|
|
p = skip(p);
|
|
if (rangemode && fpnmode && (*p>='0' && *p<='7')) { /* allows fp0-7 */
|
|
reg = *p++ - '0';
|
|
}
|
|
else if ((reg = getfreg(&p)) < 0) {
|
|
cpu_error(2); /* invalid register list */
|
|
return 0;
|
|
}
|
|
|
|
if (fpnmode < 0) {
|
|
fpnmode = (reg > 7) ? 0 : 1;
|
|
}
|
|
else {
|
|
/* disallow mixing of fp0-fp7 and fpiar/fpsr/fpcr lists */
|
|
if ((reg<=7 && !fpnmode) || (reg>7 && fpnmode)) {
|
|
cpu_error(2); /* invalid register list */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (fpnmode) {
|
|
if (rangemode) {
|
|
/* lastreg...reg describes a range of registers */
|
|
if (lastreg > reg) {
|
|
rx = reg;
|
|
reg = lastreg;
|
|
lastreg = rx;
|
|
}
|
|
for (rx=lastreg; rx<=reg; rx++)
|
|
list |= 1<<(7-rx);
|
|
rangemode = 0;
|
|
}
|
|
else
|
|
list |= 1<<(7-reg);
|
|
}
|
|
else
|
|
list |= 1<<reg;
|
|
|
|
lastreg = reg;
|
|
p = skip(p);
|
|
if (*p == '-') {
|
|
if (!fpnmode)
|
|
cpu_error(2); /* invalid register list */
|
|
else
|
|
rangemode = 1;
|
|
}
|
|
else if (*p != '/')
|
|
break;
|
|
}
|
|
*start = p;
|
|
return list;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static char *getspecreg(char *s,operand *op,int first,int last)
|
|
{
|
|
char *name = NULL;
|
|
|
|
if ((*s=='<' && *(s+1)=='<') || (*s=='>' && *(s+1)=='>')) {
|
|
/* ColdFire MAC scale factor << or >>, treated as special reg. name */
|
|
name = s;
|
|
s += 2;
|
|
}
|
|
else {
|
|
if (elfregs) {
|
|
if (*s++ != '%')
|
|
return NULL;
|
|
}
|
|
if (ISIDSTART(*s)) {
|
|
name = s++;
|
|
while (ISIDCHAR(*s))
|
|
s++;
|
|
}
|
|
}
|
|
|
|
if (name) {
|
|
int i,len;
|
|
|
|
for (i=first,len=s-name; i<=last; i++) {
|
|
if (!strnicmp(name,SpecRegs[i].name,len)) {
|
|
if (SpecRegs[i].name[len] != '\0')
|
|
continue;
|
|
if (!(SpecRegs[i].available & cpu_type))
|
|
break; /* not available for current CPU */
|
|
op->mode = MODE_SpecReg;
|
|
op->reg = i; /* @@@ Warning: indexes 128-255 are stored negative! */
|
|
op->value[0] = number_expr(SpecRegs[i].code);
|
|
return s;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int get_any_register(char **start,operand *op,int required)
|
|
/* checks for Dn, An, register lists and any other special register;
|
|
fill op->mode, op->register, op->value[0] accordingly when successful
|
|
and return with != 0 */
|
|
{
|
|
char *s = *start;
|
|
signed char reg;
|
|
struct optype *ot = &optypes[required];
|
|
|
|
if ((reg = getreg(start,0)) >= 0) {
|
|
/* Dn or An */
|
|
char *p = skip(*start);
|
|
|
|
op->mode = REGisAn(reg) ? MODE_An : MODE_Dn;
|
|
op->reg = REGget(reg);
|
|
|
|
if (ot->flags & OTF_VXRNG4) {
|
|
/* Apollo AMMX four-vector-registers range */
|
|
if (!REGisAn(reg) && *p=='-') {
|
|
*start = skip(p+1);
|
|
if (!(reg&3) && getreg(start,0)==reg+3)
|
|
return 1; /* D0-D3 or D4-D7 */
|
|
}
|
|
*start = s;
|
|
return 0;
|
|
}
|
|
else if (*p=='-' || *p=='/' || (ot->flags & OTF_REGLIST)) {
|
|
/* it's a register list */
|
|
op->mode = MODE_Extended;
|
|
op->reg = REG_RnList;
|
|
*start = s;
|
|
op->value[0] = number_expr((taddr)scan_Rnlist(start));
|
|
}
|
|
else {
|
|
unsigned char sf = reg >> 4;
|
|
|
|
if (sf==EXT_UPPER || sf==EXT_LOWER || (ot->flags & FL_MAC)) {
|
|
/* ColdFire MAC U/L register extension found, store in bf_offset */
|
|
op->flags |= FL_MAC;
|
|
op->bf_offset = sf == EXT_UPPER;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
else if ((reg = getfreg(start)) >= 0) {
|
|
/* FPn */
|
|
char *p = skip(*start);
|
|
|
|
if (*p=='-' || *p=='/' || (ot->flags & OTF_REGLIST)) {
|
|
/* it's a register list */
|
|
uint16_t lst;
|
|
|
|
op->mode = MODE_Extended;
|
|
op->reg = REG_FPnList;
|
|
*start = s;
|
|
lst = scan_FPnlist(start);
|
|
if (reg >= 10)
|
|
op->flags |= FL_FPSpec; /* fpiar/fpcr/fpsr list */
|
|
op->value[0] = number_expr((taddr)lst);
|
|
}
|
|
else {
|
|
op->mode = MODE_FPn;
|
|
if (reg >= 10) {
|
|
/* fpiar,fpsr,fpcr */
|
|
switch (reg) {
|
|
case 10: op->reg = 1; break;
|
|
case 11: op->reg = 2; break;
|
|
case 12: op->reg = 4; break;
|
|
default: ierror(0); break;
|
|
}
|
|
op->flags |= FL_FPSpec;
|
|
}
|
|
else
|
|
op->reg = reg;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
else if ((reg = getbreg(start)) >= 0) {
|
|
/* Bn (Apollo only) */
|
|
op->mode = MODE_An;
|
|
op->reg = REGget(reg);
|
|
op->flags |= FL_BnReg;
|
|
return 1;
|
|
}
|
|
|
|
else /*if (ot->flags & OTF_SPECREG)*/ {
|
|
/* check, if it's one of our special register symbols (CCR,SR, etc.) */
|
|
int first,last;
|
|
|
|
if (ot->flags & OTF_SRRANGE) {
|
|
first = ot->first;
|
|
last = ot->last;
|
|
}
|
|
else {
|
|
first = 0;
|
|
last = specreg_cnt - 1;
|
|
}
|
|
|
|
if (s = getspecreg(s,op,first,last)) {
|
|
if (ot->flags & OTF_VXRNG4) {
|
|
/* need a four-vector-register range, E0-E3, E20-E23, etc. */
|
|
int vxreg = (unsigned char)op->reg;
|
|
operand dummy;
|
|
|
|
s = skip(s);
|
|
if (*s=='-' && !(SpecRegs[vxreg].code&3)) {
|
|
s = skip(s+1);
|
|
if ((s = getspecreg(s,&dummy,vxreg+3,vxreg+3)) == NULL)
|
|
return 0;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
*start = s;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static short getbasereg(char **start)
|
|
/* returns any register which could be a base or index register,
|
|
including an optional extension and a scaling factor,
|
|
like d0-d7,a0-a7,b0-b7,pc,zd0,zd7,za0-za7,zpc
|
|
Bits 0-4:
|
|
0 - 7 = d0-d7
|
|
8 - 15 = a0-a7
|
|
this means Bit 3 identifies an address register
|
|
16 = pc
|
|
Bit 5 = treat a0-a7 as b0-b7 (Apollo Core only)
|
|
Bit 7 = Zero-flag (for suppressed registers: zdn,zan,zpc)
|
|
Bits 8-10 = [REGext_Shift] optional extension (1=.b, 2=.w ... 7=.p)
|
|
Bits 12-13 = [REGscale_Shift] scale factor (0=*1, 1=*2, 2=*4, 3=*8)
|
|
returns -1 when no valid register was found */
|
|
{
|
|
char *s = *start;
|
|
short r = 0;
|
|
|
|
if (ISIDSTART(*s) || (elfregs && *s=='%')) {
|
|
char *p = s++;
|
|
|
|
while (ISIDCHAR(*s) && *s!='.')
|
|
s++;
|
|
if (elfregs) {
|
|
if (*p == '%')
|
|
p++;
|
|
else
|
|
r = -1;
|
|
}
|
|
|
|
if ((s-p)==3 && (*p=='z' || *p=='Z')) {
|
|
r |= REGZero;
|
|
p++;
|
|
}
|
|
|
|
if ((s-p) == 2) {
|
|
if ((*p=='D' || *p=='d' || *p=='A' || *p=='a') &&
|
|
(*(p+1)>='0' && *(p+1)<='7'))
|
|
r |= ((*p=='A' || *p=='a') ? REGAn : 0) | (short)(*(p+1) - '0');
|
|
else if ((*p=='S' || *p=='s') && (*(p+1)=='P' || *(p+1)=='p'))
|
|
r |= REGAn+7;
|
|
else if ((*p=='P' || *p=='p') && (*(p+1)=='C' || *(p+1)=='c'))
|
|
r |= REGPC;
|
|
else if ((cpu_type & apollo) && (*p=='B' || *p=='b') &&
|
|
(*(p+1)>='0' && *(p+1)<='7'))
|
|
r |= (short)(*(p+1) - '0') | REGBn | REGAn;
|
|
else
|
|
r = -1;
|
|
}
|
|
else
|
|
r = -1;
|
|
|
|
if (r < 0) {
|
|
regsym *sym;
|
|
|
|
if ((sym = find_regsym(p,s-p)) != NULL) {
|
|
/* register symbol found */
|
|
if (sym->reg_type==RSTYPE_Dn || sym->reg_type==RSTYPE_An ||
|
|
((cpu_type&apollo) && sym->reg_type==RSTYPE_Bn)) {
|
|
r = ((sym->reg_type==RSTYPE_Dn) ? 0 : REGAn)
|
|
| (signed char)sym->reg_num;
|
|
if (sym->reg_type == RSTYPE_Bn)
|
|
r |= REGBn;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (r >= 0) {
|
|
if (*s == '.') { /* read size extension */
|
|
int extcode = getextcode(*(s+1));
|
|
|
|
if (extcode) {
|
|
r |= extcode << REGext_Shift;
|
|
s += 2;
|
|
}
|
|
}
|
|
|
|
if (*s == '*') { /* read scale factor */
|
|
switch (*(s+1)) {
|
|
case '1':
|
|
break;
|
|
case '2':
|
|
r |= 1 << REGscale_Shift;
|
|
break;
|
|
case '4':
|
|
r |= 2 << REGscale_Shift;
|
|
break;
|
|
case '8':
|
|
r |= 3 << REGscale_Shift;
|
|
break;
|
|
default:
|
|
cpu_error(10); /* illegal scale factor */
|
|
break;
|
|
}
|
|
s += 2;
|
|
}
|
|
|
|
*start = s;
|
|
return r;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static void set_index(operand *op,short i)
|
|
/* fill index register, including size and scale, into format word */
|
|
{
|
|
if (i >= 0) {
|
|
unsigned s = REGscale(i);
|
|
|
|
op->flags |= FL_UsesFormat;
|
|
|
|
if (REGisZero(i)) {
|
|
op->format |= FW_FullFormat | FW_IndexSuppress;
|
|
op->flags |= FL_020up;
|
|
}
|
|
else if (s) {
|
|
/* ColdFire allows scale factors *2 and *4 (*8 when FPU is present),
|
|
otherwise 68020+ is required. */
|
|
if (!(cpu_type & mcf) || (s > 2 && !(cpu_type & mcffpu)))
|
|
op->flags |= FL_020up;
|
|
}
|
|
|
|
op->format |= (REGisAn(i) ? FW_IndexAn : 0) |
|
|
FW_IndexReg(i) |
|
|
((REGext(i)==EXT_LONG) ? FW_LongIndex : 0) |
|
|
FW_Scale(s);
|
|
}
|
|
}
|
|
|
|
|
|
static taddr getbfk(char **p,int *dflag)
|
|
/* get a bit field specifier {Dm:Dn}/{m:n} or a k-factor {#n}/{Dn},
|
|
dflag is set for a data register. */
|
|
{
|
|
signed char reg = getreg(p,0);
|
|
|
|
if (reg >= 0) {
|
|
*dflag = 1;
|
|
if (REGisDn(reg))
|
|
return 0x80 + REGget(reg);
|
|
else
|
|
cpu_error(18); /* data register required */
|
|
}
|
|
else {
|
|
*dflag = 0;
|
|
if (**p == '#') /* skip for k-factor and gcc-syntax */
|
|
*p += 1;
|
|
return parse_constexpr(p);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void check_basereg(operand *op)
|
|
/* Check if the operand's address register matches one of the currently
|
|
active BASEREG registers and automatically subtract its base-expression
|
|
from the operand's displacement value. */
|
|
{
|
|
if (op->reg>=0 && op->reg<=6 && baseexp[op->reg] && op->value[0]) {
|
|
if (find_base(op->value[0],NULL,NULL,0) == BASE_OK) {
|
|
expr *new = make_expr(SUB,op->value[0],copy_tree(baseexp[op->reg]));
|
|
|
|
simplify_expr(new);
|
|
op->value[0] = new;
|
|
op->flags |= FL_BaseReg; /* mark potential BASEREG expression */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int fix_basereg(operand *op,int final)
|
|
/* Check whether the left side of the "<exp> - <base>" expression is still
|
|
undefined. When it became a constant expression in the meantime, then
|
|
do not treat it any longer as a BASEREG operand. */
|
|
{
|
|
taddr val;
|
|
expr *left;
|
|
|
|
if (op->value[0]!=NULL && (left=op->value[0]->left)!=NULL) {
|
|
if (eval_expr(left,&val,NULL,0)) {
|
|
/* Kill the subtrahend of the base-relative expression. */
|
|
if (final) {
|
|
op->value[0]->left = NULL;
|
|
free_expr(op->value[0]);
|
|
}
|
|
op->value[0] = left;
|
|
op->flags &= ~FL_BaseReg;
|
|
return 1; /* fixed */
|
|
}
|
|
}
|
|
return 0; /* nothing changed */
|
|
}
|
|
|
|
|
|
static char *parse_immediate(char *start,operand *op,int is_float,int is_quad)
|
|
/* Parse an immediate operand of unknown size, which can be byte, word,
|
|
long, quadword, single-, double-, extended-precision float or packed.
|
|
is_float: Decimal constants are parsed as floating point. Hex, octal or
|
|
binary contants directly into IEEE format.
|
|
is_quad: 64-bit expressions are converted into thuge type, not taddr,
|
|
so they don't allow labels and relocations. */
|
|
{
|
|
if (is_float)
|
|
op->value[0] = parse_expr_float(&start);
|
|
else if (is_quad)
|
|
op->value[0] = parse_expr_huge(&start);
|
|
else
|
|
op->value[0] = parse_expr(&start);
|
|
return start;
|
|
}
|
|
|
|
|
|
static int base_disp_and_ext(operand *op,char **p)
|
|
/* Parse expression to value[0] and return the displacement-extension when
|
|
given (or 0 when missing). Returns -1 when no valid expression was found. */
|
|
{
|
|
if ((op->value[0] = parse_expr(p)) != NULL) {
|
|
int disp_size;
|
|
|
|
if ((disp_size = read_extension(p,0)) != 0)
|
|
op->flags |= FL_NoOptBase; /* do not optimize, when size is given */
|
|
return disp_size;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
int parse_operand(char *p,int len,operand *op,int required)
|
|
{
|
|
uint16_t reqmode = optypes[required].modes;
|
|
uint32_t reqflags = optypes[required].flags;
|
|
char *start = p;
|
|
int i;
|
|
|
|
op->mode = op->reg = -1;
|
|
op->flags = 0;
|
|
op->format = 0;
|
|
op->value[0] = op->value[1] = NULL;
|
|
p = skip(p);
|
|
if (convert_brackets && !(cpu_type & (m68020up|cpu32|mcf))) {
|
|
char c,*p2=p;
|
|
|
|
while ((c = *p2) != '\0') {
|
|
if (c == '[')
|
|
*p2 = '(';
|
|
else if (c== ']')
|
|
*p2 = ')';
|
|
p2++;
|
|
}
|
|
}
|
|
|
|
if (reqflags & OTF_DATA) {
|
|
/* a data definition */
|
|
op->mode = MODE_Extended;
|
|
op->reg = REG_Immediate;
|
|
p = parse_immediate(p,op,(reqflags&OTF_FLTIMM)!=0,
|
|
(reqflags&OTF_QUADIMM)!=0);
|
|
}
|
|
else if (*p=='#' || (sgs && *p=='&')) {
|
|
/* immediate addressing mode */
|
|
p++;
|
|
op->mode = MODE_Extended;
|
|
op->reg = REG_Immediate;
|
|
p = parse_immediate(p,op,(reqflags&OTF_FLTIMM)!=0 && is_float_ext(),
|
|
(reqflags&OTF_QUADIMM)!=0);
|
|
}
|
|
else {
|
|
if (get_any_register(&p,op,required)) {
|
|
char *ptmp = skip(p);
|
|
|
|
if (*ptmp == ':') {
|
|
/* possible register-pair definition */
|
|
signed char reg;
|
|
|
|
ptmp = skip(ptmp+1);
|
|
|
|
if ((cpu_type&apollo) &&
|
|
(op->mode==MODE_Dn || op->mode==MODE_An || op->mode==MODE_SpecReg)
|
|
&& !(op->flags&FL_BnReg)) {
|
|
if (reqflags & OTF_VXRNG2) {
|
|
if (op->mode==MODE_Dn && !(op->reg&1)) {
|
|
/* Apollo: Dn:Dn+1 (AMMX) */
|
|
reg = getreg(&ptmp,0);
|
|
if (reg == op->reg+1)
|
|
p = ptmp;
|
|
}
|
|
else if (op->mode == MODE_SpecReg) {
|
|
/* Apollo: En:En+1 (AMMX) */
|
|
int vxreg = (unsigned char)op->reg;
|
|
operand dummy;
|
|
|
|
if (!(SpecRegs[vxreg].code&1) &&
|
|
(ptmp = getspecreg(ptmp,&dummy,vxreg+1,vxreg+1)))
|
|
p = ptmp;
|
|
}
|
|
}
|
|
else if (op->mode != MODE_SpecReg) {
|
|
/* Apollo: Rm:Rn */
|
|
reg = getreg(&ptmp,0);
|
|
if (reg >= 0) {
|
|
if (op->mode == MODE_An) {
|
|
op->mode = MODE_Dn; /* make it appear as Dn/DoubleReg mode */
|
|
op->reg |= REGAn; /* restore An-bit for Rm */
|
|
}
|
|
op->reg |= reg << 4; /* insert Rn with 4 bits too */
|
|
op->flags |= FL_DoubleReg;
|
|
p = ptmp;
|
|
}
|
|
else
|
|
cpu_error(44); /* register expected */
|
|
}
|
|
}
|
|
else if (op->mode == MODE_Dn) {
|
|
/* Dm:Dn expected */
|
|
reg = getreg(&ptmp,0);
|
|
if (reg>=0 && REGisDn(reg)) {
|
|
op->reg |= reg << 4;
|
|
op->flags |= FL_DoubleReg | FL_020up;
|
|
p = ptmp;
|
|
}
|
|
else
|
|
cpu_error(18); /* data register required */
|
|
}
|
|
else if (op->mode == MODE_FPn) {
|
|
/* FPm:FPn expected */
|
|
reg = getfreg(&ptmp);
|
|
if (reg>=0 && reg<=7) {
|
|
op->reg |= reg << 4;
|
|
op->flags |= FL_DoubleReg;
|
|
p = ptmp;
|
|
}
|
|
else
|
|
cpu_error(42); /* FP register required */
|
|
}
|
|
}
|
|
p = skip(p);
|
|
}
|
|
|
|
else {
|
|
/* no direct register */
|
|
int disp_size = 0;
|
|
int od_size;
|
|
int base;
|
|
char *start_term = NULL;
|
|
|
|
if (*p=='-' && *(p+1)=='(') {
|
|
char *ptmp = skip(p+2);
|
|
signed char reg;
|
|
|
|
reg = getreg(&ptmp,0);
|
|
if (reg<0 && (cpu_type&apollo)) {
|
|
if ((reg = getbreg(&ptmp)) >= 0)
|
|
op->flags |= FL_BnReg; /* Apollo Core: use Bn instead of An */
|
|
}
|
|
|
|
if (reg >= 0) {
|
|
/* addressing mode An indirect with predecrement */
|
|
if (!REGisAn(reg))
|
|
cpu_error(4); /* address register required */
|
|
ptmp = skip(ptmp);
|
|
if (*ptmp != ')')
|
|
cpu_error(3); /* missing ) */
|
|
else
|
|
ptmp++;
|
|
p = ptmp;
|
|
op->mode = MODE_AnPreDec;
|
|
op->reg = REGget(reg);
|
|
}
|
|
}
|
|
|
|
parse_expression:
|
|
if ((*p!='(' || start_term!=NULL) && op->mode<0) {
|
|
/* this can only be an expression - parse it */
|
|
disp_size = base_disp_and_ext(op,&p);
|
|
p = skip(p);
|
|
}
|
|
|
|
if (*p=='(' && op->mode<0) { /* "(..." */
|
|
int mem_indir;
|
|
short reg,idx=-1;
|
|
|
|
start_term = p;
|
|
p = skip(p+1);
|
|
|
|
parse_indir:
|
|
if (*p == '[') {
|
|
/* 020+ memory indirect addressing mode */
|
|
p = skip(p+1);
|
|
if (op->value[0]) {
|
|
/* An already parsed displacement expression before '[' */
|
|
/* becomes an outer displacement for compatibility. */
|
|
if (!devpac_compat)
|
|
cpu_error(6); /* warn about displacement at bad position */
|
|
op->value[1] = op->value[0];
|
|
od_size = disp_size ? disp_size : EXT_WORD;
|
|
op->value[0] = NULL;
|
|
disp_size = 0;
|
|
if (op->flags & FL_NoOptBase) {
|
|
op->flags &= ~FL_NoOptBase;
|
|
op->flags |= FL_NoOptOuter;
|
|
}
|
|
}
|
|
mem_indir = 1;
|
|
}
|
|
else
|
|
mem_indir = 0;
|
|
|
|
reg = getbasereg(&p);
|
|
if (reg<0 && op->value[0]==NULL) {
|
|
/* no register identified and still no value read: try it again */
|
|
disp_size = base_disp_and_ext(op,&p);
|
|
p = skip(p);
|
|
if (*p == ')') {
|
|
/* expression was only the first term: read the full expression */
|
|
p = start_term;
|
|
goto parse_expression;
|
|
}
|
|
if (*p == ',') { /* "(displacement," expects register */
|
|
p = skip(p+1);
|
|
if ((reg = getbasereg(&p)) < 0) {
|
|
if (*p == '[')
|
|
goto parse_indir;
|
|
cpu_error(7); /* base or index register expected */
|
|
return PO_CORRUPT;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check for illegal displacement extension */
|
|
if (op->value[0] && disp_size>EXT_LONG) {
|
|
cpu_error(5); /* bad extension */
|
|
disp_size = 0;
|
|
}
|
|
|
|
p = skip(p);
|
|
if (mem_indir && reg<0) {
|
|
/* "([val" without register means base reg is suppressed */
|
|
reg = REGZero | REGAn | 0;
|
|
}
|
|
else if (reg>0 && REGisZero(reg))
|
|
op->flags |= FL_ZBase; /* ZAn was explicitely specified */
|
|
|
|
if (reg >= 0) { /* "(Rn" or "(d,Rn" or "([Rn" or "([bd,Rn" */
|
|
int clbrk = 0;
|
|
|
|
if ((REGisAn(reg) || REGisPC(reg)) &&
|
|
!REGscale(reg) && !REGext(reg)) {
|
|
/* Rn is a base register An or PC, try to read index now */
|
|
|
|
if (*p == ']' && mem_indir) { /* "([bd,Rn]" */
|
|
/* if we ever see an index, it will be postindexed */
|
|
op->format |= FW_Postindexed;
|
|
p = skip(p+1);
|
|
clbrk = 1;
|
|
}
|
|
|
|
parse_index:
|
|
if (*p == ',') { /* read index register */
|
|
char *pidx = p;
|
|
|
|
p = skip(p+1);
|
|
if ((idx = getbasereg(&p)) >= 0) {
|
|
/* "(d,Rn,Xn" or "([bd,Rn],Xn" or "([bd,Rn,Xn" */
|
|
p = skip(p);
|
|
if (*p == ']' && mem_indir) { /* "([bd,Rn,Xn]" */
|
|
p = skip(p+1);
|
|
if (clbrk)
|
|
cpu_error(13); /* too many ] */
|
|
else
|
|
clbrk = 1;
|
|
}
|
|
}
|
|
else {
|
|
if (!mem_indir || !clbrk) {
|
|
if (op->value[0] == NULL) {
|
|
/* (An,bd) is treated as (bd,An) for compatibility */
|
|
if ((disp_size = base_disp_and_ext(op,&p)) < 0) {
|
|
cpu_error(12); /* index register expected */
|
|
return PO_CORRUPT;
|
|
}
|
|
else {
|
|
if (!devpac_compat)
|
|
cpu_error(6); /* displacement at bad position */
|
|
p = skip(p);
|
|
goto parse_index;
|
|
}
|
|
}
|
|
else {
|
|
cpu_error(12); /* index register expected */
|
|
return PO_CORRUPT;
|
|
}
|
|
}
|
|
else
|
|
p = pidx; /* back to ',' to parse outer displacement */
|
|
}
|
|
}
|
|
if (idx < 0)
|
|
op->format &= ~FW_Postindexed; /* index was suppressed */
|
|
}
|
|
else {
|
|
/* Rn is already the index, assume ZA0 as base */
|
|
if (!(reqflags & FL_DoubleReg)) {
|
|
idx = reg;
|
|
reg = REGZero | REGAn | 0;
|
|
op->flags &= ~FL_ZBase;
|
|
}
|
|
}
|
|
|
|
if (mem_indir) {
|
|
if (!clbrk) {
|
|
if (*p != ']')
|
|
cpu_error(8); /* missing ] */
|
|
else
|
|
p = skip(p+1);
|
|
}
|
|
if (*p == ',') { /* "([bd,Rn],Xn,od" or "([bd,Rn,Xn],od" */
|
|
/* read outer displacement */
|
|
p = skip(p+1);
|
|
if (op->value[1] == NULL) {
|
|
if ((op->value[1] = parse_expr(&p)) != NULL) {
|
|
if ((od_size = read_extension(&p,0)) != 0)
|
|
op->flags |= FL_NoOptOuter; /* do not optimize with size */
|
|
else
|
|
od_size = EXT_WORD;
|
|
p = skip(p);
|
|
}
|
|
else
|
|
cpu_error(14); /* missing outer displacement */
|
|
}
|
|
}
|
|
if (op->value[1] != NULL) {
|
|
/* set outer displacement */
|
|
if (od_size == EXT_WORD)
|
|
op->format |= FW_IndSize(FW_Word);
|
|
else if (od_size == EXT_LONG)
|
|
op->format |= FW_IndSize(FW_Long);
|
|
else
|
|
cpu_error(5); /* bad extension */
|
|
}
|
|
else
|
|
op->format |= FW_IndSize(FW_Null); /* no outer disp. given */
|
|
}
|
|
}
|
|
|
|
if (*p==',' && op->value[0]==NULL) {
|
|
/* (Rn,bd) is treated as (bd,Rn) for compatibility reasons */
|
|
p = skip(p+1);
|
|
disp_size = base_disp_and_ext(op,&p);
|
|
if (disp_size >= 0) {
|
|
p = skip(p);
|
|
if (!devpac_compat)
|
|
cpu_error(6); /* warn about displacement at bad position */
|
|
}
|
|
}
|
|
if (*p++ != ')')
|
|
cpu_error(15,')'); /* ) expected */
|
|
|
|
/* parsing completed, determine addressing mode */
|
|
|
|
if (reg >= 0) {
|
|
if (idx >= 0) {
|
|
if (REGisPC(idx)) {
|
|
cpu_error(16); /* can't use PC register as index */
|
|
return PO_CORRUPT;
|
|
}
|
|
if (REGisBn(idx)) {
|
|
cpu_error(61,(int)REGget(idx)); /* can't use Bn as index */
|
|
return PO_CORRUPT;
|
|
}
|
|
if (cpu_type & mcf) {
|
|
if (REGext(idx)!=0 && REGext(idx)!=EXT_LONG)
|
|
cpu_error(5); /* bad extension - only .l for ColdFire */
|
|
idx &= ~(EXT_MASK<<REGext_Shift);
|
|
idx |= EXT_LONG<<REGext_Shift;
|
|
}
|
|
else if (REGext(idx)!=0 &&
|
|
REGext(idx)!=EXT_LONG && REGext(idx)!=EXT_WORD) {
|
|
cpu_error(5); /* bad extension */
|
|
idx &= ~(EXT_MASK<<REGext_Shift);
|
|
}
|
|
if (REGisZero(idx))
|
|
op->flags |= FL_ZIndex; /* ZRn was explicitely specified */
|
|
}
|
|
|
|
/* set default displacement sizes */
|
|
if (!disp_size)
|
|
disp_size = idx<0 ? EXT_WORD : EXT_BYTE;
|
|
|
|
if (!mem_indir && !REGisZero(reg) && op->value[1]==NULL &&
|
|
((idx<0 && disp_size==EXT_WORD) ||
|
|
(idx>=0 && disp_size==EXT_BYTE && !REGisZero(idx)))) {
|
|
/* normal 68000 addressing modes, including 020+ scaling */
|
|
if (idx < 0) {
|
|
if (op->value[0]) {
|
|
if (REGisPC(reg)) {
|
|
op->mode = MODE_Extended; /* (d16,PC) */
|
|
op->reg = REG_PC16Disp;
|
|
}
|
|
else {
|
|
op->mode = MODE_An16Disp; /* (d16,An) */
|
|
op->reg = REGget(reg);
|
|
check_basereg(op);
|
|
}
|
|
}
|
|
else if (REGisPC(reg)) {
|
|
op->mode = MODE_Extended; /* (PC) -> (0,PC) */
|
|
op->reg = REG_PC16Disp;
|
|
op->value[0] = number_expr(0);
|
|
}
|
|
else {
|
|
if (*p == '+') {
|
|
op->mode = MODE_AnPostInc; /* (An)+ */
|
|
op->reg = REGget(reg);
|
|
p++;
|
|
}
|
|
else {
|
|
char *ptmp = skip(p);
|
|
|
|
op->mode = MODE_AnIndir; /* (An) */
|
|
op->reg = REGget(reg);
|
|
if (*ptmp == ':') {
|
|
/* (Rm):(Rn) expected, store reg as 4 bit */
|
|
op->reg = REGgetA(reg);
|
|
ptmp = skip(ptmp+1);
|
|
if (*ptmp == '(') {
|
|
ptmp = skip(ptmp+1);
|
|
reg = getreg(&ptmp,0);
|
|
if (reg >= 0) {
|
|
ptmp = skip(ptmp);
|
|
if (*ptmp++ == ')') { /* (Rn):(Rm) */
|
|
op->reg |= REGgetA(reg) << 4;
|
|
op->flags |= FL_DoubleReg | FL_020up | FL_noCPU32;
|
|
p = ptmp;
|
|
}
|
|
}
|
|
}
|
|
if (p != ptmp) {
|
|
cpu_error(1); /* illegal addressing mode */
|
|
return PO_CORRUPT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (!op->value[0]) {
|
|
/* need a displacement for indexed addressing modes */
|
|
op->value[0] = number_expr(0);
|
|
}
|
|
if (REGisPC(reg)) { /* (d8,PC,Xn) */
|
|
op->mode = MODE_Extended;
|
|
op->reg = REG_PC8Format;
|
|
}
|
|
else {
|
|
op->mode = MODE_An8Format; /* (d8,An,Xn) */
|
|
op->reg = REGget(reg);
|
|
check_basereg(op);
|
|
}
|
|
set_index(op,idx);
|
|
}
|
|
}
|
|
else {
|
|
/* the remaining addressing modes require a full format word */
|
|
op->format |= FW_FullFormat;
|
|
op->flags |= FL_UsesFormat | FL_020up;
|
|
/* no memory-indirect modes for CPU32 */
|
|
if (mem_indir)
|
|
op->flags |= FL_noCPU32;
|
|
|
|
if (REGisPC(reg)) {
|
|
op->mode = MODE_Extended; /* ([bd,PC,Xn],od) */
|
|
op->reg = REG_PC8Format;
|
|
}
|
|
else {
|
|
op->mode = MODE_An8Format; /* ([bd,An,Xn],od) */
|
|
op->reg = REGget(reg);
|
|
check_basereg(op);
|
|
}
|
|
if (REGisZero(reg))
|
|
op->format |= FW_BaseSuppress;
|
|
|
|
if (idx < 0)
|
|
idx = REGZero | 0; /* no index given: assume ZD0.w */
|
|
set_index(op,idx);
|
|
|
|
if (op->value[0]) {
|
|
if (disp_size == EXT_LONG)
|
|
op->format |= FW_BDSize(FW_Long);
|
|
else
|
|
op->format |= FW_BDSize(FW_Word);
|
|
}
|
|
else
|
|
op->format |= FW_BDSize(FW_Null); /* base disp. suppressed */
|
|
}
|
|
|
|
if (REGisBn(reg))
|
|
op->flags |= FL_BnReg; /* Apollo Core: Bn instead of An */
|
|
}
|
|
}
|
|
|
|
if (op->mode<0 && op->value[0]!=NULL) {
|
|
/* we have read a value but no register at all,
|
|
then it's an absolute addressing mode */
|
|
op->mode = MODE_Extended;
|
|
if (disp_size == EXT_WORD) {
|
|
op->reg = REG_AbsShort;
|
|
}
|
|
else {
|
|
if (reqflags & OTF_REGLIST) {
|
|
op->reg = (required==RL) ? REG_RnList : REG_FPnList;
|
|
/* op->flags |= FL_PossRegList; @@@ not needed? */
|
|
}
|
|
else
|
|
op->reg = REG_AbsLong;
|
|
if (disp_size!=0 && disp_size!=EXT_LONG)
|
|
cpu_error(5); /* bad extension */
|
|
}
|
|
}
|
|
|
|
p = skip(p);
|
|
if (*p=='&' || (reqflags & FL_MAC)) {
|
|
/* ColdFire MAC MASK specifier */
|
|
op->flags |= FL_MAC;
|
|
if (*p == '&') {
|
|
op->bf_width = 1;
|
|
p = skip(p+1);
|
|
}
|
|
else
|
|
op->bf_width = 0;
|
|
}
|
|
}
|
|
|
|
if (*p == '{') {
|
|
/* bit field specifier or k-factor */
|
|
int dflag,absk = 0;
|
|
taddr bfval;
|
|
|
|
p = skip(p+1);
|
|
if (*p == '#')
|
|
absk = 1; /* probably absolute k-factor */
|
|
bfval = getbfk(&p,&dflag);
|
|
op->flags |= dflag ? FL_BFoffsetDyn : 0;
|
|
p = skip(p);
|
|
if (*p==':') {
|
|
absk = 0;
|
|
p = skip(p+1);
|
|
op->bf_offset = (unsigned char)bfval;
|
|
op->bf_width = (unsigned char)getbfk(&p,&dflag);
|
|
op->flags |= dflag ? FL_BFwidthDyn : 0;
|
|
p = skip(p);
|
|
if (*p == '}')
|
|
p++;
|
|
else
|
|
cpu_error(15,'}'); /* } expected */
|
|
op->flags |= FL_Bitfield | FL_020up | FL_noCPU32;
|
|
}
|
|
else if (*p == '}') {
|
|
if (absk) {
|
|
op->bf_offset = (unsigned char)bfval;
|
|
if (typechk && (bfval<-64 || bfval>63))
|
|
cpu_error(21); /* value from -64 to 63 required */
|
|
}
|
|
else /* k-factor is a data register */
|
|
op->bf_offset = (unsigned char)(bfval & 7) << 4;
|
|
op->flags |= FL_KFactor;
|
|
p++;
|
|
}
|
|
else {
|
|
cpu_error(1); /* illegal addressing mode */
|
|
return PO_CORRUPT;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* compare parsed addressing mode against requirements */
|
|
|
|
for (i=0; i<16; i++) {
|
|
if (reqmode & (1<<i)) {
|
|
/*printf("%x:%x %d:%d %d:%d\n",op->flags&FL_CheckMask,reqflags&FL_CheckMask,op->mode,addrmodes[i].mode,op->reg,addrmodes[i].reg);*/
|
|
if ((op->flags&FL_CheckMask)==(reqflags&FL_CheckMask) &&
|
|
addrmodes[i].mode==op->mode &&
|
|
(addrmodes[i].reg<0 || addrmodes[i].reg==op->reg)) {
|
|
if (reqflags & OTF_CHKREG) {
|
|
if ((unsigned char)op->reg < optypes[required].first ||
|
|
(unsigned char)op->reg > optypes[required].last)
|
|
return PO_NOMATCH;
|
|
}
|
|
#if 0 /* @@@ not used */
|
|
if (reqflags & OTF_CHKVAL) {
|
|
if (op->value[0] == NULL)
|
|
ierror(0);
|
|
simplify_expr(op->value[0]);
|
|
if (op->value[0]->type == NUM) {
|
|
if (op->value[0]->c.val < (taddr)optypes[required].first ||
|
|
op->value[0]->c.val > (taddr)optypes[required].last)
|
|
return PO_NOMATCH;
|
|
}
|
|
else
|
|
ierror(0);
|
|
}
|
|
#endif
|
|
if (required == DP) {
|
|
/* never optimize d(An) operand for MOVEP */
|
|
op->flags |= FL_NoOpt;
|
|
if (op->mode == MODE_AnIndir) {
|
|
/* translate (An) into 0(An) for MOVEP */
|
|
op->mode = MODE_An16Disp;
|
|
op->value[0] = number_expr(0);
|
|
cpu_error(48,(int)op->reg,(int)op->reg); /* warn about it */
|
|
}
|
|
}
|
|
|
|
p = skip(p);
|
|
if (*p=='\0' || p>=(start+len))
|
|
return PO_MATCH;
|
|
}
|
|
}
|
|
}
|
|
|
|
return PO_NOMATCH;
|
|
}
|
|
|
|
|
|
static void eval_oper(operand *op,section *sec,taddr pc,int final)
|
|
/* evaluate operand expression */
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<2; i++) {
|
|
op->base[i] = NULL;
|
|
if (type_of_expr(op->value[i]) == NUM) {
|
|
eval:
|
|
if (!eval_expr(op->value[i],&op->extval[i],sec,pc)) {
|
|
op->basetype[i] = find_base(op->value[i],&op->base[i],sec,pc);
|
|
|
|
if (op->basetype[i] == BASE_ILLEGAL) {
|
|
if (op->flags & FL_BaseReg) {
|
|
if (fix_basereg(op,final))
|
|
goto eval; /* evaluate fixed operand again */
|
|
}
|
|
if (final)
|
|
general_error(38); /* illegal relocation */
|
|
}
|
|
}
|
|
op->flags |= FL_ExtVal0 << i;
|
|
}
|
|
else {
|
|
op->extval[i] = 0x7fffffff; /* dummy to prevent immediate opt. */
|
|
op->flags &= ~(FL_ExtVal0 << i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int copy_float_exp(unsigned char *d,operand *op,int size)
|
|
/* write immediate floating point expression of 'size' to
|
|
destination buffer, return 0 when everything was ok, else error-code */
|
|
{
|
|
int et;
|
|
thuge h;
|
|
tfloat f;
|
|
|
|
if (op->flags & FL_ExtVal0)
|
|
et = NUM;
|
|
else
|
|
et = type_of_expr(op->value[0]);
|
|
|
|
if (et == NUM) {
|
|
if (op->base[0])
|
|
return 20; /* constant integer expression required */
|
|
}
|
|
else if (et == HUG) {
|
|
if (!eval_expr_huge(op->value[0],&h))
|
|
return 59; /* cannot evaluate huge integer */
|
|
}
|
|
else if (et == FLT) {
|
|
if (!eval_expr_float(op->value[0],&f))
|
|
return 60; /* cannot evaluate floating point */
|
|
}
|
|
else
|
|
return 37; /* immediate operand has illegal type */
|
|
|
|
switch (size) {
|
|
|
|
case EXT_SINGLE:
|
|
if (et == NUM)
|
|
setval(1,d,4,op->extval[0]);
|
|
else if (et == HUG)
|
|
huge_to_mem(1,d,4,h);
|
|
else
|
|
conv2ieee32(1,d,f);
|
|
break;
|
|
|
|
case EXT_DOUBLE:
|
|
if (et == NUM)
|
|
setval_signext(1,d,4,4,op->extval[0]);
|
|
else if (et == HUG)
|
|
huge_to_mem(1,d,8,h);
|
|
else
|
|
conv2ieee64(1,d,f);
|
|
break;
|
|
|
|
case EXT_EXTENDED:
|
|
if (et == NUM)
|
|
setval_signext(1,d,8,4,op->extval[0]);
|
|
else if (et == HUG)
|
|
huge_to_mem(1,d,12,h);
|
|
else
|
|
conv2ieee80(1,d,f);
|
|
break;
|
|
|
|
case EXT_PACKED:
|
|
if (et == NUM)
|
|
setval_signext(1,d,8,4,op->extval[0]);
|
|
else if (et == HUG)
|
|
huge_to_mem(1,d,12,h);
|
|
else
|
|
conv2packed(d,f);
|
|
break;
|
|
|
|
default:
|
|
return 34; /* illegal opcode extension */
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void optimize_oper(operand *op,struct optype *ot,section *sec,
|
|
taddr pc,taddr cpc,int final)
|
|
/* evaluate expressions in operand and try to optimize addressing modes */
|
|
{
|
|
int size16[2]; /* true, when extval[0/1] fits into 16 bits */
|
|
taddr pcdisp; /* calculated pc displacement = (label - current_pc) */
|
|
int pcdisp16; /* true, when pcdisp fits into 16 bits */
|
|
int undef; /* true, when base-symbol is still undefined */
|
|
int secrel; /* pc-relative reference in current section */
|
|
int bdopt; /* true, when base-displacement optimization allowed */
|
|
int odopt; /* true, when outer-displacement optimization allowed */
|
|
|
|
if (!(op->flags & FL_DoNotEval))
|
|
eval_oper(op,sec,pc,final);
|
|
|
|
if ((op->flags & FL_NoOpt) == FL_NoOpt)
|
|
return;
|
|
|
|
/* optimize and fix addressing modes */
|
|
|
|
bdopt = !(op->flags & FL_NoOptBase);
|
|
odopt = !(op->flags & FL_NoOptOuter);
|
|
size16[0] = op->extval[0]>=-0x8000 && op->extval[0]<=0x7fff;
|
|
size16[1] = op->extval[1]>=-0x8000 && op->extval[1]<=0x7fff;
|
|
pcdisp = op->extval[0] - cpc;
|
|
pcdisp16 = (op->base[0]==NULL) ? 0 : (pcdisp>=-0x8000 && pcdisp<=0x7fff);
|
|
undef = (op->base[0]==NULL) ? 0 : EXTREF(op->base[0]);
|
|
secrel = op->base[0]!=NULL && LOCREF(op->base[0]) && op->base[0]->sec==sec;
|
|
|
|
if (bdopt) { /* base displacement optimizations allowed */
|
|
|
|
if (op->mode==MODE_An16Disp) {
|
|
if (opt_disp && !op->base[0] && op->extval[0]==0 &&
|
|
(ot->modes & (1<<AM_AnIndir))) {
|
|
/* (0,An) --> (An) */
|
|
op->mode = MODE_AnIndir;
|
|
if (final) {
|
|
free_expr(op->value[0]);
|
|
if (warn_opts>1)
|
|
cpu_error(49,"(0,An)->(An)");
|
|
}
|
|
op->value[0] = NULL;
|
|
}
|
|
else if (((op->base[0] && !undef) || (!op->base[0] && !size16[0])) &&
|
|
(cpu_type & (m68020up|cpu32)) && op->reg!=sdreg &&
|
|
(ot->modes & (1<<AM_An8Format))) {
|
|
/* (d16,An) --> (bd32,An,ZDn.w) for 020+ only */
|
|
op->mode = MODE_An8Format;
|
|
op->format = FW_FullFormat | FW_IndexSuppress | FW_BDSize(FW_Long);
|
|
op->flags |= FL_UsesFormat | FL_020up;
|
|
if (final && warn_opts>1)
|
|
cpu_error(50,"(d16,An)->(bd32,An,ZDn.w)");
|
|
}
|
|
}
|
|
|
|
else if (op->mode==MODE_Extended && op->reg==REG_PC16Disp &&
|
|
(cpu_type & (m68020up|cpu32)) &&
|
|
(ot->modes & (1<<AM_PC8Format))) {
|
|
if ((!op->base[0] && !size16[0]) ||
|
|
(op->base[0] && !pcdisp16 && !undef && secrel)) {
|
|
/* (d16,PC) --> (bd32,PC,ZDn.w) for 020+ only */
|
|
op->reg = REG_PC8Format;
|
|
op->format = FW_FullFormat | FW_IndexSuppress | FW_BDSize(FW_Long);
|
|
op->flags |= FL_UsesFormat | FL_020up;
|
|
if (final && warn_opts>1)
|
|
cpu_error(50,"(d16,PC)->(bd32,PC,ZDn.w)");
|
|
}
|
|
}
|
|
|
|
else if (op->mode==MODE_An8Format && (op->flags & FL_UsesFormat) &&
|
|
!(op->format & FW_FullFormat)) {
|
|
if ((cpu_type & (m68020up|cpu32)) &&
|
|
((op->base[0] && !undef) ||
|
|
(!op->base[0] && (op->extval[0]<-0x80 || op->extval[0]>0x7f)))) {
|
|
/* (d8,An,Rn) --> (bd,An,Rn) */
|
|
op->format &= 0xff00;
|
|
op->format |= FW_FullFormat | FW_BDSize(FW_Word);
|
|
op->flags |= FL_020up;
|
|
if (final && warn_opts>1)
|
|
cpu_error(50,"(d8,An,Rn)->(bd,An,Rn)");
|
|
}
|
|
}
|
|
|
|
else if (op->mode==MODE_Extended && op->reg==REG_PC8Format &&
|
|
(op->flags & FL_UsesFormat) && !(op->format & FW_FullFormat)) {
|
|
int pcdisp8 = (op->base[0]) ?
|
|
((pcdisp>=-0x80 && pcdisp<=0x7f) || undef) :
|
|
(op->extval[0]>=-0x80 && op->extval[0]<=0x7f);
|
|
|
|
if ((cpu_type & (m68020up|cpu32)) && !pcdisp8) {
|
|
/* (d8,PC,Rn) --> (bd,PC,Rn) */
|
|
op->format &= 0xff00;
|
|
op->format |= FW_FullFormat | FW_BDSize(FW_Word);
|
|
op->flags |= FL_020up;
|
|
if (final && warn_opts>1)
|
|
cpu_error(50,"(d8,PC,Rn)->(bd,PC,Rn)");
|
|
}
|
|
}
|
|
|
|
else if (op->mode==MODE_Extended && op->reg==REG_AbsShort &&
|
|
(ot->modes & (1<<AM_AbsLong))) {
|
|
if (!op->base[0] && !size16[0]) {
|
|
/* absval.w --> absval.l */
|
|
op->reg = REG_AbsLong;
|
|
if (final && warn_opts>1)
|
|
cpu_error(50,"abs.w->abs.l");
|
|
}
|
|
else if (op->base[0] && typechk && LOCREF(op->base[0]) &&
|
|
!(op->base[0]->flags & ABSLABEL)) {
|
|
/* label.w --> label.l */
|
|
op->reg = REG_AbsLong;
|
|
if (final)
|
|
cpu_error(22); /* need 32 bits to reference a program label */
|
|
}
|
|
}
|
|
|
|
else if (op->mode==MODE_Extended && op->reg==REG_AbsLong) {
|
|
if (opt_abs && (!op->base[0] || (op->base[0]->flags & ABSLABEL)) &&
|
|
size16[0] && (ot->modes & (1<<AM_AbsShort))) {
|
|
/* absval.l --> absval.w */
|
|
op->reg = REG_AbsShort;
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"abs.l->abs.w");
|
|
}
|
|
else if (sdreg>=0 && op->base[0]!=NULL &&
|
|
(ot->modes & (1<<AM_An16Disp)) &&
|
|
((opt_gen && (op->base[0]->flags&NEAR)) ||
|
|
(opt_sd && op->base[0]->type==LABSYM && op->base[0]->sec!=NULL
|
|
&& (op->base[0]->sec->flags&NEAR_ADDRESSING))) &&
|
|
op->extval[0]>=0 && op->extval[0]<=0xffff) {
|
|
/* label.l --> label(An) base relative near addressing */
|
|
op->mode = MODE_An16Disp;
|
|
op->reg = sdreg;
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"label->(label,An)");
|
|
}
|
|
else if (opt_pc && op->base[0] && (ot->modes & (1<<AM_PC16Disp))) {
|
|
if (!undef && pcdisp16 && secrel) {
|
|
/* label.l --> d16(PC) */
|
|
op->reg = REG_PC16Disp;
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"label->(d16,PC)");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (op->mode==MODE_An8Format && (op->flags & FL_UsesFormat) &&
|
|
(op->format & FW_FullFormat)) {
|
|
|
|
if (FW_getIndSize(op->format)==FW_None) { /* no memory indirection */
|
|
if (FW_getBDSize(op->format) > FW_Null) {
|
|
if (opt_bdisp && bdopt && !op->base[0] && op->extval[0]==0) {
|
|
/* (0,An,...) --> (An,...) */
|
|
op->format &= ~FW_BDSize(FW_SizeMask);
|
|
op->format |= FW_BDSize(FW_Null);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"(0,An,...)->(An,...)");
|
|
}
|
|
else if (bdopt && (op->base[0] || (!op->base[0] && !size16[0])) &&
|
|
FW_getBDSize(op->format)==FW_Word) {
|
|
/* (bd16,An,...) --> (bd32,An,...) */
|
|
op->format &= ~FW_BDSize(FW_SizeMask);
|
|
op->format |= FW_BDSize(FW_Long);
|
|
if (final && warn_opts>1)
|
|
cpu_error(50,"(bd16,An,...)->(bd32,An,...)");
|
|
}
|
|
else if (opt_bdisp && bdopt && !op->base[0] && size16[0] &&
|
|
FW_getBDSize(op->format)==FW_Long) {
|
|
if ((op->format & FW_IndexSuppress) &&
|
|
(ot->modes & (1<<AM_An16Disp)) &&
|
|
!(op->flags & FL_ZIndex)) {
|
|
/* (bd32,An,ZRn) --> (d16,An) */
|
|
op->mode = MODE_An16Disp;
|
|
op->format = 0;
|
|
op->flags &= ~(FL_UsesFormat | FL_020up | FL_noCPU32);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"(bd32,An,ZRn)->(d16,An)");
|
|
}
|
|
else {
|
|
/* (bd32,An,Rn) --> (bd16,An,Rn) */
|
|
op->format &= ~FW_BDSize(FW_SizeMask);
|
|
op->format |= FW_BDSize(FW_Word);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"(bd32,An,Rn)->(d16,An,Rn)");
|
|
}
|
|
}
|
|
}
|
|
else if (opt_gen && !op->base[0] &&
|
|
(op->format & FW_IndexSuppress) &&
|
|
!(op->format & FW_BaseSuppress) &&
|
|
(ot->modes & (1<<AM_AnIndir)) &&
|
|
!(op->flags & FL_ZIndex)) {
|
|
/* (An,ZRn) --> (An) */
|
|
op->mode = MODE_AnIndir;
|
|
op->format = 0;
|
|
op->flags &= ~(FL_UsesFormat | FL_020up | FL_noCPU32);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"(An,ZRn)->(An)");
|
|
}
|
|
}
|
|
|
|
else { /* memory indirection */
|
|
if (opt_bdisp && bdopt && !op->base[0] &&
|
|
FW_getBDSize(op->format)>FW_Null && op->extval[0]==0) {
|
|
/* ([0,An,...],...) --> ([An,...],...) */
|
|
op->format &= ~FW_BDSize(FW_SizeMask);
|
|
op->format |= FW_BDSize(FW_Null);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"([0,An,...],...)->([An,...],...)");
|
|
}
|
|
else if (bdopt && (op->base[0] || (!op->base[0] && !size16[0])) &&
|
|
FW_getBDSize(op->format)==FW_Word) {
|
|
/* ([bd16,An,...],...) --> ([bd32,An,...],...) */
|
|
op->format &= ~FW_BDSize(FW_SizeMask);
|
|
op->format |= FW_BDSize(FW_Long);
|
|
if (final && warn_opts>1)
|
|
cpu_error(50,"([bd16,An,...],...)->([bd32,An,...],...)");
|
|
}
|
|
else if (opt_bdisp && bdopt && !op->base[0] && size16[0] &&
|
|
FW_getBDSize(op->format)==FW_Long) {
|
|
/* ([bd32,An,...],...) --> ([bd16,An,...],...) */
|
|
op->format &= ~FW_BDSize(FW_SizeMask);
|
|
op->format |= FW_BDSize(FW_Word);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"([bd32,An,...],...)->([bd16,An,...],...)");
|
|
}
|
|
if (FW_getIndSize(op->format) >= FW_Word) { /* outer displacement */
|
|
if (opt_odisp && odopt && !op->base[1] && op->extval[1]==0) {
|
|
/* ([...],0) --> ([...]) */
|
|
op->format &= ~FW_IndSize(FW_SizeMask);
|
|
op->format |= FW_IndSize(FW_Null);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"([...],0)->([...])");
|
|
}
|
|
else if (odopt && (op->base[1] || (!op->base[1] && !size16[1])) &&
|
|
FW_getIndSize(op->format)==FW_Word) {
|
|
/* ([...],od16) --> ([...],od32) */
|
|
op->format &= ~FW_IndSize(FW_SizeMask);
|
|
op->format |= FW_IndSize(FW_Long);
|
|
if (final && warn_opts>1)
|
|
cpu_error(50,"([...],od16)->([...],od32)");
|
|
}
|
|
else if (opt_odisp && odopt && !op->base[1] && size16[1] &&
|
|
FW_getIndSize(op->format)==FW_Long) {
|
|
/* ([...],od32) --> ([...],od16) */
|
|
op->format &= ~FW_IndSize(FW_SizeMask);
|
|
op->format |= FW_IndSize(FW_Word);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"([...],od32)->([...],od16)");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (op->mode==MODE_Extended && op->reg==REG_PC8Format &&
|
|
(op->flags & FL_UsesFormat) && (op->format & FW_FullFormat)) {
|
|
|
|
if (FW_getIndSize(op->format)==FW_None) { /* no memory indirection */
|
|
if (FW_getBDSize(op->format) > FW_Null) {
|
|
if (opt_bdisp && bdopt && !op->base[0] && op->extval[0]==0) {
|
|
/* (0,PC,...) --> (PC,...) */
|
|
op->format &= ~FW_BDSize(FW_SizeMask);
|
|
op->format |= FW_BDSize(FW_Null);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"(0,PC,...)->(PC,...)");
|
|
}
|
|
else if (bdopt && ((op->base[0] && !pcdisp16 && !undef && secrel) ||
|
|
(!op->base[0] && !size16[0]))
|
|
&& FW_getBDSize(op->format)==FW_Word) {
|
|
/* (bd16,PC,...) --> (bd32,PC,...) */
|
|
op->format &= ~FW_BDSize(FW_SizeMask);
|
|
op->format |= FW_BDSize(FW_Long);
|
|
if (final && warn_opts>1)
|
|
cpu_error(50,"(bd16,PC,...)->(bd32,PC,...)");
|
|
}
|
|
else if (opt_bdisp && bdopt &&
|
|
((op->base[0] && pcdisp16 && !undef && secrel) ||
|
|
(!op->base[0] && size16[0]))
|
|
&& FW_getBDSize(op->format)==FW_Long) {
|
|
if ((op->format & FW_IndexSuppress) &&
|
|
(ot->modes & (1<<AM_PC16Disp)) &&
|
|
!(op->flags & FL_ZIndex)) {
|
|
/* (bd32,PC,ZRn) --> (d16,PC) */
|
|
op->reg = REG_PC16Disp;
|
|
op->format = 0;
|
|
op->flags &= ~(FL_UsesFormat | FL_020up | FL_noCPU32);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"(bd32,PC,ZRn)->(d16,PC)");
|
|
}
|
|
else {
|
|
/* (bd32,PC,Rn) --> (bd16,PC,Rn) */
|
|
op->format &= ~FW_BDSize(FW_SizeMask);
|
|
op->format |= FW_BDSize(FW_Word);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"(bd32,PC,Rn)->(bd16,PC,Rn)");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else { /* memory indirection */
|
|
if (opt_bdisp && !op->base[0] && bdopt &&
|
|
FW_getBDSize(op->format)>FW_Null && op->extval[0]==0) {
|
|
/* ([0,PC,...],...) --> ([PC,...],...) */
|
|
op->format &= ~FW_BDSize(FW_SizeMask);
|
|
op->format |= FW_BDSize(FW_Null);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"([0,PC,...],...)->([PC,...],...)");
|
|
}
|
|
else if (bdopt && ((op->base[0] && !pcdisp16 && !undef && secrel) ||
|
|
(!op->base[0] && !size16[0])) &&
|
|
FW_getBDSize(op->format)==FW_Word) {
|
|
/* ([bd16,PC,...],...) --> ([bd32,PC,...],...) */
|
|
op->format &= ~FW_BDSize(FW_SizeMask);
|
|
op->format |= FW_BDSize(FW_Long);
|
|
if (final && warn_opts>1)
|
|
cpu_error(50,"([bd16,PC,...],...)->([bd32,PC,...],...)");
|
|
}
|
|
else if (opt_bdisp && bdopt &&
|
|
((op->base[0] && pcdisp16 && !undef && secrel) ||
|
|
(!op->base[0] && size16[0])) &&
|
|
FW_getBDSize(op->format)==FW_Long) {
|
|
/* ([bd32,PC,...],...) --> ([bd16,PC,...],...) */
|
|
op->format &= ~FW_BDSize(FW_SizeMask);
|
|
op->format |= FW_BDSize(FW_Word);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"([bd32,PC,...],...)->([bd16,PC,...],...)");
|
|
}
|
|
if (FW_getIndSize(op->format) >= FW_Word) { /* outer displacement */
|
|
if (opt_odisp && !op->base[1] && odopt && op->extval[1]==0) {
|
|
/* ([...],0) --> ([...]) */
|
|
op->format &= ~FW_IndSize(FW_SizeMask);
|
|
op->format |= FW_IndSize(FW_Null);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"([...],0)->([...])");
|
|
}
|
|
else if (odopt && (op->base[1] || (!op->base[1] && !size16[1])) &&
|
|
FW_getIndSize(op->format)==FW_Word) {
|
|
/* ([...],od16) --> ([...],od32) */
|
|
op->format &= ~FW_IndSize(FW_SizeMask);
|
|
op->format |= FW_IndSize(FW_Long);
|
|
if (final && warn_opts>1)
|
|
cpu_error(50,"([...],od16)->([...],od32)");
|
|
}
|
|
else if (opt_odisp && odopt && !op->base[1] && size16[1] &&
|
|
FW_getIndSize(op->format)==FW_Long) {
|
|
/* ([...],od32) --> ([...],od16) */
|
|
op->format &= ~FW_IndSize(FW_SizeMask);
|
|
op->format |= FW_IndSize(FW_Word);
|
|
if (final && warn_opts>1)
|
|
cpu_error(49,"([...],od32)->([...],od16)");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int optypes_subset(mnemonic *mold,mnemonic *mnew)
|
|
/* returns TRUE, when optypes of mold are a subset of mnew */
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<MAX_OPERANDS; i++) {
|
|
int ot_old = mold->operand_type[i];
|
|
int ot_new = mnew->operand_type[i];
|
|
uint32_t fl_old = optypes[ot_old].flags;
|
|
uint32_t fl_new = optypes[ot_new].flags;
|
|
uint16_t m = optypes[ot_old].modes;
|
|
|
|
if ((!ot_old && ot_new) || (!ot_new && ot_old))
|
|
return 0; /* different number of operands */
|
|
|
|
if ((optypes[ot_new].modes & m) != m ||
|
|
(fl_old&FL_CheckMask) != (fl_new&FL_CheckMask))
|
|
return 0; /* addressing modes are not a subset of current mnemo */
|
|
|
|
if ((fl_old & OTF_SPECREG) && (fl_new & OTF_SPECREG)) {
|
|
if (optypes[ot_old].first < optypes[ot_new].first ||
|
|
optypes[ot_old].last > optypes[ot_new].last)
|
|
return 0; /* special register range is not a subset */
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static instruction *ip_doubleop(int code,char *q,signed char mode1,
|
|
signed char reg1,uint16_t flags1,
|
|
uint16_t format1,expr *exp1,
|
|
signed char mode2, signed char reg2,
|
|
uint16_t flags2, uint16_t format2,
|
|
expr *exp2)
|
|
{
|
|
instruction *ip;
|
|
|
|
if (ipslot >= MAX_IP_COPIES)
|
|
ierror(0);
|
|
ip = clr_instruction(&newip[ipslot]);
|
|
ip->code = code;
|
|
ip->qualifiers[0] = q;
|
|
ip->op[0] = clr_operand(&newop[ipslot][0]);
|
|
ip->op[0]->mode = mode1;
|
|
ip->op[0]->reg = reg1;
|
|
ip->op[0]->flags = flags1;
|
|
ip->op[0]->format = format1;
|
|
ip->op[0]->value[0] = exp1;
|
|
if (mode2 >= 0) {
|
|
ip->op[1] = clr_operand(&newop[ipslot][1]);
|
|
ip->op[1]->mode = mode2;
|
|
ip->op[1]->reg = reg2;
|
|
ip->op[1]->flags = flags2;
|
|
ip->op[1]->format = format2;
|
|
ip->op[1]->value[0] = exp2;
|
|
}
|
|
++ipslot;
|
|
return ip;
|
|
}
|
|
|
|
|
|
static instruction *ip_singleop(int code,char *q,signed char mode,
|
|
signed char reg,uint16_t flags,
|
|
uint16_t format,expr *exp)
|
|
{
|
|
return ip_doubleop(code,q,mode,reg,flags,format,exp,-1,0,0,0,NULL);
|
|
}
|
|
|
|
|
|
static int test_incr_ea(operand *op,taddr offset)
|
|
/* test if we could increment the EA in this operand */
|
|
{
|
|
signed char m,r;
|
|
taddr val;
|
|
|
|
m = op->mode;
|
|
if (m<MODE_AnIndir || m>MODE_Extended)
|
|
return 0;
|
|
val = op->extval[0] + offset;
|
|
if (m == MODE_An8Format) {
|
|
if (op->format & FW_FullFormat)
|
|
return 0; /* too complex, doesn't make sense */
|
|
if (val<-0x80 || val>0x7f)
|
|
return 0;
|
|
}
|
|
else if (m == MODE_An16Disp) {
|
|
if (val<-0x8000 || val>0x7fff)
|
|
return 0;
|
|
}
|
|
else if (m == MODE_Extended) {
|
|
r = op->reg;
|
|
if (r > REG_AbsLong)
|
|
return 0; /* no PC-modes for now */
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
static void incr_ea(operand *op,taddr offset,int final)
|
|
/* increment the EA in 'op' by 'offset' when the addressing mode is suitable */
|
|
{
|
|
if (final) {
|
|
signed char m = op->mode;
|
|
signed char r = op->reg;
|
|
|
|
if (m == MODE_AnIndir) {
|
|
/* change to (d16,An) and allocate the d16 expression */
|
|
op->mode = MODE_An16Disp;
|
|
op->value[0] = number_expr(offset);
|
|
}
|
|
else if (m==MODE_An16Disp || m==MODE_An8Format ||
|
|
(m==MODE_Extended && (r==REG_AbsShort || r==REG_AbsLong))) {
|
|
/* increment the expression by 'offset' */
|
|
op->value[0] = make_expr(ADD,op->value[0],number_expr(offset));
|
|
}
|
|
}
|
|
else {
|
|
/* we will definitely need (d16,An) after incrementation for (An) */
|
|
if (op->mode == MODE_AnIndir) {
|
|
op->mode = MODE_An16Disp;
|
|
op->flags |= FL_NoOpt;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static int aindir_in_list(operand *op,taddr list)
|
|
/* tests if operand is (An)+ or -(An) and An is present in register list */
|
|
{
|
|
if (op->mode==MODE_AnPostInc || op->mode==MODE_AnPreDec)
|
|
return (list & (1 << (REGAn + REGget(op->reg)))) != 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static unsigned char optimize_instruction(instruction *iplist,section *sec,
|
|
taddr pc,int final)
|
|
{
|
|
instruction *ip = iplist;
|
|
mnemonic *mnemo = &mnemonics[ip->code];
|
|
char ext = ip->qualifiers[0] ?
|
|
tolower((unsigned char)ip->qualifiers[0][0]) : '\0';
|
|
unsigned char ipflags = ip->ext.un.real.flags;
|
|
signed char lastsize = ip->ext.un.real.last_size;
|
|
char orig_ext = (char)ip->ext.un.real.orig_ext;
|
|
uint16_t oc;
|
|
taddr val,cpc;
|
|
int abs,i;
|
|
|
|
/* See if the next instruction fits as well, and includes the
|
|
addressing modes of the current one. Following instructions
|
|
usually have higher CPU requirements. */
|
|
while (!strcmp(mnemo->name,mnemonics[ip->code+1].name) &&
|
|
(mnemonics[ip->code+1].ext.available & cpu_type) != 0) {
|
|
mnemonic *nextmn = &mnemonics[ip->code+1];
|
|
uint16_t nextsize = nextmn->ext.size;
|
|
|
|
/* first check if next instruction supports current size extension */
|
|
if ((mnemo->ext.size&SIZE_MASK) != SIZE_UNSIZED ||
|
|
(nextsize&SIZE_MASK) != SIZE_UNSIZED) {
|
|
if ((nextsize&S_CFCHECK) && (cpu_type&mcf))
|
|
nextsize &= ~(SIZE_BYTE|SIZE_WORD); /* ColdFire */
|
|
if ((nextsize & lc_ext_to_size(ext)) == 0)
|
|
break; /* size not supported */
|
|
}
|
|
if (!optypes_subset(mnemo,nextmn) ||
|
|
S_OPCODE_SIZE(nextmn->ext.size) > S_OPCODE_SIZE(mnemo->ext.size))
|
|
break; /* not all operand types supported or instruction is bigger */
|
|
ip->code++;
|
|
}
|
|
mnemo = &mnemonics[ip->code];
|
|
|
|
cpc = pc + (S_OPCODE_SIZE(mnemo->ext.size) << 1);
|
|
if (phxass_compat) {
|
|
/* For PhxAss-compatibility, set value of the "current-pc-symbol"
|
|
to pc + S_OPCODE_SIZE. */
|
|
pc = cpc;
|
|
}
|
|
|
|
/* Make sure JMP/JSR (label,PC) is never optimized (to a short-branch) */
|
|
if ((mnemo->ext.opcode[0]==0x4ec0 || mnemo->ext.opcode[0]==0x4e80) &&
|
|
ip->op[0]!=NULL && ip->op[0]->mode==MODE_Extended &&
|
|
ip->op[0]->reg==REG_PC16Disp)
|
|
ip->op[0]->flags |= FL_NoOpt; /* do not optimize */
|
|
|
|
/* evaluate and optimize operands */
|
|
for (i=0; i<MAX_OPERANDS && ip->op[i]!=NULL; i++)
|
|
optimize_oper(ip->op[i],&optypes[mnemo->operand_type[i]],sec,pc,cpc,final);
|
|
|
|
/* resolve register lists in MOVEM instructions */
|
|
if (mnemo->ext.opcode[0]==0x4880 && mnemo->operand_type[0]!=IR &&
|
|
mnemo->ext.place[0]==D16 && mnemo->ext.place[1]==SEA &&
|
|
ip->op[1]->base[0]==NULL && ip->op[1]->value[0]!=NULL &&
|
|
ip->op[1]->value[0]->type==SYM &&
|
|
(ip->op[1]->value[0]->c.sym->flags & REGLIST)) {
|
|
/* destination operand seems to be the register list - swap them */
|
|
ip->op[0]->reg = REG_AbsLong;
|
|
ip->op[1]->reg = REG_RnList;
|
|
ip->code += 3; /* take matching mnemonic with swapped operands */
|
|
mnemo = &mnemonics[ip->code];
|
|
if (final && ip->op[0]->base[0]==NULL && ip->op[0]->value[0]!=NULL &&
|
|
ip->op[0]->value[0]->type==SYM &&
|
|
(ip->op[0]->value[0]->c.sym->flags & REGLIST))
|
|
cpu_error(62); /* register list on both sides */
|
|
}
|
|
/* resolve register lists in FMOVEM instructions */
|
|
else if (mnemo->operand_type[0] == FL) {
|
|
if (ip->op[1]->base[0]==NULL && ip->op[1]->value[0]!=NULL &&
|
|
ip->op[1]->value[0]->type==SYM &&
|
|
(ip->op[1]->value[0]->c.sym->flags & REGLIST)) {
|
|
if (ip->op[1]->extval[0] & 0x1c00)
|
|
ip->code = OC_FMOVEMTOSPEC; /* list contains special registers */
|
|
else if (mnemo->operand_type[1] == AC)
|
|
ip->code = OC_FMOVEMTOLIST;
|
|
else if (mnemo->operand_type[1] == CFMM)
|
|
ip->code = OC_FMOVEMTOLIST - 1;
|
|
else
|
|
goto dontswap;
|
|
/* destination operand seems to be register list - swap them */
|
|
ip->op[0]->reg = REG_AbsLong;
|
|
ip->op[1]->reg = REG_FPnList;
|
|
mnemo = &mnemonics[ip->code];
|
|
if (final && ip->op[0]->base[0]==NULL && ip->op[0]->value[0]!=NULL &&
|
|
ip->op[0]->value[0]->type==SYM &&
|
|
(ip->op[0]->value[0]->c.sym->flags & REGLIST))
|
|
cpu_error(62); /* register list on both sides */
|
|
}
|
|
else if (ip->op[0]->base[0]==NULL && (ip->op[0]->extval[0] & 0x1c00)) {
|
|
/* register list in first operand contains special registers */
|
|
ip->code = OC_FMOVEMFROMSPEC;
|
|
mnemo = &mnemonics[ip->code];
|
|
}
|
|
}
|
|
else if (mnemo->operand_type[1]==FL && ip->op[1]->base[0]==NULL &&
|
|
(ip->op[1]->extval[0] & 0x1c00)) {
|
|
/* register list in first operand contains special registers */
|
|
ip->code = OC_FMOVEMTOSPEC;
|
|
mnemo = &mnemonics[ip->code];
|
|
}
|
|
dontswap:
|
|
ip->ext.un.copy.next = NULL;
|
|
|
|
if (no_opt)
|
|
return ipflags; /* no optimizations wanted */
|
|
|
|
/* STAGE 1
|
|
all instructions optimized here may be optimized again in the 2nd stage */
|
|
oc = mnemo->ext.opcode[0];
|
|
if (ip->op[0]) {
|
|
val = ip->op[0]->extval[0];
|
|
abs = ip->op[0]->base[0]==NULL;
|
|
}
|
|
|
|
if (opt_mul && abs && (oc==0xc0c0 || oc==0xc1c0 || oc==0x4c00) &&
|
|
!(mnemo->ext.opcode[1] & 0x0400) &&
|
|
ip->op[0]->mode==MODE_Extended && ip->op[0]->reg==REG_Immediate) {
|
|
/* MULU/MULS #x,Dn */
|
|
int muls = (oc&0x0100)!=0 || (mnemo->ext.opcode[1]&0x0800)!=0;
|
|
|
|
if (val == 0) {
|
|
/* mulu/muls #0,Dn -> moveq #0,Dn */
|
|
ip->code = OC_MOVEQ;
|
|
ip->qualifiers[0] = l_str;
|
|
if (final && warn_opts)
|
|
cpu_error(51,"mulu/muls #0,Dn -> moveq #0,Dn");
|
|
}
|
|
|
|
else if (val == 1) {
|
|
if (ext=='w' && muls) {
|
|
/* muls.w #1,Dn -> ext.l Dn */
|
|
ip->code = OC_EXT;
|
|
ip->qualifiers[0] = l_str;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
if (warn_opts)
|
|
cpu_error(51,"muls.w #1,Dn -> ext.l Dn");
|
|
}
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
}
|
|
else if (ext=='w' && !muls && (cpu_type&(mcfb|mcfc))) {
|
|
/* ColdFire ISA_B/ISA_C: mulu.w #1,Dn -> mvz.w Dn,Dn */
|
|
ip->code = OC_MVZ;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
if (warn_opts)
|
|
cpu_error(51,"mulu.w #1,Dn -> mvz.w Dn,Dn");
|
|
}
|
|
ip->op[0] = ip->op[1];
|
|
}
|
|
else if (ext == 'l') {
|
|
/* mulu.l/muls.l #1,Dn -> tst.l Dn */
|
|
ip->code = OC_TST;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
if (warn_opts)
|
|
cpu_error(51,"mulu/muls.l #1,Dn -> tst.l Dn");
|
|
}
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
}
|
|
}
|
|
|
|
else if (val==-1 && muls) {
|
|
if (ext == 'w') {
|
|
/* muls.w #-1,Dn -> ext.l Dn + neg.l Dn */
|
|
if (opt_speed) {
|
|
ip->code = OC_EXT;
|
|
ip->qualifiers[0] = l_str;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
if (warn_opts)
|
|
cpu_error(51,"muls.w #-1,Dn -> ext.l Dn + neg.l Dn");
|
|
}
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
ip->ext.un.copy.next = ip_singleop(OC_NEG,l_str,MODE_Dn,
|
|
ip->op[0]->reg,0,0,NULL);
|
|
}
|
|
}
|
|
else {
|
|
/* muls.l #-1,Dn -> neg.l Dn */
|
|
ip->code = OC_NEG;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
if (warn_opts)
|
|
cpu_error(51,"mulu/muls #-1,Dn -> neg.l Dn");
|
|
}
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
}
|
|
}
|
|
|
|
else if (val>=2 && val<=0x100 && cntones(val,9)==1) {
|
|
val = bfffo(val,1,9);
|
|
if (ext=='w' && muls && opt_speed) {
|
|
/* muls.w #x,Dn -> ext.l Dn + asl.l #x,Dn */
|
|
instruction *ip2 = copy_instruction(ip);
|
|
|
|
ip->code = OC_EXT;
|
|
ip->qualifiers[0] = l_str;
|
|
ip2->code = OC_ASLI;
|
|
ip2->qualifiers[0] = l_str;
|
|
ip2->op[0]->extval[0] = val;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
free_expr(ip2->op[0]->value[0]);
|
|
ip2->op[0]->value[0] = number_expr(val);
|
|
if (warn_opts)
|
|
cpu_error(51,"muls.w #x,Dn -> ext.l Dn + asl.l #x,Dn");
|
|
}
|
|
else
|
|
ip2->op[0]->flags |= FL_DoNotEval;
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
ip->ext.un.copy.next = ip2; /* append ASL */
|
|
ip = ip2; /* make stage 2 look at the ASL */
|
|
}
|
|
else if (ext=='w' && !muls && (cpu_type&(mcfb|mcfc)) && opt_speed) {
|
|
/* ColdFire ISA_B/ISA_C: mulu.w #x,Dn -> mvz.w Dn,Dn + lsl.l #x,Dn */
|
|
instruction *ip2 = copy_instruction(ip);
|
|
|
|
ip->code = OC_MVZ;
|
|
ip->qualifiers[0] = w_str;
|
|
ip->op[0] = ip->op[1];
|
|
ip2->code = OC_LSLI;
|
|
ip2->qualifiers[0] = l_str;
|
|
ip2->op[0]->extval[0] = val;
|
|
if (final) {
|
|
free_expr(ip2->op[0]->value[0]);
|
|
ip2->op[0]->value[0] = number_expr(val);
|
|
if (warn_opts)
|
|
cpu_error(51,"mulu.w #x,Dn -> mvz.w Dn,Dn + lsl.l #x,Dn");
|
|
}
|
|
else
|
|
ip2->op[0]->flags |= FL_DoNotEval;
|
|
ip->ext.un.copy.next = ip2; /* append LSL */
|
|
/* ip = ip2; stage 2 optimization doesn't make sense for ColdFire */
|
|
}
|
|
else if (ext == 'l') {
|
|
/* mulu/muls.l #x,Dn -> lsl/asl.l #x,Dn */
|
|
ip->code = muls ? OC_ASLI : OC_LSLI;
|
|
if (final) {
|
|
free_expr(ip->op[0]->value[0]);
|
|
ip->op[0]->value[0] = number_expr(val);
|
|
if (warn_opts)
|
|
cpu_error(51,"mulu/muls.l #x,Dn -> lsl/asl.l #x,Dn");
|
|
}
|
|
else
|
|
ip->op[0]->flags |= FL_DoNotEval;
|
|
ip->op[0]->extval[0] = val;
|
|
}
|
|
}
|
|
|
|
else if (val<=-2 && val>=-0x100 && muls && cntones(-val,9)==1) {
|
|
val = bfffo(-val,1,9);
|
|
if (opt_speed && ext=='w') {
|
|
/* muls.w #-x,Dn -> ext.l Dn + asl.l #x,Dn + neg.l Dn */
|
|
instruction *ip2 = copy_instruction(ip);
|
|
|
|
ip->code = OC_EXT;
|
|
ip->qualifiers[0] = l_str;
|
|
ip2->code = OC_ASLI;
|
|
ip2->qualifiers[0] = l_str;
|
|
ip2->op[0]->extval[0] = val;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
free_expr(ip2->op[0]->value[0]);
|
|
ip2->op[0]->value[0] = number_expr(val);
|
|
if (warn_opts)
|
|
cpu_error(51,"muls.w #-x,Dn -> ext.l Dn + asl.l #x,Dn + neg.l Dn");
|
|
}
|
|
else
|
|
ip2->op[0]->flags |= FL_DoNotEval;
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
ip->ext.un.copy.next = ip2; /* append ASL */
|
|
ip2->ext.un.copy.next = ip_singleop(OC_NEG,l_str,MODE_Dn,
|
|
ip2->op[1]->reg,0,0,NULL);
|
|
ip = ip2; /* make stage 2 look at the ASL */
|
|
}
|
|
else if (ext == 'l' && opt_speed) {
|
|
/* muls.l #-x,Dn -> asl.l #x,Dn + neg.l Dn */
|
|
ip->code = OC_ASLI;
|
|
if (final) {
|
|
free_expr(ip->op[0]->value[0]);
|
|
ip->op[0]->value[0] = number_expr(val);
|
|
if (warn_opts)
|
|
cpu_error(51,"muls.l #-x,Dn -> asl.l #x,Dn + neg.l Dn");
|
|
}
|
|
else
|
|
ip->op[0]->flags |= FL_DoNotEval;
|
|
ip->op[0]->extval[0] = val;
|
|
ip->ext.un.copy.next = ip_singleop(OC_NEG,l_str,MODE_Dn,
|
|
ip->op[1]->reg,0,0,NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* STAGE 2 - reread, in case instruction was optimized in stage 1 */
|
|
if (ip->code >= 0) {
|
|
mnemo = &mnemonics[ip->code];
|
|
oc = mnemo->ext.opcode[0];
|
|
ext = ip->qualifiers[0] ?
|
|
tolower((unsigned char)ip->qualifiers[0][0]) : '\0';
|
|
}
|
|
|
|
if ((ip->code==OC_MOVE || oc==0x0040) &&
|
|
ip->op[0]->mode==MODE_Extended && ip->op[0]->reg==REG_Immediate) {
|
|
/* MOVE/MOVEA immediate instruction */
|
|
if (opt_moveq && abs && ext=='l' && val>=-0x80 && val<=0x7f &&
|
|
ip->op[1]->mode==MODE_Dn) {
|
|
/* move.l #x,Dn --> moveq #x,Dn */
|
|
ip->code = OC_MOVEQ;
|
|
ip->qualifiers[0] = l_str;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"move.l #x,Dn -> moveq #x,Dn");
|
|
}
|
|
else if (opt_gen && abs && val==0 && !(oc&0x0040) &&
|
|
((cpu_type & (m68010up|mcf|cpu32)) || opt_clr)) {
|
|
/* move #0,<ea> --> clr <ea> */
|
|
ip->code = OC_CLR;
|
|
if (final)
|
|
free_operand(ip->op[0]);
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
if (final && (warn_opts>1 ||
|
|
(warn_opts && !(cpu_type&(m68010up|mcf|cpu32)))))
|
|
cpu_error(51,"move #0 -> clr");
|
|
}
|
|
else if (opt_moveq && abs && ext=='l' && (val==-1 || (val>=1 && val<=7)) &&
|
|
(cpu_type & (mcfb|mcfc))) {
|
|
/* move.l #x,<ea> -> mov3q #x,<ea> for x=-1,1..7 */
|
|
ip->code = OC_MOV3Q;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"move.l #x -> mov3q #x");
|
|
}
|
|
else if (opt_st && abs && ext=='b' && !(oc&0x0040) && (val&0xff)==0xff) {
|
|
/* move.b #-1,<ea> --> st <ea> */
|
|
if (!(cpu_type & mcf) || ip->op[1]->mode==MODE_Dn) {
|
|
ip->code = OC_ST;
|
|
if (final)
|
|
free_operand(ip->op[0]);
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
if (final && warn_opts)
|
|
cpu_error(51,"move.b #-1 -> st");
|
|
}
|
|
}
|
|
else if (opt_pea && ip->op[1]->mode==MODE_AnPreDec &&
|
|
ip->op[1]->reg==7 && ext=='l') {
|
|
/* move.l #x,-(a7) --> pea x */
|
|
if (abs && val>=-0x8000 && val<=0x7fff) {
|
|
ip->op[0]->reg = REG_AbsShort;
|
|
ip->code = OC_PEA;
|
|
if (final)
|
|
free_operand(ip->op[1]);
|
|
ip->op[1] = NULL;
|
|
if (final && warn_opts)
|
|
cpu_error(51,"move.l #x,-(a7) -> pea x.w");
|
|
}
|
|
else if (cpu_type & (m68000|m68010|m68020|m68030|cpu32)) {
|
|
/* Faster for 68000-68030 only. */
|
|
ip->op[0]->reg = REG_AbsLong;
|
|
ip->code = OC_PEA;
|
|
if (final)
|
|
free_operand(ip->op[1]);
|
|
ip->op[1] = NULL;
|
|
if (final && warn_opts)
|
|
cpu_error(51,"move.l #x,-(a7) -> pea x.l");
|
|
}
|
|
}
|
|
else if (opt_gen && oc==0x0040) {
|
|
if (abs && val==0) {
|
|
/* movea #0,An --> suba.l An,An */
|
|
ip->code = OC_SUBA;
|
|
ip->qualifiers[0] = l_str;
|
|
ip->op[0]->mode = MODE_An;
|
|
ip->op[0]->reg = ip->op[1]->reg;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"movea #0,An -> suba.l An,An");
|
|
}
|
|
else if (!abs && ext=='l') {
|
|
/* movea.l #label,An --> lea label,An */
|
|
ip->code = OC_LEA;
|
|
ip->op[0]->reg = REG_AbsLong;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"movea.l #label,An -> lea label,An");
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (opt_moveq && abs && (oc & 0xff7f)==0x7100 &&
|
|
ip->op[0]->mode==MODE_Extended && ip->op[0]->reg==REG_Immediate &&
|
|
((val>=-0x80 && !(oc&0x0080)) || val>=0) && val<=0x7f) {
|
|
/* MVS #-128..127,Dn or MVZ #0..127,Dn --> MOVEQ */
|
|
ip->code = OC_MOVEQ;
|
|
ip->qualifiers[0] = l_str;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"mvs/mvz #x,Dn -> moveq #x,Dn");
|
|
}
|
|
|
|
else if ((opt_gen || opt_movem) && !strcmp(mnemo->name,"movem")) {
|
|
/* MOVEM */
|
|
int o = (oc & 0x0400) ? 1 : 0;
|
|
|
|
if (ip->op[o]->mode==MODE_Extended &&
|
|
(ip->op[o]->reg==REG_RnList || ip->op[o]->reg==REG_Immediate) &&
|
|
ip->op[o]->base[0]==NULL && !(ip->op[o]->flags & FL_NoOpt)) {
|
|
taddr list = ip->op[o]->extval[0];
|
|
int regs = cntones(list,16);
|
|
|
|
if (regs == 0) {
|
|
/* no registers in list - delete instruction */
|
|
ip->code = -1;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"movem deleted");
|
|
}
|
|
else if (regs == 1) {
|
|
/* a single register - MOVEM <ea>,Rn --> MOVE <ea>,Rn */
|
|
if ((opt_movem || (!(list&0xff) && o==1)) &&
|
|
!aindir_in_list(ip->op[o^1],list)) {
|
|
signed char r = bfffo(list,0,16);
|
|
|
|
ip->code = OC_MOVE;
|
|
ip->op[o]->mode = REGisAn(r) ? MODE_An : MODE_Dn;
|
|
ip->op[o]->reg = REGget(r);
|
|
if (final && (warn_opts>1 || (warn_opts && ((list&0xff) || o==0))))
|
|
cpu_error(51,"movem ea,Rn -> move ea,Rn");
|
|
}
|
|
}
|
|
else if (regs==2 && opt_speed &&
|
|
((cpu_type & m68040) || (!(cpu_type & (m68000|m68010)) &&
|
|
ip->op[o^1]->mode<=MODE_AnPreDec))) {
|
|
/* MOVEM with two registers is faster with two separate MOVEs,
|
|
when not using 68000 or 68010. Addressing modes with displacement
|
|
or extended addressing modes for 68040 only. */
|
|
taddr offs = ext=='l' ? 4 : 2;
|
|
|
|
if ((opt_movem || (!(list&0xff) && o==1)) &&
|
|
test_incr_ea(ip->op[o^1],offs) &&
|
|
!aindir_in_list(ip->op[o^1],list)) {
|
|
signed char r = bfffo(list,0,16);
|
|
instruction *ip2;
|
|
|
|
/* make two identical move instructions */
|
|
ip->code = OC_MOVE;
|
|
ip->op[o]->mode = REGisAn(r) ? MODE_An : MODE_Dn;
|
|
ip->op[o]->reg = REGget(r);
|
|
ip2 = copy_instruction(ip);
|
|
r = bfffo(list,r+1,16);
|
|
/* determine which register to move first */
|
|
if (o==0 && ip->op[1]->mode==MODE_AnPreDec) {
|
|
ip->op[o]->mode = REGisAn(r) ? MODE_An : MODE_Dn;
|
|
ip->op[o]->reg = REGget(r);
|
|
}
|
|
else {
|
|
ip2->op[o]->mode = REGisAn(r) ? MODE_An : MODE_Dn;
|
|
ip2->op[o]->reg = REGget(r);
|
|
}
|
|
/* increment EA of second move */
|
|
incr_ea(ip2->op[o^1],offs,final);
|
|
ip->ext.un.copy.next = ip2; /* append the 2nd MOVE */
|
|
if (final && (warn_opts>1 || (warn_opts && ((list&0xff) || o==0))))
|
|
cpu_error(51,"movem ea,Rm/Rn -> move ea,Rm + move ea,Rn");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (opt_gen && !strcmp(mnemo->name,"fmovem") &&
|
|
(mnemo->ext.opcode[1] & 0xcfff) == 0xc000) {
|
|
/* FMOVEM */
|
|
int o = (mnemo->ext.opcode[1] & 0x2000) ? 0 : 1;
|
|
|
|
if (ip->op[o]->mode==MODE_Extended && ip->op[o]->reg==REG_FPnList &&
|
|
ip->op[o]->base[0]==NULL && !(ip->op[o]->flags & FL_NoOpt) &&
|
|
ip->op[o]->extval[0]==0) {
|
|
/* no registers in list - delete instruction */
|
|
ip->code = -1;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"fmovem deleted");
|
|
}
|
|
}
|
|
|
|
else if (opt_gen && oc==0x4200 && ip->op[0]->mode==MODE_Dn && ext=='l') {
|
|
/* CLR.L Dn --> MOVEQ #0,Dn */
|
|
ip->code = OC_MOVEQ;
|
|
ip->qualifiers[0] = l_str;
|
|
if (final) {
|
|
ip->op[1] = ip->op[0];
|
|
ip->op[0] = new_operand();
|
|
ip->op[0]->mode = MODE_Extended;
|
|
ip->op[0]->reg = REG_Immediate;
|
|
ip->op[0]->value[0] = number_expr(0);
|
|
if (warn_opts>1)
|
|
cpu_error(51,"clr.l Dn -> moveq #0,Dn");
|
|
}
|
|
else
|
|
ip->op[0] = NULL;
|
|
}
|
|
|
|
else if (opt_gen && abs && (oc==0xc000 || oc==0x0200) &&
|
|
ip->op[0]->mode==MODE_Extended && ip->op[0]->reg==REG_Immediate) {
|
|
/* ANDI/AND #x,<ea> */
|
|
|
|
if ((cpu_type & (mcfb|mcfc)) && ip->op[1]->mode==MODE_Dn && ext=='l' &&
|
|
(val==0xff || val==0xffff)) {
|
|
/* ColdFire ISA_B/ISA_C: andi.l #$ff/$ffff,Dn -> mvz.b/w Dn,Dn */
|
|
ip->code = OC_MVZ;
|
|
ip->qualifiers[0] = val==0xff ? b_str : w_str;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
if (warn_opts>1)
|
|
cpu_error(51,"andi.l #$ff/$ffff,Dn -> mvz.b/w Dn,Dn");
|
|
}
|
|
ip->op[0] = ip->op[1];
|
|
}
|
|
else if ((val==0xff && ext=='b') || (val==0xffff && ext=='w') ||
|
|
(val==0xffffffff && ext=='l')) {
|
|
/* andi.b/w/l #$ff/$ffff/$ffffffff,<ea> -> tst.b/w/l <ea> */
|
|
ip->code = OC_TST;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
if (warn_opts>1)
|
|
cpu_error(51,"andi.b/w/l #$ff/$ffff/$fffffff -> tst");
|
|
}
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
}
|
|
else if (val==0 && ((cpu_type & (m68010up|mcf|cpu32)) || opt_clr)) {
|
|
/* andi.x #0,<ea> -> clr.x <ea> */
|
|
ip->code = OC_CLR;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
if (warn_opts>1)
|
|
cpu_error(51,"and #0 -> clr");
|
|
}
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
}
|
|
}
|
|
|
|
else if (opt_gen && abs && val==0 &&
|
|
(oc==0x8000 || oc==0x0000 || oc==0x0a00) &&
|
|
ip->op[0]->mode==MODE_Extended && ip->op[0]->reg==REG_Immediate) {
|
|
/* ORI/OR/EORI #0,<ea> --> TST <ea> */
|
|
ip->code = OC_TST;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
if (warn_opts>1)
|
|
cpu_error(51,"ori #0 -> tst");
|
|
}
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
}
|
|
|
|
else if (opt_gen && abs && oc==0x0a00) {
|
|
if ((ext=='b' && (val&0xff)==0xff) ||
|
|
(ext=='w' && (val&0xffff)==0xffff) || (ext=='l' && val==-1)) {
|
|
/* EORI #-1,<ea> --> NOT <ea> */
|
|
ip->code = OC_NOT;
|
|
if (final)
|
|
free_operand(ip->op[0]);
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"eori #-1 -> not");
|
|
}
|
|
}
|
|
|
|
else if ((oc==0x0600 || oc==0xd000 || oc==0xd0c0 ||
|
|
oc==0x0400 || oc==0x9000 || oc==0x90c0)) {
|
|
if (ip->op[0]->mode==MODE_Extended &&
|
|
ip->op[0]->reg==REG_Immediate && abs) {
|
|
/* ADD/ADDI/ADDA/SUB/SUBI/SUBA Immediate --> ADDQ/SUBQ */
|
|
if (opt_quick && val>=1 && val<=8) {
|
|
ip->code = (oc&0x4200) ? OC_ADDQ : OC_SUBQ;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"add/sub #x -> addq/subq #x");
|
|
}
|
|
else if ((oc&0x90c0) == 0x90c0) { /* ADDA/SUBA */
|
|
if (!(oc & 0x4000))
|
|
val = -val;
|
|
if (opt_gen && val == 0) {
|
|
ip->code = -1; /* delete ADDA/SUBA #0,An */
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"adda/suba #0,An deleted");
|
|
}
|
|
else if (opt_lea && val>=-0x8000 && val<=0x7fff) {
|
|
/* ADDA/SUBA Immediate --> LEA d(An),An */
|
|
ip->qualifiers[0] = l_str;
|
|
ip->code = OC_LEA;
|
|
ip->op[0]->mode = MODE_An16Disp;
|
|
ip->op[0]->reg = ip->op[1]->reg;
|
|
if (!(oc&0x4000) && final) {
|
|
free_expr(ip->op[0]->value[0]);
|
|
ip->op[0]->value[0] = number_expr(val);
|
|
}
|
|
else {
|
|
ip->op[0]->flags |= FL_DoNotEval;
|
|
ip->op[0]->extval[0] = val;
|
|
}
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"adda/suba #x,An -> lea (d,An),An");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (oc==0x41c0) {
|
|
if (ip->op[0]->mode==MODE_An16Disp && abs) {
|
|
if (opt_gen && ip->op[0]->reg==ip->op[1]->reg && val==0) {
|
|
/* delete LEA (0,An),An */
|
|
ip->code = -1;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"lea (0,An),An deleted");
|
|
}
|
|
else if (opt_lquick && ip->op[0]->reg==ip->op[1]->reg &&
|
|
val!=0 && val>=-8 && val<=8) {
|
|
/* LEA (d,An),An --> ADDQ/SUBQ #d,An */
|
|
if (val < 0) {
|
|
ip->code = OC_SUBQ;
|
|
val = -val;
|
|
if (final) {
|
|
free_op_exp(ip->op[0]);
|
|
ip->op[0]->value[0] = number_expr(val);
|
|
}
|
|
}
|
|
else
|
|
ip->code = OC_ADDQ;
|
|
ip->qualifiers[0] = l_str;
|
|
ip->op[0]->mode = MODE_Extended;
|
|
ip->op[0]->reg = REG_Immediate;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"lea (d,An),An -> addq/subq #d,An");
|
|
}
|
|
else if (opt_gen && (val<-0x8000 || val>0x7fff) &&
|
|
!(cpu_type & (m68020up|cpu32))) {
|
|
/* 68000/010: LEA (d32,Am),An --> MOVEA.L Am,An ; ADDA.L #d32,An */
|
|
if (ip->op[0]->reg == ip->op[1]->reg) {
|
|
/* special case: LEA (d32,An),An -> ADDA.L #d32,An */
|
|
ip->code = OC_ADDA;
|
|
ip->op[0]->mode = MODE_Extended;
|
|
ip->op[0]->reg = REG_Immediate;
|
|
ip->op[0]->flags |= FL_NoOpt;
|
|
}
|
|
else {
|
|
instruction *ip2;
|
|
|
|
ip2 = ip_doubleop(OC_ADDA,l_str,
|
|
MODE_Extended,REG_Immediate,
|
|
FL_NoOpt,0,ip->op[0]->value[0],
|
|
MODE_An,ip->op[1]->reg,
|
|
FL_NoOpt,0,NULL);
|
|
ip->code = OC_MOVEA;
|
|
ip->op[0]->mode = MODE_An;
|
|
ip->ext.un.copy.next = ip2; /* append the ADDA */
|
|
}
|
|
ip->qualifiers[0] = l_str;
|
|
if (final)
|
|
cpu_error(47); /* lea-displacement out of range, changed */
|
|
}
|
|
}
|
|
else if (opt_gen && ip->op[0]->mode==MODE_AnIndir &&
|
|
ip->op[0]->reg==ip->op[1]->reg) {
|
|
/* delete LEA (An),An */
|
|
ip->code = -1;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"lea (An),An deleted");
|
|
}
|
|
else if (opt_gen && abs && val==0 && ip->op[0]->mode==MODE_Extended &&
|
|
(ip->op[0]->reg==REG_AbsShort || ip->op[0]->reg==REG_AbsLong)) {
|
|
/* LEA 0,An -> SUBA.L An,An */
|
|
ip->code = OC_SUBA;
|
|
ip->qualifiers[0] = l_str;
|
|
ip->op[0]->mode = MODE_An;
|
|
ip->op[0]->reg = ip->op[1]->reg;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"lea 0,An -> suba.l An,An");
|
|
}
|
|
}
|
|
|
|
else if (opt_gen && oc==0x4808 && ip->op[1]->base[0]==NULL) {
|
|
/* LINK.L --> LINK.W */
|
|
taddr val = ip->op[1]->extval[0];
|
|
|
|
if (val>=-0x8000 && val<=0x7fff) {
|
|
ip->qualifiers[0] = w_str;
|
|
ip->code--;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"link.l -> link.w");
|
|
}
|
|
}
|
|
|
|
else if (opt_gen && oc==0x4e50 && ip->op[1]->base[0]==NULL &&
|
|
(cpu_type & (m68020up|cpu32))) {
|
|
/* LINK.W --> LINK.L */
|
|
taddr val = ip->op[1]->extval[0];
|
|
|
|
if (val<-0x8000 || val>0x7fff) {
|
|
ip->qualifiers[0] = l_str;
|
|
ip->code++;
|
|
if (final)
|
|
cpu_error(45); /* link.w changed to link.l */
|
|
}
|
|
}
|
|
|
|
else if (oc==0x0c00 || oc==0xb000 || oc==0xb0c0) {
|
|
if (opt_gen && abs && val==0 &&
|
|
ip->op[0]->mode==MODE_Extended && ip->op[0]->reg==REG_Immediate) {
|
|
/* CMP/CMPI/CMPA #0 --> TST */
|
|
if (oc!=0xb0c0 || (cpu_type & (m68020up|cpu32|mcf))) {
|
|
if (oc == 0xb0c0) {
|
|
/* optimize both CMP.W #0,An and CMP.L #0,An to TST.L An */
|
|
ip->code = OC_TST + 1;
|
|
ip->qualifiers[0] = l_str;
|
|
}
|
|
else
|
|
ip->code = OC_TST;
|
|
if (final)
|
|
free_operand(ip->op[0]);
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"cmp #0 -> tst");
|
|
}
|
|
}
|
|
}
|
|
|
|
else if ((((oc&0xf1ff)==0xe100 && opt_gen) ||
|
|
((oc&0xf1ff)==0xe108 && opt_lsl)) && !(cpu_type&(m68060|mcf))) {
|
|
/* LSL is only optimized with opt_lsl */
|
|
if ((oc&0x0e00) == 0x0200)
|
|
val = 1; /* ASL/LSL Dn (missing immediate operand assumed as 1) */
|
|
if (val == 1) {
|
|
/* ASL/LSL #1,Dn --> ADD Dn,Dn */
|
|
ip->code = OC_ADD;
|
|
ip->op[0]->mode = MODE_Dn;
|
|
if (!(oc&0x0e00))
|
|
ip->op[0]->reg = ip->op[1]->reg;
|
|
if (final && (warn_opts>1 || (warn_opts && (oc&0x0008))))
|
|
cpu_error(51,"asl/lsl #1 -> add");
|
|
}
|
|
else if (opt_speed && opt_lsl && val==2 && (ext=='b' || ext=='w')) {
|
|
/* ASL/LSL #2,Dn --> ADD Dn,Dn + ADD Dn,Dn (just .B and .W) */
|
|
ip->code = OC_ADD;
|
|
ip->op[0]->mode = MODE_Dn;
|
|
ip->op[0]->reg = ip->op[1]->reg;
|
|
ip->ext.un.copy.next = copy_instruction(ip);
|
|
if (final && (warn_opts>1 || (warn_opts && (oc&0x0008))))
|
|
cpu_error(51,"asl/lsl #2 -> add add");
|
|
}
|
|
}
|
|
|
|
else if (opt_fconst && (mnemo->ext.available & (mfpu|m68040up)) &&
|
|
abs && mnemo->operand_type[0]==FA && mnemo->operand_type[1]==F_ &&
|
|
ip->op[0]->mode==MODE_Extended && ip->op[0]->reg==REG_Immediate) {
|
|
unsigned char buf[12];
|
|
uint64_t man;
|
|
int exp;
|
|
|
|
if (ext == 'x') {
|
|
if (copy_float_exp(buf,ip->op[0],EXT_EXTENDED) != 0)
|
|
ext = 0;
|
|
}
|
|
else if (ext == 'd') {
|
|
if (copy_float_exp(buf,ip->op[0],EXT_DOUBLE) != 0)
|
|
ext = 0;
|
|
}
|
|
else if (ext == 's') {
|
|
if (copy_float_exp(buf,ip->op[0],EXT_SINGLE) != 0)
|
|
ext = 0;
|
|
}
|
|
else
|
|
ext = 0;
|
|
|
|
if (ext == 'x') {
|
|
/* Fxxx.X #m,FPn */
|
|
int i;
|
|
|
|
for (i=1,exp=0; i<12; i++)
|
|
exp |= buf[i];
|
|
if (!exp && (buf[0]==0x00 || buf[0]==0x80)) {
|
|
/* Special case: 0.0 or -0.0 -> convert to double.
|
|
We do not need to handle the "final" case here, because
|
|
0.0 is always translated to single-precision in the next step. */
|
|
ip->qualifiers[0] = d_str;
|
|
ext = 'd';
|
|
}
|
|
else {
|
|
exp = ((((int)buf[0]&0x7f)<<8) | (int)buf[1]) - 0x3fff;
|
|
man = readval(1,buf+4,8) & 0x7fffffffffffffffLL;
|
|
if (exp>=-0x3ff && exp<=0x400 && (man&0x7ff)==0) {
|
|
/* double precision would be sufficient, so convert */
|
|
int64_t v = (buf[0] & 0x80) ? -0x8000000000000000LL : 0;
|
|
|
|
v = v | ((int64_t)(exp+0x3ff) << 52) | (man >> 11);
|
|
if (final) {
|
|
free_op_exp(ip->op[0]);
|
|
ip->op[0]->value[0] = huge_expr(huge_from_int(v));
|
|
if (warn_opts>1)
|
|
cpu_error(51,"f<op>.x #m,FPn -> f<op>.d #m,FPn");
|
|
}
|
|
ip->qualifiers[0] = d_str;
|
|
ext = 'd';
|
|
setval(1,buf,8,v);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ext == 'd') {
|
|
/* Fxxx.D #m,FPn */
|
|
exp = ((((int)buf[0]&0x7f)<<4) | (((int)buf[1]&0xf0)>>4)) - 0x3ff;
|
|
man = readval(1,buf,8) & 0xfffffffffffffLL;
|
|
if ((exp>=-0x7f && exp<=0x80 && (man&0x1fffffff)==0) ||
|
|
(exp==-0x3ff && man==0)) { /* also allow all zeros for 0.0 */
|
|
/* single precision would be sufficient, so convert */
|
|
uint32_t v = (buf[0]&0x80) ? 0x80000000 : 0; /* m. sign */
|
|
|
|
if (exp != -0x3ff)
|
|
v |= (uint32_t)(exp+0x7f) << 23; /* exponent */
|
|
v |= (uint32_t)(man >> 29); /* mantissa */
|
|
if (final) {
|
|
free_op_exp(ip->op[0]);
|
|
ip->op[0]->value[0] = number_expr((taddr)v);
|
|
if (warn_opts>1)
|
|
cpu_error(51,"f<op>.d #m,FPn -> f<op>.s #m,FPn");
|
|
}
|
|
ip->qualifiers[0] = s_str;
|
|
ext = 's';
|
|
setval(1,buf,4,v);
|
|
}
|
|
}
|
|
|
|
if (final && (!strcmp(mnemo->name,"fdiv") || !strcmp(mnemo->name,"fsdiv")
|
|
|| !strcmp(mnemo->name,"fddiv") || !strcmp(mnemo->name,"fsgldiv"))) {
|
|
/* FxDIV.s #m,FPn and FxDIV.d #m,FPn
|
|
Can be optimized to FxMUL.s/FxMUL.d #1/m,FPn when m is a power of 2,
|
|
which is the case when the mantissa is zero. */
|
|
int optok = 0;
|
|
|
|
if (ext == 's') {
|
|
exp = ((((int)buf[0]&0x7f)<<1) | (((int)buf[1]&0x80)>>7)) - 0x7f;
|
|
if ((readval(1,buf,4) & 0x007fffffLL) == 0
|
|
&& exp!=-0x7f) {
|
|
setbits(1,buf,16,1,8,0x7f-exp); /* 8-bit exponent to offset 1 */
|
|
free_op_exp(ip->op[0]);
|
|
ip->op[0]->value[0] = number_expr(readval(1,buf,4));
|
|
optok = 1;
|
|
}
|
|
}
|
|
else if (ext == 'd') {
|
|
exp = ((((int)buf[0]&0x7f)<<4) | (((int)buf[1]&0xf0)>>4)) - 0x3ff;
|
|
if ((readval(1,buf,8) & 0xfffffffffffffLL) == 0 && exp!=-0x3ff) {
|
|
setbits(1,buf,16,1,11,0x3ff-exp); /* 11-bit exponent to offset 1 */
|
|
free_op_exp(ip->op[0]);
|
|
ip->op[0]->value[0] = huge_expr(huge_from_mem(1,buf,8));
|
|
optok = 1;
|
|
}
|
|
}
|
|
else if (ext == 'x') {
|
|
exp = ((((int)buf[0]&0x7f)<<8) | (int)buf[1]) - 0x3fff;
|
|
if ((readval(1,buf+4,8) & 0x7fffffffffffffffLL) == 0) {
|
|
setbits(1,buf,16,1,15,0x3fff-exp); /* 15-bit exponent to offset 1 */
|
|
free_op_exp(ip->op[0]);
|
|
ip->op[0]->value[0] = huge_expr(huge_from_mem(1,buf,12));
|
|
optok = 1;
|
|
}
|
|
}
|
|
if (optok) {
|
|
if (!strcmp(mnemo->name,"fdiv")) {
|
|
ip->code = OC_FMUL;
|
|
if (warn_opts>1)
|
|
cpu_error(51,"fdiv #m,FPn -> fmul #1/m,FPn");
|
|
}
|
|
else if (!strcmp(mnemo->name,"fsdiv")) {
|
|
ip->code = OC_FSMUL;
|
|
if (warn_opts>1)
|
|
cpu_error(51,"fsdiv #m,FPn -> fsmul #1/m,FPn");
|
|
}
|
|
else if (!strcmp(mnemo->name,"fddiv")) {
|
|
ip->code = OC_FDMUL;
|
|
if (warn_opts>1)
|
|
cpu_error(51,"fddiv #m,FPn -> fdmul #1/m,FPn");
|
|
}
|
|
else if (!strcmp(mnemo->name,"fsgldiv")) {
|
|
ip->code = OC_FSGLMUL;
|
|
if (warn_opts>1)
|
|
cpu_error(51,"fsgldiv #m,FPn -> fsglmul #1/m,FPn");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else if ((oc&0xfeff) == 0x80c0 && abs &&
|
|
ip->op[0]->mode==MODE_Extended && ip->op[0]->reg==REG_Immediate) {
|
|
/* DIVU.W/DIVS.W #x,Dn */
|
|
|
|
if (val == 0) {
|
|
/* divu.w/divs.w #0,Dn */
|
|
if (final)
|
|
cpu_error(60); /* division by zero */
|
|
}
|
|
else if (opt_div && val == 1 && (cpu_type & (mcfb|mcfc))) {
|
|
/* ColdFire ISA_B/ISA_C: divu.w/divs.w #1,Dn -> mvz.w Dn,Dn */
|
|
ip->code = OC_MVZ;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
if (warn_opts)
|
|
cpu_error(51,"divu/divs.w #1,Dn -> mvz.w Dn,Dn");
|
|
}
|
|
ip->op[0] = ip->op[1];
|
|
}
|
|
else if (opt_div && opt_speed && val ==-1 && (oc&0x0100) &&
|
|
(cpu_type & (mcfb|mcfc))) {
|
|
/* ColdFire ISA_B/ISA_C: divs.w #-1,Dn -> neg.w Dn + mvz.w Dn,Dn */
|
|
ip->code = OC_NEG;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
if (warn_opts)
|
|
cpu_error(51,"divs.w #-1,Dn -> neg.w Dn + mvz.w Dn,Dn");
|
|
}
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
ip->ext.un.copy.next = ip_doubleop(OC_MVZ,w_str,
|
|
MODE_Dn,ip->op[0]->reg,0,0,NULL,
|
|
MODE_Dn,ip->op[0]->reg,0,0,NULL);
|
|
}
|
|
}
|
|
|
|
else if (oc == 0x4c40) {
|
|
if (abs &&
|
|
ip->op[0]->mode==MODE_Extended && ip->op[0]->reg==REG_Immediate) {
|
|
/* DIVU.L/DIVS.L #x,Dn */
|
|
|
|
if (val == 0) {
|
|
/* divu.l/divs.l #0,Dn */
|
|
if (final)
|
|
cpu_error(60); /* division by zero */
|
|
}
|
|
else if (opt_div && mnemo->operand_type[1]==D_) {
|
|
if (val == 1) {
|
|
/* divu.l/divs.l #1,Dn -> tst.l Dn */
|
|
ip->code = OC_TST;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
if (warn_opts)
|
|
cpu_error(51,"divu/divs.l #1,Dn -> tst.l Dn");
|
|
}
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
}
|
|
else if (val==-1 && (mnemo->ext.opcode[1] & 0x0800)) {
|
|
/* divs.l #-1,Dn -> neg.l Dn */
|
|
ip->code = OC_NEG;
|
|
if (final) {
|
|
free_operand(ip->op[0]);
|
|
if (warn_opts)
|
|
cpu_error(51,"divs.l #-1,Dn -> neg.l Dn");
|
|
}
|
|
ip->op[0] = ip->op[1];
|
|
ip->op[1] = NULL;
|
|
}
|
|
else if (val>=2 && val<=0x100 && (mnemo->ext.opcode[1] & 0x0800)==0 &&
|
|
cntones(val,9)==1) {
|
|
/* divu.l #x,Dn -> lsr.l #x,Dn */
|
|
ip->code = OC_LSRI;
|
|
val = bfffo(val,1,9);
|
|
if (final) {
|
|
free_expr(ip->op[0]->value[0]);
|
|
ip->op[0]->value[0] = number_expr(val);
|
|
if (warn_opts)
|
|
cpu_error(51,"divu.l #x,Dn -> lsr.l #x,Dn");
|
|
}
|
|
else
|
|
ip->op[0]->flags |= FL_DoNotEval;
|
|
ip->op[0]->extval[0] = val;
|
|
}
|
|
}
|
|
}
|
|
else if (mnemo->operand_type[1]==DD && !(mnemo->ext.opcode[1]&0x0400) &&
|
|
((ip->op[1]->reg>>4) & 0xf) == (ip->op[1]->reg & 0xf))
|
|
/* DIVxL.L <ea>,Dn:Dn is DIVx.L <ea>,Dn */
|
|
cpu_error(65); /* Dr and Dq are identical! */
|
|
}
|
|
|
|
else if ((oc==0x4ec0 || oc==0x4e80) && !abs) {
|
|
if (opt_pc && !(ip->op[0]->flags & FL_NoOpt) &&
|
|
ip->op[0]->mode==MODE_Extended &&
|
|
(ip->op[0]->reg==REG_AbsLong || ip->op[0]->reg==REG_PC16Disp) &&
|
|
LOCREF(ip->op[0]->base[0]) && ip->op[0]->base[0]->sec==sec) {
|
|
/* JMP/JSR label --> BRA/BSR label */
|
|
taddr diff = val - cpc;
|
|
|
|
if (lastsize==0 || (diff==0 && (oc & 0x40))) {
|
|
ip->code = -1; /* delete a JMP to following location */
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"jmp deleted");
|
|
}
|
|
else if (diff>=-0x8000 && diff<=0x7fff) {
|
|
if (diff>=-0x80 && diff<=0x7f) {
|
|
if ((lastsize==2 && diff==0) ||
|
|
(lastsize==4 && diff==2))
|
|
ip->qualifiers[0] = w_str;
|
|
else
|
|
ip->qualifiers[0] = b_str;
|
|
ip->code = (oc & 0x40) ? OC_BRA : OC_BSR;
|
|
ip->op[0]->reg = REG_AbsLong;
|
|
}
|
|
else {
|
|
ip->qualifiers[0] = w_str;
|
|
ip->code = (oc & 0x40) ? OC_BRA : OC_BSR;
|
|
ip->op[0]->reg = REG_AbsLong;
|
|
}
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"jmp/jsr -> bra/bsr");
|
|
}
|
|
}
|
|
else if (!(ip->op[0]->flags & FL_NoOpt) &&
|
|
ip->op[0]->mode==MODE_Extended && ip->op[0]->reg==REG_AbsLong &&
|
|
EXTREF(ip->op[0]->base[0])) {
|
|
if (opt_sc) {
|
|
/* JMP/JSR extlabel --> JMP/JSR extlabel(PC) */
|
|
ip->op[0]->reg = REG_PC16Disp;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"jmp/jsr -> jmp/jsr (PC)");
|
|
}
|
|
else if (opt_jbra && !kick1hunks && (cpu_type & (m68020up|cpu32))) {
|
|
/* JMP/JSR extlabel -> BRA.L/BSR.L extlabel (68020+, CPU32) */
|
|
ip->qualifiers[0] = l_str;
|
|
ip->code = (oc & 0x40) ? OC_BRA : OC_BSR;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"jmp/jsr -> bra.l/bsr.l");
|
|
}
|
|
}
|
|
}
|
|
|
|
else if ((oc & 0xf000)==0x6000 && !abs) {
|
|
/* Bcc label */
|
|
if (opt_bra && ((ipflags&IFL_UNSIZED) || opt_allbra) &&
|
|
LOCREF(ip->op[0]->base[0]) && ip->op[0]->base[0]->sec==sec) {
|
|
taddr diff = val - cpc;
|
|
int resolvewarn = (sec->flags&RESOLVE_WARN)!=0;
|
|
|
|
switch (lastsize) {
|
|
case 0:
|
|
#if 0
|
|
/* keep branch deleted until no more optimizations took place */
|
|
if (diff!=-2 && done)
|
|
#else
|
|
if (diff != -2)
|
|
#endif
|
|
ip->qualifiers[0] = b_str;
|
|
else
|
|
ip->code = -1;
|
|
break;
|
|
case 2:
|
|
if (diff==0 && oc!=0x6100 && !resolvewarn)
|
|
ip->code = -1;
|
|
else if (diff<-0x80 || diff>0x7f || diff==0)
|
|
ip->qualifiers[0] = w_str;
|
|
else
|
|
ip->qualifiers[0] = b_str;
|
|
break;
|
|
case 4:
|
|
if (diff==2) {
|
|
if (oc!=0x6100 && !resolvewarn)
|
|
ip->code = -1;
|
|
else
|
|
ip->qualifiers[0] = w_str;
|
|
}
|
|
else if (diff>=-0x80 && diff<=0x80 && !resolvewarn) {
|
|
ip->qualifiers[0] = b_str;
|
|
}
|
|
else if (diff<-0x8000 || diff>0x7fff) {
|
|
if (cpu_type & (m68020up|cpu32|mcfb|mcfc)) {
|
|
ip->qualifiers[0] = l_str;
|
|
}
|
|
else {
|
|
ip->qualifiers[0] = emptystr;
|
|
ipflags |= IFL_RETAINLASTSIZE;
|
|
if (oc < 0x6200) {
|
|
/* BRA/BSR label --> JMP/JSR label */
|
|
ip->code = (oc==0x6000) ? OC_JMP : OC_JSR;
|
|
if (final)
|
|
cpu_error(46); /* branch out of range changed to jmp */
|
|
}
|
|
else {
|
|
/* Bcc label --> B!cc *+8, JMP label */
|
|
instruction *ip2;
|
|
|
|
/* make a new absolute JMP to the Bcc's destination */
|
|
ip2 = ip_singleop(OC_JMP,emptystr,
|
|
MODE_Extended,REG_AbsLong,
|
|
FL_NoOpt,0,ip->op[0]->value[0]);
|
|
ip->code += (oc&0x0100) ? -2 : 2; /* negate branch condition */
|
|
ip->qualifiers[0] = b_str;
|
|
ip->op[0]->flags |= FL_NoOpt;
|
|
ip->ext.un.copy.next = ip2; /* append the JMP */
|
|
if (final) {
|
|
/* assign "*+8" as the Bcc's expression */
|
|
ip->op[0]->value[0] = make_expr(ADD,curpc_expr(),
|
|
number_expr(phxass_compat ? 6 : 8));
|
|
cpu_error(46); /* branch out of range changed to jmp */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ip->qualifiers[0] = w_str;
|
|
break;
|
|
case 6:
|
|
if (diff>=-0x8000 && diff<=0x7fff && !resolvewarn)
|
|
ip->qualifiers[0] = w_str;
|
|
else
|
|
ip->qualifiers[0] = l_str;
|
|
break;
|
|
default:
|
|
if (ext == '\0')
|
|
ip->qualifiers[0] = w_str;
|
|
break;
|
|
}
|
|
if (final && warn_opts>1) {
|
|
/* print the finally performed kind optimization */
|
|
if (ip->code == -1)
|
|
cpu_error(51,"branch deleted");
|
|
else {
|
|
char new_ext = tolower((unsigned char)ip->qualifiers[0][0]);
|
|
int oldsize = branch_size(orig_ext);
|
|
int newsize = branch_size(new_ext);
|
|
|
|
if (newsize != oldsize)
|
|
cpu_error(newsize<oldsize?53:54,new_ext);
|
|
}
|
|
}
|
|
}
|
|
else if (opt_branop && oc!=0x6100 && val-cpc==0 &&
|
|
(ext=='b' || ext=='s') && LOCREF(ip->op[0]->base[0]) &&
|
|
ip->op[0]->base[0]->sec==sec) {
|
|
/* short-branch with zero-distance which cannot be optimized
|
|
is turned into a NOP */
|
|
ip->qualifiers[0] = emptystr;
|
|
ip->code = OC_NOOP;
|
|
if (final)
|
|
free_operand(ip->op[0]);
|
|
ip->op[0] = NULL;
|
|
if (final)
|
|
cpu_error(57); /* bra.b *+2 turned into a nop */
|
|
}
|
|
else if (opt_brajmp && ip->op[0]->base[0]->sec!=sec &&
|
|
LOCREF(ip->op[0]->base[0])) {
|
|
/* reference to label from different section */
|
|
ip->qualifiers[0] = emptystr;
|
|
if (oc < 0x6200) {
|
|
/* BRA/BSR label --> JMP/JSR label */
|
|
ip->code = (oc==0x6000) ? OC_JMP : OC_JSR;
|
|
if (final && warn_opts>1)
|
|
cpu_error(52,"bra/bsr -> jmp/jsr");
|
|
}
|
|
else {
|
|
/* Bcc label --> B!cc *+8, JMP label */
|
|
instruction *ip2;
|
|
|
|
/* make a new absolute JMP to the Bcc's destination */
|
|
ip2 = ip_singleop(OC_JMP,emptystr,
|
|
MODE_Extended,REG_AbsLong,
|
|
FL_NoOpt,0,ip->op[0]->value[0]);
|
|
ip->code += (oc&0x0100) ? -2 : 2; /* negate branch condition */
|
|
ip->qualifiers[0] = b_str;
|
|
ip->op[0]->flags |= FL_NoOpt;
|
|
ip->ext.un.copy.next = ip2; /* append the JMP */
|
|
if (final) {
|
|
/* assign "*+8" as the Bcc's expression */
|
|
ip->op[0]->value[0] = make_expr(ADD,curpc_expr(),
|
|
number_expr(phxass_compat ? 6 : 8));
|
|
if (warn_opts>1)
|
|
cpu_error(52,"b<cc> label -> b<!cc> *+8, jmp label");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else if ((oc & 0xff80)==0xf080 && ip->code!=OC_FNOP) {
|
|
/* cpBcc label */
|
|
if (opt_bra && ((ipflags&IFL_UNSIZED) || opt_allbra) &&
|
|
LOCREF(ip->op[0]->base[0]) && ip->op[0]->base[0]->sec==sec) {
|
|
taddr diff = val - cpc;
|
|
|
|
switch (lastsize) {
|
|
case 4:
|
|
if (diff<-0x8000 || diff>0x7fff)
|
|
ip->qualifiers[0] = l_str;
|
|
else
|
|
ip->qualifiers[0] = w_str;
|
|
break;
|
|
case 6:
|
|
if (diff>=-0x8000 && diff<=0x7fff)
|
|
ip->qualifiers[0] = w_str;
|
|
else
|
|
ip->qualifiers[0] = l_str;
|
|
break;
|
|
default:
|
|
if (ext == '\0')
|
|
ip->qualifiers[0] = w_str;
|
|
break;
|
|
}
|
|
if (final && warn_opts>1 && (ipflags&IFL_UNSIZED))
|
|
cpu_error(53,*(ip->qualifiers[0]));
|
|
}
|
|
}
|
|
else if (oc==0xf000 && !(mnemo->ext.available & mfloat)
|
|
&& (mnemo->ext.opcode[1] & 0xe000) == 0x8000) {
|
|
if (final && ip->op[2]!=NULL && ip->op[2]->base[0]==NULL
|
|
&& ip->op[2]->extval[0]==0 && ip->op[3]!=NULL)
|
|
cpu_error(64); /* An operand at level #0 causes F-line Exception */
|
|
}
|
|
|
|
if (opt_immaddr && abs && ext=='l' && ip->op[0]!=NULL &&
|
|
ip->op[0]->mode==MODE_Extended && ip->op[0]->reg==REG_Immediate &&
|
|
ip->op[1]!=NULL && ip->op[1]->mode==MODE_An &&
|
|
val>=-0x8000 && val<=0x7fff &&
|
|
!(cpu_type & mcf) && (mnemonics[ip->code].ext.size & SIZE_WORD) &&
|
|
(mnemonics[ip->code].ext.opcode[0] & 0xfeff) != 0x5000) {
|
|
/* op.L #x,An --> op.W #x,An (if not ColdFire and not ADDQ/SUBQ) */
|
|
ip->qualifiers[0] = w_str;
|
|
if (final && warn_opts>1)
|
|
cpu_error(51,"<op>.L #x,An -> <op>.W #x,An");
|
|
}
|
|
|
|
/* Try to optimize operands again, in case an instruction was optimized. */
|
|
/* WARNING: 'ext' may be trashed at this point! */
|
|
|
|
for (ip=iplist; ip; ip=ip->ext.un.copy.next) {
|
|
if (ip->code >= 0) {
|
|
for (i=0; i<MAX_OPERANDS && ip->op[i]!=NULL; i++)
|
|
optimize_oper(ip->op[i],&optypes[mnemonics[ip->code].operand_type[i]],
|
|
sec,pc,cpc,final);
|
|
}
|
|
}
|
|
|
|
return ipflags;
|
|
}
|
|
|
|
|
|
static size_t oper_size(instruction *ip,operand *op,struct optype *ot)
|
|
/* returns number of bytes for a single operand */
|
|
{
|
|
int mode = op->mode;
|
|
int reg = op->reg;
|
|
|
|
if (ot->flags & OTF_NOSIZE) {
|
|
return 0;
|
|
}
|
|
else if (mode==MODE_An16Disp || (mode==MODE_Extended &&
|
|
(reg==REG_PC16Disp || reg==REG_AbsShort))) {
|
|
return 2;
|
|
}
|
|
else if (mode==MODE_Extended && reg==REG_AbsLong) {
|
|
if (ot->flags & OTF_BRANCH)
|
|
return (taddr)branch_size(ip->qualifiers[0] ?
|
|
tolower((unsigned char)ip->qualifiers[0][0]) :
|
|
'\0');
|
|
else
|
|
return 4;
|
|
}
|
|
else if (mode==MODE_Extended && reg==REG_Immediate) {
|
|
switch (ip->qualifiers[0] ?
|
|
tolower((unsigned char)ip->qualifiers[0][0]) : '\0') {
|
|
case 'b':
|
|
case 'w':
|
|
return 2;
|
|
case 'l':
|
|
case 's':
|
|
return 4;
|
|
case 'q':
|
|
case 'd':
|
|
return 8;
|
|
case 'x':
|
|
case 'p':
|
|
return 12;
|
|
}
|
|
}
|
|
else if (mode==MODE_An8Format ||
|
|
(mode==MODE_Extended && reg==REG_PC8Format)) {
|
|
if (op->flags & FL_UsesFormat) {
|
|
size_t n = 2;
|
|
|
|
if (op->format & FW_FullFormat) {
|
|
if (FW_getBDSize(op->format) == FW_Word)
|
|
n += 2;
|
|
else if (FW_getBDSize(op->format) == FW_Long)
|
|
n += 4;
|
|
if (FW_getIndSize(op->format) == FW_Word)
|
|
n += 2;
|
|
else if (FW_getIndSize(op->format) == FW_Long)
|
|
n += 4;
|
|
}
|
|
return n;
|
|
}
|
|
else
|
|
ierror(0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static size_t iplist_size(instruction *ip)
|
|
{
|
|
size_t size = 0;
|
|
mnemonic *mnemo;
|
|
int i;
|
|
|
|
do {
|
|
if (ip->code >= 0) {
|
|
mnemo = &mnemonics[ip->code];
|
|
size += S_OPCODE_SIZE(mnemo->ext.size) << 1;
|
|
for (i=0; i<MAX_OPERANDS && ip->op[i]!=NULL; i++)
|
|
size += oper_size(ip,ip->op[i],&optypes[mnemo->operand_type[i]]);
|
|
}
|
|
}
|
|
while ((ip = ip->ext.un.copy.next) != NULL);
|
|
return size;
|
|
}
|
|
|
|
|
|
size_t instruction_size(instruction *realip,section *sec,taddr pc)
|
|
/* Calculate the size of the current instruction; must be identical
|
|
to the data created by eval_instruction. */
|
|
{
|
|
mnemonic *mnemo = &mnemonics[realip->code];
|
|
char ext = realip->qualifiers[0] ?
|
|
tolower((unsigned char)realip->qualifiers[0][0]) : '\0';
|
|
int i;
|
|
size_t size;
|
|
instruction *ip;
|
|
unsigned char extflags;
|
|
uint16_t extsize;
|
|
|
|
/* check if current mnemonic is valid for selected cpu-type */
|
|
while (!(mnemo->ext.available & cpu_type)) {
|
|
/* try next mnemonic from table, when it has still the same
|
|
name and all operand-types */
|
|
mnemonic *lastm = mnemo;
|
|
|
|
mnemo++;
|
|
if (strcmp(lastm->name,mnemo->name) || !optypes_subset(lastm,mnemo))
|
|
cpu_error(0); /* instruction not supported */
|
|
realip->code++;
|
|
}
|
|
|
|
extsize = mnemo->ext.size;
|
|
|
|
/* remember the instruction's original extension, before optimizations */
|
|
if (realip->ext.un.real.orig_ext < 0)
|
|
realip->ext.un.real.orig_ext = (signed char)ext;
|
|
|
|
if (opt_allbra && ign_unambig_ext) {
|
|
/* Strip the size extension from branch instructions, no matter if
|
|
illegal or not. The optimizer will find the best size. */
|
|
for (i=0; i<MAX_OPERANDS; i++) {
|
|
if (mnemonics[realip->code].operand_type[i] == BR) {
|
|
ext = '\0';
|
|
realip->qualifiers[0] = emptystr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ext == '\0') {
|
|
/* remember when developer didn't specify a size extension */
|
|
realip->ext.un.real.flags |= IFL_UNSIZED;
|
|
|
|
/* assign a default extension for sized instructions, when missing */
|
|
if ((extsize & SIZE_MASK) != 0) {
|
|
if ((extsize & S_CFCHECK) && (cpu_type & mcf))
|
|
extsize &= ~(SIZE_BYTE|SIZE_WORD); /* SIZE_LONG for ColdFire only */
|
|
if ((extsize & SIZE_LONG) && (cpu_type & mcf)) /* ColdFire prefers .l */
|
|
realip->qualifiers[0] = l_str;
|
|
else if (extsize & SIZE_WORD)
|
|
realip->qualifiers[0] = w_str;
|
|
else if (extsize & SIZE_BYTE)
|
|
realip->qualifiers[0] = b_str;
|
|
else if (extsize & SIZE_LONG)
|
|
realip->qualifiers[0] = l_str;
|
|
else if (extsize & SIZE_EXTENDED)
|
|
realip->qualifiers[0] = x_str;
|
|
else if (extsize & SIZE_SINGLE)
|
|
realip->qualifiers[0] = s_str;
|
|
else if (extsize & SIZE_DOUBLE)
|
|
realip->qualifiers[0] = d_str;
|
|
else if (extsize & SIZE_PACKED)
|
|
realip->qualifiers[0] = p_str;
|
|
else
|
|
ierror(0);
|
|
ext = realip->qualifiers[0][0];
|
|
}
|
|
}
|
|
|
|
/* check if opcode extension is valid */
|
|
if ((mnemo->ext.size & SIZE_MASK) == SIZE_UNSIZED) {
|
|
if (ext != '\0') {
|
|
if (!ign_unambig_ext)
|
|
cpu_error(35); /* extension for unsized instruction ignored */
|
|
ext = '\0';
|
|
realip->qualifiers[0] = emptystr;
|
|
}
|
|
}
|
|
else {
|
|
int uacode;
|
|
int err = 0;
|
|
uint16_t extsize = mnemo->ext.size;
|
|
uint16_t sz = lc_ext_to_size(ext); /* convert ext. to SIZE-code */
|
|
|
|
if ((mnemo->ext.size&SIZE_UNAMBIG) && (mnemo->ext.available&cpu_type))
|
|
uacode = realip->code; /* last mnemonic with unambiguous size ext. */
|
|
else
|
|
uacode = -1;
|
|
|
|
/* Find a mnemonic with same name and operands which matches the
|
|
given size extension. */
|
|
while (!((((extsize&S_CFCHECK) && (cpu_type&mcf)) ?
|
|
(extsize & ~(SIZE_BYTE|SIZE_WORD)) : extsize) & sz)) {
|
|
mnemo++;
|
|
if ((err = strcmp(mnemonics[realip->code].name,mnemo->name)) != 0)
|
|
break;
|
|
if ((err = !optypes_subset(&mnemonics[realip->code],mnemo)) != 0)
|
|
break;
|
|
|
|
realip->code++;
|
|
extsize = mnemo->ext.size;
|
|
if ((mnemo->ext.size&SIZE_UNAMBIG) && (mnemo->ext.available&cpu_type))
|
|
uacode = realip->code;
|
|
}
|
|
|
|
if (err) {
|
|
if (ign_unambig_ext && uacode>=0) {
|
|
/* size extension is wrong, but we can guess the right one */
|
|
realip->code = uacode;
|
|
mnemo = &mnemonics[uacode];
|
|
extsize = mnemo->ext.size;
|
|
ext = '\0';
|
|
realip->qualifiers[0] = emptystr;
|
|
}
|
|
else
|
|
cpu_error(34); /* illegal opcode extension */
|
|
}
|
|
|
|
if (!(mnemo->ext.available & cpu_type))
|
|
cpu_error(0); /* instruction not supported */
|
|
}
|
|
|
|
/* check if we are uncertain about the side of a register list operand */
|
|
#if 0
|
|
/* @@@ Why should we forbid optimizations here? FL_PossRegList is useless? */
|
|
if (realip->op[0]!=NULL && realip->op[1]!=NULL) {
|
|
if ((realip->op[0]->flags & FL_PossRegList) &&
|
|
realip->op[1]->mode==MODE_Extended &&
|
|
realip->op[1]->reg==REG_AbsLong) {
|
|
realip->op[0]->flags &= ~FL_PossRegList;
|
|
realip->op[0]->flags |= FL_NoOpt;
|
|
realip->op[1]->flags |= FL_NoOpt;
|
|
}
|
|
else if ((realip->op[1]->flags & FL_PossRegList) &&
|
|
realip->op[0]->mode==MODE_Extended &&
|
|
realip->op[0]->reg==REG_AbsLong) {
|
|
realip->op[1]->flags &= ~FL_PossRegList;
|
|
realip->op[1]->flags |= FL_NoOpt;
|
|
realip->op[0]->flags |= FL_NoOpt;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* fix instructions, which were not correctly recognized through
|
|
parse_operand() due to missing information. */
|
|
if (mnemo->ext.opcode[0]==0xf518 && !(cpu_type & m68040up)) {
|
|
/* try 68030/68851 PFLUSHA instead */
|
|
realip->code++;
|
|
}
|
|
|
|
/* do optimizations on a copy of the current instruction */
|
|
ipslot = 0;
|
|
ip = copy_instruction(realip);
|
|
extflags = optimize_instruction(ip,sec,pc,0);
|
|
|
|
/* and determine current size (from optimized copy) */
|
|
size = iplist_size(ip);
|
|
if (!(extflags & IFL_RETAINLASTSIZE))
|
|
realip->ext.un.real.last_size = size; /* remember size for next pass */
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
static void write_val(unsigned char *d,int pos,int size,taddr val,int sign)
|
|
/* insert value 'val' with 'size' bits at bit-position 'pos' */
|
|
{
|
|
if (typechk) {
|
|
if (sign) {
|
|
if ((val > (1L << (size-1)) - 1) || (val < -(1L << (size-1)))) {
|
|
if (val > 0 && val < (1L << size))
|
|
cpu_error(27,val,-(1L<<(size-1)),
|
|
(1L<<(size-1))-1,
|
|
val-(1L<<size)); /* using signed operand as unsigned */
|
|
else
|
|
cpu_error(25,val,-(1L<<(size-1)),
|
|
(1L<<(size-1))-1); /* operand value out of range */
|
|
}
|
|
}
|
|
else {
|
|
if ((utaddr)val > (1L << size) - 1)
|
|
cpu_error(25,val,0,(1L<<size)-1); /* operand value out of range */
|
|
}
|
|
}
|
|
|
|
d += pos>>3;
|
|
pos &= 7;
|
|
|
|
while (size > 0) {
|
|
int shift = 8-pos-size;
|
|
unsigned char v;
|
|
|
|
if (shift > 0)
|
|
v = (val << shift) & 0xff;
|
|
else if (shift < 0)
|
|
v = (val >> -shift) & 0xff;
|
|
else
|
|
v = val & 0xff;
|
|
*d++ |= v;
|
|
size -= 8-pos;
|
|
pos = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static unsigned char *write_branch(dblock *db,unsigned char *d,operand *op,
|
|
char ext,section *sec,taddr pc,int bcc)
|
|
/* calculate and write branch displacement of desired size, handle relocs */
|
|
{
|
|
if (!bcc && ext!='w' && ext!='l')
|
|
ierror(0);
|
|
|
|
if (op->base[0]) {
|
|
|
|
if (is_pc_reloc(op->base[0],sec)) {
|
|
/* external branch label, or label from different section */
|
|
taddr addend = op->extval[0];
|
|
int size,offset;
|
|
|
|
switch (ext) {
|
|
case 'b':
|
|
case 's':
|
|
addend--; /* reloc-offset is stored 1 byte before PC-location */
|
|
*(d-1) = addend & 0xff;
|
|
size = 8;
|
|
offset = 1;
|
|
break;
|
|
case 'l':
|
|
if (cpu_type & (m68020up|cpu32|mcfb|mcfc|m68881|m68882|m68851)) {
|
|
if (bcc)
|
|
*(d-1) = 0xff;
|
|
offset = d - (unsigned char *)db->data;
|
|
d = setval(1,d,4,addend);
|
|
size = 32;
|
|
}
|
|
else
|
|
cpu_error(0); /* instruction not supported */
|
|
break;
|
|
case 'w':
|
|
if (bcc)
|
|
*(d-1) = 0;
|
|
offset = d - (unsigned char *)db->data;
|
|
d = setval(1,d,2,addend);
|
|
size = 16;
|
|
break;
|
|
default:
|
|
cpu_error(34); /* illegal opcode extension */
|
|
break;
|
|
}
|
|
add_extnreloc(&db->relocs,op->base[0],addend,REL_PC,0,size,offset);
|
|
}
|
|
|
|
else { /* known label from same section, can be resolved immediately */
|
|
taddr diff = op->extval[0] - pc;
|
|
|
|
switch (ext) {
|
|
case 'b':
|
|
case 's':
|
|
if (diff>=-0x80 && diff<=0x7f && diff!=0)
|
|
*(d-1) = diff & 0xff;
|
|
else
|
|
cpu_error(28); /* branch destination out of range */
|
|
break;
|
|
case 'l':
|
|
if (cpu_type & (m68020up|cpu32|mcfb|mcfc|m68881|m68882|m68851)) {
|
|
if (bcc)
|
|
*(d-1) = 0xff;
|
|
d = setval(1,d,4,diff);
|
|
}
|
|
else
|
|
cpu_error(0); /* instruction not supported */
|
|
break;
|
|
case 'w':
|
|
if (diff>=-0x8000 && diff<=0x7fff) {
|
|
if (bcc)
|
|
*(d-1) = 0;
|
|
d = setval(1,d,2,diff);
|
|
}
|
|
else
|
|
cpu_error(28); /* branch destination out of range */
|
|
break;
|
|
default:
|
|
cpu_error(34); /* illegal opcode extension */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
cpu_error(26); /* label in operand required */
|
|
|
|
return d;
|
|
}
|
|
|
|
|
|
static unsigned char *write_extval(int num,size_t size,dblock *db,
|
|
unsigned char *d,operand *op,int rtype)
|
|
{
|
|
if (rtype==REL_ABS && op->basetype[num]==BASE_PCREL)
|
|
op->extval[num] += d - db->data; /* fix addend for label differences */
|
|
|
|
return setval(1,d,size,op->extval[num]);
|
|
}
|
|
|
|
|
|
static unsigned char *write_ea_ext(dblock *db,unsigned char *d,operand *op,
|
|
char ext,section *sec,taddr pc)
|
|
/* write effective address extension words, handle relocs */
|
|
{
|
|
if (op->mode>MODE_Extended ||
|
|
(op->mode==MODE_Extended && op->reg>REG_Immediate)) {
|
|
ierror(0);
|
|
}
|
|
else if (op->mode >= MODE_An16Disp) {
|
|
int rtype = REL_NONE;
|
|
int roffs = d - (unsigned char *)db->data;
|
|
int rsize = 0;
|
|
int ortype = REL_NONE;
|
|
int orsize = 0;
|
|
|
|
if (op->flags & FL_020up) {
|
|
if (!(cpu_type & (m68020up|cpu32))) {
|
|
cpu_error(0); /* instruction not supported */
|
|
}
|
|
else if (op->flags & FL_noCPU32) {
|
|
if (cpu_type & cpu32)
|
|
cpu_error(0); /* instruction not supported */
|
|
}
|
|
}
|
|
|
|
if (op->mode == MODE_An16Disp) {
|
|
/* d16(An) needs one extension word */
|
|
if (op->base[0]) {
|
|
rsize = 16;
|
|
if ((EXTREF(op->base[0]) && op->reg!=sdreg) ||
|
|
op->basetype[0]==BASE_PCREL)
|
|
rtype = REL_ABS;
|
|
else if (op->basetype[0]==BASE_OK)
|
|
rtype = REL_SD;
|
|
else
|
|
general_error(38); /* illegal relocation */
|
|
}
|
|
if (rtype == REL_SD) {
|
|
if (typechk && (op->extval[0]<0 || op->extval[0]>0xffff))
|
|
cpu_error(29); /* displacement out of range */
|
|
}
|
|
else {
|
|
if (typechk && (op->extval[0]<-0x8000 || op->extval[0]>0x7fff))
|
|
cpu_error(29); /* displacement out of range */
|
|
}
|
|
d = write_extval(0,2,db,d,op,rtype);
|
|
}
|
|
|
|
else if (op->mode == MODE_An8Format) {
|
|
if (!(op->flags & FL_UsesFormat))
|
|
ierror(0);
|
|
if (op->format & FW_FullFormat) {
|
|
/* ([bd,An,Rn.x*s],od): format word + base- and outer-displacement */
|
|
d = setval(1,d,2,op->format);
|
|
roffs += 2;
|
|
if (FW_getBDSize(op->format) == FW_Word) {
|
|
if (typechk && (op->extval[0]<-0x8000 || op->extval[0]>0x7fff))
|
|
cpu_error(29); /* displacement out of range */
|
|
if (op->base[0]) {
|
|
rsize = 16;
|
|
if (EXTREF(op->base[0]) ||
|
|
(LOCREF(op->base[0]) && op->basetype[0]==BASE_PCREL))
|
|
rtype = REL_ABS;
|
|
else
|
|
cpu_error(30); /* absolute displacement expected */
|
|
}
|
|
d = write_extval(0,2,db,d,op,rtype);
|
|
}
|
|
else if (FW_getBDSize(op->format) == FW_Long) {
|
|
if (op->base[0]) {
|
|
rtype = REL_ABS;
|
|
rsize = 32;
|
|
}
|
|
d = write_extval(0,4,db,d,op,rtype);
|
|
}
|
|
if (FW_getIndSize(op->format) == FW_Word) {
|
|
if (typechk && (op->extval[1]<-0x8000 || op->extval[1]>0x7fff))
|
|
cpu_error(29); /* displacement out of range */
|
|
if (op->base[1]) {
|
|
orsize = 16;
|
|
if (EXTREF(op->base[1]) ||
|
|
(LOCREF(op->base[1]) && op->basetype[1]==BASE_PCREL))
|
|
ortype = REL_ABS;
|
|
else
|
|
cpu_error(30); /* absolute displacement expected */
|
|
}
|
|
d = write_extval(1,2,db,d,op,ortype);
|
|
}
|
|
else if (FW_getIndSize(op->format) == FW_Long) {
|
|
if (op->base[1]) {
|
|
ortype = REL_ABS;
|
|
orsize = 32;
|
|
}
|
|
d = write_extval(1,4,db,d,op,ortype);
|
|
}
|
|
}
|
|
else {
|
|
/* (d8,An,Rn.x*s) needs one format word as extension */
|
|
if (typechk && (op->extval[0]<-0x80 || op->extval[0]>0x7f))
|
|
cpu_error(29); /* displacement out of range */
|
|
if (op->base[0]) {
|
|
rsize = 8;
|
|
if (EXTREF(op->base[0]) ||
|
|
(LOCREF(op->base[0]) && op->basetype[0]==BASE_PCREL))
|
|
rtype = REL_ABS;
|
|
else
|
|
cpu_error(30); /* absolute displacement expected */
|
|
}
|
|
*d++ = (op->format>>8) & 0xff;
|
|
*d++ = op->extval[0] & 0xff;
|
|
}
|
|
}
|
|
|
|
else if (op->mode == MODE_Extended) {
|
|
|
|
if (op->reg == REG_PC16Disp) {
|
|
/* d16(PC) needs one extension word */
|
|
taddr disp = op->extval[0];
|
|
|
|
if (op->base[0]) {
|
|
if (is_pc_reloc(op->base[0],sec)) {
|
|
rtype = REL_PC;
|
|
rsize = 16;
|
|
}
|
|
else
|
|
disp = op->extval[0] - pc;
|
|
}
|
|
if (typechk && (disp<-0x8000 || disp>0x7fff))
|
|
cpu_error(29); /* displacement out of range */
|
|
d = setval(1,d,2,disp);
|
|
}
|
|
|
|
else if (op->reg == REG_PC8Format) {
|
|
taddr disp = op->extval[0];
|
|
|
|
if (!(op->flags & FL_UsesFormat))
|
|
ierror(0);
|
|
if (op->format & FW_FullFormat) {
|
|
/* ([bd,PC,Rn.x*s],od): format word + base- and outer-displacement */
|
|
d = setval(1,d,2,op->format);
|
|
roffs += 2;
|
|
if (FW_getBDSize(op->format) == FW_Word) {
|
|
if (op->base[0]) {
|
|
if (is_pc_reloc(op->base[0],sec)) {
|
|
rtype = REL_PC;
|
|
rsize = 16;
|
|
op->extval[0] += 2; /* pc-relative xref fix */
|
|
disp += 2;
|
|
}
|
|
else
|
|
disp = op->extval[0] - pc;
|
|
}
|
|
if (typechk && (disp<-0x8000 || disp>0x7fff))
|
|
cpu_error(29); /* displacement out of range */
|
|
d = setval(1,d,2,disp);
|
|
}
|
|
else if (FW_getBDSize(op->format) == FW_Long) {
|
|
if (op->base[0]) {
|
|
if (is_pc_reloc(op->base[0],sec)) {
|
|
rtype = REL_PC;
|
|
rsize = 32;
|
|
op->extval[0] += 2; /* pc-relative xref fix */
|
|
disp += 2;
|
|
}
|
|
else
|
|
disp = op->extval[0] - pc;
|
|
}
|
|
d = setval(1,d,4,disp);
|
|
}
|
|
if (FW_getIndSize(op->format) == FW_Word) {
|
|
if (typechk && (op->extval[1]<-0x8000 || op->extval[1]>0x7fff))
|
|
cpu_error(29); /* displacement out of range */
|
|
if (op->base[1]) {
|
|
if (EXTREF(op->base[1])) {
|
|
ortype = REL_ABS;
|
|
orsize = 16;
|
|
}
|
|
else
|
|
cpu_error(30); /* absolute displacement expected */
|
|
}
|
|
d = write_extval(1,2,db,d,op,ortype);
|
|
}
|
|
else if (FW_getIndSize(op->format) == FW_Long) {
|
|
if (op->base[1]) {
|
|
ortype = REL_ABS;
|
|
orsize = 32;
|
|
}
|
|
d = write_extval(1,4,db,d,op,ortype);
|
|
}
|
|
}
|
|
else {
|
|
/* (d8,PC,Rn.x*s) needs one format word as extension */
|
|
if (op->base[0]) {
|
|
if (is_pc_reloc(op->base[0],sec)) {
|
|
rtype = REL_PC;
|
|
rsize = 8;
|
|
roffs++;
|
|
op->extval[0] += 1; /* pc-relative xref fix */
|
|
disp += 1;
|
|
}
|
|
else
|
|
disp = op->extval[0] - pc;
|
|
}
|
|
if (typechk && (disp<-0x80 || disp>0x7f))
|
|
cpu_error(29); /* displacement out of range */
|
|
*d++ = (op->format>>8) & 0xff;
|
|
*d++ = disp & 0xff;
|
|
}
|
|
}
|
|
|
|
else if (op->reg == REG_AbsShort) {
|
|
/* label.w */
|
|
if (typechk && (op->extval[0]<-0x8000 || op->extval[0]>0x7fff))
|
|
cpu_error(32); /* absolute short address out of range */
|
|
if (op->base[0]) {
|
|
/* @@@ should we allow 16-bit address relocations? */
|
|
rtype = REL_ABS;
|
|
rsize = 16;
|
|
}
|
|
d = write_extval(0,2,db,d,op,rtype);
|
|
}
|
|
|
|
else if (op->reg == REG_AbsLong) {
|
|
/* label.l */
|
|
if (op->base[0]) {
|
|
rtype = REL_ABS;
|
|
rsize = 32;
|
|
}
|
|
d = write_extval(0,4,db,d,op,rtype);
|
|
}
|
|
|
|
else if (op->reg == REG_Immediate) {
|
|
/* #immediate */
|
|
int err;
|
|
|
|
if (op->base[0] != NULL)
|
|
rtype = REL_ABS;
|
|
|
|
switch (ext) {
|
|
case 'b':
|
|
if (op->flags & FL_ExtVal0) {
|
|
roffs++;
|
|
rsize = 8;
|
|
*d++ = 0;
|
|
d = write_extval(0,1,db,d,op,rtype);
|
|
if (typechk && (op->extval[0]<-0x80 || op->extval[0]>0xff))
|
|
cpu_error(36); /* immediate operand out of range */
|
|
}
|
|
else
|
|
cpu_error(37); /* immediate operand has illegal type */
|
|
break;
|
|
case 'w':
|
|
if (op->flags & FL_ExtVal0) {
|
|
rsize = 16;
|
|
d = write_extval(0,2,db,d,op,rtype);
|
|
if (typechk && (op->extval[0]<-0x8000 || op->extval[0]>0xffff))
|
|
cpu_error(36); /* immediate operand out of range */
|
|
}
|
|
else
|
|
cpu_error(37); /* immediate operand has illegal type */
|
|
break;
|
|
case 'l':
|
|
if (op->flags & FL_ExtVal0) {
|
|
rsize = 32;
|
|
d = write_extval(0,4,db,d,op,rtype);
|
|
}
|
|
else if (type_of_expr(op->value[0]) == FLT) {
|
|
if ((err = copy_float_exp(d,op,EXT_SINGLE)) != 0)
|
|
cpu_error(err);
|
|
d += 4;
|
|
}
|
|
else
|
|
cpu_error(37); /* immediate operand has illegal type */
|
|
break;
|
|
case 's':
|
|
if ((err = copy_float_exp(d,op,EXT_SINGLE)) != 0)
|
|
cpu_error(err);
|
|
d += 4;
|
|
break;
|
|
case 'd':
|
|
case 'q':
|
|
if ((err = copy_float_exp(d,op,EXT_DOUBLE)) != 0)
|
|
cpu_error(err);
|
|
d += 8;
|
|
break;
|
|
case 'x':
|
|
if ((err = copy_float_exp(d,op,EXT_EXTENDED)) != 0)
|
|
cpu_error(err);
|
|
d += 12;
|
|
break;
|
|
case 'p':
|
|
if ((err = copy_float_exp(d,op,EXT_PACKED)) != 0)
|
|
cpu_error(err);
|
|
d += 12;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* append relocations */
|
|
if (rtype != REL_NONE) {
|
|
if (rtype==REL_ABS && op->basetype[0]==BASE_PCREL)
|
|
rtype = REL_PC;
|
|
add_extnreloc(&db->relocs,op->base[0],op->extval[0],rtype,0,rsize,roffs);
|
|
}
|
|
if (ortype != REL_NONE) {
|
|
if (ortype==REL_ABS && op->basetype[1]==BASE_PCREL)
|
|
ortype = REL_PC;
|
|
add_extnreloc(&db->relocs,op->base[1],op->extval[1],ortype,0,orsize,
|
|
(rtype==REL_NONE) ? roffs : roffs+rsize/8);
|
|
}
|
|
}
|
|
return d;
|
|
}
|
|
|
|
|
|
dblock *eval_instruction(instruction *ip,section *sec,taddr pc)
|
|
/* Convert an instruction into a DATA atom, including relocations
|
|
if necessary. */
|
|
{
|
|
dblock *db = new_dblock();
|
|
unsigned char ipflags = ip->ext.un.real.flags;
|
|
signed char lastsize = ip->ext.un.real.last_size;
|
|
instruction *realip = ip;
|
|
unsigned char *d;
|
|
|
|
/* really execute optimizations now */
|
|
ipslot = 0;
|
|
optimize_instruction(ip,sec,pc,1);
|
|
|
|
/* determine instruction size and allocate data atom */
|
|
if ((db->size = iplist_size(ip)) != 0) {
|
|
d = db->data = mymalloc(db->size);
|
|
}
|
|
else {
|
|
db->data = NULL;
|
|
goto eval_done;
|
|
}
|
|
|
|
/* encode instructions */
|
|
do {
|
|
if (ip->code >= 0) {
|
|
mnemonic *mnemo = &mnemonics[ip->code];
|
|
char ext = ip->qualifiers[0] ?
|
|
tolower((unsigned char)ip->qualifiers[0][0]) : '\0';
|
|
uint16_t sz = ((mnemo->ext.size & SIZE_MASK) == SIZE_UNSIZED) ?
|
|
SIZE_UNSIZED : lc_ext_to_size(ext);
|
|
unsigned char *dbstart = d;
|
|
int i;
|
|
|
|
/* warn about a bad alias instruction mnemonic */
|
|
if (mnemo->ext.available & malias)
|
|
cpu_error(33); /* deprecated instruction alias */
|
|
|
|
/* copy opcode */
|
|
if (mnemo->ext.available & mcffpu)
|
|
*d++ = (mnemo->ext.opcode[0] >> 8) | 2;
|
|
else if (mnemo->ext.available & mfpu)
|
|
*d++ = (mnemo->ext.opcode[0] >> 8) | (fpu_id << 1);
|
|
else
|
|
*d++ = mnemo->ext.opcode[0] >> 8;
|
|
|
|
*d++ = mnemo->ext.opcode[0] & 0xff;
|
|
pc += 2;
|
|
|
|
if (S_OPCODE_SIZE(mnemo->ext.size) > 1) {
|
|
*d++ = mnemo->ext.opcode[1] >> 8;
|
|
*d++ = mnemo->ext.opcode[1] & 0xff;
|
|
pc += 2;
|
|
|
|
if (S_OPCODE_SIZE(mnemo->ext.size) > 2) {
|
|
*d++ = 0;
|
|
*d++ = 0;
|
|
pc += 2;
|
|
}
|
|
}
|
|
|
|
/* insert size-extension into opcode, when required */
|
|
switch (S_SIZEMODE(mnemo->ext.size)) {
|
|
case S_NONE:
|
|
break;
|
|
case S_STD:
|
|
switch (sz) {
|
|
case SIZE_WORD: *(dbstart+1) |= 0x40; break;
|
|
case SIZE_LONG: *(dbstart+1) |= 0x80; break;
|
|
}
|
|
break;
|
|
case S_STD1:
|
|
switch (sz) {
|
|
case SIZE_BYTE: *(dbstart+1) |= 0x40; break;
|
|
case SIZE_WORD: *(dbstart+1) |= 0x80; break;
|
|
case SIZE_LONG: *(dbstart+1) |= 0xc0; break;
|
|
}
|
|
break;
|
|
case S_HI:
|
|
switch (sz) {
|
|
case SIZE_WORD: *dbstart |= 0x02; break;
|
|
case SIZE_LONG: *dbstart |= 0x04; break;
|
|
}
|
|
break;
|
|
case S_CAS:
|
|
switch (sz) {
|
|
case SIZE_BYTE: *dbstart |= 0x02; break;
|
|
case SIZE_WORD: *dbstart |= 0x04; break;
|
|
case SIZE_LONG: *dbstart |= 0x06; break;
|
|
}
|
|
break;
|
|
case S_MOVE:
|
|
switch (sz) {
|
|
case SIZE_BYTE: *dbstart |= 0x10; break;
|
|
case SIZE_WORD: *dbstart |= 0x30; break;
|
|
case SIZE_LONG: *dbstart |= 0x20; break;
|
|
}
|
|
break;
|
|
case S_WL8:
|
|
if (sz == SIZE_LONG)
|
|
*dbstart |= 1;
|
|
break;
|
|
case S_LW7:
|
|
if (sz == SIZE_WORD)
|
|
*(dbstart+1) |= 0x80;
|
|
break;
|
|
case S_WL6:
|
|
if (sz == SIZE_LONG)
|
|
*(dbstart+1) |= 0x40;
|
|
break;
|
|
case S_MAC:
|
|
if (sz == SIZE_LONG)
|
|
*(dbstart+2) |= 8;
|
|
break;
|
|
case S_TRAP:
|
|
switch (sz) {
|
|
case SIZE_WORD: *(dbstart+1) |= 0x02; break;
|
|
case SIZE_LONG: *(dbstart+1) |= 0x03; break;
|
|
}
|
|
break;
|
|
case S_EXT:
|
|
switch (sz) {
|
|
case SIZE_WORD: *(dbstart+3) |= 0x40; break;
|
|
case SIZE_LONG: *(dbstart+3) |= 0x80; break;
|
|
}
|
|
break;
|
|
case S_FP:
|
|
switch (sz) {
|
|
case SIZE_SINGLE: *(dbstart+2) |= 0x04; break;
|
|
case SIZE_EXTENDED: *(dbstart+2) |= 0x08; break;
|
|
case SIZE_PACKED: *(dbstart+2) |= 0x0c; break;
|
|
case SIZE_WORD: *(dbstart+2) |= 0x10; break;
|
|
case SIZE_DOUBLE: *(dbstart+2) |= 0x14; break;
|
|
case SIZE_BYTE: *(dbstart+2) |= 0x18; break;
|
|
}
|
|
break;
|
|
default:
|
|
ierror(0);
|
|
break;
|
|
}
|
|
|
|
/* check illegal addressing mode combinations for ColdFire MOVE */
|
|
if ((cpu_type & mcf) && ip->code==OC_MOVE) {
|
|
operand *src = ip->op[0];
|
|
operand *dst = ip->op[1];
|
|
|
|
if (src->mode >= MODE_An16Disp) {
|
|
if ((src->mode==MODE_An16Disp ||
|
|
(src->mode==MODE_Extended && src->reg==REG_PC16Disp))) {
|
|
if (dst->mode==MODE_An8Format ||
|
|
(dst->mode==MODE_Extended && dst->reg<=REG_AbsLong))
|
|
cpu_error(41); /* illegal combination of CF addressing modes */
|
|
}
|
|
else {
|
|
if (dst->mode==MODE_An16Disp) {
|
|
if (!(cpu_type & (mcfb|mcfc)) ||
|
|
(src->mode!=MODE_Extended || src->reg!=REG_Immediate) ||
|
|
(sz!=SIZE_BYTE && sz!=SIZE_WORD))
|
|
/* ISA_B also allows "#x,d(An)" for byte and word size */
|
|
cpu_error(41); /* illegal combination of CF addr. modes */
|
|
}
|
|
else if (dst->mode==MODE_An8Format ||
|
|
(dst->mode==MODE_Extended && dst->reg<=REG_AbsLong))
|
|
cpu_error(41); /* illegal combination of CF addressing modes */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* write operands / insert addressing mode into opcode */
|
|
for (i=0; i<MAX_OPERANDS && ip->op[i]!=NULL; i++) {
|
|
operand *op = ip->op[i];
|
|
struct oper_insert *oii = &insert_info[mnemo->ext.place[i]];
|
|
unsigned char *newd = d;
|
|
|
|
switch (oii->mode) {
|
|
case M_bfea:
|
|
if (op->flags & FL_BFoffsetDyn) {
|
|
op->bf_offset |= 32;
|
|
}
|
|
else if (op->bf_offset>31) {
|
|
cpu_error(19); /* illegal bitfield width/offset */
|
|
}
|
|
if (op->flags & FL_BFwidthDyn) {
|
|
op->bf_width |= 32;
|
|
}
|
|
else {
|
|
if (op->bf_width > 32)
|
|
cpu_error(19); /* illegal bitfield width/offset */
|
|
op->bf_width &= 31;
|
|
}
|
|
*(dbstart+2) |= (op->bf_offset>>2) & 15;
|
|
*(dbstart+3) |= ((op->bf_offset&3)<<6) | (op->bf_width&63);
|
|
/* fall through */
|
|
|
|
case M_ea:
|
|
if (oii->flags & IIF_MASK)
|
|
*(dbstart+3) |= op->bf_width ? (1<<oii->pos) : 0;
|
|
if (oii->flags & IIF_NOMODE)
|
|
*(dbstart+1) |= REGget(op->reg);
|
|
else
|
|
*(dbstart+1) |= ((op->mode & 7) << 3) | REGget(op->reg);
|
|
/* fall through */
|
|
|
|
case M_noea:
|
|
newd = write_ea_ext(db,d,op,ext,sec,pc);
|
|
/* fall through */
|
|
|
|
case M_nop:
|
|
break;
|
|
|
|
case M_kfea:
|
|
*(dbstart+1) |= ((op->mode & 7) << 3) | REGget(op->reg);
|
|
*(dbstart+2) |= (op->flags & FL_BFoffsetDyn) ? 0x10 : 0;
|
|
*(dbstart+3) |= op->bf_offset & 0x7f;
|
|
newd = write_ea_ext(db,d,op,ext,sec,pc);
|
|
break;
|
|
|
|
case M_high_ea:
|
|
*(dbstart) |= (REGget(op->reg) << 1) | ((op->mode & 4) >> 2);
|
|
*(dbstart+1) |= (op->mode & 3) << 6;
|
|
newd = write_ea_ext(db,d,op,ext,sec,pc);
|
|
break;
|
|
|
|
case M_func:
|
|
if (oii->flags & IIF_ABSVAL) {
|
|
if (op->base[0] != NULL) {
|
|
cpu_error(24); /* absolute value expected */
|
|
break;
|
|
}
|
|
}
|
|
(oii->insert)(dbstart,oii,op);
|
|
break;
|
|
|
|
case M_branch:
|
|
newd = write_branch(db,d,op,ext,sec,pc,(oii->flags&IIF_BCC)?1:0);
|
|
break;
|
|
|
|
case M_val0:
|
|
if (op->base[0] == NULL) {
|
|
taddr v = op->extval[0];
|
|
|
|
if (oii->flags & IIF_MASK) {
|
|
if (v == 0)
|
|
v = 1 << oii->size;
|
|
else if (v == (1<<oii->size))
|
|
v = 0;
|
|
}
|
|
else if (oii->flags & IIF_3Q) {
|
|
if (v == 0)
|
|
v = -1;
|
|
else if (v == -1)
|
|
v = 0;
|
|
}
|
|
if (oii->flags & IIF_REVERSE)
|
|
v = reverse(v,oii->size);
|
|
write_val(dbstart,oii->pos,oii->size,v,
|
|
(oii->flags&IIF_SIGNED)!=0);
|
|
}
|
|
else
|
|
cpu_error(24); /* absolute value expected */
|
|
break;
|
|
|
|
case M_reg:
|
|
if (op->mode<MODE_Extended || op->mode==MODE_FPn) {
|
|
taddr r = (taddr)op->reg;
|
|
|
|
if (oii->size>3 && op->mode==MODE_An)
|
|
r += REGAn;
|
|
write_val(dbstart,oii->pos,oii->size,r,0);
|
|
}
|
|
else
|
|
ierror(0);
|
|
break;
|
|
|
|
default:
|
|
ierror(0);
|
|
break;
|
|
}
|
|
pc += newd - d;
|
|
d = newd;
|
|
}
|
|
}
|
|
}
|
|
while ((ip = ip->ext.un.copy.next) != NULL);
|
|
|
|
eval_done:
|
|
/* restore flags and last_size of real ip to allow instruction_size() */
|
|
realip->ext.un.real.flags = ipflags;
|
|
realip->ext.un.real.last_size = lastsize;
|
|
|
|
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();
|
|
symbol *base = NULL;
|
|
int btype,etype;
|
|
taddr val = 0;
|
|
thuge hval;
|
|
tfloat fval;
|
|
|
|
db->size = bitsize >> 3;
|
|
db->data = mymalloc(db->size);
|
|
|
|
etype = type_of_expr(op->value[0]);
|
|
if (etype == FLT) {
|
|
if (!eval_expr_float(op->value[0],&fval))
|
|
general_error(60); /* cannot evaluate floating point */
|
|
}
|
|
else if (bitsize > 32) {
|
|
if (!eval_expr_huge(op->value[0],&hval))
|
|
general_error(59); /* cannot evaluate huge integer */
|
|
etype = HUG;
|
|
}
|
|
else {
|
|
if (!eval_expr(op->value[0],&val,sec,pc)) {
|
|
btype = find_base(op->value[0],&base,sec,pc);
|
|
if (btype == BASE_ILLEGAL)
|
|
general_error(38); /* illegal relocation */
|
|
}
|
|
etype = NUM;
|
|
}
|
|
|
|
switch (bitsize) {
|
|
case 8:
|
|
if (etype == NUM) {
|
|
if (typechk && (val<-0x80 || val>0xff))
|
|
cpu_error(39); /* data out of range */
|
|
db->data[0] = val & 0xff;
|
|
}
|
|
else if (etype == FLT)
|
|
cpu_error(40); /* data has illegal type */
|
|
else
|
|
ierror(0);
|
|
break;
|
|
|
|
case 16:
|
|
if (etype == NUM) {
|
|
if (typechk && (val<-0x8000 || val>0xffff))
|
|
cpu_error(39); /* data out of range */
|
|
setval(1,db->data,2,val);
|
|
}
|
|
else if (etype == FLT)
|
|
cpu_error(40); /* data has illegal type */
|
|
else
|
|
ierror(0);
|
|
break;
|
|
|
|
case 32:
|
|
if (etype == NUM)
|
|
setval(1,db->data,4,val);
|
|
else if (etype == FLT)
|
|
conv2ieee32(1,db->data,fval);
|
|
else
|
|
ierror(0);
|
|
break;
|
|
|
|
case 64:
|
|
if (etype == HUG) {
|
|
if (typechk && !huge_chkrange(hval,64))
|
|
cpu_error(39); /* data out of range */
|
|
huge_to_mem(1,db->data,8,hval);
|
|
}
|
|
else if (etype == FLT)
|
|
conv2ieee64(1,db->data,fval);
|
|
else
|
|
ierror(0);
|
|
break;
|
|
|
|
case 96:
|
|
if (etype == HUG) {
|
|
if (typechk && !huge_chkrange(hval,96))
|
|
cpu_error(39); /* data out of range */
|
|
huge_to_mem(1,db->data,12,hval);
|
|
}
|
|
else if (etype == FLT)
|
|
conv2ieee80(1,db->data,fval);
|
|
else
|
|
ierror(0);
|
|
break;
|
|
|
|
default:
|
|
cpu_error(38,bitsize); /* data objects with x size are not supported */
|
|
break;
|
|
}
|
|
|
|
if (base) {
|
|
/* relocation required */
|
|
add_extnreloc(&db->relocs,base,val,btype==BASE_PCREL?REL_PC:REL_ABS,
|
|
0,bitsize,0);
|
|
}
|
|
|
|
return db;
|
|
}
|
|
|
|
|
|
int init_cpu()
|
|
{
|
|
int i,j,code_tab_cnt;
|
|
hashdata data;
|
|
|
|
if (!gas) {
|
|
/* remove gas mnemonics from the hash table */
|
|
for (i=0; i<mnemonic_cnt; i++) {
|
|
if (mnemonics[i].ext.available & mgas) {
|
|
rem_hashentry(mnemohash,mnemonics[i].name,0);
|
|
while (i+1<mnemonic_cnt &&
|
|
!strcmp(mnemonics[i].name,mnemonics[i+1].name))
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* remember all mnemonic locations which we need */
|
|
code_tab_cnt = sizeof(code_tab) / sizeof(code_tab[0]);
|
|
for (i=0,j=0; i<mnemonic_cnt && j<code_tab_cnt; i++)
|
|
if (!strcmp(mnemonics[i].name,code_tab[j].name))
|
|
if ((code_tab[j].optype[0] == 0 ||
|
|
mnemonics[i].operand_type[0] == (int)code_tab[j].optype[0]) &&
|
|
(code_tab[j].optype[1] == 0 ||
|
|
mnemonics[i].operand_type[1] == (int)code_tab[j].optype[1]))
|
|
*code_tab[j++].var = i;
|
|
if (j < code_tab_cnt)
|
|
ierror(0);
|
|
|
|
/* flag all mnemonics with an unambiguous size extension */
|
|
for (i=0; i<mnemonic_cnt; i++) {
|
|
if (countbits((taddr)mnemonics[i].ext.size & SIZE_MASK) == 1)
|
|
mnemonics[i].ext.size |= SIZE_UNAMBIG;
|
|
}
|
|
|
|
/* predefine some register symbols */
|
|
new_regsym(0,0,elfregs?"%sp":"sp",RSTYPE_An,0,7);
|
|
new_regsym(0,0,elfregs?"%fp":"fp",RSTYPE_An,0,6);
|
|
|
|
/* reset baseregs */
|
|
for (i=0; i<7; i++)
|
|
baseexp[i] = NULL; /* disable basereg for A0-A7 */
|
|
|
|
/* predefine cpu symbols */
|
|
if (phxass_compat) {
|
|
set_optc_symbol();
|
|
set_internal_abs(cpu_name,phxass_cpu_num(cpu_type));
|
|
set_internal_abs(mmu_name,(cpu_type & mmmu)!=0);
|
|
set_internal_abs(fpu_name,(cpu_type & mfloat)?fpu_id:0);
|
|
}
|
|
if (devpac_compat) {
|
|
taddr f = 99;
|
|
|
|
/* __G2 contains host, version and cpu information */
|
|
set_g2_symbol();
|
|
|
|
/* __LK is 0 for TOS executables, 1 for GST-, 2 for DRI-linkable,
|
|
3 for Amiga-linkable, 4 for Amiga executable.
|
|
Set it to 99 when creating an object file of unknown format. */
|
|
if (!strcmp(output_format,"tos"))
|
|
f = 0;
|
|
else if (!strcmp(output_format,"hunkexe"))
|
|
f = 4;
|
|
else if (!strcmp(output_format,"hunk"))
|
|
f = 3;
|
|
set_internal_abs(lk_name,f);
|
|
}
|
|
set_internal_abs(vasmsym_name,cpu_type&CPUMASK);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static void set_cpu_type(uint32_t type,int addatom)
|
|
{
|
|
if (type & (m68k|cpu32|mcf_all|apollo)) {
|
|
cpu_type = (cpu_type & ~(m68k|cpu32|mcf_all|apollo)) | type;
|
|
if (gas) {
|
|
if (!(type & (m68020|m68030|cpu32)))
|
|
cpu_type &= ~(m68881|m68882); /* no 88x FPU when not 020 or 030 */
|
|
if (!no_fpu && (type & (m68020|m68030|cpu32)))
|
|
cpu_type |= m68881|m68882; /* gas compatibility: always have FPU */
|
|
}
|
|
}
|
|
else if (type & (m68881|m68882))
|
|
cpu_type = (cpu_type & ~(m68881|m68882)) | (no_fpu ? 0 : type);
|
|
else if (type == m68851)
|
|
cpu_type |= m68851;
|
|
|
|
if (addatom)
|
|
add_cpu_opt(0,OCMD_CPU,cpu_type);
|
|
|
|
if (cpu_type & (m68020up | mcf))
|
|
m68k_mid = 2; /* need 68020+ */
|
|
}
|
|
|
|
|
|
static uint32_t get_cpu_type(char **str)
|
|
{
|
|
char *s = *str;
|
|
uint32_t type = 0;
|
|
int len,i;
|
|
|
|
while (ISIDCHAR(*s))
|
|
s++;
|
|
len = s - *str;
|
|
|
|
for (i=0; i<model_cnt; i++) {
|
|
if (strlen(models[i].name)==len && !strnicmp(*str,models[i].name,len)) {
|
|
type = models[i].type;
|
|
break;
|
|
}
|
|
}
|
|
if (type==0 && cur_src)
|
|
cpu_error(43); /* unknown cpu type */
|
|
|
|
*str = s;
|
|
return type;
|
|
}
|
|
|
|
|
|
static void clear_all_opts(void)
|
|
{
|
|
opt_movem = opt_pea = opt_clr = opt_st = opt_lsl = opt_mul = opt_div = 0;
|
|
opt_fconst = opt_brajmp = opt_pc = opt_bra = opt_allbra = opt_jbra = 0;
|
|
opt_disp = opt_abs = opt_moveq = opt_quick = opt_branop = 0;
|
|
opt_bdisp = opt_odisp = opt_lea = opt_lquick = opt_immaddr = 0;
|
|
opt_gen = opt_speed = 0;
|
|
}
|
|
|
|
|
|
int cpu_args(char *arg)
|
|
{
|
|
char *p = arg;
|
|
int i;
|
|
|
|
if (!strcmp(p,"-phxass")) {
|
|
phxass_compat = 1;
|
|
opt_allbra = opt_brajmp = opt_sd = 1;
|
|
opt_fconst = 0;
|
|
ign_unambig_ext = 1;
|
|
return 0; /* leave option visible for syntax modules */
|
|
}
|
|
|
|
if (!strcmp(p,"-devpac")) {
|
|
/* set all options to Devpac-compatible defaults */
|
|
devpac_compat = 1;
|
|
#ifdef OUTTOS
|
|
tos_hisoft_dri = 0; /* no extended symbol names until OPT X+ is given */
|
|
#endif
|
|
clear_all_opts();
|
|
no_symbols = 1;
|
|
warn_opts = 2;
|
|
ign_unambig_ext = 1;
|
|
unsigned_shift = 1;
|
|
return 0; /* leave option visible for syntax modules */
|
|
}
|
|
|
|
if (!strcmp(p,"-kick1hunks")) {
|
|
kick1hunks = 1;
|
|
return 0; /* leave option visible for syntax modules */
|
|
}
|
|
|
|
if (!strncmp(p,"-m",2)) {
|
|
uint32_t cpu;
|
|
|
|
p += 2;
|
|
if (!strcmp(p,"no-68881"))
|
|
goto nofpu;
|
|
if (!strncmp(p,"cf",2))
|
|
p += 2; /* allow -mcf for ColdFire models */
|
|
cpu = get_cpu_type(&p);
|
|
if (!cpu)
|
|
return 0;
|
|
set_cpu_type(cpu,0);
|
|
}
|
|
else if (!strncmp(p,"-sdreg=",7)) {
|
|
i = atoi(p+7);
|
|
if (i>=2 && i<=6)
|
|
sdreg = i;
|
|
else
|
|
cpu_error(58); /* not a valid small data register */
|
|
}
|
|
else if (!strcmp(p,"-no-opt")) {
|
|
clear_all_opts();
|
|
no_opt = 1;
|
|
}
|
|
else if (!strcmp(p,"-no-fpu")) {
|
|
nofpu:
|
|
no_fpu = 1;
|
|
cpu_type &= ~(m68881|m68882);
|
|
}
|
|
else if (!strcmp(p,"-gas")) {
|
|
gas = 1;
|
|
commentchar = '|';
|
|
set_cpu_type(m68020,0); /* gas compatibility defaults to 68020/68881 */
|
|
opt_jbra = !no_opt;
|
|
}
|
|
else if (!strcmp(p,"-sgs"))
|
|
sgs = 1;
|
|
else if (!strcmp(p,"-rangewarnings"))
|
|
modify_cpu_err(WARNING,25,29,32,36,0);
|
|
else if (!strcmp(p,"-conv-brackets"))
|
|
convert_brackets = 1;
|
|
else if (!strcmp(p,"-regsymredef"))
|
|
regsymredef = 1;
|
|
else if (!strcmp(p,"-elfregs"))
|
|
elfregs = 1;
|
|
else if (!strcmp(p,"-guess-ext"))
|
|
ign_unambig_ext = 1;
|
|
else if (!strcmp(p,"-showcrit"))
|
|
warn_opts = 1;
|
|
else if (!strcmp(p,"-showopt"))
|
|
warn_opts = 2;
|
|
else if (!strcmp(p,"-sc"))
|
|
opt_sc = !no_opt;
|
|
else if (!strcmp(p,"-sd"))
|
|
opt_sd = !no_opt;
|
|
else if (!strcmp(p,"-opt-movem"))
|
|
opt_movem = !no_opt;
|
|
else if (!strcmp(p,"-opt-pea"))
|
|
opt_pea = !no_opt;
|
|
else if (!strcmp(p,"-opt-clr"))
|
|
opt_clr = !no_opt;
|
|
else if (!strcmp(p,"-opt-st"))
|
|
opt_st = !no_opt;
|
|
else if (!strcmp(p,"-opt-lsl"))
|
|
opt_lsl = !no_opt;
|
|
else if (!strcmp(p,"-opt-mul"))
|
|
opt_mul = !no_opt;
|
|
else if (!strcmp(p,"-opt-div"))
|
|
opt_div = !no_opt;
|
|
else if (!strcmp(p,"-opt-fconst"))
|
|
opt_fconst = !no_opt;
|
|
else if (!strcmp(p,"-opt-brajmp"))
|
|
opt_brajmp = !no_opt;
|
|
else if (!strcmp(p,"-opt-allbra"))
|
|
opt_bra = opt_allbra = !no_opt;
|
|
else if (!strcmp(p,"-opt-jbra"))
|
|
opt_jbra = !no_opt;
|
|
else if (!strcmp(p,"-opt-speed"))
|
|
opt_speed = !no_opt;
|
|
else
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
char *parse_instruction(char *s,int *inst_len,char **ext,int *ext_len,
|
|
int *ext_cnt)
|
|
/* parse instruction and save extension locations */
|
|
{
|
|
char *inst = s;
|
|
int cnt = *ext_cnt;
|
|
|
|
if (*s == '.') /* allow dot as first char */
|
|
s++;
|
|
while (*s && *s!='.' && !isspace((unsigned char)*s))
|
|
s++;
|
|
*inst_len = s - inst;
|
|
|
|
while (*s=='.' && cnt<MAX_QUALIFIERS) {
|
|
s++;
|
|
ext[cnt] = s;
|
|
while (*s && *s!='.' && !isspace((unsigned char)*s))
|
|
s++;
|
|
ext_len[cnt] = s - ext[cnt];
|
|
if (ext_len[cnt] <= 0)
|
|
cpu_error(34); /* illegal opcode extension */
|
|
else
|
|
cnt++;
|
|
}
|
|
*ext_cnt = cnt;
|
|
|
|
if (cnt > 0)
|
|
current_ext = tolower((unsigned char)ext[0][0]);
|
|
else
|
|
current_ext = '\0';
|
|
return s;
|
|
}
|
|
|
|
|
|
int set_default_qualifiers(char **q,int *q_len)
|
|
/* fill in pointers to default qualifiers, return number of qualifiers */
|
|
{
|
|
q[0] = (cpu_type & mcf) ? l_str : w_str;
|
|
q_len[0] = 1;
|
|
return 1;
|
|
}
|
|
|
|
|
|
static char validchar(char *s)
|
|
{
|
|
return ISEOL(s) ? 0 : *s;
|
|
}
|
|
|
|
|
|
static void phxass_optc(uint16_t optc)
|
|
/* set optimizations according to PhxAss OPTC flags */
|
|
{
|
|
if (optc == 0) {
|
|
no_opt = 1; /* no optimizations */
|
|
return;
|
|
}
|
|
if (optc & 0x001) { /* N */
|
|
opt_gen = opt_disp = opt_abs = opt_moveq = opt_bdisp = opt_odisp = 1;
|
|
opt_lea = opt_immaddr = 1;
|
|
}
|
|
if (optc & 0x002) /* R */
|
|
opt_pc = 1;
|
|
if (optc & 0x004) /* Q */
|
|
opt_quick = opt_lquick = 1;
|
|
if (optc & 0x008) /* B */
|
|
opt_bra = opt_brajmp = 1;
|
|
if (optc & 0x010) /* L */
|
|
opt_lsl = 1;
|
|
if (optc & 0x020) /* P */
|
|
opt_pea = 1;
|
|
if (optc & 0x040) /* S */
|
|
opt_clr = opt_st = opt_fconst = opt_disp = opt_bdisp = opt_odisp = 1;
|
|
if (optc & 0x080) /* M */
|
|
opt_gen = 1;
|
|
if (optc & 0x100) /* T */
|
|
; /* ignored */
|
|
if (optc & 0x200) /* D */
|
|
opt_movem = 1;
|
|
}
|
|
|
|
|
|
static void phxass_option(char opt)
|
|
/* parse a phxass-style option */
|
|
{
|
|
switch (toupper((unsigned char)opt)) {
|
|
case '0':
|
|
no_opt = 1; /* no optimizations */
|
|
break;
|
|
case '3': /* enable all */
|
|
case '!':
|
|
opt_lsl = opt_pea = opt_clr = opt_st = opt_fconst = 1;
|
|
opt_disp = opt_bdisp = opt_odisp = opt_movem = 1;
|
|
/* fall through */
|
|
case '1': /* enable standard */
|
|
case '2':
|
|
case '*':
|
|
opt_pc = opt_quick = opt_lquick = opt_bra = opt_brajmp = 1;
|
|
/* fall through */
|
|
case 'N': /* normal */
|
|
opt_gen = opt_disp = opt_abs = opt_moveq = opt_bdisp = opt_odisp = 1;
|
|
opt_lea = opt_immaddr = 1;
|
|
break;
|
|
case 'R': /* relative */
|
|
opt_pc = 1;
|
|
break;
|
|
case 'Q': /* quick */
|
|
opt_quick = opt_lquick = 1;
|
|
break;
|
|
case 'B': /* branches */
|
|
opt_bra = opt_brajmp = opt_allbra = 1;
|
|
break;
|
|
case 'L': /* logical shifts */
|
|
opt_lsl = 1;
|
|
break;
|
|
case 'P': /* pea */
|
|
opt_pea = 1;
|
|
break;
|
|
case 'S': /* special */
|
|
opt_clr = opt_st = opt_fconst = opt_disp = opt_bdisp = opt_odisp = 1;
|
|
break;
|
|
case 'D': /* movem single */
|
|
opt_movem = 1;
|
|
/* fall through */
|
|
case 'M': /* delete movem */
|
|
opt_gen = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static char *devpac_option(char *s)
|
|
/* parse a devpac-style option (default) */
|
|
{
|
|
static int opt_map[] = { /* maps OPT O<n> to a vasm option */
|
|
OCMD_OPTBRA,OCMD_OPTDISP,OCMD_OPTABS,OCMD_OPTMOVEQ,OCMD_OPTQUICK,
|
|
OCMD_NOP,OCMD_OPTBRANOP,OCMD_OPTBDISP,OCMD_OPTODISP,OCMD_OPTLEA,
|
|
OCMD_OPTLQUICK,OCMD_OPTIMMADDR
|
|
};
|
|
char opt,ext,c;
|
|
int flag = 1;
|
|
int num;
|
|
|
|
if (!strnicmp(s,"no",2)) { /* no... prefix for option */
|
|
flag = 0;
|
|
s += 2;
|
|
}
|
|
|
|
if (!strnicmp(s,"autopc",6)) {
|
|
add_cpu_opt(0,OCMD_OPTPC,flag);
|
|
return s+6;
|
|
}
|
|
else if (!strnicmp(s,"case",4)) {
|
|
nocase = !flag;
|
|
return s+4;
|
|
}
|
|
else if (!strnicmp(s,"chkpc",5)) {
|
|
add_cpu_opt(0,OCMD_CHKPIC,flag);
|
|
return s+5;
|
|
}
|
|
else if (!strnicmp(s,"debug",5)) {
|
|
no_symbols = !flag;
|
|
return s+5;
|
|
}
|
|
else if (!strnicmp(s,"symtab",6)) {
|
|
listnosyms = !flag;
|
|
return s+6;
|
|
}
|
|
else if (!strnicmp(s,"type",4)) {
|
|
add_cpu_opt(0,OCMD_CHKTYPE,flag);
|
|
return s+4;
|
|
}
|
|
else if (!strnicmp(s,"warn",4)) {
|
|
no_warn = !flag; /* must also be recognized during parsing */
|
|
add_cpu_opt(0,OCMD_NOWARN,!flag);
|
|
return s+4;
|
|
}
|
|
else if (!strnicmp(s,"xdebug",6)) {
|
|
if (flag)
|
|
no_symbols = 0;
|
|
#ifdef OUTTOS
|
|
tos_hisoft_dri = flag; /* extended symbol names for Atari */
|
|
#endif
|
|
#ifdef OUTHUNK
|
|
hunk_onlyglobal = flag; /* only xdef-symbols in objects for Amiga */
|
|
#endif
|
|
return s+6;
|
|
}
|
|
else if (!flag)
|
|
goto devpac_err;
|
|
|
|
if (!strnicmp(s,"p=",2)) {
|
|
uint32_t cpu;
|
|
|
|
s++;
|
|
do {
|
|
s++;
|
|
if ((cpu = get_cpu_type(&s)) != 0)
|
|
set_cpu_type(cpu,1);
|
|
else
|
|
cpu_error(43); /* unknown cpu type */
|
|
}
|
|
while (*s == '/'); /* another CPU/FPU/MMU specification? */
|
|
return s;
|
|
}
|
|
|
|
if ((opt = tolower((unsigned char)validchar(s))) != 0) {
|
|
ext = validchar(s+1);
|
|
if (isdigit((unsigned char)ext))
|
|
num = atoi(s+1);
|
|
else
|
|
num = -1;
|
|
|
|
do
|
|
c = validchar(++s);
|
|
while (c!=0 && c!=',' && c!='+' && c!='-');
|
|
|
|
if (c==0 || c==',') {
|
|
switch (opt) {
|
|
case 'l': /* Ln : Atari only */
|
|
if (num >= 0)
|
|
exec_out = num == 0;
|
|
break;
|
|
default:
|
|
cpu_error(31,toupper((unsigned char)opt),c); /* unkn. opt ignored */
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
else if (c != 0) {
|
|
flag = c=='+';
|
|
if (ext == c)
|
|
ext = 0;
|
|
|
|
switch (opt) {
|
|
case 'a':
|
|
add_cpu_opt(0,OCMD_OPTPC,flag);
|
|
break;
|
|
case 'c':
|
|
nocase = !flag;
|
|
break;
|
|
case 'd':
|
|
no_symbols = !flag;
|
|
break;
|
|
case 'l': /* L+/- : Amiga only */
|
|
exec_out = !flag;
|
|
break;
|
|
case 'm':
|
|
/* macro expansion in listing file */
|
|
break;
|
|
case 'o':
|
|
if (isdigit((unsigned char)ext) && num>=1 && num<=12) {
|
|
add_cpu_opt(0,opt_map[num-1],flag);
|
|
}
|
|
else {
|
|
switch (tolower((unsigned char)ext)) {
|
|
case '\0':
|
|
if (!flag) {
|
|
/* clear all optimization flags, set no_opt */
|
|
clear_all_opts();
|
|
add_cpu_opt(0,OCMD_NOOPT,1);
|
|
}
|
|
else {
|
|
/* enable all safe optimizations, as in vasm-default */
|
|
add_cpu_opt(0,OCMD_OPTBRA,1);
|
|
add_cpu_opt(0,OCMD_OPTDISP,1);
|
|
add_cpu_opt(0,OCMD_OPTABS,1);
|
|
add_cpu_opt(0,OCMD_OPTMOVEQ,1);
|
|
add_cpu_opt(0,OCMD_OPTQUICK,1);
|
|
add_cpu_opt(0,OCMD_OPTBRANOP,1);
|
|
add_cpu_opt(0,OCMD_OPTBDISP,1);
|
|
add_cpu_opt(0,OCMD_OPTODISP,1);
|
|
add_cpu_opt(0,OCMD_OPTLEA,1);
|
|
add_cpu_opt(0,OCMD_OPTLQUICK,1);
|
|
add_cpu_opt(0,OCMD_OPTIMMADDR,1);
|
|
if (!devpac_compat) {
|
|
/* enable safe vasm-specific optimizations */
|
|
add_cpu_opt(0,OCMD_OPTGEN,1);
|
|
add_cpu_opt(0,OCMD_OPTPC,1);
|
|
add_cpu_opt(0,OCMD_OPTFCONST,1);
|
|
}
|
|
}
|
|
break;
|
|
case 'b': /* vasm-specific */
|
|
add_cpu_opt(0,OCMD_OPTJBRA,flag);
|
|
break;
|
|
case 'c': /* vasm-specific */
|
|
add_cpu_opt(0,OCMD_OPTCLR,flag);
|
|
break;
|
|
case 'd': /* vasm-specific */
|
|
add_cpu_opt(0,OCMD_OPTDIV,flag);
|
|
break;
|
|
case 'f': /* vasm-specific */
|
|
add_cpu_opt(0,OCMD_OPTFCONST,flag);
|
|
break;
|
|
case 'g': /* vasm-specific */
|
|
add_cpu_opt(0,OCMD_OPTGEN,flag);
|
|
break;
|
|
case 'j': /* vasm-specific */
|
|
add_cpu_opt(0,OCMD_OPTBRAJMP,flag);
|
|
break;
|
|
case 'l': /* vasm-specific */
|
|
add_cpu_opt(0,OCMD_OPTLSL,flag);
|
|
break;
|
|
case 'm': /* vasm-specific */
|
|
add_cpu_opt(0,OCMD_OPTMOVEM,flag);
|
|
break;
|
|
case 'n': /* vasm-specific */
|
|
add_cpu_opt(0,OCMD_SMALLDATA,flag);
|
|
break;
|
|
case 'p': /* vasm-specific */
|
|
add_cpu_opt(0,OCMD_OPTPEA,flag);
|
|
break;
|
|
case 's': /* vasm-specific */
|
|
add_cpu_opt(0,OCMD_OPTSPEED,flag);
|
|
break;
|
|
case 't': /* vasm-specific */
|
|
add_cpu_opt(0,OCMD_OPTST,flag);
|
|
break;
|
|
case 'w':
|
|
add_cpu_opt(0,OCMD_OPTWARN,flag?2:0);
|
|
break;
|
|
case 'x': /* vasm-specific */
|
|
add_cpu_opt(0,OCMD_OPTMUL,flag);
|
|
break;
|
|
default:
|
|
cpu_error(31,opt,ext); /* unknown option ignored */
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 'p':
|
|
add_cpu_opt(0,OCMD_CHKPIC,flag);
|
|
break;
|
|
case 's':
|
|
listnosyms = !flag;
|
|
break;
|
|
case 't':
|
|
add_cpu_opt(0,OCMD_CHKTYPE,flag);
|
|
break;
|
|
case 'w':
|
|
no_warn = !flag; /* must also be recognized during parsing */
|
|
add_cpu_opt(0,OCMD_NOWARN,!flag);
|
|
break;
|
|
case 'x':
|
|
if (flag)
|
|
no_symbols = 0;
|
|
#ifdef OUTTOS
|
|
tos_hisoft_dri = flag; /* extended symbol names for Atari */
|
|
#endif
|
|
#ifdef OUTHUNK
|
|
hunk_onlyglobal = flag; /* only xdef-symbols in objects for Amiga */
|
|
#endif
|
|
break;
|
|
default:
|
|
cpu_error(31,toupper((unsigned char)opt),c); /* unkn. opt ignored */
|
|
break;
|
|
}
|
|
return ++s;
|
|
}
|
|
}
|
|
|
|
devpac_err:
|
|
cpu_error(23); /* option expected */
|
|
return NULL;
|
|
}
|
|
|
|
|
|
char *parse_cpu_special(char *start)
|
|
/* parse cpu-specific directives; return pointer to end of
|
|
cpu-specific text */
|
|
{
|
|
char *name=start,*s=start;
|
|
uint32_t cpu;
|
|
|
|
if (ISIDSTART(*s)) {
|
|
s++;
|
|
while (ISIDCHAR(*s))
|
|
s++;
|
|
|
|
if ((s-name==4 && !strnicmp(name,"near",4)) ||
|
|
(s-name==6 && !strnicmp(name,".sdreg",6))) {
|
|
/* NEAR <An> */
|
|
signed char r;
|
|
|
|
s = skip(s);
|
|
r = getreg(&s,0);
|
|
if (r >= 0) {
|
|
if (r>=(REGAn+2) && r<=(REGAn+6)) /* a2-a6 */
|
|
sdreg = REGget(r);
|
|
else
|
|
cpu_error(58); /* not a valid small data register */
|
|
}
|
|
else if (*name!='.' && !strnicmp(s,"code",4)) {
|
|
add_cpu_opt(0,OCMD_SMALLCODE,1);
|
|
return skip_line(s);
|
|
}
|
|
else
|
|
sdreg = last_sdreg;
|
|
add_cpu_opt(0,OCMD_SDREG,sdreg);
|
|
/* skip rest of line to be compatible to other assemblers */
|
|
return skip_line(s);
|
|
}
|
|
|
|
else if (s-name==3 && !strnicmp(name,"far",3)) {
|
|
/* FAR */
|
|
if (sdreg >= 0) {
|
|
last_sdreg = sdreg;
|
|
sdreg = -1;
|
|
add_cpu_opt(0,OCMD_SDREG,sdreg);
|
|
}
|
|
add_cpu_opt(0,OCMD_SMALLCODE,0);
|
|
eol(s);
|
|
return skip_line(s);
|
|
}
|
|
|
|
else if (s-name==8 && !strnicmp(name,"initnear",8)) {
|
|
/* INITNEAR */
|
|
if (sdreg >= 0) {
|
|
dblock *db = new_dblock();
|
|
|
|
db->size = 6;
|
|
db->data = mymalloc(6);
|
|
db->data[0] = 0x41 | (sdreg << 1); /* LEA _LinkerDB,An */
|
|
db->data[1] = 0xf9;
|
|
memset(&db->data[2],0,4);
|
|
add_extnreloc(&db->relocs,new_import("_LinkerDB"),0,REL_ABS,0,32,2);
|
|
add_atom(0,new_data_atom(db,2));
|
|
}
|
|
else
|
|
cpu_error(59); /* small data mode is not enabled */
|
|
return skip_line(s);
|
|
}
|
|
|
|
else if (s-name==7 && !strnicmp(name,"basereg",7)) {
|
|
/* BASEREG <expression>,<An> */
|
|
expr *exp;
|
|
signed char reg;
|
|
|
|
s = skip(s);
|
|
if ((exp = parse_expr(&s)) != NULL) {
|
|
s = skip(s);
|
|
if (*s == ',') {
|
|
s = skip(s+1);
|
|
reg = getreg(&s,0);
|
|
if (reg>=(REGAn+0) && reg<=(REGAn+6)) {
|
|
if (baseexp[REGget(reg)]!=NULL && !phxass_compat) {
|
|
cpu_error(55,REGget(reg)); /* basereg already in use */
|
|
}
|
|
else {
|
|
baseexp[REGget(reg)] = exp;
|
|
eol(s);
|
|
}
|
|
}
|
|
else
|
|
cpu_error(4); /* address register required */
|
|
}
|
|
else
|
|
cpu_error(15,','); /* , expected */
|
|
}
|
|
return skip_line(s);
|
|
}
|
|
|
|
else if (s-name==4 && !strnicmp(name,"endb",4)) {
|
|
signed char reg;
|
|
|
|
s = skip(s);
|
|
reg = getreg(&s,0);
|
|
if (reg>=(REGAn+0) && reg<=(REGAn+6)) {
|
|
if (baseexp[REGget(reg)] != NULL) {
|
|
baseexp[REGget(reg)] = NULL;
|
|
eol(s);
|
|
}
|
|
else
|
|
cpu_error(56,REGget(reg)); /* basereg is already free */
|
|
}
|
|
else if (phxass_compat) {
|
|
/* ignore register specification as long as it is unambigious */
|
|
for (reg=0; reg<=6; reg++) {
|
|
if (baseexp[reg] != NULL) {
|
|
baseexp[reg] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
for (; reg<=6; reg++) {
|
|
if (baseexp[reg] != NULL) /* more than one register in use? */
|
|
cpu_error(4); /* address register required */
|
|
}
|
|
}
|
|
else
|
|
cpu_error(4); /* address register required */
|
|
return skip_line(s);
|
|
}
|
|
|
|
else if (s-name==7 && !strnicmp(name,"machine",7)) {
|
|
/* MACHINE <cpu-type> */
|
|
int acflag = 0;
|
|
|
|
s = skip(s);
|
|
if (!strnicmp(s,"mc",2)) {
|
|
s += 2; /* Devpac-compatible MACHINE mc680x0 */
|
|
acflag = -1;
|
|
}
|
|
else if (!strnicmp(s,"ac",2)) {
|
|
s += 2; /* Apollo Core ac680x0 */
|
|
acflag = 1;
|
|
}
|
|
|
|
cpu = get_cpu_type(&s);
|
|
|
|
if (cpu!=0 &&
|
|
((!(cpu&apollo) && acflag<=0) || ((cpu&apollo) && acflag>=0))) {
|
|
set_cpu_type(cpu,1);
|
|
eol(s);
|
|
}
|
|
else
|
|
cpu_error(43); /* unknown cpu type */
|
|
return skip_line(s);
|
|
}
|
|
|
|
else if ((s-name)>=5 && (s-name)<=8 && !strnicmp(name,"mcf",3)) {
|
|
/* MCF5xxx ColdFire */
|
|
s = name + 3;
|
|
if ((cpu = get_cpu_type(&s)) != 0) {
|
|
set_cpu_type(cpu,1);
|
|
eol(s);
|
|
}
|
|
else
|
|
cpu_error(43); /* unknown cpu type */
|
|
return skip_line(s);
|
|
}
|
|
|
|
else if (s-name==7 && !strnicmp(name,"mc",2)) {
|
|
/* MC680x0 */
|
|
s = name + 2;
|
|
cpu = get_cpu_type(&s);
|
|
if (cpu!=0 && !(cpu&apollo)) {
|
|
set_cpu_type(cpu,1);
|
|
eol(s);
|
|
}
|
|
else
|
|
cpu_error(43); /* unknown cpu type */
|
|
return skip_line(s);
|
|
}
|
|
|
|
else if (s-name==7 && !strnicmp(name,"ac",2)) {
|
|
/* Apollo Core AC680x0 */
|
|
s = name + 2;
|
|
cpu = get_cpu_type(&s);
|
|
if (cpu & apollo) {
|
|
set_cpu_type(cpu,1);
|
|
eol(s);
|
|
}
|
|
else
|
|
cpu_error(43); /* unknown cpu type */
|
|
return skip_line(s);
|
|
}
|
|
|
|
else if (s-name==5 && !strnicmp(name,"cpu32",5)) {
|
|
/* CPU32 */
|
|
set_cpu_type(cpu32,1);
|
|
eol(s);
|
|
return skip_line(s);
|
|
}
|
|
|
|
else if (s-name==3 && !strnicmp(name,"fpu",3)) {
|
|
/* FPU [<cpID>] */
|
|
taddr id = 1;
|
|
|
|
s = skip(s);
|
|
if (validchar(s))
|
|
id = parse_constexpr(&s);
|
|
if (id>0 && id<8 && !no_fpu) {
|
|
fpu_id = (unsigned char)id;
|
|
add_cpu_opt(0,OCMD_FPU,fpu_id);
|
|
cpu_type |= m68881|m68882;
|
|
}
|
|
else
|
|
cpu_type &= ~(m68881|m68882);
|
|
add_cpu_opt(0,OCMD_CPU,cpu_type);
|
|
eol(s);
|
|
return skip_line(s);
|
|
}
|
|
|
|
else if (s-name==3 && !strnicmp(name,"opt",3)) {
|
|
if (phxass_compat) {
|
|
/* OPT [single-letter-options] - PhxAss options */
|
|
s = skip(s);
|
|
clear_all_opts(); /* first disable all */
|
|
while (validchar(s))
|
|
phxass_option(*s++);
|
|
cpu_opts_optinit(NULL); /* now create new opt atoms */
|
|
}
|
|
else {
|
|
/* OPT <option>[,<option>...] - Devpac options */
|
|
char *s2 = s;
|
|
|
|
do {
|
|
if (!(s2 = devpac_option(skip(s2))))
|
|
break;
|
|
s2 = skip(s2);
|
|
}
|
|
while (*s2++ == ','); /* another option? */
|
|
}
|
|
return skip_line(s);
|
|
}
|
|
|
|
else if (phxass_compat && s-name==4 && !strnicmp(name,"optc",4)) {
|
|
/* OPTC <expression> - set PhxAss optimization flags */
|
|
taddr optc = 0;
|
|
|
|
s = skip(s);
|
|
clear_all_opts(); /* first disable all */
|
|
if (validchar(s))
|
|
optc = parse_constexpr(&s);
|
|
phxass_optc((uint16_t)optc);
|
|
cpu_opts_optinit(NULL); /* now create new opt atoms */
|
|
return skip_line(s);
|
|
}
|
|
}
|
|
return start;
|
|
}
|
|
|
|
|
|
int parse_cpu_label(char *labname,char **start)
|
|
/* parse cpu-specific directives following a label field,
|
|
return zero when no valid directive was recognized */
|
|
{
|
|
char *dir=*start,*s=*start;
|
|
hashdata data;
|
|
|
|
if (ISIDSTART(*s)) {
|
|
s++;
|
|
while (ISIDCHAR(*s))
|
|
s++;
|
|
|
|
if ((s-dir==4 && !strnicmp(dir,"equr",4)) ||
|
|
(s-dir==5 && !strnicmp(dir,"fequr",5))) {
|
|
/* label EQUR Rn */
|
|
signed char r;
|
|
|
|
s = skip(s);
|
|
if ((r = getreg(&s,0)) >= 0)
|
|
new_regsym(regsymredef,0,labname,
|
|
REGisAn(r)?RSTYPE_An:RSTYPE_Dn,0,REGget(r));
|
|
else if ((r = getfreg(&s)) >= 0)
|
|
new_regsym(regsymredef,0,labname,RSTYPE_FPn,0,r);
|
|
else if ((r = getbreg(&s)) >= 0)
|
|
new_regsym(regsymredef,0,labname,RSTYPE_Bn,0,r);
|
|
else
|
|
cpu_error(44); /* register expected */
|
|
eol(s);
|
|
*start = skip_line(s);
|
|
return 1;
|
|
}
|
|
|
|
else if ((s-dir==3 && !strnicmp(dir,"reg",3)) ||
|
|
(s-dir==5 && !strnicmp(dir,"equrl",5))) {
|
|
/* label REG reglist */
|
|
symbol *sym;
|
|
|
|
s = skip(s);
|
|
sym = new_equate(labname,number_expr((taddr)scan_Rnlist(&s)));
|
|
sym->flags |= REGLIST;
|
|
eol(s);
|
|
*start = skip_line(s);
|
|
return 1;
|
|
}
|
|
|
|
else if ((s-dir==4 && !strnicmp(dir,"freg",4)) ||
|
|
(s-dir==6 && !strnicmp(dir,"fequrl",6))) {
|
|
/* label FREG reglist */
|
|
symbol *sym;
|
|
|
|
s = skip(s);
|
|
sym = new_equate(labname,number_expr((taddr)scan_FPnlist(&s)));
|
|
sym->flags |= REGLIST;
|
|
eol(s);
|
|
*start = skip_line(s);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|