Skip to content

Commit

Permalink
refactor jumpengine handling to own pkg
Browse files Browse the repository at this point in the history
  • Loading branch information
cornelk committed Dec 23, 2024
1 parent 23fbe1c commit 94358a2
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 83 deletions.
25 changes: 6 additions & 19 deletions internal/arch/disasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import (

// Disasm represents a disassembler.
type Disasm interface {
JumpEngine

// AddAddressToParse adds an address to the list to be processed if the address has not been processed yet.
AddAddressToParse(address, context, from uint16, currentInstruction Instruction, isABranchDestination bool)
// AddVariableReference adds a variable reference if the opcode is accessing
Expand All @@ -21,8 +19,14 @@ type Disasm interface {
// ChangeAddressRangeToCodeAsData sets a range of code address to code as
// data types. It combines all data bytes that are not split by a label.
ChangeAddressRangeToCodeAsData(address uint16, data []byte)
// CodeBaseAddress returns the code base address.
CodeBaseAddress() uint16
// Constants returns the constants manager.
Constants() ConstantManager
// DeleteFunctionReturnToParse deletes a function return address from the list of addresses to parse.
DeleteFunctionReturnToParse(address uint16)
// JumpEngine returns the jump engine.
JumpEngine() JumpEngine
// Logger returns the logger.
Logger() *log.Logger
// OffsetInfo returns the offset information for the given address.
Expand All @@ -48,20 +52,3 @@ type ConstantManager interface {
// if the address of the instruction is found in the constants map.
ReplaceParameter(address uint16, opcode Opcode, paramAsString string) (string, bool)
}

// JumpEngine contains jump engine related helper.
type JumpEngine interface {
// AddJumpEngine adds a jump engine function address to the list of jump engines.
AddJumpEngine(address uint16)
// GetContextDataReferences parse all instructions of the function context until the jump
// and returns data references that could point to the function table.
GetContextDataReferences(offsets []Offset, addresses []uint16) ([]uint16, error)
// GetFunctionTableReference detects a jump engine function context and its function table.
GetFunctionTableReference(context uint16, dataReferences []uint16)
// HandleJumpEngineDestination processes a newly detected jump engine destination.
HandleJumpEngineDestination(caller, destination uint16) error
// HandleJumpEngineCallers processes all callers of a newly detected jump engine function.
HandleJumpEngineCallers(context uint16) error
// JumpContextInfo builds the list of instructions of the current function context.
JumpContextInfo(jumpAddress uint16, offsetInfo Offset) ([]Offset, []uint16)
}
21 changes: 21 additions & 0 deletions internal/arch/jumpengine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package arch

// JumpEngine contains jump engine related helper.
type JumpEngine interface {
// AddJumpEngine adds a jump engine function address to the list of jump engines.
AddJumpEngine(address uint16)
// GetContextDataReferences parse all instructions of the function context until the jump
// and returns data references that could point to the function table.
GetContextDataReferences(dis Disasm, offsets []Offset, addresses []uint16) ([]uint16, error)
// GetFunctionTableReference detects a jump engine function context and its function table.
GetFunctionTableReference(context uint16, dataReferences []uint16)
// HandleJumpEngineDestination processes a newly detected jump engine destination.
HandleJumpEngineDestination(dis Disasm, caller, destination uint16) error
// HandleJumpEngineCallers processes all callers of a newly detected jump engine function.
HandleJumpEngineCallers(dis Disasm, context uint16) error
// JumpContextInfo builds the list of instructions of the current function context.
JumpContextInfo(dis Disasm, jumpAddress uint16, offsetInfo Offset) ([]Offset, []uint16)
// ScanForNewJumpEngineEntry scans all jump engine calls for an unprocessed entry in the function address table that
// follows the call. It returns whether a new address to parse was added.
ScanForNewJumpEngineEntry(dis Disasm) (bool, error)
}
14 changes: 8 additions & 6 deletions internal/arch/m6502/jumpengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ func (ar *Arch6502) checkForJumpEngineJmp(dis arch.Disasm, jumpAddress uint16, o
return nil
}

contextOffsets, contextAddresses := dis.JumpContextInfo(jumpAddress, offsetInfo)
jumpEngine := dis.JumpEngine()
contextOffsets, contextAddresses := jumpEngine.JumpContextInfo(dis, jumpAddress, offsetInfo)
contextSize := jumpAddress - offsetInfo.Context() + 3
dataReferences, err := dis.GetContextDataReferences(contextOffsets, contextAddresses)
dataReferences, err := jumpEngine.GetContextDataReferences(dis, contextOffsets, contextAddresses)
if err != nil {
return fmt.Errorf("getting context data references: %w", err)
}

if len(dataReferences) > 1 {
dis.GetFunctionTableReference(offsetInfo.Context(), dataReferences)
jumpEngine.GetFunctionTableReference(offsetInfo.Context(), dataReferences)
}

dis.Logger().Debug("Jump engine detected",
Expand All @@ -41,10 +42,10 @@ func (ar *Arch6502) checkForJumpEngineJmp(dis arch.Disasm, jumpAddress uint16, o

// if code reaches this point, no branching instructions beside the final indirect jmp have been found
// in the function, this makes it likely a jump engine
dis.AddJumpEngine(offsetInfo.Context())
jumpEngine.AddJumpEngine(offsetInfo.Context())

if contextSize < jumpEngineMaxContextSize {
if err := dis.HandleJumpEngineCallers(offsetInfo.Context()); err != nil {
if err := jumpEngine.HandleJumpEngineCallers(dis, offsetInfo.Context()); err != nil {
return fmt.Errorf("handling jump engine callers: %w", err)
}
return nil
Expand All @@ -67,8 +68,9 @@ func (ar *Arch6502) checkForJumpEngineCall(dis arch.Disasm, address uint16, offs
return err
}

jumpEngine := dis.JumpEngine()
destination := binary.LittleEndian.Uint16(opcodes)
if err := dis.HandleJumpEngineDestination(address, destination); err != nil {
if err := jumpEngine.HandleJumpEngineDestination(dis, address, destination); err != nil {
return fmt.Errorf("handling jump engine destination: %w", err)
}
return nil
Expand Down
6 changes: 6 additions & 0 deletions internal/arch/offset.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import "github.com/retroenv/nesgodisasm/internal/program"

// Offset represents an offset in the disassembled code.
type Offset interface {
// BranchFrom returns the list of addresses that branch to this offset.
BranchFrom() []uint16
// ClearType clears the offset type.
ClearType(offsetType program.OffsetType)
// Code returns the code string of the offset.
Expand Down Expand Up @@ -32,8 +34,12 @@ type Offset interface {
SetData([]byte)
// SetLabel sets the label of the offset.
SetLabel(string)
// SetLabelComment sets the label comment of the offset.
SetLabelComment(string)
// SetOpcode sets the opcode of the offset.
SetOpcode(Opcode)
// SetType sets the offset type.
SetType(offsetType program.OffsetType)
// Type returns the offset type.
Type() program.OffsetType
}
20 changes: 14 additions & 6 deletions internal/disasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/retroenv/nesgodisasm/internal/arch"
"github.com/retroenv/nesgodisasm/internal/assembler"
"github.com/retroenv/nesgodisasm/internal/consts"
"github.com/retroenv/nesgodisasm/internal/jumpengine"
"github.com/retroenv/nesgodisasm/internal/options"
"github.com/retroenv/nesgodisasm/internal/program"
"github.com/retroenv/nesgodisasm/internal/writer"
Expand Down Expand Up @@ -38,14 +39,13 @@ type Disasm struct {
codeBaseAddress uint16 // codebase address of the cartridge, it is not always 0x8000
vectorsStartAddress uint16

jumpEngine *jumpengine.JumpEngine

constants *consts.Consts
variables map[uint16]*variable
usedVariables map[uint16]struct{}

jumpEngines map[uint16]struct{} // set of all jump engine functions addresses
jumpEngineCallers []*jumpEngineCaller // jump engine caller tables to process
jumpEngineCallersAdded map[uint16]*jumpEngineCaller
branchDestinations map[uint16]struct{} // set of all addresses that are branched to
branchDestinations map[uint16]struct{} // set of all addresses that are branched to

// TODO handle bank switch
offsetsToParse []uint16
Expand All @@ -71,12 +71,11 @@ func New(ar arch.Architecture, logger *log.Logger, cart *cartridge.Cartridge,
fileWriterConstructor: fileWriterConstructor,
variables: map[uint16]*variable{},
usedVariables: map[uint16]struct{}{},
jumpEngineCallersAdded: map[uint16]*jumpEngineCaller{},
jumpEngines: map[uint16]struct{}{},
branchDestinations: map[uint16]struct{}{},
offsetsToParseAdded: map[uint16]struct{}{},
offsetsParsed: map[uint16]struct{}{},
functionReturnsToParseAdded: map[uint16]struct{}{},
jumpEngine: jumpengine.New(ar),
}

var err error
Expand Down Expand Up @@ -148,6 +147,10 @@ func (dis *Disasm) SetHandlers(handlers program.Handlers) {
dis.handlers = handlers
}

func (dis *Disasm) CodeBaseAddress() uint16 {
return dis.codeBaseAddress
}

func (dis *Disasm) SetCodeBaseAddress(address uint16) {
dis.codeBaseAddress = address

Expand All @@ -168,6 +171,11 @@ func (dis *Disasm) Constants() arch.ConstantManager {
return dis.constants
}

// JumpEngine returns the jump engine.
func (dis *Disasm) JumpEngine() arch.JumpEngine {
return dis.jumpEngine
}

// converts the internal disassembly representation to a program type that will be used by
// the chosen assembler output instance to generate the asm file.
func (dis *Disasm) convertToProgram() (*program.Program, error) {
Expand Down
Loading

0 comments on commit 94358a2

Please sign in to comment.