void cpu_execute(struct cpu* cpu) { cpu->pc += 2; uint8_t vx = cpu->v[cpu->opcode.x]; uint8_t vy = cpu->v[cpu->opcode.y]; switch (cpu->opcode.op) { case 0x0: switch (cpu->opcode.n) { case 0x0: return cpu_clear(cpu); case 0xE: return cpu_jump(cpu, cpu->stack[--cpu->sp]); default: return cpu_error(cpu); } case 0x1: return cpu_jump(cpu, cpu->opcode.addr); case 0x2: return cpu_call(cpu, cpu->opcode.addr); case 0x3: return cpu_skip(cpu, vx == cpu->opcode.kk); case 0x4: return cpu_skip(cpu, vx != cpu->opcode.kk); case 0x5: return cpu_skip(cpu, vx == vy); case 0x6: return cpu_assign_register(cpu, cpu->opcode.x, cpu->opcode.kk); case 0x7: return cpu_assign_register(cpu, cpu->opcode.x, vx + cpu->opcode.kk); case 0x8: switch (cpu->opcode.n) { case 0x0: return cpu_assign_register(cpu, cpu->opcode.x, vy); case 0x1: return cpu_assign_register(cpu, cpu->opcode.x, vx | vy); case 0x2: return cpu_assign_register(cpu, cpu->opcode.x, vx & vy); case 0x3: return cpu_assign_register(cpu, cpu->opcode.x, vx ^ vy); case 0x4: return cpu_add_carry(cpu, vx, vy); case 0x5: return cpu_subtract_borrow(cpu, vx, vy); case 0x6: return cpu_shift_right(cpu); case 0x7: return cpu_subtract_borrow(cpu, vy, vx); case 0xE: return cpu_shift_left(cpu); default: return cpu_error(cpu); } case 0x9: return cpu_skip(cpu, vx != vy); case 0xA: return cpu_assign_i(cpu, cpu->opcode.addr); case 0xB: return cpu_jump(cpu, cpu->opcode.addr + cpu->v[0]); case 0xC: return cpu_random(cpu); case 0xD: return cpu_draw(cpu); case 0xE: switch (cpu->opcode.kk) { case 0x9E: return cpu_skip(cpu, SDL_GetKeyboardState(NULL)[key_map[vx]]); case 0xA1: return cpu_skip(cpu, !SDL_GetKeyboardState(NULL)[key_map[vx]]); default: return cpu_error(cpu); } case 0xF: switch (cpu->opcode.kk) { case 0x07: return cpu_assign_register(cpu, cpu->opcode.x, cpu->delay_timer); case 0x0A: return cpu_wait_key_press(cpu); case 0x15: return cpu_assign_delay_timer(cpu, vx); case 0x18: return cpu_assign_sound_timer(cpu, vx); case 0x1E: return cpu_assign_i(cpu, cpu->i + vx); case 0x29: return cpu_assign_i(cpu, vx * 5); case 0x33: return cpu_store_bcd(cpu); case 0x55: return cpu_copy_to_memory(cpu); case 0x65: return cpu_copy_from_memory(cpu); default: return cpu_error(cpu); } } return cpu_error(cpu); }
/* Does not do much useful parsing yet. */ int parse_operand(char *p,int len,operand *op,int requires) { p=skip(p); if(len==2&&(p[0]=='r'||p[0]=='R')&&p[1]>='0'&&p[1]<='3'){ op->type=OP_REG; op->basereg=p[1]-'0'; }else if(p[0]=='#'){ op->type=OP_IMM32; p=skip(p+1); op->offset=parse_expr(&p); }else{ int parent=0; expr *tree; op->type=-1; if(*p=='('){ parent=1; p=skip(p+1); } tree=parse_expr(&p); if(!tree) return 0; p=skip(p); if(parent){ if(*p==','){ p=skip(p+1); if((*p!='r'&&*p!='R')||p[1]<'0'||p[1]>'3'){ cpu_error(0); return 0; } op->type=OP_REGIND; op->basereg=p[1]-'0'; p=skip(p+2); } if(*p!=')'){ cpu_error(0); return 0; }else p=skip(p+1); } if(op->type!=OP_REGIND) op->type=OP_ABS; op->offset=tree; } if(requires==op->type) return 1; if(requires==OP_ALL_DEST&&op->type!=OP_IMM32) return 1; if(requires==OP_MEM&&OP_ISMEM(op->type)) return 1; if(requires==OP_ALL) return 1; return 0; }
static taddr absval(expr *tree,section *sec,taddr pc,int bits,int sgn) { taddr val; if(!eval_expr(tree,&val,sec,pc)) cpu_error(0); if(!sgn){ if(val<0||val>=(1<<bits)) cpu_error(1,bits); }else{ if(val<-(1<<(bits-1))||val>=(1<<(bits-1))) cpu_error(1,bits); } return val&((1<<bits)-1); }
static taddr dooffset(int rel,expr *tree,section *sec,taddr pc,rlist **relocs,int roffset,int size,taddr mask) { 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; } if(rel==REL_PC){ val-=pc; addend+=roffset/8; } if(rel!=REL_PC||!LOCREF(base)||base->sec!=sec){ add_nreloc_masked(relocs,base,addend,rel,size,roffset,mask); return 0; } } #if 0 if(val<-(1<<(size-1))||val>((1<<(size-1))-1)) cpu_error(1,size); #endif return val&mask; }
static char *fill_operand(operand *p,section *sec,taddr pc,char *d,rlist **relocs,int roffset) { taddr val; if(!p||p->type==OP_REG) return d; /* FIXME: Test for valid operand, create reloc */ if(!eval_expr(p->offset,&val,sec,pc)){ nreloc *reloc=new_nreloc(); rlist *rl=mymalloc(sizeof(*rl)); rl->type=REL_ABS; reloc->offset=roffset*8; reloc->size=16; reloc->sym=find_base(p->offset,sec,pc); if(!reloc->sym){ cpu_error(2); }else{ rl->reloc=reloc; rl->next=*relocs; *relocs=rl; } } *d++=val>>24; *d++=val>>16; *d++=val>>8; *d++=val; return d; }
/* Convert an instruction into a DATA atom including relocations, if necessary. */ dblock *eval_instruction(instruction *p,section *sec,taddr pc) { /* Auch simpel. Fehlerchecks fehlen. */ taddr size=instruction_size(p,sec,pc); dblock *db=new_dblock(); int c=opt_inst(p,sec,pc); char *d; taddr val; db->size=size; d=db->data=mymalloc(size); *d=c; if(p->qualifiers[0]){ if(c>5) cpu_error(1,p->qualifiers[0]); else if(!strcmp(p->qualifiers[0],"b")) *d|=128; else if(strcmp(p->qualifiers[0],"w")) cpu_error(1,p->qualifiers[0]); } d++; if(c==5){ /* addq */ taddr val; if(!eval_expr(p->op[0]->offset,&val,sec,pc)||val>15) cpu_error(0); *d=((val<<4)|operand_code(p->op[1])); return db; } if(c==7){ expr *tree=p->op[0]->offset; if(!(tree->type==SYM&&tree->c.sym->sec==sec&&tree->c.sym->type==LABSYM&& tree->c.sym->pc-pc>=-128&&tree->c.sym->pc-pc<=127)) cpu_error(0); else *d=tree->c.sym->pc-pc; return db; } *d=((operand_code(p->op[0])<<4)|operand_code(p->op[1])); d++; d=fill_operand(p->op[0],sec,pc,d,&db->relocs,d-db->data); d=fill_operand(p->op[1],sec,pc,d,&db->relocs,d-db->data); return db; }
static int chkval(expr *tree,section *sec,taddr pc,int bits,int sgn) { taddr val; if(!eval_expr(tree,&val,sec,pc)) cpu_error(0); if(!sgn){ if(val<0||val>=(1<<bits)) return 0; }else{ if(val<-(1<<(bits-1))||val>=(1<<(bits-1))) return 0; } return 1; }
int parse_operand(char *p,int len,operand *op,int required) { char *start = p; int indir = 0; p = skip(p); if (len>0 && required!=DATAOP && check_indir(p,start+len)) { indir = 1; p = skip(p+1); } switch (required) { case IMMED: if (*p++ != '#') return PO_NOMATCH; p = skip(p); break; case INDIR: case INDIRX: case INDX: case INDY: case DPINDIR: if (!indir) return PO_NOMATCH; break; default: if (indir) return PO_NOMATCH; break; } if (required < ACCU) op->value = parse_expr(&p); else op->value = NULL; switch (required) { case INDX: case INDIRX: if (*p++ == ',') { p = skip(p); if (toupper((unsigned char)*p++) != 'X') return PO_NOMATCH; } else return PO_NOMATCH; break; case ACCU: if (len != 0) { if (len!=1 || toupper((unsigned char)*p++) != 'A') return PO_NOMATCH; } break; case DUMX: if (toupper((unsigned char)*p++) != 'X') return PO_NOMATCH; break; case DUMY: if (toupper((unsigned char)*p++) != 'Y') return PO_NOMATCH; break; } if (required==INDIR || required==INDX || required==INDY || required==DPINDIR || required==INDIRX) { p = skip(p); if (*p++ != ')') { cpu_error(2); /* missing closing parenthesis */ return PO_CORRUPT; } } p = skip(p); if (p-start < len) cpu_error(1); /* trailing garbage in operand */ op->type = required; return PO_MATCH; }
int parse_operand(char *p,int len,operand *op,int req) { int r; char *s,*start=p; op->type=-1; p=skip(p); if(req>=OP_REG&&req<=OP_LR){ int reg; s=skip_reg(p,&r); if(s!=p){ if((req==OP_REG&&r<=31)|| (req==OP_SREG&&r<=15)|| (req==OP_PC&&r==31)|| (req==OP_LR&&r==26)|| (req==OP_MREG&&(r==0||r==6||r==16||r==24))){ op->type=OP_REG; op->reg=r; }else return 0; }else return 0; p=skip(s); }else if(req==OP_MREG){ int first; s=skip_reg(p,&r); if(s==p) return 0; if(r==0) op->dreg=0; else if(r==6) op->dreg=1; else if(r==16) op->dreg=2; else if(r==24) op->dreg=3; else return 0; s=skip(s); if(*s!='-') return 0; p=skip(s+1); first=r; s=skip_reg(p,&r); if(s==p) return 0; p=skip(s); op->reg=(r-first)&31; }else if(req==OP_IND){ p=skip(p); if(*p!='(') return 0; p=skip(p+1); s=skip_reg(p,&r); if(s==p) return 0; if(r>15) return 0; p=skip(s); op->reg=r; if(*p!=')') return 0; p=skip(p+1); op->type=OP_IND; }else if(req==OP_VIND){ int nodisp=0; p=skip(p); if(*p=='('){ char *m; m=s=skip(p+1); s=skip_reg(s,&r); if((m!=s)||(*s=='0')) nodisp=1; } if(!nodisp){ op->offset=parse_expr(&p); if(!op->offset) return 0; }else op->offset=0; if(*p!='(') return 0; p=skip(p+1); s=skip_reg(p,&r); if (s==p){ /* Special case: Allow '0' instead of a register */ if (*p=='0'){ p=skip(p+1); op->reg=15; } else return 0; }else{ if(r>14) return 0; p=skip(s); op->reg=r; } op->dreg=15; if(*p=='+'&&p[1]=='='){ p=skip(p+2); s=skip_reg(p,&r); if(s==p) return 0; if(r>14) return 0; p=skip(s); op->dreg=r; } if(*p!=')') return 0; p=skip(p+1); op->type=OP_IND; }else if(req==OP_PREDEC){ p=skip(p); if(p[0]!='-'||p[1]!='-') return 0; p=skip(p+2); if(*p!='(') return 0; p=skip(p+1); s=skip_reg(p,&r); if(s==p) return 0; p=skip(s); if(*p!=')') return 0; p=skip(p+1); op->reg=r; op->type=OP_PREDEC; }else if(req==OP_POSTINC){ p=skip(p); if(*p!='(') return 0; p=skip(p+1); s=skip_reg(p,&r); if(s==p) return 0; p=skip(s); if(*p!=')') return 0; p=skip(p+1); if(*p++!='+') return 0; if(*p!='+') return 0; p=skip(p+1); op->reg=r; op->type=OP_POSTINC; }else if(req==OP_REGIND){ p=skip(p); if(*p!='(') return 0; p=skip(p+1); s=skip_reg(p,&op->reg); if(s==p) return 0; p=skip(s); if(*p!=',') return 0; p=skip(p+1); s=skip_reg(p,&op->dreg); if(s==p) return 0; p=skip(s); if(*p!=')') return 0; op->type=OP_REGIND; p=skip(p+1); }else if(req==OP_IMMIND|| req==OP_IMMINDSP|| req==OP_IMMINDSD|| req==OP_IMMINDR0|| req==OP_IMMINDS|| req==OP_IMMINDPC){ op->offset=0; if(*p=='('){ char *p2; p2=skip(p+1); s=skip_reg(p2,&r); if(s!=p2){ p=skip(s); if(*p==')'){ p=skip(p+1); op->offset=number_expr(0); } } } if(!op->offset){ op->offset=parse_expr(&p); if(!op->offset) return 0; p=skip(p); if(*p!='(') return 0; p=skip(p+1); s=skip_reg(p,&r); if(s==p) return 0; p=skip(s); if(*p!=')') return 0; } if(req==OP_IMMINDSP&&r!=25) return 0; if(req==OP_IMMINDPC&&r!=31) return 0; if(req==OP_IMMINDSD&&r!=24) return 0; if(req==OP_IMMINDR0&&r!=0) return 0; if(req==OP_IMMINDS&&r>15) return 0; op->reg=r; op->type=OP_IMMIND; p=skip(p+1); }else if(req>=OP_IMM4&&req<=OP_IMM32&&skip_reg(p,&r)==p){ op->type=OP_IMM32; op->offset=parse_expr(&p); simplify_expr(op->offset); p=skip(p); }else if((req==OP_ABS||req==OP_REL)&&skip_reg(p,&r)==p){ op->type=req; op->offset=parse_expr(&p); simplify_expr(op->offset); p=skip(p); }else if(req==OP_VREG||req==OP_VREGM||req==OP_VREGMM||req==OP_VREGA80){ s=skip_reg(p,&r); op->offset=0; if(s!=p){ if (*s=='+'||*s=='-'){ if(req==OP_VREG) return 0; op->offset=parse_expr(&s); } p=skip(s); op->type=OP_VREG; if(req==OP_VREG){ op->reg=(0xE<<6)|r; op->dreg=0x3c; }else{ op->reg=(0xE<<6); op->dreg=r<<2; } }else if(*p=='-'){ p=skip(p+1); op->type=OP_VREG; op->reg=(0xF<<6); op->dreg=0x3c; }else{ int size,vert,x,y,inc=15<<2; expr *e; if(*p=='H'||*p=='h') vert=0; // Disable vertical registers in 48-bit instructions for now else if((req!=OP_VREG)&&(*p=='V'||*p=='v')) vert=1; else return 0; p=skip(p+1); if(*p=='X'||*p=='x'){ size=2; p=skip(p+1); }else if(*p=='1'&&p[1]=='6'){ size=2; p=skip(p+2); }else if(*p=='Y'||*p=='y'){ size=4; p=skip(p+1); }else if(*p=='3'&&p[1]=='2'){ size=4; p=skip(p+2); }else if(*p=='8'){ size=1; p=skip(p+1); }else{ size=1; p=skip(p); } if(*p!='(') return 0; p=skip(p+1); e=parse_expr(&p); if(e->type!=NUM) return 0; y=e->c.val; p=skip(p); #if 1 if(req!=OP_VREG&&*p=='+'){ p++; if(*p!='+') return 0; if(vert) { cpu_error(4); return 0; } p=skip(p+1); inc|=2; } #endif if(*p!=',') return 0; p=skip(p+1); if((*p=='c')||(*p=='C')) { p++; if (*p!='b'&&*p!='B') return 0; p++; if (*p!='+') return 0; inc|=1; } e=parse_expr(&p); if(e->type!=NUM) return 0; x=e->c.val; p=skip(p); #if 1 if(req!=OP_VREG&&*p=='+'){ p++; if(*p!='+') return 0; if(!vert) { cpu_error(4); return 0; } p=skip(p+1); inc|=2; } #endif if(*p!=')') return 0; p=skip(p+1); if(req!=OP_VREG&&*p=='*'){ p=skip(p+1); inc|=1; } if(req!=OP_VREG&&*p=='+'){ p=skip(p+1); s=skip_reg(p,&r); if(s==p) return 0; p=skip(s); inc&=~0x3c; inc|=(r<<2); } if(req==OP_VREGA80){ if(size==1){ op->reg=(vert<<6)|((x&48)<<3); }else if(size==2){ if(x&16) return 0; op->reg=(vert<<6)|(8<<6)|((x&32)<<2); }else{ if(x&48) return 0; op->reg=(vert<<6)|(12<<6); } if(y<0||y>63) return 0; op->reg|=y&63; inc|=(x&15)<<6; }else{ if(vert){ if(size==1){ op->reg=(1<<6)|((x&48)<<3); }else if(size==2){ if(x&16) return 0; op->reg=(9<<6)|((x&32)<<2); }else{ if(x&48) return 0; op->reg=13<<6; } if(y<0||y>63||(y&15)) return 0; op->reg|=(y&48)|(x&15); } else{ if(size==1){ if(x&15) return 0; op->reg=(x&48)<<3; }else if(size==2){ if(x&31) return 0; op->reg=(8<<6)|((x&32)<<2); }else{ if(x) return 0; op->reg=12<<6; } if(y<0||y>63) return 0; op->reg|=y&63; } } op->dreg=inc; op->type=OP_VREG; } }else return 0; if(req==OP_VREGMM||req==OP_IMM32M||req==OP_VIND){ int vecmod=0,i; while(1){ if(!strncasecmp(p,"rep",3)&&isspace((unsigned char)p[3])){ expr *e; taddr x; p=skip(p+3); if((*p=='r')&&(p[1]=='0')&&!isdigit(p[2])){ p=skip(p+2); vecmod|=7<<16; i=0; }else{ e=parse_expr(&p); if(e->type!=NUM) return 0; x=e->c.val; for(i=0;i<8;i++){ if((1<<i)==x){ vecmod|=i<<16; break; } } } if(i>=8) return 0; p=skip(p); continue; } for(i=0;i<sizeof(vflags)/sizeof(vflags[0]);i++){ if((!strncasecmp(p,vflags[i],strlen(vflags[i])))&& (!isalpha(p[strlen(vflags[i])]))){ if(vecmod&0x380) return 0; vecmod|=i<<7; p=skip(p+strlen(vflags[i])); continue; } } for (i=0;i<sizeof(sru)/sizeof(sru[0]);i++){ if(!strncasecmp(p,sru[i],strlen(sru[i]))){ p=skip(p+strlen(sru[i])); p=skip_reg(p,&r); if((r<0)||(r>7)) return 0; vecmod|=(1<<6)|(i<<3)|r; continue; } } for (i=0;i<sizeof(accs)/sizeof(accs[0]);i++){ if((!strncasecmp(p,accs[i],strlen(accs[i])))&& (!isalpha(p[strlen(accs[i])]))){ if(vecmod&0x40) return 0; vecmod|=i; p=skip(p+strlen(accs[i])); continue; } } if(!strncasecmp(p,"ena",3)){ if(vecmod&0x40) return 0; p=skip(p+3); vecmod|=32; } if(!strncasecmp(p,"setf",4)){ p=skip(p+4); vecmod|=1<<20; continue; } break; } op->vecmod=vecmod; } if(p>=start+len) return 1; else return 0; }
/* 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 c,sz,enc,cc,ww,m=0; unsigned long code,code2,code3; rlist *relocs=0; if(p->qualifiers[0]){ int i; for(i=0;i<15;i++){ if(!strcmp(p->qualifiers[0],ccs[i])){ cc=i; break; } } if(i>15) ierror(0); }else cc=14; c=translate(p,sec,pc); enc=mnemonics[c].ext.encoding; db->size=sz=oplen(enc); db->data=mymalloc(db->size); code=mnemonics[c].ext.code; switch(enc){ case EN_ARITHR16: if(cc!=14) cpu_error(3); code=0x40000000|(code<<24); code|=(p->op[0]->reg<<16)|(p->op[1]->reg<<20); break; case EN_ARITHI16: if(cc!=14) cpu_error(3); code=0x60000000|((code>>1)<<25); code|=(p->op[0]->reg<<16)|(absval(p->op[1]->offset,sec,pc,5,0)<<20); break; case EN_FIX16: if(cc!=14) cpu_error(3); break; case EN_IBRANCH16: if(cc!=14) cpu_error(3); code|=(p->op[0]->reg<<16); break; case EN_RBRANCH16: code=0x18000000; code|=cc<<23; code|=((reloffset(p->op[0]->offset,sec,pc,&relocs,0,7,0xfe)>>1)&127)<<16; break; case EN_MREG16: if(cc!=14) cpu_error(3); code|=(p->op[0]->dreg<<21)|(p->op[0]->reg<<16); break; case EN_RBRANCH32: if(code==0x90000000) code|=cc<<24; else code|=((reloffset(p->op[0]->offset,sec,pc,&relocs,8,4,0xF000000)>>24)&0xf)<<24; code|=((reloffset(p->op[0]->offset,sec,pc,&relocs,16,16,0x1fffe)>>1)&0x7fffff); code|=((reloffset(p->op[0]->offset,sec,pc,&relocs,0,7,0xfe0000)>>17)&0x7f)<<16; break; case EN_FUNARY32: code=0xC0000040|(code<<21); code|=p->op[0]->reg<<16; code|=p->op[1]->reg<<11; code|=cc<<7; break; case EN_ARITHR32: code=0xC0000000|(code<<21); code|=p->op[0]->reg<<16; if(p->op[2]){ code|=p->op[1]->reg<<11; code|=p->op[2]->reg; }else{ code|=p->op[0]->reg<<11; code|=p->op[1]->reg; } code|=cc<<7; break; case EN_ARITHI32: code=0xC0000040|(code<<21); code|=p->op[0]->reg<<16; if(p->op[2]){ code|=p->op[1]->reg<<11; code|=absval(p->op[2]->offset,sec,pc,6,1); }else{ code|=p->op[0]->reg<<11; code|=absval(p->op[1]->offset,sec,pc,6,1); } code|=cc<<7; break; case EN_ADDCMPB64: // Large offset: invert CC, place a long branch after // so we really generate the following: // addcmpb<~cc> d, a, b, skip // b <destination> // skip: // <rest of code> m=1; if(cc&1) cc--; else cc++; case EN_ADDCMPB32: code=0x80000000|(code<<14); code|=cc<<24; code|=p->op[0]->reg<<16; if(code&0x4000){ code|=absval(p->op[1]->offset,sec,pc,4,1)<<20; }else{ code|=p->op[1]->reg<<20; } if(code&0x8000){ code|=absval(p->op[2]->offset,sec,pc,6,0)<<8; if(m) code|=4; else code|=((reloffset(p->op[3]->offset,sec,pc,&relocs,16,9,0x1ff)>>1)&0xff); }else{