mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
o debugger
- RAM command now has CART option. displays any additional RAM the cartridge may have o cartridge - implemented RAM() command. returns copy of RAM array - save/restore banks functions are now save/restore state and deal with cartridge RAM in addition to bank information o debugger/memory - better error messages for peek and poke
This commit is contained in:
parent
fea2357fdf
commit
c59a4a2dde
13 changed files with 94 additions and 54 deletions
|
@ -25,7 +25,6 @@ const (
|
|||
cmdCPU = "CPU"
|
||||
cmdCartridge = "CARTRIDGE"
|
||||
cmdClear = "CLEAR"
|
||||
cmdClocks = "CLOCKS"
|
||||
cmdDebuggerState = "DEBUGGERSTATE"
|
||||
cmdDigest = "DIGEST"
|
||||
cmdDisassembly = "DISASSEMBLY"
|
||||
|
@ -72,7 +71,6 @@ var commandTemplate = []string{
|
|||
cmdCPU + " (SET [PC|A|X|Y|SP] [%N])",
|
||||
cmdCartridge + " (ANALYSIS)",
|
||||
cmdClear + " [BREAKS|TRAPS|WATCHES|ALL]",
|
||||
cmdClocks,
|
||||
cmdDebuggerState,
|
||||
cmdDigest + " (RESET)",
|
||||
cmdDisassembly,
|
||||
|
@ -90,10 +88,10 @@ var commandTemplate = []string{
|
|||
cmdPeek + " [%S] {%S}",
|
||||
cmdPlayer + " (0|1)",
|
||||
cmdPlayfield,
|
||||
cmdPoke + " [%N|%S] %N",
|
||||
cmdPoke + " [%S] %N",
|
||||
cmdQuit,
|
||||
cmdExit,
|
||||
cmdRAM,
|
||||
cmdRAM + " (CART)",
|
||||
cmdRIOT + " (TIMER)",
|
||||
cmdReset,
|
||||
cmdRun,
|
||||
|
@ -108,7 +106,7 @@ var commandTemplate = []string{
|
|||
cmdTrap + " [%S] {%S}",
|
||||
cmdVerbose,
|
||||
cmdVerbosity,
|
||||
cmdWatch + " (READ|WRITE) %N (%N)",
|
||||
cmdWatch + " (READ|WRITE) [%S] (%S)",
|
||||
}
|
||||
|
||||
// list of commands that should not be executed when recording/playing scripts
|
||||
|
@ -808,10 +806,23 @@ func (dbg *Debugger) enactCommand(tokens *commandline.Tokens, interactive bool)
|
|||
}
|
||||
|
||||
case cmdRAM:
|
||||
dbg.printMachineInfo(dbg.vcs.Mem.PIA)
|
||||
option, present := tokens.Get()
|
||||
if present {
|
||||
option = strings.ToUpper(option)
|
||||
switch option {
|
||||
case "CART":
|
||||
cartRAM := dbg.vcs.Mem.Cart.RAM()
|
||||
if len(cartRAM) > 0 {
|
||||
// !!TODO: better presentation of cartridge RAM
|
||||
dbg.print(console.StyleMachineInfo, fmt.Sprintf("%v", dbg.vcs.Mem.Cart.RAM()))
|
||||
} else {
|
||||
dbg.print(console.StyleFeedback, "cartridge does not contain any additional RAM")
|
||||
}
|
||||
|
||||
case cmdClocks:
|
||||
dbg.print(console.StyleMachineInfo, "not implemented yet")
|
||||
}
|
||||
} else {
|
||||
dbg.printMachineInfo(dbg.vcs.Mem.PIA)
|
||||
}
|
||||
|
||||
case cmdRIOT:
|
||||
option, present := tokens.Get()
|
||||
|
|
|
@ -7,7 +7,6 @@ var Help = map[string]string{
|
|||
cmdCPU: "Display the current state of the CPU",
|
||||
cmdCartridge: "Display information about the current cartridge",
|
||||
cmdClear: "Clear all entries in BREAKS and TRAPS",
|
||||
cmdClocks: "The current state of the VCS clocks",
|
||||
cmdDebuggerState: "Display summary of debugger options",
|
||||
cmdDigest: "Return the cryptographic hash of the current screen",
|
||||
cmdDisassembly: "Print the full cartridge disassembly",
|
||||
|
|
|
@ -127,7 +127,7 @@ func (mem memoryDebug) mapAddress(address interface{}, cpuRead bool) *addressInf
|
|||
func (mem memoryDebug) peek(address interface{}) (*addressInfo, error) {
|
||||
ai := mem.mapAddress(address, true)
|
||||
if ai == nil {
|
||||
return nil, errors.NewFormattedError(errors.MemoryError, fmt.Sprintf("%#04x not mapped correctly", address))
|
||||
return nil, errors.NewFormattedError(errors.UnpeekableAddress, address)
|
||||
}
|
||||
|
||||
value, err := ai.area.Peek(ai.mappedAddress)
|
||||
|
@ -141,7 +141,7 @@ func (mem memoryDebug) peek(address interface{}) (*addressInfo, error) {
|
|||
func (mem memoryDebug) poke(address interface{}, value uint8) (*addressInfo, error) {
|
||||
ai := mem.mapAddress(address, true)
|
||||
if ai == nil {
|
||||
return nil, errors.NewFormattedError(errors.MemoryError, fmt.Sprintf("%#04x not mapped correctly", address))
|
||||
return nil, errors.NewFormattedError(errors.UnpokeableAddress, address)
|
||||
}
|
||||
ai.value = value
|
||||
ai.valueSeen = true
|
||||
|
|
|
@ -21,7 +21,7 @@ func (dbg *Debugger) buildPrompt(videoCycle bool) console.Prompt {
|
|||
promptAddress = dbg.lastResult.Address
|
||||
}
|
||||
|
||||
promptBank = dbg.vcs.Mem.Cart.GetAddressBank(promptAddress)
|
||||
promptBank = dbg.vcs.Mem.Cart.GetBank(promptAddress)
|
||||
|
||||
prompt := strings.Builder{}
|
||||
prompt.WriteString("[")
|
||||
|
|
|
@ -92,9 +92,13 @@ func (dsm *Disassembly) FromMemory(cart *memory.Cartridge, symtable *symbols.Tab
|
|||
dsm.flow = make([]bank, dsm.Cart.NumBanks())
|
||||
dsm.linear = make([]bank, dsm.Cart.NumBanks())
|
||||
|
||||
state := dsm.Cart.SaveBanks()
|
||||
defer dsm.Cart.RestoreBanks(state)
|
||||
// save cartridge state and defer at end of disassembly. this is necessary
|
||||
// because during the disassembly process we may changed mutable parts of
|
||||
// the cartridge (eg. extra RAM)
|
||||
state := dsm.Cart.SaveState()
|
||||
defer dsm.Cart.RestoreState(state)
|
||||
|
||||
// put cart into its initial state
|
||||
dsm.Cart.Initialise()
|
||||
|
||||
// create new memory
|
||||
|
|
|
@ -52,7 +52,7 @@ func (dsm *Disassembly) flowDisassembly(mc *cpu.CPU) error {
|
|||
return err
|
||||
}
|
||||
|
||||
bank := dsm.Cart.GetAddressBank(r.Address)
|
||||
bank := dsm.Cart.GetBank(r.Address)
|
||||
|
||||
// if we've seen this before then finish the disassembly
|
||||
if dsm.flow[bank][r.Address&disasmMask].IsInstruction() {
|
||||
|
@ -74,7 +74,7 @@ func (dsm *Disassembly) flowDisassembly(mc *cpu.CPU) error {
|
|||
if r.Defn.AddressingMode == definitions.Indirect {
|
||||
if r.InstructionData.(uint16) > dsm.Cart.Origin() {
|
||||
// note current location
|
||||
state := dsm.Cart.SaveBanks()
|
||||
state := dsm.Cart.SaveState()
|
||||
retPC := mc.PC.ToUint16()
|
||||
|
||||
// adjust program counter
|
||||
|
@ -87,7 +87,7 @@ func (dsm *Disassembly) flowDisassembly(mc *cpu.CPU) error {
|
|||
}
|
||||
|
||||
// resume from where we left off
|
||||
dsm.Cart.RestoreBanks(state)
|
||||
dsm.Cart.RestoreState(state)
|
||||
mc.PC.Load(retPC)
|
||||
} else {
|
||||
// it's entirely possible for the program to jump
|
||||
|
@ -105,7 +105,7 @@ func (dsm *Disassembly) flowDisassembly(mc *cpu.CPU) error {
|
|||
// absolute JMP addressing
|
||||
|
||||
// note current location
|
||||
state := dsm.Cart.SaveBanks()
|
||||
state := dsm.Cart.SaveState()
|
||||
retPC := mc.PC.ToUint16()
|
||||
|
||||
// adjust program counter
|
||||
|
@ -118,14 +118,14 @@ func (dsm *Disassembly) flowDisassembly(mc *cpu.CPU) error {
|
|||
}
|
||||
|
||||
// resume from where we left off
|
||||
dsm.Cart.RestoreBanks(state)
|
||||
dsm.Cart.RestoreState(state)
|
||||
mc.PC.Load(retPC)
|
||||
}
|
||||
} else {
|
||||
// branch instructions
|
||||
|
||||
// note current location
|
||||
state := dsm.Cart.SaveBanks()
|
||||
state := dsm.Cart.SaveState()
|
||||
retPC := mc.PC.ToUint16()
|
||||
|
||||
// sign extend address and add to program counter
|
||||
|
@ -142,7 +142,7 @@ func (dsm *Disassembly) flowDisassembly(mc *cpu.CPU) error {
|
|||
}
|
||||
|
||||
// resume from where we left off
|
||||
dsm.Cart.RestoreBanks(state)
|
||||
dsm.Cart.RestoreState(state)
|
||||
mc.PC.Load(retPC)
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
func (dsm *Disassembly) linearDisassembly(mc *cpu.CPU) error {
|
||||
for bank := 0; bank < len(dsm.linear); bank++ {
|
||||
for address := dsm.Cart.Origin(); address <= dsm.Cart.Memtop(); address++ {
|
||||
if err := dsm.Cart.SetAddressBank(address, bank); err != nil {
|
||||
if err := dsm.Cart.SetBank(address, bank); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ const (
|
|||
UnreadableAddress
|
||||
UnwritableAddress
|
||||
UnpokeableAddress
|
||||
UnpeekableAddress
|
||||
UnrecognisedAddress
|
||||
|
||||
// cartridges
|
||||
|
|
|
@ -59,6 +59,7 @@ var messages = map[Errno]string{
|
|||
UnreadableAddress: "memory error: memory location is not readable (%#04x)",
|
||||
UnwritableAddress: "memory error: memory location is not writable (%#04x)",
|
||||
UnpokeableAddress: "memory error: cannot poke address (%v)",
|
||||
UnpeekableAddress: "memory error: cannot peek address (%v)",
|
||||
UnrecognisedAddress: "memory error: address unrecognised (%v)",
|
||||
|
||||
// cartridges
|
||||
|
|
|
@ -17,10 +17,11 @@ type cartMapper interface {
|
|||
read(addr uint16) (data uint8, err error)
|
||||
write(addr uint16, data uint8, isPoke bool) error
|
||||
numBanks() int
|
||||
getAddressBank(addr uint16) (bank int)
|
||||
setAddressBank(addr uint16, bank int) error
|
||||
saveBanks() interface{}
|
||||
restoreBanks(interface{}) error
|
||||
getBank(addr uint16) (bank int)
|
||||
setBank(addr uint16, bank int) error
|
||||
saveState() interface{}
|
||||
restoreState(interface{}) error
|
||||
ram() []uint8
|
||||
}
|
||||
|
||||
// Cartridge defines the information and operations for a VCS cartridge
|
||||
|
@ -108,6 +109,8 @@ func (cart Cartridge) Poke(addr uint16, data uint8) error {
|
|||
return cart.mapper.write(addr, data, true)
|
||||
}
|
||||
|
||||
// fingerprint8k attempts a divination of 8k cartridge data and decide on a
|
||||
// suitable cartMapper implementation
|
||||
func (cart Cartridge) fingerprint8k(cf io.ReadSeeker) func(io.ReadSeeker) (cartMapper, error) {
|
||||
byts := make([]byte, 8192)
|
||||
cf.Seek(0, io.SeekStart)
|
||||
|
@ -207,26 +210,31 @@ func (cart Cartridge) NumBanks() int {
|
|||
return cart.mapper.numBanks()
|
||||
}
|
||||
|
||||
// GetAddressBank calls the current mapper's addressBank function. it returns
|
||||
// the current bank number for the specified address
|
||||
func (cart Cartridge) GetAddressBank(addr uint16) int {
|
||||
// GetBank calls the current mapper's addressBank function. it returns the
|
||||
// current bank number for the specified address
|
||||
func (cart Cartridge) GetBank(addr uint16) int {
|
||||
addr &= cart.Origin() - 1
|
||||
return cart.mapper.getAddressBank(addr)
|
||||
return cart.mapper.getBank(addr)
|
||||
}
|
||||
|
||||
// SetAddressBank sets the bank for the specificed address. it sets the
|
||||
// specified address to reference the specified bank
|
||||
func (cart *Cartridge) SetAddressBank(addr uint16, bank int) error {
|
||||
// SetBank sets the bank for the specificed address. it sets the specified
|
||||
// address to reference the specified bank
|
||||
func (cart *Cartridge) SetBank(addr uint16, bank int) error {
|
||||
addr &= cart.Origin() - 1
|
||||
return cart.mapper.setAddressBank(addr, bank)
|
||||
return cart.mapper.setBank(addr, bank)
|
||||
}
|
||||
|
||||
// SaveBanks calls the current mapper's saveState function
|
||||
func (cart *Cartridge) SaveBanks() interface{} {
|
||||
return cart.mapper.saveBanks()
|
||||
// SaveState calls the current mapper's saveState function
|
||||
func (cart *Cartridge) SaveState() interface{} {
|
||||
return cart.mapper.saveState()
|
||||
}
|
||||
|
||||
// RestoreBanks calls the current mapper's restoreState function
|
||||
func (cart *Cartridge) RestoreBanks(state interface{}) error {
|
||||
return cart.mapper.restoreBanks(state)
|
||||
// RestoreState calls the current mapper's restoreState function
|
||||
func (cart *Cartridge) RestoreState(state interface{}) error {
|
||||
return cart.mapper.restoreState(state)
|
||||
}
|
||||
|
||||
// RAM returns a read only instance of any cartridge RAM
|
||||
func (cart Cartridge) RAM() []uint8 {
|
||||
return cart.mapper.ram()
|
||||
}
|
||||
|
|
|
@ -30,16 +30,19 @@ func (cart atari) String() string {
|
|||
|
||||
func (cart *atari) initialise() {
|
||||
cart.bank = len(cart.banks) - 1
|
||||
if len(cart.superchip) > 0 {
|
||||
cart.superchip = make([]uint8, len(cart.superchip))
|
||||
}
|
||||
}
|
||||
|
||||
func (cart atari) getAddressBank(addr uint16) int {
|
||||
func (cart atari) getBank(addr uint16) int {
|
||||
// because atari bank switching swaps out the entire memory space, every
|
||||
// address points to whatever the current bank is. compare to parker bros.
|
||||
// cartridges.
|
||||
return cart.bank
|
||||
}
|
||||
|
||||
func (cart *atari) setAddressBank(addr uint16, bank int) error {
|
||||
func (cart *atari) setBank(addr uint16, bank int) error {
|
||||
if bank < 0 || bank > len(cart.banks) {
|
||||
return errors.NewFormattedError(errors.CartridgeError, fmt.Sprintf("invalid bank (%d) for cartridge type (%s)", bank, cart.method))
|
||||
}
|
||||
|
@ -47,12 +50,13 @@ func (cart *atari) setAddressBank(addr uint16, bank int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cart *atari) saveBanks() interface{} {
|
||||
return cart.bank
|
||||
func (cart *atari) saveState() interface{} {
|
||||
return []interface{}{cart.bank, cart.superchip}
|
||||
}
|
||||
|
||||
func (cart *atari) restoreBanks(state interface{}) error {
|
||||
cart.bank = state.(int)
|
||||
func (cart *atari) restoreState(state interface{}) error {
|
||||
cart.bank = state.([]interface{})[0].(int)
|
||||
copy(cart.superchip, state.([]interface{})[1].([]uint8))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -99,6 +103,10 @@ func (cart *atari) addSuperchip() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (cart atari) ram() []uint8 {
|
||||
return cart.superchip
|
||||
}
|
||||
|
||||
// atari4k is the original and most straightforward format
|
||||
// o Pitfall
|
||||
// o River Raid
|
||||
|
|
|
@ -40,18 +40,22 @@ func (cart ejected) numBanks() int {
|
|||
return 0
|
||||
}
|
||||
|
||||
func (cart *ejected) setAddressBank(addr uint16, bank int) error {
|
||||
func (cart *ejected) setBank(addr uint16, bank int) error {
|
||||
return errors.NewFormattedError(errors.CartridgeError, fmt.Sprintf("invalid bank (%d) for cartridge type (%s)", bank, cart.method))
|
||||
}
|
||||
|
||||
func (cart ejected) getAddressBank(addr uint16) int {
|
||||
func (cart ejected) getBank(addr uint16) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (cart *ejected) saveBanks() interface{} {
|
||||
func (cart *ejected) saveState() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cart *ejected) restoreBanks(state interface{}) error {
|
||||
func (cart *ejected) restoreState(state interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cart ejected) ram() []uint8 {
|
||||
return []uint8{}
|
||||
}
|
||||
|
|
|
@ -166,7 +166,7 @@ func (cart parkerBros) numBanks() int {
|
|||
return 8
|
||||
}
|
||||
|
||||
func (cart parkerBros) getAddressBank(addr uint16) int {
|
||||
func (cart parkerBros) getBank(addr uint16) int {
|
||||
if addr >= 0x0000 && addr <= 0x03ff {
|
||||
return cart.segment[0]
|
||||
} else if addr >= 0x0400 && addr <= 0x07ff {
|
||||
|
@ -177,7 +177,7 @@ func (cart parkerBros) getAddressBank(addr uint16) int {
|
|||
return cart.segment[3]
|
||||
}
|
||||
|
||||
func (cart *parkerBros) setAddressBank(addr uint16, bank int) error {
|
||||
func (cart *parkerBros) setBank(addr uint16, bank int) error {
|
||||
if bank < 0 || bank > cart.numBanks() {
|
||||
return errors.NewFormattedError(errors.CartridgeError, fmt.Sprintf("invalid bank (%d) for cartridge type (%s)", bank, cart.method))
|
||||
}
|
||||
|
@ -197,11 +197,15 @@ func (cart *parkerBros) setAddressBank(addr uint16, bank int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cart *parkerBros) saveBanks() interface{} {
|
||||
func (cart *parkerBros) saveState() interface{} {
|
||||
return cart.segment
|
||||
}
|
||||
|
||||
func (cart *parkerBros) restoreBanks(state interface{}) error {
|
||||
func (cart *parkerBros) restoreState(state interface{}) error {
|
||||
cart.segment = state.([4]int)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cart parkerBros) ram() []uint8 {
|
||||
return []uint8{}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue