mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
- preparation for adding 7800 emulation cpu (6507) package uncoupled from upstream dependencies - cpu no longer logs execution of KIL instruction - randomisation of registers on startup can be handled by the called of the cpu's Reset() function - address errors are filtered by an optional IsAddressError() function in the Memory interface implementation - Error field removed from execution.Result type all references to interface{} replaced with any
156 lines
4.5 KiB
Go
156 lines
4.5 KiB
Go
// 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 dbgmem
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"github.com/jetsetilly/gopher2600/disassembly/symbols"
|
|
"github.com/jetsetilly/gopher2600/hardware"
|
|
"github.com/jetsetilly/gopher2600/hardware/memory/cpubus"
|
|
"github.com/jetsetilly/gopher2600/hardware/memory/memorymap"
|
|
)
|
|
|
|
// DbgMem is a front-end to the real VCS memory. it allows addressing by
|
|
// symbol name and uses the AddressInfo type for easier presentation.
|
|
type DbgMem struct {
|
|
VCS *hardware.VCS
|
|
Sym *symbols.Symbols
|
|
}
|
|
|
|
// GetAddressInfo allows addressing by symbols in addition to numerically.
|
|
func (dbgmem DbgMem) GetAddressInfo(address any, read bool) *AddressInfo {
|
|
ai := &AddressInfo{Read: read}
|
|
|
|
var searchTable symbols.SearchTable
|
|
|
|
if read {
|
|
searchTable = symbols.SearchRead
|
|
} else {
|
|
searchTable = symbols.SearchWrite
|
|
}
|
|
|
|
switch address := address.(type) {
|
|
case uint16:
|
|
ai.Address = address
|
|
res := dbgmem.Sym.SearchByAddress(ai.Address, searchTable)
|
|
if res == nil {
|
|
ai.MappedAddress, ai.Area = memorymap.MapAddress(ai.Address, read)
|
|
res := dbgmem.Sym.SearchByAddress(ai.MappedAddress, searchTable)
|
|
if res != nil {
|
|
ai.Symbol = res.Entry.Symbol
|
|
}
|
|
} else {
|
|
ai.MappedAddress, ai.Area = memorymap.MapAddress(ai.Address, read)
|
|
ai.Symbol = res.Entry.Symbol
|
|
}
|
|
case string:
|
|
var err error
|
|
|
|
res := dbgmem.Sym.SearchBySymbol(address, searchTable)
|
|
if res != nil {
|
|
ai.Address = res.Address
|
|
ai.Symbol = res.Entry.Symbol
|
|
ai.MappedAddress, ai.Area = memorymap.MapAddress(ai.Address, read)
|
|
} else {
|
|
// this may be a string representation of a numerical address
|
|
var addr uint64
|
|
|
|
addr, err = strconv.ParseUint(address, 0, 16)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
ai.Address = uint16(addr)
|
|
res := dbgmem.Sym.SearchByAddress(ai.Address, searchTable)
|
|
if res == nil {
|
|
ai.MappedAddress, ai.Area = memorymap.MapAddress(ai.Address, read)
|
|
res := dbgmem.Sym.SearchByAddress(ai.MappedAddress, searchTable)
|
|
if res != nil {
|
|
ai.Symbol = res.Entry.Symbol
|
|
}
|
|
} else {
|
|
ai.MappedAddress, ai.Area = memorymap.MapAddress(ai.Address, read)
|
|
ai.Symbol = res.Entry.Symbol
|
|
}
|
|
}
|
|
default:
|
|
panic(fmt.Sprintf("unsupported address type (%T)", address))
|
|
}
|
|
|
|
return ai
|
|
}
|
|
|
|
// sentinal errors returns by Peek() and Poke()
|
|
var PeekError = errors.New("cannot peek address")
|
|
var PokeError = errors.New("cannot poke address")
|
|
|
|
// Peek returns the contents of the memory address, without triggering any side
|
|
// effects. The supplied address can be numeric of symbolic.
|
|
func (dbgmem DbgMem) Peek(address any) (*AddressInfo, error) {
|
|
ai := dbgmem.GetAddressInfo(address, true)
|
|
if ai == nil {
|
|
return nil, fmt.Errorf("%w: %v", PeekError, address)
|
|
}
|
|
|
|
area := dbgmem.VCS.Mem.GetArea(ai.Area)
|
|
|
|
var err error
|
|
ai.Data, err = area.Peek(ai.MappedAddress)
|
|
if err != nil {
|
|
if errors.Is(err, cpubus.AddressError) {
|
|
return nil, fmt.Errorf("%w: %v", PeekError, address)
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
ai.Peeked = true
|
|
|
|
return ai, nil
|
|
}
|
|
|
|
// Poke writes a value at the specified address. The supplied address be
|
|
// numeric or symbolic.
|
|
func (dbgmem DbgMem) Poke(address any, data uint8) (*AddressInfo, error) {
|
|
// although the words "read" and "write" might lead us to think that we
|
|
// "peek" from "read" addresses and "poke" to "write" addresses, it is in
|
|
// fact necessary to treat "poke" addresses as "read" addresses
|
|
//
|
|
// on the surface this doesn't appear to be correct but on further thought
|
|
// it is obviously true - we are in fact changing the value that is
|
|
// subsequently read by the CPU, so that means poking to a read address
|
|
ai := dbgmem.GetAddressInfo(address, true)
|
|
if ai == nil {
|
|
return nil, fmt.Errorf("%w: %v", PokeError, address)
|
|
}
|
|
|
|
area := dbgmem.VCS.Mem.GetArea(ai.Area)
|
|
|
|
err := area.Poke(ai.MappedAddress, data)
|
|
if err != nil {
|
|
if errors.Is(err, cpubus.AddressError) {
|
|
return nil, fmt.Errorf("%w: %v", PokeError, address)
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
ai.Data = data
|
|
ai.Peeked = true
|
|
|
|
return ai, err
|
|
}
|