mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
- renamed GopherError to FormattedError - reading files now handled by ioutil.ReadAll(), rather than os.Read()
178 lines
4 KiB
Go
178 lines
4 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 {
|
|
// 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)
|
|
}
|