Skip to content

Commit

Permalink
refactor constants manager to own pkg
Browse files Browse the repository at this point in the history
  • Loading branch information
cornelk committed Dec 23, 2024
1 parent 26e3a4b commit 23fbe1c
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 133 deletions.
7 changes: 4 additions & 3 deletions internal/arch/arch.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package arch
// Architecture contains architecture specific information.
type Architecture interface {
// Constants returns the constants translation map.
Constants() (map[uint16]ConstTranslation, error)
Constants() (map[uint16]Constant, error)
// GetAddressingParam returns the address of the param if it references an address.
GetAddressingParam(param any) (uint16, bool)
// HandleDisambiguousInstructions translates disambiguous instructions into data bytes as it
Expand All @@ -28,8 +28,9 @@ type Architecture interface {
ReadOpParam(dis Disasm, addressing int, address uint16) (any, []byte, error)
}

// ConstTranslation represents a constant translation from a read and write operation to a name.
type ConstTranslation struct {
// Constant represents a constant translation from a read and write operation to a name.
// This is used to replace the parameter of an instruction by a constant name.
type Constant struct {
Address uint16

Read string
Expand Down
11 changes: 8 additions & 3 deletions internal/arch/disasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ 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)
// Constants returns the constants manager.
Constants() ConstantManager
// Logger returns the logger.
Logger() *log.Logger
// OffsetInfo returns the offset information for the given address.
Expand All @@ -33,9 +35,6 @@ type Disasm interface {
ReadMemory(address uint16) (byte, error)
// ReadMemoryWord reads a word from the memory at the given address.
ReadMemoryWord(address uint16) (uint16, error)
// ReplaceParamByConstant replaces the parameter of an instruction by a constant name
// if the address of the instruction is found in the constants map.
ReplaceParamByConstant(address uint16, opcode Opcode, paramAsString string) (string, bool)
// SetCodeBaseAddress sets the code base address.
SetCodeBaseAddress(address uint16)
// SetHandlers sets the program vector handlers.
Expand All @@ -44,6 +43,12 @@ type Disasm interface {
SetVectorsStartAddress(address uint16)
}

type ConstantManager interface {
// ReplaceParameter replaces the parameter of an instruction by a constant name
// 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.
Expand Down
6 changes: 3 additions & 3 deletions internal/arch/m6502/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (

// Constants builds the map of all known NES constants from all
// modules that maps an address to a constant name.
func (ar *Arch6502) Constants() (map[uint16]arch.ConstTranslation, error) {
m := map[uint16]arch.ConstTranslation{}
func (ar *Arch6502) Constants() (map[uint16]arch.Constant, error) {
m := map[uint16]arch.Constant{}
if err := mergeConstantsMaps(m, register.APUAddressToName); err != nil {
return nil, fmt.Errorf("processing apu constants: %w", err)
}
Expand All @@ -24,7 +24,7 @@ func (ar *Arch6502) Constants() (map[uint16]arch.ConstTranslation, error) {
return m, nil
}

func mergeConstantsMaps(destination map[uint16]arch.ConstTranslation, source map[uint16]m6502.AccessModeConstant) error {
func mergeConstantsMaps(destination map[uint16]arch.Constant, source map[uint16]m6502.AccessModeConstant) error {
for address, constantInfo := range source {
translation := destination[address]
translation.Address = address
Expand Down
3 changes: 2 additions & 1 deletion internal/arch/m6502/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ func (ar *Arch6502) replaceParamByAlias(dis arch.Disasm, address uint16, opcode
}
}

changedParamAsString, ok := dis.ReplaceParamByConstant(addressReference, opcode, paramAsString)
consts := dis.Constants()
changedParamAsString, ok := consts.ReplaceParameter(addressReference, opcode, paramAsString)
if ok {
return changedParamAsString
}
Expand Down
47 changes: 42 additions & 5 deletions internal/bank.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package disasm

import "github.com/retroenv/nesgodisasm/internal/arch"
import (
"fmt"

"github.com/retroenv/nesgodisasm/internal/program"
)

const (
singleBankName = "CODE"
Expand All @@ -10,8 +14,6 @@ const (
type bank struct {
prg []byte

constants map[uint16]arch.ConstTranslation
usedConstants map[uint16]arch.ConstTranslation
variables map[uint16]*variable
usedVariables map[uint16]struct{}

Expand All @@ -27,10 +29,45 @@ type bankReference struct {
func newBank(prg []byte) *bank {
return &bank{
prg: prg,
constants: map[uint16]arch.ConstTranslation{},
usedConstants: map[uint16]arch.ConstTranslation{},
variables: map[uint16]*variable{},
usedVariables: map[uint16]struct{}{},
offsets: make([]offset, len(prg)),
}
}

func (dis *Disasm) initializeBanks(prg []byte) {
for i := 0; i < len(prg); {
size := len(prg) - i
if size > 0x8000 {
size = 0x8000
}

b := prg[i : i+size]
bnk := newBank(b)
dis.banks = append(dis.banks, bnk)
i += size

dis.constants.AddBank()
}
}

func setBankVectors(bnk *bank, prgBank *program.PRGBank) {
idx := len(bnk.prg) - 6
for i := range 3 {
b1 := bnk.prg[idx]
idx++
b2 := bnk.prg[idx]
idx++
addr := uint16(b2)<<8 | uint16(b1)
prgBank.Vectors[i] = addr
}
}

func setBankName(prgBank *program.PRGBank, bnkIndex, numBanks int) {
if bnkIndex == 0 && numBanks == 1 {
prgBank.Name = singleBankName
return
}

prgBank.Name = fmt.Sprintf(multiBankNameTemplate, bnkIndex)
}
34 changes: 0 additions & 34 deletions internal/const.go

This file was deleted.

128 changes: 128 additions & 0 deletions internal/consts/consts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Package consts manages constants in the disassembled program.
package consts

import (
"fmt"
"sort"
"strings"

"github.com/retroenv/nesgodisasm/internal/arch"
"github.com/retroenv/nesgodisasm/internal/program"
)

var _ arch.ConstantManager = &Consts{}

// Consts manages constants in the disassembled program.
type Consts struct {
banks []*bank

constants map[uint16]arch.Constant
usedConstants map[uint16]arch.Constant
}

type bank struct {
constants map[uint16]arch.Constant
usedConstants map[uint16]arch.Constant
}

type architecture interface {
Constants() (map[uint16]arch.Constant, error)
}

// New creates a new constants manager.
func New(ar architecture) (*Consts, error) {
constants, err := ar.Constants()
if err != nil {
return nil, fmt.Errorf("getting constants: %w", err)
}

return &Consts{
constants: constants,
usedConstants: make(map[uint16]arch.Constant),
}, nil
}

// AddBank adds a new bank to the constants manager.
func (c *Consts) AddBank() {
c.banks = append(c.banks, &bank{
constants: make(map[uint16]arch.Constant),
usedConstants: make(map[uint16]arch.Constant),
})
}

// ReplaceParameter replaces the parameter of an instruction by a constant name
// if the address of the instruction is found in the constants map.
func (c *Consts) ReplaceParameter(address uint16, opcode arch.Opcode, paramAsString string) (string, bool) {
constantInfo, ok := c.constants[address]
if !ok {
return "", false
}

// split parameter string in case of x/y indexing, only the first part will be replaced by a const name
paramParts := strings.Split(paramAsString, ",")

if constantInfo.Read != "" && opcode.ReadsMemory() {
c.usedConstants[address] = constantInfo
paramParts[0] = constantInfo.Read
return strings.Join(paramParts, ","), true
}
if constantInfo.Write != "" && opcode.WritesMemory() {
c.usedConstants[address] = constantInfo
paramParts[0] = constantInfo.Write
return strings.Join(paramParts, ","), true
}

return paramAsString, true
}

// ProcessConstants processes all constants and updates all banks with the used ones. There is currently no tracking
// for in which bank a constant is used, it will be added to all banks for now.
// TODO fix constants to only output in used banks
func (c *Consts) ProcessConstants() {
constants := make([]arch.Constant, 0, len(c.constants))
for _, translation := range c.constants {
constants = append(constants, translation)
}
sort.Slice(constants, func(i, j int) bool {
return constants[i].Address < constants[j].Address
})

for _, constInfo := range constants {
_, used := c.usedConstants[constInfo.Address]
if !used {
continue
}

for _, bnk := range c.banks {
bnk.constants[constInfo.Address] = constInfo
bnk.usedConstants[constInfo.Address] = constInfo
}
}
}

// SetProgramConstants sets the used constants in the program for outputting.
func (c *Consts) SetProgramConstants(app *program.Program) {
for address := range c.usedConstants {
constantInfo := c.constants[address]
if constantInfo.Read != "" {
app.Constants[constantInfo.Read] = address
}
if constantInfo.Write != "" {
app.Constants[constantInfo.Write] = address
}
}
}

// SetBankConstants sets the used constants in the bank for outputting.
func (c *Consts) SetBankConstants(bankID int, prgBank *program.PRGBank) {
bank := c.banks[bankID]
for address := range bank.usedConstants {
constantInfo := bank.constants[address]
if constantInfo.Read != "" {
prgBank.Constants[constantInfo.Read] = address
}
if constantInfo.Write != "" {
prgBank.Constants[constantInfo.Write] = address
}
}
}
Loading

0 comments on commit 23fbe1c

Please sign in to comment.