ack/mach/m68020/as/mach5.c
2019-03-25 00:11:55 +08:00

687 lines
14 KiB
C

/* $Id$ */
/*
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
* See the copyright notice in the ACK home directory, in the file "Copyright".
*/
/*
* Motorola 68020 auxiliary functions
*/
/* With pc-relative modes the offset is calulated from the address of the
* extension word. This address is not known until the instruction opcode(s)
* have been emitted. Since this address is unknown, the offset from pc
* cannot be calculated correctly, so it cannot immediately be decided whether
* to use mode 072 (pc-relative with 16 bit offset) or mode 073 (pc_relative
* with possibly 32 bit offset) Because of this, instruction opcodes
* are not really emitted right away, but temporarily stored. This way
* the address of the extension word is known so the offset can be calculated
* correctly and it then can be decided to use mode 072 or 073; this can be
* altered in the instruction opcode, if necessary. For the sake of consistency
* the effective address(es) are also stored temporarily. The instruction is
* then emitted in one go, by emit_instr().
*/
void emit_instr(void)
{
register instr_t *ip;
for (ip=instr; ip<instrp; emit2((ip++)->i_word)) {
#ifdef RELOCATION
RELOMOVE(relonami, ip->i_relonami);
if (ip->i_reloinfo)
newrelo(ip->i_relotype, ip->i_reloinfo | RELBR | RELWR);
#endif
}
}
#ifdef RELOCATION
void t_emit2(short word, short relotype, int reloinfo, valu_t relnm)
#else
void t_emit2(short word)
#endif
{
#ifdef RELOCATION
if ((instrp->i_reloinfo = reloinfo)) {
RELOMOVE(instrp->i_relonami, relnm);
instrp->i_relotype = relotype;
}
#endif
instrp->i_word = word;
instrp++;
dot_offset += 2;
}
#ifdef RELOCATION
void t_emit4(long words, short relotype, int reloinfo, valu_t relnm)
#else
void t_emit4(long words)
#endif
{
T_EMIT2((short)(words>>16), relotype, reloinfo, relnm);
T_EMIT2((short)(words), 0, 0, 0);
}
void ea_1(int sz, int bits)
{
/* Because displacements come in three sizes (null displacement,
* word and long displacement), each displacement requires
* two bits in the bittable, so two calls to small. Sometimes
* one of these calls is a dummy call.
*/
register int flag;
register int sm, sm1, sm2;
if (mrg_1 > 074)
serror("no specials");
if ((flag = eamode[mrg_1>>3]) == 0)
if ((flag = eamode[010 + (mrg_1&07)]) == 0)
flag = eamode[015 + (sz>>6)];
if ((mrg_1 & 070) == 010)
checksize(sz, 2|4);
bits &= ~flag;
if (bits)
serror("bad addressing category");
if (mrg_1==073 && (ffew_1 & 0200) == 0 && (bd_1.typ & ~S_DOT) == DOTTYP)
bd_1.val -= (DOTVAL + dot_offset);
if ( (mrg_1==073) || (mrg_1&070)==060 ) {
sm = (
(mrg_1==073 && (bd_1.typ & ~S_DOT)==DOTTYP)
||
(bd_1.typ == S_ABS)
);
if (small(sm && fitw(bd_1.val), 2)) {
sm = (
(sm1 = ((ffew_1 & 0307)==0 && fitb(bd_1.val)))
||
(sm2 = ((ffew_1 & 0307)==0100 && mrg_1==073))
||
(bd_1.val==0)
);
if (small(sm,2)) {
if (sm1) { /* brief format extension */
T_EMIT2((ffew_1&0177000) | lowb(bd_1.val),
0, 0, 0);
return;
}
if (sm2) {
/* change mode to 072 in opcode word */
instr->i_word &= ~1;
T_EMIT2(loww(bd_1.val), 0, 0, 0);
return;
}
ffew_1 &= ~040; /* null displacement */
}
else
ffew_1 &= ~020; /* word displacement */
} else
sm = small(0,2); /* dummy call */
if (ffew_1 & 3) {
sm = (od_1.typ == S_ABS);
if (small(sm && fitw(od_1.val), 2))
ffew_1 &= small(od_1.val==0, 2) ? ~2 : ~1;
else
sm = small(0,2); /* dummy call */
}
assert((ffew_1 & 0410) == 0400);
T_EMIT2(ffew_1, 0, 0, 0);
assert(ffew_1 & 060);
switch(ffew_1 & 060) {
case 020:
break;
case 040:
T_EMIT2(loww(bd_1.val), 0, 0, 0);
break;
case 060:
T_EMIT4( bd_1.val,
bd_1.typ,
(mrg_1 == 073 && (ffew_1 & 0200) == 0)
? RELPC|RELO4
: RELO4,
bd_rel1
);
}
if (ffew_1 & 3) {
switch(ffew_1 & 3) {
case 1:
break;
case 2:
T_EMIT2(loww(od_1.val), 0, 0, 0);
break;
case 3:
T_EMIT4(od_1.val, od_1.typ, RELO4, od_rel1);
}
}
return; /* mode 060 and 073 have been dealt with */
}
if (flag & FITW)
if (
! fitw(bd_1.val)
&&
(mrg_1 != 074 || ! fit16(bd_1.val))
)
nofit();
if (flag & FITB) {
if (
! fitb(bd_1.val)
&&
(mrg_1 != 074 || ! fit8(bd_1.val))
)
nofit();
if (mrg_1 == 074)
bd_1.val &= 0xFF;
}
if (flag & PUTL)
T_EMIT4(bd_1.val, bd_1.typ, (flag>>8), bd_rel1);
if (flag & PUTW)
T_EMIT2(loww(bd_1.val), bd_1.typ, (flag>>8), bd_rel1);
}
void ea_2(int sz, int bits)
{
mrg_1 = mrg_2;
bd_1 = bd_2;
od_1 = od_2;
ffew_1 = ffew_2;
RELOMOVE(bd_rel1, bd_rel2);
RELOMOVE(od_rel1, od_rel2);
ea_1(sz, bits);
}
void checksize(int sz, int bits)
{
if ((bits & (1 << (sz>>6))) == 0)
serror("bad size");
}
void check_fsize(int sz, int size)
{
if (sz != size)
serror("bad size");
}
void ch_sz_dreg(int size, int mode)
{
if (mode == 0 &&
(size == FSIZE_X || size == FSIZE_P || size == FSIZE_D))
serror("illegal size for data register");
}
int checkscale(valu_t val)
{
int v = val;
if (v != val) v = 0;
switch(v) {
case 1: return 0;
case 2: return 1<<9;
case 4: return 2<<9;
case 8: return 3<<9;
default: serror("bad scale"); return 0;
}
}
void badoperand(void)
{
serror("bad operand(s)");
}
void shift_op(int opc, int sz)
{
if (mrg_1 < 010 && mrg_2 < 010) {
T_EMIT2((opc & 0170470) | sz | mrg_1<<9 | mrg_2, 0, 0, 0);
return;
}
if (bd_1.typ != S_ABS || mrg_1 != 074) {
badoperand();
return;
}
if (mrg_2 < 010) {
fit(fit3(bd_1.val));
T_EMIT2((opc & 0170430) | sz | low3(bd_1.val)<<9 | mrg_2,0,0,0);
return;
}
checksize(sz, 2);
fit(bd_1.val == 1);
T_EMIT2((opc & 0177700) | mrg_2, 0, 0, 0);
ea_2(SIZE_W, MEM|ALT);
}
void bitop(int opc)
{
register int bits;
bits = DTA|ALT;
if (opc == 0 && (mrg_1 < 010 || mrg_2 != 074))
bits = DTA;
if (mrg_1 < 010) {
T_EMIT2(opc | 0400 | mrg_1<<9 | mrg_2, 0, 0, 0);
ea_2(0, bits);
return;
}
if (mrg_1 == 074) {
T_EMIT2(opc | 04000 | mrg_2, 0, 0, 0);
ea_1(SIZE_W, 0);
ea_2(0, bits);
return;
}
badoperand();
}
void bitfield(int opc, int extension)
{
T_EMIT2(opc | mrg_2, 0, 0, 0);
T_EMIT2(extension, 0, 0, 0);
ea_2(SIZE_L, (mrg_2 < 010) ? 0 : (CTR | ALT));
}
void add(int opc, int sz)
{
if ((mrg_2 & 070) == 010)
checksize(sz, 2|4);
if (
mrg_1 == 074
&&
small(
bd_1.typ==S_ABS && fit3(bd_1.val),
sz==SIZE_L ? 4 : 2
)
) {
T_EMIT2((opc&0400) | 050000 | low3(bd_1.val)<<9 | sz | mrg_2,
0, 0, 0);
ea_2(sz, ALT);
return;
}
if (mrg_1 == 074 && (mrg_2 & 070) != 010) {
T_EMIT2((opc&03000) | sz | mrg_2, 0, 0, 0);
ea_1(sz, 0);
ea_2(sz, DTA|ALT);
return;
}
if ((mrg_2 & 070) == 010) {
T_EMIT2((opc&0170300) | (mrg_2&7)<<9 | sz<<1 | mrg_1, 0, 0, 0);
ea_1(sz, 0);
return;
}
if (to_dreg(opc, sz, 0))
return;
if (from_dreg(opc, sz, ALT|MEM))
return;
badoperand();
}
void and(int opc, int sz)
{
if (mrg_1 == 074 && mrg_2 >= 076) { /* ccr or sr */
if (sz != SIZE_NON)
checksize(sz, mrg_2==076 ? 1 : 2);
else
sz = (mrg_2==076 ? SIZE_B : SIZE_W);
T_EMIT2((opc&07400) | sz | 074, 0, 0, 0);
ea_1(sz, 0);
return;
}
if (sz == SIZE_NON)
sz = SIZE_DEF;
if (mrg_1 == 074) {
T_EMIT2((opc&07400) | sz | mrg_2, 0, 0, 0);
ea_1(sz, 0);
ea_2(sz, DTA|ALT);
return;
}
if ((opc & 010000) == 0 && to_dreg(opc, sz, DTA))
return;
if (from_dreg(opc, sz, (opc & 010000) ? DTA|ALT : ALT|MEM))
return;
badoperand();
}
int to_dreg(int opc, int sz, int bits)
{
if ((mrg_2 & 070) != 000)
return(0);
T_EMIT2((opc & 0170000) | sz | (mrg_2&7)<<9 | mrg_1, 0, 0, 0);
ea_1(sz, bits);
return(1);
}
int from_dreg(int opc, int sz, int bits)
{
if ((mrg_1 & 070) != 000)
return(0);
T_EMIT2((opc & 0170000) | sz | (mrg_1&7)<<9 | 0400 | mrg_2, 0, 0, 0);
ea_2(sz, bits);
return(1);
}
void cmp(int sz)
{
register int opc;
if ((mrg_1&070) == 030 && (mrg_2&070) == 030) {
T_EMIT2(0130410 | sz | (mrg_1&7) | (mrg_2&7)<<9, 0, 0, 0);
return;
}
if (mrg_1 == 074 && (mrg_2 & 070) != 010) {
if (mrg_2==074)
badoperand();
T_EMIT2(06000 | sz | mrg_2, 0, 0, 0);
ea_1(sz, 0);
ea_2(sz, DTA);
return;
}
if (mrg_2 < 020) {
if (mrg_2 >= 010) {
checksize(sz, 2|4);
opc = 0130300 | sz<<1;
mrg_2 &= 7;
} else
opc = 0130000 | sz;
T_EMIT2(opc | mrg_2<<9 | mrg_1, 0, 0, 0);
ea_1(sz, 0);
return;
}
badoperand();
}
void link_instr(int sz, int areg)
{
if (sz == SIZE_NON) {
if (bd_2.typ == S_ABS && fitw(bd_2.val))
sz = SIZE_W;
else
sz = SIZE_L;
}
checksize(sz, 2|4);
if (sz == SIZE_W)
T_EMIT2(047120 | areg, 0, 0, 0);
else /* sz == SIZE_L */
T_EMIT2(044010 | areg, 0, 0, 0);
ea_2(sz, 0);
}
void move(int sz)
{
register int opc;
if (mrg_1 > 074 || mrg_2 > 074) {
move_special(sz);
return;
}
if (sz == SIZE_NON)
sz = SIZE_DEF;
if (
mrg_2<010
&&
mrg_1==074
&&
sz==SIZE_L
&&
small(bd_1.typ==S_ABS && fitb(bd_1.val), 4)
) {
T_EMIT2(070000 | mrg_2<<9 | lowb(bd_1.val), 0, 0, 0);
return;
}
switch (sz) {
case SIZE_B: opc = 010000; break;
case SIZE_W: opc = 030000; break;
case SIZE_L: opc = 020000; break;
}
T_EMIT2(opc | mrg_1 | (mrg_2&7)<<9 | (mrg_2&070)<<3, 0, 0, 0);
ea_1(sz, 0);
ea_2(sz, ALT);
}
void move_special(int sz)
{
if (mrg_2 >= 076) {
if (sz != SIZE_NON)
checksize(sz, 2);
T_EMIT2(042300 | (mrg_2==076?0:01000) | mrg_1, 0, 0, 0);
ea_1(SIZE_W, DTA);
return;
}
if (mrg_1 >= 076) {
if (sz != SIZE_NON)
checksize(sz, 2);
T_EMIT2(040300 | (mrg_1==076?01000:0) | mrg_2, 0, 0, 0);
ea_2(SIZE_W, DTA|ALT);
return;
}
if (sz != SIZE_NON)
checksize(sz, 4);
if (mrg_1==075 && (mrg_2&070)==010) {
T_EMIT2(047150 | (mrg_2&7), 0, 0, 0);
return;
}
if (mrg_2==075 && (mrg_1&070)==010) {
T_EMIT2(047140 | (mrg_1&7), 0, 0, 0);
return;
}
badoperand();
}
void movem(int dr, int sz, int regs)
{
register int i;
if ((mrg_2>>3) == 04) {
regs = reverse(regs, 16);
}
checksize(sz, 2|4);
if ((mrg_2>>3)-3 == dr)
badoperand();
T_EMIT2(044200 | dr<<10 | (sz & 0200) >> 1 | mrg_2, 0, 0, 0);
T_EMIT2(regs, 0, 0, 0);
i = CTR;
if (dr == 0 && (mrg_2&070) == 040)
i = MEM;
if (dr != 0 && (mrg_2&070) == 030)
i = MEM;
if (dr == 0)
i |= ALT;
ea_2(sz, i);
}
int reverse(register int regs, int max)
{
register int r, i;
r = regs; regs = 0;
for (i = max; i > 0; i--) {
regs <<= 1;
if (r & 1)
regs++;
r >>= 1;
}
return regs;
}
void movep(int sz)
{
checksize(sz, 2|4);
if (mrg_1<010 && (mrg_2&070)==050) {
T_EMIT2(0610 | (sz & 0200)>>1 | mrg_1<<9 | (mrg_2&7), 0, 0, 0);
ea_2(sz, 0);
return;
}
if (mrg_2<010 && (mrg_1&070)==050) {
T_EMIT2(0410 | (sz & 0200)>>1 | mrg_2<<9 | (mrg_1&7), 0, 0, 0);
ea_1(sz, 0);
return;
}
badoperand();
}
void branch(int opc, expr_t exp)
{
register int sm;
exp.val -= (DOTVAL + 2);
if ((pass == PASS_2)
&&
(exp.val > 0)
&&
((exp.typ & S_DOT) == 0)
)
exp.val -= DOTGAIN;
sm = fitw(exp.val);
if ((exp.typ & ~S_DOT) != DOTTYP)
sm = 0;
if (small(sm,2)) {
if (small(fitb(exp.val),2)) {
if (exp.val == 0)
T_EMIT2(047161, 0, 0, 0); /* NOP */
else if (exp.val == -1) {
T_EMIT2(047161, 0, 0, 0);
serror("bad branch offset");
} else
T_EMIT2(opc | lowb(exp.val), 0, 0, 0);
} else {
T_EMIT2(opc, 0, 0, 0);
T_EMIT2(loww(exp.val), 0, 0, 0);
}
return;
}
sm = small(0,2); /* dummy call; two calls to small per branch */
T_EMIT2(opc | 0377, 0, 0, 0); /* 4 byte offset */
T_EMIT4(exp.val, exp.typ, RELPC|RELO4, relonami);
}
void cpbcc(int opc, expr_t exp)
{
register int sm;
exp.val -= (DOTVAL + 2);
if ((pass == PASS_2)
&&
(exp.val > 0)
&&
((exp.typ & S_DOT) == 0)
)
exp.val -= DOTGAIN;
sm = fitw(exp.val);
if ((exp.typ & ~S_DOT) != DOTTYP)
sm = 0;
if (small(sm,2)) {
T_EMIT2(opc, 0, 0, 0);
T_EMIT2(loww(exp.val), 0, 0, 0);
return;
}
T_EMIT2(opc | 0100, 0, 0, 0); /* 4 byte offset */
/* NB: no coprocessor defined extension words are emitted */
T_EMIT4(exp.val, exp.typ, RELPC|RELO4, relonami);
}
void ea7071(int sz)
{
mrg_2 = 071;
switch (sz) {
case SIZE_B:
badoperand();
case SIZE_W:
mrg_2 = 070;
case SIZE_L:
return;
case SIZE_NON:
break;
}
/* If this absolute address is in program space, and if we
* can assume that the only references to program space are made
* by instructins like 'jsr', 'jmp', 'lea' and 'pea', it might
* be possible to use a (PC,d16) effective address mode instead
* of absolute long. This is done here. If this scheme is in
* some way undesirable (e.g. when references to program space
* are made by instructions with more than one opcode word or by
* second effective addresses in instructions), the rest
* of this routine can simply be removed and replaced by the
* next two lines (which of course are in comment brackets now).
if (small(bd_2.typ == S_ABS && fitw(bd_2.val), 2))
mrg_2 = 070;
*/
if (pass == PASS_1) {
/* Reserve a bit in the bittable; in the following
* passes one call to small() will be done, but know yet
* which one, because bd_2.typ cannot be trusted yet.
*/
small(0, 2);
return;
}
if ((bd_2.typ & ~S_DOT) == DOTTYP) {
/* the "off" variable fixes the problem described above,
* e.g., when references to program space are made by
* instructions with more than one opcode word.
*/
int off = 2;
switch(curr_instr) {
case MOVEM:
case FMOVE:
case FMOVEM:
case FDYADIC:
case FMONADIC:
case FSINCOS:
case FSCC:
case FTST:
case DIVL:
case OP_RANGE:
case CALLM:
case CAS:
case CPSCC:
case CPTRAPCC:
case PFLUSH:
case PTEST:
case PMOVE:
case PLOAD:
off = 4;
break;
case MOVESP:
if (curr_size != 0) off = 4;
break;
case DIVMUL:
if (curr_size != SIZE_W) off = 4;
break;
}
if (small(fitw(bd_2.val-(DOTVAL+off)), 2)) {
bd_2.val -= (DOTVAL+off);
mrg_2 = 072;
}
} else
if (small(bd_2.typ == S_ABS && fitw(bd_2.val), 2))
mrg_2 = 070;
}
void fbranch(int opc, expr_t exp)
{
register int sm;
exp.val -= (DOTVAL + 2);
if ((pass == PASS_2)
&&
(exp.val > 0)
&&
((exp.typ & S_DOT) == 0)
)
exp.val -= DOTGAIN;
sm = fitw(exp.val);
if ((exp.typ & ~S_DOT) != DOTTYP)
sm = 0;
if (small(sm,2)) {
T_EMIT2(0170200|co_id|opc, 0, 0, 0);
T_EMIT2(loww(exp.val), 0, 0, 0);
return;
}
T_EMIT2(0170300|co_id|opc, 0, 0, 0); /* 4 byte offset */
T_EMIT4(exp.val, exp.typ, RELPC|RELO4, relonami);
}