-
Notifications
You must be signed in to change notification settings - Fork 8
/
divisor.go
150 lines (134 loc) · 3.62 KB
/
divisor.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package main
import (
"ndsemu/emu/hwio"
log "ndsemu/emu/logger"
)
var modDiv = log.NewModule("divisor")
type HwDivisor struct {
DivCnt hwio.Reg32 `hwio:"offset=0x00,rwmask=0x3,wcb,rcb"`
Numer hwio.Reg64 `hwio:"offset=0x10,wcb=WriteIN"`
Denom hwio.Reg64 `hwio:"offset=0x18,wcb=WriteIN"`
Res hwio.Reg64 `hwio:"offset=0x20,rcb"`
Mod hwio.Reg64 `hwio:"offset=0x28,rcb"`
SqrtCnt hwio.Reg32 `hwio:"offset=0x30,rwmask=0x1"`
SqrtRes hwio.Reg32 `hwio:"offset=0x34,readonly,rcb"`
SqrtParm hwio.Reg64 `hwio:"offset=0x38"`
dirty bool
}
func NewHwDivisor() *HwDivisor {
hwdiv := new(HwDivisor)
hwio.MustInitRegs(hwdiv)
return hwdiv
}
func (div *HwDivisor) WriteIN(_, _ uint64) { div.dirty = true }
func (div *HwDivisor) WriteDIVCNT(_, _ uint32) { div.dirty = true }
func (div *HwDivisor) ReadRES(val uint64) uint64 {
if div.dirty {
div.calc()
div.dirty = false
}
return div.Res.Value
}
func (div *HwDivisor) ReadMOD(val uint64) uint64 {
if div.dirty {
div.calc()
div.dirty = false
}
return div.Mod.Value
}
func (div *HwDivisor) ReadDIVCNT(val uint32) uint32 {
if div.Denom.Value == 0 {
// division by zero flag -- always check the full denominator, even if
// configured in 32-bit mode
val |= (1 << 14)
}
return val
}
func (div *HwDivisor) calc() {
mode := div.DivCnt.Value & 3
if mode == 0 {
// 32-bit divisions
if int32(div.Denom.Value) == 0 {
div.Mod.Value = div.Numer.Value
if int32(div.Numer.Value) >= 0 {
div.Res.Value = uint64(0xFFFFFFFFF)
} else {
div.Res.Value = ^uint64(0xFFFFFFFFF)
}
} else if int32(div.Denom.Value) == -1 && uint32(div.Numer.Value) == 0x80000000 {
div.Mod.Value = 0
// upper 64-bits are 0 (no sign-extension)
div.Res.Value = uint64(uint32(div.Numer.Value))
} else {
res := int32(div.Numer.Value) / int32(div.Denom.Value)
mod := int32(div.Numer.Value) % int32(div.Denom.Value)
// results are sign-extended
div.Res.Value = uint64(int64(res))
div.Mod.Value = uint64(int64(mod))
}
modDiv.InfoZ("32-bit division").
Int32("num", int32(div.Numer.Value)).
Int32("den", int32(div.Denom.Value)).
Int64("res", int64(div.Res.Value)).
Int64("mod", int64(div.Mod.Value)).
End()
return
}
denom := int64(div.Denom.Value)
if mode != 2 {
// 64-bit / 32-bit division: truncate (and sign-extend)
// the denominator.
denom = int64(int32(div.Denom.Value))
}
if denom == 0 {
div.Mod.Value = div.Numer.Value
if div.Numer.Value > 0 {
div.Res.Value = uint64(0xFFFFFFFFFFFFFFFF) // -1
} else {
div.Res.Value = 1
}
} else if denom == -1 && uint64(div.Numer.Value) == 0x8000000000000000 {
div.Mod.Value = 0
div.Res.Value = div.Numer.Value
} else {
// Normal division
div.Res.Value = uint64(int64(div.Numer.Value) / denom)
div.Mod.Value = uint64(int64(div.Numer.Value) % denom)
}
modDiv.InfoZ("64-bit division").
Int64("num", int64(div.Numer.Value)).
Int64("den", denom).
Int64("res", int64(div.Res.Value)).
Int64("mod", int64(div.Mod.Value)).
End()
}
func (div *HwDivisor) ReadSQRTRES(_ uint32) uint32 {
if div.SqrtParm.Value == 0x0 {
return 0
}
val := div.SqrtParm.Value
resbits := 32
if div.SqrtCnt.Value&1 == 0 {
resbits = 16
val &= 0xFFFFFFFF
}
res := uint32(0)
add := uint32(1 << uint(resbits-1))
for i := 0; i < resbits; i++ {
temp := res | add
g2 := uint64(temp) * uint64(temp)
if val >= g2 {
res = temp
}
add >>= 1
}
// Sanity check -- shouldn't be necessary
if uint64(res)*uint64(res) > val || uint64(res+1)*uint64(res+1) <= val {
modDiv.WithFields(log.Fields{
"parm": val,
"res": res,
"nbits": resbits * 2,
}).Fatal("bug in sqrt computation")
}
return res
}