Work around bug in FX2C and FX2L.
These SANE operations can sometimes return incorrect values for certain negative integers such as -2147483648 and -53021371269120 (numbers with at least 16 low-order zero bits in their two's-complement representation). To work around this, we now avoid calling FX2C or FX2L on negative numbers, generally by saving and restoring the sign separately. These workarounds are used in several of the new <math.h> rounding functions, and also for code that converts floating-point values to comp or long long. There are some places in SysFloat that should be patched similarly, so we may still hit this problem in certain situations until that is done.
This commit is contained in:
parent
503182e435
commit
e00c21dd70
19
fpextra.asm
19
fpextra.asm
|
@ -75,12 +75,20 @@ fpextra private dummy segment
|
||||||
* Inputs:
|
* Inputs:
|
||||||
* extended-format real on stack
|
* extended-format real on stack
|
||||||
*
|
*
|
||||||
|
* Note: This avoids calling FX2C on negative numbers,
|
||||||
|
* because it is buggy for certain values.
|
||||||
|
*
|
||||||
****************************************************************
|
****************************************************************
|
||||||
*
|
*
|
||||||
~CompPrecision start
|
~CompPrecision start
|
||||||
tsc
|
lda 4+8,s
|
||||||
|
pha save original sign
|
||||||
|
asl a force sign to positive
|
||||||
|
lsr a
|
||||||
|
sta 6+8,s
|
||||||
|
tsc limit precision
|
||||||
clc
|
clc
|
||||||
adc #4
|
adc #6
|
||||||
ldy #0
|
ldy #0
|
||||||
phy
|
phy
|
||||||
pha
|
pha
|
||||||
|
@ -92,6 +100,11 @@ fpextra private dummy segment
|
||||||
pha
|
pha
|
||||||
FX2C
|
FX2C
|
||||||
FC2X
|
FC2X
|
||||||
rtl
|
pla restore original sign
|
||||||
|
bpl ret
|
||||||
|
lda 4+8,s
|
||||||
|
ora #$8000
|
||||||
|
sta 4+8,s
|
||||||
|
ret rtl
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
31
int64.asm
31
int64.asm
|
@ -646,6 +646,9 @@ ret pld
|
||||||
* Outputs:
|
* Outputs:
|
||||||
* signed long long int on stack
|
* signed long long int on stack
|
||||||
*
|
*
|
||||||
|
* Note: This avoids calling FX2C on negative numbers,
|
||||||
|
* because it is buggy for certain values.
|
||||||
|
*
|
||||||
****************************************************************
|
****************************************************************
|
||||||
*
|
*
|
||||||
~CnvRealLongLong start
|
~CnvRealLongLong start
|
||||||
|
@ -668,11 +671,16 @@ ret pld
|
||||||
sta 10,s
|
sta 10,s
|
||||||
sta 8,s
|
sta 8,s
|
||||||
sta 6,s
|
sta 6,s
|
||||||
bra done
|
bra done otherwise
|
||||||
|
|
||||||
convert tsc if it is not LONG_MIN, call fx2c:
|
convert lda 4+8,s
|
||||||
|
pha save original sign
|
||||||
|
asl a force sign to positive
|
||||||
|
lsr a
|
||||||
|
sta 6+8,s
|
||||||
|
tsc
|
||||||
clc
|
clc
|
||||||
adc #4
|
adc #6
|
||||||
pea 0 push src address for fx2c
|
pea 0 push src address for fx2c
|
||||||
pha
|
pha
|
||||||
pea 0 push dst address for fx2c
|
pea 0 push dst address for fx2c
|
||||||
|
@ -680,7 +688,22 @@ convert tsc if it is not LONG_MIN, call fx2c:
|
||||||
inc a
|
inc a
|
||||||
pha
|
pha
|
||||||
fx2c convert
|
fx2c convert
|
||||||
|
pla if original value was negative
|
||||||
|
bpl done
|
||||||
|
sec
|
||||||
|
lda #0 negate result
|
||||||
|
sbc 6,s
|
||||||
|
sta 6,s
|
||||||
|
lda #0
|
||||||
|
sbc 6+2,s
|
||||||
|
sta 6+2,s
|
||||||
|
lda #0
|
||||||
|
sbc 6+4,s
|
||||||
|
sta 6+4,s
|
||||||
|
lda #0
|
||||||
|
sbc 6+6,s
|
||||||
|
sta 6+6,s
|
||||||
|
|
||||||
done phb move return address
|
done phb move return address
|
||||||
pla
|
pla
|
||||||
plx
|
plx
|
||||||
|
|
102
math2.asm
102
math2.asm
|
@ -16,7 +16,7 @@ math2 private dummy segment
|
||||||
copy equates.asm
|
copy equates.asm
|
||||||
end
|
end
|
||||||
|
|
||||||
INVALID gequ $0001 exceptions
|
INVALID gequ $0001 exceptions
|
||||||
UNDERFLOW gequ $0002
|
UNDERFLOW gequ $0002
|
||||||
OVERFLOW gequ $0004
|
OVERFLOW gequ $0004
|
||||||
DIVBYZERO gequ $0008
|
DIVBYZERO gequ $0008
|
||||||
|
@ -682,6 +682,9 @@ ret creturn 2:x return it
|
||||||
* Rounds x to an integer using current rounding direction
|
* Rounds x to an integer using current rounding direction
|
||||||
* and returns it as a long long (if representable).
|
* and returns it as a long long (if representable).
|
||||||
*
|
*
|
||||||
|
* Note: This avoids calling FX2C on negative numbers,
|
||||||
|
* because it is buggy for certain values.
|
||||||
|
*
|
||||||
****************************************************************
|
****************************************************************
|
||||||
*
|
*
|
||||||
llrint start
|
llrint start
|
||||||
|
@ -706,7 +709,7 @@ retptr equ 1
|
||||||
FCPXX compare with LLONG_MIN
|
FCPXX compare with LLONG_MIN
|
||||||
bne convert
|
bne convert
|
||||||
|
|
||||||
lda #$8000 if it is LONG_MIN, use that value
|
lda #$8000 if it is LLONG_MIN, use that value
|
||||||
ldy #6
|
ldy #6
|
||||||
sta [retptr],y
|
sta [retptr],y
|
||||||
asl a
|
asl a
|
||||||
|
@ -717,16 +720,40 @@ retptr equ 1
|
||||||
dey
|
dey
|
||||||
sta [retptr],y
|
sta [retptr],y
|
||||||
sta [retptr]
|
sta [retptr]
|
||||||
bra done
|
bra done otherwise
|
||||||
|
|
||||||
convert tdc if it is not LONG_MIN, call fx2c:
|
convert pei x+8 save sign of x
|
||||||
|
asl x+8 x = abs(x)
|
||||||
|
lsr x+8
|
||||||
|
tdc
|
||||||
clc
|
clc
|
||||||
adc #x
|
adc #x
|
||||||
pea 0 push src address for fx2c
|
pea 0 push src address for fx2c
|
||||||
pha
|
pha
|
||||||
pei retptr+2 push dst address for fx2c
|
pei retptr+2 push dst address for fx2c
|
||||||
pei retptr
|
pei retptr
|
||||||
FX2C convert
|
FX2C convert x
|
||||||
|
|
||||||
|
pla if x was negative
|
||||||
|
bpl done
|
||||||
|
sec
|
||||||
|
lda #0 negate result
|
||||||
|
sbc [retptr]
|
||||||
|
sta [retptr]
|
||||||
|
ldy #2
|
||||||
|
lda #0
|
||||||
|
sbc [retptr],y
|
||||||
|
sta [retptr],y
|
||||||
|
iny
|
||||||
|
iny
|
||||||
|
lda #0
|
||||||
|
sbc [retptr],y
|
||||||
|
sta [retptr],y
|
||||||
|
iny
|
||||||
|
iny
|
||||||
|
lda #0
|
||||||
|
sbc [retptr],y
|
||||||
|
sta [retptr],y
|
||||||
|
|
||||||
done creturn
|
done creturn
|
||||||
|
|
||||||
|
@ -861,6 +888,9 @@ logbl entry
|
||||||
* Rounds x to an integer using current rounding direction
|
* Rounds x to an integer using current rounding direction
|
||||||
* and returns it as a long (if representable).
|
* and returns it as a long (if representable).
|
||||||
*
|
*
|
||||||
|
* Note: This avoids calling FX2L or FX2C on negative numbers,
|
||||||
|
* because they are buggy for certain values.
|
||||||
|
*
|
||||||
****************************************************************
|
****************************************************************
|
||||||
*
|
*
|
||||||
lrint start
|
lrint start
|
||||||
|
@ -869,6 +899,10 @@ lrintl entry
|
||||||
|
|
||||||
csubroutine (10:x),0
|
csubroutine (10:x),0
|
||||||
|
|
||||||
|
pei x+8 save sign of x
|
||||||
|
asl x+8 x = abs(x)
|
||||||
|
lsr x+8
|
||||||
|
|
||||||
tdc convert to integer
|
tdc convert to integer
|
||||||
clc
|
clc
|
||||||
adc #x
|
adc #x
|
||||||
|
@ -876,7 +910,22 @@ lrintl entry
|
||||||
pha
|
pha
|
||||||
pea 0
|
pea 0
|
||||||
pha
|
pha
|
||||||
FX2L
|
FX2C
|
||||||
|
|
||||||
|
lda x+4 if x is out of range of long
|
||||||
|
ora x+6
|
||||||
|
bne flag_inv
|
||||||
|
cmpl x,#$80000000
|
||||||
|
blt chk_neg
|
||||||
|
bne flag_inv
|
||||||
|
lda 1,s
|
||||||
|
bmi chk_neg
|
||||||
|
flag_inv pea INVALID raise "invalid" exception
|
||||||
|
FSETXCP
|
||||||
|
|
||||||
|
chk_neg pla if x was negative
|
||||||
|
bpl ret
|
||||||
|
sub4 #0,x,x negate result
|
||||||
|
|
||||||
ret creturn 4:x return it
|
ret creturn 4:x return it
|
||||||
rtl
|
rtl
|
||||||
|
@ -889,6 +938,9 @@ ret creturn 4:x return it
|
||||||
* Rounds x to the nearest integer, rounding halfway cases
|
* Rounds x to the nearest integer, rounding halfway cases
|
||||||
* away from 0, and returns it as a long (if representable).
|
* away from 0, and returns it as a long (if representable).
|
||||||
*
|
*
|
||||||
|
* Note: This avoids calling FX2L or FX2C on negative numbers,
|
||||||
|
* because they are buggy for certain values.
|
||||||
|
*
|
||||||
****************************************************************
|
****************************************************************
|
||||||
*
|
*
|
||||||
lround start
|
lround start
|
||||||
|
@ -896,7 +948,7 @@ lroundf entry
|
||||||
lroundl entry
|
lroundl entry
|
||||||
result equ 1 result value
|
result equ 1 result value
|
||||||
|
|
||||||
csubroutine (10:x),4
|
csubroutine (10:x),8
|
||||||
|
|
||||||
phb
|
phb
|
||||||
phk
|
phk
|
||||||
|
@ -909,6 +961,10 @@ result equ 1 result value
|
||||||
pha
|
pha
|
||||||
FPROCENTRY
|
FPROCENTRY
|
||||||
|
|
||||||
|
pei x+8 save sign of x
|
||||||
|
asl x+8 x = abs(x)
|
||||||
|
lsr x+8
|
||||||
|
|
||||||
tdc round to integer with default rounding
|
tdc round to integer with default rounding
|
||||||
clc
|
clc
|
||||||
adc #x
|
adc #x
|
||||||
|
@ -917,30 +973,25 @@ result equ 1 result value
|
||||||
adc #result-x
|
adc #result-x
|
||||||
pea 0
|
pea 0
|
||||||
pha
|
pha
|
||||||
FX2L
|
FX2C
|
||||||
|
|
||||||
pea INEXACT
|
pea INEXACT
|
||||||
FTESTXCP if there was no inexact exception
|
FTESTXCP if there was no inexact exception
|
||||||
beq ret we are done: x was an integer/nan/inf
|
beq chkrange we are done: x was an integer/nan/inf
|
||||||
|
|
||||||
FGETENV
|
FGETENV
|
||||||
txa
|
txa
|
||||||
ora #TOWARDZERO*$4000 set rounding direction to "toward zero"
|
ora #TOWARDZERO*$4000 set rounding direction to "toward zero"
|
||||||
pha
|
pha
|
||||||
FSETENV
|
FSETENV
|
||||||
|
|
||||||
lda x+8
|
ph4 #onehalf x = x + 0.5 (rounded toward 0)
|
||||||
pha save sign of x
|
|
||||||
ora #$8000
|
|
||||||
sta x+8 x = -abs(x)
|
|
||||||
|
|
||||||
ph4 #onehalf x = x - 0.5 (rounded toward 0)
|
|
||||||
tdc
|
tdc
|
||||||
clc
|
clc
|
||||||
adc #x
|
adc #x
|
||||||
pea 0
|
pea 0
|
||||||
pha
|
pha
|
||||||
FSUBS
|
FADDS
|
||||||
tdc round to integer
|
tdc round to integer
|
||||||
clc
|
clc
|
||||||
adc #x
|
adc #x
|
||||||
|
@ -949,10 +1000,21 @@ result equ 1 result value
|
||||||
adc #result-x
|
adc #result-x
|
||||||
pea 0
|
pea 0
|
||||||
pha
|
pha
|
||||||
FX2L
|
FX2C
|
||||||
|
|
||||||
|
chkrange lda result+4 if x is out of range of long
|
||||||
|
ora result+6
|
||||||
|
bne flag_inv
|
||||||
|
cmpl result,#$80000000
|
||||||
|
blt chk_neg
|
||||||
|
bne flag_inv
|
||||||
|
lda 1,s
|
||||||
|
bmi chk_neg
|
||||||
|
flag_inv pea INVALID raise "invalid" exception
|
||||||
|
FSETXCP
|
||||||
|
|
||||||
pla if x was positive
|
chk_neg pla if x was negative
|
||||||
bmi ret
|
bpl ret
|
||||||
sub4 #0,result,result negate result
|
sub4 #0,result,result negate result
|
||||||
|
|
||||||
ret FPROCEXIT restore env & raise any new exceptions
|
ret FPROCEXIT restore env & raise any new exceptions
|
||||||
|
|
22
math2.macros
22
math2.macros
|
@ -358,6 +358,18 @@
|
||||||
.d
|
.d
|
||||||
sta 2+&op
|
sta 2+&op
|
||||||
mend
|
mend
|
||||||
|
macro
|
||||||
|
&l cmpl &n1,&n2
|
||||||
|
lclb &yistwo
|
||||||
|
&l ~setm
|
||||||
|
~lda.h &n1
|
||||||
|
~op.h cmp,&n2
|
||||||
|
bne ~a&SYSCNT
|
||||||
|
~lda &n1
|
||||||
|
~op cmp,&n2
|
||||||
|
~a&SYSCNT anop
|
||||||
|
~restm
|
||||||
|
mend
|
||||||
MACRO
|
MACRO
|
||||||
&LAB FCLASSS
|
&LAB FCLASSS
|
||||||
&LAB PEA $021C
|
&LAB PEA $021C
|
||||||
|
@ -443,12 +455,6 @@
|
||||||
JSL $E10000
|
JSL $E10000
|
||||||
MEND
|
MEND
|
||||||
MACRO
|
MACRO
|
||||||
&LAB FX2L
|
|
||||||
&LAB PEA $0310
|
|
||||||
LDX #$090A
|
|
||||||
JSL $E10000
|
|
||||||
MEND
|
|
||||||
MACRO
|
|
||||||
&LAB FXPWRY
|
&LAB FXPWRY
|
||||||
&LAB PEA $0012
|
&LAB PEA $0012
|
||||||
LDX #$0B0A
|
LDX #$0B0A
|
||||||
|
@ -569,8 +575,8 @@
|
||||||
JSL $E10000
|
JSL $E10000
|
||||||
MEND
|
MEND
|
||||||
MACRO
|
MACRO
|
||||||
&LAB FSUBS
|
&LAB FSETXCP
|
||||||
&LAB PEA $0202
|
&LAB PEA $0015
|
||||||
LDX #$090A
|
LDX #$090A
|
||||||
JSL $E10000
|
JSL $E10000
|
||||||
MEND
|
MEND
|
||||||
|
|
Loading…
Reference in New Issue