Add <functional> to files that use std::function
[lsnes.git] / src / library / assembler-intrinsics-i386.cpp
blobb0c103e37b464124517e226af67519bc5f861acc
1 #include "assembler-intrinsics-i386.hpp"
2 #include "serialization.hpp"
3 #include <stdexcept>
5 namespace assembler_intrinsics
7 const I386::reg I386::reg_none(255);
8 const I386::reg I386::reg_ax(0);
9 const I386::reg I386::reg_bx(3);
10 const I386::reg I386::reg_cx(1);
11 const I386::reg I386::reg_dx(2);
12 const I386::reg I386::reg_bp(5);
13 const I386::reg I386::reg_sp(4);
14 const I386::reg I386::reg_si(6);
15 const I386::reg I386::reg_di(7);
16 const I386::reg I386::reg_r8(8);
17 const I386::reg I386::reg_r9(9);
18 const I386::reg I386::reg_r10(10);
19 const I386::reg I386::reg_r11(11);
20 const I386::reg I386::reg_r12(12);
21 const I386::reg I386::reg_r13(13);
22 const I386::reg I386::reg_r14(14);
23 const I386::reg I386::reg_r15(15);
24 const I386::reg I386::reg_rip(254);
26 const I386::reg i386_r0(0);
27 const I386::reg i386_r1(1);
28 const I386::reg i386_r2(2);
29 const I386::reg i386_r3(3);
30 const I386::reg i386_r4(4);
31 const I386::reg i386_r5(5);
32 const I386::reg i386_r6(6);
33 const I386::reg i386_r7(7);
35 I386::reg::reg(uint8_t _val)
37 val = _val;
40 I386::ref I386::reg::operator[](int32_t off) const
42 if(val < 16)
43 return ref::reg_off(*this, off);
44 else if(val == 254)
45 return I386::ref::rip_off(off);
46 else
47 throw std::runtime_error("Bad register offset-base");
50 I386::sib_scale_intermediate I386::reg::operator*(uint8_t scale) const
52 return sib_scale_intermediate(*this, scale);
55 I386::ref I386::reg::operator[](const reg r) const
57 return ref::sib(*this, r, 1);
60 I386::ref I386::reg::operator[](sib_scale_intermediate r) const
62 return ref::sib(*this, r.index, r.scale);
65 I386::ref I386::reg::operator[](sib_scale_off_intermediate r) const
67 return ref::sib(*this, r.index, r.scale, r.offset);
70 I386::sib_scale_off_intermediate I386::reg::operator+(int32_t off) const
72 return sib_scale_off_intermediate(*this, 1, off);
75 bool I386::reg::hbit() { return (val & 8); }
76 uint8_t I386::reg::lbits() { return val & 7; }
77 uint8_t I386::reg::num() { return val & 15; }
78 bool I386::reg::valid(bool amd64) { return (val < (amd64 ? 16 : 8)); }
79 bool I386::reg::is_none() { return (val == 255); }
81 I386::sib_scale_off_intermediate::sib_scale_off_intermediate(reg _idx, uint8_t _scale, int32_t _offset)
82 : index(_idx), scale(_scale), offset(_offset)
86 I386::sib_scale_intermediate::sib_scale_intermediate(reg _idx, uint8_t _scale)
87 : index(_idx), scale(_scale)
91 I386::sib_scale_off_intermediate I386::sib_scale_intermediate::operator+(int32_t off) const
93 return sib_scale_off_intermediate(index, scale, off);
97 void check_register(I386::reg r, bool amd64)
99 if(!r.valid(amd64))
100 throw std::runtime_error("Illegal register");
103 void I386::label(assembler::label& l)
105 a._label(l);
108 I386::low::low(bool _need_amd64, uint8_t _rex_prefix, std::vector<uint8_t> _ref)
109 : needs_amd64(_need_amd64), rex(_rex_prefix), mref(_ref)
111 if(rex) needs_amd64 = true;
113 bool I386::low::need_amd64() { return needs_amd64; }
114 bool I386::low::has_rex() { return (rex != 0); }
115 uint8_t I386::low::rex_prefix() { return rex; }
116 std::vector<uint8_t> I386::low::bytes() { return mref; }
118 void I386::low::emit_rex(assembler::assembler& a)
120 if(has_rex()) a(rex);
123 void I386::low::emit_bytes(assembler::assembler& a)
125 for(auto i : mref) a(i);
128 I386::ref::ref()
132 I386::ref::ref(reg r)
134 if(!r.valid(true))
135 throw std::runtime_error("Illegal register");
136 needs_amd64 = r.hbit();
137 rex = r.hbit() ? 1 : 0;
138 mref.push_back(0xC0 + r.lbits());
141 I386::ref::ref(sib_scale_intermediate r)
143 *this = sib(reg_none, r.index, r.scale);
146 I386::ref::ref(sib_scale_off_intermediate r)
148 *this = sib(reg_none, r.index, r.scale, r.offset);
151 I386::ref I386::ref::reg_off(reg r, int32_t off)
153 if(!r.valid(true))
154 throw std::runtime_error("Illegal register");
155 bool need_off = (off != 0);
156 bool need_loff = (off < -128 || off > 127);
157 I386::ref x;
158 x.needs_amd64 = r.hbit();
159 x.rex = r.hbit() ? 1 : 0;
160 if(r.lbits() == 5)
161 need_off = true; //EBP and R13 always need offset.
162 uint8_t rtype = need_loff ? 2 : (need_off ? 1 : 0);
163 if(r.lbits() == 4) {
164 //SIB is required for these.
165 x.mref.push_back(0x04 + rtype * 0x40);
166 x.mref.push_back(0x24);
167 } else {
168 x.mref.push_back(r.lbits() + rtype * 0x40);
170 size_t bytes = x.mref.size();
171 if(need_loff) {
172 x.mref.resize(bytes + 4);
173 serialization::s32l(&x.mref[bytes], off);
174 } else if(need_off) {
175 x.mref.resize(bytes + 1);
176 serialization::s8l(&x.mref[bytes], off);
178 return x;
181 I386::ref I386::ref::rip_off(int32_t off)
183 I386::ref x;
184 x.needs_amd64 = true;
185 x.mref.resize(5);
186 x.mref[0] = 0x05;
187 serialization::s32l(&x.mref[1], off);
188 x.rex = 0;
189 return x;
192 I386::ref I386::ref::sib(reg base, reg index, uint8_t scale, int32_t off)
194 I386::ref x;
195 if(!base.is_none() && !base.valid(true))
196 throw std::runtime_error("Illegal base in SIB");
197 if((!index.is_none() && !index.valid(true)) || index.num() == 4)
198 throw std::runtime_error("Illegal index in SIB");
199 uint8_t ss = 0;
200 switch(scale) {
201 case 1: ss = 0; break;
202 case 2: ss = 1; break;
203 case 4: ss = 2; break;
204 case 8: ss = 3; break;
205 default: throw std::runtime_error("Illegal scale in SIB");
208 bool need_off = (off != 0);
209 bool need_loff = (off < -128 || off > 127);
210 if(base.is_none()) {
211 //Base is the offset.
212 x.mref.push_back(0x04); //SIB coming, no offset
213 x.mref.push_back(ss * 64 + index.lbits() + 5); //Base is "EBP".
214 need_loff = true;
215 } else {
216 if(base.num() == 5)
217 need_off = true; //This always needs offset.
218 uint8_t rtype = need_loff ? 2 : (need_off ? 1 : 0);
219 x.mref.push_back(0x04 + rtype * 0x40); //SIB coming.
220 x.mref.push_back(ss * 64 + index.lbits() * 8 + base.lbits());
222 x.rex = 0;
223 if(!base.is_none()) x.rex |= (base.hbit() ? 1 : 0);
224 if(!index.is_none()) x.rex |= (index.hbit() ? 2 : 0);
225 x.needs_amd64 = (x.rex != 0);
226 size_t bytes = x.mref.size();
227 if(need_loff) {
228 x.mref.resize(bytes + 4);
229 serialization::s32l(&x.mref[bytes], off);
230 } else if(need_off) {
231 x.mref.resize(bytes + 1);
232 serialization::s8l(&x.mref[bytes], off);
234 return x;
237 I386::low I386::ref::operator()(reg r, bool set_size_flag, bool amd64)
239 check_register(r, amd64);
240 auto c = mref;
241 c[0] |= r.lbits() * 8;
242 uint8_t _rex = rex;
243 if(r.hbit()) _rex+=4; //Set R
244 if(set_size_flag && amd64) _rex+=8; //Set S if needed.
245 if(_rex) _rex+=0x40; //Ret rex prefix bits.
246 if((_rex || needs_amd64) && !amd64)
247 throw std::runtime_error("Illegal memory reference for i386");
248 return low(needs_amd64, _rex, c);
251 void I386::ref::emit(assembler::assembler& a, bool set_size_flag, bool amd64, reg r, uint8_t op1)
253 auto xref = (*this)(r, set_size_flag, amd64);
254 xref.emit_rex(a);
255 a(op1);
256 xref.emit_bytes(a);
259 void I386::ref::emit(assembler::assembler& a, bool set_size_flag, bool amd64, reg r, uint8_t op1,
260 uint8_t op2)
262 auto xref = (*this)(r, set_size_flag, amd64);
263 xref.emit_rex(a);
264 a(op1, op2);
265 xref.emit_bytes(a);
268 I386::I386(assembler::assembler& _a, bool _amd64)
269 : a(_a), amd64(_amd64)
273 bool I386::is_amd64()
275 return amd64;
278 //Is i386?
279 bool I386::is_i386()
281 return !amd64;
284 //Get word size.
285 uint8_t I386::wordsize()
287 return amd64 ? 8 : 4;
290 //PUSH NWORD <reg>
291 void I386::push_reg(reg r)
293 check_register(r, amd64);
294 if(amd64 && r.hbit()) a(0x41);
295 a(0x50 + r.lbits());
297 //POP NWORD <reg>
298 void I386::pop_reg(reg r)
300 check_register(r, amd64);
301 if(amd64 && r.hbit()) a(0x41);
302 a(0x58 + r.lbits());
304 //MOV NWORD <reg>,<regmem>
305 void I386::mov_reg_regmem(reg r, I386::ref mem)
307 mem.emit(a, true, amd64, r, 0x8B);
309 //XOR NWORD <reg>,<regmem>
310 void I386::xor_reg_regmem(reg r, I386::ref mem)
312 mem.emit(a, true, amd64, r, 0x33);
315 //LEA <reg>,<mem>
316 void I386::lea_reg_mem(reg r, I386::ref mem)
318 mem.emit(a, true, amd64, r, 0x8D);
321 //AND NWORD <regmem>, imm
322 void I386::and_regmem_imm(I386::ref mem, int32_t imm)
324 mem.emit(a, true, amd64, i386_r4, 0x81);
325 a(assembler::vle<int32_t>(imm));
328 //SHL NWORD <regmem>, imm
329 void I386::shl_regmem_imm(I386::ref mem, uint8_t imm)
331 mem.emit(a, true, amd64, i386_r4, 0xC1);
332 a(imm);
335 //SHL BYTE <regmem>, imm
336 void I386::shl_regmem_imm8(ref mem, uint8_t imm)
338 mem.emit(a, true, amd64, i386_r4, 0xC0);
339 a(imm);
342 //ADD NWORD <regmem>, imm
343 void I386::add_regmem_imm(I386::ref mem, int32_t imm)
345 mem.emit(a, true, amd64, i386_r0, 0x81);
346 a(assembler::vle<int32_t>(imm));
349 //ADD NWORD <reg>,<regmem>
350 void I386::add_reg_regmem(reg r, I386::ref mem)
352 mem.emit(a, true, amd64, r, 0x03);
355 //MOV BYTE <regmem>, imm
356 void I386::mov_regmem_imm8(I386::ref mem, uint8_t imm)
358 mem.emit(a, false, amd64, i386_r0, 0xC6);
359 a(imm);
362 //MOV DWORD <regmem>, imm
363 void I386::mov_regmem_imm32(I386::ref mem, uint32_t imm)
365 mem.emit(a, false, amd64, i386_r0, 0xC7);
366 a(assembler::vle<uint32_t>(imm));
369 //MOV NWORD <reg>, imm.
370 void I386::mov_reg_imm(reg r, size_t imm)
372 check_register(r, amd64);
373 if(amd64) a(0x48 | (r.hbit() ? 1 : 0));
374 a(0xB8 + r.lbits());
375 a(assembler::vle<size_t>(imm));
378 //CMP NWORD <regmem>, imm
379 void I386::cmp_regmem_imm(I386::ref mem, int32_t imm)
381 mem.emit(a, true, amd64, i386_r7, 0x81);
382 a(assembler::vle<int32_t>(imm));
385 //MOV WORD <regmem>, <reg>
386 void I386::mov_regmem_reg16(I386::ref mem, reg r)
388 a(0x66);
389 mem.emit(a, false, amd64, r, 0x89);
392 //MOV BYTE <regmem>, <reg>
393 void I386::mov_regmem_reg8(ref mem, reg r)
395 mem.emit(a, false, amd64, r, 0x88);
398 //MOV WORD <reg>, <regmem>
399 void I386::mov_reg_regmem16(reg r, I386::ref mem)
401 a(0x66);
402 mem.emit(a, false, amd64, r, 0x8B);
405 //MOV BYTE <reg>, <regmem>
406 void I386::mov_reg_regmem8(reg r, ref mem)
408 mem.emit(a, false, amd64, r, 0x8A);
411 //OR BYTE <regmem>, imm
412 void I386::or_regmem_imm8(I386::ref mem, uint8_t imm)
414 mem.emit(a, false, amd64, i386_r1, 0x80);
415 a(imm);
418 //AND BYTE <regmem>, imm
419 void I386::and_regmem_imm8(I386::ref mem, uint8_t imm)
421 mem.emit(a, false, amd64, i386_r4, 0x80);
422 a(imm);
425 //INC NWORD <regmem>
426 void I386::inc_regmem(I386::ref mem)
428 mem.emit(a, true, amd64, i386_r0, 0xFF);
431 //TEST BYTE <regmem>, imm
432 void I386::test_regmem_imm8(I386::ref mem, uint8_t imm)
434 mem.emit(a, false, amd64, i386_r0, 0xF6);
435 a(imm);
438 //CMP BYTE <regmem>, imm
439 void I386::cmp_regmem_imm8(I386::ref mem, uint8_t imm)
441 mem.emit(a, false, amd64, i386_r7, 0x80);
442 a(imm);
445 //MOV NWORD <reg>, <addr>.
446 void I386::mov_reg_addr(reg r, assembler::label& l)
448 if(amd64) a(0x48 + (r.hbit() ? 1 : 0));
449 a(0xB8 + r.lbits());
450 address(l);
453 //NEG BYTE <regmem>
454 void I386::neg_regmem8(ref mem)
456 mem.emit(a, false, amd64, i386_r3, 0xF6);
459 //ADD BYTE <regmem>, imm
460 void I386::add_regmem_imm8(ref mem, uint8_t imm)
462 mem.emit(a, false, amd64, i386_r0, 0x80);
463 a(imm);
466 //OR BYTE <regmem>, <reg>
467 void I386::or_regmem_reg8(ref mem, reg r)
469 mem.emit(a, false, amd64, r, 0x08);
472 //CALL <addr>
473 void I386::call_addr(assembler::label& l)
475 if(amd64) {
476 call_regmem(reg_rip[2]);
477 a(0xEB, 0x08); //JMP +8
478 address(l);
479 } else {
480 a(0xE8, assembler::relocation_tag(assembler::i386_reloc_rel32, l), assembler::pad_tag(4));
484 //CALL <regmem>
485 void I386::call_regmem(I386::ref mem)
487 mem.emit(a, false, amd64, i386_r2, 0xFF);
490 //JMP <regmem>
491 void I386::jmp_regmem(I386::ref mem)
493 mem.emit(a, false, amd64, i386_r4, 0xFF);
496 //SETNZ <regmem>
497 void I386::setnz_regmem(I386::ref mem)
499 mem.emit(a, false, amd64, i386_r0, 0x0F, 0x95);
502 //JNZ SHORT <label>
503 void I386::jnz_short(assembler::label& l)
505 a(0x75, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
508 //JZ SHORT <label>
509 void I386::jz_short(assembler::label& l)
511 a(0x74, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
514 //JMP SHORT <label>
515 void I386::jmp_short(assembler::label& l)
517 a(0xEB, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
520 //JMP LONG <label>
521 void I386::jmp_long(assembler::label& l)
523 a(0xE9, assembler::relocation_tag(assembler::i386_reloc_rel32, l), assembler::pad_tag(4));
526 //JZ LONG <label>
527 void I386::jz_long(assembler::label& l)
529 a(0x0F, 0x84, assembler::relocation_tag(assembler::i386_reloc_rel32, l), assembler::pad_tag(4));
532 //JAE SHORT <label>
533 void I386::jae_short(assembler::label& l)
535 a(0x73, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
538 //JAE LONG <label>
539 void I386::jae_long(assembler::label& l)
541 a(0x0F, 0x83, assembler::relocation_tag(assembler::i386_reloc_rel32, l), assembler::pad_tag(4));
544 void I386::ret()
546 a(0xC3);
549 void I386::address(assembler::label& l)
551 a(assembler::relocation_tag((amd64 ? assembler::i386_reloc_abs64 : assembler::i386_reloc_abs32), l),
552 assembler::pad_tag(wordsize()));