1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-07-05 09:28:54 +00:00

Shifting by variable amount

This commit is contained in:
Karol Stasiak 2018-03-11 23:02:34 +01:00
parent 465ba2f02f
commit 38f3923d4d
5 changed files with 137 additions and 15 deletions

View File

@ -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`

View File

@ -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

View File

@ -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))
}
}

View File

@ -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))
}
}
}

View File

@ -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)
}
}
}