c65gm/internal/commands/next.go

144 lines
3.4 KiB
Go

package commands
import (
"fmt"
"strings"
"c65gm/internal/compiler"
"c65gm/internal/preproc"
"c65gm/internal/utils"
)
// NextCommand handles NEXT statements
// Syntax: NEXT
// Increments loop variable and jumps back to loop start
type NextCommand struct {
info *compiler.ForLoopInfo
}
func (c *NextCommand) WillHandle(line preproc.Line) bool {
params, err := utils.ParseParams(line.Text)
if err != nil || len(params) == 0 {
return false
}
return strings.ToUpper(params[0]) == "NEXT"
}
func (c *NextCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) error {
params, err := utils.ParseParams(line.Text)
if err != nil {
return err
}
if len(params) != 1 {
return fmt.Errorf("NEXT: expected 1 parameter, got %d", len(params))
}
// Pop FOR info
info, err := ctx.ForStack.Pop()
if err != nil {
return fmt.Errorf("NEXT: not inside FOR loop")
}
c.info = info
// Pop and validate labels
loopLabel, err := ctx.LoopStartStack.Pop()
if err != nil || loopLabel != info.LoopLabel {
return fmt.Errorf("NEXT: loop stack mismatch")
}
skipLabel, err := ctx.LoopEndStack.Pop()
if err != nil || skipLabel != info.SkipLabel {
return fmt.Errorf("NEXT: loop stack mismatch")
}
return nil
}
func (c *NextCommand) Generate(ctx *compiler.CompilerContext) ([]string, error) {
var asm []string
// Generate increment
asm = append(asm, c.generateIncrement(ctx)...)
// Jump back to loop start
asm = append(asm, fmt.Sprintf("\tjmp %s", c.info.LoopLabel))
// Emit skip label
asm = append(asm, c.info.SkipLabel)
return asm, nil
}
func (c *NextCommand) generateIncrement(ctx *compiler.CompilerContext) []string {
// Check for step = 1 literal optimization
if !c.info.StepOperand.IsVar && c.info.StepOperand.Value == 1 {
return c.generateIncrementByOne(ctx)
}
// General case: var = var + step
return c.generateAdd()
}
func (c *NextCommand) generateIncrementByOne(ctx *compiler.CompilerContext) []string {
var asm []string
if c.info.VarKind == compiler.KindByte {
asm = append(asm, fmt.Sprintf("\tinc %s", c.info.VarName))
return asm
}
// Word variable - handle carry to high byte
label := ctx.GeneralStack.Push()
asm = append(asm, fmt.Sprintf("\tinc %s", c.info.VarName))
asm = append(asm, fmt.Sprintf("\tbne %s", label))
asm = append(asm, fmt.Sprintf("\tinc %s+1", c.info.VarName))
asm = append(asm, label)
return asm
}
func (c *NextCommand) generateAdd() []string {
var asm []string
// var = var + step
stepOp := c.info.StepOperand
asm = append(asm, "\tclc")
// Load var low byte
asm = append(asm, fmt.Sprintf("\tlda %s", c.info.VarName))
// Add step low byte
if stepOp.IsVar {
asm = append(asm, fmt.Sprintf("\tadc %s", stepOp.VarName))
} else {
asm = append(asm, fmt.Sprintf("\tadc #$%02x", uint8(stepOp.Value&0xFF)))
}
// Store low byte
asm = append(asm, fmt.Sprintf("\tsta %s", c.info.VarName))
// If variable is word, handle high byte
if c.info.VarKind == compiler.KindWord {
// Load var high byte
asm = append(asm, fmt.Sprintf("\tlda %s+1", c.info.VarName))
// Add step high byte (with carry)
if stepOp.IsVar {
if stepOp.VarKind == compiler.KindWord {
asm = append(asm, fmt.Sprintf("\tadc %s+1", stepOp.VarName))
} else {
asm = append(asm, "\tadc #0")
}
} else {
hi := uint8((stepOp.Value >> 8) & 0xFF)
asm = append(asm, fmt.Sprintf("\tadc #$%02x", hi))
}
// Store high byte
asm = append(asm, fmt.Sprintf("\tsta %s+1", c.info.VarName))
}
return asm
}