-
Notifications
You must be signed in to change notification settings - Fork 19
/
coprocessor.go
387 lines (318 loc) · 12.2 KB
/
coprocessor.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
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
// 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 coprocessor
import (
"fmt"
"strings"
"github.com/jetsetilly/gopher2600/coprocessor/developer/faults"
)
// CoProcExecutionState details the current condition of the coprocessor's execution
type CoProcExecutionState struct {
Sync CoProcSynchronisation
Yield CoProcYield
}
// CoProcSynchronisation is used to describe the VCS synchronisation state of
// the coprocessor
type CoProcSynchronisation int
func (s CoProcSynchronisation) String() string {
switch s {
case CoProcIdle:
return "idle"
case CoProcNOPFeed:
return "nop feed"
case CoProcStrongARMFeed:
return "strongarm feed"
case CoProcParallel:
return "parallel"
}
panic("unknown CoProcSynchronisation")
}
// List of valid CoProcSynchronisation values.
//
// A mapper will probably not employ all of these states depending on the
// synchronisation strategy. In reality the state will alternate between
// Idle-and-NOPFeed and StronARMFeed-and-Parallel.
//
// Other synchronisation strategies may need the addition of additional states
// or a different mechanism altogether.
const (
// the idle state means that the coprocessor is not interacting with the
// VCS at that moment. the coprocessor might be running but it is waiting
// to be instructed by the VCS program
CoProcIdle CoProcSynchronisation = iota
// a NOP feed describes the state where a cartridge mapper is waiting for
// the coprocessor to finish processing. in the meantime, the cartridge is
// feeding NOP instructions to the VCS
CoProcNOPFeed
// a StrongARM feed describes the state where the coprocessor has yielded
// to the VCS in order for the next instruction to be read by the 6507
CoProcStrongARMFeed
// parallel execution describes the state where the coprocessor is running
// without immediate concern with VCS synchronisation
CoProcParallel
)
// YieldHookResponse is returned by the CartYieldHook implementation to instruct
// the mapper in how to proceed
type YieldHookResponse int
// List of valid YieldHookCommands
const (
YieldHookContinue YieldHookResponse = iota
YieldHookEnd
)
// CartYieldHook allows a cartridge to halt execution if the cartridge
// coprocessor has reached a breakpoint or some other yield point (eg.
// undefined behaviour)
type CartYieldHook interface {
CartYield(CoProcYieldType) YieldHookResponse
}
// StubCartYieldHook is a stub implementation for the CartYieldHook interface.
type StubCartYieldHook struct{}
// CartYield is a stub implementation for the CartYieldHook interface.
func (_ StubCartYieldHook) CartYield(yld CoProcYieldType) YieldHookResponse {
if yld.Normal() {
return YieldHookContinue
}
return YieldHookEnd
}
// ExtendedRegisterGroup specifies the numeric range for a coprocessor register group
type ExtendedRegisterGroup struct {
// name of the group
Name string
// the numeric range of the registers in this group
Start int
End int
// whether the register range is private to the implementation. a private
// range means that is not meaningul in relation to DWARF
Private bool
// whether the registers in the group will return meaningful data from the
// RegisterFormatted() function
Formatted bool
// the label to use for the register
Label func(register int) string
}
// ExtendedRegisterSpec is the specification returned by CartCoProc.RegisterSpec() function
type ExtendedRegisterSpec []ExtendedRegisterGroup
// Group returns the ExtendedRegisterGroup from the specifciation if it exists.
// For the purposes of this function, group names are not case-sensitive
func (spec ExtendedRegisterSpec) Group(name string) (ExtendedRegisterGroup, bool) {
name = strings.ToUpper(name)
for _, grp := range spec {
if strings.ToUpper(grp.Name) == name {
return grp, true
}
}
return ExtendedRegisterGroup{}, false
}
// The basic set of registers present in a coprocessor. Every implementation
// should specify this group at a minimum
const ExtendedRegisterCoreGroup = "Core"
// CartCoProc is implemented by processors that are used in VCS cartridges.
// Principally this means ARM type processors but other processor types may be
// possible.
type CartCoProc interface {
ProcessorID() string
// set disassembler and developer hooks
SetDisassembler(CartCoProcDisassembler)
SetDeveloper(CartCoProcDeveloper)
// breakpoint control of coprocessor
BreakpointsEnable(bool)
// RegisterSpec returns the specification for the registers visible in the
// coprocessor. Implementations should ensure that these conform to the
// DWARF extended register specification for the processor type (if
// avaiable)
//
// Additional registers not required by the DWARF specification may be
// supported as required
//
// Implementations should include the ExtendedRegisterCoreGroup at a minimum
RegisterSpec() ExtendedRegisterSpec
// the contents of a register. the implementation should support extended
// register values defined by DWARF for the coprocessor
//
// if the register is unrecognised or unsupported the function will return
// false
Register(register int) (uint32, bool)
// the contents of the register and a formatted string appropriate for the
// register type
RegisterFormatted(register int) (uint32, string, bool)
// as above but setting the value of the register
RegisterSet(register int, value uint32) bool
// returns the current stack frame
StackFrame() uint32
// read coprocessor memory address for 32bit value. return false if address is out of range
Peek(addr uint32) (uint32, bool)
}
// CartCoProcBus is implemented by cartridge mappers that have a coprocessor
type CartCoProcBus interface {
// return the actual coprocessor interface. if the cartridge implements the
// CartCoProcBus then it should always return a non-nil CartCoProc instance
GetCoProc() CartCoProc
// set interface for cartridge yields
SetYieldHook(CartYieldHook)
// the state of the coprocessor
CoProcExecutionState() CoProcExecutionState
}
// CartCoProcRelocatable is implemented by cartridge mappers where coprocessor
// programs can be located anywhere in the coprcessor's memory
type CartCoProcRelocatable interface {
// returns the offset of the named ELF section and whether the named
// section exists. not all cartridges that implement this interface will be
// able to meaningfully answer this function call
ELFSection(string) ([]uint8, uint32, bool)
}
// CartCoProcOrigin is implemented by cartridge mappers where coprocessor
// programs are located at a specific address
type CartCoProcOrigin interface {
ExecutableOrigin() uint32
}
// CartCoProcProfileEntry indicates the number of coprocessor cycles used by the
// instruction at the specified adress
type CartCoProcProfileEntry struct {
Addr uint32
Cycles float32
}
// CartCoProcProfiler is shared by CartCoProcDeveloper and used by a coprocessor
// to record profiling information
type CartCoProcProfiler struct {
Entries []CartCoProcProfileEntry
}
// CartCoProcDeveloper is implemented by a coprocessor to provide functions
// available to developers when the source code is available.
type CartCoProcDeveloper interface {
// a memory fault has occured
MemoryFault(event string, explanation faults.Category, instructionAddr uint32, accessAddr uint32)
// returns the highest address used by the program. the coprocessor uses
// this value to detect stack collisions
HighAddress() uint32
// checks if address has a breakpoint assigned to it
CheckBreakpoint(addr uint32) bool
// returns a map that can be used to count cycles for each PC address
Profiling() *CartCoProcProfiler
// notifies developer that the start of a new profiling session is about to begin
StartProfiling()
// instructs developer implementation to accumulate profiling data. there
// can be many calls to profiling profiling for every call to start
// profiling
ProcessProfiling()
// called whenever the ARM yields to the VCS. it communicates the PC of the
// most recent instruction, the current PC (as it is now), and the reason
// for the yield
OnYield(currentPC uint32, reason CoProcYield)
}
// CoProcYield describes a coprocessor yield state
type CoProcYield struct {
Type CoProcYieldType
Error error
}
// CoProcYieldType specifies the type of yield. This is a broad categorisation
type CoProcYieldType int
func (t CoProcYieldType) String() string {
switch t {
case YieldProgramEnded:
return "ended"
case YieldSyncWithVCS:
return "sync"
case YieldBreakpoint:
return "breakpoint"
case YieldUndefinedBehaviour:
return "undefined behaviour"
case YieldUnimplementedFeature:
return "unimplement feature"
case YieldMemoryAccessError:
return "memory error"
case YieldStackError:
return "stack error"
case YieldExecutionError:
return "execution error"
case YieldRunning:
return "running"
}
panic("unknown CoProcYieldType")
}
// Normal returns true if yield type is expected during normal operation of the
// coprocessor
func (t CoProcYieldType) Normal() bool {
return t == YieldRunning || t == YieldProgramEnded || t == YieldSyncWithVCS
}
// Bug returns true if the yield type indicates a likely bug
func (t CoProcYieldType) Bug() bool {
return t == YieldUndefinedBehaviour || t == YieldUnimplementedFeature ||
t == YieldExecutionError || t == YieldMemoryAccessError || t == YieldStackError
}
// List of CoProcYieldType values
const (
// the coprocessor has yielded because the program has ended. in this instance the
// CoProcessor is not considered to be in a "yielded" state and can be modified
//
// Expected YieldReason for CDF and DPC+ type ROMs
YieldProgramEnded CoProcYieldType = iota
// the coprocessor has reached a synchronisation point in the program. it
// must wait for the VCS before continuing
//
// Expected YieldReason for ACE and ELF type ROMs
YieldSyncWithVCS
// a user supplied breakpoint has been encountered
YieldBreakpoint
// the program has triggered undefined behaviour in the coprocessor
YieldUndefinedBehaviour
// the program has triggered an unimplemented feature in the coprocessor
YieldUnimplementedFeature
// the program has tried to access memory illegally. details will have been
// communicated by the IllegalAccess() function of the CartCoProcDeveloper
// interface
YieldMemoryAccessError
// something has gone wrong with the stack
YieldStackError
// execution error indicates that something has gone very wrong
YieldExecutionError
// the coprocessor has not yet yielded and is still running
YieldRunning
)
// CartCoProcDisasmSummary represents a summary of a coprocessor execution.
type CartCoProcDisasmSummary interface {
String() string
}
// CartCoProcDisasmEntry represents a single decoded instruction by the coprocessor.
type CartCoProcDisasmEntry interface {
String() string
Key() string
CSV() string
Size() int
}
// CartCoProcDisassembler defines the functions that must be defined for a
// disassembler to be attached to a coprocessor.
type CartCoProcDisassembler interface {
// Start is called at the beginning of coprocessor program execution.
Start()
// Step called after every instruction in the coprocessor program.
Step(CartCoProcDisasmEntry)
// End is called when coprocessor program has finished.
End(CartCoProcDisasmSummary)
}
// CartCoProcDisassemblerStdout is a minimial implementation of the CartCoProcDisassembler
// interface. It outputs entries to stdout immediately upon request.
type CartCoProcDisassemblerStdout struct {
}
// Start implements the CartCoProcDisassembler interface.
func (c *CartCoProcDisassemblerStdout) Start() {
}
// Instruction implements the CartCoProcDisassembler interface.
func (c *CartCoProcDisassemblerStdout) Step(e CartCoProcDisasmEntry) {
fmt.Println(e)
}
// End implements the CartCoProcDisassembler interface.
func (c *CartCoProcDisassemblerStdout) End(s CartCoProcDisasmSummary) {
fmt.Println(s)
}