/
functions_conversions.go
123 lines (103 loc) · 3.44 KB
/
functions_conversions.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// This file is part of Gopher2600.
//
// Gopher2600 is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Gopher2600 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
package fpu
import "math"
func (fpu *FPU) FixedToFP(operand uint64, N int, fractionBits int, unsigned bool, nearest bool, fpscrControlled bool) uint64 {
// page A2-59 of "ARMv7-M"
var fpscr FPSCR
if fpscrControlled {
fpscr = fpu.Status
} else {
fpscr = fpu.StandardFPSCRValue()
}
if nearest {
fpscr.SetRMode(FPRoundNearest)
}
var realOperand float64
switch N {
case 32:
if unsigned {
realOperand = float64(uint32(operand) / uint32(math.Pow(2, float64(fractionBits))))
} else {
realOperand = float64(int32(operand) / int32(math.Pow(2, float64(fractionBits))))
}
case 64:
if unsigned {
realOperand = float64(uint64(operand) / uint64(math.Pow(2, float64(fractionBits))))
} else {
realOperand = float64(int64(operand) / int64(math.Pow(2, float64(fractionBits))))
}
default:
panic("unsupported number of bits in FixedToFP()")
}
if realOperand == 0.0 {
return fpu.FPZero(false, N)
}
return fpu.FPRound(realOperand, N, fpscr)
}
func (fpu *FPU) FPToFixed(operand uint64, N int, fractionBits int, unsigned bool, roundZero bool, fpscrControlled bool) uint64 {
// page A2-58 to A2-59 of "ARMv7-M"
// comments in quotation marks are taken from the psuedo code for FPToFixed()
var fpscr FPSCR
if fpscrControlled {
fpscr = fpu.Status
} else {
fpscr = fpu.StandardFPSCRValue()
}
if roundZero {
fpscr.SetRMode(FPRoundZero)
}
typ, _, val := fpu.FPUnpack(operand, N, fpscr)
// "For NaNs and infinities, FPUnpack() has produced a value that will round to the
// required result of the conversion. Also, the value produced for infinities will
// cause the conversion to overflow and signal an Invalid Operation floating-point
// exception as required. NaNs must also generate such a floating-point exception"
if typ == FPType_SNaN || typ == FPType_QNaN {
fpu.FPProcessException(FPExc_InvalidOp, fpscr)
}
// "Scale value by specified number of fraction bits, then start rounding to an integer
// and determine the rounding error"
val = val * math.Pow(2, float64(fractionBits))
intResult := int(val)
roundingError := val - float64(intResult)
// "Apply the specified rounding mode"
var roundUp bool
switch fpscr.RMode() {
case FPRoundNearest:
roundUp = (roundingError > 0.5) || (roundingError == 0.5 && intResult&0x01 == 0x01)
case FPRoundPlusInf:
roundUp = roundingError != 0.0
case FPRoundNegInf:
roundUp = false
case FPRoundZero:
roundUp = (roundingError != 0.0 && intResult < 0)
}
if roundUp {
intResult++
}
var result uint64
var overflow bool
if unsigned {
result, overflow = fpu.UnsignedSatQ(intResult, N)
} else {
result, overflow = fpu.SignedSatQ(intResult, N)
}
if overflow {
fpu.FPProcessException(FPExc_InvalidOp, fpscr)
} else if roundingError != 0.0 {
fpu.FPProcessException(FPExc_Inexact, fpscr)
}
return result
}