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 {
var asm []string
// Copy source to destination if needed
copyAsm := c.generateByteCopy()
asm = append(asm, copyAsm...)
// Apply shift
for i := uint16(0); i < amount; i++ {
asm = append(asm, fmt.Sprintf("\tasl %s", c.destVarName))
// Check if same variable
if c.sourceIsVar && c.sourceVarName == c.destVarName {
// Same variable: load to accumulator, shift, store back (faster)
asm = append(asm, fmt.Sprintf("\tlda %s", c.sourceVarName))
// Shift in accumulator (faster: 2 cycles vs 5 for memory)
for i := uint16(0); i < amount; i++ {
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
}
@ -267,24 +288,61 @@ func (c *ShiftLCommand) generateByteShiftConst(amount uint16) []string {
func (c *ShiftLCommand) generateByteShiftVar(ctx *compiler.CompilerContext) ([]string, error) {
var asm []string
// Copy source to destination if needed
copyAsm := c.generateByteCopy()
asm = append(asm, copyAsm...)
// Check if same variable
if c.sourceIsVar && c.sourceVarName == c.destVarName {
// 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
loopLabel := ctx.GeneralStack.Push()
ctx.GeneralStack.Pop()
doneLabel := ctx.GeneralStack.Push()
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("\tbeq %s", doneLabel))
// Shift loop (in accumulator, 2 cycles vs 5 for memory)
asm = append(asm, loopLabel)
asm = append(asm, fmt.Sprintf("\tasl %s", c.destVarName))
asm = append(asm, "\tasl")
asm = append(asm, "\tdex")
asm = append(asm, fmt.Sprintf("\tbne %s", loopLabel))
// Store result
asm = append(asm, doneLabel)
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
return asm, nil
}

View file

@ -366,10 +366,10 @@ func TestShiftLCommand_Generate(t *testing.T) {
},
wantLines: []string{
"\tlda #$01",
"\tasl",
"\tasl",
"\tasl",
"\tsta result",
"\tasl result",
"\tasl result",
"\tasl result",
},
},
{
@ -450,14 +450,14 @@ func TestShiftLCommand_Generate(t *testing.T) {
},
wantLines: []string{
"\tlda value",
"\tsta result",
"\tldx shift",
"\tbeq _L2",
"_L1",
"\tasl result",
"\tasl",
"\tdex",
"\tbne _L1",
"_L2",
"\tsta result",
},
},
{
@ -509,13 +509,15 @@ func TestShiftLCommand_Generate(t *testing.T) {
}
},
wantLines: []string{
"\tlda value",
"\tldx shift",
"\tbeq _L2",
"_L1",
"\tasl value",
"\tasl",
"\tdex",
"\tbne _L1",
"_L2",
"\tsta value",
},
},
{
@ -609,11 +611,11 @@ func TestShiftLCommand_Generate(t *testing.T) {
},
wantLines: []string{
"\tlda wordval",
"\tasl",
"\tasl",
"\tasl",
"\tasl",
"\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 {
var asm []string
// Copy source to destination if needed
copyAsm := c.generateByteCopy()
asm = append(asm, copyAsm...)
// Apply shift (right shift uses LSR)
for i := uint16(0); i < amount; i++ {
asm = append(asm, fmt.Sprintf("\tlsr %s", c.destVarName))
// Check if same variable
if c.sourceIsVar && c.sourceVarName == c.destVarName {
// Same variable: load to accumulator, shift, store back (faster)
asm = append(asm, fmt.Sprintf("\tlda %s", c.sourceVarName))
// Shift in accumulator (faster: 2 cycles vs 5 for memory)
for i := uint16(0); i < amount; i++ {
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
}
@ -279,24 +300,61 @@ func (c *ShiftRCommand) generateByteShiftConst(amount uint16) []string {
func (c *ShiftRCommand) generateByteShiftVar(ctx *compiler.CompilerContext) ([]string, error) {
var asm []string
// Copy source to destination if needed
copyAsm := c.generateByteCopy()
asm = append(asm, copyAsm...)
// Check if same variable
if c.sourceIsVar && c.sourceVarName == c.destVarName {
// 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
loopLabel := ctx.GeneralStack.Push()
ctx.GeneralStack.Pop()
doneLabel := ctx.GeneralStack.Push()
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("\tbeq %s", doneLabel))
// Shift loop (in accumulator, 2 cycles vs 5 for memory)
asm = append(asm, loopLabel)
asm = append(asm, fmt.Sprintf("\tlsr %s", c.destVarName))
asm = append(asm, "\tlsr")
asm = append(asm, "\tdex")
asm = append(asm, fmt.Sprintf("\tbne %s", loopLabel))
// Store result
asm = append(asm, doneLabel)
asm = append(asm, fmt.Sprintf("\tsta %s", c.destVarName))
return asm, nil
}

View file

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