Mercurial > repos > blastem
comparison gen_x86.c @ 14:2bdad0f52f42
x86 code gen, initial work on translator
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Tue, 27 Nov 2012 09:28:13 -0800 |
parents | |
children | c0f339564819 |
comparison
equal
deleted
inserted
replaced
13:168b1a873895 | 14:2bdad0f52f42 |
---|---|
1 #include "gen_x86.h" | |
2 #include "68kinst.h" | |
3 #include <stddef.h> | |
4 #include <stdio.h> | |
5 | |
6 #define REX_RM_FIELD 0x1 | |
7 #define REX_SIB_FIELD 0x2 | |
8 #define REX_REG_FIELD 0x4 | |
9 #define REX_QUAD 0x8 | |
10 | |
11 #define OP_ADD 0x00 | |
12 #define OP_OR 0x08 | |
13 #define PRE_2BYTE 0x0F | |
14 #define OP_ADC 0x10 | |
15 #define OP_SBB 0x18 | |
16 #define OP_AND 0x20 | |
17 #define OP_SUB 0x28 | |
18 #define OP_XOR 0x30 | |
19 #define OP_CMP 0x38 | |
20 #define PRE_REX 0x40 | |
21 #define OP_PUSH 0x50 | |
22 #define OP_POP 0x58 | |
23 #define PRE_SIZE 0x66 | |
24 #define OP_JCC 0x70 | |
25 #define OP_IMMED_ARITH 0x80 | |
26 #define OP_MOV 0x88 | |
27 #define OP_PUSHF 0x9C | |
28 #define OP_POPF 0x9D | |
29 #define OP_MOV_I8R 0xB0 | |
30 #define OP_MOV_IR 0xB8 | |
31 #define OP_RETN 0xC3 | |
32 #define OP_CALL 0xE8 | |
33 #define OP_CALL_EA 0xFF | |
34 | |
35 #define OP2_JCC 0x80 | |
36 #define OP2_SETCC 0x90 | |
37 | |
38 #define OP_EX_ADDI 0x0 | |
39 #define OP_EX_ORI 0x1 | |
40 #define OP_EX_ADCI 0x2 | |
41 #define OP_EX_SBBI 0x3 | |
42 #define OP_EX_ANDI 0x4 | |
43 #define OP_EX_SUBI 0x5 | |
44 #define OP_EX_XORI 0x6 | |
45 #define OP_EX_CMPI 0x7 | |
46 | |
47 #define BIT_IMMED_RAX 0x4 | |
48 #define BIT_DIR 0x2 | |
49 #define BIT_SIZE 0x1 | |
50 | |
51 #define M68K_N_REG RBX | |
52 #define M68K_V_REG BH | |
53 #define M68K_Z_REG RDX | |
54 #define M68K_C_REG DH | |
55 | |
56 #define M68K_SCRATCH RCX | |
57 | |
58 enum { | |
59 X86_RAX = 0, | |
60 X86_RCX, | |
61 X86_RDX, | |
62 X86_RBX, | |
63 X86_RSP, | |
64 X86_RBP, | |
65 X86_RSI, | |
66 X86_RDI, | |
67 X86_AH=4, | |
68 X86_CH, | |
69 X86_DH, | |
70 X86_BH, | |
71 X86_R8=0, | |
72 X86_R9, | |
73 X86_R10, | |
74 X86_R11, | |
75 X86_R12, | |
76 X86_R13, | |
77 X86_R14, | |
78 X86_R15 | |
79 } x86_regs_enc; | |
80 | |
81 enum { | |
82 MODE_REG_INDIRECT = 0, | |
83 MODE_REG_DISPLACE8 = 0x40, | |
84 MODE_REG_DIPSLACE32 = 0x80, | |
85 MODE_REG_DIRECT = 0xC0 | |
86 } x86_modes; | |
87 | |
88 uint8_t * x86_rr_sizedir(uint8_t * out, uint8_t opcode, uint8_t src, uint8_t dst, uint8_t size) | |
89 { | |
90 //TODO: Deal with the fact that AH, BH, CH and DH can only be in the R/M param when there's a REX prefix | |
91 uint8_t tmp; | |
92 if (size == SZ_W) { | |
93 *(out++) = PRE_SIZE; | |
94 } | |
95 if (size == SZ_B && dst >= RSP && dst <= RDI) { | |
96 opcode |= BIT_DIR; | |
97 tmp = dst; | |
98 dst = src; | |
99 src = dst; | |
100 } | |
101 if (size == SZ_Q || src >= R8 || dst >= R8 || (size == SZ_B && src >= RSP && src <= RDI)) { | |
102 *out = PRE_REX; | |
103 if (size == SZ_Q) { | |
104 *out |= REX_QUAD; | |
105 } | |
106 if (src >= R8) { | |
107 *out |= REX_REG_FIELD; | |
108 src -= (R8 - X86_R8); | |
109 } | |
110 if (dst >= R8) { | |
111 *out |= REX_RM_FIELD; | |
112 dst -= (R8 - X86_R8); | |
113 } | |
114 out++; | |
115 } | |
116 if (size == SZ_B) { | |
117 if (src >= AH && src <= BH) { | |
118 src -= (AH-X86_AH); | |
119 } | |
120 if (dst >= AH && dst <= BH) { | |
121 dst -= (AH-X86_AH); | |
122 } | |
123 } else { | |
124 opcode |= BIT_SIZE; | |
125 } | |
126 *(out++) = opcode; | |
127 *(out++) = MODE_REG_DIRECT | dst | (src << 3); | |
128 return out; | |
129 } | |
130 | |
131 uint8_t * x86_rrdisp8_sizedir(uint8_t * out, uint8_t opcode, uint8_t reg, uint8_t base, int8_t disp, uint8_t size, uint8_t dir) | |
132 { | |
133 //TODO: Deal with the fact that AH, BH, CH and DH can only be in the R/M param when there's a REX prefix | |
134 uint8_t tmp; | |
135 if (size == SZ_W) { | |
136 *(out++) = PRE_SIZE; | |
137 } | |
138 if (size == SZ_Q || reg >= R8 || base >= R8 || (size == SZ_B && reg >= RSP && reg <= RDI)) { | |
139 *out = PRE_REX; | |
140 if (size == SZ_Q) { | |
141 *out |= REX_QUAD; | |
142 } | |
143 if (reg >= R8) { | |
144 *out |= REX_REG_FIELD; | |
145 reg -= (R8 - X86_R8); | |
146 } | |
147 if (base >= R8) { | |
148 *out |= REX_RM_FIELD; | |
149 base -= (R8 - X86_R8); | |
150 } | |
151 out++; | |
152 } | |
153 if (size == SZ_B) { | |
154 if (reg >= AH && reg <= BH) { | |
155 reg -= (AH-X86_AH); | |
156 } | |
157 } else { | |
158 opcode |= BIT_SIZE; | |
159 } | |
160 *(out++) = opcode | dir; | |
161 *(out++) = MODE_REG_DISPLACE8 | base | (reg << 3); | |
162 *(out++) = disp; | |
163 return out; | |
164 } | |
165 | |
166 uint8_t * x86_rrind_sizedir(uint8_t * out, uint8_t opcode, uint8_t reg, uint8_t base, uint8_t size, uint8_t dir) | |
167 { | |
168 //TODO: Deal with the fact that AH, BH, CH and DH can only be in the R/M param when there's a REX prefix | |
169 uint8_t tmp; | |
170 if (size == SZ_W) { | |
171 *(out++) = PRE_SIZE; | |
172 } | |
173 if (size == SZ_Q || reg >= R8 || base >= R8 || (size == SZ_B && reg >= RSP && reg <= RDI)) { | |
174 *out = PRE_REX; | |
175 if (size == SZ_Q) { | |
176 *out |= REX_QUAD; | |
177 } | |
178 if (reg >= R8) { | |
179 *out |= REX_REG_FIELD; | |
180 reg -= (R8 - X86_R8); | |
181 } | |
182 if (base >= R8) { | |
183 *out |= REX_RM_FIELD; | |
184 base -= (R8 - X86_R8); | |
185 } | |
186 out++; | |
187 } | |
188 if (size == SZ_B) { | |
189 if (reg >= AH && reg <= BH) { | |
190 reg -= (AH-X86_AH); | |
191 } | |
192 } else { | |
193 opcode |= BIT_SIZE; | |
194 } | |
195 *(out++) = opcode | dir; | |
196 *(out++) = MODE_REG_INDIRECT | base | (reg << 3); | |
197 return out; | |
198 } | |
199 | |
200 uint8_t * x86_i8r(uint8_t * out, uint8_t opcode, uint8_t op_ex, uint8_t al_opcode, uint8_t val, uint8_t dst) | |
201 { | |
202 if (dst == RAX) { | |
203 *(out++) = al_opcode | BIT_IMMED_RAX; | |
204 } else { | |
205 if (dst >= AH && dst <= BH) { | |
206 dst -= (AH-X86_AH); | |
207 } else if(dst >= R8) { | |
208 *(out++) = PRE_REX | REX_RM_FIELD; | |
209 } | |
210 *(out++) = opcode; | |
211 *(out++) = MODE_REG_DIRECT | dst | (op_ex << 3); | |
212 } | |
213 *(out++) = val; | |
214 return out; | |
215 } | |
216 | |
217 uint8_t * x86_i32r(uint8_t * out, uint8_t opcode, uint8_t op_ex, uint8_t al_opcode, int32_t val, uint8_t dst) | |
218 { | |
219 uint8_t sign_extend = 0; | |
220 if (val <= 0x7F && val >= -0x80) { | |
221 sign_extend = 1; | |
222 opcode |= BIT_DIR; | |
223 } | |
224 if (dst == RAX && !sign_extend) { | |
225 *(out++) = al_opcode | BIT_IMMED_RAX | BIT_SIZE; | |
226 } else { | |
227 if(dst >= R8) { | |
228 *(out++) = PRE_REX | REX_RM_FIELD; | |
229 } | |
230 *(out++) = opcode | BIT_SIZE; | |
231 *(out++) = MODE_REG_DIRECT | dst | (op_ex << 3); | |
232 } | |
233 *(out++) = val; | |
234 if (!sign_extend) { | |
235 val >>= 8; | |
236 *(out++) = val; | |
237 val >>= 8; | |
238 *(out++) = val; | |
239 val >>= 8; | |
240 *(out++) = val; | |
241 } | |
242 return out; | |
243 } | |
244 | |
245 uint8_t * add_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size) | |
246 { | |
247 return x86_rr_sizedir(out, OP_ADD, src, dst, size); | |
248 } | |
249 | |
250 uint8_t * add_i8r(uint8_t * out, uint8_t val, uint8_t dst) | |
251 { | |
252 return x86_i8r(out, OP_IMMED_ARITH, OP_EX_ADDI, OP_ADD, val, dst); | |
253 } | |
254 | |
255 uint8_t * add_i32r(uint8_t * out, int32_t val, uint8_t dst) | |
256 { | |
257 return x86_i32r(out, OP_IMMED_ARITH, OP_EX_ADDI, OP_ADD, val, dst); | |
258 } | |
259 | |
260 uint8_t * or_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size) | |
261 { | |
262 return x86_rr_sizedir(out, OP_OR, src, dst, size); | |
263 } | |
264 | |
265 uint8_t * or_i8r(uint8_t * out, uint8_t val, uint8_t dst) | |
266 { | |
267 return x86_i8r(out, OP_IMMED_ARITH, OP_EX_ORI, OP_OR, val, dst); | |
268 } | |
269 | |
270 uint8_t * or_i32r(uint8_t * out, int32_t val, uint8_t dst) | |
271 { | |
272 return x86_i32r(out, OP_IMMED_ARITH, OP_EX_ORI, OP_OR, val, dst); | |
273 } | |
274 | |
275 uint8_t * and_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size) | |
276 { | |
277 return x86_rr_sizedir(out, OP_AND, src, dst, size); | |
278 } | |
279 | |
280 uint8_t * and_i8r(uint8_t * out, uint8_t val, uint8_t dst) | |
281 { | |
282 return x86_i8r(out, OP_IMMED_ARITH, OP_EX_ANDI, OP_AND, val, dst); | |
283 } | |
284 | |
285 uint8_t * and_i32r(uint8_t * out, int32_t val, uint8_t dst) | |
286 { | |
287 return x86_i32r(out, OP_IMMED_ARITH, OP_EX_ANDI, OP_AND, val, dst); | |
288 } | |
289 | |
290 uint8_t * xor_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size) | |
291 { | |
292 return x86_rr_sizedir(out, OP_XOR, src, dst, size); | |
293 } | |
294 | |
295 uint8_t * xor_i8r(uint8_t * out, uint8_t val, uint8_t dst) | |
296 { | |
297 return x86_i8r(out, OP_IMMED_ARITH, OP_EX_XORI, OP_XOR, val, dst); | |
298 } | |
299 | |
300 uint8_t * xor_i32r(uint8_t * out, int32_t val, uint8_t dst) | |
301 { | |
302 return x86_i32r(out, OP_IMMED_ARITH, OP_EX_XORI, OP_XOR, val, dst); | |
303 } | |
304 | |
305 uint8_t * sub_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size) | |
306 { | |
307 return x86_rr_sizedir(out, OP_SUB, src, dst, size); | |
308 } | |
309 | |
310 uint8_t * sub_i8r(uint8_t * out, uint8_t val, uint8_t dst) | |
311 { | |
312 return x86_i8r(out, OP_IMMED_ARITH, OP_EX_SUBI, OP_SUB, val, dst); | |
313 } | |
314 | |
315 uint8_t * sub_i32r(uint8_t * out, int32_t val, uint8_t dst) | |
316 { | |
317 return x86_i32r(out, OP_IMMED_ARITH, OP_EX_SUBI, OP_SUB, val, dst); | |
318 } | |
319 | |
320 | |
321 uint8_t * cmp_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size) | |
322 { | |
323 return x86_rr_sizedir(out, OP_CMP, src, dst, size); | |
324 } | |
325 | |
326 uint8_t * cmp_i8r(uint8_t * out, uint8_t val, uint8_t dst) | |
327 { | |
328 return x86_i8r(out, OP_IMMED_ARITH, OP_EX_CMPI, OP_CMP, val, dst); | |
329 } | |
330 | |
331 uint8_t * cmp_i32r(uint8_t * out, int32_t val, uint8_t dst) | |
332 { | |
333 return x86_i32r(out, OP_IMMED_ARITH, OP_EX_CMPI, OP_CMP, val, dst); | |
334 } | |
335 | |
336 uint8_t * mov_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size) | |
337 { | |
338 return x86_rr_sizedir(out, OP_MOV, src, dst, size); | |
339 } | |
340 | |
341 uint8_t * mov_rrdisp8(uint8_t * out, uint8_t src, uint8_t dst_base, int8_t disp, uint8_t size) | |
342 { | |
343 return x86_rrdisp8_sizedir(out, OP_MOV, src, dst_base, disp, size, 0); | |
344 } | |
345 | |
346 uint8_t * mov_rdisp8r(uint8_t * out, uint8_t src_base, int8_t disp, uint8_t dst, uint8_t size) | |
347 { | |
348 return x86_rrdisp8_sizedir(out, OP_MOV, dst, src_base, disp, size, BIT_DIR); | |
349 } | |
350 | |
351 uint8_t * mov_rrind(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size) | |
352 { | |
353 return x86_rrind_sizedir(out, OP_MOV, src, dst, size, 0); | |
354 } | |
355 | |
356 uint8_t * mov_rindr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size) | |
357 { | |
358 return x86_rrind_sizedir(out, OP_MOV, dst, src, size, BIT_DIR); | |
359 } | |
360 | |
361 uint8_t * mov_i8r(uint8_t * out, uint8_t val, uint8_t dst) | |
362 { | |
363 if (dst >= AH && dst <= BH) { | |
364 dst -= AH - X86_AH; | |
365 } else if (dst >= RSP && dst <= RDI) { | |
366 *(out++) = PRE_REX; | |
367 } else if (dst >= R8) { | |
368 *(out++) = PRE_REX | REX_RM_FIELD; | |
369 dst -= R8 - X86_R8; | |
370 } | |
371 *(out++) = OP_MOV_I8R | dst; | |
372 *(out++) = val; | |
373 return out; | |
374 } | |
375 | |
376 uint8_t * mov_i16r(uint8_t * out, uint16_t val, uint8_t dst) | |
377 { | |
378 *(out++) = PRE_SIZE; | |
379 if (dst >= R8) { | |
380 *(out++) = PRE_REX | REX_RM_FIELD; | |
381 dst -= R8 - X86_R8; | |
382 } | |
383 *(out++) = OP_MOV_IR | dst; | |
384 *(out++) = val; | |
385 val >>= 8; | |
386 *(out++) = val; | |
387 return out; | |
388 } | |
389 | |
390 uint8_t * mov_i32r(uint8_t * out, uint32_t val, uint8_t dst) | |
391 { | |
392 if (dst >= R8) { | |
393 *(out++) = PRE_REX | REX_RM_FIELD; | |
394 dst -= R8 - X86_R8; | |
395 } | |
396 *(out++) = OP_MOV_IR | dst; | |
397 *(out++) = val; | |
398 val >>= 8; | |
399 *(out++) = val; | |
400 val >>= 8; | |
401 *(out++) = val; | |
402 val >>= 8; | |
403 *(out++) = val; | |
404 return out; | |
405 } | |
406 | |
407 uint8_t * pushf(uint8_t * out) | |
408 { | |
409 *(out++) = OP_PUSHF; | |
410 return out; | |
411 } | |
412 | |
413 uint8_t * popf(uint8_t * out) | |
414 { | |
415 *(out++) = OP_POPF; | |
416 return out; | |
417 } | |
418 | |
419 uint8_t * push_r(uint8_t * out, uint8_t reg) | |
420 { | |
421 if (reg >= R8) { | |
422 *(out++) = PRE_REX | REX_RM_FIELD; | |
423 reg -= R8 - X86_R8; | |
424 } | |
425 *(out++) = OP_PUSH | reg; | |
426 return out; | |
427 } | |
428 | |
429 uint8_t * pop_r(uint8_t * out, uint8_t reg) | |
430 { | |
431 if (reg >= R8) { | |
432 *(out++) = PRE_REX | REX_RM_FIELD; | |
433 reg -= R8 - X86_R8; | |
434 } | |
435 *(out++) = OP_POP | reg; | |
436 return out; | |
437 } | |
438 | |
439 uint8_t * setcc_r(uint8_t * out, uint8_t cc, uint8_t dst) | |
440 { | |
441 if (dst >= R8) { | |
442 *(out++) = PRE_REX | REX_RM_FIELD; | |
443 dst -= R8 - X86_R8; | |
444 } else if (dst >= RSP && dst <= RDI) { | |
445 *(out++) = PRE_REX; | |
446 } else if (dst >= AH && dst <= BH) { | |
447 dst -= AH - X86_AH; | |
448 } | |
449 *(out++) = PRE_2BYTE; | |
450 *(out++) = OP2_SETCC | cc; | |
451 *(out++) = MODE_REG_DIRECT | dst; | |
452 return out; | |
453 } | |
454 | |
455 uint8_t * setcc_rind(uint8_t * out, uint8_t cc, uint8_t dst) | |
456 { | |
457 if (dst >= R8) { | |
458 *(out++) = PRE_REX | REX_RM_FIELD; | |
459 dst -= R8 - X86_R8; | |
460 } | |
461 *(out++) = PRE_2BYTE; | |
462 *(out++) = OP2_SETCC | cc; | |
463 *(out++) = MODE_REG_INDIRECT | dst; | |
464 return out; | |
465 } | |
466 | |
467 uint8_t * jcc(uint8_t * out, uint8_t cc, int32_t disp) | |
468 { | |
469 if (disp <= 0x7F && disp >= -0x80) { | |
470 *(out++) = OP_JCC | cc; | |
471 *(out++) = disp; | |
472 } else { | |
473 *(out++) = PRE_2BYTE; | |
474 *(out++) = OP2_JCC | cc; | |
475 *(out++) = disp; | |
476 disp >>= 8; | |
477 *(out++) = disp; | |
478 disp >>= 8; | |
479 *(out++) = disp; | |
480 disp >>= 8; | |
481 *(out++) = disp; | |
482 } | |
483 return out; | |
484 } | |
485 | |
486 uint8_t * call(uint8_t * out, uint8_t * fun) | |
487 { | |
488 ptrdiff_t disp = fun-(out+5); | |
489 if (disp <= 0x7FFFFFFF && disp >= -2147483648) { | |
490 *(out++) = OP_CALL; | |
491 *(out++) = disp; | |
492 disp >>= 8; | |
493 *(out++) = disp; | |
494 disp >>= 8; | |
495 *(out++) = disp; | |
496 disp >>= 8; | |
497 *(out++) = disp; | |
498 } else { | |
499 //TODO: Implement far call | |
500 printf("%p - %p = %ld, %d, %d, %d\n", fun, out + 5, disp, disp <= 0x7FFFFFFF, disp >= (-2147483648), -2147483648); | |
501 return NULL; | |
502 } | |
503 return out; | |
504 } | |
505 | |
506 uint8_t * retn(uint8_t * out) | |
507 { | |
508 *(out++) = OP_RETN; | |
509 return out; | |
510 } | |
511 | |
512 |