/*************************************************************************** x86drc.c x86 Dynamic recompiler support routines. Copyright Nicola Salmoria and the MAME Team. Visit http://mamedev.org for licensing and usage restrictions. ***************************************************************************/ #include "debugger.h" #include "deprecat.h" #include "x86drc.h" #define LOG_DISPATCHES 0 #define BREAK_ON_MODIFIED_CODE 0 static const UINT16 fp_control[4] = { 0x023f, 0x063f, 0x0a3f, 0x0e3f }; static const UINT32 sse_control[4] = { 0x9fc0, 0xbfc0, 0xdfc0, 0xffc0 }; static void append_entry_point(drc_core *drc); static void append_recompile(drc_core *drc); static void append_flush(drc_core *drc); static void append_out_of_cycles(drc_core *drc); #if LOG_DISPATCHES static void log_dispatch(drc_core *drc); #endif /*************************************************************************** EXTERNAL INTERFACES ***************************************************************************/ /*------------------------------------------------- drc_init - initialize the DRC core -------------------------------------------------*/ drc_core *drc_init(UINT8 cpunum, drc_config *config) { int address_bits = config->address_bits; int effective_address_bits = address_bits - config->lsbs_to_ignore; UINT8 cache_allocated = FALSE; drc_core *drc = NULL; /* allocate memory */ if (config->cache_base == NULL) { config->cache_base = osd_alloc_executable(config->cache_size); if (config->cache_base == NULL) goto error; cache_allocated = TRUE; } /* the drc structure lives at the start of the cache */ drc = (drc_core *)config->cache_base; memset(drc, 0, sizeof(*drc)); /* copy in relevant data from the config */ drc->pcptr = config->pcptr; drc->icountptr = config->icountptr; drc->esiptr = config->esiptr; drc->cb_reset = config->cb_reset; drc->cb_recompile = config->cb_recompile; drc->cb_entrygen = config->cb_entrygen; drc->uses_fp = config->uses_fp; drc->uses_sse = config->uses_sse; drc->pc_in_memory = config->pc_in_memory; drc->icount_in_memory = config->icount_in_memory; drc->fpcw_curr = fp_control[0]; drc->mxcsr_curr = sse_control[0]; /* configure cache */ drc->cache_base = (UINT8 *)config->cache_base + sizeof(*drc); drc->cache_size = config->cache_size - sizeof(*drc); drc->cache_end = drc->cache_base + drc->cache_size; drc->cache_danger = drc->cache_end - 65536; drc->cache_allocated = cache_allocated; /* compute shifts and masks */ drc->l1bits = effective_address_bits/2; drc->l2bits = effective_address_bits - drc->l1bits; drc->l1shift = config->lsbs_to_ignore + drc->l2bits; drc->l2mask = ((1 << drc->l2bits) - 1) << config->lsbs_to_ignore; drc->l2scale = 4 >> config->lsbs_to_ignore; /* allocate lookup tables */ drc->lookup_l1 = malloc(sizeof(*drc->lookup_l1) * (1 << drc->l1bits)); drc->lookup_l2_recompile = malloc(sizeof(*drc->lookup_l2_recompile) * (1 << drc->l2bits)); if (drc->lookup_l1 == NULL || drc->lookup_l2_recompile == NULL) goto error; memset(drc->lookup_l1, 0, sizeof(*drc->lookup_l1) * (1 << drc->l1bits)); memset(drc->lookup_l2_recompile, 0, sizeof(*drc->lookup_l2_recompile) * (1 << drc->l2bits)); /* allocate the sequence and tentative lists */ drc->sequence_count_max = config->max_instructions; drc->sequence_list = malloc(drc->sequence_count_max * sizeof(*drc->sequence_list)); if (drc->sequence_list == NULL) goto error; drc->tentative_count_max = config->max_instructions; drc->tentative_list = malloc(drc->tentative_count_max * sizeof(*drc->tentative_list)); if (!drc->tentative_list) return NULL; return drc; error: if (drc != NULL) drc_exit(drc); return NULL; } /*------------------------------------------------- drc_alloc - allocate memory from the top of the DRC cache -------------------------------------------------*/ void *drc_alloc(drc_core *drc, size_t amount) { /* if we don't have enough space, reset the cache */ if (drc->cache_top >= drc->cache_danger - amount) drc_cache_reset(drc); /* if we still don't have enough space, fail */ if (drc->cache_top >= drc->cache_danger - amount) return NULL; /* adjust the end and danger values downward */ drc->cache_end -= amount; drc->cache_danger -= amount; return drc->cache_end; } /*------------------------------------------------- drc_cache_reset - reset the DRC cache -------------------------------------------------*/ void drc_cache_reset(drc_core *drc) { int i; /* reset the cache and add the basics */ drc->cache_top = drc->cache_base; /* append the core entry points to the fresh cache */ drc->entry_point = (void (*)(void))(UINT32)drc->cache_top; append_entry_point(drc); drc->out_of_cycles = drc->cache_top; append_out_of_cycles(drc); /* append an INT 3 before the recompile so that BREAK_ON_MODIFIED_CODE works */ emit_int_3(DRCTOP); drc->recompile = drc->cache_top; append_recompile(drc); drc->dispatch = drc->cache_top; drc_append_dispatcher(drc); drc->flush = drc->cache_top; append_flush(drc); /* populate the recompile table */ for (i = 0; i < (1 << drc->l2bits); i++) drc->lookup_l2_recompile[i] = drc->recompile; /* reset all the l1 tables */ for (i = 0; i < (1 << drc->l1bits); i++) { /* point NULL entries to the generic recompile table */ if (drc->lookup_l1[i] == NULL) drc->lookup_l1[i] = drc->lookup_l2_recompile; /* reset allocated tables to point all entries back to the recompiler */ else if (drc->lookup_l1[i] != drc->lookup_l2_recompile) memcpy(drc->lookup_l1[i], drc->lookup_l2_recompile, sizeof(*drc->lookup_l2_recompile) * (1 << drc->l2bits)); } /* call back to the host */ if (drc->cb_reset) (*drc->cb_reset)(drc); } /*------------------------------------------------------------------ drc_execute ------------------------------------------------------------------*/ void drc_execute(drc_core *drc) { (*drc->entry_point)(); } /*------------------------------------------------------------------ drc_exit ------------------------------------------------------------------*/ void drc_exit(drc_core *drc) { int i; /* free all the l2 tables allocated */ for (i = 0; i < (1 << drc->l1bits); i++) if (drc->lookup_l1[i] != drc->lookup_l2_recompile) free(drc->lookup_l1[i]); /* free the l1 table */ if (drc->lookup_l1) free(drc->lookup_l1); /* free the default l2 table */ if (drc->lookup_l2_recompile) free(drc->lookup_l2_recompile); /* free the lists */ if (drc->sequence_list) free(drc->sequence_list); if (drc->tentative_list) free(drc->tentative_list); /* and the drc itself */ if (drc->cache_allocated) osd_free_executable(drc, drc->cache_size + sizeof(*drc)); } /*------------------------------------------------------------------ drc_begin_sequence ------------------------------------------------------------------*/ void drc_begin_sequence(drc_core *drc, UINT32 pc) { UINT32 l1index = pc >> drc->l1shift; UINT32 l2index = ((pc & drc->l2mask) * drc->l2scale) / 4; /* reset the sequence and tentative counts */ drc->sequence_count = 0; drc->tentative_count = 0; /* allocate memory if necessary */ if (drc->lookup_l1[l1index] == drc->lookup_l2_recompile) { /* create a new copy of the recompile table */ drc->lookup_l1[l1index] = malloc_or_die(sizeof(*drc->lookup_l2_recompile) * (1 << drc->l2bits)); memcpy(drc->lookup_l1[l1index], drc->lookup_l2_recompile, sizeof(*drc->lookup_l2_recompile) * (1 << drc->l2bits)); } /* nuke any previous link to this instruction */ if (drc->lookup_l1[l1index][l2index] != drc->recompile) { UINT8 *cache_save = drc->cache_top; drc->cache_top = drc->lookup_l1[l1index][l2index]; emit_jmp(DRCTOP, drc->dispatch); drc->cache_top = cache_save; } /* note the current location for this instruction */ drc->lookup_l1[l1index][l2index] = drc->cache_top; } /*------------------------------------------------------------------ drc_end_sequence ------------------------------------------------------------------*/ void drc_end_sequence(drc_core *drc) { int i, j; /* fix up any internal links */ for (i = 0; i < drc->tentative_count; i++) for (j = 0; j < drc->sequence_count; j++) if (drc->tentative_list[i].pc == drc->sequence_list[j].pc) { UINT8 *cache_save = drc->cache_top; drc->cache_top = drc->tentative_list[i].target; emit_jmp(DRCTOP, drc->sequence_list[j].target); drc->cache_top = cache_save; break; } } /*------------------------------------------------------------------ drc_register_code_at_cache_top ------------------------------------------------------------------*/ void drc_register_code_at_cache_top(drc_core *drc, UINT32 pc) { pc_ptr_pair *pair = &drc->sequence_list[drc->sequence_count++]; assert_always(drc->sequence_count <= drc->sequence_count_max, "drc_register_code_at_cache_top: too many instructions!"); pair->target = drc->cache_top; pair->pc = pc; } /*------------------------------------------------------------------ drc_get_code_at_pc ------------------------------------------------------------------*/ void *drc_get_code_at_pc(drc_core *drc, UINT32 pc) { UINT32 l1index = pc >> drc->l1shift; UINT32 l2index = ((pc & drc->l2mask) * drc->l2scale) / 4; return (drc->lookup_l1[l1index][l2index] != drc->recompile) ? drc->lookup_l1[l1index][l2index] : NULL; } /*------------------------------------------------------------------ drc_append_verify_code ------------------------------------------------------------------*/ void drc_append_verify_code(drc_core *drc, void *code, UINT8 length) { #if BREAK_ON_MODIFIED_CODE x86code *recompile = drc->recompile - 1; #else x86code *recompile = drc->recompile; #endif if (length > 8) { UINT32 *codeptr = code, sum = 0; void *target; int i; for (i = 0; i < length / 4; i++) { sum = (sum >> 1) | (sum << 31); sum += *codeptr++; } emit_xor_r32_r32(DRCTOP, REG_EAX, REG_EAX); // xor eax,eax emit_mov_r32_imm(DRCTOP, REG_EBX, (FPTR)code); // mov ebx,code emit_mov_r32_imm(DRCTOP, REG_ECX, length / 4); // mov ecx,length / 4 target = drc->cache_top; // target: emit_ror_r32_imm(DRCTOP, REG_EAX, 1); // ror eax,1 emit_add_r32_m32(DRCTOP, REG_EAX, MBD(REG_EBX, 0)); // add eax,[ebx] emit_sub_r32_imm(DRCTOP, REG_ECX, 1); // sub ecx,1 emit_lea_r32_m32(DRCTOP, REG_EBX, MBD(REG_EBX, 4)); // lea ebx,[ebx+4] emit_jcc(DRCTOP, COND_NZ, target); // jnz target emit_cmp_r32_imm(DRCTOP, REG_EAX, sum); // cmp eax,sum emit_jcc(DRCTOP, COND_NE, recompile); // jne recompile } else if (length >= 12) { emit_cmp_m32_imm(DRCTOP, MABS(code), *(UINT32 *)code); // cmp [pc],opcode emit_jcc(DRCTOP, COND_NE, recompile); // jne recompile emit_cmp_m32_imm(DRCTOP, MABS((UINT8 *)code + 4), ((UINT32 *)code)[1]); // cmp [pc+4],opcode+4 emit_jcc(DRCTOP, COND_NE, recompile); // jne recompile emit_cmp_m32_imm(DRCTOP, MABS((UINT8 *)code + 8), ((UINT32 *)code)[2]); // cmp [pc+8],opcode+8 emit_jcc(DRCTOP, COND_NE, recompile); // jne recompile } else if (length >= 8) { emit_cmp_m32_imm(DRCTOP, MABS(code), *(UINT32 *)code); // cmp [pc],opcode emit_jcc(DRCTOP, COND_NE, recompile); // jne recompile emit_cmp_m32_imm(DRCTOP, MABS((UINT8 *)code + 4), ((UINT32 *)code)[1]); // cmp [pc+4],opcode+4 emit_jcc(DRCTOP, COND_NE, recompile); // jne recompile } else if (length >= 4) { emit_cmp_m32_imm(DRCTOP, MABS(code), *(UINT32 *)code); // cmp [pc],opcode emit_jcc(DRCTOP, COND_NE, recompile); // jne recompile } else if (length >= 2) { emit_cmp_m16_imm(DRCTOP, MABS(code), *(UINT16 *)code); // cmp [pc],opcode emit_jcc(DRCTOP, COND_NE, recompile); // jne recompile } else { emit_cmp_m8_imm(DRCTOP, MABS(code), *(UINT8 *)code); // cmp [pc],opcode emit_jcc(DRCTOP, COND_NE, recompile); // jne recompile } } /*------------------------------------------------------------------ drc_append_call_debugger ------------------------------------------------------------------*/ void drc_append_call_debugger(drc_core *drc) { #ifdef ENABLE_DEBUGGER if (Machine->debug_mode) { emit_link link; emit_cmp_m32_imm(DRCTOP, MABS(&Machine->debug_mode), 0); // cmp [Machine->debug_mode],0 emit_jcc_short_link(DRCTOP, COND_E, &link); // je skip emit_sub_r32_imm(DRCTOP, REG_ESP, 12); // align stack drc_append_save_call_restore(drc, (x86code *)mame_debug_hook, 12); // save volatiles resolve_link(DRCTOP, &link); } #endif } /*------------------------------------------------------------------ drc_append_save_volatiles ------------------------------------------------------------------*/ void drc_append_save_volatiles(drc_core *drc) { if (drc->icountptr && !drc->icount_in_memory) emit_mov_m32_r32(DRCTOP, MABS(drc->icountptr), REG_EBP); if (drc->pcptr && !drc->pc_in_memory) emit_mov_m32_r32(DRCTOP, MABS(drc->pcptr), REG_EDI); if (drc->esiptr) emit_mov_m32_r32(DRCTOP, MABS(drc->esiptr), REG_ESI); } /*------------------------------------------------------------------ drc_append_restore_volatiles ------------------------------------------------------------------*/ void drc_append_restore_volatiles(drc_core *drc) { if (drc->icountptr && !drc->icount_in_memory) emit_mov_r32_m32(DRCTOP, REG_EBP, MABS(drc->icountptr)); if (drc->pcptr && !drc->pc_in_memory) emit_mov_r32_m32(DRCTOP, REG_EDI, MABS(drc->pcptr)); if (drc->esiptr) emit_mov_r32_m32(DRCTOP, REG_ESI, MABS(drc->esiptr)); } /*------------------------------------------------------------------ drc_append_save_call_restore ------------------------------------------------------------------*/ void drc_append_save_call_restore(drc_core *drc, x86code *target, UINT32 stackadj) { drc_append_save_volatiles(drc); // save volatiles emit_call(DRCTOP, target); // call target drc_append_restore_volatiles(drc); // restore volatiles if (stackadj) emit_add_r32_imm(DRCTOP, REG_ESP, stackadj); // adjust stack } /*------------------------------------------------------------------ drc_append_standard_epilogue ------------------------------------------------------------------*/ void drc_append_standard_epilogue(drc_core *drc, INT32 cycles, INT32 pcdelta, int allow_exit) { if (pcdelta != 0 && drc->pc_in_memory) emit_add_m32_imm(DRCTOP, MABS(drc->pcptr), pcdelta); // add [pc],pcdelta if (cycles != 0) { if (drc->icount_in_memory) emit_sub_m32_imm(DRCTOP, MABS(drc->icountptr), cycles); // sub [icount],cycles else emit_sub_r32_imm(DRCTOP, REG_EBP, cycles); // sub ebp,cycles } if (pcdelta != 0 && !drc->pc_in_memory) emit_lea_r32_m32(DRCTOP, REG_EDI, MBD(REG_EDI, pcdelta)); // lea edi,[edi+pcdelta] if (allow_exit && cycles != 0) emit_jcc(DRCTOP, COND_S, drc->out_of_cycles); // js out_of_cycles } /*------------------------------------------------------------------ drc_append_dispatcher ------------------------------------------------------------------*/ void drc_append_dispatcher(drc_core *drc) { #if LOG_DISPATCHES emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // align stack emit_push_imm(DRCTOP, drc); // push drc drc_append_save_call_restore(drc, (x86code *)log_dispatch, 12); // call log_dispatch #endif if (drc->pc_in_memory) emit_mov_r32_m32(DRCTOP, REG_EDI, MABS(drc->pcptr)); // mov edi,[pc] emit_mov_r32_r32(DRCTOP, REG_EAX, REG_EDI); // mov eax,edi emit_shr_r32_imm(DRCTOP, REG_EAX, drc->l1shift); // shr eax,l1shift emit_mov_r32_r32(DRCTOP, REG_EDX, REG_EDI); // mov edx,edi emit_mov_r32_m32(DRCTOP, REG_EAX, MISD(REG_EAX, 4, (FPTR)drc->lookup_l1)); // mov eax,[eax*4 + l1lookup] emit_and_r32_imm(DRCTOP, REG_EDX, drc->l2mask); // and edx,l2mask emit_jmp_m32(DRCTOP, MBISD(REG_EAX, REG_EDX, drc->l2scale, 0)); // jmp [eax+edx*l2scale] } /*------------------------------------------------------------------ drc_append_fixed_dispatcher ------------------------------------------------------------------*/ void drc_append_fixed_dispatcher(drc_core *drc, UINT32 newpc) { x86code **base = drc->lookup_l1[newpc >> drc->l1shift]; if (base == drc->lookup_l2_recompile) { emit_mov_r32_m32(DRCTOP, REG_EAX, MABS(&drc->lookup_l1[newpc >> drc->l1shift])); // mov eax,[(newpc >> l1shift)*4 + l1lookup] emit_jmp_m32(DRCTOP, MBD(REG_EAX, (newpc & drc->l2mask) * drc->l2scale)); // jmp [eax+(newpc & l2mask)*l2scale] } else emit_jmp_m32(DRCTOP, MABS((UINT8 *)base + (newpc & drc->l2mask) * drc->l2scale)); // jmp [eax+(newpc & l2mask)*l2scale] } /*------------------------------------------------------------------ drc_append_tentative_fixed_dispatcher ------------------------------------------------------------------*/ void drc_append_tentative_fixed_dispatcher(drc_core *drc, UINT32 newpc) { pc_ptr_pair *pair = &drc->tentative_list[drc->tentative_count++]; assert_always(drc->tentative_count <= drc->tentative_count_max, "drc_append_tentative_fixed_dispatcher: too many tentative branches!"); pair->target = drc->cache_top; pair->pc = newpc; drc_append_fixed_dispatcher(drc, newpc); } /*------------------------------------------------------------------ drc_append_set_fp_rounding ------------------------------------------------------------------*/ void drc_append_set_fp_rounding(drc_core *drc, UINT8 regindex) { emit_fldcw_m16(DRCTOP, MISD(regindex, 2, (INT32)&fp_control[0])); // fldcw [fp_control + reg*2] emit_fstcw_m16(DRCTOP, MABS(&drc->fpcw_curr)); // fnstcw [fpcw_curr] } /*------------------------------------------------------------------ drc_append_set_temp_fp_rounding ------------------------------------------------------------------*/ void drc_append_set_temp_fp_rounding(drc_core *drc, UINT8 rounding) { emit_fldcw_m16(DRCTOP, MABS(&fp_control[rounding])); // fldcw [fp_control] } /*------------------------------------------------------------------ drc_append_restore_fp_rounding ------------------------------------------------------------------*/ void drc_append_restore_fp_rounding(drc_core *drc) { emit_fldcw_m16(DRCTOP, MABS(&drc->fpcw_curr)); // fldcw [fpcw_curr] } /*------------------------------------------------------------------ drc_append_set_sse_rounding ------------------------------------------------------------------*/ void drc_append_set_sse_rounding(drc_core *drc, UINT8 regindex) { emit_ldmxcsr_m32(DRCTOP, MISD(regindex, 4, (INT32)&sse_control[0])); // ldmxcsr [sse_control + reg*2] emit_stmxcsr_m32(DRCTOP, MABS(&drc->mxcsr_curr)); // stmxcsr [mxcsr_curr] } /*------------------------------------------------------------------ drc_append_set_temp_sse_rounding ------------------------------------------------------------------*/ void drc_append_set_temp_sse_rounding(drc_core *drc, UINT8 rounding) { emit_ldmxcsr_m32(DRCTOP, MABS(&sse_control[rounding])); // ldmxcsr [sse_control] } /*------------------------------------------------------------------ drc_append_restore_sse_rounding ------------------------------------------------------------------*/ void drc_append_restore_sse_rounding(drc_core *drc) { emit_ldmxcsr_m32(DRCTOP, MABS(&drc->mxcsr_curr)); // ldmxcsr [mxcsr_curr] } /*------------------------------------------------------------------ drc_dasm An attempt to make a disassembler for DRC code; currently limited by the functionality of DasmI386 ------------------------------------------------------------------*/ void drc_dasm(FILE *f, const void *begin, const void *end) { extern int i386_dasm_one(char *buffer, UINT32 eip, UINT8 *oprom, int mode); char buffer[256]; const UINT8 *begin_ptr = (const UINT8 *) begin; const UINT8 *end_ptr = (const UINT8 *) end; UINT32 pc = (UINT32) begin; int length; while(begin_ptr < end_ptr) { #if defined(ENABLE_DEBUGGER) && HAS_I386 length = i386_dasm_one(buffer, pc, (UINT8 *) begin_ptr, 32) & DASMFLAG_LENGTHMASK; #else sprintf(buffer, "%02X", *begin_ptr); length = 1; #endif fprintf(f, "%08X:\t%s\n", (unsigned) pc, buffer); begin_ptr += length; pc += length; } } /*************************************************************************** INTERNAL CODEGEN ***************************************************************************/ /*------------------------------------------------------------------ append_entry_point ------------------------------------------------------------------*/ static void append_entry_point(drc_core *drc) { emit_pushad(DRCTOP); // pushad if (drc->uses_fp) { emit_fstcw_m16(DRCTOP, MABS(&drc->fpcw_save)); // fstcw [fpcw_save] emit_fldcw_m16(DRCTOP, MABS(&drc->fpcw_curr)); // fldcw [fpcw_curr] } if (drc->uses_sse) { emit_stmxcsr_m32(DRCTOP, MABS(&drc->mxcsr_save)); // stmxcsr [mxcsr_save] emit_ldmxcsr_m32(DRCTOP, MABS(&drc->mxcsr_curr)); // ldmxcsr [mxcsr_curr] } drc_append_restore_volatiles(drc); // load volatiles if (drc->cb_entrygen) (*drc->cb_entrygen)(drc); // additional entry point duties drc_append_dispatcher(drc); // dispatch } /*------------------------------------------------------------------ recompile_code ------------------------------------------------------------------*/ static void recompile_code(drc_core *drc) { if (drc->cache_top >= drc->cache_danger) drc_cache_reset(drc); (*drc->cb_recompile)(drc); } /*------------------------------------------------------------------ append_recompile ------------------------------------------------------------------*/ static void append_recompile(drc_core *drc) { emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // align stack emit_push_imm(DRCTOP, (FPTR)drc); // push drc drc_append_save_call_restore(drc, (x86code *)recompile_code, 12); // call recompile_code drc_append_dispatcher(drc); // dispatch } /*------------------------------------------------------------------ append_flush ------------------------------------------------------------------*/ static void append_flush(drc_core *drc) { emit_sub_r32_imm(DRCTOP, REG_ESP, 8); // align stack emit_push_imm(DRCTOP, (FPTR)drc); // push drc drc_append_save_call_restore(drc, (x86code *)drc_cache_reset, 12); // call drc_cache_reset drc_append_dispatcher(drc); // dispatch } /*------------------------------------------------------------------ append_out_of_cycles ------------------------------------------------------------------*/ static void append_out_of_cycles(drc_core *drc) { drc_append_save_volatiles(drc); // save volatiles if (drc->uses_fp) { emit_fclex(DRCTOP); // fnclex emit_fldcw_m16(DRCTOP, MABS(&drc->fpcw_save)); // fldcw [fpcw_save] } if (drc->uses_sse) emit_ldmxcsr_m32(DRCTOP, MABS(&drc->mxcsr_save)); // ldmxcsr [mxcsr_save] emit_popad(DRCTOP); // popad emit_ret(DRCTOP); // ret } /*------------------------------------------------------------------ drc_x86_get_features() ------------------------------------------------------------------*/ UINT32 drc_x86_get_features(void) { UINT32 features = 0; #ifdef _MSC_VER __asm { mov eax, 1 xor ebx, ebx xor ecx, ecx xor edx, edx __asm _emit 0Fh __asm _emit 0A2h // cpuid mov features, edx } #else /* !_MSC_VER */ __asm__ ( "pushl %%ebx ; " "movl $1,%%eax ; " "xorl %%ebx,%%ebx ; " "xorl %%ecx,%%ecx ; " "xorl %%edx,%%edx ; " "cpuid ; " "movl %%edx,%0 ; " "popl %%ebx ; " : "=&a" (features) /* result has to go in eax */ : /* no inputs */ : "%ecx", "%edx" /* clobbers ebx, ecx and edx */ ); #endif /* MSC_VER */ return features; } /*------------------------------------------------------------------ log_dispatch ------------------------------------------------------------------*/ #if LOG_DISPATCHES static void log_dispatch(drc_core *drc) { if (input_code_pressed(KEYCODE_D)) logerror("Disp:%08X\n", *drc->pcptr); } #endif