4 * Provides the implementation for the ia32 ABI variant
5 * (eg prologue, epilogue, and calling conventions). Stack frame is
6 * aligned on 16-byte boundaries for the benefit of OS X (which
9 * Copyright (c) 2007 Nathan Keynes.
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
22 #ifndef lxdream_ia32mac_H
23 #define lxdream_ia32mac_H 1
25 #define load_ptr( reg, ptr ) load_imm32( reg, (uint32_t)ptr );
28 * Note: clobbers EAX to make the indirect call - this isn't usually
29 * a problem since the callee will usually clobber it anyway.
31 static inline void call_func0( void *ptr )
37 static inline void call_func1( void *ptr, int arg1 )
40 MOV_r32_r32( arg1, R_EAX );
45 static inline void call_func1_r32( int addr_reg, int arg1 )
48 MOV_r32_r32( arg1, R_EAX );
53 static inline void call_func1_r32ind( int preg, uint32_t disp32, int arg1 )
56 MOV_r32_r32( arg1, R_EAX );
58 CALL_r32ind(preg, disp32);
61 static inline void call_func2( void *ptr, int arg1, int arg2 )
64 MOV_r32_r32( arg2, R_EDX );
67 MOV_r32_r32( arg1, R_EAX );
72 static inline void call_func2_r32( int addr_reg, int arg1, int arg2 )
75 MOV_r32_r32( arg2, R_EDX );
78 MOV_r32_r32( arg1, R_EAX );
83 static inline void call_func2_r32ind( int preg, uint32_t disp32, int arg1, int arg2 )
86 MOV_r32_r32( arg2, R_EDX );
89 MOV_r32_r32( arg1, R_EAX );
91 CALL_r32ind(preg, disp32);
96 static inline void call_func1_exc( void *ptr, int arg1, int pc )
99 MOV_r32_r32( arg1, R_EAX );
101 load_exc_backpatch(R_EDX);
105 static inline void call_func2_exc( void *ptr, int arg1, int arg2, int pc )
107 if( arg2 != R_EDX ) {
108 MOV_r32_r32( arg2, R_EDX );
110 if( arg1 != R_EAX ) {
111 MOV_r32_r32( arg1, R_EAX );
113 load_exc_backpatch(R_ECX);
118 * Write a double (64-bit) value into memory, with the first word in arg2a, and
119 * the second in arg2b
121 static inline void MEM_WRITE_DOUBLE( int addr, int arg2a, int arg2b )
123 MOV_r32_esp8(addr, 0);
124 MOV_r32_esp8(arg2b, 4);
125 call_func2(sh4_write_long, addr, arg2a);
126 MOV_esp8_r32(0, R_EAX);
127 MOV_esp8_r32(4, R_EDX);
128 ADD_imm8s_r32(4, R_EAX);
129 call_func0(sh4_write_long);
133 * Read a double (64-bit) value from memory, writing the first word into arg2a
134 * and the second into arg2b. The addr must not be in EAX
136 static inline void MEM_READ_DOUBLE( int addr, int arg2a, int arg2b )
138 MOV_r32_esp8(addr, 0);
139 call_func1(sh4_read_long, addr);
140 MOV_r32_esp8(R_EAX, 4);
141 MOV_esp8_r32(0, R_EAX);
142 ADD_imm8s_r32(4, R_EAX);
143 call_func0(sh4_read_long);
144 if( arg2b != R_EAX ) {
145 MOV_r32_r32(R_EAX, arg2b);
147 MOV_esp8_r32(4, arg2a);
150 static inline void call_func1( void *ptr, int arg1 )
152 SUB_imm8s_r32( 12, R_ESP );
155 ADD_imm8s_r32( 16, R_ESP );
158 static inline void call_func2( void *ptr, int arg1, int arg2 )
160 SUB_imm8s_r32( 8, R_ESP );
164 ADD_imm8s_r32( 16, R_ESP );
168 * Write a double (64-bit) value into memory, with the first word in arg2a, and
169 * the second in arg2b
171 static inline void MEM_WRITE_DOUBLE( int addr, int arg2a, int arg2b )
173 SUB_imm8s_r32( 8, R_ESP );
175 LEA_r32disp8_r32( addr, 4, arg2b );
177 SUB_imm8s_r32( 8, R_ESP );
180 CALL_ptr(sh4_write_long);
181 ADD_imm8s_r32( 16, R_ESP );
182 CALL_ptr(sh4_write_long);
183 ADD_imm8s_r32( 16, R_ESP );
187 * Read a double (64-bit) value from memory, writing the first word into arg2a
188 * and the second into arg2b. The addr must not be in EAX
190 static inline void MEM_READ_DOUBLE( int addr, int arg2a, int arg2b )
192 SUB_imm8s_r32( 12, R_ESP );
194 CALL_ptr(sh4_read_long);
195 MOV_r32_esp8(R_EAX, 4);
196 ADD_imm8s_esp8(4, 0);
197 CALL_ptr(sh4_read_long);
198 if( arg2b != R_EAX ) {
199 MOV_r32_r32( R_EAX, arg2b );
201 MOV_esp8_r32( 4, arg2a );
202 ADD_imm8s_r32( 16, R_ESP );
208 * Emit the 'start of block' assembly. Sets up the stack frame and save
210 * Allocates 8 bytes for local variables, which also has the convenient
211 * side-effect of aligning the stack.
216 load_ptr( R_EBP, ((uint8_t *)&sh4r) + 128 );
218 SUB_imm8s_r32( 4, R_ESP );
221 static inline void exit_block( )
223 ADD_imm8s_r32( 4, R_ESP );
230 * Exit the block with sh4r.new_pc written with the target pc
232 void exit_block_pcset( sh4addr_t pc )
234 load_imm32( R_ECX, ((pc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
235 ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) ); // 6
236 load_spreg( R_EAX, R_PC );
237 if( sh4_x86.tlb_on ) {
238 call_func1(xlat_get_code_by_vma,R_EAX);
240 call_func1(xlat_get_code,R_EAX);
246 * Exit the block with sh4r.new_pc written with the target pc
248 void exit_block_newpcset( sh4addr_t pc )
250 load_imm32( R_ECX, ((pc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
251 ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) ); // 6
252 load_spreg( R_EAX, R_NEW_PC );
253 store_spreg( R_EAX, R_PC );
254 if( sh4_x86.tlb_on ) {
255 call_func1(xlat_get_code_by_vma,R_EAX);
257 call_func1(xlat_get_code,R_EAX);
264 * Exit the block to an absolute PC
266 void exit_block_abs( sh4addr_t pc, sh4addr_t endpc )
268 load_imm32( R_ECX, pc ); // 5
269 store_spreg( R_ECX, REG_OFFSET(pc) ); // 3
270 if( IS_IN_ICACHE(pc) ) {
271 MOV_moff32_EAX( xlat_get_lut_entry(GET_ICACHE_PHYS(pc)) ); // 5
272 AND_imm8s_r32( 0xFC, R_EAX ); // 3
273 } else if( sh4_x86.tlb_on ) {
274 call_func1(xlat_get_code_by_vma,R_ECX);
276 call_func1(xlat_get_code,R_ECX);
278 load_imm32( R_ECX, ((endpc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
279 ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) ); // 6
284 * Exit the block to a relative PC
286 void exit_block_rel( sh4addr_t pc, sh4addr_t endpc )
288 load_imm32( R_ECX, pc - sh4_x86.block_start_pc ); // 5
289 ADD_sh4r_r32( R_PC, R_ECX );
290 store_spreg( R_ECX, REG_OFFSET(pc) ); // 3
291 if( IS_IN_ICACHE(pc) ) {
292 MOV_moff32_EAX( xlat_get_lut_entry(GET_ICACHE_PHYS(pc)) ); // 5
293 AND_imm8s_r32( 0xFC, R_EAX ); // 3
294 } else if( sh4_x86.tlb_on ) {
295 call_func1(xlat_get_code_by_vma,R_ECX);
297 call_func1(xlat_get_code,R_ECX);
299 load_imm32( R_ECX, ((endpc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
300 ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) ); // 6
305 * Write the block trailer (exception handling block)
307 void sh4_translate_end_block( sh4addr_t pc ) {
308 if( sh4_x86.branch_taken == FALSE ) {
309 // Didn't exit unconditionally already, so write the termination here
310 exit_block_rel( pc, pc );
312 if( sh4_x86.backpatch_posn != 0 ) {
315 uint8_t *end_ptr = xlat_output;
316 MOV_r32_r32( R_EDX, R_ECX );
317 ADD_r32_r32( R_EDX, R_ECX );
318 ADD_r32_sh4r( R_ECX, R_PC );
319 MOV_moff32_EAX( &sh4_cpu_period );
321 ADD_r32_sh4r( R_EAX, REG_OFFSET(slice_cycle) );
324 call_func1( sh4_raise_exception, R_EAX );
325 load_spreg( R_EAX, R_PC );
326 if( sh4_x86.tlb_on ) {
327 call_func1(xlat_get_code_by_vma,R_EAX);
329 call_func1(xlat_get_code,R_EAX);
333 // Exception already raised - just cleanup
334 uint8_t *preexc_ptr = xlat_output;
335 MOV_r32_r32( R_EDX, R_ECX );
336 ADD_r32_r32( R_EDX, R_ECX );
337 ADD_r32_sh4r( R_ECX, R_SPC );
338 MOV_moff32_EAX( &sh4_cpu_period );
340 ADD_r32_sh4r( R_EAX, REG_OFFSET(slice_cycle) );
341 load_spreg( R_EAX, R_PC );
342 if( sh4_x86.tlb_on ) {
343 call_func1(xlat_get_code_by_vma,R_EAX);
345 call_func1(xlat_get_code,R_EAX);
349 for( i=0; i< sh4_x86.backpatch_posn; i++ ) {
350 uint32_t *fixup_addr = (uint32_t *)&xlat_current_block->code[sh4_x86.backpatch_list[i].fixup_offset];
351 if( sh4_x86.backpatch_list[i].exc_code < 0 ) {
352 if( sh4_x86.backpatch_list[i].exc_code == -2 ) {
353 *fixup_addr = (uint32_t)xlat_output;
355 *fixup_addr += xlat_output - (uint8_t *)&xlat_current_block->code[sh4_x86.backpatch_list[i].fixup_offset] - 4;
357 load_imm32( R_EDX, sh4_x86.backpatch_list[i].fixup_icount );
358 int rel = preexc_ptr - xlat_output;
361 *fixup_addr += xlat_output - (uint8_t *)&xlat_current_block->code[sh4_x86.backpatch_list[i].fixup_offset] - 4;
362 PUSH_imm32( sh4_x86.backpatch_list[i].exc_code );
363 load_imm32( R_EDX, sh4_x86.backpatch_list[i].fixup_icount );
364 int rel = end_ptr - xlat_output;
373 * The unwind methods only work if we compiled with DWARF2 frame information
374 * (ie -fexceptions), otherwise we have to use the direct frame scan.
376 #ifdef HAVE_EXCEPTIONS
380 uintptr_t block_start;
385 _Unwind_Reason_Code xlat_check_frame( struct _Unwind_Context *context, void *arg )
387 struct UnwindInfo *info = arg;
388 void *pc = (void *)_Unwind_GetIP(context);
389 if( ((uintptr_t)pc) >= info->block_start && ((uintptr_t)pc) < info->block_end ) {
391 return _URC_NORMAL_STOP;
394 return _URC_NO_REASON;
397 void *xlat_get_native_pc( void *code, uint32_t code_size )
399 struct _Unwind_Exception exc;
400 struct UnwindInfo info;
403 info.block_start = (uintptr_t)code;
404 info.block_end = info.block_start + code_size;
406 _Unwind_Backtrace( xlat_check_frame, &info );
410 void *xlat_get_native_pc( void *code, uint32_t code_size )
414 "mov %%ebp, %%eax\n\t"
415 "mov $0x8, %%ecx\n\t"
417 "frame_loop: test %%eax, %%eax\n\t"
418 "je frame_not_found\n\t"
419 "cmp (%%eax), %%edx\n\t"
421 "sub $0x1, %%ecx\n\t"
422 "je frame_not_found\n\t"
423 "movl (%%eax), %%eax\n\t"
425 "frame_found: movl 0x4(%%eax), %0\n"
428 : "r" (((uint8_t *)&sh4r) + 128 )
429 : "eax", "ecx", "edx" );
434 #endif /* !lxdream_ia32mac.h */
.