Skip to content

Commit

Permalink
Add two forms of movq and some jumps and arithmetic ops
Browse files Browse the repository at this point in the history
  • Loading branch information
tbodt committed May 26, 2017
1 parent 18602df commit c6e628d
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 40 deletions.
84 changes: 61 additions & 23 deletions emu/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ int cpu_step(struct cpu_state *cpu) {
#define REGPTR_(regptr,size) REG_(CONCAT3(regptr.reg,size,_id),size)
#define REGPTR(regptr) REGPTR_(regptr, OP_SIZE)
#define REGPTR8(regptr) REGPTR_(regptr, 8)
#define REGPTR64(regptr) REGPTR_(regptr, 64)

#define CHECK_W(addr) CHECK_WRITE(cpu, addr)

Expand All @@ -66,8 +67,10 @@ int cpu_step(struct cpu_state *cpu) {
#define modrm_val modrm_val_(OP_SIZE)
#define modrm_val8 modrm_val_(8)
#define modrm_val16 modrm_val_(16)
#define modrm_val64 modrm_val_(64)
#define modrm_reg REGPTR(modrm.reg)
#define modrm_reg8 REGPTR8(modrm.reg)
#define modrm_reg64 REGPTR64(modrm.reg)

#undef imm
byte_t imm8;
Expand All @@ -82,25 +85,29 @@ int cpu_step(struct cpu_state *cpu) {
#define READIMM8 READIMM_(imm8, 8)
#define READADDR READIMM_(addr, 32)
#define READADDR_W READADDR; CHECK_W(addr)
byte_t insn;
#define READINSN \
insn = MEM8(cpu->eip); \
cpu->eip++; \
TRACE("%02x ", insn);

TRACE("%08x\t", cpu->eip);
byte_t insn = MEM8(cpu->eip);
cpu->eip++;
TRACE("%02x ", insn);
READINSN;
switch (insn) {
// if any instruction handlers declare variables, they should create a
// new block for those variables.
// any subtraction that occurs probably needs to have a cast to a
// signed type, so sign extension happens.

case 0x01: TRACEI("add reg, modrm");
READMODRM_W; ADD(modrm_reg, modrm_val); break;

case 0x0d: TRACEI("or imm, eax\t");
READIMM; OR(imm, ax); break;

case 0x0f:
// 2-byte opcode prefix
insn = MEM8(cpu->eip);
cpu->eip++;
TRACE("%02x ", insn);
READINSN;
switch (insn) {
case 0xa2:
TRACEI("cpuid");
Expand All @@ -122,18 +129,29 @@ int cpu_step(struct cpu_state *cpu) {
case 0xb7: TRACEI("movz modrm16, reg");
READMODRM; MOV(modrm_val16, modrm_reg); break;

case 0xd6:
// someone tell intel to get sane
if (OP_SIZE == 16) {
TRACEI("movq xmm, modrm");
READMODRM_W; MOV(modrm_reg64, modrm_reg64);
}
break;

default:
TRACEI("undefined");
return INT_UNDEFINED;
}
break;

case 0x31:
TRACEI("xor reg, modrm");
READMODRM_W; XOR(modrm_reg, modrm_val); break;
case 0x33:
TRACEI("xor modrm, reg");
READMODRM; XOR(modrm_val, modrm_reg); break;
case 0x31: TRACEI("xor reg, modrm");
READMODRM_W; XOR(modrm_reg, modrm_val); break;
case 0x33: TRACEI("xor modrm, reg");
READMODRM; XOR(modrm_val, modrm_reg); break;

case 0x39: TRACEI("cmp reg, modrm");
READMODRM; CMP(modrm_reg, modrm_val); break;
case 0x3d: TRACEI("cmp imm, eax");
READIMM; CMP(imm, ax); break;

case 0x50: TRACEI("push eax");
PUSH(ax); break;
Expand Down Expand Up @@ -189,22 +207,28 @@ int cpu_step(struct cpu_state *cpu) {

case 0x68: TRACEI("push imm\t");
READIMM; PUSH(imm); break;
case 0x6a: TRACEI("push imm8\t");
READIMM8; PUSH(imm8); break;

case 0x73: TRACEI("jnb rel8\t");
READIMM8; J_REL(!B, (int8_t) imm8); break;
case 0x74: TRACEI("je rel8\t");
READIMM8; J_REL(E, (int8_t) imm8); break;
case 0x75: TRACEI("jne rel8\t");
READIMM8; J_REL(!E, (int8_t) imm8); break;
case 0x76: TRACEI("jbe rel8\t");
READIMM8; J_REL(BE, (int8_t) imm8); break;
case 0x77: TRACEI("ja rel8\t");
READIMM8; J_REL(!BE, (int8_t) imm8); break;
case 0x7e: TRACEI("jle rel8\t");
READIMM8; J_REL(LE, (int8_t) imm8); break;

case 0x80: TRACEI("grp1 imm8, modrm8");
READMODRM_W; READIMM8; GRP1(imm8, modrm_val8); break;
READMODRM; READIMM8; GRP1(imm8, modrm_val8); break;
case 0x81: TRACEI("grp1 imm, modrm");
READMODRM_W; READIMM; GRP1(imm, modrm_val); break;
READMODRM; READIMM; GRP1(imm, modrm_val); break;
case 0x83: TRACEI("grp1 imm8, modrm");
READMODRM_W; READIMM8; GRP1((uint32_t) (int8_t) imm8, modrm_val); break;
READMODRM; READIMM8; GRP1((uint32_t) (int8_t) imm8, modrm_val); break;

case 0x84: TRACEI("test reg8, modrm8");
READMODRM; TEST(modrm_reg8, modrm_val8); break;
Expand Down Expand Up @@ -243,6 +267,12 @@ int cpu_step(struct cpu_state *cpu) {
READIMM; MOV(imm, bx); break;
case 0xbc: TRACEI("mov imm, esp\t");
READIMM; MOV(imm, sp); break;
case 0xbd: TRACEI("mov imm, ebp\t");
READIMM; MOV(imm, bp); break;
case 0xbe: TRACEI("mov imm, esi\t");
READIMM; MOV(imm, si); break;
case 0xbf: TRACEI("mov imm, edi\t");
READIMM; MOV(imm, di); break;

case 0xc1:
TRACEI("shift imm8, modrm");
Expand All @@ -253,7 +283,7 @@ int cpu_step(struct cpu_state *cpu) {
case 0xc3: TRACEI("ret near");
RET_NEAR(); break;

case 0xcd: TRACEI("int imm8");
case 0xcd: TRACEI("int imm8\t");
READIMM8; INT(imm8); break;

case 0xc6: TRACEI("mov imm8, modrm8");
Expand All @@ -263,19 +293,28 @@ int cpu_step(struct cpu_state *cpu) {

case 0xe8:
TRACEI("call near\t");
READIMM;
PUSH(cpu->eip);
JMP_REL(imm);
READIMM; CALL_REL(imm);
break;

case 0xe9: TRACEI("jmp rel\t");
READIMM; JMP_REL(imm); break;

case 0xf3:
insn = MEM8(cpu->eip);
cpu->eip++;
TRACE("%02x ", insn);
READINSN;
switch (insn) {
case 0x0f:
// 2-byte opcode prefix
// after a rep prefix, means we have sse/mmx insanity
READINSN;
switch (insn) {
case 0x7e: TRACEI("movq modrm, xmm");
READMODRM; MOV(modrm_val64, modrm_reg64);
}
break;
// repz ret is equivalent to ret but on some amd chips there's
// a branch prediction penalty if the target of a branch is a
// ret. gcc used to use nop ret but repz ret is only one
// instruction
case 0xc3: TRACEI("repz ret\t"); RET_NEAR(); break;
default: TRACE("undefined\n"); return INT_UNDEFINED;
}
Expand All @@ -291,7 +330,6 @@ int cpu_step(struct cpu_state *cpu) {

default:
TRACE("undefined\n");
debugger;
return INT_UNDEFINED;
}
TRACE("\n");
Expand Down
9 changes: 9 additions & 0 deletions emu/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ void cpu_run(struct cpu_state *cpu);
int cpu_step32(struct cpu_state *cpu);
int cpu_step16(struct cpu_state *cpu);

union xmm_reg {
struct {
qword_t qhigh, qlow;
};
// TODO more forms
};

struct cpu_state {
pagetable pt;

Expand Down Expand Up @@ -42,6 +49,8 @@ struct cpu_state {
_REG(bp);
_REG(sp);

union xmm_reg xmm[8];

dword_t eip;

// flags
Expand Down
16 changes: 10 additions & 6 deletions emu/instructions.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,19 @@
#define INC(val) ADD(1, val)
#define DEC(val) SUB(1, val)

#define CALL(loc) PUSH(cpu->eip); JMP(loc)
#define CALL_REL(offset) PUSH(cpu->eip); JMP_REL(offset)

#define GRP1(src, dst) \
switch (modrm.opcode) { \
case 0b000: TRACE("add"); \
ADD(src, dst); break; \
MODRM_CHECK_W; ADD(src, dst); break; \
case 0b001: TRACE("or"); \
OR(src, dst); break; \
MODRM_CHECK_W; OR(src, dst); break; \
case 0b100: TRACE("and"); \
AND(src, dst); break; \
MODRM_CHECK_W; AND(src, dst); break; \
case 0b101: TRACE("sub"); \
SUB(src, dst); break; \
MODRM_CHECK_W; SUB(src, dst); break; \
case 0b111: TRACE("cmp"); \
CMP(src, dst); break; \
default: \
Expand Down Expand Up @@ -101,8 +104,9 @@
MODRM_CHECK_W; INC(val); break; \
case 1: TRACE("dec"); \
MODRM_CHECK_W; DEC(val); break; \
case 2: TRACE("call modrm near"); return INT_UNDEFINED; \
case 3: TRACE("call modrm far"); return INT_UNDEFINED; \
case 2: TRACE("call indirect near"); \
CALL(modrm_val); break; \
case 3: TRACE("call indirect far"); return INT_UNDEFINED; \
case 4: TRACE("jmp indirect near"); \
JMP(modrm_val); break; \
case 5: TRACE("jmp indirect far"); return INT_UNDEFINED; \
Expand Down
22 changes: 12 additions & 10 deletions emu/modrm.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@
#define REG(byte) ((byte & 0b00111000) >> 3)
#define RM(byte) ((byte & 0b00000111) >> 0)

#define MAKE_REGPTR(r32, r16, r8) ((struct regptr) { \
#define MAKE_REGPTR(r32, r16, r8, xmm) ((struct regptr) { \
.reg32_id = REG_ID(r32), \
.reg16_id = REG_ID(r16), \
.reg8_id = REG_ID(r8) \
.reg8_id = REG_ID(r8), \
.reg64_id = REG_ID(xmm.qlow), \
.reg64_high_id = REG_ID(xmm.qhigh) \
})

static inline struct regptr decode_reg(byte_t reg) {
switch (reg) {
case 0b000: return MAKE_REGPTR(eax,ax,al);
case 0b001: return MAKE_REGPTR(ecx,cx,cl);
case 0b010: return MAKE_REGPTR(edx,dx,dl);
case 0b011: return MAKE_REGPTR(ebx,bx,bl);
case 0b100: return MAKE_REGPTR(esp,sp,ah);
case 0b101: return MAKE_REGPTR(ebp,bp,ch);
case 0b110: return MAKE_REGPTR(esi,si,dh);
case 0b111: return MAKE_REGPTR(edi,di,bh);
case 0b000: return MAKE_REGPTR(eax,ax,al,xmm[0]);
case 0b001: return MAKE_REGPTR(ecx,cx,cl,xmm[1]);
case 0b010: return MAKE_REGPTR(edx,dx,dl,xmm[2]);
case 0b011: return MAKE_REGPTR(ebx,bx,bl,xmm[3]);
case 0b100: return MAKE_REGPTR(esp,sp,ah,xmm[4]);
case 0b101: return MAKE_REGPTR(ebp,bp,ch,xmm[5]);
case 0b110: return MAKE_REGPTR(esi,si,dh,xmm[6]);
case 0b111: return MAKE_REGPTR(edi,di,bh,xmm[7]);
}
fprintf(stderr, "fuck\n"); abort();
}
Expand Down
4 changes: 3 additions & 1 deletion emu/modrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ struct regptr {
reg_id_t reg8_id;
reg_id_t reg16_id;
reg_id_t reg32_id;
reg_id_t reg64_id;
reg_id_t reg64_high_id;
};
static const char *regptr_name(struct regptr regptr) {
static char buf[15];
sprintf(buf, "%s/%s/%s",
sprintf(buf, "%s/%s/%s",
reg8_name(regptr.reg8_id),
reg16_name(regptr.reg16_id),
reg32_name(regptr.reg32_id));
Expand Down
1 change: 1 addition & 0 deletions misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

// types
// word_t will be 64-bit to read 64-bit elves
typedef uint64_t qword_t;
typedef uint32_t dword_t;
typedef uint16_t word_t;
typedef uint8_t byte_t;
Expand Down

0 comments on commit c6e628d

Please sign in to comment.