From 35e4b77ee35abad5386b62331582073a612f23c1 Mon Sep 17 00:00:00 2001 From: Mattias Hansson Date: Fri, 2 Jan 2026 14:10:53 +0100 Subject: [PATCH] optimized xor generation somewhat. --- internal/commands/xor.go | 46 ++++++++++- internal/commands/xor_test.go | 149 +++++++++++++++++++++++++++++++++- 2 files changed, 188 insertions(+), 7 deletions(-) diff --git a/internal/commands/xor.go b/internal/commands/xor.go index 72d3e5a..cf3b422 100644 --- a/internal/commands/xor.go +++ b/internal/commands/xor.go @@ -162,6 +162,26 @@ func (c *XorCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext) } } + // Normalize operands: leverage XOR commutativity to swap if needed + // This ensures word operands are in param1 when mixed with bytes + // Makes code generation simpler and more consistent + param1IsByteSized := false + if c.param1IsVar { + param1IsByteSized = (c.param1VarKind == compiler.KindByte) + } else { + param1IsByteSized = ((c.param1Value >> 8) & 0xFF) == 0 + } + + param2IsWord := c.param2IsVar && c.param2VarKind == compiler.KindWord + + if param1IsByteSized && param2IsWord { + // Swap param1 and param2 + c.param1VarName, c.param2VarName = c.param2VarName, c.param1VarName + c.param1VarKind, c.param2VarKind = c.param2VarKind, c.param1VarKind + c.param1Value, c.param2Value = c.param2Value, c.param1Value + c.param1IsVar, c.param2IsVar = c.param2IsVar, c.param1IsVar + } + return nil } @@ -205,6 +225,22 @@ func (c *XorCommand) Generate(_ *compiler.CompilerContext) ([]string, error) { // If destination is word, handle high byte if c.destVarKind == compiler.KindWord { + // Optimization: skip high byte for self-assignment when param2 is byte-sized + // e.g., word_var = word_var ^ byte_var would just copy high byte to itself + if c.destVarName == c.param1VarName { + param2IsByteSized := false + if c.param2IsVar { + param2IsByteSized = (c.param2VarKind == compiler.KindByte) + } else { + param2IsByteSized = ((c.param2Value >> 8) & 0xFF) == 0 + } + + if param2IsByteSized { + // High byte would be: dest+1 = dest+1 ^ 0 (complete no-op) + return asm, nil + } + } + // Load high byte of param1 if c.param1IsVar { if c.param1VarKind == compiler.KindWord { @@ -217,16 +253,18 @@ func (c *XorCommand) Generate(_ *compiler.CompilerContext) ([]string, error) { asm = append(asm, fmt.Sprintf("\tlda #$%02x", hi)) } - // XOR with high byte of param2 + // XOR with high byte of param2 (skip if param2 high byte is 0, as eor #0 is a no-op) if c.param2IsVar { if c.param2VarKind == compiler.KindWord { asm = append(asm, fmt.Sprintf("\teor %s+1", c.param2VarName)) - } else { - asm = append(asm, "\teor #0") } + // Skip eor when param2 is byte var (high byte is 0) } else { hi := uint8((c.param2Value >> 8) & 0xFF) - asm = append(asm, fmt.Sprintf("\teor #$%02x", hi)) + if hi != 0 { + asm = append(asm, fmt.Sprintf("\teor #$%02x", hi)) + } + // Skip eor when param2 const high byte is 0 } // Store high byte diff --git a/internal/commands/xor_test.go b/internal/commands/xor_test.go index 608ce89..66b6ec7 100644 --- a/internal/commands/xor_test.go +++ b/internal/commands/xor_test.go @@ -71,7 +71,6 @@ func TestXorCommand_OldSyntax(t *testing.T) { "\teor b", "\tsta result", "\tlda #0", - "\teor #0", "\tsta result+1", }, }, @@ -183,7 +182,6 @@ func TestXorCommand_OldSyntax(t *testing.T) { "\teor bval", "\tsta result", "\tlda wval+1", - "\teor #0", "\tsta result+1", }, }, @@ -286,7 +284,6 @@ func TestXorCommand_NewSyntax(t *testing.T) { "\teor b", "\tsta result", "\tlda #0", - "\teor #0", "\tsta result+1", }, }, @@ -307,6 +304,152 @@ func TestXorCommand_NewSyntax(t *testing.T) { "\tsta result+1", }, }, + { + name: "byte ^ byte_const -> word (optimization: skip eor #0)", + line: "result = b ^ 5", + setupVars: func(st *compiler.SymbolTable) { + st.AddVar("b", "", compiler.KindByte, 0xFF) + st.AddVar("result", "", compiler.KindWord, 0) + }, + wantAsm: []string{ + "\tlda b", + "\teor #$05", + "\tsta result", + "\tlda #0", + "\tsta result+1", + }, + }, + { + name: "byte ^ word -> word (swapped to word ^ byte)", + line: "result = bval ^ wval", + setupVars: func(st *compiler.SymbolTable) { + st.AddVar("bval", "", compiler.KindByte, 0xFF) + st.AddVar("wval", "", compiler.KindWord, 0x1234) + st.AddVar("result", "", compiler.KindWord, 0) + }, + wantAsm: []string{ + "\tlda wval", + "\teor bval", + "\tsta result", + "\tlda wval+1", + "\tsta result+1", + }, + }, + { + name: "byte_const ^ byte -> word (optimization: skip eor #0)", + line: "result = 5 ^ b", + setupVars: func(st *compiler.SymbolTable) { + st.AddVar("b", "", compiler.KindByte, 0xFF) + st.AddVar("result", "", compiler.KindWord, 0) + }, + wantAsm: []string{ + "\tlda #$05", + "\teor b", + "\tsta result", + "\tlda #$00", + "\tsta result+1", + }, + }, + { + name: "byte ^ word_const -> word (no optimization)", + line: "result = b ^ 300", + setupVars: func(st *compiler.SymbolTable) { + st.AddVar("b", "", compiler.KindByte, 0xFF) + st.AddVar("result", "", compiler.KindWord, 0) + }, + wantAsm: []string{ + "\tlda b", + "\teor #$2c", + "\tsta result", + "\tlda #0", + "\teor #$01", + "\tsta result+1", + }, + }, + { + name: "word_const ^ byte -> word (optimization: skip eor #0)", + line: "result = 300 ^ b", + setupVars: func(st *compiler.SymbolTable) { + st.AddVar("b", "", compiler.KindByte, 0xFF) + st.AddVar("result", "", compiler.KindWord, 0) + }, + wantAsm: []string{ + "\tlda #$2c", + "\teor b", + "\tsta result", + "\tlda #$01", + "\tsta result+1", + }, + }, + { + name: "self-assignment: word ^= byte (optimization: skip high byte entirely)", + line: "wval = wval ^ bval", + setupVars: func(st *compiler.SymbolTable) { + st.AddVar("wval", "", compiler.KindWord, 0x1234) + st.AddVar("bval", "", compiler.KindByte, 0xFF) + }, + wantAsm: []string{ + "\tlda wval", + "\teor bval", + "\tsta wval", + }, + }, + { + name: "self-assignment reversed: word ^= byte (optimization via swap)", + line: "wval = bval ^ wval", + setupVars: func(st *compiler.SymbolTable) { + st.AddVar("wval", "", compiler.KindWord, 0x1234) + st.AddVar("bval", "", compiler.KindByte, 0xFF) + }, + wantAsm: []string{ + "\tlda wval", + "\teor bval", + "\tsta wval", + }, + }, + { + name: "self-assignment: word ^= byte_const (optimization: skip high byte entirely)", + line: "wval = wval ^ 42", + setupVars: func(st *compiler.SymbolTable) { + st.AddVar("wval", "", compiler.KindWord, 0x1234) + }, + wantAsm: []string{ + "\tlda wval", + "\teor #$2a", + "\tsta wval", + }, + }, + { + name: "self-assignment: word ^= word_const (no optimization: high byte needed)", + line: "wval = wval ^ 300", + setupVars: func(st *compiler.SymbolTable) { + st.AddVar("wval", "", compiler.KindWord, 0x1234) + }, + wantAsm: []string{ + "\tlda wval", + "\teor #$2c", + "\tsta wval", + "\tlda wval+1", + "\teor #$01", + "\tsta wval+1", + }, + }, + { + name: "self-assignment: word ^= word (no optimization: both high bytes needed)", + line: "wval = wval ^ wval2", + setupVars: func(st *compiler.SymbolTable) { + st.AddVar("wval", "", compiler.KindWord, 0x1234) + st.AddVar("wval2", "", compiler.KindWord, 0x5678) + }, + wantAsm: []string{ + "\tlda wval", + "\teor wval2", + "\tsta wval", + "\tlda wval+1", + "\teor wval2+1", + "\tsta wval+1", + }, + }, { name: "variable ^ literal", line: "result = a ^ $AA",