mirror of
https://github.com/golang/go
synced 2024-10-04 11:11:21 -06:00
99cc2fee81
and expected review latency I needed to combine the CLs. 1. Made the 5* toolpath build using the go build system. Hooked the subdirectories to clean.bash but added a separate make5.bash for now. Minor massage to make the code more similar to the current structure of 6c/6a/6l. 2. Change all references from long to int32 in line with similar change for the other toolchains. The end result is that 5c, 5a and 5l can now be compiled and the executables start up properly. Haven't thrown any input at them yet. R=rsc APPROVED=rsc DELTA=1052 (392 added, 328 deleted, 332 changed) OCL=26757 CL=26761
1668 lines
41 KiB
C
1668 lines
41 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 + INITDAT;
|
|
return C_LEXT; /* INITDAT unknown at this stage */
|
|
// return immacon(instoffset, p, C_SEXT, C_LEXT);
|
|
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 + INITDAT;
|
|
if(s->type == STEXT || s->type == SLEAF){
|
|
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_FCON;
|
|
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:
|
|
case SLEAF:
|
|
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 + INITDAT;
|
|
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_LEXT, C_NONE, C_LCON, 0, 0, 0 },
|
|
{ ATEXT, C_LEXT, 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_LEXT, 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_SEXT, C_NONE, C_REG, 30, 4, 0 },
|
|
{ AMOVW, C_SOREG, C_NONE, C_REG, 19, 2, 0 },
|
|
{ AMOVHU, C_SEXT, C_NONE, C_REG, 30, 4, 0 },
|
|
{ AMOVHU, C_SOREG, C_NONE, C_REG, 19, 2, 0 },
|
|
{ AMOVBU, C_SEXT, C_NONE, C_REG, 30, 4, 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_SEXT, 31, 4, 0 },
|
|
{ AMOVW, C_REG, C_NONE, C_SOREG, 21, 2, 0 },
|
|
{ AMOVH, C_REG, C_NONE, C_SEXT, 31, 4, 0 },
|
|
{ AMOVH, C_REG, C_NONE, C_SOREG, 21, 2, 0 },
|
|
{ AMOVB, C_REG, C_NONE, C_SEXT, 31, 4, 0 },
|
|
{ AMOVB, C_REG, C_NONE, C_SOREG, 21, 2, 0 },
|
|
{ AMOVHU, C_REG, C_NONE, C_SEXT, 31, 4, 0 },
|
|
{ AMOVHU, C_REG, C_NONE, C_SOREG, 21, 2, 0 },
|
|
{ AMOVBU, C_REG, C_NONE, C_SEXT, 31, 4, 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_SEXT, C_NONE, C_REG, 32, 6, 0 },
|
|
{ AMOVH, C_SOREG, C_NONE, C_REG, 24, 4, 0 },
|
|
{ AMOVB, C_SEXT, C_NONE, C_REG, 32, 6, 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_LEXT, C_NONE, C_REG, 30, 4, 0, LFROM },
|
|
{ AMOVH, C_LEXT, C_NONE, C_REG, 32, 6, 0, LFROM },
|
|
{ AMOVB, C_LEXT, C_NONE, C_REG, 32, 6, 0, LFROM },
|
|
{ AMOVHU, C_LEXT, C_NONE, C_REG, 30, 4, 0, LFROM },
|
|
{ AMOVBU, C_LEXT, C_NONE, C_REG, 30, 4, 0, LFROM },
|
|
{ AMOVW, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO },
|
|
{ AMOVH, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO },
|
|
{ AMOVB, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO },
|
|
{ AMOVHU, C_REG, C_NONE, C_LEXT, 31, 4, 0, LTO },
|
|
{ AMOVBU, C_REG, C_NONE, C_LEXT, 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("%ulx: %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("%ulx: %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 */
|
|
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 */
|
|
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, " %.8lux:\t\t%P\n", v, p);
|
|
break;
|
|
case 2:
|
|
if(debug['a'])
|
|
Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p);
|
|
hputl(o1);
|
|
break;
|
|
case 4:
|
|
if(debug['a'])
|
|
Bprint(&bso, " %.8lux: %.8lux %.8lux\t%P\n", v, o1, o2, p);
|
|
hputl(o1);
|
|
hputl(o2);
|
|
break;
|
|
case 6:
|
|
if(debug['a'])
|
|
Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux\t%P\n", v, o1, o2, o3, p);
|
|
hputl(o1);
|
|
hputl(o2);
|
|
hputl(o3);
|
|
break;
|
|
case 8:
|
|
if(debug['a'])
|
|
Bprint(&bso, "%.8lux: %.8lux %.8lux %.8lux %.8lux\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, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux\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, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux\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, "%.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux\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("%lx: word %ld\n", p->pc, (o2<<16)+o1);
|
|
return;
|
|
}
|
|
if(o->type == 50){
|
|
print("%lx: word %ld\n", p->pc, (o2<<16)+o1);
|
|
print("%lx: word %ld\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;
|
|
}
|
|
}
|
|
}
|