127 lines
3.4 KiB
Go
127 lines
3.4 KiB
Go
package optimizer
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"c65gm/internal/preproc"
|
|
)
|
|
|
|
type Config struct {
|
|
EnableLoad bool
|
|
EnableImm bool
|
|
EnableJmp bool
|
|
EnableSelf bool
|
|
EnableStoreLoad bool
|
|
Debug bool
|
|
ShowMarkers bool
|
|
IOMap [65536]bool
|
|
}
|
|
|
|
func NewConfig(ps preproc.PragmaSet) *Config {
|
|
all := ps.GetPragma("_P_OPT_ALL") != "" && ps.GetPragma("_P_OPT_ALL") != "0"
|
|
|
|
return &Config{
|
|
EnableLoad: all || (ps.GetPragma("_P_OPT_LOAD") != "" && ps.GetPragma("_P_OPT_LOAD") != "0"),
|
|
EnableImm: all || (ps.GetPragma("_P_OPT_IMM") != "" && ps.GetPragma("_P_OPT_IMM") != "0"),
|
|
EnableJmp: all || (ps.GetPragma("_P_OPT_JMP") != "" && ps.GetPragma("_P_OPT_JMP") != "0"),
|
|
EnableSelf: all || (ps.GetPragma("_P_OPT_SELF") != "" && ps.GetPragma("_P_OPT_SELF") != "0"),
|
|
EnableStoreLoad: all || (ps.GetPragma("_P_OPT_STLD") != "" && ps.GetPragma("_P_OPT_STLD") != "0"),
|
|
Debug: (ps.GetPragma("_P_OPT_DEBUG") != "" && ps.GetPragma("_P_OPT_DEBUG") != "0"),
|
|
ShowMarkers: (ps.GetPragma("_P_OPT_MARKERS") != "" && ps.GetPragma("_P_OPT_MARKERS") != "0"),
|
|
}
|
|
}
|
|
|
|
func (c *Config) Any() bool {
|
|
return c.EnableLoad || c.EnableImm || c.EnableJmp || c.EnableSelf || c.EnableStoreLoad
|
|
}
|
|
|
|
// BuildIOMap scans all pragma sets for _P_OPT_IO and marks I/O regions.
|
|
func (c *Config) BuildIOMap(pragma *preproc.Pragma) {
|
|
for i := 0; i <= pragma.GetCurrentPragmaSetIndex(); i++ {
|
|
val := pragma.GetPragmaSetByIndex(i).GetPragma("_P_OPT_IO")
|
|
if val == "" {
|
|
continue
|
|
}
|
|
parts := strings.Fields(val)
|
|
if len(parts) >= 2 {
|
|
start := parsePragmaAddr(parts[0])
|
|
end := parsePragmaAddr(parts[1])
|
|
if start >= 0 && end >= 0 && start <= end && start < 65536 {
|
|
for a := start; a <= end && a < 65536; a++ {
|
|
c.IOMap[a] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func parsePragmaAddr(s string) int {
|
|
if strings.HasPrefix(s, "$") {
|
|
v, err := strconv.ParseUint(s[1:], 16, 16)
|
|
if err != nil {
|
|
return -1
|
|
}
|
|
return int(v)
|
|
}
|
|
v, err := strconv.ParseUint(s, 10, 16)
|
|
if err != nil {
|
|
return -1
|
|
}
|
|
return int(v)
|
|
}
|
|
|
|
// IORegion defines an address range to exclude from optimization.
|
|
type IORegion struct {
|
|
Start, End uint16
|
|
}
|
|
|
|
// AddIORegions marks all addresses in the given regions as I/O in the IOMap.
|
|
func (c *Config) AddIORegions(regions []IORegion) {
|
|
for _, r := range regions {
|
|
for a := r.Start; a <= r.End; a++ {
|
|
c.IOMap[a] = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// ParseExcludeRange parses a string like "0xD000:0xDFFF" or "53248:57343".
|
|
// Returns an IORegion or an error.
|
|
func ParseExcludeRange(s string) (IORegion, error) {
|
|
parts := strings.SplitN(s, ":", 2)
|
|
if len(parts) != 2 {
|
|
return IORegion{}, fmt.Errorf("expected START:END, got %q", s)
|
|
}
|
|
|
|
start, err := parseExcludeAddr(parts[0])
|
|
if err != nil {
|
|
return IORegion{}, fmt.Errorf("invalid start %q: %w", parts[0], err)
|
|
}
|
|
end, err := parseExcludeAddr(parts[1])
|
|
if err != nil {
|
|
return IORegion{}, fmt.Errorf("invalid end %q: %w", parts[1], err)
|
|
}
|
|
if start > end {
|
|
return IORegion{}, fmt.Errorf("start %s > end %s", parts[0], parts[1])
|
|
}
|
|
return IORegion{Start: start, End: end}, nil
|
|
}
|
|
|
|
func parseExcludeAddr(s string) (uint16, error) {
|
|
s = strings.TrimSpace(s)
|
|
// Hex: 0x prefix (case-insensitive)
|
|
if len(s) > 2 && (s[:2] == "0x" || s[:2] == "0X") {
|
|
v, err := strconv.ParseUint(s[2:], 16, 16)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return uint16(v), nil
|
|
}
|
|
// Decimal
|
|
v, err := strconv.ParseUint(s, 10, 16)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return uint16(v), nil
|
|
}
|