forked from ish-app/ish
-
Notifications
You must be signed in to change notification settings - Fork 12
/
modrm.h
149 lines (140 loc) · 4.33 KB
/
modrm.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include "misc.h"
#include "emu/cpu.h"
struct regptr {
// offsets into the cpu_state structure
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",
reg8_name(regptr.reg8_id),
reg16_name(regptr.reg16_id),
reg32_name(regptr.reg32_id));
return buf;
}
struct modrm_info {
// MOD/RM BITS
bool sib;
enum {
mod_disp0,
mod_disp8,
mod_disp32,
mod_reg,
} type;
struct regptr modrm_regid;
// REG BITS
// offsets of the register into the cpu_state structure
struct regptr reg;
// for when it's not a register
uint8_t opcode;
};
#ifdef DISABLE_MODRM_TABLE
#define modrm_get_info modrm_compute_info
struct modrm_info modrm_compute_info(byte_t byte);
#else
struct modrm_info modrm_table[0x100];
static inline struct modrm_info modrm_get_info(byte_t byte) {
struct modrm_info info = modrm_table[byte];
TRACE("reg %s opcode %d ", regptr_name(info.reg), info.opcode);
switch (info.type) {
case mod_reg:
TRACE("mod_reg ");
break;
case mod_disp0:
TRACE("mod_disp0 ");
break;
case mod_disp8:
TRACE("mod_disp8 ");
break;
case mod_disp32:
TRACE("mod_disp32");
break;
}
if (info.sib) {
TRACE("with sib ");
} else {
TRACE("%s ", regptr_name(info.modrm_regid));
}
return info;
}
#endif
#define MOD(byte) ((byte & 0b11000000) >> 6)
#define REG(byte) ((byte & 0b00111000) >> 3)
#define RM(byte) ((byte & 0b00000111) >> 0)
// Decodes ModR/M and SIB byte pointed to by cpu->eip, increments cpu->eip past
// them, and returns everything in out parameters.
// TODO currently only does 32-bit
static inline void modrm_decode32(struct cpu_state *cpu, addr_t *addr_out, struct modrm_info *info_out) {
byte_t modrm = MEM_GET(cpu, cpu->eip, 8);
struct modrm_info info = modrm_get_info(modrm);
cpu->eip++;
*info_out = info;
if (info.type == mod_reg) return;
*addr_out = 0;
if (!info.sib) {
if (info.modrm_regid.reg32_id != 0) {
*addr_out += REG_VAL(cpu, info.modrm_regid.reg32_id, 32);
}
} else {
// sib is simple enough to not use a table for
byte_t sib = MEM_GET(cpu, cpu->eip, 8);
TRACE("sib %x ", sib);
cpu->eip++;
dword_t reg = 0;
switch (REG(sib)) {
case 0b000: reg += cpu->eax; break;
case 0b001: reg += cpu->ecx; break;
case 0b010: reg += cpu->edx; break;
case 0b011: reg += cpu->ebx; break;
case 0b101: reg += cpu->ebp; break;
case 0b110: reg += cpu->esi; break;
case 0b111: reg += cpu->edi; break;
}
switch (MOD(sib)) {
case 0b01: reg *= 2; break;
case 0b10: reg *= 4; break;
case 0b11: reg *= 8; break;
}
switch (RM(sib)) {
case 0b000: reg += cpu->eax; break;
case 0b001: reg += cpu->ecx; break;
case 0b010: reg += cpu->edx; break;
case 0b011: reg += cpu->ebx; break;
case 0b100: reg += cpu->esp; break;
case 0b101:
// i know this is weird but this is what intel says
if (info.type == mod_disp0) {
info.type = mod_disp32;
} else {
reg += cpu->ebp;
}
break;
case 0b110: reg += cpu->esi; break;
case 0b111: reg += cpu->edi; break;
}
*addr_out += reg;
}
int disp;
switch (info.type) {
case mod_disp8: {
disp = (int8_t) MEM_GET(cpu, cpu->eip, 8);
TRACE("disp %s0x%x ", (disp < 0 ? "-" : ""), (disp < 0 ? -disp : disp));
*addr_out += disp;
cpu->eip++;
break;
}
case mod_disp32: {
disp = (int32_t) MEM_GET(cpu, cpu->eip, 32);
TRACE("disp %s0x%x ", (disp < 0 ? "-" : ""), (disp < 0 ? -disp : disp));
*addr_out += disp;
cpu->eip += 4;
break;
}
// shut up compiler I don't want to handle other cases
default:;
}
}