From 893c4d2f2496201b637c3aa2da0d20aaa7f0cf98 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:09:31 -0500 Subject: [PATCH] Import 1.1.18 --- Makefile | 2 +- drivers/FPU-emu/Makefile | 3 +- drivers/FPU-emu/README | 22 +- drivers/FPU-emu/errors.c | 84 +++-- drivers/FPU-emu/fpu_arith.c | 44 +-- drivers/FPU-emu/fpu_aux.c | 35 +- drivers/FPU-emu/fpu_emu.h | 59 ++-- drivers/FPU-emu/fpu_entry.c | 250 ++++++++------ drivers/FPU-emu/fpu_etc.c | 40 ++- drivers/FPU-emu/fpu_proto.h | 51 +-- drivers/FPU-emu/fpu_system.h | 36 +- drivers/FPU-emu/fpu_trig.c | 546 +++++++++++++++-------------- drivers/FPU-emu/get_address.c | 267 +++++++++----- drivers/FPU-emu/load_store.c | 359 ++++++++++--------- drivers/FPU-emu/poly_atan.c | 8 - drivers/FPU-emu/reg_compare.c | 77 ++--- drivers/FPU-emu/reg_constant.c | 2 +- drivers/FPU-emu/reg_div.S | 8 +- drivers/FPU-emu/reg_ld_str.c | 434 +++++++++++------------ drivers/FPU-emu/reg_round.S | 12 +- drivers/FPU-emu/reg_u_div.S | 8 +- drivers/FPU-emu/reg_u_mul.S | 8 +- drivers/FPU-emu/version.h | 3 +- drivers/FPU-emu/wm_sqrt.S | 8 +- drivers/block/README.sbpcd | 276 ++++++++------- drivers/block/blk.h | 3 + drivers/block/ll_rw_blk.c | 4 - drivers/block/sbpcd.c | 615 ++++++++++++++++++++++++--------- drivers/net/Space.c | 8 + drivers/net/ppp.c | 32 +- drivers/scsi/buslogic.c | 20 +- fs/buffer.c | 9 + include/linux/sbpcd.h | 36 +- kernel/Makefile | 2 +- kernel/sched.c | 2 + kernel/time.c | 81 +++-- mm/memory.c | 19 +- net/inet/af_inet.c | 4 +- net/inet/arp.c | 14 +- net/inet/dev.c | 4 +- net/inet/ip.c | 5 +- 41 files changed, 2029 insertions(+), 1471 deletions(-) diff --git a/Makefile b/Makefile index 094edc39cadb..d5c87f4ecba1 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 1 -SUBLEVEL = 17 +SUBLEVEL = 18 all: Version zImage diff --git a/drivers/FPU-emu/Makefile b/drivers/FPU-emu/Makefile index 3563c1d4b968..544c357cec62 100644 --- a/drivers/FPU-emu/Makefile +++ b/drivers/FPU-emu/Makefile @@ -5,14 +5,13 @@ #DEBUG = -DDEBUGGING DEBUG = PARANOID = -DPARANOID -REENTRANT = -DREENTRANT_FPU CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin .c.o: $(CC) $(CFLAGS) $(MATH_EMULATION) -c $< .S.o: - $(CC) -D__ASSEMBLER__ $(PARANOID) $(REENTRANT) -c $< + $(CC) -D__ASSEMBLER__ $(PARANOID) -c $< .s.o: $(CC) -c $< diff --git a/drivers/FPU-emu/README b/drivers/FPU-emu/README index efe0367528c1..032f8d1d52ce 100644 --- a/drivers/FPU-emu/README +++ b/drivers/FPU-emu/README @@ -47,7 +47,7 @@ Please report bugs, etc to me at: --Bill Metzenthen - March 1994 + June 1994 ----------------------- Internals of wm-FPU-emu ----------------------- @@ -80,20 +80,24 @@ emulate each FPU instruction to completion without interruption. However, it may happen that when the emulator is accessing the user memory space, swapping may be needed. In this case the emulator may be temporarily suspended while disk i/o takes place. During this time -another process may use the emulator, thereby changing some static -variables (eg FPU_st0_ptr, etc). The code which accesses user memory -is confined to five files: +another process may use the emulator, thereby perhaps changing static +variables. The code which accesses user memory is confined to five +files: fpu_entry.c reg_ld_str.c load_store.c get_address.c errors.c +As from version 1.12 of the emulator, no static variables are used +(apart from those in the kernel's per-process tables). The emulator is +therefore now fully re-entrant, rather than having just the restricted +form of re-entrancy which is required by the Linux kernel. ----------------------- Limitations of wm-FPU-emu ----------------------- There are a number of differences between the current wm-FPU-emu -(version beta 1.11) and the 80486 FPU (apart from bugs). Some of the -more important differences are listed below: +(version 1.12) and the 80486 FPU (apart from bugs). Some of the more +important differences are listed below: The Roundup flag does not have much meaning for the transcendental functions and its 80486 value with these functions is likely to differ @@ -154,6 +158,11 @@ crashes dosemu under Linux and also brings Windows down with a general protection fault message when run under the MS-DOS prompt of Windows 3.1. (The program simply reads data from a valid address). +The emulator supports 16-bit protected mode, with one difference from +an 80486DX. A 80486DX will allow some floating point instructions to +write a few bytes below the lowest address of the stack. The emulator +will not allow this in 16-bit protected mode: no instructions are +allowed to write outside the bounds set by the protection. ----------------------- Performance of wm-FPU-emu ----------------------- @@ -322,6 +331,7 @@ Hamish Coleman, t933093@minyos.xx.rmit.oz.au Bruce Evans, bde@kralizec.zeta.org.au Timo Korvola, Timo.Korvola@hut.fi Rick Lyons, rick@razorback.brisnet.org.au +Rick, jrs@world.std.com ...and numerous others who responded to my request for help with a real 80486. diff --git a/drivers/FPU-emu/errors.c b/drivers/FPU-emu/errors.c index f8f9554bc67c..ddc589a952a2 100644 --- a/drivers/FPU-emu/errors.c +++ b/drivers/FPU-emu/errors.c @@ -42,20 +42,28 @@ void Un_impl(void) RE_ENTRANT_CHECK_OFF; /* No need to verify_area(), we have previously fetched these bytes. */ printk("Unimplemented FPU Opcode at eip=%p : ", (void *) address); - while ( 1 ) + if ( FPU_CS == USER_CS ) { - byte1 = get_fs_byte((unsigned char *) address); - if ( (byte1 & 0xf8) == 0xd8 ) break; - printk("[%02x]", byte1); - address++; + while ( 1 ) + { + byte1 = get_fs_byte((unsigned char *) address); + if ( (byte1 & 0xf8) == 0xd8 ) break; + printk("[%02x]", byte1); + address++; + } + printk("%02x ", byte1); + FPU_modrm = get_fs_byte(1 + (unsigned char *) address); + + if (FPU_modrm >= 0300) + printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); + else + printk("/%d\n", (FPU_modrm >> 3) & 7); } - printk("%02x ", byte1); - FPU_modrm = get_fs_byte(1 + (unsigned char *) address); - - if (FPU_modrm >= 0300) - printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); else - printk("/%d\n", (FPU_modrm >> 3) & 7); + { + printk("cs selector = %04x\n", FPU_CS); + } + RE_ENTRANT_CHECK_ON; EXCEPTION(EX_Invalid); @@ -85,29 +93,36 @@ void emu_printall() RE_ENTRANT_CHECK_OFF; /* No need to verify_area(), we have previously fetched these bytes. */ printk("At %p:", (void *) address); -#define MAX_PRINTED_BYTES 20 - for ( i = 0; i < MAX_PRINTED_BYTES; i++ ) + if ( FPU_CS == USER_CS ) { - byte1 = get_fs_byte((unsigned char *) address); - if ( (byte1 & 0xf8) == 0xd8 ) +#define MAX_PRINTED_BYTES 20 + for ( i = 0; i < MAX_PRINTED_BYTES; i++ ) { - printk(" %02x", byte1); - break; + byte1 = get_fs_byte((unsigned char *) address); + if ( (byte1 & 0xf8) == 0xd8 ) + { + printk(" %02x", byte1); + break; + } + printk(" [%02x]", byte1); + address++; + } + if ( i == MAX_PRINTED_BYTES ) + printk(" [more..]\n"); + else + { + FPU_modrm = get_fs_byte(1 + (unsigned char *) address); + + if (FPU_modrm >= 0300) + printk(" %02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); + else + printk(" /%d, mod=%d rm=%d\n", + (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7); } - printk(" [%02x]", byte1); - address++; } - if ( i == MAX_PRINTED_BYTES ) - printk(" [more..]\n"); else { - FPU_modrm = get_fs_byte(1 + (unsigned char *) address); - - if (FPU_modrm >= 0300) - printk(" %02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7); - else - printk(" /%d, mod=%d rm=%d\n", - (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7); + printk("%04x\n", FPU_CS); } partial_status = status_word(); @@ -181,6 +196,7 @@ printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld iem=%d ef=%d%d%d%d%d%d\n", printk("%s\n", tag_desc[(int) (unsigned) r->tag]); } +#ifdef OBSOLETE printk("[data] %c .%04lx %04lx %04lx %04lx e%+-6ld ", FPU_loaded_data.sign ? '-' : '+', (long)(FPU_loaded_data.sigh >> 16), @@ -189,6 +205,7 @@ printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld iem=%d ef=%d%d%d%d%d%d\n", (long)(FPU_loaded_data.sigl & 0xFFFF), FPU_loaded_data.exp - EXP_BIAS + 1); printk("%s\n", tag_desc[(int) (unsigned) FPU_loaded_data.tag]); +#endif OBSOLETE RE_ENTRANT_CHECK_ON; } @@ -214,7 +231,6 @@ static struct { error was detected. Internal error types: - 0 in load_store.c 0x14 in fpu_etc.c 0x1nn in a *.c file: 0x101 in reg_add_sub.c @@ -244,7 +260,13 @@ static struct { 0x126 in fpu_entry.c 0x127 in poly_2xm1.c 0x128 in fpu_entry.c + 0x129 in fpu_entry.c 0x130 in get_address.c + 0x131 in get_address.c + 0x132 in get_address.c + 0x133 in get_address.c + 0x140 in load_store.c + 0x141 in load_store.c 0x2nn in an *.S file: 0x201 in reg_u_add.S 0x202 in reg_u_div.S @@ -583,7 +605,7 @@ void stack_overflow(void) { /* The masked response */ top--; - reg_move(&CONST_QNaN, FPU_st0_ptr = &st(0)); + reg_move(&CONST_QNaN, &st(0)); } EXCEPTION(EX_StackOver); @@ -599,7 +621,7 @@ void stack_underflow(void) if ( control_word & CW_Invalid ) { /* The masked response */ - reg_move(&CONST_QNaN, FPU_st0_ptr); + reg_move(&CONST_QNaN, &st(0)); } EXCEPTION(EX_StackUnder); diff --git a/drivers/FPU-emu/fpu_arith.c b/drivers/FPU-emu/fpu_arith.c index 3871a56a7243..96e6bd89ba6f 100644 --- a/drivers/FPU-emu/fpu_arith.c +++ b/drivers/FPU-emu/fpu_arith.c @@ -20,7 +20,7 @@ void fadd__() { /* fadd st,st(i) */ clear_C1(); - reg_add(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); + reg_add(&st(0), &st(FPU_rm), &st(0), control_word); } @@ -28,7 +28,7 @@ void fmul__() { /* fmul st,st(i) */ clear_C1(); - reg_mul(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); + reg_mul(&st(0), &st(FPU_rm), &st(0), control_word); } @@ -37,7 +37,7 @@ void fsub__() { /* fsub st,st(i) */ clear_C1(); - reg_sub(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); + reg_sub(&st(0), &st(FPU_rm), &st(0), control_word); } @@ -45,7 +45,7 @@ void fsubr_() { /* fsubr st,st(i) */ clear_C1(); - reg_sub(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word); + reg_sub(&st(FPU_rm), &st(0), &st(0), control_word); } @@ -53,7 +53,7 @@ void fdiv__() { /* fdiv st,st(i) */ clear_C1(); - reg_div(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); + reg_div(&st(0), &st(FPU_rm), &st(0), control_word); } @@ -61,7 +61,7 @@ void fdivr_() { /* fdivr st,st(i) */ clear_C1(); - reg_div(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word); + reg_div(&st(FPU_rm), &st(0), &st(0), control_word); } @@ -70,7 +70,7 @@ void fadd_i() { /* fadd st(i),st */ clear_C1(); - reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); + reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); } @@ -78,7 +78,7 @@ void fmul_i() { /* fmul st(i),st */ clear_C1(); - reg_mul(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); + reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); } @@ -86,9 +86,9 @@ void fsubri() { /* fsubr st(i),st */ /* This is the sense of the 80486 manual - reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */ + reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */ clear_C1(); - reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); + reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); } @@ -96,9 +96,9 @@ void fsub_i() { /* fsub st(i),st */ /* This is the sense of the 80486 manual - reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */ + reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */ clear_C1(); - reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); + reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); } @@ -106,7 +106,7 @@ void fdivri() { /* fdivr st(i),st */ clear_C1(); - reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); + reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); } @@ -114,7 +114,7 @@ void fdiv_i() { /* fdiv st(i),st */ clear_C1(); - reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); + reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); } @@ -123,7 +123,7 @@ void faddp_() { /* faddp st(i),st */ clear_C1(); - if ( !reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) ) + if ( !reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) pop(); } @@ -132,7 +132,7 @@ void fmulp_() { /* fmulp st(i),st */ clear_C1(); - if ( !reg_mul(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) ) + if ( !reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) pop(); } @@ -142,9 +142,9 @@ void fsubrp() { /* fsubrp st(i),st */ /* This is the sense of the 80486 manual - reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */ + reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */ clear_C1(); - if ( !reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) ) + if ( !reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) pop(); } @@ -153,9 +153,9 @@ void fsubp_() { /* fsubp st(i),st */ /* This is the sense of the 80486 manual - reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */ + reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */ clear_C1(); - if ( !reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word) ) + if ( !reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) ) pop(); } @@ -164,7 +164,7 @@ void fdivrp() { /* fdivrp st(i),st */ clear_C1(); - if ( !reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word) ) + if ( !reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) ) pop(); } @@ -173,7 +173,7 @@ void fdivp_() { /* fdivp st(i),st */ clear_C1(); - if ( !reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word) ) + if ( !reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) ) pop(); } diff --git a/drivers/FPU-emu/fpu_aux.c b/drivers/FPU-emu/fpu_aux.c index d9f3a2208501..0d35fe19b706 100644 --- a/drivers/FPU-emu/fpu_aux.c +++ b/drivers/FPU-emu/fpu_aux.c @@ -26,8 +26,7 @@ void fclex(void) partial_status &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision| SW_Underflow|SW_Overflow|SW_Zero_Div|SW_Denorm_Op| SW_Invalid); - NO_NET_DATA_EFFECT; - FPU_entry_eip = ip_offset; /* We want no net effect */ + no_ip_update = 1; } /* Needs to be externally visible */ @@ -43,11 +42,12 @@ void finit() } /* The behaviour is different to that detailed in Section 15.1.6 of the Intel manual */ - data_operand_offset = 0; - operand_selector = 0; - NO_NET_DATA_EFFECT; - FPU_entry_op_cs = 0; - FPU_entry_eip = ip_offset = 0; + operand_address.offset = 0; + operand_address.selector = 0; + instruction_address.offset = 0; + instruction_address.selector = 0; + instruction_address.opcode = 0; + no_ip_update = 1; } /* @@ -71,7 +71,7 @@ void finit_() static void fstsw_ax(void) { *(short *) &FPU_EAX = status_word(); - NO_NET_INSTR_EFFECT; + no_ip_update = 1; } static FUNC const fstsw_table[] = { @@ -108,10 +108,9 @@ void fld_i_() { reg_move(&st(FPU_rm), st_new_ptr); push(); } else { - if ( control_word & EX_Invalid ) + if ( control_word & CW_Invalid ) { /* The masked response */ - push(); stack_underflow(); } else @@ -125,9 +124,9 @@ void fxch_i() { /* fxch st(i) */ FPU_REG t; - register FPU_REG *sti_ptr = &st(FPU_rm); + register FPU_REG *sti_ptr = &st(FPU_rm), *st0_ptr = &st(0); - if ( FPU_st0_tag == TW_Empty ) + if ( st0_ptr->tag == TW_Empty ) { if ( sti_ptr->tag == TW_Empty ) { @@ -136,20 +135,20 @@ void fxch_i() return; } if ( control_word & CW_Invalid ) - reg_move(sti_ptr, FPU_st0_ptr); /* Masked response */ + reg_move(sti_ptr, st0_ptr); /* Masked response */ stack_underflow_i(FPU_rm); return; } if ( sti_ptr->tag == TW_Empty ) { if ( control_word & CW_Invalid ) - reg_move(FPU_st0_ptr, sti_ptr); /* Masked response */ + reg_move(st0_ptr, sti_ptr); /* Masked response */ stack_underflow(); return; } clear_C1(); - reg_move(FPU_st0_ptr, &t); - reg_move(sti_ptr, FPU_st0_ptr); + reg_move(st0_ptr, &t); + reg_move(sti_ptr, st0_ptr); reg_move(&t, sti_ptr); } @@ -172,14 +171,14 @@ void ffreep() void fst_i_() { /* fst st(i) */ - reg_move(FPU_st0_ptr, &st(FPU_rm)); + reg_move(&st(0), &st(FPU_rm)); } void fstp_i() { /* fstp st(i) */ - reg_move(FPU_st0_ptr, &st(FPU_rm)); + reg_move(&st(0), &st(FPU_rm)); pop(); } diff --git a/drivers/FPU-emu/fpu_emu.h b/drivers/FPU-emu/fpu_emu.h index f4921c5b284d..a0d1387d521d 100644 --- a/drivers/FPU-emu/fpu_emu.h +++ b/drivers/FPU-emu/fpu_emu.h @@ -60,14 +60,18 @@ #include #include -#ifdef PARANOID +/* +#define RE_ENTRANT_CHECKING + */ + +#ifdef RE_ENTRANT_CHECKING extern char emulating; # define RE_ENTRANT_CHECK_OFF emulating = 0 # define RE_ENTRANT_CHECK_ON emulating = 1 #else # define RE_ENTRANT_CHECK_OFF # define RE_ENTRANT_CHECK_ON -#endif PARANOID +#endif RE_ENTRANT_CHECKING #define FWAIT_OPCODE 0x9b #define OP_SIZE_PREFIX 0x66 @@ -89,46 +93,43 @@ extern char emulating; #define PREFIX_SS_ 6 #define PREFIX_DEFAULT 7 -/* These are to defeat the default action, giving the instruction - no net effect: */ -#define NO_NET_DATA_EFFECT \ - { FPU_data_address = (void *)data_operand_offset; \ - FPU_data_selector = operand_selector; } -#define NO_NET_INSTR_EFFECT \ - { FPU_entry_eip = ip_offset; \ - FPU_entry_op_cs = cs_selector; } - - +struct address { + unsigned int offset; + unsigned int selector:16; + unsigned int opcode:11; + unsigned int empty:5; +}; typedef void (*FUNC)(void); typedef struct fpu_reg FPU_REG; +typedef void (*FUNC_ST0)(FPU_REG *st0_ptr); typedef struct { unsigned char address_size, operand_size, segment; } overrides; -/* This structure is 48 bits: */ +/* This structure is 32 bits: */ typedef struct { overrides override; - unsigned char mode16, vm86, p286; } fpu_addr_modes; + unsigned char default_mode; } fpu_addr_modes; +/* PROTECTED has a restricted meaning in the emulator; it is used + to signal that the emulator needs to do special things to ensure + that protection is respected in a segmented model. */ +#define PROTECTED 4 +#define SIXTEEN 1 /* We rely upon this being 1 (true) */ +#define VM86 SIXTEEN +#define PM16 (SIXTEEN | PROTECTED) +#define SEG32 PROTECTED +extern unsigned char const data_sizes_16[32]; #define st(x) ( regs[((top+x) &7 )] ) #define STACK_OVERFLOW (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty) #define NOT_EMPTY(i) (st(i).tag != TW_Empty) -#define NOT_EMPTY_0 (FPU_st0_tag ^ TW_Empty) - -extern unsigned char FPU_rm; - -extern char FPU_st0_tag; -extern FPU_REG *FPU_st0_ptr; - -/* ###### These need to be shifted to somewhere safe. */ -/* extern void *FPU_data_address; has been shifted */ -extern unsigned short FPU_data_selector; -extern unsigned long FPU_entry_op_cs; - -extern FPU_REG FPU_loaded_data; +#define NOT_EMPTY_ST0 (st0_tag ^ TW_Empty) -#define pop() { FPU_st0_ptr->tag = TW_Empty; top++; } +#define pop() { regs[(top++ & 7 )].tag = TW_Empty; } +#define poppop() { regs[((top + 1) & 7 )].tag \ + = regs[(top & 7 )].tag = TW_Empty; \ + top += 2; } /* push() does not affect the tags */ -#define push() { top--; FPU_st0_ptr = st_new_ptr; } +#define push() { top--; } #define reg_move(x, y) { \ diff --git a/drivers/FPU-emu/fpu_entry.c b/drivers/FPU-emu/fpu_entry.c index f68b13936f3f..994733133efa 100644 --- a/drivers/FPU-emu/fpu_entry.c +++ b/drivers/FPU-emu/fpu_entry.c @@ -122,39 +122,36 @@ static unsigned char const type_table[64] = { #endif NO_UNDOC_CODE -/* Be careful when using any of these global variables... - they might change if swapping is triggered */ -unsigned char FPU_rm; -char FPU_st0_tag; -FPU_REG *FPU_st0_ptr; - -/* ######## To be shifted */ -unsigned long FPU_entry_op_cs; -unsigned short FPU_data_selector; - - -#ifdef PARANOID +#ifdef RE_ENTRANT_CHECKING char emulating=0; -#endif PARANOID +#endif RE_ENTRANT_CHECKING static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip, overrides *override); - asmlinkage void math_emulate(long arg) { unsigned char FPU_modrm, byte1; unsigned short code; fpu_addr_modes addr_modes; int unmasked; - -#ifdef PARANOID + FPU_REG loaded_data; + void *data_address; + struct address data_sel_off; + struct address entry_sel_off; + unsigned long code_base = 0; + unsigned long code_limit = 0; /* Initialized to stop compiler warnings */ + char st0_tag; + FPU_REG *st0_ptr; + struct desc_struct code_descriptor; + +#ifdef RE_ENTRANT_CHECKING if ( emulating ) { printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n"); } RE_ENTRANT_CHECK_ON; -#endif PARANOID +#endif RE_ENTRANT_CHECKING if (!current->used_math) { @@ -172,34 +169,51 @@ asmlinkage void math_emulate(long arg) SETUP_DATA_AREA(arg); - addr_modes.vm86 = (FPU_EFLAGS & 0x00020000) != 0; - addr_modes.p286 = (!addr_modes.vm86 - && current->ldt - && (current->ldt[FPU_CS >> 3].b & 0xf000) == 0xf000 - && (current->ldt[FPU_CS >> 3].b & (1 << 22)) == 0); - addr_modes.mode16 = addr_modes.vm86 | addr_modes.p286; - - if ( addr_modes.vm86 ) - FPU_EIP += FPU_CS << 4; - else if ( addr_modes.p286 ) - FPU_EIP += LDT_BASE_ADDR(FPU_CS); - FPU_ORIG_EIP = FPU_EIP; - if ( !addr_modes.mode16 ) + if ( (FPU_EFLAGS & 0x00020000) != 0 ) + { + /* Virtual 8086 mode */ + addr_modes.default_mode = VM86; + FPU_EIP += code_base = FPU_CS << 4; + code_limit = code_base + 0xffff; /* Assumes code_base <= 0xffff0000 */ + } + else if ( FPU_CS == USER_CS && FPU_DS == USER_DS ) + { + addr_modes.default_mode = 0; + } + else if ( FPU_CS == KERNEL_CS ) + { + printk("math_emulate: %04x:%08lx\n",FPU_CS,FPU_EIP); + panic("Math emulation needed in kernel"); + } + else { - /* user code space? */ - if (FPU_CS == KERNEL_CS) + + if ( (FPU_CS & 4) != 4 ) /* Must be in the LDT */ { - printk("math_emulate: %04x:%08lx\n",FPU_CS,FPU_EIP); - panic("Math emulation needed in kernel"); + /* Can only handle segmented addressing via the LDT + for now, and it must be 16 bit */ + printk("FPU emulator: Unsupported addressing mode\n"); + math_abort(FPU_info, SIGILL); } - /* We cannot handle multiple segments yet */ - if (FPU_CS != USER_CS || FPU_DS != USER_DS) + if ( SEG_D_SIZE(code_descriptor = LDT_DESCRIPTOR(FPU_CS)) ) + { + /* The above test may be wrong, the book is not clear */ + /* Segmented 32 bit protected mode */ + addr_modes.default_mode = SEG32; + } + else { - math_abort(FPU_info,SIGILL); + /* 16 bit protected mode */ + addr_modes.default_mode = PM16; } + FPU_EIP += code_base = SEG_BASE_ADDR(code_descriptor); + code_limit = code_base + + (SEG_LIMIT(code_descriptor)+1) * SEG_GRANULARITY(code_descriptor) + - 1; + if ( code_limit < code_base ) code_limit = 0xffffffff; } FPU_lookahead = 1; @@ -220,14 +234,17 @@ asmlinkage void math_emulate(long arg) do_another_FPU_instruction: + no_ip_update = 0; + FPU_EIP++; /* We have fetched the prefix and first code bytes. */ -#ifdef PECULIAR_486 - /* It would be more logical to do this only in get_address(), - but although it is supposed to be undefined for many fpu - instructions, an 80486 behaves as if this were done here: */ - FPU_data_selector = FPU_DS; -#endif PECULIAR_486 + if ( addr_modes.default_mode ) + { + /* This checks for the minimum instruction bytes. + We also need to check any extra (address mode) code access. */ + if ( FPU_EIP > code_limit ) + math_abort(FPU_info,SIGSEGV); + } if ( (byte1 & 0xf8) != 0xd8 ) { @@ -273,9 +290,8 @@ do_another_FPU_instruction: * via the cs selector and operand selector, so we do the same. */ do_the_FPU_interrupt: - cs_selector &= 0xffff0000; - cs_selector |= status_word(); - operand_selector = tag_word(); + instruction_address.selector = status_word(); + operand_address.selector = tag_word(); partial_status = 0; top = 0; { @@ -288,11 +304,6 @@ do_another_FPU_instruction: FPU_EIP = FPU_ORIG_EIP; /* Point to current FPU instruction. */ - if ( addr_modes.vm86 ) - FPU_EIP -= FPU_CS << 4; - else if ( addr_modes.p286 ) - FPU_EIP -= LDT_BASE_ADDR(FPU_CS); - RE_ENTRANT_CHECK_OFF; current->tss.trap_no = 16; current->tss.error_code = 0; @@ -301,57 +312,74 @@ do_another_FPU_instruction: } } - FPU_entry_eip = FPU_ORIG_EIP; - - FPU_entry_op_cs = (byte1 << 24) | (FPU_modrm << 16) | (FPU_CS & 0xffff) ; + entry_sel_off.offset = FPU_ORIG_EIP; + entry_sel_off.selector = FPU_CS; + entry_sel_off.opcode = (byte1 << 8) | FPU_modrm; FPU_rm = FPU_modrm & 7; if ( FPU_modrm < 0300 ) { /* All of these instructions use the mod/rm byte to get a data address */ - if ( addr_modes.vm86 + + if ( (addr_modes.default_mode & SIXTEEN) ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX) ) - get_address_16(FPU_modrm, &FPU_EIP, addr_modes); + data_address = get_address_16(FPU_modrm, &FPU_EIP, &data_sel_off, + addr_modes); else - get_address(FPU_modrm, &FPU_EIP, addr_modes); + data_address = get_address(FPU_modrm, &FPU_EIP, &data_sel_off, + addr_modes); + + if ( addr_modes.default_mode ) + { + if ( FPU_EIP-1 > code_limit ) + math_abort(FPU_info,SIGSEGV); + } if ( !(byte1 & 1) ) { unsigned short status1 = partial_status; - FPU_st0_ptr = &st(0); - FPU_st0_tag = FPU_st0_ptr->tag; + + st0_ptr = &st(0); + st0_tag = st0_ptr->tag; /* Stack underflow has priority */ - if ( NOT_EMPTY_0 ) + if ( NOT_EMPTY_ST0 ) { + if ( addr_modes.default_mode & PROTECTED ) + { + /* This table works for 16 and 32 bit protected mode */ + if ( access_limit < data_sizes_16[(byte1 >> 1) & 3] ) + math_abort(FPU_info,SIGSEGV); + } + unmasked = 0; /* Do this here to stop compiler warnings. */ switch ( (byte1 >> 1) & 3 ) { case 0: - unmasked = reg_load_single(); + unmasked = reg_load_single((float *)data_address, + &loaded_data); break; case 1: - reg_load_int32(); + reg_load_int32((long *)data_address, &loaded_data); break; case 2: - unmasked = reg_load_double(); + unmasked = reg_load_double((double *)data_address, + &loaded_data); break; case 3: - reg_load_int16(); + reg_load_int16((short *)data_address, &loaded_data); 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) ) + if ( (st0_tag == TW_NaN) || + (loaded_data.tag == TW_NaN) ) { /* Restore the status word; we might have loaded a denormal. */ @@ -371,13 +399,13 @@ do_another_FPU_instruction: identical to an 80486 */ if ( (FPU_modrm & 0x28) == 0x20 ) /* fdiv or fsub */ - real_2op_NaN(&FPU_loaded_data, FPU_st0_ptr, - FPU_st0_ptr); + real_2op_NaN(&loaded_data, st0_ptr, + st0_ptr); else #endif PECULIAR_486 /* fadd, fdivr, fmul, or fsubr */ - real_2op_NaN(FPU_st0_ptr, &FPU_loaded_data, - FPU_st0_ptr); + real_2op_NaN(st0_ptr, &loaded_data, + st0_ptr); } goto reg_mem_instr_done; } @@ -388,11 +416,11 @@ do_another_FPU_instruction: if ( (FPU_modrm & 0x38) == 0x38 ) { /* fdivr */ - if ( (FPU_st0_tag == TW_Zero) && - (FPU_loaded_data.tag == TW_Valid) ) + if ( (st0_tag == TW_Zero) && + (loaded_data.tag == TW_Valid) ) { - if ( divide_by_zero(FPU_loaded_data.sign, - FPU_st0_ptr) ) + if ( divide_by_zero(loaded_data.sign, + st0_ptr) ) { /* We use the fact here that the unmasked exception in the loaded data was for a @@ -410,42 +438,42 @@ do_another_FPU_instruction: { case 0: /* fadd */ clear_C1(); - reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, + reg_add(st0_ptr, &loaded_data, st0_ptr, control_word); break; case 1: /* fmul */ clear_C1(); - reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, + reg_mul(st0_ptr, &loaded_data, st0_ptr, control_word); break; case 2: /* fcom */ - compare_st_data(); + compare_st_data(&loaded_data); break; case 3: /* fcomp */ - if ( !compare_st_data() && !unmasked ) + if ( !compare_st_data(&loaded_data) && !unmasked ) pop(); break; case 4: /* fsub */ clear_C1(); - reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, + reg_sub(st0_ptr, &loaded_data, st0_ptr, control_word); break; case 5: /* fsubr */ clear_C1(); - reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, + reg_sub(&loaded_data, st0_ptr, st0_ptr, control_word); break; case 6: /* fdiv */ clear_C1(); - reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, + reg_div(st0_ptr, &loaded_data, st0_ptr, control_word); break; case 7: /* fdivr */ clear_C1(); - if ( FPU_st0_tag == TW_Zero ) + if ( st0_tag == TW_Zero ) partial_status = status1; /* Undo any denorm tag, zero-divide has priority. */ - reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, + reg_div(&loaded_data, st0_ptr, st0_ptr, control_word); break; } @@ -463,19 +491,19 @@ do_another_FPU_instruction: else stack_underflow(); } + reg_mem_instr_done: + operand_address = data_sel_off; } else { - load_store_instr(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1, - addr_modes); + if ( !(no_ip_update = + load_store_instr(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1, + addr_modes, data_address)) ) + { + operand_address = data_sel_off; + } } - reg_mem_instr_done: - -#ifndef PECULIAR_486 - *(unsigned short *)&operand_selector = FPU_data_selector; -#endif PECULIAR_486 - ; } else { @@ -485,38 +513,39 @@ do_another_FPU_instruction: #ifdef PECULIAR_486 /* This is supposed to be undefined, but a real 80486 seems to do this: */ - FPU_data_address = 0; + operand_address.offset = 0; + operand_address.selector = FPU_DS; #endif PECULIAR_486 - FPU_st0_ptr = &st(0); - FPU_st0_tag = FPU_st0_ptr->tag; + st0_ptr = &st(0); + st0_tag = st0_ptr->tag; switch ( type_table[(int) instr_index] ) { case _NONE_: /* also _REGIc: _REGIn */ break; case _REG0_: - if ( !NOT_EMPTY_0 ) + if ( !NOT_EMPTY_ST0 ) { stack_underflow(); goto FPU_instruction_done; } break; case _REGIi: - if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) ) + if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) { stack_underflow_i(FPU_rm); goto FPU_instruction_done; } break; case _REGIp: - if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) ) + if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) { stack_underflow_pop(FPU_rm); goto FPU_instruction_done; } break; case _REGI_: - if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) ) + if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) { stack_underflow(); goto FPU_instruction_done; @@ -532,17 +561,14 @@ do_another_FPU_instruction: goto FPU_instruction_done; } (*st_instr_table[(int) instr_index])(); - } FPU_instruction_done: + ; + } + + if ( ! no_ip_update ) + instruction_address = entry_sel_off; - ip_offset = FPU_entry_eip; - cs_selector = FPU_entry_op_cs; - data_operand_offset = (unsigned long)FPU_data_address; -#ifdef PECULIAR_486 - *(unsigned short *)&operand_selector = FPU_data_selector; -#endif PECULIAR_486 - FPU_fwait_done: #ifdef DEBUG @@ -553,16 +579,14 @@ FPU_fwait_done: if (FPU_lookahead && !need_resched) { - FPU_ORIG_EIP = FPU_EIP; + FPU_ORIG_EIP = FPU_EIP - code_base; if ( valid_prefix(&byte1, (unsigned char **)&FPU_EIP, &addr_modes.override) ) goto do_another_FPU_instruction; } - if ( addr_modes.vm86 ) - FPU_EIP -= FPU_CS << 4; - else if ( addr_modes.p286 ) - FPU_EIP -= LDT_BASE_ADDR(FPU_CS); + if ( addr_modes.default_mode ) + FPU_EIP -= code_base; RE_ENTRANT_CHECK_OFF; } diff --git a/drivers/FPU-emu/fpu_etc.c b/drivers/FPU-emu/fpu_etc.c index b7e1154cbd3e..c1e113a7f6fc 100644 --- a/drivers/FPU-emu/fpu_etc.c +++ b/drivers/FPU-emu/fpu_etc.c @@ -17,22 +17,22 @@ #include "reg_constant.h" -static void fchs(void) +static void fchs(FPU_REG *st0_ptr) { - if ( NOT_EMPTY_0 ) + if ( st0_ptr->tag ^ TW_Empty ) { - FPU_st0_ptr->sign ^= SIGN_POS^SIGN_NEG; + st0_ptr->sign ^= SIGN_POS^SIGN_NEG; clear_C1(); } else stack_underflow(); } -static void fabs(void) +static void fabs(FPU_REG *st0_ptr) { - if ( FPU_st0_tag ^ TW_Empty ) + if ( st0_ptr->tag ^ TW_Empty ) { - FPU_st0_ptr->sign = SIGN_POS; + st0_ptr->sign = SIGN_POS; clear_C1(); } else @@ -40,25 +40,25 @@ static void fabs(void) } -static void ftst_(void) +static void ftst_(FPU_REG *st0_ptr) { - switch (FPU_st0_tag) + switch (st0_ptr->tag) { case TW_Zero: setcc(SW_C3); break; case TW_Valid: - if (FPU_st0_ptr->sign == SIGN_POS) + if (st0_ptr->sign == SIGN_POS) setcc(0); else setcc(SW_C0); #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) { #ifdef PECULIAR_486 /* This is wierd! */ - if (FPU_st0_ptr->sign == SIGN_POS) + if (st0_ptr->sign == SIGN_POS) setcc(SW_C3); #endif PECULIAR_486 return; @@ -71,7 +71,7 @@ static void ftst_(void) EXCEPTION(EX_Invalid); break; case TW_Infinity: - if (FPU_st0_ptr->sign == SIGN_POS) + if (st0_ptr->sign == SIGN_POS) setcc(0); else setcc(SW_C0); @@ -87,10 +87,10 @@ static void ftst_(void) } } -static void fxam(void) +static void fxam(FPU_REG *st0_ptr) { int c=0; - switch (FPU_st0_tag) + switch (st0_ptr->tag) { case TW_Empty: c = SW_C3|SW_C0; @@ -100,7 +100,7 @@ static void fxam(void) break; case TW_Valid: /* This will need to be changed if TW_Denormal is ever used. */ - if ( FPU_st0_ptr->exp <= EXP_UNDER ) + if ( st0_ptr->exp <= EXP_UNDER ) c = SW_C2|SW_C3; /* Denormal */ else c = SW_C2; @@ -112,16 +112,18 @@ static void fxam(void) c = SW_C2|SW_C0; break; } - if (FPU_st0_ptr->sign == SIGN_NEG) + if (st0_ptr->sign == SIGN_NEG) c |= SW_C1; setcc(c); } -static FUNC const fp_etc_table[] = { - fchs, fabs, FPU_illegal, FPU_illegal, ftst_, fxam, FPU_illegal, FPU_illegal + +static FUNC_ST0 const fp_etc_table[] = { + fchs, fabs, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal, + ftst_, fxam, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal }; void fp_etc() { - (fp_etc_table[FPU_rm])(); + (fp_etc_table[FPU_rm])(&st(0)); } diff --git a/drivers/FPU-emu/fpu_proto.h b/drivers/FPU-emu/fpu_proto.h index 5003cca447b1..a5a4650fc248 100644 --- a/drivers/FPU-emu/fpu_proto.h +++ b/drivers/FPU-emu/fpu_proto.h @@ -63,13 +63,16 @@ extern void trig_a(void); extern void trig_b(void); /* get_address.c */ -extern void get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, - fpu_addr_modes); -extern void get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, - fpu_addr_modes); +extern void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, + struct address *addr, + fpu_addr_modes); +extern void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, + struct address *addr, + fpu_addr_modes); /* load_store.c */ -extern void load_store_instr(char type, fpu_addr_modes addr_modes); +extern int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, + void *address); /* poly_2xm1.c */ extern int poly_2xm1(FPU_REG const *arg, FPU_REG *result); @@ -96,7 +99,7 @@ extern int reg_sub(FPU_REG const *a, FPU_REG const *b, /* reg_compare.c */ extern int compare(FPU_REG const *b); -extern int compare_st_data(void); +extern int compare_st_data(FPU_REG const *b); extern void fcom_st(void); extern void fcompst(void); extern void fcompp(void); @@ -108,26 +111,26 @@ extern void fucompp(void); extern void fconst(void); /* reg_ld_str.c */ -extern int reg_load_extended(void); -extern int reg_load_double(void); -extern int reg_load_single(void); -extern void reg_load_int64(void); -extern void reg_load_int32(void); -extern void reg_load_int16(void); -extern void reg_load_bcd(void); -extern int reg_store_extended(void); -extern int reg_store_double(void); -extern int reg_store_single(void); -extern int reg_store_int64(void); -extern int reg_store_int32(void); -extern int reg_store_int16(void); -extern int reg_store_bcd(void); +extern int reg_load_extended(long double *addr, FPU_REG *loaded_data); +extern int reg_load_double(double *dfloat, FPU_REG *loaded_data); +extern int reg_load_single(float *single, FPU_REG *loaded_data); +extern void reg_load_int64(long long *_s, FPU_REG *loaded_data); +extern void reg_load_int32(long *_s, FPU_REG *loaded_data); +extern void reg_load_int16(short *_s, FPU_REG *loaded_data); +extern void reg_load_bcd(char *s, FPU_REG *loaded_data); +extern int reg_store_extended(long double *d, FPU_REG *st0_ptr); +extern int reg_store_double(double *dfloat, FPU_REG *st0_ptr); +extern int reg_store_single(float *single, FPU_REG *st0_ptr); +extern int reg_store_int64(long long *d, FPU_REG *st0_ptr); +extern int reg_store_int32(long *d, FPU_REG *st0_ptr); +extern int reg_store_int16(short *d, FPU_REG *st0_ptr); +extern int reg_store_bcd(char *d, FPU_REG *st0_ptr); extern int round_to_int(FPU_REG *r); -extern char *fldenv(fpu_addr_modes addr_modes); -extern void frstor(fpu_addr_modes addr_modes); +extern char *fldenv(fpu_addr_modes addr_modes, char *address); +extern void frstor(fpu_addr_modes addr_modes, char *address); extern unsigned short tag_word(void); -extern char *fstenv(fpu_addr_modes addr_modes); -extern void fsave(fpu_addr_modes addr_modes); +extern char *fstenv(fpu_addr_modes addr_modes, char *address); +extern void fsave(fpu_addr_modes addr_modes, char *address); /* reg_mul.c */ extern int reg_mul(FPU_REG const *a, FPU_REG const *b, diff --git a/drivers/FPU-emu/fpu_system.h b/drivers/FPU-emu/fpu_system.h index 24ba92d1f6e3..e09e08b15b6a 100644 --- a/drivers/FPU-emu/fpu_system.h +++ b/drivers/FPU-emu/fpu_system.h @@ -19,6 +19,19 @@ of the stack frame of math_emulate() */ #define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg +#define LDT_DESCRIPTOR(s) (current->ldt[(s) >> 3]) +#define SEG_D_SIZE(x) ((x).b & (3 << 21)) +#define SEG_G_BIT(x) ((x).b & (1 << 23)) +#define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1) +#define SEG_286_MODE(x) ((x).b & ( 0xff000000 | 0xf0000 | (1 << 23))) +#define SEG_BASE_ADDR(s) (((s).b & 0xff000000) \ + | (((s).b & 0xff) << 16) | ((s).a >> 16)) +#define SEG_LIMIT(s) (((s).b & 0xff0000) | ((s).a & 0xffff)) +#define SEG_EXECUTE_ONLY(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 11)) +#define SEG_WRITE_PERM(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 9)) +#define SEG_EXPAND_DOWN(s) (((s).b & ((1 << 11) | (1 << 10))) \ + == (1 << 10)) + #define I387 (current->tss.i387) #define FPU_info (I387.soft.info) @@ -30,22 +43,24 @@ #define FPU_EIP (FPU_info->___eip) #define FPU_ORIG_EIP (FPU_info->___orig_eip) -#define LDT_BASE_ADDR(s) ((current->ldt[(s) >> 3].b & 0xff000000) \ - | ((current->ldt[(s) >> 3].b & 0xff) << 16) \ - | (current->ldt[(s) >> 3].a >> 16)) - #define FPU_lookahead (I387.soft.lookahead) -#define FPU_entry_eip (I387.soft.entry_eip) + +/* nz if ip_offset and cs_selector are not to be set for the current + instruction. */ +#define no_ip_update (((char *)&(I387.soft.twd))[0]) +#define FPU_rm (((unsigned char *)&(I387.soft.twd))[1]) + +/* Number of bytes of data which can be legally accessed by the current + instruction. This only needs to hold a number <= 108, so a byte will do. */ +#define access_limit (((unsigned char *)&(I387.soft.twd))[2]) #define partial_status (I387.soft.swd) #define control_word (I387.soft.cwd) #define regs (I387.soft.regs) #define top (I387.soft.top) -#define ip_offset (I387.soft.fip) -#define cs_selector (I387.soft.fcs) -#define data_operand_offset (I387.soft.foo) -#define operand_selector (I387.soft.fos) +#define instruction_address (*(struct address *)&I387.soft.fip) +#define operand_address (*(struct address *)&I387.soft.foo) #define FPU_verify_area(x,y,z) if ( verify_area(x,y,z) ) \ math_abort(FPU_info,SIGSEGV) @@ -64,7 +79,4 @@ #define FPU_code_verify_area(z) FPU_verify_area(VERIFY_READ,(void *)FPU_EIP,z) #endif -/* ######## temporary and ugly ;-) */ -#define FPU_data_address ((void *)(I387.soft.twd)) - #endif diff --git a/drivers/FPU-emu/fpu_trig.c b/drivers/FPU-emu/fpu_trig.c index 1de4317a5c37..a368a2e77ca3 100644 --- a/drivers/FPU-emu/fpu_trig.c +++ b/drivers/FPU-emu/fpu_trig.c @@ -166,16 +166,16 @@ void convert_l2reg(long const *arg, FPU_REG *dest) } -static void single_arg_error(void) +static void single_arg_error(FPU_REG *st0_ptr) { - switch ( FPU_st0_tag ) + switch ( st0_ptr->tag ) { case TW_NaN: - if ( !(FPU_st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ + if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ { EXCEPTION(EX_Invalid); if ( control_word & CW_Invalid ) - FPU_st0_ptr->sigh |= 0x40000000; /* Convert to a QNaN */ + st0_ptr->sigh |= 0x40000000; /* Convert to a QNaN */ } break; /* return with a NaN in st(0) */ case TW_Empty: @@ -189,24 +189,24 @@ static void single_arg_error(void) } -static void single_arg_2_error(void) +static void single_arg_2_error(FPU_REG *st0_ptr) { FPU_REG *st_new_ptr; - switch ( FPU_st0_tag ) + switch ( st0_ptr->tag ) { case TW_NaN: - if ( !(FPU_st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ + if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ { EXCEPTION(EX_Invalid); if ( control_word & CW_Invalid ) { /* The masked response */ /* Convert to a QNaN */ - FPU_st0_ptr->sigh |= 0x40000000; + st0_ptr->sigh |= 0x40000000; st_new_ptr = &st(-1); push(); - reg_move(&st(1), FPU_st0_ptr); + reg_move(&st(1), st_new_ptr); } } else @@ -214,7 +214,7 @@ static void single_arg_2_error(void) /* A QNaN */ st_new_ptr = &st(-1); push(); - reg_move(&st(1), FPU_st0_ptr); + reg_move(&st(1), st_new_ptr); } break; /* return with a NaN in st(0) */ #ifdef PARANOID @@ -227,48 +227,48 @@ static void single_arg_2_error(void) /*---------------------------------------------------------------------------*/ -static void f2xm1(void) +static void f2xm1(FPU_REG *st0_ptr) { clear_C1(); - switch ( FPU_st0_tag ) + switch ( st0_ptr->tag ) { case TW_Valid: { FPU_REG rv, tmp; - if ( FPU_st0_ptr->exp >= 0 ) + if ( st0_ptr->exp >= 0 ) { /* For an 80486 FPU, the result is undefined. */ } - else if ( FPU_st0_ptr->exp >= -64 ) + else if ( st0_ptr->exp >= -64 ) { - if ( FPU_st0_ptr->sign == SIGN_POS ) + if ( st0_ptr->sign == SIGN_POS ) { /* poly_2xm1(x) requires 0 < x < 1. */ - poly_2xm1(FPU_st0_ptr, &rv); - reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); + poly_2xm1(st0_ptr, &rv); + reg_mul(&rv, st0_ptr, st0_ptr, FULL_PRECISION); } else { /* poly_2xm1(x) doesn't handle negative numbers yet. */ /* So we compute z=poly_2xm1(-x), and the answer is then -z/(1+z) */ - FPU_st0_ptr->sign = SIGN_POS; - poly_2xm1(FPU_st0_ptr, &rv); - reg_mul(&rv, FPU_st0_ptr, &rv, FULL_PRECISION); + st0_ptr->sign = SIGN_POS; + poly_2xm1(st0_ptr, &rv); + reg_mul(&rv, st0_ptr, &rv, FULL_PRECISION); reg_add(&rv, &CONST_1, &tmp, FULL_PRECISION); - reg_div(&rv, &tmp, FPU_st0_ptr, FULL_PRECISION); - FPU_st0_ptr->sign = SIGN_NEG; + reg_div(&rv, &tmp, st0_ptr, FULL_PRECISION); + st0_ptr->sign = SIGN_NEG; } } else { #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND /* For very small arguments, this is accurate enough. */ - reg_mul(&CONST_LN2, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); + reg_mul(&CONST_LN2, st0_ptr, st0_ptr, FULL_PRECISION); } set_precision_flag_up(); return; @@ -276,27 +276,28 @@ static void f2xm1(void) case TW_Zero: return; case TW_Infinity: - if ( FPU_st0_ptr->sign == SIGN_NEG ) + if ( st0_ptr->sign == SIGN_NEG ) { /* -infinity gives -1 (p16-10) */ - reg_move(&CONST_1, FPU_st0_ptr); - FPU_st0_ptr->sign = SIGN_NEG; + reg_move(&CONST_1, st0_ptr); + st0_ptr->sign = SIGN_NEG; } return; default: - single_arg_error(); + single_arg_error(st0_ptr); } } -static void fptan(void) +static void fptan(FPU_REG *st0_ptr) { + char st0_tag = st0_ptr->tag; FPU_REG *st_new_ptr; int q; - char arg_sign = FPU_st0_ptr->sign; + char arg_sign = st0_ptr->sign; /* Stack underflow has higher priority */ - if ( FPU_st0_tag == TW_Empty ) + if ( st0_tag == TW_Empty ) { stack_underflow(); /* Puts a QNaN in st(0) */ if ( control_word & CW_Invalid ) @@ -311,24 +312,24 @@ static void fptan(void) if ( STACK_OVERFLOW ) { stack_overflow(); return; } - switch ( FPU_st0_tag ) + switch ( st0_tag ) { case TW_Valid: - if ( FPU_st0_ptr->exp > EXP_BIAS - 40 ) + if ( st0_ptr->exp > EXP_BIAS - 40 ) { - FPU_st0_ptr->sign = SIGN_POS; - if ( (q = trig_arg(FPU_st0_ptr, FPTAN)) != -1 ) + st0_ptr->sign = SIGN_POS; + if ( (q = trig_arg(st0_ptr, FPTAN)) != -1 ) { - reg_div(FPU_st0_ptr, &CONST_PI2, FPU_st0_ptr, + reg_div(st0_ptr, &CONST_PI2, st0_ptr, FULL_PRECISION); - poly_tan(FPU_st0_ptr, FPU_st0_ptr, q & FCOS); - FPU_st0_ptr->sign = (q & 1) ^ arg_sign; + poly_tan(st0_ptr, st0_ptr, q & FCOS); + st0_ptr->sign = (q & 1) ^ arg_sign; } else { /* Operand is out of range */ - FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ + st0_ptr->sign = arg_sign; /* restore st(0) */ return; } } @@ -337,7 +338,7 @@ static void fptan(void) /* For a small arg, the result == the argument */ /* Underflow may happen */ - if ( FPU_st0_ptr->exp <= EXP_UNDER ) + if ( st0_ptr->exp <= EXP_UNDER ) { #ifdef DENORM_OPERAND if ( denormal_operand() ) @@ -346,90 +347,91 @@ static void fptan(void) /* A denormal result has been produced. Precision must have been lost, this is always an underflow. */ - if ( arith_underflow(FPU_st0_ptr) ) + if ( arith_underflow(st0_ptr) ) return; } else set_precision_flag_up(); /* Must be up. */ } push(); - reg_move(&CONST_1, FPU_st0_ptr); + reg_move(&CONST_1, st_new_ptr); return; break; case TW_Infinity: /* The 80486 treats infinity as an invalid operand */ - arith_invalid(FPU_st0_ptr); + arith_invalid(st0_ptr); if ( control_word & CW_Invalid ) { st_new_ptr = &st(-1); push(); - arith_invalid(FPU_st0_ptr); + arith_invalid(st_new_ptr); } return; case TW_Zero: push(); - reg_move(&CONST_1, FPU_st0_ptr); + reg_move(&CONST_1, st_new_ptr); setcc(0); break; default: - single_arg_2_error(); + single_arg_2_error(st0_ptr); break; } } -static void fxtract(void) +static void fxtract(FPU_REG *st0_ptr) { + char st0_tag = st0_ptr->tag; FPU_REG *st_new_ptr; - register FPU_REG *st1_ptr = FPU_st0_ptr; /* anticipate */ + register FPU_REG *st1_ptr = st0_ptr; /* anticipate */ if ( STACK_OVERFLOW ) { stack_overflow(); return; } clear_C1(); - if ( !(FPU_st0_tag ^ TW_Valid) ) + if ( !(st0_tag ^ TW_Valid) ) { long e; #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND push(); - reg_move(st1_ptr, FPU_st0_ptr); - FPU_st0_ptr->exp = EXP_BIAS; + reg_move(st1_ptr, st_new_ptr); + st_new_ptr->exp = EXP_BIAS; e = st1_ptr->exp - EXP_BIAS; convert_l2reg(&e, st1_ptr); return; } - else if ( FPU_st0_tag == TW_Zero ) + else if ( st0_tag == TW_Zero ) { - char sign = FPU_st0_ptr->sign; - if ( divide_by_zero(SIGN_NEG, FPU_st0_ptr) ) + char sign = st0_ptr->sign; + if ( divide_by_zero(SIGN_NEG, st0_ptr) ) return; push(); - reg_move(&CONST_Z, FPU_st0_ptr); - FPU_st0_ptr->sign = sign; + reg_move(&CONST_Z, st_new_ptr); + st_new_ptr->sign = sign; return; } - else if ( FPU_st0_tag == TW_Infinity ) + else if ( st0_tag == TW_Infinity ) { - char sign = FPU_st0_ptr->sign; - FPU_st0_ptr->sign = SIGN_POS; + char sign = st0_ptr->sign; + st0_ptr->sign = SIGN_POS; push(); - reg_move(&CONST_INF, FPU_st0_ptr); - FPU_st0_ptr->sign = sign; + reg_move(&CONST_INF, st_new_ptr); + st_new_ptr->sign = sign; return; } - else if ( FPU_st0_tag == TW_NaN ) + else if ( st0_tag == TW_NaN ) { - if ( real_2op_NaN(FPU_st0_ptr, FPU_st0_ptr, FPU_st0_ptr) ) + if ( real_2op_NaN(st0_ptr, st0_ptr, st0_ptr) ) return; push(); - reg_move(st1_ptr, FPU_st0_ptr); + reg_move(st1_ptr, st_new_ptr); return; } - else if ( FPU_st0_tag == TW_Empty ) + else if ( st0_tag == TW_Empty ) { /* Is this the correct behaviour? */ if ( control_word & EX_Invalid ) @@ -448,110 +450,114 @@ static void fxtract(void) } -static void fdecstp(void) +static void fdecstp(FPU_REG *st0_ptr) { clear_C1(); - top--; /* FPU_st0_ptr will be fixed in math_emulate() before the next instr */ + top--; /* st0_ptr will be fixed in math_emulate() before the next instr */ } -static void fincstp(void) +static void fincstp(FPU_REG *st0_ptr) { clear_C1(); - top++; /* FPU_st0_ptr will be fixed in math_emulate() before the next instr */ + top++; /* st0_ptr will be fixed in math_emulate() before the next instr */ } -static void fsqrt_(void) +static void fsqrt_(FPU_REG *st0_ptr) { + char st0_tag = st0_ptr->tag; + clear_C1(); - if ( !(FPU_st0_tag ^ TW_Valid) ) + if ( !(st0_tag ^ TW_Valid) ) { int expon; - if (FPU_st0_ptr->sign == SIGN_NEG) + if (st0_ptr->sign == SIGN_NEG) { - arith_invalid(FPU_st0_ptr); /* sqrt(negative) is invalid */ + arith_invalid(st0_ptr); /* sqrt(negative) is invalid */ return; } #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND - expon = FPU_st0_ptr->exp - EXP_BIAS; - FPU_st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0 .. 4.0) */ + expon = st0_ptr->exp - EXP_BIAS; + st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0 .. 4.0) */ - wm_sqrt(FPU_st0_ptr, control_word); /* Do the computation */ + wm_sqrt(st0_ptr, control_word); /* Do the computation */ - FPU_st0_ptr->exp += expon >> 1; - FPU_st0_ptr->sign = SIGN_POS; + st0_ptr->exp += expon >> 1; + st0_ptr->sign = SIGN_POS; } - else if ( FPU_st0_tag == TW_Zero ) + else if ( st0_tag == TW_Zero ) return; - else if ( FPU_st0_tag == TW_Infinity ) + else if ( st0_tag == TW_Infinity ) { - if ( FPU_st0_ptr->sign == SIGN_NEG ) - arith_invalid(FPU_st0_ptr); /* sqrt(-Infinity) is invalid */ + if ( st0_ptr->sign == SIGN_NEG ) + arith_invalid(st0_ptr); /* sqrt(-Infinity) is invalid */ return; } else - { single_arg_error(); return; } + { single_arg_error(st0_ptr); return; } } -static void frndint_(void) +static void frndint_(FPU_REG *st0_ptr) { + char st0_tag = st0_ptr->tag; int flags; - if ( !(FPU_st0_tag ^ TW_Valid) ) + if ( !(st0_tag ^ TW_Valid) ) { - if (FPU_st0_ptr->exp > EXP_BIAS+63) + if (st0_ptr->exp > EXP_BIAS+63) return; #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND /* Fortunately, this can't overflow to 2^64 */ - if ( (flags = round_to_int(FPU_st0_ptr)) ) + if ( (flags = round_to_int(st0_ptr)) ) set_precision_flag(flags); - FPU_st0_ptr->exp = EXP_BIAS + 63; - normalize(FPU_st0_ptr); + st0_ptr->exp = EXP_BIAS + 63; + normalize(st0_ptr); return; } - else if ( (FPU_st0_tag == TW_Zero) || (FPU_st0_tag == TW_Infinity) ) + else if ( (st0_tag == TW_Zero) || (st0_tag == TW_Infinity) ) return; else - single_arg_error(); + single_arg_error(st0_ptr); } -static void fsin(void) +static void fsin(FPU_REG *st0_ptr) { - char arg_sign = FPU_st0_ptr->sign; + char st0_tag = st0_ptr->tag; + char arg_sign = st0_ptr->sign; - if ( FPU_st0_tag == TW_Valid ) + if ( st0_tag == TW_Valid ) { FPU_REG rv; int q; - if ( FPU_st0_ptr->exp > EXP_BIAS - 40 ) + if ( st0_ptr->exp > EXP_BIAS - 40 ) { - FPU_st0_ptr->sign = SIGN_POS; - if ( (q = trig_arg(FPU_st0_ptr, 0)) != -1 ) + st0_ptr->sign = SIGN_POS; + if ( (q = trig_arg(st0_ptr, 0)) != -1 ) { - reg_div(FPU_st0_ptr, &CONST_PI2, FPU_st0_ptr, FULL_PRECISION); + reg_div(st0_ptr, &CONST_PI2, st0_ptr, FULL_PRECISION); - poly_sine(FPU_st0_ptr, &rv); + poly_sine(st0_ptr, &rv); if (q & 2) rv.sign ^= SIGN_POS ^ SIGN_NEG; rv.sign ^= arg_sign; - reg_move(&rv, FPU_st0_ptr); + reg_move(&rv, st0_ptr); /* We do not really know if up or down */ set_precision_flag_up(); @@ -560,7 +566,7 @@ static void fsin(void) else { /* Operand is out of range */ - FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ + st0_ptr->sign = arg_sign; /* restore st(0) */ return; } } @@ -569,7 +575,7 @@ static void fsin(void) /* For a small arg, the result == the argument */ /* Underflow may happen */ - if ( FPU_st0_ptr->exp <= EXP_UNDER ) + if ( st0_ptr->exp <= EXP_UNDER ) { #ifdef DENORM_OPERAND if ( denormal_operand() ) @@ -578,26 +584,26 @@ static void fsin(void) /* A denormal result has been produced. Precision must have been lost, this is always an underflow. */ - arith_underflow(FPU_st0_ptr); + arith_underflow(st0_ptr); return; } set_precision_flag_up(); /* Must be up. */ } } - else if ( FPU_st0_tag == TW_Zero ) + else if ( st0_tag == TW_Zero ) { setcc(0); return; } - else if ( FPU_st0_tag == TW_Infinity ) + else if ( st0_tag == TW_Infinity ) { /* The 80486 treats infinity as an invalid operand */ - arith_invalid(FPU_st0_ptr); + arith_invalid(st0_ptr); return; } else - single_arg_error(); + single_arg_error(st0_ptr); } @@ -658,33 +664,34 @@ static int f_cos(FPU_REG *arg) setcc(0); return 0; } - else if ( FPU_st0_tag == TW_Infinity ) + else if ( arg->tag == TW_Infinity ) { /* The 80486 treats infinity as an invalid operand */ - arith_invalid(FPU_st0_ptr); + arith_invalid(arg); return 1; } else { - single_arg_error(); /* requires arg == &st(0) */ + single_arg_error(arg); /* requires arg == &st(0) */ return 1; } } -static void fcos(void) +static void fcos(FPU_REG *st0_ptr) { - f_cos(FPU_st0_ptr); + f_cos(st0_ptr); } -static void fsincos(void) +static void fsincos(FPU_REG *st0_ptr) { + char st0_tag = st0_ptr->tag; FPU_REG *st_new_ptr; FPU_REG arg; /* Stack underflow has higher priority */ - if ( FPU_st0_tag == TW_Empty ) + if ( st0_tag == TW_Empty ) { stack_underflow(); /* Puts a QNaN in st(0) */ if ( control_word & CW_Invalid ) @@ -699,29 +706,29 @@ static void fsincos(void) if ( STACK_OVERFLOW ) { stack_overflow(); return; } - if ( FPU_st0_tag == TW_NaN ) + if ( st0_tag == TW_NaN ) { - single_arg_2_error(); + single_arg_2_error(st0_ptr); return; } - else if ( FPU_st0_tag == TW_Infinity ) + else if ( st0_tag == TW_Infinity ) { /* The 80486 treats infinity as an invalid operand */ - if ( !arith_invalid(FPU_st0_ptr) ) + if ( !arith_invalid(st0_ptr) ) { /* unmasked response */ push(); - arith_invalid(FPU_st0_ptr); + arith_invalid(st_new_ptr); } return; } - reg_move(FPU_st0_ptr,&arg); + reg_move(st0_ptr,&arg); if ( !f_cos(&arg) ) { - fsin(); + fsin(st0_ptr); push(); - reg_move(&arg,FPU_st0_ptr); + reg_move(&arg,st_new_ptr); } } @@ -760,23 +767,24 @@ static void rem_kernel(unsigned long long st0, unsigned long long *y, /* Remainder of st(0) / st(1) */ /* This routine produces exact results, i.e. there is never any rounding or truncation, etc of the result. */ -static void do_fprem(int round) +static void do_fprem(FPU_REG *st0_ptr, int round) { FPU_REG *st1_ptr = &st(1); char st1_tag = st1_ptr->tag; - char sign = FPU_st0_ptr->sign; + char st0_tag = st0_ptr->tag; + char sign = st0_ptr->sign; - if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) { FPU_REG tmp; int old_cw = control_word; - int expdif = FPU_st0_ptr->exp - st1_ptr->exp; + int expdif = st0_ptr->exp - st1_ptr->exp; long long q; unsigned short saved_status; int cc = 0; #ifdef DENORM_OPERAND - if ( ((FPU_st0_ptr->exp <= EXP_UNDER) || + if ( ((st0_ptr->exp <= EXP_UNDER) || (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) return; #endif DENORM_OPERAND @@ -793,7 +801,7 @@ static void do_fprem(int round) if ( expdif > -2 ) { - reg_div(FPU_st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f); + reg_div(st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f); if ( tmp.exp >= EXP_BIAS ) { @@ -801,7 +809,7 @@ static void do_fprem(int round) to 2^64 */ q = significand(&tmp); - rem_kernel(significand(FPU_st0_ptr), + rem_kernel(significand(st0_ptr), &significand(&tmp), significand(st1_ptr), q, expdif); @@ -810,7 +818,7 @@ static void do_fprem(int round) } else { - reg_move(FPU_st0_ptr, &tmp); + reg_move(st0_ptr, &tmp); q = 0; } tmp.sign = sign; @@ -858,7 +866,7 @@ static void do_fprem(int round) /* prevent overflow here */ /* N is 'a number between 32 and 63' (p26-113) */ - reg_move(FPU_st0_ptr, &tmp); + reg_move(st0_ptr, &tmp); tmp.exp = EXP_BIAS + 56; exp_1 = st1_ptr->exp; st1_ptr->exp = EXP_BIAS; expdif -= 56; @@ -868,7 +876,7 @@ static void do_fprem(int round) round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */ - rem_kernel(significand(FPU_st0_ptr), + rem_kernel(significand(st0_ptr), &significand(&tmp), significand(st1_ptr), significand(&tmp), @@ -887,8 +895,8 @@ static void do_fprem(int round) /* The result is zero */ control_word = old_cw; partial_status = saved_status; - reg_move(&CONST_Z, FPU_st0_ptr); - FPU_st0_ptr->sign = sign; + reg_move(&CONST_Z, st0_ptr); + st0_ptr->sign = sign; #ifdef PECULIAR_486 setcc(SW_C2); #else @@ -902,23 +910,23 @@ static void do_fprem(int round) control_word = old_cw; partial_status = saved_status; normalize_nuo(&tmp); - reg_move(&tmp, FPU_st0_ptr); + reg_move(&tmp, st0_ptr); setcc(cc); /* The only condition to be looked for is underflow, and it can occur here only if underflow is unmasked. */ - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (FPU_st0_ptr->tag != TW_Zero) + if ( (st0_ptr->exp <= EXP_UNDER) && (st0_ptr->tag != TW_Zero) && !(control_word & CW_Underflow) ) - arith_underflow(FPU_st0_ptr); + arith_underflow(st0_ptr); return; } - else if ( (FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) + else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) { stack_underflow(); return; } - else if ( FPU_st0_tag == TW_Zero ) + else if ( st0_tag == TW_Zero ) { if ( st1_tag == TW_Valid ) { @@ -930,21 +938,21 @@ static void do_fprem(int round) setcc(0); return; } else if ( st1_tag == TW_Zero ) - { arith_invalid(FPU_st0_ptr); return; } /* fprem(?,0) always invalid */ + { arith_invalid(st0_ptr); return; } /* fprem(?,0) always invalid */ else if ( st1_tag == TW_Infinity ) { setcc(0); return; } } - else if ( FPU_st0_tag == TW_Valid ) + else if ( st0_tag == TW_Valid ) { if ( st1_tag == TW_Zero ) { - arith_invalid(FPU_st0_ptr); /* fprem(Valid,Zero) is invalid */ + arith_invalid(st0_ptr); /* fprem(Valid,Zero) is invalid */ return; } else if ( st1_tag != TW_NaN ) { #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND @@ -955,11 +963,11 @@ static void do_fprem(int round) } } } - else if ( FPU_st0_tag == TW_Infinity ) + else if ( st0_tag == TW_Infinity ) { if ( st1_tag != TW_NaN ) { - arith_invalid(FPU_st0_ptr); /* fprem(Infinity,?) is invalid */ + arith_invalid(st0_ptr); /* fprem(Infinity,?) is invalid */ return; } } @@ -967,30 +975,31 @@ static void do_fprem(int round) /* One of the registers must contain a NaN is we got here. */ #ifdef PARANOID - if ( (FPU_st0_tag != TW_NaN) && (st1_tag != TW_NaN) ) + if ( (st0_tag != TW_NaN) && (st1_tag != TW_NaN) ) EXCEPTION(EX_INTERNAL | 0x118); #endif PARANOID - real_2op_NaN(st1_ptr, FPU_st0_ptr, FPU_st0_ptr); + real_2op_NaN(st1_ptr, st0_ptr, st0_ptr); } /* ST(1) <- ST(1) * log ST; pop ST */ -static void fyl2x(void) +static void fyl2x(FPU_REG *st0_ptr) { + char st0_tag = st0_ptr->tag; FPU_REG *st1_ptr = &st(1); char st1_tag = st1_ptr->tag; clear_C1(); - if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) { - if ( FPU_st0_ptr->sign == SIGN_POS ) + if ( st0_ptr->sign == SIGN_POS ) { int saved_control, saved_status; #ifdef DENORM_OPERAND - if ( ((FPU_st0_ptr->exp <= EXP_UNDER) || + if ( ((st0_ptr->exp <= EXP_UNDER) || (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) return; #endif DENORM_OPERAND @@ -1001,16 +1010,16 @@ static void fyl2x(void) saved_control = control_word; control_word = FULL_PRECISION; - poly_l2(FPU_st0_ptr, FPU_st0_ptr); + poly_l2(st0_ptr, st0_ptr); /* Enough of the basic arithmetic is done now */ control_word = saved_control; partial_status = saved_status; /* Let the multiply set the flags */ - reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION); + reg_mul(st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION); - pop(); FPU_st0_ptr = &st(0); + pop(); } else { @@ -1020,21 +1029,21 @@ static void fyl2x(void) return; } } - else if ( (FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) + else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) { stack_underflow_pop(1); return; } - else if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) + else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) { - if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) ) + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) pop(); return; } - else if ( (FPU_st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) ) + else if ( (st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) ) { /* one of the args is zero, the other valid, or both zero */ - if ( FPU_st0_tag == TW_Zero ) + if ( st0_tag == TW_Zero ) { if ( st1_tag == TW_Zero ) { @@ -1046,7 +1055,7 @@ static void fyl2x(void) /* This case is not specifically covered in the manual, but divide-by-zero would seem to be the best response. However, a real 80486 does it this way... */ - else if ( FPU_st0_ptr->tag == TW_Infinity ) + else if ( st0_ptr->tag == TW_Infinity ) { reg_move(&CONST_INF, st1_ptr); pop(); @@ -1065,7 +1074,7 @@ static void fyl2x(void) /* Zero is the valid answer */ char sign = st1_ptr->sign; - if ( FPU_st0_ptr->sign == SIGN_NEG ) + if ( st0_ptr->sign == SIGN_NEG ) { /* log(negative) */ if ( !arith_invalid(st1_ptr) ) @@ -1074,21 +1083,21 @@ static void fyl2x(void) } #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND - if ( FPU_st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS; - pop(); FPU_st0_ptr = &st(0); - reg_move(&CONST_Z, FPU_st0_ptr); - FPU_st0_ptr->sign = sign; + if ( st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS; + pop(); st0_ptr = &st(0); + reg_move(&CONST_Z, st0_ptr); + st0_ptr->sign = sign; return; } } /* One or both arg must be an infinity */ - else if ( FPU_st0_tag == TW_Infinity ) + else if ( st0_tag == TW_Infinity ) { - if ( (FPU_st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) ) + if ( (st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) ) { /* log(-infinity) or 0*log(infinity) */ if ( !arith_invalid(st1_ptr) ) @@ -1104,20 +1113,20 @@ static void fyl2x(void) return; #endif DENORM_OPERAND - pop(); FPU_st0_ptr = &st(0); - reg_move(&CONST_INF, FPU_st0_ptr); - FPU_st0_ptr->sign = sign; + pop(); st0_ptr = &st(0); + reg_move(&CONST_INF, st0_ptr); + st0_ptr->sign = sign; return; } } /* st(1) must be infinity here */ - else if ( (FPU_st0_tag == TW_Valid) && (FPU_st0_ptr->sign == SIGN_POS) ) + else if ( (st0_tag == TW_Valid) && (st0_ptr->sign == SIGN_POS) ) { - if ( FPU_st0_ptr->exp >= EXP_BIAS ) + if ( st0_ptr->exp >= EXP_BIAS ) { - if ( (FPU_st0_ptr->exp == EXP_BIAS) && - (FPU_st0_ptr->sigh == 0x80000000) && - (FPU_st0_ptr->sigl == 0) ) + if ( (st0_ptr->exp == EXP_BIAS) && + (st0_ptr->sigh == 0x80000000) && + (st0_ptr->sigl == 0) ) { /* st(0) holds 1.0 */ /* infinity*log(1) */ @@ -1133,7 +1142,7 @@ static void fyl2x(void) /* st(0) is positive and < 1.0 */ #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND @@ -1145,7 +1154,7 @@ static void fyl2x(void) else { /* st(0) must be zero or negative */ - if ( FPU_st0_ptr->tag == TW_Zero ) + if ( st0_ptr->tag == TW_Zero ) { /* This should be invalid, but a real 80486 is happy with it. */ #ifndef PECULIAR_486 @@ -1167,21 +1176,22 @@ static void fyl2x(void) } -static void fpatan(void) +static void fpatan(FPU_REG *st0_ptr) { + char st0_tag = st0_ptr->tag; FPU_REG *st1_ptr = &st(1); char st1_tag = st1_ptr->tag; - char st1_sign = st1_ptr->sign, st0_sign = FPU_st0_ptr->sign; + char st1_sign = st1_ptr->sign, st0_sign = st0_ptr->sign; clear_C1(); - if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) { int saved_control, saved_status; FPU_REG sum; char inverted; #ifdef DENORM_OPERAND - if ( ((FPU_st0_ptr->exp <= EXP_UNDER) || + if ( ((st0_ptr->exp <= EXP_UNDER) || (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) return; #endif DENORM_OPERAND @@ -1191,28 +1201,28 @@ static void fpatan(void) saved_control = control_word; control_word = FULL_PRECISION; - st1_ptr->sign = FPU_st0_ptr->sign = SIGN_POS; + st1_ptr->sign = st0_ptr->sign = SIGN_POS; if ( (compare(st1_ptr) & ~COMP_Denormal) == COMP_A_lt_B ) { inverted = 1; - reg_div(FPU_st0_ptr, st1_ptr, &sum, FULL_PRECISION); + reg_div(st0_ptr, st1_ptr, &sum, FULL_PRECISION); } else { inverted = 0; if ( (st0_sign == 0) && - (st1_ptr->exp - FPU_st0_ptr->exp < -64) ) + (st1_ptr->exp - st0_ptr->exp < -64) ) { control_word = saved_control; partial_status = saved_status; - reg_div(st1_ptr, FPU_st0_ptr, st1_ptr, + reg_div(st1_ptr, st0_ptr, st1_ptr, control_word | PR_64_BITS); st1_ptr->sign = st1_sign; pop(); set_precision_flag_down(); return; } - reg_div(st1_ptr, FPU_st0_ptr, &sum, FULL_PRECISION); + reg_div(st1_ptr, st0_ptr, &sum, FULL_PRECISION); } poly_atan(&sum); @@ -1233,25 +1243,25 @@ static void fpatan(void) reg_move(&sum, st1_ptr); } - else if ( (FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) + else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) { stack_underflow_pop(1); return; } - else if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) + else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) { - if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) ) + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) pop(); return; } - else if ( (FPU_st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) ) + else if ( (st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) ) { char sign = st1_ptr->sign; - if ( FPU_st0_tag == TW_Infinity ) + if ( st0_tag == TW_Infinity ) { if ( st1_tag == TW_Infinity ) { - if ( FPU_st0_ptr->sign == SIGN_POS ) + if ( st0_ptr->sign == SIGN_POS ) { reg_move(&CONST_PI4, st1_ptr); } else reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION); @@ -1266,7 +1276,7 @@ static void fpatan(void) } #endif DENORM_OPERAND - if ( FPU_st0_ptr->sign == SIGN_POS ) + if ( st0_ptr->sign == SIGN_POS ) { reg_move(&CONST_Z, st1_ptr); st1_ptr->sign = sign; /* An 80486 preserves the sign */ @@ -1281,9 +1291,9 @@ static void fpatan(void) { /* st(1) is infinity, st(0) not infinity */ #ifdef DENORM_OPERAND - if ( FPU_st0_tag != TW_Zero ) + if ( st0_tag != TW_Zero ) { - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; } #endif DENORM_OPERAND @@ -1298,20 +1308,20 @@ static void fpatan(void) char sign = st1_ptr->sign; #ifdef DENORM_OPERAND - if ( FPU_st0_tag != TW_Zero ) + if ( st0_tag != TW_Zero ) { - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; } #endif DENORM_OPERAND - if ( FPU_st0_ptr->sign == SIGN_POS ) + if ( st0_ptr->sign == SIGN_POS ) { /* An 80486 preserves the sign */ pop(); return; } else reg_move(&CONST_PI, st1_ptr); st1_ptr->sign = sign; } - else if ( FPU_st0_tag == TW_Zero ) + else if ( st0_tag == TW_Zero ) { /* st(1) must be TW_Valid here */ char sign = st1_ptr->sign; @@ -1334,30 +1344,31 @@ static void fpatan(void) } -static void fprem(void) +static void fprem(FPU_REG *st0_ptr) { - do_fprem(RC_CHOP); + do_fprem(st0_ptr, RC_CHOP); } -static void fprem1(void) +static void fprem1(FPU_REG *st0_ptr) { - do_fprem(RC_RND); + do_fprem(st0_ptr, RC_RND); } -static void fyl2xp1(void) +static void fyl2xp1(FPU_REG *st0_ptr) { + char st0_tag = st0_ptr->tag; FPU_REG *st1_ptr = &st(1); char st1_tag = st1_ptr->tag; clear_C1(); - if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) { int saved_control, saved_status; #ifdef DENORM_OPERAND - if ( ((FPU_st0_ptr->exp <= EXP_UNDER) || + if ( ((st0_ptr->exp <= EXP_UNDER) || (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() ) return; #endif DENORM_OPERAND @@ -1367,7 +1378,7 @@ static void fyl2xp1(void) saved_control = control_word; control_word = FULL_PRECISION; - if ( poly_l2p1(FPU_st0_ptr, FPU_st0_ptr) ) + if ( poly_l2p1(st0_ptr, st0_ptr) ) { #ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ st1_ptr->sign ^= SIGN_POS^SIGN_NEG; @@ -1386,16 +1397,16 @@ static void fyl2xp1(void) partial_status = saved_status; /* Let the multiply set the flags */ - reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION); + reg_mul(st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION); pop(); } - else if ( (FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) + else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) { stack_underflow_pop(1); return; } - else if ( FPU_st0_tag == TW_Zero ) + else if ( st0_tag == TW_Zero ) { if ( st1_tag <= TW_Zero ) { @@ -1405,8 +1416,8 @@ static void fyl2xp1(void) return; #endif DENORM_OPERAND - FPU_st0_ptr->sign ^= st1_ptr->sign; - reg_move(FPU_st0_ptr, st1_ptr); + st0_ptr->sign ^= st1_ptr->sign; + reg_move(st0_ptr, st1_ptr); } else if ( st1_tag == TW_Infinity ) { @@ -1417,7 +1428,7 @@ static void fyl2xp1(void) } else if ( st1_tag == TW_NaN ) { - if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) ) + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) pop(); return; } @@ -1430,13 +1441,13 @@ static void fyl2xp1(void) #endif PARANOID pop(); return; } - else if ( FPU_st0_tag == TW_Valid ) + else if ( st0_tag == TW_Valid ) { if ( st1_tag == TW_Zero ) { - if ( FPU_st0_ptr->sign == SIGN_NEG ) + if ( st0_ptr->sign == SIGN_NEG ) { - if ( FPU_st0_ptr->exp >= EXP_BIAS ) + if ( st0_ptr->exp >= EXP_BIAS ) { /* st(0) holds <= -1.0 */ #ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ @@ -1447,25 +1458,25 @@ static void fyl2xp1(void) pop(); return; } #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND st1_ptr->sign ^= SIGN_POS^SIGN_NEG; pop(); return; } #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND pop(); return; } if ( st1_tag == TW_Infinity ) { - if ( FPU_st0_ptr->sign == SIGN_NEG ) + if ( st0_ptr->sign == SIGN_NEG ) { - if ( (FPU_st0_ptr->exp >= EXP_BIAS) && - !((FPU_st0_ptr->sigh == 0x80000000) && - (FPU_st0_ptr->sigl == 0)) ) + if ( (st0_ptr->exp >= EXP_BIAS) && + !((st0_ptr->sigh == 0x80000000) && + (st0_ptr->sigl == 0)) ) { /* st(0) holds < -1.0 */ #ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ @@ -1476,40 +1487,40 @@ static void fyl2xp1(void) pop(); return; } #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND st1_ptr->sign ^= SIGN_POS^SIGN_NEG; pop(); return; } #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND pop(); return; } if ( st1_tag == TW_NaN ) { - if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) ) + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) pop(); return; } } - else if ( FPU_st0_tag == TW_NaN ) + else if ( st0_tag == TW_NaN ) { - if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) ) + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) pop(); return; } - else if ( FPU_st0_tag == TW_Infinity ) + else if ( st0_tag == TW_Infinity ) { if ( st1_tag == TW_NaN ) { - if ( !real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr) ) + if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) ) pop(); return; } - else if ( FPU_st0_ptr->sign == SIGN_NEG ) + else if ( st0_ptr->sign == SIGN_NEG ) { int exponent = st1_ptr->exp; #ifndef PECULIAR_486 @@ -1565,21 +1576,22 @@ static void fyl2xp1(void) } -static void fscale(void) +static void fscale(FPU_REG *st0_ptr) { + char st0_tag = st0_ptr->tag; FPU_REG *st1_ptr = &st(1); char st1_tag = st1_ptr->tag; int old_cw = control_word; - char sign = FPU_st0_ptr->sign; + char sign = st0_ptr->sign; clear_C1(); - if ( !((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) + if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) ) { long scale; FPU_REG tmp; #ifdef DENORM_OPERAND - if ( ((FPU_st0_ptr->exp <= EXP_UNDER) || + if ( ((st0_ptr->exp <= EXP_UNDER) || (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) ) return; #endif DENORM_OPERAND @@ -1592,16 +1604,16 @@ static void fscale(void) if ( st1_ptr->sign == SIGN_POS ) { EXCEPTION(EX_Overflow); - sign = FPU_st0_ptr->sign; - reg_move(&CONST_INF, FPU_st0_ptr); - FPU_st0_ptr->sign = sign; + sign = st0_ptr->sign; + reg_move(&CONST_INF, st0_ptr); + st0_ptr->sign = sign; } else { EXCEPTION(EX_Underflow); - sign = FPU_st0_ptr->sign; - reg_move(&CONST_Z, FPU_st0_ptr); - FPU_st0_ptr->sign = sign; + sign = st0_ptr->sign; + reg_move(&CONST_Z, st0_ptr); + st0_ptr->sign = sign; } return; } @@ -1612,21 +1624,21 @@ static void fscale(void) round_to_int(&tmp); /* This can never overflow here */ control_word = old_cw; scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl; - scale += FPU_st0_ptr->exp; - FPU_st0_ptr->exp = scale; + scale += st0_ptr->exp; + st0_ptr->exp = scale; /* Use round_reg() to properly detect under/overflow etc */ - round_reg(FPU_st0_ptr, 0, control_word); + round_reg(st0_ptr, 0, control_word); return; } - else if ( FPU_st0_tag == TW_Valid ) + else if ( st0_tag == TW_Valid ) { if ( st1_tag == TW_Zero ) { #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND @@ -1635,21 +1647,21 @@ static void fscale(void) if ( st1_tag == TW_Infinity ) { #ifdef DENORM_OPERAND - if ( (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) + if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) ) return; #endif DENORM_OPERAND if ( st1_ptr->sign == SIGN_POS ) - { reg_move(&CONST_INF, FPU_st0_ptr); } + { reg_move(&CONST_INF, st0_ptr); } else - reg_move(&CONST_Z, FPU_st0_ptr); - FPU_st0_ptr->sign = sign; + reg_move(&CONST_Z, st0_ptr); + st0_ptr->sign = sign; return; } if ( st1_tag == TW_NaN ) - { real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); return; } + { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } } - else if ( FPU_st0_tag == TW_Zero ) + else if ( st0_tag == TW_Zero ) { if ( st1_tag == TW_Valid ) { @@ -1668,14 +1680,14 @@ static void fscale(void) return; else { - arith_invalid(FPU_st0_ptr); /* Zero scaled by +Infinity */ + arith_invalid(st0_ptr); /* Zero scaled by +Infinity */ return; } } else if ( st1_tag == TW_NaN ) - { real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); return; } + { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } } - else if ( FPU_st0_tag == TW_Infinity ) + else if ( st0_tag == TW_Infinity ) { if ( st1_tag == TW_Valid ) { @@ -1692,20 +1704,20 @@ static void fscale(void) return; else if ( st1_tag == TW_Infinity ) { - arith_invalid(FPU_st0_ptr); /* Infinity scaled by -Infinity */ + arith_invalid(st0_ptr); /* Infinity scaled by -Infinity */ return; } else if ( st1_tag == TW_NaN ) - { real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); return; } + { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } } - else if ( FPU_st0_tag == TW_NaN ) + else if ( st0_tag == TW_NaN ) { if ( st1_tag != TW_Empty ) - { real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); return; } + { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; } } #ifdef PARANOID - if ( !((FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty)) ) + if ( !((st0_tag == TW_Empty) || (st1_tag == TW_Empty)) ) { EXCEPTION(EX_INTERNAL | 0x115); return; @@ -1720,22 +1732,22 @@ static void fscale(void) /*---------------------------------------------------------------------------*/ -static FUNC const trig_table_a[] = { +static FUNC_ST0 const trig_table_a[] = { f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp }; void trig_a(void) { - (trig_table_a[FPU_rm])(); + (trig_table_a[FPU_rm])(&st(0)); } -static FUNC const trig_table_b[] = +static FUNC_ST0 const trig_table_b[] = { fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos }; void trig_b(void) { - (trig_table_b[FPU_rm])(); + (trig_table_b[FPU_rm])(&st(0)); } diff --git a/drivers/FPU-emu/get_address.c b/drivers/FPU-emu/get_address.c index a9c15cff1264..79d01879ca4e 100644 --- a/drivers/FPU-emu/get_address.c +++ b/drivers/FPU-emu/get_address.c @@ -19,6 +19,7 @@ #include +#include #include @@ -26,6 +27,9 @@ #include "exception.h" #include "fpu_emu.h" + +#define FPU_WRITE_BIT 0x10 + static int reg_offset[] = { offsetof(struct info,___eax), offsetof(struct info,___ecx), @@ -52,7 +56,7 @@ static int reg_offset_vm86[] = { #define VM86_REG_(x) (*(unsigned short *) \ (reg_offset_vm86[((unsigned)x)]+(char *) FPU_info)) -static int reg_offset_p286[] = { +static int reg_offset_pm[] = { offsetof(struct info,___cs), offsetof(struct info,___ds), offsetof(struct info,___es), @@ -62,12 +66,12 @@ static int reg_offset_p286[] = { offsetof(struct info,___ds) }; -#define P286_REG_(x) (*(unsigned short *) \ - (reg_offset_p286[((unsigned)x)]+(char *) FPU_info)) +#define PM_REG_(x) (*(unsigned short *) \ + (reg_offset_pm[((unsigned)x)]+(char *) FPU_info)) /* Decode the SIB byte. This function assumes mod != 0 */ -static void *sib(int mod, unsigned long *fpu_eip) +static int sib(int mod, unsigned long *fpu_eip) { unsigned char ss,index,base; long offset; @@ -117,14 +121,14 @@ static void *sib(int mod, unsigned long *fpu_eip) (*fpu_eip) += 4; } - return (void *) offset; + return offset; } -static unsigned long mode16_segment(fpu_addr_modes addr_modes) +static unsigned long vm86_segment(unsigned char segment, + unsigned short *selector) { - int segment = addr_modes.override.segment - 1; - + segment--; #ifdef PARANOID if ( segment > PREFIX_SS_ ) { @@ -132,11 +136,61 @@ static unsigned long mode16_segment(fpu_addr_modes addr_modes) math_abort(FPU_info,SIGSEGV); } #endif PARANOID - if ( addr_modes.vm86 ) - return (unsigned long)VM86_REG_(segment) << 4; - else if ( addr_modes.p286 ) - return (unsigned long)LDT_BASE_ADDR(P286_REG_(segment)); - return 0; + *selector = VM86_REG_(segment); + return (unsigned long)VM86_REG_(segment) << 4; +} + + +/* This should work for 16 and 32 bit protected mode. */ +static long pm_address(unsigned char FPU_modrm, unsigned char segment, + unsigned short *selector, long offset) +{ + struct desc_struct descriptor; + unsigned long base_address, limit, address, seg_top; + + segment--; +#ifdef PARANOID + if ( segment > PREFIX_SS_ ) + { + EXCEPTION(EX_INTERNAL|0x132); + math_abort(FPU_info,SIGSEGV); + } +#endif PARANOID + + *selector = PM_REG_(segment); + + descriptor = LDT_DESCRIPTOR(PM_REG_(segment)); + base_address = SEG_BASE_ADDR(descriptor); + address = base_address + offset; + limit = base_address + + (SEG_LIMIT(descriptor)+1) * SEG_GRANULARITY(descriptor) - 1; + if ( limit < base_address ) limit = 0xffffffff; + + if ( SEG_EXPAND_DOWN(descriptor) ) + { + if ( SEG_G_BIT(descriptor) ) + seg_top = 0xffffffff; + else + { + seg_top = base_address + (1 << 20); + if ( seg_top < base_address ) seg_top = 0xffffffff; + } + access_limit = + (address <= limit) || (address >= seg_top) ? 0 : + ((seg_top-address) >= 255 ? 255 : seg_top-address); + } + else + { + access_limit = + (address > limit) || (address < base_address) ? 0 : + ((limit-address) >= 254 ? 255 : limit-address+1); + } + if ( SEG_EXECUTE_ONLY(descriptor) || + (!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT)) ) + { + access_limit = 0; + } + return address; } @@ -157,117 +211,132 @@ static unsigned long mode16_segment(fpu_addr_modes addr_modes) */ -void get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, - fpu_addr_modes addr_modes) +void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip, + struct address *addr, +/* unsigned short *selector, unsigned long *offset, */ + fpu_addr_modes addr_modes) { unsigned char mod; + unsigned rm = FPU_modrm & 7; long *cpu_reg_ptr; - int offset = 0; /* Initialized just to stop compiler warnings. */ - -#ifndef PECULIAR_486 - /* This is a reasonable place to do this */ - FPU_data_selector = FPU_DS; -#endif PECULIAR_486 + int address = 0; /* Initialized just to stop compiler warnings. */ /* Memory accessed via the cs selector is write protected - in protected mode. */ -#define FPU_WRITE_BIT 0x10 - if ( !addr_modes.vm86 && (FPU_modrm & FPU_WRITE_BIT) + in `non-segmented' 32 bit protected mode. */ + if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) && (addr_modes.override.segment == PREFIX_CS_) ) { math_abort(FPU_info,SIGSEGV); } + addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ + mod = (FPU_modrm >> 6) & 3; - if (FPU_rm == 4 && mod != 3) + if (rm == 4 && mod != 3) { - FPU_data_address = sib(mod, fpu_eip); - return; + address = sib(mod, fpu_eip); } - - cpu_reg_ptr = & REG_(FPU_rm); - switch (mod) + else { - case 0: - if (FPU_rm == 5) + cpu_reg_ptr = & REG_(rm); + switch (mod) { - /* Special case: disp32 */ + case 0: + if (rm == 5) + { + /* Special case: disp32 */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(4); + address = get_fs_long((unsigned long *) (*fpu_eip)); + (*fpu_eip) += 4; + RE_ENTRANT_CHECK_ON; + addr->offset = address; + return (void *) address; + } + else + { + address = *cpu_reg_ptr; /* Just return the contents + of the cpu register */ + addr->offset = address; + return (void *) address; + } + case 1: + /* 8 bit signed displacement */ + RE_ENTRANT_CHECK_OFF; + FPU_code_verify_area(1); + address = (signed char) get_fs_byte((char *) (*fpu_eip)); + RE_ENTRANT_CHECK_ON; + (*fpu_eip)++; + break; + case 2: + /* 32 bit displacement */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(4); - offset = get_fs_long((unsigned long *) (*fpu_eip)); + address = (signed) get_fs_long((unsigned long *) (*fpu_eip)); (*fpu_eip) += 4; RE_ENTRANT_CHECK_ON; - FPU_data_address = (void *) offset; - return; - } - else - { - FPU_data_address = (void *)*cpu_reg_ptr; /* Just return the contents - of the cpu register */ - return; + break; + case 3: + /* Not legal for the FPU */ + EXCEPTION(EX_Invalid); } - case 1: - /* 8 bit signed displacement */ - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(1); - offset = (signed char) get_fs_byte((char *) (*fpu_eip)); - RE_ENTRANT_CHECK_ON; - (*fpu_eip)++; - break; - case 2: - /* 32 bit displacement */ - RE_ENTRANT_CHECK_OFF; - FPU_code_verify_area(4); - offset = (signed) get_fs_long((unsigned long *) (*fpu_eip)); - (*fpu_eip) += 4; - RE_ENTRANT_CHECK_ON; - break; - case 3: - /* Not legal for the FPU */ - EXCEPTION(EX_Invalid); + address += *cpu_reg_ptr; } - if ( addr_modes.mode16 ) + addr->offset = address; + + switch ( addr_modes.default_mode ) { - offset += mode16_segment(addr_modes); + case 0: + break; + case VM86: + address += vm86_segment(addr_modes.override.segment, + (unsigned short *)&(addr->selector)); + break; + case PM16: + case SEG32: + address = pm_address(FPU_modrm, addr_modes.override.segment, + (unsigned short *)&(addr->selector), address); + break; + default: + EXCEPTION(EX_INTERNAL|0x133); } - FPU_data_address = offset + (char *)*cpu_reg_ptr; + return (void *)address; } -void get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, - fpu_addr_modes addr_modes) +void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, + struct address *addr, +/* unsigned short *selector, unsigned long *offset, */ + fpu_addr_modes addr_modes) { unsigned char mod; - int offset = 0; /* Default used for mod == 0 */ - -#ifndef PECULIAR_486 - /* This is a reasonable place to do this */ - FPU_data_selector = FPU_DS; -#endif PECULIAR_486 + unsigned rm = FPU_modrm & 7; + int address = 0; /* Default used for mod == 0 */ /* Memory accessed via the cs selector is write protected - in protected mode. */ -#define FPU_WRITE_BIT 0x10 - if ( !addr_modes.vm86 && (FPU_modrm & FPU_WRITE_BIT) + in `non-segmented' 32 bit protected mode. */ + if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) && (addr_modes.override.segment == PREFIX_CS_) ) { math_abort(FPU_info,SIGSEGV); } + addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ + mod = (FPU_modrm >> 6) & 3; switch (mod) { case 0: - if (FPU_rm == 6) + if (rm == 6) { /* Special case: disp16 */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(2); - offset = (unsigned short)get_fs_word((unsigned short *) (*fpu_eip)); + address = (unsigned short)get_fs_word((unsigned short *) (*fpu_eip)); (*fpu_eip) += 2; RE_ENTRANT_CHECK_ON; goto add_segment; @@ -277,7 +346,7 @@ void get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, /* 8 bit signed displacement */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(1); - offset = (signed char) get_fs_byte((signed char *) (*fpu_eip)); + address = (signed char) get_fs_byte((signed char *) (*fpu_eip)); RE_ENTRANT_CHECK_ON; (*fpu_eip)++; break; @@ -285,7 +354,7 @@ void get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, /* 16 bit displacement */ RE_ENTRANT_CHECK_OFF; FPU_code_verify_area(2); - offset = (unsigned) get_fs_word((unsigned short *) (*fpu_eip)); + address = (unsigned) get_fs_word((unsigned short *) (*fpu_eip)); (*fpu_eip) += 2; RE_ENTRANT_CHECK_ON; break; @@ -294,47 +363,61 @@ void get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip, EXCEPTION(EX_Invalid); break; } - switch ( FPU_rm ) + switch ( rm ) { case 0: - offset += FPU_info->___ebx + FPU_info->___esi; + address += FPU_info->___ebx + FPU_info->___esi; break; case 1: - offset += FPU_info->___ebx + FPU_info->___edi; + address += FPU_info->___ebx + FPU_info->___edi; break; case 2: - offset += FPU_info->___ebp + FPU_info->___esi; + address += FPU_info->___ebp + FPU_info->___esi; if ( addr_modes.override.segment == PREFIX_DEFAULT ) addr_modes.override.segment = PREFIX_SS_; break; case 3: - offset += FPU_info->___ebp + FPU_info->___edi; + address += FPU_info->___ebp + FPU_info->___edi; if ( addr_modes.override.segment == PREFIX_DEFAULT ) addr_modes.override.segment = PREFIX_SS_; break; case 4: - offset += FPU_info->___esi; + address += FPU_info->___esi; break; case 5: - offset += FPU_info->___edi; + address += FPU_info->___edi; break; case 6: - offset += FPU_info->___ebp; + address += FPU_info->___ebp; if ( addr_modes.override.segment == PREFIX_DEFAULT ) addr_modes.override.segment = PREFIX_SS_; break; case 7: - offset += FPU_info->___ebx; + address += FPU_info->___ebx; break; } add_segment: - offset &= 0xffff; + address &= 0xffff; + + addr->offset = address; - if ( addr_modes.mode16 ) + switch ( addr_modes.default_mode ) { - offset += mode16_segment(addr_modes); + case 0: + break; + case VM86: + address += vm86_segment(addr_modes.override.segment, + (unsigned short *)&(addr->selector)); + break; + case PM16: + case SEG32: + address = pm_address(FPU_modrm, addr_modes.override.segment, + (unsigned short *)&(addr->selector), address); + break; + default: + EXCEPTION(EX_INTERNAL|0x131); } - FPU_data_address = (void *)offset ; + return (void *)address ; } diff --git a/drivers/FPU-emu/load_store.c b/drivers/FPU-emu/load_store.c index d1c1f755b098..6f0e167d6d03 100644 --- a/drivers/FPU-emu/load_store.c +++ b/drivers/FPU-emu/load_store.c @@ -27,12 +27,12 @@ #include "control_w.h" -#define _NONE_ 0 /* FPU_st0_ptr etc not needed */ +#define _NONE_ 0 /* st0_ptr etc not needed */ #define _REG0_ 1 /* Will be storing st(0) */ #define _PUSH_ 3 /* Need to check for space to push onto stack */ #define _null_ 4 /* Function illegal or not implemented */ -#define pop_0() { pop_ptr->tag = TW_Empty; top++; } +#define pop_0() { st0_ptr->tag = TW_Empty; top++; } static unsigned char const type_table[32] = { @@ -46,192 +46,215 @@ static unsigned char const type_table[32] = { _NONE_, _REG0_, _NONE_, _REG0_ }; -void load_store_instr(char type, fpu_addr_modes addr_modes) +unsigned char const data_sizes_16[32] = { + 4, 4, 8, 2, 0, 0, 0, 0, + 4, 4, 8, 2, 4, 4, 8, 2, + 14, 0, 94, 10, 2, 10, 0, 8, + 14, 0, 94, 10, 2, 10, 2, 8 +}; + +unsigned char const data_sizes_32[32] = { + 4, 4, 8, 2, 0, 0, 0, 0, + 4, 4, 8, 2, 4, 4, 8, 2, + 28, 0,108, 10, 2, 10, 0, 8, + 28, 0,108, 10, 2, 10, 2, 8 +}; + +int load_store_instr(unsigned char type, fpu_addr_modes addr_modes, + void *data_address) { - FPU_REG *pop_ptr; /* We need a version of FPU_st0_ptr which won't - change if the emulator is re-entered. */ + FPU_REG loaded_data; + FPU_REG *st0_ptr; + + st0_ptr = NULL; /* Initialized just to stop compiler warnings. */ - pop_ptr = NULL; /* Initialized just to stop compiler warnings. */ - switch ( type_table[(int) (unsigned) type] ) + 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_: - pop_ptr = &st(0); /* Some of these instructions pop after + st0_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; } + st0_ptr = &st(-1); + if ( st0_ptr->tag != TW_Empty ) + { stack_overflow(); return 0; } top--; } break; case _null_: FPU_illegal(); - return; + return 0; #ifdef PARANOID default: - EXCEPTION(EX_INTERNAL); - return; + EXCEPTION(EX_INTERNAL|0x141); + return 0; #endif PARANOID } -switch ( type ) - { - case 000: /* fld m32real */ - clear_C1(); - reg_load_single(); - if ( (FPU_loaded_data.tag == TW_NaN) && - real_2op_NaN(&FPU_loaded_data, &FPU_loaded_data, &FPU_loaded_data) ) - { - top++; - break; - } - reg_move(&FPU_loaded_data, pop_ptr); - break; - case 001: /* fild m32int */ - clear_C1(); - reg_load_int32(); - reg_move(&FPU_loaded_data, pop_ptr); - break; - case 002: /* fld m64real */ - clear_C1(); - reg_load_double(); - if ( (FPU_loaded_data.tag == TW_NaN) && - real_2op_NaN(&FPU_loaded_data, &FPU_loaded_data, &FPU_loaded_data) ) - { - top++; - break; - } - reg_move(&FPU_loaded_data, pop_ptr); - break; - case 003: /* fild m16int */ - clear_C1(); - reg_load_int16(); - reg_move(&FPU_loaded_data, pop_ptr); - break; - case 010: /* fst m32real */ - clear_C1(); - reg_store_single(); - break; - case 011: /* fist m32int */ - clear_C1(); - reg_store_int32(); - break; - case 012: /* fst m64real */ - clear_C1(); - reg_store_double(); - break; - case 013: /* fist m16int */ - clear_C1(); - reg_store_int16(); - break; - case 014: /* fstp m32real */ - clear_C1(); - 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 */ - clear_C1(); - 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 */ - clear_C1(); - 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 */ - clear_C1(); - 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(addr_modes); - break; - case 022: /* frstor m94/108byte */ - frstor(addr_modes); - break; - case 023: /* fbld m80dec */ - clear_C1(); - reg_load_bcd(); - reg_move(&FPU_loaded_data, pop_ptr); - break; - case 024: /* fldcw */ - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_READ, FPU_data_address, 2); - control_word = get_fs_word((unsigned short *) FPU_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); + switch ( type ) + { + case 000: /* fld m32real */ + clear_C1(); + reg_load_single((float *)data_address, &loaded_data); + if ( (loaded_data.tag == TW_NaN) && + real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) ) + { + top++; + break; + } + reg_move(&loaded_data, st0_ptr); + break; + case 001: /* fild m32int */ + clear_C1(); + reg_load_int32((long *)data_address, st0_ptr); + break; + case 002: /* fld m64real */ + clear_C1(); + reg_load_double((double *)data_address, &loaded_data); + if ( (loaded_data.tag == TW_NaN) && + real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) ) + { + top++; + break; + } + reg_move(&loaded_data, st0_ptr); + break; + case 003: /* fild m16int */ + clear_C1(); + reg_load_int16((short *)data_address, st0_ptr); + break; + case 010: /* fst m32real */ + clear_C1(); + reg_store_single((float *)data_address, st0_ptr); + break; + case 011: /* fist m32int */ + clear_C1(); + reg_store_int32((long *)data_address, st0_ptr); + break; + case 012: /* fst m64real */ + clear_C1(); + reg_store_double((double *)data_address, st0_ptr); + break; + case 013: /* fist m16int */ + clear_C1(); + reg_store_int16((short *)data_address, st0_ptr); + break; + case 014: /* fstp m32real */ + clear_C1(); + if ( reg_store_single((float *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 015: /* fistp m32int */ + clear_C1(); + if ( reg_store_int32((long *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 016: /* fstp m64real */ + clear_C1(); + if ( reg_store_double((double *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + case 017: /* fistp m16int */ + clear_C1(); + if ( reg_store_int16((short *)data_address, st0_ptr) ) + 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, (char *)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, (char *)data_address); + /* Ensure that the values just loaded are not changed by + fix-up operations. */ + return 1; + case 023: /* fbld m80dec */ + clear_C1(); + reg_load_bcd((char *)data_address, st0_ptr); + break; + case 024: /* fldcw */ + RE_ENTRANT_CHECK_OFF; + FPU_verify_area(VERIFY_READ, data_address, 2); + control_word = get_fs_word((unsigned short *) 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 */ + control_word |= 0x40; /* An 80486 appears to always set this bit */ #endif PECULIAR_486 - NO_NET_DATA_EFFECT; - NO_NET_INSTR_EFFECT; - break; - case 025: /* fld m80real */ - clear_C1(); - reg_load_extended(); - reg_move(&FPU_loaded_data, pop_ptr); - break; - case 027: /* fild m64int */ - clear_C1(); - reg_load_int64(); - reg_move(&FPU_loaded_data, pop_ptr); - break; - case 030: /* fstenv m14/28byte */ - fstenv(addr_modes); - NO_NET_DATA_EFFECT; - break; - case 032: /* fsave */ - fsave(addr_modes); - NO_NET_DATA_EFFECT; - break; - case 033: /* fbstp m80dec */ - clear_C1(); - 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; - FPU_verify_area(VERIFY_WRITE,FPU_data_address,2); - put_fs_word(control_word, (short *) FPU_data_address); - RE_ENTRANT_CHECK_ON; - NO_NET_DATA_EFFECT; - NO_NET_INSTR_EFFECT; - break; - case 035: /* fstp m80real */ - clear_C1(); - 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 */ - RE_ENTRANT_CHECK_OFF; - FPU_verify_area(VERIFY_WRITE,FPU_data_address,2); - put_fs_word(status_word(),(short *) FPU_data_address); - RE_ENTRANT_CHECK_ON; - NO_NET_DATA_EFFECT; - NO_NET_INSTR_EFFECT; - break; - case 037: /* fistp m64int */ - clear_C1(); - if ( reg_store_int64() ) - pop_0(); /* pop only if the number was actually stored - (see the 80486 manual p16-28) */ - break; - } + return 1; + case 025: /* fld m80real */ + clear_C1(); + reg_load_extended((long double *)data_address, st0_ptr); + break; + case 027: /* fild m64int */ + clear_C1(); + reg_load_int64((long long *)data_address, st0_ptr); + break; + case 030: /* fstenv m14/28byte */ + fstenv(addr_modes, (char *)data_address); + return 1; + case 032: /* fsave */ + fsave(addr_modes, (char *)data_address); + return 1; + case 033: /* fbstp m80dec */ + clear_C1(); + if ( reg_store_bcd((char *)data_address, st0_ptr) ) + 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); + put_fs_word(control_word, (short *) data_address); + RE_ENTRANT_CHECK_ON; + return 1; + case 035: /* fstp m80real */ + clear_C1(); + if ( reg_store_extended((long double *)data_address, st0_ptr) ) + 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); + put_fs_word(status_word(),(short *) data_address); + RE_ENTRANT_CHECK_ON; + return 1; + case 037: /* fistp m64int */ + clear_C1(); + if ( reg_store_int64((long long *)data_address, st0_ptr) ) + pop_0(); /* pop only if the number was actually stored + (see the 80486 manual p16-28) */ + break; + } + return 0; } diff --git a/drivers/FPU-emu/poly_atan.c b/drivers/FPU-emu/poly_atan.c index ea606f976943..521eeb8a8d65 100644 --- a/drivers/FPU-emu/poly_atan.c +++ b/drivers/FPU-emu/poly_atan.c @@ -187,16 +187,8 @@ void poly_add_1(FPU_REG *src) for the use of this function in poly_atan. Simple truncation is used here instead of round-to-nearest. */ -#ifdef OBSOLETE -char round = (src->sigl & 3) == 3; -#endif OBSOLETE - shrx(&src->sigl, 1); -#ifdef OBSOLETE -if ( round ) significand(src)++; /* Round to even */ -#endif OBSOLETE - src->sigh |= 0x80000000; src->exp = EXP_BIAS; diff --git a/drivers/FPU-emu/reg_compare.c b/drivers/FPU-emu/reg_compare.c index 020030119e0a..eb4a1fa999f5 100644 --- a/drivers/FPU-emu/reg_compare.c +++ b/drivers/FPU-emu/reg_compare.c @@ -24,10 +24,15 @@ int compare(FPU_REG const *b) { int diff; + char st0_tag; + FPU_REG *st0_ptr; - if ( FPU_st0_ptr->tag | b->tag ) + st0_ptr = &st(0); + st0_tag = st0_ptr->tag; + + if ( st0_tag | b->tag ) { - if ( FPU_st0_ptr->tag == TW_Zero ) + if ( st0_tag == TW_Zero ) { if ( b->tag == TW_Zero ) return COMP_A_eq_B; if ( b->tag == TW_Valid ) @@ -42,23 +47,23 @@ int compare(FPU_REG const *b) } else if ( b->tag == TW_Zero ) { - if ( FPU_st0_ptr->tag == TW_Valid ) + if ( st0_tag == TW_Valid ) { - return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) #ifdef DENORM_OPERAND - | ((FPU_st0_ptr->exp <= EXP_UNDER ) + | ((st0_ptr->exp <= EXP_UNDER ) ? COMP_Denormal : 0 ) #endif DENORM_OPERAND ; } } - if ( FPU_st0_ptr->tag == TW_Infinity ) + if ( st0_tag == TW_Infinity ) { if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) ) { - return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) #ifdef DENORM_OPERAND | (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ? @@ -69,19 +74,19 @@ int compare(FPU_REG const *b) else if ( b->tag == TW_Infinity ) { /* The 80486 book says that infinities can be equal! */ - return (FPU_st0_ptr->sign == b->sign) ? COMP_A_eq_B : - ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); + return (st0_ptr->sign == b->sign) ? COMP_A_eq_B : + ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); } /* Fall through to the NaN code */ } else if ( b->tag == TW_Infinity ) { - if ( (FPU_st0_ptr->tag == TW_Valid) || (FPU_st0_ptr->tag == TW_Zero) ) + if ( (st0_tag == TW_Valid) || (st0_tag == TW_Zero) ) { return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) #ifdef DENORM_OPERAND - | (((FPU_st0_ptr->tag == TW_Valid) - && (FPU_st0_ptr->exp <= EXP_UNDER)) ? + | (((st0_tag == TW_Valid) + && (st0_ptr->exp <= EXP_UNDER)) ? COMP_Denormal : 0) #endif DENORM_OPERAND ; @@ -91,9 +96,9 @@ int compare(FPU_REG const *b) /* The only possibility now should be that one of the arguments is a NaN */ - if ( (FPU_st0_ptr->tag == TW_NaN) || (b->tag == TW_NaN) ) + if ( (st0_tag == TW_NaN) || (b->tag == TW_NaN) ) { - if ( ((FPU_st0_ptr->tag == TW_NaN) && !(FPU_st0_ptr->sigh & 0x40000000)) + if ( ((st0_tag == TW_NaN) && !(st0_ptr->sigh & 0x40000000)) || ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)) ) /* At least one arg is a signaling NaN */ return COMP_No_Comp | COMP_SNaN | COMP_NaN; @@ -106,51 +111,51 @@ int compare(FPU_REG const *b) } #ifdef PARANOID - if (!(FPU_st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid); + if (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid); if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid); #endif PARANOID - if (FPU_st0_ptr->sign != b->sign) + if (st0_ptr->sign != b->sign) { - return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) #ifdef DENORM_OPERAND | - ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? COMP_Denormal : 0) #endif DENORM_OPERAND ; } - diff = FPU_st0_ptr->exp - b->exp; + diff = st0_ptr->exp - b->exp; if ( diff == 0 ) { - diff = FPU_st0_ptr->sigh - b->sigh; /* Works only if ms bits are + diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are identical */ if ( diff == 0 ) { - diff = FPU_st0_ptr->sigl > b->sigl; + diff = st0_ptr->sigl > b->sigl; if ( diff == 0 ) - diff = -(FPU_st0_ptr->sigl < b->sigl); + diff = -(st0_ptr->sigl < b->sigl); } } if ( diff > 0 ) { - return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) #ifdef DENORM_OPERAND | - ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? COMP_Denormal : 0) #endif DENORM_OPERAND ; } if ( diff < 0 ) { - return ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) + return ((st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) #ifdef DENORM_OPERAND | - ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? COMP_Denormal : 0) #endif DENORM_OPERAND ; @@ -159,7 +164,7 @@ int compare(FPU_REG const *b) return COMP_A_eq_B #ifdef DENORM_OPERAND | - ( ((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? + ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ? COMP_Denormal : 0) #endif DENORM_OPERAND ; @@ -168,11 +173,11 @@ int compare(FPU_REG const *b) /* This function requires that st(0) is not empty */ -int compare_st_data(void) +int compare_st_data(FPU_REG const *loaded_data) { int f, c; - c = compare(&FPU_loaded_data); + c = compare(loaded_data); if (c & COMP_NaN) { @@ -214,7 +219,7 @@ static int compare_st_st(int nr) { int f, c; - if ( !NOT_EMPTY_0 || !NOT_EMPTY(nr) ) + if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) { setcc(SW_C3 | SW_C2 | SW_C0); /* Stack fault */ @@ -264,7 +269,7 @@ static int compare_u_st_st(int nr) { int f, c; - if ( !NOT_EMPTY_0 || !NOT_EMPTY(nr) ) + if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) { setcc(SW_C3 | SW_C2 | SW_C0); /* Stack fault */ @@ -340,10 +345,7 @@ void fcompp() return; } if ( !compare_st_st(1) ) - { - pop(); FPU_st0_ptr = &st(0); - pop(); - } + poppop(); } @@ -369,10 +371,7 @@ void fucompp() if (FPU_rm == 1) { if ( !compare_u_st_st(1) ) - { - pop(); FPU_st0_ptr = &st(0); - pop(); - } + poppop(); } else FPU_illegal(); diff --git a/drivers/FPU-emu/reg_constant.c b/drivers/FPU-emu/reg_constant.c index 29d157c8b80d..c1981ce24140 100644 --- a/drivers/FPU-emu/reg_constant.c +++ b/drivers/FPU-emu/reg_constant.c @@ -66,7 +66,7 @@ static void fld_const(FPU_REG const *c) return; } push(); - reg_move(c, FPU_st0_ptr); + reg_move(c, st_new_ptr); clear_C1(); } diff --git a/drivers/FPU-emu/reg_div.S b/drivers/FPU-emu/reg_div.S index 2727cfaff6fa..2fbc5f7c41a6 100644 --- a/drivers/FPU-emu/reg_div.S +++ b/drivers/FPU-emu/reg_div.S @@ -25,9 +25,9 @@ _reg_div: pushl %ebp movl %esp,%ebp -#ifdef REENTRANT_FPU +#ifndef NON_REENTRANT_FPU subl $28,%esp /* Needed by divide_kernel */ -#endif REENTRANT_FPU +#endif NON_REENTRANT_FPU pushl %esi pushl %edi @@ -214,11 +214,11 @@ LDiv_negative_result: xorl %eax,%eax /* Valid result */ LDiv_exit: -#ifdef REENTRANT_FPU +#ifndef NON_REENTRANT_FPU leal -40(%ebp),%esp #else leal -12(%ebp),%esp -#endif REENTRANT_FPU +#endif NON_REENTRANT_FPU popl %ebx popl %edi diff --git a/drivers/FPU-emu/reg_ld_str.c b/drivers/FPU-emu/reg_ld_str.c index dcafbe378d34..9f8133b6b18b 100644 --- a/drivers/FPU-emu/reg_ld_str.c +++ b/drivers/FPU-emu/reg_ld_str.c @@ -40,42 +40,34 @@ static void write_to_extended(FPU_REG *rp, char *d); -FPU_REG FPU_loaded_data; - /* Get a long double from user memory */ -int reg_load_extended(void) +int reg_load_extended(long double *s, FPU_REG *loaded_data) { - long double *s = (long double *)FPU_data_address; unsigned long sigl, sigh, exp; RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, s, 10); - /* Use temporary variables here because FPU_loaded data is - static and hence re-entrancy problems can arise */ sigl = get_fs_long((unsigned long *) s); sigh = get_fs_long(1 + (unsigned long *) s); exp = get_fs_word(4 + (unsigned short *) s); RE_ENTRANT_CHECK_ON; - FPU_loaded_data.tag = TW_Valid; /* Default */ - FPU_loaded_data.sigl = sigl; - FPU_loaded_data.sigh = sigh; + loaded_data->tag = TW_Valid; /* Default */ + loaded_data->sigl = sigl; + loaded_data->sigh = sigh; if (exp & 0x8000) - FPU_loaded_data.sign = SIGN_NEG; + loaded_data->sign = SIGN_NEG; else - FPU_loaded_data.sign = SIGN_POS; + loaded_data->sign = SIGN_POS; exp &= 0x7fff; - FPU_loaded_data.exp = exp - EXTENDED_Ebias + EXP_BIAS; + loaded_data->exp = exp - EXTENDED_Ebias + EXP_BIAS; - /* Assume that optimisation can keep sigl, sigh, and exp in - registers, otherwise it would be more efficient to work - with FPU_loaded_data (which is static) here. */ if ( exp == 0 ) { if ( !(sigh | sigl) ) { - FPU_loaded_data.tag = TW_Zero; + loaded_data->tag = TW_Zero; return 0; } /* The number is a de-normal or pseudodenormal. */ @@ -85,15 +77,15 @@ int reg_load_extended(void) /* Convert it for internal use. */ /* This is non-80486 behaviour because the number loses its 'denormal' identity. */ - FPU_loaded_data.exp++; + loaded_data->exp++; return 1; } else { /* Is a denormal. */ /* Convert it for internal use. */ - FPU_loaded_data.exp++; - normalize_nuo(&FPU_loaded_data); + loaded_data->exp++; + normalize_nuo(loaded_data); return 0; } } @@ -102,13 +94,13 @@ int reg_load_extended(void) if ( !((sigh ^ 0x80000000) | sigl) ) { /* Matches the bit pattern for Infinity. */ - FPU_loaded_data.exp = EXP_Infinity; - FPU_loaded_data.tag = TW_Infinity; + loaded_data->exp = EXP_Infinity; + loaded_data->tag = TW_Infinity; return 0; } - FPU_loaded_data.exp = EXP_NaN; - FPU_loaded_data.tag = TW_NaN; + loaded_data->exp = EXP_NaN; + loaded_data->tag = TW_NaN; if ( !(sigh & 0x80000000) ) { /* NaNs have the ms bit set to 1. */ @@ -116,9 +108,9 @@ int reg_load_extended(void) /* This is non 80486 behaviour */ /* This should generate an Invalid Operand exception later, so we convert it to a SNaN */ - FPU_loaded_data.sigh = 0x80000000; - FPU_loaded_data.sigl = 0x00000001; - FPU_loaded_data.sign = SIGN_NEG; + loaded_data->sigh = 0x80000000; + loaded_data->sigl = 0x00000001; + loaded_data->sign = SIGN_NEG; return 1; } return 0; @@ -133,11 +125,11 @@ int reg_load_extended(void) /* This is non-80486 behaviour */ /* This should generate an Invalid Operand exception later, so we convert it to a SNaN */ - FPU_loaded_data.sigh = 0x80000000; - FPU_loaded_data.sigl = 0x00000001; - FPU_loaded_data.sign = SIGN_NEG; - FPU_loaded_data.exp = EXP_NaN; - FPU_loaded_data.tag = TW_NaN; + loaded_data->sigh = 0x80000000; + loaded_data->sigl = 0x00000001; + loaded_data->sign = SIGN_NEG; + loaded_data->exp = EXP_NaN; + loaded_data->tag = TW_NaN; return 1; } return 0; @@ -145,9 +137,8 @@ int reg_load_extended(void) /* Get a double from user memory */ -int reg_load_double(void) +int reg_load_double(double *dfloat, FPU_REG *loaded_data) { - double *dfloat = (double *)FPU_data_address; int exp; unsigned m64, l64; @@ -158,9 +149,9 @@ int reg_load_double(void) RE_ENTRANT_CHECK_ON; if (m64 & 0x80000000) - FPU_loaded_data.sign = SIGN_NEG; + loaded_data->sign = SIGN_NEG; else - FPU_loaded_data.sign = SIGN_POS; + loaded_data->sign = SIGN_POS; exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias; m64 &= 0xfffff; if (exp > DOUBLE_Emax) @@ -169,20 +160,20 @@ int reg_load_double(void) if ((m64 == 0) && (l64 == 0)) { /* +- infinity */ - FPU_loaded_data.sigh = 0x80000000; - FPU_loaded_data.sigl = 0x00000000; - FPU_loaded_data.exp = EXP_Infinity; - FPU_loaded_data.tag = TW_Infinity; + loaded_data->sigh = 0x80000000; + loaded_data->sigl = 0x00000000; + loaded_data->exp = EXP_Infinity; + loaded_data->tag = TW_Infinity; return 0; } else { /* Must be a signaling or quiet NaN */ - FPU_loaded_data.exp = EXP_NaN; - FPU_loaded_data.tag = TW_NaN; - FPU_loaded_data.sigh = (m64 << 11) | 0x80000000; - FPU_loaded_data.sigh |= l64 >> 21; - FPU_loaded_data.sigl = l64 << 11; + loaded_data->exp = EXP_NaN; + loaded_data->tag = TW_NaN; + loaded_data->sigh = (m64 << 11) | 0x80000000; + loaded_data->sigh |= l64 >> 21; + loaded_data->sigl = l64 << 11; return 0; /* The calling function must look for NaNs */ } } @@ -192,30 +183,30 @@ int reg_load_double(void) if ((m64 == 0) && (l64 == 0)) { /* Zero */ - int c = FPU_loaded_data.sign; - reg_move(&CONST_Z, &FPU_loaded_data); - FPU_loaded_data.sign = c; + int c = loaded_data->sign; + reg_move(&CONST_Z, loaded_data); + loaded_data->sign = c; return 0; } else { /* De-normal */ - FPU_loaded_data.exp = DOUBLE_Emin + EXP_BIAS; - FPU_loaded_data.tag = TW_Valid; - FPU_loaded_data.sigh = m64 << 11; - FPU_loaded_data.sigh |= l64 >> 21; - FPU_loaded_data.sigl = l64 << 11; - normalize_nuo(&FPU_loaded_data); + loaded_data->exp = DOUBLE_Emin + EXP_BIAS; + loaded_data->tag = TW_Valid; + loaded_data->sigh = m64 << 11; + loaded_data->sigh |= l64 >> 21; + loaded_data->sigl = l64 << 11; + normalize_nuo(loaded_data); return denormal_operand(); } } else { - FPU_loaded_data.exp = exp + EXP_BIAS; - FPU_loaded_data.tag = TW_Valid; - FPU_loaded_data.sigh = (m64 << 11) | 0x80000000; - FPU_loaded_data.sigh |= l64 >> 21; - FPU_loaded_data.sigl = l64 << 11; + loaded_data->exp = exp + EXP_BIAS; + loaded_data->tag = TW_Valid; + loaded_data->sigh = (m64 << 11) | 0x80000000; + loaded_data->sigh |= l64 >> 21; + loaded_data->sigl = l64 << 11; return 0; } @@ -223,9 +214,8 @@ int reg_load_double(void) /* Get a float from user memory */ -int reg_load_single(void) +int reg_load_single(float *single, FPU_REG *loaded_data) { - float *single = (float *)FPU_data_address; unsigned m32; int exp; @@ -235,15 +225,15 @@ int reg_load_single(void) RE_ENTRANT_CHECK_ON; if (m32 & 0x80000000) - FPU_loaded_data.sign = SIGN_NEG; + loaded_data->sign = SIGN_NEG; else - FPU_loaded_data.sign = SIGN_POS; + loaded_data->sign = SIGN_POS; if (!(m32 & 0x7fffffff)) { /* Zero */ - int c = FPU_loaded_data.sign; - reg_move(&CONST_Z, &FPU_loaded_data); - FPU_loaded_data.sign = c; + int c = loaded_data->sign; + reg_move(&CONST_Z, loaded_data); + loaded_data->sign = c; return 0; } exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias; @@ -251,11 +241,11 @@ int reg_load_single(void) if ( exp < SINGLE_Emin ) { /* De-normals */ - FPU_loaded_data.exp = SINGLE_Emin + EXP_BIAS; - FPU_loaded_data.tag = TW_Valid; - FPU_loaded_data.sigh = m32; - FPU_loaded_data.sigl = 0; - normalize_nuo(&FPU_loaded_data); + loaded_data->exp = SINGLE_Emin + EXP_BIAS; + loaded_data->tag = TW_Valid; + loaded_data->sigh = m32; + loaded_data->sigl = 0; + normalize_nuo(loaded_data); return denormal_operand(); } else if ( exp > SINGLE_Emax ) @@ -264,37 +254,36 @@ int reg_load_single(void) if ( m32 == 0 ) { /* +- infinity */ - FPU_loaded_data.sigh = 0x80000000; - FPU_loaded_data.sigl = 0x00000000; - FPU_loaded_data.exp = EXP_Infinity; - FPU_loaded_data.tag = TW_Infinity; + loaded_data->sigh = 0x80000000; + loaded_data->sigl = 0x00000000; + loaded_data->exp = EXP_Infinity; + loaded_data->tag = TW_Infinity; return 0; } else { /* Must be a signaling or quiet NaN */ - FPU_loaded_data.exp = EXP_NaN; - FPU_loaded_data.tag = TW_NaN; - FPU_loaded_data.sigh = m32 | 0x80000000; - FPU_loaded_data.sigl = 0; + loaded_data->exp = EXP_NaN; + loaded_data->tag = TW_NaN; + loaded_data->sigh = m32 | 0x80000000; + loaded_data->sigl = 0; return 0; /* The calling function must look for NaNs */ } } else { - FPU_loaded_data.exp = exp + EXP_BIAS; - FPU_loaded_data.sigh = m32 | 0x80000000; - FPU_loaded_data.sigl = 0; - FPU_loaded_data.tag = TW_Valid; + loaded_data->exp = exp + EXP_BIAS; + loaded_data->sigh = m32 | 0x80000000; + loaded_data->sigl = 0; + loaded_data->tag = TW_Valid; return 0; } } /* Get a long long from user memory */ -void reg_load_int64(void) +void reg_load_int64(long long *_s, FPU_REG *loaded_data) { - long long *_s = (long long *)FPU_data_address; int e; long long s; @@ -305,28 +294,27 @@ void reg_load_int64(void) RE_ENTRANT_CHECK_ON; if (s == 0) - { reg_move(&CONST_Z, &FPU_loaded_data); return; } + { reg_move(&CONST_Z, loaded_data); return; } if (s > 0) - FPU_loaded_data.sign = SIGN_POS; + loaded_data->sign = SIGN_POS; else { s = -s; - FPU_loaded_data.sign = SIGN_NEG; + loaded_data->sign = SIGN_NEG; } e = EXP_BIAS + 63; - significand(&FPU_loaded_data) = s; - FPU_loaded_data.exp = e; - FPU_loaded_data.tag = TW_Valid; - normalize_nuo(&FPU_loaded_data); + significand(loaded_data) = s; + loaded_data->exp = e; + loaded_data->tag = TW_Valid; + normalize_nuo(loaded_data); } /* Get a long from user memory */ -void reg_load_int32(void) +void reg_load_int32(long *_s, FPU_REG *loaded_data) { - long *_s = (long *)FPU_data_address; long s; int e; @@ -336,29 +324,28 @@ void reg_load_int32(void) RE_ENTRANT_CHECK_ON; if (s == 0) - { reg_move(&CONST_Z, &FPU_loaded_data); return; } + { reg_move(&CONST_Z, loaded_data); return; } if (s > 0) - FPU_loaded_data.sign = SIGN_POS; + loaded_data->sign = SIGN_POS; else { s = -s; - FPU_loaded_data.sign = SIGN_NEG; + loaded_data->sign = SIGN_NEG; } e = EXP_BIAS + 31; - FPU_loaded_data.sigh = s; - FPU_loaded_data.sigl = 0; - FPU_loaded_data.exp = e; - FPU_loaded_data.tag = TW_Valid; - normalize_nuo(&FPU_loaded_data); + loaded_data->sigh = s; + loaded_data->sigl = 0; + loaded_data->exp = e; + loaded_data->tag = TW_Valid; + normalize_nuo(loaded_data); } /* Get a short from user memory */ -void reg_load_int16(void) +void reg_load_int16(short *_s, FPU_REG *loaded_data) { - short *_s = (short *)FPU_data_address; int s, e; RE_ENTRANT_CHECK_OFF; @@ -368,30 +355,29 @@ void reg_load_int16(void) RE_ENTRANT_CHECK_ON; if (s == 0) - { reg_move(&CONST_Z, &FPU_loaded_data); return; } + { reg_move(&CONST_Z, loaded_data); return; } if (s > 0) - FPU_loaded_data.sign = SIGN_POS; + loaded_data->sign = SIGN_POS; else { s = -s; - FPU_loaded_data.sign = SIGN_NEG; + loaded_data->sign = SIGN_NEG; } e = EXP_BIAS + 15; - FPU_loaded_data.sigh = s << 16; + loaded_data->sigh = s << 16; - FPU_loaded_data.sigl = 0; - FPU_loaded_data.exp = e; - FPU_loaded_data.tag = TW_Valid; - normalize_nuo(&FPU_loaded_data); + loaded_data->sigl = 0; + loaded_data->exp = e; + loaded_data->tag = TW_Valid; + normalize_nuo(loaded_data); } /* Get a packed bcd array from user memory */ -void reg_load_bcd(void) +void reg_load_bcd(char *s, FPU_REG *loaded_data) { - char *s = (char *)FPU_data_address; int pos; unsigned char bcd; long long l=0; @@ -409,48 +395,45 @@ void reg_load_bcd(void) l *= 10; l += bcd & 0x0f; } - - /* Finish all access to user memory before putting stuff into - the static FPU_loaded_data */ + RE_ENTRANT_CHECK_OFF; - FPU_loaded_data.sign = + loaded_data->sign = ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ? SIGN_NEG : SIGN_POS; RE_ENTRANT_CHECK_ON; if (l == 0) { - char sign = FPU_loaded_data.sign; - reg_move(&CONST_Z, &FPU_loaded_data); - FPU_loaded_data.sign = sign; + char sign = loaded_data->sign; + reg_move(&CONST_Z, loaded_data); + loaded_data->sign = sign; } else { - significand(&FPU_loaded_data) = l; - FPU_loaded_data.exp = EXP_BIAS + 63; - FPU_loaded_data.tag = TW_Valid; - normalize_nuo(&FPU_loaded_data); + significand(loaded_data) = l; + loaded_data->exp = EXP_BIAS + 63; + loaded_data->tag = TW_Valid; + normalize_nuo(loaded_data); } } /*===========================================================================*/ /* Put a long double into user memory */ -int reg_store_extended(void) +int reg_store_extended(long double *d, FPU_REG *st0_ptr) { /* The only exception raised by an attempt to store to an extended format is the Invalid Stack exception, i.e. attempting to store from an empty register. */ - long double *d = (long double *)FPU_data_address; - if ( FPU_st0_tag != TW_Empty ) + if ( st0_ptr->tag != TW_Empty ) { RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE, d, 10); RE_ENTRANT_CHECK_ON; - write_to_extended(FPU_st0_ptr, (char *) FPU_data_address); + write_to_extended(st0_ptr, (char *) d); return 1; } @@ -475,18 +458,18 @@ int reg_store_extended(void) /* Put a double into user memory */ -int reg_store_double(void) +int reg_store_double(double *dfloat, FPU_REG *st0_ptr) { - double *dfloat = (double *)FPU_data_address; unsigned long l[2]; unsigned long increment = 0; /* avoid gcc warnings */ + char st0_tag = st0_ptr->tag; - if (FPU_st0_tag == TW_Valid) + if (st0_tag == TW_Valid) { int exp; FPU_REG tmp; - reg_move(FPU_st0_ptr, &tmp); + reg_move(st0_ptr, &tmp); exp = tmp.exp - EXP_BIAS; if ( exp < DOUBLE_Emin ) /* It may be a denormal */ @@ -497,7 +480,7 @@ int reg_store_double(void) #ifndef PECULIAR_486 /* An 80486 is supposed to be able to generate a denormal exception here, but... */ - if ( FPU_st0_ptr->exp <= EXP_UNDER ) + if ( st0_ptr->exp <= EXP_UNDER ) { /* Underflow has priority. */ if ( control_word & CW_Underflow ) @@ -515,7 +498,7 @@ int reg_store_double(void) that the 80486 rounds to the dest precision, then converts to decide underflow. */ if ( !((tmp.sigh == 0x00100000) && (tmp.sigl == 0) && - (FPU_st0_ptr->sigl & 0x000007ff)) ) + (st0_ptr->sigl & 0x000007ff)) ) #endif PECULIAR_486 { EXCEPTION(EX_Underflow); @@ -612,23 +595,23 @@ int reg_store_double(void) } } } - else if (FPU_st0_tag == TW_Zero) + else if (st0_tag == TW_Zero) { /* Number is zero */ l[0] = 0; l[1] = 0; } - else if (FPU_st0_tag == TW_Infinity) + else if (st0_tag == TW_Infinity) { l[0] = 0; l[1] = 0x7ff00000; } - else if (FPU_st0_tag == TW_NaN) + else if (st0_tag == TW_NaN) { /* See if we can get a valid NaN from the FPU_REG */ - l[0] = (FPU_st0_ptr->sigl >> 11) | (FPU_st0_ptr->sigh << 21); - l[1] = ((FPU_st0_ptr->sigh >> 11) & 0xfffff); - if ( !(FPU_st0_ptr->sigh & 0x40000000) ) + l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21); + l[1] = ((st0_ptr->sigh >> 11) & 0xfffff); + if ( !(st0_ptr->sigh & 0x40000000) ) { /* It is a signalling NaN */ EXCEPTION(EX_Invalid); @@ -638,7 +621,7 @@ int reg_store_double(void) } l[1] |= 0x7ff00000; } - else if ( FPU_st0_tag == TW_Empty ) + else if ( st0_tag == TW_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); @@ -656,7 +639,7 @@ int reg_store_double(void) else return 0; } - if (FPU_st0_ptr->sign) + if ( st0_ptr->sign ) l[1] |= 0x80000000; RE_ENTRANT_CHECK_OFF; @@ -670,18 +653,18 @@ int reg_store_double(void) /* Put a float into user memory */ -int reg_store_single(void) +int reg_store_single(float *single, FPU_REG *st0_ptr) { - float *single = (float *)FPU_data_address; long templ; unsigned long increment = 0; /* avoid gcc warnings */ + char st0_tag = st0_ptr->tag; - if (FPU_st0_tag == TW_Valid) + if (st0_tag == TW_Valid) { int exp; FPU_REG tmp; - reg_move(FPU_st0_ptr, &tmp); + reg_move(st0_ptr, &tmp); exp = tmp.exp - EXP_BIAS; if ( exp < SINGLE_Emin ) @@ -692,7 +675,7 @@ int reg_store_single(void) #ifndef PECULIAR_486 /* An 80486 is supposed to be able to generate a denormal exception here, but... */ - if ( FPU_st0_ptr->exp <= EXP_UNDER ) + if ( st0_ptr->exp <= EXP_UNDER ) { /* Underflow has priority. */ if ( control_word & CW_Underflow ) @@ -710,7 +693,7 @@ int reg_store_single(void) that the 80486 rounds to the dest precision, then converts to decide underflow. */ if ( !((tmp.sigl == 0x00800000) && - ((FPU_st0_ptr->sigh & 0x000000ff) || FPU_st0_ptr->sigl)) ) + ((st0_ptr->sigh & 0x000000ff) || st0_ptr->sigl)) ) #endif PECULIAR_486 { EXCEPTION(EX_Underflow); @@ -800,19 +783,19 @@ int reg_store_single(void) templ |= ((exp+SINGLE_Ebias) & 0xff) << 23; } } - else if (FPU_st0_tag == TW_Zero) + else if (st0_tag == TW_Zero) { templ = 0; } - else if (FPU_st0_tag == TW_Infinity) + else if (st0_tag == TW_Infinity) { templ = 0x7f800000; } - else if (FPU_st0_tag == TW_NaN) + else if (st0_tag == TW_NaN) { /* See if we can get a valid NaN from the FPU_REG */ - templ = FPU_st0_ptr->sigh >> 8; - if ( !(FPU_st0_ptr->sigh & 0x40000000) ) + templ = st0_ptr->sigh >> 8; + if ( !(st0_ptr->sigh & 0x40000000) ) { /* It is a signalling NaN */ EXCEPTION(EX_Invalid); @@ -822,7 +805,7 @@ int reg_store_single(void) } templ |= 0x7f800000; } - else if ( FPU_st0_tag == TW_Empty ) + else if ( st0_tag == TW_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); @@ -846,7 +829,7 @@ int reg_store_single(void) return 0; } #endif - if (FPU_st0_ptr->sign) + if (st0_ptr->sign) templ |= 0x80000000; RE_ENTRANT_CHECK_OFF; @@ -859,27 +842,27 @@ int reg_store_single(void) /* Put a long long into user memory */ -int reg_store_int64(void) +int reg_store_int64(long long *d, FPU_REG *st0_ptr) { - long long *d = (long long *)FPU_data_address; FPU_REG t; long long tll; int precision_loss; + char st0_tag = st0_ptr->tag; - if ( FPU_st0_tag == TW_Empty ) + if ( st0_tag == TW_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); goto invalid_operand; } - else if ( (FPU_st0_tag == TW_Infinity) || - (FPU_st0_tag == TW_NaN) ) + else if ( (st0_tag == TW_Infinity) || + (st0_tag == TW_NaN) ) { EXCEPTION(EX_Invalid); goto invalid_operand; } - reg_move(FPU_st0_ptr, &t); + reg_move(st0_ptr, &t); precision_loss = round_to_int(&t); ((long *)&tll)[0] = t.sigl; ((long *)&tll)[1] = t.sigh; @@ -918,26 +901,26 @@ int reg_store_int64(void) /* Put a long into user memory */ -int reg_store_int32(void) +int reg_store_int32(long *d, FPU_REG *st0_ptr) { - long *d = (long *)FPU_data_address; FPU_REG t; int precision_loss; + char st0_tag = st0_ptr->tag; - if ( FPU_st0_tag == TW_Empty ) + if ( st0_tag == TW_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); goto invalid_operand; } - else if ( (FPU_st0_tag == TW_Infinity) || - (FPU_st0_tag == TW_NaN) ) + else if ( (st0_tag == TW_Infinity) || + (st0_tag == TW_NaN) ) { EXCEPTION(EX_Invalid); goto invalid_operand; } - reg_move(FPU_st0_ptr, &t); + reg_move(st0_ptr, &t); precision_loss = round_to_int(&t); if (t.sigh || ((t.sigl & 0x80000000) && @@ -972,26 +955,26 @@ int reg_store_int32(void) /* Put a short into user memory */ -int reg_store_int16(void) +int reg_store_int16(short *d, FPU_REG *st0_ptr) { - short *d = (short *)FPU_data_address; FPU_REG t; int precision_loss; + char st0_tag = st0_ptr->tag; - if ( FPU_st0_tag == TW_Empty ) + if ( st0_tag == TW_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); goto invalid_operand; } - else if ( (FPU_st0_tag == TW_Infinity) || - (FPU_st0_tag == TW_NaN) ) + else if ( (st0_tag == TW_Infinity) || + (st0_tag == TW_NaN) ) { EXCEPTION(EX_Invalid); goto invalid_operand; } - reg_move(FPU_st0_ptr, &t); + reg_move(st0_ptr, &t); precision_loss = round_to_int(&t); if (t.sigh || ((t.sigl & 0xffff8000) && @@ -1026,23 +1009,23 @@ int reg_store_int16(void) /* Put a packed bcd array into user memory */ -int reg_store_bcd(void) +int reg_store_bcd(char *d, FPU_REG *st0_ptr) { - char *d = (char *)FPU_data_address; FPU_REG t; unsigned long long ll; unsigned char b; int i, precision_loss; - unsigned char sign = (FPU_st0_ptr->sign == SIGN_NEG) ? 0x80 : 0; + unsigned char sign = (st0_ptr->sign == SIGN_NEG) ? 0x80 : 0; + char st0_tag = st0_ptr->tag; - if ( FPU_st0_tag == TW_Empty ) + if ( st0_tag == TW_Empty ) { /* Empty register (stack underflow) */ EXCEPTION(EX_StackUnder); goto invalid_operand; } - reg_move(FPU_st0_ptr, &t); + reg_move(st0_ptr, &t); precision_loss = round_to_int(&t); ll = significand(&t); @@ -1163,36 +1146,32 @@ int round_to_int(FPU_REG *r) /*===========================================================================*/ -char *fldenv(fpu_addr_modes addr_modes) +char *fldenv(fpu_addr_modes addr_modes, char *s) { - char *s = (char *)FPU_data_address; unsigned short tag_word = 0; unsigned char tag; int i; - if ( addr_modes.mode16 - || (addr_modes.override.operand_size == OP_SIZE_PREFIX) ) + if ( (addr_modes.default_mode == VM86) || + ((addr_modes.default_mode == PM16) + ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) ) { RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, s, 0x0e); control_word = get_fs_word((unsigned short *) s); partial_status = get_fs_word((unsigned short *) (s+2)); tag_word = get_fs_word((unsigned short *) (s+4)); - ip_offset = get_fs_word((unsigned short *) (s+6)); - cs_selector = get_fs_word((unsigned short *) (s+8)); - data_operand_offset = get_fs_word((unsigned short *) (s+0x0a)); - operand_selector = get_fs_word((unsigned short *) (s+0x0c)); + instruction_address.offset = get_fs_word((unsigned short *) (s+6)); + instruction_address.selector = get_fs_word((unsigned short *) (s+8)); + operand_address.offset = get_fs_word((unsigned short *) (s+0x0a)); + operand_address.selector = get_fs_word((unsigned short *) (s+0x0c)); RE_ENTRANT_CHECK_ON; s += 0x0e; - if ( addr_modes.vm86 ) - { - ip_offset += (cs_selector & 0xf000) << 4; - data_operand_offset += (operand_selector & 0xf000) << 4; - } - else if ( addr_modes.p286 ) + if ( addr_modes.default_mode == VM86 ) { - ip_offset += LDT_BASE_ADDR(cs_selector); - data_operand_offset += LDT_BASE_ADDR(operand_selector); + instruction_address.offset + += (instruction_address.selector & 0xf000) << 4; + operand_address.offset += (operand_address.selector & 0xf000) << 4; } } else @@ -1202,10 +1181,11 @@ char *fldenv(fpu_addr_modes addr_modes) control_word = get_fs_word((unsigned short *) s); partial_status = get_fs_word((unsigned short *) (s+4)); tag_word = get_fs_word((unsigned short *) (s+8)); - ip_offset = get_fs_long((unsigned long *) (s+0x0c)); - cs_selector = get_fs_long((unsigned long *) (s+0x10)); - data_operand_offset = get_fs_long((unsigned long *) (s+0x14)); - operand_selector = get_fs_long((unsigned long *) (s+0x18)); + instruction_address.offset = get_fs_long((unsigned long *) (s+0x0c)); + instruction_address.selector = get_fs_word((unsigned short *) (s+0x10)); + instruction_address.opcode = get_fs_word((unsigned short *) (s+0x12)); + operand_address.offset = get_fs_long((unsigned long *) (s+0x14)); + operand_address.selector = get_fs_long((unsigned long *) (s+0x18)); RE_ENTRANT_CHECK_ON; s += 0x1c; } @@ -1254,37 +1234,26 @@ char *fldenv(fpu_addr_modes addr_modes) remains correct */ } - /* Ensure that the values just loaded are not changed by - fix-up operations. */ - NO_NET_DATA_EFFECT; - NO_NET_INSTR_EFFECT; - return s; } -void frstor(fpu_addr_modes addr_modes) +void frstor(fpu_addr_modes addr_modes, char *data_address) { int i, stnr; unsigned char tag; - char *s = fldenv(addr_modes); + char *s = fldenv(addr_modes, data_address); for ( i = 0; i < 8; i++ ) { /* Load each register. */ - FPU_data_address = (void *)(s+i*10); - reg_load_extended(); stnr = (i+top) & 7; - tag = regs[stnr].tag; /* Derived from the loaded tag word. */ - reg_move(&FPU_loaded_data, ®s[stnr]); + tag = regs[stnr].tag; /* Derived from the fldenv() loaded tag word. */ + reg_load_extended((long double *)(s+i*10), ®s[stnr]); if ( tag == TW_Empty ) /* The loaded data over-rides all other cases. */ regs[stnr].tag = tag; } - /* Reverse the effect which loading the registers had on the - data pointer */ - NO_NET_DATA_EFFECT; - } @@ -1318,12 +1287,11 @@ unsigned short tag_word(void) } -char *fstenv(fpu_addr_modes addr_modes) +char *fstenv(fpu_addr_modes addr_modes, char *d) { - char *d = (char *)FPU_data_address; - - if ( addr_modes.mode16 - || (addr_modes.override.operand_size == OP_SIZE_PREFIX) ) + if ( (addr_modes.default_mode == VM86) || + ((addr_modes.default_mode == PM16) + ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) ) { RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,d,14); @@ -1334,19 +1302,19 @@ char *fstenv(fpu_addr_modes addr_modes) #endif PECULIAR_486 put_fs_word(status_word(), (unsigned short *) (d+2)); put_fs_word(tag_word(), (unsigned short *) (d+4)); - put_fs_word(ip_offset, (unsigned short *) (d+6)); - put_fs_word(data_operand_offset, (unsigned short *) (d+0x0a)); - if ( addr_modes.vm86 ) + put_fs_word(instruction_address.offset, (unsigned short *) (d+6)); + put_fs_word(operand_address.offset, (unsigned short *) (d+0x0a)); + if ( addr_modes.default_mode == VM86 ) { - put_fs_word((ip_offset & 0xf0000) >> 4, + put_fs_word((instruction_address.offset & 0xf0000) >> 4, (unsigned short *) (d+8)); - put_fs_word((data_operand_offset & 0xf0000) >> 4, + put_fs_word((operand_address.offset & 0xf0000) >> 4, (unsigned short *) (d+0x0c)); } else { - put_fs_word(cs_selector, (unsigned short *) (d+8)); - put_fs_word(operand_selector, (unsigned short *) (d+0x0c)); + put_fs_word(instruction_address.selector, (unsigned short *) (d+8)); + put_fs_word(operand_address.selector, (unsigned short *) (d+0x0c)); } RE_ENTRANT_CHECK_ON; d += 0x0e; @@ -1365,14 +1333,16 @@ char *fstenv(fpu_addr_modes addr_modes) put_fs_word(status_word(), (unsigned short *) (d+4)); put_fs_word(tag_word(), (unsigned short *) (d+8)); #endif PECULIAR_486 - put_fs_long(ip_offset, (unsigned long *) (d+0x0c)); - put_fs_long(cs_selector & ~0xf8000000, (unsigned long *) (d+0x10)); - put_fs_long(data_operand_offset, (unsigned long *) (d+0x14)); + put_fs_long(instruction_address.offset, (unsigned long *) (d+0x0c)); + put_fs_word(instruction_address.selector, (unsigned short *) (d+0x10)); + put_fs_word(instruction_address.opcode, (unsigned short *) (d+0x12)); + put_fs_long(operand_address.offset, (unsigned long *) (d+0x14)); #ifdef PECULIAR_486 /* An 80486 sets all the reserved bits to 1. */ - put_fs_long(0xffff0000 | operand_selector, (unsigned long *) (d+0x18)); + put_fs_word(operand_address.selector, (unsigned short *) (d+0x18)); + put_fs_word(0xffff, (unsigned short *) (d+0x1a)); #else - put_fs_long(operand_selector, (unsigned long *) (d+0x18)); + put_fs_long(operand_address.selector, (unsigned long *) (d+0x18)); #endif PECULIAR_486 RE_ENTRANT_CHECK_ON; d += 0x1c; @@ -1385,12 +1355,12 @@ char *fstenv(fpu_addr_modes addr_modes) } -void fsave(fpu_addr_modes addr_modes) +void fsave(fpu_addr_modes addr_modes, char *data_address) { char *d; int i; - d = fstenv(addr_modes); + d = fstenv(addr_modes, data_address); RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,d,80); RE_ENTRANT_CHECK_ON; diff --git a/drivers/FPU-emu/reg_round.S b/drivers/FPU-emu/reg_round.S index 88a6bbd9a1d2..33c4bd2bb496 100644 --- a/drivers/FPU-emu/reg_round.S +++ b/drivers/FPU-emu/reg_round.S @@ -82,7 +82,7 @@ #define UNMASKED_UNDERFLOW $2 -#ifdef REENTRANT_FPU +#ifndef NON_REENTRANT_FPU /* Make the code re-entrant by putting local storage on the stack: */ #define FPU_bits_lost (%esp) @@ -97,7 +97,7 @@ FPU_bits_lost: .byte 0 FPU_denormal: .byte 0 -#endif REENTRANT_FPU +#endif NON_REENTRANT_FPU .text @@ -127,9 +127,9 @@ fpu_reg_round: /* Normal entry point */ fpu_reg_round_sqrt: /* Entry point from wm_sqrt.S */ -#ifdef REENTRANT_FPU +#ifndef NON_REENTRANT_FPU pushl %ebx /* adjust the stack pointer */ -#endif REENTRANT_FPU +#endif NON_REENTRANT_FPU #ifdef PARANOID /* Cannot use this here yet */ @@ -417,9 +417,9 @@ xL_Store_significand: jge L_overflow fpu_reg_round_exit: -#ifdef REENTRANT_FPU +#ifndef NON_REENTRANT_FPU popl %ebx /* adjust the stack pointer */ -#endif REENTRANT_FPU +#endif NON_REENTRANT_FPU fpu_Arith_exit: popl %ebx diff --git a/drivers/FPU-emu/reg_u_div.S b/drivers/FPU-emu/reg_u_div.S index 0098324d62dd..328e9116ec94 100644 --- a/drivers/FPU-emu/reg_u_div.S +++ b/drivers/FPU-emu/reg_u_div.S @@ -29,7 +29,7 @@ /* #define dSIGH(x) 4(x) */ -#ifdef REENTRANT_FPU +#ifndef NON_REENTRANT_FPU /* Local storage on the stack: Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0 @@ -65,7 +65,7 @@ FPU_result_2: .long 0 FPU_ovfl_flag: .byte 0 -#endif REENTRANT_FPU +#endif NON_REENTRANT_FPU .text @@ -78,9 +78,9 @@ FPU_ovfl_flag: _reg_u_div: pushl %ebp movl %esp,%ebp -#ifdef REENTRANT_FPU +#ifndef NON_REENTRANT_FPU subl $28,%esp -#endif REENTRANT_FPU +#endif NON_REENTRANT_FPU pushl %esi pushl %edi diff --git a/drivers/FPU-emu/reg_u_mul.S b/drivers/FPU-emu/reg_u_mul.S index d041bf3e4480..8250666bd879 100644 --- a/drivers/FPU-emu/reg_u_mul.S +++ b/drivers/FPU-emu/reg_u_mul.S @@ -27,7 +27,7 @@ -#ifdef REENTRANT_FPU +#ifndef NON_REENTRANT_FPU /* Local storage on the stack: */ #define FPU_accum_0 -4(%ebp) /* ms word */ #define FPU_accum_1 -8(%ebp) @@ -40,7 +40,7 @@ FPU_accum_0: .long 0 FPU_accum_1: .long 0 -#endif REENTRANT_FPU +#endif NON_REENTRANT_FPU .text @@ -50,9 +50,9 @@ FPU_accum_1: _reg_u_mul: pushl %ebp movl %esp,%ebp -#ifdef REENTRANT_FPU +#ifndef NON_REENTRANT_FPU subl $8,%esp -#endif REENTRANT_FPU +#endif NON_REENTRANT_FPU pushl %esi pushl %edi diff --git a/drivers/FPU-emu/version.h b/drivers/FPU-emu/version.h index 83fcc74c2996..1dc3b1a00e94 100644 --- a/drivers/FPU-emu/version.h +++ b/drivers/FPU-emu/version.h @@ -9,5 +9,4 @@ | | +---------------------------------------------------------------------------*/ -#define FPU_VERSION "wm-FPU-emu version Beta 1.11" - +#define FPU_VERSION "wm-FPU-emu version 1.12" diff --git a/drivers/FPU-emu/wm_sqrt.S b/drivers/FPU-emu/wm_sqrt.S index 7f8b6c60aad5..4e028cb80753 100644 --- a/drivers/FPU-emu/wm_sqrt.S +++ b/drivers/FPU-emu/wm_sqrt.S @@ -29,7 +29,7 @@ #include "fpu_asm.h" -#ifdef REENTRANT_FPU +#ifndef NON_REENTRANT_FPU /* Local storage on the stack: */ #define FPU_accum_3 -4(%ebp) /* ms word */ #define FPU_accum_2 -8(%ebp) @@ -70,7 +70,7 @@ FPU_fsqrt_arg_1: .long 0 FPU_fsqrt_arg_0: .long 0 /* ls word, at most the ms bit is set */ -#endif REENTRANT_FPU +#endif NON_REENTRANT_FPU .text @@ -80,9 +80,9 @@ FPU_fsqrt_arg_0: _wm_sqrt: pushl %ebp movl %esp,%ebp -#ifdef REENTRANT_FPU +#ifndef NON_REENTRANT_FPU subl $28,%esp -#endif REENTRANT_FPU +#endif NON_REENTRANT_FPU pushl %esi pushl %edi pushl %ebx diff --git a/drivers/block/README.sbpcd b/drivers/block/README.sbpcd index 1f8d09a6a72e..88473f34bc20 100644 --- a/drivers/block/README.sbpcd +++ b/drivers/block/README.sbpcd @@ -1,41 +1,58 @@ -This is release 1.4 of the SoundBlaster Pro (Matsushita, Kotobuki, -Panasonic, CreativeLabs, Aztech) CD-ROM driver for Linux. +This README belongs to release 1.6 of the SoundBlaster Pro (Matsushita, +Kotobuki, Panasonic, CreativeLabs) CD-ROM driver for Linux. The driver is able to drive the whole family of IDE-style -Matsushita/Kotobuki/Panasonic drives (the "double speed" versions -like CR-562 and CR-563, too), and it will work with the soundcard -interfaces (SB Pro, SB 16, Galaxy, SoundFX, ...) and/or with -the "no-sound" cards (Panasonic CI-101P, LaserMate, Aztech, ...). +Matsushita/Kotobuki/Panasonic drives (the "double speed" versions like CR-562 +and CR-563, too), and it will work with the soundcard interfaces (SB Pro, +SB 16, Galaxy, SoundFX, ...) and/or with the "no-sound" cards (Panasonic +CI-101P, LaserMate, Aztech, ...). +It should work too now with the "configurable" interface "Sequoia S-1000", +which is found on the Spea Media FX sound card. The interface type has to get configured in /usr/include/linux/sbpcd.h, because the behavior is different. -The driver respects different drive firmware releases - my old drive -is a 2.11, but it should work with "old" drives <2.01 ... >3.00 -and with "new" drives (which count the releases around 0.75 or -1.00). - -Up to 4 drives are supported. CR-52x ("old") and CR-56x ("new") drives -can be mixed, but the CR-521 ones are hard-wired to drive ID 0. -The drives have to use different drive IDs, but the same controller -(it will be a little bit harder to support up to four interface cards - -but I plan to do it the day somebody wishes to connect a fifth drive). -Each drive has to get a unique minor number (0...3), corresponding -to it's drive ID. The drive IDs may be selected freely from 0 to 3 - -they must not be in consecutive order. - -The driver supports reading of data from the CD and playing of -audio tracks. The audio part should run with WorkMan, xcdplayer, -with the "non-X11" products CDplayer and WorkBone - tell me if -it is not compatible with other software. - -MultiSession is supported (but "old" drives lack this capability), -"ManySession" (see below) alternatively. -Photo CDs work, too. At ftp.gwdg.de:/pub/linux/hpcdtoppm/ is a package -to convert photo CD image files. - -The transfer rate will reach 150 kB/sec with "old" drives and -the full 300 kB/sec with double-speed drives. XA (PhotoCD) disks -with "old" drives are as slow as 50 kB/sec. +The driver respects different drive firmware releases - my old drive is a 2.11, +but it should work with "old" drives <2.01 ... >3.00 and with "new" drives +(which count the releases around 0.75 or 1.00). + +Up to 4 drives are supported. CR-52x ("old") and CR-56x ("new") drives can be +mixed, but the CR-521 ones are hard-wired to drive ID 0. + +As Don Carroll, don@ds9.us.dell.com or FIDO 1:382/14, told me, it is possible +to change old drives to any ID, too. He writes in this sense: + "In order to be able to use more than one single speed drive + (they do not have the ID jumpers) you must add a DIP switch + and two resistors. The pads are already on the board next to + the power connector. You will see the silkscreen for the + switch if you remove the top cover. + 1 2 3 4 + ID 0 = x F F x O = "on" + ID 1 = x O F x F = "off" + ID 2 = x F O x x = "don't care" + ID 3 = x O O x + Next to the switch are the positions for R76 (7k) and R78 + (12k). I had to play around with the resistor values - ID 3 + did not work with other values. If the values are not good, + ID 3 behaves like ID 0." + +The drives have to use different drive IDs, but the same controller (it will +be a little bit harder to support up to four interface cards - but I plan to +do it the day somebody wishes to connect a fifth drive). +Each drive has to get a unique minor number (0...3), corresponding to it's +drive ID. The drive IDs may be selected freely from 0 to 3 - they must not be +in consecutive order. + +The driver supports reading of data from the CD and playing of audio tracks. +The audio part should run with WorkMan, xcdplayer, with the "non-X11" products +CDplayer and WorkBone - tell me if it is not compatible with other software. + +MultiSession is supported (even my "old" CR-521 can handle it), "ManySession" +(not recommended, see below) alternatively. +Photo CDs work, too. At ftp.gwdg.de:/pub/linux/hpcdtoppm/ is Hadmut Danisch's +package to convert photo CD image files. + +The transfer rate will reach 150 kB/sec with "old" drives and 300 kB/sec with +double-speed drives. XA (PhotoCD) disks with "old" drives give only 50 kB/sec. This release is part of the standard kernel and consists of - this README file @@ -46,43 +63,47 @@ This release is part of the standard kernel and consists of To install: ----------- -1. Setup your hardware parameters. Though the driver does "auto-probing" - now, this step is recommended for every-day use. - a. Go into /usr/src/linux/include/linux/sbpcd.h and configure - it for your hardware (near the beginning): +1. Setup your hardware parameters. Though the driver does "auto-probing" at a + lot of (not all possible!) addresses, this step is recommended for + every-day use. + a. Go into /usr/src/linux/include/linux/sbpcd.h and configure it for your + hardware (near the beginning): a1. Set it up for the appropriate type of interface board. - Most "compatible" sound boards (for example "Highscreen", - "SoundFX" and "Galaxy") need the "SBPRO 0" setup. The - "no-sound" board from OmniCd needs the "SBPRO 1" setup. + "Original" CreativeLabs sound cards need "SBPRO 1". + Most "compatible" sound cards (for example "Highscreen", "SoundFX" + and "Galaxy") need "SBPRO 0". + The "no-sound" board from OmniCd needs the "SBPRO 1" setup. + The Spea Media FX sound card needs "SBPRO 2". sbpcd.c holds some examples in it's auto-probe list. - a2. Tell the address of your CDROM_PORT. + If you configure "SBPRO" wrong, the playing of audio CDs will work, + but you will not be able to mount a data CD. + a2. Tell the address of your CDROM_PORT (not of the sound port). b. Additionally for 2.a1 and 2.a2, the setup may be done during boot time (via the "kernel command line" or "LILO option"): sbpcd=0x230,SoundBlaster or sbpcd=0x320,LaserMate + or + sbpcd=0x330,SPEA (these strings are case sensitive!). - -2. Do a "make config" and select "yes" for Matsushita CD-ROM - support and for ISO9660 FileSystem support. + This is especially useful if you install a fresh distribution. +2. Do a "make config" and select "yes" for Matsushita CD-ROM support and for + ISO9660 FileSystem support. SCSI and/or SCSI CD-ROM support is not needed. -3. Then do a "make dep", then make the kernel image ("make zlilo" - or else). - -4. Make the device file(s). The driver uses definitely and exclusive - the MAJOR 25, so do +3. Then do a "make dep", then make the kernel image ("make zlilo" or else). +4. Make the device file(s). The driver uses definitely and exclusive the + MAJOR 25, so do mknod /dev/sbpcd b 25 0 (if you have only drive #0) -and/or + and/or mknod /dev/sbpcd0 b 25 0 mknod /dev/sbpcd1 b 25 1 mknod /dev/sbpcd2 b 25 2 mknod /dev/sbpcd3 b 25 3 - to make the node(s). - Take care that you create a node with the same MINOR as your drive - id is. So, if the DOS driver tells you have drive id #3, you have to + Take care that you create a node with the same MINOR as your drive ID is. + So, if the DOS driver tells you have drive id #3, you have to mknod /dev/ b 25 3 If you further make a link like @@ -91,123 +112,120 @@ and/or 5. Reboot with the new kernel. -You should now be able to do "mount -t iso9660 /dev/sbpcd /mnt" -and see the contents of your CD in the /mnt directory, and/or -hear music with "workman -c /dev/sbpcd &". +You should now be able to do "mount -t iso9660 -o block=2048 /dev/sbpcd /mnt" +and see the contents of your CD in the /mnt directory, and/or hear music with +"workman -c /dev/sbpcd &". Things of interest: ------------------- -The driver is configured to try the SoundBlaster Pro type of -interface at I/O port 0x0230 first. If this is not appropriate, -sbpcd.h should get changed (you will find the right place - -just at the beginning). - -No DMA and no IRQ is used, so the IRQ adjusting is not necessary, -and the IRQ line stays free for the SB Pro sound drivers. - -To reduce or increase the amount of kernel messages, edit -sbpcd.c and change the initialization of the variable -"sbpcd_debug". This is the way to get rid of the initial -warning message block, too. - -With "#define MANY_SESSION 1" (sbpcd.c), the driver can use -"many-session" CDs. This will work only with "new" drives like -CR-562 or CR-563. That is NOT multisession - it is a CD -with multiple independent sessions, each containing block -addresses as if it were the only session. With this feature -enabled, the driver will read the LAST session. Without it, -the FIRST session gets read. -If you would like the support of reading "in-between" sessions, -drop me a mail and some food for the soul. :-) -Those "many-session" CDs can get made by CDROM writers like -Philips CDD 521. -If you enable this feature, it is impossible to read true -multisession CDs. +The driver is configured to try the SoundBlaster Pro type of interface at +I/O port 0x0230 first. If this is not appropriate, sbpcd.h should get changed +(you will find the right place - just at the beginning). + +No DMA and no IRQ is used, so the IRQ line stays free for the SB Pro sound +drivers. + +To reduce or increase the amount of kernel messages, edit sbpcd.c and change +the initialization of the variable "sbpcd_debug". This is the way to get rid +of the initial warning message block, too. + +With "#define MANY_SESSION 1" (sbpcd.c), the driver can use "many-session" CDs. +This will work only with "new" drives like CR-562 or CR-563. That is NOT +multisession - it is a CD with multiple independent sessions, each containing +block addresses as if it were the only session. With this feature enabled, the +driver will read the LAST session. Without it, the FIRST session gets read. +If you would like the support of reading "in-between" sessions, drop me a mail +and some food for the soul. :-) +Those "many-session" CDs can get made by CDROM writers like Philips CDD 521. +If you enable this feature, it is impossible to read true multisession CDs. + +The driver uses the "variable BLOCK_SIZE" feature. To use it, you have to +specify "block=2048" as a mount option. Auto-probing at boot time: -------------------------- -The driver does auto-probing at all well-known interface card -addresses now. The idea to do that came from Adam J. Richter -(YGGDRASIL). +The driver does auto-probing at many well-known interface card addresses. The +idea to do that came from Adam J. Richter (YGGDRASIL). Some well-known +addresses are excluded from auto-probing because they can cause a hang if an +ethernet card gets touched. -This auto-probing looks first at the configured address resp. -the address submitted by the kernel command line. With this, -it is possible to use this driver within installation boot -floppies, and for any non-standard address, too. +This auto-probing looks first at the configured address resp. the address +submitted by the kernel command line. With this, it is possible to use this +driver within installation boot floppies, and for any non-standard address, +too. -Auto-probing will make an assumption about the interface type -("SBPRO" or not), based upon the address. That assumption may -be wrong (initialization will be o.k., but you will get I/O -errors during mount). In that case, use the "kernel command -line" feature and specify address & type at boot time to find -out the right setup. +Auto-probing will make an assumption about the interface type ("SBPRO" or not), +based upon the address. That assumption may be wrong (initialization will be +o.k., but you will get I/O errors during mount). In that case, use the "kernel +command line" feature and specify address & type at boot time to find out the +right setup. -SBPCD's auto-probing happens before the initialization of the -net drivers. That makes a hang possible if an ethernet card -gets touched. +SBPCD's auto-probing happens before the initialization of the net drivers. That +makes a hang possible if an ethernet card gets touched. -For every-day use, address and type should get configured -within sbpcd.h. That will stop the auto-probing due to success -with the first try. +For every-day use, address and type should get configured within sbpcd.h. That +will stop the auto-probing due to success with the first try. Setting up address and interface type: -------------------------------------- -If your I/O port address is not 0x0230 or if you use a "no-sound" -interface other than OmniCD, you have to look for the #defines -near the beginning of sbpcd.h and configure them: set SBPRO to -0 or 1, and change CDROM_PORT to the address of your CDROM I/O port. +If your I/O port address is not 0x0230 or if you use a "no-sound" interface +other than OmniCD, you have to look for the #defines near the beginning of +sbpcd.h and configure them: set SBPRO to 0 or 1 or 2, and change CDROM_PORT to +the address of your CDROM I/O port. -Most of the "SoundBlaster compatible" cards behave like the -no-sound interfaces! +Most of the "SoundBlaster compatible" cards behave like the no-sound +interfaces! -With "original" SB Pro cards, an initial setting of CD_volume -through the sound cards MIXER register gets done. That happens -at the end of "sbpcd_init". If you are using a "compatible" -sound card of type "LaserMate", you can change that code to get -it done with your card, too... +With "original" SB Pro cards, an initial setting of CD_volume through the +sound cards MIXER register gets done. That happens at the end of "sbpcd_init". +If you are using a "compatible" sound card of type "LaserMate", you can change +that code to get it done with your card, too... Using audio CDs: ---------------- -Workman, WorkBone, xcdplayer and cdplayer should work good now, -even with the double-speed drives. +Workman, WorkBone, xcdplayer and cdplayer should work good now, even with the +double-speed drives. -The program CDplayer likes to talk to "/dev/mcd" only, xcdplayer -wants "/dev/rsr0", workman loves "/dev/sr0" - so, do the appropriate -links for using them without the need of supplying parameters. +The program CDplayer likes to talk to "/dev/mcd" only, xcdplayer wants +"/dev/rsr0", workman loves "/dev/sr0" - so, do the appropriate links for using +them without the need of supplying parameters. Known problems: --------------- -Currently, the detection of disk change or removal does not -work as good as it should. +Currently, the detection of disk change or removal does not work as good as it +should. + +The "door (un)lock" commands get done at every "(u)mount" (only the "new" +drives support it), but after an unlock, locking again does not work properly. -The "door (un)lock" commands get done at every "(u)mount" (only the -"new" drives support it), but after an unlock, locking again does not -work. +All attempts to read the UPC/EAN code result in a stream of zeroes. All my +drives are telling there is no UPC/EAN code on disk or there is, but it is an +all-zero number. -All attempts to read the UPC/EAN code result in a stream of zeroes. -All my drives are telling there is no UPC/EAN code on disk or there -is, but it is an all-zero number. +My attempts to read audio tracks like data files are of no success. Contact me, +if you have an idea, please. -Bug reports, comments, wishes, donations (technical information -is a donation, too :-) etc. to +Bug reports, comments, wishes, donations (technical information is a donation, +too :-) etc. to emoenke@gwdg.de or to eberhard_moenkeberg@rollo.central.de or to my FIDO address: Eberhard Moenkeberg, 2:2437/210.27 -SnailMail address, preferable for CD editors if they want to submit -a free "cooperation" copy: +SnailMail address, preferable for CD editors if they want to submit a free +"cooperation" copy: Eberhard Moenkeberg Reinholdstr. 14 D-37083 Goettingen Germany +--- diff --git a/drivers/block/blk.h b/drivers/block/blk.h index faa5adfc527f..f21eb238eca0 100644 --- a/drivers/block/blk.h +++ b/drivers/block/blk.h @@ -82,6 +82,9 @@ extern int * blksize_size[MAX_BLKDEV]; extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end); extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end); extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end); +#ifdef CONFIG_SBPCD +extern unsigned long sbpcd_init(unsigned long, unsigned long); +#endif CONFIG_SBPCD extern int is_read_only(int dev); extern void set_device_ro(int dev,int flag); diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index a39d1b4fe65d..daae20f010ad 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -19,10 +19,6 @@ #include "blk.h" -#ifdef CONFIG_SBPCD -extern u_long sbpcd_init(u_long, u_long); -#endif CONFIG_SBPCD - /* * The request-struct contains all necessary data * to load a nr of sectors into memory diff --git a/drivers/block/sbpcd.c b/drivers/block/sbpcd.c index 9e2246a71c82..42f7e5d98d98 100644 --- a/drivers/block/sbpcd.c +++ b/drivers/block/sbpcd.c @@ -5,7 +5,7 @@ * and for "no-sound" interfaces like Lasermate and the * Panasonic CI-101P. * - * NOTE: This is release 1.5. + * NOTE: This is release 1.6. * It works with my SbPro & drive CR-521 V2.11 from 2/92 * and with the new CR-562-B V0.75 on a "naked" Panasonic * CI-101P interface. And vice versa. @@ -76,6 +76,22 @@ * Adapt to kernel 1.1.8 change (have to explicitely include * now). * + * 1.6 Trying to read audio frames as data. Impossible with the current + * drive firmware levels, as it seems. Awaiting any hint. ;-) + * Changed "door unlock": repeat it until success. + * Changed CDROMSTOP routine (stop somewhat "softer" so that Workman + * won't get confused). + * Added a third interface type: Sequoia S-1000, as used with the SPEA + * Media FX sound card. This interface (useable for Sony and Mitsumi + * drives, too) needs a special configuration setup and behaves like a + * LaserMate type after that. Still experimental - I do not have such + * an interface. + * Use the "variable BLOCK_SIZE" feature (2048). But it does only work + * if you give the mount option "block=2048". + * The media_check routine is currently disabled; now that it gets + * called as it should I fear it must get synchronized for not to + * disturb the normal driver's activity. + * * special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine * elaborated speed-up experiments (and the fabulous results!), for * the "push" towards load-free wait loops, and for the extensive mail @@ -111,6 +127,7 @@ #include #include +/* #undef DS */ #include #include #include @@ -118,13 +135,7 @@ #include #include #include - -#if SBPCD_USE_IRQ -#include -#endif SBPCD_USE_IRQ - #include - #include #include #include @@ -133,7 +144,7 @@ #define MAJOR_NR MATSUSHITA_CDROM_MAJOR #include "blk.h" -#define VERSION "1.5 Eberhard Moenkeberg " +#define VERSION "1.6 Eberhard Moenkeberg " #define SBPCD_DEBUG @@ -159,6 +170,7 @@ #define XA_TEST2 #define TEST_UPC 0 +#define READ_AUDIO 0 /* does not work today (the drives won't read audio) */ /*==========================================================================*/ /*==========================================================================*/ @@ -167,8 +179,14 @@ #undef LONG_TIMING #define LONG_TIMING 1 #endif - /*==========================================================================*/ +#if SBPCD_DIS_IRQ +#define SBPCD_CLI cli() +#define SBPCD_STI sti() +#else +#define SBPCD_CLI +#define SBPCD_STI +#endif SBPCD_DIS_IRQ /*==========================================================================*/ /* * auto-probing address list @@ -201,6 +219,10 @@ static int autoprobe[] = 0x650, 0, /* "sound card #9" */ 0x670, 0, /* "sound card #9" */ 0x690, 0, /* "sound card #9" */ + 0x330, 2, /* SPEA Media FX (default) */ + 0x320, 2, /* SPEA Media FX */ + 0x340, 2, /* SPEA Media FX */ + 0x350, 2, /* SPEA Media FX */ #if 0 /* some "hazardous" locations (ethernet cards) */ 0x330, 0, /* Lasermate, CI-101P */ @@ -227,7 +249,7 @@ static int sbp_data(void); * pattern for printk selection: * * (1<>1; + if (sbpro_type==1) i=(i&0x01)<<1|(i&0x02)>>1; OUT(CDo_enable,i); DPRINTF((DBG_DID,"SBPCD: switch_drive: drive %d activated.\n",DriveStruct[d].drv_minor)); return (0); @@ -1951,6 +1977,10 @@ static int DiskInfo(void) { int i; +#if READ_AUDIO + DriveStruct[d].mode=READ_M1; +#endif READ_AUDIO + i=SetSpeed(); if (i<0) { @@ -2139,6 +2169,51 @@ static int xx_PlayAudioMSF(int pos_audio_start,int pos_audio_end) } /*==========================================================================*/ /*==========================================================================*/ +/*==========================================================================*/ +/* + * Called from the timer to check the results of the get-status cmd. + */ +static int sbp_status(void) +{ + int st; + + st=ResponseStatus(); + if (st<0) + { + DPRINTF((DBG_INF,"SBPCD: sbp_status: timeout.\n")); + return (0); + } + + if (!st_spinning) DPRINTF((DBG_SPI,"SBPCD: motor got off - ignoring.\n")); + + if (st_check) + { + DPRINTF((DBG_INF,"SBPCD: st_check detected - retrying.\n")); + return (0); + } + if (!st_door_closed) + { + DPRINTF((DBG_INF,"SBPCD: door is open - retrying.\n")); + return (0); + } + if (!st_caddy_in) + { + DPRINTF((DBG_INF,"SBPCD: disk removed - retrying.\n")); + return (0); + } + if (!st_diskok) + { + DPRINTF((DBG_INF,"SBPCD: !st_diskok detected - retrying.\n")); + return (0); + } + if (st_busy) + { + DPRINTF((DBG_INF,"SBPCD: st_busy detected - retrying.\n")); + return (0); + } + return (1); +} +/*==========================================================================*/ /*==========================================================================*/ /*==========================================================================*/ @@ -2310,14 +2385,8 @@ static int sbpcd_ioctl(struct inode *inode,struct file *file, case CDROMSTOP: /* Spin down the drive */ DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMSTOP entered.\n")); - i=DriveReset(); -#if WORKMAN - DriveStruct[d].CD_changed=0xFF; - DriveStruct[d].diskstate_flags=0; -#endif WORKMAN - DPRINTF((DBG_IOC,"SBPCD: ioctl: DriveReset returns %d\n",i)); + i=xx_Pause_Resume(1); DriveStruct[d].audio_state=0; - i=DiskInfo(); return (0); case CDROMSTART: /* Spin up the drive */ @@ -2396,14 +2465,238 @@ static int sbpcd_ioctl(struct inode *inode,struct file *file, SC.cdsc_absaddr,SC.cdsc_reladdr)); return (0); + case CDROMREADMODE1: + DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADMODE1 requested.\n")); + xx_ModeSelect(CD_FRAMESIZE); + xx_ModeSense(); + DriveStruct[d].mode=READ_M1; + return (0); + case CDROMREADMODE2: DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADMODE2 requested.\n")); - return (-EINVAL); + xx_ModeSelect(CD_FRAMESIZE_XA); + xx_ModeSense(); + DriveStruct[d].mode=READ_M2; + return (0); + +#if READ_AUDIO + case CDROMREADAUDIO: + { /* start of CDROMREADAUDIO */ + + int i=0, j=0, frame, block; + u_int try=0; + u_long timeout; + u_char *p; + u_int data_tries = 0; + u_int data_waits = 0; + u_int data_retrying = 0; + int status_tries; + int error_flag; + struct cdrom_aud aud_arg; + + error_flag=0; + +#if 0 +#define AUD_FRM_SIZ CD_FRAMESIZE_RAW +#else +#define AUD_FRM_SIZ CD_FRAMESIZE_XA +#endif + + DPRINTF((DBG_IOC,"SBPCD: read_audio: ioctl: CDROMREADAUDIO requested.\n")); + + i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_aud)); + if (i) return (i); + memcpy_fromfs(&aud_arg, (void *) arg, sizeof(struct cdrom_aud)); + i=verify_area(VERIFY_WRITE, aud_arg.buf, AUD_FRM_SIZ); + if (i) return (i); + DPRINTF((DBG_AUD,"SBPCD: read_audio: lba: %d, buffer: %08X\n", aud_arg.lba, aud_arg.buf)); + + DPRINTF((DBG_AUD,"SBPCD: read_audio: before xx_ReadStatus.\n")); + for (data_tries=5; data_tries>0; data_tries--) + { + DPRINTF((DBG_AUD,"SBPCD: data_tries=%d ...\n", data_tries)); + DriveStruct[d].mode=READ_AU; + xx_ModeSelect(AUD_FRM_SIZ); + xx_ModeSense(); + + + for (status_tries=3; status_tries > 0; status_tries--) + { + flags_cmd_out |= f_respo3; + xx_ReadStatus(); + if (sbp_status() != 0) break; + sbp_sleep(1); /* wait a bit, try again */ + } + if (status_tries == 0) + { + DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_status: failed after 3 tries.\n")); + continue; + } + DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_status: ok.\n")); + + + block=aud_arg.lba; + flags_cmd_out = f_putcmd | + f_respo2 | + f_ResponseStatus | + f_obey_p_check; + + + + + if (!new_drive) + { + flags_cmd_out |= f_lopsta | f_getsta | f_bit1; + cmd_type=READ_M2; + drvcmd[0]=0x03; /* "read XA frames" command for old drives */ + drvcmd[1]=(block>>16)&0x000000ff; + drvcmd[2]=(block>>8)&0x000000ff; + drvcmd[3]=block&0x000000ff; + drvcmd[4]=0; + drvcmd[5]=1; /* # of frames */ + drvcmd[6]=0; + } + else /* if new_drive */ + { + drvcmd[0]=0x10; /* "read frames" command for new drives */ + lba2msf(block,&drvcmd[1]); /* msf-bin format required */ + drvcmd[4]=0; + drvcmd[5]=0; + drvcmd[6]=1; /* # of frames */ + } + + + + DPRINTF((DBG_AUD,"SBPCD: read_audio: before giving \"read\" command.\n")); + for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]); + + sbp_sleep(0); + + DPRINTF((DBG_AUD,"SBPCD: read_audio: after giving \"read\" command.\n")); + for (frame=1;frame<2 && !error_flag; frame++) + { + try=maxtim_data; + for (timeout=jiffies+900; ; ) + { + for ( ; try!=0;try--) + { + j=inb(CDi_status); + if (!(j&s_not_data_ready)) break; + if (!(j&s_not_result_ready)) break; + if (!new_drive) if (j&s_attention) break; + } + if (try != 0 || timeout <= jiffies) break; + if (data_retrying == 0) data_waits++; + data_retrying = 1; + sbp_sleep(1); + try = 1; + } + if (try==0) + { + DPRINTF((DBG_INF,"SBPCD: read_audio: sbp_data: CDi_status timeout.\n")); + error_flag++; + break; + } + DPRINTF((DBG_INF,"SBPCD: read_audio: sbp_data: CDi_status ok.\n")); + + if (j&s_not_data_ready) + { + printk("SBPCD: read_audio: sbp_data: DATA_READY timeout.\n"); + error_flag++; + break; + } + + DPRINTF((DBG_AUD,"SBPCD: read_audio: before reading data.\n")); + CLEAR_TIMER; + error_flag=0; + p = DriveStruct[d].aud_buf; + if (sbpro_type==1) OUT(CDo_sel_d_i,0x01); + READ_DATA(CDi_data, p, AUD_FRM_SIZ); + if (sbpro_type==1) OUT(CDo_sel_d_i,0x00); + data_retrying = 0; + } + DPRINTF((DBG_AUD,"SBPCD: read_audio: after reading data.\n")); + if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */ + { + DPRINTF((DBG_AUD,"SBPCD: read_audio: read aborted by drive\n")); +#if 0000 + i=DriveReset(); /* ugly fix to prevent a hang */ +#endif 0000 + continue; + } + + if (!new_drive) + { + i=maxtim_data; + for (timeout=jiffies+900; timeout > jiffies; timeout--) + { + for ( ;i!=0;i--) + { + j=inb(CDi_status); + if (!(j&s_not_data_ready)) break; + if (!(j&s_not_result_ready)) break; + if (j&s_attention) break; + } + if (i != 0 || timeout <= jiffies) break; + sbp_sleep(0); + i = 1; + } + if (i==0) { DPRINTF((DBG_AUD,"SBPCD: read_audio: STATUS TIMEOUT AFTER READ")); } + if (!(j&s_attention)) + { + DPRINTF((DBG_AUD,"SBPCD: read_audio: sbp_data: timeout waiting DRV_ATTN - retrying\n")); + i=DriveReset(); /* ugly fix to prevent a hang */ + continue; + } + } + + do + { + if (!new_drive) xx_ReadStatus(); + i=ResponseStatus(); /* builds status_byte, returns orig. status (old) or faked p_success_old (new) */ + if (i<0) { DPRINTF((DBG_AUD, + "SBPCD: read_audio: xx_ReadStatus error after read: %02X\n", + DriveStruct[d].status_byte)); + continue; /* FIXME */ + } + } + while ((!new_drive)&&(!st_check)&&(!(i&p_success_old))); + if (st_check) + { + i=xx_ReadError(); + DPRINTF((DBG_AUD,"SBPCD: read_audio: xx_ReadError was necessary after read: %02X\n",i)); + continue; + } + memcpy_tofs((u_char *) aud_arg.buf, + (u_char *) DriveStruct[d].aud_buf, AUD_FRM_SIZ); + DPRINTF((DBG_AUD,"SBPCD: read_audio: memcpy_tofs done.\n")); + break; + } + xx_ModeSelect(CD_FRAMESIZE); + xx_ModeSense(); + DriveStruct[d].mode=READ_M1; + + + if (data_tries == 0) + { + DPRINTF((DBG_AUD,"SBPCD: read_audio: failed after 5 tries.\n")); + return (-8); + } + DPRINTF((DBG_AUD,"SBPCD: read_audio: successful return.\n")); + return (0); + } /* end of CDROMREADAUDIO */ +#endif READ_AUDIO + + + + + case BLKRASET: + if(!suser()) return -EACCES; + if(!inode->i_rdev) return -EINVAL; + if(arg > 0xff) return -EINVAL; + read_ahead[MAJOR(inode->i_rdev)] = arg; + return (0); - case CDROMREADMODE1: - DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADMODE1 requested.\n")); - return (-EINVAL); - default: DPRINTF((DBG_IOC,"SBPCD: ioctl: unknown function request %04X\n", cmd)); return (-EINVAL); @@ -2430,63 +2723,6 @@ static void sbp_transfer(void) } } /*==========================================================================*/ -/* - * We seem to get never an interrupt. - */ -#if SBPCD_USE_IRQ -static void sbpcd_interrupt(int unused) -{ - int st; - - st = inb(CDi_status) & 0xFF; - DPRINTF((DBG_IRQ,"SBPCD: INTERRUPT received - CDi_status=%02X\n", st)); -} -#endif SBPCD_USE_IRQ -/*==========================================================================*/ -/* - * Called from the timer to check the results of the get-status cmd. - */ -static int sbp_status(void) -{ - int st; - - st=ResponseStatus(); - if (st<0) - { - DPRINTF((DBG_INF,"SBPCD: sbp_status: timeout.\n")); - return (0); - } - - if (!st_spinning) DPRINTF((DBG_SPI,"SBPCD: motor got off - ignoring.\n")); - - if (st_check) - { - DPRINTF((DBG_INF,"SBPCD: st_check detected - retrying.\n")); - return (0); - } - if (!st_door_closed) - { - DPRINTF((DBG_INF,"SBPCD: door is open - retrying.\n")); - return (0); - } - if (!st_caddy_in) - { - DPRINTF((DBG_INF,"SBPCD: disk removed - retrying.\n")); - return (0); - } - if (!st_diskok) - { - DPRINTF((DBG_INF,"SBPCD: !st_diskok detected - retrying.\n")); - return (0); - } - if (st_busy) - { - DPRINTF((DBG_INF,"SBPCD: st_busy detected - retrying.\n")); - return (0); - } - return (1); -} -/*==========================================================================*/ /* * I/O request routine, called from Linux kernel. */ @@ -2513,9 +2749,8 @@ request_loop: switch_drive(dev); INIT_REQUEST; - block = CURRENT->sector; - nsect = CURRENT->nr_sectors; - + block = CURRENT->sector; /* always numbered as 512-byte-pieces */ + nsect = CURRENT->nr_sectors; /* always counted as 512-byte-pieces */ if (CURRENT->cmd != READ) { printk("SBPCD: bad cmd %d\n", CURRENT->cmd); @@ -2523,11 +2758,11 @@ request_loop: goto request_loop; } + DPRINTF((DBG_BSZ,"SBPCD: read sector %d (%d sectors)\n", block, nsect)); DPRINTF((DBG_MUL,"SBPCD: read LBA %d\n", block/4)); - sbp_transfer(); + sbp_transfer(); /* if we satisfied the request from the buffer, we're done. */ - if (CURRENT->nr_sectors == 0) { end_request(1); @@ -2535,7 +2770,8 @@ request_loop: } i=prepare(0,0); /* at moment not really a hassle check, but ... */ - if (i!=0) DPRINTF((DBG_INF,"SBPCD: \"prepare\" tells error %d -- ignored\n", i)); + if (i!=0) + DPRINTF((DBG_INF,"SBPCD: \"prepare\" tells error %d -- ignored\n", i)); if (!st_spinning) xx_SpinUp(); @@ -2674,13 +2910,9 @@ static void sbp_read_cmd(void) drvcmd[5]=0; drvcmd[6]=DriveStruct[d].sbp_read_frames; } -#if SBPCD_DIS_IRQ - cli(); -#endif SBPCD_DIS_IRQ + SBPCD_CLI; for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]); -#if SBPCD_DIS_IRQ - sti(); -#endif SBPCD_DIS_IRQ + SBPCD_STI; return; } @@ -2704,9 +2936,7 @@ static int sbp_data(void) for (frame=DriveStruct[d].sbp_current;frame jiffies; timeout--) { @@ -2808,14 +3031,10 @@ static int sbp_data(void) { DPRINTF((DBG_INF,"SBPCD: sbp_data: timeout waiting DRV_ATTN - retrying\n")); i=DriveReset(); /* ugly fix to prevent a hang */ -#if SBPCD_DIS_IRQ - sti(); -#endif SBPCD_DIS_IRQ + SBPCD_STI; return (0); } -#if SBPCD_DIS_IRQ - sti(); -#endif SBPCD_DIS_IRQ + SBPCD_STI; } do @@ -2916,7 +3135,12 @@ static void sbpcd_release(struct inode * ip, struct file * file) */ DPRINTF((DBG_LCK,"SBPCD: open_count: %d -> %d\n", DriveStruct[d].open_count,DriveStruct[d].open_count-1)); - if (--DriveStruct[d].open_count==0) yy_LockDoor(0); + if (--DriveStruct[d].open_count==0) + { + do + i=yy_LockDoor(0); + while (i!=0); + } } /*==========================================================================*/ /* @@ -2932,22 +3156,11 @@ static struct file_operations sbpcd_fops = sbpcd_ioctl, /* ioctl */ NULL, /* mmap */ sbpcd_open, /* open */ - sbpcd_release /* release */ + sbpcd_release, /* release */ + NULL, /* fsync */ + NULL /* fasync */ }; /*==========================================================================*/ -/* - * SBP interrupt descriptor - */ -#if SBPCD_USE_IRQ -static struct sigaction sbpcd_sigaction = -{ - sbpcd_interrupt, - 0, - SA_INTERRUPT, - NULL -}; -#endif SBPCD_USE_IRQ -/*==========================================================================*/ /* * accept "kernel command line" parameters * (suggested by Peter MacDonald with SLS 1.03) @@ -2956,6 +3169,8 @@ static struct sigaction sbpcd_sigaction = * sbpcd=0x230,SoundBlaster * or * sbpcd=0x300,LaserMate + * or + * sbpcd=0x330,SPEA * * (upper/lower case sensitive here!!!). * @@ -2966,8 +3181,9 @@ static struct sigaction sbpcd_sigaction = void sbpcd_setup(char *s, int *p) { DPRINTF((DBG_INI,"SBPCD: sbpcd_setup called with %04X,%s\n",p[1], s)); + sbpro_type=0; if (!strcmp(s,str_sb)) sbpro_type=1; - else sbpro_type=0; + else if (!strcmp(s,str_sp)) sbpro_type=2; if (p[0]>0) sbpcd_ioaddr=p[1]; CDo_command=sbpcd_ioaddr; @@ -2985,6 +3201,57 @@ void sbpcd_setup(char *s, int *p) else CDi_data=sbpcd_ioaddr+2; } /*==========================================================================*/ +/* + * Sequoia S-1000 CD-ROM Interface Configuration + * as used within SPEA Media FX card + * The SPEA soundcard has to get jumpered for + * -> interface type "Matsushita/Panasonic" (not Sony or Mitsumi) + * -> I/O base address (0x320, 0x330, 0x340, 0x350) + */ +int config_spea(void) +{ + int n_ports=0x10; /* 2:0x00, 8:0x10, 16:0x20, 32:0x30 */ + int irq_number=0; /* 2:0x01, 7:0x03, 12:0x05, 15:0x07, OFF:0x00 */ + int dma_channel=0; /* 0:0x08, 1:0x18, 3:0x38, 5:0x58, 6:0x68, 7:0x78, OFF: 0x00 */ + int dack_polarity=0; /* L:0x00, H:0x80 */ + int drq_polarity=0x40; /* L:0x00, H:0x40 */ + + int i; + +#define SPEA_REG_1 sbpcd_ioaddr+4 +#define SPEA_REG_2 sbpcd_ioaddr+5 + + OUT(SPEA_REG_1,0xFF); + i=inb(SPEA_REG_1); + if (i!=0x0F) + { + DPRINTF((DBG_INF,"SBPCD: no SPEA interface at %04X present.\n", + sbpcd_ioaddr)); + return (-1); /* no interface found */ + } + OUT(SPEA_REG_1,0x04); + OUT(SPEA_REG_2,0xC0); + + OUT(SPEA_REG_1,0x05); + OUT(SPEA_REG_2,0x10|drq_polarity|dack_polarity); + +#if 1 +#define SPEA_PATTERN 0x80 +#else +#define SPEA_PATTERN 0x00 +#endif + OUT(SPEA_REG_1,0x06); + OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN); + OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN); + + OUT(SPEA_REG_1,0x09); + i=(inb(SPEA_REG_2)&0xCF)|n_ports; + OUT(SPEA_REG_2,i); + + sbpro_type = 0; /* acts like a LaserMate interface now */ + return (0); +} +/*==========================================================================*/ /* * Test for presence of drive and initialize it. Called at boot time. */ @@ -3006,6 +3273,8 @@ u_long sbpcd_init(u_long mem_start, u_long mem_end) DPRINTF((DBG_WRN,"SBPCD: LILO boot: linux sbpcd=0x230,SoundBlaster\n")); DPRINTF((DBG_WRN,"SBPCD: or like:\n")); DPRINTF((DBG_WRN,"SBPCD: LILO boot: linux sbpcd=0x300,LaserMate\n")); + DPRINTF((DBG_WRN,"SBPCD: or like:\n")); + DPRINTF((DBG_WRN,"SBPCD: LILO boot: linux sbpcd=0x330,SPEA\n")); DPRINTF((DBG_WRN,"SBPCD: \n")); DPRINTF((DBG_WRN,"SBPCD: with your REAL address.\n")); DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = END of WARNING = = = = = = = = = =\n")); @@ -3021,13 +3290,19 @@ u_long sbpcd_init(u_long mem_start, u_long mem_end) if (check_region(addr[1],4)) continue; DPRINTF((DBG_INI,"SBPCD: check_region done.\n")); if (autoprobe[port_index+1]==0) type=str_lm; - else type=str_sb; + else if (autoprobe[port_index+1]==1) type=str_sb; + else type=str_sp; sbpcd_setup(type, addr); DPRINTF((DBG_INF,"SBPCD: Trying to detect a %s CD-ROM drive at 0x%X.\n", type, CDo_command)); DPRINTF((DBG_INF,"SBPCD: - ")); sti(); /* to avoid possible "printk" bug */ + if (autoprobe[port_index+1]==2) + { + i=config_spea(); + if (i<0) continue; + } i=check_drives(); DPRINTF((DBG_INI,"SBPCD: check_drives done.\n")); sti(); /* to avoid possible "printk" bug */ @@ -3098,62 +3373,66 @@ u_long sbpcd_init(u_long mem_start, u_long mem_end) if (i>=0) DriveStruct[d].CD_changed=1; } - if (sbpro_type) + if (sbpro_type==1) { OUT(MIXER_addr,MIXER_CD_Volume); OUT(MIXER_data,0xCC); /* one nibble per channel */ } - if (register_blkdev(MATSUSHITA_CDROM_MAJOR, "sbpcd", &sbpcd_fops) != 0) + if (register_blkdev(MAJOR_NR, "sbpcd", &sbpcd_fops) != 0) { - printk("SBPCD: Can't get MAJOR %d for Matsushita CDROM\n", - MATSUSHITA_CDROM_MAJOR); + printk("SBPCD: Can't get MAJOR %d for Matsushita CDROM\n", MAJOR_NR); sti(); /* to avoid possible "printk" bug */ return (mem_start); } - blk_dev[MATSUSHITA_CDROM_MAJOR].request_fn = DEVICE_REQUEST; - read_ahead[MATSUSHITA_CDROM_MAJOR] = 4; /* just one frame */ + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + read_ahead[MAJOR_NR] = SBP_BUFFER_FRAMES * (CD_FRAMESIZE / 512); snarf_region(CDo_command,4); -#if SBPCD_USE_IRQ - if (irqaction(SBPCD_INTR_NR, &sbpcd_sigaction)) + for (j=0;j=NR_SBPCD)) { - printk("SBPCD: media_check: invalid device.\n"); + printk("SBPCD: media_check: invalid device %04X.\n", full_dev); return (-1); } + + switch_drive(MINOR(full_dev)); xx_ReadStatus(); /* command: give 1-byte status */ st=ResponseStatus(); diff --git a/drivers/net/Space.c b/drivers/net/Space.c index 290ac256b3c5..91cb8e2e09bd 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -281,6 +281,14 @@ static struct device ppp0_dev = { #define NEXT_DEV (&ppp0_dev) #endif /* PPP */ +#ifdef CONFIG_DUMMY + extern int dummy_init(struct device *dev); + static struct device dummy_dev = { + "dummy", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, dummy_init, }; +# undef NEXT_DEV +# define NEXT_DEV (&dummy_dev) +#endif + #ifdef LOOPBACK extern int loopback_init(struct device *dev); static struct device loopback_dev = { diff --git a/drivers/net/ppp.c b/drivers/net/ppp.c index 8f87fa08266d..83bae0407c42 100644 --- a/drivers/net/ppp.c +++ b/drivers/net/ppp.c @@ -94,6 +94,7 @@ static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n"; int ppp_init(struct device *); static void ppp_init_ctrl_blk(struct ppp *); static int ppp_dev_open(struct device *); +static int ppp_dev_ioctl(struct device *dev, struct ifreq *ifr); static int ppp_dev_close(struct device *); static void ppp_kick_tty(struct ppp *); @@ -264,6 +265,7 @@ ppp_init(struct device *dev) dev->mtu = PPP_MTU; dev->hard_start_xmit = ppp_xmit; dev->open = ppp_dev_open; + dev->do_ioctl = ppp_dev_ioctl; dev->stop = ppp_dev_close; dev->get_stats = ppp_get_stats; dev->hard_header = ppp_header; @@ -601,6 +603,32 @@ ppp_dev_close(struct device *dev) return 0; } +static int ppp_dev_ioctl(struct device *dev, struct ifreq *ifr) +{ + struct ppp *ppp = &ppp_ctrl[dev->base_addr]; + int error; + + struct stats + { + struct ppp_stats ppp_stats; + struct slcompress slhc; + } *result; + + error = verify_area (VERIFY_READ, + ifr->ifr_ifru.ifru_data, + sizeof (struct stats)); + + if (error == 0) { + result = (struct stats *) ifr->ifr_ifru.ifru_data; + + memcpy_tofs (&result->ppp_stats, &ppp->stats, sizeof (struct ppp_stats)); + if (ppp->slcomp) + memcpy_tofs (&result->slhc, ppp->slcomp, sizeof (struct slcompress)); + } + + return error; +} + /************************************************************* * TTY OUTPUT * The following function delivers a fully-formed PPP @@ -882,6 +910,8 @@ static void ppp_receive_buf(struct tty_struct *tty, unsigned char *cp, if (ppp_debug >= 5) { ppp_print_buffer ("receive buffer", cp, count, KERNEL_DS); } + + ppp->stats.rbytes += count; while (count-- > 0) { c = *cp++; @@ -1367,7 +1397,7 @@ ppp_ioctl(struct tty_struct *tty, struct file *file, unsigned int i, PRINTKN (3,(KERN_INFO "ppp_ioctl: set mru to %x\n", temp_i)); temp_i = (int) get_fs_long (l); if (ppp->mru != temp_i) - ppp_changedmtu (ppp, ppp->mtu, temp_i); + ppp_changedmtu (ppp, ppp->dev->mtu, temp_i); } break; diff --git a/drivers/scsi/buslogic.c b/drivers/scsi/buslogic.c index b8d92ba35d39..2f80a3a335e2 100644 --- a/drivers/scsi/buslogic.c +++ b/drivers/scsi/buslogic.c @@ -562,7 +562,6 @@ int buslogic_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) if (bufflen != sizeof SCpnt->sense_buffer) { buslogic_printk("Wrong buffer length supplied for request sense (%d)\n", bufflen); - panic("buslogic.c: wrong buffer length for request sense"); } #endif SCpnt->result = 0; @@ -781,6 +780,7 @@ static int setup_mailboxes(unsigned int base, struct Scsi_Host *SHpnt) buslogic_printk("buslogic_detect: failed setting up mailboxes\n"); } ok = TRUE; + return ok; must_be_adaptec: INTR_RESET(base); printk("- must be Adaptec\n"); /* So that the adaptec detect looks clean */ @@ -888,7 +888,6 @@ static int getconfig(unsigned int base, unsigned char *irq, /* Query the board to find out the model. */ static int buslogic_query(unsigned int base, int *trans) { -#if 0 unsigned const char inquiry_cmd[] = { CMD_INQUIRY }; unsigned char inquiry_result[4]; int i; @@ -899,14 +898,18 @@ static int buslogic_query(unsigned int base, int *trans) buslogic_out(base, inquiry_cmd, sizeof inquiry_cmd); buslogic_in(base, inquiry_result, 4); WAIT_UNTIL(INTERRUPT(base), CMDC); + INTR_RESET(base); + + buslogic_printk("Inquiry Bytes: %X %X %X %X\n", + inquiry_result[0],inquiry_result[1], + inquiry_result[2],inquiry_result[3]); while (0) { fail: - buslogic_printk("buslogic_detect: query card type\n"); + buslogic_printk("buslogic_query: query board settings\n"); + return TRUE; } - INTR_RESET(base); -#endif - *trans = BIOS_TRANSLATION_6432; /* Default case */ + *trans = BIOS_TRANSLATION_6432; /* Default case */ return FALSE; } @@ -1018,8 +1021,13 @@ int buslogic_detect(int hostnum) host[irq - 9] = SHpnt; SHpnt->this_id = id; +#ifdef CONFIG_NO_BUGGY_BUSLOGIC /* Only type 'A' (AT/ISA) bus adapters use unchecked DMA. */ SHpnt->unchecked_isa_dma = (bus_type == 'A'); +#else + /* bugs in the firmware with 16M+. Gaah */ + SHpnt->unchecked_isa_dma = 1; +#endif SHpnt->sg_tablesize = max_sg; if (SHpnt->sg_tablesize > BUSLOGIC_MAX_SG) SHpnt->sg_tablesize = BUSLOGIC_MAX_SG; diff --git a/fs/buffer.c b/fs/buffer.c index 6cbf5ce0a97e..c6d80b6d8380 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -44,6 +44,9 @@ extern int check_cdu31a_media_change(int, int); #ifdef CONFIG_MCD extern int check_mcd_media_change(int, int); #endif +#ifdef CONFIG_SBPCD +extern int check_sbpcd_media_change(int, int); +#endif #define NR_SIZES 4 static char buffersize_index[9] = {-1, 0, 1, -1, 2, -1, -1, -1, 3}; @@ -334,6 +337,12 @@ void check_disk_change(dev_t dev) break; #endif +#if defined(CONFIG_SBPCD) + case MATSUSHITA_CDROM_MAJOR: + i = check_sbpcd_media_change(dev, 0); + break; +#endif + default: return; }; diff --git a/include/linux/sbpcd.h b/include/linux/sbpcd.h index 48efbed62068..4370dd099739 100644 --- a/include/linux/sbpcd.h +++ b/include/linux/sbpcd.h @@ -8,6 +8,8 @@ * sbpcd=0x230,SoundBlaster * or * sbpcd=0x300,LaserMate + * or + * sbpcd=0x330,SPEA * these strings are case sensitive !!! */ @@ -17,6 +19,7 @@ * set SBPRO to 1 for "true" SoundBlaster card * set SBPRO to 0 for "poor" (no sound) interface cards * and for "compatible" soundcards. + * set SBPRO to 2 for the SPEA Media FX card * * most "compatible" sound boards like Galaxy need to set SBPRO to 0 !!! * if SBPRO gets set wrong, the drive will get found - but any @@ -33,6 +36,7 @@ * put your CDROM port base address here: * SBPRO addresses typically are 0x0230 (=0x220+0x10), 0x0250, ... * LASERMATE (CI-101P) adresses typically are 0x0300, 0x0310, ... + * SPEA addresses are 0x320, 0x330, 0x340, 0x350 * there are some soundcards on the market with 0x0630, 0x0650, ... * * example: if your SBPRO audio address is 0x220, specify 0x230. @@ -52,7 +56,7 @@ * Debug output levels */ #define DBG_INF 1 /* necessary information */ -#define DBG_IRQ 2 /* interrupt trace */ +#define DBG_BSZ 2 /* BLOCK_SIZE trace */ #define DBG_REA 3 /* "read" status trace */ #define DBG_CHK 4 /* "media check" trace */ #define DBG_TIM 5 /* datarate timer test */ @@ -75,7 +79,8 @@ #define DBG_XA 22 /* XA mode debugging */ #define DBG_LCK 23 /* door (un)lock info */ #define DBG_SQ 24 /* dump SubQ frame */ -#define DBG_000 25 /* unnecessary information */ +#define DBG_AUD 25 /* "read audio" debugging */ +#define DBG_000 26 /* unnecessary information */ /*==========================================================================*/ /*==========================================================================*/ @@ -199,6 +204,16 @@ #define READ_M1 0x01 /* "data mode 1": 2048 bytes per frame */ #define READ_M2 0x02 /* "data mode 2": 12+2048+280 bytes per frame */ #define READ_SC 0x04 /* "subchannel info": 96 bytes per frame */ +#define READ_AU 0x08 /* "audio frame": 2352 bytes per frame */ + +/* + * preliminary extensions to cdrom.h for transfering audio frames: + */ +#define CDROMREADAUDIO 0xE0 /* IOCTL function (arg = &cdrom_aud) */ + +struct cdrom_aud { u_int lba; /* frame address */ + u_char *buf; /* frame buffer (2352 bytes) */ + }; /* * sense byte: used only if new_drive @@ -400,17 +415,6 @@ Read XA Parameter: */ #define SBPCD_DIS_IRQ 0 -/* - * we don't use the IRQ line - leave it free for the sound driver - */ -#define SBPCD_USE_IRQ 0 - -/* - * you can set the interrupt number of your interface board here: - * It is not used at this time. No need to set it correctly. - */ -#define SBPCD_INTR_NR 7 - /* * "write byte to port" */ @@ -463,9 +467,3 @@ typedef union _blk BLK; /*==========================================================================*/ - - - - - - diff --git a/kernel/Makefile b/kernel/Makefile index 20bfeae17dc7..fb27ea6ab951 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -18,7 +18,7 @@ OBJS = sched.o sys_call.o traps.o irq.o dma.o fork.o \ panic.o printk.o vsprintf.o sys.o module.o ksyms.o exit.o \ - signal.o mktime.o ptrace.o ioport.o itimer.o \ + signal.o ptrace.o ioport.o itimer.o \ info.o ldt.o time.o tqueue.o vm86.o all: kernel.o diff --git a/kernel/sched.c b/kernel/sched.c index 13fd24f793f8..45f489cb029e 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -517,6 +517,8 @@ static void second_overflow(void) if (xtime.tv_sec > last_rtc_update + 660) if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in one min */ } /* diff --git a/kernel/time.c b/kernel/time.c index 3161e8f90799..92ecbc66dac6 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -14,6 +14,8 @@ * Created file with time related functions from sched.c and adjtimex() * 08 Oct 93 Torsten Duwe * adjtime interface update and CMOS clock write code + * 02 Jul 94 Alan Modra + * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime */ #include @@ -32,12 +34,36 @@ #include extern struct timeval xtime; -#include -extern long kernel_mktime(struct mktime * time); +/* converts date to days since 1/1/1970 + * assumes year,mon,day in normal date format + * ie. 1/1/1970 => year=1970, mon=1, day=1 + * + * For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10. + * + * This algorithm was first published by Gauss (I think). + */ +static inline unsigned long mktime(unsigned int year, unsigned int mon, + unsigned int day, unsigned int hour, + unsigned int min, unsigned int sec) +{ + if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + return ((( + (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} void time_init(void) { - struct mktime time; + unsigned int year, mon, day, hour, min, sec; int i; /* checking for Update-In-Progress could be done more elegantly @@ -53,25 +79,26 @@ void time_init(void) if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) break; do { /* Isn't this overkill ? UIP above should guarantee consistency */ - time.sec = CMOS_READ(RTC_SECONDS); - time.min = CMOS_READ(RTC_MINUTES); - time.hour = CMOS_READ(RTC_HOURS); - time.day = CMOS_READ(RTC_DAY_OF_MONTH); - time.mon = CMOS_READ(RTC_MONTH); - time.year = CMOS_READ(RTC_YEAR); - } while (time.sec != CMOS_READ(RTC_SECONDS)); + sec = CMOS_READ(RTC_SECONDS); + min = CMOS_READ(RTC_MINUTES); + hour = CMOS_READ(RTC_HOURS); + day = CMOS_READ(RTC_DAY_OF_MONTH); + mon = CMOS_READ(RTC_MONTH); + year = CMOS_READ(RTC_YEAR); + } while (sec != CMOS_READ(RTC_SECONDS)); if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - BCD_TO_BIN(time.sec); - BCD_TO_BIN(time.min); - BCD_TO_BIN(time.hour); - BCD_TO_BIN(time.day); - BCD_TO_BIN(time.mon); - BCD_TO_BIN(time.year); + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); } - time.mon--; - xtime.tv_sec = kernel_mktime(&time); - } + if ((year += 1900) < 1970) + year += 100; + xtime.tv_sec = mktime(year, mon, day, hour, min, sec); +} /* * The timezone where the local system is located. Used as a default by some * programs who obtain this value by using gettimeofday. @@ -403,8 +430,8 @@ asmlinkage int sys_adjtimex(struct timex *txc_p) int set_rtc_mmss(unsigned long nowtime) { int retval = 0; - short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60; - unsigned char save_control, save_freq_select, cmos_minutes; + int real_seconds, real_minutes, cmos_minutes; + unsigned char save_control, save_freq_select; save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */ CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); @@ -419,11 +446,15 @@ int set_rtc_mmss(unsigned long nowtime) /* since we're only adjusting minutes and seconds, * don't interfere with hour overflow. This avoids * messing with unknown time zones but requires your - * RTC not to be off by more than 30 minutes + * RTC not to be off by more than 15 minutes */ - if (((cmos_minutes < real_minutes) ? - (real_minutes - cmos_minutes) : - (cmos_minutes - real_minutes)) < 30) + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) + real_minutes += 30; /* correct for half hour time zone */ + real_minutes %= 60; + + if (abs(real_minutes - cmos_minutes) < 30) { if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { diff --git a/mm/memory.c b/mm/memory.c index e5119c9fdfd2..0748e937699a 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -46,6 +46,13 @@ #include #include +/* + * Define this if things work differently on a i386 and a i486: + * it will (on a i486) warn about kernel memory accesses that are + * done without a 'verify_area(VERIFY_WRITE,..)' + */ +#undef CONFIG_TEST_VERIFY_AREA + unsigned long high_memory = 0; extern unsigned long pg0[1024]; /* page table for 0-4MB for everybody */ @@ -902,10 +909,15 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) } else user_esp = regs->esp; } - if (error_code & PAGE_PRESENT) + if (error_code & PAGE_PRESENT) { +#ifdef CONFIG_TEST_VERIFY_AREA + if (regs->cs == KERNEL_CS) + printk("WP fault at %08x\n", regs->eip); +#endif do_wp_page(error_code, address, current, user_esp); - else + } else { do_no_page(error_code, address, current, user_esp); + } return; } address -= TASK_SIZE; @@ -1124,6 +1136,9 @@ void mem_init(unsigned long start_low_mem, invalidate(); if (wp_works_ok < 0) wp_works_ok = 0; +#ifdef CONFIG_TEST_VERIFY_AREA + wp_works_ok = 0; +#endif return; } diff --git a/net/inet/af_inet.c b/net/inet/af_inet.c index b25c5395c935..ee51c9726a46 100644 --- a/net/inet/af_inet.c +++ b/net/inet/af_inet.c @@ -1137,7 +1137,7 @@ static int inet_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, return -EINVAL; if(size==0) return 0; - err=verify_area(VERIFY_READ,ubuf,size); + err=verify_area(VERIFY_WRITE,ubuf,size); if(err) return err; @@ -1259,6 +1259,8 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCSIFMAP: case SIOCGIFMAP: case SIOCDEVPRIVATE: + case SIOCSIFSLAVE: + case SIOCGIFSLAVE: return(dev_ioctl(cmd,(void *) arg)); default: diff --git a/net/inet/arp.c b/net/inet/arp.c index d0b6fc638113..f5ceb5e4f293 100644 --- a/net/inet/arp.c +++ b/net/inet/arp.c @@ -26,6 +26,7 @@ * Alan Cox : Make ARP add its own protocol entry * * Ross Martin : Rewrote arp_rcv() and arp_get_info() + * Stephen Henson : Add AX25 support to arp_get_info() */ #include @@ -846,6 +847,13 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length) /* * Convert hardware address to XX:XX:XX:XX ... form. */ +#ifdef CONFIG_AX25 + + if(entry->htype==ARPHRD_AX25) + strcpy(hbuffer,ax2asc((ax25_address *)entry->ha)); + else { +#endif + for(k=0,j=0;khlen;j++) { hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ]; @@ -854,6 +862,9 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length) } hbuffer[--k]=0; +#ifdef CONFIG_AX25 + } +#endif size = sprintf(buffer+len, "%-17s0x%-10x0x%-10x%s\n", in_ntoa(entry->ip), @@ -926,9 +937,6 @@ static int arp_req_set(struct arpreq *req) */ switch (r.arp_ha.sa_family) { - case 0: - /* Moan about this. ARP family 0 is NetROM and _will_ be needed */ - printk("Application using old BSD convention for arp set. Please recompile it.\n"); case ARPHRD_ETHER: htype = ARPHRD_ETHER; hlen = ETH_ALEN; diff --git a/net/inet/dev.c b/net/inet/dev.c index c43986210807..ef8dbafde510 100644 --- a/net/inet/dev.c +++ b/net/inet/dev.c @@ -1267,7 +1267,7 @@ static int dev_ifsioc(void *arg, unsigned int getset) return -ENODEV; } cli(); - if(slave->flags&(IFF_UP|IFF_RUNNING)!=(IFF_UP|IFF_RUNNING)) + if((slave->flags&(IFF_UP|IFF_RUNNING))!=(IFF_UP|IFF_RUNNING)) { restore_flags(flags); return -EINVAL; @@ -1275,7 +1275,7 @@ static int dev_ifsioc(void *arg, unsigned int getset) if(dev->flags&IFF_SLAVE) { restore_flags(flags); - return -EINVAL; + return -EBUSY; } if(dev->slave!=NULL) { diff --git a/net/inet/ip.c b/net/inet/ip.c index 41bf38049bc6..87f11ce5f1f6 100644 --- a/net/inet/ip.c +++ b/net/inet/ip.c @@ -90,6 +90,7 @@ extern int last_retran; extern void sort_send(struct sock *sk); #define min(a,b) ((a)<(b)?(a):(b)) +#define LOOPBACK(x) (((x) & htonl(0xff000000)) == htonl(0x7f000000)) /* * SNMP management statistics @@ -226,7 +227,7 @@ int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long dadd * If the frame is from us and going off machine it MUST MUST MUST * have the output device ip address and never the loopback */ - if (saddr == htonl(0x7F000001L) && daddr != htonl(0x7F000001L)) + if (LOOPBACK(saddr) && !LOOPBACK(daddr)) saddr = src;/*rt->rt_dev->pa_addr;*/ raddr = rt->rt_gateway; @@ -245,7 +246,7 @@ int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long dadd * If the frame is from us and going off machine it MUST MUST MUST * have the output device ip address and never the loopback */ - if (saddr == 0x0100007FL && daddr != 0x0100007FL) + if (LOOPBACK(saddr) && !LOOPBACK(daddr)) saddr = src;/*rt->rt_dev->pa_addr;*/ raddr = (rt == NULL) ? 0 : rt->rt_gateway; -- 2.39.5