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 }