mirror of
git://c9x.me/qbe.git
synced 2026-05-26 15:34:41 +00:00
The parser now uses a vector to get tokens into, similar to how quoted strings are handled. This buffer is PFn-scoped, so function and data parsing can use it. Type parsing has to copy its names to the global heap. While this avoids the "identifier too long" error, it of course calls malloc more often, so could potentially have a small negative performance impact on parsing.
775 lines
12 KiB
C
775 lines
12 KiB
C
#include "all.h"
|
|
#include <stdarg.h>
|
|
|
|
typedef struct Bitset Bitset;
|
|
typedef struct Vec Vec;
|
|
typedef struct Bucket Bucket;
|
|
|
|
struct Vec {
|
|
ulong mag;
|
|
Pool pool;
|
|
size_t esz;
|
|
ulong cap;
|
|
union {
|
|
long long ll;
|
|
long double ld;
|
|
void *ptr;
|
|
} align[];
|
|
};
|
|
|
|
struct Bucket {
|
|
uint nstr;
|
|
char **str;
|
|
};
|
|
|
|
enum {
|
|
VMin = 2,
|
|
VMag = 0xcabba9e,
|
|
NPtr = 256,
|
|
IBits = 12,
|
|
IMask = (1<<IBits) - 1,
|
|
};
|
|
|
|
Typ *typ;
|
|
Ins insb[NIns], *curi;
|
|
|
|
static void *ptr[NPtr];
|
|
static void **pool = ptr;
|
|
static int nptr = 1;
|
|
|
|
static Bucket itbl[IMask+1]; /* string interning table */
|
|
|
|
uint32_t
|
|
hash(char *s)
|
|
{
|
|
uint32_t h;
|
|
|
|
for (h=0; *s; ++s)
|
|
h = *s + 17*h;
|
|
return h;
|
|
}
|
|
|
|
void
|
|
die_(char *file, char *s, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
fprintf(stderr, "%s: dying: ", file);
|
|
va_start(ap, s);
|
|
vfprintf(stderr, s, ap);
|
|
va_end(ap);
|
|
fputc('\n', stderr);
|
|
abort();
|
|
}
|
|
|
|
void *
|
|
emalloc(size_t n)
|
|
{
|
|
void *p;
|
|
|
|
p = calloc(1, n);
|
|
if (!p)
|
|
die("emalloc, out of memory");
|
|
return p;
|
|
}
|
|
|
|
void *
|
|
alloc(size_t n)
|
|
{
|
|
void **pp;
|
|
|
|
if (n == 0)
|
|
return 0;
|
|
if (nptr >= NPtr) {
|
|
pp = emalloc(NPtr * sizeof(void *));
|
|
pp[0] = pool;
|
|
pool = pp;
|
|
nptr = 1;
|
|
}
|
|
return pool[nptr++] = emalloc(n);
|
|
}
|
|
|
|
void
|
|
freeall()
|
|
{
|
|
void **pp;
|
|
|
|
for (;;) {
|
|
for (pp = &pool[1]; pp < &pool[nptr]; pp++)
|
|
free(*pp);
|
|
pp = pool[0];
|
|
if (!pp)
|
|
break;
|
|
free(pool);
|
|
pool = pp;
|
|
nptr = NPtr;
|
|
}
|
|
nptr = 1;
|
|
}
|
|
|
|
void *
|
|
vnew(ulong len, size_t esz, Pool pool)
|
|
{
|
|
void *(*f)(size_t);
|
|
ulong cap;
|
|
Vec *v;
|
|
|
|
for (cap=VMin; cap<len; cap*=2)
|
|
;
|
|
f = pool == PHeap ? emalloc : alloc;
|
|
v = f(cap * esz + sizeof(Vec));
|
|
v->mag = VMag;
|
|
v->cap = cap;
|
|
v->esz = esz;
|
|
v->pool = pool;
|
|
return v + 1;
|
|
}
|
|
|
|
void
|
|
vfree(void *p)
|
|
{
|
|
Vec *v;
|
|
|
|
v = (Vec *)p - 1;
|
|
assert(v->mag == VMag);
|
|
if (v->pool == PHeap) {
|
|
v->mag = 0;
|
|
free(v);
|
|
}
|
|
}
|
|
|
|
void
|
|
vgrow(void *vp, ulong len)
|
|
{
|
|
Vec *v;
|
|
void *v1;
|
|
|
|
v = *(Vec **)vp - 1;
|
|
assert(v+1 && v->mag == VMag);
|
|
if (v->cap >= len)
|
|
return;
|
|
v1 = vnew(len, v->esz, v->pool);
|
|
memcpy(v1, v+1, v->cap * v->esz);
|
|
vfree(v+1);
|
|
*(Vec **)vp = v1;
|
|
}
|
|
|
|
void
|
|
addins(Ins **pvins, uint *pnins, Ins *i)
|
|
{
|
|
if (i->op == Onop)
|
|
return;
|
|
vgrow(pvins, ++(*pnins));
|
|
(*pvins)[(*pnins)-1] = *i;
|
|
}
|
|
|
|
void
|
|
addbins(Ins **pvins, uint *pnins, Blk *b)
|
|
{
|
|
Ins *i;
|
|
|
|
for (i=b->ins; i<&b->ins[b->nins]; i++)
|
|
addins(pvins, pnins, i);
|
|
}
|
|
|
|
char *
|
|
strf(char *s, ...)
|
|
{
|
|
va_list ap;
|
|
int n;
|
|
char *p;
|
|
|
|
va_start(ap, s);
|
|
n = vsnprintf(NULL, 0, s, ap);
|
|
va_end(ap);
|
|
p = alloc(n + 1);
|
|
va_start(ap, s);
|
|
vsnprintf(p, n + 1, s, ap);
|
|
va_end(ap);
|
|
return p;
|
|
}
|
|
|
|
uint32_t
|
|
intern(char *s)
|
|
{
|
|
Bucket *b;
|
|
uint32_t h;
|
|
uint i, n;
|
|
|
|
h = hash(s) & IMask;
|
|
b = &itbl[h];
|
|
n = b->nstr;
|
|
|
|
for (i=0; i<n; i++)
|
|
if (strcmp(s, b->str[i]) == 0)
|
|
return h + (i<<IBits);
|
|
|
|
if (n == 1<<(32-IBits))
|
|
die("interning table overflow");
|
|
if (n == 0)
|
|
b->str = vnew(1, sizeof b->str[0], PHeap);
|
|
else if ((n & (n-1)) == 0)
|
|
vgrow(&b->str, n+n);
|
|
|
|
b->str[n] = emalloc(strlen(s)+1);
|
|
b->nstr = n + 1;
|
|
strcpy(b->str[n], s);
|
|
return h + (n<<IBits);
|
|
}
|
|
|
|
char *
|
|
str(uint32_t id)
|
|
{
|
|
assert(id>>IBits < itbl[id&IMask].nstr);
|
|
return itbl[id&IMask].str[id>>IBits];
|
|
}
|
|
|
|
int
|
|
isreg(Ref r)
|
|
{
|
|
return rtype(r) == RTmp && r.val < Tmp0;
|
|
}
|
|
|
|
int
|
|
iscmp(int op, int *pk, int *pc)
|
|
{
|
|
if (Ocmpw <= op && op <= Ocmpw1) {
|
|
*pc = op - Ocmpw;
|
|
*pk = Kw;
|
|
}
|
|
else if (Ocmpl <= op && op <= Ocmpl1) {
|
|
*pc = op - Ocmpl;
|
|
*pk = Kl;
|
|
}
|
|
else if (Ocmps <= op && op <= Ocmps1) {
|
|
*pc = NCmpI + op - Ocmps;
|
|
*pk = Ks;
|
|
}
|
|
else if (Ocmpd <= op && op <= Ocmpd1) {
|
|
*pc = NCmpI + op - Ocmpd;
|
|
*pk = Kd;
|
|
}
|
|
else
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
igroup(Blk *b, Ins *i, Ins **i0, Ins **i1)
|
|
{
|
|
Ins *ib, *ie;
|
|
|
|
ib = b->ins;
|
|
ie = ib + b->nins;
|
|
switch (i->op) {
|
|
case Oblit0:
|
|
*i0 = i;
|
|
*i1 = i + 2;
|
|
return;
|
|
case Oblit1:
|
|
*i0 = i - 1;
|
|
*i1 = i + 1;
|
|
return;
|
|
case_Opar:
|
|
for (; i>ib && ispar((i-1)->op); i--)
|
|
;
|
|
*i0 = i;
|
|
for (; i<ie && ispar(i->op); i++)
|
|
;
|
|
*i1 = i;
|
|
return;
|
|
case Ocall:
|
|
case_Oarg:
|
|
for (; i>ib && isarg((i-1)->op); i--)
|
|
;
|
|
*i0 = i;
|
|
for (; i<ie && i->op != Ocall; i++)
|
|
;
|
|
assert(i < ie);
|
|
*i1 = i + 1;
|
|
return;
|
|
case Osel1:
|
|
for (; i>ib && (i-1)->op == Osel1; i--)
|
|
;
|
|
assert(i->op == Osel0);
|
|
/* fall through */
|
|
case Osel0:
|
|
*i0 = i++;
|
|
for (; i<ie && i->op == Osel1; i++)
|
|
;
|
|
*i1 = i;
|
|
return;
|
|
default:
|
|
if (ispar(i->op))
|
|
goto case_Opar;
|
|
if (isarg(i->op))
|
|
goto case_Oarg;
|
|
*i0 = i;
|
|
*i1 = i + 1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
int
|
|
argcls(Ins *i, int n)
|
|
{
|
|
return optab[i->op].argcls[n][i->cls];
|
|
}
|
|
|
|
void
|
|
emit(int op, int k, Ref to, Ref arg0, Ref arg1)
|
|
{
|
|
if (curi == insb)
|
|
die("emit, too many instructions");
|
|
*--curi = (Ins){
|
|
.op = op, .cls = k,
|
|
.to = to, .arg = {arg0, arg1}
|
|
};
|
|
}
|
|
|
|
void
|
|
emiti(Ins i)
|
|
{
|
|
emit(i.op, i.cls, i.to, i.arg[0], i.arg[1]);
|
|
}
|
|
|
|
void
|
|
idup(Blk *b, Ins *s, ulong n)
|
|
{
|
|
vgrow(&b->ins, n);
|
|
icpy(b->ins, s, n);
|
|
b->nins = n;
|
|
}
|
|
|
|
Ins *
|
|
icpy(Ins *d, Ins *s, ulong n)
|
|
{
|
|
if (n)
|
|
memmove(d, s, n * sizeof(Ins));
|
|
return d + n;
|
|
}
|
|
|
|
static int cmptab[][2] ={
|
|
/* negation swap */
|
|
[Ciule] = {Ciugt, Ciuge},
|
|
[Ciult] = {Ciuge, Ciugt},
|
|
[Ciugt] = {Ciule, Ciult},
|
|
[Ciuge] = {Ciult, Ciule},
|
|
[Cisle] = {Cisgt, Cisge},
|
|
[Cislt] = {Cisge, Cisgt},
|
|
[Cisgt] = {Cisle, Cislt},
|
|
[Cisge] = {Cislt, Cisle},
|
|
[Cieq] = {Cine, Cieq},
|
|
[Cine] = {Cieq, Cine},
|
|
[NCmpI+Cfle] = {-1, NCmpI+Cfge},
|
|
[NCmpI+Cflt] = {-1, NCmpI+Cfgt},
|
|
[NCmpI+Cfgt] = {-1, NCmpI+Cflt},
|
|
[NCmpI+Cfge] = {-1, NCmpI+Cfle},
|
|
[NCmpI+Cfeq] = {-1, NCmpI+Cfeq},
|
|
[NCmpI+Cfne] = {-1, NCmpI+Cfne},
|
|
[NCmpI+Cfo] = {-1, NCmpI+Cfo},
|
|
[NCmpI+Cfuo] = {-1, NCmpI+Cfuo},
|
|
};
|
|
|
|
int
|
|
cmpop(int c)
|
|
{
|
|
assert(0 <= c && c < NCmp);
|
|
return cmptab[c][1];
|
|
}
|
|
|
|
int
|
|
cmpwlneg(int op)
|
|
{
|
|
if (INRANGE(op, Ocmpw, Ocmpw1))
|
|
return cmptab[op - Ocmpw][0] + Ocmpw;
|
|
if (INRANGE(op, Ocmpl, Ocmpl1))
|
|
return cmptab[op - Ocmpl][0] + Ocmpl;
|
|
die("not a wl comparison");
|
|
}
|
|
|
|
int
|
|
clsmerge(short *pk, short k)
|
|
{
|
|
short k1;
|
|
|
|
k1 = *pk;
|
|
if (k1 == Kx) {
|
|
*pk = k;
|
|
return 0;
|
|
}
|
|
if ((k1 == Kw && k == Kl) || (k1 == Kl && k == Kw)) {
|
|
*pk = Kw;
|
|
return 0;
|
|
}
|
|
return k1 != k;
|
|
}
|
|
|
|
int
|
|
phicls(int t, Tmp *tmp)
|
|
{
|
|
int t1;
|
|
|
|
t1 = tmp[t].phi;
|
|
if (!t1)
|
|
return t;
|
|
t1 = phicls(t1, tmp);
|
|
tmp[t].phi = t1;
|
|
return t1;
|
|
}
|
|
|
|
uint
|
|
phiargn(Phi *p, Blk *b)
|
|
{
|
|
uint n;
|
|
|
|
if (p)
|
|
for (n=0; n<p->narg; n++)
|
|
if (p->blk[n] == b)
|
|
return n;
|
|
return -1;
|
|
}
|
|
|
|
Ref
|
|
phiarg(Phi *p, Blk *b)
|
|
{
|
|
uint n;
|
|
|
|
n = phiargn(p, b);
|
|
assert(n != -1u && "block not found");
|
|
return p->arg[n];
|
|
}
|
|
|
|
Ref
|
|
newtmp(char *prfx, int k, Fn *fn)
|
|
{
|
|
static int n;
|
|
int t;
|
|
|
|
t = fn->ntmp++;
|
|
vgrow(&fn->tmp, fn->ntmp);
|
|
memset(&fn->tmp[t], 0, sizeof(Tmp));
|
|
if (prfx)
|
|
fn->tmp[t].name = strf("%s.%d", prfx, ++n);
|
|
fn->tmp[t].cls = k;
|
|
fn->tmp[t].slot = -1;
|
|
fn->tmp[t].nuse = +1;
|
|
fn->tmp[t].ndef = +1;
|
|
return TMP(t);
|
|
}
|
|
|
|
void
|
|
chuse(Ref r, int du, Fn *fn)
|
|
{
|
|
if (rtype(r) == RTmp)
|
|
fn->tmp[r.val].nuse += du;
|
|
}
|
|
|
|
int
|
|
symeq(Sym s0, Sym s1)
|
|
{
|
|
return s0.type == s1.type && s0.id == s1.id;
|
|
}
|
|
|
|
Ref
|
|
newcon(Con *c0, Fn *fn)
|
|
{
|
|
Con *c1;
|
|
int i;
|
|
|
|
for (i=1; i<fn->ncon; i++) {
|
|
c1 = &fn->con[i];
|
|
if (c0->type == c1->type
|
|
&& symeq(c0->sym, c1->sym)
|
|
&& c0->bits.i == c1->bits.i)
|
|
return CON(i);
|
|
}
|
|
vgrow(&fn->con, ++fn->ncon);
|
|
fn->con[i] = *c0;
|
|
return CON(i);
|
|
}
|
|
|
|
Ref
|
|
getcon(int64_t val, Fn *fn)
|
|
{
|
|
int c;
|
|
|
|
for (c=1; c<fn->ncon; c++)
|
|
if (fn->con[c].type == CBits
|
|
&& fn->con[c].bits.i == val)
|
|
return CON(c);
|
|
vgrow(&fn->con, ++fn->ncon);
|
|
fn->con[c] = (Con){.type = CBits, .bits.i = val};
|
|
return CON(c);
|
|
}
|
|
|
|
int
|
|
addcon(Con *c0, Con *c1, int m)
|
|
{
|
|
if (m != 1 && c1->type == CAddr)
|
|
return 0;
|
|
if (c0->type == CUndef) {
|
|
*c0 = *c1;
|
|
c0->bits.i *= m;
|
|
} else {
|
|
if (c1->type == CAddr) {
|
|
if (c0->type == CAddr)
|
|
return 0;
|
|
c0->type = CAddr;
|
|
c0->sym = c1->sym;
|
|
}
|
|
c0->bits.i += c1->bits.i * m;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
isconbits(Fn *fn, Ref r, int64_t *v)
|
|
{
|
|
Con *c;
|
|
|
|
if (rtype(r) == RCon) {
|
|
c = &fn->con[r.val];
|
|
if (c->type == CBits) {
|
|
*v = c->bits.i;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
salloc(Ref rt, Ref rs, Fn *fn)
|
|
{
|
|
Ref r0, r1;
|
|
int64_t sz;
|
|
|
|
/* we need to make sure
|
|
* the stack remains aligned
|
|
* (rsp = 0) mod 16
|
|
*/
|
|
fn->dynalloc = 1;
|
|
if (rtype(rs) == RCon) {
|
|
sz = fn->con[rs.val].bits.i;
|
|
if (sz < 0 || sz >= INT_MAX-15)
|
|
err("invalid alloc size %"PRId64, sz);
|
|
sz = (sz + 15) & -16;
|
|
emit(Osalloc, Kl, rt, getcon(sz, fn), R);
|
|
} else {
|
|
/* r0 = (r + 15) & -16 */
|
|
r0 = newtmp("isel", Kl, fn);
|
|
r1 = newtmp("isel", Kl, fn);
|
|
emit(Osalloc, Kl, rt, r0, R);
|
|
emit(Oand, Kl, r0, r1, getcon(-16, fn));
|
|
emit(Oadd, Kl, r1, rs, getcon(15, fn));
|
|
if (fn->tmp[rs.val].slot != -1)
|
|
err("unlikely alloc argument %%%s for %%%s",
|
|
fn->tmp[rs.val].name, fn->tmp[rt.val].name);
|
|
}
|
|
}
|
|
|
|
void
|
|
bsinit(BSet *bs, uint n)
|
|
{
|
|
n = (n + NBit-1) / NBit;
|
|
bs->nt = n;
|
|
bs->t = alloc(n * sizeof bs->t[0]);
|
|
}
|
|
|
|
MAKESURE(NBit_is_64, NBit == 64);
|
|
inline static uint
|
|
popcnt(bits b)
|
|
{
|
|
b = (b & 0x5555555555555555) + ((b>>1) & 0x5555555555555555);
|
|
b = (b & 0x3333333333333333) + ((b>>2) & 0x3333333333333333);
|
|
b = (b & 0x0f0f0f0f0f0f0f0f) + ((b>>4) & 0x0f0f0f0f0f0f0f0f);
|
|
b += (b>>8);
|
|
b += (b>>16);
|
|
b += (b>>32);
|
|
return b & 0xff;
|
|
}
|
|
|
|
inline static int
|
|
firstbit(bits b)
|
|
{
|
|
int n;
|
|
|
|
n = 0;
|
|
if (!(b & 0xffffffff)) {
|
|
n += 32;
|
|
b >>= 32;
|
|
}
|
|
if (!(b & 0xffff)) {
|
|
n += 16;
|
|
b >>= 16;
|
|
}
|
|
if (!(b & 0xff)) {
|
|
n += 8;
|
|
b >>= 8;
|
|
}
|
|
if (!(b & 0xf)) {
|
|
n += 4;
|
|
b >>= 4;
|
|
}
|
|
n += (char[16]){4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0}[b & 0xf];
|
|
return n;
|
|
}
|
|
|
|
uint
|
|
bscount(BSet *bs)
|
|
{
|
|
uint i, n;
|
|
|
|
n = 0;
|
|
for (i=0; i<bs->nt; i++)
|
|
n += popcnt(bs->t[i]);
|
|
return n;
|
|
}
|
|
|
|
static inline uint
|
|
bsmax(BSet *bs)
|
|
{
|
|
return bs->nt * NBit;
|
|
}
|
|
|
|
void
|
|
bsset(BSet *bs, uint elt)
|
|
{
|
|
assert(elt < bsmax(bs));
|
|
bs->t[elt/NBit] |= BIT(elt%NBit);
|
|
}
|
|
|
|
void
|
|
bsclr(BSet *bs, uint elt)
|
|
{
|
|
assert(elt < bsmax(bs));
|
|
bs->t[elt/NBit] &= ~BIT(elt%NBit);
|
|
}
|
|
|
|
#define BSOP(f, op) \
|
|
void \
|
|
f(BSet *a, BSet *b) \
|
|
{ \
|
|
uint i; \
|
|
\
|
|
assert(a->nt == b->nt); \
|
|
for (i=0; i<a->nt; i++) \
|
|
a->t[i] op b->t[i]; \
|
|
}
|
|
|
|
BSOP(bscopy, =)
|
|
BSOP(bsunion, |=)
|
|
BSOP(bsinter, &=)
|
|
BSOP(bsdiff, &= ~)
|
|
|
|
int
|
|
bsequal(BSet *a, BSet *b)
|
|
{
|
|
uint i;
|
|
|
|
assert(a->nt == b->nt);
|
|
for (i=0; i<a->nt; i++)
|
|
if (a->t[i] != b->t[i])
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
bszero(BSet *bs)
|
|
{
|
|
memset(bs->t, 0, bs->nt * sizeof bs->t[0]);
|
|
}
|
|
|
|
/* iterates on a bitset, use as follows
|
|
*
|
|
* for (i=0; bsiter(set, &i); i++)
|
|
* use(i);
|
|
*
|
|
*/
|
|
int
|
|
bsiter(BSet *bs, int *elt)
|
|
{
|
|
bits b;
|
|
uint t, i;
|
|
|
|
i = *elt;
|
|
t = i/NBit;
|
|
if (t >= bs->nt)
|
|
return 0;
|
|
b = bs->t[t];
|
|
b &= ~(BIT(i%NBit) - 1);
|
|
while (!b) {
|
|
++t;
|
|
if (t >= bs->nt)
|
|
return 0;
|
|
b = bs->t[t];
|
|
}
|
|
*elt = NBit*t + firstbit(b);
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
dumpts(BSet *bs, Tmp *tmp, FILE *f)
|
|
{
|
|
int t;
|
|
|
|
fprintf(f, "[");
|
|
for (t=Tmp0; bsiter(bs, &t); t++)
|
|
fprintf(f, " %s", tmp[t].name);
|
|
fprintf(f, " ]\n");
|
|
}
|
|
|
|
void
|
|
runmatch(uchar *code, Num *tn, Ref ref, Ref *var)
|
|
{
|
|
Ref stkbuf[20], *stk;
|
|
uchar *s, *pc;
|
|
int bc, i;
|
|
int n, nl, nr;
|
|
|
|
assert(rtype(ref) == RTmp);
|
|
stk = stkbuf;
|
|
pc = code;
|
|
while ((bc = *pc))
|
|
switch (bc) {
|
|
case 1: /* pushsym */
|
|
case 2: /* push */
|
|
assert(stk < &stkbuf[20]);
|
|
assert(rtype(ref) == RTmp);
|
|
nl = tn[ref.val].nl;
|
|
nr = tn[ref.val].nr;
|
|
if (bc == 1 && nl > nr) {
|
|
*stk++ = tn[ref.val].l;
|
|
ref = tn[ref.val].r;
|
|
} else {
|
|
*stk++ = tn[ref.val].r;
|
|
ref = tn[ref.val].l;
|
|
}
|
|
pc++;
|
|
break;
|
|
case 3: /* set */
|
|
var[*++pc] = ref;
|
|
if (*(pc + 1) == 0)
|
|
return;
|
|
/* fall through */
|
|
case 4: /* pop */
|
|
assert(stk > &stkbuf[0]);
|
|
ref = *--stk;
|
|
pc++;
|
|
break;
|
|
case 5: /* switch */
|
|
assert(rtype(ref) == RTmp);
|
|
n = tn[ref.val].n;
|
|
s = pc + 1;
|
|
for (i=*s++; i>0; i--, s++)
|
|
if (n == *s++)
|
|
break;
|
|
pc += *s;
|
|
break;
|
|
default: /* jump */
|
|
assert(bc >= 10);
|
|
pc = code + (bc - 10);
|
|
break;
|
|
}
|
|
}
|