From 5b3e5737b8bf15472253a1e0ea26f6469ed5fd3a Mon Sep 17 00:00:00 2001 From: Mattias Hansson Date: Wed, 5 Nov 2025 19:29:35 +0100 Subject: [PATCH] Added label and origin commands. --- internal/commands/label.go | 48 ++++++++++++++++++++++++++++ internal/commands/origin.go | 62 +++++++++++++++++++++++++++++++++++++ main.go | 3 +- 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 internal/commands/label.go create mode 100644 internal/commands/origin.go diff --git a/internal/commands/label.go b/internal/commands/label.go new file mode 100644 index 0000000..c6f9663 --- /dev/null +++ b/internal/commands/label.go @@ -0,0 +1,48 @@ +package commands + +import ( + "fmt" + "strings" + + "c65gm/internal/compiler" + "c65gm/internal/preproc" + "c65gm/internal/utils" +) + +// LabelCommand handles LABEL declarations +// Syntax: LABEL name +// Emits an assembly label for GOTO/GOSUB/CALL to target +type LabelCommand struct { + labelName string +} + +func (c *LabelCommand) WillHandle(line preproc.Line) bool { + params, err := utils.ParseParams(line.Text) + if err != nil || len(params) == 0 { + return false + } + return strings.ToUpper(params[0]) == "LABEL" +} + +func (c *LabelCommand) Interpret(line preproc.Line, _ *compiler.CompilerContext) error { + params, err := utils.ParseParams(line.Text) + if err != nil { + return err + } + + if len(params) != 2 { + return fmt.Errorf("LABEL: expected 2 parameters, got %d", len(params)) + } + + c.labelName = params[1] + + if len(c.labelName) < 2 { + return fmt.Errorf("LABEL: label name must be at least 2 characters long") + } + + return nil +} + +func (c *LabelCommand) Generate(_ *compiler.CompilerContext) ([]string, error) { + return []string{c.labelName}, nil +} diff --git a/internal/commands/origin.go b/internal/commands/origin.go new file mode 100644 index 0000000..eccb604 --- /dev/null +++ b/internal/commands/origin.go @@ -0,0 +1,62 @@ +package commands + +import ( + "fmt" + "strings" + + "c65gm/internal/compiler" + "c65gm/internal/preproc" + "c65gm/internal/utils" +) + +// OriginCommand handles ORIGIN declarations +// Syntax: ORIGIN
+// Sets the assembly origin address (ACME: *= address) +type OriginCommand struct { + address uint16 +} + +func (c *OriginCommand) WillHandle(line preproc.Line) bool { + params, err := utils.ParseParams(line.Text) + if err != nil || len(params) == 0 { + return false + } + upper := strings.ToUpper(params[0]) + return upper == "ORIGIN" || upper == "ORGIN" // Support common typo +} + +func (c *OriginCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) error { + params, err := utils.ParseParams(line.Text) + if err != nil { + return err + } + + if len(params) != 2 { + return fmt.Errorf("ORIGIN: expected 2 parameters, got %d", len(params)) + } + + scope := ctx.CurrentScope() + constLookup := func(name string) (int64, bool) { + sym := ctx.SymbolTable.Lookup(name, scope) + if sym != nil && sym.IsConst() { + return int64(sym.Value), true + } + return 0, false + } + + val, err := utils.EvaluateExpression(params[1], constLookup) + if err != nil { + return fmt.Errorf("ORIGIN: invalid address expression: %w", err) + } + + if val < 0 || val > 65535 { + return fmt.Errorf("ORIGIN: address %d out of range (0-65535)", val) + } + + c.address = uint16(val) + return nil +} + +func (c *OriginCommand) Generate(_ *compiler.CompilerContext) ([]string, error) { + return []string{fmt.Sprintf("*= $%04x", c.address)}, nil +} diff --git a/main.go b/main.go index 0280c6f..d8474a9 100644 --- a/main.go +++ b/main.go @@ -96,7 +96,8 @@ func registerCommands(comp *compiler.Compiler) { comp.Registry().Register(&commands.IncrCommand{}) comp.Registry().Register(&commands.DecrCommand{}) comp.Registry().Register(&commands.GotoCommand{}) - + comp.Registry().Register(&commands.LabelCommand{}) + comp.Registry().Register(&commands.OriginCommand{}) } func writeOutput(filename string, lines []string) error {