static int math_emulate(struct trapframe * tframe) { unsigned char FPU_modrm; unsigned short code; #ifdef LOOKAHEAD_LIMIT int lookahead_limit = LOOKAHEAD_LIMIT; #endif #ifdef PARANOID if (emulating) { printf("ERROR: wm-FPU-emu is not RE-ENTRANT!\n"); } REENTRANT_CHECK(ON); #endif /* PARANOID */ if ((((struct pcb *) curproc->p_addr)->pcb_flags & FP_SOFTFP) == 0) { finit(); control_word = __INITIAL_NPXCW__; ((struct pcb *) curproc->p_addr)->pcb_flags |= FP_SOFTFP; } FPU_info = tframe; FPU_ORIG_EIP = FPU_EIP; /* --pink-- */ if (FPU_CS != 0x001f) { printf("math_emulate: %x : %x\n", FPU_CS, FPU_EIP); panic("FPU emulation in kernel"); } #ifdef notyet /* We cannot handle emulation in v86-mode */ if (FPU_EFLAGS & 0x00020000) { FPU_ORIG_EIP = FPU_EIP; math_abort(FPU_info, SIGILL); } #endif FPU_lookahead = FPU_LOOKAHEAD; if (curproc->p_flag & P_TRACED) FPU_lookahead = 0; do_another_FPU_instruction: REENTRANT_CHECK(OFF); code = fuword((u_int *) FPU_EIP); REENTRANT_CHECK(ON); if ((code & 0xff) == 0x9b) { /* fwait */ if (status_word & SW_Summary) goto do_the_FPU_interrupt; else { FPU_EIP++; goto FPU_instruction_done; } } if (status_word & SW_Summary) { /* Ignore the error for now if the current instruction is a * no-wait control instruction */ /* The 80486 manual contradicts itself on this topic, so I use * the following list of such instructions until I can check * on a real 80486: fninit, fnstenv, fnsave, fnstsw, fnstenv, * fnclex. */ if (!((((code & 0xf803) == 0xe003) || /* fnclex, fninit, * fnstsw */ (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, * fnstenv, fnstsw */ ((code & 0xc000) != 0xc000))))) { /* This is a guess about what a real FPU might do to * this bit: */ /* status_word &= ~SW_Summary; ****/ /* We need to simulate the action of the kernel to FPU * interrupts here. Currently, the "real FPU" part of * the kernel (0.99.10) clears the exception flags, * sets the registers to empty, and passes information * back to the interrupted process via the cs selector * and operand selector, so we do the same. */ do_the_FPU_interrupt: cs_selector &= 0xffff0000; cs_selector |= (status_word & ~SW_Top) | ((top & 7) << SW_Top_Shift); operand_selector = tag_word(); status_word = 0; top = 0; { int r; for (r = 0; r < 8; r++) { regs[r].tag = TW_Empty; } } REENTRANT_CHECK(OFF); math_abort(SIGFPE); } } FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP; if ((code & 0xff) == 0x66) { /* size prefix */ FPU_EIP++; REENTRANT_CHECK(OFF); code = fuword((u_int *) FPU_EIP); REENTRANT_CHECK(ON); } FPU_EIP += 2; FPU_modrm = code >> 8; FPU_rm = FPU_modrm & 7; if (FPU_modrm < 0300) { /* All of these instructions use the mod/rm byte to get a data * address */ get_address(FPU_modrm); if (!(code & 1)) { unsigned short status1 = status_word; FPU_st0_ptr = &st(0); FPU_st0_tag = FPU_st0_ptr->tag; /* Stack underflow has priority */ if (NOT_EMPTY_0) { switch ((code >> 1) & 3) { case 0: reg_load_single(); break; case 1: reg_load_int32(); break; case 2: reg_load_double(); break; case 3: reg_load_int16(); break; } /* No more access to user memory, it is safe * to use static data now */ FPU_st0_ptr = &st(0); FPU_st0_tag = FPU_st0_ptr->tag; /* NaN operands have the next priority. */ /* We have to delay looking at st(0) until * after loading the data, because that data * might contain an SNaN */ if ((FPU_st0_tag == TW_NaN) || (FPU_loaded_data.tag == TW_NaN)) { /* Restore the status word; we might * have loaded a denormal. */ status_word = status1; if ((FPU_modrm & 0x30) == 0x10) { /* fcom or fcomp */ EXCEPTION(EX_Invalid); setcc(SW_C3 | SW_C2 | SW_C0); if (FPU_modrm & 0x08) pop(); /* fcomp, so we pop. */ } else real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr); goto reg_mem_instr_done; } switch ((FPU_modrm >> 3) & 7) { case 0: /* fadd */ reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word); break; case 1: /* fmul */ reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word); break; case 2: /* fcom */ compare_st_data(); break; case 3: /* fcomp */ compare_st_data(); pop(); break; case 4: /* fsub */ reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word); break; case 5: /* fsubr */ reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word); break; case 6: /* fdiv */ reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, control_word); break; case 7: /* fdivr */ if (FPU_st0_tag == TW_Zero) status_word = status1; /* Undo any denorm tag, * zero-divide has * priority. */ reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, control_word); break; } } else { if ((FPU_modrm & 0x30) == 0x10) {
void load_store_instr(char type) { FPU_REG *pop_ptr; /* We need a version of FPU_st0_ptr which won't change. */ pop_ptr = NULL; /* Initialized just to stop compiler warnings. */ switch ( type_table[(int) (unsigned) type] ) { case _NONE_: break; case _REG0_: pop_ptr = &st(0); /* Some of these instructions pop after storing */ FPU_st0_ptr = pop_ptr; /* Set the global variables. */ FPU_st0_tag = FPU_st0_ptr->tag; break; case _PUSH_: { pop_ptr = &st(-1); if ( pop_ptr->tag != TW_Empty ) { stack_overflow(); return; } top--; } break; case _null_: return Un_impl(); #ifdef PARANOID default: return EXCEPTION(EX_INTERNAL); #endif PARANOID } switch ( type ) { case 000: /* fld m32real */ reg_load_single(); reg_move(&FPU_loaded_data, pop_ptr); break; case 001: /* fild m32int */ reg_load_int32(); reg_move(&FPU_loaded_data, pop_ptr); break; case 002: /* fld m64real */ reg_load_double(); reg_move(&FPU_loaded_data, pop_ptr); break; case 003: /* fild m16int */ reg_load_int16(); reg_move(&FPU_loaded_data, pop_ptr); break; case 010: /* fst m32real */ reg_store_single(); break; case 011: /* fist m32int */ reg_store_int32(); break; case 012: /* fst m64real */ reg_store_double(); break; case 013: /* fist m16int */ reg_store_int16(); break; case 014: /* fstp m32real */ if ( reg_store_single() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 015: /* fistp m32int */ if ( reg_store_int32() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 016: /* fstp m64real */ if ( reg_store_double() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 017: /* fistp m16int */ if ( reg_store_int16() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 020: /* fldenv m14/28byte */ fldenv(); break; case 022: /* frstor m94/108byte */ frstor(); break; case 023: /* fbld m80dec */ reg_load_bcd(); reg_move(&FPU_loaded_data, pop_ptr); break; case 024: /* fldcw */ RE_ENTRANT_CHECK_OFF control_word = get_fs_word((unsigned short *) FPU_data_address); RE_ENTRANT_CHECK_ON #ifdef NO_UNDERFLOW_TRAP if ( !(control_word & EX_Underflow) ) { control_word |= EX_Underflow; } #endif FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ FPU_entry_eip = ip_offset; /* We want no net effect */ break; case 025: /* fld m80real */ reg_load_extended(); reg_move(&FPU_loaded_data, pop_ptr); break; case 027: /* fild m64int */ reg_load_int64(); reg_move(&FPU_loaded_data, pop_ptr); break; case 030: /* fstenv m14/28byte */ fstenv(); FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ FPU_entry_eip = ip_offset; /* We want no net effect */ break; case 032: /* fsave */ fsave(); FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ FPU_entry_eip = ip_offset; /* We want no net effect */ break; case 033: /* fbstp m80dec */ if ( reg_store_bcd() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 034: /* fstcw m16int */ RE_ENTRANT_CHECK_OFF verify_area(VERIFY_WRITE,FPU_data_address,2); put_fs_word(control_word, (short *) FPU_data_address); RE_ENTRANT_CHECK_ON FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ FPU_entry_eip = ip_offset; /* We want no net effect */ break; case 035: /* fstp m80real */ if ( reg_store_extended() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 036: /* fstsw m2byte */ status_word &= ~SW_TOP; status_word |= (top&7) << SW_TOPS; RE_ENTRANT_CHECK_OFF verify_area(VERIFY_WRITE,FPU_data_address,2); put_fs_word(status_word,(short *) FPU_data_address); RE_ENTRANT_CHECK_ON FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ FPU_entry_eip = ip_offset; /* We want no net effect */ break; case 037: /* fistp m64int */ if ( reg_store_int64() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; } }