package commands import ( "fmt" "strings" "c65gm/internal/compiler" "c65gm/internal/preproc" "c65gm/internal/utils" ) // IncrCommand handles INCREMENT operations // Syntax: // // INC # old syntax // INCREMENT # old syntax // ++ # new syntax (literal, no space - variables only) // // can be a variable or absolute address (old syntax only) type IncrCommand struct { varName string varKind compiler.VarKind isAbsolute bool absAddr uint16 } func (c *IncrCommand) WillHandle(line preproc.Line) bool { params, err := utils.ParseParams(line.Text) if err != nil || len(params) == 0 { return false } // Old syntax: INC/INCREMENT keyword := strings.ToUpper(params[0]) if (keyword == "INC" || keyword == "INCREMENT") && len(params) == 2 { return true } // New syntax: ++ (literal, no space) if len(params) == 1 && strings.HasSuffix(params[0], "++") { return true } return false } func (c *IncrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) error { // Clear state c.varName = "" c.isAbsolute = false c.absAddr = 0 params, err := utils.ParseParams(line.Text) if err != nil { return err } scope := ctx.CurrentScope() keyword := strings.ToUpper(params[0]) var targetParam string var isNewSyntax bool if keyword == "INC" || keyword == "INCREMENT" { // Old syntax: INC/INCREMENT if len(params) != 2 { return fmt.Errorf("INC: wrong number of parameters") } targetParam = params[1] isNewSyntax = false } else if strings.HasSuffix(params[0], "++") { // New syntax: ++ (literal) if len(params) != 1 { return fmt.Errorf("INC: wrong number of parameters") } targetParam = strings.TrimSuffix(params[0], "++") isNewSyntax = true } else { return fmt.Errorf("INC: unrecognized syntax") } // Try variable lookup sym := ctx.SymbolTable.Lookup(targetParam, scope) if sym != nil { if sym.IsConst() { return fmt.Errorf("INC: cannot increment constant %q", targetParam) } c.varName = sym.FullName() c.varKind = sym.GetVarKind() c.isAbsolute = false return nil } // For new syntax (++), must be a variable if isNewSyntax { return fmt.Errorf("INC: unknown variable %q", targetParam) } // Old syntax allows absolute addresses constLookup := func(name string) (int64, bool) { s := ctx.SymbolTable.Lookup(name, scope) if s != nil && s.IsConst() { return int64(s.Value), true } return 0, false } val, evalErr := utils.EvaluateExpression(targetParam, constLookup) if evalErr != nil { return fmt.Errorf("INC: expected variable or absolute address, got %q: %w", targetParam, evalErr) } if val < 0 || val > 65535 { return fmt.Errorf("INC: address %d out of range (0-65535)", val) } c.isAbsolute = true c.absAddr = uint16(val) return nil } func (c *IncrCommand) Generate(ctx *compiler.CompilerContext) ([]string, error) { var asm []string if c.isAbsolute { // Absolute address asm = append(asm, fmt.Sprintf("\tinc $%04x", c.absAddr)) return asm, nil } // Variable if c.varKind == compiler.KindByte { asm = append(asm, fmt.Sprintf("\tinc %s", c.varName)) return asm, nil } // Word variable - handle carry to high byte label := ctx.GeneralStack.Push() asm = append(asm, fmt.Sprintf("\tinc %s", c.varName)) asm = append(asm, fmt.Sprintf("\tbne %s", label)) asm = append(asm, fmt.Sprintf("\tinc %s+1", c.varName)) asm = append(asm, label) return asm, nil }