1 #include "controller-data.hpp"
2 #include "controller-parse-asmgen.hpp"
3 #include "assembler-intrinsics-i386.hpp"
5 using namespace assembler_intrinsics
;
9 int calc_mask_bit(uint8_t mask
)
24 template<> void emit_serialize_prologue(I386
& a
, assembler::label_list
& labels
)
26 //Variable assignments:
29 //AX: Write position counter.
31 //On amd64, the ABI places the parameters of serialize() into those registers, but on i386, the parameters
32 //are passed on stack. So load the parameters into correct registers. Also, save the registers beforehand.
34 a
.push_reg(I386::reg_si
);
35 a
.push_reg(I386::reg_dx
);
36 a
.mov_reg_regmem(I386::reg_si
, I386::reg_sp
[16]);
37 a
.mov_reg_regmem(I386::reg_dx
, I386::reg_sp
[20]);
39 //Set write position to 0.
40 a
.xor_reg_regmem(I386::reg_ax
, I386::reg_ax
);
43 template<> void emit_serialize_button(I386
& a
, assembler::label_list
& labels
, int32_t offset
, uint8_t mask
,
46 //Test the bit with specified mask on specified position. If set, set CL to 1, otherwise to 0.
47 a
.test_regmem_imm8(I386::reg_si
[offset
], mask
);
48 a
.setnz_regmem(I386::reg_cx
);
49 //Negate CL. This causes CL to become 0xFF or 0x00.
50 a
.neg_regmem8(I386::reg_cx
);
51 //AND CL with ch-'.' and add '.'. This results in ch if CL was 0xFF and '.' if CL was 0x00.
52 a
.and_regmem_imm8(I386::reg_cx
, ch
- '.');
53 a
.add_regmem_imm8(I386::reg_cx
, '.');
54 //Write the byte to write position and advance write position.
55 a
.mov_regmem_reg8(I386::reg_dx
[I386::reg_ax
], I386::reg_cx
);
56 a
.inc_regmem(I386::reg_ax
);
59 template<> void emit_serialize_axis(I386
& a
, assembler::label_list
& labels
, int32_t offset
)
61 //Get reference to write_axis_value() routine.
62 assembler::label
& axisprint
= labels
.external((void*)write_axis_value
);
64 //Save the state registers.
65 if(a
.is_i386()) a
.push_reg(I386::reg_di
);
66 a
.push_reg(I386::reg_si
);
67 a
.push_reg(I386::reg_dx
);
68 a
.push_reg(I386::reg_ax
);
69 //Load the axis value from controller state (i386/amd64 is LE, so endianess is right).
70 a
.mov_reg_regmem16(I386::reg_si
, I386::reg_si
[offset
]);
71 //The high bits of SI are junk. Mask those off. This will become the second parameter of axis_print().
72 a
.and_regmem_imm(I386::reg_si
, 0xFFFF);
73 //Set first parameter of axis_print() to current write position.
74 a
.lea_reg_mem(I386::reg_di
, I386::reg_dx
[I386::reg_ax
]);
75 //If on i386, push the parameters into stack, as parameters are passed there.
76 if(a
.is_i386()) a
.push_reg(I386::reg_si
);
77 if(a
.is_i386()) a
.push_reg(I386::reg_di
);
78 //Call the axis_print() routine.
79 a
.call_addr(axisprint
);
80 //If on i386, clean up the passed parameters.
81 if(a
.is_i386()) a
.add_regmem_imm(I386::reg_sp
, 8);
82 //Restore the state. We restore ax to cx, so we preserve the return value of axis_print().
83 a
.pop_reg(I386::reg_cx
);
84 a
.pop_reg(I386::reg_dx
);
85 a
.pop_reg(I386::reg_si
);
86 if(a
.is_i386()) a
.pop_reg(I386::reg_di
);
87 //Add the old pointer to return value of axis_print() and make that the new pointer. Addition is commutative,
88 //so new = old + delta and new = delta + old are the same.
89 a
.add_reg_regmem(I386::reg_ax
, I386::reg_cx
);
92 template<> void emit_serialize_pipe(I386
& a
, assembler::label_list
& labels
)
94 //Write '|' to write position and adavance write position.
95 a
.mov_regmem_imm8(I386::reg_dx
[I386::reg_ax
], '|');
96 a
.inc_regmem(I386::reg_ax
);
99 template<> void emit_serialize_epilogue(I386
& a
, assembler::label_list
& labels
)
101 //If on i386, restore the saved registers.
102 if(a
.is_i386()) a
.pop_reg(I386::reg_dx
);
103 if(a
.is_i386()) a
.pop_reg(I386::reg_si
);
104 //Exit routine. The pointer in AX is correct for return value.
108 template<> void emit_deserialize_prologue(I386
& a
, assembler::label_list
& labels
)
110 //Variable assignments:
113 //AX: read position counter.
115 //On amd64, the ABI places the parameters of deserialize() into those registers, but on i386, the parameters
116 //are passed on stack. So load the parameters into correct registers. Also, save the registers beforehand.
118 a
.push_reg(I386::reg_si
);
119 a
.push_reg(I386::reg_dx
);
120 a
.mov_reg_regmem(I386::reg_si
, I386::reg_sp
[16]);
121 a
.mov_reg_regmem(I386::reg_dx
, I386::reg_sp
[20]);
123 //Set read position to 0.
124 a
.xor_reg_regmem(I386::reg_ax
, I386::reg_ax
);
127 template<> void emit_deserialize_clear_storage(I386
& a
, assembler::label_list
& labels
, int32_t size
)
130 //Write four zero bytes at once if there is space for so.
131 for(i
= 0; i
< size
- 3; i
+= 4)
132 a
.mov_regmem_imm32(I386::reg_si
[i
], 0);
133 //Write the rest one byte at a time.
135 a
.mov_regmem_imm8(I386::reg_si
[i
], 0);
138 template<> void emit_deserialize_button(I386
& a
, assembler::label_list
& labels
, int32_t offset
,
139 uint8_t mask
, assembler::label
& next_pipe
, assembler::label
& end_deserialize
)
141 assembler::label
& clear_button
= labels
;
143 //Load the next input byte into CL.
144 a
.mov_reg_regmem8(I386::reg_cx
, I386::reg_dx
[I386::reg_ax
]);
145 //Check for special values '|', '\r', '\n', '\0'.
146 a
.cmp_regmem_imm8(I386::reg_cx
, '|');
147 a
.jz_long(next_pipe
);
148 a
.cmp_regmem_imm8(I386::reg_cx
, '\r');
149 a
.jz_long(end_deserialize
);
150 a
.cmp_regmem_imm8(I386::reg_cx
, '\n');
151 a
.jz_long(end_deserialize
);
152 a
.cmp_regmem_imm8(I386::reg_cx
, '\0');
153 a
.jz_long(end_deserialize
);
154 //Check for released values ' ' and '.'. If found, skip setting the bit.
155 a
.cmp_regmem_imm8(I386::reg_cx
, ' ');
156 a
.jz_short(clear_button
);
157 a
.cmp_regmem_imm8(I386::reg_cx
, '.');
158 a
.jz_short(clear_button
);
159 //Pressed. Set the button bit.
160 a
.or_regmem_imm8(I386::reg_si
[offset
], mask
);
161 a
.label(clear_button
);
162 //Advance the read pointer.
163 a
.inc_regmem(I386::reg_ax
);
166 template<> void emit_deserialize_axis(I386
& a
, assembler::label_list
& labels
, int32_t offset
)
168 //Get reference to read_axis_value() routine.
169 assembler::label
& axisread
= labels
.external((void*)read_axis_value
);
171 //Save status registers.
172 if(a
.is_i386()) a
.push_reg(I386::reg_di
);
173 a
.push_reg(I386::reg_si
);
174 a
.push_reg(I386::reg_dx
);
175 a
.push_reg(I386::reg_ax
);
176 //Set first parameter of read_axis_value() to start of serialization input buffer.
177 //Set the second parameter to be reference to saved read counter on stack. On i386, push the parameters
179 a
.mov_reg_regmem(I386::reg_di
, I386::reg_dx
);
180 a
.mov_reg_regmem(I386::reg_si
, I386::reg_sp
);
181 if(a
.is_i386()) a
.push_reg(I386::reg_si
);
182 if(a
.is_i386()) a
.push_reg(I386::reg_di
);
183 //Call read_axis_value().
184 a
.call_addr(axisread
);
185 //Clean up the paraemters from stack (i386 only).
186 if(a
.is_i386()) a
.add_regmem_imm(I386::reg_sp
, 8);
187 //Temporarily put the return value into CX.
188 a
.mov_reg_regmem(I386::reg_cx
, I386::reg_ax
);
189 //Restore the status registers. Note that AX (read position) has been modified by reference.
190 a
.pop_reg(I386::reg_ax
);
191 a
.pop_reg(I386::reg_dx
);
192 a
.pop_reg(I386::reg_si
);
193 if(a
.is_i386()) a
.pop_reg(I386::reg_di
);
194 //Write the axis value into controller state.
195 a
.mov_regmem_reg16(I386::reg_si
[offset
], I386::reg_cx
);
198 template<> void emit_deserialize_skip_until_pipe(I386
& a
, assembler::label_list
& labels
, assembler::label
& next_pipe
, assembler::label
& deserialize_end
)
200 assembler::label
& loop
= labels
;
203 //Load next character.
204 a
.mov_reg_regmem8(I386::reg_cx
, I386::reg_dx
[I386::reg_ax
]);
205 //If it is '|', handle as pipe.
206 a
.cmp_regmem_imm8(I386::reg_cx
, '|');
207 a
.jz_long(next_pipe
);
208 //If it is '\r', \n' or '\0', handle as end of input.
209 a
.cmp_regmem_imm8(I386::reg_cx
, '\r');
210 a
.jz_long(deserialize_end
);
211 a
.cmp_regmem_imm8(I386::reg_cx
, '\n');
212 a
.jz_long(deserialize_end
);
213 a
.cmp_regmem_imm8(I386::reg_cx
, '\0');
214 a
.jz_long(deserialize_end
);
215 //Not interesting: Advance read position and go back to loop.
216 a
.inc_regmem(I386::reg_ax
);
220 template<> void emit_deserialize_skip(I386
& a
, assembler::label_list
& labels
)
222 //Increment the read position to skip the byte.
223 a
.inc_regmem(I386::reg_ax
);
226 template<> void emit_deserialize_special_blank(I386
& a
, assembler::label_list
& labels
)
228 //AX is the pending return value. Set that.
229 a
.mov_reg_imm(I386::reg_ax
, DESERIALIZE_SPECIAL_BLANK
);
232 template<> void emit_deserialize_epilogue(I386
& a
, assembler::label_list
& labels
)
234 //Restore the saved registers on i386.
235 if(a
.is_i386()) a
.pop_reg(I386::reg_dx
);
236 if(a
.is_i386()) a
.pop_reg(I386::reg_si
);
237 //Exit routine. The pointer in AX is correct for return value (changed to DESERIALIZE_SPECIAL_BLANK if
242 template<> void emit_read_prologue(I386
& a
, assembler::label_list
& labels
)
244 //Variable assignments:
246 //DX: Controller number
248 //AX: Pending return value.
250 //Save registers if i386 and load the parameters from stack into variables.
252 a
.push_reg(I386::reg_si
);
253 a
.push_reg(I386::reg_dx
);
254 a
.push_reg(I386::reg_cx
);
255 a
.mov_reg_regmem(I386::reg_si
, I386::reg_sp
[20]);
256 a
.mov_reg_regmem(I386::reg_dx
, I386::reg_sp
[24]);
257 a
.mov_reg_regmem(I386::reg_cx
, I386::reg_sp
[28]);
259 //Zero out the pending return value.
260 a
.xor_reg_regmem(I386::reg_ax
, I386::reg_ax
);
263 template<> void emit_read_epilogue(I386
& a
, assembler::label_list
& labels
)
265 //Restore the saved registers on i386.
266 if(a
.is_i386()) a
.pop_reg(I386::reg_cx
);
267 if(a
.is_i386()) a
.pop_reg(I386::reg_dx
);
268 if(a
.is_i386()) a
.pop_reg(I386::reg_si
);
269 //Exit routine. AX is the pending return.
273 template<> void emit_read_dispatch(I386
& a
, assembler::label_list
& labels
,
274 unsigned controllers
, unsigned ilog2controls
, assembler::label
& end
)
276 //Get reference to table after dispatch code.
277 assembler::label
& table
= labels
;
278 //Is the controller number out of range? If yes, jump to epilogue (pending return register is 0).
279 a
.cmp_regmem_imm(I386::reg_dx
, controllers
);
281 //Is the control number out of range? If yes, jump to epilogue (pending return register is 0).
282 a
.cmp_regmem_imm(I386::reg_cx
, 1 << ilog2controls
);
284 //The numbers are in range. Compute 1-D index as controller * (2^ilog2controls) + control into DX.
285 a
.shl_regmem_imm(I386::reg_dx
, ilog2controls
);
286 a
.add_reg_regmem(I386::reg_dx
, I386::reg_cx
);
287 //Load base address of jump table to CX.
288 a
.mov_reg_addr(I386::reg_cx
, table
);
289 //Jump to entry in position DX in table (doing words to bytes conversion).
290 a
.jmp_regmem(I386::reg_cx
[I386::reg_dx
* a
.wordsize()]);
291 //The table comes after this routine.
295 template<> assembler::label
& emit_read_label(I386
& a
, assembler::label_list
& labels
)
297 //Get reference to whatever code is called.
298 assembler::label
& l
= labels
;
299 //Write address entry.
301 //Return the newly created reference.
305 template<> void emit_read_label_bad(I386
& a
, assembler::label_list
& labels
, assembler::label
& b
)
307 //Write address entry for specified label.
311 template<> void emit_read_button(I386
& a
, assembler::label_list
& labels
, assembler::label
& l
,
312 assembler::label
& end
, int32_t offset
, uint8_t mask
)
314 //Declare the label jumping into this code.
316 //The pending return is 0, so we can just set low byte of it to 1 if button bit is set.
317 a
.test_regmem_imm8(I386::reg_si
[offset
], mask
);
318 a
.setnz_regmem(I386::reg_ax
); //Really AL.
323 template<> void emit_read_axis(I386
& a
, assembler::label_list
& labels
, assembler::label
& l
,
324 assembler::label
& end
, int32_t offset
)
326 //Declare the label jumping into this code.
328 //Just read the axis value. i386/amd64 is LE, so endianess is correct.
329 a
.mov_reg_regmem16(I386::reg_ax
, I386::reg_si
[offset
]);
334 template<> void emit_write_prologue(I386
& a
, assembler::label_list
& labels
)
336 //Variable assignments:
338 //DX: Controller number
340 //AX: Value to write.
342 //Save registers if i386 and load the parameters from stack into variables.
344 a
.push_reg(I386::reg_si
);
345 a
.push_reg(I386::reg_dx
);
346 a
.push_reg(I386::reg_cx
);
347 a
.push_reg(I386::reg_ax
);
348 a
.mov_reg_regmem(I386::reg_si
, I386::reg_sp
[24]);
349 a
.mov_reg_regmem(I386::reg_dx
, I386::reg_sp
[28]);
350 a
.mov_reg_regmem(I386::reg_cx
, I386::reg_sp
[32]);
351 a
.mov_reg_regmem(I386::reg_ax
, I386::reg_sp
[36]);
353 //On amd64, the fifth parameter is in r8. Copy it to ax to be consistent with i386 case.
354 a
.mov_reg_regmem(I386::reg_ax
, I386::reg_r8
);
358 template<> void emit_write_epilogue(I386
& a
, assembler::label_list
& labels
)
360 //Restore saved registers on i386.
361 if(a
.is_i386()) a
.pop_reg(I386::reg_ax
);
362 if(a
.is_i386()) a
.pop_reg(I386::reg_cx
);
363 if(a
.is_i386()) a
.pop_reg(I386::reg_dx
);
364 if(a
.is_i386()) a
.pop_reg(I386::reg_si
);
365 //Return. This function has no return value.
369 template<> void emit_write_button(I386
& a
, assembler::label_list
& labels
, assembler::label
& l
,
370 assembler::label
& end
, int32_t offset
, uint8_t mask
)
372 //See if mask can be done as shift.
373 int mask_bit
= calc_mask_bit(mask
);
374 //Label jumping to this code.
376 //Is the write value nonzero? If not, set AL to 1 (otherwise set to 0).
377 //We don't need write value after this, so freely trash it.
378 a
.cmp_regmem_imm(I386::reg_ax
, 0);
379 a
.setnz_regmem(I386::reg_ax
);
380 //Set AL to be mask if write value was nonzero.
382 //Shift the 0 or 1 from LSB to correct place.
383 a
.shl_regmem_imm8(I386::reg_ax
, mask_bit
);
385 //Negate the number to 0xFF or 0x00 and then mask the bits not in mask, giving either mask or 0.
386 a
.neg_regmem8(I386::reg_ax
);
387 a
.and_regmem_imm8(I386::reg_ax
, mask
);
389 //Clear the bits in mask.
390 a
.and_regmem_imm8(I386::reg_si
[offset
], ~mask
);
391 //If needed, set the bits back.
392 a
.or_regmem_reg8(I386::reg_si
[offset
], I386::reg_ax
);
397 template<> void emit_write_axis(I386
& a
, assembler::label_list
& labels
, assembler::label
& l
,
398 assembler::label
& end
, int32_t offset
)
400 //Label jumping to this code.
402 //Write the write value to axis value. i386/amd64 is LE, so endianess is correct.
403 a
.mov_regmem_reg16(I386::reg_si
[offset
], I386::reg_ax
);