-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
bank.go
122 lines (98 loc) · 3.16 KB
/
bank.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package nesasm
import (
"fmt"
"io"
"github.com/retroenv/nesgodisasm/internal/program"
)
const bankSize = 0x2000
// addPrgBankSelectors adds PRG bank selectors to every 0x2000 byte offsets, as required
// by nesasm to avoid the error "Bank overflow, offset > $1FFF".
func addPrgBankSelectors(codeBaseAddress int, prg []*program.PRGBank) int {
counter := 0
bankNumber := 0
bankAddress := codeBaseAddress
bankSwitch := true
for _, bank := range prg {
index := 0
for {
if bankSwitch { // if switch was carried over after last bank was filled
setPrgBankSelector(bank.PRG, index, &bankAddress, &bankNumber)
bankSwitch = false
}
bankSpaceLeft := counter % bankSize
if bankSpaceLeft == 0 {
bankSpaceLeft = bankSize
}
bankBytesLeft := len(bank.PRG[index:])
if bankSpaceLeft > bankBytesLeft {
counter += bankBytesLeft
break
}
if bankSpaceLeft == bankBytesLeft {
counter += bankBytesLeft
bankSwitch = true
break
}
setPrgBankSelector(bank.PRG, index+bankSpaceLeft, &bankAddress, &bankNumber)
index += bankSpaceLeft
counter += bankSpaceLeft
}
}
return bankNumber
}
// chrBanks adds CHR bank selectors to every 0x2000 byte offsets, as required
// by nesasm to avoid the error "Bank overflow, offset > $1FFF".
func chrBanks(nextBank int, chr program.CHR) []program.CHR {
banks := make([]program.CHR, 0, len(chr)/bankSize)
remaining := len(chr)
for index := 0; remaining > 0; nextBank++ {
toWrite := remaining
if toWrite > bankSize {
toWrite = bankSize
}
bank := chr[index : index+toWrite]
//WriteCallback: writeBankSelector(nextBank, -1),
banks = append(banks, bank)
index += toWrite
remaining -= toWrite
}
return banks
}
func setPrgBankSelector(prg []program.Offset, index int, bankAddress, bankNumber *int) {
offsetInfo := &prg[index]
// handle bank switches in the middle of an instruction by converting it to data bytes
if offsetInfo.IsType(program.CodeOffset) && len(offsetInfo.OpcodeBytes) == 0 {
// look backwards for instruction start
instructionStartIndex := index - 1
for offsetInfo = &prg[instructionStartIndex]; len(offsetInfo.OpcodeBytes) == 0; {
instructionStartIndex--
offsetInfo = &prg[instructionStartIndex]
}
offsetInfo.Comment = fmt.Sprintf("bank switch in instruction detected: %s %s",
offsetInfo.Comment, offsetInfo.Code)
data := offsetInfo.OpcodeBytes
for i := 0; i < len(data); i++ {
offsetInfo = &prg[instructionStartIndex+i]
offsetInfo.OpcodeBytes = data[i : i+1]
offsetInfo.ClearType(program.CodeOffset)
offsetInfo.SetType(program.CodeAsData | program.DataOffset)
}
offsetInfo = &prg[index]
}
offsetInfo.WriteCallback = writeBankSelector(*bankNumber, *bankAddress)
*bankAddress += bankSize
*bankNumber++
}
func writeBankSelector(bankNumber, bankAddress int) func(writer io.Writer) error {
return func(writer io.Writer) error {
if _, err := fmt.Fprintf(writer, "\n .bank %d\n", bankNumber); err != nil {
return fmt.Errorf("writing bank switch: %w", err)
}
if bankAddress >= 0 {
if _, err := fmt.Fprintf(writer, " .org $%04x\n\n", bankAddress); err != nil {
return fmt.Errorf("writing segment: %w", err)
}
}
return nil
}
}