| git.druid.rocks | index | druid520 | jury | src/ | isa.c |
src/isa.c
/* isa.c: encoding tables and the one decoder everybody uses. */
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "isa.h"
/* each table is indexed by the op byte's high nibble. unlisted slots
* are zero, so name == NULL, which decodes as "bad opcode." */
static const opdef NORMAL[16] =
{
/* 0 */ {"HALT", FMT_NONE, OP_HALT},
/* 1 */ {"LDI", FMT_IR, OP_LDI},
/* 2 */ {"ADD", FMT_IR, OP_ADD},
/* 3 */ {"SUB", FMT_IR, OP_SUB},
/* 4 */ {"AND", FMT_IR, OP_AND},
/* 5 */ {"OR", FMT_IR, OP_OR},
/* 6 */ {"XOR", FMT_IR, OP_XOR},
/* 7 */ {"CMP", FMT_IR, OP_CMP},
/* 8 */ {"JMP", FMT_CA, OP_JCC}, /* name comes from CONDS */
/* 9 */ {"LD", FMT_AR, OP_LD},
/* a */ {"ST", FMT_RA, OP_ST},
/* b */ {"CALL", FMT_A, OP_CALL},
/* c */ {"MODE", FMT_MODE, OP_MODE},
/* d */ {"PUSH", FMT_R, OP_PUSH},
/* e */ {"POP", FMT_R, OP_POP},
/* f */ {"RET", FMT_NONE, OP_RET}
};
static const opdef MODE1[16] =
{
/* 0 */ {"LDR", FMT_PR, OP_LDR},
/* 1 */ {"STR", FMT_RP, OP_STR},
/* 2 */ {"JMPR", FMT_P, OP_JMPR},
/* 3 */ {"CALLR", FMT_P, OP_CALLR},
/* 4 */ {"INCP", FMT_P, OP_INCP},
/* 5 */ {"DECP", FMT_P, OP_DECP}
};
static const opdef MODE2[16] =
{
/* 0 */ {"SHL", FMT_R, OP_SHL},
/* 1 */ {"SHR", FMT_R, OP_SHR},
/* 2 */ {"ROL", FMT_R, OP_ROL},
/* 3 */ {"ROR", FMT_R, OP_ROR},
/* 4 */ {"NOT", FMT_R, OP_NOT},
/* 5 */ {"NEG", FMT_R, OP_NEG},
/* 6 */ {"INC", FMT_R, OP_INC},
/* 7 */ {"DEC", FMT_R, OP_DEC},
/* 8 */ {"OUT", FMT_R, OP_OUT},
/* 9 */ {"OUTC", FMT_R, OP_OUTC}
};
static const opdef MODE3[16] =
{
/* 0 */ {"MOV", FMT_RR, OP_MOV},
/* 1 */ {"SWAP", FMT_RR, OP_SWAP},
/* 2 */ {"ADDR", FMT_RR, OP_ADDR},
/* 3 */ {"ADCR", FMT_RR, OP_ADCR},
/* 4 */ {"SUBR", FMT_RR, OP_SUBR},
/* 5 */ {"SBCR", FMT_RR, OP_SBCR},
/* 6 */ {"CMPR", FMT_RR, OP_CMPR},
/* 7 */ {"ANDR", FMT_RR, OP_ANDR},
/* 8 */ {"ORR", FMT_RR, OP_ORR},
/* 9 */ {"XORR", FMT_RR, OP_XORR}
};
static const opdef MODE4[16] =
{
/* 0 */ {"ADC", FMT_IR, OP_ADC},
/* 1 */ {"SBC", FMT_IR, OP_SBC},
/* 2 */ {"TST", FMT_IR, OP_TST}
};
const opdef* MODES[NMODES] = {NORMAL, MODE1, MODE2, MODE3, MODE4};
const char* CONDS[NCONDS] = {"JMP", "JZ", "JNZ", "JC", "JNC", "JN", "JNN"};
const char* DECMSG[] =
{
"ok.", "pc out of range.", "bad opcode.", "bad mode.", "bad register."
};
int
sameci(const char* a, const char* b)
{
while(*a != '\0' && *b != '\0')
{
if(toupper((unsigned char)*a) != toupper((unsigned char)*b))
{
return 0;
}
a = a + 1;
b = b + 1;
}
return *a == '\0' && *b == '\0';
}
/* bytes after the op byte */
int
fmtextra(int fmt)
{
switch(fmt)
{
case FMT_IR: case FMT_P: case FMT_PR: case FMT_RP: case FMT_RR:
return 1;
case FMT_AR: case FMT_RA: case FMT_CA: case FMT_A:
return 2;
default:
return 0;
}
}
/* decode the instruction at mem[at], mem being len bytes long. fills in
* *in and returns DEC_OK, or says why the bytes arent an instruction. */
int
isadecode(const unsigned char* mem, unsigned long len, unsigned long at, ins* in)
{
unsigned long p = at;
int b;
int n;
memset(in, 0, sizeof(*in));
if(p >= len)
{
return DEC_SHORT;
}
b = mem[p];
p = p + 1;
if((b >> 4) == 0xC)
{
in->mode = b & 0xF;
if(in->mode < 1 || in->mode >= NMODES)
{
return DEC_BADMODE;
}
if(p >= len)
{
return DEC_SHORT;
}
b = mem[p];
p = p + 1;
}
in->op = (b >> 4) & 0xF;
in->nib = b & 0xF;
in->def = &MODES[in->mode][in->op];
if(in->def->name == NULL || in->def->fmt == FMT_MODE)
{
return DEC_BADOP;
}
n = fmtextra(in->def->fmt);
if(p + n > len)
{
return DEC_SHORT;
}
switch(in->def->fmt)
{
case FMT_R: case FMT_IR: case FMT_AR: case FMT_RA:
case FMT_PR: case FMT_RP:
if(in->nib >= NREGS)
{
return DEC_BADREG;
}
break;
case FMT_CA:
if(in->nib >= NCONDS)
{
return DEC_BADOP;
}
break;
default:
if(in->nib != 0)
{
return DEC_BADOP;
}
break;
}
if(n == 1)
{
in->val = mem[p];
in->x = (mem[p] >> 4) & 0xF;
in->y = mem[p] & 0xF;
}
if(n == 2)
{
in->val = ((unsigned int)mem[p] << 8) | mem[p + 1];
}
if(in->def->fmt == FMT_P || in->def->fmt == FMT_PR || in->def->fmt == FMT_RP ||
in->def->fmt == FMT_RR)
{
if(in->x >= NREGS || in->y >= NREGS)
{
return DEC_BADREG;
}
}
in->len = (int)(p + n - at);
return DEC_OK;
}
const char*
isaname(const ins* in)
{
if(in->def->fmt == FMT_CA)
{
return CONDS[in->nib];
}
return in->def->name;
}
/* an instruction as asm source, asm reads this straight back in.
* operand order here and in asm.c's parseins have to match. */
void
isafmt(const ins* in, char* buf)
{
const char* nm = isaname(in);
switch(in->def->fmt)
{
case FMT_R:
sprintf(buf, "%s r%d", nm, in->nib);
break;
case FMT_IR:
sprintf(buf, "%s 0x%02x, r%d", nm, in->val, in->nib);
break;
case FMT_AR:
sprintf(buf, "%s 0x%04x, r%d", nm, in->val, in->nib);
break;
case FMT_RA:
sprintf(buf, "%s r%d, 0x%04x", nm, in->nib, in->val);
break;
case FMT_CA: case FMT_A:
sprintf(buf, "%s 0x%04x", nm, in->val);
break;
case FMT_P:
sprintf(buf, "%s r%d:r%d", nm, in->x, in->y);
break;
case FMT_PR:
sprintf(buf, "%s r%d:r%d, r%d", nm, in->x, in->y, in->nib);
break;
case FMT_RP:
sprintf(buf, "%s r%d, r%d:r%d", nm, in->nib, in->x, in->y);
break;
case FMT_RR:
/* source is the low nibble, dest the high, dest written last */
sprintf(buf, "%s r%d, r%d", nm, in->y, in->x);
break;
default:
sprintf(buf, "%s", nm);
break;
}
}
/* look a mnemonic up. MODE isnt findable, asm adds it for you. */
int
isafind(const char* name, int* mode, int* op, int* nib)
{
int m;
int o;
int c;
const opdef* d;
for(m = 0; m < NMODES; m = m + 1)
{
for(o = 0; o < 16; o = o + 1)
{
d = &MODES[m][o];
if(d->name == NULL || d->fmt == FMT_MODE)
{
continue;
}
if(d->fmt == FMT_CA)
{
for(c = 0; c < NCONDS; c = c + 1)
{
if(sameci(name, CONDS[c]) != 0)
{
*mode = m;
*op = o;
*nib = c;
return 1;
}
}
continue;
}
if(sameci(name, d->name) != 0)
{
*mode = m;
*op = o;
*nib = 0;
return 1;
}
}
}
return 0;
}