Lua: Don't lua_error() out of context with pending dtors
[lsnes.git] / src / library / assembler-intrinsics-i386.cpp
blobb5918eb0be42d4bc351432c0fb9447228397b10e
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)
87 I386::sib_scale_intermediate::sib_scale_intermediate(reg _idx, uint8_t _scale)
88 : index(_idx), scale(_scale)
92 I386::sib_scale_off_intermediate I386::sib_scale_intermediate::operator+(int32_t off) const
94 return sib_scale_off_intermediate(index, scale, off);
98 void check_register(I386::reg r, bool amd64)
100 if(!r.valid(amd64))
101 throw std::runtime_error("Illegal register");
104 void I386::label(assembler::label& l)
106 a._label(l);
109 I386::low::low(bool _need_amd64, uint8_t _rex_prefix, std::vector<uint8_t> _ref)
110 : needs_amd64(_need_amd64), rex(_rex_prefix), mref(_ref)
112 if(rex) needs_amd64 = true;
114 bool I386::low::need_amd64() { return needs_amd64; }
115 bool I386::low::has_rex() { return (rex != 0); }
116 uint8_t I386::low::rex_prefix() { return rex; }
117 std::vector<uint8_t> I386::low::bytes() { return mref; }
119 void I386::low::emit_rex(assembler::assembler& a)
121 if(has_rex()) a(rex);
124 void I386::low::emit_bytes(assembler::assembler& a)
126 for(auto i : mref) a(i);
129 I386::ref::ref()
133 I386::ref::ref(reg r)
135 if(!r.valid(true))
136 throw std::runtime_error("Illegal register");
137 needs_amd64 = r.hbit();
138 rex = r.hbit() ? 1 : 0;
139 mref.push_back(0xC0 + r.lbits());
142 I386::ref::ref(sib_scale_intermediate r)
144 *this = sib(reg_none, r.index, r.scale);
147 I386::ref::ref(sib_scale_off_intermediate r)
149 *this = sib(reg_none, r.index, r.scale, r.offset);
152 I386::ref I386::ref::reg_off(reg r, int32_t off)
154 if(!r.valid(true))
155 throw std::runtime_error("Illegal register");
156 bool need_off = (off != 0);
157 bool need_loff = (off < -128 || off > 127);
158 I386::ref x;
159 x.needs_amd64 = r.hbit();
160 x.rex = r.hbit() ? 1 : 0;
161 if(r.lbits() == 5)
162 need_off = true; //EBP and R13 always need offset.
163 uint8_t rtype = need_loff ? 2 : (need_off ? 1 : 0);
164 if(r.lbits() == 4) {
165 //SIB is required for these.
166 x.mref.push_back(0x04 + rtype * 0x40);
167 x.mref.push_back(0x24);
168 } else {
169 x.mref.push_back(r.lbits() + rtype * 0x40);
171 size_t bytes = x.mref.size();
172 if(need_loff) {
173 x.mref.resize(bytes + 4);
174 serialization::s32l(&x.mref[bytes], off);
175 } else if(need_off) {
176 x.mref.resize(bytes + 1);
177 serialization::s8l(&x.mref[bytes], off);
179 return x;
182 I386::ref I386::ref::rip_off(int32_t off)
184 I386::ref x;
185 x.needs_amd64 = true;
186 x.mref.resize(5);
187 x.mref[0] = 0x05;
188 serialization::s32l(&x.mref[1], off);
189 x.rex = 0;
190 return x;
193 I386::ref I386::ref::sib(reg base, reg index, uint8_t scale, int32_t off)
195 I386::ref x;
196 if(!base.is_none() && !base.valid(true))
197 throw std::runtime_error("Illegal base in SIB");
198 if((!index.is_none() && !index.valid(true)) || index.num() == 4)
199 throw std::runtime_error("Illegal index in SIB");
200 uint8_t ss = 0;
201 switch(scale) {
202 case 1: ss = 0; break;
203 case 2: ss = 1; break;
204 case 4: ss = 2; break;
205 case 8: ss = 3; break;
206 default: throw std::runtime_error("Illegal scale in SIB");
209 bool need_off = (off != 0);
210 bool need_loff = (off < -128 || off > 127);
211 if(base.is_none()) {
212 //Base is the offset.
213 x.mref.push_back(0x04); //SIB coming, no offset
214 x.mref.push_back(ss * 64 + index.lbits() + 5); //Base is "EBP".
215 need_loff = true;
216 } else {
217 if(base.num() == 5)
218 need_off = true; //This always needs offset.
219 uint8_t rtype = need_loff ? 2 : (need_off ? 1 : 0);
220 x.mref.push_back(0x04 + rtype * 0x40); //SIB coming.
221 x.mref.push_back(ss * 64 + index.lbits() * 8 + base.lbits());
223 x.rex = 0;
224 if(!base.is_none()) x.rex |= (base.hbit() ? 1 : 0);
225 if(!index.is_none()) x.rex |= (index.hbit() ? 2 : 0);
226 x.needs_amd64 = (x.rex != 0);
227 size_t bytes = x.mref.size();
228 if(need_loff) {
229 x.mref.resize(bytes + 4);
230 serialization::s32l(&x.mref[bytes], off);
231 } else if(need_off) {
232 x.mref.resize(bytes + 1);
233 serialization::s8l(&x.mref[bytes], off);
235 return x;
238 I386::low I386::ref::operator()(reg r, bool set_size_flag, bool amd64)
240 check_register(r, amd64);
241 auto c = mref;
242 c[0] |= r.lbits() * 8;
243 uint8_t _rex = rex;
244 if(r.hbit()) _rex+=4; //Set R
245 if(set_size_flag && amd64) _rex+=8; //Set S if needed.
246 if(_rex) _rex+=0x40; //Ret rex prefix bits.
247 if((_rex || needs_amd64) && !amd64)
248 throw std::runtime_error("Illegal memory reference for i386");
249 return low(needs_amd64, _rex, c);
252 void I386::ref::emit(assembler::assembler& a, bool set_size_flag, bool amd64, reg r, uint8_t op1)
254 auto xref = (*this)(r, set_size_flag, amd64);
255 xref.emit_rex(a);
256 a(op1);
257 xref.emit_bytes(a);
260 void I386::ref::emit(assembler::assembler& a, bool set_size_flag, bool amd64, reg r, uint8_t op1,
261 uint8_t op2)
263 auto xref = (*this)(r, set_size_flag, amd64);
264 xref.emit_rex(a);
265 a(op1, op2);
266 xref.emit_bytes(a);
269 I386::I386(assembler::assembler& _a, bool _amd64)
270 : a(_a), amd64(_amd64)
274 bool I386::is_amd64()
276 return amd64;
279 //Is i386?
280 bool I386::is_i386()
282 return !amd64;
285 //Get word size.
286 uint8_t I386::wordsize()
288 return amd64 ? 8 : 4;
291 //PUSH NWORD <reg>
292 void I386::push_reg(reg r)
294 check_register(r, amd64);
295 if(amd64 && r.hbit()) a(0x41);
296 a(0x50 + r.lbits());
298 //POP NWORD <reg>
299 void I386::pop_reg(reg r)
301 check_register(r, amd64);
302 if(amd64 && r.hbit()) a(0x41);
303 a(0x58 + r.lbits());
305 //MOV NWORD <reg>,<regmem>
306 void I386::mov_reg_regmem(reg r, I386::ref mem)
308 mem.emit(a, true, amd64, r, 0x8B);
310 //XOR NWORD <reg>,<regmem>
311 void I386::xor_reg_regmem(reg r, I386::ref mem)
313 mem.emit(a, true, amd64, r, 0x33);
316 //LEA <reg>,<mem>
317 void I386::lea_reg_mem(reg r, I386::ref mem)
319 mem.emit(a, true, amd64, r, 0x8D);
322 //AND NWORD <regmem>, imm
323 void I386::and_regmem_imm(I386::ref mem, int32_t imm)
325 mem.emit(a, true, amd64, i386_r4, 0x81);
326 a(assembler::vle<int32_t>(imm));
329 //SHL NWORD <regmem>, imm
330 void I386::shl_regmem_imm(I386::ref mem, uint8_t imm)
332 mem.emit(a, true, amd64, i386_r4, 0xC1);
333 a(imm);
336 //SHL BYTE <regmem>, imm
337 void I386::shl_regmem_imm8(ref mem, uint8_t imm)
339 mem.emit(a, true, amd64, i386_r4, 0xC0);
340 a(imm);
343 //ADD NWORD <regmem>, imm
344 void I386::add_regmem_imm(I386::ref mem, int32_t imm)
346 mem.emit(a, true, amd64, i386_r0, 0x81);
347 a(assembler::vle<int32_t>(imm));
350 //ADD NWORD <reg>,<regmem>
351 void I386::add_reg_regmem(reg r, I386::ref mem)
353 mem.emit(a, true, amd64, r, 0x03);
356 //MOV BYTE <regmem>, imm
357 void I386::mov_regmem_imm8(I386::ref mem, uint8_t imm)
359 mem.emit(a, false, amd64, i386_r0, 0xC6);
360 a(imm);
363 //MOV DWORD <regmem>, imm
364 void I386::mov_regmem_imm32(I386::ref mem, uint32_t imm)
366 mem.emit(a, false, amd64, i386_r0, 0xC7);
367 a(assembler::vle<uint32_t>(imm));
370 //MOV NWORD <reg>, imm.
371 void I386::mov_reg_imm(reg r, size_t imm)
373 check_register(r, amd64);
374 if(amd64) a(0x48 | (r.hbit() ? 1 : 0));
375 a(0xB8 + r.lbits());
376 a(assembler::vle<size_t>(imm));
379 //CMP NWORD <regmem>, imm
380 void I386::cmp_regmem_imm(I386::ref mem, int32_t imm)
382 mem.emit(a, true, amd64, i386_r7, 0x81);
383 a(assembler::vle<int32_t>(imm));
386 //MOV WORD <regmem>, <reg>
387 void I386::mov_regmem_reg16(I386::ref mem, reg r)
389 a(0x66);
390 mem.emit(a, false, amd64, r, 0x89);
393 //MOV BYTE <regmem>, <reg>
394 void I386::mov_regmem_reg8(ref mem, reg r)
396 mem.emit(a, false, amd64, r, 0x88);
399 //MOV WORD <reg>, <regmem>
400 void I386::mov_reg_regmem16(reg r, I386::ref mem)
402 a(0x66);
403 mem.emit(a, false, amd64, r, 0x8B);
406 //MOV BYTE <reg>, <regmem>
407 void I386::mov_reg_regmem8(reg r, ref mem)
409 mem.emit(a, false, amd64, r, 0x8A);
412 //OR BYTE <regmem>, imm
413 void I386::or_regmem_imm8(I386::ref mem, uint8_t imm)
415 mem.emit(a, false, amd64, i386_r1, 0x80);
416 a(imm);
419 //AND BYTE <regmem>, imm
420 void I386::and_regmem_imm8(I386::ref mem, uint8_t imm)
422 mem.emit(a, false, amd64, i386_r4, 0x80);
423 a(imm);
426 //INC NWORD <regmem>
427 void I386::inc_regmem(I386::ref mem)
429 mem.emit(a, true, amd64, i386_r0, 0xFF);
432 //TEST BYTE <regmem>, imm
433 void I386::test_regmem_imm8(I386::ref mem, uint8_t imm)
435 mem.emit(a, false, amd64, i386_r0, 0xF6);
436 a(imm);
439 //CMP BYTE <regmem>, imm
440 void I386::cmp_regmem_imm8(I386::ref mem, uint8_t imm)
442 mem.emit(a, false, amd64, i386_r7, 0x80);
443 a(imm);
446 //MOV NWORD <reg>, <addr>.
447 void I386::mov_reg_addr(reg r, assembler::label& l)
449 if(amd64) a(0x48 + (r.hbit() ? 1 : 0));
450 a(0xB8 + r.lbits());
451 address(l);
454 //NEG BYTE <regmem>
455 void I386::neg_regmem8(ref mem)
457 mem.emit(a, false, amd64, i386_r3, 0xF6);
460 //ADD BYTE <regmem>, imm
461 void I386::add_regmem_imm8(ref mem, uint8_t imm)
463 mem.emit(a, false, amd64, i386_r0, 0x80);
464 a(imm);
467 //OR BYTE <regmem>, <reg>
468 void I386::or_regmem_reg8(ref mem, reg r)
470 mem.emit(a, false, amd64, r, 0x08);
473 //CALL <addr>
474 void I386::call_addr(assembler::label& l)
476 if(amd64) {
477 call_regmem(reg_rip[2]);
478 a(0xEB, 0x08); //JMP +8
479 address(l);
480 } else {
481 a(0xE8, assembler::relocation_tag(assembler::i386_reloc_rel32, l), assembler::pad_tag(4));
485 //CALL <regmem>
486 void I386::call_regmem(I386::ref mem)
488 mem.emit(a, false, amd64, i386_r2, 0xFF);
491 //JMP <regmem>
492 void I386::jmp_regmem(I386::ref mem)
494 mem.emit(a, false, amd64, i386_r4, 0xFF);
497 //SETNZ <regmem>
498 void I386::setnz_regmem(I386::ref mem)
500 mem.emit(a, false, amd64, i386_r0, 0x0F, 0x95);
503 //JNZ SHORT <label>
504 void I386::jnz_short(assembler::label& l)
506 a(0x75, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
509 //JZ SHORT <label>
510 void I386::jz_short(assembler::label& l)
512 a(0x74, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
515 //JMP SHORT <label>
516 void I386::jmp_short(assembler::label& l)
518 a(0xEB, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
521 //JMP LONG <label>
522 void I386::jmp_long(assembler::label& l)
524 a(0xE9, assembler::relocation_tag(assembler::i386_reloc_rel32, l), assembler::pad_tag(4));
527 //JZ LONG <label>
528 void I386::jz_long(assembler::label& l)
530 a(0x0F, 0x84, assembler::relocation_tag(assembler::i386_reloc_rel32, l), assembler::pad_tag(4));
533 //JAE SHORT <label>
534 void I386::jae_short(assembler::label& l)
536 a(0x73, assembler::relocation_tag(assembler::i386_reloc_rel8, l), 0x00);
539 //JAE LONG <label>
540 void I386::jae_long(assembler::label& l)
542 a(0x0F, 0x83, assembler::relocation_tag(assembler::i386_reloc_rel32, l), assembler::pad_tag(4));
545 void I386::ret()
547 a(0xC3);
550 void I386::address(assembler::label& l)
552 a(assembler::relocation_tag((amd64 ? assembler::i386_reloc_abs64 : assembler::i386_reloc_abs32), l),
553 assembler::pad_tag(wordsize()));