Gopher2600/disassembly/disassembly.go
steve c59a4a2dde 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
2020-01-05 18:58:36 +00:00

144 lines
3.9 KiB
Go

package disassembly
import (
"fmt"
"gopher2600/errors"
"gopher2600/hardware/cpu"
"gopher2600/hardware/memory"
"gopher2600/hardware/memory/addresses"
"gopher2600/symbols"
"io"
)
const disasmMask = 0x0fff
type bank [disasmMask + 1]Entry
// Disassembly represents the annotated disassembly of a 6502 binary
type Disassembly struct {
Cart *memory.Cartridge
// simply anlysis of the cartridge
selfModifyingCode bool
interrupts bool
forcedRTS bool
// symbols used to build disassembly output
Symtable *symbols.Table
// linear is the decoding of every possible address in the cartridge (see
// linear.go for fuller commentary)
linear []bank
// flow is the decoding of cartridge addresses that follow the flow from
// the address pointed to by the reset address of the cartridge. (see
// flow.go for fuller commentary)
flow []bank
}
func (dsm Disassembly) String() string {
return fmt.Sprintf("non-cart JMPs: %v\ninterrupts: %v\nforced RTS: %v\n", dsm.selfModifyingCode, dsm.interrupts, dsm.forcedRTS)
}
// Get returns the disassembled entry at the specified bank/address
func (dsm Disassembly) Get(bank int, address uint16) (Entry, bool) {
entry := dsm.linear[bank][address&disasmMask]
return entry, entry.IsInstruction()
}
// Dump writes the entire disassembly to the write interface
func (dsm *Disassembly) Dump(output io.Writer) {
for bank := 0; bank < len(dsm.flow); bank++ {
output.Write([]byte(fmt.Sprintf("--- bank %d ---\n", bank)))
for i := range dsm.flow[bank] {
if entry := dsm.flow[bank][i]; entry.instructionDefinition != nil {
output.Write([]byte(entry.instruction))
output.Write([]byte("\n"))
}
}
}
}
// FromCartrige initialises a new partial emulation and returns a
// disassembly from the supplied cartridge filename. - useful for one-shot
// disassemblies, like the gopher2600 "disasm" mode
func FromCartrige(cartridgeFilename string) (*Disassembly, error) {
// ignore errors caused by loading of symbols table - we always get a
// standard symbols table even in the event of an error
symtable, _ := symbols.ReadSymbolsFile(cartridgeFilename)
cart := memory.NewCartridge()
err := cart.Attach(cartridgeFilename)
if err != nil {
return nil, errors.NewFormattedError(errors.DisasmError, err)
}
dsm := new(Disassembly)
err = dsm.FromMemory(cart, symtable)
if err != nil {
return dsm, err // no need to wrap error
}
return dsm, nil
}
// FromMemory disassembles an existing instance of cartridge memory using a
// cpu with no flow control
func (dsm *Disassembly) FromMemory(cart *memory.Cartridge, symtable *symbols.Table) error {
dsm.Cart = cart
dsm.Symtable = symtable
dsm.flow = make([]bank, dsm.Cart.NumBanks())
dsm.linear = make([]bank, dsm.Cart.NumBanks())
// 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
mem, err := newDisasmMemory(dsm.Cart)
if err != nil {
return errors.NewFormattedError(errors.DisasmError, err)
}
// create a new NoFlowControl CPU to help disassemble memory
mc, err := cpu.NewCPU(mem)
if err != nil {
return errors.NewFormattedError(errors.DisasmError, err)
}
mc.NoFlowControl = true
// disassemble linearly
err = mc.LoadPCIndirect(addresses.Reset)
if err != nil {
return errors.NewFormattedError(errors.DisasmError, err)
}
err = dsm.linearDisassembly(mc)
if err != nil {
return errors.NewFormattedError(errors.DisasmError, err)
}
// disassemble as best we can with manual flow control
mc.Reset()
dsm.Cart.Initialise()
err = mc.LoadPCIndirect(addresses.Reset)
if err != nil {
return errors.NewFormattedError(errors.DisasmError, err)
}
err = dsm.flowDisassembly(mc)
if err != nil {
return errors.NewFormattedError(errors.DisasmError, err)
}
return nil
}