1 #include "portctrl-data.hpp"
2 #include "portctrl-parse-asmgen.hpp"
3 #include "assembler-intrinsics-i386.hpp"
5 using namespace assembler_intrinsics
;
11 int calc_mask_bit(uint8_t mask
)
26 template<> void emit_serialize_prologue(I386
& a
, assembler::label_list
& labels
)
28 //Variable assignments:
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.
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
,
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.
110 template<> void emit_deserialize_prologue(I386
& a
, assembler::label_list
& labels
)
112 //Variable assignments:
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.
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
)
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.
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
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
;
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
);
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
244 template<> void emit_read_prologue(I386
& a
, assembler::label_list
& labels
)
246 //Variable assignments:
248 //DX: Controller number
250 //AX: Pending return value.
252 //Save registers if i386 and load the parameters from stack into variables.
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.
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
);
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
);
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.
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.
303 //Return the newly created reference.
307 template<> void emit_read_label_bad(I386
& a
, assembler::label_list
& labels
, assembler::label
& b
)
309 //Write address entry for specified label.
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.
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.
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.
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
]);
336 template<> void emit_write_prologue(I386
& a
, assembler::label_list
& labels
)
338 //Variable assignments:
340 //DX: Controller number
342 //AX: Value to write.
344 //Save registers if i386 and load the parameters from stack into variables.
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]);
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.
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.
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.
384 //Shift the 0 or 1 from LSB to correct place.
385 a
.shl_regmem_imm8(I386::reg_ax
, mask_bit
);
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
);
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.
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
);