Gopher2600/disassembly/parse.go
steve 8ddaec1233 o debugger
- scripts now pass through input loop, allowing commands that
    control the emulation (like RUN or STEP) to be effective
    - reworked ONSTEP and ONHALT commands
    - added STICK0 and STICK1 commands

o memory / pia
    - improved RAM debugging output
2020-01-05 18:58:32 +00:00

186 lines
4.3 KiB
Go

package disassembly
import (
"gopher2600/errors"
"gopher2600/hardware/cpu"
"gopher2600/hardware/cpu/definitions"
"gopher2600/hardware/cpu/result"
"gopher2600/hardware/memory"
"gopher2600/symbols"
)
func (dsm *Disassembly) parseLoop(mc *cpu.CPU) error {
for {
currentBank := dsm.Cart.Bank
ir, err := mc.ExecuteInstruction(func(ir *result.Instruction) {})
// filter out some errors
if err != nil {
switch err := err.(type) {
case errors.FormattedError:
switch err.Errno {
case errors.ProgramCounterCycled:
return nil
case errors.UnimplementedInstruction:
continue // for loop
case errors.InvalidOpcode:
continue // for loop
default:
return err
}
default:
return err
}
}
// check validity of instruction result
err = ir.IsValid()
if err != nil {
return err
}
// if we've seen this before then finish the disassembly
if dsm.put(currentBank, ir) == false {
return nil
}
// we've disabled flow-control in the cpu but we still need to pay
// attention to what's going on or we won't get to see all the areas of
// the ROM.
switch ir.Defn.Effect {
case definitions.Flow:
if ir.Defn.Mnemonic == "JMP" {
if ir.Defn.AddressingMode == definitions.Indirect {
if ir.InstructionData.(uint16) > dsm.Cart.Origin() {
// note current location
retBank := dsm.Cart.Bank
retPC := mc.PC.ToUint16()
// adjust program counter
mc.LoadPCIndirect(ir.InstructionData.(uint16))
// recurse
err = dsm.parseLoop(mc)
if err != nil {
return err
}
// resume from where we left off
dsm.Cart.BankSwitch(retBank)
mc.LoadPC(retPC)
} else {
// it's entirely possible for the program to jump
// outside of cartridge space and run inside RIOT RAM,
// for instance (test-ane.bin does this for instance).
//
// it's difficult to see what we can do in these cases
// without actually running the program for real (with
// actual side-effects, that is)
}
} else {
// absolute addressing
// note current location
retBank := dsm.Cart.Bank
retPC := mc.PC.ToUint16()
// adjust program counter
mc.LoadPC(ir.InstructionData.(uint16))
// recurse
err = dsm.parseLoop(mc)
if err != nil {
return err
}
// resume from where we left off
dsm.Cart.BankSwitch(retBank)
mc.LoadPC(retPC)
}
} else {
// branch instructions
// note current location
retBank := dsm.Cart.Bank
retPC := mc.PC.ToUint16()
// sign extend address and add to program counter
address := uint16(ir.InstructionData.(uint8))
if address&0x0080 == 0x0080 {
address |= 0xff00
}
mc.PC.Add(address, false)
// recurse
err = dsm.parseLoop(mc)
if err != nil {
return err
}
// resume from where we left off
dsm.Cart.BankSwitch(retBank)
mc.LoadPC(retPC)
}
case definitions.Subroutine:
if ir.Defn.Mnemonic == "RTS" {
return nil
}
// note current location
retBank := dsm.Cart.Bank
retPC := mc.PC.ToUint16()
// adjust program counter
mc.LoadPC(ir.InstructionData.(uint16))
// recurse
err = dsm.parseLoop(mc)
if err != nil {
return err
}
// resume from where we left off
dsm.Cart.BankSwitch(retBank)
mc.LoadPC(retPC)
case definitions.Interrupt:
// do nothing with interrupts
}
}
}
// ParseMemory disassembles an existing instance of cartridge memory using a
// cpu with no flow control
func (dsm *Disassembly) ParseMemory(cart *memory.Cartridge, symtable *symbols.Table) error {
dsm.Cart = cart
dsm.Symtable = symtable
dsm.program = make([]map[uint16]*result.Instruction, dsm.Cart.NumBanks)
// create new memory
mem, err := newMinimalMemory(dsm.Cart)
if err != nil {
return err
}
// create a new non-branching CPU to disassemble memory
mc, err := cpu.NewCPU(mem)
if err != nil {
return err
}
mc.NoFlowControl = true
// allocate memory for disassembly
for bank := 0; bank < dsm.Cart.NumBanks; bank++ {
dsm.Cart.BankSwitch(bank)
dsm.program[bank] = make(map[uint16]*result.Instruction)
}
// make sure we're in the starting bank - at the beginning of the
// disassembly and at the end
dsm.Cart.BankSwitch(0)
defer dsm.Cart.BankSwitch(0)
mc.LoadPCIndirect(memory.AddressReset)
return dsm.parseLoop(mc)
}