mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-06-02 12:08:01 -04:00
coprocessor interface will return extended registers spec
ammended COPROC REGS function to use new spec system. FPU registers display formatted values removed RegisterStatus() function for now. the ARM status register is more complex in reality than how I've modelled it (CSPR vs ASPR and and an ESPR in some models). the information is there but not in a coherent way. probably requires adding a status register group
This commit is contained in:
parent
6c7dc2ec75
commit
74c3457406
|
@ -206,12 +206,16 @@ be found at:
|
|||
https://www.st.com/resource/en/reference_manual/dm00031020-stm32f405-415-stm32f407-417-stm32f427-437-and-stm32f429-439-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf
|
||||
|
||||
In relation to ARM development, information about the DWARF format is being
|
||||
taken from the DWARF2 and DWARF4 standards
|
||||
taken from the DWARF2 and DWARF4 standards:
|
||||
|
||||
https://dwarfstd.org/doc/dwarf-2.0.0.pdf
|
||||
|
||||
https://dwarfstd.org/doc/DWARF4.pdf
|
||||
|
||||
ARM specific DWARF information taken from:
|
||||
|
||||
https://github.com/ARM-software/abi-aa/releases/download/2023Q1/aadwarf32.pdf
|
||||
|
||||
## Other Software / Libraries
|
||||
|
||||
The following projects are used in the `Gopher2600` project:
|
||||
|
|
|
@ -101,6 +101,36 @@ func (_ StubCartYieldHook) CartYield(yld CoProcYieldType) YieldHookResponse {
|
|||
return YieldHookEnd
|
||||
}
|
||||
|
||||
// ExtendedRegisterGroup specifies the numeric range for a coprocessor register group
|
||||
type ExtendedRegisterGroup struct {
|
||||
// name of the group
|
||||
Name string
|
||||
|
||||
// the prefix letter to use when labelling the register. See the Label()
|
||||
// function which will also adjust the register number appropriately
|
||||
Prefix string
|
||||
|
||||
// the numeric range of the registers in this group
|
||||
Start int
|
||||
End int
|
||||
|
||||
// whether the registers int he group will return meaningful data from the
|
||||
// RegisterFormatted() function
|
||||
Formatted bool
|
||||
}
|
||||
|
||||
// Label creates a string for the numbered register
|
||||
func (group ExtendedRegisterGroup) Label(register int) string {
|
||||
return fmt.Sprintf("%s%02d", group.Prefix, register-group.Start)
|
||||
}
|
||||
|
||||
// ExtendedRegisterSpec is the specification returned by CartCoProc.RegisterSpec() function
|
||||
type ExtendedRegisterSpec map[string]ExtendedRegisterGroup
|
||||
|
||||
// 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.
|
||||
|
@ -114,15 +144,30 @@ type CartCoProc interface {
|
|||
// 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(n int) (uint32, bool)
|
||||
Register(register int) (uint32, bool)
|
||||
|
||||
// as above but setting the named register
|
||||
RegisterSet(n int, value 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
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/cartridgeloader"
|
||||
"github.com/jetsetilly/gopher2600/coprocessor"
|
||||
coproc_breakpoints "github.com/jetsetilly/gopher2600/coprocessor/developer/breakpoints"
|
||||
"github.com/jetsetilly/gopher2600/coprocessor/developer/callstack"
|
||||
"github.com/jetsetilly/gopher2600/coprocessor/developer/dwarf"
|
||||
|
@ -1467,18 +1468,25 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
|
|||
}
|
||||
|
||||
case "REGS":
|
||||
coproc := bus.GetCoProc()
|
||||
|
||||
// list registers in order until we get a not-ok reply
|
||||
regs := func(start int, id rune) {
|
||||
reg := start
|
||||
regs := func(group coprocessor.ExtendedRegisterGroup) {
|
||||
s := strings.Builder{}
|
||||
for {
|
||||
if n, ok := bus.GetCoProc().Register(reg); !ok {
|
||||
break
|
||||
} else {
|
||||
s.WriteString(fmt.Sprintf("%c%02d: %08x\t", id, reg-start, n))
|
||||
for r := group.Start; r <= group.End; r++ {
|
||||
v, f, ok := coproc.RegisterFormatted(r)
|
||||
if !ok {
|
||||
dbg.printLine(terminal.StyleError,
|
||||
fmt.Sprintf("coprocessor doesn't have the %d register in the %s group", r, group.Name))
|
||||
return
|
||||
}
|
||||
reg++
|
||||
if (reg-start)%3 == 0 {
|
||||
if group.Formatted {
|
||||
s.WriteString(fmt.Sprintf("%s: %s [%08x]\t", group.Label(r), f, v))
|
||||
} else {
|
||||
s.WriteString(fmt.Sprintf("%s: %08x\t", group.Label(r), v))
|
||||
}
|
||||
|
||||
if (r-group.Start+1)%3 == 0 {
|
||||
dbg.printLine(terminal.StyleFeedback, s.String())
|
||||
s.Reset()
|
||||
}
|
||||
|
@ -1488,12 +1496,20 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
|
|||
}
|
||||
}
|
||||
|
||||
// core registers
|
||||
regs(0, 'R')
|
||||
|
||||
// fpu registers
|
||||
if arg, ok := tokens.Get(); ok && arg == "FPU" {
|
||||
regs(64, 'S')
|
||||
// use named group if supplied or default to core group
|
||||
spec := coproc.RegisterSpec()
|
||||
if arg, ok := tokens.Get(); ok {
|
||||
if group, ok := spec[arg]; ok {
|
||||
regs(group)
|
||||
} else {
|
||||
dbg.printLine(terminal.StyleError, fmt.Sprintf("coprocessor doesn't have a %s register group", arg))
|
||||
}
|
||||
} else {
|
||||
if group, ok := spec[coprocessor.ExtendedRegisterCoreGroup]; ok {
|
||||
regs(group)
|
||||
} else {
|
||||
dbg.printLine(terminal.StyleError, "coprocessor doesn't seem to have any registers")
|
||||
}
|
||||
}
|
||||
|
||||
case "SET":
|
||||
|
|
|
@ -358,7 +358,15 @@ basis and only for the duration of the session (the ROM must be changed for it t
|
|||
The NICK and ID can be changed for the session but also be saved to disk and be used across
|
||||
all PlusROM cartridges.`,
|
||||
|
||||
cmdCoProc: `Returns information about any coprocessor in the inserted cartridge.`,
|
||||
cmdCoProc: `Returns information about any coprocessor in the inserted cartridge.
|
||||
|
||||
The REGS argument will display the registers of the coprocessor. By default it will show the
|
||||
"core" registers. Other register groups can be specified. For example, some ARM coprocessors
|
||||
will have a "FPU" group.
|
||||
|
||||
The SET argument will set a register value. The 'register' number must be the 'extended register'
|
||||
number rather than the display number.
|
||||
`,
|
||||
|
||||
cmdDWARF: `Debugging information for cartridge types that support DWARF debugging.
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ var commandTemplate = []string{
|
|||
|
||||
// peripherals (components that might not be present)
|
||||
cmdPlusROM + " (NICK [%<name>S]|ID [%<id>S]|HOST [%<host>S]|PATH [%<path>S])",
|
||||
cmdCoProc + " (ID|LIST [FAULTS|SOURCEFILES|FUNCTIONS]|TOP (%<top>N)|MEM|REGS (FPU)|SET %<register>N %<value>N|STEP)",
|
||||
cmdCoProc + " (ID|LIST [FAULTS|SOURCEFILES|FUNCTIONS]|TOP (%<top>N)|MEM|REGS %<group>S|SET %<register>N %<value>N|STEP)",
|
||||
cmdDWARF + " [FUNCTIONS|GLOBALS|LOCALS {DERIVATION|RANGES|ERROR}|FRAMEBASE {DERIVATION}|LINE %<file:line>S|CALLSTACK|CALLERS %<function>S]",
|
||||
|
||||
// user input
|
||||
|
|
|
@ -80,7 +80,9 @@ type decodeFunction func(opcode uint16) *DisasmEntry
|
|||
type ARMState struct {
|
||||
// ARM registers
|
||||
registers [NumCoreRegisters]uint32
|
||||
status Status
|
||||
|
||||
// see note about status register in the documentation of the type
|
||||
status status
|
||||
|
||||
mam mam
|
||||
rng peripherals.RNG
|
||||
|
@ -670,48 +672,11 @@ func (arm *ARM) Interrupt() {
|
|||
arm.state.yield.Type = coprocessor.YieldSyncWithVCS
|
||||
}
|
||||
|
||||
// Register implements the coprocess.CartCoProc interface. Returns the value in
|
||||
// the register. Returns false if the requested register is not recognised
|
||||
func (arm *ARM) Register(extendedReg int) (uint32, bool) {
|
||||
// general registers
|
||||
if extendedReg <= 15 {
|
||||
return arm.state.registers[extendedReg], true
|
||||
}
|
||||
|
||||
// FPU registers
|
||||
if extendedReg >= 64 && extendedReg <= 95 {
|
||||
return arm.state.fpu.Registers[extendedReg-64], true
|
||||
}
|
||||
|
||||
// extended register values for ARM defined in:
|
||||
// https://github.com/ARM-software/abi-aa/releases/download/2023Q1/aadwarf32.pdf
|
||||
|
||||
// TODO: implement extended registers
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// RegisterSet implements the coprocess.CartCoProc interface. Set the register
|
||||
// to the specified value. Returns false if the requested register is not
|
||||
// recognised
|
||||
func (arm *ARM) RegisterSet(reg int, value uint32) bool {
|
||||
if reg >= NumCoreRegisters {
|
||||
return false
|
||||
}
|
||||
arm.state.registers[reg] = value
|
||||
return true
|
||||
}
|
||||
|
||||
// StackFrame implements the coprocess.CartCoProc interface
|
||||
func (arm *ARM) StackFrame() uint32 {
|
||||
return arm.state.stackFrame
|
||||
}
|
||||
|
||||
// Status returns a copy of the current status register.
|
||||
func (arm *ARM) Status() Status {
|
||||
return arm.state.status
|
||||
}
|
||||
|
||||
// BreakpointsEnable implements the coprocessor.CartCoProc interface. Enables
|
||||
// breakpoint checking for the duration that disable is true.
|
||||
func (arm *ARM) BreakpointsEnable(enable bool) {
|
||||
|
|
120
hardware/memory/cartridge/arm/extended_registers.go
Normal file
120
hardware/memory/cartridge/arm/extended_registers.go
Normal file
|
@ -0,0 +1,120 @@
|
|||
// 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 arm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/coprocessor"
|
||||
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/arm/architecture"
|
||||
)
|
||||
|
||||
// this file implements the coprocessor.CartCoProc interface where it relates to
|
||||
// coprocessor registers
|
||||
//
|
||||
// extended registers are how the DWARF specification describes the system for
|
||||
// identifying registers in the coprocessor. details of the extended registers
|
||||
// can be found at:
|
||||
//
|
||||
// https://github.com/ARM-software/abi-aa/releases/download/2023Q1/aadwarf32.pdf
|
||||
|
||||
var arm7tdmiRegisterSpec = coprocessor.ExtendedRegisterSpec{
|
||||
coprocessor.ExtendedRegisterCoreGroup: {
|
||||
Name: coprocessor.ExtendedRegisterCoreGroup,
|
||||
Prefix: "R",
|
||||
Start: 0,
|
||||
End: 15,
|
||||
},
|
||||
}
|
||||
|
||||
var armv7mRegisterSpec = coprocessor.ExtendedRegisterSpec{
|
||||
coprocessor.ExtendedRegisterCoreGroup: {
|
||||
Name: coprocessor.ExtendedRegisterCoreGroup,
|
||||
Prefix: "R",
|
||||
Start: 0,
|
||||
End: 15,
|
||||
},
|
||||
"fpu": {
|
||||
Name: "fpu",
|
||||
Prefix: "S",
|
||||
Start: 64,
|
||||
End: 95,
|
||||
Formatted: true,
|
||||
},
|
||||
}
|
||||
|
||||
// RegisterSpec implements the coprocessor.CartCoProc interface
|
||||
func (arm *ARM) RegisterSpec() coprocessor.ExtendedRegisterSpec {
|
||||
switch arm.mmap.ARMArchitecture {
|
||||
case architecture.ARM7TDMI:
|
||||
return arm7tdmiRegisterSpec
|
||||
case architecture.ARMv7_M:
|
||||
return armv7mRegisterSpec
|
||||
}
|
||||
panic("register spec: unrecognised arm architecture")
|
||||
}
|
||||
|
||||
func (arm *ARM) register(register int, formatted bool) (uint32, string, bool) {
|
||||
for k, spec := range arm.RegisterSpec() {
|
||||
if register >= spec.Start && register <= spec.End {
|
||||
switch k {
|
||||
case coprocessor.ExtendedRegisterCoreGroup:
|
||||
return arm.state.registers[register], "", true
|
||||
case "fpu":
|
||||
var s string
|
||||
if formatted {
|
||||
s = fmt.Sprintf("%f", math.Float32frombits(arm.state.fpu.Registers[register-64]))
|
||||
}
|
||||
return arm.state.fpu.Registers[register-spec.Start], s, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0, "", false
|
||||
}
|
||||
|
||||
// Register implements the coprocess.CartCoProc interface. Returns the value in
|
||||
// the register. Returns false if the requested register is not recognised
|
||||
func (arm *ARM) Register(register int) (uint32, bool) {
|
||||
v, _, ok := arm.register(register, false)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// RegisterFormatted implements the coprocess.CartCoProc interface. Returns the value in
|
||||
// the register. Returns false if the requested register is not recognised
|
||||
func (arm *ARM) RegisterFormatted(register int) (uint32, string, bool) {
|
||||
return arm.register(register, true)
|
||||
}
|
||||
|
||||
// RegisterSet implements the coprocess.CartCoProc interface. Sets the register
|
||||
// to the specified value. Returns false if the requested register is not
|
||||
// recognised
|
||||
func (arm *ARM) RegisterSet(register int, value uint32) bool {
|
||||
for k, spec := range arm.RegisterSpec() {
|
||||
if register >= spec.Start && register <= spec.End {
|
||||
switch k {
|
||||
case coprocessor.ExtendedRegisterCoreGroup:
|
||||
arm.state.registers[register] = value
|
||||
return true
|
||||
case "fpu":
|
||||
arm.state.fpu.Registers[register-spec.Start] = value
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -16,14 +16,18 @@
|
|||
package arm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// the arm has a 32 bit Status register but we only need the CSPR bits
|
||||
// currently.
|
||||
type Status struct {
|
||||
// CPSR (current program status register) bits
|
||||
// the status register is an incomplete implementation or CPSR/APSR register
|
||||
//
|
||||
// the structure also contains the IT state fields (itCond and itMask) which are
|
||||
// technically part of the EPSR register in 32bit architectures
|
||||
//
|
||||
// this makeshift type will suffice for now but should be replaced with a more
|
||||
// flexible and more accurate system in the future
|
||||
type status struct {
|
||||
// basic CPSR bits. present in the APSR too
|
||||
negative bool
|
||||
zero bool
|
||||
carry bool
|
||||
|
@ -42,8 +46,10 @@ type Status struct {
|
|||
itMask uint8
|
||||
}
|
||||
|
||||
func (sr Status) String() string {
|
||||
func (sr status) String() string {
|
||||
s := strings.Builder{}
|
||||
s.WriteString("Status: ")
|
||||
|
||||
if sr.negative {
|
||||
s.WriteRune('N')
|
||||
} else {
|
||||
|
@ -70,27 +76,25 @@ func (sr Status) String() string {
|
|||
s.WriteRune('q')
|
||||
}
|
||||
|
||||
s.WriteString(fmt.Sprintf(" itMask: %04b", sr.itMask))
|
||||
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func (sr *Status) reset() {
|
||||
func (sr *status) reset() {
|
||||
sr.negative = false
|
||||
sr.zero = false
|
||||
sr.overflow = false
|
||||
sr.carry = false
|
||||
}
|
||||
|
||||
func (sr *Status) isNegative(a uint32) {
|
||||
func (sr *status) isNegative(a uint32) {
|
||||
sr.negative = a&0x80000000 == 0x80000000
|
||||
}
|
||||
|
||||
func (sr *Status) isZero(a uint32) {
|
||||
func (sr *status) isZero(a uint32) {
|
||||
sr.zero = a == 0x00
|
||||
}
|
||||
|
||||
func (sr *Status) isOverflow(a, b, c uint32) {
|
||||
func (sr *status) isOverflow(a, b, c uint32) {
|
||||
d := (a & 0x7fffffff) + (b & 0x7fffffff) + c
|
||||
d >>= 31
|
||||
e := (d & 0x01) + ((a >> 31) & 0x01) + ((b >> 31) & 0x01)
|
||||
|
@ -98,22 +102,22 @@ func (sr *Status) isOverflow(a, b, c uint32) {
|
|||
sr.overflow = (d^e)&0x01 == 0x01
|
||||
}
|
||||
|
||||
func (sr *Status) isCarry(a, b, c uint32) {
|
||||
func (sr *status) isCarry(a, b, c uint32) {
|
||||
d := (a & 0x7fffffff) + (b & 0x7fffffff) + c
|
||||
d = (d >> 31) + (a >> 31) + (b >> 31)
|
||||
sr.carry = d&0x02 == 0x02
|
||||
}
|
||||
|
||||
func (sr *Status) setCarry(a bool) {
|
||||
func (sr *status) setCarry(a bool) {
|
||||
sr.carry = a
|
||||
}
|
||||
|
||||
func (sr *Status) setOverflow(a bool) {
|
||||
func (sr *status) setOverflow(a bool) {
|
||||
sr.overflow = a
|
||||
}
|
||||
|
||||
// conditional execution information from "A7.3 Conditional execution" in "ARMv7-M"
|
||||
func (sr *Status) condition(cond uint8) (bool, string) {
|
||||
func (sr *status) condition(cond uint8) (bool, string) {
|
||||
var mnemonic string
|
||||
var b bool
|
||||
|
||||
|
|
Loading…
Reference in a new issue