package commands import ( "strings" "testing" "c65gm/internal/compiler" "c65gm/internal/preproc" ) func TestPointerCommand_Generate(t *testing.T) { tests := []struct { name string line string setupVars func(*compiler.SymbolTable) wantAsm []string }{ { name: "pointer to label — uses A only", line: "POINTER ptr -> TARGET", setupVars: func(st *compiler.SymbolTable) { st.AddVar("ptr", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) }, wantAsm: []string{ "\tlda #TARGET", "\tsta ptr+1", }, }, { name: "pointer to variable — uses A only", line: "POINTER ptr TO targetVar", setupVars: func(st *compiler.SymbolTable) { st.AddVar("ptr", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) st.AddVar("targetVar", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) }, wantAsm: []string{ "\tlda #targetVar", "\tsta ptr+1", }, }, { name: "pointer to numeric address — uses A only", line: "POINTER ptr -> 53280", setupVars: func(st *compiler.SymbolTable) { st.AddVar("ptr", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pragma := preproc.NewPragma() ctx := compiler.NewCompilerContext(pragma) tt.setupVars(ctx.SymbolTable) cmd := &PointerCommand{} line := preproc.Line{ Text: tt.line, Kind: preproc.Source, PragmaSetIndex: pragma.GetCurrentPragmaSetIndex(), } if err := cmd.Interpret(line, ctx); err != nil { t.Fatalf("Interpret() error = %v", err) } asm, err := cmd.Generate(ctx) if err != nil { t.Fatalf("Generate() error = %v", err) } if tt.name == "pointer to numeric address — uses A only" { foundLo := false foundHi := false foundSta := false for _, a := range asm { if strings.Contains(a, "lda #<") { foundLo = true } if strings.Contains(a, "lda #>") { foundHi = true } if strings.Contains(a, "sta ptr+1") { foundSta = true } } if !foundLo || !foundHi || !foundSta { t.Errorf("expected A-only pattern (lda #< / sta ptr / lda #> / sta ptr+1), got:\n%s", strings.Join(asm, "\n")) } return } if !equalAsm(asm, tt.wantAsm) { t.Errorf("Generate() mismatch\ngot:\n%s\nwant:\n%s", strings.Join(asm, "\n"), strings.Join(tt.wantAsm, "\n")) } }) } } func TestPointerCommand_NoXRegister(t *testing.T) { pragma := preproc.NewPragma() ctx := compiler.NewCompilerContext(pragma) ctx.SymbolTable.AddVar("ptr", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1}) cmd := &PointerCommand{} line := preproc.Line{ Text: "POINTER ptr -> $0400", Kind: preproc.Source, PragmaSetIndex: pragma.GetCurrentPragmaSetIndex(), } if err := cmd.Interpret(line, ctx); err != nil { t.Fatalf("Interpret() error = %v", err) } asm, err := cmd.Generate(ctx) if err != nil { t.Fatalf("Generate() error = %v", err) } joined := strings.Join(asm, "\n") if strings.Contains(joined, "ldx") || strings.Contains(joined, "stx") { t.Errorf("POINTER should not use X register, got:\n%s", joined) } }