303 lines
6.2 KiB
C
303 lines
6.2 KiB
C
|
/* cpu.c qnice cpu-description file */
|
||
|
/* (c) in 2016 by Volker Barthelmann */
|
||
|
|
||
|
#include "vasm.h"
|
||
|
|
||
|
char *cpu_copyright="vasm qnice cpu backend 0.1 (c) in 2016 Volker Barthelmann";
|
||
|
char *cpuname="qnice";
|
||
|
|
||
|
mnemonic mnemonics[]={
|
||
|
#include "opcodes.h"
|
||
|
};
|
||
|
|
||
|
int mnemonic_cnt=sizeof(mnemonics)/sizeof(mnemonics[0]);
|
||
|
|
||
|
int bitsperbyte=8;
|
||
|
int bytespertaddr=4;
|
||
|
|
||
|
static char *skip_reg(char *s,int *reg)
|
||
|
{
|
||
|
int r=-1;
|
||
|
if(*s!='r'&&*s!='R'){
|
||
|
cpu_error(1);
|
||
|
return s;
|
||
|
}
|
||
|
s++;
|
||
|
if(*s<'0'||*s>'9'){
|
||
|
cpu_error(1);
|
||
|
return s;
|
||
|
}
|
||
|
r=*s++-'0';
|
||
|
if(*s>='0'&&*s<='5')
|
||
|
r=10*r+*s++-'0';
|
||
|
*reg=r;
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
int parse_operand(char *p,int len,operand *op,int requires)
|
||
|
{
|
||
|
op->type=-1;
|
||
|
p=skip(p);
|
||
|
if(requires==OP_REL){
|
||
|
char *s=p;
|
||
|
op->type=OP_REL;
|
||
|
op->offset=parse_expr(&s);
|
||
|
simplify_expr(op->offset);
|
||
|
if(s==p)
|
||
|
return 0;
|
||
|
else
|
||
|
return 1;
|
||
|
}
|
||
|
if(requires==OP_CC){
|
||
|
static const char ccodes[]="1xcznvim";
|
||
|
int i;
|
||
|
op->type=OP_CC;
|
||
|
if((p[0]=='!'&&len!=2)||(p[0]!='!'&&len!=1))
|
||
|
return 0;
|
||
|
if(*p=='!'){
|
||
|
op->cc=8;
|
||
|
p++;
|
||
|
}else
|
||
|
op->cc=0;
|
||
|
for(i=0;i<sizeof(ccodes);i++){
|
||
|
if(*p==ccodes[i]||tolower(*p)==ccodes[i])
|
||
|
break;
|
||
|
}
|
||
|
if(i>=sizeof(ccodes))
|
||
|
return 0;
|
||
|
op->cc|=i;
|
||
|
return 1;
|
||
|
}
|
||
|
if((p[0]=='r'||p[0]=='R')&&p[1]>='0'&&p[1]<='9'&&len==2){
|
||
|
op->type=OP_REG;
|
||
|
op->reg=p[1]-'0';
|
||
|
}else if((p[0]=='r'||p[0]=='R')&&p[1]=='1'&&p[2]>='0'&&p[2]<='5'&&len==3){
|
||
|
op->type=OP_REG;
|
||
|
op->reg=(p[1]-'0')*10+p[2]-'0';
|
||
|
}else if(*p=='@'){
|
||
|
p=skip(p+1);
|
||
|
if(*p=='-'&&p[1]=='-'){
|
||
|
p=skip(p+2);
|
||
|
p=skip_reg(p,&op->reg);
|
||
|
p=skip(p);
|
||
|
op->type=OP_PREDEC;
|
||
|
}else{
|
||
|
p=skip_reg(p,&op->reg);
|
||
|
p=skip(p);
|
||
|
if(*p=='+'&&p[1]=='+'){
|
||
|
p=skip(p+2);
|
||
|
op->type=OP_POSTINC;
|
||
|
}else{
|
||
|
op->type=OP_REGIND;
|
||
|
}
|
||
|
}
|
||
|
}else{
|
||
|
if(*p=='#'){
|
||
|
op->reg=1;
|
||
|
p=skip(p+1);
|
||
|
}else
|
||
|
op->reg=0;
|
||
|
op->offset=parse_expr(&p);
|
||
|
op->type=OP_ABS;
|
||
|
}
|
||
|
if(requires==op->type)
|
||
|
return 1;
|
||
|
if(requires==OP_GEN&&op->type>=OP_REG&&op->type<=OP_ABS)
|
||
|
return 1;
|
||
|
if(requires==OP_DGEN&&op->type>=OP_REG&&op->type<OP_ABS)
|
||
|
return 1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static taddr reloffset(expr *tree,section *sec,taddr pc)
|
||
|
{
|
||
|
symbol *sym;
|
||
|
int btype;
|
||
|
taddr val;
|
||
|
simplify_expr(tree);
|
||
|
if(tree->type==NUM){
|
||
|
/* should we do it like this?? */
|
||
|
val=tree->c.val;
|
||
|
}else{
|
||
|
btype=find_base(tree,&sym,sec,pc);
|
||
|
if(btype!=BASE_OK||!LOCREF(sym)||sym->sec!=sec)
|
||
|
general_error(38);
|
||
|
else{
|
||
|
eval_expr(tree,&val,sec,pc);
|
||
|
val=(val-pc-4)/2;
|
||
|
}
|
||
|
}
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static taddr absoffset(expr *tree,section *sec,taddr pc,rlist **relocs,int mode,int roffset,int size)
|
||
|
{
|
||
|
taddr val;
|
||
|
if(!eval_expr(tree,&val,sec,pc)){
|
||
|
taddr addend=val;
|
||
|
symbol *base;
|
||
|
if(find_base(tree,&base,sec,pc)!=BASE_OK){
|
||
|
general_error(38);
|
||
|
return val;
|
||
|
}
|
||
|
add_nreloc_masked(relocs,base,addend,REL_ABS,size,roffset,mode?0x1FFFE:0xFFFF);
|
||
|
return val;
|
||
|
}
|
||
|
if(mode!=0)
|
||
|
val=(val>>1)&0xFFFF;
|
||
|
else if(size==16)
|
||
|
val&=0xffff;
|
||
|
else
|
||
|
val&=((1<<size)-1);
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
static int translate(instruction *p,section *sec,taddr pc)
|
||
|
{
|
||
|
int c=p->code;
|
||
|
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
/* Convert an instruction into a DATA atom including relocations,
|
||
|
if necessary. */
|
||
|
dblock *eval_instruction(instruction *p,section *sec,taddr pc)
|
||
|
{
|
||
|
dblock *db=new_dblock();
|
||
|
int opcode,c,osize;
|
||
|
unsigned int code,addr1,addr2,aflag=0;
|
||
|
char *d;
|
||
|
taddr val;
|
||
|
rlist *relocs=0;
|
||
|
|
||
|
c=translate(p,sec,pc);
|
||
|
|
||
|
db->size=osize=instruction_size(p,sec,pc);
|
||
|
db->data=mymalloc(db->size);
|
||
|
|
||
|
opcode=mnemonics[c].ext.opcode;
|
||
|
|
||
|
code=opcode<<12;
|
||
|
if(p->op[0]){
|
||
|
if(p->op[0]->type==OP_ABS){
|
||
|
code|=15<<8;
|
||
|
code|=(OP_POSTINC-1)<<6;
|
||
|
addr1=absoffset(p->op[0]->offset,sec,pc,&relocs,p->op[0]->reg,16,16);
|
||
|
aflag=1;
|
||
|
}else if(p->op[0]->type==OP_REL){
|
||
|
code|=15<<8;
|
||
|
code|=(OP_POSTINC-1)<<6;
|
||
|
addr1=reloffset(p->op[0]->offset,sec,pc);
|
||
|
aflag=1;
|
||
|
}else{
|
||
|
code|=p->op[0]->reg<<8;
|
||
|
code|=(p->op[0]->type-1)<<6;
|
||
|
}
|
||
|
}
|
||
|
if(mnemonics[c].ext.encoding==0){
|
||
|
if(p->op[1]){
|
||
|
if(p->op[1]->type==OP_ABS){
|
||
|
code|=15<<2;
|
||
|
code|=(OP_POSTINC-1)<<0;
|
||
|
addr2=absoffset(p->op[1]->offset,sec,pc,&relocs,p->op[1]->reg,16,16);
|
||
|
aflag|=2;
|
||
|
}else if(p->op[1]->type==OP_REL){
|
||
|
/* case should not exist */
|
||
|
code|=15<<2;
|
||
|
code|=(OP_POSTINC-1)<<0;
|
||
|
addr2=reloffset(p->op[1]->offset,sec,pc);
|
||
|
}else{
|
||
|
code|=p->op[1]->reg<<2;
|
||
|
code|=(p->op[1]->type-1)<<0;
|
||
|
}
|
||
|
}
|
||
|
}else{
|
||
|
code|=(mnemonics[c].ext.encoding-1)<<4;
|
||
|
code|=p->op[1]->cc<<0;
|
||
|
}
|
||
|
|
||
|
d=db->data;
|
||
|
*d++=code;
|
||
|
*d++=code>>8;
|
||
|
|
||
|
if(aflag&1){
|
||
|
*d++=addr1;
|
||
|
*d++=addr1>>8;
|
||
|
}
|
||
|
if(aflag&2){
|
||
|
*d++=addr2;
|
||
|
*d++=addr2>>8;
|
||
|
}
|
||
|
|
||
|
db->relocs=relocs;
|
||
|
return db;
|
||
|
}
|
||
|
|
||
|
/* Create a dblock (with relocs, if necessary) for size bits of data. */
|
||
|
dblock *eval_data(operand *op,size_t bitsize,section *sec,taddr pc)
|
||
|
{
|
||
|
dblock *new=new_dblock();
|
||
|
taddr val;
|
||
|
new->size=(bitsize+7)/8;
|
||
|
new->data=mymalloc(new->size);
|
||
|
if(op->type!=OP_ABS)
|
||
|
ierror(0);
|
||
|
if(bitsize!=8&&bitsize!=16&&bitsize!=32)
|
||
|
cpu_error(4);
|
||
|
val=absoffset(op->offset,sec,pc,&new->relocs,op->reg,0,bitsize);
|
||
|
if(bitsize==32){
|
||
|
new->data[3]=val>>24;
|
||
|
new->data[2]=val>>16;
|
||
|
new->data[1]=val>>8;
|
||
|
new->data[0]=val;
|
||
|
}else if(bitsize==16){
|
||
|
new->data[1]=val>>8;
|
||
|
new->data[0]=val;
|
||
|
}else
|
||
|
new->data[0]=val;
|
||
|
return new;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Calculate the size of the current instruction; must be identical
|
||
|
to the data created by eval_instruction. */
|
||
|
size_t instruction_size(instruction *p,section *sec,taddr pc)
|
||
|
{
|
||
|
int sz=2;
|
||
|
|
||
|
//int c=translate(p,sec,pc),add=0;
|
||
|
|
||
|
if(p->op[0]&&(p->op[0]->type==OP_ABS||p->op[0]->type==OP_REL))
|
||
|
sz+=2;
|
||
|
if(p->op[1]&&(p->op[1]->type==OP_ABS||p->op[1]->type==OP_REL))
|
||
|
sz+=2;
|
||
|
return sz;
|
||
|
}
|
||
|
|
||
|
operand *new_operand()
|
||
|
{
|
||
|
operand *new=mymalloc(sizeof(*new));
|
||
|
new->type=-1;
|
||
|
return new;
|
||
|
}
|
||
|
|
||
|
/* return true, if initialization was successfull */
|
||
|
int init_cpu()
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* return true, if the passed argument is understood */
|
||
|
int cpu_args(char *p)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* parse cpu-specific directives; return pointer to end of
|
||
|
cpu-specific text */
|
||
|
char *parse_cpu_special(char *s)
|
||
|
{
|
||
|
return s;
|
||
|
}
|