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:
JetSetIlly 2023-08-09 22:24:11 +01:00
parent 6c7dc2ec75
commit 74c3457406
8 changed files with 237 additions and 75 deletions

View file

@ -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:

View file

@ -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

View file

@ -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":

View file

@ -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.

View file

@ -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

View file

@ -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) {

View 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
}

View file

@ -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