Commit 893c4d2f authored by Linus Torvalds's avatar Linus Torvalds

Import 1.1.18

parent 9ec7e58c
VERSION = 1
PATCHLEVEL = 1
SUBLEVEL = 17
SUBLEVEL = 18
all: Version zImage
......
......@@ -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 $<
......
......@@ -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.
......
......@@ -42,6 +42,8 @@ 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);
if ( FPU_CS == USER_CS )
{
while ( 1 )
{
byte1 = get_fs_byte((unsigned char *) address);
......@@ -56,6 +58,12 @@ void Un_impl(void)
printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
else
printk("/%d\n", (FPU_modrm >> 3) & 7);
}
else
{
printk("cs selector = %04x\n", FPU_CS);
}
RE_ENTRANT_CHECK_ON;
EXCEPTION(EX_Invalid);
......@@ -85,6 +93,8 @@ void emu_printall()
RE_ENTRANT_CHECK_OFF;
/* No need to verify_area(), we have previously fetched these bytes. */
printk("At %p:", (void *) address);
if ( FPU_CS == USER_CS )
{
#define MAX_PRINTED_BYTES 20
for ( i = 0; i < MAX_PRINTED_BYTES; i++ )
{
......@@ -109,6 +119,11 @@ void emu_printall()
printk(" /%d, mod=%d rm=%d\n",
(FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7);
}
}
else
{
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);
......
......@@ -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();
}
......@@ -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();
}
......@@ -60,14 +60,18 @@
#include <linux/math_emu.h>
#include <linux/linkage.h>
#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) { \
......
......@@ -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 )
{
/* user code space? */
if (FPU_CS == KERNEL_CS)
/* 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
{
/* We cannot handle multiple segments yet */
if (FPU_CS != USER_CS || FPU_DS != USER_DS)
if ( (FPU_CS & 4) != 4 ) /* Must be in the LDT */
{
math_abort(FPU_info,SIGILL);
/* 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);
}
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
{
/* 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 @@ asmlinkage void math_emulate(long arg)
* 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 @@ asmlinkage void math_emulate(long arg)
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 @@ asmlinkage void math_emulate(long arg)
}
}
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 @@ asmlinkage void math_emulate(long arg)
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 @@ asmlinkage void math_emulate(long arg)
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 @@ asmlinkage void math_emulate(long arg)
{
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 @@ asmlinkage void math_emulate(long arg)
else
stack_underflow();
}
reg_mem_instr_done:
operand_address = data_sel_off;
}
else
{
if ( !(no_ip_update =
load_store_instr(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1,
addr_modes);
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 @@ asmlinkage void math_emulate(long arg)
#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,16 +561,13 @@ asmlinkage void math_emulate(long arg)
goto FPU_instruction_done;
}
(*st_instr_table[(int) instr_index])();
}
FPU_instruction_done:
;
}
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
if ( ! no_ip_update )
instruction_address = entry_sel_off;
FPU_fwait_done:
......@@ -553,16 +579,14 @@ asmlinkage void math_emulate(long arg)
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;
}
......
......@@ -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));
}
......@@ -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,
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,
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,
......
......@@ -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
......@@ -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));
}
......@@ -19,6 +19,7 @@
#include <linux/stddef.h>
#include <linux/head.h>
#include <asm/segment.h>
......@@ -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 )
*selector = VM86_REG_(segment);
return (unsigned long)VM86_REG_(segment) << 4;
else if ( addr_modes.p286 )
return (unsigned long)LDT_BASE_ADDR(P286_REG_(segment));
return 0;
}
/* 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,61 +211,61 @@ static unsigned long mode16_segment(fpu_addr_modes addr_modes)
*/
void get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
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);
else
{
cpu_reg_ptr = & REG_(rm);
switch (mod)
{
case 0:
if (FPU_rm == 5)
if (rm == 5)
{
/* Special case: disp32 */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(4);
offset = get_fs_long((unsigned long *) (*fpu_eip));
address = get_fs_long((unsigned long *) (*fpu_eip));
(*fpu_eip) += 4;
RE_ENTRANT_CHECK_ON;
FPU_data_address = (void *) offset;
return;
addr->offset = address;
return (void *) address;
}
else
{
FPU_data_address = (void *)*cpu_reg_ptr; /* Just return the contents
address = *cpu_reg_ptr; /* Just return the contents
of the cpu register */
return;
addr->offset = address;
return (void *) address;
}
case 1:
/* 8 bit signed displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
offset = (signed char) get_fs_byte((char *) (*fpu_eip));
address = (signed char) get_fs_byte((char *) (*fpu_eip));
RE_ENTRANT_CHECK_ON;
(*fpu_eip)++;
break;
......@@ -219,7 +273,7 @@ void get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
/* 32 bit displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(4);
offset = (signed) get_fs_long((unsigned long *) (*fpu_eip));
address = (signed) get_fs_long((unsigned long *) (*fpu_eip));
(*fpu_eip) += 4;
RE_ENTRANT_CHECK_ON;
break;
......@@ -227,47 +281,62 @@ void get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
/* Not legal for the FPU */
EXCEPTION(EX_Invalid);
}
address += *cpu_reg_ptr;
}
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|0x133);
}
FPU_data_address = offset + (char *)*cpu_reg_ptr;
return (void *)address;
}
void get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
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 ;
}
......@@ -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,130 +46,162 @@ 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 )
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) )
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(&FPU_loaded_data, pop_ptr);
reg_move(&loaded_data, st0_ptr);
break;
case 001: /* fild m32int */
clear_C1();
reg_load_int32();
reg_move(&FPU_loaded_data, pop_ptr);
reg_load_int32((long *)data_address, st0_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) )
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(&FPU_loaded_data, pop_ptr);
reg_move(&loaded_data, st0_ptr);
break;
case 003: /* fild m16int */
clear_C1();
reg_load_int16();
reg_move(&FPU_loaded_data, pop_ptr);
reg_load_int16((short *)data_address, st0_ptr);
break;
case 010: /* fst m32real */
clear_C1();
reg_store_single();
reg_store_single((float *)data_address, st0_ptr);
break;
case 011: /* fist m32int */
clear_C1();
reg_store_int32();
reg_store_int32((long *)data_address, st0_ptr);
break;
case 012: /* fst m64real */
clear_C1();
reg_store_double();
reg_store_double((double *)data_address, st0_ptr);
break;
case 013: /* fist m16int */
clear_C1();
reg_store_int16();
reg_store_int16((short *)data_address, st0_ptr);
break;
case 014: /* fstp m32real */
clear_C1();
if ( reg_store_single() )
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() )
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() )
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() )
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);
break;
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);
break;
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();
reg_move(&FPU_loaded_data, pop_ptr);
reg_load_bcd((char *)data_address, st0_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);
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);
......@@ -178,60 +210,51 @@ switch ( type )
#ifdef PECULIAR_486
control_word |= 0x40; /* An 80486 appears to always set this bit */
#endif PECULIAR_486
NO_NET_DATA_EFFECT;
NO_NET_INSTR_EFFECT;
break;
return 1;
case 025: /* fld m80real */
clear_C1();
reg_load_extended();
reg_move(&FPU_loaded_data, pop_ptr);
reg_load_extended((long double *)data_address, st0_ptr);
break;
case 027: /* fild m64int */
clear_C1();
reg_load_int64();
reg_move(&FPU_loaded_data, pop_ptr);
reg_load_int64((long long *)data_address, st0_ptr);
break;
case 030: /* fstenv m14/28byte */
fstenv(addr_modes);
NO_NET_DATA_EFFECT;
break;
fstenv(addr_modes, (char *)data_address);
return 1;
case 032: /* fsave */
fsave(addr_modes);
NO_NET_DATA_EFFECT;
break;
fsave(addr_modes, (char *)data_address);
return 1;
case 033: /* fbstp m80dec */
clear_C1();
if ( reg_store_bcd() )
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,FPU_data_address,2);
put_fs_word(control_word, (short *) FPU_data_address);
FPU_verify_area(VERIFY_WRITE,data_address,2);
put_fs_word(control_word, (short *) data_address);
RE_ENTRANT_CHECK_ON;
NO_NET_DATA_EFFECT;
NO_NET_INSTR_EFFECT;
break;
return 1;
case 035: /* fstp m80real */
clear_C1();
if ( reg_store_extended() )
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,FPU_data_address,2);
put_fs_word(status_word(),(short *) FPU_data_address);
FPU_verify_area(VERIFY_WRITE,data_address,2);
put_fs_word(status_word(),(short *) data_address);
RE_ENTRANT_CHECK_ON;
NO_NET_DATA_EFFECT;
NO_NET_INSTR_EFFECT;
break;
return 1;
case 037: /* fistp m64int */
clear_C1();
if ( reg_store_int64() )
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;
}
......@@ -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;
......
......@@ -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();
......
......@@ -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();
}
......
......@@ -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
......
......@@ -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;
......@@ -410,47 +396,44 @@ void reg_load_bcd(void)
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, &regs[stnr]);
tag = regs[stnr].tag; /* Derived from the fldenv() loaded tag word. */
reg_load_extended((long double *)(s+i*10), &regs[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;
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -9,5 +9,4 @@
| |
+---------------------------------------------------------------------------*/
#define FPU_VERSION "wm-FPU-emu version Beta 1.11"
#define FPU_VERSION "wm-FPU-emu version 1.12"
......@@ -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
......
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/<any_name> 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
---
......@@ -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);
......
......@@ -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
......
......@@ -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
* <linux/string.h> 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 <linux/errno.h>
#include <linux/sched.h>
/* #undef DS */
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/kernel.h>
......@@ -118,13 +135,7 @@
#include <linux/ioport.h>
#include <linux/sbpcd.h>
#include <linux/string.h>
#if SBPCD_USE_IRQ
#include <linux/signal.h>
#endif SBPCD_USE_IRQ
#include <linux/major.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
......@@ -133,7 +144,7 @@
#define MAJOR_NR MATSUSHITA_CDROM_MAJOR
#include "blk.h"
#define VERSION "1.5 Eberhard Moenkeberg <emoenke@gwdg.de>"
#define VERSION "1.6 Eberhard Moenkeberg <emoenke@gwdg.de>"
#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<<DBG_INF) necessary information
* (1<<DBG_IRQ) interrupt trace
* (1<<DBG_BSZ) BLOCK_SIZE trace
* (1<<DBG_REA) "read" status trace
* (1<<DBG_CHK) "media check" trace
* (1<<DBG_TIM) datarate timer test
......@@ -250,6 +272,7 @@ static int sbp_data(void);
* (1<<DBG_XA) XA mode debugging
* (1<<DBG_LCK) door (un)lock info
* (1<<DBG_SQ) dump SubQ frame
* (1<<DBG_AUD) "read audio" debugging
* (1<<DBG_000) unnecessary information
*/
#if 1
......@@ -261,7 +284,9 @@ static int sbpcd_debug = (1<<DBG_INF) |
(1<<DBG_IOC) |
(1<<DBG_XA) |
(1<<DBG_LCK) |
(1<<DBG_SQ) |
(1<<DBG_CHK) |
(1<<DBG_AUD) |
(1<<DBG_BSZ) |
(1<<DBG_IOX);
#endif
static int sbpcd_ioaddr = CDROM_PORT; /* default I/O base address */
......@@ -278,6 +303,7 @@ static struct cdrom_subchnl SC;
static struct cdrom_volctrl volctrl;
char *str_sb = "SoundBlaster";
char *str_lm = "LaserMate";
char *str_sp = "SPEA";
char *type;
/*==========================================================================*/
......@@ -323,6 +349,8 @@ static u_char drv_pattern[4]={ 0x80, 0x80, 0x80, 0x80 }; /* auto speed */
/* /A:... for (i=0;i<4;i++) drv_pattern[i] |= sax_a; */
/* /N:... ndrives=i-'0'; */
static int sbpcd_blocksizes[NR_SBPCD] = {0, };
/*==========================================================================*/
/*
* drive space begins here (needed separate for each unit)
......@@ -341,6 +369,12 @@ static struct {
int sbp_read_frames; /* Number of frames being read to buffer */
int sbp_current; /* Frame being currently read */
u_char mode; /* read_mode: READ_M1, READ_M2, READ_SC, READ_AU */
#if READ_AUDIO
u_char *aud_buf; /* Pointer to internal data buffer,
space allocated during sbpcd_init() */
#endif READ_AUDIO
u_char drv_type;
u_char drv_options;
u_char status_byte;
......@@ -405,7 +439,7 @@ static struct {
int in_SpinUp;
} DriveStruct[4];
} DriveStruct[NR_SBPCD];
/*
* drive space ends here (needed separate for each unit)
......@@ -739,14 +773,10 @@ static void xx_ReadStatus(void)
if (!new_drive) OUT(CDo_command,0x81);
else
{
#if SBPCD_DIS_IRQ
cli();
#endif SBPCD_DIS_IRQ
SBPCD_CLI;
OUT(CDo_command,0x05);
for (i=0;i<6;i++) OUT(CDo_command,0);
#if SBPCD_DIS_IRQ
sti();
#endif SBPCD_DIS_IRQ
SBPCD_STI;
}
}
/*==========================================================================*/
......@@ -791,21 +821,17 @@ int cmd_out(void)
for (i=0;i<7;i++) DPRINTF((DBG_CMD," %02X",drvcmd[i]));
DPRINTF((DBG_CMD,"\n"));
#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;
}
if (response_count!=0)
{
if (cmd_type!=0)
{
if (sbpro_type) OUT(CDo_sel_d_i,0x01);
if (sbpro_type==1) OUT(CDo_sel_d_i,0x01);
DPRINTF((DBG_INF,"SBPCD: misleaded to try ResponseData.\n"));
if (sbpro_type) OUT(CDo_sel_d_i,0x00);
if (sbpro_type==1) OUT(CDo_sel_d_i,0x00);
return (-22);
}
else i=ResponseInfo();
......@@ -1738,7 +1764,7 @@ static int switch_drive(int num)
d=num;
i=num;
if (sbpro_type) i=(i&0x01)<<1|(i&0x02)>>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,13 +2465,237 @@ 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);
case CDROMREADMODE1:
DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADMODE1 requested.\n"));
return (-EINVAL);
#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);
default:
DPRINTF((DBG_IOC,"SBPCD: ioctl: unknown function request %04X\n", cmd));
......@@ -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 @@ static void do_sbpcd_request(void)
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 @@ static void do_sbpcd_request(void)
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 @@ static void do_sbpcd_request(void)
}
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<DriveStruct[d].sbp_read_frames&&!error_flag; frame++)
{
#if SBPCD_DIS_IRQ
cli();
#endif SBPCD_DIS_IRQ
SBPCD_CLI;
try=maxtim_data;
#if LONG_TIMING
for (timeout=jiffies+900; ; )
......@@ -2743,19 +2973,16 @@ static int sbp_data(void)
break;
}
#if SBPCD_DIS_IRQ
sti();
#endif SBPCD_DIS_IRQ
SBPCD_STI;
CLEAR_TIMER;
error_flag=0;
p = DriveStruct[d].sbp_buf + frame * CD_FRAMESIZE;
if (sbpro_type) OUT(CDo_sel_d_i,0x01);
if (sbpro_type==1) OUT(CDo_sel_d_i,0x01);
if (cmd_type==READ_M2) READ_DATA(CDi_data, xa_head_buf, CD_XA_HEAD);
READ_DATA(CDi_data, p, CD_FRAMESIZE);
if (cmd_type==READ_M2) READ_DATA(CDi_data, xa_tail_buf, CD_XA_TAIL);
if (sbpro_type) OUT(CDo_sel_d_i,0x00);
if (sbpro_type==1) OUT(CDo_sel_d_i,0x00);
DriveStruct[d].sbp_current++;
if (cmd_type==READ_M2)
{
......@@ -2773,9 +3000,7 @@ static int sbp_data(void)
data_waits = data_tries = 0;
}
}
#if SBPCD_DIS_IRQ
sti();
#endif SBPCD_DIS_IRQ
SBPCD_STI;
if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
{
......@@ -2786,9 +3011,7 @@ static int sbp_data(void)
if (!new_drive)
{
#if SBPCD_DIS_IRQ
cli();
#endif SBPCD_DIS_IRQ
SBPCD_CLI;
i=maxtim_data;
for (timeout=jiffies+100; timeout > 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,21 +3156,10 @@ static struct file_operations sbpcd_fops =
sbpcd_ioctl, /* ioctl */
NULL, /* mmap */
sbpcd_open, /* open */
sbpcd_release /* release */
};
/*==========================================================================*/
/*
* SBP interrupt descriptor
*/
#if SBPCD_USE_IRQ
static struct sigaction sbpcd_sigaction =
{
sbpcd_interrupt,
0,
SA_INTERRUPT,
NULL
sbpcd_release, /* release */
NULL, /* fsync */
NULL /* fasync */
};
#endif SBPCD_USE_IRQ
/*==========================================================================*/
/*
* accept "kernel command line" parameters
......@@ -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,63 +3373,67 @@ 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;j++)
{
printk("SBPCD: Can't get IRQ%d for sbpcd driver\n", SBPCD_INTR_NR);
sti(); /* to avoid possible "printk" bug */
}
#endif SBPCD_USE_IRQ
if (DriveStruct[j].drv_minor==-1) continue;
/*
* allocate memory for the frame buffers
*/
for (j=0;j<NR_SBPCD;j++)
{
if (DriveStruct[j].drv_minor==-1) continue;
DriveStruct[j].sbp_buf=(u_char *)mem_start;
mem_start += SBP_BUFFER_FRAMES*CD_FRAMESIZE;
#if READ_AUDIO
DriveStruct[j].aud_buf=(u_char *)mem_start;
mem_start += CD_FRAMESIZE_RAW;
#endif READ_AUDIO
/*
* set the block size
*/
sbpcd_blocksizes[j]=CD_FRAMESIZE;
}
blksize_size[MAJOR_NR]=sbpcd_blocksizes;
DPRINTF((DBG_INF,"SBPCD: init done.\n"));
sti(); /* to avoid possible "printk" bug */
return (mem_start);
}
/*==========================================================================*/
/*
* adopted from sr.c
*
* Check if the media has changed in the CD-ROM drive.
* used externally (isofs/inode.c) - but still does not work.
*
* used externally (isofs/inode.c, fs/buffer.c)
* Currently disabled (has to get "synchronized").
*/
int check_sbpcd_media_change(int full_dev, int unused_minor)
{
int st;
if (MAJOR(full_dev) != MATSUSHITA_CDROM_MAJOR)
DPRINTF((DBG_CHK,"SBPCD: media_check (%d) called\n", MINOR(full_dev)));
return (0); /* "busy" test necessary before we really can check */
if ((MAJOR(full_dev)!=MAJOR_NR)||(MINOR(full_dev)>=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();
DPRINTF((DBG_CHK,"SBPCD: media_check: %02X\n",DriveStruct[d].status_byte));
......
......@@ -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 = {
......
......@@ -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
......@@ -883,6 +911,8 @@ static void ppp_receive_buf(struct tty_struct *tty, unsigned char *cp,
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;
......
......@@ -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,12 +898,16 @@ 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 */
......@@ -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;
......
......@@ -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;
};
......
......@@ -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;
/*==========================================================================*/
......@@ -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
......
......@@ -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 */
}
/*
......
......@@ -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 <linux/config.h>
......@@ -32,12 +34,36 @@
#include <linux/timex.h>
extern struct timeval xtime;
#include <linux/mktime.h>
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);
}
time.mon--;
xtime.tv_sec = kernel_mktime(&time);
BCD_TO_BIN(sec);
BCD_TO_BIN(min);
BCD_TO_BIN(hour);
BCD_TO_BIN(day);
BCD_TO_BIN(mon);
BCD_TO_BIN(year);
}
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)
{
......
......@@ -46,6 +46,13 @@
#include <linux/ptrace.h>
#include <linux/mman.h>
/*
* 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;
}
......
......@@ -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:
......
......@@ -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 <linux/types.h>
......@@ -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;k<HBUFFERLEN-3 && j<entry->hlen;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;
......
......@@ -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)
{
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment