Mercurial > repos > blastem
comparison m68k_to_x86.c @ 18:3e7bfde7606e
M68K to x86 translation works for a limited subset of instructions and addressing modes
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Tue, 04 Dec 2012 19:13:12 -0800 |
parents | 2bdad0f52f42 |
children | 4717146a7606 |
comparison
equal
deleted
inserted
replaced
17:de0085d4ea40 | 18:3e7bfde7606e |
---|---|
1 #include "gen_x86.h" | 1 #include "gen_x86.h" |
2 #include "m68k_to_x86.h" | 2 #include "m68k_to_x86.h" |
3 | 3 #include <stdio.h> |
4 #include <stddef.h> | |
5 #include <stdlib.h> | |
6 #include <string.h> | |
4 | 7 |
5 #define BUS 4 | 8 #define BUS 4 |
9 #define PREDEC_PENALTY 2 | |
6 #define CYCLES RAX | 10 #define CYCLES RAX |
7 #define LIMIT RBP | 11 #define LIMIT RBP |
8 #define SCRATCH RCX | 12 #define SCRATCH1 RCX |
13 #define SCRATCH2 RDI | |
9 #define CONTEXT RSI | 14 #define CONTEXT RSI |
10 | 15 |
11 #define FLAG_N RBX | 16 #define FLAG_N RBX |
12 #define FLAG_V BH | 17 #define FLAG_V BH |
13 #define FLAG_Z RDX | 18 #define FLAG_Z RDX |
20 uint8_t index; | 25 uint8_t index; |
21 uint8_t cycles; | 26 uint8_t cycles; |
22 } x86_ea; | 27 } x86_ea; |
23 | 28 |
24 void handle_cycle_limit(); | 29 void handle_cycle_limit(); |
30 void m68k_read_word_scratch1(); | |
31 void m68k_read_long_scratch1(); | |
32 void m68k_read_byte_scratch1(); | |
33 void m68k_write_word(); | |
34 void m68k_write_long_lowfirst(); | |
35 void m68k_write_long_highfirst(); | |
36 void m68k_write_byte(); | |
37 void m68k_save_context(); | |
38 void m68k_modified_ret_addr(); | |
39 void m68k_start_context(uint8_t * addr, m68k_context * context); | |
25 | 40 |
26 uint8_t * cycles(uint8_t * dst, uint32_t num) | 41 uint8_t * cycles(uint8_t * dst, uint32_t num) |
27 { | 42 { |
28 dst = add_i32r(dst, num, CYCLES); | 43 dst = add_ir(dst, num, CYCLES, SZ_D); |
29 } | 44 } |
30 | 45 |
31 uint8_t * check_cycles(uint8_t * dst) Ivds | 46 uint8_t * check_cycles(uint8_t * dst) |
32 { | 47 { |
33 dst = cmp_rr(dst, CYCLES, LIMIT, SZ_D); | 48 dst = cmp_rr(dst, CYCLES, LIMIT, SZ_D); |
34 dst = jcc(dst, CC_G, 5); | 49 dst = jcc(dst, CC_G, dst+7); |
35 dst = call(dst, (char *)handle_cycle_limit); | 50 dst = call(dst, (char *)handle_cycle_limit); |
36 } | 51 } |
37 | 52 |
38 int8_t native_reg(m68k_op_info * op, x86_68k_options * opts) | 53 int8_t native_reg(m68k_op_info * op, x86_68k_options * opts) |
39 { | 54 { |
44 return opts->aregs[op->params.regs.pri]; | 59 return opts->aregs[op->params.regs.pri]; |
45 } | 60 } |
46 return -1; | 61 return -1; |
47 } | 62 } |
48 | 63 |
49 uint8_t * translate_m68k_ea(m68k_op_info * op, x86_ea * dst, uint8_t * out, x86_68k_options * opts) | 64 void print_regs_exit(m68k_context * context) |
50 { | 65 { |
51 int8_t reg = native_reg(op, opts); | 66 for (int i = 0; i < 8; i++) { |
67 printf("d%d: %X\n", i, context->dregs[i]); | |
68 } | |
69 for (int i = 0; i < 8; i++) { | |
70 printf("a%d: %X\n", i, context->aregs[i]); | |
71 } | |
72 exit(0); | |
73 } | |
74 | |
75 uint8_t * translate_m68k_src(m68kinst * inst, x86_ea * ea, uint8_t * out, x86_68k_options * opts) | |
76 { | |
77 int8_t reg = native_reg(&(inst->src), opts); | |
78 int32_t dec_amount,inc_amount; | |
52 if (reg >= 0) { | 79 if (reg >= 0) { |
53 dst->mode = MODE_REG_DIRECT; | 80 ea->mode = MODE_REG_DIRECT; |
54 dst->base = reg; | 81 ea->base = reg; |
55 return; | 82 return out; |
56 } | 83 } |
57 switch (op->addr_mode) | 84 switch (inst->src.addr_mode) |
58 { | 85 { |
59 case MODE_REG: | 86 case MODE_REG: |
60 case MODE_AREG: | 87 case MODE_AREG: |
61 dst->mode = MODE_DISPLACE8; | 88 //We only get one memory parameter, so if the dst operand is a register in memory, |
62 dst->base = CONTEXT; | 89 //we need to copy this to a temp register first |
63 dst->disp = (op->addr_mode = MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * op->params.regs.pri; | 90 reg = native_reg(&(inst->dst), opts); |
64 break; | 91 if (reg >= 0 || inst->dst.addr_mode == MODE_UNUSED || (inst->dst.addr_mode != MODE_REG && inst->dst.addr_mode == MODE_AREG) |
92 || inst->op == M68K_EXG) { | |
93 | |
94 ea->mode = MODE_REG_DISPLACE8; | |
95 ea->base = CONTEXT; | |
96 ea->disp = (inst->src.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * inst->src.params.regs.pri; | |
97 } else { | |
98 out = mov_rdisp8r(out, CONTEXT, (inst->src.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * inst->src.params.regs.pri, SCRATCH1, inst->extra.size); | |
99 ea->mode = MODE_REG_DIRECT; | |
100 ea->base = SCRATCH1; | |
101 } | |
102 break; | |
103 case MODE_AREG_PREDEC: | |
104 dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : 1); | |
105 out = cycles(out, PREDEC_PENALTY); | |
106 if (opts->aregs[inst->src.params.regs.pri] >= 0) { | |
107 out = sub_ir(out, inc_amount, opts->aregs[inst->src.params.regs.pri], SZ_D); | |
108 } else { | |
109 out = sub_irdisp8(out, inc_amount, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SZ_D); | |
110 } | |
111 out = check_cycles(out); | |
65 case MODE_AREG_INDIRECT: | 112 case MODE_AREG_INDIRECT: |
113 case MODE_AREG_POSTINC: | |
114 if (opts->aregs[inst->src.params.regs.pri] >= 0) { | |
115 out = mov_rr(out, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); | |
116 } else { | |
117 out = mov_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D); | |
118 } | |
119 switch (inst->extra.size) | |
120 { | |
121 case OPSIZE_BYTE: | |
122 out = call(out, (char *)m68k_read_byte_scratch1); | |
123 break; | |
124 case OPSIZE_WORD: | |
125 out = call(out, (char *)m68k_read_word_scratch1); | |
126 break; | |
127 case OPSIZE_LONG: | |
128 out = call(out, (char *)m68k_read_long_scratch1); | |
129 break; | |
130 } | |
66 | 131 |
67 break; | 132 if (inst->src.addr_mode == MODE_AREG_POSTINC) { |
68 } | 133 inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : 1); |
69 } | 134 if (opts->aregs[inst->src.params.regs.pri] >= 0) { |
70 | 135 out = add_ir(out, inc_amount, opts->aregs[inst->src.params.regs.pri], SZ_D); |
71 uint8_t * translate_m68k(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) | 136 } else { |
72 { | 137 out = add_irdisp8(out, inc_amount, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SZ_D); |
73 int8_t reg_a, reg_b, flags_reg; | 138 } |
139 } | |
140 ea->mode = MODE_REG_DIRECT; | |
141 ea->base = SCRATCH1; | |
142 break; | |
143 case MODE_IMMEDIATE: | |
144 if (inst->variant != VAR_QUICK) { | |
145 if (inst->extra.size == OPSIZE_LONG) { | |
146 out = cycles(out, BUS); | |
147 out = check_cycles(out); | |
148 } | |
149 out = cycles(out, BUS); | |
150 out = check_cycles(out); | |
151 } | |
152 ea->mode = MODE_IMMED; | |
153 ea->disp = inst->src.params.immed; | |
154 break; | |
155 default: | |
156 printf("address mode %d not implemented (src)\n", inst->src.addr_mode); | |
157 exit(1); | |
158 } | |
159 return out; | |
160 } | |
161 | |
162 uint8_t * translate_m68k_dst(m68kinst * inst, x86_ea * ea, uint8_t * out, x86_68k_options * opts) | |
163 { | |
164 int8_t reg = native_reg(&(inst->dst), opts); | |
165 int32_t dec_amount, inc_amount; | |
166 if (reg >= 0) { | |
167 ea->mode = MODE_REG_DIRECT; | |
168 ea->base = reg; | |
169 return out; | |
170 } | |
171 switch (inst->dst.addr_mode) | |
172 { | |
173 case MODE_REG: | |
174 case MODE_AREG: | |
175 ea->mode = MODE_REG_DISPLACE8; | |
176 ea->base = CONTEXT; | |
177 ea->disp = (inst->dst.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * inst->dst.params.regs.pri; | |
178 break; | |
179 case MODE_AREG_PREDEC: | |
180 dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : 1); | |
181 if (opts->aregs[inst->dst.params.regs.pri] >= 0) { | |
182 out = sub_ir(out, dec_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D); | |
183 } else { | |
184 out = sub_irdisp8(out, dec_amount, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D); | |
185 } | |
186 case MODE_AREG_INDIRECT: | |
187 case MODE_AREG_POSTINC: | |
188 if (opts->aregs[inst->dst.params.regs.pri] >= 0) { | |
189 out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], SCRATCH1, SZ_D); | |
190 } else { | |
191 out = mov_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SCRATCH1, SZ_D); | |
192 } | |
193 switch (inst->extra.size) | |
194 { | |
195 case OPSIZE_BYTE: | |
196 out = call(out, (char *)m68k_read_byte_scratch1); | |
197 break; | |
198 case OPSIZE_WORD: | |
199 out = call(out, (char *)m68k_read_word_scratch1); | |
200 break; | |
201 case OPSIZE_LONG: | |
202 out = call(out, (char *)m68k_read_long_scratch1); | |
203 break; | |
204 } | |
205 //save reg value in SCRATCH2 so we can use it to save the result in memory later | |
206 if (opts->aregs[inst->dst.params.regs.pri] >= 0) { | |
207 out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D); | |
208 } else { | |
209 out = mov_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SCRATCH2, SZ_D); | |
210 } | |
211 | |
212 if (inst->src.addr_mode == MODE_AREG_POSTINC) { | |
213 inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : 1); | |
214 if (opts->aregs[inst->dst.params.regs.pri] >= 0) { | |
215 out = add_ir(out, inc_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D); | |
216 } else { | |
217 out = add_irdisp8(out, inc_amount, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D); | |
218 } | |
219 } | |
220 ea->mode = MODE_REG_DIRECT; | |
221 ea->base = SCRATCH1; | |
222 break; | |
223 default: | |
224 printf("address mode %d not implemented (dst)\n", inst->dst.addr_mode); | |
225 exit(1); | |
226 } | |
227 return out; | |
228 } | |
229 | |
230 uint8_t * m68k_save_result(m68kinst * inst, uint8_t * out, x86_68k_options * opts) | |
231 { | |
232 if (inst->dst.addr_mode != MODE_REG && inst->dst.addr_mode != MODE_AREG) { | |
233 switch (inst->extra.size) | |
234 { | |
235 case OPSIZE_BYTE: | |
236 out = call(out, (char *)m68k_write_byte); | |
237 break; | |
238 case OPSIZE_WORD: | |
239 out = call(out, (char *)m68k_write_word); | |
240 break; | |
241 case OPSIZE_LONG: | |
242 out = call(out, (char *)m68k_write_long_lowfirst); | |
243 break; | |
244 } | |
245 } | |
246 return out; | |
247 } | |
248 | |
249 uint8_t * get_native_address(native_map_slot * native_code_map, uint32_t address) | |
250 { | |
251 address &= 0xFFFFFF; | |
252 uint32_t chunk = address / NATIVE_CHUNK_SIZE; | |
253 if (!native_code_map[chunk].base) { | |
254 return NULL; | |
255 } | |
256 uint32_t offset = address % NATIVE_CHUNK_SIZE; | |
257 if (native_code_map[chunk].offsets[offset] == INVALID_OFFSET) { | |
258 return NULL; | |
259 } | |
260 return native_code_map[chunk].base + native_code_map[chunk].offsets[offset]; | |
261 } | |
262 | |
263 deferred_addr * defer_address(deferred_addr * old_head, uint32_t address, uint8_t *dest) | |
264 { | |
265 deferred_addr * new_head = malloc(sizeof(deferred_addr)); | |
266 new_head->next = old_head; | |
267 new_head->address = address & 0xFFFFFF; | |
268 new_head->dest = dest; | |
269 return new_head; | |
270 } | |
271 | |
272 void process_deferred(x86_68k_options * opts) | |
273 { | |
274 deferred_addr * cur = opts->deferred; | |
275 deferred_addr **last_next = &(opts->deferred); | |
276 while(cur) | |
277 { | |
278 uint8_t * native = get_native_address(opts->native_code_map, cur->address); | |
279 if (native) { | |
280 int32_t disp = native - (cur->dest + 4); | |
281 printf("Native dest: %p, Offset address: %p, displacement: %X\n", native, cur->dest, disp); | |
282 uint8_t * out = cur->dest; | |
283 *(out++) = disp; | |
284 disp >>= 8; | |
285 *(out++) = disp; | |
286 disp >>= 8; | |
287 *(out++) = disp; | |
288 disp >>= 8; | |
289 *out = disp; | |
290 *last_next = cur->next; | |
291 free(cur); | |
292 cur = *last_next; | |
293 } else { | |
294 last_next = &(cur->next); | |
295 cur = cur->next; | |
296 } | |
297 } | |
298 } | |
299 | |
300 void map_native_address(native_map_slot * native_code_map, uint32_t address, uint8_t * native_addr) | |
301 { | |
302 //FIXME: This probably isn't going to work with real code in a lot of cases, no guarantee that | |
303 //all the code in 1KB block is going to be translated at the same time | |
304 address &= 0xFFFFFF; | |
305 uint32_t chunk = address / NATIVE_CHUNK_SIZE; | |
306 if (!native_code_map[chunk].base) { | |
307 native_code_map[chunk].base = native_addr; | |
308 native_code_map[chunk].offsets = malloc(sizeof(uint16_t) * NATIVE_CHUNK_SIZE); | |
309 memset(native_code_map[chunk].offsets, 0xFF, sizeof(uint16_t) * NATIVE_CHUNK_SIZE); | |
310 } | |
311 uint32_t offset = address % NATIVE_CHUNK_SIZE; | |
312 native_code_map[chunk].offsets[offset] = native_addr-native_code_map[chunk].base; | |
313 } | |
314 | |
315 uint8_t * translate_m68k_move(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) | |
316 { | |
317 int8_t reg, flags_reg; | |
74 uint8_t dir = 0; | 318 uint8_t dir = 0; |
75 int32_t offset; | 319 int32_t offset; |
320 int32_t inc_amount, dec_amount; | |
321 x86_ea src; | |
322 dst = translate_m68k_src(inst, &src, dst, opts); | |
323 reg = native_reg(&(inst->dst), opts); | |
324 if (src.mode == MODE_REG_DIRECT) { | |
325 flags_reg = src.base; | |
326 } else { | |
327 if (reg >= 0) { | |
328 flags_reg = reg; | |
329 } else { | |
330 printf("moving %d to temp register %d\n", src.disp, SCRATCH1); | |
331 dst = mov_ir(dst, src.disp, SCRATCH1, SZ_D); | |
332 src.mode = MODE_REG_DIRECT; | |
333 flags_reg = src.base = SCRATCH1; | |
334 } | |
335 } | |
336 switch(inst->dst.addr_mode) | |
337 { | |
338 case MODE_REG: | |
339 case MODE_AREG: | |
340 if (reg >= 0) { | |
341 if (src.mode == MODE_REG_DIRECT) { | |
342 dst = mov_rr(dst, src.base, reg, inst->extra.size); | |
343 } else if (src.mode == MODE_REG_DISPLACE8) { | |
344 dst = mov_rdisp8r(dst, src.base, src.disp, reg, inst->extra.size); | |
345 } else { | |
346 dst = mov_ir(dst, src.disp, reg, inst->extra.size); | |
347 } | |
348 } else if(src.mode == MODE_REG_DIRECT) { | |
349 printf("mov_rrdisp8 from reg %d to offset %d from reg %d (%d)\n", src.base, (inst->dst.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * inst->dst.params.regs.pri, CONTEXT, inst->dst.params.regs.pri); | |
350 dst = mov_rrdisp8(dst, src.base, CONTEXT, (inst->dst.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * inst->dst.params.regs.pri, inst->extra.size); | |
351 } else { | |
352 dst = mov_irdisp8(dst, src.disp, CONTEXT, (inst->dst.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * inst->dst.params.regs.pri, inst->extra.size); | |
353 } | |
354 break; | |
355 case MODE_AREG_PREDEC: | |
356 dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : 1); | |
357 if (opts->aregs[inst->dst.params.regs.pri] >= 0) { | |
358 dst = sub_ir(dst, dec_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D); | |
359 } else { | |
360 dst = sub_irdisp8(dst, dec_amount, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D); | |
361 } | |
362 case MODE_AREG_INDIRECT: | |
363 case MODE_AREG_POSTINC: | |
364 if (opts->aregs[inst->dst.params.regs.pri] >= 0) { | |
365 dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D); | |
366 } else { | |
367 dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SCRATCH2, SZ_D); | |
368 } | |
369 if (src.mode == MODE_REG_DIRECT) { | |
370 if (src.base != SCRATCH1) { | |
371 dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size); | |
372 } | |
373 } else if (src.mode == MODE_REG_DISPLACE8) { | |
374 dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size); | |
375 } else { | |
376 dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size); | |
377 } | |
378 switch (inst->extra.size) | |
379 { | |
380 case OPSIZE_BYTE: | |
381 dst = call(dst, (char *)m68k_write_byte); | |
382 break; | |
383 case OPSIZE_WORD: | |
384 dst = call(dst, (char *)m68k_write_word); | |
385 break; | |
386 case OPSIZE_LONG: | |
387 dst = call(dst, (char *)m68k_write_long_highfirst); | |
388 break; | |
389 } | |
390 if (inst->dst.addr_mode == MODE_AREG_POSTINC) { | |
391 inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : 1); | |
392 if (opts->aregs[inst->dst.params.regs.pri] >= 0) { | |
393 dst = add_ir(dst, inc_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D); | |
394 } else { | |
395 dst = add_irdisp8(dst, inc_amount, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D); | |
396 } | |
397 } | |
398 break; | |
399 default: | |
400 printf("address mode %d not implemented (move dst)\n", inst->dst.addr_mode); | |
401 exit(1); | |
402 } | |
403 | |
404 //add cycles for prefetch | |
405 dst = cycles(dst, BUS); | |
406 //update flags | |
407 dst = mov_ir(dst, 0, FLAG_V, SZ_B); | |
408 dst = mov_ir(dst, 0, FLAG_C, SZ_B); | |
409 dst = cmp_ir(dst, 0, flags_reg, inst->extra.size); | |
410 dst = setcc_r(dst, CC_Z, FLAG_Z); | |
411 dst = setcc_r(dst, CC_S, FLAG_N); | |
412 dst = check_cycles(dst); | |
413 return dst; | |
414 } | |
415 | |
416 uint8_t * translate_m68k_lea(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) | |
417 { | |
418 int8_t dst_reg = native_reg(&(inst->dst), opts); | |
419 switch(inst->src.addr_mode) | |
420 { | |
421 case MODE_AREG_INDIRECT: | |
422 dst = cycles(dst, BUS); | |
423 if (opts->aregs[inst->src.params.regs.pri] >= 0) { | |
424 if (dst_reg >= 0) { | |
425 dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], dst_reg, SZ_D); | |
426 } else { | |
427 dst = mov_rrdisp8(dst, opts->aregs[inst->src.params.regs.pri], CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D); | |
428 } | |
429 } else { | |
430 if (dst_reg >= 0) { | |
431 dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, dst_reg, SZ_D); | |
432 } else { | |
433 dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D); | |
434 dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D); | |
435 } | |
436 } | |
437 dst = check_cycles(dst); | |
438 break; | |
439 case MODE_ABSOLUTE: | |
440 dst = cycles(dst, BUS); | |
441 dst = check_cycles(dst); | |
442 case MODE_ABSOLUTE_SHORT: | |
443 dst = cycles(dst, BUS); | |
444 dst = check_cycles(dst); | |
445 dst = cycles(dst, BUS); | |
446 if (dst_reg >= 0) { | |
447 dst = mov_ir(dst, inst->src.params.immed, dst_reg, SZ_D); | |
448 } else { | |
449 dst = mov_irdisp8(dst, inst->src.params.immed, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D); | |
450 } | |
451 dst = check_cycles(dst); | |
452 break; | |
453 } | |
454 return dst; | |
455 } | |
456 | |
457 uint8_t * translate_m68k_bsr(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) | |
458 { | |
459 //TODO: Add cycles | |
460 int32_t disp = inst->src.params.immed; | |
461 uint32_t after = inst->address + (inst->variant == VAR_BYTE ? 2 : (inst->variant == VAR_WORD ? 4 : 6)); | |
462 dst = mov_ir(dst, after, SCRATCH1, SZ_D); | |
463 dst = push_r(dst, SCRATCH1); | |
464 dst = sub_ir(dst, 4, opts->aregs[7], SZ_D); | |
465 dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D); | |
466 dst = call(dst, (char *)m68k_write_long_highfirst); | |
467 printf("bsr@%X: after=%X, disp=%X, dest=%X\n", inst->address, after, disp, after+disp); | |
468 uint8_t * dest_addr = get_native_address(opts->native_code_map, after + disp); | |
469 if (!dest_addr) { | |
470 opts->deferred = defer_address(opts->deferred, after + disp, dst + 1); | |
471 //dummy address to be replaced later | |
472 dest_addr = dst + 5; | |
473 } | |
474 dst = call(dst, (char *)dest_addr); | |
475 //would add_ir(dst, 8, RSP, SZ_Q) be faster here? | |
476 dst = pop_r(dst, SCRATCH1); | |
477 return dst; | |
478 } | |
479 | |
480 uint8_t * translate_m68k_bcc(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) | |
481 { | |
482 //TODO: Add cycles | |
483 int32_t disp = inst->src.params.immed; | |
484 uint32_t after = inst->address + (inst->variant == VAR_BYTE ? 2 : (inst->variant == VAR_WORD ? 4 : 6)); | |
485 printf("bcc@%X: after=%X, disp=%X, dest=%X\n", inst->address, after, disp, after+disp); | |
486 uint8_t * dest_addr = get_native_address(opts->native_code_map, after + disp); | |
487 if (inst->extra.cond == COND_TRUE) { | |
488 if (!dest_addr) { | |
489 opts->deferred = defer_address(opts->deferred, after + disp, dst + 1); | |
490 //dummy address to be replaced later, make sure it generates a 4-byte displacement | |
491 dest_addr = dst + 256; | |
492 } | |
493 dst = jmp(dst, dest_addr); | |
494 } else { | |
495 uint8_t cond = CC_NZ; | |
496 switch (inst->extra.cond) | |
497 { | |
498 case COND_HIGH: | |
499 cond = CC_Z; | |
500 case COND_LOW_SAME: | |
501 dst = mov_rr(dst, FLAG_Z, SCRATCH1, SZ_B); | |
502 dst = or_rr(dst, FLAG_C, SCRATCH1, SZ_B); | |
503 break; | |
504 case COND_CARRY_CLR: | |
505 cond = CC_Z; | |
506 case COND_CARRY_SET: | |
507 dst = cmp_ir(dst, 0, FLAG_C, SZ_B); | |
508 break; | |
509 case COND_NOT_EQ: | |
510 cond = CC_Z; | |
511 case COND_EQ: | |
512 dst = cmp_ir(dst, 0, FLAG_Z, SZ_B); | |
513 break; | |
514 case COND_OVERF_CLR: | |
515 cond = CC_Z; | |
516 case COND_OVERF_SET: | |
517 dst = cmp_ir(dst, 0, FLAG_V, SZ_B); | |
518 break; | |
519 case COND_PLUS: | |
520 cond = CC_Z; | |
521 case COND_MINUS: | |
522 dst = cmp_ir(dst, 0, FLAG_N, SZ_B); | |
523 break; | |
524 case COND_GREATER_EQ: | |
525 cond = CC_Z; | |
526 case COND_LESS: | |
527 dst = cmp_rr(dst, FLAG_N, FLAG_V, SZ_B); | |
528 break; | |
529 case COND_GREATER: | |
530 cond = CC_Z; | |
531 case COND_LESS_EQ: | |
532 dst = mov_rr(dst, FLAG_V, SCRATCH1, SZ_B); | |
533 dst = xor_rr(dst, FLAG_N, SCRATCH1, SZ_B); | |
534 dst = or_rr(dst, FLAG_Z, SCRATCH1, SZ_B); | |
535 break; | |
536 } | |
537 if (!dest_addr) { | |
538 opts->deferred = defer_address(opts->deferred, after + disp, dst + 2); | |
539 //dummy address to be replaced later, make sure it generates a 4-byte displacement | |
540 dest_addr = dst + 256; | |
541 } | |
542 dst = jcc(dst, cond, dest_addr); | |
543 } | |
544 return dst; | |
545 } | |
546 | |
547 uint8_t * translate_m68k_rts(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) | |
548 { | |
549 //TODO: Add cycles | |
550 dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D); | |
551 dst = add_ir(dst, 4, opts->aregs[7], SZ_D); | |
552 dst = call(dst, (char *)m68k_read_long_scratch1); | |
553 dst = cmp_rdisp8r(dst, RSP, 8, SCRATCH1, SZ_D); | |
554 dst = jcc(dst, CC_NZ, dst+3); | |
555 dst = retn(dst); | |
556 dst = jmp(dst, (char *)m68k_modified_ret_addr); | |
557 return dst; | |
558 } | |
559 | |
560 uint8_t * translate_m68k(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) | |
561 { | |
562 map_native_address(opts->native_code_map, inst->address, dst); | |
563 if (inst->op == M68K_MOVE) { | |
564 return translate_m68k_move(dst, inst, opts); | |
565 } else if(inst->op == M68K_LEA) { | |
566 return translate_m68k_lea(dst, inst, opts); | |
567 } else if(inst->op == M68K_BSR) { | |
568 return translate_m68k_bsr(dst, inst, opts); | |
569 } else if(inst->op == M68K_BCC) { | |
570 return translate_m68k_bcc(dst, inst, opts); | |
571 } else if(inst->op == M68K_RTS) { | |
572 return translate_m68k_rts(dst, inst, opts); | |
573 } | |
574 x86_ea src_op, dst_op; | |
575 if (inst->src.addr_mode != MODE_UNUSED) { | |
576 dst = translate_m68k_src(inst, &src_op, dst, opts); | |
577 } | |
578 if (inst->dst.addr_mode != MODE_UNUSED) { | |
579 dst = translate_m68k_dst(inst, &dst_op, dst, opts); | |
580 } | |
76 switch(inst->op) | 581 switch(inst->op) |
77 { | 582 { |
78 case M68K_ABCD: | 583 case M68K_ABCD: |
584 break; | |
79 case M68K_ADD: | 585 case M68K_ADD: |
586 dst = cycles(dst, BUS); | |
587 if (src_op.mode == MODE_REG_DIRECT) { | |
588 if (dst_op.mode == MODE_REG_DIRECT) { | |
589 dst = add_rr(dst, src_op.base, dst_op.base, inst->extra.size); | |
590 } else { | |
591 dst = add_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size); | |
592 } | |
593 } else if (src_op.mode == MODE_REG_DISPLACE8) { | |
594 dst = add_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size); | |
595 } else { | |
596 if (dst_op.mode == MODE_REG_DIRECT) { | |
597 dst = add_ir(dst, src_op.disp, dst_op.base, inst->extra.size); | |
598 } else { | |
599 dst = add_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); | |
600 } | |
601 } | |
602 dst = setcc_r(dst, CC_C, FLAG_C); | |
603 dst = setcc_r(dst, CC_Z, FLAG_Z); | |
604 dst = setcc_r(dst, CC_S, FLAG_N); | |
605 dst = setcc_r(dst, CC_O, FLAG_V); | |
606 dst = mov_rrind(dst, FLAG_C, CONTEXT, SZ_B); | |
607 dst = check_cycles(dst); | |
608 break; | |
80 case M68K_ADDX: | 609 case M68K_ADDX: |
81 case M68K_AND: | 610 case M68K_AND: |
82 case M68K_ANDI_CCR: | 611 case M68K_ANDI_CCR: |
83 case M68K_ANDI_SR: | 612 case M68K_ANDI_SR: |
84 case M68K_ASL: | 613 case M68K_ASL: |
90 case M68K_BSR: | 619 case M68K_BSR: |
91 case M68K_BTST: | 620 case M68K_BTST: |
92 case M68K_CHK: | 621 case M68K_CHK: |
93 case M68K_CLR: | 622 case M68K_CLR: |
94 case M68K_CMP: | 623 case M68K_CMP: |
624 dst = cycles(dst, BUS); | |
625 if (src_op.mode == MODE_REG_DIRECT) { | |
626 if (dst_op.mode == MODE_REG_DIRECT) { | |
627 dst = cmp_rr(dst, src_op.base, dst_op.base, inst->extra.size); | |
628 } else { | |
629 dst = cmp_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size); | |
630 } | |
631 } else if (src_op.mode == MODE_REG_DISPLACE8) { | |
632 dst = cmp_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size); | |
633 } else { | |
634 if (dst_op.mode == MODE_REG_DIRECT) { | |
635 dst = cmp_ir(dst, src_op.disp, dst_op.base, inst->extra.size); | |
636 } else { | |
637 dst = cmp_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); | |
638 } | |
639 } | |
640 dst = setcc_r(dst, CC_C, FLAG_C); | |
641 dst = setcc_r(dst, CC_Z, FLAG_Z); | |
642 dst = setcc_r(dst, CC_S, FLAG_N); | |
643 dst = setcc_r(dst, CC_O, FLAG_V); | |
644 dst = check_cycles(dst); | |
645 break; | |
95 case M68K_DBCC: | 646 case M68K_DBCC: |
96 case M68K_DIVS: | 647 case M68K_DIVS: |
97 case M68K_DIVU: | 648 case M68K_DIVU: |
98 case M68K_EOR: | 649 case M68K_EOR: |
99 case M68K_EORI_CCR: | 650 case M68K_EORI_CCR: |
100 case M68K_EORI_SR: | 651 case M68K_EORI_SR: |
101 case M68K_EXG: | 652 case M68K_EXG: |
653 dst = cycles(dst, 6); | |
654 if (dst_op.mode == MODE_REG_DIRECT) { | |
655 dst = mov_rr(dst, dst_op.base, SCRATCH2, SZ_D); | |
656 if (src_op.mode == MODE_REG_DIRECT) { | |
657 dst = mov_rr(dst, src_op.base, dst_op.base, SZ_D); | |
658 dst = mov_rr(dst, SCRATCH2, src_op.base, SZ_D); | |
659 } else { | |
660 dst = mov_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, SZ_D); | |
661 dst = mov_rrdisp8(dst, SCRATCH2, src_op.base, src_op.disp, SZ_D); | |
662 } | |
663 } else { | |
664 dst = mov_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH2, SZ_D); | |
665 if (src_op.mode == MODE_REG_DIRECT) { | |
666 dst = mov_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, SZ_D); | |
667 dst = mov_rr(dst, SCRATCH2, src_op.base, SZ_D); | |
668 } else { | |
669 dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_D); | |
670 dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_D); | |
671 dst = mov_rrdisp8(dst, SCRATCH2, src_op.base, src_op.disp, SZ_D); | |
672 } | |
673 } | |
674 dst = check_cycles(dst); | |
675 break; | |
102 case M68K_EXT: | 676 case M68K_EXT: |
103 case M68K_ILLEGAL: | 677 case M68K_ILLEGAL: |
678 dst = call(dst, (uint8_t *)m68k_save_context); | |
679 dst = mov_rr(dst, CONTEXT, RDI, SZ_Q); | |
680 dst = call(dst, (uint8_t *)print_regs_exit); | |
681 break; | |
104 case M68K_JMP: | 682 case M68K_JMP: |
105 case M68K_JSR: | 683 case M68K_JSR: |
106 case M68K_LEA: | 684 case M68K_LEA: |
107 case M68K_LINK: | 685 case M68K_LINK: |
108 case M68K_LSL: | 686 case M68K_LSL: |
109 case M68K_LSR: | 687 case M68K_LSR: |
110 case M68K_MOVE: | |
111 | |
112 if ((inst->src.addr_mode == MODE_REG || inst->src.addr_mode == MODE_AREG || (inst->src.addr_mode == MODE_IMMEDIATE && inst->src.variant == VAR_QUICK)) && (inst->dst.addr_mode == MODE_REG || inst->dst.addr_mode == MODE_AREG)) { | |
113 dst = cycles(dst, BUS); | |
114 reg_a = native_reg(&(inst->src), opts); | |
115 reg_b = native_reg(&(inst->dst), opts); | |
116 dst = cycles(dst, BUS); | |
117 if (reg_a >= 0 && reg_b >= 0) { | |
118 dst = mov_rr(dst, reg_a, reg_b, inst->extra.size); | |
119 flags_reg = reg_b; | |
120 } else if(reg_a >= 0) { | |
121 offset = inst->dst.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs); | |
122 dst = mov_rrdisp8(dst, reg_a, CONTEXT, offset + 4 * inst->dst.params.regs.pri, inst->extra.size); | |
123 flags_reg = reg_a; | |
124 } else if(reg_b >= 0) { | |
125 if (inst->src.addr_mode == MODE_REG) { | |
126 dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + 4 * inst->src.params.regs.pri, reg_b, inst->extra.size); | |
127 } else if(inst->src.addr_mode == MODE_AREG) { | |
128 dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, reg_b, inst->extra.size); | |
129 } else { | |
130 dst = mov_i32r(dst, inst->src.params.u32, reg_b); | |
131 } | |
132 flags_reg = reg_b; | |
133 } else { | |
134 | |
135 } | |
136 dst = mov_i8r(dst, 0, FLAG_V); | |
137 dst = mov_i8r(dst, 0, FLAG_C); | |
138 switch (inst->extra.size) | |
139 { | |
140 case OPSIZE_BYTE: | |
141 dst = cmp_i8r(dst, 0, reg_b, SZ_B); | |
142 break; | |
143 case OPSIZE_WORD: | |
144 dst = cmp_i8r(dst, 0, reg_b, SZ_W); | |
145 break; | |
146 case OPSIZE_LONG: | |
147 dst = cmp_i8r(dst, 0, reg_b, SZ_D); | |
148 break; | |
149 } | |
150 dst = setcc_r(dst, CC_Z, FLAG_Z); | |
151 dst = setcc_r(dst, CC_S, FLAG_N); | |
152 dst = check_cycles(dst); | |
153 } | |
154 | |
155 if (reg_a >= 0 && reg_b >= 0) { | |
156 dst = cycles(dst, BUS); | |
157 dst = mov_rr(dst, reg_a, reg_b, inst->extra.size); | |
158 dst = mov_i8r(dst, 0, FLAG_V); | |
159 dst = mov_i8r(dst, 0, FLAG_C); | |
160 switch (inst->extra.size) | |
161 { | |
162 case OPSIZE_BYTE: | |
163 dst = cmp_i8r(dst, 0, reg_b, SZ_B); | |
164 break; | |
165 case OPSIZE_WORD: | |
166 dst = cmp_i8r(dst, 0, reg_b, SZ_W); | |
167 break; | |
168 case OPSIZE_LONG: | |
169 dst = cmp_i8r(dst, 0, reg_b, SZ_D); | |
170 break; | |
171 } | |
172 dst = setcc_r(dst, CC_Z, FLAG_Z); | |
173 dst = setcc_r(dst, CC_S, FLAG_N); | |
174 dst = check_cycles(dst); | |
175 } else if(reg_a >= 0 || reg_b >= 0) { | |
176 if (reg_a >= 0) { | |
177 switch (inst->dst.addr_mode) | |
178 { | |
179 case MODE_REG: | |
180 dst = cycles(dst, BUS); | |
181 dst = mov_rr(dst, reg_a, reg_b, inst->extra.size); | |
182 dst = check_cycles(dst); | |
183 break; | |
184 case MODE_AREG: | |
185 break; | |
186 } | |
187 } else { | |
188 } | |
189 } | |
190 break; | |
191 case M68K_MOVE_CCR: | 688 case M68K_MOVE_CCR: |
192 case M68K_MOVE_FROM_SR: | 689 case M68K_MOVE_FROM_SR: |
193 case M68K_MOVE_SR: | 690 case M68K_MOVE_SR: |
194 case M68K_MOVE_USP: | 691 case M68K_MOVE_USP: |
195 case M68K_MOVEM: | 692 case M68K_MOVEM: |
215 case M68K_RTS: | 712 case M68K_RTS: |
216 case M68K_SBCD: | 713 case M68K_SBCD: |
217 case M68K_SCC: | 714 case M68K_SCC: |
218 case M68K_STOP: | 715 case M68K_STOP: |
219 case M68K_SUB: | 716 case M68K_SUB: |
717 dst = cycles(dst, BUS); | |
718 if (src_op.mode == MODE_REG_DIRECT) { | |
719 if (dst_op.mode == MODE_REG_DIRECT) { | |
720 dst = sub_rr(dst, src_op.base, dst_op.base, inst->extra.size); | |
721 } else { | |
722 dst = sub_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size); | |
723 } | |
724 } else if (src_op.mode == MODE_REG_DISPLACE8) { | |
725 dst = sub_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size); | |
726 } else { | |
727 if (dst_op.mode == MODE_REG_DIRECT) { | |
728 dst = sub_ir(dst, src_op.disp, dst_op.base, inst->extra.size); | |
729 } else { | |
730 dst = sub_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size); | |
731 } | |
732 } | |
733 dst = setcc_r(dst, CC_C, FLAG_C); | |
734 dst = setcc_r(dst, CC_Z, FLAG_Z); | |
735 dst = setcc_r(dst, CC_S, FLAG_N); | |
736 dst = setcc_r(dst, CC_O, FLAG_V); | |
737 dst = mov_rrind(dst, FLAG_C, CONTEXT, SZ_B); | |
738 dst = check_cycles(dst); | |
739 break; | |
220 case M68K_SUBX: | 740 case M68K_SUBX: |
221 case M68K_SWAP: | 741 case M68K_SWAP: |
222 case M68K_TAS: | 742 case M68K_TAS: |
223 case M68K_TRAP: | 743 case M68K_TRAP: |
224 case M68K_TRAPV: | 744 case M68K_TRAPV: |
225 case M68K_TST: | 745 case M68K_TST: |
226 case M68K_UNLK: | 746 case M68K_UNLK: |
227 case M68K_INVALID: | 747 case M68K_INVALID: |
228 break; | 748 break; |
229 } | 749 } |
230 } | 750 return dst; |
231 | 751 } |
752 | |
753 uint8_t * translate_m68k_stream(uint8_t * dst, uint8_t * dst_end, uint32_t address, m68k_context * context) | |
754 { | |
755 m68kinst instbuf; | |
756 x86_68k_options * opts = context->options; | |
757 char disbuf[1024]; | |
758 uint16_t *encoded = context->mem_pointers[0] + address/2, *next; | |
759 do { | |
760 do { | |
761 if (dst_end-dst < 128) { | |
762 puts("out of code memory"); | |
763 exit(1); | |
764 } | |
765 next = m68k_decode(encoded, &instbuf, address); | |
766 address += (next-encoded)*2; | |
767 encoded = next; | |
768 m68k_disasm(&instbuf, disbuf); | |
769 printf("%X: %s\n", instbuf.address, disbuf); | |
770 dst = translate_m68k(dst, &instbuf, opts); | |
771 } while(instbuf.op != M68K_ILLEGAL && instbuf.op != M68K_RTS && instbuf.op != M68K_RTE && !(instbuf.op == M68K_BCC && instbuf.extra.cond == COND_TRUE) && instbuf.op != M68K_JMP); | |
772 process_deferred(opts); | |
773 if (opts->deferred) { | |
774 address = opts->deferred->address; | |
775 encoded = context->mem_pointers[0] + address/2; | |
776 } else { | |
777 encoded = NULL; | |
778 } | |
779 } while(encoded != NULL); | |
780 return dst; | |
781 } | |
782 | |
783 void start_68k_context(m68k_context * context, uint32_t address) | |
784 { | |
785 uint8_t * addr = get_native_address(context->native_code_map, address); | |
786 m68k_start_context(addr, context); | |
787 } | |
788 | |
789 void init_x86_68k_opts(x86_68k_options * opts) | |
790 { | |
791 opts->flags = 0; | |
792 for (int i = 0; i < 8; i++) | |
793 opts->dregs[i] = opts->aregs[i] = -1; | |
794 opts->dregs[0] = R10; | |
795 opts->dregs[1] = R11; | |
796 opts->dregs[2] = R12; | |
797 opts->aregs[0] = R13; | |
798 opts->aregs[1] = R14; | |
799 opts->aregs[7] = R15; | |
800 opts->native_code_map = malloc(sizeof(native_map_slot) * NATIVE_MAP_CHUNKS); | |
801 memset(opts->native_code_map, 0, sizeof(native_map_slot) * NATIVE_MAP_CHUNKS); | |
802 opts->deferred = NULL; | |
803 } | |
804 | |
805 void init_68k_context(m68k_context * context, native_map_slot * native_code_map, void * opts) | |
806 { | |
807 memset(context, 0, sizeof(m68k_context)); | |
808 context->native_code_map = native_code_map; | |
809 context->options = opts; | |
810 } | |
811 |