mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-11 12:29:46 +00:00
Shifting by variable amount
This commit is contained in:
parent
465ba2f02f
commit
38f3923d4d
@ -87,8 +87,8 @@ There are no division, remainder or modulo operators.
|
||||
`word | word` (zpreg)
|
||||
|
||||
* `<<`, `>>`: bit shifting; shifting pads the result with zeroes
|
||||
`byte << constant byte`
|
||||
`word << constant byte` (zpreg)
|
||||
`byte << byte`
|
||||
`word << byte` (zpreg)
|
||||
`constant word << constant byte`
|
||||
`constant long << constant byte`
|
||||
|
||||
@ -150,7 +150,12 @@ and fail to compile otherwise. This will be changed in the future.
|
||||
`mutable word += word`
|
||||
`mutable long += long`
|
||||
|
||||
* `<<=`, `>>=`, `<<'=`, `>>'=`: shift in place
|
||||
* `<<=`, `>>=`: shift in place
|
||||
`mutable byte <<= byte`
|
||||
`mutable word <<= byte`
|
||||
`mutable long <<= byte`
|
||||
|
||||
* `<<'=`, `>>'=`: decimal shift in place
|
||||
`mutable byte <<= constant byte`
|
||||
`mutable word <<= constant byte`
|
||||
`mutable long <<= constant byte`
|
||||
|
@ -473,6 +473,10 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
|
||||
case W => ReadsW(opcode)
|
||||
}
|
||||
|
||||
def concernsX: Boolean = addrMode == AbsoluteX || addrMode == LongAbsoluteX || addrMode == ZeroPageX || addrMode == IndexedX || ConcernsXAlways(opcode)
|
||||
|
||||
def concernsY: Boolean = addrMode == AbsoluteY || addrMode == ZeroPageY || addrMode == IndexedY || addrMode == LongIndexedY || ConcernsYAlways(opcode)
|
||||
|
||||
def treatment(state: State.Value): Treatment.Value = opcode match {
|
||||
case LABEL => Unchanged // TODO: ???
|
||||
case NOP => Unchanged
|
||||
|
@ -6,6 +6,7 @@ import millfork.env._
|
||||
import millfork.node._
|
||||
import millfork.assembly.Opcode._
|
||||
import millfork.assembly.AddrMode._
|
||||
import millfork.assembly.opt.ConcernsY
|
||||
import millfork.error.ErrorReporting
|
||||
|
||||
import scala.collection.mutable
|
||||
@ -186,8 +187,18 @@ object BuiltIns {
|
||||
case Some(NumericConstant(v, _)) if v > 0 =>
|
||||
firstParamCompiled ++ List.fill(v.toInt)(AssemblyLine.implied(opcode))
|
||||
case _ =>
|
||||
ErrorReporting.error("Cannot shift by a non-constant amount")
|
||||
Nil
|
||||
val compileCounter = ExpressionCompiler.preserveRegisterIfNeeded(ctx, Register.A,
|
||||
ExpressionCompiler.compile(ctx, r, Some(b -> RegisterVariable(Register.X, b)), NoBranching))
|
||||
val labelSkip = MfCompiler.nextLabel("ss")
|
||||
val labelRepeat = MfCompiler.nextLabel("sr")
|
||||
val loop = List(
|
||||
AssemblyLine.relative(BEQ, labelSkip),
|
||||
AssemblyLine.label(labelRepeat),
|
||||
AssemblyLine.implied(opcode),
|
||||
AssemblyLine.implied(DEX),
|
||||
AssemblyLine.relative(BNE, labelRepeat),
|
||||
AssemblyLine.label(labelSkip))
|
||||
firstParamCompiled ++ compileCounter ++ loop
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,7 +216,7 @@ object BuiltIns {
|
||||
}
|
||||
env.eval(rhs) match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
Nil
|
||||
ExpressionCompiler.compile(ctx, lhs, None, NoBranching)
|
||||
case Some(NumericConstant(shift, _)) if shift > 0 =>
|
||||
if (ctx.options.flag(CompilationFlag.RorWarning))
|
||||
ErrorReporting.warn("ROR instruction generated", ctx.options, lhs.position)
|
||||
@ -233,13 +244,12 @@ object BuiltIns {
|
||||
val firstParamCompiled = ExpressionCompiler.compile(ctx, lhs, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
|
||||
env.eval(rhs) match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
Nil
|
||||
ExpressionCompiler.compile(ctx, lhs, None, NoBranching)
|
||||
case Some(NumericConstant(v, _)) if v > 0 =>
|
||||
val result = simpleOperation(opcode, ctx, lhs, IndexChoice.RequireX, preserveA = true, commutative = false)
|
||||
result ++ List.fill(v.toInt - 1)(result.last)
|
||||
case _ =>
|
||||
ErrorReporting.error("Non-constant shift amount", rhs.position) // TODO
|
||||
Nil
|
||||
compileShiftOps(opcode, ctx, lhs, rhs) ++ ExpressionCompiler.compileByteStorage(ctx, Register.A, lhs)
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,9 +259,10 @@ object BuiltIns {
|
||||
val targetBytes = getStorageForEachByte(ctx, lhs)
|
||||
val lo = targetBytes.head
|
||||
val hi = targetBytes.last
|
||||
// TODO: this probably breaks in case of complex split word expressions
|
||||
env.eval(rhs) match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
Nil
|
||||
ExpressionCompiler.compile(ctx, lhs, None, NoBranching)
|
||||
case Some(NumericConstant(shift, _)) if shift > 0 =>
|
||||
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
targetBytes match {
|
||||
@ -259,6 +270,7 @@ object BuiltIns {
|
||||
if (a1 == a2 && l.+(1).quickSimplify == h) {
|
||||
return List(AssemblyLine.accu16) ++ List.fill(shift.toInt)(AssemblyLine(if (aslRatherThanLsr) ASL_W else LSR_W, a1, l)) ++ List(AssemblyLine.accu8)
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
List.fill(shift.toInt)(if (aslRatherThanLsr) {
|
||||
@ -269,8 +281,50 @@ object BuiltIns {
|
||||
staTo(LSR, hi) ++ targetBytes.reverse.tail.flatMap { b => staTo(ROR, b) }
|
||||
}).flatten
|
||||
case _ =>
|
||||
ErrorReporting.error("Non-constant shift amount", rhs.position) // TODO
|
||||
Nil
|
||||
val usesX = targetBytes.exists(_.exists(_.concernsX))
|
||||
val usesY = targetBytes.exists(_.exists(_.concernsY))
|
||||
val (register, decrease) = (usesX, usesY) match {
|
||||
case (true, false) => Register.Y -> DEY
|
||||
case (false, true) => Register.X -> DEX
|
||||
case (false, false) => Register.X -> DEX
|
||||
case (true, true) => ???
|
||||
}
|
||||
|
||||
val compileCounter = ExpressionCompiler.preserveRegisterIfNeeded(ctx, Register.A,
|
||||
ExpressionCompiler.compile(ctx, rhs, Some(b -> RegisterVariable(register, b)), NoBranching))
|
||||
val labelSkip = MfCompiler.nextLabel("ss")
|
||||
val labelRepeat = MfCompiler.nextLabel("sr")
|
||||
|
||||
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
targetBytes match {
|
||||
case List(List(AssemblyLine(STA, a1, l, _)), List(AssemblyLine(STA, a2, h, _))) =>
|
||||
if (a1 == a2 && l.+(1).quickSimplify == h) {
|
||||
return compileCounter ++ List(
|
||||
AssemblyLine.relative(BEQ, labelSkip),
|
||||
AssemblyLine.accu16,
|
||||
AssemblyLine.label(labelRepeat),
|
||||
AssemblyLine(if (aslRatherThanLsr) ASL_W else LSR_W, a1, l),
|
||||
AssemblyLine.implied(decrease),
|
||||
AssemblyLine.relative(BNE, labelRepeat),
|
||||
AssemblyLine.accu8,
|
||||
AssemblyLine.label(labelSkip))
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
compileCounter ++ List(
|
||||
AssemblyLine.relative(BEQ, labelSkip),
|
||||
AssemblyLine.label(labelRepeat)) ++ (if (aslRatherThanLsr) {
|
||||
staTo(ASL, lo) ++ targetBytes.tail.flatMap { b => staTo(ROL, b) }
|
||||
} else {
|
||||
if (ctx.options.flag(CompilationFlag.RorWarning))
|
||||
ErrorReporting.warn("ROR instruction generated", ctx.options, lhs.position)
|
||||
staTo(LSR, hi) ++ targetBytes.reverse.tail.flatMap { b => staTo(ROR, b) }
|
||||
}) ++ List(
|
||||
AssemblyLine.implied(decrease),
|
||||
AssemblyLine.relative(BNE, labelRepeat),
|
||||
AssemblyLine.label(labelSkip))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,7 +174,7 @@ object PseudoregisterBuiltIns {
|
||||
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
firstParamCompiled ++
|
||||
List(AssemblyLine.accu16) ++
|
||||
List.fill(v.toInt)(if (left) AssemblyLine.zeropage(ASL, reg) else AssemblyLine.zeropage(LSR, reg)) ++
|
||||
List.fill(v.toInt)(if (left) AssemblyLine.zeropage(ASL_W, reg) else AssemblyLine.zeropage(LSR_W, reg)) ++
|
||||
List(AssemblyLine.accu8, AssemblyLine.zeropage(LDA, reg), AssemblyLine.zeropage(LDX, reg, 1))
|
||||
} else {
|
||||
val cycle =
|
||||
@ -183,8 +183,44 @@ object PseudoregisterBuiltIns {
|
||||
firstParamCompiled ++ List.fill(v.toInt)(cycle).flatten ++ List(AssemblyLine.zeropage(LDA, reg), AssemblyLine.zeropage(LDX, reg, 1))
|
||||
}
|
||||
case _ =>
|
||||
ErrorReporting.error("Cannot shift by a non-constant amount")
|
||||
Nil
|
||||
val compileCounter = ExpressionCompiler.compile(ctx, r, Some(b -> RegisterVariable(Register.X, b)), NoBranching)
|
||||
val compileCounterAndPrepareFirstParam = compileCounter match {
|
||||
case List(AssemblyLine(LDX, _, _, _)) => firstParamCompiled ++ compileCounter
|
||||
case List(AssemblyLine(LDY, _, _, _), AssemblyLine(LDX, _, _, _)) => firstParamCompiled ++ compileCounter
|
||||
case _ =>
|
||||
ExpressionCompiler.compile(ctx, r, Some(b -> RegisterVariable(Register.A, b)), NoBranching) ++
|
||||
List(AssemblyLine.implied(PHA)) ++
|
||||
firstParamCompiled ++ (
|
||||
if (ctx.options.flag(CompilationFlag.EmitCmosOpcodes)) List(AssemblyLine.implied(PLX))
|
||||
else List(AssemblyLine.implied(PLA), AssemblyLine.implied(TAX))
|
||||
)
|
||||
}
|
||||
val labelRepeat = MfCompiler.nextLabel("sr")
|
||||
val labelSkip = MfCompiler.nextLabel("ss")
|
||||
if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
compileCounterAndPrepareFirstParam ++ List(
|
||||
AssemblyLine.relative(BEQ, labelSkip),
|
||||
AssemblyLine.accu16,
|
||||
AssemblyLine.label(labelRepeat),
|
||||
AssemblyLine.zeropage(if (left) ASL_W else LSR_W, reg),
|
||||
AssemblyLine.implied(DEX),
|
||||
AssemblyLine.relative(BNE, labelRepeat),
|
||||
AssemblyLine.accu8,
|
||||
AssemblyLine.label(labelSkip),
|
||||
AssemblyLine.zeropage(LDA, reg),
|
||||
AssemblyLine.zeropage(LDX, reg, 1))
|
||||
} else {
|
||||
compileCounterAndPrepareFirstParam ++ List(
|
||||
AssemblyLine.relative(BEQ, labelSkip),
|
||||
AssemblyLine.label(labelRepeat),
|
||||
if (left) AssemblyLine.zeropage(ASL, reg) else AssemblyLine.zeropage(LSR, reg, 1),
|
||||
if (left) AssemblyLine.zeropage(ROL, reg, 1) else AssemblyLine.zeropage(ROR, reg),
|
||||
AssemblyLine.implied(DEX),
|
||||
AssemblyLine.relative(BNE, labelRepeat),
|
||||
AssemblyLine.label(labelSkip),
|
||||
AssemblyLine.zeropage(LDA, reg),
|
||||
AssemblyLine.zeropage(LDX, reg, 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,4 +71,27 @@ class ShiftSuite extends FunSuite with Matchers {
|
||||
| word identity(word w) { return w }
|
||||
""".stripMargin)(_.readWord(0xc000) should equal(0x180))
|
||||
}
|
||||
|
||||
test("Variable shifting") {
|
||||
EmuBenchmarkRun("""
|
||||
| word output0 @$c000
|
||||
| word output2 @$c002
|
||||
| byte output4 @$c004
|
||||
| byte output5 @$c005
|
||||
| void main () {
|
||||
| byte a
|
||||
| a = b(3)
|
||||
| output0 = $0001 << a
|
||||
| output2 = $0001 << b(3)
|
||||
| output4 = 1 << a
|
||||
| output5 = 1 << b(3)
|
||||
| }
|
||||
| noinline byte b(byte x) { return x }
|
||||
""".stripMargin){m =>
|
||||
m.readWord(0xc000) should equal(8)
|
||||
m.readWord(0xc002) should equal(8)
|
||||
m.readByte(0xc004) should equal(8)
|
||||
m.readByte(0xc005) should equal(8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user