Lua: Don't lua_error() out of context with pending dtors
[lsnes.git] / src / library / portctrl-parse-i386.cpp
blobe82c8fc87e2ce92eb754d53c294e13f4f4279844
1 #include "portctrl-data.hpp"
2 #include "portctrl-parse-asmgen.hpp"
3 #include "assembler-intrinsics-i386.hpp"
5 using namespace assembler_intrinsics;
7 namespace portctrl
9 namespace codegen
11 int calc_mask_bit(uint8_t mask)
13 switch(mask) {
14 case 0x01: return 0;
15 case 0x02: return 1;
16 case 0x04: return 2;
17 case 0x08: return 3;
18 case 0x10: return 4;
19 case 0x20: return 5;
20 case 0x40: return 6;
21 case 0x80: return 7;
22 default: return -1;
26 template<> void emit_serialize_prologue(I386& a, assembler::label_list& labels)
28 //Variable assignments:
29 //SI: State block.
30 //DX: Output buffer.
31 //AX: Write position counter.
33 //On amd64, the ABI places the parameters of serialize() into those registers, but on i386, the parameters
34 //are passed on stack. So load the parameters into correct registers. Also, save the registers beforehand.
35 if(a.is_i386()) {
36 a.push_reg(I386::reg_si);
37 a.push_reg(I386::reg_dx);
38 a.mov_reg_regmem(I386::reg_si, I386::reg_sp[16]);
39 a.mov_reg_regmem(I386::reg_dx, I386::reg_sp[20]);
41 //Set write position to 0.
42 a.xor_reg_regmem(I386::reg_ax, I386::reg_ax);
45 template<> void emit_serialize_button(I386& a, assembler::label_list& labels, int32_t offset, uint8_t mask,
46 uint8_t ch)
48 //Test the bit with specified mask on specified position. If set, set CL to 1, otherwise to 0.
49 a.test_regmem_imm8(I386::reg_si[offset], mask);
50 a.setnz_regmem(I386::reg_cx);
51 //Negate CL. This causes CL to become 0xFF or 0x00.
52 a.neg_regmem8(I386::reg_cx);
53 //AND CL with ch-'.' and add '.'. This results in ch if CL was 0xFF and '.' if CL was 0x00.
54 a.and_regmem_imm8(I386::reg_cx, ch - '.');
55 a.add_regmem_imm8(I386::reg_cx, '.');
56 //Write the byte to write position and advance write position.
57 a.mov_regmem_reg8(I386::reg_dx[I386::reg_ax], I386::reg_cx);
58 a.inc_regmem(I386::reg_ax);
61 template<> void emit_serialize_axis(I386& a, assembler::label_list& labels, int32_t offset)
63 //Get reference to write_axis_value() routine.
64 assembler::label& axisprint = labels.external((void*)write_axis_value);
66 //Save the state registers.
67 if(a.is_i386()) a.push_reg(I386::reg_di);
68 a.push_reg(I386::reg_si);
69 a.push_reg(I386::reg_dx);
70 a.push_reg(I386::reg_ax);
71 //Load the axis value from controller state (i386/amd64 is LE, so endianess is right).
72 a.mov_reg_regmem16(I386::reg_si, I386::reg_si[offset]);
73 //The high bits of SI are junk. Mask those off. This will become the second parameter of axis_print().
74 a.and_regmem_imm(I386::reg_si, 0xFFFF);
75 //Set first parameter of axis_print() to current write position.
76 a.lea_reg_mem(I386::reg_di, I386::reg_dx[I386::reg_ax]);
77 //If on i386, push the parameters into stack, as parameters are passed there.
78 if(a.is_i386()) a.push_reg(I386::reg_si);
79 if(a.is_i386()) a.push_reg(I386::reg_di);
80 //Call the axis_print() routine.
81 a.call_addr(axisprint);
82 //If on i386, clean up the passed parameters.
83 if(a.is_i386()) a.add_regmem_imm(I386::reg_sp, 8);
84 //Restore the state. We restore ax to cx, so we preserve the return value of axis_print().
85 a.pop_reg(I386::reg_cx);
86 a.pop_reg(I386::reg_dx);
87 a.pop_reg(I386::reg_si);
88 if(a.is_i386()) a.pop_reg(I386::reg_di);
89 //Add the old pointer to return value of axis_print() and make that the new pointer. Addition is commutative,
90 //so new = old + delta and new = delta + old are the same.
91 a.add_reg_regmem(I386::reg_ax, I386::reg_cx);
94 template<> void emit_serialize_pipe(I386& a, assembler::label_list& labels)
96 //Write '|' to write position and adavance write position.
97 a.mov_regmem_imm8(I386::reg_dx[I386::reg_ax], '|');
98 a.inc_regmem(I386::reg_ax);
101 template<> void emit_serialize_epilogue(I386& a, assembler::label_list& labels)
103 //If on i386, restore the saved registers.
104 if(a.is_i386()) a.pop_reg(I386::reg_dx);
105 if(a.is_i386()) a.pop_reg(I386::reg_si);
106 //Exit routine. The pointer in AX is correct for return value.
107 a.ret();
110 template<> void emit_deserialize_prologue(I386& a, assembler::label_list& labels)
112 //Variable assignments:
113 //SI: State block.
114 //DX: Input buffer.
115 //AX: read position counter.
117 //On amd64, the ABI places the parameters of deserialize() into those registers, but on i386, the parameters
118 //are passed on stack. So load the parameters into correct registers. Also, save the registers beforehand.
119 if(a.is_i386()) {
120 a.push_reg(I386::reg_si);
121 a.push_reg(I386::reg_dx);
122 a.mov_reg_regmem(I386::reg_si, I386::reg_sp[16]);
123 a.mov_reg_regmem(I386::reg_dx, I386::reg_sp[20]);
125 //Set read position to 0.
126 a.xor_reg_regmem(I386::reg_ax, I386::reg_ax);
129 template<> void emit_deserialize_clear_storage(I386& a, assembler::label_list& labels, int32_t size)
131 int32_t i;
132 //Write four zero bytes at once if there is space for so.
133 for(i = 0; i < size - 3; i += 4)
134 a.mov_regmem_imm32(I386::reg_si[i], 0);
135 //Write the rest one byte at a time.
136 for(; i < size; i++)
137 a.mov_regmem_imm8(I386::reg_si[i], 0);
140 template<> void emit_deserialize_button(I386& a, assembler::label_list& labels, int32_t offset,
141 uint8_t mask, assembler::label& next_pipe, assembler::label& end_deserialize)
143 assembler::label& clear_button = labels;
145 //Load the next input byte into CL.
146 a.mov_reg_regmem8(I386::reg_cx, I386::reg_dx[I386::reg_ax]);
147 //Check for special values '|', '\r', '\n', '\0'.
148 a.cmp_regmem_imm8(I386::reg_cx, '|');
149 a.jz_long(next_pipe);
150 a.cmp_regmem_imm8(I386::reg_cx, '\r');
151 a.jz_long(end_deserialize);
152 a.cmp_regmem_imm8(I386::reg_cx, '\n');
153 a.jz_long(end_deserialize);
154 a.cmp_regmem_imm8(I386::reg_cx, '\0');
155 a.jz_long(end_deserialize);
156 //Check for released values ' ' and '.'. If found, skip setting the bit.
157 a.cmp_regmem_imm8(I386::reg_cx, ' ');
158 a.jz_short(clear_button);
159 a.cmp_regmem_imm8(I386::reg_cx, '.');
160 a.jz_short(clear_button);
161 //Pressed. Set the button bit.
162 a.or_regmem_imm8(I386::reg_si[offset], mask);
163 a.label(clear_button);
164 //Advance the read pointer.
165 a.inc_regmem(I386::reg_ax);
168 template<> void emit_deserialize_axis(I386& a, assembler::label_list& labels, int32_t offset)
170 //Get reference to read_axis_value() routine.
171 assembler::label& axisread = labels.external((void*)read_axis_value);
173 //Save status registers.
174 if(a.is_i386()) a.push_reg(I386::reg_di);
175 a.push_reg(I386::reg_si);
176 a.push_reg(I386::reg_dx);
177 a.push_reg(I386::reg_ax);
178 //Set first parameter of read_axis_value() to start of serialization input buffer.
179 //Set the second parameter to be reference to saved read counter on stack. On i386, push the parameters
180 //to proper places.
181 a.mov_reg_regmem(I386::reg_di, I386::reg_dx);
182 a.mov_reg_regmem(I386::reg_si, I386::reg_sp);
183 if(a.is_i386()) a.push_reg(I386::reg_si);
184 if(a.is_i386()) a.push_reg(I386::reg_di);
185 //Call read_axis_value().
186 a.call_addr(axisread);
187 //Clean up the paraemters from stack (i386 only).
188 if(a.is_i386()) a.add_regmem_imm(I386::reg_sp, 8);
189 //Temporarily put the return value into CX.
190 a.mov_reg_regmem(I386::reg_cx, I386::reg_ax);
191 //Restore the status registers. Note that AX (read position) has been modified by reference.
192 a.pop_reg(I386::reg_ax);
193 a.pop_reg(I386::reg_dx);
194 a.pop_reg(I386::reg_si);
195 if(a.is_i386()) a.pop_reg(I386::reg_di);
196 //Write the axis value into controller state.
197 a.mov_regmem_reg16(I386::reg_si[offset], I386::reg_cx);
200 template<> void emit_deserialize_skip_until_pipe(I386& a, assembler::label_list& labels, assembler::label& next_pipe, assembler::label& deserialize_end)
202 assembler::label& loop = labels;
203 //While...
204 a.label(loop);
205 //Load next character.
206 a.mov_reg_regmem8(I386::reg_cx, I386::reg_dx[I386::reg_ax]);
207 //If it is '|', handle as pipe.
208 a.cmp_regmem_imm8(I386::reg_cx, '|');
209 a.jz_long(next_pipe);
210 //If it is '\r', \n' or '\0', handle as end of input.
211 a.cmp_regmem_imm8(I386::reg_cx, '\r');
212 a.jz_long(deserialize_end);
213 a.cmp_regmem_imm8(I386::reg_cx, '\n');
214 a.jz_long(deserialize_end);
215 a.cmp_regmem_imm8(I386::reg_cx, '\0');
216 a.jz_long(deserialize_end);
217 //Not interesting: Advance read position and go back to loop.
218 a.inc_regmem(I386::reg_ax);
219 a.jmp_short(loop);
222 template<> void emit_deserialize_skip(I386& a, assembler::label_list& labels)
224 //Increment the read position to skip the byte.
225 a.inc_regmem(I386::reg_ax);
228 template<> void emit_deserialize_special_blank(I386& a, assembler::label_list& labels)
230 //AX is the pending return value. Set that.
231 a.mov_reg_imm(I386::reg_ax, DESERIALIZE_SPECIAL_BLANK);
234 template<> void emit_deserialize_epilogue(I386& a, assembler::label_list& labels)
236 //Restore the saved registers on i386.
237 if(a.is_i386()) a.pop_reg(I386::reg_dx);
238 if(a.is_i386()) a.pop_reg(I386::reg_si);
239 //Exit routine. The pointer in AX is correct for return value (changed to DESERIALIZE_SPECIAL_BLANK if
240 //needed).
241 a.ret();
244 template<> void emit_read_prologue(I386& a, assembler::label_list& labels)
246 //Variable assignments:
247 //SI: State block.
248 //DX: Controller number
249 //CX: Button index.
250 //AX: Pending return value.
252 //Save registers if i386 and load the parameters from stack into variables.
253 if(a.is_i386()) {
254 a.push_reg(I386::reg_si);
255 a.push_reg(I386::reg_dx);
256 a.push_reg(I386::reg_cx);
257 a.mov_reg_regmem(I386::reg_si, I386::reg_sp[20]);
258 a.mov_reg_regmem(I386::reg_dx, I386::reg_sp[24]);
259 a.mov_reg_regmem(I386::reg_cx, I386::reg_sp[28]);
261 //Zero out the pending return value.
262 a.xor_reg_regmem(I386::reg_ax, I386::reg_ax);
265 template<> void emit_read_epilogue(I386& a, assembler::label_list& labels)
267 //Restore the saved registers on i386.
268 if(a.is_i386()) a.pop_reg(I386::reg_cx);
269 if(a.is_i386()) a.pop_reg(I386::reg_dx);
270 if(a.is_i386()) a.pop_reg(I386::reg_si);
271 //Exit routine. AX is the pending return.
272 a.ret();
275 template<> void emit_read_dispatch(I386& a, assembler::label_list& labels,
276 unsigned controllers, unsigned ilog2controls, assembler::label& end)
278 //Get reference to table after dispatch code.
279 assembler::label& table = labels;
280 //Is the controller number out of range? If yes, jump to epilogue (pending return register is 0).
281 a.cmp_regmem_imm(I386::reg_dx, controllers);
282 a.jae_long(end);
283 //Is the control number out of range? If yes, jump to epilogue (pending return register is 0).
284 a.cmp_regmem_imm(I386::reg_cx, 1 << ilog2controls);
285 a.jae_long(end);
286 //The numbers are in range. Compute 1-D index as controller * (2^ilog2controls) + control into DX.
287 a.shl_regmem_imm(I386::reg_dx, ilog2controls);
288 a.add_reg_regmem(I386::reg_dx, I386::reg_cx);
289 //Load base address of jump table to CX.
290 a.mov_reg_addr(I386::reg_cx, table);
291 //Jump to entry in position DX in table (doing words to bytes conversion).
292 a.jmp_regmem(I386::reg_cx[I386::reg_dx * a.wordsize()]);
293 //The table comes after this routine.
294 a.label(table);
297 template<> assembler::label& emit_read_label(I386& a, assembler::label_list& labels)
299 //Get reference to whatever code is called.
300 assembler::label& l = labels;
301 //Write address entry.
302 a.address(l);
303 //Return the newly created reference.
304 return l;
307 template<> void emit_read_label_bad(I386& a, assembler::label_list& labels, assembler::label& b)
309 //Write address entry for specified label.
310 a.address(b);
313 template<> void emit_read_button(I386& a, assembler::label_list& labels, assembler::label& l,
314 assembler::label& end, int32_t offset, uint8_t mask)
316 //Declare the label jumping into this code.
317 a.label(l);
318 //The pending return is 0, so we can just set low byte of it to 1 if button bit is set.
319 a.test_regmem_imm8(I386::reg_si[offset], mask);
320 a.setnz_regmem(I386::reg_ax); //Really AL.
321 //Jump to epilogue.
322 a.jmp_long(end);
325 template<> void emit_read_axis(I386& a, assembler::label_list& labels, assembler::label& l,
326 assembler::label& end, int32_t offset)
328 //Declare the label jumping into this code.
329 a.label(l);
330 //Just read the axis value. i386/amd64 is LE, so endianess is correct.
331 a.mov_reg_regmem16(I386::reg_ax, I386::reg_si[offset]);
332 //Jump to epilogue.
333 a.jmp_long(end);
336 template<> void emit_write_prologue(I386& a, assembler::label_list& labels)
338 //Variable assignments:
339 //SI: State block.
340 //DX: Controller number
341 //CX: Button index.
342 //AX: Value to write.
344 //Save registers if i386 and load the parameters from stack into variables.
345 if(a.is_i386()) {
346 a.push_reg(I386::reg_si);
347 a.push_reg(I386::reg_dx);
348 a.push_reg(I386::reg_cx);
349 a.push_reg(I386::reg_ax);
350 a.mov_reg_regmem(I386::reg_si, I386::reg_sp[24]);
351 a.mov_reg_regmem(I386::reg_dx, I386::reg_sp[28]);
352 a.mov_reg_regmem(I386::reg_cx, I386::reg_sp[32]);
353 a.mov_reg_regmem(I386::reg_ax, I386::reg_sp[36]);
354 } else {
355 //On amd64, the fifth parameter is in r8. Copy it to ax to be consistent with i386 case.
356 a.mov_reg_regmem(I386::reg_ax, I386::reg_r8);
360 template<> void emit_write_epilogue(I386& a, assembler::label_list& labels)
362 //Restore saved registers on i386.
363 if(a.is_i386()) a.pop_reg(I386::reg_ax);
364 if(a.is_i386()) a.pop_reg(I386::reg_cx);
365 if(a.is_i386()) a.pop_reg(I386::reg_dx);
366 if(a.is_i386()) a.pop_reg(I386::reg_si);
367 //Return. This function has no return value.
368 a.ret();
371 template<> void emit_write_button(I386& a, assembler::label_list& labels, assembler::label& l,
372 assembler::label& end, int32_t offset, uint8_t mask)
374 //See if mask can be done as shift.
375 int mask_bit = calc_mask_bit(mask);
376 //Label jumping to this code.
377 a.label(l);
378 //Is the write value nonzero? If not, set AL to 1 (otherwise set to 0).
379 //We don't need write value after this, so freely trash it.
380 a.cmp_regmem_imm(I386::reg_ax, 0);
381 a.setnz_regmem(I386::reg_ax);
382 //Set AL to be mask if write value was nonzero.
383 if(mask_bit >= 0) {
384 //Shift the 0 or 1 from LSB to correct place.
385 a.shl_regmem_imm8(I386::reg_ax, mask_bit);
386 } else {
387 //Negate the number to 0xFF or 0x00 and then mask the bits not in mask, giving either mask or 0.
388 a.neg_regmem8(I386::reg_ax);
389 a.and_regmem_imm8(I386::reg_ax, mask);
391 //Clear the bits in mask.
392 a.and_regmem_imm8(I386::reg_si[offset], ~mask);
393 //If needed, set the bits back.
394 a.or_regmem_reg8(I386::reg_si[offset], I386::reg_ax);
395 //Jump to epilogue.
396 a.jmp_long(end);
399 template<> void emit_write_axis(I386& a, assembler::label_list& labels, assembler::label& l,
400 assembler::label& end, int32_t offset)
402 //Label jumping to this code.
403 a.label(l);
404 //Write the write value to axis value. i386/amd64 is LE, so endianess is correct.
405 a.mov_regmem_reg16(I386::reg_si[offset], I386::reg_ax);
406 //Jump to epilogue.
407 a.jmp_long(end);