static void do_emu(struct info * info) { unsigned short code; temp_real tmp; char * address; if (I387.cwd & I387.swd & 0x3f) I387.swd |= 0x8000; else I387.swd &= 0x7fff; ORIG_EIP = EIP; /* 0x0007 means user code space */ if (CS != 0x000F) { printk("math_emulate: %04x:%08x\n\r",CS,EIP); panic("Math emulation needed in kernel"); } code = get_fs_word((unsigned short *) EIP); bswapw(code); code &= 0x7ff; I387.fip = EIP; *(unsigned short *) &I387.fcs = CS; *(1+(unsigned short *) &I387.fcs) = code; EIP += 2; switch (code) { case 0x1d0: /* fnop */ return; case 0x1d1: case 0x1d2: case 0x1d3: case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7: math_abort(info,1<<(SIGILL-1)); case 0x1e0: ST(0).exponent ^= 0x8000; return; case 0x1e1: ST(0).exponent &= 0x7fff; return; case 0x1e2: case 0x1e3: math_abort(info,1<<(SIGILL-1)); case 0x1e4: ftst(PST(0)); return; case 0x1e5: printk("fxam not implemented\n\r"); math_abort(info,1<<(SIGILL-1)); case 0x1e6: case 0x1e7: math_abort(info,1<<(SIGILL-1)); case 0x1e8: fpush(); ST(0) = CONST1; return; case 0x1e9: fpush(); ST(0) = CONSTL2T; return; case 0x1ea: fpush(); ST(0) = CONSTL2E; return; case 0x1eb: fpush(); ST(0) = CONSTPI; return; case 0x1ec: fpush(); ST(0) = CONSTLG2; return; case 0x1ed: fpush(); ST(0) = CONSTLN2; return; case 0x1ee: fpush(); ST(0) = CONSTZ; return; case 0x1ef: math_abort(info,1<<(SIGILL-1)); case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3: case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7: case 0x1f8: case 0x1f9: case 0x1fa: case 0x1fb: case 0x1fc: case 0x1fd: case 0x1fe: case 0x1ff: printk("%04x fxxx not implemented\n\r",code + 0xc800); math_abort(info,1<<(SIGILL-1)); case 0x2e9: fucom(PST(1),PST(0)); fpop(); fpop(); return; case 0x3d0: case 0x3d1: return; case 0x3e2: I387.swd &= 0x7f00; return; case 0x3e3: I387.cwd = 0x037f; I387.swd = 0x0000; I387.twd = 0x0000; return; case 0x3e4: return; case 0x6d9: fcom(PST(1),PST(0)); fpop(); fpop(); return; case 0x7e0: *(short *) &EAX = I387.swd; return; } switch (code >> 3) { case 0x18: fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return; case 0x19: fmul(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return; case 0x1a: fcom(PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return; case 0x1b: fcom(PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); fpop(); return; case 0x1c: real_to_real(&ST(code & 7),&tmp); tmp.exponent ^= 0x8000; fadd(PST(0),&tmp,&tmp); real_to_real(&tmp,&ST(0)); return; case 0x1d: ST(0).exponent ^= 0x8000; fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return; case 0x1e: fdiv(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return; case 0x1f: fdiv(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(0)); return; case 0x38: fpush(); ST(0) = ST((code & 7)+1); return; case 0x39: fxchg(&ST(0),&ST(code & 7)); return; case 0x3b: ST(code & 7) = ST(0); fpop(); return; case 0x98: fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return; case 0x99: fmul(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return; case 0x9a: fcom(PST(code & 7),PST(0)); return; case 0x9b: fcom(PST(code & 7),PST(0)); fpop(); return; case 0x9c: ST(code & 7).exponent ^= 0x8000; fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return; case 0x9d: real_to_real(&ST(0),&tmp); tmp.exponent ^= 0x8000; fadd(PST(code & 7),&tmp,&tmp); real_to_real(&tmp,&ST(code & 7)); return; case 0x9e: fdiv(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return; case 0x9f: fdiv(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); return; case 0xb8: printk("ffree not implemented\n\r"); math_abort(info,1<<(SIGILL-1)); case 0xb9: fxchg(&ST(0),&ST(code & 7)); return; case 0xba: ST(code & 7) = ST(0); return; case 0xbb: ST(code & 7) = ST(0); fpop(); return; case 0xbc: fucom(PST(code & 7),PST(0)); return; case 0xbd: fucom(PST(code & 7),PST(0)); fpop(); return; case 0xd8: fadd(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return; case 0xd9: fmul(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return; case 0xda: fcom(PST(code & 7),PST(0)); fpop(); return; case 0xdc: ST(code & 7).exponent ^= 0x8000; fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return; case 0xdd: real_to_real(&ST(0),&tmp); tmp.exponent ^= 0x8000; fadd(PST(code & 7),&tmp,&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return; case 0xde: fdiv(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return; case 0xdf: fdiv(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return; case 0xf8: printk("ffree not implemented\n\r"); math_abort(info,1<<(SIGILL-1)); fpop(); return; case 0xf9: fxchg(&ST(0),&ST(code & 7)); return; case 0xfa: case 0xfb: ST(code & 7) = ST(0); fpop(); return; } switch ((code>>3) & 0xe7) { case 0x22: put_short_real(PST(0),info,code); return; case 0x23: put_short_real(PST(0),info,code); fpop(); return; case 0x24: address = ea(info,code); for (code = 0 ; code < 7 ; code++) { ((long *) & I387)[code] = get_fs_long((unsigned long *) address); address += 4; } return; case 0x25: address = ea(info,code); *(unsigned short *) &I387.cwd = get_fs_word((unsigned short *) address); return; case 0x26: address = ea(info,code); verify_area(address,28); for (code = 0 ; code < 7 ; code++) { put_fs_long( ((long *) & I387)[code], (unsigned long *) address); address += 4; } return; case 0x27: address = ea(info,code); verify_area(address,2); put_fs_word(I387.cwd,(short *) address); return; case 0x62: put_long_int(PST(0),info,code); return; case 0x63: put_long_int(PST(0),info,code); fpop(); return; case 0x65: fpush(); get_temp_real(&tmp,info,code); real_to_real(&tmp,&ST(0)); return; case 0x67: put_temp_real(PST(0),info,code); fpop(); return; case 0xa2: put_long_real(PST(0),info,code); return; case 0xa3: put_long_real(PST(0),info,code); fpop(); return; case 0xa4: address = ea(info,code); for (code = 0 ; code < 27 ; code++) { ((long *) & I387)[code] = get_fs_long((unsigned long *) address); address += 4; } return; case 0xa6: address = ea(info,code); verify_area(address,108); for (code = 0 ; code < 27 ; code++) { put_fs_long( ((long *) & I387)[code], (unsigned long *) address); address += 4; } I387.cwd = 0x037f; I387.swd = 0x0000; I387.twd = 0x0000; return; case 0xa7: address = ea(info,code); verify_area(address,2); put_fs_word(I387.swd,(short *) address); return; case 0xe2: put_short_int(PST(0),info,code); return; case 0xe3: put_short_int(PST(0),info,code); fpop(); return; case 0xe4: fpush(); get_BCD(&tmp,info,code); real_to_real(&tmp,&ST(0)); return; case 0xe5: fpush(); get_longlong_int(&tmp,info,code); real_to_real(&tmp,&ST(0)); return; case 0xe6: put_BCD(PST(0),info,code); fpop(); return; case 0xe7: put_longlong_int(PST(0),info,code); fpop(); return; } switch (code >> 9) { case 0: get_short_real(&tmp,info,code); break; case 1: get_long_int(&tmp,info,code); break; case 2: get_long_real(&tmp,info,code); break; case 4: get_short_int(&tmp,info,code); } switch ((code>>3) & 0x27) { case 0: fadd(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return; case 1: fmul(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return; case 2: fcom(&tmp,PST(0)); return; case 3: fcom(&tmp,PST(0)); fpop(); return; case 4: tmp.exponent ^= 0x8000; fadd(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return; case 5: ST(0).exponent ^= 0x8000; fadd(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return; case 6: fdiv(PST(0),&tmp,&tmp); real_to_real(&tmp,&ST(0)); return; case 7: fdiv(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return; } if ((code & 0x138) == 0x100) { fpush(); real_to_real(&tmp,&ST(0)); return; } printk("Unknown math-insns: %04x:%08x %04x\n\r",CS,EIP,code); math_abort(info,1<<(SIGFPE-1)); }
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) {
/* Called for opcodes which are illegal and which are known to result in a SIGILL with a real 80486. */ void FPU_illegal(void) { math_abort(FPU_info, SIGILL); }
static int math_emulate(struct trapframe * info) { unsigned short code; temp_real tmp; char * address; u_long oldeip; /* ever used fp? */ if ((((struct pcb *)curproc->p_addr)->pcb_flags & FP_SOFTFP) == 0) { ((struct pcb *)curproc->p_addr)->pcb_flags |= FP_SOFTFP; I387.cwd = 0x037f; I387.swd = 0x0000; I387.twd = 0x0000; } if (I387.cwd & I387.swd & 0x3f) I387.swd |= 0x8000; else I387.swd &= 0x7fff; oldeip = info->tf_eip; /* 0x001f means user code space */ if ((u_short)info->tf_cs != 0x001F) { printf("math_emulate: %04x:%08lx\n", (u_short)info->tf_cs, oldeip); panic("?Math emulation needed in kernel?"); } code = get_fs_word((unsigned short *) oldeip); bswapw(code); code &= 0x7ff; I387.fip = oldeip; *(unsigned short *) &I387.fcs = (u_short) info->tf_cs; *(1+(unsigned short *) &I387.fcs) = code; info->tf_eip += 2; switch (code) { case 0x1d0: /* fnop */ return(0); case 0x1d1: case 0x1d2: case 0x1d3: /* fst to 32-bit mem */ case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7: math_abort(info,SIGILL); case 0x1e0: /* fchs */ ST(0).exponent ^= 0x8000; return(0); case 0x1e1: /* fabs */ ST(0).exponent &= 0x7fff; return(0); case 0x1e2: case 0x1e3: math_abort(info,SIGILL); case 0x1e4: /* ftst */ ftst(PST(0)); return(0); case 0x1e5: /* fxam */ printf("fxam not implemented\n"); math_abort(info,SIGILL); case 0x1e6: case 0x1e7: /* fldenv */ math_abort(info,SIGILL); case 0x1e8: /* fld1 */ fpush(); ST(0) = CONST1; return(0); case 0x1e9: /* fld2t */ fpush(); ST(0) = CONSTL2T; return(0); case 0x1ea: /* fld2e */ fpush(); ST(0) = CONSTL2E; return(0); case 0x1eb: /* fldpi */ fpush(); ST(0) = CONSTPI; return(0); case 0x1ec: /* fldlg2 */ fpush(); ST(0) = CONSTLG2; return(0); case 0x1ed: /* fldln2 */ fpush(); ST(0) = CONSTLN2; return(0); case 0x1ee: /* fldz */ fpush(); ST(0) = CONSTZ; return(0); case 0x1ef: math_abort(info,SIGILL); case 0x1f0: /* f2xm1 */ case 0x1f1: /* fyl2x */ case 0x1f2: /* fptan */ case 0x1f3: /* fpatan */ case 0x1f4: /* fxtract */ case 0x1f5: /* fprem1 */ case 0x1f6: /* fdecstp */ case 0x1f7: /* fincstp */ case 0x1f8: /* fprem */ case 0x1f9: /* fyl2xp1 */ case 0x1fa: /* fsqrt */ case 0x1fb: /* fsincos */ case 0x1fe: /* fsin */ case 0x1ff: /* fcos */ uprintf( "math_emulate: instruction %04x not implemented\n", code + 0xd800); math_abort(info,SIGILL); case 0x1fc: /* frndint */ frndint(PST(0),&tmp); real_to_real(&tmp,&ST(0)); return(0); case 0x1fd: /* fscale */ /* incomplete and totally inadequate -wfj */ Fscale(PST(0), PST(1), &tmp); real_to_real(&tmp,&ST(0)); return(0); /* 19 Sep 92*/ case 0x2e9: /* ????? */ /* if this should be a fucomp ST(0),ST(1) , it must be a 0x3e9 ATS */ fucom(PST(1),PST(0)); fpop(); fpop(); return(0); case 0x3d0: case 0x3d1: /* fist ?? */ return(0); case 0x3e2: /* fclex */ I387.swd &= 0x7f00; return(0); case 0x3e3: /* fninit */ I387.cwd = 0x037f; I387.swd = 0x0000; I387.twd = 0x0000; return(0); case 0x3e4: return(0); case 0x6d9: /* fcompp */ fcom(PST(1),PST(0)); fpop(); fpop(); return(0); case 0x7e0: /* fstsw ax */ *(short *) &info->tf_eax = I387.swd; return(0); } switch (code >> 3) { case 0x18: /* fadd */ fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return(0); case 0x19: /* fmul */ fmul(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return(0); case 0x1a: /* fcom */ fcom(PST(code & 7),PST(0)); return(0); case 0x1b: /* fcomp */ fcom(PST(code & 7),PST(0)); fpop(); return(0); case 0x1c: /* fsubr */ real_to_real(&ST(code & 7),&tmp); tmp.exponent ^= 0x8000; fadd(PST(0),&tmp,&tmp); real_to_real(&tmp,&ST(0)); return(0); case 0x1d: /* fsub */ ST(0).exponent ^= 0x8000; fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return(0); case 0x1e: /* fdivr */ fdiv(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return(0); case 0x1f: /* fdiv */ fdiv(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(0)); return(0); case 0x38: /* fld */ fpush(); ST(0) = ST((code & 7)+1); /* why plus 1 ????? ATS */ return(0); case 0x39: /* fxch */ fxchg(&ST(0),&ST(code & 7)); return(0); case 0x3b: /* ??? ??? wrong ???? ATS */ ST(code & 7) = ST(0); fpop(); return(0); case 0x98: /* fadd */ fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return(0); case 0x99: /* fmul */ fmul(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return(0); case 0x9a: /* ???? , my manual don't list a direction bit for fcom , ??? ATS */ fcom(PST(code & 7),PST(0)); return(0); case 0x9b: /* same as above , ATS */ fcom(PST(code & 7),PST(0)); fpop(); return(0); case 0x9c: /* fsubr */ ST(code & 7).exponent ^= 0x8000; fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return(0); case 0x9d: /* fsub */ real_to_real(&ST(0),&tmp); tmp.exponent ^= 0x8000; fadd(PST(code & 7),&tmp,&tmp); real_to_real(&tmp,&ST(code & 7)); return(0); case 0x9e: /* fdivr */ fdiv(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return(0); case 0x9f: /* fdiv */ fdiv(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); return(0); case 0xb8: /* ffree */ printf("ffree not implemented\n"); math_abort(info,SIGILL); case 0xb9: /* fstp ???? where is the pop ? ATS */ fxchg(&ST(0),&ST(code & 7)); return(0); case 0xba: /* fst */ ST(code & 7) = ST(0); return(0); case 0xbb: /* ????? encoding of fstp to mem ? ATS */ ST(code & 7) = ST(0); fpop(); return(0); case 0xbc: /* fucom */ fucom(PST(code & 7),PST(0)); return(0); case 0xbd: /* fucomp */ fucom(PST(code & 7),PST(0)); fpop(); return(0); case 0xd8: /* faddp */ fadd(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return(0); case 0xd9: /* fmulp */ fmul(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return(0); case 0xda: /* ??? encoding of ficom with 16 bit mem ? ATS */ fcom(PST(code & 7),PST(0)); fpop(); return(0); case 0xdc: /* fsubrp */ ST(code & 7).exponent ^= 0x8000; fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return(0); case 0xdd: /* fsubp */ real_to_real(&ST(0),&tmp); tmp.exponent ^= 0x8000; fadd(PST(code & 7),&tmp,&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return(0); case 0xde: /* fdivrp */ fdiv(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return(0); case 0xdf: /* fdivp */ fdiv(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return(0); case 0xf8: /* fild 16-bit mem ???? ATS */ printf("ffree not implemented\n"); math_abort(info,SIGILL); fpop(); return(0); case 0xf9: /* ????? ATS */ fxchg(&ST(0),&ST(code & 7)); return(0); case 0xfa: /* fist 16-bit mem ? ATS */ case 0xfb: /* fistp 16-bit mem ? ATS */ ST(code & 7) = ST(0); fpop(); return(0); } switch ((code>>3) & 0xe7) { case 0x22: put_short_real(PST(0),info,code); return(0); case 0x23: put_short_real(PST(0),info,code); fpop(); return(0); case 0x24: address = ea(info,code); for (code = 0 ; code < 7 ; code++) { ((long *) & I387)[code] = get_fs_long((unsigned long *) address); address += 4; } return(0); case 0x25: address = ea(info,code); *(unsigned short *) &I387.cwd = get_fs_word((unsigned short *) address); return(0); case 0x26: address = ea(info,code); /*verify_area(address,28);*/ for (code = 0 ; code < 7 ; code++) { put_fs_long( ((long *) & I387)[code], (unsigned long *) address); address += 4; } return(0); case 0x27: address = ea(info,code); /*verify_area(address,2);*/ put_fs_word(I387.cwd,(short *) address); return(0); case 0x62: put_long_int(PST(0),info,code); return(0); case 0x63: put_long_int(PST(0),info,code); fpop(); return(0); case 0x65: fpush(); get_temp_real(&tmp,info,code); real_to_real(&tmp,&ST(0)); return(0); case 0x67: put_temp_real(PST(0),info,code); fpop(); return(0); case 0xa2: put_long_real(PST(0),info,code); return(0); case 0xa3: put_long_real(PST(0),info,code); fpop(); return(0); case 0xa4: address = ea(info,code); for (code = 0 ; code < 27 ; code++) { ((long *) & I387)[code] = get_fs_long((unsigned long *) address); address += 4; } return(0); case 0xa6: address = ea(info,code); /*verify_area(address,108);*/ for (code = 0 ; code < 27 ; code++) { put_fs_long( ((long *) & I387)[code], (unsigned long *) address); address += 4; } I387.cwd = 0x037f; I387.swd = 0x0000; I387.twd = 0x0000; return(0); case 0xa7: address = ea(info,code); /*verify_area(address,2);*/ put_fs_word(I387.swd,(short *) address); return(0); case 0xe2: put_short_int(PST(0),info,code); return(0); case 0xe3: put_short_int(PST(0),info,code); fpop(); return(0); case 0xe4: fpush(); get_BCD(&tmp,info,code); real_to_real(&tmp,&ST(0)); return(0); case 0xe5: fpush(); get_longlong_int(&tmp,info,code); real_to_real(&tmp,&ST(0)); return(0); case 0xe6: put_BCD(PST(0),info,code); fpop(); return(0); case 0xe7: put_longlong_int(PST(0),info,code); fpop(); return(0); } switch (code >> 9) { case 0: get_short_real(&tmp,info,code); break; case 1: get_long_int(&tmp,info,code); break; case 2: get_long_real(&tmp,info,code); break; case 4: get_short_int(&tmp,info,code); } switch ((code>>3) & 0x27) { case 0: fadd(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return(0); case 1: fmul(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return(0); case 2: fcom(&tmp,PST(0)); return(0); case 3: fcom(&tmp,PST(0)); fpop(); return(0); case 4: tmp.exponent ^= 0x8000; fadd(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return(0); case 5: ST(0).exponent ^= 0x8000; fadd(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return(0); case 6: fdiv(PST(0),&tmp,&tmp); real_to_real(&tmp,&ST(0)); return(0); case 7: fdiv(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return(0); } if ((code & 0x138) == 0x100) { fpush(); real_to_real(&tmp,&ST(0)); return(0); } printf("Unknown math-insns: %04x:%08x %04x\n",(u_short)info->tf_cs, info->tf_eip,code); math_abort(info,SIGFPE); }
int FPU_load_store(u_char type, fpu_addr_modes addr_modes, void __user *data_address) { FPU_REG loaded_data; FPU_REG *st0_ptr; u_char st0_tag = TAG_Empty; /* This is just to stop a gcc warning. */ u_char loaded_tag; st0_ptr = NULL; /* Initialized just to stop compiler warnings. */ if ( addr_modes.default_mode & PROTECTED ) { if ( addr_modes.default_mode == SEG32 ) { if ( access_limit < data_sizes_32[type] ) math_abort(FPU_info,SIGSEGV); } else if ( addr_modes.default_mode == PM16 ) { if ( access_limit < data_sizes_16[type] ) math_abort(FPU_info,SIGSEGV); } #ifdef PARANOID else EXCEPTION(EX_INTERNAL|0x140); #endif /* PARANOID */ } switch ( type_table[type] ) { case _NONE_: break; case _REG0_: st0_ptr = &st(0); /* Some of these instructions pop after storing */ st0_tag = FPU_gettag0(); break; case _PUSH_: { if ( FPU_gettagi(-1) != TAG_Empty ) { FPU_stack_overflow(); return 0; } top--; st0_ptr = &st(0); } break; case _null_: FPU_illegal(); return 0; #ifdef PARANOID default: EXCEPTION(EX_INTERNAL|0x141); return 0; #endif /* PARANOID */ } switch ( type ) { case 000: /* fld m32real */ clear_C1(); loaded_tag = FPU_load_single((float __user *)data_address, &loaded_data); if ( (loaded_tag == TAG_Special) && isNaN(&loaded_data) && (real_1op_NaN(&loaded_data) < 0) ) { top++; break; } FPU_copy_to_reg0(&loaded_data, loaded_tag); break; case 001: /* fild m32int */ clear_C1(); loaded_tag = FPU_load_int32((long __user *)data_address, &loaded_data); FPU_copy_to_reg0(&loaded_data, loaded_tag); break; case 002: /* fld m64real */ clear_C1(); loaded_tag = FPU_load_double((double __user *)data_address, &loaded_data); if ( (loaded_tag == TAG_Special) && isNaN(&loaded_data) && (real_1op_NaN(&loaded_data) < 0) ) { top++; break; } FPU_copy_to_reg0(&loaded_data, loaded_tag); break; case 003: /* fild m16int */ clear_C1(); loaded_tag = FPU_load_int16((short __user *)data_address, &loaded_data); FPU_copy_to_reg0(&loaded_data, loaded_tag); break; case 010: /* fst m32real */ clear_C1(); FPU_store_single(st0_ptr, st0_tag, (float __user *)data_address); break; case 011: /* fist m32int */ clear_C1(); FPU_store_int32(st0_ptr, st0_tag, (long __user *)data_address); break; case 012: /* fst m64real */ clear_C1(); FPU_store_double(st0_ptr, st0_tag, (double __user *)data_address); break; case 013: /* fist m16int */ clear_C1(); FPU_store_int16(st0_ptr, st0_tag, (short __user *)data_address); break; case 014: /* fstp m32real */ clear_C1(); if ( FPU_store_single(st0_ptr, st0_tag, (float __user *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 015: /* fistp m32int */ clear_C1(); if ( FPU_store_int32(st0_ptr, st0_tag, (long __user *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 016: /* fstp m64real */ clear_C1(); if ( FPU_store_double(st0_ptr, st0_tag, (double __user *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 017: /* fistp m16int */ clear_C1(); if ( FPU_store_int16(st0_ptr, st0_tag, (short __user *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 020: /* fldenv m14/28byte */ fldenv(addr_modes, (u_char __user *)data_address); /* Ensure that the values just loaded are not changed by fix-up operations. */ return 1; case 022: /* frstor m94/108byte */ frstor(addr_modes, (u_char __user *)data_address); /* Ensure that the values just loaded are not changed by fix-up operations. */ return 1; case 023: /* fbld m80dec */ clear_C1(); loaded_tag = FPU_load_bcd((u_char __user *)data_address); FPU_settag0(loaded_tag); break; case 024: /* fldcw */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, data_address, 2); FPU_get_user(control_word, (unsigned short __user *) data_address); RE_ENTRANT_CHECK_ON; if ( partial_status & ~control_word & CW_Exceptions ) partial_status |= (SW_Summary | SW_Backward); else partial_status &= ~(SW_Summary | SW_Backward); #ifdef PECULIAR_486 control_word |= 0x40; /* An 80486 appears to always set this bit */ #endif /* PECULIAR_486 */ return 1; case 025: /* fld m80real */ clear_C1(); loaded_tag = FPU_load_extended((long double __user *)data_address, 0); FPU_settag0(loaded_tag); break; case 027: /* fild m64int */ clear_C1(); loaded_tag = FPU_load_int64((long long __user *)data_address); FPU_settag0(loaded_tag); break; case 030: /* fstenv m14/28byte */ fstenv(addr_modes, (u_char __user *)data_address); return 1; case 032: /* fsave */ fsave(addr_modes, (u_char __user *)data_address); return 1; case 033: /* fbstp m80dec */ clear_C1(); if ( FPU_store_bcd(st0_ptr, st0_tag, (u_char __user *)data_address) ) 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; FPU_verify_area(VERIFY_WRITE,data_address,2); FPU_put_user(control_word, (unsigned short __user *) data_address); RE_ENTRANT_CHECK_ON; return 1; case 035: /* fstp m80real */ clear_C1(); if ( FPU_store_extended(st0_ptr, st0_tag, (long double __user *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 036: /* fstsw m2byte */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,data_address,2); FPU_put_user(status_word(),(unsigned short __user *) data_address); RE_ENTRANT_CHECK_ON; return 1; case 037: /* fistp m64int */ clear_C1(); if ( FPU_store_int64(st0_ptr, st0_tag, (long long __user *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; } return 0; }