mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
- 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
186 lines
4.3 KiB
Go
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)
|
|
}
|