package commands import ( "fmt" "strings" "c65gm/internal/compiler" "c65gm/internal/preproc" "c65gm/internal/utils" ) // DecrCommand handles DECREMENT operations // Syntax: // // DEC # old syntax // DECREMENT # old syntax // -- # new syntax (literal, no space - variables only) // // can be a variable or absolute address (old syntax only) type DecrCommand struct { varName string varKind compiler.VarKind isAbsolute bool absAddr uint16 } func (c *DecrCommand) WillHandle(line preproc.Line) bool { params, err := utils.ParseParams(line.Text) if err != nil || len(params) == 0 { return false } // Old syntax: DEC/DECREMENT keyword := strings.ToUpper(params[0]) if (keyword == "DEC" || keyword == "DECREMENT") && len(params) == 2 { return true } // New syntax: -- (literal, no space) if len(params) == 1 && strings.HasSuffix(params[0], "--") { return true } return false } func (c *DecrCommand) 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 == "DEC" || keyword == "DECREMENT" { // Old syntax: DEC/DECREMENT if len(params) != 2 { return fmt.Errorf("DEC: 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("DEC: wrong number of parameters") } targetParam = strings.TrimSuffix(params[0], "--") isNewSyntax = true } else { return fmt.Errorf("DEC: unrecognized syntax") } // Try variable lookup sym := ctx.SymbolTable.Lookup(targetParam, scope) if sym != nil { if sym.IsConst() { return fmt.Errorf("DEC: cannot decrement 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("DEC: 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("DEC: expected variable or absolute address, got %q: %w", targetParam, evalErr) } if val < 0 || val > 65535 { return fmt.Errorf("DEC: address %d out of range (0-65535)", val) } c.isAbsolute = true c.absAddr = uint16(val) return nil } func (c *DecrCommand) Generate(ctx *compiler.CompilerContext) ([]string, error) { var asm []string if c.isAbsolute { // Absolute address asm = append(asm, fmt.Sprintf("\tdec $%04x", c.absAddr)) return asm, nil } // Variable if c.varKind == compiler.KindByte { asm = append(asm, fmt.Sprintf("\tdec %s", c.varName)) return asm, nil } // Word variable - handle borrow from high byte label := ctx.GeneralStack.Push() asm = append(asm, fmt.Sprintf("\tlda %s", c.varName)) asm = append(asm, fmt.Sprintf("\tbne %s", label)) asm = append(asm, fmt.Sprintf("\tdec %s+1", c.varName)) asm = append(asm, label) asm = append(asm, fmt.Sprintf("\tdec %s", c.varName)) return asm, nil }