Source to machdep/i386/fp_emul/fp_status.s
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* @[email protected]
*
* "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
* Reserved. This file contains Original Code and/or Modifications of
* Original Code as defined in and that are subject to the Apple Public
* Source License Version 1.0 (the 'License'). You may not use this file
* except in compliance with the License. Please obtain a copy of the
* License at http://www.apple.com/publicsource and read it before using
* this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License."
*
* @[email protected]
*/
.file "status.s"
.ident "@(#)kern-fp:status.s 1.1"
//$tt(*80387 emulator + + + s t a t u s + + + *)
// ************************************************************************
//
// s t a t u s . m o d
// ===================
//
// ================================================================
// intel corporation proprietary information
// this software is supplied under the terms of a license
// agreement or non-disclosure agreement with intel corporation
// and may not be copied nor disclosed except in accordance with
// the terms of that agreement.
// =================================================================
//
// function:
// operation cluster for 80387 status register
//
// public procedures:
// stcw stenv pop_free
// incr_top decr_top save_status
// restore_status clear_p_error init
// clex stsw ldcw
// get_precision store_precision get_rnd_control
// store_rnd_control u_masked? z_masked?
// d_masked? i_masked? o_masked?
// get_reg_tag affine_infinity? get_top
// set_p_error p_error? set_u_error
// set_o_error set_z_error set_i_error
// i_error? d_error? set_d_error
// set_z_bit clear_z_bit set_s_bit
// clear_s_bit set_a_bit clear_a_bits
// set_c_bit clear_c_bit store_reg_tag
// set_i_masked? set_d_masked? set_z_masked?
// clear_cond_bits
//
// ************************************************************************
//
//...March 3, 1987...
//
.data //a_msr segment rw public
//
//...define the 80387 status register...
//
//%gs:sr_masks: .byte 0x07f / bit 6 on for d-step 8087 05/21/81
//%gs:sr_controls: .byte 0x13
//%gs:sr_reserved1: .value 0 / reserveds take care of 386 env
//%gs:sr_errors: .byte 0
//%gs:sr_flags: .byte 0 / bit 2 off for sim286 09/14/83
//%gs:sr_reserved2: .value 0
//%gs:sr_tags: .value 0x0ffff
//%gs:sr_reserved3: .value 0
//%gs:sr_instr_offset: .long 0
//%gs:sr_instr_base: .value 0
//%gs:sr_reserved4: .value 0
//%gs:sr_mem_offset: .long 0
//%gs:sr_mem_base: .value 0
//%gs:sr_reserved5: .value 0
//%gs:sr_regstack: .value 40 dup(0)
//#define a_m%gs:sr_data %gs:sr_masks
//a_msr ends
// assume %ds:a_msr
//$nolist
#include "fp_e80387.h"
//$list
//
// .globl %gs:sr_mem_offset
// .globl %gs:sr_controls
// .globl %gs:sr_mem_base
// .globl a_m%gs:sr_data
// .globl %gs:sr_regstack
// .globl %gs:sr_flags
// .globl %gs:sr_masks
// .globl %gs:sr_errors
// .globl %gs:sr_instr_offset
// .globl %gs:sr_instr_base
.globl incr_top
.globl decr_top
.globl save_status
.globl pop_free
.globl ldcw
.globl stenv
.globl clex
.globl stsw
.globl stcw
.globl get_precision
.globl store_precision
.globl get_rnd_control
.globl store_rnd_control
.globl u_masked_
.globl d_masked_
.globl get_top
.globl i_masked_
.globl o_masked_
.globl get_reg_tag
.globl affine_infinity_
.globl set_p_error
.globl set_u_error
.globl set_o_error
.globl init
.globl clear_p_error
.globl set_i_error
.globl set_d_error
.globl i_error_
.globl d_error_
.globl p_error_
.globl set_z_bit
.globl clear_z_bit
.globl set_s_bit
.globl clear_s_bit
.globl set_a_bit
.globl clear_cond_bits
.globl set_c_bit
.globl clear_c_bit
.globl store_reg_tag
.globl set_i_masked_
.globl set_d_masked_
.globl set_z_masked_
.globl clear_a_bit
.globl restore_status
.globl set_stk_u_error
.globl set_stk_o_error
.globl correct_tag_word
.globl free_reg
//
.text //a_med segment er public
// --------to be added for unix
//.. extrn fpfulong:far,fpfushort:far,fpsulong:far,fpsushort:far
//
// *************************************************************************
//
// the routines in this section manipulate the status and control
// information stored in the 80387 status data segment, a?msr.
//
// ************************************************************************
// transfer field group: get_precision, store_precision, get_rnd_control
// store_rnd_control, get_top, get_reg_tag, store_reg_tag
//
// inputs: the *get* routines require no input parameters, except for the
// procedures get_reg_tag and store_reg_tag which expect the
// register number to be in al and cl, respectively
// the *store* routines input the value to be stored in al.
//
// outputs: get_precison returns the 2-bit precision field in dl.
// all other routines return justified values in al.
// ************************************************************************
ALIGN
get_precision: //proc
movb $precision_mask,%dl // load precision field mask
andb %gs:sr_controls,%dl // mask in precision control
ret
//get_precision endp
//
ALIGN
store_precision: //proc
andb $~precision_mask,%gs:sr_controls // clear precision
orb %al,%gs:sr_controls // store new precision control
ret
//store_precision endp
//
ALIGN
get_rnd_control: //proc
movb %gs:sr_controls,%al // load control byte
andb $rnd_control_mask,%al // mask in rounding control
ret
//get_rnd_control endp
//
ALIGN
store_rnd_control: //proc
andb $~rnd_control_mask,%gs:sr_controls // clear old field
orb %al,%gs:sr_controls // store new rounding control
ret
//store_rnd_control endp
//
ALIGN
get_top: //proc
movb %gs:sr_flags,%al // load status flag byte
andb $top_mask,%al // mask in top field
shrb $3,%al // right justify top field
ret
//get_top endp
//
ALIGN
get_reg_tag: //proc
movzbl %al,%eax // form word bit count
mov %eax,%ecx // store in cx
shl $1,%ecx // bit count = 2 * reg num
mov %ecx,%ebx // else, must examine register
shl $2,%ebx // form index to %gs:sr_regstack
add %ecx,%ebx // index = 10 * register num
add $sr_regstack,%ebx // + start of %gs:sr_regstack
movw %gs:sr_tags,%ax // load tag word
shrw %cl,%ax // shift to tag of interest
andb $empty,%al // mask out other tags
cmpb inv,%al // if tag not = 2,
jne got_tag // no further decoding needed
movw %gs:8(%ebx),%ax // load register exponent
andw $0x7fff,%ax // mask off sign bit
jnz check_unsupp // if expon /= 0, check unsupported format
movb denormd,%al // if exponent zero, denormal
ret
ALIGN
check_unsupp:
mov %gs:4(%ebx),%eax // get top 32 bits of significand
test $0x080000000,%eax // is highest bit set.
jnz check_nan // if so, check for nan
movb unsupp,%al // if not, its an unsupported format.
ret
ALIGN
check_nan:
and $0x07fffffff,%eax // zero out most significant bit
or %gs:(%ebx),%eax // or bottom 32 bits into top 32 bits.
movb infinty,%al // tentatively set tag to infinitys
jz got_tag // if fraction is 0, its infinity
movb inv,%al // fraction is non zero, so tag as nan.
got_tag:
ret
//get_reg_tag endp
// ************************************************************************
// test status bit group: u_masked?, z_masked?, d_masked?, i_masked?,
// o_masked?, affine_infinity?, p_error?, i_error?, d_error?,
// set_i_masked?, set_d_masked?
//
// inputs: no input values are required.
//
// outputs: all boolean function return the complemented bit value
// in the zf. (test with jz on bit = 0.)
// ************************************************************************
ALIGN
u_masked_: //proc
testb underflow_mask,%gs:sr_masks // test the u mask
ret
//u_masked_ endp
//
ALIGN
set_d_masked_: //proc
call set_d_error // set the d error
d_masked_:
testb denorm_mask,%gs:sr_masks // test the d mask
ret
//set_d_masked_ endp
//
ALIGN
set_i_masked_: //proc
call set_i_error // set the i error
i_masked_:
testb invalid_mask,%gs:sr_masks // test the i mask
ret
//set_i_masked_ endp
//
ALIGN
o_masked_: //proc
testb overflow_mask,%gs:sr_masks // test the o mask
ret
//o_masked_ endp
//
ALIGN
affine_infinity_: //proc
testb infinity_control_mask,%gs:sr_controls // test ic
ret
//affine_infinity_ endp
//
ALIGN
p_error_: //proc
testb $inexact_mask,%gs:sr_errors // test the p error
ret
//p_error_ endp
//
ALIGN
i_error_: //proc
testb invalid_mask,%gs:sr_errors // test the i error
ret
//i_error_ endp
//
ALIGN
d_error_: //proc
testb denorm_mask,%gs:sr_errors // test the d error
ret
//d_error_ endp
// ************************************************************************
// set and reset bit group: set_p_error, set_u_error, set_o_error
// set_z_masked?, set_i_error, set_d_error, clear_s_bit, clear_z_bit
// set_s_bit, set_z_bit, set_a_bit, clear_a_bit, set_c_bit, clear_c_bit
// clear_cond_bits, clear_p_error, set_stk_u_error, set_stk_o_error
//
// inputs: no input values are required.
//
// outputs: all procedures set or reset the indicated status bit
// ************************************************************************
ALIGN
set_stk_u_error: //proc
orb invalid_mask+zero_mask,%gs:sr_errors
andb $~a_mask,%gs:sr_flags // clear the a-bit
ret
//set_stk_u_error endp
//
ALIGN
set_stk_o_error: //proc
orb invalid_mask+zero_mask,%gs:sr_errors
orb $a_mask,%gs:sr_flags // set the a-bit
ret
//set_stk_o_error endp
//
ALIGN
set_p_error: //proc
orb $inexact_mask,%gs:sr_errors // set the p-error
ret
//set_p_error endp
//
ALIGN
set_u_error: //proc
orb underflow_mask,%gs:sr_errors // set the u-error
ret
//set_u_error endp
//
ALIGN
set_o_error: //proc
orb overflow_mask,%gs:sr_errors // set the o-error
ret
//set_o_error endp
//
ALIGN
set_i_error: //proc
orb invalid_mask,%gs:sr_errors // set the i-error
ret
//set_i_error endp
//
ALIGN
set_d_error: //proc
orb denorm_mask,%gs:sr_errors // set the d-error
ret
//set_d_error endp
//
ALIGN
set_z_masked_: //proc
orb zero_divide_mask,%gs:sr_errors // set the z-error
testb zero_divide_mask,%gs:sr_masks // test the z mask
ret
//set_z_masked_ endp
//
ALIGN
set_s_bit: //proc
orb $sign_mask,%gs:sr_flags // set the s-bit
ret
//set_s_bit endp
//
ALIGN
set_z_bit: //proc
orb $zero_mask,%gs:sr_flags // set the z-bit
ret
//set_z_bit endp
//
ALIGN
set_a_bit: //proc
orb $a_mask,%gs:sr_flags // set the a-bit
ret
//set_a_bit endp
//
ALIGN
set_c_bit: //proc
orb $c_mask,%gs:sr_flags // set the c-bit
ret
//set_c_bit endp
//
ALIGN
clear_s_bit: //proc
andb $~sign_mask,%gs:sr_flags // clear the s-bit
ret
//clear_s_bit endp
//
ALIGN
clear_z_bit: //proc
andb $~zero_mask,%gs:sr_flags // clear the z-bit
ret
//clear_z_bit endp
//
ALIGN
clear_a_bit: //proc
andb $~a_mask,%gs:sr_flags // clear the a-bit
ret
//clear_a_bit endp
//
ALIGN
clear_cond_bits: //proc
andb $~(c_mask+zero_mask+sign_mask+a_mask),%gs:sr_flags
ret // clear all condition bits
//clear_cond_bits endp
//
ALIGN
clear_c_bit: //proc
andb $~c_mask,%gs:sr_flags // clear the c-bit
ret
//clear_c_bit endp
//
ALIGN
clear_p_error: //proc
andb $~inexact_mask ,%gs:sr_errors // clear the p-error
ret
//clear_p_error endp
//
// ************************************************************************
// pop_free:
// function:
// pops the stack and/or frees the register(s) as required
//
// ************************************************************************
ALIGN
pop_free: //proc
call i_masked_ // is invalid masked?
jnz check_op1 // if so, forget error
call i_error_ // check for i-error
jnz common_return // if error, dont pop
free_reg:
check_op1:
movb op1_use_up(%ebp),%al // pop or free op1
call process_use_up
movb op2_use_up(%ebp),%al // pop or free op2
process_use_up:
cmpb pop_stack,%al // is it a pop_stack?
je pop_it // yes, process it
xorb free,%al // no, is it free reg?
jnz exit_process // no, done with use_up
call get_top // yes, get top pointer
addb reg_num(%ebp),%al // convert relative num
andb $0x07,%al // to absolute reg num
empty_reg_tag:
movb %al,%cl // cl = reg number
movb $empty,%al // al = new tag value
store_reg_tag:
shlb $1,%cl // bit count = 2 * reg num
rorw %cl,%gs:sr_tags // rotate tag to low bits
andb $~empty,%gs:sr_tags // clear old reg num tag
andb $empty,%al // clear bits 2-7 of new tag
orb %al,%gs:sr_tags // store new reg num tag and
rolw %cl,%gs:sr_tags // rotate the tag word back
exit_process:
ret
pop_it:
call get_top // pop the stack once
// the following six instructions have been commented out to prevent
// an invalid operation from being signaled when the 87 tos is subject
// to a freep st(i) (*6 in the 80387 t-spec). in other words, it will
// be o.k. to free st(0) and decrement stackpointer, so it will also be
// o.k. to free an already empty st(0).
// call get_reg_tag ; get register tag
// cmp al,empty ; is the top empty?
// jne pop_ok ; no, stack may be popped
// call set_i_masked? ; yes, stack error
// jz exit_process ; abort if unmasked
//pop_ok:
// call get_top
call empty_reg_tag //set tag of top empty
//pop_free endp ; enter incr_top
//
// **************************************************************************
// incr_top:
// function:
// increments stack pointer
//
// **************************************************************************
FALLSTHRU
incr_top: //proc
movb $0x08,%cl // load increment top constant
jmp adjust_top // merge with decr_top
ALIGN
//incr_top endp
// **************************************************************************
// decr_top:
// function:
// decrements stack pointer
//
// **************************************************************************
ALIGN
decr_top: //proc
movb $0x38,%cl // load decrement top constant
adjust_top:
movb %gs:sr_flags,%al // get old top
andb $top_mask,%al
xorb %al,%gs:sr_flags // clear old top field
addb %cl,%al // increment/decrement top
andb $top_mask,%al // mask off bits 6-7
orb %al,%gs:sr_flags // store new top field
common_return:
ret
//decr_top endp
//
// ******************************************************************************
// restore_status:
// function:
// implements the 80387 ldenv instruction.
// restores status register from memory.
//
// **********************************************************************
ALIGN
restore_status: //proc
push %ds // save %ds
push %gs
pop %es // load destination base
lds mem_operand_pointer(%ebp),%esi // load environment pointer
mov $sr_masks,%edi // load dest offset
mov $0x0007,%ecx // load environment from memory
cmpb $1,oprnd_siz32(%ebp) //is it a 32 bit operand
jne restore_status16 //no restore 16 bit status
FAST_MOVSL_ES
pop %ds
orb $0x40,%gs:sr_masks // set stack mask
jmp correct_tag_word
ALIGN
restore_status16: //16 bit protected mode
movsw // mov words
inc %edi
inc %edi
loop restore_status16
// ---------
//------------------------------------------------------------------------
//.. pushad
//.. push ds
//..restore_status32:
//.. push esi
//.. call fpfulong
//.. pop esi
//.. stosd
//.. add esi,4
//.. loop restore_status32
//.. pop ds
//.. popad
//.. cld
//.. ret
//..restore_status16:
//.. pushad
//.. push ds
//..restor_loop:
//.. push esi
//.. call fpfushort
//.. pop esi
//.. stosw
//.. add esi,2
//.. add edi,2
//.. loop restor_loop
//.. pop ds
//.. popad
//.. cld
//---------------------------------------------------------------------
pop %ds
orb $0x40,%gs:sr_masks // set stack mask
correct_tag_word:
mov $8,%ecx
xor %ebx,%ebx
set_tags_loop: //looping through physical locations of 0-7 regs
mov $10,%eax
mul %ebx
mov $sr_regstack,%esi
add %eax,%esi
mov $0x00000003,%eax
and %gs:sr_tags,%eax
cmp $3,%eax
je loop_back
movw %gs:8(%esi),%ax
andw $0x7fff,%ax // strip off sign bit
jz expon_zero // branch if exponent 0
testb $0x80,%gs:7(%esi)
jnz max_expon_
movb $2,%dl // we have an
jmp set_tag // unsupported format
ALIGN
max_expon_:
cmpw $0x7fff,%ax // check for invalid
je not_validx // or infinity
movb $0,%dl // operand is valid
jmp set_tag
ALIGN
not_validx:
movb $2,%dl
jmp set_tag
ALIGN
expon_zero:
mov %gs:4(%esi),%eax
or %gs:(%esi),%eax
movb $1,%dl // set tag to special
jz set_tag // if number is +/- 0
movb $2,%dl // set tag to denormd
set_tag:
andb $~empty,%gs:sr_tags // clear old reg num tag
orb %dl,%gs:sr_tags // store new reg num tag and
loop_back:
rorw $2,%gs:sr_tags // get next two bits
inc %ebx
loop set_tags_loop
ret
//restore_status endp
// **************************************************************************
// init:
// function:
// implements 80387 init instruction. intializes
// status register including mode word and error mask.
//
// ***********************************************************************
ALIGN
init: //proc
movl $0x137f,%eax // initialize mode word
movw %ax,%gs:sr_masks
movl $0xffffffff,%ax // ax = 0ffffh
movw %ax,%gs:sr_tags // register tags = empty
xor %eax,%eax // clear a reg
movw %ax,%gs:sr_errors // clear the error flags
// and %gs:sr_flags,not top_mask ; top of stack = 0, cbit=0
ret
//init endp
// ***********************************************************************
// ldcw:
// function:
// implements 80387 ldcw instruction. 80387 control word
// loaded (from memory location specified in instruction).
//
// ***********************************************************************
ALIGN
ldcw: //proc
les mem_operand_pointer(%ebp),%ebx //get new mode word
movw %es:(%ebx),%ax
//..
//.. pushad
//.. push es
//.. push ebx
//.. call fpfushort
//.. pop ebx
//.. pop es
//.. popad
//.. cld
//..
orw $0x40,%ax // set stack mask
movw %ax,%gs:sr_masks // store in status reg
ret
//ldcw endp
// **********************************************************************
// stenv:
// function:
// implements 80387 fstenv instruction. 80387 environmemt
// stored (in memory location specified in the instruction).
//
// **********************************************************************
ALIGN
stenv: //proc
call save_status // store status to memory
orb $0x07f,%gs:sr_masks // set all individual masks
ret
//stenv endp
// **********************************************************************
// clex:
// function:
// clears all 80387 errors set in status register
//
// **********************************************************************
ALIGN
clex: //proc
movb $0,%gs:sr_errors // clear error byte
ret
//clex endp
// **********************************************************************
// stsw
// function:
// stores status word in memory location. (i.e., implements
// fstsw instruction.) also implements 'fstsw ax'.
//
// **********************************************************************
ALIGN
stsw: //proc
movb op2_location(%ebp),%al // load op2 location
cmpb $reg,%al // is this fstsw ax?
movw %gs:sr_errors,%ax // get status word
je stsw_ax // branch on fstsw ax
store_word:
les mem_operand_pointer(%ebp),%ebx
movw %ax,%es:(%ebx) // store status to memory
//.. pushad
//.. push es
//.. push ebx
//.. push ax
//.. call fpsushort
//.. add esp,12
//.. popad
//.. cld
//..
ret
stsw_ax:
movw %ax,saved_eax(%ebp) // store into register ax
// above instruction left for completeness but the ax
// really has to be restored to the EAX above the global
// re-entrant segment
// movl %eax, (offset_eax)(%ebp)
ret
//stsw endp
// *********************************************************************
// stcw:
// function:
// stores control word in memory location. (i.e., implements
// fstcw instruction.)
//
// *********************************************************************
ALIGN
stcw: //proc
movw %gs:sr_masks,%ax //get control word
andw $0x1f7f,%ax
orw $0x1040,%ax //set/reset reserved bits
jmp store_word //store it
ALIGN
//stcw endp
// *********************************************************************
// save_status:
// function:
// saves status register to location specified by
// memory operand pointer(in es:di)
//
// *********************************************************************
ALIGN
save_status: //proc
push %ds // save %ds
push %gs
pop %ds // and set it to address sr_masks
andw $0x1f7f,%gs:sr_masks
orw $0x1040,%gs:sr_masks //set/reset reserved bits
mov $sr_masks,%esi // load source offset
les mem_operand_pointer(%ebp),%edi // load destination pointer
mov $0x0007,%ecx
cmpb $1,oprnd_siz32(%ebp)
jne save_status16
FAST_MOVSL_ES
pop %ds // restore %ds
ret
ALIGN
save_status16: //16 bit protected mode
movsw // mov words
inc %esi
inc %esi
loop save_status16
//------------------------------------------------------------------------
//.. pushad
//.. push es
//..save_status32:
//.. push edi
//.. push dword ptr (esi)
//.. call fpsulong
//.. add esp,8
//.. add esi,4
//.. add edi,4
//.. loop save_status32
//.. pop es
//.. popad
//.. cld
//.. ret
//..save_status16:
//.. pushad
//.. push es
//..save_loop:
//.. push edi
//.. push word ptr (esi)
//.. call fpsushort
//.. add esp,8
//.. add edi,2
//.. add esi,4
//.. loop save_loop
//.. pop es
//.. popad
//.. cld
pop %ds // restore %ds
ret
//save_status endp
//
//a_med ends
//
// end