Skip to content

Commit

Permalink
refactor ca65 config handling in preparation for multi bank support
Browse files Browse the repository at this point in the history
  • Loading branch information
cornelk committed Jun 9, 2024
1 parent b02cb3b commit c20120c
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 90 deletions.
74 changes: 74 additions & 0 deletions internal/assembler/ca65/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package ca65

import (
"fmt"
"strings"
)

const (
memoryConfigPart1 = `
MEMORY {
ZP: start = $00, size = $100, type = rw, file = "";
RAM: start = $0200, size = $600, type = rw, file = "";
HDR: start = $0000, size = $10, type = ro, file = %O, fill = yes;
`

memoryPrgBankTemplate = ` %-12s start = $%04X, size = $%04X, type = ro, file = %%O, fill = yes;
`

memoryConfigPart2 = ` CHR: start = $0000, size = $%04X, type = ro, file = %%O, fill = yes;
}
`

segmentsConfigPart1 = `
SEGMENTS {
ZEROPAGE: load = ZP, type = zp;
OAM: load = RAM, type = bss, start = $200, optional = yes;
BSS: load = RAM, type = bss;
HEADER: load = HDR, type = ro;
`

segmentsPrgBankTemplate = ` %-12s load = %s, type = ro, start = $%04X;
`

segmentsConfigPart2 = ` VECTORS: load = %s, type = ro, start = $%04X;
TILES: load = CHR, type = ro;
}
`
)

// GenerateMapperConfig generates a ca65 linker config dynamically based on the passed ROM settings.
func GenerateMapperConfig(conf Config) (string, error) {
prgSize := conf.PRGSize
vectorStart := conf.App.CodeBaseAddress + uint16(prgSize) - 6

buf := &strings.Builder{}
buf.WriteString(memoryConfigPart1)

for _, bank := range conf.App.PRG {
if _, err := fmt.Fprintf(buf, memoryPrgBankTemplate, bank.Name+":", conf.App.CodeBaseAddress, len(bank.Offsets)); err != nil {
return "", fmt.Errorf("writing memory bank line: %w", err)
}
}

if _, err := fmt.Fprintf(buf, memoryConfigPart2, conf.CHRSize); err != nil {
return "", fmt.Errorf("writing memory config: %w", err)
}

buf.WriteString(segmentsConfigPart1)

for _, bank := range conf.App.PRG {
if _, err := fmt.Fprintf(buf, segmentsPrgBankTemplate, bank.Name+":", bank.Name, conf.App.CodeBaseAddress); err != nil {
return "", fmt.Errorf("writing segment bank line: %w", err)
}
}

lastBank := conf.App.PRG[len(conf.App.PRG)-1]
if _, err := fmt.Fprintf(buf, segmentsConfigPart2, lastBank.Name, vectorStart); err != nil {
return "", fmt.Errorf("writing segments config: %w", err)
}

generated := buf.String()
return generated, nil
}
10 changes: 8 additions & 2 deletions internal/assembler/ca65/external.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"os/exec"
"runtime"
"strings"

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

const (
Expand All @@ -16,7 +18,8 @@ const (

// Config holds the ROM building configuration.
type Config struct {
PrgBase int
App *program.Program

PRGSize int
CHRSize int
}
Expand Down Expand Up @@ -51,7 +54,10 @@ func AssembleUsingExternalApp(asmFile, objectFile, outputFile string, conf Confi
_ = os.Remove(configFile.Name())
}()

mapperConfig := GenerateMapperConfig(conf)
mapperConfig, err := GenerateMapperConfig(conf)
if err != nil {
return fmt.Errorf("generating ca65 config: %w", err)
}

if err := os.WriteFile(configFile.Name(), []byte(mapperConfig), 0666); err != nil {
return fmt.Errorf("writing linker config: %w", err)
Expand Down
33 changes: 0 additions & 33 deletions internal/assembler/ca65/mapper.go

This file was deleted.

6 changes: 3 additions & 3 deletions internal/assembler/nesasm/bank.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func addPrgBankSelectors(codeBaseAddress int, prg []*program.PRGBank) int {

for {
if bankSwitch { // if switch was carried over after last bank was filled
setPrgBankSelector(bank.PRG, index, &bankAddress, &bankNumber)
setPrgBankSelector(bank.Offsets, index, &bankAddress, &bankNumber)
bankSwitch = false
}

Expand All @@ -31,7 +31,7 @@ func addPrgBankSelectors(codeBaseAddress int, prg []*program.PRGBank) int {
bankSpaceLeft = bankSize
}

bankBytesLeft := len(bank.PRG[index:])
bankBytesLeft := len(bank.Offsets[index:])
if bankSpaceLeft > bankBytesLeft {
counter += bankBytesLeft
break
Expand All @@ -42,7 +42,7 @@ func addPrgBankSelectors(codeBaseAddress int, prg []*program.PRGBank) int {
break
}

setPrgBankSelector(bank.PRG, index+bankSpaceLeft, &bankAddress, &bankNumber)
setPrgBankSelector(bank.Offsets, index+bankSpaceLeft, &bankAddress, &bankNumber)

index += bankSpaceLeft
counter += bankSpaceLeft
Expand Down
5 changes: 5 additions & 0 deletions internal/bank.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package disasm

const (
singleBankName = "CODE"
multiBankNameTemplate = "PRG_BANK_%d"
)

type bank struct {
prg []byte

Expand Down
31 changes: 23 additions & 8 deletions internal/disasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,27 +124,32 @@ func New(logger *log.Logger, cart *cartridge.Cartridge, options *options.Disasse
}

// Process disassembles the cartridge.
func (dis *Disasm) Process(mainWriter io.Writer, newBankWriter assembler.NewBankWriter) error {
func (dis *Disasm) Process(mainWriter io.Writer, newBankWriter assembler.NewBankWriter) (*program.Program, error) {
if err := dis.followExecutionFlow(); err != nil {
return err
return nil, err
}

dis.processData()
if err := dis.processVariables(); err != nil {
return err
return nil, err
}
dis.processConstants()
dis.processJumpDestinations()

app, err := dis.convertToProgram()
if err != nil {
return err
return nil, err
}
fileWriter := dis.fileWriterConstructor(app, dis.options, mainWriter, newBankWriter)
if err = fileWriter.Write(); err != nil {
return fmt.Errorf("writing app to file: %w", err)
return nil, fmt.Errorf("writing app to file: %w", err)
}
return nil
return app, nil
}

// Cart returns the loaded cartridge.
func (dis *Disasm) Cart() *cartridge.Cartridge {
return dis.cart
}

func (dis *Disasm) initializeBanks(prg []byte) {
Expand Down Expand Up @@ -277,7 +282,7 @@ func (dis *Disasm) convertToProgram() (*program.Program, error) {
app.VectorsStartAddress = dis.vectorsStartAddress
app.Handlers = dis.handlers

for _, bnk := range dis.banks {
for bnkIndex, bnk := range dis.banks {
prgBank := program.NewPRGBank(len(bnk.offsets))

for i := range len(bnk.offsets) {
Expand All @@ -287,7 +292,7 @@ func (dis *Disasm) convertToProgram() (*program.Program, error) {
return nil, err
}

prgBank.PRG[i] = programOffsetInfo
prgBank.Offsets[i] = programOffsetInfo
}

for address := range bnk.usedConstants {
Expand All @@ -304,6 +309,7 @@ func (dis *Disasm) convertToProgram() (*program.Program, error) {
prgBank.Variables[varInfo.name] = address
}

setBankName(prgBank, bnkIndex, len(dis.banks))
setBankVectors(bnk, prgBank)

app.PRG = append(app.PRG, prgBank)
Expand Down Expand Up @@ -355,6 +361,15 @@ func (dis *Disasm) loadCodeDataLog() error {
return nil
}

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

prgBank.Name = fmt.Sprintf(multiBankNameTemplate, bnkIndex)
}

func setBankVectors(bnk *bank, prgBank *program.PRGBank) {
idx := len(bnk.prg) - 6
for i := range 3 {
Expand Down
3 changes: 2 additions & 1 deletion internal/disasm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,9 @@ func runDisasm(t *testing.T, setup func(options *options.Disassembler, cart *car
return nil, nil // nolint: nilnil
}

err := disasm.Process(writer, newBankWriter)
app, err := disasm.Process(writer, newBankWriter)
assert.NoError(t, err)
assert.True(t, app != nil, "app should not be nil")

assert.NoError(t, writer.Flush())

Expand Down
12 changes: 7 additions & 5 deletions internal/program/prg.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ import (
// NewPRGBank creates a new PRG bank.
func NewPRGBank(size int) *PRGBank {
return &PRGBank{
PRG: make([]Offset, size),
Offsets: make([]Offset, size),
Constants: map[string]uint16{},
Variables: map[string]uint16{},
}
}

// PRGBank defines a PRG bank.
type PRGBank struct {
PRG []Offset
Name string

Offsets []Offset
Vectors [3]uint16

Constants map[string]uint16
Expand All @@ -24,15 +26,15 @@ type PRGBank struct {

// GetLastNonZeroByte searches for the last byte in PRG that is not zero.
func (bank PRGBank) GetLastNonZeroByte(options *options.Disassembler) int {
endIndex := len(bank.PRG) - 6 // leave space for vectors
endIndex := len(bank.Offsets) - 6 // leave space for vectors
if options.ZeroBytes {
return endIndex
}

start := len(bank.PRG) - 1 - 6 // skip irq pointers
start := len(bank.Offsets) - 1 - 6 // skip irq pointers

for i := start; i >= 0; i-- {
offset := bank.PRG[i]
offset := bank.Offsets[i]
if (len(offset.OpcodeBytes) == 0 || offset.OpcodeBytes[0] == 0) && offset.Label == "" {
continue
}
Expand Down
2 changes: 1 addition & 1 deletion internal/program/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func New(cart *cartridge.Cartridge) *Program {
func (p Program) PrgSize() int {
var size int
for _, bnk := range p.PRG {
size += len(bnk.PRG)
size += len(bnk.Offsets)
}
return size
}
12 changes: 8 additions & 4 deletions internal/verification/verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ import (
"github.com/retroenv/nesgodisasm/internal/assembler/ca65"
"github.com/retroenv/nesgodisasm/internal/assembler/nesasm"
"github.com/retroenv/nesgodisasm/internal/options"
"github.com/retroenv/nesgodisasm/internal/program"
"github.com/retroenv/retrogolib/arch/nes/cartridge"
"github.com/retroenv/retrogolib/log"
)

// VerifyOutput verifies that the output file recreates the exact input file.
func VerifyOutput(logger *log.Logger, cart *cartridge.Cartridge, options *options.Program, codeBaseAddress uint16) error {
func VerifyOutput(logger *log.Logger, options *options.Program,
cart *cartridge.Cartridge, app *program.Program) error {

if options.Output == "" {
return errors.New("can not verify console output")
}
Expand Down Expand Up @@ -47,7 +50,7 @@ func VerifyOutput(logger *log.Logger, cart *cartridge.Cartridge, options *option
}()
}

if err := assembleFile(cart, options, codeBaseAddress, filePart, outputFile.Name()); err != nil {
if err := assembleFile(options, cart, app, filePart, outputFile.Name()); err != nil {
return err
}

Expand All @@ -68,7 +71,7 @@ func VerifyOutput(logger *log.Logger, cart *cartridge.Cartridge, options *option
return nil
}

func assembleFile(cart *cartridge.Cartridge, options *options.Program, codeBaseAddress uint16,
func assembleFile(options *options.Program, cart *cartridge.Cartridge, app *program.Program,
filePart, outputFile string) error {

switch options.Assembler {
Expand All @@ -87,10 +90,11 @@ func assembleFile(cart *cartridge.Cartridge, options *options.Program, codeBaseA
}()

ca65Config := ca65.Config{
PrgBase: int(codeBaseAddress),
App: app,
PRGSize: len(cart.PRG),
CHRSize: len(cart.CHR),
}

if err = ca65.AssembleUsingExternalApp(options.Output, objectFile.Name(), outputFile, ca65Config); err != nil {
return fmt.Errorf("reassembling .nes file using ca65 failed: %w", err)
}
Expand Down
6 changes: 3 additions & 3 deletions internal/writer/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (w Writer) ProcessPRG(bank *program.PRGBank, endIndex int) error {
var previousLineWasCode bool

for i := 0; i < endIndex; i++ {
offset := bank.PRG[i]
offset := bank.Offsets[i]

if offset.WriteCallback != nil {
if err := offset.WriteCallback(w.writer); err != nil {
Expand Down Expand Up @@ -238,7 +238,7 @@ func (w Writer) bundlePRGDataWrites(bank *program.PRGBank, startIndex, endIndex
lineWriter := func(line string, byteCount int) error {
var err error

offset := bank.PRG[currentIndex]
offset := bank.Offsets[currentIndex]
if w.options.OffsetComments && !offset.HasAddressComment {
comment := fmt.Sprintf("$%04X", offset.Address)
if offset.Comment == "" {
Expand Down Expand Up @@ -272,7 +272,7 @@ func getPrgData(bank *program.PRGBank, startIndex, endIndex int) []byte {
var data []byte

for i := startIndex; i < endIndex; i++ {
offset := bank.PRG[i]
offset := bank.Offsets[i]

// opcode bytes can be nil if data bytes have been combined for an unofficial nop
if !offset.IsType(program.DataOffset) || len(offset.OpcodeBytes) == 0 {
Expand Down
Loading

0 comments on commit c20120c

Please sign in to comment.