bit shift operators prefer shifting in ACC if possible

This commit is contained in:
Mattias Hansson 2026-04-15 07:11:56 +02:00
parent 822326f993
commit 5f25d66aeb
4 changed files with 160 additions and 40 deletions

View file

@ -251,15 +251,36 @@ func (c *ShiftLCommand) generateByteCopy() []string {
func (c *ShiftLCommand) generateByteShiftConst(amount uint16) []string { func (c *ShiftLCommand) generateByteShiftConst(amount uint16) []string {
var asm []string var asm []string
// Copy source to destination if needed // Check if same variable
copyAsm := c.generateByteCopy() if c.sourceIsVar && c.sourceVarName == c.destVarName {
asm = append(asm, copyAsm...) // Same variable: load to accumulator, shift, store back (faster)
asm = append(asm, fmt.Sprintf("\tlda %s", c.sourceVarName))
// Apply shift // Shift in accumulator (faster: 2 cycles vs 5 for memory)
for i := uint16(0); i < amount; i++ { for i := uint16(0); i < amount; i++ {
asm = append(asm, fmt.Sprintf("\tasl %s", c.destVarName)) asm = append(asm, "\tasl")
}
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
return asm
} }
// Different variables or literal source: shift in accumulator
if c.sourceIsVar {
// Variable source
asm = append(asm, fmt.Sprintf("\tlda %s", c.sourceVarName))
} else {
// Literal source
val := uint8(c.sourceValue & 0xFF)
asm = append(asm, fmt.Sprintf("\tlda #$%02x", val))
}
// Shift in accumulator (faster: 2 cycles vs 5 for memory)
for i := uint16(0); i < amount; i++ {
asm = append(asm, "\tasl")
}
// Store result
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
return asm return asm
} }
@ -267,24 +288,61 @@ func (c *ShiftLCommand) generateByteShiftConst(amount uint16) []string {
func (c *ShiftLCommand) generateByteShiftVar(ctx *compiler.CompilerContext) ([]string, error) { func (c *ShiftLCommand) generateByteShiftVar(ctx *compiler.CompilerContext) ([]string, error) {
var asm []string var asm []string
// Copy source to destination if needed // Check if same variable
copyAsm := c.generateByteCopy() if c.sourceIsVar && c.sourceVarName == c.destVarName {
asm = append(asm, copyAsm...) // Same variable: load to accumulator, shift loop, store back (faster)
// Generate labels
loopLabel := ctx.GeneralStack.Push()
ctx.GeneralStack.Pop()
doneLabel := ctx.GeneralStack.Push()
ctx.GeneralStack.Pop()
// Load value to accumulator
asm = append(asm, fmt.Sprintf("\tlda %s", c.sourceVarName))
// Variable shift loop (in accumulator, 2 cycles vs 5 for memory)
asm = append(asm, fmt.Sprintf("\tldx %s", c.amountVarName))
asm = append(asm, fmt.Sprintf("\tbeq %s", doneLabel))
asm = append(asm, loopLabel)
asm = append(asm, "\tasl")
asm = append(asm, "\tdex")
asm = append(asm, fmt.Sprintf("\tbne %s", loopLabel))
asm = append(asm, doneLabel)
// Store result back
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
return asm, nil
}
// Different variables or literal: shift in accumulator (faster)
// Generate labels // Generate labels
loopLabel := ctx.GeneralStack.Push() loopLabel := ctx.GeneralStack.Push()
ctx.GeneralStack.Pop() ctx.GeneralStack.Pop()
doneLabel := ctx.GeneralStack.Push() doneLabel := ctx.GeneralStack.Push()
ctx.GeneralStack.Pop() ctx.GeneralStack.Pop()
// Variable shift loop // Load source to accumulator
if c.sourceIsVar {
asm = append(asm, fmt.Sprintf("\tlda %s", c.sourceVarName))
} else {
val := uint8(c.sourceValue & 0xFF)
asm = append(asm, fmt.Sprintf("\tlda #$%02x", val))
}
// Load shift amount to X
asm = append(asm, fmt.Sprintf("\tldx %s", c.amountVarName)) asm = append(asm, fmt.Sprintf("\tldx %s", c.amountVarName))
asm = append(asm, fmt.Sprintf("\tbeq %s", doneLabel)) asm = append(asm, fmt.Sprintf("\tbeq %s", doneLabel))
// Shift loop (in accumulator, 2 cycles vs 5 for memory)
asm = append(asm, loopLabel) asm = append(asm, loopLabel)
asm = append(asm, fmt.Sprintf("\tasl %s", c.destVarName)) asm = append(asm, "\tasl")
asm = append(asm, "\tdex") asm = append(asm, "\tdex")
asm = append(asm, fmt.Sprintf("\tbne %s", loopLabel)) asm = append(asm, fmt.Sprintf("\tbne %s", loopLabel))
// Store result
asm = append(asm, doneLabel) asm = append(asm, doneLabel)
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
return asm, nil return asm, nil
} }

View file

@ -366,10 +366,10 @@ func TestShiftLCommand_Generate(t *testing.T) {
}, },
wantLines: []string{ wantLines: []string{
"\tlda #$01", "\tlda #$01",
"\tasl",
"\tasl",
"\tasl",
"\tsta result", "\tsta result",
"\tasl result",
"\tasl result",
"\tasl result",
}, },
}, },
{ {
@ -450,14 +450,14 @@ func TestShiftLCommand_Generate(t *testing.T) {
}, },
wantLines: []string{ wantLines: []string{
"\tlda value", "\tlda value",
"\tsta result",
"\tldx shift", "\tldx shift",
"\tbeq _L2", "\tbeq _L2",
"_L1", "_L1",
"\tasl result", "\tasl",
"\tdex", "\tdex",
"\tbne _L1", "\tbne _L1",
"_L2", "_L2",
"\tsta result",
}, },
}, },
{ {
@ -509,13 +509,15 @@ func TestShiftLCommand_Generate(t *testing.T) {
} }
}, },
wantLines: []string{ wantLines: []string{
"\tlda value",
"\tldx shift", "\tldx shift",
"\tbeq _L2", "\tbeq _L2",
"_L1", "_L1",
"\tasl value", "\tasl",
"\tdex", "\tdex",
"\tbne _L1", "\tbne _L1",
"_L2", "_L2",
"\tsta value",
}, },
}, },
{ {
@ -609,11 +611,11 @@ func TestShiftLCommand_Generate(t *testing.T) {
}, },
wantLines: []string{ wantLines: []string{
"\tlda wordval", "\tlda wordval",
"\tasl",
"\tasl",
"\tasl",
"\tasl",
"\tsta result", "\tsta result",
"\tasl result",
"\tasl result",
"\tasl result",
"\tasl result",
}, },
}, },
{ {

View file

@ -263,15 +263,36 @@ func (c *ShiftRCommand) generateByteCopy() []string {
func (c *ShiftRCommand) generateByteShiftConst(amount uint16) []string { func (c *ShiftRCommand) generateByteShiftConst(amount uint16) []string {
var asm []string var asm []string
// Copy source to destination if needed // Check if same variable
copyAsm := c.generateByteCopy() if c.sourceIsVar && c.sourceVarName == c.destVarName {
asm = append(asm, copyAsm...) // Same variable: load to accumulator, shift, store back (faster)
asm = append(asm, fmt.Sprintf("\tlda %s", c.sourceVarName))
// Apply shift (right shift uses LSR) // Shift in accumulator (faster: 2 cycles vs 5 for memory)
for i := uint16(0); i < amount; i++ { for i := uint16(0); i < amount; i++ {
asm = append(asm, fmt.Sprintf("\tlsr %s", c.destVarName)) asm = append(asm, "\tlsr")
}
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
return asm
} }
// Different variables or literal source: shift in accumulator
if c.sourceIsVar {
// Variable source
asm = append(asm, fmt.Sprintf("\tlda %s", c.sourceVarName))
} else {
// Literal source
val := uint8(c.sourceValue & 0xFF)
asm = append(asm, fmt.Sprintf("\tlda #$%02x", val))
}
// Shift in accumulator (faster: 2 cycles vs 5 for memory)
for i := uint16(0); i < amount; i++ {
asm = append(asm, "\tlsr")
}
// Store result
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
return asm return asm
} }
@ -279,24 +300,61 @@ func (c *ShiftRCommand) generateByteShiftConst(amount uint16) []string {
func (c *ShiftRCommand) generateByteShiftVar(ctx *compiler.CompilerContext) ([]string, error) { func (c *ShiftRCommand) generateByteShiftVar(ctx *compiler.CompilerContext) ([]string, error) {
var asm []string var asm []string
// Copy source to destination if needed // Check if same variable
copyAsm := c.generateByteCopy() if c.sourceIsVar && c.sourceVarName == c.destVarName {
asm = append(asm, copyAsm...) // Same variable: load to accumulator, shift loop, store back (faster)
// Generate labels
loopLabel := ctx.GeneralStack.Push()
ctx.GeneralStack.Pop()
doneLabel := ctx.GeneralStack.Push()
ctx.GeneralStack.Pop()
// Load value to accumulator
asm = append(asm, fmt.Sprintf("\tlda %s", c.sourceVarName))
// Variable shift loop (in accumulator, 2 cycles vs 5 for memory)
asm = append(asm, fmt.Sprintf("\tldx %s", c.amountVarName))
asm = append(asm, fmt.Sprintf("\tbeq %s", doneLabel))
asm = append(asm, loopLabel)
asm = append(asm, "\tlsr")
asm = append(asm, "\tdex")
asm = append(asm, fmt.Sprintf("\tbne %s", loopLabel))
asm = append(asm, doneLabel)
// Store result back
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
return asm, nil
}
// Different variables or literal: shift in accumulator (faster)
// Generate labels // Generate labels
loopLabel := ctx.GeneralStack.Push() loopLabel := ctx.GeneralStack.Push()
ctx.GeneralStack.Pop() ctx.GeneralStack.Pop()
doneLabel := ctx.GeneralStack.Push() doneLabel := ctx.GeneralStack.Push()
ctx.GeneralStack.Pop() ctx.GeneralStack.Pop()
// Variable shift loop (right shift uses LSR) // Load source to accumulator
if c.sourceIsVar {
asm = append(asm, fmt.Sprintf("\tlda %s", c.sourceVarName))
} else {
val := uint8(c.sourceValue & 0xFF)
asm = append(asm, fmt.Sprintf("\tlda #$%02x", val))
}
// Load shift amount to X
asm = append(asm, fmt.Sprintf("\tldx %s", c.amountVarName)) asm = append(asm, fmt.Sprintf("\tldx %s", c.amountVarName))
asm = append(asm, fmt.Sprintf("\tbeq %s", doneLabel)) asm = append(asm, fmt.Sprintf("\tbeq %s", doneLabel))
// Shift loop (in accumulator, 2 cycles vs 5 for memory)
asm = append(asm, loopLabel) asm = append(asm, loopLabel)
asm = append(asm, fmt.Sprintf("\tlsr %s", c.destVarName)) asm = append(asm, "\tlsr")
asm = append(asm, "\tdex") asm = append(asm, "\tdex")
asm = append(asm, fmt.Sprintf("\tbne %s", loopLabel)) asm = append(asm, fmt.Sprintf("\tbne %s", loopLabel))
// Store result
asm = append(asm, doneLabel) asm = append(asm, doneLabel)
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
return asm, nil return asm, nil
} }

View file

@ -366,10 +366,10 @@ func TestShiftRCommand_Generate(t *testing.T) {
}, },
wantLines: []string{ wantLines: []string{
"\tlda #$08", "\tlda #$08",
"\tlsr",
"\tlsr",
"\tlsr",
"\tsta result", "\tsta result",
"\tlsr result",
"\tlsr result",
"\tlsr result",
}, },
}, },
{ {
@ -450,14 +450,14 @@ func TestShiftRCommand_Generate(t *testing.T) {
}, },
wantLines: []string{ wantLines: []string{
"\tlda value", "\tlda value",
"\tsta result",
"\tldx shift", "\tldx shift",
"\tbeq _L2", "\tbeq _L2",
"_L1", "_L1",
"\tlsr result", "\tlsr",
"\tdex", "\tdex",
"\tbne _L1", "\tbne _L1",
"_L2", "_L2",
"\tsta result",
}, },
}, },
{ {
@ -509,13 +509,15 @@ func TestShiftRCommand_Generate(t *testing.T) {
} }
}, },
wantLines: []string{ wantLines: []string{
"\tlda value",
"\tldx shift", "\tldx shift",
"\tbeq _L2", "\tbeq _L2",
"_L1", "_L1",
"\tlsr value", "\tlsr",
"\tdex", "\tdex",
"\tbne _L1", "\tbne _L1",
"_L2", "_L2",
"\tsta value",
}, },
}, },
{ {