-
Notifications
You must be signed in to change notification settings - Fork 19
/
static.go
306 lines (263 loc) · 8.76 KB
/
static.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
// 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 dpcplus
import (
"fmt"
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/mapper"
)
type segment struct {
name string
data []byte
origin uint32
memtop uint32
}
func (seg segment) snapshot() segment {
n := seg
n.data = make([]byte, len(seg.data))
copy(n.data, seg.data)
return n
}
// Static implements the mapper.CartStatic interface.
type Static struct {
version version
// slices of cartDataROM that should not be modified during execution
driverROM segment
customROM segment
dataROM segment
freqROM segment
// slices of cartDataRAM that will be modified during execution
driverRAM segment
dataRAM segment
freqRAM segment
}
func (cart *dpcPlus) newDPCplusStatic(version version, cartData []byte) (*Static, error) {
stc := Static{
version: version,
}
// the offset into the cart data where the data segment begins
dataOffset := driverSize + (cart.bankSize * cart.NumBanks())
// ARM driver
stc.driverROM.name = "Driver ROM"
stc.driverROM.data = cartData[:driverSize]
stc.driverROM.origin = version.driverROMOrigin
stc.driverROM.memtop = version.driverROMOrigin + uint32(len(stc.driverROM.data)) - 1
if stc.driverROM.memtop > version.driverROMMemtop {
return nil, fmt.Errorf("driver ROM is too large")
}
// custom ARM program immediately after the ARM driver and where we've
// figured the data segment to start. note that some of this will be the
// 6507 program but we can't really know for sure where that begins.
stc.customROM.name = "Custom ROM"
stc.customROM.data = cartData[driverSize:dataOffset]
stc.customROM.origin = version.customROMOrigin
stc.customROM.memtop = version.customROMOrigin + uint32(len(stc.customROM.data)) - 1
if stc.customROM.memtop > version.customROMMemtop {
return nil, fmt.Errorf("custom ROM is too large")
}
// gfx and frequency table at end of file
// unlike CDF ROMs data and frequency tables are initialised from the ROM
stc.dataROM.name = "Data ROM"
stc.dataROM.data = cartData[dataOffset : dataOffset+dataSize]
stc.dataROM.origin = version.dataROMOrigin
stc.dataROM.memtop = version.dataROMOrigin + uint32(len(stc.dataROM.data)) - 1
if stc.dataROM.memtop > version.dataROMMemtop {
return nil, fmt.Errorf("data ROM is too large")
}
stc.freqROM.name = "Freq ROM"
stc.freqROM.data = cartData[dataOffset+dataSize:]
stc.freqROM.origin = version.freqROMOrigin
stc.freqROM.memtop = version.freqROMOrigin + uint32(len(stc.freqROM.data)) - 1
if stc.freqROM.memtop > version.freqROMMemtop {
return nil, fmt.Errorf("freq ROM is too large")
}
// RAM areas. because these areas are the ones that we are likely to show
// most often in UI, the RAM suffix has been omitted
stc.driverRAM.name = "Driver"
stc.driverRAM.data = make([]byte, len(stc.driverROM.data))
stc.driverRAM.origin = version.driverRAMOrigin
stc.driverRAM.memtop = version.driverRAMOrigin + uint32(len(stc.driverRAM.data)) - 1
if stc.driverRAM.memtop > version.driverRAMMemtop {
return nil, fmt.Errorf("driver RAM is too large")
}
copy(stc.driverRAM.data, stc.driverROM.data)
stc.dataRAM.name = "Data"
stc.dataRAM.data = make([]byte, len(stc.dataROM.data))
stc.dataRAM.origin = version.dataRAMOrigin
stc.dataRAM.memtop = version.dataRAMOrigin + uint32(len(stc.dataRAM.data)) - 1
if stc.dataRAM.memtop > version.dataRAMMemtop {
return nil, fmt.Errorf("data RAM is too large")
}
copy(stc.dataRAM.data, stc.dataROM.data)
stc.freqRAM.name = "Freq"
stc.freqRAM.data = make([]byte, len(stc.freqROM.data))
stc.freqRAM.origin = version.freqRAMOrigin
stc.freqRAM.memtop = version.freqRAMOrigin + uint32(len(stc.freqRAM.data)) - 1
if stc.freqRAM.memtop > version.freqRAMMemtop {
return nil, fmt.Errorf("freq RAM is too large")
}
copy(stc.freqRAM.data, stc.freqROM.data)
return &stc, nil
}
// ResetVectors implements the arm7tdmi.SharedMemory interface.
func (stc *Static) ResetVectors() (uint32, uint32, uint32) {
return stc.version.stackOrigin, stc.customROM.origin, stc.customROM.origin + 8
}
// IsExecutable implements the arm.SharedMemory interface.
func (mem *Static) IsExecutable(addr uint32) bool {
return true
}
func (stc *Static) Snapshot() *Static {
n := *stc
n.driverRAM = stc.driverRAM.snapshot()
n.dataRAM = stc.dataRAM.snapshot()
n.freqRAM = stc.freqRAM.snapshot()
return &n
}
// MapAddress implements the arm7tdmi.SharedMemory interface.
func (stc *Static) MapAddress(addr uint32, write bool) (*[]byte, uint32) {
// tests arranged in order of most likely to be used. determined by running
// ZaxxonHDDemo through the go profiler
// data (RAM)
if addr >= stc.dataRAM.origin && addr <= stc.dataRAM.memtop {
return &stc.dataRAM.data, stc.dataRAM.origin
}
// custom ARM code (ROM)
if addr >= stc.customROM.origin && addr <= stc.customROM.memtop {
if write {
return nil, 0
}
return &stc.customROM.data, stc.customROM.origin
}
// driver ARM code (RAM)
if addr >= stc.driverRAM.origin && addr <= stc.driverRAM.memtop {
return &stc.driverRAM.data, stc.driverRAM.origin
}
// frequency table (RAM)
if addr >= stc.freqRAM.origin && addr <= stc.freqRAM.memtop {
return &stc.freqRAM.data, stc.freqRAM.origin
}
// driver ARM code (ROM)
if addr >= stc.driverROM.origin && addr <= stc.driverROM.memtop {
if write {
return nil, 0
}
return &stc.driverROM.data, stc.driverROM.origin
}
// data (ROM)
if addr >= stc.dataROM.origin && addr <= stc.dataROM.memtop {
if write {
return nil, 0
}
return &stc.dataROM.data, stc.dataROM.origin
}
// frequency table (ROM)
if addr >= stc.freqROM.origin && addr <= stc.freqROM.memtop {
if write {
return nil, 0
}
return &stc.freqROM.data, stc.freqROM.origin
}
return nil, 0
}
// Segments implements the mapper.CartStatic interface
func (stc *Static) Segments() []mapper.CartStaticSegment {
return []mapper.CartStaticSegment{
{
Name: stc.driverRAM.name,
Origin: stc.driverRAM.origin,
Memtop: stc.driverRAM.memtop,
},
{
Name: stc.dataRAM.name,
Origin: stc.dataRAM.origin,
Memtop: stc.dataRAM.memtop,
},
{
Name: stc.freqRAM.name,
Origin: stc.freqRAM.origin,
Memtop: stc.freqRAM.memtop,
},
}
}
// Reference implements the mapper.CartStatic interface
func (stc *Static) Reference(segment string) ([]uint8, bool) {
switch segment {
case stc.driverRAM.name:
return stc.driverRAM.data, true
case stc.dataRAM.name:
return stc.dataRAM.data, true
case stc.freqRAM.name:
return stc.freqRAM.data, true
}
return []uint8{}, false
}
// Read8bit implements the mapper.CartStatic interface
func (stc *Static) Read8bit(addr uint32) (uint8, bool) {
mem, origin := stc.MapAddress(addr, false)
addr -= origin
if mem == nil || addr >= uint32(len(*mem)) {
return 0, false
}
return (*mem)[addr], true
}
// Read16bit implements the mapper.CartStatic interface
func (stc *Static) Read16bit(addr uint32) (uint16, bool) {
mem, origin := stc.MapAddress(addr, false)
addr -= origin
if mem == nil || addr >= uint32(len(*mem)-1) {
return 0, false
}
return uint16((*mem)[addr]) |
uint16((*mem)[addr+1])<<8, true
}
// Read32bit implements the mapper.CartStatic interface
func (stc *Static) Read32bit(addr uint32) (uint32, bool) {
mem, origin := stc.MapAddress(addr, false)
addr -= origin
if mem == nil || addr >= uint32(len(*mem)-3) {
return 0, false
}
return uint32((*mem)[addr]) |
uint32((*mem)[addr+1])<<8 |
uint32((*mem)[addr+2])<<16 |
uint32((*mem)[addr+3])<<24, true
}
// GetStatic implements the mapper.CartStaticBus interface
func (cart *dpcPlus) GetStatic() mapper.CartStatic {
return cart.state.static.Snapshot()
}
// StaticWrite implements the mapper.CartStaticBus interface
func (cart *dpcPlus) PutStatic(segment string, idx int, data uint8) bool {
switch segment {
case cart.state.static.driverRAM.name:
if idx >= len(cart.state.static.driverRAM.data) {
return false
}
cart.state.static.driverRAM.data[idx] = data
case cart.state.static.dataRAM.name:
if idx >= len(cart.state.static.dataRAM.data) {
return false
}
cart.state.static.dataRAM.data[idx] = data
case cart.state.static.freqRAM.name:
if idx >= len(cart.state.static.freqRAM.data) {
return false
}
cart.state.static.freqRAM.data[idx] = data
default:
return false
}
return true
}