147 lines
3.5 KiB
Go
147 lines
3.5 KiB
Go
package commands
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"c65gm/internal/compiler"
|
|
"c65gm/internal/preproc"
|
|
"c65gm/internal/utils"
|
|
)
|
|
|
|
// DecrCommand handles DECREMENT operations
|
|
// Syntax:
|
|
//
|
|
// DEC <target> # old syntax
|
|
// DECREMENT <target> # old syntax
|
|
// <var>-- # new syntax (literal, no space - variables only)
|
|
//
|
|
// <target> 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: <var>-- (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 <target>
|
|
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: <var>-- (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
|
|
}
|