Init
This commit is contained in:
20
sys/sparc/Makefile
Normal file
20
sys/sparc/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# @(#)Makefile 1.1 94/10/31 SMI
|
||||
#
|
||||
FILES= addupc.s copy.s crt.s float.s mcount.s ocsum.s overflow.s \
|
||||
sparc_subr.s swtch.s underflow.s zs_asm.s
|
||||
HFILES= a.out.h asm_linkage.h frame.h pcb.h psl.h reg.h setjmp.h trap.h
|
||||
HDIR=$(DESTDIR)/usr/include/sparc
|
||||
|
||||
all install: $(FILES)
|
||||
cd fpu && $(MAKE) $@
|
||||
|
||||
clean:
|
||||
|
||||
install_h: $(HFILES) FRC
|
||||
install -d -m 755 $(HDIR)
|
||||
install -m 444 $(HFILES) $(HDIR)
|
||||
cd fpu && $(MAKE) $@
|
||||
|
||||
FRC:
|
||||
|
||||
181
sys/sparc/a.out.h
Normal file
181
sys/sparc/a.out.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/* @(#)a.out.h 1.1 94/10/31 SMI; from UCB 4.1 83/05/03 */
|
||||
|
||||
#ifndef _sparc_a_out_h
|
||||
#define _sparc_a_out_h
|
||||
|
||||
#include <sys/exec.h>
|
||||
|
||||
/*
|
||||
* memory management parameters
|
||||
*/
|
||||
|
||||
#define PAGSIZ 0x02000
|
||||
#define SEGSIZ PAGSIZ
|
||||
#define OLD_PAGSIZ 0x00800 /* Page size under Release 2.0 */
|
||||
#define OLD_SEGSIZ 0x08000 /* Segment size under Release 2.0 */
|
||||
|
||||
/*
|
||||
* returns 1 if an object file type is invalid, i.e., if the other macros
|
||||
* defined below will not yield the correct offsets. Note that a file may
|
||||
* have N_BADMAG(x) = 0 and may be fully linked, but still may not be
|
||||
* executable.
|
||||
*/
|
||||
|
||||
#define N_BADMAG(x) \
|
||||
((x).a_magic!=OMAGIC && (x).a_magic!=NMAGIC && (x).a_magic!=ZMAGIC)
|
||||
|
||||
/*
|
||||
* relocation parameters. These are architecture-dependent
|
||||
* and can be deduced from the machine type. They are used
|
||||
* to calculate offsets of segments within the object file;
|
||||
* See N_TXTOFF(x), etc. below.
|
||||
*/
|
||||
|
||||
#define N_PAGSIZ(x) \
|
||||
((x).a_machtype == M_OLDSUN2? OLD_PAGSIZ : PAGSIZ)
|
||||
#define N_SEGSIZ(x) \
|
||||
((x).a_machtype == M_OLDSUN2? OLD_SEGSIZ : SEGSIZ)
|
||||
|
||||
/*
|
||||
* offsets of various sections of an object file.
|
||||
*/
|
||||
|
||||
#define N_TXTOFF(x) \
|
||||
/* text segment */ \
|
||||
( (x).a_machtype == M_OLDSUN2 \
|
||||
? ((x).a_magic==ZMAGIC ? N_PAGSIZ(x) : sizeof (struct exec)) \
|
||||
: ((x).a_magic==ZMAGIC ? 0 : sizeof (struct exec)) )
|
||||
|
||||
#define N_DATOFF(x) /* data segment */ \
|
||||
(N_TXTOFF(x) + (x).a_text)
|
||||
|
||||
#define N_TRELOFF(x) /* text reloc'n */ \
|
||||
(N_DATOFF(x) + (x).a_data)
|
||||
|
||||
#define N_DRELOFF(x) /* data relocation*/ \
|
||||
(N_TRELOFF(x) + (x).a_trsize)
|
||||
|
||||
#define N_SYMOFF(x) \
|
||||
/* symbol table */ \
|
||||
(N_TXTOFF(x)+(x).a_text+(x).a_data+(x).a_trsize+(x).a_drsize)
|
||||
|
||||
#define N_STROFF(x) \
|
||||
/* string table */ \
|
||||
(N_SYMOFF(x) + (x).a_syms)
|
||||
|
||||
/*
|
||||
* Macros which take exec structures as arguments and tell where the
|
||||
* various pieces will be loaded.
|
||||
*/
|
||||
|
||||
#define _N_BASEADDR(x) \
|
||||
(((x).a_magic == ZMAGIC) && ((x).a_entry < N_PAGSIZ(x)) ? \
|
||||
0 : N_PAGSIZ(x))
|
||||
|
||||
#define N_TXTADDR(x) \
|
||||
((x).a_machtype == M_OLDSUN2 ? N_SEGSIZ(x) : _N_BASEADDR(x))
|
||||
|
||||
#define N_DATADDR(x) \
|
||||
(((x).a_magic==OMAGIC)? (N_TXTADDR(x)+(x).a_text) \
|
||||
: (N_SEGSIZ(x)+((N_TXTADDR(x)+(x).a_text-1) & ~(N_SEGSIZ(x)-1))))
|
||||
|
||||
#define N_BSSADDR(x) (N_DATADDR(x)+(x).a_data)
|
||||
|
||||
/*
|
||||
* Format of a relocation datum.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Sparc relocation types
|
||||
*/
|
||||
|
||||
enum reloc_type
|
||||
{
|
||||
RELOC_8, RELOC_16, RELOC_32, /* simplest relocs */
|
||||
RELOC_DISP8, RELOC_DISP16, RELOC_DISP32, /* Disp's (pc-rel) */
|
||||
RELOC_WDISP30, RELOC_WDISP22, /* SR word disp's */
|
||||
RELOC_HI22, RELOC_22, /* SR 22-bit relocs */
|
||||
RELOC_13, RELOC_LO10, /* SR 13&10-bit relocs*/
|
||||
RELOC_SFA_BASE, RELOC_SFA_OFF13, /* SR S.F.A. relocs */
|
||||
RELOC_BASE10, RELOC_BASE13, RELOC_BASE22, /* base_relative pic */
|
||||
RELOC_PC10, RELOC_PC22, /* special pc-rel pic*/
|
||||
RELOC_JMP_TBL, /* jmp_tbl_rel in pic */
|
||||
RELOC_SEGOFF16, /* ShLib offset-in-seg*/
|
||||
RELOC_GLOB_DAT, RELOC_JMP_SLOT, RELOC_RELATIVE, /* rtld relocs */
|
||||
};
|
||||
|
||||
/*
|
||||
* Format of a relocation datum.
|
||||
*/
|
||||
|
||||
struct reloc_info_sparc /* used when header.a_machtype == M_SPARC */
|
||||
{
|
||||
unsigned long int r_address; /* relocation addr (offset in segment)*/
|
||||
unsigned int r_index :24; /* segment index or symbol index */
|
||||
unsigned int r_extern : 1; /* if F, r_index==SEG#; if T, SYM idx */
|
||||
int : 2; /* <unused> */
|
||||
enum reloc_type r_type : 5; /* type of relocation to perform */
|
||||
long int r_addend; /* addend for relocation value */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Format of a symbol table entry
|
||||
*/
|
||||
struct nlist {
|
||||
union {
|
||||
char *n_name; /* for use when in-core */
|
||||
long n_strx; /* index into file string table */
|
||||
} n_un;
|
||||
unsigned char n_type; /* type flag (N_TEXT,..) */
|
||||
char n_other; /* unused */
|
||||
short n_desc; /* see <stab.h> */
|
||||
unsigned long n_value; /* value of symbol (or sdb offset) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Simple values for n_type.
|
||||
*/
|
||||
#define N_UNDF 0x0 /* undefined */
|
||||
#define N_ABS 0x2 /* absolute */
|
||||
#define N_TEXT 0x4 /* text */
|
||||
#define N_DATA 0x6 /* data */
|
||||
#define N_BSS 0x8 /* bss */
|
||||
#define N_COMM 0x12 /* common (internal to ld) */
|
||||
#define N_FN 0x1e /* file name symbol */
|
||||
|
||||
#define N_EXT 01 /* external bit, or'ed in */
|
||||
#define N_TYPE 0x1e /* mask for all the type bits */
|
||||
|
||||
/*
|
||||
* Dbx entries have some of the N_STAB bits set.
|
||||
* These are given in <stab.h>
|
||||
*/
|
||||
#define N_STAB 0xe0 /* if any of these bits set, a dbx symbol */
|
||||
|
||||
/*
|
||||
* Format for namelist values.
|
||||
*/
|
||||
#define N_FORMAT "%08x"
|
||||
|
||||
/*
|
||||
* secondary sections.
|
||||
* this stuff follows the string table.
|
||||
* not even its presence or absence is noted in the
|
||||
* exec header (?). the secondary header gives
|
||||
* the number of sections. following it is an
|
||||
* array of "extra_nsects" int's which give the
|
||||
* sizeof of the individual sections. the presence of
|
||||
* even the header is optional.
|
||||
*/
|
||||
|
||||
#define EXTRA_MAGIC 1040 /* taxing concept */
|
||||
#define EXTRA_IDENT 0 /* ident's in 0th extra section */
|
||||
|
||||
struct extra_sections {
|
||||
int extra_magic; /* should be EXTRA_MAGIC */
|
||||
int extra_nsects; /* number of extra sections */
|
||||
};
|
||||
|
||||
#endif /*!_sparc_a_out_h*/
|
||||
95
sys/sparc/addupc.s
Normal file
95
sys/sparc/addupc.s
Normal file
@@ -0,0 +1,95 @@
|
||||
! @(#)addupc.s 1.1 94/10/31 SMI
|
||||
! Copyright (c) 1986 by Sun Microsystems, Inc.
|
||||
|
||||
.seg "text"
|
||||
.align 4
|
||||
|
||||
#include <machine/asm_linkage.h>
|
||||
|
||||
!
|
||||
! Add to user profiling counters.
|
||||
!
|
||||
! struct uprof { /* profile arguments */
|
||||
! short *pr_base; /* buffer base */
|
||||
! unsigned pr_size; /* buffer size */
|
||||
! unsigned pr_off; /* pc offset */
|
||||
! unsigned pr_scale; /* pc scaling */
|
||||
! } ;
|
||||
!
|
||||
! addupc( pc, pr, incr)
|
||||
! register int pc;
|
||||
! register struct uprof *pr;
|
||||
! int incr;
|
||||
! {
|
||||
! register short *slot;
|
||||
! short counter;
|
||||
!
|
||||
! slot = pr->pr_base
|
||||
! + (((pc - pr->pr_off) * pr->pr_scale) >> 16)/(sizeof *slot);
|
||||
! if (slot >= pr->pr_base &&
|
||||
! slot < (short *)(pr->pr_size + (int)pr->pr_base)) {
|
||||
! if ((counter=fusword(slot))<0) {
|
||||
! pr->pr_scale = 0; /* turn off profiling */
|
||||
! } else {
|
||||
! counter += incr;
|
||||
! susword(slot, counter);
|
||||
! }
|
||||
! }
|
||||
! }
|
||||
!
|
||||
|
||||
.global .mul
|
||||
|
||||
PR_BASE = 0
|
||||
PR_SIZE = 4
|
||||
PR_OFF = 8
|
||||
PR_SCALE= 12
|
||||
|
||||
/*
|
||||
* addupc(pc, pr, incr)
|
||||
*/
|
||||
ENTRY(addupc)
|
||||
save %sp, -SA(MINFRAME), %sp
|
||||
|
||||
ld [%i1 + PR_OFF], %l0 ! pr->pr_off
|
||||
subcc %i0, %l0, %o0 ! pc - pr->pr_off
|
||||
bl out
|
||||
srl %o0, 1, %o0 ! /2, for sign bit
|
||||
ld [%i1 + PR_SCALE], %o1
|
||||
call .mul ! ((pc - pr->pr_off) * pr->pr_scale)/4
|
||||
srl %o1, 1, %o1 ! /2
|
||||
|
||||
sra %o1, 14, %g1 ! overflow after >>14?
|
||||
tst %g1
|
||||
bnz out
|
||||
srl %o0, 14, %l0 ! >>14 double word
|
||||
sll %o1, 32-14, %g1
|
||||
or %l0, %g1, %l0
|
||||
bclr 1, %l0 ! make even
|
||||
ld [%i1 + PR_SIZE], %l1 ! check length
|
||||
cmp %l0, %l1
|
||||
bgeu out
|
||||
ld [%i1 + PR_BASE], %l1 ! add base
|
||||
add %l1, %l0, %l0
|
||||
call _fusword ! fetch counter from user
|
||||
mov %l0, %o0 ! delay slot
|
||||
tst %o0
|
||||
bge,a 1f ! fusword succeeded
|
||||
add %o0, %i2, %o1 ! counter += incr
|
||||
!
|
||||
! Fusword failed turn off profiling.
|
||||
!
|
||||
clr [%i1 + PR_SCALE]
|
||||
ret
|
||||
restore
|
||||
|
||||
!
|
||||
! Store new counter
|
||||
!
|
||||
1:
|
||||
call _susword ! store counter, checking permissions
|
||||
mov %l0, %o0 ! delay slot
|
||||
|
||||
out:
|
||||
ret
|
||||
restore
|
||||
219
sys/sparc/asm_linkage.h
Normal file
219
sys/sparc/asm_linkage.h
Normal file
@@ -0,0 +1,219 @@
|
||||
/* @(#)asm_linkage.h 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1987 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A stack frame looks like:
|
||||
*
|
||||
* %fp->| |
|
||||
* |-------------------------------|
|
||||
* | Locals, temps, saved floats |
|
||||
* |-------------------------------|
|
||||
* | outgoing parameters past 6 |
|
||||
* |-------------------------------|-\
|
||||
* | 6 words for callee to dump | |
|
||||
* | register arguments | |
|
||||
* |-------------------------------| > minimum stack frame
|
||||
* | One word struct-ret address | |
|
||||
* |-------------------------------| |
|
||||
* | 16 words to save IN and | |
|
||||
* %sp->| LOCAL register on overflow | |
|
||||
* |-------------------------------|-/
|
||||
*/
|
||||
|
||||
#ifndef _sparc_asm_linkage_h
|
||||
#define _sparc_asm_linkage_h
|
||||
|
||||
/*
|
||||
* Constants defining a stack frame.
|
||||
*/
|
||||
#define WINDOWSIZE (16*4) /* size of window save area */
|
||||
#define ARGPUSHSIZE (6*4) /* size of arg dump area */
|
||||
#define ARGPUSH (WINDOWSIZE+4) /* arg dump area offset */
|
||||
#define MINFRAME (WINDOWSIZE+ARGPUSHSIZE+4) /* min frame */
|
||||
|
||||
/*
|
||||
* Stack alignment macros.
|
||||
*/
|
||||
#define STACK_ALIGN 8
|
||||
#define SA(X) (((X)+(STACK_ALIGN-1)) & ~(STACK_ALIGN-1))
|
||||
|
||||
|
||||
/*
|
||||
* Profiling causes defintions of the MCOUNT and RTMCOUNT
|
||||
* particular to the type. Note: the nop in the following
|
||||
* macros allows them to be used in a delay slot.
|
||||
*/
|
||||
#ifdef GPROF
|
||||
|
||||
#define MCOUNT(x) \
|
||||
nop; \
|
||||
save %sp, -SA(MINFRAME), %sp; \
|
||||
call mcount; \
|
||||
nop ; \
|
||||
restore ;
|
||||
|
||||
#define RTMCOUNT(x) \
|
||||
nop; \
|
||||
save %sp, -SA(MINFRAME), %sp; \
|
||||
call mcount; \
|
||||
nop ; \
|
||||
restore ;
|
||||
|
||||
#endif GPROF
|
||||
|
||||
|
||||
#ifdef PROF
|
||||
|
||||
#define MCOUNT(x) \
|
||||
nop; \
|
||||
save %sp, -SA(MINFRAME), %sp; \
|
||||
sethi %hi(L_/**/x/**/1), %o0; \
|
||||
call mcount; \
|
||||
or %o0, %lo(L_/**/x/**/1), %o0; \
|
||||
restore; \
|
||||
.reserve L_/**/x/**/1, 4, "data", 4
|
||||
|
||||
#define RTMCOUNT(x) \
|
||||
nop; \
|
||||
save %sp, -SA(MINFRAME), %sp; \
|
||||
sethi %hi(L/**/x/**/1), %o0; \
|
||||
call mcount; \
|
||||
or %o0, %lo(L/**/x/**/1), %o0; \
|
||||
restore; \
|
||||
.reserve L/**/x/**/1, 4, "data", 4
|
||||
|
||||
#endif PROF
|
||||
|
||||
/*
|
||||
* if we are not profiling,
|
||||
* MCOUNT and RTMCOUNT should be defined to nothing
|
||||
*/
|
||||
#if !defined(PROF) && !defined(GPROF)
|
||||
#define MCOUNT(x)
|
||||
#define RTMCOUNT(x)
|
||||
#endif !PROF && !GPROF
|
||||
|
||||
/*
|
||||
* Entry macros for assembler subroutines.
|
||||
* NAME prefixes the underscore before a symbol.
|
||||
* ENTRY provides a way to insert the calls to mcount for profiling.
|
||||
* RTENTRY is similar to the above but provided for run-time routines
|
||||
* whose names should not be prefixed with an underscore.
|
||||
*/
|
||||
|
||||
#define NAME(x) _/**/x
|
||||
|
||||
#define ENTRY(x) \
|
||||
.global NAME(x); \
|
||||
NAME(x): MCOUNT(x)
|
||||
|
||||
#define ENTRY2(x,y) \
|
||||
.global NAME(x), NAME(y); \
|
||||
NAME(x): ; \
|
||||
NAME(y): MCOUNT(x)
|
||||
|
||||
#define RTENTRY(x) \
|
||||
.global x; x: RTMCOUNT(x)
|
||||
|
||||
/*
|
||||
* For additional entry points.
|
||||
*/
|
||||
#define ALTENTRY(x) \
|
||||
.global NAME(x); \
|
||||
NAME(x):
|
||||
|
||||
#ifdef KERNEL
|
||||
/*
|
||||
* Macros for saving/restoring registers.
|
||||
*/
|
||||
|
||||
#define SAVE_GLOBALS(RP) \
|
||||
st %g1, [RP + G1*4]; \
|
||||
std %g2, [RP + G2*4]; \
|
||||
std %g4, [RP + G4*4]; \
|
||||
std %g6, [RP + G6*4]; \
|
||||
mov %y, %g1; \
|
||||
st %g1, [RP + Y*4]
|
||||
|
||||
#define RESTORE_GLOBALS(RP) \
|
||||
ld [RP + Y*4], %g1; \
|
||||
mov %g1, %y; \
|
||||
ld [RP + G1*4], %g1; \
|
||||
ldd [RP + G2*4], %g2; \
|
||||
ldd [RP + G4*4], %g4; \
|
||||
ldd [RP + G6*4], %g6;
|
||||
|
||||
#define SAVE_OUTS(RP) \
|
||||
std %i0, [RP + O0*4]; \
|
||||
std %i2, [RP + O2*4]; \
|
||||
std %i4, [RP + O4*4]; \
|
||||
std %i6, [RP + O6*4]
|
||||
|
||||
#define RESTORE_OUTS(RP) \
|
||||
ldd [RP + O0*4], %i0; \
|
||||
ldd [RP + O2*4], %i2; \
|
||||
ldd [RP + O4*4], %i4; \
|
||||
ldd [RP + O6*4], %i6;
|
||||
|
||||
#define SAVE_WINDOW(SBP) \
|
||||
std %l0, [SBP + (0*4)]; \
|
||||
std %l2, [SBP + (2*4)]; \
|
||||
std %l4, [SBP + (4*4)]; \
|
||||
std %l6, [SBP + (6*4)]; \
|
||||
std %i0, [SBP + (8*4)]; \
|
||||
std %i2, [SBP + (10*4)]; \
|
||||
std %i4, [SBP + (12*4)]; \
|
||||
std %i6, [SBP + (14*4)]
|
||||
|
||||
#define RESTORE_WINDOW(SBP) \
|
||||
ldd [SBP + (0*4)], %l0; \
|
||||
ldd [SBP + (2*4)], %l2; \
|
||||
ldd [SBP + (4*4)], %l4; \
|
||||
ldd [SBP + (6*4)], %l6; \
|
||||
ldd [SBP + (8*4)], %i0; \
|
||||
ldd [SBP + (10*4)], %i2; \
|
||||
ldd [SBP + (12*4)], %i4; \
|
||||
ldd [SBP + (14*4)], %i6;
|
||||
|
||||
#define STORE_FPREGS(FCP) \
|
||||
std %f0, [FCP + FPCTX_REGS]; \
|
||||
std %f2, [FCP + FPCTX_REGS + 8]; \
|
||||
std %f4, [FCP + FPCTX_REGS + 16]; \
|
||||
std %f6, [FCP + FPCTX_REGS + 24]; \
|
||||
std %f8, [FCP + FPCTX_REGS + 32]; \
|
||||
std %f10, [FCP + FPCTX_REGS + 40]; \
|
||||
std %f12, [FCP + FPCTX_REGS + 48]; \
|
||||
std %f14, [FCP + FPCTX_REGS + 56]; \
|
||||
std %f16, [FCP + FPCTX_REGS + 64]; \
|
||||
std %f18, [FCP + FPCTX_REGS + 72]; \
|
||||
std %f20, [FCP + FPCTX_REGS + 80]; \
|
||||
std %f22, [FCP + FPCTX_REGS + 88]; \
|
||||
std %f24, [FCP + FPCTX_REGS + 96]; \
|
||||
std %f26, [FCP + FPCTX_REGS + 104]; \
|
||||
std %f28, [FCP + FPCTX_REGS + 112]; \
|
||||
std %f30, [FCP + FPCTX_REGS + 120]
|
||||
|
||||
#define LOAD_FPREGS(FCP) \
|
||||
ldd [FCP + FPCTX_REGS ], %f0; \
|
||||
ldd [FCP + FPCTX_REGS + 8], %f2; \
|
||||
ldd [FCP + FPCTX_REGS + 16], %f4; \
|
||||
ldd [FCP + FPCTX_REGS + 24], %f6; \
|
||||
ldd [FCP + FPCTX_REGS + 32], %f8; \
|
||||
ldd [FCP + FPCTX_REGS + 40], %f10; \
|
||||
ldd [FCP + FPCTX_REGS + 48], %f12; \
|
||||
ldd [FCP + FPCTX_REGS + 56], %f14; \
|
||||
ldd [FCP + FPCTX_REGS + 64], %f16; \
|
||||
ldd [FCP + FPCTX_REGS + 72], %f18; \
|
||||
ldd [FCP + FPCTX_REGS + 80], %f20; \
|
||||
ldd [FCP + FPCTX_REGS + 88], %f22; \
|
||||
ldd [FCP + FPCTX_REGS + 96], %f24; \
|
||||
ldd [FCP + FPCTX_REGS + 104], %f26; \
|
||||
ldd [FCP + FPCTX_REGS + 112], %f28; \
|
||||
ldd [FCP + FPCTX_REGS + 120], %f30; \
|
||||
|
||||
#endif KERNEL
|
||||
|
||||
#endif /*!_sparc_asm_linkage_h*/
|
||||
1955
sys/sparc/copy.s
Normal file
1955
sys/sparc/copy.s
Normal file
File diff suppressed because it is too large
Load Diff
1618
sys/sparc/crt.s
Normal file
1618
sys/sparc/crt.s
Normal file
File diff suppressed because it is too large
Load Diff
470
sys/sparc/float.s
Normal file
470
sys/sparc/float.s
Normal file
@@ -0,0 +1,470 @@
|
||||
/* @(#)float.s 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1989 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
#include <machine/asm_linkage.h>
|
||||
#include <machine/trap.h>
|
||||
#include <machine/psl.h>
|
||||
#include <machine/reg.h>
|
||||
#include <sys/signal.h>
|
||||
#include "assym.s"
|
||||
|
||||
/*
|
||||
* Floating point trap handling.
|
||||
*
|
||||
* The FPU may not be in the current configuration.
|
||||
* If an fp_disabled trap is generated and the EF bit
|
||||
* in the psr equals 1 (floating point was enabled)
|
||||
* then there is not a FPU in the configuration
|
||||
* and the global variable fp_exists is cleared.
|
||||
*
|
||||
* When a user process is first started via exec,
|
||||
* floating point operations will be disabled by default.
|
||||
* Upon execution of the first floating point instruction,
|
||||
* a fp_disabled trap will be generated; at which point
|
||||
* a check is made to see if the FPU exists, (fp_exists > 0),
|
||||
* if not the instruction is emulated in software, otherwise
|
||||
* the uarea is updated signifying use of the FPU so that
|
||||
* future floating point context switches will save and restore
|
||||
* floating point state. The trapped instruction will be
|
||||
* restarted and processing will continue as normal.
|
||||
*
|
||||
* When a operation occurs that the hardware cannot properly
|
||||
* handle, an unfinshed fp_op exception will be generated.
|
||||
* Software routines in the kernel will be executed to
|
||||
* simulate proper handling of such conditions.
|
||||
* Exception handling will emulate all instructions
|
||||
* in the floating point address queue.
|
||||
*
|
||||
* At process context switch time we save fp state of
|
||||
* the current process if it was using the fpu.
|
||||
* The next user of the FPU will get a fp_disabled
|
||||
* trap which restores the state if it was already
|
||||
* using it or initializes it if the trap was because
|
||||
* the first use of the FPU by a process.
|
||||
* An optimization in context switch causes a user whose
|
||||
* context is already in the FPU to have the FPU reenabled
|
||||
* automatically without taking fp_disabled trap.
|
||||
*
|
||||
* The fp context of process is not stored in the uarea.
|
||||
* Only a pointer to its state is kept in the uarea.
|
||||
* The variable fp_ctxp points to the floating point
|
||||
* context save area for the current context loaded in
|
||||
* the fpu.
|
||||
*
|
||||
* Forks currently always execute the child before returning
|
||||
* to the parent, therefore fp state of the parent is stored into
|
||||
* its fp context durring the fork. The child starts with the fpu
|
||||
* enabled inheriting the parent's registers. When the parent runs
|
||||
* again it will restore the fp state from the context saved
|
||||
* during the fork.
|
||||
*
|
||||
* NOTE: This code DOES NOT SUPPORT KERNEL USE OF THE FPU
|
||||
*/
|
||||
.seg "text"
|
||||
.align 4
|
||||
/*
|
||||
* syncfpu() synchronizes the fpu with the
|
||||
* the iu as it causes the processor to wait until all
|
||||
* floating point operations in the FPQ complete...
|
||||
* by the time the kernel gets to the point of executing
|
||||
* syncfpu, most floating point operations should have completed
|
||||
* and generally this happens quickly unless there is pending
|
||||
* exception which has to be handled.
|
||||
* We are passed the pointer to trap's (or syscall's) register
|
||||
* structure, which we save in a global for use in fp_traps() in case we
|
||||
* do take a pending exception. If we didn't, we'd process the
|
||||
* exception using the kernel's registers and not the user's, and could
|
||||
* panic if the user was doing an unimplemented fpop (1025966).
|
||||
*/
|
||||
ENTRY(syncfpu)
|
||||
|
||||
mov %psr, %o5 ! is floating point enabled
|
||||
sethi %hi(PSR_EF), %o4
|
||||
btst %o4, %o5
|
||||
bz 1f
|
||||
sethi %hi(_fptraprp), %o5
|
||||
ld [%o5 + %lo(_fptraprp)], %o3
|
||||
st %o0, [%o5 + %lo(_fptraprp)]
|
||||
sethi %hi(fsrholder), %o4
|
||||
!
|
||||
! synchronize with fpu, take an exception, if one is pending
|
||||
!
|
||||
st %fsr, [%o4 + %lo(fsrholder)]
|
||||
retl
|
||||
st %o3, [%o5 + %lo(_fptraprp)]
|
||||
|
||||
1: retl
|
||||
.empty ! the following set PSR_EF is ok in delay slot
|
||||
|
||||
/*
|
||||
* FPU probe - try a floating point instruction to see if there
|
||||
* really is an FPU in the current configuration, exectued once
|
||||
* from autoconf when booting.
|
||||
*/
|
||||
ENTRY(fpu_probe)
|
||||
set PSR_EF, %g1 ! enable floating-point
|
||||
mov %psr, %g2 ! read psr, save value in %g2
|
||||
or %g1, %g2, %g3 ! new psr with fpu enabled
|
||||
mov %g3, %psr ! write psr
|
||||
nop;nop ! psr delay...
|
||||
sethi %hi(_zeros), %g3
|
||||
fpuprobe_ld:
|
||||
ld [%g3 + %lo(_zeros)], %fsr ! probe for fpu, maybe trap
|
||||
|
||||
!
|
||||
! If there is not an FPU, we will get a fp_disabled trap
|
||||
! when we try to load the fsr, which will clear the fpu_exists flag,
|
||||
! and skip over the ld
|
||||
|
||||
! part of fix for 1041977 (fitoX fix can panic kernel)
|
||||
! snarf the FPU version, if it exists
|
||||
sethi %hi(_fpu_exists), %g3
|
||||
ld [%g3 + %lo(_fpu_exists)], %g3
|
||||
mov 7, %g1 ! assume no FPU
|
||||
tst %g3
|
||||
bz 1f
|
||||
sethi %hi(_fpu_version), %g3
|
||||
|
||||
! We know the fpu exists; we are still enabled for it
|
||||
sethi %hi(fsrholder), %g1
|
||||
st %fsr, [%g1 + %lo(fsrholder)]
|
||||
ld [%g1 + %lo(fsrholder)], %g1 ! snarf the FSR
|
||||
set FSR_VERS, %o0
|
||||
and %g1, %o0, %g1 ! get version
|
||||
srl %g1, FSR_VERS_SHIFT, %g1 ! and shift it down
|
||||
|
||||
1:
|
||||
st %g1, [%g3 + %lo(_fpu_version)]
|
||||
|
||||
retl
|
||||
mov %g2, %psr ! restore old psr, turn off FPU
|
||||
|
||||
/*
|
||||
* Floating Point Exceptions.
|
||||
* handled according to type:
|
||||
* 0) no_exception
|
||||
* do nothing
|
||||
* 1) IEEE_exception
|
||||
* re-execute the faulty instruction(s) using
|
||||
* software emulation (must do every instruction in FQ)
|
||||
* 2) unfinished_fpop
|
||||
* re-execute the faulty instruction(s) using
|
||||
* software emulation (must do every instruction in FQ)
|
||||
* 3) unimplemented_fpop
|
||||
* an unimplemented instruction, if it is legal,
|
||||
* will cause emulation of the instruction (and all
|
||||
* other instuctions in the FQ)
|
||||
* 4) sequence_error
|
||||
* panic, this should not happen, and if it does it
|
||||
* it is the result of a kernel bug
|
||||
*
|
||||
* This code assumes the trap preamble has set up the window evironment
|
||||
* for execution of kernel code.
|
||||
*/
|
||||
.global _fp_exception
|
||||
_fp_exception:
|
||||
sethi %hi(_fp_ctxp), %l5
|
||||
ld [%l5 + %lo(_fp_ctxp)], %l5
|
||||
|
||||
mov %l5, %g4
|
||||
|
||||
st %fsr, [%l5 + FPCTX_FSR] ! get floating point status
|
||||
ld [%l5 + FPCTX_FSR], %g1
|
||||
set FSR_FTT, %l4
|
||||
and %g1, %l4, %g1 ! mask out trap type
|
||||
srl %g1, FSR_FTT_SHIFT, %l4 ! use ftt after we dump queue
|
||||
|
||||
dumpfq:
|
||||
clr %g2 ! FQ offset
|
||||
set FSR_QNE, %g3 ! the queue is not empty bit
|
||||
add %l5, FPCTX_Q, %l3
|
||||
1:
|
||||
ld [%l5 + FPCTX_FSR], %g1 ! test fsr
|
||||
btst %g3, %g1 ! is the queue empty?
|
||||
bz 2f ! yes, go figure out what to do
|
||||
|
||||
|
||||
! for Calvin FPU, if two instructions in FQ, and exception and
|
||||
! [%l3 + %g2] not in cache, gives wrong answer. we preload cache
|
||||
! BUGID 1038405
|
||||
ld [%l3 + %g2], %g0 ! Calvin FPU FIX KLUDGE XXX HACK
|
||||
std %fq, [%l3 + %g2] ! store an entry from FQ
|
||||
|
||||
! the fpc that works with the ti8847 requires this
|
||||
nop; nop;
|
||||
|
||||
add %g2, 8, %g2 ! increment offset in FQ
|
||||
b 1b
|
||||
st %fsr, [%l5 + FPCTX_FSR] ! get floating point status
|
||||
2:
|
||||
!
|
||||
! emulating floating point instructions
|
||||
!
|
||||
wr %l0, PSR_ET, %psr ! enable traps
|
||||
! three cycle delay required, we are
|
||||
! not changing the wim so the next
|
||||
! three instructions are safe
|
||||
|
||||
#ifdef MULTIPROCESSOR
|
||||
nop ! psr delay
|
||||
call _klock_enter
|
||||
nop
|
||||
#endif MULTIPROCESSOR
|
||||
cmp %l4, FTT_SEQ ! sanity check for bogus exceptions
|
||||
blt,a fpeok
|
||||
mov FPCTX_Q, %l4 ! offset into stored queue entries
|
||||
!
|
||||
! Sequence error or unknown ftt exception.
|
||||
!
|
||||
set badfpexcpmsg, %o0 ! panic
|
||||
call _panic
|
||||
mov %l4, %o1 ! mov ftt to o1 for stack backtrace
|
||||
|
||||
fpeok:
|
||||
srl %g2, 3, %l6 ! number of entries stored from fpq
|
||||
st %l6, [%l5 + FPCTX_QCNT] ! store number of entries (for debug)
|
||||
|
||||
! run the floating point q
|
||||
call _fp_runq
|
||||
add %sp, MINFRAME, %o0
|
||||
|
||||
fp_ret:
|
||||
!
|
||||
! clear interrupting condition in fsr
|
||||
!
|
||||
ld [%l5 + FPCTX_FSR], %o0
|
||||
set (FSR_FTT|FSR_QNE), %o1 ! clear ftt bits and qne
|
||||
andn %o0, %o1, %o0
|
||||
st %o0, [%l5 + FPCTX_FSR]
|
||||
b sys_rtt
|
||||
ld [%l5 + FPCTX_FSR], %fsr ! ld new fsr to set condition codes
|
||||
|
||||
/*
|
||||
* fp_enable(fp)
|
||||
* struct fpu *fp;
|
||||
*
|
||||
* Initialization when there is a hardware fpu.
|
||||
* Clear the fsr and initialize registers to NaN (-1)
|
||||
* The caller is supposed to update the return psr
|
||||
* so when the return to usrerland is made, the fpu is enabled.
|
||||
*/
|
||||
|
||||
ENTRY(fp_enable)
|
||||
mov %psr, %o1 ! enable the fpu
|
||||
set PSR_EF, %o2
|
||||
or %o1, %o2, %o3
|
||||
mov %o3, %psr
|
||||
nop;nop;nop ! psr delay
|
||||
LOAD_FPREGS(%o0) ! read in fpu state
|
||||
retl
|
||||
ld [%o0 + FPCTX_FSR], %fsr
|
||||
|
||||
|
||||
#
|
||||
/*
|
||||
* fp_dumpregs(fp_ctx_pointer)
|
||||
*
|
||||
* This routine is called when a fork is done to cause the current
|
||||
* register set inside the fpu is put into the new context.
|
||||
*/
|
||||
ENTRY(fp_dumpregs)
|
||||
st %fsr, [%o0 + FPCTX_FSR] ! store fsr in current fpctx
|
||||
STORE_FPREGS(%o0) ! store rest of regsiters
|
||||
retl
|
||||
nop
|
||||
|
||||
!void _fp_read_pfreg(pf, n)
|
||||
! FPU_REGS_TYPE *pf; /* Old freg value. */
|
||||
! unsigned n; /* Want to read register n. */
|
||||
!
|
||||
!{
|
||||
! *pf = %f[n];
|
||||
!}
|
||||
!
|
||||
!void
|
||||
!_fp_write_pfreg(pf, n)
|
||||
! FPU_REGS_TYPE *pf; /* New freg value. */
|
||||
! unsigned n; /* Want to read register n. */
|
||||
!
|
||||
!{
|
||||
! %f[n] = *pf;
|
||||
!}
|
||||
|
||||
.global __fp_read_pfreg
|
||||
__fp_read_pfreg:
|
||||
sll %o1, 3, %o1 ! Table entries are 8 bytes each.
|
||||
set stable, %g1 ! g1 gets base of table.
|
||||
jmp %g1 + %o1 ! Jump into table
|
||||
nop ! Can't follow CTI by CTI.
|
||||
|
||||
.global __fp_write_pfreg
|
||||
__fp_write_pfreg:
|
||||
sll %o1, 3, %o1 ! Table entries are 8 bytes each.
|
||||
set ltable, %g1 ! g1 gets base of table.
|
||||
jmp %g1 + %o1 ! Jump into table
|
||||
nop ! Can't follow CTI by CTI.
|
||||
|
||||
#define STOREFP(n) jmp %o7+8 ; st %f/**/n, [%o0]
|
||||
|
||||
stable:
|
||||
STOREFP(0)
|
||||
STOREFP(1)
|
||||
STOREFP(2)
|
||||
STOREFP(3)
|
||||
STOREFP(4)
|
||||
STOREFP(5)
|
||||
STOREFP(6)
|
||||
STOREFP(7)
|
||||
STOREFP(8)
|
||||
STOREFP(9)
|
||||
STOREFP(10)
|
||||
STOREFP(11)
|
||||
STOREFP(12)
|
||||
STOREFP(13)
|
||||
STOREFP(14)
|
||||
STOREFP(15)
|
||||
STOREFP(16)
|
||||
STOREFP(17)
|
||||
STOREFP(18)
|
||||
STOREFP(19)
|
||||
STOREFP(20)
|
||||
STOREFP(21)
|
||||
STOREFP(22)
|
||||
STOREFP(23)
|
||||
STOREFP(24)
|
||||
STOREFP(25)
|
||||
STOREFP(26)
|
||||
STOREFP(27)
|
||||
STOREFP(28)
|
||||
STOREFP(29)
|
||||
STOREFP(30)
|
||||
STOREFP(31)
|
||||
|
||||
#define LOADFP(n) jmp %o7+8 ; ld [%o0],%f/**/n
|
||||
|
||||
ltable:
|
||||
LOADFP(0)
|
||||
LOADFP(1)
|
||||
LOADFP(2)
|
||||
LOADFP(3)
|
||||
LOADFP(4)
|
||||
LOADFP(5)
|
||||
LOADFP(6)
|
||||
LOADFP(7)
|
||||
LOADFP(8)
|
||||
LOADFP(9)
|
||||
LOADFP(10)
|
||||
LOADFP(11)
|
||||
LOADFP(12)
|
||||
LOADFP(13)
|
||||
LOADFP(14)
|
||||
LOADFP(15)
|
||||
LOADFP(16)
|
||||
LOADFP(17)
|
||||
LOADFP(18)
|
||||
LOADFP(19)
|
||||
LOADFP(20)
|
||||
LOADFP(21)
|
||||
LOADFP(22)
|
||||
LOADFP(23)
|
||||
LOADFP(24)
|
||||
LOADFP(25)
|
||||
LOADFP(26)
|
||||
LOADFP(27)
|
||||
LOADFP(28)
|
||||
LOADFP(29)
|
||||
LOADFP(30)
|
||||
LOADFP(31)
|
||||
|
||||
.global __fp_write_pfsr
|
||||
__fp_write_pfsr:
|
||||
retl
|
||||
ld [%o0], %fsr
|
||||
|
||||
badfpexcpmsg:
|
||||
.asciz "unexpected floating point exception %x\n"
|
||||
fpemptyqmsg:
|
||||
.asciz "fp exception with empty queue, fsr 0x%x\n"
|
||||
badfpdisabledmsg:
|
||||
.asciz "fp_disabled: no FPU but not fpu_probe"
|
||||
.align 4
|
||||
|
||||
.seg "data"
|
||||
.align 8
|
||||
fpjunk:
|
||||
.double 0
|
||||
.double 0
|
||||
|
||||
/* in the case of MP, we assume either both processors have a
|
||||
* FPU or they don't.
|
||||
*/
|
||||
.global _fpu_exists
|
||||
_fpu_exists:
|
||||
.word 1 ! assume FPU exists
|
||||
|
||||
|
||||
fsrholder:
|
||||
.word 0 ! dummy place to write fsr
|
||||
|
||||
! part of fix for 1041977 (fitoX fix can panic kernel)
|
||||
.global _fpu_version
|
||||
_fpu_version:
|
||||
.word -1 ! place to store FPU version
|
||||
|
||||
.seg "text"
|
||||
|
||||
/*
|
||||
* Floating point disabled trap, run after trap preamble.
|
||||
* If FPU does not exist, emulate instruction otherwise,
|
||||
* enable floating point.
|
||||
*
|
||||
*/
|
||||
.global _fp_disabled
|
||||
_fp_disabled:
|
||||
!
|
||||
! lower priority, allow other interrupts, overflows, ...
|
||||
!
|
||||
wr %l0, PSR_ET, %psr ! enable traps
|
||||
|
||||
#ifdef MULTIPROCESSOR
|
||||
nop ! psr delay
|
||||
call _klock_enter
|
||||
nop
|
||||
#endif MULTIPROCESSOR
|
||||
|
||||
!
|
||||
! fp_disable trap when the FPU is enabled; should only happen
|
||||
! once from autoconf when there is not an FPU in the board.
|
||||
!
|
||||
set fpuprobe_ld, %l7
|
||||
cmp %l1, %l7 ! was trap in fpu_probe, above?
|
||||
bne 2f ! if not let fp_is_disabled handle it
|
||||
set PSR_EF, %l5 ! set pSR_EF in delay slot
|
||||
|
||||
sethi %hi(_fpu_exists), %l6
|
||||
clr [%l6 + %lo(_fpu_exists)] ! FPU does not exist
|
||||
|
||||
!
|
||||
! Get the system trap handler's version of the psr and fix it up
|
||||
! so that sys_rtt will restore a psr with fpu disabled.
|
||||
!
|
||||
ld [%sp + MINFRAME + PSR*4], %l6
|
||||
bclr %l5, %l6 ! clear enable fp bit in psr
|
||||
st %l6, [%sp + MINFRAME + PSR*4]
|
||||
!
|
||||
! skip the probe instruction
|
||||
!
|
||||
ld [%sp + MINFRAME + nPC*4], %l6 ! skip the probe instruction
|
||||
st %l6, [%sp + MINFRAME + PC*4] ! pc = npc
|
||||
add %l6, 4, %l6
|
||||
b sys_rtt ! return from trap
|
||||
st %l6, [%sp + MINFRAME + nPC*4] ! npc += 4
|
||||
2:
|
||||
call _fp_is_disabled
|
||||
add %sp, MINFRAME, %o0
|
||||
|
||||
ba sys_rtt
|
||||
nop
|
||||
19
sys/sparc/fpu/Makefile
Normal file
19
sys/sparc/fpu/Makefile
Normal file
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# @(#)Makefile 1.1 94/10/31 SMI
|
||||
#
|
||||
FILES= addsub.c compare.c div.c fpu.c fpu_simulator.c iu_simulator.c \
|
||||
mul.c pack.c unpack.c utility.c uword.c
|
||||
HFILES= fpu_simulator.h globals.h ieee.h
|
||||
HDIR=$(DESTDIR)/usr/include/sparc/fpu
|
||||
|
||||
all: $(HFILES) $(FILES)
|
||||
|
||||
install: $(FILES)
|
||||
|
||||
clean:
|
||||
|
||||
install_h: $(HFILES) FRC
|
||||
install -d -m 755 $(HDIR)
|
||||
install -m 444 $(HFILES) $(HDIR)
|
||||
FRC:
|
||||
|
||||
221
sys/sparc/fpu/addsub.c
Normal file
221
sys/sparc/fpu/addsub.c
Normal file
@@ -0,0 +1,221 @@
|
||||
#ifdef sccsid
|
||||
static char sccsid[] = "@(#)addsub.c 1.1 94/10/31 SMI";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
#include <machine/fpu/fpu_simulator.h>
|
||||
#include <machine/fpu/globals.h>
|
||||
|
||||
#ifdef sun4m
|
||||
extern int ross625_flag; /* flag to identify hyperSPARC */
|
||||
#endif sun4m
|
||||
|
||||
/*ARGSUSED*/
|
||||
PRIVATE void
|
||||
true_add(pfpsd, px, py, pz)
|
||||
fp_simd_type *pfpsd;
|
||||
unpacked *px, *py, *pz;
|
||||
{
|
||||
unsigned c;
|
||||
unpacked *pt;
|
||||
|
||||
#ifdef sun4m
|
||||
if (((int) px->fpclass < (int) py->fpclass) ||
|
||||
(ross625_flag && ((int) px->fpclass <= (int) py->fpclass)))
|
||||
#else sun4m
|
||||
if ((int) px->fpclass < (int) py->fpclass)
|
||||
#endif sun4m
|
||||
{ /* Reverse. */
|
||||
pt = py;
|
||||
py = px;
|
||||
px = pt;
|
||||
}
|
||||
/* Now class(x) >= class(y). */
|
||||
switch (px->fpclass) {
|
||||
case fp_quiet: /* NaN + x -> NaN */
|
||||
case fp_signaling: /* NaN + x -> NaN */
|
||||
case fp_infinity: /* Inf + x -> Inf */
|
||||
case fp_zero: /* 0 + 0 -> 0 */
|
||||
*pz = *px;
|
||||
return;
|
||||
default:
|
||||
if (py->fpclass == fp_zero) {
|
||||
*pz = *px;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Now z is normal or subnormal. */
|
||||
/* Now y is normal or subnormal. */
|
||||
if (px->exponent < py->exponent) { /* Reverse. */
|
||||
pt = py;
|
||||
py = px;
|
||||
px = pt;
|
||||
}
|
||||
/* Now class(x) >= class(y). */
|
||||
pz->fpclass = px->fpclass;
|
||||
pz->sign = px->sign;
|
||||
pz->exponent = px->exponent;
|
||||
pz->rounded = pz->sticky = 0;
|
||||
|
||||
if (px->exponent != py->exponent) { /* pre-alignment required */
|
||||
fpu_rightshift(py, pz->exponent - py->exponent);
|
||||
pz->rounded = py->rounded;
|
||||
pz->sticky = py->sticky;
|
||||
}
|
||||
c = 0;
|
||||
c = fpu_add3wc(&(pz->significand[3]), px->significand[3],
|
||||
py->significand[3], c);
|
||||
c = fpu_add3wc(&(pz->significand[2]), px->significand[2],
|
||||
py->significand[2], c);
|
||||
c = fpu_add3wc(&(pz->significand[1]), px->significand[1],
|
||||
py->significand[1], c);
|
||||
c = fpu_add3wc(&(pz->significand[0]), px->significand[0],
|
||||
py->significand[0], c);
|
||||
|
||||
/* Handle carry out of msb. */
|
||||
if (pz->significand[0]>=0x20000) {
|
||||
fpu_rightshift(pz, 1); /* Carried out bit. */
|
||||
pz->exponent++; /* Renormalize. */
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PRIVATE void
|
||||
true_sub(pfpsd, px, py, pz)
|
||||
fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
unpacked *px, *py, *pz;
|
||||
{
|
||||
unsigned *z, g, s, r, c;
|
||||
unpacked *pt;
|
||||
|
||||
#ifdef sun4m
|
||||
if (((int) px->fpclass < (int) py->fpclass) ||
|
||||
(ross625_flag && ((int) px->fpclass <= (int) py->fpclass)))
|
||||
#else sun4m
|
||||
if ((int) px->fpclass < (int) py->fpclass)
|
||||
#endif sun4m
|
||||
{ /* Reverse. */
|
||||
pt = py;
|
||||
py = px;
|
||||
px = pt;
|
||||
}
|
||||
/* Now class(x) >= class(y). */
|
||||
*pz = *px; /* Tentative difference: x. */
|
||||
switch (pz->fpclass) {
|
||||
case fp_quiet: /* NaN - x -> NaN */
|
||||
case fp_signaling: /* NaN - x -> NaN */
|
||||
return;
|
||||
case fp_infinity: /* Inf - x -> Inf */
|
||||
if (py->fpclass == fp_infinity) {
|
||||
fpu_error_nan(pfpsd, pz); /* Inf - Inf -> NaN */
|
||||
pz->fpclass = fp_quiet;
|
||||
}
|
||||
return;
|
||||
case fp_zero: /* 0 - 0 -> 0 */
|
||||
pz->sign = (pfpsd->fp_direction == fp_negative);
|
||||
return;
|
||||
default:
|
||||
if (py->fpclass == fp_zero)
|
||||
return;
|
||||
}
|
||||
|
||||
/* x and y are both normal or subnormal. */
|
||||
|
||||
if (px->exponent < py->exponent) { /* Reverse. */
|
||||
pt = py;
|
||||
py = px;
|
||||
px = pt;
|
||||
}
|
||||
/* Now exp(x) >= exp(y). */
|
||||
pz->fpclass = px->fpclass;
|
||||
pz->sign = px->sign;
|
||||
pz->exponent = px->exponent;
|
||||
pz->rounded = 0;
|
||||
pz->sticky = 0;
|
||||
z = pz->significand;
|
||||
|
||||
if (px->exponent == py->exponent) { /* no pre-alignment required */
|
||||
c = 0;
|
||||
c = fpu_sub3wc(&z[3], px->significand[3],
|
||||
py->significand[3], c);
|
||||
c = fpu_sub3wc(&z[2], px->significand[2],
|
||||
py->significand[2], c);
|
||||
c = fpu_sub3wc(&z[1], px->significand[1],
|
||||
py->significand[1], c);
|
||||
c = fpu_sub3wc(&z[0], px->significand[0],
|
||||
py->significand[0], c);
|
||||
if ((z[0]|z[1]|z[2]|z[3])==0) { /* exact zero result */
|
||||
pz->sign = (pfpsd->fp_direction == fp_negative);
|
||||
pz->fpclass = fp_zero;
|
||||
return;
|
||||
}
|
||||
if (z[0]>=0x20000) { /* sign reversal occurred */
|
||||
pz->sign = py->sign;
|
||||
c = 0;
|
||||
c = fpu_neg2wc(&z[3], z[3], c);
|
||||
c = fpu_neg2wc(&z[2], z[2], c);
|
||||
c = fpu_neg2wc(&z[1], z[1], c);
|
||||
c = fpu_neg2wc(&z[0], z[0], c);
|
||||
}
|
||||
fpu_normalize(pz);
|
||||
return;
|
||||
} else { /* pre-alignment required */
|
||||
fpu_rightshift(py, pz->exponent - py->exponent - 1);
|
||||
r = py->rounded; /* rounded bit */
|
||||
s = py->sticky; /* sticky bit */
|
||||
fpu_rightshift(py, 1);
|
||||
g = py->rounded; /* guard bit */
|
||||
if (s!=0) r = (r==0);
|
||||
if ((r|s)!=0) g = (g==0); /* guard and rounded bits of z */
|
||||
c = ((g|r|s)!=0);
|
||||
c = fpu_sub3wc(&z[3], px->significand[3],
|
||||
py->significand[3], c);
|
||||
c = fpu_sub3wc(&z[2], px->significand[2],
|
||||
py->significand[2], c);
|
||||
c = fpu_sub3wc(&z[1], px->significand[1],
|
||||
py->significand[1], c);
|
||||
c = fpu_sub3wc(&z[0], px->significand[0],
|
||||
py->significand[0], c);
|
||||
|
||||
if (z[0]>=0x10000) { /* don't need post-shifted */
|
||||
pz->sticky = s|r;
|
||||
pz->rounded = g;
|
||||
} else { /* post-shifted left 1 bit */
|
||||
pz->sticky = s;
|
||||
pz->rounded = r;
|
||||
pz->significand[0] = (z[0]<<1)|((z[1]&0x80000000)>>31);
|
||||
pz->significand[1] = (z[1]<<1)|((z[2]&0x80000000)>>31);
|
||||
pz->significand[2] = (z[2]<<1)|((z[3]&0x80000000)>>31);
|
||||
pz->significand[3] = (z[3]<<1)|g;
|
||||
pz->exponent -= 1;
|
||||
if (z[0]<0x10000) fpu_normalize(pz);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_fp_add(pfpsd, px, py, pz)
|
||||
fp_simd_type *pfpsd;
|
||||
unpacked *px, *py, *pz;
|
||||
{
|
||||
if (px->sign == py->sign)
|
||||
true_add(pfpsd, px, py, pz);
|
||||
else
|
||||
true_sub(pfpsd, px, py, pz);
|
||||
}
|
||||
|
||||
void
|
||||
_fp_sub(pfpsd, px, py, pz)
|
||||
fp_simd_type *pfpsd;
|
||||
unpacked *px, *py, *pz;
|
||||
{
|
||||
if ((int)py->fpclass < (int)fp_quiet) py->sign= 1- py->sign;
|
||||
if (px->sign == py->sign)
|
||||
true_add(pfpsd, px, py, pz);
|
||||
else
|
||||
true_sub(pfpsd, px, py, pz);
|
||||
}
|
||||
65
sys/sparc/fpu/compare.c
Normal file
65
sys/sparc/fpu/compare.c
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifdef sccsid
|
||||
static char sccsid[] = "@(#)compare.c 1.1 94/10/31 Copyr 1988 Sun Micro";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
#include <machine/fpu/fpu_simulator.h>
|
||||
#include <machine/fpu/globals.h>
|
||||
|
||||
enum fcc_type
|
||||
_fp_compare(pfpsd, px, py, strict)
|
||||
fp_simd_type *pfpsd; /* simulator data */
|
||||
unpacked *px, *py;
|
||||
int strict; /*
|
||||
* 0 if quiet NaN unexceptional, 1 if
|
||||
* exceptional
|
||||
*/
|
||||
{
|
||||
enum fcc_type cc;
|
||||
int n;
|
||||
|
||||
if ((px->fpclass == fp_quiet) || (py->fpclass == fp_quiet) ||
|
||||
(px->fpclass == fp_signaling) || (py->fpclass == fp_signaling)) {
|
||||
if (strict) /* NaN */
|
||||
fpu_set_exception(pfpsd, fp_invalid);
|
||||
cc = fcc_unordered;
|
||||
} else if ((px->fpclass == fp_zero) && (py->fpclass == fp_zero))
|
||||
cc = fcc_equal;
|
||||
/* both zeros */
|
||||
else if (px->sign < py->sign)
|
||||
cc = fcc_greater;
|
||||
else if (px->sign > py->sign)
|
||||
cc = fcc_less;
|
||||
else { /* signs the same, compute magnitude cc */
|
||||
if ((int) px->fpclass > (int) py->fpclass)
|
||||
cc = fcc_greater;
|
||||
else if ((int) px->fpclass < (int) py->fpclass)
|
||||
cc = fcc_less;
|
||||
else
|
||||
/* same classes */ if (px->fpclass == fp_infinity)
|
||||
cc = fcc_equal; /* same infinity */
|
||||
else if (px->exponent > py->exponent)
|
||||
cc = fcc_greater;
|
||||
else if (px->exponent < py->exponent)
|
||||
cc = fcc_less;
|
||||
else { /* equal exponents */
|
||||
n = fpu_cmpli(px->significand, py->significand, 4);
|
||||
if (n>0) cc = fcc_greater;
|
||||
else if (n<0) cc = fcc_less;
|
||||
else cc = fcc_equal;
|
||||
}
|
||||
if (px->sign)
|
||||
switch (cc) { /* negative numbers */
|
||||
case fcc_less:
|
||||
cc = fcc_greater;
|
||||
break;
|
||||
case fcc_greater:
|
||||
cc = fcc_less;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (cc);
|
||||
}
|
||||
292
sys/sparc/fpu/div.c
Normal file
292
sys/sparc/fpu/div.c
Normal file
@@ -0,0 +1,292 @@
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)div.c 1.1 94/10/31 SMI";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
#include <machine/fpu/fpu_simulator.h>
|
||||
#include <machine/fpu/globals.h>
|
||||
|
||||
#ifdef sun4m
|
||||
extern int ross625_flag; /* to identify hyperSPARC */
|
||||
#endif sun4m
|
||||
|
||||
void
|
||||
_fp_div(pfpsd, px, py, pz)
|
||||
fp_simd_type *pfpsd;
|
||||
unpacked *px, *py, *pz;
|
||||
{
|
||||
unsigned r[4], *y, q, c;
|
||||
int n;
|
||||
|
||||
*pz = *px;
|
||||
|
||||
if (((int)py->fpclass >= (int)fp_quiet) ||
|
||||
((int)px->fpclass >= (int)fp_quiet)) {
|
||||
#ifdef sun4m
|
||||
if (((int)py->fpclass > (int)px->fpclass) ||
|
||||
(ross625_flag && ((int)py->fpclass >= (int)px->fpclass)))
|
||||
*pz = *py;
|
||||
#else sun4m
|
||||
if ((int)py->fpclass > (int)px->fpclass) *pz = *py;
|
||||
#endif sun4m
|
||||
return;
|
||||
}
|
||||
|
||||
pz->sign = px->sign ^ py->sign;
|
||||
switch (px->fpclass) {
|
||||
case fp_quiet:
|
||||
case fp_signaling:
|
||||
return;
|
||||
case fp_zero:
|
||||
case fp_infinity:
|
||||
if (px->fpclass == py->fpclass) { /* 0/0 or inf/inf */
|
||||
fpu_error_nan(pfpsd, pz);
|
||||
pz->fpclass = fp_quiet;
|
||||
}
|
||||
return;
|
||||
case fp_normal:
|
||||
switch (py->fpclass) {
|
||||
case fp_zero: /* number/0 */
|
||||
fpu_set_exception(pfpsd, fp_division);
|
||||
pz->fpclass = fp_infinity;
|
||||
return;
|
||||
case fp_infinity: /* number/inf */
|
||||
pz->fpclass = fp_zero;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now x and y are both normal or subnormal. */
|
||||
|
||||
r[0] = px->significand[0];
|
||||
r[1] = px->significand[1];
|
||||
r[2] = px->significand[2];
|
||||
r[3] = px->significand[3];
|
||||
y = py->significand;
|
||||
|
||||
if (fpu_cmpli(r, y, 4) >= 0)
|
||||
pz->exponent = px->exponent - py->exponent;
|
||||
else
|
||||
pz->exponent = px->exponent - py->exponent - 1;
|
||||
|
||||
q=0;
|
||||
while (q<0x10000) { /* generate quo[0] */
|
||||
q <<= 1;
|
||||
if (fpu_cmpli(r, y, 4) >= 0) {
|
||||
q += 1; /* if r>y do r-=y and q+=1 */
|
||||
c = 0;
|
||||
c = fpu_sub3wc(&r[3], r[3], y[3], c);
|
||||
c = fpu_sub3wc(&r[2], r[2], y[2], c);
|
||||
c = fpu_sub3wc(&r[1], r[1], y[1], c);
|
||||
c = fpu_sub3wc(&r[0], r[0], y[0], c);
|
||||
}
|
||||
r[0] = (r[0]<<1)|((r[1]&0x80000000)>>31); /* r << 1 */
|
||||
r[1] = (r[1]<<1)|((r[2]&0x80000000)>>31);
|
||||
r[2] = (r[2]<<1)|((r[3]&0x80000000)>>31);
|
||||
r[3] = (r[3]<<1);
|
||||
}
|
||||
pz->significand[0]=q;
|
||||
q=0; /* generate quo[1] */
|
||||
n = 32;
|
||||
while (n--) {
|
||||
q <<= 1;
|
||||
if (fpu_cmpli(r, y, 4) >= 0) {
|
||||
q += 1; /* if r>y do r-=y and q+=1 */
|
||||
c = 0;
|
||||
c = fpu_sub3wc(&r[3], r[3], y[3], c);
|
||||
c = fpu_sub3wc(&r[2], r[2], y[2], c);
|
||||
c = fpu_sub3wc(&r[1], r[1], y[1], c);
|
||||
c = fpu_sub3wc(&r[0], r[0], y[0], c);
|
||||
}
|
||||
r[0] = (r[0]<<1)|((r[1]&0x80000000)>>31); /* r << 1 */
|
||||
r[1] = (r[1]<<1)|((r[2]&0x80000000)>>31);
|
||||
r[2] = (r[2]<<1)|((r[3]&0x80000000)>>31);
|
||||
r[3] = (r[3]<<1);
|
||||
}
|
||||
pz->significand[1] = q;
|
||||
q=0; /* generate quo[2] */
|
||||
n = 32;
|
||||
while (n--) {
|
||||
q <<= 1;
|
||||
if (fpu_cmpli(r, y, 4) >= 0) {
|
||||
q += 1; /* if r>y do r-=y and q+=1 */
|
||||
c = 0;
|
||||
c = fpu_sub3wc(&r[3], r[3], y[3], c);
|
||||
c = fpu_sub3wc(&r[2], r[2], y[2], c);
|
||||
c = fpu_sub3wc(&r[1], r[1], y[1], c);
|
||||
c = fpu_sub3wc(&r[0], r[0], y[0], c);
|
||||
}
|
||||
r[0] = (r[0]<<1)|((r[1]&0x80000000)>>31); /* r << 1 */
|
||||
r[1] = (r[1]<<1)|((r[2]&0x80000000)>>31);
|
||||
r[2] = (r[2]<<1)|((r[3]&0x80000000)>>31);
|
||||
r[3] = (r[3]<<1);
|
||||
}
|
||||
pz->significand[2] = q;
|
||||
q=0; /* generate quo[3] */
|
||||
n = 32;
|
||||
while (n--) {
|
||||
q <<= 1;
|
||||
if (fpu_cmpli(r, y, 4) >= 0) {
|
||||
q += 1; /* if r>y do r-=y and q+=1 */
|
||||
c = 0;
|
||||
c = fpu_sub3wc(&r[3], r[3], y[3], c);
|
||||
c = fpu_sub3wc(&r[2], r[2], y[2], c);
|
||||
c = fpu_sub3wc(&r[1], r[1], y[1], c);
|
||||
c = fpu_sub3wc(&r[0], r[0], y[0], c);
|
||||
}
|
||||
r[0] = (r[0]<<1)|((r[1]&0x80000000)>>31); /* r << 1 */
|
||||
r[1] = (r[1]<<1)|((r[2]&0x80000000)>>31);
|
||||
r[2] = (r[2]<<1)|((r[3]&0x80000000)>>31);
|
||||
r[3] = (r[3]<<1);
|
||||
}
|
||||
pz->significand[3] = q;
|
||||
if ((r[0]|r[1]|r[2]|r[3])==0) pz->sticky = pz->rounded = 0;
|
||||
else {
|
||||
pz->sticky = 1; /* half way case won't occur */
|
||||
if (fpu_cmpli(r, y, 4) >= 0) pz->rounded = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_fp_sqrt(pfpsd, px, pz)
|
||||
fp_simd_type *pfpsd;
|
||||
unpacked *px, *pz;
|
||||
{ /* *pz gets sqrt(*px) */
|
||||
|
||||
unsigned *x, r, c, q, t[4], s[4];
|
||||
*pz = *px;
|
||||
switch (px->fpclass) {
|
||||
case fp_quiet:
|
||||
case fp_signaling:
|
||||
case fp_zero:
|
||||
return;
|
||||
case fp_infinity:
|
||||
if (px->sign == 1) { /* sqrt(-inf) */
|
||||
fpu_error_nan(pfpsd, pz);
|
||||
pz->fpclass = fp_quiet;
|
||||
}
|
||||
return;
|
||||
case fp_normal:
|
||||
if (px->sign == 1) { /* sqrt(-norm) */
|
||||
fpu_error_nan(pfpsd, pz);
|
||||
pz->fpclass = fp_quiet;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now x is normal. */
|
||||
x = px->significand;
|
||||
if (px->exponent & 1) { /*
|
||||
* sqrt(1.f * 2**odd) = sqrt (2.+2f)
|
||||
* 2**(odd-1)/2
|
||||
*/
|
||||
pz->exponent = (px->exponent - 1) / 2;
|
||||
x[0] = (x[0]<<1)|((x[1]&0x80000000)>>31); /* x<<1 */
|
||||
x[1] = (x[1]<<1)|((x[2]&0x80000000)>>31);
|
||||
x[2] = (x[2]<<1)|((x[3]&0x80000000)>>31);
|
||||
x[3] = (x[3]<<1);
|
||||
} else { /*
|
||||
* sqrt(1.f * 2**even) = sqrt (1.f)
|
||||
* 2**(even)/2
|
||||
*/
|
||||
pz->exponent = px->exponent / 2;
|
||||
}
|
||||
s[0]=s[1]=s[2]=s[3]=t[0]=t[1]=t[2]=t[3]=0;
|
||||
q = 0;
|
||||
r = 0x00010000;
|
||||
while (r!=0) { /* compute sqrt[0] */
|
||||
t[0] = s[0]+r;
|
||||
if (t[0] <= x[0]) {
|
||||
s[0] = t[0]+r;
|
||||
x[0] -= t[0];
|
||||
q += r;
|
||||
}
|
||||
x[0] = (x[0]<<1)|((x[1]&0x80000000)>>31); /* x<<1 */
|
||||
x[1] = (x[1]<<1)|((x[2]&0x80000000)>>31);
|
||||
x[2] = (x[2]<<1)|((x[3]&0x80000000)>>31);
|
||||
x[3] = (x[3]<<1);
|
||||
r >>= 1;
|
||||
}
|
||||
pz->significand[0] = q;
|
||||
q = 0;
|
||||
r = 0x80000000;
|
||||
while (r!=0) { /* compute sqrt[1] */
|
||||
t[1] = s[1]+r; /* no carry */
|
||||
t[0] = s[0];
|
||||
if (fpu_cmpli(t, x, 2) <= 0) {
|
||||
c = 0;
|
||||
c = fpu_add3wc(&s[1], t[1], r, c);
|
||||
c = fpu_add3wc(&s[0], t[0], 0, c);
|
||||
c = 0;
|
||||
c = fpu_sub3wc(&x[1], x[1], t[1], c);
|
||||
c = fpu_sub3wc(&x[0], x[0], t[0], c);
|
||||
q += r;
|
||||
}
|
||||
x[0] = (x[0]<<1)|((x[1]&0x80000000)>>31); /* x<<1 */
|
||||
x[1] = (x[1]<<1)|((x[2]&0x80000000)>>31);
|
||||
x[2] = (x[2]<<1)|((x[3]&0x80000000)>>31);
|
||||
x[3] = (x[3]<<1);
|
||||
r >>= 1;
|
||||
}
|
||||
pz->significand[1] = q;
|
||||
q = 0;
|
||||
r = 0x80000000;
|
||||
while (r!=0) { /* compute sqrt[2] */
|
||||
t[2] = s[2]+r; /* no carry */
|
||||
t[1] = s[1];
|
||||
t[0] = s[0];
|
||||
if (fpu_cmpli(t, x, 3) <= 0) {
|
||||
c = 0;
|
||||
c = fpu_add3wc(&s[2], t[2], r, c);
|
||||
c = fpu_add3wc(&s[1], t[1], 0, c);
|
||||
c = fpu_add3wc(&s[0], t[0], 0, c);
|
||||
c = 0;
|
||||
c = fpu_sub3wc(&x[2], x[2], t[2], c);
|
||||
c = fpu_sub3wc(&x[1], x[1], t[1], c);
|
||||
c = fpu_sub3wc(&x[0], x[0], t[0], c);
|
||||
q += r;
|
||||
}
|
||||
x[0] = (x[0]<<1)|((x[1]&0x80000000)>>31); /* x<<1 */
|
||||
x[1] = (x[1]<<1)|((x[2]&0x80000000)>>31);
|
||||
x[2] = (x[2]<<1)|((x[3]&0x80000000)>>31);
|
||||
x[3] = (x[3]<<1);
|
||||
r >>= 1;
|
||||
}
|
||||
pz->significand[2] = q;
|
||||
q = 0;
|
||||
r = 0x80000000;
|
||||
while (r!=0) { /* compute sqrt[3] */
|
||||
t[3] = s[3]+r; /* no carry */
|
||||
t[2] = s[2];
|
||||
t[1] = s[1];
|
||||
t[0] = s[0];
|
||||
if (fpu_cmpli(t, x, 4) <= 0) {
|
||||
c = 0;
|
||||
c = fpu_add3wc(&s[3], t[3], r, c);
|
||||
c = fpu_add3wc(&s[2], t[2], 0, c);
|
||||
c = fpu_add3wc(&s[1], t[1], 0, c);
|
||||
c = fpu_add3wc(&s[0], t[0], 0, c);
|
||||
c = 0;
|
||||
c = fpu_sub3wc(&x[3], x[3], t[3], c);
|
||||
c = fpu_sub3wc(&x[2], x[2], t[2], c);
|
||||
c = fpu_sub3wc(&x[1], x[1], t[1], c);
|
||||
c = fpu_sub3wc(&x[0], x[0], t[0], c);
|
||||
q += r;
|
||||
}
|
||||
x[0] = (x[0]<<1)|((x[1]&0x80000000)>>31); /* x<<1 */
|
||||
x[1] = (x[1]<<1)|((x[2]&0x80000000)>>31);
|
||||
x[2] = (x[2]<<1)|((x[3]&0x80000000)>>31);
|
||||
x[3] = (x[3]<<1);
|
||||
r >>= 1;
|
||||
}
|
||||
pz->significand[3] = q;
|
||||
if ((x[0]|x[1]|x[2]|x[3])==0) {
|
||||
pz->sticky = pz->rounded = 0;
|
||||
} else {
|
||||
pz->sticky = 1;
|
||||
if (fpu_cmpli(s, x, 4)<0) pz->rounded=1; else pz->rounded = 0;
|
||||
}
|
||||
}
|
||||
396
sys/sparc/fpu/fpu.c
Normal file
396
sys/sparc/fpu/fpu.c
Normal file
@@ -0,0 +1,396 @@
|
||||
/* @(#)fpu.c 1.1 94/10/31 SMI; */
|
||||
#include <sys/types.h>
|
||||
#include <machine/reg.h>
|
||||
#include <machine/psl.h>
|
||||
#include <machine/buserr.h>
|
||||
#include <machine/trap.h>
|
||||
#include <sun/fault.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/map.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/core.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <machine/fpu/fpu_simulator.h>
|
||||
#include <machine/fpu/globals.h>
|
||||
|
||||
/*
|
||||
* the global pointer to the current floating point context
|
||||
*/
|
||||
|
||||
#ifdef MULTIPROCESSOR
|
||||
extern struct fpu *fp_ctxp;
|
||||
#else MULTIPROCESSOR
|
||||
struct fpu *fp_ctxp = (struct fpu *)0;
|
||||
#endif MULTIPROCESSOR
|
||||
|
||||
extern int fpu_exists;
|
||||
|
||||
struct fpu *
|
||||
fpu_ctxalloc()
|
||||
{
|
||||
register struct fpu *fp;
|
||||
register int i;
|
||||
|
||||
fp = (struct fpu *)new_kmem_alloc(sizeof (*fp), KMEM_SLEEP);
|
||||
fp->fpu_fsr = 0; /* zero fsr */
|
||||
i = -1;
|
||||
fp->fpu_regs[0] = i; /* initialize registers to NAN */
|
||||
fp->fpu_regs[1] = i;
|
||||
fp->fpu_regs[2] = i;
|
||||
fp->fpu_regs[3] = i;
|
||||
fp->fpu_regs[4] = i;
|
||||
fp->fpu_regs[5] = i;
|
||||
fp->fpu_regs[6] = i;
|
||||
fp->fpu_regs[7] = i;
|
||||
fp->fpu_regs[8] = i;
|
||||
fp->fpu_regs[9] = i;
|
||||
fp->fpu_regs[10] = i;
|
||||
fp->fpu_regs[11] = i;
|
||||
fp->fpu_regs[12] = i;
|
||||
fp->fpu_regs[13] = i;
|
||||
fp->fpu_regs[14] = i;
|
||||
fp->fpu_regs[15] = i;
|
||||
fp->fpu_regs[16] = i;
|
||||
fp->fpu_regs[17] = i;
|
||||
fp->fpu_regs[18] = i;
|
||||
fp->fpu_regs[19] = i;
|
||||
fp->fpu_regs[20] = i;
|
||||
fp->fpu_regs[21] = i;
|
||||
fp->fpu_regs[22] = i;
|
||||
fp->fpu_regs[23] = i;
|
||||
fp->fpu_regs[24] = i;
|
||||
fp->fpu_regs[25] = i;
|
||||
fp->fpu_regs[26] = i;
|
||||
fp->fpu_regs[27] = i;
|
||||
fp->fpu_regs[28] = i;
|
||||
fp->fpu_regs[29] = i;
|
||||
fp->fpu_regs[30] = i;
|
||||
fp->fpu_regs[31] = i;
|
||||
return (fp);
|
||||
}
|
||||
|
||||
fpu_ctxfree()
|
||||
{
|
||||
register struct pcb *pcb;
|
||||
|
||||
pcb = &u.u_pcb;
|
||||
if (pcb->pcb_fpctxp) {
|
||||
if (fp_ctxp == pcb->pcb_fpctxp)
|
||||
fp_ctxp = 0;
|
||||
kmem_free((caddr_t)pcb->pcb_fpctxp, sizeof (struct fpu));
|
||||
pcb->pcb_fpctxp = (struct fpu *) 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
fpu_fork_context(p2, nfp, new_context)
|
||||
struct proc *p2;
|
||||
struct file **nfp;
|
||||
int *new_context;
|
||||
{
|
||||
register struct fpu *fp;
|
||||
register struct pcb *pcb;
|
||||
|
||||
/*
|
||||
* if the current process is using the fpu,
|
||||
* allocate a new fp context and initialize it
|
||||
* with the current fp context state.
|
||||
*
|
||||
*/
|
||||
pcb = &u.u_pcb;
|
||||
|
||||
if (pcb->pcb_fpctxp)
|
||||
{
|
||||
/* we are forking fpu context due to users fork() call.
|
||||
* At this time the fpu chip registers belong to the
|
||||
* parent. First dump the registers to the fpu context.
|
||||
* then copy the context to the child's context.
|
||||
*
|
||||
* Child runs first and therefore the fp_ctxp should
|
||||
* point to childs fp context. However, the child may not
|
||||
* started due to other errors during the rest of the fork.
|
||||
* therefore shutoff PSR_EF for the parent and this will be
|
||||
* copied to the child in fork(). THen make fp_ctxp 0.
|
||||
* This will ensure the child and parent have different
|
||||
* fpu contexts.
|
||||
*/
|
||||
|
||||
if (fpu_exists)
|
||||
fp_dumpregs((caddr_t)pcb->pcb_fpctxp);
|
||||
fp = fpu_ctxalloc();
|
||||
*new_context = (int)fp;
|
||||
bcopy((caddr_t)pcb->pcb_fpctxp, (caddr_t)fp, sizeof *fp);
|
||||
u.u_ar0[PS] = u.u_ar0[PS] & ~PSR_EF;
|
||||
fp_ctxp = 0; /* current loaded context does not belong
|
||||
* to anyone.
|
||||
*/
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
fpu_ofile()
|
||||
{
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
fpu_newproc(nfp, new_context, childu)
|
||||
struct file *nfp;
|
||||
int new_context;
|
||||
struct user *childu;
|
||||
{
|
||||
/*
|
||||
* initialize the fp context pointer in the new procs
|
||||
* uarea, it was allocated above in fpu_fork_context
|
||||
*/
|
||||
childu->u_pcb.pcb_fpctxp = (struct fpu *)new_context;
|
||||
}
|
||||
|
||||
fpu_core(corep)
|
||||
struct core *corep;
|
||||
{
|
||||
if (u.u_pcb.pcb_fpctxp)
|
||||
bcopy((caddr_t)u.u_pcb.pcb_fpctxp, (caddr_t)&corep->c_fpu,
|
||||
sizeof (struct fpu));
|
||||
}
|
||||
|
||||
fpu_ptrace(req, p, ipc)
|
||||
enum ptracereq req;
|
||||
struct proc *p;
|
||||
struct ipc *ipc;
|
||||
{
|
||||
|
||||
switch (req) {
|
||||
case PTRACE_GETFPREGS:
|
||||
runchild(p);
|
||||
if (copyout((caddr_t)&(ipc->ip_fpu), ipc->ip_addr,
|
||||
sizeof (struct fpu)) != 0) {
|
||||
ipc->ip_error = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case PTRACE_SETFPREGS:
|
||||
if (copyin(ipc->ip_addr, (caddr_t)&ipc->ip_fpu,
|
||||
sizeof (struct fpu)) != 0) {
|
||||
ipc->ip_error = 1;
|
||||
}
|
||||
runchild(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fpu_procxmt(req, ipc)
|
||||
enum ptracereq req;
|
||||
struct ipc *ipc;
|
||||
{
|
||||
register struct pcb *pcb = &u.u_pcb;
|
||||
extern int fpu_exists;
|
||||
extern struct fpu *fpu_ctxalloc();
|
||||
|
||||
switch (req) {
|
||||
case PTRACE_GETFPREGS:
|
||||
if (pcb->pcb_fpctxp)
|
||||
bcopy((caddr_t)pcb->pcb_fpctxp, (caddr_t)&ipc->ip_fpu,
|
||||
sizeof (struct fpu));
|
||||
break;
|
||||
|
||||
case PTRACE_SETFPREGS:
|
||||
if (fpu_exists && pcb->pcb_fpctxp) {
|
||||
register u_int i;
|
||||
for (i = 0; i < 32; i++) {
|
||||
_fp_write_pfreg((FPU_REGS_TYPE *)&ipc->ip_fpu.fpu_regs[i], i);
|
||||
}
|
||||
_fp_write_pfsr(&ipc->ip_fpu.fpu_fsr);
|
||||
} else {
|
||||
if (pcb->pcb_fpctxp == (struct fpu *) 0) {
|
||||
pcb->pcb_fpctxp = fpu_ctxalloc();
|
||||
}
|
||||
bcopy((caddr_t)&ipc->ip_fpu, (caddr_t)pcb->pcb_fpctxp,
|
||||
sizeof (struct fpu));
|
||||
pcb->pcb_fpflags &= ~FP_UNINITIALIZED;
|
||||
if (!fpu_exists)
|
||||
pcb->pcb_fpflags |= FP_DISABLE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
/*
|
||||
* Handle floaing point traps generated by simulation/emulation.
|
||||
*/
|
||||
void
|
||||
fp_traps(pfpsd, ftt, rp)
|
||||
fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
register enum ftt_type ftt; /* trap type */
|
||||
register struct regs *rp; /* ptr to regs fro trap */
|
||||
{
|
||||
extern void trap();
|
||||
extern u_int beval; /* beval is set in trap.c for
|
||||
specific architectures */
|
||||
|
||||
struct regs *saved_fptraprp = 0;
|
||||
|
||||
/*
|
||||
* If we take a user's exception in kernel mode, we want to trap
|
||||
* with the user's registers. Clear fptraprp in case we don't
|
||||
* return from trap.
|
||||
*/
|
||||
saved_fptraprp = fptraprp;
|
||||
if (fptraprp != 0) {
|
||||
rp = fptraprp;
|
||||
fptraprp = 0;
|
||||
}
|
||||
|
||||
switch (ftt) {
|
||||
case ftt_ieee:
|
||||
trap(T_FP_EXCEPTION, rp, pfpsd->fp_trapaddr,
|
||||
pfpsd->fp_trapcode, 0);
|
||||
break;
|
||||
case ftt_fault:
|
||||
trap(T_DATA_FAULT, rp, pfpsd->fp_trapaddr, beval, pfpsd->fp_traprw);
|
||||
break;
|
||||
case ftt_alignment:
|
||||
trap(T_ALIGNMENT, rp, pfpsd->fp_trapaddr, 0, 0);
|
||||
break;
|
||||
case ftt_unimplemented:
|
||||
trap(T_UNIMP_INSTR, rp, pfpsd->fp_trapaddr, 0, 0);
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* We don't expect any of the other types here.
|
||||
*/
|
||||
panic("fp_traps: bad ftt");
|
||||
}
|
||||
fptraprp = saved_fptraprp;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* floating point unit disabled:
|
||||
* One of two possibilities
|
||||
* 1. There is no fpu in the confguration. If so, emulate in SW.
|
||||
* 2. THere is a FPU in config., however, this is the first time the
|
||||
* process doing a fp op. If so, allocate a fpu context and
|
||||
* turn on the EF bit in the PSR and let the proc go.
|
||||
*/
|
||||
|
||||
fp_is_disabled(rp)
|
||||
|
||||
struct regs *rp;
|
||||
{
|
||||
struct fpu *fp;
|
||||
enum ftt_type ftt;
|
||||
|
||||
if (u.u_pcb.pcb_fpctxp == 0) {
|
||||
fp = fpu_ctxalloc(); /* allocate a fpu context */
|
||||
uunix->u_pcb.pcb_fpctxp = fp;
|
||||
}
|
||||
else
|
||||
fp = uunix->u_pcb.pcb_fpctxp;
|
||||
|
||||
fp_ctxp = fp;
|
||||
|
||||
if (fpu_exists) { /* there is a fpu, go ahead and enable the fpu */
|
||||
if (rp->r_psr&PSR_EF) {
|
||||
panic("fp_disabled: no FPU but EF bit set");
|
||||
/* NOT REACHED */
|
||||
}
|
||||
else {
|
||||
/* turn on enable fpu bit in the PSR and let the proc go */
|
||||
rp->r_psr |= PSR_EF;
|
||||
fp_enable(fp);
|
||||
}
|
||||
} else { /* no fpu, emulate the instruction */
|
||||
fp_simd_type fpsd;
|
||||
|
||||
flush_user_windows_to_stack();
|
||||
|
||||
if (ftt = fp_emulator(&fpsd, (fp_inst_type *)rp->r_pc,
|
||||
rp, (struct rwindow *)rp->r_sp,
|
||||
(struct fpu *) &fp->fpu_regs[0]))
|
||||
fp_traps(&fpsd, ftt, rp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Process the floating point queue
|
||||
*
|
||||
* Each entry in the floating point queue is processed in turn.
|
||||
* If processing an entry results in an exception fp_traps() is called to
|
||||
* handle the exception - this usually results in the generation of a signal
|
||||
* to be delivered to the user. There are 2 possible outcomes to this (note
|
||||
* that hardware generated signals cannot be held!):
|
||||
*
|
||||
* 1. If the signal is being ignored we continue to process the rest
|
||||
* of the entries in the queue.
|
||||
*
|
||||
* 2. If arrangements have been made for return to a user signal handler,
|
||||
* sendsig() will have copied the floating point queue onto the user's
|
||||
* signal stack and zero'ed the queue count in the u_pcb. Note that
|
||||
* this has the side effect of terminating fp_runq's processing loop.
|
||||
* We will re-run the floating point queue on return from the user
|
||||
* signal handler if necessary as part of normal setcontext processing.
|
||||
*/
|
||||
void
|
||||
fp_runq(rp)
|
||||
register struct regs *rp; /* ptr to regs for trap */
|
||||
{
|
||||
register struct fpu *fp = u.u_pcb.pcb_fpctxp;
|
||||
register struct fq *fqp = fp->fpu_q;
|
||||
fp_simd_type fpsd;
|
||||
extern int fpu_exists;
|
||||
|
||||
/*
|
||||
* don't preempt while manipulating the queue
|
||||
*/
|
||||
|
||||
while (fp->fpu_qcnt) {
|
||||
enum ftt_type fptrap;
|
||||
|
||||
fptrap = fpu_simulator((fp_simd_type *)&fpsd,
|
||||
(fp_inst_type *)fqp->FQu.fpq.addr,
|
||||
(fsr_type *)&fp->fpu_fsr,
|
||||
fqp->FQu.fpq.instr);
|
||||
if (fptrap) {
|
||||
/*
|
||||
* fpu_simulator uses the fp registers directly but it
|
||||
* uses the software copy of the fsr. We need to write
|
||||
* that back to fpu so that fpu's state is current for
|
||||
* ucontext.
|
||||
*/
|
||||
if (fpu_exists)
|
||||
_fp_write_pfsr(&fp->fpu_fsr);
|
||||
|
||||
/* post signal */
|
||||
fp_traps(&fpsd, fptrap, rp);
|
||||
|
||||
/*
|
||||
* Break from loop to allow signal to be sent.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
fp->fpu_qcnt--;
|
||||
fqp++;
|
||||
}
|
||||
|
||||
/*
|
||||
* fpu_simulator uses the fp registers directly, so we have
|
||||
* to update the pcb copies to keep current, but it uses the
|
||||
* software copy of the fsr, so we write that back to fpu
|
||||
*/
|
||||
if (fpu_exists) {
|
||||
register u_int i;
|
||||
|
||||
for (i = 0; i < 32; i++)
|
||||
_fp_read_pfreg((FPU_REGS_TYPE *)&fp->fpu_regs[i], i);
|
||||
_fp_write_pfsr((FPU_FSR_TYPE *)&fp->fpu_fsr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
247
sys/sparc/fpu/fpu_simulator.c
Normal file
247
sys/sparc/fpu/fpu_simulator.c
Normal file
@@ -0,0 +1,247 @@
|
||||
#ifdef sccsid
|
||||
static char sccsid[] = "@(#)fpu_simulator.c 1.1 94/10/31 SMI;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/* Main procedures for Sparc FPU simulator. */
|
||||
|
||||
#include <machine/fpu/fpu_simulator.h>
|
||||
#include <machine/fpu/globals.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/signal.h>
|
||||
|
||||
/* PUBLIC FUNCTIONS */
|
||||
|
||||
PRIVATE enum ftt_type
|
||||
_fp_fpu_simulator(pfpsd, inst, pfsr)
|
||||
fp_simd_type *pfpsd; /* Pointer to fpu simulotor data */
|
||||
fp_inst_type inst; /* FPU instruction to simulate. */
|
||||
fsr_type *pfsr; /* Pointer to image of FSR to read and write. */
|
||||
{
|
||||
unpacked us1, us2, ud; /* Unpacked operands and result. */
|
||||
unsigned nrs1, nrs2, nrd; /* Register number fields. */
|
||||
fsr_type fsr;
|
||||
unsigned usr;
|
||||
unsigned andexcep;
|
||||
enum fcc_type cc;
|
||||
|
||||
nrs1 = inst.rs1;
|
||||
nrs2 = inst.rs2;
|
||||
nrd = inst.rd;
|
||||
fsr = *pfsr;
|
||||
pfpsd->fp_current_exceptions = 0; /* Init current exceptions. */
|
||||
pfpsd->fp_fsrtem = fsr.tem; /* Obtain fsr's tem */
|
||||
pfpsd->fp_direction = fsr.rd; /* Obtain rounding direction. */
|
||||
pfpsd->fp_precision = fsr.rp; /* Obtain rounding precision. */
|
||||
switch (inst.opcode) {
|
||||
case fmovs:
|
||||
_fp_unpack_word(pfpsd, &usr, nrs2);
|
||||
_fp_pack_word(pfpsd, &usr, nrd);
|
||||
break;
|
||||
case fabss:
|
||||
_fp_unpack_word(pfpsd, &usr, nrs2);
|
||||
usr &= 0x7fffffff;
|
||||
_fp_pack_word(pfpsd, &usr, nrd);
|
||||
break;
|
||||
case fnegs:
|
||||
_fp_unpack_word(pfpsd, &usr, nrs2);
|
||||
usr ^= 0x80000000;
|
||||
_fp_pack_word(pfpsd, &usr, nrd);
|
||||
break;
|
||||
case fadd:
|
||||
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
|
||||
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
|
||||
_fp_add(pfpsd, &us1, &us2, &ud);
|
||||
_fp_pack(pfpsd, &ud, nrd, inst.prec);
|
||||
break;
|
||||
case fsub:
|
||||
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
|
||||
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
|
||||
_fp_sub(pfpsd, &us1, &us2, &ud);
|
||||
_fp_pack(pfpsd, &ud, nrd, inst.prec);
|
||||
break;
|
||||
case fmul:
|
||||
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
|
||||
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
|
||||
_fp_mul(pfpsd, &us1, &us2, &ud);
|
||||
_fp_pack(pfpsd, &ud, nrd, inst.prec);
|
||||
break;
|
||||
case fsmuld:
|
||||
case fdmulx:
|
||||
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
|
||||
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
|
||||
_fp_mul(pfpsd, &us1, &us2, &ud);
|
||||
_fp_pack(pfpsd, &ud, nrd, (enum fp_op_type) ((int)inst.prec+1));
|
||||
break;
|
||||
case fdiv:
|
||||
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
|
||||
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
|
||||
_fp_div(pfpsd, &us1, &us2, &ud);
|
||||
_fp_pack(pfpsd, &ud, nrd, inst.prec);
|
||||
break;
|
||||
case fcmp:
|
||||
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
|
||||
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
|
||||
cc = _fp_compare(pfpsd, &us1, &us2, 0);
|
||||
if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
|
||||
fsr.fcc = cc;
|
||||
break;
|
||||
case fcmpe:
|
||||
_fp_unpack(pfpsd, &us1, nrs1, inst.prec);
|
||||
_fp_unpack(pfpsd, &us2, nrs2, inst.prec);
|
||||
cc = _fp_compare(pfpsd, &us1, &us2, 1);
|
||||
if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
|
||||
fsr.fcc = cc;
|
||||
break;
|
||||
case fsqrt:
|
||||
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
|
||||
_fp_sqrt(pfpsd, &us1, &ud);
|
||||
_fp_pack(pfpsd, &ud, nrd, inst.prec);
|
||||
break;
|
||||
case ftoi:
|
||||
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
|
||||
pfpsd->fp_direction = fp_tozero;
|
||||
/* Force rounding toward zero. */
|
||||
_fp_pack(pfpsd, &us1, nrd, fp_op_integer);
|
||||
break;
|
||||
case ftos:
|
||||
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
|
||||
_fp_pack(pfpsd, &us1, nrd, fp_op_single);
|
||||
break;
|
||||
case ftod:
|
||||
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
|
||||
_fp_pack(pfpsd, &us1, nrd, fp_op_double);
|
||||
break;
|
||||
case ftox:
|
||||
_fp_unpack(pfpsd, &us1, nrs2, inst.prec);
|
||||
_fp_pack(pfpsd, &us1, nrd, fp_op_extended);
|
||||
break;
|
||||
default:
|
||||
return (ftt_unimplemented);
|
||||
}
|
||||
|
||||
fsr.cexc = pfpsd->fp_current_exceptions;
|
||||
if (pfpsd->fp_current_exceptions) { /* Exception(s) occurred. */
|
||||
andexcep = pfpsd->fp_current_exceptions & fsr.tem;
|
||||
if (andexcep != 0) { /* Signal an IEEE SIGFPE here. */
|
||||
if (andexcep & (1 << fp_invalid))
|
||||
pfpsd->fp_trapcode = FPE_FLTOPERR_TRAP;
|
||||
else if (andexcep & (1 << fp_overflow))
|
||||
pfpsd->fp_trapcode = FPE_FLTOVF_TRAP;
|
||||
else if (andexcep & (1 << fp_underflow))
|
||||
pfpsd->fp_trapcode = FPE_FLTUND_TRAP;
|
||||
else if (andexcep & (1 << fp_division))
|
||||
pfpsd->fp_trapcode = FPE_FLTDIV_TRAP;
|
||||
else if (andexcep & (1 << fp_inexact))
|
||||
pfpsd->fp_trapcode = FPE_FLTINEX_TRAP;
|
||||
else
|
||||
pfpsd->fp_trapcode = 0;
|
||||
*pfsr = fsr;
|
||||
return (ftt_ieee);
|
||||
} else { /* Just set accrued exception field. */
|
||||
fsr.aexc |= pfpsd->fp_current_exceptions;
|
||||
}
|
||||
}
|
||||
*pfsr = fsr;
|
||||
return (ftt_none);
|
||||
}
|
||||
|
||||
/*
|
||||
* fpu_simulator simulates FPU instructions only;
|
||||
* reads and writes FPU data registers directly.
|
||||
*/
|
||||
enum ftt_type
|
||||
fpu_simulator(pfpsd, pinst, pfsr, inst)
|
||||
fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
fp_inst_type *pinst; /* Address of FPU instruction to simulate. */
|
||||
fsr_type *pfsr; /* Pointer to image of FSR to read and write.*/
|
||||
unsigned long inst; /* The FPU instruction to simulate */
|
||||
{
|
||||
union {
|
||||
int i;
|
||||
fp_inst_type inst;
|
||||
} kluge;
|
||||
|
||||
kluge.i = inst;
|
||||
pfpsd->fp_trapaddr = (char *) pinst;
|
||||
pfpsd->fp_current_read_freg = _fp_read_pfreg;
|
||||
pfpsd->fp_current_write_freg = _fp_write_pfreg;
|
||||
return (_fp_fpu_simulator(pfpsd, kluge.inst, pfsr));
|
||||
}
|
||||
|
||||
/*
|
||||
* fp_emulator simulates FPU and CPU-FPU instructions; reads and writes FPU
|
||||
* data registers from image in pfpu.
|
||||
*/
|
||||
enum ftt_type
|
||||
fp_emulator(pfpsd, pinst, pregs, pwindow, pfpu)
|
||||
fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
fp_inst_type *pinst; /* Pointer to FPU instruction to simulate. */
|
||||
struct regs *pregs; /* Pointer to PCB image of registers. */
|
||||
struct rwindow *pwindow; /* Pointer to locals and ins. */
|
||||
struct fpu *pfpu; /* Pointer to FPU register block. */
|
||||
{
|
||||
union {
|
||||
int i;
|
||||
fp_inst_type inst;
|
||||
} kluge;
|
||||
enum ftt_type ftt;
|
||||
u_int tfsr;
|
||||
|
||||
tfsr = pfpu->fpu_fsr;
|
||||
pfpsd->fp_current_pfregs = pfpu;
|
||||
pfpsd->fp_current_read_freg = _fp_read_vfreg;
|
||||
pfpsd->fp_current_write_freg = _fp_write_vfreg;
|
||||
|
||||
pfpsd->fp_trapaddr = (char *) pinst; /* bad inst addr in case we trap */
|
||||
ftt = _fp_read_word((caddr_t) pinst, &(kluge.i), pfpsd);
|
||||
if (ftt != ftt_none)
|
||||
return (ftt);
|
||||
if ((kluge.inst.hibits == 2) &&
|
||||
((kluge.inst.op3 == 0x34) || (kluge.inst.op3 == 0x35))) {
|
||||
ftt = _fp_fpu_simulator(pfpsd, kluge.inst, (fsr_type *)&tfsr);
|
||||
pfpu->fpu_fsr = tfsr;
|
||||
/* Do not retry emulated instruction. */
|
||||
pregs->r_pc = pregs->r_npc;
|
||||
pregs->r_npc += 4;
|
||||
} else
|
||||
ftt = _fp_iu_simulator(pfpsd, kluge.inst, pregs, pwindow, pfpu);
|
||||
|
||||
if (ftt != ftt_none)
|
||||
return (ftt);
|
||||
|
||||
again:
|
||||
/*
|
||||
* now read next instruction and see if it can be emulated
|
||||
*/
|
||||
pinst = (fp_inst_type *)pregs->r_pc;
|
||||
pfpsd->fp_trapaddr = (char *) pinst; /* bad inst addr in case we trap */
|
||||
ftt = _fp_read_word((caddr_t) pinst, &(kluge.i), pfpsd);
|
||||
if (ftt != ftt_none)
|
||||
return (ftt);
|
||||
|
||||
|
||||
if ((kluge.inst.hibits == 2) && /* fpops */
|
||||
((kluge.inst.op3 == 0x34) || (kluge.inst.op3 == 0x35))) {
|
||||
ftt = _fp_fpu_simulator(pfpsd, kluge.inst, (fsr_type *)&tfsr);
|
||||
/* Do not retry emulated instruction. */
|
||||
pfpu->fpu_fsr = tfsr;
|
||||
pregs->r_pc = pregs->r_npc;
|
||||
pregs->r_npc += 4;
|
||||
} else if (
|
||||
/* fldst */
|
||||
((kluge.inst.hibits == 3) && ((kluge.inst.op3 & 0x38) == 0x20)) ||
|
||||
/* fbcc */
|
||||
((kluge.inst.hibits == 0) && (((kluge.i>>21) & 0x7) == 6))) {
|
||||
ftt = _fp_iu_simulator(pfpsd, kluge.inst, pregs, pwindow, pfpu);
|
||||
} else
|
||||
return (ftt);
|
||||
|
||||
if (ftt != ftt_none)
|
||||
return (ftt);
|
||||
else
|
||||
goto again;
|
||||
}
|
||||
193
sys/sparc/fpu/fpu_simulator.h
Normal file
193
sys/sparc/fpu/fpu_simulator.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/* @(#)fpu_simulator.h 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/* Sparc floating-point simulator PUBLIC include file. */
|
||||
|
||||
#include <machine/reg.h>
|
||||
#include <sys/ieeefp.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <vm/seg.h>
|
||||
|
||||
|
||||
/* PUBLIC TYPES */
|
||||
|
||||
enum fcc_type { /* relationships */
|
||||
fcc_equal = 0,
|
||||
fcc_less = 1,
|
||||
fcc_greater = 2,
|
||||
fcc_unordered = 3
|
||||
};
|
||||
|
||||
/* FSR types. */
|
||||
|
||||
enum ftt_type { /* types of traps */
|
||||
ftt_none = 0,
|
||||
ftt_ieee = 1,
|
||||
ftt_unfinished = 2,
|
||||
ftt_unimplemented = 3,
|
||||
ftt_sequence = 4,
|
||||
ftt_alignment = 5, /* defined by software convention only */
|
||||
ftt_fault = 6, /* defined by software convention only */
|
||||
ftt_7 = 7
|
||||
};
|
||||
|
||||
typedef struct { /* Sparc FSR. */
|
||||
enum fp_direction_type rd : 2; /* rounding direction */
|
||||
enum fp_precision_type rp : 2; /* rounding precision */
|
||||
unsigned tem : 5; /* trap enable mask */
|
||||
unsigned : 6;
|
||||
enum ftt_type ftt : 3; /* FPU trap type */
|
||||
unsigned qne : 1; /* FPQ not empty */
|
||||
unsigned pr : 1; /* partial result */
|
||||
enum fcc_type fcc : 2; /* FPU condition code */
|
||||
unsigned aexc : 5; /* accumulated exception */
|
||||
unsigned cexc : 5; /* current exception */
|
||||
} fsr_type;
|
||||
|
||||
typedef /* FPU register viewed as single components. */
|
||||
struct {
|
||||
unsigned sign : 1;
|
||||
unsigned exponent : 8;
|
||||
unsigned significand : 23;
|
||||
} single_type;
|
||||
|
||||
typedef /* FPU register viewed as double components. */
|
||||
struct {
|
||||
unsigned sign : 1;
|
||||
unsigned exponent : 11;
|
||||
unsigned significand : 20;
|
||||
} double_type;
|
||||
|
||||
typedef /* FPU register viewed as extended components. */
|
||||
struct {
|
||||
unsigned sign : 1;
|
||||
unsigned exponent : 15;
|
||||
unsigned significand : 16;
|
||||
} extended_type;
|
||||
|
||||
typedef /* FPU register with multiple data views. */
|
||||
union {
|
||||
int int_reg;
|
||||
long long_reg;
|
||||
unsigned unsigned_reg;
|
||||
float float_reg;
|
||||
single_type single_reg;
|
||||
double_type double_reg;
|
||||
extended_type extended_reg;
|
||||
} freg_type;
|
||||
|
||||
enum fp_op_type { /* Type specifiers in FPU instructions. */
|
||||
fp_op_integer = 0, /* Not in hardware, but convenient to define. */
|
||||
fp_op_single = 1,
|
||||
fp_op_double = 2,
|
||||
fp_op_extended = 3
|
||||
};
|
||||
|
||||
enum fp_opcode { /* FPU op codes, minus precision and leading 0. */
|
||||
fmovs = 0x0,
|
||||
fnegs = 0x1,
|
||||
fabss = 0x2,
|
||||
fp_op_3 = 3, fp_op_4 = 4, fp_op_5 = 5, fp_op_6 = 6, fp_op_7 = 7,
|
||||
fp_op_8 = 0x8,
|
||||
fp_op_9 = 0x9,
|
||||
fsqrt = 0xa,
|
||||
fp_op_b = 0xb, fp_op_c = 0xc, fp_op_d = 0xd,
|
||||
fp_op_e = 0xe, fp_op_f = 0xf,
|
||||
fadd = 0x10,
|
||||
fsub = 0x11,
|
||||
fmul = 0x12,
|
||||
fdiv = 0x13,
|
||||
fcmp = 0x14,
|
||||
fcmpe = 0x15,
|
||||
fp_op_16 = 0x16, fp_op_17 = 0x17,
|
||||
fp_op_18 = 0x18,
|
||||
fp_op_19 = 0x19,
|
||||
fsmuld = 0x1a,
|
||||
fdmulx = 0x1b,
|
||||
fp_op_20 = 0x20,
|
||||
fp_op_21 = 0x21,
|
||||
fp_op_22 = 0x22,
|
||||
fp_op_23 = 0x23,
|
||||
fp_op_24 = 0x24, fp_op_25 = 0x25, fp_op_26 = 0x26, fp_op_27 = 0x27,
|
||||
fp_op_28 = 0x28, fp_op_29 = 0x29, fp_op_2a = 0x2a, fp_op_2b = 0x2b,
|
||||
fp_op_2c = 0x2c, fp_op_2d = 0x2d, fp_op_2e = 0x2e, fp_op_2f = 0x2f,
|
||||
fp_op_30 = 0x30,
|
||||
ftos = 0x31,
|
||||
ftod = 0x32,
|
||||
ftox = 0x33,
|
||||
ftoi = 0x34,
|
||||
fp_op_35 = 0x35, fp_op_36 = 0x36, fp_op_37 = 0x37,
|
||||
ft_op_38 = 0x38,
|
||||
fp_op_39 = 0x39, fp_op_3a = 0x3a, fp_op_3b = 0x3b,
|
||||
fp_op_3c = 0x3c,
|
||||
fp_op_3d = 0x3d, fp_op_3e = 0x3e, fp_op_3f = 0x3f
|
||||
};
|
||||
|
||||
typedef /* FPU instruction. */
|
||||
struct {
|
||||
unsigned hibits : 2; /* Top two bits. */
|
||||
unsigned rd : 5; /* Destination. */
|
||||
unsigned op3 : 6; /* Main op code. */
|
||||
unsigned rs1 : 5; /* First operand. */
|
||||
unsigned ibit : 1; /* I format bit. */
|
||||
enum fp_opcode opcode : 6; /* Floating-point op code. */
|
||||
enum fp_op_type prec : 2; /* Precision. */
|
||||
unsigned rs2 : 5; /* Second operand. */
|
||||
} fp_inst_type;
|
||||
|
||||
typedef /* FPU data used by simulator. */
|
||||
struct {
|
||||
unsigned fp_fsrtem;
|
||||
enum fp_direction_type fp_direction;
|
||||
enum fp_precision_type fp_precision;
|
||||
unsigned fp_current_exceptions;
|
||||
struct fpu * fp_current_pfregs;
|
||||
void (* fp_current_read_freg) ();
|
||||
void (* fp_current_write_freg) ();
|
||||
int fp_trapcode;
|
||||
char *fp_trapaddr;
|
||||
struct regs *fp_traprp;
|
||||
enum seg_rw fp_traprw;
|
||||
} fp_simd_type;
|
||||
|
||||
/* PUBLIC FUNCTIONS */
|
||||
|
||||
|
||||
extern enum ftt_type fpu_simulator(/* pfpsd, pinst, pfsr, instr */);
|
||||
/* fp_simd_type *pfpsd; Pointer to FPU simulator data */
|
||||
/* fp_inst_type *pinst; Pointer to FPU instruction to simulate. */
|
||||
/* fsr_type *pfsr; Pointer to image of FSR to read and write. */
|
||||
/* int instr; Instruction to emulate. */
|
||||
|
||||
/*
|
||||
* fpu_simulator simulates FPU instructions only; reads and writes FPU data
|
||||
* registers directly.
|
||||
*/
|
||||
|
||||
extern enum ftt_type fp_emulator (/* pfpsd, pinst, pregs, pwindow, pfpu */);
|
||||
/* fp_simd_type *pfpsd; Pointer to FPU simulator data */
|
||||
/* fp_inst_type *pinst; Pointer to FPU instruction to simulate. */
|
||||
/* struct regs *pregs; Pointer to PCB image of registers. */
|
||||
/* struct rwindow *pwindow; Pointer to locals and ins. */
|
||||
/* struct fpu *pfpu; Pointer to FPU register block. */
|
||||
|
||||
/*
|
||||
* fp_emulator simulates FPU and CPU-FPU instructions; reads and writes FPU
|
||||
* data registers from image in pfpu.
|
||||
*/
|
||||
|
||||
extern void fp_traps(/* pfpsd, ftt, rp */);
|
||||
/* fp_simd_type *pfpsd; Pointer to FPU simulator data */
|
||||
/* enum ftt_type ftt; Type of trap. */
|
||||
/* struct regs *rp; Pointer to PCB image of registers. */
|
||||
/*
|
||||
* fp_traps handles passing execption conditions to the kernel.
|
||||
* It is called after fp_simulator or fp_emulator fail (return a non-zero ftt).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
128
sys/sparc/fpu/globals.h
Normal file
128
sys/sparc/fpu/globals.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/* @(#)globals.h 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/* Sparc floating-point simulator PRIVATE include file. */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <vm/seg.h>
|
||||
|
||||
/* PRIVATE CONSTANTS */
|
||||
|
||||
/* PRIVATE CONSTANTS */
|
||||
|
||||
#define INTEGER_BIAS 31
|
||||
#define SINGLE_BIAS 127
|
||||
#define DOUBLE_BIAS 1023
|
||||
#define EXTENDED_BIAS 16383
|
||||
|
||||
/* PRIVATE TYPES */
|
||||
|
||||
#ifdef DEBUG
|
||||
#define PRIVATE
|
||||
#else
|
||||
#define PRIVATE static
|
||||
#endif
|
||||
|
||||
#define DOUBLE_E(n) (n & 0xfffe) /* More significant word of double. */
|
||||
#define DOUBLE_F(n) (1+DOUBLE_E(n)) /* Less significant word of double. */
|
||||
#define EXTENDED_E(n) (n & 0xfffc) /* Sign/exponent/significand of extended. */
|
||||
#define EXTENDED_F(n) (1+EXTENDED_E(n)) /* 2nd word of extended significand. */
|
||||
#define EXTENDED_G(n) (2+EXTENDED_E(n)) /* 3rd word of extended significand. */
|
||||
#define EXTENDED_H(n) (3+EXTENDED_E(n)) /* 4th word of extended significand. */
|
||||
|
||||
|
||||
typedef struct {
|
||||
int sign;
|
||||
enum fp_class_type fpclass;
|
||||
int exponent; /* Unbiased exponent */
|
||||
unsigned significand[4]; /* Four significand word . */
|
||||
int rounded; /* rounded bit */
|
||||
int sticky; /* stick bit */
|
||||
} unpacked;
|
||||
|
||||
struct regs *fptraprp; /* Users regs for syncfpu() */
|
||||
|
||||
/* PRIVATE FUNCTIONS */
|
||||
extern void _fp_read_pfreg(/* pf, n */);
|
||||
/* FPU_REGS_TYPE *pf Where to put current %fn. */
|
||||
/* unsigned n; Want to read register n. */
|
||||
|
||||
extern void _fp_write_pfreg(/* pf, n */);
|
||||
/* FPU_REGS_TYPE *pf Where to get new %fn. */
|
||||
/* unsigned n; Want to read register n. */
|
||||
|
||||
/* vfreg routines use "virtual" FPU registers at *_fp_current_pfregs. */
|
||||
|
||||
extern void _fp_read_vfreg(/* pf, n, pfpsd */);
|
||||
/* FPU_REGS_TYPE *pf Where to put current %fn. */
|
||||
/* unsigned n; Want to read register n. */
|
||||
|
||||
extern void _fp_write_vfreg(/* pf, n, pfpsd */);
|
||||
/* FPU_REGS_TYPE *pf Where to get new %fn. */
|
||||
/* unsigned n; Want to read register n. */
|
||||
|
||||
extern enum ftt_type _fp_iu_simulator(/* pinst, pregs, pwindow, pfpu */);
|
||||
/* fp_inst_type pinst; FPU instruction to simulate. */
|
||||
/* struct regs *pregs; Pointer to PCB image of registers. */
|
||||
/* struct rwindow *pwindow; Pointer to locals and ins. */
|
||||
/* struct fpu *pfpu; Pointer to FPU register block. */
|
||||
|
||||
extern void _fp_unpack(/* pu, n, type */);
|
||||
/* unpacked *pu; unpacked result */
|
||||
/* unsigned n; register where data starts */
|
||||
/* fp_op_type type; type of datum */
|
||||
|
||||
extern void _fp_pack(/* pu, n, type */);
|
||||
/* unpacked *pu; unpacked result */
|
||||
/* unsigned n; register where data starts */
|
||||
/* fp_op_type type; type of datum */
|
||||
|
||||
extern void _fp_unpack_word(/* pu, n */);
|
||||
/* unsigned *pu; unpacked result */
|
||||
/* unsigned n; register where data starts */
|
||||
|
||||
extern void _fp_pack_word(/* pu, n, type */);
|
||||
/* unsigned *pu; unpacked result */
|
||||
/* unsigned n; register where data starts */
|
||||
|
||||
extern void fpu_normalize(/* pu */);
|
||||
/* unpacked *pu; unpacked operand and result */
|
||||
|
||||
extern void fpu_rightshift(/* pu, n */);
|
||||
/* unpacked *pu; unsigned n; */
|
||||
/* Right shift significand sticky by n bits. */
|
||||
|
||||
extern void fpu_set_exception(/* ex */);
|
||||
/* enum fp_exception_type ex; exception to be set in curexcep */
|
||||
|
||||
extern void fpu_error_nan(/* pu */);
|
||||
/* unpacked *pu; Set invalid exception and error nan in *pu */
|
||||
|
||||
extern void unpacksingle(/* pfpsd, pu, x */);
|
||||
/* fp_simd_type *pfpsd; simulator data */
|
||||
/* unpacked *pu; unpacked result */
|
||||
/* single_type x; packed single */
|
||||
|
||||
extern void unpackdouble(/* pfpsd, pu, x, y */);
|
||||
/* fp_simd_type *pfpsd; simulator data */
|
||||
/* unpacked *pu; unpacked result */
|
||||
/* double_type x; packed double */
|
||||
/* unsigned y; */
|
||||
|
||||
|
||||
extern enum fcc_type _fp_compare(/* px, py */);
|
||||
|
||||
extern void _fp_add(/* pfpsd, px, py, pz */);
|
||||
extern void _fp_sub(/* pfpsd, px, py, pz */);
|
||||
extern void _fp_mul(/* pfpsd, px, py, pz */);
|
||||
extern void _fp_div(/* pfpsd, px, py, pz */);
|
||||
extern void _fp_sqrt(/* pfpsd, px, pz */);
|
||||
|
||||
extern enum ftt_type _fp_write_word(/* caddr_t, value */);
|
||||
extern enum ftt_type _fp_read_word(/* caddr_t, pvalue */);
|
||||
extern enum ftt_type read_iureg(/* n, pregs, pwindow, pvalue */);
|
||||
|
||||
|
||||
66
sys/sparc/fpu/ieee.h
Normal file
66
sys/sparc/fpu/ieee.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* @(#)ieee.h 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1987 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/* Sparc IEEE floating-point support PUBLIC include file. */
|
||||
|
||||
/* PUBLIC CONSTANTS */
|
||||
|
||||
/* PUBLIC TYPES */
|
||||
|
||||
/* IEEE Arithmetic types... numbered to correspond to fsr fields. */
|
||||
|
||||
enum fp_rounding_direction /* rounding direction */
|
||||
{
|
||||
fp_rd_nearest = 0,
|
||||
fp_rd_zero = 1,
|
||||
fp_rd_plus = 2,
|
||||
fp_rd_minus = 3
|
||||
} ;
|
||||
|
||||
enum fp_rounding_precision /* extended rounding precision */
|
||||
{
|
||||
fp_rp_extended = 0,
|
||||
fp_rp_single = 1,
|
||||
fp_rp_double = 2,
|
||||
fp_rp_3 = 3
|
||||
} ;
|
||||
|
||||
enum fp_exception_type /* exceptions according to cexc bit number */
|
||||
{
|
||||
fp_inexact = 0,
|
||||
fp_divide = 1,
|
||||
fp_underflow = 2,
|
||||
fp_overflow = 3,
|
||||
fp_invalid = 4
|
||||
} ;
|
||||
|
||||
enum fp_class_type /* floating-point classes according to fclass */
|
||||
{
|
||||
fp_zero = 0,
|
||||
fp_normal = 1, /* Includes subnormal. */
|
||||
fp_infinity = 2,
|
||||
fp_nan = 3, /* Includes quiet and signaling NaN. */
|
||||
} ;
|
||||
|
||||
/* PUBLIC GLOBAL VARIABLES */
|
||||
|
||||
unsigned fp_accrued_exceptions ; /* Sticky accumulated exceptions. */
|
||||
|
||||
/* PUBLIC FUNCTIONS */
|
||||
|
||||
extern enum fp_rounding_direction swap_rounding_direction( /* rd */ ) ;
|
||||
/*
|
||||
extern enum fp_rounding_direction rd ;
|
||||
|
||||
/* Change rounding mode; return previous. */
|
||||
|
||||
extern int swap_accrued_exceptions ( /* x */ ) ;
|
||||
/*
|
||||
int x ;
|
||||
|
||||
/* Change accrued exceptions ; return previous. */
|
||||
|
||||
|
||||
241
sys/sparc/fpu/iu_simulator.c
Normal file
241
sys/sparc/fpu/iu_simulator.c
Normal file
@@ -0,0 +1,241 @@
|
||||
#ifdef sccsid
|
||||
static char sccsid[] = "@(#)iu_simulator.c 1.1 94/10/31 SMI";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/* Integer Unit simulator for Sparc FPU simulator. */
|
||||
|
||||
#include <machine/fpu/fpu_simulator.h>
|
||||
#include <machine/fpu/globals.h>
|
||||
|
||||
#define FPU_REG_FIELD unsigned_reg /* Coordinate with FPU_REGS_TYPE. */
|
||||
#define FPU_FSR_FIELD unsigned_reg /* Coordinate with FPU_FSR_TYPE. */
|
||||
|
||||
PRIVATE enum ftt_type
|
||||
fbcc(pinst, pregs, pfpu)
|
||||
fp_inst_type pinst; /* FPU instruction to simulate. */
|
||||
struct regs *pregs; /* Pointer to PCB image of registers. */
|
||||
struct fpu *pfpu; /* Pointer to FPU register block. */
|
||||
|
||||
{
|
||||
union {
|
||||
fsr_type fsr;
|
||||
long i;
|
||||
} klugefsr;
|
||||
union {
|
||||
fp_inst_type fi;
|
||||
int i;
|
||||
} kluge;
|
||||
enum fcc_type fcc;
|
||||
enum icc_type {
|
||||
fbn, fbne, fblg, fbul, fbl, fbug, fbg, fbu,
|
||||
fba, fbe, fbue, fbge, fbuge, fble, fbule, fbo
|
||||
} icc;
|
||||
|
||||
unsigned annul, takeit;
|
||||
|
||||
klugefsr.i = pfpu->fpu_fsr;
|
||||
fcc = klugefsr.fsr.fcc;
|
||||
icc = (enum icc_type) (pinst.rd & 0xf);
|
||||
annul = pinst.rd & 0x10;
|
||||
|
||||
switch (icc) {
|
||||
case fbn:
|
||||
takeit = 0;
|
||||
break;
|
||||
case fbl:
|
||||
takeit = fcc == fcc_less;
|
||||
break;
|
||||
case fbg:
|
||||
takeit = fcc == fcc_greater;
|
||||
break;
|
||||
case fbu:
|
||||
takeit = fcc == fcc_unordered;
|
||||
break;
|
||||
case fbe:
|
||||
takeit = fcc == fcc_equal;
|
||||
break;
|
||||
case fblg:
|
||||
takeit = (fcc == fcc_less) || (fcc == fcc_greater);
|
||||
break;
|
||||
case fbul:
|
||||
takeit = (fcc == fcc_unordered) || (fcc == fcc_less);
|
||||
break;
|
||||
case fbug:
|
||||
takeit = (fcc == fcc_unordered) || (fcc == fcc_greater);
|
||||
break;
|
||||
case fbue:
|
||||
takeit = (fcc == fcc_unordered) || (fcc == fcc_equal);
|
||||
break;
|
||||
case fbge:
|
||||
takeit = (fcc == fcc_greater) || (fcc == fcc_equal);
|
||||
break;
|
||||
case fble:
|
||||
takeit = (fcc == fcc_less) || (fcc == fcc_equal);
|
||||
break;
|
||||
case fbne:
|
||||
takeit = fcc != fcc_equal;
|
||||
break;
|
||||
case fbuge:
|
||||
takeit = fcc != fcc_less;
|
||||
break;
|
||||
case fbule:
|
||||
takeit = fcc != fcc_greater;
|
||||
break;
|
||||
case fbo:
|
||||
takeit = fcc != fcc_unordered;
|
||||
break;
|
||||
case fba:
|
||||
takeit = 1;
|
||||
break;
|
||||
}
|
||||
if (takeit) { /* Branch taken. */
|
||||
int tpc;
|
||||
|
||||
kluge.fi = pinst;
|
||||
tpc = pregs->r_pc;
|
||||
if (annul && (icc == fba)) { /* fba,a is wierd */
|
||||
pregs->r_pc = tpc + (int) ((kluge.i << 10) >> 8);
|
||||
pregs->r_npc = pregs->r_pc + 4;
|
||||
} else {
|
||||
pregs->r_pc = pregs->r_npc;
|
||||
pregs->r_npc = tpc + (int) ((kluge.i << 10) >> 8);
|
||||
}
|
||||
} else { /* Branch not taken. */
|
||||
if (annul) { /* Annul next instruction. */
|
||||
pregs->r_pc = pregs->r_npc + 4;
|
||||
pregs->r_npc += 8;
|
||||
} else { /* Execute next instruction. */
|
||||
pregs->r_pc = pregs->r_npc;
|
||||
pregs->r_npc += 4;
|
||||
}
|
||||
}
|
||||
return (ftt_none);
|
||||
}
|
||||
|
||||
/*
|
||||
* Simulator for loads and stores between floating-point unit and memory.
|
||||
*/
|
||||
PRIVATE enum ftt_type
|
||||
fldst(pfpsd, pinst, pregs, pwindow, pfpu)
|
||||
fp_simd_type *pfpsd; /* FPU simulator data. */
|
||||
fp_inst_type pinst; /* FPU instruction to simulate. */
|
||||
struct regs *pregs; /* Pointer to PCB image of registers. */
|
||||
struct rwindow *pwindow; /* Pointer to locals and ins. */
|
||||
struct fpu *pfpu; /* Pointer to FPU register block. */
|
||||
{
|
||||
unsigned nrs1, nrs2, nrd; /* Register number fields. */
|
||||
freg_type f;
|
||||
int ea;
|
||||
int i;
|
||||
union {
|
||||
fp_inst_type fi;
|
||||
int i;
|
||||
} kluge;
|
||||
enum ftt_type ftt;
|
||||
|
||||
nrs1 = pinst.rs1;
|
||||
nrs2 = pinst.rs2;
|
||||
nrd = pinst.rd;
|
||||
if (pinst.ibit == 0) { /* effective address = rs1 + rs2 */
|
||||
if (read_iureg(nrs1, pregs,
|
||||
(struct rwindow *)pwindow, &ea, pfpsd))
|
||||
return (ftt_fault);
|
||||
if (read_iureg(nrs2, pregs,
|
||||
(struct rwindow *)pwindow, &i, pfpsd))
|
||||
return (ftt_fault);
|
||||
ea += i;
|
||||
} else { /* effective address = rs1 + imm13 */
|
||||
kluge.fi = pinst;
|
||||
ea = (kluge.i << 19) >> 19; /* Extract simm13 field. */
|
||||
if (read_iureg(nrs1, pregs,
|
||||
(struct rwindow *)pwindow, &i, pfpsd))
|
||||
return (ftt_fault);
|
||||
ea += i;
|
||||
}
|
||||
|
||||
pfpsd->fp_trapaddr = (char *) ea; /* setup bad addr in case we trap */
|
||||
switch (pinst.op3 & 7) {
|
||||
case 0: /* LDF */
|
||||
ftt = _fp_read_word((caddr_t) ea, &(f.int_reg), pfpsd);
|
||||
if (ftt != ftt_none)
|
||||
return (ftt);
|
||||
pfpu->fpu_regs[nrd] = f.FPU_REG_FIELD;
|
||||
break;
|
||||
case 1: /* LDFSR */
|
||||
ftt = _fp_read_word((caddr_t) ea, &(f.int_reg), pfpsd);
|
||||
if (ftt != ftt_none)
|
||||
return (ftt);
|
||||
pfpu->fpu_fsr = f.FPU_FSR_FIELD;
|
||||
break;
|
||||
case 3: /* LDDF */
|
||||
if ((ea & 0x7) != 0)
|
||||
return (ftt_alignment); /* Require double-alignment. */
|
||||
ftt = _fp_read_word((caddr_t) ea, &(f.int_reg), pfpsd);
|
||||
if (ftt != ftt_none)
|
||||
return (ftt);
|
||||
pfpu->fpu_regs[DOUBLE_E(nrd)] = f.FPU_REG_FIELD;
|
||||
ftt = _fp_read_word((caddr_t) (ea + 4), &(f.int_reg), pfpsd);
|
||||
if (ftt != ftt_none)
|
||||
return (ftt);
|
||||
pfpu->fpu_regs[DOUBLE_F(nrd)] = f.FPU_REG_FIELD;
|
||||
break;
|
||||
case 4: /* STF */
|
||||
f.FPU_REG_FIELD = pfpu->fpu_regs[nrd];
|
||||
ftt = _fp_write_word((caddr_t) ea, f.int_reg, pfpsd);
|
||||
if (ftt != ftt_none)
|
||||
return (ftt);
|
||||
break;
|
||||
case 5: /* STFSR */
|
||||
f.FPU_FSR_FIELD = pfpu->fpu_fsr;
|
||||
f.FPU_FSR_FIELD &= ~0x301000; /* Clear reserved bits. */
|
||||
f.FPU_FSR_FIELD |= 0x0E0000; /* Set version number=7 . */
|
||||
ftt = _fp_write_word((caddr_t) ea, f.int_reg, pfpsd);
|
||||
if (ftt != ftt_none)
|
||||
return (ftt);
|
||||
break;
|
||||
case 7: /* STDF */
|
||||
if ((ea & 0x7) != 0)
|
||||
return (ftt_alignment); /* Require double-alignment. */
|
||||
f.FPU_REG_FIELD = pfpu->fpu_regs[DOUBLE_E(nrd)];
|
||||
ftt = _fp_write_word((caddr_t) ea, f.int_reg, pfpsd);
|
||||
if (ftt != ftt_none)
|
||||
return (ftt);
|
||||
f.FPU_REG_FIELD = pfpu->fpu_regs[DOUBLE_F(nrd)];
|
||||
ftt = _fp_write_word((caddr_t) (ea + 4), f.int_reg, pfpsd);
|
||||
if (ftt != ftt_none)
|
||||
return (ftt);
|
||||
break;
|
||||
default:
|
||||
/* addr of unimp inst */
|
||||
pfpsd->fp_trapaddr = (char *) pregs->r_pc;
|
||||
return (ftt_unimplemented);
|
||||
}
|
||||
|
||||
pregs->r_pc = pregs->r_npc; /* Do not retry emulated instruction. */
|
||||
pregs->r_npc += 4;
|
||||
return (ftt_none);
|
||||
}
|
||||
|
||||
/* PUBLIC FUNCTIONS */
|
||||
|
||||
enum ftt_type
|
||||
_fp_iu_simulator(pfpsd, pinst, pregs, pwindow, pfpu)
|
||||
fp_simd_type *pfpsd; /* FPU simulator data. */
|
||||
fp_inst_type pinst; /* FPU instruction to simulate. */
|
||||
struct regs *pregs; /* Pointer to PCB image of registers. */
|
||||
struct rwindow *pwindow; /* Pointer to locals and ins. */
|
||||
struct fpu *pfpu; /* Pointer to FPU register block. */
|
||||
{
|
||||
switch (pinst.hibits) {
|
||||
case 0:
|
||||
return (fbcc(pinst, pregs, pfpu));
|
||||
case 3:
|
||||
return (fldst(pfpsd, pinst, pregs, pwindow, pfpu));
|
||||
default:
|
||||
return (ftt_unimplemented);
|
||||
}
|
||||
}
|
||||
174
sys/sparc/fpu/mul.c
Normal file
174
sys/sparc/fpu/mul.c
Normal file
@@ -0,0 +1,174 @@
|
||||
#ifdef sccsid
|
||||
static char sccsid[] = "@(#)mul.c 1.1 94/10/31 SMI";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
#include <machine/fpu/fpu_simulator.h>
|
||||
#include <machine/fpu/globals.h>
|
||||
|
||||
#ifdef sun4m
|
||||
extern int ross625_flag; /* to identify hyperSPARC */
|
||||
#endif sun4m
|
||||
|
||||
void
|
||||
_fp_mul(pfpsd, px, py, pz)
|
||||
fp_simd_type *pfpsd;
|
||||
unpacked *px, *py, *pz;
|
||||
|
||||
{
|
||||
unpacked *pt;
|
||||
unsigned acc[4]; /* Product accumulator. */
|
||||
unsigned j, y, *x, s, r, c;
|
||||
|
||||
#ifdef sun4m
|
||||
if (((int) px->fpclass < (int) py->fpclass) ||
|
||||
(ross625_flag && ((int) px->fpclass <= (int) py->fpclass)))
|
||||
#else sun4m
|
||||
if ((int) px->fpclass < (int) py->fpclass)
|
||||
#endif sun4m
|
||||
{
|
||||
pt = px;
|
||||
px = py;
|
||||
py = pt;
|
||||
}
|
||||
/* Now class(x) >= class(y). */
|
||||
|
||||
*pz = *px;
|
||||
if ((int)pz->fpclass < (int)fp_quiet) pz->sign = px->sign ^ py->sign;
|
||||
|
||||
switch (px->fpclass) {
|
||||
case fp_quiet:
|
||||
case fp_signaling:
|
||||
case fp_zero:
|
||||
return;
|
||||
case fp_infinity:
|
||||
if (py->fpclass == fp_zero) {
|
||||
fpu_error_nan(pfpsd, pz);
|
||||
pz->fpclass = fp_quiet;
|
||||
}
|
||||
return;
|
||||
case fp_normal:
|
||||
if (py->fpclass == fp_zero) {
|
||||
pz->fpclass = fp_zero;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now x and y are both normal or subnormal. */
|
||||
|
||||
x = px->significand; /* save typing */
|
||||
|
||||
s=r=acc[0]=acc[1]=acc[2]=acc[3]=0; /* intialize acc to zero */
|
||||
|
||||
y = py->significand[3]; /* py->significand[3] * x */
|
||||
if (y!=0) {
|
||||
j=1;
|
||||
do {
|
||||
s |= r; /* shift acc right one bit */
|
||||
r = acc[3]&1;
|
||||
acc[3] = ((acc[2]&1)<<31)|(acc[3]>>1);
|
||||
acc[2] = ((acc[1]&1)<<31)|(acc[2]>>1);
|
||||
acc[1] = ((acc[0]&1)<<31)|(acc[1]>>1);
|
||||
acc[0] = (acc[0]>>1);
|
||||
if (j&y) { /* bit i of y != 0, add x to acc */
|
||||
c = 0;
|
||||
c = fpu_add3wc(&acc[3], acc[3], x[3], c);
|
||||
c = fpu_add3wc(&acc[2], acc[2], x[2], c);
|
||||
c = fpu_add3wc(&acc[1], acc[1], x[1], c);
|
||||
c = fpu_add3wc(&acc[0], acc[0], x[0], c);
|
||||
}
|
||||
j += j;
|
||||
} while (j!=0);
|
||||
}
|
||||
|
||||
y = py->significand[2]; /* py->significand[2] * x */
|
||||
if (y!=0) {
|
||||
j=1;
|
||||
do {
|
||||
s |= r; /* shift acc right one bit */
|
||||
r = acc[3]&1;
|
||||
acc[3] = ((acc[2]&1)<<31)|(acc[3]>>1);
|
||||
acc[2] = ((acc[1]&1)<<31)|(acc[2]>>1);
|
||||
acc[1] = ((acc[0]&1)<<31)|(acc[1]>>1);
|
||||
acc[0] = (acc[0]>>1);
|
||||
if (j&y) { /* bit i of y != 0, add x to acc */
|
||||
c = 0;
|
||||
c = fpu_add3wc(&acc[3], acc[3], x[3], c);
|
||||
c = fpu_add3wc(&acc[2], acc[2], x[2], c);
|
||||
c = fpu_add3wc(&acc[1], acc[1], x[1], c);
|
||||
c = fpu_add3wc(&acc[0], acc[0], x[0], c);
|
||||
}
|
||||
j += j;
|
||||
} while (j!=0);
|
||||
} else {
|
||||
s |= r|(acc[3]&0x7fffffff);
|
||||
r = (acc[3]&0x80000000)>>31;
|
||||
acc[3]=acc[2]; acc[2]=acc[1]; acc[1]=acc[0]; acc[0]=0;
|
||||
}
|
||||
|
||||
y = py->significand[1]; /* py->significand[1] * x */
|
||||
if (y!=0) {
|
||||
j=1;
|
||||
do {
|
||||
s |= r; /* shift acc right one bit */
|
||||
r = acc[3]&1;
|
||||
acc[3] = ((acc[2]&1)<<31)|(acc[3]>>1);
|
||||
acc[2] = ((acc[1]&1)<<31)|(acc[2]>>1);
|
||||
acc[1] = ((acc[0]&1)<<31)|(acc[1]>>1);
|
||||
acc[0] = (acc[0]>>1);
|
||||
if (j&y) { /* bit i of y != 0, add x to acc */
|
||||
c = 0;
|
||||
c = fpu_add3wc(&acc[3], acc[3], x[3], c);
|
||||
c = fpu_add3wc(&acc[2], acc[2], x[2], c);
|
||||
c = fpu_add3wc(&acc[1], acc[1], x[1], c);
|
||||
c = fpu_add3wc(&acc[0], acc[0], x[0], c);
|
||||
}
|
||||
j += j;
|
||||
} while (j!=0);
|
||||
} else {
|
||||
s |= r|(acc[3]&0x7fffffff);
|
||||
r = (acc[3]&0x80000000)>>31;
|
||||
acc[3]=acc[2]; acc[2]=acc[1]; acc[1]=acc[0]; acc[0]=0;
|
||||
}
|
||||
|
||||
/* py->significand[0] * x */
|
||||
y = py->significand[0]; /* y is of form 0x0001???? */
|
||||
j=1;
|
||||
do {
|
||||
s |= r; /* shift acc right one bit */
|
||||
r = acc[3]&1;
|
||||
acc[3] = ((acc[2]&1)<<31)|(acc[3]>>1);
|
||||
acc[2] = ((acc[1]&1)<<31)|(acc[2]>>1);
|
||||
acc[1] = ((acc[0]&1)<<31)|(acc[1]>>1);
|
||||
acc[0] = (acc[0]>>1);
|
||||
if (j&y) { /* bit i of y != 0, add x to acc */
|
||||
c = 0;
|
||||
c = fpu_add3wc(&acc[3], acc[3], x[3], c);
|
||||
c = fpu_add3wc(&acc[2], acc[2], x[2], c);
|
||||
c = fpu_add3wc(&acc[1], acc[1], x[1], c);
|
||||
c = fpu_add3wc(&acc[0], acc[0], x[0], c);
|
||||
}
|
||||
j += j;
|
||||
} while (j<=y);
|
||||
|
||||
if (acc[0]>=0x20000) { /* right shift one bit to normalize */
|
||||
pz->exponent = px->exponent + py->exponent + 1;
|
||||
pz->sticky = s|r;
|
||||
pz->rounded = acc[3]&1;
|
||||
pz->significand[3]=((acc[2]&1)<<31)|(acc[3]>>1);
|
||||
pz->significand[2]=((acc[1]&1)<<31)|(acc[2]>>1);
|
||||
pz->significand[1]=((acc[0]&1)<<31)|(acc[1]>>1);
|
||||
pz->significand[0]=(acc[0]>>1);
|
||||
} else {
|
||||
pz->exponent = px->exponent + py->exponent;
|
||||
pz->sticky = s;
|
||||
pz->rounded = r;
|
||||
pz->significand[3]=acc[3];
|
||||
pz->significand[2]=acc[2];
|
||||
pz->significand[1]=acc[1];
|
||||
pz->significand[0]=acc[0];
|
||||
}
|
||||
}
|
||||
479
sys/sparc/fpu/pack.c
Normal file
479
sys/sparc/fpu/pack.c
Normal file
@@ -0,0 +1,479 @@
|
||||
#ifdef sccsid
|
||||
static char sccsid[] = "@(#)pack.c 1.1 94/10/31 SMI";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/* Pack procedures for Sparc FPU simulator. */
|
||||
|
||||
#include <machine/fpu/fpu_simulator.h>
|
||||
#include <machine/fpu/globals.h>
|
||||
|
||||
/*
|
||||
* Returns 1 if overflow should go to infinity, 0 if to max finite.
|
||||
*/
|
||||
PRIVATE int
|
||||
overflow_to_infinity(pfpsd, sign)
|
||||
fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
int sign;
|
||||
{
|
||||
int inf;
|
||||
|
||||
switch (pfpsd->fp_direction) {
|
||||
case fp_nearest:
|
||||
inf = 1;
|
||||
break;
|
||||
case fp_tozero:
|
||||
inf = 0;
|
||||
break;
|
||||
case fp_positive:
|
||||
inf = !sign;
|
||||
break;
|
||||
case fp_negative:
|
||||
inf = sign;
|
||||
break;
|
||||
}
|
||||
return (inf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Round according to current rounding mode.
|
||||
*/
|
||||
PRIVATE void
|
||||
round(pfpsd, pu)
|
||||
register fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
register unpacked *pu;
|
||||
{
|
||||
int increment; /* boolean to indicate round up */
|
||||
int sr;
|
||||
|
||||
sr = pu->sticky|pu->rounded;
|
||||
|
||||
if (sr == 0)
|
||||
return;
|
||||
fpu_set_exception(pfpsd, fp_inexact);
|
||||
switch (pfpsd->fp_direction) {
|
||||
case fp_nearest:
|
||||
increment = pu->rounded;
|
||||
break;
|
||||
case fp_tozero:
|
||||
increment = 0;
|
||||
break;
|
||||
case fp_positive:
|
||||
increment = (pu->sign == 0) & (sr != 0);
|
||||
break;
|
||||
case fp_negative:
|
||||
increment = (pu->sign != 0) & (sr != 0);
|
||||
break;
|
||||
}
|
||||
if (increment) {
|
||||
pu->significand[3]++;
|
||||
if (pu->significand[3] == 0) {
|
||||
pu->significand[2]++;
|
||||
if (pu->significand[2] == 0) {
|
||||
pu->significand[1]++;
|
||||
if (pu->significand[1] == 0) {
|
||||
pu->significand[0]++; /* rounding carried out */
|
||||
if (pu->significand[0] == 0x20000) {
|
||||
pu->exponent++;
|
||||
pu->significand[0] = 0x10000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((pfpsd->fp_direction == fp_nearest) &&
|
||||
(pu->sticky == 0) && increment!=0) { /* ambiguous case */
|
||||
pu->significand[3] &= 0xfffffffe; /* force round to even */
|
||||
}
|
||||
}
|
||||
|
||||
PRIVATE void
|
||||
packinteger(pfpsd, pu, px)
|
||||
fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
unpacked *pu; /* unpacked result */
|
||||
int *px; /* packed integer */
|
||||
{
|
||||
switch (pu->fpclass) {
|
||||
case fp_zero:
|
||||
*px = 0;
|
||||
break;
|
||||
case fp_normal:
|
||||
if (pu->exponent >= 32)
|
||||
goto overflow;
|
||||
fpu_rightshift(pu, 112 - pu->exponent);
|
||||
round(pfpsd, pu);
|
||||
if (pu->significand[3] >= 0x80000000)
|
||||
if ((pu->sign == 0)||(pu->significand[3] > 0x80000000))
|
||||
goto overflow;
|
||||
*px = pu->significand[3];
|
||||
if (pu->sign)
|
||||
*px = -*px;
|
||||
break;
|
||||
case fp_infinity:
|
||||
case fp_quiet:
|
||||
case fp_signaling:
|
||||
overflow:
|
||||
if (pu->sign)
|
||||
*px = 0x80000000;
|
||||
else
|
||||
*px = 0x7fffffff;
|
||||
pfpsd->fp_current_exceptions &= ~(1 << (int) fp_inexact);
|
||||
fpu_set_exception(pfpsd, fp_invalid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PRIVATE void
|
||||
packsingle(pfpsd, pu, px)
|
||||
fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
unpacked *pu; /* unpacked result */
|
||||
single_type *px; /* packed single */
|
||||
{
|
||||
px->sign = pu->sign;
|
||||
switch (pu->fpclass) {
|
||||
case fp_zero:
|
||||
px->exponent = 0;
|
||||
px->significand = 0;
|
||||
break;
|
||||
case fp_infinity:
|
||||
infinity:
|
||||
px->exponent = 0xff;
|
||||
px->significand = 0;
|
||||
break;
|
||||
case fp_quiet:
|
||||
case fp_signaling:
|
||||
fpu_rightshift(pu, 113-24);
|
||||
px->exponent = 0xff;
|
||||
px->significand = 0x400000|(0x3fffff&pu->significand[3]);
|
||||
break;
|
||||
case fp_normal:
|
||||
fpu_rightshift(pu, 113-24);
|
||||
pu->exponent += SINGLE_BIAS;
|
||||
if (pu->exponent <= 0) {
|
||||
px->exponent = 0;
|
||||
fpu_rightshift(pu, 1 - pu->exponent);
|
||||
round(pfpsd, pu);
|
||||
if (pu->significand[3] == 0x800000) { /*
|
||||
* rounded
|
||||
* back up to
|
||||
* normal
|
||||
*/
|
||||
px->exponent = 1;
|
||||
px->significand = 0;
|
||||
fpu_set_exception(pfpsd, fp_inexact);
|
||||
} else
|
||||
px->significand = 0x7fffff & pu->significand[3];
|
||||
|
||||
if (pfpsd->fp_current_exceptions & (1 << fp_inexact))
|
||||
fpu_set_exception(pfpsd, fp_underflow);
|
||||
if (pfpsd->fp_fsrtem & (1<<fp_underflow)) {
|
||||
fpu_set_exception(pfpsd, fp_underflow);
|
||||
pfpsd->fp_current_exceptions &=
|
||||
~(1 << (int) fp_inexact);
|
||||
}
|
||||
return;
|
||||
}
|
||||
round(pfpsd, pu);
|
||||
if (pu->significand[3] == 0x1000000) { /* rounding overflow */
|
||||
pu->significand[3] = 0x800000;
|
||||
pu->exponent += 1;
|
||||
}
|
||||
if (pu->exponent >= 0xff) {
|
||||
fpu_set_exception(pfpsd, fp_overflow);
|
||||
fpu_set_exception(pfpsd, fp_inexact);
|
||||
if (pfpsd->fp_fsrtem & (1<<fp_overflow)) {
|
||||
pfpsd->fp_current_exceptions &=
|
||||
~(1 << (int) fp_inexact);
|
||||
}
|
||||
if (overflow_to_infinity(pfpsd, pu->sign))
|
||||
goto infinity;
|
||||
px->exponent = 0xfe;
|
||||
px->significand = 0x7fffff;
|
||||
return;
|
||||
}
|
||||
px->exponent = pu->exponent;
|
||||
px->significand = 0x7fffff & pu->significand[3];
|
||||
}
|
||||
}
|
||||
|
||||
PRIVATE void
|
||||
packdouble(pfpsd, pu, px, py)
|
||||
fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
unpacked *pu; /* unpacked result */
|
||||
double_type *px; /* packed double */
|
||||
unsigned *py;
|
||||
{
|
||||
px->sign = pu->sign;
|
||||
switch (pu->fpclass) {
|
||||
case fp_zero:
|
||||
px->exponent = 0;
|
||||
px->significand = 0;
|
||||
*py = 0;
|
||||
break;
|
||||
case fp_infinity:
|
||||
infinity:
|
||||
px->exponent = 0x7ff;
|
||||
px->significand = 0;
|
||||
*py = 0;
|
||||
break;
|
||||
case fp_quiet:
|
||||
case fp_signaling:
|
||||
fpu_rightshift(pu, 113-53);
|
||||
px->exponent = 0x7ff;
|
||||
px->significand = 0x80000 | (0x7ffff & pu->significand[2]);
|
||||
*py = pu->significand[3];
|
||||
break;
|
||||
case fp_normal:
|
||||
fpu_rightshift(pu, 113-53);
|
||||
pu->exponent += DOUBLE_BIAS;
|
||||
if (pu->exponent <= 0) { /* underflow */
|
||||
px->exponent = 0;
|
||||
fpu_rightshift(pu, 1 - pu->exponent);
|
||||
round(pfpsd, pu);
|
||||
if (pu->significand[2] == 0x100000) { /*
|
||||
* rounded
|
||||
* back up to
|
||||
* normal
|
||||
*/
|
||||
px->exponent = 1;
|
||||
px->significand = 0;
|
||||
*py = 0;
|
||||
fpu_set_exception(pfpsd, fp_inexact);
|
||||
} else {
|
||||
px->exponent = 0;
|
||||
px->significand = 0xfffff & pu->significand[2];
|
||||
*py = pu->significand[3];
|
||||
}
|
||||
if (pfpsd->fp_current_exceptions & (1 << fp_inexact))
|
||||
fpu_set_exception(pfpsd, fp_underflow);
|
||||
if (pfpsd->fp_fsrtem & (1<<fp_underflow)) {
|
||||
fpu_set_exception(pfpsd, fp_underflow);
|
||||
pfpsd->fp_current_exceptions &=
|
||||
~(1 << (int) fp_inexact);
|
||||
}
|
||||
return;
|
||||
}
|
||||
round(pfpsd, pu);
|
||||
if (pu->significand[2] == 0x200000) { /* rounding overflow */
|
||||
pu->significand[2] = 0x100000;
|
||||
pu->exponent += 1;
|
||||
}
|
||||
if (pu->exponent >= 0x7ff) { /* overflow */
|
||||
fpu_set_exception(pfpsd, fp_overflow);
|
||||
fpu_set_exception(pfpsd, fp_inexact);
|
||||
if (pfpsd->fp_fsrtem & (1<<fp_overflow)) {
|
||||
pfpsd->fp_current_exceptions &=
|
||||
~(1 << (int) fp_inexact);
|
||||
}
|
||||
if (overflow_to_infinity(pfpsd, pu->sign))
|
||||
goto infinity;
|
||||
px->exponent = 0x7fe;
|
||||
px->significand = 0xfffff;
|
||||
*py = 0xffffffff;
|
||||
return;
|
||||
}
|
||||
px->exponent = pu->exponent;
|
||||
px->significand = 0xfffff & pu->significand[2];
|
||||
*py = pu->significand[3];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PRIVATE void
|
||||
packextended(pfpsd, pu, px, py, pz, pw)
|
||||
fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
unpacked *pu; /* unpacked result */
|
||||
extended_type *px; /* packed extended */
|
||||
unsigned *py, *pz, *pw;
|
||||
{
|
||||
px->sign = pu->sign;
|
||||
switch (pu->fpclass) {
|
||||
case fp_zero:
|
||||
px->exponent = 0;
|
||||
px->significand = 0;
|
||||
*pz = 0;
|
||||
*py = 0;
|
||||
*pw = 0;
|
||||
break;
|
||||
case fp_infinity:
|
||||
infinity:
|
||||
px->exponent = 0x7fff;
|
||||
px->significand = 0;
|
||||
*pz = 0;
|
||||
*py = 0;
|
||||
*pw = 0;
|
||||
break;
|
||||
case fp_quiet:
|
||||
case fp_signaling:
|
||||
px->exponent = 0x7fff;
|
||||
px->significand = 0x8000 | pu->significand[0]; /*
|
||||
* Insure quiet
|
||||
* nan.
|
||||
*/
|
||||
*py = pu->significand[1];
|
||||
*pz = pu->significand[2];
|
||||
*pw = pu->significand[3];
|
||||
break;
|
||||
case fp_normal:
|
||||
pu->exponent += EXTENDED_BIAS;
|
||||
if (pu->exponent <= 0) { /* underflow */
|
||||
fpu_rightshift(pu, 1-pu->exponent);
|
||||
round(pfpsd, pu);
|
||||
if (pu->significand[0] < 0x00010000) { /*
|
||||
* not rounded
|
||||
* back up
|
||||
* to normal
|
||||
*/
|
||||
px->exponent = 0;
|
||||
} else {
|
||||
px->exponent = 1;
|
||||
fpu_set_exception(pfpsd, fp_inexact);
|
||||
}
|
||||
if (pfpsd->fp_current_exceptions & (1 << fp_inexact))
|
||||
fpu_set_exception(pfpsd, fp_underflow);
|
||||
if (pfpsd->fp_fsrtem & (1<<fp_underflow)) {
|
||||
fpu_set_exception(pfpsd, fp_underflow);
|
||||
pfpsd->fp_current_exceptions &=
|
||||
~(1 << (int) fp_inexact);
|
||||
}
|
||||
px->significand = pu->significand[0];
|
||||
*py = pu->significand[1];
|
||||
*pz = pu->significand[2];
|
||||
*pw = pu->significand[3];
|
||||
return;
|
||||
}
|
||||
round(pfpsd, pu); /* rounding overflow handled in round() */
|
||||
if (pu->exponent >= 0x7fff) { /* overflow */
|
||||
fpu_set_exception(pfpsd, fp_overflow);
|
||||
fpu_set_exception(pfpsd, fp_inexact);
|
||||
if (pfpsd->fp_fsrtem & (1<<fp_overflow)) {
|
||||
pfpsd->fp_current_exceptions &=
|
||||
~(1 << (int) fp_inexact);
|
||||
}
|
||||
if (overflow_to_infinity(pfpsd, pu->sign))
|
||||
goto infinity;
|
||||
px->exponent = 0x7ffe; /* overflow to max norm */
|
||||
px->significand = 0xffff;
|
||||
*py = 0xffffffff;
|
||||
*pz = 0xffffffff;
|
||||
*pw = 0xffffffff;
|
||||
return;
|
||||
}
|
||||
px->exponent = pu->exponent;
|
||||
px->significand = pu->significand[0];
|
||||
*py = pu->significand[1];
|
||||
*pz = pu->significand[2];
|
||||
*pw = pu->significand[3];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_fp_pack(pfpsd, pu, n, type)
|
||||
fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
unpacked *pu; /* unpacked operand */
|
||||
unsigned n; /* register where datum starts */
|
||||
enum fp_op_type type; /* type of datum */
|
||||
|
||||
{
|
||||
switch (type) {
|
||||
case fp_op_integer:
|
||||
{
|
||||
int x;
|
||||
packinteger(pfpsd, pu, &x);
|
||||
if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
|
||||
pfpsd->fp_current_write_freg(&x, n, pfpsd);
|
||||
break;
|
||||
}
|
||||
case fp_op_single:
|
||||
{
|
||||
single_type x;
|
||||
packsingle(pfpsd, pu, &x);
|
||||
if (!(pfpsd->fp_current_exceptions & pfpsd->fp_fsrtem))
|
||||
pfpsd->fp_current_write_freg(&x, n, pfpsd);
|
||||
break;
|
||||
}
|
||||
case fp_op_double:
|
||||
{
|
||||
double_type x;
|
||||
unsigned y;
|
||||
packdouble(pfpsd, pu, &x, &y);
|
||||
if (!(pfpsd->fp_current_exceptions &
|
||||
pfpsd->fp_fsrtem)) {
|
||||
pfpsd->fp_current_write_freg(&x, DOUBLE_E(n),
|
||||
pfpsd);
|
||||
pfpsd->fp_current_write_freg(&y, DOUBLE_F(n),
|
||||
pfpsd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case fp_op_extended:
|
||||
{
|
||||
extended_type x;
|
||||
unsigned y, z, w;
|
||||
unpacked U;
|
||||
int k;
|
||||
switch (pfpsd->fp_precision) { /*
|
||||
* Implement extended
|
||||
* rounding precision
|
||||
* mode.
|
||||
*/
|
||||
case fp_single:
|
||||
{
|
||||
single_type tx;
|
||||
packsingle(pfpsd, pu, &tx);
|
||||
pu = &U;
|
||||
unpacksingle(pfpsd, pu, tx);
|
||||
break;
|
||||
}
|
||||
case fp_double:
|
||||
{
|
||||
double_type tx;
|
||||
unsigned ty;
|
||||
packdouble(pfpsd, pu, &tx, &ty);
|
||||
pu = &U;
|
||||
unpackdouble(pfpsd, pu, tx, ty);
|
||||
break;
|
||||
}
|
||||
case fp_precision_3: /* rounded to 64 bits */
|
||||
{
|
||||
k = pu->exponent + EXTENDED_BIAS;
|
||||
if (k>=0) k = 113-64;
|
||||
else k = 113-64-k;
|
||||
fpu_rightshift(pu, 113-64);
|
||||
round(pfpsd, pu);
|
||||
pu->sticky=pu->rounded=0;
|
||||
pu->exponent += k;
|
||||
fpu_normalize(pu);
|
||||
break;
|
||||
}
|
||||
}
|
||||
packextended(pfpsd, pu, &x, &y, &z, &w);
|
||||
if (!(pfpsd->fp_current_exceptions &
|
||||
pfpsd->fp_fsrtem)) {
|
||||
pfpsd->fp_current_write_freg(&x, EXTENDED_E(n),
|
||||
pfpsd);
|
||||
pfpsd->fp_current_write_freg(&y, EXTENDED_F(n),
|
||||
pfpsd);
|
||||
pfpsd->fp_current_write_freg(&z, EXTENDED_G(n),
|
||||
pfpsd);
|
||||
pfpsd->fp_current_write_freg(&w, EXTENDED_H(n),
|
||||
pfpsd);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_fp_pack_word(pfpsd, pu, n)
|
||||
fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
unsigned *pu; /* unpacked operand */
|
||||
unsigned n; /* register where datum starts */
|
||||
{
|
||||
pfpsd->fp_current_write_freg(pu, n, pfpsd);
|
||||
}
|
||||
218
sys/sparc/fpu/unpack.c
Normal file
218
sys/sparc/fpu/unpack.c
Normal file
@@ -0,0 +1,218 @@
|
||||
#ifdef sccsid
|
||||
static char sccsid[] = "@(#)unpack.c 1.1 94/10/31 Copyr 1987 Sun Micro";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/* Unpack procedures for Sparc FPU simulator. */
|
||||
|
||||
#include <machine/fpu/fpu_simulator.h>
|
||||
#include <machine/fpu/globals.h>
|
||||
|
||||
PRIVATE void
|
||||
unpackinteger(pu, x)
|
||||
unpacked *pu; /* unpacked result */
|
||||
int x; /* packed integer */
|
||||
{
|
||||
unsigned ux;
|
||||
pu->sticky = pu->rounded = 0;
|
||||
if (x == 0) {
|
||||
pu->sign = 0;
|
||||
pu->fpclass = fp_zero;
|
||||
} else {
|
||||
(*pu).sign = x < 0;
|
||||
(*pu).fpclass = fp_normal;
|
||||
(*pu).exponent = INTEGER_BIAS;
|
||||
if (x<0) ux = -x; else ux = x;
|
||||
(*pu).significand[0] = ux>>15;
|
||||
(*pu).significand[1] = (ux&0x7fff)<<17;
|
||||
(*pu).significand[2] = 0;
|
||||
(*pu).significand[3] = 0;
|
||||
fpu_normalize(pu);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
unpacksingle(pfpsd, pu, x)
|
||||
fp_simd_type *pfpsd; /* simulator data */
|
||||
unpacked *pu; /* unpacked result */
|
||||
single_type x; /* packed single */
|
||||
{
|
||||
unsigned U;
|
||||
pu->sticky = pu->rounded = 0;
|
||||
U = x.significand;
|
||||
(*pu).sign = x.sign;
|
||||
pu->significand[1] = 0;
|
||||
pu->significand[2] = 0;
|
||||
pu->significand[3] = 0;
|
||||
if (x.exponent == 0) { /* zero or sub */
|
||||
if (x.significand == 0) { /* zero */
|
||||
pu->fpclass = fp_zero;
|
||||
return;
|
||||
} else { /* subnormal */
|
||||
pu->fpclass = fp_normal;
|
||||
pu->exponent = -SINGLE_BIAS-6;
|
||||
pu->significand[0]=U;
|
||||
fpu_normalize(pu);
|
||||
return;
|
||||
}
|
||||
} else if (x.exponent == 0xff) { /* inf or nan */
|
||||
if (x.significand == 0) { /* inf */
|
||||
pu->fpclass = fp_infinity;
|
||||
return;
|
||||
} else { /* nan */
|
||||
if ((U & 0x400000) != 0) { /* quiet */
|
||||
pu->fpclass = fp_quiet;
|
||||
} else { /* signaling */
|
||||
pu->fpclass = fp_signaling;
|
||||
fpu_set_exception(pfpsd, fp_invalid);
|
||||
}
|
||||
pu->significand[0] = 0x18000 | (U >> 7);
|
||||
(*pu).significand[1]=((U&0x7f)<<25);
|
||||
return;
|
||||
}
|
||||
}
|
||||
(*pu).exponent = x.exponent - SINGLE_BIAS;
|
||||
(*pu).fpclass = fp_normal;
|
||||
(*pu).significand[0]=0x10000|(U>>7);
|
||||
(*pu).significand[1]=((U&0x7f)<<25);
|
||||
}
|
||||
|
||||
void
|
||||
unpackdouble(pfpsd, pu, x, y)
|
||||
fp_simd_type *pfpsd; /* simulator data */
|
||||
unpacked *pu; /* unpacked result */
|
||||
double_type x; /* packed double */
|
||||
unsigned y;
|
||||
{
|
||||
unsigned U;
|
||||
pu->sticky = pu->rounded = 0;
|
||||
U = x.significand;
|
||||
(*pu).sign = x.sign;
|
||||
pu->significand[1] = y;
|
||||
pu->significand[2] = 0;
|
||||
pu->significand[3] = 0;
|
||||
if (x.exponent == 0) { /* zero or sub */
|
||||
if ((x.significand == 0) && (y == 0)) { /* zero */
|
||||
pu->fpclass = fp_zero;
|
||||
return;
|
||||
} else { /* subnormal */
|
||||
pu->fpclass = fp_normal;
|
||||
pu->exponent = -DOUBLE_BIAS-3;
|
||||
pu->significand[0] = U;
|
||||
fpu_normalize(pu);
|
||||
return;
|
||||
}
|
||||
} else if (x.exponent == 0x7ff) { /* inf or nan */
|
||||
if ((U|y) == 0) { /* inf */
|
||||
pu->fpclass = fp_infinity;
|
||||
return;
|
||||
} else { /* nan */
|
||||
if ((U & 0x80000) != 0) { /* quiet */
|
||||
pu->fpclass = fp_quiet;
|
||||
} else { /* signaling */
|
||||
pu->fpclass = fp_signaling;
|
||||
fpu_set_exception(pfpsd, fp_invalid);
|
||||
}
|
||||
pu->significand[0] = 0x18000 | (U >> 4);
|
||||
(*pu).significand[1]=((U&0xf)<<28)|(y>>4);
|
||||
(*pu).significand[2]=((y&0xf)<<28);
|
||||
return;
|
||||
}
|
||||
}
|
||||
(*pu).exponent = x.exponent - DOUBLE_BIAS;
|
||||
(*pu).fpclass = fp_normal;
|
||||
(*pu).significand[0]=0x10000|(U>>4);
|
||||
(*pu).significand[1]=((U&0xf)<<28)|(y>>4);
|
||||
(*pu).significand[2]=((y&0xf)<<28);
|
||||
}
|
||||
|
||||
PRIVATE void
|
||||
unpackextended(pfpsd, pu, x, y, z, w)
|
||||
fp_simd_type *pfpsd; /* simulator data */
|
||||
unpacked *pu; /* unpacked result */
|
||||
extended_type x; /* packed extended */
|
||||
unsigned y, z, w;
|
||||
{
|
||||
unsigned U;
|
||||
pu->sticky = pu->rounded = 0;
|
||||
U = x.significand;
|
||||
(*pu).sign = x.sign;
|
||||
(*pu).fpclass = fp_normal;
|
||||
(*pu).exponent = x.exponent - EXTENDED_BIAS;
|
||||
(*pu).significand[0] = (x.exponent==0)? U:0x10000|U;
|
||||
(*pu).significand[1] = y;
|
||||
(*pu).significand[2] = z;
|
||||
(*pu).significand[3] = w;
|
||||
if (x.exponent < 0x7fff) { /* zero, normal, or subnormal */
|
||||
if ((z|y|w|pu->significand[0]) == 0) { /* zero */
|
||||
pu->fpclass = fp_zero;
|
||||
return;
|
||||
} else { /* normal or subnormal */
|
||||
if (x.exponent==0) {
|
||||
fpu_normalize(pu);
|
||||
pu->exponent += 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else { /* inf or nan */
|
||||
if ((U|z|y|w) == 0) { /* inf */
|
||||
pu->fpclass = fp_infinity;
|
||||
return;
|
||||
} else { /* nan */
|
||||
if ((U & 0x00008000) != 0) { /* quiet */
|
||||
pu->fpclass = fp_quiet;
|
||||
} else { /* signaling */
|
||||
pu->fpclass = fp_signaling;
|
||||
fpu_set_exception(pfpsd, fp_invalid);
|
||||
}
|
||||
pu->significand[0] |= 0x8000; /* make quiet */
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_fp_unpack(pfpsd, pu, n, dtype)
|
||||
fp_simd_type *pfpsd; /* simulator data */
|
||||
unpacked *pu; /* unpacked result */
|
||||
unsigned n; /* register where data starts */
|
||||
enum fp_op_type dtype; /* type of datum */
|
||||
{
|
||||
freg_type f, fy, fz, fw;
|
||||
|
||||
switch ((int) dtype) {
|
||||
case fp_op_integer:
|
||||
pfpsd->fp_current_read_freg(&f, n, pfpsd);
|
||||
unpackinteger(pu, f.int_reg);
|
||||
break;
|
||||
case fp_op_single:
|
||||
pfpsd->fp_current_read_freg(&f, n, pfpsd);
|
||||
unpacksingle(pfpsd, pu, f.single_reg);
|
||||
break;
|
||||
case fp_op_double:
|
||||
pfpsd->fp_current_read_freg(&f, DOUBLE_E(n), pfpsd);
|
||||
pfpsd->fp_current_read_freg(&fy, DOUBLE_F(n), pfpsd);
|
||||
unpackdouble(pfpsd, pu, f.double_reg, fy.unsigned_reg);
|
||||
break;
|
||||
case fp_op_extended:
|
||||
pfpsd->fp_current_read_freg(&f, EXTENDED_E(n), pfpsd);
|
||||
pfpsd->fp_current_read_freg(&fy, EXTENDED_F(n), pfpsd);
|
||||
pfpsd->fp_current_read_freg(&fz, EXTENDED_G(n), pfpsd);
|
||||
pfpsd->fp_current_read_freg(&fw, EXTENDED_H(n), pfpsd);
|
||||
unpackextended(pfpsd, pu, f.extended_reg, fy.unsigned_reg,
|
||||
fz.unsigned_reg, fw.unsigned_reg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_fp_unpack_word(pfpsd, pu, n)
|
||||
fp_simd_type *pfpsd; /* simulator data */
|
||||
unsigned *pu; /* unpacked result */
|
||||
unsigned n; /* register where data starts */
|
||||
{
|
||||
pfpsd->fp_current_read_freg(pu, n, pfpsd);
|
||||
}
|
||||
294
sys/sparc/fpu/utility.c
Normal file
294
sys/sparc/fpu/utility.c
Normal file
@@ -0,0 +1,294 @@
|
||||
#ifdef sccsid
|
||||
static char sccsid[] = "@(#)utility.c 1.1 94/10/31 SMI";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/* Utility functions for Sparc FPU simulator. */
|
||||
|
||||
#include <machine/fpu/fpu_simulator.h>
|
||||
#include <machine/fpu/globals.h>
|
||||
|
||||
void
|
||||
_fp_read_vfreg(pf, n, pfpsd)
|
||||
FPU_REGS_TYPE *pf; /* Old freg value. */
|
||||
unsigned n; /* Want to read register n. */
|
||||
fp_simd_type *pfpsd;
|
||||
{
|
||||
*pf = pfpsd->fp_current_pfregs->fpu_regs[n];
|
||||
}
|
||||
|
||||
void
|
||||
_fp_write_vfreg(pf, n, pfpsd)
|
||||
FPU_REGS_TYPE *pf; /* New freg value. */
|
||||
unsigned n; /* Want to read register n. */
|
||||
fp_simd_type *pfpsd;
|
||||
{
|
||||
pfpsd->fp_current_pfregs->fpu_regs[n] = *pf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalize a number. Does not affect zeros, infs, or NaNs.
|
||||
* The number will be normalized to 113 bit extended:
|
||||
* 0x0001####, 0x########, 0x########, 0x########.
|
||||
*/
|
||||
void
|
||||
fpu_normalize(pu)
|
||||
unpacked *pu;
|
||||
{
|
||||
unsigned U, u0, u1, u2, u3, m, n, k;
|
||||
u0 = pu->significand[0];
|
||||
u1 = pu->significand[1];
|
||||
u2 = pu->significand[2];
|
||||
u3 = pu->significand[3];
|
||||
if ((*pu).fpclass == fp_normal) {
|
||||
if ((u0|u1|u2|u3)==0) {
|
||||
(*pu).fpclass = fp_zero;
|
||||
return;
|
||||
}
|
||||
while (u0 == 0) {
|
||||
u0 = u1; u1=u2; u2=u3; u3=0;
|
||||
(*pu).exponent = (*pu).exponent - 32;
|
||||
}
|
||||
if (u0 >= 0x20000) { /* u3 should be zero */
|
||||
n=1; U = u0 >> 1;
|
||||
while (U >= 0x20000) {
|
||||
U >>= 1;
|
||||
n += 1;
|
||||
}
|
||||
m = (1 << n)-1;
|
||||
k = 32-n;
|
||||
(*pu).exponent += n;
|
||||
u3 = ((u2&m)<<k)|(u3>>n);
|
||||
u2 = ((u1&m)<<k)|(u2>>n);
|
||||
u1 = ((u0&m)<<k)|(u1>>n);
|
||||
u0 = U;
|
||||
} else if (u0 < 0x10000) {
|
||||
n=1; U = u0<<1;
|
||||
while (U < 0x10000) {
|
||||
U <<= 1;
|
||||
n += 1;
|
||||
}
|
||||
k = 32-n;
|
||||
m = -(1 << k);
|
||||
(*pu).exponent -= n;
|
||||
u0 = (u0<<n)|((u1&m)>>k);
|
||||
u1 = (u1<<n)|((u2&m)>>k);
|
||||
u2 = (u2<<n)|((u3&m)>>k);
|
||||
u3 = (u3<<n);
|
||||
}
|
||||
pu->significand[0] = u0;
|
||||
pu->significand[1] = u1;
|
||||
pu->significand[2] = u2;
|
||||
pu->significand[3] = u3;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Right shift significand sticky by n bits.
|
||||
*/
|
||||
void
|
||||
fpu_rightshift(pu, n)
|
||||
unpacked *pu;
|
||||
int n;
|
||||
{
|
||||
unsigned m, k, j, u0, u1, u2, u3;
|
||||
if (n > 113) { /* drastic */
|
||||
if (((*pu).significand[0] | (*pu).significand[1]
|
||||
| (*pu).significand[2] | (*pu).significand[3]) == 0){
|
||||
/* really zero */
|
||||
pu->fpclass = fp_zero;
|
||||
return;
|
||||
} else {
|
||||
pu->rounded = 0;
|
||||
pu->sticky = 1;
|
||||
pu->significand[3] = 0;
|
||||
pu->significand[2] = 0;
|
||||
pu->significand[1] = 0;
|
||||
pu->significand[0] = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (n >= 32) { /* big shift */
|
||||
pu->sticky |= pu->rounded | (pu->significand[3]&0x7fffffff);
|
||||
pu->rounded = (*pu).significand[3] >> 31;
|
||||
(*pu).significand[3] = (*pu).significand[2];
|
||||
(*pu).significand[2] = (*pu).significand[1];
|
||||
(*pu).significand[1] = (*pu).significand[0];
|
||||
(*pu).significand[0] = 0;
|
||||
n -= 32;
|
||||
}
|
||||
if (n > 0) { /* small shift */
|
||||
u0 = pu->significand[0];
|
||||
u1 = pu->significand[1];
|
||||
u2 = pu->significand[2];
|
||||
u3 = pu->significand[3];
|
||||
m = (1<<n)-1;
|
||||
k = 32 - n;
|
||||
j = (1<<(n-1))-1;
|
||||
pu->sticky |= pu->rounded | (u3&j);
|
||||
pu->rounded = (u3&m)>>(n-1);
|
||||
pu->significand[3] = ((u2&m)<<k)|(u3>>n);
|
||||
pu->significand[2] = ((u1&m)<<k)|(u2>>n);
|
||||
pu->significand[1] = ((u0&m)<<k)|(u1>>n);
|
||||
pu->significand[0] = u0>>n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the exception bit in the current exception register.
|
||||
*/
|
||||
void
|
||||
fpu_set_exception(pfpsd, ex)
|
||||
fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
enum fp_exception_type ex;
|
||||
{
|
||||
pfpsd->fp_current_exceptions |= 1 << (int) ex;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set invalid exception and error nan in *pu
|
||||
*/
|
||||
void
|
||||
fpu_error_nan(pfpsd, pu)
|
||||
fp_simd_type *pfpsd; /* Pointer to simulator data */
|
||||
unpacked *pu;
|
||||
{
|
||||
fpu_set_exception(pfpsd, fp_invalid);
|
||||
pu->sign = 0;
|
||||
pu->significand[0] = 0x7fffffff;
|
||||
pu->significand[1] = 0xffffffff;
|
||||
pu->significand[2] = 0xffffffff;
|
||||
pu->significand[3] = 0xffffffff;
|
||||
}
|
||||
|
||||
/*
|
||||
* the following fpu_add3wc should be inlined as
|
||||
* .inline _fpu_add3wc, 3
|
||||
* ld [%o1], %o4 ! sum = x
|
||||
* addcc -1, %o3, %g0 ! restore last carry in cc reg
|
||||
* addxcc %o4, %o2, %o4 ! sum = sum + y + last carry
|
||||
* st %o4, [%o0] ! *z = sum
|
||||
* addx %g0, %g0, %o0 ! return new carry
|
||||
* .end
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
unsigned
|
||||
#endif !lint
|
||||
fpu_add3wc(z, x, y, carry)
|
||||
unsigned *z, x, y, carry;
|
||||
{ /* *z = x + y + carry, set carry; */
|
||||
if (carry==0) {
|
||||
*z = x+y;
|
||||
return (*z < y);
|
||||
} else {
|
||||
*z = x+y+1;
|
||||
return (*z <= y);
|
||||
}
|
||||
}
|
||||
|
||||
/* the following fpu_sub3wc should be inlined as
|
||||
* .inline _fpu_sub3wc, 3
|
||||
* ld [%o1], %o4 ! sum = *x
|
||||
* addcc -1, %o3, %g0 ! restore last carry in cc reg
|
||||
* subxcc %o4, %o2, %o4 ! sum = sum - y - last carry
|
||||
* st %o4, [%o0] ! *x = sum
|
||||
* addx %g0, %g0, %o0 ! return new carry
|
||||
* .end
|
||||
*/
|
||||
#ifndef lint
|
||||
unsigned
|
||||
#endif !lint
|
||||
|
||||
fpu_sub3wc(z, x, y, carry)
|
||||
unsigned *z, x, y, carry;
|
||||
{ /* *z = x - y - carry, set carry; */
|
||||
if (carry==0) {
|
||||
*z = x-y;
|
||||
return (*z > x);
|
||||
} else {
|
||||
*z = x-y-1;
|
||||
return (*z >= x);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* the following fpu_neg2wc should be inlined as
|
||||
* .inline _fpu_neg2wc, 2
|
||||
* ld [%o1], %o3 ! tmp = *x
|
||||
* addcc -1, %o2, %g0 ! restore last carry in cc reg
|
||||
* subxcc %g0, %o3, %o3 ! sum = 0 - tmp - last carry
|
||||
* st %o3, [%o0] ! *x = sum
|
||||
* addx %g0, %g0, %o0 ! return new carry
|
||||
* .end
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
unsigned
|
||||
#endif !lint
|
||||
|
||||
fpu_neg2wc(z, x, carry)
|
||||
unsigned *z, x, carry;
|
||||
{ /* *x = 0 - *x - carry, set carry; */
|
||||
if (carry==0) {
|
||||
*z = -x;
|
||||
return ((*z) != 0);
|
||||
} else {
|
||||
*z = -x-1;
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
fpu_cmpli(x, y, n)
|
||||
unsigned x[], y[]; int n;
|
||||
{ /* compare two unsigned array */
|
||||
int i;
|
||||
i=0;
|
||||
while (i < n) {
|
||||
if (x[i] > y[i]) return 1;
|
||||
else if (x[i] < y[i]) return -1;
|
||||
i++;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
/*
|
||||
* Print out unpacked record.
|
||||
*/
|
||||
void
|
||||
display_unpacked(pu)
|
||||
unpacked *pu;
|
||||
{
|
||||
(void) printf(" unpacked ");
|
||||
if (pu->sign)
|
||||
(void) printf("-");
|
||||
else
|
||||
(void) printf("+");
|
||||
|
||||
switch (pu->fpclass) {
|
||||
case fp_zero:
|
||||
(void) printf("0 ");
|
||||
break;
|
||||
case fp_normal:
|
||||
(void) printf("normal");
|
||||
break;
|
||||
case fp_infinity:
|
||||
(void) printf("Inf ");
|
||||
break;
|
||||
case fp_quiet:
|
||||
case fp_signaling:
|
||||
(void) printf("nan ");
|
||||
break;
|
||||
}
|
||||
(void) printf(" %X %X %X %X (%X, %X) exponent %X \n",
|
||||
pu->significand[0], pu->significand[1], pu->significand[2],
|
||||
pu->significand[3], (pu->rounded!=0),
|
||||
(pu->sticky!=0), pu->exponent);
|
||||
}
|
||||
#endif
|
||||
100
sys/sparc/fpu/uword.c
Normal file
100
sys/sparc/fpu/uword.c
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifdef sccsid
|
||||
static char sccsid[] = "@(#)uword.c 1.1 94/10/31 Copyr 1987 Sun Micro";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Copyright (c) 1987 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/* Read/write user memory procedures for Sparc FPU simulator. */
|
||||
#include <machine/param.h>
|
||||
#include <machine/fpu/fpu_simulator.h>
|
||||
#include <machine/fpu/globals.h>
|
||||
|
||||
#include <vm/seg.h>
|
||||
|
||||
extern int fuword();
|
||||
extern int suword();
|
||||
extern int fubyte();
|
||||
|
||||
enum ftt_type
|
||||
_fp_read_word(address, pvalue, pfpsd)
|
||||
caddr_t address;
|
||||
int *pvalue;
|
||||
fp_simd_type *pfpsd;
|
||||
{
|
||||
int w;
|
||||
int b;
|
||||
|
||||
if (((int) address & 0x3) != 0)
|
||||
return (ftt_alignment); /* Must be word-aligned. */
|
||||
w = fuword(address);
|
||||
if (w == -1) {
|
||||
b = fubyte(address);
|
||||
if (b == -1) {
|
||||
pfpsd->fp_trapaddr = address;
|
||||
pfpsd->fp_traprw = S_READ;
|
||||
return (ftt_fault);
|
||||
}
|
||||
}
|
||||
*pvalue = w;
|
||||
return (ftt_none);
|
||||
}
|
||||
|
||||
enum ftt_type
|
||||
_fp_write_word(address, value, pfpsd)
|
||||
caddr_t address;
|
||||
int value;
|
||||
fp_simd_type *pfpsd;
|
||||
{
|
||||
int w;
|
||||
|
||||
if (((int) address & 0x3) != 0)
|
||||
return (ftt_alignment); /* Must be word-aligned. */
|
||||
w = suword(address, value);
|
||||
if (w == -1) {
|
||||
pfpsd->fp_trapaddr = address;
|
||||
pfpsd->fp_traprw = S_WRITE;
|
||||
return (ftt_fault);
|
||||
} else {
|
||||
return (ftt_none);
|
||||
}
|
||||
}
|
||||
|
||||
enum ftt_type
|
||||
read_iureg(n, pregs, pwindow, pvalue, pfpsd)
|
||||
unsigned n;
|
||||
struct regs *pregs; /* Pointer to PCB image of registers. */
|
||||
struct rwindow *pwindow;/* Pointer to locals and ins. */
|
||||
int *pvalue;
|
||||
fp_simd_type *pfpsd;
|
||||
|
||||
{ /* Reads integer unit's register n. */
|
||||
register int *pint;
|
||||
|
||||
if (n <= 15) {
|
||||
if (n == 0) {
|
||||
*pvalue = 0;
|
||||
return (ftt_none); /* Read global register 0. */
|
||||
}
|
||||
if (n <= 7) { /* globals */
|
||||
pint = &(pregs->r_g1) + (n - 1);
|
||||
*pvalue = *pint;
|
||||
return (ftt_none);
|
||||
} else { /* outs */
|
||||
pint = &(pregs->r_o0) + (n - 8);
|
||||
*pvalue = *pint;
|
||||
return (ftt_none);
|
||||
}
|
||||
} else { /* locals and ins */
|
||||
if (n <= 23)
|
||||
pint = &pwindow->rw_local[n - 16];
|
||||
else
|
||||
pint = &pwindow->rw_in[n - 24];
|
||||
if ((int) pint > KERNELBASE){
|
||||
*pvalue = *pint;
|
||||
return (ftt_none);
|
||||
}
|
||||
return _fp_read_word((char *) pint, pvalue, pfpsd);
|
||||
}
|
||||
}
|
||||
24
sys/sparc/frame.h
Normal file
24
sys/sparc/frame.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/* @(#)frame.h 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1987 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Definition of the sparc stack frame (when it is pushed on the stack).
|
||||
*/
|
||||
|
||||
#ifndef _sparc_frame_h
|
||||
#define _sparc_frame_h
|
||||
|
||||
struct frame {
|
||||
int fr_local[8]; /* saved locals */
|
||||
int fr_arg[6]; /* saved arguments [0 - 5] */
|
||||
struct frame *fr_savfp; /* saved frame pointer */
|
||||
int fr_savpc; /* saved program counter */
|
||||
char *fr_stret; /* struct return addr */
|
||||
int fr_argd[6]; /* arg dump area */
|
||||
int fr_argx[1]; /* array of args past the sixth */
|
||||
};
|
||||
|
||||
#endif /*!_sparc_frame_h*/
|
||||
231
sys/sparc/mcount.s
Normal file
231
sys/sparc/mcount.s
Normal file
@@ -0,0 +1,231 @@
|
||||
.seg "data"
|
||||
.asciz "@(#)mcount.s 1.1 94/10/31 Copyr 1987 Sun Micro"
|
||||
.align 4
|
||||
|
||||
! Copyright (c) 1986 by Sun Microsystems, Inc.
|
||||
|
||||
#include <machine/asm_linkage.h>
|
||||
|
||||
|
||||
/* don't mess with FROMPC and SELFPC, or you won't be able to return */
|
||||
#define FROMPC %i7
|
||||
#define SELFPC %o7
|
||||
|
||||
/*
|
||||
* register usage: we're in the same window as the calling routine, so we
|
||||
* only use the first 6 output registers, and there's lots of sharing
|
||||
*/
|
||||
|
||||
#define PROFIL %o5
|
||||
|
||||
#define FROM_P %o4
|
||||
|
||||
#define TOS_P %o3
|
||||
#define SCRATCH3 TOS_P
|
||||
|
||||
#define TOINDEX %o2
|
||||
#define PREVTOP TOINDEX
|
||||
#define SCRATCH2 TOINDEX
|
||||
|
||||
#define TOP %o1
|
||||
#define SCRATCH1 TOP
|
||||
#define PROF_P TOP
|
||||
|
||||
#define SCRATCH0 %o0
|
||||
|
||||
/* some offsets */
|
||||
#define T_SELF 0
|
||||
#define T_CNT 4
|
||||
#define T_LINK 8
|
||||
|
||||
|
||||
.seg "text"
|
||||
.proc 0
|
||||
.global mcount
|
||||
|
||||
mcount:
|
||||
|
||||
#ifdef MULTIPROCESSOR
|
||||
|
||||
! only do mcount work if inside the kernel lock.
|
||||
|
||||
mov %tbr, %o0
|
||||
sethi %hi(_klock), %o1
|
||||
ld [%o1+%lo(_klock)], %o1
|
||||
srl %o0, 20, %o0
|
||||
and %o0, 3, %o0
|
||||
set 0xFF000008, %o2
|
||||
or %o0, %o2, %o0
|
||||
cmp %o0, %o1
|
||||
bne out
|
||||
nop
|
||||
|
||||
#endif MULTIPROCESSOR
|
||||
|
||||
! Make sure that we're profiling, and that we aren't recursively called
|
||||
|
||||
sethi %hi(_tolimit), SCRATCH0 !Make sure we haven't run out
|
||||
ld [SCRATCH0+%lo(_tolimit)], SCRATCH0
|
||||
sethi %hi(_profiling), PROF_P !moved up to avoid lockout
|
||||
tst SCRATCH0
|
||||
be out
|
||||
sethi %hi(_s_lowpc), SCRATCH3 !moved up to avoid lockout
|
||||
ld [PROF_P+%lo(_profiling)], PROFIL
|
||||
sethi %hi(_s_textsize), SCRATCH0 !moved up to avoid lockout
|
||||
tst PROFIL
|
||||
bne out
|
||||
add PROFIL, 1, SCRATCH2 ! increment profiling
|
||||
st SCRATCH2, [PROF_P+%lo(_profiling)]
|
||||
|
||||
|
||||
! Make sure (frompc-lowpc) > textsize
|
||||
|
||||
ld [SCRATCH3+%lo(_s_lowpc)], SCRATCH3
|
||||
ld [SCRATCH0+%lo(_s_textsize)], SCRATCH0
|
||||
sub FROMPC, SCRATCH3, FROM_P
|
||||
cmp FROM_P, SCRATCH0
|
||||
bgu,a out
|
||||
st PROFIL, [PROF_P+%lo(_profiling)] !delay slot; clear profiling
|
||||
|
||||
|
||||
! from_p = &froms[(frompc-s_lowpc)/(HASHFRACTION*sizeof(short))]
|
||||
|
||||
sethi %hi(_froms), SCRATCH0
|
||||
ld [SCRATCH0+%lo(_froms)], SCRATCH0
|
||||
sra FROM_P, 2, FROM_P !divide by 4 then multiply by
|
||||
sll FROM_P, 1, FROM_P !two; MUST be even
|
||||
add SCRATCH0, FROM_P, FROM_P
|
||||
lduh [FROM_P], TOINDEX ! toindex = *from_p
|
||||
sethi %hi(_tos), TOS_P !for later; locked out anyway
|
||||
tst TOINDEX
|
||||
bne,a oldarc
|
||||
sll TOINDEX, 2, TOINDEX ! delay slot
|
||||
|
||||
|
||||
! This is the first time we've seen a call from here; get new tostruct
|
||||
|
||||
ld [TOS_P+%lo(_tos)], TOS_P ! &tos[0]
|
||||
sethi %hi(_tolimit), SCRATCH1
|
||||
lduh [TOS_P+T_LINK], TOINDEX !tos[0].link(last used struct)
|
||||
ld [SCRATCH1+%lo(_tolimit)], SCRATCH0
|
||||
inc TOINDEX !increment to free struct
|
||||
sth TOINDEX, [TOS_P+T_LINK]
|
||||
cmp TOINDEX, SCRATCH0 !too many?
|
||||
bge,a overflow
|
||||
st %g0, [SCRATCH1+%lo(_tolimit)] !delay slot;
|
||||
|
||||
|
||||
! Initialize arc
|
||||
|
||||
sth TOINDEX, [FROM_P] !*from_p = toindex
|
||||
sll TOINDEX, 2, TOINDEX
|
||||
sll TOINDEX, 1, SCRATCH0
|
||||
add TOINDEX, SCRATCH0, TOINDEX !toindex*sizeof(tostruct))
|
||||
add TOS_P, TOINDEX, TOP !top = &tos[toindex]
|
||||
st SELFPC, [TOP+T_SELF] !top->selfpc = selfpc (%i7)
|
||||
mov 1, SCRATCH0
|
||||
st SCRATCH0, [TOP+T_CNT] !top->count = 1
|
||||
b done
|
||||
sth %g0, [TOP+T_LINK] !delay slot; top->link = 0
|
||||
|
||||
|
||||
! Not a new frompc
|
||||
|
||||
oldarc:
|
||||
sll TOINDEX, 1, SCRATCH0
|
||||
ld [TOS_P+%lo(_tos)], TOS_P
|
||||
add TOINDEX, SCRATCH0, TOINDEX !(toindex*sizeof(tostruct))
|
||||
add TOS_P, TOINDEX, TOP !top = &tos[toindex]
|
||||
ld [TOP+T_SELF], SCRATCH0 !top->selfpc
|
||||
ld [TOP+T_CNT], SCRATCH2 !for later; don't want to be locked out
|
||||
cmp SCRATCH0, SELFPC !is this the right arc?
|
||||
bne,a chainloop
|
||||
lduh [TOP+T_LINK], SCRATCH0 !delay slot; top->link
|
||||
|
||||
|
||||
! Our arc was at the head of the chain. This is the most common case.
|
||||
|
||||
inc SCRATCH2
|
||||
b done
|
||||
st SCRATCH2, [TOP+T_CNT] !delay slot; increment count
|
||||
|
||||
|
||||
! We have to wander down the linked list, looking for our arc. We only get
|
||||
! here for calls like "*foo()"
|
||||
|
||||
chainloop:
|
||||
tst SCRATCH0 !top->link = 0?
|
||||
bne,a next
|
||||
mov TOP, PREVTOP !delay slot; prevtop = top
|
||||
|
||||
|
||||
! We're at the end of the chain and didn't find top->selfpc == selfpc
|
||||
! Allocate a new tostruct
|
||||
|
||||
lduh [TOS_P+T_LINK], TOINDEX !tos[0].link (last used struct)
|
||||
sethi %hi(_tolimit), SCRATCH1
|
||||
inc TOINDEX !increment to free struct
|
||||
ld [SCRATCH1+%lo(_tolimit)], SCRATCH0
|
||||
sth TOINDEX, [TOS_P+T_LINK]
|
||||
cmp TOINDEX, SCRATCH0 !check for overflow
|
||||
bge,a overflow
|
||||
st %g0, [SCRATCH1+%lo(_tolimit)] !delay slot;
|
||||
|
||||
|
||||
! Now initialize it and put it at the head of the chain
|
||||
|
||||
sll TOINDEX, 2, SCRATCH0
|
||||
sll SCRATCH0, 1, TOP
|
||||
add SCRATCH0, TOP, TOP !(toindex*sizeof(tostruct))
|
||||
add TOS_P, TOP, TOP !top=&tos[toindex]
|
||||
st SELFPC, [TOP+T_SELF] !top->selfpc = selfpc
|
||||
mov 1, SCRATCH0
|
||||
st SCRATCH0, [TOP+T_CNT] !top->count = 1
|
||||
lduh [FROM_P], SCRATCH0
|
||||
sth TOINDEX, [FROM_P] !*from_p = toindex
|
||||
b done
|
||||
sth SCRATCH0, [TOP+T_LINK] !top->link = *from_p
|
||||
|
||||
|
||||
! Check the next arc on the chain
|
||||
|
||||
next:
|
||||
sll SCRATCH0, 2, TOP
|
||||
sll TOP, 1, SCRATCH0
|
||||
add TOP, SCRATCH0, TOP
|
||||
add TOS_P, TOP, TOP !top = &tos[top->link]
|
||||
ld [TOP+T_SELF], SCRATCH0
|
||||
cmp SCRATCH0, SELFPC !is top->selfpc = selfpc?
|
||||
bne,a chainloop
|
||||
lduh [TOP+T_LINK], SCRATCH0 !delay slot; top->link
|
||||
|
||||
|
||||
! We're home. Increment the count and move it to the head of the chain
|
||||
|
||||
ld [TOP+T_CNT], SCRATCH3
|
||||
lduh [TOP+T_LINK], SCRATCH0
|
||||
inc SCRATCH3
|
||||
st SCRATCH3, [TOP+T_CNT] !top->count++
|
||||
lduh [PREVTOP+T_LINK], SCRATCH3 !temp1 = prevtop->link
|
||||
sth SCRATCH0, [PREVTOP+T_LINK] !prevtop->link = top->link
|
||||
lduh [FROM_P], SCRATCH0 !temp2 = *from_p
|
||||
sth SCRATCH3, [FROM_P] !*from_p = temp1
|
||||
sth SCRATCH0, [TOP+T_LINK] !top->link = temp2
|
||||
done:
|
||||
sethi %hi(_profiling),PROF_P
|
||||
retl
|
||||
st PROFIL, [PROF_P+%lo(_profiling)] ! clear profiling
|
||||
out:
|
||||
retl
|
||||
nop
|
||||
overflow:
|
||||
save %sp, -SA(MINFRAME), %sp
|
||||
sethi %hi(OVERMSG), %o0
|
||||
call _printf, 1 !printf(OVERMSG)
|
||||
or %o0, %lo(OVERMSG), %o0
|
||||
ret
|
||||
restore
|
||||
.seg "data1"
|
||||
OVERMSG:
|
||||
.ascii "mcount: tos overflow\n\0"
|
||||
|
||||
88
sys/sparc/ocsum.s
Normal file
88
sys/sparc/ocsum.s
Normal file
@@ -0,0 +1,88 @@
|
||||
! @(#)ocsum.s 1.1 94/10/31 SMI
|
||||
! Copyright (c) 1989 by Sun Microsystems, Inc.
|
||||
|
||||
.seg "text"
|
||||
.align 4
|
||||
|
||||
|
||||
#include <machine/asm_linkage.h>
|
||||
|
||||
/*
|
||||
* ocsum(address, halfword_count)
|
||||
* Do a 16 bit one's complement sum of a given number of (16-bit)
|
||||
* halfwords. The halfword pointer must not be odd.
|
||||
* %o0 address; %o1 count; %o3 accumulator; %o4 temp
|
||||
* %g2 and %g3 used in main loop
|
||||
*/
|
||||
ENTRY(ocsum)
|
||||
clr %o3 ! clear accumulator
|
||||
cmp %o1, 31 ! less than 32 bytes?
|
||||
bl,a dohw ! just do halfwords
|
||||
tst %o1 ! delay slot, test count
|
||||
|
||||
btst 31, %o0 ! (delay slot)
|
||||
bz 2f ! if 32 byte aligned, skip
|
||||
nop
|
||||
|
||||
!
|
||||
! Do first halfwords until 32-byte aligned
|
||||
!
|
||||
1:
|
||||
lduh [%o0], %g2 ! read data
|
||||
add %o0, 2, %o0 ! increment address
|
||||
add %o3, %g2, %o3 ! add to accumulator, don't need carry yet
|
||||
btst 31, %o0 ! 32 byte aligned?
|
||||
bnz 1b
|
||||
sub %o1, 1, %o1 ! decrement count
|
||||
!
|
||||
! loop to add in 32 byte chunks
|
||||
! The loads and adds are staggered to help avoid load/use
|
||||
! interlocks on highly pipelined implementations, and double
|
||||
! loads are used for 64-bit wide memory systems.
|
||||
!
|
||||
2:
|
||||
sub %o1, 16, %o1 ! decrement count to aid testing
|
||||
4:
|
||||
ldd [%o0], %g2 ! read data
|
||||
ldd [%o0+8], %o4 ! read more data
|
||||
addcc %o3, %g2, %o3 ! add to accumulator
|
||||
addxcc %o3, %g3, %o3 ! add to accumulator with carry
|
||||
ldd [%o0+16], %g2 ! read more data
|
||||
addxcc %o3, %o4, %o3 ! add to accumulator with carry
|
||||
addxcc %o3, %o5, %o3 ! add to accumulator with carry
|
||||
ldd [%o0+24], %o4 ! read more data
|
||||
addxcc %o3, %g2, %o3 ! add to accumulator with carry
|
||||
addxcc %o3, %g3, %o3 ! add to accumulator with carry
|
||||
addxcc %o3, %o4, %o3 ! add to accumulator
|
||||
addxcc %o3, %o5, %o3 ! add to accumulator with carry
|
||||
addxcc %o3, 0, %o3 ! if final carry, add it in
|
||||
subcc %o1, 16, %o1 ! decrement count (in halfwords)
|
||||
bge 4b
|
||||
add %o0, 32, %o0 ! delay slot, increment address
|
||||
|
||||
add %o1, 16, %o1 ! add back in
|
||||
!
|
||||
! Do any remaining halfwords
|
||||
!
|
||||
b dohw
|
||||
tst %o1 ! delay slot, for more to do
|
||||
|
||||
3:
|
||||
add %o0, 2, %o0 ! increment address
|
||||
addcc %o3, %g2, %o3 ! add to accumulator
|
||||
addxcc %o3, 0, %o3 ! if carry, add it in
|
||||
subcc %o1, 1, %o1 ! decrement count
|
||||
dohw:
|
||||
bg,a 3b ! more to do?
|
||||
lduh [%o0], %g2 ! read data
|
||||
|
||||
!
|
||||
! at this point the 32-bit accumulator
|
||||
! has the result that needs to be returned in 16-bits
|
||||
!
|
||||
sll %o3, 16, %o4 ! put low halfword in high halfword %o4
|
||||
addcc %o4, %o3, %o3 ! add the 2 halfwords in high %o3, set carry
|
||||
srl %o3, 16, %o3 ! shift to low halfword
|
||||
retl ! return
|
||||
addxcc %o3, 0, %o0 ! add in carry if any. result in %o0
|
||||
|
||||
347
sys/sparc/overflow.s
Normal file
347
sys/sparc/overflow.s
Normal file
@@ -0,0 +1,347 @@
|
||||
/* @(#)overflow.s 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1989 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
#include <machine/asm_linkage.h>
|
||||
#include <machine/param.h>
|
||||
#include <machine/psl.h>
|
||||
#include <machine/trap.h>
|
||||
#include <machine/mmu.h>
|
||||
#include <machine/pte.h>
|
||||
#include <machine/reg.h>
|
||||
#include <machine/buserr.h>
|
||||
#include <machine/pcb.h>
|
||||
|
||||
#include "assym.s"
|
||||
|
||||
.seg "text"
|
||||
.align 4
|
||||
|
||||
PROT = (PG_V | PG_W) >> PG_S_BIT
|
||||
P_INVAL = (PG_V) >> PG_S_BIT
|
||||
|
||||
/*
|
||||
* window overflow trap handler
|
||||
*/
|
||||
.global window_overflow
|
||||
window_overflow:
|
||||
|
||||
#ifdef PERFMETER
|
||||
sethi %hi(_overflowcnt), %l5
|
||||
ld [%l5 + %lo(_overflowcnt)], %l7
|
||||
inc %l7
|
||||
st %l7, [%l5 + %lo(_overflowcnt)]
|
||||
#endif PERFMETER
|
||||
|
||||
! wim stored into %l3 by trap vector
|
||||
mov %g1, %l7 ! save %g1
|
||||
mov %g2, %l5 ! save %g2
|
||||
srl %l3, 1, %g1 ! next WIM = %g1 = ror(WIM, 1, NW)
|
||||
sll %l3, %l6, %l4 ! trap vector set %l6 = NW - 1
|
||||
btst PSR_PS, %l0 ! test for user or sup trap
|
||||
bz wo_user
|
||||
or %l4, %g1, %g1 ! delay slot
|
||||
|
||||
!
|
||||
! Overflow from supervisor mode. Determine whether the window
|
||||
! to be saved is a user window.
|
||||
! U.u_pcb.pcb_uwm has a bit set for each user window which is still
|
||||
! in the register file. If u.u_pcb.pcb_uwm has any bits on, then it
|
||||
! is a user window that must be saved.
|
||||
!
|
||||
sethi %hi(_uunix), %g2 ! XXX - global u register?
|
||||
ld [%g2 + %lo(_uunix)], %g2
|
||||
ld [%g2+PCB_UWM], %l4 ! if (u.u_pcb.pcb_uwm != 0) sup window
|
||||
tst %l4
|
||||
bne,a wo_user_window
|
||||
bclr %g1, %l4 ! delay slot, UWM &= ~(new WIM)
|
||||
|
||||
!
|
||||
! Window to be saved is a supervisor window.
|
||||
! Put it on the stack.
|
||||
!
|
||||
save ! get into window to be saved
|
||||
mov %g1, %wim ! install new wim
|
||||
wo_stack_res:
|
||||
SAVE_WINDOW(%sp)
|
||||
restore ! go back to trap window
|
||||
mov %l5, %g2 ! restore g2
|
||||
mov %l0, %psr ! reinstall system PSR_CC
|
||||
mov %l7, %g1 ! restore g1
|
||||
jmp %l1 ! reexecute save
|
||||
rett %l2
|
||||
|
||||
wo_user_window:
|
||||
!
|
||||
! Window to be saved is a user window.
|
||||
!
|
||||
sethi %hi(_uunix), %g2
|
||||
ld [%g2 + %lo(_uunix)], %g2
|
||||
st %l4, [%g2+PCB_UWM] ! update u.u_uwm
|
||||
|
||||
wo_user:
|
||||
!
|
||||
! The window to be saved is a user window.
|
||||
! We must check whether the user stack is resident where the window
|
||||
! will be saved, which is pointed to by the window's sp.
|
||||
! We must also check that the sp is aligned to a word boundary.
|
||||
!
|
||||
save ! get into window to be saved
|
||||
mov %g1, %wim ! install new wim
|
||||
!
|
||||
! Normally, we would check the alignment, and then probe the top
|
||||
! and bottom of the save area on the stack. However we optimize
|
||||
! this by checking that both ends of the save area are within a
|
||||
! 4k unit (the biggest mask we can generate in one cycle), and
|
||||
! the alignment in one shot. This allows us to do one probe to
|
||||
! the page map. NOTE: this assumes a page size of at least 4k.
|
||||
!
|
||||
and %sp, 0xfff, %g1
|
||||
#ifdef VA_HOLE
|
||||
! check if the sp points into the hole in the address space
|
||||
sethi %hi(_hole_shift), %g2 ! hole shift address
|
||||
ld [%g2 + %lo(_hole_shift)], %g2
|
||||
add %g1, (14*4), %g1 ! interlock, bottom of save area
|
||||
sra %sp, %g2, %g2
|
||||
inc %g2
|
||||
andncc %g2, 1, %g2
|
||||
bz 1f
|
||||
andncc %g1, 0xff8, %g0
|
||||
b,a wo_stack_not_res ! stack page is in the hole
|
||||
1:
|
||||
#else
|
||||
add %g1, (14*4), %g1
|
||||
andncc %g1, 0xff8, %g0
|
||||
#endif VA_HOLE
|
||||
bz,a wo_sp_bot
|
||||
lda [%sp]ASI_PM, %g1 ! check for stack page resident
|
||||
!
|
||||
! Stack is either misaligned or crosses a 4k boundary.
|
||||
!
|
||||
btst 0x7, %sp ! test sp alignment
|
||||
bz wo_sp_top
|
||||
add %sp, (14*4), %g1 ! delay slot, check top of save area
|
||||
|
||||
!
|
||||
! Misaligned sp. If this is a userland trap fake a memory alignment
|
||||
! trap. Otherwise, put the window in the window save buffer so that
|
||||
! we can catch it again later.
|
||||
!
|
||||
mov %psr, %g1 ! get psr (we are not in trap window)
|
||||
btst PSR_PS, %g1 ! test for user or sup trap
|
||||
bnz wo_save_to_buf ! sup trap, save window in uarea buf
|
||||
nop
|
||||
|
||||
restore ! get back to orig window
|
||||
mov %l5, %g2 ! restore g2
|
||||
mov %l7, %g1 ! restore g1
|
||||
mov %l3, %wim ! restore old wim, so regs are dumped
|
||||
b sys_trap
|
||||
mov T_ALIGNMENT, %l4 ! delay slot, fake alignment trap
|
||||
|
||||
wo_sp_top:
|
||||
#ifdef VA_HOLE
|
||||
sethi %hi(_hole_shift), %g2 ! hole shift address
|
||||
ld [%g2 + %lo(_hole_shift)], %g2
|
||||
sra %g1, %g2, %g2
|
||||
inc %g2
|
||||
andncc %g2, 1, %g2
|
||||
bz,a 1f
|
||||
lda [%g1]ASI_PM, %g1 ! get pme for this address
|
||||
b,a wo_stack_not_res ! stack page can never be resident
|
||||
1:
|
||||
sethi %hi(_hole_shift), %g2 ! hole shift address
|
||||
ld [%g2 + %lo(_hole_shift)], %g2
|
||||
srl %g1, PG_S_BIT, %g1 ! get vws bits
|
||||
sra %sp, %g2, %g2
|
||||
inc %g2
|
||||
andncc %g2, 1, %g2
|
||||
bz,a 1f
|
||||
cmp %g1, PROT ! look for valid, writeable, user
|
||||
b,a wo_stack_not_res ! stack page can never be resident
|
||||
1:
|
||||
#else
|
||||
lda [%g1]ASI_PM, %g1 ! get pme for this address
|
||||
srl %g1, PG_S_BIT, %g1 ! get vws bits
|
||||
cmp %g1, PROT ! look for valid, writeable, user
|
||||
#endif VA_HOLE
|
||||
be,a wo_sp_bot
|
||||
lda [%sp]ASI_PM, %g1 ! delay slot, check bottom of save area
|
||||
|
||||
b wo_stack_not_res ! stack page not resident
|
||||
bset 1, %sp ! note that this is top of save area
|
||||
|
||||
wo_sp_bot:
|
||||
srl %g1, PG_S_BIT, %g1 ! get vws bits
|
||||
cmp %g1, PROT ! look for valid, writeable, user
|
||||
be wo_ustack_res
|
||||
nop ! extra nop
|
||||
|
||||
wo_stack_not_res:
|
||||
!
|
||||
! The stack save area for user window is not resident.
|
||||
!
|
||||
mov %psr, %g1 ! get psr (we are not in trap window)
|
||||
btst PSR_PS, %g1 ! test for user or sup trap
|
||||
bnz,a wo_save_to_buf ! sup trap, save window in uarea buf
|
||||
bclr 1, %sp ! no need to know which end failed
|
||||
|
||||
btst 1, %sp ! reconstruct fault address
|
||||
bz,a 1f ! top of save area?
|
||||
mov %sp, %g1 ! no, use sp
|
||||
bclr 1, %sp ! yes, clear "which end" flag
|
||||
add %sp, (14*4), %g1 ! add appropriate amount
|
||||
1:
|
||||
!
|
||||
! We first save the window in the first window buffer in the u area.
|
||||
! Then we fake a user data fault. If the fault succeeds, we will
|
||||
! reexecute the save and overflow again, but this time the page
|
||||
! will be resident
|
||||
!
|
||||
sethi %hi(_uunix), %g2
|
||||
ld [%g2 + %lo(_uunix)], %g2
|
||||
st %sp, [%g2+PCB_SPBUF] ! save sp
|
||||
SAVE_WINDOW(%g2+PCB_WBUF)
|
||||
restore ! get back into original window
|
||||
!
|
||||
! Set the save buffer ptr to next buffer
|
||||
!
|
||||
mov 1, %l4
|
||||
st %l4, [%g2+PCB_WBCNT] ! u->u_pcb.pcb_wbcnt = 1
|
||||
!
|
||||
! Compute the user window mask (u.u_pcb.pcb_uwm), which is a mask of
|
||||
! which windows contain user data. In this case it is all the register
|
||||
! except the one at the old WIM and the one we just saved.
|
||||
!
|
||||
mov %wim, %l4 ! get new WIM
|
||||
or %l4, %l3, %l4 ! or in old WIM
|
||||
not %l4
|
||||
mov -2, %g2
|
||||
sll %g2, %l6, %g2
|
||||
andn %l4, %g2, %l4
|
||||
sethi %hi(_uunix), %g2
|
||||
ld [%g2 + %lo(_uunix)], %g2
|
||||
st %l4, [%g2+PCB_UWM] ! u->u_pcb.pcb_uwm = ~(OWIM|NWIM)
|
||||
set _masterprocp, %g2
|
||||
ld [%g2], %g2
|
||||
ld [%g2 + P_STACK], %sp
|
||||
mov %g1, %l6 ! save fault address, arg to _trap
|
||||
mov %l7, %g1 ! restore g1, so we can save it
|
||||
mov %l5, %g2
|
||||
SAVE_GLOBALS(%sp + MINFRAME)
|
||||
SAVE_OUTS(%sp + MINFRAME)
|
||||
st %l0, [%sp + MINFRAME + PSR*4] ! psr
|
||||
st %l1, [%sp + MINFRAME + PC*4] ! pc
|
||||
st %l2, [%sp + MINFRAME + nPC*4] ! npc
|
||||
#ifdef VA_HOLE
|
||||
sethi %hi(_hole_shift), %g1 ! hole shift address
|
||||
ld [%g1 + %lo(_hole_shift)], %g1
|
||||
sra %l6, %g1, %g1
|
||||
inc %g1
|
||||
andncc %g1, 1, %g1
|
||||
bz,a 2f
|
||||
lda [%l6]ASI_PM, %g1 ! get pme for this address
|
||||
b 1f ! stack page in the hole
|
||||
mov GENERIC_INVALID, %o3
|
||||
2:
|
||||
#else
|
||||
lda [%l6]ASI_PM, %g1 ! compute proper bus error reg
|
||||
#endif VA_HOLE
|
||||
mov GENERIC_INVALID, %o3
|
||||
srl %g1, PG_S_BIT, %g1
|
||||
btst P_INVAL, %g1
|
||||
bnz,a 1f
|
||||
mov GENERIC_PROTERR, %o3
|
||||
1:
|
||||
wr %l0, PSR_ET, %psr ! enable traps
|
||||
mov T_DATA_FAULT, %o0
|
||||
add %sp, MINFRAME, %o1
|
||||
mov %l6, %o2
|
||||
call _trap ! trap(T_DATA_FAULT,
|
||||
mov S_WRITE, %o4 ! rp, addr, be, S_WRITE)
|
||||
|
||||
b,a sys_rtt ! return
|
||||
|
||||
wo_save_to_buf:
|
||||
!
|
||||
! The user's stack is not accessable while trying to save a user window
|
||||
! during a supervisor overflow. We save the window in the u area to
|
||||
! be processed when we return to the user.
|
||||
!
|
||||
sethi %hi(_uunix), %g2
|
||||
ld [%g2 + %lo(_uunix)], %g2
|
||||
ld [%g2 + PCB_WBCNT], %g1
|
||||
sll %g1, 2, %g1
|
||||
|
||||
add %g1, %g2, %g1
|
||||
st %sp, [%g1 + PCB_SPBUF] ! save sp
|
||||
sub %g1, %g2, %g1
|
||||
|
||||
sll %g1, 4, %g1 ! convert to offset
|
||||
|
||||
add %g2, PCB_WBUF, %g2
|
||||
add %g1, %g2, %g1
|
||||
SAVE_WINDOW(%g1)
|
||||
sub %g1, %g2, %g1
|
||||
sub %g2, PCB_WBUF, %g2
|
||||
|
||||
srl %g1, 6, %g1 ! increment u.u_pcb.pcb_wbcnt
|
||||
add %g1, 1, %g1
|
||||
|
||||
set _uunix, %g2
|
||||
ld [%g2], %g2
|
||||
st %g1, [%g2 + PCB_WBCNT]
|
||||
restore ! get back to orig window
|
||||
|
||||
!
|
||||
! Return to supervisor. Rett will not underflow since traps
|
||||
! were never disabled.
|
||||
!
|
||||
mov %l0, %psr ! reinstall system PSR_CC
|
||||
mov %l7, %g1 ! restore g1
|
||||
mov %l5, %g2 ! restore g2
|
||||
jmp %l1 ! reexecute save
|
||||
rett %l2
|
||||
|
||||
!
|
||||
! The user's save area is resident. Save the window.
|
||||
!
|
||||
wo_ustack_res:
|
||||
SAVE_WINDOW(%sp)
|
||||
restore ! go back to trap window
|
||||
wo_out:
|
||||
sethi %hi(_uunix), %l3
|
||||
ld [%l3 + %lo(_uunix)], %l3
|
||||
mov %l5, %g2 ! restore g2
|
||||
ld [%l3+PCB_FLAGS], %l3 ! check for clean window maintenance
|
||||
mov %l7, %g1 ! restore g1
|
||||
btst CLEAN_WINDOWS, %l3
|
||||
bz 1f
|
||||
mov %l0, %psr ! reinstall system PSR_CC
|
||||
|
||||
!
|
||||
! Maintain clean windows.
|
||||
!
|
||||
mov %l1, %o6 ! put pc, npc in an unobtrusive place
|
||||
mov %l2, %o7
|
||||
clr %l0 ! clean the rest
|
||||
clr %l1
|
||||
clr %l2
|
||||
clr %l3
|
||||
clr %l4
|
||||
clr %l5
|
||||
clr %l6
|
||||
clr %l7
|
||||
clr %o0
|
||||
clr %o1
|
||||
clr %o2
|
||||
clr %o3
|
||||
clr %o4
|
||||
clr %o5
|
||||
jmp %o6 ! reexecute save
|
||||
rett %o7
|
||||
1:
|
||||
jmp %l1 ! reexecute save
|
||||
rett %l2
|
||||
70
sys/sparc/pcb.h
Normal file
70
sys/sparc/pcb.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/* @(#)pcb.h 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1985 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _sparc_pcb_h
|
||||
#define _sparc_pcb_h
|
||||
|
||||
/*
|
||||
* Sun software process control block
|
||||
*/
|
||||
|
||||
#include <machine/reg.h>
|
||||
|
||||
#define MAXWIN 12 /* max number of windows currently supported */
|
||||
|
||||
/*
|
||||
* The system actually supports one more than the above number.
|
||||
* There is always one window reserved for trap handlers that
|
||||
* never has to be saved into the pcb struct.
|
||||
*/
|
||||
|
||||
#ifndef LOCORE
|
||||
#ifdef STD_REF_MMU
|
||||
#include <machine/vm_hat.h>
|
||||
#endif
|
||||
struct pcb {
|
||||
#ifdef STD_REF_MMU
|
||||
/* this struct MUST be 1K aligned -- checked for in assym.s */
|
||||
struct l1pt pcb_l1pt; /* level 1 page table */
|
||||
#endif
|
||||
label_t pcb_regs; /* saved pc and sp */
|
||||
int pcb_psr; /* processor status word */
|
||||
int pcb_uwm; /* user window mask */
|
||||
struct rwindow pcb_wbuf[MAXWIN]; /* user window save buffer */
|
||||
char *pcb_spbuf[MAXWIN]; /* sp's for each wbuf */
|
||||
int pcb_wbcnt; /* number of saved windows in pcb_wbuf */
|
||||
|
||||
int *pcb_psrp; /* psr pointer to en/disable coprocessors */
|
||||
struct fpu *pcb_fpctxp;/* pointer to fpu state */
|
||||
int pcb_fpflags; /* fpu enable flags */
|
||||
int *pcb_cpctxp; /* pointer to coprocessor state */
|
||||
int pcb_cpflags; /* coprocessor enable flags */
|
||||
|
||||
int pcb_flags; /* various state flags */
|
||||
int pcb_wocnt; /* window overflow count */
|
||||
int pcb_wucnt; /* window underflow count */
|
||||
};
|
||||
|
||||
#define pcb_pc pcb_regs.val[0]
|
||||
#define pcb_sp pcb_regs.val[1]
|
||||
|
||||
#define aston() {u.u_pcb.pcb_flags |= AST_SCHED;}
|
||||
|
||||
#define astoff() {u.u_pcb.pcb_flags &= ~AST_SCHED;}
|
||||
|
||||
#endif !LOCORE
|
||||
|
||||
/* pcb_flags */
|
||||
#define AST_SCHED 0x80000000 /* force a reschedule */
|
||||
#define AST_CLR 0x80000000
|
||||
#define PME_CLR 0
|
||||
#define AST_NONE 0
|
||||
|
||||
/* pcb_flags */
|
||||
#define CLEAN_WINDOWS 0x1 /* keep user regs clean */
|
||||
#define FIX_ALIGNMENT 0x2 /* fix unaligned references */
|
||||
|
||||
#endif /*!_sparc_pcb_h*/
|
||||
69
sys/sparc/psl.h
Normal file
69
sys/sparc/psl.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* @(#)psl.h 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1986 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _sparc_psl_h
|
||||
#define _sparc_psl_h
|
||||
|
||||
/*
|
||||
* Definition of bits in the Sun-4 PSR (Processor Status Register)
|
||||
* ________________________________________________________________________
|
||||
* | IMPL | VER | ICC | resvd | EC | EF | PIL | S | PS | ET | CWP |
|
||||
* | | | N | Z | V | C | | | | | | | | |
|
||||
* |------|-----|---|---|---|---|-------|----|----|-----|---|----|----|-----|
|
||||
* 31 28 27 24 23 22 21 20 19 14 13 12 11 8 7 6 5 4 0
|
||||
*
|
||||
* Reserved bits are defined to be initialized to zero and must
|
||||
* be preserved if written, for compatibility with future revisions.
|
||||
*/
|
||||
|
||||
#define PSR_CWP 0x0000001F /* current window pointer */
|
||||
#define PSR_ET 0x00000020 /* enable traps */
|
||||
#define PSR_PS 0x00000040 /* previous supervisor mode */
|
||||
#define PSR_S 0x00000080 /* supervisor mode */
|
||||
#define PSR_PIL 0x00000F00 /* processor interrupt level */
|
||||
#define PSR_EF 0x00001000 /* enable floating point unit */
|
||||
#define PSR_EC 0x00002000 /* enable coprocessor */
|
||||
#define PSR_RSV 0x000FC000 /* reserved */
|
||||
#define PSR_ICC 0x00F00000 /* integer condition codes */
|
||||
#define PSR_C 0x00100000 /* carry bit */
|
||||
#define PSR_V 0x00200000 /* overflow bit */
|
||||
#define PSR_Z 0x00400000 /* zero bit */
|
||||
#define PSR_N 0x00800000 /* negative bit */
|
||||
#define PSR_VER 0x0F000000 /* mask version */
|
||||
#define PSR_IMPL 0xF0000000 /* implementation */
|
||||
|
||||
#define PSL_ALLCC PSR_ICC /* for portability */
|
||||
#define SR_SMODE PSR_PS
|
||||
|
||||
/*
|
||||
* Handy psr values.
|
||||
*/
|
||||
#define PSL_USER (PSR_S) /* initial user psr */
|
||||
#define PSL_USERMASK (PSR_ICC) /* user variable psr bits */
|
||||
#define PSR_PIL1 0x100 /* for rounding up interrupt pri */
|
||||
|
||||
/*
|
||||
* Macros to decode psr.
|
||||
*/
|
||||
#define USERMODE(ps) (((ps) & PSR_PS) == 0)
|
||||
#define BASEPRI(ps) (((ps) & PSR_PIL) == 0)
|
||||
|
||||
/*
|
||||
* Convert system interrupt priorities (0-7) into a psr for splx.
|
||||
* In general, the processor priority (0-15) should be 2 times
|
||||
* the system pririty.
|
||||
*/
|
||||
#define pritospl(n) ((n) << 9)
|
||||
|
||||
/*
|
||||
* Convert a hardware interrupt priority level (0-15) into a psr for splx.
|
||||
* Also are macros to convert back.
|
||||
*/
|
||||
#define ipltospl(n) (((n) & 0xf) << 8)
|
||||
#define spltoipl(n) (((n) >> 8) & 0xf)
|
||||
#define spltopri(n) ((((n) + (1 << 8)) >> 9) & 0xf)
|
||||
|
||||
#endif /*!_sparc_psl_h*/
|
||||
254
sys/sparc/reg.h
Normal file
254
sys/sparc/reg.h
Normal file
@@ -0,0 +1,254 @@
|
||||
/* @(#)reg.h 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1985 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _sparc_reg_h
|
||||
#define _sparc_reg_h
|
||||
|
||||
/*
|
||||
* Location of the users' stored
|
||||
* registers relative to R0.
|
||||
* Usage is u.u_ar0[XX].
|
||||
*/
|
||||
#define PSR (0)
|
||||
#define PC (1)
|
||||
#define nPC (2)
|
||||
#define Y (3)
|
||||
#define G1 (4)
|
||||
#define G2 (5)
|
||||
#define G3 (6)
|
||||
#define G4 (7)
|
||||
#define G5 (8)
|
||||
#define G6 (9)
|
||||
#define G7 (10)
|
||||
#define O0 (11)
|
||||
#define O1 (12)
|
||||
#define O2 (13)
|
||||
#define O3 (14)
|
||||
#define O4 (15)
|
||||
#define O5 (16)
|
||||
#define O6 (17)
|
||||
#define O7 (18)
|
||||
|
||||
/* the following defines are for portability */
|
||||
#define PS PSR
|
||||
#define SP O6
|
||||
#define R0 O0
|
||||
#define R1 O1
|
||||
|
||||
/*
|
||||
* And now for something completely the same...
|
||||
*/
|
||||
#ifndef LOCORE
|
||||
struct regs {
|
||||
int r_psr; /* processor status register */
|
||||
int r_pc; /* program counter */
|
||||
int r_npc; /* next program counter */
|
||||
int r_y; /* the y register */
|
||||
int r_g1; /* user global regs */
|
||||
int r_g2;
|
||||
int r_g3;
|
||||
int r_g4;
|
||||
int r_g5;
|
||||
int r_g6;
|
||||
int r_g7;
|
||||
int r_o0;
|
||||
int r_o1;
|
||||
int r_o2;
|
||||
int r_o3;
|
||||
int r_o4;
|
||||
int r_o5;
|
||||
int r_o6;
|
||||
int r_o7;
|
||||
};
|
||||
|
||||
#define r_ps r_psr /* for portablility */
|
||||
#define r_r0 r_o0
|
||||
#define r_sp r_o6
|
||||
|
||||
#endif !LOCORE
|
||||
|
||||
/*
|
||||
* Floating point definitions.
|
||||
*/
|
||||
|
||||
#define FPU /* we have an external float unit */
|
||||
|
||||
#ifndef LOCORE
|
||||
|
||||
#define FQ_DEPTH 16 /* maximum instuctions in FQ */
|
||||
|
||||
/*
|
||||
* struct fp_status is the floating point processor state
|
||||
* struct fpu is the sum total of all possible floating point state
|
||||
* which includes the state of external floating point hardware,
|
||||
* fpa registers, etc..., if it exists.
|
||||
*/
|
||||
struct fpq {
|
||||
unsigned long *addr; /* address */
|
||||
unsigned long instr; /* instruction */
|
||||
};
|
||||
struct fq {
|
||||
union { /* FPU inst/addr queue */
|
||||
double whole;
|
||||
struct fpq fpq;
|
||||
} FQu;
|
||||
};
|
||||
|
||||
|
||||
#define FPU_REGS_TYPE unsigned
|
||||
#define FPU_FSR_TYPE unsigned
|
||||
|
||||
struct fp_status {
|
||||
union { /* FPU floating point regs */
|
||||
FPU_REGS_TYPE Fpu_regs[32]; /* 32 singles */
|
||||
double Fpu_dregs[16]; /* 16 doubles */
|
||||
} fpu_fr;
|
||||
FPU_FSR_TYPE Fpu_fsr; /* FPU status register */
|
||||
unsigned Fpu_flags; /* control flags */
|
||||
unsigned Fpu_extra; /* extra word */
|
||||
unsigned Fpu_qcnt; /* count of valid entries in fps_q */
|
||||
struct fq Fpu_q[FQ_DEPTH]; /* FPU instruction address queue */
|
||||
};
|
||||
|
||||
#define fpu_regs f_fpstatus.fpu_fr.Fpu_regs
|
||||
#define fpu_dregs f_fpstatus.fpu_fr.Fpu_dregs
|
||||
#define fpu_fsr f_fpstatus.Fpu_fsr
|
||||
#define fpu_flags f_fpstatus.Fpu_flags
|
||||
#define fpu_extra f_fpstatus.Fpu_extra
|
||||
#define fpu_q f_fpstatus.Fpu_q
|
||||
#define fpu_qcnt f_fpstatus.Fpu_qcnt
|
||||
|
||||
struct fpu {
|
||||
struct fp_status f_fpstatus;
|
||||
};
|
||||
#endif !LOCORE
|
||||
|
||||
|
||||
/*
|
||||
* Definition of bits in the Sun-4 FSR (Floating-point Status Register)
|
||||
* ________________________________________________________________________
|
||||
* | RD | RP | TEM | NS | res | vers | FTT | QNE | PR | FCC | AEXC | CEXC |
|
||||
* |-----|---- |-----|----|-----|------|-----|-----|----|-----|------|------|
|
||||
* 31 30 29 28 27 23 22 21 20 19 17 16 14 13 12 11 10 9 5 4 0
|
||||
*/
|
||||
#define FSR_CEXC 0x0000001f /* Current Exception */
|
||||
#define FSR_AEXC 0x000003e0 /* ieee accrued exceptions */
|
||||
#define FSR_FCC 0x00000c00 /* Floating-point Condition Codes */
|
||||
#define FSR_PR 0x00001000 /* Partial Remainder */
|
||||
#define FSR_QNE 0x00002000 /* Queue not empty */
|
||||
#define FSR_FTT 0x0001c000 /* Floating-point Trap Type */
|
||||
#define FSR_VERS 0x000e0000 /* version field */
|
||||
#define FSR_RESV 0x00300000 /* reserved */
|
||||
#define FSR_NS 0x00400000 /* non-standard fp */
|
||||
#define FSR_TEM 0x0f800000 /* ieee Trap Enable Mask */
|
||||
#define FSR_RP 0x30000000 /* Rounding Precision */
|
||||
#define FSR_RD 0xc0000000 /* Rounding Direction */
|
||||
|
||||
#define FSR_VERS_SHIFT (17) /* amount to shift version field */
|
||||
|
||||
/*
|
||||
* Definition of CEXC (Current EXCeption) bit field of fsr
|
||||
*/
|
||||
#define FSR_CEXC_NX 0x00000001 /* inexact */
|
||||
#define FSR_CEXC_DZ 0x00000002 /* divide-by-zero */
|
||||
#define FSR_CEXC_UF 0x00000004 /* underflow */.
|
||||
#define FSR_CEXC_OF 0x00000008 /* overflow */
|
||||
#define FSR_CEXC_NV 0x00000010 /* invalid */
|
||||
|
||||
/*
|
||||
* Definition of AEXC (Accrued EXCeption) bit field of fsr
|
||||
*/
|
||||
#define FSR_AEXC_NX (0x1 << 5) /* inexact */
|
||||
#define FSR_AEXC_DZ (0x2 << 5) /* divide-by-zero */
|
||||
#define FSR_AEXC_UF (0x4 << 5) /* underflow */.
|
||||
#define FSR_AEXC_OF (0x8 << 5) /* overflow */
|
||||
#define FSR_AEXC_NV (0x10 << 5) /* invalid */
|
||||
|
||||
/*
|
||||
* Defintion of FTT (Floating-point Trap Type) field within the FSR
|
||||
*/
|
||||
#define FTT_NONE 0 /* no excepitons */
|
||||
#define FTT_IEEE 1 /* IEEE exception */
|
||||
#define FTT_UNFIN 2 /* unfinished fpop */
|
||||
#define FTT_UNIMP 3 /* unimplemented fpop */
|
||||
#define FTT_SEQ 4 /* sequence error */
|
||||
#define FTT_ALIGN 5 /* alignment, by software convention */
|
||||
#define FTT_DFAULT 6 /* data fault, by software convention */
|
||||
#define FSR_FTT_SHIFT 14 /* shift needed to justfy ftt field */
|
||||
#define FSR_FTT_IEEE (FTT_IEEE << FSR_FTT_SHIFT)
|
||||
#define FSR_FTT_UNFIN (FTT_UNFIN << FSR_FTT_SHIFT)
|
||||
#define FSR_FTT_UNIMP (FTT_UNIMP << FSR_FTT_SHIFT)
|
||||
#define FSR_FTT_SEQ (FTT_SEQ << FSR_FTT_SHIFT)
|
||||
#define FSR_FTT_ALIGN (FTT_ALIGN << FSR_FTT_SHIFT)
|
||||
#define FSR_FTT_DFAULT (FTT_DFAULT << FSR_FTT_SHIFT)
|
||||
|
||||
/*
|
||||
* Values of VERS (version) field within the FSR
|
||||
* NOTE: these values are overloaded; the cpu type must be used to
|
||||
* further discriminate amongst these. For that reason, no #defines are
|
||||
* provided.
|
||||
*
|
||||
* Version cpu = 21-22, 51-54 cpu = 23-24, 55-57
|
||||
* 0 Weitek 1164/5 (FAB 1-4) TI 8847
|
||||
* 1 Weitek 1164/5 (FAB 5-6) LSI L64814
|
||||
* 2 TI 8847 TI TMS390C602A
|
||||
* 3 Weitek 3170 Weitek 3171
|
||||
* 4 Meiko ?
|
||||
* 5 ? ?
|
||||
* 6 ? ?
|
||||
* 7 No FP Hardware No FP Hardware
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Definition of TEM (Trap Enable Mask) bit field of fsr
|
||||
*/
|
||||
#define FSR_TEM_NX (0x1 << 23) /* inexact */
|
||||
#define FSR_TEM_DZ (0x2 << 23) /* divide-by-zero */
|
||||
#define FSR_TEM_UF (0x4 << 23) /* underflow */.
|
||||
#define FSR_TEM_OF (0x8 << 23) /* overflow */
|
||||
#define FSR_TEM_NV (0x10 << 23) /* invalid */
|
||||
|
||||
/*
|
||||
* Definition of RP (Rounding Precision) field of fsr
|
||||
*/
|
||||
#define RP_DBLEXT 0 /* double-extended */
|
||||
#define RP_SINGLE 1 /* single */
|
||||
#define RP_DOUBLE 2 /* double */
|
||||
#define RP_RESERVED 3 /* unused and reserved */
|
||||
|
||||
/*
|
||||
* Defintion of RD (Rounding Direction) field of fsr
|
||||
*/
|
||||
#define RD_NEAR 0 /* nearest or even if tie */
|
||||
#define RD_ZER0 1 /* to zero */
|
||||
#define RD_POSINF 2 /* positive infinity */
|
||||
#define RD_NEGINF 3 /* negative infinity */
|
||||
|
||||
/*
|
||||
* Definition of the FP enable flags of the pcb struct
|
||||
* Normal operation, all flags are zero
|
||||
*/
|
||||
#define FP_UNINITIALIZED 1
|
||||
#define FP_STARTSIG 2
|
||||
#define FP_DISABLE 4
|
||||
#define FP_ENABLE 8
|
||||
|
||||
#ifndef LOCORE
|
||||
/*
|
||||
* How a register window looks on the stack.
|
||||
*/
|
||||
struct rwindow {
|
||||
int rw_local[8]; /* locals */
|
||||
int rw_in[8]; /* ins */
|
||||
};
|
||||
|
||||
#define rw_fp rw_in[6] /* frame pointer */
|
||||
#define rw_rtn rw_in[7] /* return address */
|
||||
|
||||
#endif !LOCORE
|
||||
|
||||
#endif /*!_sparc_reg_h*/
|
||||
34
sys/sparc/setjmp.h
Normal file
34
sys/sparc/setjmp.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* @(#)setjmp.h 1.1 94/10/31 SMI; from UCB 4.1 83/05/03 */
|
||||
|
||||
#ifndef __sparc_setjmp_h
|
||||
#define __sparc_setjmp_h
|
||||
|
||||
/*
|
||||
* onsstack,sigmask,sp,pc,npc,psr,g1,o0,wbcnt (sigcontext).
|
||||
* All else recovered by under/over(flow) handling.
|
||||
*/
|
||||
#define _JBLEN 9
|
||||
|
||||
typedef int jmp_buf[_JBLEN];
|
||||
|
||||
/*
|
||||
* One extra word for the "signal mask saved here" flag.
|
||||
*/
|
||||
typedef int sigjmp_buf[_JBLEN+1];
|
||||
|
||||
int setjmp(/* jmp_buf env */);
|
||||
int _setjmp(/* jmp_buf env */);
|
||||
int sigsetjmp(/* sigjmp_buf env, int savemask */);
|
||||
void longjmp(/* jmp_buf env, int val */);
|
||||
void _longjmp(/* jmp_buf env, int val */);
|
||||
void siglongjmp(/* sigjmp_buf env, int val */);
|
||||
|
||||
/*
|
||||
* Routines that call setjmp have strange control flow graphs,
|
||||
* since a call to a routine that calls resume/longjmp will eventually
|
||||
* return at the setjmp site, not the original call site. This
|
||||
* utterly wrecks control flow analysis.
|
||||
*/
|
||||
#pragma unknown_control_flow(sigsetjmp, setjmp, _setjmp)
|
||||
|
||||
#endif /* !__sparc_setjmp_h */
|
||||
654
sys/sparc/sparc_subr.s
Normal file
654
sys/sparc/sparc_subr.s
Normal file
@@ -0,0 +1,654 @@
|
||||
/* @(#)sparc_subr.s 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1989 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
* General assembly language routines.
|
||||
* It is the intent of this file to contain routines that are
|
||||
* independent of the specific kernel architecture, and those that are
|
||||
* common across kernel architectures.
|
||||
* As architectures diverge, and implementations of specific
|
||||
* architecture-dependent routines change, the routines should be moved
|
||||
* from this file into the respective ../`arch -k`/subr.s file.
|
||||
* For example, this file used to contain getidprom(), but the sun4c
|
||||
* getidprom() diverged from the sun4 getidprom() so getidprom() was
|
||||
* moved.
|
||||
*/
|
||||
|
||||
#include <machine/param.h>
|
||||
#include <machine/asm_linkage.h>
|
||||
#include <machine/psl.h>
|
||||
#include <machine/mmu.h>
|
||||
#include <machine/intreg.h>
|
||||
#include <machine/enable.h>
|
||||
#include "assym.s"
|
||||
|
||||
.seg "text"
|
||||
.align 4
|
||||
|
||||
/*
|
||||
* Macro to raise processor priority level.
|
||||
* Avoid dropping processor priority if already at high level.
|
||||
* Note, these macros return the priority field
|
||||
* as it appears in place in the psr
|
||||
*/
|
||||
#define RAISE(level) \
|
||||
mov %psr, %o0; \
|
||||
and %o0, PSR_PIL, %g1; \
|
||||
cmp %g1, (level << PSR_PIL_BIT); \
|
||||
bl,a 1f; \
|
||||
andn %o0, PSR_PIL, %g1; \
|
||||
retl; \
|
||||
nop; \
|
||||
1: or %g1, (level << PSR_PIL_BIT), %g1; \
|
||||
mov %g1, %psr; \
|
||||
nop; \
|
||||
retl; \
|
||||
nop;
|
||||
|
||||
#define SETPRI(level) \
|
||||
mov %psr, %o0; \
|
||||
andn %o0, PSR_PIL, %g1; \
|
||||
or %g1, (level << PSR_PIL_BIT), %g1; \
|
||||
mov %g1, %psr; \
|
||||
nop; \
|
||||
retl; \
|
||||
nop;
|
||||
|
||||
/*
|
||||
* Berkley 4.3 introduced symbolically named interrupt levels
|
||||
* as a way deal with priority in a machine independent fashion.
|
||||
* Numbered priorities are machine specific, and should be
|
||||
* discouraged where possible.
|
||||
*
|
||||
* Note, for the machine specific priorities there are
|
||||
* examples listed for devices that use a particular priority.
|
||||
* It should not be construed that all devices of that
|
||||
* type should be at that priority. It is currently were
|
||||
* the current devices fit into the priority scheme based
|
||||
* upon time criticalness.
|
||||
*
|
||||
* The underlying assumption of these assignments is that
|
||||
* SPARC IPL 10 is the highest level from which a device
|
||||
* routine can call wakeup. Devices that interrupt from higher
|
||||
* levels are restricted in what they can do. If they need
|
||||
* kernels services they should schedule a routine at a lower
|
||||
* level (via software interrupt) to do the required
|
||||
* processing.
|
||||
*
|
||||
* Examples of this higher usage:
|
||||
* Level Usage
|
||||
* 15 Asynchronous memory exceptions (Non-maskable)
|
||||
* 14 Profiling clock (and PROM uart polling clock)
|
||||
* 13 Audio device (on sun4c)
|
||||
* 12 Serial ports
|
||||
* 11 Floppy controller (on sun4c)
|
||||
*
|
||||
* The serial ports request lower level processing on level 6.
|
||||
* Audio and floppy request lower level processing on level 4.
|
||||
*
|
||||
* Also, almost all splN routines (where N is a number or a
|
||||
* mnemonic) will do a RAISE(), on the assumption that they are
|
||||
* never used to lower our priority.
|
||||
* The exceptions are:
|
||||
* spl8() Because you can't be above 15 to begin with!
|
||||
* splzs() Because this is used at boot time to lower our
|
||||
* priority, to allow the PROM to poll the uart.
|
||||
* spl1() Used by queuerun() to lower priority
|
||||
* spl0() Used to lower priority to 0.
|
||||
* splsoftclock() Used by hardclock to lower priority.
|
||||
*/
|
||||
|
||||
/* locks out all interrupts, including memory errors */
|
||||
ENTRY(spl8)
|
||||
SETPRI(15)
|
||||
|
||||
/* just below the level that profiling runs */
|
||||
#if defined(sun4c) || defined(sun4m)
|
||||
ALTENTRY(splaudio) /* only meaningful on 4c, 4m */
|
||||
#endif /* sun4c || sun4m */
|
||||
ENTRY(spl7)
|
||||
RAISE(13)
|
||||
|
||||
/* sun specific - highest priority onboard serial i/o zs ports */
|
||||
ALTENTRY(splzs)
|
||||
SETPRI(12) /* Can't be a RAISE, as it's used to lower us */
|
||||
|
||||
|
||||
/*
|
||||
* should lock out clocks and all interrupts,
|
||||
* as you can see, there are exceptions
|
||||
*/
|
||||
ALTENTRY(splhigh)
|
||||
|
||||
/* the standard clock interrupt priority */
|
||||
ALTENTRY(splclock)
|
||||
|
||||
/* highest priority for any tty handling */
|
||||
ALTENTRY(spltty)
|
||||
|
||||
/* highest priority required for protection of buffered io system */
|
||||
ALTENTRY(splbio)
|
||||
|
||||
/* machine specific */
|
||||
ENTRY2(spl6,spl5)
|
||||
RAISE(10)
|
||||
|
||||
/*
|
||||
* machine specific
|
||||
* for sun, some frame buffers must be at this priority
|
||||
*/
|
||||
ENTRY(spl4)
|
||||
RAISE(8)
|
||||
|
||||
/* highest level that any network device will use */
|
||||
/* must be lower than highest interrupting ether device */
|
||||
ENTRY(splimp)
|
||||
RAISE(7)
|
||||
|
||||
/*
|
||||
* machine specific
|
||||
* for sun, devices with limited buffering: tapes, ethernet
|
||||
*/
|
||||
ENTRY(spl3)
|
||||
RAISE(6)
|
||||
|
||||
/*
|
||||
* machine specific - not as time critical as above
|
||||
* for sun, disks
|
||||
*/
|
||||
ENTRY(spl2)
|
||||
RAISE(4)
|
||||
|
||||
ENTRY(spl1)
|
||||
SETPRI(2)
|
||||
|
||||
/* highest level that any protocol handler will run */
|
||||
ENTRY(splnet)
|
||||
RAISE(1)
|
||||
|
||||
/* softcall priority */
|
||||
/* used by hardclock to LOWER priority */
|
||||
ENTRY(splsoftclock)
|
||||
SETPRI(1)
|
||||
|
||||
/* allow all interrupts */
|
||||
ENTRY(spl0)
|
||||
SETPRI(0)
|
||||
|
||||
/*
|
||||
* splvm will raise the processor priority to the value implied by splvm_val
|
||||
* which is initialized by the autoconfiguration code to a sane default.
|
||||
*/
|
||||
ENTRY(splvm)
|
||||
sethi %hi(_splvm_val), %o0
|
||||
ld [%o0 + %lo(_splvm_val)], %o0
|
||||
|
||||
! fall through to splr
|
||||
/*
|
||||
* splr(psr_pri_field)
|
||||
* splr is like splx but will only raise the priority and never drop it
|
||||
*/
|
||||
ENTRY(splr)
|
||||
mov %psr, %o1; ! get current psr value
|
||||
and %o1, PSR_PIL, %o3 ! interlock slot, mask out rest of psr
|
||||
and %o0, PSR_PIL, %o0 ! only look at priority, paranoid
|
||||
cmp %o3, %o0 ! if current pri < new pri, set new pri
|
||||
bl pri_common ! use common code to set priority
|
||||
andn %o1, PSR_PIL, %o2 ! zero current priority level
|
||||
retl ! return old priority
|
||||
mov %o3, %o0
|
||||
|
||||
/*
|
||||
* splx(psr_pri_field)
|
||||
* set the priority to given value
|
||||
* Note, this takes a sun4 priority number and 'ors' it into the psr
|
||||
* A spl() returns this type of cookie so that a splx() does the right thing
|
||||
*/
|
||||
ENTRY(splx)
|
||||
pri_set_common:
|
||||
mov %psr, %o1 ! get current priority level
|
||||
andn %o1, PSR_PIL, %o2 ! zero current priority level
|
||||
and %o0, PSR_PIL, %o0 ! only look at priority, paranoid
|
||||
pri_common:
|
||||
or %o2, %o0, %o2 ! or in new priority level
|
||||
mov %o2, %psr ! set priority
|
||||
and %o1, PSR_PIL, %o0 ! psr delay...
|
||||
retl
|
||||
.empty ! next instruction ok in delay slot
|
||||
|
||||
/*
|
||||
* no_fault()
|
||||
* turn off fault catching.
|
||||
*/
|
||||
ENTRY(no_fault)
|
||||
sethi %hi(_uunix), %o2
|
||||
ld [%o2 + %lo(_uunix)], %o2
|
||||
retl
|
||||
clr [%o2+U_LOFAULT] ! turn off lofault
|
||||
|
||||
/*
|
||||
* on_fault()
|
||||
* Catch lofault faults. Like setjmp except it returns one
|
||||
* if code following causes uncorrectable fault. Turned off
|
||||
* by calling no_fault().
|
||||
*/
|
||||
ENTRY(on_fault)
|
||||
set lfault, %o0 ! use special jmp_buf
|
||||
set catch_fault, %o1
|
||||
sethi %hi(_uunix), %o2
|
||||
ld [%o2 + %lo(_uunix)], %o2
|
||||
b _setjmp ! let setjmp do the rest
|
||||
st %o1, [%o2+U_LOFAULT] ! put catch_fault in u.u_lofault
|
||||
|
||||
catch_fault:
|
||||
save %sp, -WINDOWSIZE, %sp ! goto next window so that we can rtn
|
||||
set lfault, %o0 ! use special jmp_buf
|
||||
sethi %hi(_uunix), %o2
|
||||
ld [%o2 + %lo(_uunix)], %o2
|
||||
b _longjmp ! let longjmp do the rest
|
||||
clr [%o2+U_LOFAULT] ! turn off lofault
|
||||
|
||||
.reserve lfault, (2*4), "data", 4 ! special jmp_buf for on_fault
|
||||
|
||||
|
||||
/*
|
||||
* Setjmp and longjmp implement non-local gotos using state vectors
|
||||
* type label_t.
|
||||
*
|
||||
* setjmp(lp)
|
||||
* label_t *lp;
|
||||
*/
|
||||
ENTRY(setjmp)
|
||||
st %o7, [%o0 + L_PC] ! save return address
|
||||
st %sp, [%o0 + L_SP] ! save stack ptr
|
||||
retl
|
||||
clr %o0 ! return 0
|
||||
|
||||
/*
|
||||
* longjmp(lp)
|
||||
* label_t *lp;
|
||||
*/
|
||||
ENTRY(longjmp)
|
||||
!
|
||||
! The following save is required so that an extra register
|
||||
! window is flushed. Flush_windows flushes nwindows-2
|
||||
! register windows. If setjmp and longjmp are called from
|
||||
! within the same window, that window will not get pushed
|
||||
! out onto the stack without the extra save below. Tail call
|
||||
! optimization can lead to callers of longjmp executing
|
||||
! from a window that could be the same as the setjmp,
|
||||
! thus the need for the following save.
|
||||
!
|
||||
save %sp, -SA(WINDOWSIZE), %sp
|
||||
call _flush_windows ! flush all but this window
|
||||
nop
|
||||
ld [%i0 + L_PC], %i7 ! restore return addr
|
||||
ld [%i0 + L_SP], %fp ! restore sp for dest on foreign stack
|
||||
ret ! return 1
|
||||
restore %g0, 1, %o0 ! takes underflow, switches stacks
|
||||
|
||||
#ifdef sun4
|
||||
/*
|
||||
* Enable and disable video.
|
||||
*/
|
||||
ENTRY(setvideoenable)
|
||||
tst %o0
|
||||
bnz _on_enablereg
|
||||
mov ENA_VIDEO, %o0
|
||||
b,a _off_enablereg
|
||||
#endif sun4
|
||||
|
||||
#ifndef sun4m
|
||||
/*
|
||||
* Enable DVMA.
|
||||
*/
|
||||
ENTRY(enable_dvma)
|
||||
mov ENA_SDVMA, %o0 ! enable system DVMA
|
||||
|
||||
! fall through to on_enablereg
|
||||
|
||||
/*
|
||||
* Turn on a bit in the system enable register.
|
||||
* on_enablereg((u_char)bit)
|
||||
*/
|
||||
ENTRY(on_enablereg)
|
||||
mov %psr, %o3
|
||||
or %o3, PSR_PIL, %g1 ! spl hi to lock enable reg update
|
||||
mov %g1, %psr
|
||||
nop; nop; ! psr delay
|
||||
sethi %hi(_enablereg), %o1 ! software copy of enable register
|
||||
ldub [%o1 + %lo(_enablereg)], %g1 ! get software copy
|
||||
set ENABLEREG, %o2 ! address of real version in hardware
|
||||
bset %o0, %g1 ! turn on bit
|
||||
stb %g1, [%o1 + %lo(_enablereg)] ! update software copy
|
||||
stba %g1, [%o2]ASI_CTL ! write out new enable register
|
||||
stb %g1, [%o1 + %lo(_enablereg)] ! update soft copy, for cache off
|
||||
mov %o3, %psr ! restore psr
|
||||
nop ! psr delay
|
||||
retl
|
||||
nop
|
||||
#endif !sun4m
|
||||
|
||||
/*
|
||||
* usec_delay(n)
|
||||
* int n;
|
||||
* delay for n microseconds. numbers <= 0 delay 1 usec
|
||||
*
|
||||
* the inner loop counts for current sun4s, initialized in startup():
|
||||
* inner loop is 2 cycles, the outer loop adds 3 more cycles.
|
||||
* Cpudelay*cycletime(ns)*2 + cycletime(ns)*3 >= 1000
|
||||
*
|
||||
* model cycletime(ns) Cpudelay
|
||||
* 110 66 7
|
||||
* 260 60 7
|
||||
* 330 40 11
|
||||
* 470 30 16
|
||||
*
|
||||
* sun4m var. calculated in machdep.c
|
||||
*/
|
||||
ENTRY(usec_delay)
|
||||
sethi %hi(_Cpudelay), %o5
|
||||
ld [%o5 + %lo(_Cpudelay)], %o4 ! microsecond countdown counter
|
||||
orcc %o4, 0, %o3 ! set cc bits to nz
|
||||
|
||||
1: bnz 1b ! microsecond countdown loop
|
||||
subcc %o3, 1, %o3 ! 2 instructions in loop
|
||||
|
||||
subcc %o0, 1, %o0 ! now, for each microsecond...
|
||||
bg 1b ! go n times through above loop
|
||||
orcc %o4, 0, %o3
|
||||
retl
|
||||
nop
|
||||
|
||||
/*
|
||||
* movtuc(length, from, to, table)
|
||||
*
|
||||
* VAX movtuc instruction (sort of).
|
||||
*/
|
||||
ENTRY(movtuc)
|
||||
tst %o0
|
||||
ble 2f ! check length
|
||||
clr %o4
|
||||
|
||||
ldub [%o1 + %o4], %g1 ! get next byte in string
|
||||
0:
|
||||
ldub [%o3 + %g1], %g1 ! get corresponding table entry
|
||||
tst %g1 ! escape char?
|
||||
bnz 1f
|
||||
stb %g1, [%o2 + %o4] ! delay slot, store it
|
||||
|
||||
retl ! return (bytes moved)
|
||||
mov %o4, %o0
|
||||
1:
|
||||
inc %o4 ! increment index
|
||||
cmp %o4, %o0 ! index < length ?
|
||||
bl,a 0b
|
||||
ldub [%o1 + %o4], %g1 ! delay slot, get next byte in string
|
||||
2:
|
||||
retl ! return (bytes moved)
|
||||
mov %o4, %o0
|
||||
|
||||
#ifndef sun4m
|
||||
/*
|
||||
* Disable DVMA.
|
||||
*/
|
||||
ENTRY(disable_dvma)
|
||||
mov ENA_SDVMA, %o0 ! disable system DVMA
|
||||
|
||||
! fall through to off_enablereg
|
||||
|
||||
/*
|
||||
* Turn off a bit in the system enable register.
|
||||
* off_enablereg((u_char)bit)
|
||||
*/
|
||||
ENTRY(off_enablereg)
|
||||
mov %psr, %o3
|
||||
or %o3, PSR_PIL, %g1 ! spl hi to lock enable reg update
|
||||
mov %g1, %psr
|
||||
nop; nop; ! psr delay
|
||||
sethi %hi(_enablereg), %o1 ! software copy of enable register
|
||||
ldub [%o1 + %lo(_enablereg)], %g1 ! get software copy
|
||||
set ENABLEREG, %o2 ! address of real version in hardware
|
||||
bclr %o0, %g1 ! turn off bit
|
||||
stb %g1, [%o1 + %lo(_enablereg)] ! update software copy
|
||||
stba %g1, [%o2]ASI_CTL ! write out new enable register
|
||||
mov %o3, %psr ! restore psr
|
||||
nop ! psr delay
|
||||
retl
|
||||
nop
|
||||
#endif !sun4m
|
||||
|
||||
/*
|
||||
* scanc(length, string, table, mask)
|
||||
*
|
||||
* VAX scanc instruction.
|
||||
*/
|
||||
ENTRY(scanc)
|
||||
tst %o0
|
||||
ble 1f ! check length
|
||||
clr %o4
|
||||
0:
|
||||
ldub [%o1 + %o4], %g1 ! get next byte in string
|
||||
cmp %o4, %o0 ! interlock slot, index < length ?
|
||||
ldub [%o2 + %g1], %g1 ! get corresponding table entry
|
||||
bge 1f ! interlock slot
|
||||
btst %o3, %g1 ! apply the mask
|
||||
bz,a 0b
|
||||
inc %o4 ! delay slot, increment index
|
||||
1:
|
||||
retl ! return(length - index)
|
||||
sub %o0, %o4, %o0
|
||||
|
||||
/*
|
||||
* if a() calls b() calls caller(),
|
||||
* caller() returns return address in a().
|
||||
*/
|
||||
ALTENTRY(caller)
|
||||
retl
|
||||
mov %i7, %o0
|
||||
|
||||
/*
|
||||
* if a() calls callee(), callee() returns the
|
||||
* return address in a();
|
||||
*/
|
||||
ALTENTRY(callee)
|
||||
retl
|
||||
mov %o7, %o0
|
||||
|
||||
/*
|
||||
* Get vector base register
|
||||
*/
|
||||
ALTENTRY(gettbr)
|
||||
ALTENTRY(getvbr)
|
||||
retl ! leaf routine return
|
||||
mov %tbr, %o0 ! read trap base register
|
||||
|
||||
/*
|
||||
* Get processor state register
|
||||
*/
|
||||
ALTENTRY(getpsr)
|
||||
retl ! leaf routine return
|
||||
mov %psr, %o0 ! read program status register
|
||||
|
||||
/*
|
||||
* return the current stack pointer
|
||||
*/
|
||||
ALTENTRY(getsp)
|
||||
retl ! leaf routine return
|
||||
mov %sp, %o0 ! return sp
|
||||
|
||||
/*
|
||||
* insque(entryp, predp)
|
||||
*
|
||||
* Insert entryp after predp in a doubly linked list.
|
||||
*/
|
||||
ENTRY(_insque)
|
||||
ld [%o1], %g1 ! predp->forw
|
||||
st %o1, [%o0 + 4] ! entryp->back = predp
|
||||
st %g1, [%o0] ! entryp->forw = predp->forw
|
||||
st %o0, [%o1] ! predp->forw = entryp
|
||||
retl
|
||||
st %o0, [%g1 + 4] ! predp->forw->back = entryp
|
||||
|
||||
/*
|
||||
* remque(entryp)
|
||||
*
|
||||
* Remove entryp from a doubly linked list
|
||||
*/
|
||||
ENTRY(_remque)
|
||||
ld [%o0], %g1 ! entryp->forw
|
||||
ld [%o0 + 4], %g2 ! entryp->back
|
||||
st %g1, [%g2] ! entryp->back = entryp->forw
|
||||
retl
|
||||
st %g2, [%g1 + 4] ! entryp->forw = entryp->back
|
||||
|
||||
#ifndef sun4m
|
||||
/*
|
||||
* Turn on or off bits in the interrupt register.
|
||||
* We must lock out interrupts, since we don't have an atomic or/and to mem.
|
||||
* set_intreg(bit, flag)
|
||||
* int bit; bit mask in interrupt reg
|
||||
* int flag; 0 = off, otherwise on
|
||||
*/
|
||||
ALTENTRY(set_intreg)
|
||||
ALTENTRY(set_intmask) ! alt entry point, mask is here.
|
||||
mov %psr, %g2
|
||||
or %g2, PSR_PIL, %g1 ! spl hi to protect intreg update
|
||||
mov %g1, %psr
|
||||
nop; ! psr delay
|
||||
tst %o1
|
||||
set INTREG_ADDR, %o3 ! interrupt register address
|
||||
ldub [%o3], %g1 ! read interrupt register
|
||||
bnz,a 1f
|
||||
bset %o0, %g1 ! on
|
||||
bclr %o0, %g1 ! off
|
||||
1:
|
||||
stb %g1, [%o3] ! request a level 1 interrupt
|
||||
mov %g2, %psr ! splx
|
||||
nop ! psr delay
|
||||
retl
|
||||
nop
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* return 1 if an interrupt is being serviced (on interrupt stack),
|
||||
* otherwise return 0.
|
||||
#ifdef MULTIPROCESSOR
|
||||
* The interrupt stack resides in per-cpu storage high in the
|
||||
* virtual address space, higher than the user stacks and lower
|
||||
* than the kernel stack. Use a range check instead of just
|
||||
* assuming any stack ptr less than eintstack is on this stack.
|
||||
#endif MULTIPROCESSOR
|
||||
*/
|
||||
ALTENTRY(servicing_interrupt)
|
||||
clr %o0 ! assume no
|
||||
#ifdef MULTIPROCESSOR
|
||||
set intstack, %g1
|
||||
cmp %sp, %g1 ! check if on int stack
|
||||
blu 1f ! if no, return.
|
||||
.empty ! first half of following set ok in trailer.
|
||||
#endif MULTIPROCESSOR
|
||||
set eintstack, %g1
|
||||
cmp %sp, %g1 ! check if on int stack
|
||||
bleu,a 1f ! no, we annul the next instruction
|
||||
mov 1,%o0
|
||||
1:
|
||||
retl ! leaf routine return
|
||||
nop !
|
||||
/*
|
||||
* Turn on a software interrupt (H/W level 1).
|
||||
*/
|
||||
ALTENTRY(siron)
|
||||
set IR_SOFT_INT1, %o0
|
||||
b _set_intreg
|
||||
mov 1, %o1
|
||||
|
||||
#ifdef sun4
|
||||
/*
|
||||
* Enable and disable video interrupt. (sun4 only)
|
||||
* setintrenable(value)
|
||||
* int value; 0 = off, otherwise on
|
||||
*/
|
||||
ALTENTRY(setintrenable)
|
||||
mov %o0, %o1
|
||||
b _set_intreg
|
||||
mov IR_ENA_VID8, %o0
|
||||
#endif sun4
|
||||
|
||||
/***********************************************************************
|
||||
* Hardware Access Routines
|
||||
*/
|
||||
|
||||
/*
|
||||
* Make the "LDSTUB" instruction available
|
||||
* for C code to use. Parameter is ptr to char;
|
||||
* sets byte to 0xFF and returns the old value.
|
||||
*/
|
||||
ALTENTRY(ldstub)
|
||||
mov %o0, %o1
|
||||
retl
|
||||
ldstub [%o1], %o0
|
||||
|
||||
/*
|
||||
* Make the "SWAPL" instruction available
|
||||
* for C code to use. First parm is new value,
|
||||
* second parm is ptr to int; returns old value.
|
||||
*/
|
||||
ALTENTRY(swapl)
|
||||
|
||||
#ifdef JUNK_AROUND_SWAPL
|
||||
mov %psr, %o4
|
||||
andn %o4, PSR_ET, %o3
|
||||
mov %o3, %psr ! disable traps
|
||||
nop ; nop ; nop ! complete paranoia
|
||||
mov 1, %o5 ! what we will write to ECC register ...
|
||||
ld [%o1], %g0 ! prevent tlb miss on swap
|
||||
#endif
|
||||
swap [%o1], %o0
|
||||
|
||||
#ifdef JUNK_AROUND_SWAPL
|
||||
sta %o5, [%g0]0x2F ! force some MBus activity (write ECC reg)
|
||||
nop ; nop ; nop ! complete paranoia
|
||||
mov %o4, %psr ! restore trap enable bit
|
||||
nop ; nop ; nop ! complete paranoia
|
||||
#endif
|
||||
retl
|
||||
nop
|
||||
|
||||
/*
|
||||
* Generic "test and set" function
|
||||
*/
|
||||
ALTENTRY(tas)
|
||||
mov %o0, %o1
|
||||
b _swapl
|
||||
sub %g0, 1, %o0
|
||||
|
||||
/*
|
||||
* Generic "test and clear" function
|
||||
*/
|
||||
ALTENTRY(tac)
|
||||
mov %o0, %o1
|
||||
b _swapl
|
||||
mov %g0, %o0
|
||||
|
||||
/*
|
||||
* stack register access
|
||||
*/
|
||||
ALTENTRY(stackpointer)
|
||||
retl
|
||||
mov %o6, %o0
|
||||
|
||||
ALTENTRY(framepointer)
|
||||
retl
|
||||
mov %i6, %o0
|
||||
|
||||
/*
|
||||
* flush(va)
|
||||
*/
|
||||
ALTENTRY(flush)
|
||||
retl
|
||||
iflush %o0
|
||||
788
sys/sparc/swtch.s
Normal file
788
sys/sparc/swtch.s
Normal file
@@ -0,0 +1,788 @@
|
||||
/* @(#)swtch.s 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1987 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Process switching routines.
|
||||
*/
|
||||
|
||||
#include <machine/param.h>
|
||||
#include <machine/asm_linkage.h>
|
||||
#include <machine/mmu.h>
|
||||
#include <machine/psl.h>
|
||||
#include <machine/reg.h>
|
||||
#include <machine/pcb.h>
|
||||
#include "assym.s"
|
||||
|
||||
.seg "text"
|
||||
.align 4
|
||||
|
||||
/*
|
||||
* whichqs tells which of the 32 queues qs have processes in them.
|
||||
* Setrq puts processes into queues, Remrq removes them from queues.
|
||||
* The running process is on no queue, other processes are on a queue
|
||||
* related to p->p_pri, divided by 4 actually to shrink the 0-127 range
|
||||
* of priorities into the 32 available queues.
|
||||
*/
|
||||
|
||||
/*
|
||||
* setrq(p)
|
||||
*
|
||||
* Call should be made at spl6(), and p->p_stat should be SRUN
|
||||
*/
|
||||
ENTRY(setrq)
|
||||
ld [%o0 + P_RLINK], %g1 ! firewall: p->p_rlink must be 0
|
||||
ldub [%o0 + P_PRI], %g2 ! interlock slot
|
||||
tst %g1
|
||||
bz,a 1f
|
||||
srl %g2, 2, %g4 ! delay slot, p->p_pri / 4
|
||||
|
||||
save %sp, -SA(MINFRAME), %sp ! need to buy a window
|
||||
set 2f, %o0 ! p->p_rlink not 0
|
||||
call _panic
|
||||
nop
|
||||
2:
|
||||
.asciz "setrq"
|
||||
.align 4
|
||||
1:
|
||||
sll %g4, 3, %g2 ! (p->p_pri / 4) * sizeof(*qs)
|
||||
set _qs+4, %g3 ! back ptr of queue head
|
||||
ld [%g3 + %g2], %g3 ! get qs[p->p_pri/4].ph_rlink
|
||||
mov 1, %g2 ! interlock slot
|
||||
ld [%g3], %g1 ! insque(p, qs[p->p_pri].ph_rlink)
|
||||
st %g3, [%o0 + 4]
|
||||
st %g1, [%o0]
|
||||
st %o0, [%g1 + 4]
|
||||
st %o0, [%g3]
|
||||
sethi %hi(_whichqs), %g7
|
||||
ld [%g7 + %lo(_whichqs)], %g1
|
||||
sll %g2, %g4, %g2 ! interlock slot
|
||||
bset %g2, %g1
|
||||
retl
|
||||
st %g1, [%g7 + %lo(_whichqs)]
|
||||
|
||||
/*
|
||||
* remrq(p)
|
||||
*
|
||||
* Call should be made at spl6().
|
||||
*/
|
||||
ENTRY(remrq)
|
||||
ldub [%o0 + P_PRI], %g3
|
||||
sethi %hi(_whichqs), %g7
|
||||
ld [%g7 + %lo(_whichqs)], %g4
|
||||
srl %g3, 2, %g3 ! p->p_pri / 4
|
||||
mov 1, %g1 ! test appropriate bit in whichqs
|
||||
sll %g1, %g3, %g3
|
||||
btst %g3, %g4
|
||||
bnz,a 1f
|
||||
ld [%o0], %g1 ! delay slot, remque(p)
|
||||
|
||||
save %sp, -SA(MINFRAME), %sp ! need to buy a window
|
||||
set 2f, %o0 ! p not in appropriate queue
|
||||
call _panic
|
||||
nop
|
||||
2:
|
||||
.asciz "remrq"
|
||||
.align 4
|
||||
1:
|
||||
ld [%o0 + 4], %g2
|
||||
st %g1, [%g2]
|
||||
st %g2, [%g1 + 4]
|
||||
cmp %g1, %g2
|
||||
bne 3f ! queue not empty
|
||||
andn %g4, %g3, %g4 ! delay slot, queue empty,
|
||||
st %g4, [%g7 + %lo(_whichqs)]
|
||||
3:
|
||||
retl
|
||||
clr [%o0 + P_RLINK] ! p->p_rlink = 0
|
||||
|
||||
.global _qrunflag, _runqueues
|
||||
|
||||
/*
|
||||
* When no processes are on the runq, swtch branches to idle
|
||||
* to wait for something to come ready.
|
||||
*/
|
||||
.global _idle
|
||||
#ifdef LWP
|
||||
.global ___Nrunnable, _lwpschedule
|
||||
#endif LWP
|
||||
_idle:
|
||||
sethi %hi(_whichqs), %g1
|
||||
ld [%g1 + %lo(_whichqs)], %g1
|
||||
tst %g1
|
||||
bz 1f ! none check other stuff
|
||||
nop
|
||||
|
||||
!
|
||||
! Found something.
|
||||
!
|
||||
mov %psr, %g1 ! splhi
|
||||
bclr PSR_PIL, %g1
|
||||
or %g1, 10 << PSR_PIL_BIT, %g1
|
||||
mov %g1, %psr
|
||||
nop ! psr delay
|
||||
b sw_testq
|
||||
nop ! more psr delay
|
||||
|
||||
!
|
||||
! Test for other async events.
|
||||
!
|
||||
1:
|
||||
#ifdef LWP
|
||||
sethi %hi(___Nrunnable),%g2
|
||||
ld [%g2+%lo(___Nrunnable)],%g2
|
||||
tst %g2
|
||||
bz 2f
|
||||
nop
|
||||
call _lwpschedule
|
||||
nop
|
||||
set CONTEXT_REG, %g2
|
||||
stba %g0, [%g2]ASI_CTL
|
||||
2:
|
||||
#endif LWP
|
||||
sethi %hi(_qrunflag), %g1 ! need to run stream queues?
|
||||
ldub [%g1 + %lo(_qrunflag)], %g1
|
||||
tst %g1
|
||||
#ifndef SAS
|
||||
bz _idle ! no
|
||||
nop
|
||||
#else SAS
|
||||
/*
|
||||
* Instead of idle-looping when we have nothing to do,
|
||||
* fake a bunch of clock ticks.
|
||||
*/
|
||||
bnz 1f
|
||||
nop
|
||||
call _fake_clockticks
|
||||
nop
|
||||
b,a _idle
|
||||
1:
|
||||
#endif SAS
|
||||
|
||||
call _runqueues ! go do it
|
||||
nop
|
||||
b,a _idle
|
||||
|
||||
/*
|
||||
* swtch()
|
||||
*/
|
||||
ENTRY(swtch)
|
||||
save %sp, -SA(MINFRAME), %sp
|
||||
mov %psr, %l0 ! save PSR
|
||||
andn %l0, PSR_PIL, %g1
|
||||
or %g1, 10 << PSR_PIL_BIT, %g1
|
||||
mov %g1, %psr ! spl hi
|
||||
nop
|
||||
mov 1, %g2
|
||||
sethi %hi(_noproc), %g6
|
||||
st %g2, [%g6 + %lo(_noproc)]
|
||||
sethi %hi(_runrun), %g6
|
||||
clr [%g6 + %lo(_runrun)]
|
||||
sethi %hi(_masterprocp), %l1
|
||||
ld [%l1 + %lo(_masterprocp)], %l2
|
||||
tst %l2 ! if no proc skip saving state
|
||||
bz sw_testq ! this happens after exit
|
||||
.empty ! hush assembler warnings
|
||||
|
||||
set _uunix, %g5 ! XXX - global u register?
|
||||
ld [%g5], %g5
|
||||
ld [%g5+PCB_FLAGS], %g1 ! pcb_flags &= ~AST_SCHED
|
||||
set AST_SCHED, %g2
|
||||
bclr %g2, %g1
|
||||
st %g1, [%g5+PCB_FLAGS]
|
||||
|
||||
!
|
||||
! Loop through whichqs bits looking for a non-empty queue.
|
||||
!
|
||||
sw_testq:
|
||||
sethi %hi(_whichqs), %g7
|
||||
ld [%g7 + %lo(_whichqs)], %l1
|
||||
mov 1, %l2
|
||||
mov 0, %l3
|
||||
btst %l2, %l1
|
||||
1:
|
||||
bnz sw_foundq ! found one
|
||||
nop
|
||||
|
||||
sll %l2, 1, %l2
|
||||
add %l3, 8, %l3
|
||||
tst %l2
|
||||
bnz,a 1b
|
||||
btst %l2, %l1 ! delay slot
|
||||
!
|
||||
! Found no procs, goto idle loop
|
||||
!
|
||||
mov %psr, %g1 ! allow interrupts
|
||||
andn %g1, PSR_PIL, %g1
|
||||
mov %g1, %psr
|
||||
b,a _idle
|
||||
|
||||
sw_foundq:
|
||||
set _qs, %l4 ! get qs proc list
|
||||
ld [%l4 + %l3], %l4 ! p=qs[bit].ph_link=highest pri process
|
||||
ld [%l4], %g1 ! remque(p)
|
||||
cmp %l4, %g1 ! is queue empty?
|
||||
bne,a 2f
|
||||
ld [%l4 + 4], %g2 ! delay slot
|
||||
|
||||
sw_bad:
|
||||
set 4f, %o0
|
||||
call _panic
|
||||
nop
|
||||
4:
|
||||
.asciz "swtch"
|
||||
.align 4
|
||||
|
||||
2:
|
||||
st %g1, [%g2]
|
||||
st %g2, [%g1 + 4]
|
||||
bclr %l2, %l1 ! whichqs if queue is now empty
|
||||
cmp %g1, %g2
|
||||
be,a 3f ! queue now empty
|
||||
st %l1, [%g7 + %lo(_whichqs)]
|
||||
3:
|
||||
sethi %hi(_noproc), %g6
|
||||
clr [%g6 + %lo(_noproc)]
|
||||
ld [%l4 + P_WCHAN], %g1 ! firewalls
|
||||
ldub [%l4 + P_STAT], %g2
|
||||
tst %g1
|
||||
bz,a 4f ! p->p_wchan must be 0
|
||||
cmp %g2, SRUN ! p->p_stat must be SRUN
|
||||
|
||||
b,a sw_bad
|
||||
4:
|
||||
be,a 5f
|
||||
clr [%l4 + P_RLINK] ! p->p_rlink = 0
|
||||
|
||||
b,a sw_bad
|
||||
5:
|
||||
sethi %hi(_masterprocp), %l1
|
||||
ld [%l1 + %lo(_masterprocp)], %l2
|
||||
set _cnt, %g1 ! cnt.v_swtch++
|
||||
ld [%g1 + V_SWTCH], %g2
|
||||
cmp %l2, %l4 ! if (p == masterprocp) ...
|
||||
be 6f ! skip resume() call
|
||||
inc 1, %g2 ! delay slot
|
||||
mov %l4, %o0 ! resume(p)
|
||||
call _resume
|
||||
.empty
|
||||
6:
|
||||
st %g2, [%g1 + V_SWTCH] ! delay slot, update ctx switch count
|
||||
|
||||
mov %psr, %g2 ! restore processor priority
|
||||
andn %g2, PSR_PIL, %g2
|
||||
and %l0, PSR_PIL, %l0
|
||||
or %l0, %g2, %l0
|
||||
mov %l0, %psr
|
||||
nop ! psr delay
|
||||
ret
|
||||
restore
|
||||
|
||||
/*
|
||||
* masterprocp is the pointer to the proc structure for the currently
|
||||
* mapped u area. It is used to set up the mapping for the u area
|
||||
* by the debugger since the u area is not in the Sysmap.
|
||||
*/
|
||||
.seg "data"
|
||||
.global _masterprocp
|
||||
_masterprocp:
|
||||
.word 0 ! struct proc *masterprocp
|
||||
|
||||
/*
|
||||
* XXX The following is a hack so that we don't have to splzs() to
|
||||
* XXX protect the u_pcb from being clobbered by a BREAK that causes
|
||||
* XXX kadb to be entered. We also don't want to find masterprocp and
|
||||
* XXX uunix inconsistent.
|
||||
* XXX Instead, we set kadb_defer before the critical region and clear
|
||||
* XXX it afterwards.
|
||||
* XXX Where zsa_xsint would normally CALL_DEBUG, it first checks
|
||||
* XXX kadb_defer, and if set it sets kadb_want instead.
|
||||
* XXX We check kadb_want (after clearing kadb_defer, to avoid races)
|
||||
* XXX and, if set, we call kadb.
|
||||
*/
|
||||
.global _kadb_defer
|
||||
_kadb_defer:
|
||||
.word 0 ! int kadb_defer
|
||||
|
||||
.global _kadb_want
|
||||
_kadb_want:
|
||||
.word 0 ! int kadb_want
|
||||
|
||||
.seg "text"
|
||||
|
||||
/*
|
||||
* resume(p)
|
||||
*/
|
||||
ENTRY(resume)
|
||||
save %sp, -SA(MINFRAME), %sp
|
||||
mov %psr, %l0
|
||||
sethi %hi(_masterprocp), %l1
|
||||
ld [%l1 + %lo(_masterprocp)], %l2
|
||||
tst %l2 ! if there is a current proc,
|
||||
bnz 1f ! save its state,
|
||||
! if no proc, skip saving state
|
||||
! this happens after exit
|
||||
! trash_windows, no current proc
|
||||
or %l0, PSR_PIL, %o4 ! spl hi
|
||||
set _scb, %o1 ! get NW-1, which is
|
||||
ldub [%o1 + 31], %o3 ! last byte of text fault trap vector
|
||||
and %l0, 0x1f, %o2 ! mask cwp
|
||||
cmp %o2, %o3 ! compare cwp to NW-1
|
||||
be 2f ! calculate wim
|
||||
mov 1, %o3 ! if equal new wim is 1, wraparound
|
||||
inc %o2
|
||||
sll %o3, %o2, %o3 ! if less create wim of sll(1,cwp+1)
|
||||
2:
|
||||
mov %o4, %psr ! raise priority
|
||||
nop;nop ! psr delay
|
||||
mov (10<<PSR_PIL_BIT), %l6 ! clock priority is 10
|
||||
mov %o3, %wim ! install new wim
|
||||
b 4f
|
||||
mov %l0, %psr ! restore priority
|
||||
|
||||
! store state for current proc
|
||||
1: set _uunix, %g5 ! XXX - global u register?
|
||||
ld [%g5], %g5
|
||||
|
||||
ld [%g5 + PCB_FPCTXP], %l3 ! is floating point being used
|
||||
tst %l3 ! by the current process?
|
||||
bz 5f ! if not skip saving FPU state
|
||||
st %l0, [%g5 + PCB_PSR] ! save psr (for PSR_PIL)
|
||||
|
||||
sethi %hi(_fpu_exists), %l4 ! fpu is present flag
|
||||
ld [%l4 + %lo(_fpu_exists)], %l4
|
||||
tst %l4 ! well, do we have FPU hardware?
|
||||
bz 5f ! no fpu, nothing to do
|
||||
nop
|
||||
st %fsr, [%l3 + FPCTX_FSR] ! save current fpu state
|
||||
STORE_FPREGS(%l3) ! assumes %l3 points at fpreg save area
|
||||
5:
|
||||
ld [%l2 + P_AS], %o0 ! does it
|
||||
tst %o0 ! have an address space?
|
||||
bz 3f ! skip if not
|
||||
mov (10<<PSR_PIL_BIT), %l6 ! clock priority is 10
|
||||
|
||||
call _rm_asrss ! find out memory claim for
|
||||
nop ! as associated with this process
|
||||
|
||||
st %o0, [%l2 + P_RSSIZE] ! store with process
|
||||
3:
|
||||
!
|
||||
! Flush the register windows to the stack.
|
||||
! This saves all the windows except the one we're in now.
|
||||
! The WIM will be immediately ahead of us after this.
|
||||
!
|
||||
call _flush_windows
|
||||
nop
|
||||
4:
|
||||
andn %l0, PSR_PIL, %l0 ! clear priotity field
|
||||
or %l0, %l6, %l0 ! spl clock, or in new priority
|
||||
mov %l0, %psr ! raise priority
|
||||
|
||||
!
|
||||
! Begin critical region where we can't let BREAK call kadb
|
||||
! (this also serves as a psr delay)
|
||||
!
|
||||
sethi %hi(_kadb_defer), %g1
|
||||
mov 1, %g2
|
||||
st %g2, [%g1 + %lo(_kadb_defer)]
|
||||
sethi %hi(_uunix), %g5
|
||||
ld [%g5 + %lo(_uunix)], %g5
|
||||
st %i7, [%g5 + PCB_PC] ! save ret pc and sp in pcb
|
||||
st %fp, [%g5 + PCB_SP]
|
||||
st %i0, [%l1 + %lo(_masterprocp)]
|
||||
|
||||
!
|
||||
! Switch to new u area.
|
||||
!
|
||||
set CONTEXT_REG, %l3 ! setup kernel context (0)
|
||||
stba %g0, [%l3]ASI_CTL
|
||||
|
||||
ld [%i0 + P_UAREA], %g5 ! address of uarea for new proc
|
||||
set _uunix, %g2 ! interlock, set uunix - XXX
|
||||
st %g5, [%g2] ! install new uarea
|
||||
|
||||
!
|
||||
! We snarf the PC and SP here so we can end the critical
|
||||
! region that much sooner.
|
||||
ld [%g5 + PCB_PC], %i7
|
||||
ld [%g5 + PCB_SP], %fp
|
||||
!
|
||||
! End critical region where we can't let BREAK call kadb.
|
||||
! (We just changed uunix, so if we take a BREAK we'll store the
|
||||
! registers for kadb on the new pcb, which isn't in use now).
|
||||
! So we clear the flag, and then check to see if we owe kadb a
|
||||
! call.
|
||||
!
|
||||
sethi %hi(_kadb_defer), %g1 ! clear defer first
|
||||
st %g0, [%g1 + %lo(_kadb_defer)]
|
||||
sethi %hi(_kadb_want), %g1 ! then check want
|
||||
ld [%g1 + %lo(_kadb_want)], %g2
|
||||
tst %g2 ! in case BREAK'd between
|
||||
bz 1f
|
||||
ld [%i0 + P_AS], %l0 ! check p->p_as
|
||||
|
||||
mov %g5, %l1 ! save %g5 (uptr) over call
|
||||
call _call_debug_from_asm
|
||||
st %g0, [%g1 + %lo(_kadb_want)]
|
||||
mov %l1, %g5 ! restore %g5
|
||||
1:
|
||||
|
||||
!
|
||||
! Check to see if we already have context. If so then set up the
|
||||
! context. Otherwise we leave the proc in the kernels context which
|
||||
! will cause it to fault if it ever gets back to userland.
|
||||
!
|
||||
tst %l0
|
||||
bz 1f ! if zero, skip ahead
|
||||
nop ! delay slot
|
||||
|
||||
ld [%l0 + A_HAT_CTX], %l0 ! check (p->p_as->a_hat.hat_ctx)
|
||||
tst %l0
|
||||
bz 1f ! if zero, skip ahead
|
||||
nop ! delay slot
|
||||
|
||||
!
|
||||
! Switch to the procs context.
|
||||
!
|
||||
ldub [%l0 + C_NUM], %g1 ! get context number
|
||||
stba %g1, [%l3]ASI_CTL ! set it up
|
||||
sethi %hi(_ctx_time), %l1
|
||||
lduh [%l1 + %lo(_ctx_time)], %g1
|
||||
add %g1, 1, %g2
|
||||
sth %g1, [%l0 + C_TIME] ! Set context clock to LRU clock
|
||||
sth %g2, [%l1 + %lo(_ctx_time)]! ++ctx_time
|
||||
ldub [%l0], %g1 ! get the context bits
|
||||
andn %g1, C_CLEAN_BITMASK, %g1 ! zero the context's clean bit
|
||||
stb %g1, [%l0]
|
||||
1:
|
||||
!
|
||||
! if the new process does not have any fp ctx
|
||||
! we do nothing with regards to the fpu
|
||||
! if the new process does have fp ctx
|
||||
! we enable the fpu and load its context now
|
||||
! if we have fpu hardware
|
||||
!
|
||||
ld [%g5 + PCB_FPCTXP], %g1 ! new process fp ctx pointer
|
||||
tst %g1 ! if new process fp ctx printer == 0
|
||||
bz 2f ! it hasn't used the fpu yet
|
||||
ld [%g5 + PCB_PSR], %l0
|
||||
|
||||
sethi %hi(_fp_ctxp), %g2 ! old fp ctx pointer
|
||||
ld [%g2 + %lo(_fp_ctxp)], %l1
|
||||
|
||||
sethi %hi(_fpu_exists), %l2
|
||||
ld [%l2 + %lo(_fpu_exists)], %l3
|
||||
tst %l3
|
||||
bz 2f
|
||||
st %g1, [%g2 + %lo(_fp_ctxp)] ! install new fp context
|
||||
|
||||
!
|
||||
! if this is the same process that last used the fpu
|
||||
! we can skip loading its state
|
||||
!
|
||||
cmp %g1, %l1 ! if the fp ctx belongs to new process
|
||||
be 3f ! nothing to do
|
||||
! maybe branch to 2 instead
|
||||
|
||||
!
|
||||
! New process is different than last process that used the fpu
|
||||
! Load the new process floating point state
|
||||
! The fpu must be enabled via the psr as the last
|
||||
! process may not have used it
|
||||
!
|
||||
set PSR_EF, %l6
|
||||
mov %psr, %g3
|
||||
or %g3, %l6, %g3
|
||||
mov %g3, %psr
|
||||
|
||||
nop; nop; nop ! psr delay, (paranoid)
|
||||
ld [%g1 + FPCTX_FSR], %fsr ! restore fsr
|
||||
LOAD_FPREGS(%g1)
|
||||
3:
|
||||
ld [%i0 + P_STACK], %l1 ! initial kernel sp for this process
|
||||
add %l1, (MINFRAME + PSR*4), %l1 ! psr addr on process stack
|
||||
ld [%l1], %l3 ! read the psr value from the stack
|
||||
or %l0, %l6, %l0 ! enable fpu, new psr
|
||||
or %l3, %l6, %g1 ! enable fpu, psr stored in stack
|
||||
st %g1, [%l1] ! update stack, sys_rtt will use it
|
||||
2:
|
||||
!
|
||||
! Return to new proc. Restore will cause underflow.
|
||||
!
|
||||
sub %fp, WINDOWSIZE, %sp ! establish sp, for interrupt overflow
|
||||
mov %psr, %g2
|
||||
and %l0, PSR_PIL, %l0 ! restore PSR_PIL
|
||||
andn %g2, PSR_PIL, %g2
|
||||
or %l0, %g2, %l0
|
||||
mov %l0, %psr
|
||||
nop ! psr delay
|
||||
ret
|
||||
restore
|
||||
|
||||
/*
|
||||
* start_child(parentp, parentu, childp, nfp, new_context)
|
||||
* struct proc *parentp, *childp;
|
||||
* struct user *parentu;
|
||||
* int *new_context;
|
||||
* struct file **nfp;
|
||||
* This saves the parent's state such that when resume'd, the
|
||||
* parent will return to the caller of startchild.
|
||||
* Equivalent to the first half of resume.
|
||||
* It then calls conschild to create the child process
|
||||
*
|
||||
* We always resume the parent in the kernel.
|
||||
* The child usually resumes in userland.
|
||||
*
|
||||
* start_child is only called from newproc
|
||||
*/
|
||||
ENTRY(start_child)
|
||||
save %sp, -SA(MINFRAME), %sp
|
||||
mov %psr, %l0
|
||||
sethi %hi(_masterprocp), %l1
|
||||
ld [%l1 + %lo(_masterprocp)], %l2
|
||||
|
||||
!
|
||||
! Begin critical region where we can't let BREAK call kadb
|
||||
! (This critical region lasts through cons_child, ends in yield_child.)
|
||||
!
|
||||
sethi %hi(_kadb_defer), %g1
|
||||
mov 1, %g2
|
||||
st %g2, [%g1 + %lo(_kadb_defer)]
|
||||
|
||||
!
|
||||
! arrange for the parent to resume the caller of start child
|
||||
!
|
||||
set _uunix, %l3 ! get u pointer, XXX, global u register
|
||||
ld [%l3], %l4
|
||||
st %l0, [%l4 + PCB_PSR] ! save psr, ret pc, and sp in pcb
|
||||
st %i7, [%l4 + PCB_PC]
|
||||
st %fp, [%l4 + PCB_SP]
|
||||
|
||||
ld [%l2 + P_AS], %o0 ! does it have an address space?
|
||||
sethi %hi(_cons_child), %l5 ! interlock slot
|
||||
tst %o0
|
||||
bz 1f ! skip if not
|
||||
or %l5, %lo(_cons_child), %l5
|
||||
|
||||
call _rm_asrss ! find out memory claim for
|
||||
nop ! as associated with this process
|
||||
st %o0, [%l2 + P_RSSIZE] ! store with process
|
||||
1:
|
||||
jmp %l5
|
||||
restore ! give back register window
|
||||
! args already in the 'out' register
|
||||
|
||||
/*
|
||||
* yield_child(parent_ar0, child_stack, childp, parent_pid, childu, seg)
|
||||
*
|
||||
* Should be called at splclock()!
|
||||
* used in fork() instead of newproc/sleep/swtch/resume
|
||||
* cons up a child process stack and start it running
|
||||
* basically copy the needed parts of the kernel stack frame of
|
||||
* the parent, fixing appropriate entries for the child
|
||||
*/
|
||||
ENTRY(yield_child)
|
||||
save %sp, -WINDOWSIZE, %sp
|
||||
|
||||
call _flush_windows ! get the all registers on the stack
|
||||
sethi %hi(PSR_C), %l2 ! delay slot, set C bit in %l2
|
||||
! the parent will eventually return
|
||||
! via resume to cons_child which
|
||||
! will return to newproc
|
||||
!
|
||||
! Saved %l5 flags child is kernel proc. kernel procs never return to
|
||||
! user land, so we skip setting up the child's u_ar0 regs. This is
|
||||
! required, since if the parent is itself a kernel proc, it's
|
||||
! u_ar0 may not be meaningful.
|
||||
!
|
||||
ld [%i1 + 5*4], %l5
|
||||
tst %l5
|
||||
bnz 1f
|
||||
nop
|
||||
|
||||
! struct regs psr, pc, ncp, y
|
||||
ld [%i0 + PSR*4], %o4
|
||||
ld [%i0 + nPC*4], %o5 ! skip trap instruction
|
||||
andn %o4, %l2, %o4 ! clear carry bit
|
||||
st %o4, [%i1 + MINFRAME + PSR*4]
|
||||
st %o5, [%i1 + MINFRAME + PC*4] ! pc = npc
|
||||
add %o5, 4, %o5 ! npc += 4
|
||||
st %o5, [%i1 + MINFRAME + nPC*4]
|
||||
ld [%i0 + Y*4], %o4
|
||||
st %o4, [%i1 + MINFRAME + Y*4]
|
||||
|
||||
! struct regs, user globals
|
||||
ld [%i0 + G1*4], %o4
|
||||
st %o4, [%i1 + MINFRAME + G1*4]
|
||||
ldd [%i0 + G2*4], %o4
|
||||
std %o4, [%i1 + MINFRAME + G2*4]
|
||||
ldd [%i0 + G4*4], %o4
|
||||
std %o4, [%i1 + MINFRAME + G4*4]
|
||||
ldd [%i0 + G6*4], %o4
|
||||
std %o4, [%i1 + MINFRAME + G6*4]
|
||||
|
||||
! struct regs, user outs
|
||||
mov %i3, %o4 ! parent pid childs ret val r0
|
||||
mov 1, %o5 ! 1 childs ret value r1
|
||||
std %o4, [%i1 + MINFRAME + O0*4]
|
||||
ldd [%i0 + O2*4], %o4
|
||||
std %o4, [%i1 + MINFRAME + O2*4]
|
||||
ldd [%i0 + O4*4], %o4
|
||||
std %o4, [%i1 + MINFRAME + O4*4]
|
||||
ldd [%i0 + O6*4], %o4
|
||||
std %o4, [%i1 + MINFRAME + O6*4]
|
||||
1:
|
||||
! fake a return from syscall() to return to the child
|
||||
mov %psr, %g1 ! spl hi
|
||||
or %g1, PSR_PIL, %g2
|
||||
mov %g2, %psr
|
||||
|
||||
set _masterprocp, %o0 ! install child proc as master
|
||||
st %i2, [%o0]
|
||||
set _uunix, %o1 ! install child uarea - XXX
|
||||
st %i4, [%o1] ! global uarea pointer?
|
||||
|
||||
!
|
||||
! End critical region where we can't let BREAK call kadb.
|
||||
! (We just changed uunix, so if we take a BREAK we'll store the
|
||||
! registers for kadb on the new pcb, which isn't in use now).
|
||||
! So we clear the flag, and then check to see if we owe kadb a
|
||||
! call.
|
||||
!
|
||||
sethi %hi(_kadb_defer), %g1 ! clear defer first
|
||||
st %g0, [%g1 + %lo(_kadb_defer)]
|
||||
sethi %hi(_kadb_want), %g1 ! then check want
|
||||
ld [%g1 + %lo(_kadb_want)], %g2
|
||||
tst %g2 ! in case BREAK'd between
|
||||
bz 1f
|
||||
ld [%i2 + P_AS], %l4 ! check p->p_as
|
||||
|
||||
call _call_debug_from_asm
|
||||
st %g0, [%g1 + %lo(_kadb_want)]
|
||||
1:
|
||||
|
||||
!
|
||||
! Check to see if we already have context. If so then set up the
|
||||
! context. Otherwise we leave the proc in the kernels context which
|
||||
! will cause it to fault if it ever gets back to userland.
|
||||
!
|
||||
set CONTEXT_REG, %l3 ! setup context reg pointer
|
||||
tst %l4
|
||||
bz 1f ! if zero, skip ahead
|
||||
|
||||
! delay slot, get new process start address
|
||||
ld [%i1 + 6*4], %l5 ! %l6 has proc start address
|
||||
|
||||
ld [%l4 + A_HAT_CTX], %l4 ! check (p->p_as->a_hat.hat_ctx)
|
||||
tst %l4
|
||||
bnz 2f ! if zero, skip ahead
|
||||
.empty ! hush assembler warnings
|
||||
1: mov %i1, %fp ! install the new childs stack
|
||||
|
||||
stba %g0, [%l3]ASI_CTL ! if no context, use kernels
|
||||
|
||||
3: sub %fp, WINDOWSIZE, %sp ! establish sp, for interrupt overflow
|
||||
mov %psr, %g1 ! spl 0
|
||||
andn %g1, PSR_PIL, %g1
|
||||
mov %g1, %psr
|
||||
ld [%i1], %i0 ! %l0 has arg, set arg to new proc
|
||||
jmp %l5
|
||||
restore ! restore causes underflow
|
||||
|
||||
!
|
||||
! Switch to the procs context.
|
||||
!
|
||||
2: ldub [%l4 + C_NUM], %g1 ! get context number
|
||||
stba %g1, [%l3]ASI_CTL ! set it up
|
||||
sethi %hi(_ctx_time), %l1
|
||||
lduh [%l1 + %lo(_ctx_time)], %g1
|
||||
add %g1, 1, %g2
|
||||
sth %g1, [%l4 + C_TIME] ! Set context clock to LRU clock
|
||||
ldub [%l4], %g1 ! get the context bits
|
||||
andn %g1, C_CLEAN_BITMASK, %g1 ! zero the context's clean bit
|
||||
stb %g1, [%l4]
|
||||
b 3b
|
||||
sth %g2, [%l1 + %lo(_ctx_time)]! ++ctx_time
|
||||
|
||||
/*
|
||||
* finishexit(vaddr)
|
||||
* Done with the current stack;
|
||||
* switch to the exit stack, segu_release, and swtch.
|
||||
*/
|
||||
ENTRY(finishexit)
|
||||
save %sp, -SA(MINFRAME), %sp
|
||||
|
||||
! XXX - previous calls caused windows to be flushed
|
||||
! so the following flush may not be needed
|
||||
call _flush_windows ! dump reigster file now
|
||||
nop ! registers needed for post mortem
|
||||
|
||||
mov %psr, %l4
|
||||
or %l4, PSR_PIL, %o5
|
||||
mov %o5, %psr ! spl hi
|
||||
set eintstack, %o3 ! Are we on interrupt stack?
|
||||
cmp %sp, %o3 ! If so, panic
|
||||
ble 1f
|
||||
.empty
|
||||
set eexitstack, %o3
|
||||
sub %o3, SA(MINFRAME), %sp ! switch to exit stack
|
||||
|
||||
set intu, %o2 ! temporary uarea
|
||||
set _uunix, %o1
|
||||
st %o2, [%o1]
|
||||
|
||||
set _noproc, %o2 ! no process now
|
||||
mov 1, %o1
|
||||
st %o1, [%o2]
|
||||
|
||||
call _segu_release
|
||||
mov %i0, %o0 ! arg to segu_release
|
||||
|
||||
set _masterprocp, %o5
|
||||
clr [%o5]
|
||||
mov %l4, %psr ! restore previous priority
|
||||
nop
|
||||
b,a _swtch
|
||||
/* NOTREACHED */
|
||||
1:
|
||||
set 3f, %o0
|
||||
call _panic
|
||||
nop
|
||||
3: .asciz "finishexit"
|
||||
.align 4
|
||||
|
||||
/*
|
||||
* Setmmucntxt(procp)
|
||||
*
|
||||
* initialize mmu context for a process, if it has one
|
||||
*/
|
||||
ENTRY(setmmucntxt)
|
||||
ld [%o0 + P_AS], %o1 ! check p->p_as
|
||||
set CONTEXT_REG, %o4
|
||||
tst %o1
|
||||
bz 1f ! if zero, skip ahead
|
||||
mov %g0, %o0
|
||||
|
||||
ld [%o1 + A_HAT_CTX], %o1 ! check (p->p_as->a_hat.hat_ctx)
|
||||
tst %o1
|
||||
bz 1f ! if zero, skip ahead
|
||||
mov %g0, %o0
|
||||
|
||||
!
|
||||
! Switch to the procs context.
|
||||
!
|
||||
ldub [%o1 + C_NUM], %o0 ! get context number
|
||||
sethi %hi(_ctx_time), %o5
|
||||
lduh [%o5 + %lo(_ctx_time)], %o2
|
||||
add %o2, 1, %o3
|
||||
sth %o2, [%o1 + C_TIME] ! Set context clock to LRU clock
|
||||
sth %o3, [%o5 + %lo(_ctx_time)]! ++ctx_time
|
||||
ldub [%o1], %g1 ! get the context bits
|
||||
andn %g1, C_CLEAN_BITMASK, %g1 ! zero the context's clean bit
|
||||
stb %g1, [%o1]
|
||||
1:
|
||||
retl
|
||||
stba %o0, [%o4]ASI_CTL ! set it up
|
||||
126
sys/sparc/trap.h
Normal file
126
sys/sparc/trap.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/* @(#)trap.h 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1985 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _sparc_trap_h
|
||||
#define _sparc_trap_h
|
||||
|
||||
/*
|
||||
* Trap type values.
|
||||
*/
|
||||
#define TT(X) ((X)<<4)
|
||||
|
||||
/*
|
||||
* The Coprocessor bit.
|
||||
*/
|
||||
#define CP_BIT 0x20
|
||||
|
||||
#define SPARC_V8_TRAPS
|
||||
#ifdef SPARC_V8_TRAPS
|
||||
/*
|
||||
* The Error (versus Fault==Exception) bit.
|
||||
*/
|
||||
#define ERR_BIT 0x20
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Hardware traps.
|
||||
*/
|
||||
#define T_RESET 0x00
|
||||
#define T_TEXT_FAULT 0x01
|
||||
#ifdef SPARC_V8_TRAPS
|
||||
#define T_TEXT_ERROR (0x1 | ERR_BIT)
|
||||
#endif
|
||||
#define T_UNIMP_INSTR 0x02
|
||||
#define T_PRIV_INSTR 0x03
|
||||
#define T_FP_DISABLED 0x04
|
||||
#define T_CP_DISABLED (0x4 | CP_BIT)
|
||||
#define T_WIN_OVERFLOW 0x05
|
||||
#define T_WIN_UNDERFLOW 0x06
|
||||
#define T_ALIGNMENT 0x07
|
||||
#define T_FP_EXCEPTION 0x08
|
||||
#define T_CP_EXCEPTION (0x8 | CP_BIT)
|
||||
#define T_DATA_FAULT 0x09
|
||||
#ifdef SPARC_V8_TRAPS
|
||||
#define T_DATA_ERROR (0x9 | ERR_BIT)
|
||||
#endif
|
||||
#define T_TAG_OVERFLOW 0x0A
|
||||
#define T_INT 0x10
|
||||
#define T_INT_LEVEL 0x0F
|
||||
#define T_INT_LEVEL_1 0x11
|
||||
#define T_INT_LEVEL_2 0x12
|
||||
#define T_INT_LEVEL_3 0x13
|
||||
#define T_INT_LEVEL_4 0x14
|
||||
#define T_INT_LEVEL_5 0x15
|
||||
#define T_INT_LEVEL_6 0x16
|
||||
#define T_INT_LEVEL_7 0x17
|
||||
#define T_INT_LEVEL_8 0x18
|
||||
#define T_INT_LEVEL_9 0x19
|
||||
#define T_INT_LEVEL_10 0x1A
|
||||
#define T_INT_LEVEL_11 0x1B
|
||||
#define T_INT_LEVEL_12 0x1C
|
||||
#define T_INT_LEVEL_13 0x1D
|
||||
#define T_INT_LEVEL_14 0x1E
|
||||
#define T_INT_LEVEL_15 0x1F
|
||||
#ifdef SPARC_V8_TRAPS
|
||||
#define T_UMIMP_FLUSH 0x25
|
||||
#endif
|
||||
#define T_IDIV0 0x2A
|
||||
#define T_DATA_STORE 0x2B
|
||||
#ifdef SPARC_V8_TRAPS
|
||||
#define T_DATA_MISS 0x2C
|
||||
#define T_TEXT_MISS 0x3C
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Software traps (ticc instructions).
|
||||
*/
|
||||
#define ST_SYSCALL 0x00
|
||||
#define ST_BREAKPOINT 0x01
|
||||
#define ST_DIV0 0x02
|
||||
#define ST_FLUSH_WINDOWS 0x03
|
||||
#define ST_CLEAN_WINDOWS 0x04
|
||||
#define ST_RANGE_CHECK 0x05
|
||||
#define ST_FIX_ALIGN 0x06
|
||||
#define ST_INT_OVERFLOW 0x07
|
||||
|
||||
#define ST_GETCC 0x20
|
||||
#define ST_SETCC 0x21
|
||||
#if defined(sun4c) || defined(sun4m)
|
||||
#define ST_MON_BREAKPOINT 0x7F
|
||||
#endif sun4c
|
||||
|
||||
/*
|
||||
* Software trap vectors 16 - 31 are reserved for use by the user
|
||||
* and will not be usurped by Sun.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Software trap type values.
|
||||
*/
|
||||
#define T_SOFTWARE_TRAP 0x80
|
||||
#define T_ESOFTWARE_TRAP 0xFF
|
||||
#define T_SYSCALL (T_SOFTWARE_TRAP + ST_SYSCALL)
|
||||
#define T_BREAKPOINT (T_SOFTWARE_TRAP + ST_BREAKPOINT)
|
||||
#define T_DIV0 (T_SOFTWARE_TRAP + ST_DIV0)
|
||||
#define T_FLUSH_WINDOWS (T_SOFTWARE_TRAP + ST_FLUSH_WINDOWS)
|
||||
#define T_CLEAN_WINDOWS (T_SOFTWARE_TRAP + ST_CLEAN_WINDOWS)
|
||||
#define T_RANGE_CHECK (T_SOFTWARE_TRAP + ST_RANGE_CHECK)
|
||||
#define T_FIX_ALIGN (T_SOFTWARE_TRAP + ST_FIX_ALIGN)
|
||||
#define T_INT_OVERFLOW (T_SOFTWARE_TRAP + ST_INT_OVERFLOW)
|
||||
|
||||
#define T_GETCC (T_SOFTWARE_TRAP + ST_GETCC)
|
||||
#define T_SETCC (T_SOFTWARE_TRAP + ST_SETCC)
|
||||
|
||||
/*
|
||||
* Pseudo traps.
|
||||
*/
|
||||
#define T_INTERRUPT 0x100
|
||||
#define T_SPURIOUS (T_INTERRUPT | T_INT)
|
||||
#define T_FAULT 0x200
|
||||
#define T_AST 0x400
|
||||
#define T_ZERO 0x00
|
||||
|
||||
#endif /*!_sparc_trap_h*/
|
||||
247
sys/sparc/underflow.s
Normal file
247
sys/sparc/underflow.s
Normal file
@@ -0,0 +1,247 @@
|
||||
/* @(#)underflow.s 1.1 94/10/31 SMI */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1989 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
#include <machine/asm_linkage.h>
|
||||
#include <machine/param.h>
|
||||
#include <machine/psl.h>
|
||||
#include <machine/trap.h>
|
||||
#include <machine/mmu.h>
|
||||
#include <machine/pte.h>
|
||||
#include <machine/reg.h>
|
||||
#include <machine/buserr.h>
|
||||
#include <machine/pcb.h>
|
||||
|
||||
#include "assym.s"
|
||||
|
||||
.seg "text"
|
||||
.align 4
|
||||
|
||||
PROT_INVAL = (PG_V) >> PG_S_BIT
|
||||
VALID = (PG_V) >> PG_S_BIT
|
||||
USER_INVAL = (PG_S) >> PG_S_BIT
|
||||
|
||||
/*
|
||||
* Window underflow trap handler.
|
||||
*/
|
||||
.global window_underflow
|
||||
window_underflow:
|
||||
|
||||
#ifdef PERFMETER
|
||||
sethi %hi(_underflowcnt), %l5
|
||||
ld [%l5 + %lo(_underflowcnt)], %l7
|
||||
inc %l7
|
||||
st %l7, [%l5 + %lo(_underflowcnt)]
|
||||
#endif PERFMETER
|
||||
|
||||
! wim stored into %l3 by trap vector
|
||||
sll %l3, 1, %l4 ! next WIM = rol(WIM, 1, NW)
|
||||
srl %l3, %l6, %l5 ! trap vector set %l6 = NW-1
|
||||
or %l5, %l4, %l5
|
||||
mov %l5, %wim ! install it
|
||||
btst PSR_PS, %l0 ! (wim delay 1) test for user trap
|
||||
bz wu_user ! (wim delay 2)
|
||||
restore ! delay slot
|
||||
|
||||
!
|
||||
! Supervisor underflow.
|
||||
! We do one more restore to get into the window to be restored.
|
||||
! The first one was done in the delay slot coming here.
|
||||
! We then restore from the stack.
|
||||
!
|
||||
restore ! get into window to be restored
|
||||
wu_stack_res:
|
||||
RESTORE_WINDOW(%sp)
|
||||
save ! get back to original window
|
||||
save
|
||||
mov %l0, %psr ! reinstall sup PSR_CC
|
||||
nop ! psr delay
|
||||
jmp %l1 ! reexecute restore
|
||||
rett %l2
|
||||
|
||||
wu_user:
|
||||
!
|
||||
! User underflow. Window to be restored is a user window.
|
||||
! We must check whether the user stack is resident where the window
|
||||
! will be restored from, which is pointed to by the windows sp.
|
||||
! The sp is the fp of the window which tried to do the restore,
|
||||
! so that it is still valid.
|
||||
!
|
||||
restore ! get into window to be restored
|
||||
!
|
||||
! Normally, we would check the alignment, and then probe the top
|
||||
! and bottom of the save area on the stack. However we optimize
|
||||
! this by checking that both ends of the save area are within a
|
||||
! 4k unit (the biggest mask we can generate in one cycle), and
|
||||
! the alignment in one shot. This allows us to do one probe to
|
||||
! the page map. NOTE: this assumes a page size of at least 4k.
|
||||
!
|
||||
and %sp, 0xfff, %l0
|
||||
#ifdef VA_HOLE
|
||||
! check if the sp points into the hole in the address space
|
||||
sethi %hi(_hole_shift), %l5 ! hole shift address
|
||||
ld [%l5 + %lo(_hole_shift)], %l7
|
||||
add %l0, (14*4), %l0 ! interlock, bottom of save area
|
||||
sra %sp, %l7, %l5
|
||||
inc %l5
|
||||
andncc %l5, 1, %l5
|
||||
bz 1f
|
||||
andncc %l0, 0xff8, %g0
|
||||
b,a wu_stack_not_res ! sp is in the hole
|
||||
1:
|
||||
#else
|
||||
add %l0, (14*4), %l0
|
||||
andncc %l0, 0xff8, %g0
|
||||
#endif VA_HOLE
|
||||
bz,a wu_sp_bot
|
||||
lda [%sp]ASI_PM, %l1 ! check for stack page resident
|
||||
!
|
||||
! Stack is either misaligned or crosses a 4k boundary.
|
||||
!
|
||||
btst 0x7, %sp ! test sp alignment
|
||||
bz wu_sp_top
|
||||
add %sp, (14*4), %l0 ! delay slot, check top of save area
|
||||
|
||||
!
|
||||
! A user underflow trap has happened with a misaligned sp.
|
||||
! Fake a memory alignment trap.
|
||||
!
|
||||
save ! get back to orig window
|
||||
save
|
||||
mov %l3, %wim ! restore old wim, so regs are dumped
|
||||
b sys_trap
|
||||
mov T_ALIGNMENT, %l4 ! delay slot, fake alignment trap
|
||||
|
||||
wu_sp_top:
|
||||
#ifdef VA_HOLE
|
||||
sra %l0, %l7, %l5
|
||||
inc %l5
|
||||
andncc %l5, 1, %l5
|
||||
bz,a 1f
|
||||
lda [%l0]ASI_PM, %l1 ! get pme for this address
|
||||
b,a wu_stack_not_res ! address is in the hole
|
||||
1:
|
||||
srl %l1, PG_S_BIT, %l1 ! get vws bits
|
||||
sra %sp, %l7, %l5
|
||||
inc %l5
|
||||
andncc %l5, 1, %l5
|
||||
bz 1f
|
||||
andcc %l1, VALID, %g0 ! look for valid bit
|
||||
b,a wu_stack_not_res ! stack page can never be resident
|
||||
1:
|
||||
#else
|
||||
lda [%l0]ASI_PM, %l1 ! get pme for this address
|
||||
srl %l1, PG_S_BIT, %l1 ! get vws bits
|
||||
andcc %l1, VALID, %g0 ! look for valid bit
|
||||
#endif VA_HOLE
|
||||
|
||||
bnz,a wu_sp_bot
|
||||
lda [%sp]ASI_PM, %l1 ! delay slot, check bottom of save area
|
||||
|
||||
b,a wu_stack_not_res ! stack page not resident
|
||||
|
||||
wu_sp_bot:
|
||||
srl %l1, PG_S_BIT, %l1 ! get vws bits
|
||||
|
||||
!!! SECURITY NOTE: check for user access to kernel space
|
||||
!!! XXX - assumes that kernel space is always resident
|
||||
|
||||
andcc %l1, USER_INVAL, %g0 ! look for supervisor bit
|
||||
bnz wu_stack_not_user
|
||||
nop
|
||||
|
||||
andcc %l1, VALID, %g0 ! look for valid bit
|
||||
bnz wu_ustack_res
|
||||
nop
|
||||
|
||||
mov %sp, %l0 ! save fault address
|
||||
|
||||
wu_stack_not_res:
|
||||
!
|
||||
! Restore area on user stack is not resident.
|
||||
! We punt and fake a page fault so that trap can bring the page in.
|
||||
! If the page fault is successful we will reexecute the restore,
|
||||
! and underflow with the page now resident.
|
||||
!
|
||||
sethi %hi(_masterprocp), %l6
|
||||
ld [%l6 + %lo(_masterprocp)], %l6
|
||||
ld [%l6 + P_STACK], %l7 ! setup kernel stack
|
||||
SAVE_GLOBALS(%l7 + MINFRAME)
|
||||
mov %l7, %g1 ! save computed sp
|
||||
mov %l0, %g2 ! save fault address
|
||||
mov GENERIC_INVALID, %g3 ! compute bus error reg code
|
||||
btst PROT_INVAL, %l1
|
||||
bnz,a 1f
|
||||
mov GENERIC_PROTERR, %g3
|
||||
1:
|
||||
save ! back to last user window
|
||||
mov %psr, %g4 ! get CWP
|
||||
save ! back to trap window
|
||||
|
||||
!
|
||||
! save remaining user state
|
||||
!
|
||||
mov %g1, %sp ! setup kernel stack
|
||||
SAVE_OUTS(%sp + MINFRAME)
|
||||
st %l0, [%sp + MINFRAME + PSR*4] ! psr
|
||||
st %l1, [%sp + MINFRAME + PC*4] ! pc
|
||||
st %l2, [%sp + MINFRAME + nPC*4] ! npc
|
||||
|
||||
mov %l3, %wim ! reinstall old wim
|
||||
mov 1, %g1 ! UWM = 0x01 << CWP
|
||||
sll %g1, %g4, %g1
|
||||
sethi %hi(_uunix), %l6
|
||||
ld [%l6 + %lo(_uunix)], %l6
|
||||
st %g1, [%l6 + PCB_UWM] ! setup u.u_pcb.pcb_uwm
|
||||
clr [%l6 + PCB_WBCNT]
|
||||
|
||||
wr %l0, PSR_ET, %psr ! enable traps
|
||||
mov T_DATA_FAULT, %o0
|
||||
add %sp, MINFRAME, %o1
|
||||
mov %g2, %o2
|
||||
mov %g3, %o3
|
||||
call _trap ! trap(T_DATA_FAULT,
|
||||
mov S_READ, %o4 ! rp, addr, be, S_READ)
|
||||
|
||||
b,a sys_rtt
|
||||
|
||||
!
|
||||
! The user's save area is resident. Restore the window.
|
||||
!
|
||||
wu_ustack_res:
|
||||
RESTORE_WINDOW(%sp)
|
||||
save ! get back to original window
|
||||
save
|
||||
wu_out:
|
||||
sethi %hi(_uunix), %l3
|
||||
ld [%l3 + %lo(_uunix)], %l3
|
||||
ld [%l3+PCB_FLAGS], %l3
|
||||
clr %l5 ! interlock slot, clear a used reg
|
||||
btst CLEAN_WINDOWS, %l3 ! check for clean window maint.
|
||||
bz 1f
|
||||
mov %l0, %psr ! reinstall system PSR_CC
|
||||
|
||||
!
|
||||
! Maintain clean windows. We only need to clean the registers
|
||||
! used in underflow as we know this is a user window.
|
||||
!
|
||||
mov %l1, %o6 ! put pc, npc in an unobtrusive place
|
||||
mov %l2, %o7
|
||||
clr %l0 ! clean the used ones
|
||||
clr %l1
|
||||
clr %l2
|
||||
clr %l3
|
||||
clr %l4
|
||||
jmp %o6 ! reexecute restore
|
||||
rett %o7
|
||||
1:
|
||||
nop
|
||||
jmp %l1 ! reexecute restore
|
||||
rett %l2
|
||||
|
||||
wu_stack_not_user:
|
||||
mov PROT_INVAL, %l1
|
||||
b wu_stack_not_res
|
||||
mov %sp, %l0 ! [fake up SEVAR]
|
||||
138
sys/sparc/zs_asm.s
Normal file
138
sys/sparc/zs_asm.s
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 1987, 1988, 1989 by Sun Microsystems, Inc.
|
||||
*/
|
||||
#include "assym.s"
|
||||
#include <machine/param.h>
|
||||
#include <machine/asm_linkage.h>
|
||||
#include <machine/mmu.h>
|
||||
#include <machine/psl.h>
|
||||
#include <sundev/zsreg.h>
|
||||
#include <machine/intreg.h>
|
||||
|
||||
.seg "data"
|
||||
.asciz "@(#)zs_asm.s 1.1 94/10/31"
|
||||
.align 4
|
||||
.seg "text"
|
||||
|
||||
|
||||
#ifndef sun4c
|
||||
/*
|
||||
* UARTs require 1.6 usec recovery time
|
||||
* note ZSRECOVER can't be used in a leaf routine
|
||||
* this is overkill by .4 usec but it scales properly for all cpus
|
||||
*/
|
||||
#define ZSRECOVER() \
|
||||
call _usec_delay; \
|
||||
mov 2, %o0;
|
||||
#else sun4c
|
||||
/* SPARCstation 1 doesn't need a delay. */
|
||||
#define ZSRECOVER()
|
||||
#endif sun4c
|
||||
|
||||
/*
|
||||
* We come here for auto-vectored interrupts
|
||||
* We assume that the most recently interrupting
|
||||
* chip is interrupting again and read the vector out of the
|
||||
* chip and branch to the right routine. The "special receive"
|
||||
* interrupt routine gets control if the assumption was wrong
|
||||
* and performs the longer task of figuring out who is really interrupting
|
||||
* and why.
|
||||
*/
|
||||
.global _zscurr
|
||||
.global _zsNcurr
|
||||
|
||||
ENTRY(zslevel12)
|
||||
save %sp, -SA(MINFRAME), %sp ! get a new window
|
||||
set _zscurr, %g1 ! most active channel desc pointer
|
||||
ld [%g1], %l0 ! read it
|
||||
mov 2, %g2 ! register number 2 in channel B
|
||||
ld [%l0 + ZS_ADDR], %l1 ! get address of hardware channel
|
||||
stb %g2, [%l1] ! set register to read
|
||||
ZSRECOVER();
|
||||
ldub [%l1], %l2 ! get interrupt vector number
|
||||
ZSRECOVER();
|
||||
btst 8, %l2 ! channel A?
|
||||
bnz,a 1f ! if not anull next instruction
|
||||
sub %l0, ZSSIZE, %l0 ! point to channel A's descriptor
|
||||
1:
|
||||
set _zsNcurr, %g1 ! stash the [modified] channel
|
||||
st %l0, [%g1] ! where it MIGHT be changed
|
||||
and %l2, 6, %l2 ! get vetor bits
|
||||
add %l2, %l2, %l2 ! get multiple of 4
|
||||
ld [%l0 + %l2], %g1 ! get address of routine to call
|
||||
call %g1 ! go to it
|
||||
mov %l0, %o0 ! delay slot, fix argument
|
||||
|
||||
set _zsNcurr, %g1 ! restore the channel, since
|
||||
ld [%g1], %l0 ! it might have been changed
|
||||
ld [%l0 + ZS_ADDR], %l1 ! get address of hardware channel
|
||||
mov ZSWR0_CLR_INTR, %g2 ! value to clear interrupt
|
||||
stb %g2, [%l1] ! clear interrupt
|
||||
ZSRECOVER();
|
||||
ret
|
||||
restore
|
||||
|
||||
/*
|
||||
* Turn on a zs soft interrupt.
|
||||
*/
|
||||
ENTRY(setzssoft)
|
||||
mov %psr, %g2
|
||||
or %g2, PSR_PIL, %g1 ! spl hi to protect intreg update
|
||||
mov %g1, %psr
|
||||
nop; nop; ! psr delay
|
||||
set INTREG_ADDR, %o0 ! set address of interrupt register
|
||||
ldub [%o0], %o1 ! read it
|
||||
bset IR_SOFT_INT6, %o1 ! set zs soft interrupt bit
|
||||
stb %o1, [%o0] ! write it out
|
||||
mov %g2, %psr ! restore psr
|
||||
nop ! psr delay
|
||||
retl ! leaf routine return
|
||||
nop
|
||||
|
||||
/*
|
||||
* Test and clear a zs soft interrupt.
|
||||
*/
|
||||
ENTRY(clrzssoft)
|
||||
set INTREG_ADDR, %o1 ! set address of interrupt register
|
||||
ldub [%o1], %o2 ! read it
|
||||
btst IR_SOFT_INT6, %o2 ! was there an interrupt pending?
|
||||
bz,a 1f ! no, simply return 0
|
||||
mov 0, %o0 ! return value
|
||||
|
||||
mov %psr, %g2
|
||||
or %g2, PSR_PIL, %g1 ! spl hi to protect intreg update
|
||||
mov %g1, %psr
|
||||
nop; nop; ! psr delay
|
||||
mov 1, %o0 ! otherwise one was pending
|
||||
ldub [%o1], %o2 ! read it
|
||||
bclr IR_SOFT_INT6, %o2 ! clear zs soft int bit
|
||||
stb %o2, [%o1] ! write interrupt register
|
||||
mov %g2, %psr ! restore psr
|
||||
nop ! psr delay
|
||||
1:
|
||||
retl
|
||||
nop
|
||||
|
||||
/*
|
||||
* Read a control register in the chip
|
||||
*/
|
||||
ENTRY(zszread)
|
||||
save %sp, -SA(MINFRAME), %sp ! get a new window
|
||||
stb %i1, [%i0] ! write register number
|
||||
ZSRECOVER();
|
||||
ldub [%i0], %i0 ! read register
|
||||
ZSRECOVER();
|
||||
ret
|
||||
restore
|
||||
|
||||
/*
|
||||
* Write a control register in the chip
|
||||
*/
|
||||
ENTRY(zszwrite)
|
||||
save %sp, -SA(MINFRAME), %sp ! get a new window
|
||||
stb %i1, [%i0] ! write register number
|
||||
ZSRECOVER();
|
||||
stb %i2, [%i0] ! write data
|
||||
ZSRECOVER();
|
||||
ret
|
||||
restore
|
||||
Reference in New Issue
Block a user