Optimized and, or and xor commands
This commit is contained in:
parent
c76c514d35
commit
205ae92d0e
6 changed files with 893 additions and 68 deletions
|
|
@ -156,6 +156,26 @@ func (c *AndCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
|
|||
}
|
||||
}
|
||||
|
||||
// Normalize operands: leverage AND 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
|
||||
}
|
||||
|
||||
|
|
@ -180,51 +200,110 @@ func (c *AndCommand) Generate(_ *compiler.CompilerContext) ([]string, error) {
|
|||
}
|
||||
|
||||
// At least one param is a variable - generate AND code
|
||||
// Load param1
|
||||
if c.param1IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s", c.param1VarName))
|
||||
} else {
|
||||
asm = append(asm, fmt.Sprintf("\tlda #$%02x", uint8(c.param1Value&0xFF)))
|
||||
|
||||
// Same variable on both sides: a & a = a
|
||||
if c.param1IsVar && c.param2IsVar && c.param1VarName == c.param2VarName {
|
||||
if c.destVarName == c.param1VarName {
|
||||
return asm, nil
|
||||
}
|
||||
if c.param1VarKind == compiler.KindWord {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s", c.param1VarName))
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s+1", c.param1VarName))
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s+1", c.destVarName))
|
||||
} else {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s", c.param1VarName))
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||
if c.destVarKind == compiler.KindWord {
|
||||
asm = append(asm, "\tlda #0")
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s+1", c.destVarName))
|
||||
}
|
||||
}
|
||||
return asm, nil
|
||||
}
|
||||
|
||||
// AND with param2
|
||||
if c.param2IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\tand %s", c.param2VarName))
|
||||
} else {
|
||||
asm = append(asm, fmt.Sprintf("\tand #$%02x", uint8(c.param2Value&0xFF)))
|
||||
}
|
||||
// Track if A is known to be 0 after low byte (avoids redundant lda #0 for high byte)
|
||||
aIsZero := false
|
||||
|
||||
// Store low byte
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||
// If either param is literal 0, low byte result is always 0
|
||||
param1LoIsZero := !c.param1IsVar && uint8(c.param1Value&0xFF) == 0
|
||||
param2LoIsZero := !c.param2IsVar && uint8(c.param2Value&0xFF) == 0
|
||||
|
||||
// If param1 is literal $FF and param2 is a var, just load param2 ($FF AND a = a)
|
||||
param1IsFF := !c.param1IsVar && uint8(c.param1Value&0xFF) == 0xFF
|
||||
|
||||
if param1LoIsZero || param2LoIsZero {
|
||||
asm = append(asm, "\tlda #0")
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||
aIsZero = true
|
||||
} else if param1IsFF && c.param2IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s", c.param2VarName))
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||
} else {
|
||||
// Load param1
|
||||
if c.param1IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s", c.param1VarName))
|
||||
} else {
|
||||
asm = append(asm, fmt.Sprintf("\tlda #$%02x", uint8(c.param1Value&0xFF)))
|
||||
}
|
||||
|
||||
// AND with param2 (skip if literal $FF, as and #$ff preserves accumulator)
|
||||
if c.param2IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\tand %s", c.param2VarName))
|
||||
} else if uint8(c.param2Value&0xFF) != 0xFF {
|
||||
asm = append(asm, fmt.Sprintf("\tand #$%02x", uint8(c.param2Value&0xFF)))
|
||||
}
|
||||
|
||||
// Store low byte
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||
}
|
||||
|
||||
// If destination is word, handle high byte
|
||||
if c.destVarKind == compiler.KindWord {
|
||||
// Load high byte of param1
|
||||
if c.param1IsVar {
|
||||
if c.param1VarKind == compiler.KindWord {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s+1", c.param1VarName))
|
||||
} else {
|
||||
// Determine if param2 high byte is effectively 0
|
||||
// (AND with 0 always yields 0 regardless of param1 high byte)
|
||||
param2HiEffectiveZero := false
|
||||
if c.param2IsVar {
|
||||
if c.param2VarKind == compiler.KindByte {
|
||||
param2HiEffectiveZero = true
|
||||
}
|
||||
} else {
|
||||
param2HiEffectiveZero = ((c.param2Value >> 8) & 0xFF) == 0
|
||||
}
|
||||
|
||||
if param2HiEffectiveZero {
|
||||
if !aIsZero {
|
||||
asm = append(asm, "\tlda #0")
|
||||
}
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s+1", c.destVarName))
|
||||
} else {
|
||||
hi := uint8((c.param1Value >> 8) & 0xFF)
|
||||
asm = append(asm, fmt.Sprintf("\tlda #$%02x", hi))
|
||||
}
|
||||
|
||||
// AND with high byte of param2
|
||||
if c.param2IsVar {
|
||||
if c.param2VarKind == compiler.KindWord {
|
||||
asm = append(asm, fmt.Sprintf("\tand %s+1", c.param2VarName))
|
||||
// Load high byte of param1
|
||||
if c.param1IsVar {
|
||||
if c.param1VarKind == compiler.KindWord {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s+1", c.param1VarName))
|
||||
} else {
|
||||
asm = append(asm, "\tlda #0")
|
||||
}
|
||||
} else {
|
||||
asm = append(asm, "\tand #0")
|
||||
hi := uint8((c.param1Value >> 8) & 0xFF)
|
||||
asm = append(asm, fmt.Sprintf("\tlda #$%02x", hi))
|
||||
}
|
||||
} else {
|
||||
hi := uint8((c.param2Value >> 8) & 0xFF)
|
||||
asm = append(asm, fmt.Sprintf("\tand #$%02x", hi))
|
||||
}
|
||||
|
||||
// Store high byte
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s+1", c.destVarName))
|
||||
// AND with high byte of param2
|
||||
if c.param2IsVar {
|
||||
if c.param2VarKind == compiler.KindWord {
|
||||
asm = append(asm, fmt.Sprintf("\tand %s+1", c.param2VarName))
|
||||
} else {
|
||||
asm = append(asm, fmt.Sprintf("\tand #$%02x", uint8(c.param2Value&0xFF)))
|
||||
}
|
||||
} else {
|
||||
hi := uint8((c.param2Value >> 8) & 0xFF)
|
||||
asm = append(asm, fmt.Sprintf("\tand #$%02x", hi))
|
||||
}
|
||||
|
||||
// Store high byte
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s+1", c.destVarName))
|
||||
}
|
||||
}
|
||||
|
||||
return asm, nil
|
||||
|
|
|
|||
|
|
@ -71,7 +71,6 @@ func TestAndCommand_OldSyntax(t *testing.T) {
|
|||
"\tand b",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tand #0",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
|
|
@ -113,8 +112,7 @@ func TestAndCommand_OldSyntax(t *testing.T) {
|
|||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #$ff",
|
||||
"\tand b",
|
||||
"\tlda b",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
|
|
@ -182,8 +180,7 @@ func TestAndCommand_OldSyntax(t *testing.T) {
|
|||
"\tlda wval",
|
||||
"\tand bval",
|
||||
"\tsta result",
|
||||
"\tlda wval+1",
|
||||
"\tand #0",
|
||||
"\tlda #0",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
|
|
@ -286,7 +283,6 @@ func TestAndCommand_NewSyntax(t *testing.T) {
|
|||
"\tand b",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tand #0",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
|
|
@ -320,6 +316,96 @@ func TestAndCommand_NewSyntax(t *testing.T) {
|
|||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte & $FF -> byte (optimization: skip and #$ff)",
|
||||
line: "result = a & $FF",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xAB, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda a",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte & $FF -> word (optimization: skip and #$ff)",
|
||||
line: "result = a & $FF",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xAB, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda a",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte & 0 -> byte (optimization: lda #0)",
|
||||
line: "result = a & 0",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xAB, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #0",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "0 & byte -> byte (optimization: lda #0)",
|
||||
line: "result = 0 & a",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xAB, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #0",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte & 0 -> word (optimization: lda #0)",
|
||||
line: "result = a & 0",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xAB, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #0",
|
||||
"\tsta result",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte & $100 -> byte (optimization: lda #0, low byte of $100 is 0)",
|
||||
line: "result = a & $100",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xAB, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #0",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte & $100 -> word (optimization: lda #0)",
|
||||
line: "result = a & $100",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xAB, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #0",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tand #$01",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "constant folding",
|
||||
line: "result = 255 & 15",
|
||||
|
|
@ -358,6 +444,206 @@ func TestAndCommand_NewSyntax(t *testing.T) {
|
|||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte & byte -> byte (same variable: a & a = a)",
|
||||
line: "result = a & a",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xAB, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda a",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "word & word -> word (same variable: x & x = x)",
|
||||
line: "result = x & x",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("x", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda x",
|
||||
"\tsta result",
|
||||
"\tlda x+1",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "$FF & byte -> word (optimization: lda param2)",
|
||||
line: "result = $FF & a",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xAB, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda a",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte & word -> byte (swap case)",
|
||||
line: "result = bval & wval",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("bval", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("wval", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda wval",
|
||||
"\tand bval",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte & word -> word (swap case)",
|
||||
line: "result = bval & wval",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("bval", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("wval", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda wval",
|
||||
"\tand bval",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "word_const & byte -> byte",
|
||||
line: "result = 300 & b",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("b", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #$2c",
|
||||
"\tand b",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "word_const & byte -> word",
|
||||
line: "result = 300 & b",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("b", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #$2c",
|
||||
"\tand b",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte & word_const -> byte",
|
||||
line: "result = b & 300",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("b", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda b",
|
||||
"\tand #$2c",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte & word_const -> word",
|
||||
line: "result = b & 300",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("b", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda b",
|
||||
"\tand #$2c",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tand #$01",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "0 & byte -> word (optimization: lda #0, skip high byte)",
|
||||
line: "result = 0 & a",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xAB, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #0",
|
||||
"\tsta result",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "self-assignment: word &= byte",
|
||||
line: "wval = wval & bval",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("wval", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("bval", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda wval",
|
||||
"\tand bval",
|
||||
"\tsta wval",
|
||||
"\tlda #0",
|
||||
"\tsta wval+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "self-assignment: word &= byte_const",
|
||||
line: "wval = wval & 42",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("wval", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda wval",
|
||||
"\tand #$2a",
|
||||
"\tsta wval",
|
||||
"\tlda #0",
|
||||
"\tsta wval+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "self-assignment: word &= word_const",
|
||||
line: "wval = wval & 300",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("wval", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda wval",
|
||||
"\tand #$2c",
|
||||
"\tsta wval",
|
||||
"\tlda wval+1",
|
||||
"\tand #$01",
|
||||
"\tsta wval+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "self-assignment: word &= word",
|
||||
line: "wval = wval & wval2",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("wval", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("wval2", "", compiler.KindWord, 0x5678, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda wval",
|
||||
"\tand wval2",
|
||||
"\tsta wval",
|
||||
"\tlda wval+1",
|
||||
"\tand wval2+1",
|
||||
"\tsta wval+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error: unknown destination",
|
||||
line: "unknown = a & b",
|
||||
|
|
|
|||
|
|
@ -156,6 +156,26 @@ func (c *OrCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContext)
|
|||
}
|
||||
}
|
||||
|
||||
// Normalize operands: leverage OR 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
|
||||
}
|
||||
|
||||
|
|
@ -180,25 +200,73 @@ func (c *OrCommand) Generate(_ *compiler.CompilerContext) ([]string, error) {
|
|||
}
|
||||
|
||||
// At least one param is a variable - generate OR code
|
||||
// Load param1
|
||||
if c.param1IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s", c.param1VarName))
|
||||
} else {
|
||||
asm = append(asm, fmt.Sprintf("\tlda #$%02x", uint8(c.param1Value&0xFF)))
|
||||
|
||||
// Same variable on both sides: a | a = a
|
||||
if c.param1IsVar && c.param2IsVar && c.param1VarName == c.param2VarName {
|
||||
if c.destVarName == c.param1VarName {
|
||||
return asm, nil
|
||||
}
|
||||
if c.param1VarKind == compiler.KindWord {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s", c.param1VarName))
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s+1", c.param1VarName))
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s+1", c.destVarName))
|
||||
} else {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s", c.param1VarName))
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||
if c.destVarKind == compiler.KindWord {
|
||||
asm = append(asm, "\tlda #0")
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s+1", c.destVarName))
|
||||
}
|
||||
}
|
||||
return asm, nil
|
||||
}
|
||||
|
||||
// OR with param2
|
||||
if c.param2IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\tora %s", c.param2VarName))
|
||||
} else {
|
||||
asm = append(asm, fmt.Sprintf("\tora #$%02x", uint8(c.param2Value&0xFF)))
|
||||
}
|
||||
// If either param is literal $FF, low byte result is always $FF
|
||||
param1IsFF := !c.param1IsVar && uint8(c.param1Value&0xFF) == 0xFF
|
||||
param2IsFF := !c.param2IsVar && uint8(c.param2Value&0xFF) == 0xFF
|
||||
|
||||
// Store low byte
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||
if param1IsFF || param2IsFF {
|
||||
asm = append(asm, "\tlda #$ff")
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||
} else if !c.param1IsVar && uint8(c.param1Value&0xFF) == 0 && c.param2IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s", c.param2VarName))
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||
} else {
|
||||
// Load param1
|
||||
if c.param1IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s", c.param1VarName))
|
||||
} else {
|
||||
asm = append(asm, fmt.Sprintf("\tlda #$%02x", uint8(c.param1Value&0xFF)))
|
||||
}
|
||||
|
||||
// OR with param2 (skip if literal 0, as ora #0 is a no-op)
|
||||
if c.param2IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\tora %s", c.param2VarName))
|
||||
} else if uint8(c.param2Value&0xFF) != 0 {
|
||||
asm = append(asm, fmt.Sprintf("\tora #$%02x", uint8(c.param2Value&0xFF)))
|
||||
}
|
||||
|
||||
// Store low byte
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return asm, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Load high byte of param1
|
||||
if c.param1IsVar {
|
||||
if c.param1VarKind == compiler.KindWord {
|
||||
|
|
@ -211,16 +279,18 @@ func (c *OrCommand) Generate(_ *compiler.CompilerContext) ([]string, error) {
|
|||
asm = append(asm, fmt.Sprintf("\tlda #$%02x", hi))
|
||||
}
|
||||
|
||||
// OR with high byte of param2
|
||||
// OR with high byte of param2 (skip if literal 0, as ora #0 is a no-op)
|
||||
if c.param2IsVar {
|
||||
if c.param2VarKind == compiler.KindWord {
|
||||
asm = append(asm, fmt.Sprintf("\tora %s+1", c.param2VarName))
|
||||
} else {
|
||||
asm = append(asm, "\tora #0")
|
||||
}
|
||||
// Skip ora when param2 is byte var (high byte is 0)
|
||||
} else {
|
||||
hi := uint8((c.param2Value >> 8) & 0xFF)
|
||||
asm = append(asm, fmt.Sprintf("\tora #$%02x", hi))
|
||||
if hi != 0 {
|
||||
asm = append(asm, fmt.Sprintf("\tora #$%02x", hi))
|
||||
}
|
||||
// Skip ora when param2 const high byte is 0
|
||||
}
|
||||
|
||||
// Store high byte
|
||||
|
|
|
|||
|
|
@ -71,7 +71,6 @@ func TestOrCommand_OldSyntax(t *testing.T) {
|
|||
"\tora b",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tora #0",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
|
|
@ -183,7 +182,6 @@ func TestOrCommand_OldSyntax(t *testing.T) {
|
|||
"\tora bval",
|
||||
"\tsta result",
|
||||
"\tlda wval+1",
|
||||
"\tora #0",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
|
|
@ -286,7 +284,6 @@ func TestOrCommand_NewSyntax(t *testing.T) {
|
|||
"\tora b",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tora #0",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
|
|
@ -320,6 +317,32 @@ func TestOrCommand_NewSyntax(t *testing.T) {
|
|||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte | 0 -> byte (optimization: skip ora #0)",
|
||||
line: "result = a | 0",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xF0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda a",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte | 0 -> word (optimization: skip ora #0)",
|
||||
line: "result = a | 0",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xF0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda a",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "constant folding",
|
||||
line: "result = 15 | 240",
|
||||
|
|
@ -358,6 +381,294 @@ func TestOrCommand_NewSyntax(t *testing.T) {
|
|||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte | $FF -> byte (optimization: lda #$ff)",
|
||||
line: "result = a | $FF",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xF0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #$ff",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "$FF | byte -> byte (optimization: lda #$ff)",
|
||||
line: "result = $FF | a",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xF0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #$ff",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte | $FF -> word (optimization: lda #$ff)",
|
||||
line: "result = a | $FF",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xF0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #$ff",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte | byte -> byte (same variable: a | a = a)",
|
||||
line: "result = a | a",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xF0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda a",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "0 | byte -> word (optimization: skip ora #0)",
|
||||
line: "result = 0 | a",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xF0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda a",
|
||||
"\tsta result",
|
||||
"\tlda #$00",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte | word -> byte (swap case)",
|
||||
line: "result = bval | wval",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("bval", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("wval", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda wval",
|
||||
"\tora bval",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte | word -> word (swap case)",
|
||||
line: "result = bval | wval",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("bval", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("wval", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda wval",
|
||||
"\tora bval",
|
||||
"\tsta result",
|
||||
"\tlda wval+1",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "$FF | word -> word",
|
||||
line: "result = $FF | wval",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("wval", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #$ff",
|
||||
"\tsta result",
|
||||
"\tlda wval+1",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "word | $FF -> word",
|
||||
line: "result = wval | $FF",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("wval", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #$ff",
|
||||
"\tsta result",
|
||||
"\tlda wval+1",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "$FF00 | byte -> byte",
|
||||
line: "result = $FF00 | b",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("b", "", compiler.KindByte, 0xAB, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda b",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "$FF00 | byte -> word",
|
||||
line: "result = $FF00 | b",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("b", "", compiler.KindByte, 0xAB, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda b",
|
||||
"\tsta result",
|
||||
"\tlda #$ff",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte | word_const -> byte",
|
||||
line: "result = b | 300",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("b", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda b",
|
||||
"\tora #$2c",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte | word_const -> word",
|
||||
line: "result = b | 300",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("b", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda b",
|
||||
"\tora #$2c",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tora #$01",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "word_const | byte -> byte",
|
||||
line: "result = 300 | b",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("b", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #$2c",
|
||||
"\tora b",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "word_const | byte -> word",
|
||||
line: "result = 300 | b",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("b", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #$2c",
|
||||
"\tora 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, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("bval", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda wval",
|
||||
"\tora 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, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("bval", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda wval",
|
||||
"\tora 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, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda wval",
|
||||
"\tora #$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, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda wval",
|
||||
"\tora #$2c",
|
||||
"\tsta wval",
|
||||
"\tlda wval+1",
|
||||
"\tora #$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, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("wval2", "", compiler.KindWord, 0x5678, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda wval",
|
||||
"\tora wval2",
|
||||
"\tsta wval",
|
||||
"\tlda wval+1",
|
||||
"\tora wval2+1",
|
||||
"\tsta wval+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "word | word -> word (same variable: x | x = x)",
|
||||
line: "result = x | x",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("x", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda x",
|
||||
"\tsta result",
|
||||
"\tlda x+1",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error: unknown destination",
|
||||
line: "unknown = a | b",
|
||||
|
|
|
|||
|
|
@ -200,18 +200,34 @@ func (c *XorCommand) Generate(_ *compiler.CompilerContext) ([]string, error) {
|
|||
}
|
||||
|
||||
// At least one param is a variable - generate XOR code
|
||||
// Load param1
|
||||
if c.param1IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s", c.param1VarName))
|
||||
} else {
|
||||
asm = append(asm, fmt.Sprintf("\tlda #$%02x", uint8(c.param1Value&0xFF)))
|
||||
|
||||
// Same variable on both sides: a ^ a = 0
|
||||
if c.param1IsVar && c.param2IsVar && c.param1VarName == c.param2VarName {
|
||||
asm = append(asm, "\tlda #0")
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
|
||||
if c.destVarKind == compiler.KindWord {
|
||||
asm = append(asm, fmt.Sprintf("\tsta %s+1", c.destVarName))
|
||||
}
|
||||
return asm, nil
|
||||
}
|
||||
|
||||
// XOR with param2
|
||||
if c.param2IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\teor %s", c.param2VarName))
|
||||
// If param1 is literal 0, just load param2 directly (0 XOR a = a)
|
||||
if !c.param1IsVar && uint8(c.param1Value&0xFF) == 0 && c.param2IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s", c.param2VarName))
|
||||
} else {
|
||||
asm = append(asm, fmt.Sprintf("\teor #$%02x", uint8(c.param2Value&0xFF)))
|
||||
// Load param1
|
||||
if c.param1IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\tlda %s", c.param1VarName))
|
||||
} else {
|
||||
asm = append(asm, fmt.Sprintf("\tlda #$%02x", uint8(c.param1Value&0xFF)))
|
||||
}
|
||||
|
||||
// XOR with param2 (skip if literal 0, as eor #0 is a no-op)
|
||||
if c.param2IsVar {
|
||||
asm = append(asm, fmt.Sprintf("\teor %s", c.param2VarName))
|
||||
} else if uint8(c.param2Value&0xFF) != 0 {
|
||||
asm = append(asm, fmt.Sprintf("\teor #$%02x", uint8(c.param2Value&0xFF)))
|
||||
}
|
||||
}
|
||||
|
||||
// Store low byte
|
||||
|
|
|
|||
|
|
@ -463,6 +463,44 @@ func TestXorCommand_NewSyntax(t *testing.T) {
|
|||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte ^ 0 -> byte (optimization: skip eor #0)",
|
||||
line: "result = a ^ 0",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda a",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "0 ^ byte -> byte (optimization: skip eor #0)",
|
||||
line: "result = 0 ^ a",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda a",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte ^ 0 -> word (optimization: skip eor #0)",
|
||||
line: "result = a ^ 0",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda a",
|
||||
"\tsta result",
|
||||
"\tlda #0",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "constant folding",
|
||||
line: "result = 255 ^ 170",
|
||||
|
|
@ -501,6 +539,31 @@ func TestXorCommand_NewSyntax(t *testing.T) {
|
|||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "byte ^ byte -> byte (same variable: a ^ a = 0)",
|
||||
line: "result = a ^ a",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("a", "", compiler.KindByte, 0xFF, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindByte, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #0",
|
||||
"\tsta result",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "word ^ word -> word (same variable: x ^ x = 0)",
|
||||
line: "result = x ^ x",
|
||||
setupVars: func(st *compiler.SymbolTable) {
|
||||
st.AddVar("x", "", compiler.KindWord, 0x1234, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
st.AddVar("result", "", compiler.KindWord, 0, preproc.Line{Filename: "test.c65", LineNo: 1})
|
||||
},
|
||||
wantAsm: []string{
|
||||
"\tlda #0",
|
||||
"\tsta result",
|
||||
"\tsta result+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error: unknown destination",
|
||||
line: "unknown = a ^ b",
|
||||
|
|
|
|||
Loading…
Reference in a new issue