Search
lxdream.org :: lxdream/src/sh4/ia32abi.h
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/ia32abi.h
changeset 929:fd8cb0c82f5f
prev927:17b6b9e245d8
next930:07e5b11419db
author nkeynes
date Sat Dec 20 03:01:40 2008 +0000 (12 years ago)
branchlxdream-mem
permissions -rw-r--r--
last change First pass experiment using cached decoding.
view annotate diff log raw
     1 /**
     2  * $Id$
     3  * 
     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 
     7  * requires it).
     8  *
     9  * Copyright (c) 2007 Nathan Keynes.
    10  *
    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.
    15  *
    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.
    20  */
    22 #ifndef lxdream_ia32mac_H
    23 #define lxdream_ia32mac_H 1
    25 #define load_ptr( reg, ptr ) load_imm32( reg, (uint32_t)ptr );
    27 /**
    28  * Note: clobbers EAX to make the indirect call - this isn't usually
    29  * a problem since the callee will usually clobber it anyway.
    30  */
    31 static inline void call_func0( void *ptr )
    32 {
    33     CALL_ptr(ptr);
    34 }
    36 #ifdef HAVE_FASTCALL
    37 static inline void call_func1( void *ptr, int arg1 )
    38 {
    39     if( arg1 != R_EAX ) {
    40         MOV_r32_r32( arg1, R_EAX );
    41     }
    42     CALL_ptr(ptr);
    43 }
    45 static inline void call_func1_r32( int addr_reg, int arg1 )
    46 {
    47     if( arg1 != R_EAX ) {
    48         MOV_r32_r32( arg1, R_EAX );
    49     }
    50     CALL_r32(addr_reg);
    51 }
    53 static inline void call_func1_r32ind( int preg, uint32_t disp32, int arg1 )
    54 {
    55     if( arg1 != R_EAX ) {
    56         MOV_r32_r32( arg1, R_EAX );
    57     }
    58     CALL_r32ind(preg, disp32);
    59 }
    61 static inline void call_func2( void *ptr, int arg1, int arg2 )
    62 {
    63     if( arg2 != R_EDX ) {
    64         MOV_r32_r32( arg2, R_EDX );
    65     }
    66     if( arg1 != R_EAX ) {
    67         MOV_r32_r32( arg1, R_EAX );
    68     }
    69     CALL_ptr(ptr);
    70 }
    72 static inline void call_func2_r32( int addr_reg, int arg1, int arg2 )
    73 {
    74     if( arg2 != R_EDX ) {
    75         MOV_r32_r32( arg2, R_EDX );
    76     }
    77     if( arg1 != R_EAX ) {
    78         MOV_r32_r32( arg1, R_EAX );
    79     }
    80     CALL_r32(addr_reg);
    81 }
    83 static inline void call_func2_r32ind( int preg, uint32_t disp32, int arg1, int arg2 )
    84 {
    85     if( arg2 != R_EDX ) {
    86         MOV_r32_r32( arg2, R_EDX );
    87     }
    88     if( arg1 != R_EAX ) {
    89         MOV_r32_r32( arg1, R_EAX );
    90     }
    91     CALL_r32ind(preg, disp32);
    92 }
    96 static inline void call_func1_exc( void *ptr, int arg1, int pc )
    97 {
    98     if( arg1 != R_EAX ) {
    99         MOV_r32_r32( arg1, R_EAX );
   100     }
   101     load_exc_backpatch(R_EDX);
   102     CALL_ptr(ptr);
   103 }   
   105 static inline void call_func2_exc( void *ptr, int arg1, int arg2, int pc )
   106 {
   107     if( arg2 != R_EDX ) {
   108         MOV_r32_r32( arg2, R_EDX );
   109     }
   110     if( arg1 != R_EAX ) {
   111         MOV_r32_r32( arg1, R_EAX );
   112     }
   113     load_exc_backpatch(R_ECX);
   114     CALL_ptr(ptr);
   115 }
   117 /**
   118  * Write a double (64-bit) value into memory, with the first word in arg2a, and
   119  * the second in arg2b
   120  */
   121 static inline void MEM_WRITE_DOUBLE( int addr, int arg2a, int arg2b )
   122 {
   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);
   130 }
   132 /**
   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
   135  */
   136 static inline void MEM_READ_DOUBLE( int addr, int arg2a, int arg2b )
   137 {
   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);
   146     }
   147     MOV_esp8_r32(4, arg2a);
   148 }
   149 #else
   150 static inline void call_func1( void *ptr, int arg1 )
   151 {
   152     SUB_imm8s_r32( 12, R_ESP );
   153     PUSH_r32(arg1);
   154     CALL_ptr(ptr);
   155     ADD_imm8s_r32( 16, R_ESP );
   156 }
   158 static inline void call_func2( void *ptr, int arg1, int arg2 )
   159 {
   160     SUB_imm8s_r32( 8, R_ESP );
   161     PUSH_r32(arg2);
   162     PUSH_r32(arg1);
   163     CALL_ptr(ptr);
   164     ADD_imm8s_r32( 16, R_ESP );
   165 }
   167 /**
   168  * Write a double (64-bit) value into memory, with the first word in arg2a, and
   169  * the second in arg2b
   170  */
   171 static inline void MEM_WRITE_DOUBLE( int addr, int arg2a, int arg2b )
   172 {
   173     SUB_imm8s_r32( 8, R_ESP );
   174     PUSH_r32(arg2b);
   175     LEA_r32disp8_r32( addr, 4, arg2b );
   176     PUSH_r32(arg2b);
   177     SUB_imm8s_r32( 8, R_ESP );
   178     PUSH_r32(arg2a);
   179     PUSH_r32(addr);
   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 );
   184 }
   186 /**
   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
   189  */
   190 static inline void MEM_READ_DOUBLE( int addr, int arg2a, int arg2b )
   191 {
   192     SUB_imm8s_r32( 12, R_ESP );
   193     PUSH_r32(addr);
   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 );
   200     }
   201     MOV_esp8_r32( 4, arg2a );
   202     ADD_imm8s_r32( 16, R_ESP );
   203 }
   205 #endif
   207 /**
   208  * Emit the 'start of block' assembly. Sets up the stack frame and save
   209  * SI/DI as required
   210  * Allocates 8 bytes for local variables, which also has the convenient
   211  * side-effect of aligning the stack.
   212  */
   213 void enter_block( ) 
   214 {
   215     PUSH_r32(R_EBP);
   216     load_ptr( R_EBP, ((uint8_t *)&sh4r) + 128 );
   217     PUSH_r32(R_EBX);
   218     SUB_imm8s_r32( 4, R_ESP ); 
   219 }
   221 static inline void exit_block( )
   222 {
   223     ADD_imm8s_r32( 4, R_ESP );
   224     POP_r32(R_EBX);
   225     POP_r32(R_EBP);
   226     RET();
   227 }
   229 /**
   230  * Exit the block with sh4r.new_pc written with the target pc
   231  */
   232 void exit_block_pcset( sh4addr_t pc )
   233 {
   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);
   239     } else {
   240         call_func1(xlat_get_code,R_EAX);
   241     }
   242     exit_block();
   243 }
   245 /**
   246  * Exit the block with sh4r.new_pc written with the target pc
   247  */
   248 void exit_block_newpcset( sh4addr_t pc )
   249 {
   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);
   256     } else {
   257         call_func1(xlat_get_code,R_EAX);
   258     }
   259     exit_block();
   260 }
   263 /**
   264  * Exit the block to an absolute PC
   265  */
   266 void exit_block_abs( sh4addr_t pc, sh4addr_t endpc )
   267 {
   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);
   275     } else {
   276         call_func1(xlat_get_code,R_ECX);
   277     }
   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
   280     exit_block();
   281 }
   283 /**
   284  * Exit the block to a relative PC
   285  */
   286 void exit_block_rel( sh4addr_t pc, sh4addr_t endpc )
   287 {
   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);
   296     } else {
   297         call_func1(xlat_get_code,R_ECX);
   298     }
   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
   301     exit_block();
   302 }
   304 /**
   305  * Write the block trailer (exception handling block)
   306  */
   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 );
   311     }
   312     if( sh4_x86.backpatch_posn != 0 ) {
   313         unsigned int i;
   314         // Raise exception
   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 );
   320         MUL_r32( R_EDX );
   321         ADD_r32_sh4r( R_EAX, REG_OFFSET(slice_cycle) );
   323         POP_r32(R_EAX);
   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);
   328         } else {
   329             call_func1(xlat_get_code,R_EAX);
   330         }
   331         exit_block();
   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 );
   339         MUL_r32( R_EDX );
   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);
   344         } else {
   345             call_func1(xlat_get_code,R_EAX);
   346         }
   347         exit_block();
   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;
   354                 } else {
   355                     *fixup_addr += xlat_output - (uint8_t *)&xlat_current_block->code[sh4_x86.backpatch_list[i].fixup_offset] - 4;
   356                 }
   357                 load_imm32( R_EDX, sh4_x86.backpatch_list[i].fixup_icount );
   358                 int rel = preexc_ptr - xlat_output;
   359                 JMP_rel(rel);
   360             } else {
   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;
   365                 JMP_rel(rel);
   366             }
   367         }
   368     }
   369 }
   372 /**
   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.
   375  */
   376 #ifdef HAVE_EXCEPTIONS
   377 #include <unwind.h>
   379 struct UnwindInfo {
   380     uintptr_t block_start;
   381     uintptr_t block_end;
   382     void *pc;
   383 };
   385 _Unwind_Reason_Code xlat_check_frame( struct _Unwind_Context *context, void *arg )
   386 {
   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 ) {
   390         info->pc = pc;
   391         return _URC_NORMAL_STOP;
   392     }
   394     return _URC_NO_REASON;
   395 }
   397 void *xlat_get_native_pc( void *code, uint32_t code_size )
   398 {
   399     struct _Unwind_Exception exc;
   400     struct UnwindInfo info;
   402     info.pc = NULL;
   403     info.block_start = (uintptr_t)code;
   404     info.block_end = info.block_start + code_size;
   405     void *result = NULL;
   406     _Unwind_Backtrace( xlat_check_frame, &info );
   407     return info.pc;
   408 }
   409 #else 
   410 void *xlat_get_native_pc( void *code, uint32_t code_size )
   411 {
   412     void *result = NULL;
   413     asm(
   414         "mov %%ebp, %%eax\n\t"
   415         "mov $0x8, %%ecx\n\t"
   416         "mov %1, %%edx\n"
   417         "frame_loop: test %%eax, %%eax\n\t"
   418         "je frame_not_found\n\t"
   419         "cmp (%%eax), %%edx\n\t"
   420         "je frame_found\n\t"
   421         "sub $0x1, %%ecx\n\t"
   422         "je frame_not_found\n\t"
   423         "movl (%%eax), %%eax\n\t"
   424         "jmp frame_loop\n"
   425         "frame_found: movl 0x4(%%eax), %0\n"
   426         "frame_not_found:"
   427         : "=r" (result)
   428         : "r" (((uint8_t *)&sh4r) + 128 )
   429         : "eax", "ecx", "edx" );
   430     return result;
   431 }
   432 #endif
   434 #endif /* !lxdream_ia32mac.h */
.