mirror of
https://github.com/golang/go
synced 2024-10-04 22:21:22 -06:00
9c6df3ca13
currently, softfloat does not work and there are some unsigned-to-float conversion errors. R=rsc CC=golang-dev https://golang.org/cl/2886041
1658 lines
40 KiB
C
1658 lines
40 KiB
C
// Inferno utils/5l/thumb.c
|
|
// http://code.google.com/p/inferno-os/source/browse/utils/5l/thumb.c
|
|
//
|
|
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
|
|
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
|
|
// Portions Copyright © 1997-1999 Vita Nuova Limited
|
|
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
|
|
// Portions Copyright © 2004,2006 Bruce Ellis
|
|
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
|
|
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
|
|
// Portions Copyright © 2009 The Go Authors. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
#include "l.h"
|
|
|
|
static int32 thumboprr(int);
|
|
static int32 thumboprrr(int, int);
|
|
static int32 thumbopirr(int , int);
|
|
static int32 thumbopri(int);
|
|
static int32 thumbophh(int);
|
|
static int32 thumbopbra(int);
|
|
static int32 thumbopmv(int, int);
|
|
static void lowreg(Prog *, int);
|
|
static void mult(Prog *, int, int);
|
|
static void numr(Prog *, int, int, int);
|
|
static void regis(Prog *, int, int, int);
|
|
static void dis(int, int);
|
|
|
|
// build a constant using neg, add and shift - only worth it if < 6 bytes */
|
|
static int
|
|
immbuildcon(int c, Prog *p)
|
|
{
|
|
int n = 0;
|
|
|
|
USED(p);
|
|
if(c >= 0 && c <= 255)
|
|
return 0; // mv
|
|
if(c >= -255 && c < 0) // mv, neg
|
|
return 1;
|
|
if(c >= 256 && c <= 510) // mv, add
|
|
return 1;
|
|
if(c < 0)
|
|
return 0;
|
|
while(!(c & 1)){
|
|
n++;
|
|
c >>= 1;
|
|
}
|
|
if(c >= 0 && c <= 255) // mv, lsl
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
// positive 5 bit offset from register - O(R)
|
|
// positive 8 bit offset from register - mov O, R then [R, R]
|
|
// otherwise O goes in literal pool - mov O1(PC), R then [R, R]
|
|
static int
|
|
immoreg(int off, Prog *p)
|
|
{
|
|
int v = 1;
|
|
int as = p->as;
|
|
|
|
if(off < 0)
|
|
return C_GOREG;
|
|
if(as == AMOVW)
|
|
v = 4;
|
|
else if(as == AMOVH || as == AMOVHU)
|
|
v = 2;
|
|
else if(as == AMOVB || as == AMOVBU)
|
|
v = 1;
|
|
else
|
|
diag("bad op in immoreg");
|
|
if(off/v <= 31)
|
|
return C_SOREG;
|
|
if(off <= 255)
|
|
return C_LOREG;
|
|
return C_GOREG;
|
|
}
|
|
|
|
// positive 8 bit - mov O, R then 0(R)
|
|
// otherwise O goes in literal pool - mov O1(PC), R then 0(R)
|
|
static int
|
|
immacon(int off, Prog *p, int t1, int t2)
|
|
{
|
|
USED(p);
|
|
if(off < 0)
|
|
return t2;
|
|
if(off <= 255)
|
|
return t1;
|
|
return t2;
|
|
}
|
|
|
|
// unsigned 8 bit in words
|
|
static int
|
|
immauto(int off, Prog *p)
|
|
{
|
|
if(p->as != AMOVW)
|
|
diag("bad op in immauto");
|
|
mult(p, off, 4);
|
|
if(off >= 0 && off <= 1020)
|
|
return C_SAUTO;
|
|
return C_LAUTO;
|
|
}
|
|
|
|
static int
|
|
immsmall(int off, Prog *p, int t1, int t2, int t3)
|
|
{
|
|
USED(p);
|
|
if(off >= 0 && off <= 7)
|
|
return t1;
|
|
if(off >= 0 && off <= 255)
|
|
return t2;
|
|
return t3;
|
|
}
|
|
|
|
static int
|
|
immcon(int off, Prog *p)
|
|
{
|
|
int as = p->as;
|
|
|
|
if(as == ASLL || as == ASRL || as == ASRA)
|
|
return C_SCON;
|
|
if(p->to.type == D_REG && p->to.reg == REGSP){
|
|
if(as == AADD || as == ASUB){
|
|
if(off >= 0 && off <= 508)
|
|
return C_SCON;
|
|
if(as == ASUB){
|
|
p->as = AADD;
|
|
p->from.offset = -p->from.offset;
|
|
}
|
|
return C_LCON;
|
|
}
|
|
diag("unknown type in immcon");
|
|
}
|
|
if(as == AADD || as == ASUB){
|
|
if(p->reg != NREG)
|
|
return immsmall(off, p, C_SCON, C_LCON, C_GCON);
|
|
return immacon(off, p, C_SCON, C_LCON);
|
|
}
|
|
if(as == AMOVW && p->from.type == D_CONST && p->to.type == D_REG && immbuildcon(off, p))
|
|
return C_BCON;
|
|
if(as == ACMP && p->from.type == D_CONST && immbuildcon(off, p))
|
|
return C_BCON;
|
|
if(as == ACMP || as == AMOVW)
|
|
return immacon(off, p, C_SCON, C_LCON);
|
|
return C_LCON;
|
|
}
|
|
|
|
int
|
|
thumbaclass(Adr *a, Prog *p)
|
|
{
|
|
Sym *s;
|
|
int t;
|
|
|
|
switch(a->type) {
|
|
case D_NONE:
|
|
return C_NONE;
|
|
case D_REG:
|
|
if(a->reg == REGSP)
|
|
return C_SP;
|
|
if(a->reg == REGPC)
|
|
return C_PC;
|
|
if(a->reg >= 8)
|
|
return C_HREG;
|
|
return C_REG;
|
|
case D_SHIFT:
|
|
diag("D_SHIFT in thumbaclass");
|
|
return C_SHIFT;
|
|
case D_FREG:
|
|
diag("D_FREG in thumbaclass");
|
|
return C_FREG;
|
|
case D_FPCR:
|
|
diag("D_FPCR in thumbaclass");
|
|
return C_FCR;
|
|
case D_OREG:
|
|
switch(a->name) {
|
|
case D_EXTERN:
|
|
case D_STATIC:
|
|
if(a->sym == 0 || a->sym->name == 0) {
|
|
print("null sym external\n");
|
|
print("%D\n", a);
|
|
return C_GOK;
|
|
}
|
|
t = a->sym->type;
|
|
if(t == 0 || t == SXREF) {
|
|
diag("undefined external: %s in %s\n",
|
|
a->sym->name, TNAME);
|
|
a->sym->type = SDATA;
|
|
}
|
|
instoffset = a->sym->value + a->offset;
|
|
return C_ADDR; /* INITDAT unknown at this stage */
|
|
case D_AUTO:
|
|
instoffset = autosize + a->offset;
|
|
return immauto(instoffset, p);
|
|
case D_PARAM:
|
|
instoffset = autosize + a->offset + 4L;
|
|
// print("D_PARAM %s %d+%d+%d = %d\n", a->sym != S ? a->sym->name : "noname", autosize, a->offset, 4, autosize+a->offset+4);
|
|
return immauto(instoffset, p);
|
|
case D_NONE:
|
|
instoffset = a->offset;
|
|
if(a->reg == REGSP)
|
|
return immauto(instoffset, p);
|
|
else
|
|
return immoreg(instoffset, p);
|
|
}
|
|
return C_GOK;
|
|
case D_PSR:
|
|
diag("D_PSR in thumbaclass");
|
|
return C_PSR;
|
|
case D_OCONST:
|
|
switch(a->name) {
|
|
case D_EXTERN:
|
|
case D_STATIC:
|
|
s = a->sym;
|
|
t = s->type;
|
|
if(t == 0 || t == SXREF) {
|
|
diag("undefined external: %s in %s\n",
|
|
s->name, TNAME);
|
|
s->type = SDATA;
|
|
}
|
|
instoffset = s->value + a->offset;
|
|
if(s->type == STEXT){
|
|
instoffset = s->value + a->offset;
|
|
#ifdef CALLEEBX
|
|
instoffset += fnpinc(s);
|
|
#else
|
|
if(s->thumb)
|
|
instoffset++; // T bit
|
|
#endif
|
|
return C_LCON;
|
|
}
|
|
return C_LCON; /* INITDAT unknown at this stage */
|
|
// return immcon(instoffset, p);
|
|
}
|
|
return C_GOK;
|
|
case D_FCONST:
|
|
diag("D_FCONST in thumaclass");
|
|
return C_LFCON;
|
|
case D_CONST:
|
|
switch(a->name) {
|
|
case D_NONE:
|
|
instoffset = a->offset;
|
|
if(a->reg != NREG)
|
|
goto aconsize;
|
|
return immcon(instoffset, p);
|
|
case D_EXTERN:
|
|
case D_STATIC:
|
|
s = a->sym;
|
|
if(s == S)
|
|
break;
|
|
t = s->type;
|
|
switch(t) {
|
|
case 0:
|
|
case SXREF:
|
|
diag("undefined external: %s in %s\n",
|
|
s->name, TNAME);
|
|
s->type = SDATA;
|
|
break;
|
|
case SCONST:
|
|
case STEXT:
|
|
instoffset = s->value + a->offset;
|
|
#ifdef CALLEEBX
|
|
instoffset += fnpinc(s);
|
|
#else
|
|
if(s->thumb)
|
|
instoffset++; // T bit
|
|
#endif
|
|
return C_LCON;
|
|
}
|
|
instoffset = s->value + a->offset;
|
|
return C_LCON; /* INITDAT unknown at this stage */
|
|
// return immcon(instoffset, p);
|
|
case D_AUTO:
|
|
instoffset = autosize + a->offset;
|
|
goto aconsize;
|
|
case D_PARAM:
|
|
instoffset = autosize + a->offset + 4L;
|
|
aconsize:
|
|
if(p->from.reg == REGSP || p->from.reg == NREG)
|
|
return instoffset >= 0 && instoffset < 1024 ? C_SACON : C_GACON;
|
|
else if(p->from.reg == p->to.reg)
|
|
return immacon(instoffset, p, C_SACON, C_GACON);
|
|
return immsmall(instoffset, p, C_SACON, C_LACON, C_GACON);
|
|
}
|
|
return C_GOK;
|
|
case D_BRANCH: {
|
|
int v, va;
|
|
|
|
p->align = 0;
|
|
v = -4;
|
|
va = 0;
|
|
if(p->cond != P){
|
|
v = (p->cond->pc - p->pc) - 4;
|
|
va = p->cond->pc;
|
|
}
|
|
instoffset = v;
|
|
if(p->as == AB){
|
|
if(v >= -2048 && v <= 2046)
|
|
return C_SBRA;
|
|
p->align = 4;
|
|
instoffset = va;
|
|
return C_LBRA;
|
|
}
|
|
if(p->as == ABL){
|
|
#ifdef CALLEEBX
|
|
int e;
|
|
|
|
if((e = fninc(p->to.sym))) {
|
|
v += e;
|
|
va += e;
|
|
instoffset += e;
|
|
}
|
|
#endif
|
|
if(v >= -4194304 && v <= 4194302)
|
|
return C_SBRA;
|
|
p->align = 2;
|
|
instoffset = va;
|
|
return C_LBRA;
|
|
}
|
|
if(p->as == ABX){
|
|
v = va;
|
|
if(v >= 0 && v <= 255)
|
|
return C_SBRA;
|
|
p->align = 2;
|
|
instoffset = va;
|
|
return C_LBRA;
|
|
}
|
|
if(v >= -256 && v <= 254)
|
|
return C_SBRA;
|
|
if(v >= -(2048-2) && v <= (2046+2))
|
|
return C_LBRA;
|
|
p->align = 2;
|
|
instoffset = va;
|
|
return C_GBRA;
|
|
}
|
|
}
|
|
return C_GOK;
|
|
}
|
|
|
|
// as a1 a2 a3 type size param lit vers
|
|
Optab thumboptab[] =
|
|
{
|
|
{ ATEXT, C_ADDR, C_NONE, C_LCON, 0, 0, 0 },
|
|
{ ATEXT, C_ADDR, C_REG, C_LCON, 0, 0, 0 },
|
|
{ AMVN, C_REG, C_NONE, C_REG, 1, 2, 0 },
|
|
{ ASRL, C_REG, C_NONE, C_REG, 1, 2, 0 },
|
|
{ ACMP, C_REG, C_REG, C_NONE, 1, 2, 0 },
|
|
{ ACMN, C_REG, C_REG, C_NONE, 1, 2, 0 },
|
|
{ AADD, C_REG, C_REG, C_REG, 2, 2, 0 },
|
|
{ AADD, C_REG, C_NONE, C_REG, 2, 2, 0 },
|
|
{ AADD, C_SCON, C_REG, C_REG, 3, 2, 0 },
|
|
{ AADD, C_LCON, C_REG, C_REG, 49, 4, 0 },
|
|
{ AADD, C_GCON, C_REG, C_REG, 36, 4, 0, LFROM },
|
|
// { AADD, C_LCON, C_NONE, C_REG, 3, 2, 0, LFROM },
|
|
{ ASRL, C_SCON, C_REG, C_REG, 4, 2, 0 },
|
|
{ ASRL, C_SCON, C_NONE, C_REG, 4, 2, 0 },
|
|
{ AADD, C_SCON, C_NONE, C_REG, 5, 2, 0 },
|
|
{ AADD, C_LCON, C_NONE, C_REG, 37, 4, 0, LFROM },
|
|
{ ACMP, C_SCON, C_REG, C_NONE, 5, 2, 0 },
|
|
{ ACMP, C_BCON, C_REG, C_NONE, 48, 6, 0 },
|
|
{ ACMP, C_LCON, C_REG, C_NONE, 39, 4, 0, LFROM },
|
|
{ AMOVW, C_SCON, C_NONE, C_REG, 5, 2, 0 },
|
|
{ AMOVW, C_BCON, C_NONE, C_REG, 47, 4, 0 },
|
|
{ AMOVW, C_LCON, C_NONE, C_REG, 38, 2, 0, LFROM },
|
|
// { AADD, C_LCON, C_PC, C_REG, 6, 2, 0, LFROM },
|
|
// { AADD, C_LCON, C_SP, C_REG, 6, 2, 0, LFROM },
|
|
{ AADD, C_SCON, C_NONE, C_SP, 7, 2, 0 },
|
|
{ AADD, C_LCON, C_NONE, C_SP, 40, 4, 0, LFROM },
|
|
{ AADD, C_REG, C_NONE, C_HREG, 8, 2, 0 },
|
|
{ AADD, C_HREG, C_NONE, C_REG, 8, 2, 0 },
|
|
{ AADD, C_HREG, C_NONE, C_HREG, 8, 2, 0 },
|
|
{ AMOVW, C_REG, C_NONE, C_HREG, 8, 2, 0 },
|
|
{ AMOVW, C_HREG, C_NONE, C_REG, 8, 2, 0 },
|
|
{ AMOVW, C_HREG, C_NONE, C_HREG, 8, 2, 0 },
|
|
{ ACMP, C_REG, C_HREG, C_NONE, 8, 2, 0 },
|
|
{ ACMP, C_HREG, C_REG, C_NONE, 8, 2, 0 },
|
|
{ ACMP, C_HREG, C_HREG, C_NONE, 8, 2, 0 },
|
|
{ AB, C_NONE, C_NONE, C_SBRA, 9, 2, 0, LPOOL },
|
|
{ ABEQ, C_NONE, C_NONE, C_SBRA, 10, 2, 0 },
|
|
{ ABL, C_NONE, C_NONE, C_SBRA, 11, 4, 0 },
|
|
{ ABX, C_NONE, C_NONE, C_SBRA, 12, 10, 0 },
|
|
{ AB, C_NONE, C_NONE, C_LBRA, 41, 8, 0, LPOOL },
|
|
{ ABEQ, C_NONE, C_NONE, C_LBRA, 46, 4, 0 },
|
|
{ ABL, C_NONE, C_NONE, C_LBRA, 43, 14, 0 },
|
|
{ ABX, C_NONE, C_NONE, C_LBRA, 44, 14, 0 },
|
|
{ ABEQ, C_NONE, C_NONE, C_GBRA, 42, 10, 0 },
|
|
// { AB, C_NONE, C_NONE, C_SOREG, 13, 0, 0 },
|
|
// { ABL, C_NONE, C_NONE, C_SOREG, 14, 0, 0 },
|
|
{ ABL, C_NONE, C_NONE, C_REG, 51, 4, 0 },
|
|
{ ABX, C_NONE, C_NONE, C_REG, 15, 8, 0 },
|
|
{ ABX, C_NONE, C_NONE, C_HREG, 15, 8, 0 },
|
|
{ ABXRET, C_NONE, C_NONE, C_REG, 45, 2, 0 },
|
|
{ ABXRET, C_NONE, C_NONE, C_HREG, 45, 2, 0 },
|
|
{ ASWI, C_NONE, C_NONE, C_LCON, 16, 2, 0 },
|
|
{ AWORD, C_NONE, C_NONE, C_LCON, 17, 4, 0 },
|
|
{ AWORD, C_NONE, C_NONE, C_GCON, 17, 4, 0 },
|
|
{ AWORD, C_NONE, C_NONE, C_ADDR, 17, 4, 0 },
|
|
{ ADWORD, C_LCON, C_NONE, C_LCON, 50, 8, 0 },
|
|
{ AMOVW, C_SAUTO, C_NONE, C_REG, 18, 2, REGSP },
|
|
{ AMOVW, C_LAUTO, C_NONE, C_REG, 33, 6, 0, LFROM },
|
|
// { AMOVW, C_OFFPC, C_NONE, C_REG, 18, 2, REGPC, LFROM },
|
|
{ AMOVW, C_SOREG, C_NONE, C_REG, 19, 2, 0 },
|
|
{ AMOVHU, C_SOREG, C_NONE, C_REG, 19, 2, 0 },
|
|
{ AMOVBU, C_SOREG, C_NONE, C_REG, 19, 2, 0 },
|
|
{ AMOVW, C_REG, C_NONE, C_SAUTO, 20, 2, 0 },
|
|
{ AMOVW, C_REG, C_NONE, C_LAUTO, 34, 6, 0, LTO },
|
|
{ AMOVW, C_REG, C_NONE, C_SOREG, 21, 2, 0 },
|
|
{ AMOVH, C_REG, C_NONE, C_SOREG, 21, 2, 0 },
|
|
{ AMOVB, C_REG, C_NONE, C_SOREG, 21, 2, 0 },
|
|
{ AMOVHU, C_REG, C_NONE, C_SOREG, 21, 2, 0 },
|
|
{ AMOVBU, C_REG, C_NONE, C_SOREG, 21, 2, 0 },
|
|
{ AMOVW, C_REG, C_NONE, C_REG, 22, 2, 0 },
|
|
{ AMOVB, C_REG, C_NONE, C_REG, 23, 4, 0 },
|
|
{ AMOVH, C_REG, C_NONE, C_REG, 23, 4, 0 },
|
|
{ AMOVBU, C_REG, C_NONE, C_REG, 23, 4, 0 },
|
|
{ AMOVHU, C_REG, C_NONE, C_REG, 23, 4, 0 },
|
|
{ AMOVH, C_SOREG, C_NONE, C_REG, 24, 4, 0 },
|
|
{ AMOVB, C_SOREG, C_NONE, C_REG, 24, 4, 0 },
|
|
{ AMOVW, C_SACON, C_NONE, C_REG, 25, 2, 0 },
|
|
{ AMOVW, C_LACON, C_NONE, C_REG, 35, 4, 0 },
|
|
{ AMOVW, C_GACON, C_NONE, C_REG, 35, 4, 0, LFROM },
|
|
{ AMOVM, C_LCON, C_NONE, C_REG, 26, 2, 0 },
|
|
{ AMOVM, C_REG, C_NONE, C_LCON, 27, 2, 0 },
|
|
{ AMOVW, C_LOREG, C_NONE, C_REG, 28, 4, 0 },
|
|
{ AMOVH, C_LOREG, C_NONE, C_REG, 28, 4, 0 },
|
|
{ AMOVB, C_LOREG, C_NONE, C_REG, 28, 4, 0 },
|
|
{ AMOVHU, C_LOREG, C_NONE, C_REG, 28, 4, 0 },
|
|
{ AMOVBU, C_LOREG, C_NONE, C_REG, 28, 4, 0 },
|
|
{ AMOVW, C_REG, C_NONE, C_LOREG, 29, 4, 0 },
|
|
{ AMOVH, C_REG, C_NONE, C_LOREG, 29, 4, 0 },
|
|
{ AMOVB, C_REG, C_NONE, C_LOREG, 29, 4, 0 },
|
|
{ AMOVHU, C_REG, C_NONE, C_LOREG, 29, 4, 0 },
|
|
{ AMOVBU, C_REG, C_NONE, C_LOREG, 29, 4, 0 },
|
|
{ AMOVW, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM },
|
|
{ AMOVH, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM },
|
|
{ AMOVB, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM },
|
|
{ AMOVHU, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM },
|
|
{ AMOVBU, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM },
|
|
{ AMOVW, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO },
|
|
{ AMOVH, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO },
|
|
{ AMOVB, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO },
|
|
{ AMOVHU, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO },
|
|
{ AMOVBU, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO },
|
|
{ AMOVW, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM },
|
|
{ AMOVH, C_ADDR, C_NONE, C_REG, 32, 6, 0, LFROM },
|
|
{ AMOVB, C_ADDR, C_NONE, C_REG, 32, 6, 0, LFROM },
|
|
{ AMOVHU, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM },
|
|
{ AMOVBU, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM },
|
|
{ AMOVW, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO },
|
|
{ AMOVH, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO },
|
|
{ AMOVB, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO },
|
|
{ AMOVHU, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO },
|
|
{ AMOVBU, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO },
|
|
|
|
{ AXXX, C_NONE, C_NONE, C_NONE, 0, 2, 0 },
|
|
};
|
|
|
|
#define OPCNTSZ 52
|
|
int opcount[OPCNTSZ];
|
|
|
|
// is this too pessimistic ?
|
|
int
|
|
brextra(Prog *p)
|
|
{
|
|
int c;
|
|
|
|
// +2 is for padding
|
|
if(p->as == ATEXT)
|
|
return 0-0+2;
|
|
if(!isbranch(p))
|
|
diag("bad op in brextra()");
|
|
c = thumbaclass(&p->to, p);
|
|
switch(p->as){
|
|
case AB:
|
|
if(c != C_SBRA)
|
|
return 0;
|
|
return 8-2+2;
|
|
case ABL:
|
|
if(c != C_SBRA)
|
|
return 0;
|
|
return 14-4+2;
|
|
case ABX:
|
|
if(c == C_REG || c == C_HREG)
|
|
return 0;
|
|
#ifdef CALLEEBX
|
|
diag("ABX $I in brextra");
|
|
#endif
|
|
if(c != C_SBRA)
|
|
return 0;
|
|
return 14-10+2;
|
|
default:
|
|
if(c == C_GBRA)
|
|
return 0;
|
|
if(c == C_LBRA)
|
|
return 10-4+2;
|
|
return 10-2+2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define high(r) ((r)>=8)
|
|
|
|
static int32
|
|
mv(Prog *p, int r, int off)
|
|
{
|
|
int v, o;
|
|
if(p != nil && p->cond != nil){ // in literal pool
|
|
v = p->cond->pc - p->pc - 4;
|
|
if(p->cond->pc & 3)
|
|
diag("mv: bad literal pool alignment");
|
|
if(v & 3)
|
|
v += 2; // ensure M(4) offset
|
|
mult(p, v, 4);
|
|
off = v/4;
|
|
numr(p, off, 0, 255);
|
|
o = 0x9<<11;
|
|
}
|
|
else{
|
|
numr(p, off, 0, 255);
|
|
o = 0x4<<11;
|
|
}
|
|
o |= (r<<8) | off;
|
|
return o;
|
|
}
|
|
|
|
static void
|
|
mvcon(Prog *p, int r, int c, int32 *o1, int32 *o2)
|
|
{
|
|
int op = 0, n = 0;
|
|
|
|
if(c >= 0 && c <= 255)
|
|
diag("bad c in mvcon");
|
|
if(c >= -255 && c < 0) // mv, neg
|
|
c = -c;
|
|
else if(c >= 256 && c <= 510){ // mv, add
|
|
n = rand()%(511-c) + (c-255);
|
|
c -= n;
|
|
// n = c-255;
|
|
// c = 255;
|
|
op = AADD;
|
|
}
|
|
else{
|
|
if(c < 0)
|
|
diag("-ve in mvcon");
|
|
while(!(c & 1)){
|
|
n++;
|
|
c >>= 1;
|
|
}
|
|
if(c >= 0 && c <= 255) // mv, lsl
|
|
op = ASLL;
|
|
else
|
|
diag("bad shift in mvcon");
|
|
}
|
|
*o1 = mv(p, r, c);
|
|
switch(op){
|
|
case 0:
|
|
*o2 = (1<<14) | (9<<6) | (r<<3) | r;
|
|
break;
|
|
case AADD:
|
|
*o2 = (6<<11) | (r<<8) | n;
|
|
break;
|
|
case ASLL:
|
|
*o2 = (n<<6) | (r<<3) | r;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int32
|
|
mvlh(int rs, int rd)
|
|
{
|
|
int o = 0x46<<8;
|
|
|
|
if(high(rs)){
|
|
rs -= 8;
|
|
o |= 1<<6;
|
|
}
|
|
if(high(rd)){
|
|
rd -= 8;
|
|
o |= 1<<7;
|
|
}
|
|
o |= (rs<<3) | rd;
|
|
return o;
|
|
}
|
|
|
|
void
|
|
thumbbuildop()
|
|
{
|
|
int i, n, r;
|
|
Optab *optab = thumboptab;
|
|
Oprang *oprange = thumboprange;
|
|
|
|
for(n=0; optab[n].as != AXXX; n++)
|
|
;
|
|
qsort(optab, n, sizeof(optab[0]), ocmp);
|
|
for(i=0; i<n; i++) {
|
|
r = optab[i].as;
|
|
oprange[r].start = optab+i;
|
|
while(optab[i].as == r)
|
|
i++;
|
|
oprange[r].stop = optab+i;
|
|
i--;
|
|
|
|
switch(r)
|
|
{
|
|
default:
|
|
break;
|
|
case ABEQ:
|
|
oprange[ABNE] = oprange[r];
|
|
oprange[ABCS] = oprange[r];
|
|
oprange[ABHS] = oprange[r];
|
|
oprange[ABCC] = oprange[r];
|
|
oprange[ABLO] = oprange[r];
|
|
oprange[ABMI] = oprange[r];
|
|
oprange[ABPL] = oprange[r];
|
|
oprange[ABVS] = oprange[r];
|
|
oprange[ABVC] = oprange[r];
|
|
oprange[ABHI] = oprange[r];
|
|
oprange[ABLS] = oprange[r];
|
|
oprange[ABGE] = oprange[r];
|
|
oprange[ABLT] = oprange[r];
|
|
oprange[ABGT] = oprange[r];
|
|
oprange[ABLE] = oprange[r];
|
|
break;
|
|
case AMVN:
|
|
oprange[AADC] = oprange[r];
|
|
oprange[ASBC] = oprange[r];
|
|
oprange[AMUL] = oprange[r];
|
|
oprange[AAND] = oprange[r];
|
|
oprange[AEOR] = oprange[r];
|
|
oprange[AORR] = oprange[r];
|
|
oprange[ABIC] = oprange[r];
|
|
oprange[AMULU] = oprange[r];
|
|
break;
|
|
case ACMN:
|
|
oprange[ATST] = oprange[r];
|
|
break;
|
|
case ASRL:
|
|
oprange[ASRA] = oprange[r];
|
|
oprange[ASLL] = oprange[r];
|
|
break;
|
|
case AADD:
|
|
oprange[ASUB] = oprange[r];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
thumbasmout(Prog *p, Optab *o)
|
|
{
|
|
int32 o1, o2, o3, o4, o5, o6, o7, v;
|
|
int r, rf, rt;
|
|
|
|
rf = p->from.reg;
|
|
rt = p->to.reg;
|
|
r = p->reg;
|
|
o1 = o2 = o3 = o4 = o5 = o6 = o7 = 0;
|
|
if(debug['P']) print("%ux: %P type %d %d\n", (uint32)(p->pc), p, o->type, p->align);
|
|
opcount[o->type] += o->size;
|
|
switch(o->type) {
|
|
default:
|
|
diag("unknown asm %d", o->type);
|
|
prasm(p);
|
|
break;
|
|
case 0: /* pseudo ops */
|
|
if(debug['G']) print("%ux: %s: thumb\n", (uint32)(p->pc), p->from.sym->name);
|
|
break;
|
|
case 1: /* op R, -, R or op R, R, - */
|
|
o1 = thumboprr(p->as);
|
|
if(rt == NREG)
|
|
rt = r;
|
|
lowreg(p, rf);
|
|
lowreg(p, rt);
|
|
o1 |= (0x10<<10) | (rf<<3) | rt;
|
|
break;
|
|
case 2: /* add/sub R, R, R or add/sub R, -, R */
|
|
o1 = p->as == AADD ? 0x0<<9 : 0x1<<9;
|
|
if(r == NREG)
|
|
r = rt;
|
|
lowreg(p, rf);
|
|
lowreg(p, r);
|
|
lowreg(p, rt);
|
|
o1 |= (0x6<<10) | (rf<<6) | (r<<3) | rt;
|
|
break;
|
|
case 3: /* add/sub $I, R, R or add/sub $I, -, R */
|
|
thumbaclass(&p->from, p);
|
|
o1 = p->as == AADD ? 0x0<<9 : 0x1<<9;
|
|
if(r == NREG)
|
|
r = rt;
|
|
numr(p, instoffset, 0, 7);
|
|
lowreg(p, r);
|
|
lowreg(p, rt);
|
|
o1 |= (0x7<<10) | (instoffset<<6) | (r<<3) | rt;
|
|
break;
|
|
case 4: /* shift $I, R, R or shift $I, -, R */
|
|
thumbaclass(&p->from, p);
|
|
if(instoffset < 0)
|
|
diag("negative shift in thumbasmout");
|
|
instoffset %= 32;
|
|
o1 = thumbopri(p->as);
|
|
if(r == NREG)
|
|
r = rt;
|
|
numr(p, instoffset, 0, 31);
|
|
lowreg(p, r);
|
|
lowreg(p, rt);
|
|
o1 |= (0x0<<13) | (instoffset<<6) | (r<<3) | rt;
|
|
break;
|
|
case 5: /* add/sub/mov $I, -, R or cmp $I, R, - */
|
|
thumbaclass(&p->from, p);
|
|
o1 = thumbopri(p->as);
|
|
if(rt == NREG)
|
|
rt = r;
|
|
numr(p, instoffset, 0, 255);
|
|
lowreg(p, rt);
|
|
o1 |= (0x1<<13) | (rt<<8) | instoffset;
|
|
break;
|
|
case 6: /* add $I, PC/SP, R */
|
|
if(p->as == ASUB)
|
|
diag("subtract in add $I, PC/SP, R");
|
|
thumbaclass(&p->from, p);
|
|
o1 = r == REGSP ? 0x1<<11 : 0x0<<11;
|
|
numr(p, instoffset, 0, 255);
|
|
regis(p, r, REGSP, REGPC);
|
|
lowreg(p, rt);
|
|
o1 |= (0xa<<12) | (rt<<8) | instoffset;
|
|
break;
|
|
case 7: /* add, sub $I, SP */
|
|
thumbaclass(&p->from, p);
|
|
o1 = p->as == AADD ? 0x0<<7 : 0x1<<7;
|
|
numr(p, instoffset, 0, 508);
|
|
mult(p, instoffset, 4);
|
|
regis(p, rt, REGSP, REGSP);
|
|
o1 |= (0xb0<<8) | (instoffset>>2);
|
|
break;
|
|
case 8: /* add/mov/cmp R, R where at least 1 reg is high */
|
|
o1 = 0;
|
|
if(rt == NREG)
|
|
rt = r;
|
|
if(high(rf)){
|
|
o1 |= 1<<6;
|
|
rf -= 8;
|
|
}
|
|
if(high(rt)){
|
|
o1 |= 2<<6;
|
|
rt -= 8;
|
|
}
|
|
if(o1 == 0)
|
|
diag("no high register(%P)", p);
|
|
o1 |= thumbophh(p->as);
|
|
o1 |= (0x11<<10) | (rf<<3) | rt;
|
|
break;
|
|
case 9: /* B $I */
|
|
thumbaclass(&p->to, p);
|
|
numr(p, instoffset, -2048, 2046);
|
|
o1 = (0x1c<<11) | ((instoffset>>1)&0x7ff);
|
|
break;
|
|
case 10: /* Bcc $I */
|
|
thumbaclass(&p->to, p);
|
|
numr(p, instoffset, -256, 254);
|
|
o1 = thumbopbra(p->as);
|
|
o1 |= (0xd<<12) | ((instoffset>>1)&0xff);
|
|
break;
|
|
case 11: /* BL $I */
|
|
thumbaclass(&p->to, p);
|
|
numr(p, instoffset, -4194304, 4194302);
|
|
o1 = (0x1e<<11) | ((instoffset>>12)&0x7ff);
|
|
o2 = (0x1f<<11) | ((instoffset>>1)&0x7ff);
|
|
break;
|
|
case 12: /* BX $I */
|
|
#ifdef CALLEEBX
|
|
diag("BX $I case");
|
|
#endif
|
|
thumbaclass(&p->to, p);
|
|
if(p->to.sym->thumb)
|
|
instoffset |= 1; // T bit
|
|
o1 = mvlh(REGPC, REGTMPT);
|
|
o2 = (0x6<<11) | (REGTMPT<<8) | 7; // add 7, RTMP (T bit + PC offset)
|
|
o3 = mvlh(REGTMPT, REGLINK);
|
|
o4 = mv(nil, REGTMPT, instoffset);
|
|
o5 = (0x11c<<6) | (REGTMPT<<3);
|
|
// o1 = mv(nil, REGTMPT, v);
|
|
// o2 = (0x11b<<6) | (REGPC<<3) | REGLINK;
|
|
// o3 = (0x11c<<6) | (REGTMPT<<3);
|
|
break;
|
|
case 13: /* B O(R) */
|
|
diag("B O(R)");
|
|
break;
|
|
case 14: /* BL O(R) */
|
|
diag("BL O(R)");
|
|
break;
|
|
case 15: /* BX R */
|
|
o1 = mvlh(REGPC, REGTMPT);
|
|
o2 = (0x6<<11) | (REGTMPT<<8) | 5; // add 5, RTMP (T bit + PC offset)
|
|
o3 = mvlh(REGTMPT, REGLINK);
|
|
o4 = 0;
|
|
if(high(rt)){
|
|
rt -= 8;
|
|
o4 |= 1<<6;
|
|
}
|
|
o4 |= (0x8e<<7) | (rt<<3);
|
|
// o1 = (0x11c<<6) | (rt<<3);
|
|
break;
|
|
case 16: /* SWI $I */
|
|
thumbaclass(&p->to, p);
|
|
numr(p, instoffset, 0, 255);
|
|
o1 = (0xdf<<8) | instoffset;
|
|
break;
|
|
case 17: /* AWORD */
|
|
thumbaclass(&p->to, p);
|
|
o1 = instoffset&0xffff;
|
|
o2 = (instoffset>>16)&0xffff;
|
|
break;
|
|
case 18: /* AMOVW O(SP), R and AMOVW O(PC), R */
|
|
thumbaclass(&p->from, p);
|
|
rf = o->param;
|
|
o1 = rf == REGSP ? 0x13<<11 : 0x9<<11;
|
|
regis(p, rf, REGSP, REGPC);
|
|
lowreg(p, rt);
|
|
mult(p, instoffset, 4);
|
|
numr(p, instoffset/4, 0, 255);
|
|
o1 |= (rt<<8) | (instoffset/4);
|
|
break;
|
|
case 19: /* AMOVW... O(R), R */
|
|
thumbaclass(&p->from, p);
|
|
o1 = thumbopmv(p->as, 1);
|
|
v = 4;
|
|
if(p->as == AMOVHU)
|
|
v = 2;
|
|
else if(p->as == AMOVBU)
|
|
v = 1;
|
|
mult(p, instoffset, v);
|
|
lowreg(p, rf);
|
|
lowreg(p, rt);
|
|
numr(p, instoffset/v, 0, 31);
|
|
o1 |= ((instoffset/v)<<6) | (rf<<3) | rt;
|
|
break;
|
|
case 20: /* AMOVW R, O(SP) */
|
|
thumbaclass(&p->to, p);
|
|
o1 = 0x12<<11;
|
|
if(rt != NREG) regis(p, rt, REGSP, REGSP);
|
|
lowreg(p, rf);
|
|
mult(p, instoffset, 4);
|
|
numr(p, instoffset/4, 0, 255);
|
|
o1 |= (rf<<8) | (instoffset/4);
|
|
break;
|
|
case 21: /* AMOVW... R, O(R) */
|
|
thumbaclass(&p->to, p);
|
|
o1 = thumbopmv(p->as, 0);
|
|
v = 4;
|
|
if(p->as == AMOVHU || p->as == AMOVH)
|
|
v = 2;
|
|
else if(p->as == AMOVBU || p->as == AMOVB)
|
|
v = 1;
|
|
lowreg(p, rf);
|
|
lowreg(p, rt);
|
|
mult(p, instoffset, v);
|
|
numr(p, instoffset/v, 0, 31);
|
|
o1 |= ((instoffset/v)<<6) | (rt<<3) | rf;
|
|
break;
|
|
case 22: /* AMOVW R, R -> ASLL $0, R, R */
|
|
o1 = thumbopri(ASLL);
|
|
lowreg(p, rf);
|
|
lowreg(p, rt);
|
|
o1 |= (0x0<<13) | (rf<<3) | rt;
|
|
break;
|
|
case 23: /* AMOVB/AMOVH/AMOVBU/AMOVHU R, R */
|
|
o1 = thumbopri(ASLL);
|
|
o2 = p->as == AMOVB || p->as == AMOVH ? thumbopri(ASRA) : thumbopri(ASRL);
|
|
v = p->as == AMOVB || p->as == AMOVBU ? 24 : 16;
|
|
lowreg(p, rf);
|
|
lowreg(p, rt);
|
|
o1 |= (0x0<<13) | (v<<6) | (rf<<3) | rt;
|
|
o2 |= (0x0<<13) | (v<<6) | (rt<<3) | rt;
|
|
break;
|
|
case 24: /* AMOVH/AMOVB O(R), R -> AMOVH/AMOVB [R, R], R */
|
|
thumbaclass(&p->from, p);
|
|
lowreg(p, rf);
|
|
lowreg(p, rt);
|
|
if(rf == rt)
|
|
r = REGTMPT;
|
|
else
|
|
r = rt;
|
|
if(p->as == AMOVB)
|
|
numr(p, instoffset, 0, 31);
|
|
else{
|
|
mult(p, instoffset, 2);
|
|
numr(p, instoffset, 0, 62);
|
|
}
|
|
o1 = mv(p, r, instoffset);
|
|
o2 = p->as == AMOVH ? 0x2f<<9 : 0x2b<<9;
|
|
o2 |= (r<<6) | (rf<<3) | rt;
|
|
break;
|
|
case 25: /* MOVW $sacon, R */
|
|
thumbaclass(&p->from, p);
|
|
// print("25: %d %d %d %d\n", instoffset, rf, r, rt);
|
|
if(rf == NREG)
|
|
rf = REGSP;
|
|
lowreg(p, rt);
|
|
if(rf == REGSP){
|
|
mult(p, instoffset, 4);
|
|
numr(p, instoffset>>2, 0, 255);
|
|
o1 = (0x15<<11) | (rt<<8) | (instoffset>>2); // add $O, SP, R
|
|
}
|
|
else if(rf == rt){
|
|
numr(p, instoffset, 0, 255);
|
|
o1 = (0x6<<11) | (rt<<8) | instoffset; // add $O, R
|
|
}
|
|
else{
|
|
lowreg(p, rf);
|
|
numr(p, instoffset, 0, 7);
|
|
o1 = (0xe<<9) | (instoffset<<6) | (rf<<3) | rt; // add $O, Rs, Rd
|
|
}
|
|
break;
|
|
case 26: /* AMOVM $c, oreg -> stmia */
|
|
lowreg(p, rt);
|
|
numr(p, p->from.offset, -256, 255);
|
|
o1 = (0x18<<11) | (rt<<8) | (p->from.offset&0xff);
|
|
break;
|
|
case 27: /* AMOVM oreg, $c ->ldmia */
|
|
lowreg(p, rf);
|
|
numr(p, p->to.offset, -256, 256);
|
|
o1 = (0x19<<11) | (rf<<8) | (p->to.offset&0xff);
|
|
break;
|
|
case 28: /* AMOV* O(R), R -> AMOV* [R, R], R (offset large) */
|
|
thumbaclass(&p->from, p);
|
|
lowreg(p, rf);
|
|
lowreg(p, rt);
|
|
if(rf == rt)
|
|
r = REGTMPT;
|
|
else
|
|
r = rt;
|
|
o1 = mv(p, r, instoffset);
|
|
o2 = thumboprrr(p->as, 1);
|
|
o2 |= (r<<6) | (rf<<3) | rt;
|
|
break;
|
|
case 29: /* AMOV* R, O(R) -> AMOV* R, [R, R] (offset large) */
|
|
thumbaclass(&p->to, p);
|
|
lowreg(p, rf);
|
|
lowreg(p, rt);
|
|
if(rt == REGTMPT){ // used as tmp reg
|
|
if(instoffset >= 0 && instoffset <= 255){
|
|
o1 = (1<<13) | (2<<11) | (rt<<8) | instoffset; // add $O, R7
|
|
o2 = thumbopirr(p->as, 0);
|
|
o2 |= (0<<6) | (rt<<3) | rf; // mov* R, 0(R)
|
|
}
|
|
else
|
|
diag("big offset - case 29");
|
|
}
|
|
else{
|
|
o1 = mv(p, REGTMPT, instoffset);
|
|
o2 = thumboprrr(p->as, 0);
|
|
o2 |= (REGTMPT<<6) | (rt<<3) | rf;
|
|
}
|
|
break;
|
|
case 30: /* AMOVW... *addr, R */
|
|
diag("likely broken"); // does this still refer to SB?
|
|
thumbaclass(&p->from, p);
|
|
o1 = mv(p, rt, instoffset); // MOV addr, rtmp
|
|
o2 = thumbopmv(p->as, 1);
|
|
lowreg(p, rt);
|
|
o2 |= (rt<<3) | rt; // MOV* 0(rtmp), R
|
|
break;
|
|
case 31: /* AMOVW... R, *addr */
|
|
diag("likely broken"); // does this still refer to SB?
|
|
thumbaclass(&p->to, p);
|
|
o1 = mv(p, REGTMPT, instoffset);
|
|
o2 = thumbopmv(p->as, 0);
|
|
lowreg(p, rf);
|
|
o2 |= (REGTMPT<<3) | rf;
|
|
break;
|
|
case 32: /* AMOVH/AMOVB *addr, R -> AMOVH/AMOVB [R, R], R */
|
|
thumbaclass(&p->from, p);
|
|
o1 = mv(p, rt, instoffset);
|
|
lowreg(p, rt);
|
|
o2 = mv(nil, REGTMPT, 0);
|
|
o3 = p->as == AMOVH ? 0x2f<<9 : 0x2b<<9;
|
|
o3 |= (REGTMPT<<6) | (rt<<3) | rt;
|
|
break;
|
|
case 33: /* AMOVW O(SP), R (O large) */
|
|
thumbaclass(&p->from, p);
|
|
lowreg(p, rt);
|
|
o1 = mv(p, rt, instoffset);
|
|
o2 = (0x111<<6) | (REGSP-8)<<3 | rt; // add SP, rt
|
|
o3 = thumbopmv(p->as, 1);
|
|
o3 |= (rt<<3) | rt;
|
|
break;
|
|
case 34: /* AMOVW R, O(SP) (O large) */
|
|
thumbaclass(&p->to, p);
|
|
lowreg(p, rf);
|
|
o1 = mv(p, REGTMPT, instoffset);
|
|
o2 = (0x111<<6) | (REGSP-8)<<3 | REGTMPT; // add SP, REGTMP
|
|
o3 = thumbopmv(p->as, 0);
|
|
o3 |= (REGTMPT<<3) | rf;
|
|
break;
|
|
case 35: /* AMOVW $lacon, R */
|
|
thumbaclass(&p->from, p);
|
|
lowreg(p, rt);
|
|
if(rf == NREG)
|
|
rf = REGSP;
|
|
if(rf == rt)
|
|
rf = r = REGTMPT;
|
|
else
|
|
r = rt;
|
|
// print("35: io=%d rf=%d rt=%d\n", instoffset, rf, rt);
|
|
o1 = mv(p, r, instoffset); // mov O, Rd
|
|
if(high(rf))
|
|
o2 = (0x44<<8) | (0x1<<6) | ((rf-8)<<3) | rt; // add Rs, Rd
|
|
else
|
|
o2 = (0x6<<10) | (rf<<6) | (rt<<3) | rt; // add Rs, Rd
|
|
break;
|
|
case 36: /* AADD/ASUB $i, r, r when $i too big */
|
|
thumbaclass(&p->from, p);
|
|
lowreg(p, r);
|
|
lowreg(p, rt);
|
|
o1 = mv(p, REGTMPT, instoffset);
|
|
o2 = p->as == AADD ? 0xc<<9 : 0xd<<9;
|
|
o2 |= (REGTMPT<<6) | (r<<3) | rt;
|
|
break;
|
|
case 37: /* AADD/ASUB $i, r when $i too big */
|
|
thumbaclass(&p->from, p);
|
|
lowreg(p, rt);
|
|
o1 = mv(p, REGTMPT, instoffset);
|
|
o2 = p->as == AADD ? 0xc<<9 : 0xd<<9;
|
|
o2 |= (REGTMPT<<6) | (rt<<3) | rt;
|
|
break;
|
|
case 38: /* AMOVW $i, r when $i too big */
|
|
thumbaclass(&p->from, p);
|
|
lowreg(p, rt);
|
|
o1 = mv(p, rt, instoffset);
|
|
break;
|
|
case 39: /* ACMP $i, r when $i too big */
|
|
thumbaclass(&p->from, p);
|
|
lowreg(p, r);
|
|
o1 = mv(p, REGTMPT, instoffset);
|
|
o2 = (0x10a<<6) | (REGTMPT<<3) | r;
|
|
break;
|
|
case 40: /* add, sub $I, SP when $I large*/
|
|
thumbaclass(&p->from, p);
|
|
if(p->as == ASUB)
|
|
instoffset = -instoffset;
|
|
o1 = mv(p, REGTMPT, instoffset);
|
|
o2 = (0x112<<6) | (REGTMPT<<3) | (REGSP-8);
|
|
regis(p, rt, REGSP, REGSP);
|
|
break;
|
|
case 41: /* BL LBRA */
|
|
thumbaclass(&p->to, p);
|
|
o1 = (0x9<<11) | (REGTMPT<<8); // mov 0(pc), r7
|
|
o2 = mvlh(REGTMPT, REGPC); // mov r7, pc
|
|
o3 = instoffset&0xffff; // $lab
|
|
o4 = (instoffset>>16)&0xffff;
|
|
break;
|
|
case 42: /* Bcc GBRA */
|
|
thumbaclass(&p->to, p);
|
|
o1 = (0xd<<12) | thumbopbra(relinv(p->as)) | (6>>1); // bccnot
|
|
// ab lbra
|
|
o2 = (0x9<<11) | (REGTMPT<<8); // mov 0(pc), r7
|
|
o3 = mvlh(REGTMPT, REGPC); // mov r7, pc
|
|
o4 = instoffset&0xffff; // $lab
|
|
o5 = (instoffset>>16)&0xffff;
|
|
break;
|
|
case 43: /* BL LBRA */
|
|
thumbaclass(&p->to, p);
|
|
o1 = mvlh(REGPC, REGTMPT); // mov pc, r7
|
|
o2 = (0x6<<11) | (REGTMPT<<8) | 10; // add 10, r7
|
|
o3 = mvlh(REGTMPT, REGLINK); // mov r7, lr
|
|
o4 = (0x9<<11) | (REGTMPT<<8); // mov o(pc), r7
|
|
o5 = mvlh(REGTMPT, REGPC); // mov r7, pc
|
|
o6 = instoffset&0xffff; // $lab
|
|
o7 = (instoffset>>16)&0xffff;
|
|
break;
|
|
case 44: /* BX LBRA */
|
|
#ifdef CALLEEBX
|
|
diag("BX LBRA case");
|
|
#endif
|
|
thumbaclass(&p->to, p);
|
|
if(p->to.sym->thumb)
|
|
instoffset |= 1; // T bit
|
|
o1 = mvlh(REGPC, REGTMPT); // mov pc, r7
|
|
o2 = (0x6<<11) | (REGTMPT<<8) | 11; // add 11, r7
|
|
o3 = mvlh(REGTMPT, REGLINK); // mov r7, lr
|
|
o4 = (0x9<<11) | (REGTMPT<<8); // mov o(pc), r7
|
|
o5 = (0x11c<<6) | (REGTMPT<<3); // bx r7
|
|
o6 = instoffset&0xffff; // $lab
|
|
o7 = (instoffset>>16)&0xffff;
|
|
break;
|
|
case 45: /* BX R when returning from fn */
|
|
o1 = 0;
|
|
if(high(rt)){
|
|
rt -= 8;
|
|
o1 |= 1<<6;
|
|
}
|
|
o1 |= (0x8e<<7) | (rt<<3);
|
|
break;
|
|
case 46: /* Bcc LBRA */
|
|
thumbaclass(&p->to, p);
|
|
o1 = (0xd<<12) | thumbopbra(relinv(p->as)) | (0>>1); // bccnot
|
|
// ab lbra
|
|
instoffset -= 2;
|
|
numr(p, instoffset, -2048, 2046);
|
|
o2 = (0x1c<<11) | ((instoffset>>1)&0x7ff);
|
|
break;
|
|
case 47: /* mov $i, R where $i can be built */
|
|
thumbaclass(&p->from, p);
|
|
mvcon(p, rt, instoffset, &o1, &o2);
|
|
break;
|
|
case 48: /* ACMP $i, r when $i built up */
|
|
thumbaclass(&p->from, p);
|
|
lowreg(p, r);
|
|
mvcon(p, REGTMPT, instoffset, &o1, &o2);
|
|
o3 = (0x10a<<6) | (REGTMPT<<3) | r;
|
|
break;
|
|
case 49: /* AADD $i, r, r when $i is between 0 and 255 - could merge with case 36 */
|
|
thumbaclass(&p->from, p);
|
|
lowreg(p, r);
|
|
lowreg(p, rt);
|
|
numr(p, instoffset, 0, 255);
|
|
o1 = mv(p, REGTMPT, instoffset);
|
|
o2 = p->as == AADD ? 0xc<<9 : 0xd<<9;
|
|
o2 |= (REGTMPT<<6) | (r<<3) | rt;
|
|
break;
|
|
case 50: /* ADWORD */
|
|
thumbaclass(&p->from, p);
|
|
o1 = instoffset&0xffff;
|
|
o2 = (instoffset>>16)&0xffff;
|
|
thumbaclass(&p->to, p);
|
|
o3 = instoffset&0xffff;
|
|
o4 = (instoffset>>16)&0xffff;
|
|
break;
|
|
case 51: /* BL r */
|
|
o1 = mvlh(REGPC, REGLINK); // mov pc, lr
|
|
o2 = mvlh(rt, REGPC); // mov r, pc
|
|
break;
|
|
}
|
|
|
|
v = p->pc;
|
|
switch(o->size) {
|
|
default:
|
|
if(debug['a'])
|
|
Bprint(&bso, " %.8ux:\t\t%P\n", v, p);
|
|
break;
|
|
case 2:
|
|
if(debug['a'])
|
|
Bprint(&bso, " %.8ux: %.8ux\t%P\n", v, o1, p);
|
|
hputl(o1);
|
|
break;
|
|
case 4:
|
|
if(debug['a'])
|
|
Bprint(&bso, " %.8ux: %.8ux %.8ux\t%P\n", v, o1, o2, p);
|
|
hputl(o1);
|
|
hputl(o2);
|
|
break;
|
|
case 6:
|
|
if(debug['a'])
|
|
Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, p);
|
|
hputl(o1);
|
|
hputl(o2);
|
|
hputl(o3);
|
|
break;
|
|
case 8:
|
|
if(debug['a'])
|
|
Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, p);
|
|
hputl(o1);
|
|
hputl(o2);
|
|
hputl(o3);
|
|
hputl(o4);
|
|
break;
|
|
case 10:
|
|
if(debug['a'])
|
|
Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, p);
|
|
hputl(o1);
|
|
hputl(o2);
|
|
hputl(o3);
|
|
hputl(o4);
|
|
hputl(o5);
|
|
break;
|
|
case 12:
|
|
if(debug['a'])
|
|
Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, o6, p);
|
|
hputl(o1);
|
|
hputl(o2);
|
|
hputl(o3);
|
|
hputl(o4);
|
|
hputl(o5);
|
|
hputl(o6);
|
|
break;
|
|
case 14:
|
|
if(debug['a'])
|
|
Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, o6, o7, p);
|
|
hputl(o1);
|
|
hputl(o2);
|
|
hputl(o3);
|
|
hputl(o4);
|
|
hputl(o5);
|
|
hputl(o6);
|
|
hputl(o7);
|
|
break;
|
|
}
|
|
if(debug['G']){
|
|
if(o->type == 17){
|
|
print("%x: word %d\n", p->pc, (o2<<16)+o1);
|
|
return;
|
|
}
|
|
if(o->type == 50){
|
|
print("%x: word %d\n", p->pc, (o2<<16)+o1);
|
|
print("%x: word %d\n", p->pc, (o4<<16)+o3);
|
|
return;
|
|
}
|
|
if(o->size > 0) dis(o1, p->pc);
|
|
if(o->size > 2) dis(o2, p->pc+2);
|
|
if(o->size > 4) dis(o3, p->pc+4);
|
|
if(o->size > 6) dis(o4, p->pc+6);
|
|
if(o->size > 8) dis(o5, p->pc+8);
|
|
if(o->size > 10) dis(o6, p->pc+10);
|
|
if(o->size > 12) dis(o7, p->pc+12);
|
|
// if(o->size > 14) dis(o8, p->pc+14);
|
|
}
|
|
}
|
|
|
|
static int32
|
|
thumboprr(int a)
|
|
{
|
|
switch(a) {
|
|
case AMVN: return 0xf<<6;
|
|
case ACMP: return 0xa<<6;
|
|
case ACMN: return 0xb<<6;
|
|
case ATST: return 0x8<<6;
|
|
case AADC: return 0x5<<6;
|
|
case ASBC: return 0x6<<6;
|
|
case AMUL:
|
|
case AMULU: return 0xd<<6;
|
|
case AAND: return 0x0<<6;
|
|
case AEOR: return 0x1<<6;
|
|
case AORR: return 0xc<<6;
|
|
case ABIC: return 0xe<<6;
|
|
case ASRL: return 0x3<<6;
|
|
case ASRA: return 0x4<<6;
|
|
case ASLL: return 0x2<<6;
|
|
}
|
|
diag("bad thumbop oprr %d", a);
|
|
prasm(curp);
|
|
return 0;
|
|
}
|
|
|
|
static int32
|
|
thumbopirr(int a, int ld)
|
|
{
|
|
if(ld)
|
|
diag("load in thumbopirr");
|
|
switch(a){
|
|
case AMOVW: return 0xc<<11;
|
|
case AMOVH:
|
|
case AMOVHU: return 0x10<<11;
|
|
case AMOVB:
|
|
case AMOVBU: return 0xe<<11;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int32
|
|
thumboprrr(int a, int ld)
|
|
{
|
|
if(ld){
|
|
switch(a){
|
|
case AMOVW: return 0x2c<<9;
|
|
case AMOVH: return 0x2f<<9;
|
|
case AMOVB: return 0x2b<<9;
|
|
case AMOVHU: return 0x2d<<9;
|
|
case AMOVBU: return 0x2e<<9;
|
|
}
|
|
}
|
|
else{
|
|
switch(a){
|
|
case AMOVW: return 0x28<<9;
|
|
case AMOVHU:
|
|
case AMOVH: return 0x29<<9;
|
|
case AMOVBU:
|
|
case AMOVB: return 0x2a<<9;
|
|
}
|
|
}
|
|
diag("bad thumbop oprrr %d", a);
|
|
prasm(curp);
|
|
return 0;
|
|
}
|
|
|
|
static int32
|
|
thumbopri(int a)
|
|
{
|
|
switch(a) {
|
|
case ASRL: return 0x1<<11;
|
|
case ASRA: return 0x2<<11;
|
|
case ASLL: return 0x0<<11;
|
|
case AADD: return 0x2<<11;
|
|
case ASUB: return 0x3<<11;
|
|
case AMOVW: return 0x0<<11;
|
|
case ACMP: return 0x1<<11;
|
|
}
|
|
diag("bad thumbop opri %d", a);
|
|
prasm(curp);
|
|
return 0;
|
|
}
|
|
|
|
static int32
|
|
thumbophh(int a)
|
|
{
|
|
switch(a) {
|
|
case AADD: return 0x0<<8;
|
|
case AMOVW: return 0x2<<8;
|
|
case ACMP: return 0x1<<8;
|
|
}
|
|
diag("bad thumbop ophh %d", a);
|
|
prasm(curp);
|
|
return 0;
|
|
}
|
|
|
|
static int32
|
|
thumbopbra(int a)
|
|
{
|
|
switch(a) {
|
|
case ABEQ: return 0x0<<8;
|
|
case ABNE: return 0x1<<8;
|
|
case ABCS: return 0x2<<8;
|
|
case ABHS: return 0x2<<8;
|
|
case ABCC: return 0x3<<8;
|
|
case ABLO: return 0x3<<8;
|
|
case ABMI: return 0x4<<8;
|
|
case ABPL: return 0x5<<8;
|
|
case ABVS: return 0x6<<8;
|
|
case ABVC: return 0x7<<8;
|
|
case ABHI: return 0x8<<8;
|
|
case ABLS: return 0x9<<8;
|
|
case ABGE: return 0xa<<8;
|
|
case ABLT: return 0xb<<8;
|
|
case ABGT: return 0xc<<8;
|
|
case ABLE: return 0xd<<8;
|
|
}
|
|
diag("bad thumbop opbra %d", a);
|
|
prasm(curp);
|
|
return 0;
|
|
}
|
|
|
|
static int32
|
|
thumbopmv(int a, int ld)
|
|
{
|
|
switch(a) {
|
|
case AMOVW: return (ld ? 0xd : 0xc)<<11;
|
|
case AMOVH:
|
|
case AMOVHU: return (ld ? 0x11: 0x10)<<11;
|
|
case AMOVB:
|
|
case AMOVBU: return (ld ? 0xf : 0xe)<<11;
|
|
}
|
|
diag("bad thumbop opmv %d", a);
|
|
prasm(curp);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
lowreg(Prog *p, int r)
|
|
{
|
|
if(high(r))
|
|
diag("high reg [%P]", p);
|
|
}
|
|
|
|
static void
|
|
mult(Prog *p, int n, int m)
|
|
{
|
|
if(m*(n/m) != n)
|
|
diag("%d not M(%d) [%P]", n, m, p);
|
|
}
|
|
|
|
static void
|
|
numr(Prog *p, int n, int min, int max)
|
|
{
|
|
if(n < min || n > max)
|
|
diag("%d not in %d-%d [%P]", n, min, max, p);
|
|
}
|
|
|
|
static void
|
|
regis(Prog *p, int r, int r1, int r2)
|
|
{
|
|
if(r != r1 && r != r2)
|
|
diag("reg %d not %d or %d [%P]", r, r1, r2, p);
|
|
}
|
|
|
|
void
|
|
hputl(int n)
|
|
{
|
|
cbp[1] = n>>8;
|
|
cbp[0] = n;
|
|
cbp += 2;
|
|
cbc -= 2;
|
|
if(cbc <= 0)
|
|
cflush();
|
|
}
|
|
|
|
void
|
|
thumbcount()
|
|
{
|
|
int i, c = 0, t = 0;
|
|
|
|
for (i = 0; i < OPCNTSZ; i++)
|
|
t += opcount[i];
|
|
if(t == 0)
|
|
return;
|
|
for (i = 0; i < OPCNTSZ; i++){
|
|
c += opcount[i];
|
|
print("%d: %d %d %d%%\n", i, opcount[i], c, (opcount[i]*100+t/2)/t);
|
|
}
|
|
}
|
|
|
|
char *op1[] = { "lsl", "lsr", "asr" };
|
|
char *op2[] = { "add", "sub" };
|
|
char *op3[] = { "movw", "cmp", "add", "sub" };
|
|
char *op4[] = { "and", "eor", "lsl", "lsr", "asr", "adc", "sbc", "ror",
|
|
"tst", "neg", "cmp", "cmpn", "or", "mul", "bitc", "movn" };
|
|
char *op5[] = { "add", "cmp", "movw", "bx" };
|
|
char *op6[] = { "smovw", "smovh", "smovb", "lmovb", "lmovw", "lmovhu", "lmovbu", "lmovh" };
|
|
char *op7[] = { "smovw", "lmovw", "smovb", "lmovbu" };
|
|
char *op8[] = { "smovh", "lmovhu" };
|
|
char *op9[] = { "smovw", "lmovw" };
|
|
char *op10[] = { "push", "pop" };
|
|
char *op11[] = { "stmia", "ldmia" };
|
|
|
|
char *cond[] = { "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
|
|
"hi", "ls", "ge", "lt", "gt", "le", "al", "nv" };
|
|
|
|
#define B(h, l) bits(i, h, l)
|
|
#define IMM(h, l) B(h, l)
|
|
#define REG(h, l) reg(B(h, l))
|
|
#define LHREG(h, l, lh) lhreg(B(h, l), B(lh, lh))
|
|
#define COND(h, l) cond[B(h, l)]
|
|
#define OP1(h, l) op1[B(h, l)]
|
|
#define OP2(h, l) op2[B(h, l)]
|
|
#define OP3(h, l) op3[B(h, l)]
|
|
#define OP4(h, l) op4[B(h, l)]
|
|
#define OP5(h, l) op5[B(h, l)]
|
|
#define OP6(h, l) op6[B(h, l)]
|
|
#define OP7(h, l) op7[B(h, l)]
|
|
#define OP8(h, l) op8[B(h, l)]
|
|
#define OP9(h, l) op9[B(h, l)]
|
|
#define OP10(h, l) op10[B(h, l)]
|
|
#define OP11(h, l) op11[B(h, l)]
|
|
#define SBZ(h, l) if(IMM(h, l) != 0) diag("%x: %x bits %d,%d not zero", pc, i, h, l)
|
|
#define SNBZ(h, l) if(IMM(h, l) == 0) diag("%x: %x bits %d,%d zero", pc, i, h, l)
|
|
#define SBO(h, l) if(IMM(h, l) != 1) diag("%x: %x bits %d,%d not one", pc, i, h, l)
|
|
|
|
static int
|
|
bits(int i, int h, int l)
|
|
{
|
|
if(h < l)
|
|
diag("h < l in bits");
|
|
return (i&(((1<<(h-l+1))-1)<<l))>>l;
|
|
}
|
|
|
|
static char *
|
|
reg(int r)
|
|
{
|
|
static char s[4][4];
|
|
static int i = 0;
|
|
|
|
if(r < 0 || r > 7)
|
|
diag("register %d out of range", r);
|
|
i++;
|
|
if(i == 4)
|
|
i = 0;
|
|
sprint(s[i], "r%d", r);
|
|
return s[i];
|
|
}
|
|
|
|
static char *regnames[] = { "sp", "lr", "pc" };
|
|
|
|
static char *
|
|
lhreg(int r, int lh)
|
|
{
|
|
static char s[4][4];
|
|
static int i = 0;
|
|
|
|
if(lh == 0)
|
|
return reg(r);
|
|
if(r < 0 || r > 7)
|
|
diag("high register %d out of range", r);
|
|
i++;
|
|
if(i == 4)
|
|
i = 0;
|
|
if(r >= 5)
|
|
sprint(s[i], "%s", regnames[r-5]);
|
|
else
|
|
sprint(s[i], "r%d", r+8);
|
|
return s[i];
|
|
}
|
|
|
|
static void
|
|
illegal(int i, int pc)
|
|
{
|
|
diag("%x: %x illegal instruction", pc, i);
|
|
}
|
|
|
|
static void
|
|
dis(int i, int pc)
|
|
{
|
|
static int lasto;
|
|
int o, l;
|
|
char *op;
|
|
|
|
print("%x: %x: ", pc, i);
|
|
if(i&0xffff0000)
|
|
illegal(i, pc);
|
|
o = B(15, 13);
|
|
switch(o){
|
|
case 0:
|
|
o = B(12, 11);
|
|
switch(o){
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
print("%s %d, %s, %s\n", OP1(12, 11), IMM(10, 6), REG(5, 3), REG(2, 0));
|
|
return;
|
|
case 3:
|
|
if(B(10, 10) == 0)
|
|
print("%s %s, %s, %s\n", OP2(9, 9), REG(8, 6), REG(5, 3), REG(2, 0));
|
|
else
|
|
print("%s %d, %s, %s\n", OP2(9, 9), IMM(8, 6), REG(5, 3), REG(2, 0));
|
|
return;
|
|
}
|
|
case 1:
|
|
print("%s %d, %s\n", OP3(12, 11), IMM(7, 0), REG(10, 8));
|
|
return;
|
|
case 2:
|
|
o = B(12, 10);
|
|
if(o == 0){
|
|
print("%s %s, %s\n", OP4(9, 6), REG(5, 3), REG(2, 0));
|
|
return;
|
|
}
|
|
if(o == 1){
|
|
o = B(9, 8);
|
|
if(o == 3){
|
|
SBZ(7, 7);
|
|
SBZ(2, 0);
|
|
print("%s %s\n", OP5(9, 8), LHREG(5, 3, 6));
|
|
return;
|
|
}
|
|
SNBZ(7, 6);
|
|
print("%s %s, %s\n", OP5(9, 8), LHREG(5, 3, 6), LHREG(2, 0, 7));
|
|
return;
|
|
}
|
|
if(o == 2 || o == 3){
|
|
print("movw %d(pc)[%x], %s\n", 4*IMM(7, 0), 4*IMM(7, 0)+pc+4, REG(10, 8));
|
|
return;
|
|
}
|
|
op = OP6(11, 9);
|
|
if(*op == 'l')
|
|
print("%s [%s, %s], %s\n", op+1, REG(8, 6), REG(5, 3), REG(2, 0));
|
|
else
|
|
print("%s %s, [%s, %s]\n", op+1, REG(2, 0), REG(8, 6), REG(5, 3));
|
|
return;
|
|
case 3:
|
|
op = OP7(12, 11);
|
|
if(B(12, 11) == 0 || B(12,11) == 1)
|
|
l = 4;
|
|
else
|
|
l = 1;
|
|
if(*op == 'l')
|
|
print("%s %d(%s), %s\n", op+1, l*IMM(10, 6), REG(5, 3), REG(2, 0));
|
|
else
|
|
print("%s %s, %d(%s)\n", op+1, REG(2, 0), l*IMM(10, 6), REG(5, 3));
|
|
return;
|
|
case 4:
|
|
if(B(12, 12) == 0){
|
|
op = OP8(11, 11);
|
|
if(*op == 'l')
|
|
print("%s %d(%s), %s\n", op+1, 2*IMM(10, 6), REG(5, 3), REG(2, 0));
|
|
else
|
|
print("%s %s, %d(%s)\n", op+1, REG(2, 0), 2*IMM(10, 6), REG(5, 3));
|
|
return;
|
|
}
|
|
op = OP9(11, 11);
|
|
if(*op == 'l')
|
|
print("%s %d(sp), %s\n", op+1, 4*IMM(7, 0), REG(10, 8));
|
|
else
|
|
print("%s %s, %d(sp)\n", op+1, REG(10, 8), 4*IMM(7, 0));
|
|
return;
|
|
case 5:
|
|
if(B(12, 12) == 0){
|
|
if(B(11, 11) == 0)
|
|
print("add %d, pc, %s\n", 4*IMM(7, 0), REG(10, 8));
|
|
else
|
|
print("add %d, sp, %s\n", 4*IMM(7, 0), REG(10, 8));
|
|
return;
|
|
}
|
|
if(B(11, 8) == 0){
|
|
print("%s %d, sp\n", OP2(7, 7), 4*IMM(6, 0));
|
|
return;
|
|
}
|
|
SBO(10, 10);
|
|
SBZ(9, 9);
|
|
if(B(8, 8) == 0)
|
|
print("%s sp, %d\n", OP10(11, 11), IMM(7, 0));
|
|
else
|
|
print("%s sp, %d|15\n", OP10(11, 11), IMM(7, 0));
|
|
return;
|
|
case 6:
|
|
if(B(12, 12) == 0){
|
|
print("%s %s, %d\n", OP11(11, 11), REG(10, 8), IMM(7, 0));
|
|
return;
|
|
}
|
|
if(B(11, 8) == 0xf){
|
|
print("swi %d\n", IMM(7, 0));
|
|
return;
|
|
}
|
|
o = IMM(7, 0);
|
|
if(o&0x80)
|
|
o |= 0xffffff00;
|
|
o = pc+4+(o<<1);
|
|
print("b%s %x\n", COND(11, 8), o);
|
|
return;
|
|
case 7:
|
|
o = B(12, 11);
|
|
switch(o){
|
|
case 0:
|
|
o = IMM(10, 0);
|
|
if(o&0x400)
|
|
o |= 0xfffff800;
|
|
o = pc+4+(o<<1);
|
|
print("b %x\n", o);
|
|
return;
|
|
case 1:
|
|
illegal(i, pc);
|
|
return;
|
|
case 2:
|
|
lasto = IMM(10, 0);
|
|
print("bl\n");
|
|
return;
|
|
case 3:
|
|
if(lasto&0x400)
|
|
lasto |= 0xfffff800;
|
|
o = IMM(10, 0);
|
|
o = (pc-2)+4+(o<<1)+(lasto<<12);
|
|
print("bl %x\n", o);
|
|
return;
|
|
}
|
|
}
|
|
}
|