2018-06-12 20:46:20 +00:00
|
|
|
package millfork.compiler.mos
|
2017-12-06 23:23:30 +00:00
|
|
|
|
2018-06-12 20:46:20 +00:00
|
|
|
import millfork.CompilationFlag
|
2018-06-17 00:01:35 +00:00
|
|
|
import millfork.assembly.mos.AddrMode._
|
2018-06-12 20:46:20 +00:00
|
|
|
import millfork.assembly.mos.Opcode._
|
|
|
|
import millfork.assembly.mos._
|
2018-06-17 00:01:35 +00:00
|
|
|
import millfork.compiler._
|
2017-12-06 23:23:30 +00:00
|
|
|
import millfork.env._
|
2018-07-30 16:15:44 +00:00
|
|
|
import millfork.error.ConsoleLogger
|
2018-06-12 20:46:20 +00:00
|
|
|
import millfork.node._
|
2017-12-06 23:23:30 +00:00
|
|
|
|
|
|
|
import scala.collection.mutable
|
|
|
|
import scala.collection.mutable.ListBuffer
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @author Karol Stasiak
|
|
|
|
*/
|
2017-12-16 16:55:08 +00:00
|
|
|
//noinspection RedundantDefaultArgument
|
2017-12-06 23:23:30 +00:00
|
|
|
object BuiltIns {
|
|
|
|
|
|
|
|
object IndexChoice extends Enumeration {
|
|
|
|
val RequireX, PreferX, PreferY = Value
|
|
|
|
}
|
|
|
|
|
|
|
|
def wrapInSedCldIfNeeded(decimal: Boolean, code: List[AssemblyLine]): List[AssemblyLine] = {
|
|
|
|
if (decimal) {
|
|
|
|
AssemblyLine.implied(SED) :: (code :+ AssemblyLine.implied(CLD))
|
|
|
|
} else {
|
|
|
|
code
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
def staTo(op: Opcode.Value, l: List[AssemblyLine]): List[AssemblyLine] = l.map(x => if (x.opcode == STA) x.copy(opcode = op) else x)
|
|
|
|
|
2018-06-18 22:00:48 +00:00
|
|
|
def cmpTo(op: Opcode.Value, l: List[AssemblyLine]): List[AssemblyLine] = l.map(x => if (x.opcode == CMP) x.copy(opcode = op) else x)
|
|
|
|
|
2017-12-06 23:23:30 +00:00
|
|
|
def ldTo(op: Opcode.Value, l: List[AssemblyLine]): List[AssemblyLine] = l.map(x => if (x.opcode == LDA || x.opcode == LDX || x.opcode == LDY) x.copy(opcode = op) else x)
|
|
|
|
|
2017-12-16 16:55:08 +00:00
|
|
|
def simpleOperation(opcode: Opcode.Value, ctx: CompilationContext, source: Expression, indexChoice: IndexChoice.Value, preserveA: Boolean, commutative: Boolean, decimal: Boolean = false): List[AssemblyLine] = {
|
2017-12-06 23:23:30 +00:00
|
|
|
val env = ctx.env
|
|
|
|
val parts: (List[AssemblyLine], List[AssemblyLine]) = env.eval(source).fold {
|
|
|
|
val b = env.get[Type]("byte")
|
|
|
|
source match {
|
|
|
|
case VariableExpression(name) =>
|
|
|
|
val v = env.get[Variable](name)
|
|
|
|
if (v.typ.size > 1) {
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.error(s"Variable `$name` is too big for a built-in operation", source.position)
|
2017-12-06 23:23:30 +00:00
|
|
|
return Nil
|
|
|
|
}
|
|
|
|
Nil -> AssemblyLine.variable(ctx, opcode, v)
|
|
|
|
case IndexedExpression(arrayName, index) =>
|
2018-03-07 11:36:21 +00:00
|
|
|
val pointy = env.getPointy(arrayName)
|
2018-07-20 20:46:53 +00:00
|
|
|
AbstractExpressionCompiler.checkIndexType(ctx, pointy, index)
|
2018-03-07 11:36:21 +00:00
|
|
|
val (variablePart, constantPart) = env.evalVariableAndConstantSubParts(index)
|
|
|
|
val indexerSize = variablePart.map(v => getIndexerSize(ctx, v)).getOrElse(1)
|
|
|
|
val totalIndexSize = getIndexerSize(ctx, index)
|
|
|
|
(pointy, totalIndexSize, indexerSize, indexChoice, variablePart) match {
|
|
|
|
case (p: ConstantPointy, _, _, _, None) =>
|
|
|
|
Nil -> List(AssemblyLine.absolute(opcode, p.value + constantPart))
|
|
|
|
case (p: ConstantPointy, _, 1, IndexChoice.RequireX | IndexChoice.PreferX, Some(v)) =>
|
2018-07-20 20:46:53 +00:00
|
|
|
MosExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(MosRegister.X, pointy.indexType)), NoBranching) -> List(AssemblyLine.absoluteX(opcode, p.value + constantPart))
|
2018-03-07 11:36:21 +00:00
|
|
|
case (p: ConstantPointy, _, 1, IndexChoice.PreferY, Some(v)) =>
|
2018-07-20 20:46:53 +00:00
|
|
|
MosExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(MosRegister.Y, pointy.indexType)), NoBranching) -> List(AssemblyLine.absoluteY(opcode, p.value + constantPart))
|
2018-03-07 11:36:21 +00:00
|
|
|
case (p: VariablePointy, 0 | 1, _, IndexChoice.PreferX | IndexChoice.PreferY, _) =>
|
2018-07-20 20:46:53 +00:00
|
|
|
MosExpressionCompiler.compile(ctx, index, Some(b -> RegisterVariable(MosRegister.Y, pointy.indexType)), NoBranching) -> List(AssemblyLine.indexedY(opcode, p.addr))
|
2018-03-07 11:36:21 +00:00
|
|
|
case (p: ConstantPointy, _, 2, IndexChoice.PreferX | IndexChoice.PreferY, Some(v)) =>
|
2018-06-12 20:46:20 +00:00
|
|
|
MosExpressionCompiler.prepareWordIndexing(ctx, p, index) -> List(AssemblyLine.indexedY(opcode, env.get[VariableInMemory]("__reg")))
|
2018-03-07 11:36:21 +00:00
|
|
|
case (p: VariablePointy, 2, _, IndexChoice.PreferX | IndexChoice.PreferY, _) =>
|
2018-06-12 20:46:20 +00:00
|
|
|
MosExpressionCompiler.prepareWordIndexing(ctx, p, index) -> List(AssemblyLine.indexedY(opcode, env.get[VariableInMemory]("__reg")))
|
2018-03-07 11:36:21 +00:00
|
|
|
case _ =>
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.error("Invalid index for simple operation argument", index.position)
|
2018-03-07 11:36:21 +00:00
|
|
|
Nil -> Nil
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
2018-01-20 00:54:10 +00:00
|
|
|
case FunctionCallExpression(name, List(param)) if env.maybeGet[Type](name).isDefined =>
|
|
|
|
return simpleOperation(opcode, ctx, param, indexChoice, preserveA, commutative, decimal)
|
2017-12-16 16:55:08 +00:00
|
|
|
case _: FunctionCallExpression | _:SumExpression if commutative =>
|
2017-12-06 23:23:30 +00:00
|
|
|
// TODO: is it ok?
|
2018-03-03 13:32:11 +00:00
|
|
|
if (ctx.options.flag(CompilationFlag.EmitEmulation65816Opcodes)) {
|
2018-06-12 20:46:20 +00:00
|
|
|
return List(AssemblyLine.implied(PHA)) ++ MosExpressionCompiler.compile(ctx.addStack(1), source, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching) ++ wrapInSedCldIfNeeded(decimal, List(
|
2018-03-03 13:32:11 +00:00
|
|
|
AssemblyLine.stackRelative(opcode, 1),
|
|
|
|
AssemblyLine.implied(PHX)))
|
|
|
|
} else {
|
2018-06-12 20:46:20 +00:00
|
|
|
return List(AssemblyLine.implied(PHA)) ++ MosExpressionCompiler.compile(ctx.addStack(1), source, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching) ++ wrapInSedCldIfNeeded(decimal, List(
|
2018-03-03 13:32:11 +00:00
|
|
|
AssemblyLine.implied(TSX),
|
|
|
|
AssemblyLine.absoluteX(opcode, 0x101),
|
|
|
|
AssemblyLine.implied(INX),
|
|
|
|
AssemblyLine.implied(TXS))) // this TXS is fine, it won't appear in 65816 code
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
case _ =>
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.error("Right-hand-side expression is too complex", source.position)
|
2017-12-06 23:23:30 +00:00
|
|
|
return Nil
|
|
|
|
}
|
|
|
|
} {
|
|
|
|
const =>
|
|
|
|
if (const.requiredSize > 1) {
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.error("Constant too big for a built-in operation", source.position)
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
Nil -> List(AssemblyLine.immediate(opcode, const))
|
|
|
|
}
|
|
|
|
val preparations = parts._1
|
2017-12-16 16:55:08 +00:00
|
|
|
val finalRead = wrapInSedCldIfNeeded(decimal, parts._2)
|
2017-12-06 23:23:30 +00:00
|
|
|
if (preserveA && AssemblyLine.treatment(preparations, State.A) != Treatment.Unchanged) {
|
2018-07-23 23:38:10 +00:00
|
|
|
AssemblyLine.implied(PHA) :: (MosExpressionCompiler.fixTsx(preparations) ++ (AssemblyLine.implied(PLA) :: finalRead))
|
2017-12-06 23:23:30 +00:00
|
|
|
} else {
|
|
|
|
preparations ++ finalRead
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
def insertBeforeLast(item: AssemblyLine, list: List[AssemblyLine]): List[AssemblyLine] = list match {
|
|
|
|
case Nil => Nil
|
2018-02-24 23:45:25 +00:00
|
|
|
case last :: cld :: Nil if cld.opcode == CLD => item :: last :: cld :: Nil
|
|
|
|
case last :: cld :: dex :: txs :: Nil if cld.opcode == CLD && dex.opcode == DEX && txs.opcode == TXS => item :: last :: cld :: dex :: txs :: Nil
|
|
|
|
case last :: cld :: inx :: txs :: Nil if cld.opcode == CLD && inx.opcode == INX && txs.opcode == TXS => item :: last :: cld :: inx :: txs :: Nil
|
2017-12-06 23:23:30 +00:00
|
|
|
case last :: dex :: txs :: Nil if dex.opcode == DEX && txs.opcode == TXS => item :: last :: dex :: txs :: Nil
|
|
|
|
case last :: inx :: txs :: Nil if inx.opcode == INX && txs.opcode == TXS => item :: last :: inx :: txs :: Nil
|
|
|
|
case last :: Nil => item :: last :: Nil
|
|
|
|
case first :: rest => first :: insertBeforeLast(item, rest)
|
|
|
|
}
|
|
|
|
|
|
|
|
def compileAddition(ctx: CompilationContext, params: List[(Boolean, Expression)], decimal: Boolean): List[AssemblyLine] = {
|
2018-08-03 11:06:23 +00:00
|
|
|
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode) && ctx.options.zpRegisterSize < 4) {
|
|
|
|
ctx.log.error("Unsupported decimal operation. Consider increasing the size of the zeropage register.", params.head._2.position)
|
|
|
|
return compileAddition(ctx, params, decimal = false)
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
// if (params.isEmpty) {
|
|
|
|
// return Nil
|
|
|
|
// }
|
|
|
|
val env = ctx.env
|
|
|
|
val b = env.get[Type]("byte")
|
|
|
|
val sortedParams = params.sortBy { case (subtract, expr) =>
|
2018-01-07 22:30:43 +00:00
|
|
|
simplicity(env, expr) + (if (subtract) "X" else "P")
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
// TODO: merge constants
|
|
|
|
val normalizedParams = sortedParams
|
|
|
|
|
|
|
|
val h = normalizedParams.head
|
2018-06-12 20:46:20 +00:00
|
|
|
val firstParamCompiled = MosExpressionCompiler.compile(ctx, h._2, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
val firstParamSignCompiled = if (h._1) {
|
2017-12-16 16:55:08 +00:00
|
|
|
// TODO: check if decimal subtraction works correctly here
|
2017-12-06 23:23:30 +00:00
|
|
|
List(AssemblyLine.immediate(EOR, 0xff), AssemblyLine.implied(SEC), AssemblyLine.immediate(ADC, 0))
|
|
|
|
} else {
|
|
|
|
Nil
|
|
|
|
}
|
|
|
|
|
|
|
|
val remainingParamsCompiled = normalizedParams.tail.flatMap { p =>
|
2018-08-03 11:06:23 +00:00
|
|
|
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode)) {
|
|
|
|
val reg = ctx.env.get[VariableInMemory]("__reg")
|
|
|
|
if (p._1) {
|
|
|
|
List(AssemblyLine.zeropage(STA, reg, 2)) ++
|
|
|
|
MosExpressionCompiler.preserveZpregIfNeededDestroyingAAndX(ctx, 2,
|
|
|
|
MosExpressionCompiler.compileToA(ctx, p._2)) ++
|
|
|
|
List(AssemblyLine.zeropage(STA, reg, 3), AssemblyLine.absolute(JSR, ctx.env.get[FunctionInMemory]("__sub_decimal")))
|
|
|
|
} else {
|
|
|
|
List(AssemblyLine.zeropage(STA, reg, 2), AssemblyLine.implied(CLC)) ++
|
|
|
|
MosExpressionCompiler.preserveZpregIfNeededDestroyingAAndX(ctx, 2, MosExpressionCompiler.compileToA(ctx, p._2)) ++
|
|
|
|
List(AssemblyLine.zeropage(STA, reg, 3), AssemblyLine.absolute(JSR, ctx.env.get[FunctionInMemory]("__adc_decimal")))
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
} else {
|
2018-08-03 11:06:23 +00:00
|
|
|
if (p._1) {
|
|
|
|
insertBeforeLast(AssemblyLine.implied(SEC), simpleOperation(SBC, ctx, p._2, IndexChoice.PreferY, preserveA = true, commutative = false, decimal = decimal))
|
|
|
|
} else {
|
|
|
|
insertBeforeLast(AssemblyLine.implied(CLC), simpleOperation(ADC, ctx, p._2, IndexChoice.PreferY, preserveA = true, commutative = true, decimal = decimal))
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-16 16:55:08 +00:00
|
|
|
firstParamCompiled ++ firstParamSignCompiled ++ remainingParamsCompiled
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
|
2018-01-07 22:30:43 +00:00
|
|
|
private def simplicity(env: Environment, expr: Expression): Char = {
|
|
|
|
val constPart = env.eval(expr) match {
|
|
|
|
case Some(NumericConstant(_, _)) => 'Z'
|
|
|
|
case Some(_) => 'Y'
|
|
|
|
case None => expr match {
|
|
|
|
case VariableExpression(_) => 'V'
|
|
|
|
case IndexedExpression(_, LiteralExpression(_, _)) => 'K'
|
2018-07-21 21:59:16 +00:00
|
|
|
case IndexedExpression(_, GeneratedConstantExpression(_, _)) => 'K'
|
2018-07-20 20:46:53 +00:00
|
|
|
case IndexedExpression(_, expr@VariableExpression(v)) =>
|
|
|
|
env.eval(expr) match {
|
|
|
|
case Some(_) => 'K'
|
|
|
|
case None => env.get[Variable](v).typ.size match {
|
|
|
|
case 1 => 'J'
|
|
|
|
case _ => 'I'
|
|
|
|
}
|
|
|
|
}
|
2018-03-07 11:36:21 +00:00
|
|
|
case IndexedExpression(_, VariableExpression(v)) if env.get[Variable](v).typ.size == 1 => 'J'
|
2018-01-07 22:30:43 +00:00
|
|
|
case IndexedExpression(_, _) => 'I'
|
|
|
|
case _ => 'A'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
constPart
|
|
|
|
}
|
|
|
|
|
2017-12-06 23:23:30 +00:00
|
|
|
def compileBitOps(opcode: Opcode.Value, ctx: CompilationContext, params: List[Expression]): List[AssemblyLine] = {
|
|
|
|
val b = ctx.env.get[Type]("byte")
|
|
|
|
|
2018-03-07 11:36:21 +00:00
|
|
|
val sortedParams = params.sortBy { expr => simplicity(ctx.env, expr) }
|
2017-12-06 23:23:30 +00:00
|
|
|
|
|
|
|
val h = sortedParams.head
|
2018-06-12 20:46:20 +00:00
|
|
|
val firstParamCompiled = MosExpressionCompiler.compile(ctx, h, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
|
|
|
|
val remainingParamsCompiled = sortedParams.tail.flatMap { p =>
|
|
|
|
simpleOperation(opcode, ctx, p, IndexChoice.PreferY, preserveA = true, commutative = true)
|
|
|
|
}
|
|
|
|
|
|
|
|
firstParamCompiled ++ remainingParamsCompiled
|
|
|
|
}
|
|
|
|
|
|
|
|
def compileShiftOps(opcode: Opcode.Value, ctx: CompilationContext, l: Expression, r: Expression): List[AssemblyLine] = {
|
|
|
|
val b = ctx.env.get[Type]("byte")
|
2018-06-12 20:46:20 +00:00
|
|
|
val firstParamCompiled = MosExpressionCompiler.compile(ctx, l, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
ctx.env.eval(r) match {
|
|
|
|
case Some(NumericConstant(0, _)) =>
|
2018-06-12 20:46:20 +00:00
|
|
|
MosExpressionCompiler.compile(ctx, l, None, NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
case Some(NumericConstant(v, _)) if v > 0 =>
|
|
|
|
firstParamCompiled ++ List.fill(v.toInt)(AssemblyLine.implied(opcode))
|
|
|
|
case _ =>
|
2018-06-12 20:46:20 +00:00
|
|
|
val compileCounter = MosExpressionCompiler.preserveRegisterIfNeeded(ctx, MosRegister.A,
|
|
|
|
MosExpressionCompiler.compile(ctx, r, Some(b -> RegisterVariable(MosRegister.X, b)), NoBranching))
|
2018-07-31 16:16:36 +00:00
|
|
|
val labelSkip = ctx.nextLabel("ss")
|
|
|
|
val labelRepeat = ctx.nextLabel("sr")
|
2018-03-11 22:02:34 +00:00
|
|
|
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
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-27 22:02:57 +00:00
|
|
|
def compileNonetOps(ctx: CompilationContext, lhs: Expression, rhs: Expression): List[AssemblyLine] = {
|
2017-12-06 23:23:30 +00:00
|
|
|
val env = ctx.env
|
|
|
|
val b = env.get[Type]("byte")
|
2018-07-27 22:02:57 +00:00
|
|
|
val (ldaHi, ldaLo) = env.eval(lhs) match {
|
|
|
|
case Some(c) =>
|
|
|
|
List(AssemblyLine.immediate(LDA, c.hiByte)) -> List(AssemblyLine.immediate(LDA, c.loByte))
|
|
|
|
case _ => lhs match {
|
|
|
|
case v: VariableExpression =>
|
|
|
|
val variable = env.get[Variable](v.name)
|
|
|
|
AssemblyLine.variable(ctx, LDA, variable, 1) -> AssemblyLine.variable(ctx, LDA, variable, 0)
|
|
|
|
case SeparateBytesExpression(h: VariableExpression, l: VariableExpression) =>
|
|
|
|
AssemblyLine.variable(ctx, LDA, env.get[Variable](h.name), 0) -> AssemblyLine.variable(ctx, LDA, env.get[Variable](l.name), 0)
|
|
|
|
case _ =>
|
|
|
|
???
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
env.eval(rhs) match {
|
|
|
|
case Some(NumericConstant(0, _)) =>
|
2018-06-12 20:46:20 +00:00
|
|
|
MosExpressionCompiler.compile(ctx, lhs, None, NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
case Some(NumericConstant(shift, _)) if shift > 0 =>
|
|
|
|
if (ctx.options.flag(CompilationFlag.RorWarning))
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.warn("ROR instruction generated", lhs.position)
|
2017-12-06 23:23:30 +00:00
|
|
|
ldaHi ++ List(AssemblyLine.implied(ROR)) ++ ldaLo ++ List(AssemblyLine.implied(ROR)) ++ List.fill(shift.toInt - 1)(AssemblyLine.implied(LSR))
|
|
|
|
case _ =>
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.error("Non-constant shift amount", rhs.position) // TODO
|
2017-12-06 23:23:30 +00:00
|
|
|
Nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
def compileInPlaceByteShiftOps(opcode: Opcode.Value, ctx: CompilationContext, lhs: LhsExpression, rhs: Expression): List[AssemblyLine] = {
|
|
|
|
val env = ctx.env
|
|
|
|
val b = env.get[Type]("byte")
|
2018-06-12 20:46:20 +00:00
|
|
|
val firstParamCompiled = MosExpressionCompiler.compile(ctx, lhs, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
env.eval(rhs) match {
|
|
|
|
case Some(NumericConstant(0, _)) =>
|
2018-06-12 20:46:20 +00:00
|
|
|
MosExpressionCompiler.compile(ctx, lhs, None, NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
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 _ =>
|
2018-06-12 20:46:20 +00:00
|
|
|
compileShiftOps(opcode, ctx, lhs, rhs) ++ MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, lhs)
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
def compileInPlaceWordOrLongShiftOps(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, aslRatherThanLsr: Boolean): List[AssemblyLine] = {
|
|
|
|
val env = ctx.env
|
|
|
|
val b = env.get[Type]("byte")
|
2018-01-07 22:30:43 +00:00
|
|
|
val targetBytes = getStorageForEachByte(ctx, lhs)
|
2017-12-06 23:23:30 +00:00
|
|
|
val lo = targetBytes.head
|
|
|
|
val hi = targetBytes.last
|
2018-03-11 22:02:34 +00:00
|
|
|
// TODO: this probably breaks in case of complex split word expressions
|
2017-12-06 23:23:30 +00:00
|
|
|
env.eval(rhs) match {
|
|
|
|
case Some(NumericConstant(0, _)) =>
|
2018-06-12 20:46:20 +00:00
|
|
|
MosExpressionCompiler.compile(ctx, lhs, None, NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
case Some(NumericConstant(shift, _)) if shift > 0 =>
|
2018-03-03 00:21:57 +00:00
|
|
|
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 List(AssemblyLine.accu16) ++ List.fill(shift.toInt)(AssemblyLine(if (aslRatherThanLsr) ASL_W else LSR_W, a1, l)) ++ List(AssemblyLine.accu8)
|
|
|
|
}
|
2018-03-11 22:02:34 +00:00
|
|
|
case _ =>
|
2018-03-03 00:21:57 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
List.fill(shift.toInt)(if (aslRatherThanLsr) {
|
|
|
|
staTo(ASL, lo) ++ targetBytes.tail.flatMap { b => staTo(ROL, b) }
|
|
|
|
} else {
|
|
|
|
if (ctx.options.flag(CompilationFlag.RorWarning))
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.warn("ROR instruction generated", lhs.position)
|
2017-12-06 23:23:30 +00:00
|
|
|
staTo(LSR, hi) ++ targetBytes.reverse.tail.flatMap { b => staTo(ROR, b) }
|
|
|
|
}).flatten
|
|
|
|
case _ =>
|
2018-03-11 22:02:34 +00:00
|
|
|
val usesX = targetBytes.exists(_.exists(_.concernsX))
|
|
|
|
val usesY = targetBytes.exists(_.exists(_.concernsY))
|
|
|
|
val (register, decrease) = (usesX, usesY) match {
|
2018-06-12 20:46:20 +00:00
|
|
|
case (true, false) => MosRegister.Y -> DEY
|
|
|
|
case (false, true) => MosRegister.X -> DEX
|
|
|
|
case (false, false) => MosRegister.X -> DEX
|
2018-03-11 22:02:34 +00:00
|
|
|
case (true, true) => ???
|
|
|
|
}
|
|
|
|
|
2018-06-12 20:46:20 +00:00
|
|
|
val compileCounter = MosExpressionCompiler.preserveRegisterIfNeeded(ctx, MosRegister.A,
|
|
|
|
MosExpressionCompiler.compile(ctx, rhs, Some(b -> RegisterVariable(register, b)), NoBranching))
|
2018-07-31 16:16:36 +00:00
|
|
|
val labelSkip = ctx.nextLabel("ss")
|
|
|
|
val labelRepeat = ctx.nextLabel("sr")
|
2018-03-11 22:02:34 +00:00
|
|
|
|
|
|
|
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))
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.warn("ROR instruction generated", lhs.position)
|
2018-03-11 22:02:34 +00:00
|
|
|
staTo(LSR, hi) ++ targetBytes.reverse.tail.flatMap { b => staTo(ROR, b) }
|
|
|
|
}) ++ List(
|
|
|
|
AssemblyLine.implied(decrease),
|
|
|
|
AssemblyLine.relative(BNE, labelRepeat),
|
|
|
|
AssemblyLine.label(labelSkip))
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
def compileByteComparison(ctx: CompilationContext, compType: ComparisonType.Value, lhs: Expression, rhs: Expression, branches: BranchSpec): List[AssemblyLine] = {
|
|
|
|
val env = ctx.env
|
|
|
|
val b = env.get[Type]("byte")
|
2018-01-20 00:30:46 +00:00
|
|
|
if (simplicity(env, lhs) >= 'J' && simplicity(env, rhs) < 'J') {
|
|
|
|
return compileByteComparison(ctx, ComparisonType.flip(compType), rhs, lhs, branches)
|
|
|
|
}
|
2018-06-12 20:46:20 +00:00
|
|
|
val firstParamCompiled = MosExpressionCompiler.compile(ctx, lhs, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
|
2018-01-18 21:38:17 +00:00
|
|
|
val maybeConstant = env.eval(rhs)
|
|
|
|
maybeConstant match {
|
2017-12-06 23:23:30 +00:00
|
|
|
case Some(NumericConstant(0, _)) =>
|
|
|
|
compType match {
|
|
|
|
case ComparisonType.LessUnsigned =>
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.warn("Unsigned < 0 is always false", lhs.position)
|
2017-12-06 23:23:30 +00:00
|
|
|
case ComparisonType.LessOrEqualUnsigned =>
|
|
|
|
if (ctx.options.flag(CompilationFlag.ExtraComparisonWarnings))
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.warn("Unsigned <= 0 means the same as unsigned == 0", lhs.position)
|
2017-12-06 23:23:30 +00:00
|
|
|
case ComparisonType.GreaterUnsigned =>
|
|
|
|
if (ctx.options.flag(CompilationFlag.ExtraComparisonWarnings))
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.warn("Unsigned > 0 means the same as unsigned != 0", lhs.position)
|
2017-12-06 23:23:30 +00:00
|
|
|
case ComparisonType.GreaterOrEqualUnsigned =>
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.warn("Unsigned >= 0 is always true", lhs.position)
|
2017-12-06 23:23:30 +00:00
|
|
|
case _ =>
|
|
|
|
}
|
|
|
|
case Some(NumericConstant(1, _)) =>
|
|
|
|
if (ctx.options.flag(CompilationFlag.ExtraComparisonWarnings)) {
|
|
|
|
compType match {
|
|
|
|
case ComparisonType.LessUnsigned =>
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.warn("Unsigned < 1 means the same as unsigned == 0", lhs.position)
|
2017-12-06 23:23:30 +00:00
|
|
|
case ComparisonType.GreaterOrEqualUnsigned =>
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.warn("Unsigned >= 1 means the same as unsigned != 0", lhs.position)
|
2017-12-06 23:23:30 +00:00
|
|
|
case _ =>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case _ =>
|
|
|
|
}
|
2018-08-03 14:21:02 +00:00
|
|
|
val cmpOp = if (ComparisonType.isSigned(compType)) SBC else CMP
|
|
|
|
var comparingAgainstZero = false
|
|
|
|
val secondParamCompiled0 = maybeConstant match {
|
2018-01-18 21:38:17 +00:00
|
|
|
case Some(x) =>
|
|
|
|
compType match {
|
2018-08-03 14:21:02 +00:00
|
|
|
case ComparisonType.Equal | ComparisonType.NotEqual | ComparisonType.LessSigned | ComparisonType.GreaterOrEqualSigned | ComparisonType.LessOrEqualSigned | ComparisonType.GreaterSigned =>
|
|
|
|
if (x.quickSimplify.isLowestByteAlwaysEqual(0) && OpcodeClasses.ChangesAAlways(firstParamCompiled.last.opcode)) {
|
|
|
|
comparingAgainstZero = true
|
|
|
|
Nil
|
|
|
|
} else List(AssemblyLine.immediate(cmpOp, x))
|
2018-01-18 21:38:17 +00:00
|
|
|
case _ =>
|
2018-08-03 14:21:02 +00:00
|
|
|
List(AssemblyLine.immediate(cmpOp, x))
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
2018-01-18 21:38:17 +00:00
|
|
|
case _ => compType match {
|
|
|
|
case ComparisonType.Equal | ComparisonType.NotEqual | ComparisonType.LessSigned | ComparisonType.GreaterOrEqualSigned =>
|
2018-08-03 14:21:02 +00:00
|
|
|
val secondParamCompiledUnoptimized = simpleOperation(cmpOp, ctx, rhs, IndexChoice.PreferY, preserveA = true, commutative = false)
|
2018-01-18 21:38:17 +00:00
|
|
|
secondParamCompiledUnoptimized match {
|
2018-08-03 14:21:02 +00:00
|
|
|
case List(AssemblyLine(cmpOp, Immediate, NumericConstant(0, _), true)) =>
|
2018-01-18 21:38:17 +00:00
|
|
|
if (OpcodeClasses.ChangesAAlways(firstParamCompiled.last.opcode)) {
|
|
|
|
Nil
|
|
|
|
} else {
|
|
|
|
secondParamCompiledUnoptimized
|
|
|
|
}
|
|
|
|
case _ => secondParamCompiledUnoptimized
|
|
|
|
}
|
|
|
|
case _ =>
|
2018-08-03 14:21:02 +00:00
|
|
|
simpleOperation(cmpOp, ctx, rhs, IndexChoice.PreferY, preserveA = true, commutative = false)
|
2018-01-18 21:38:17 +00:00
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
2018-08-03 14:21:02 +00:00
|
|
|
val secondParamCompiled = if(cmpOp == SBC && !comparingAgainstZero) AssemblyLine.implied(SEC) :: secondParamCompiled0 else secondParamCompiled0
|
2017-12-06 23:23:30 +00:00
|
|
|
val (effectiveComparisonType, label) = branches match {
|
|
|
|
case NoBranching => return Nil
|
|
|
|
case BranchIfTrue(l) => compType -> l
|
|
|
|
case BranchIfFalse(l) => ComparisonType.negate(compType) -> l
|
|
|
|
}
|
2018-07-27 22:02:57 +00:00
|
|
|
(env.eval(lhs), env.eval(rhs)) match {
|
|
|
|
case (Some(NumericConstant(lc, _)), Some(NumericConstant(rc, _))) =>
|
|
|
|
return if (effectiveComparisonType match {
|
|
|
|
// TODO: those masks are probably wrong
|
|
|
|
case ComparisonType.Equal =>
|
|
|
|
(lc & 0xff) == (rc & 0xff)
|
|
|
|
case ComparisonType.NotEqual =>
|
|
|
|
(lc & 0xff) != (rc & 0xff)
|
|
|
|
case ComparisonType.LessOrEqualUnsigned =>
|
|
|
|
(lc & 0xff) <= (rc & 0xff)
|
|
|
|
case ComparisonType.GreaterOrEqualUnsigned =>
|
|
|
|
(lc & 0xff) >= (rc & 0xff)
|
|
|
|
case ComparisonType.GreaterUnsigned =>
|
|
|
|
(lc & 0xff) > (rc & 0xff)
|
|
|
|
case ComparisonType.LessUnsigned =>
|
|
|
|
(lc & 0xff) < (rc & 0xff)
|
|
|
|
case ComparisonType.LessOrEqualSigned =>
|
|
|
|
lc.toByte <= rc.toByte
|
|
|
|
case ComparisonType.GreaterOrEqualSigned =>
|
|
|
|
lc.toByte >= rc.toByte
|
|
|
|
case ComparisonType.GreaterSigned =>
|
|
|
|
lc.toByte > rc.toByte
|
|
|
|
case ComparisonType.LessSigned =>
|
|
|
|
lc.toByte < rc.toByte
|
|
|
|
}) List(AssemblyLine.absolute(JMP, Label(label))) else Nil
|
|
|
|
case _ =>
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
val branchingCompiled = effectiveComparisonType match {
|
|
|
|
case ComparisonType.Equal =>
|
|
|
|
List(AssemblyLine.relative(BEQ, Label(label)))
|
|
|
|
case ComparisonType.NotEqual =>
|
|
|
|
List(AssemblyLine.relative(BNE, Label(label)))
|
|
|
|
|
|
|
|
case ComparisonType.LessUnsigned =>
|
|
|
|
List(AssemblyLine.relative(BCC, Label(label)))
|
|
|
|
case ComparisonType.GreaterOrEqualUnsigned =>
|
|
|
|
List(AssemblyLine.relative(BCS, Label(label)))
|
|
|
|
case ComparisonType.LessOrEqualUnsigned =>
|
|
|
|
List(AssemblyLine.relative(BCC, Label(label)), AssemblyLine.relative(BEQ, Label(label)))
|
|
|
|
case ComparisonType.GreaterUnsigned =>
|
2018-07-31 16:16:36 +00:00
|
|
|
val x = ctx.nextLabel("co")
|
2017-12-06 23:23:30 +00:00
|
|
|
List(
|
|
|
|
AssemblyLine.relative(BEQ, x),
|
|
|
|
AssemblyLine.relative(BCS, Label(label)),
|
|
|
|
AssemblyLine.label(x))
|
|
|
|
|
|
|
|
case ComparisonType.LessSigned =>
|
2018-08-03 14:21:02 +00:00
|
|
|
if (comparingAgainstZero) List(AssemblyLine.relative(BMI, label)) else {
|
|
|
|
val fixup = ctx.nextLabel("co")
|
|
|
|
List(
|
|
|
|
AssemblyLine.relative(BVC, fixup),
|
|
|
|
AssemblyLine.immediate(EOR, 0x80),
|
|
|
|
AssemblyLine.label(fixup),
|
|
|
|
AssemblyLine.relative(BMI, label))
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
case ComparisonType.GreaterOrEqualSigned =>
|
2018-08-03 14:21:02 +00:00
|
|
|
if (comparingAgainstZero) List(AssemblyLine.relative(BPL, label)) else {
|
|
|
|
val fixup = ctx.nextLabel("co")
|
|
|
|
List(
|
|
|
|
AssemblyLine.relative(BVC, fixup),
|
|
|
|
AssemblyLine.immediate(EOR, 0x80),
|
|
|
|
AssemblyLine.label(fixup), AssemblyLine.relative(BPL, label))
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
case ComparisonType.LessOrEqualSigned =>
|
2018-08-03 14:21:02 +00:00
|
|
|
if (comparingAgainstZero) {
|
|
|
|
List(AssemblyLine.relative(BEQ, label),
|
|
|
|
AssemblyLine.relative(BMI, label))
|
|
|
|
} else {
|
|
|
|
val fixup = ctx.nextLabel("co")
|
|
|
|
List(AssemblyLine.relative(BVC, fixup),
|
|
|
|
AssemblyLine.immediate(EOR, 0x80),
|
|
|
|
AssemblyLine.label(fixup),
|
|
|
|
AssemblyLine.relative(BMI, label),
|
|
|
|
AssemblyLine.relative(BEQ, label))
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
case ComparisonType.GreaterSigned =>
|
2018-08-03 14:21:02 +00:00
|
|
|
if (comparingAgainstZero) {
|
|
|
|
val x = ctx.nextLabel("co")
|
|
|
|
List(AssemblyLine.relative(BEQ, x),
|
|
|
|
AssemblyLine.relative(BPL, label),
|
|
|
|
AssemblyLine.label(x))
|
|
|
|
} else {
|
|
|
|
val fixup = ctx.nextLabel("co")
|
|
|
|
val x = ctx.nextLabel("co")
|
|
|
|
List(
|
|
|
|
AssemblyLine.relative(BVC, fixup),
|
|
|
|
AssemblyLine.immediate(EOR, 0x80),
|
|
|
|
AssemblyLine.label(fixup),
|
|
|
|
AssemblyLine.relative(BEQ, x),
|
|
|
|
AssemblyLine.relative(BPL, label),
|
|
|
|
AssemblyLine.label(x))
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
firstParamCompiled ++ secondParamCompiled ++ branchingCompiled
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
def compileWordComparison(ctx: CompilationContext, compType: ComparisonType.Value, lhs: Expression, rhs: Expression, branches: BranchSpec): List[AssemblyLine] = {
|
|
|
|
val env = ctx.env
|
2017-12-23 23:08:49 +00:00
|
|
|
// TODO: comparing longer variables
|
2017-12-06 23:23:30 +00:00
|
|
|
val b = env.get[Type]("byte")
|
|
|
|
val w = env.get[Type]("word")
|
|
|
|
|
|
|
|
val (effectiveComparisonType, x) = branches match {
|
|
|
|
case NoBranching => return Nil
|
|
|
|
case BranchIfTrue(label) => compType -> label
|
|
|
|
case BranchIfFalse(label) => ComparisonType.negate(compType) -> label
|
|
|
|
}
|
2017-12-23 23:08:49 +00:00
|
|
|
val (lh, ll, rh, rl) = (lhs, env.eval(lhs), rhs, env.eval(rhs)) match {
|
2017-12-06 23:23:30 +00:00
|
|
|
case (_, Some(NumericConstant(lc, _)), _, Some(NumericConstant(rc, _))) =>
|
|
|
|
return if (effectiveComparisonType match {
|
|
|
|
// TODO: those masks are probably wrong
|
|
|
|
case ComparisonType.Equal =>
|
|
|
|
(lc & 0xffff) == (rc & 0xffff) // ??
|
|
|
|
case ComparisonType.NotEqual =>
|
|
|
|
(lc & 0xffff) != (rc & 0xffff) // ??
|
|
|
|
|
|
|
|
case ComparisonType.LessOrEqualUnsigned =>
|
|
|
|
(lc & 0xffff) <= (rc & 0xffff)
|
|
|
|
case ComparisonType.GreaterOrEqualUnsigned =>
|
|
|
|
(lc & 0xffff) >= (rc & 0xffff)
|
|
|
|
case ComparisonType.GreaterUnsigned =>
|
|
|
|
(lc & 0xffff) > (rc & 0xffff)
|
|
|
|
case ComparisonType.LessUnsigned =>
|
|
|
|
(lc & 0xffff) < (rc & 0xffff)
|
|
|
|
|
|
|
|
case ComparisonType.LessOrEqualSigned =>
|
|
|
|
lc.toShort <= rc.toShort
|
|
|
|
case ComparisonType.GreaterOrEqualSigned =>
|
|
|
|
lc.toShort >= rc.toShort
|
|
|
|
case ComparisonType.GreaterSigned =>
|
|
|
|
lc.toShort > rc.toShort
|
|
|
|
case ComparisonType.LessSigned =>
|
|
|
|
lc.toShort < rc.toShort
|
|
|
|
}) List(AssemblyLine.absolute(JMP, Label(x))) else Nil
|
|
|
|
case (_, Some(lc), _, Some(rc)) =>
|
|
|
|
// TODO: comparing late-bound constants
|
|
|
|
???
|
2018-05-14 00:18:46 +00:00
|
|
|
case (_, Some(lc), rv: VariableExpression, None) =>
|
2017-12-06 23:23:30 +00:00
|
|
|
return compileWordComparison(ctx, ComparisonType.flip(compType), rhs, lhs, branches)
|
|
|
|
case (v: VariableExpression, None, _, Some(rc)) =>
|
2017-12-23 23:08:49 +00:00
|
|
|
val lva = env.get[VariableInMemory](v.name)
|
2018-06-18 22:00:48 +00:00
|
|
|
(AssemblyLine.variable(ctx, CMP, lva, 1),
|
|
|
|
AssemblyLine.variable(ctx, CMP, lva, 0),
|
|
|
|
List(AssemblyLine.immediate(CMP, rc.hiByte.quickSimplify)),
|
|
|
|
List(AssemblyLine.immediate(CMP, rc.loByte.quickSimplify)))
|
2017-12-06 23:23:30 +00:00
|
|
|
case (lv: VariableExpression, None, rv: VariableExpression, None) =>
|
2017-12-23 23:08:49 +00:00
|
|
|
val lva = env.get[VariableInMemory](lv.name)
|
|
|
|
val rva = env.get[VariableInMemory](rv.name)
|
2018-06-18 22:00:48 +00:00
|
|
|
(AssemblyLine.variable(ctx, CMP, lva, 1),
|
|
|
|
AssemblyLine.variable(ctx, CMP, lva, 0),
|
|
|
|
AssemblyLine.variable(ctx, CMP, rva, 1),
|
|
|
|
AssemblyLine.variable(ctx, CMP, rva, 0))
|
2018-03-16 12:19:54 +00:00
|
|
|
case _ =>
|
|
|
|
// TODO comparing expressions
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.error("Too complex expressions in comparison", lhs.position)
|
2018-03-16 12:19:54 +00:00
|
|
|
(Nil, Nil, Nil, Nil)
|
|
|
|
}
|
2018-06-12 20:46:20 +00:00
|
|
|
val lType = MosExpressionCompiler.getExpressionType(ctx, lhs)
|
|
|
|
val rType = MosExpressionCompiler.getExpressionType(ctx, rhs)
|
2018-03-16 12:19:54 +00:00
|
|
|
val compactEqualityComparison = if (ctx.options.flag(CompilationFlag.OptimizeForSpeed)) {
|
|
|
|
None
|
|
|
|
} else if (lType.size == 1 && !lType.isSigned) {
|
2018-06-18 22:00:48 +00:00
|
|
|
Some(cmpTo(LDA, ll) ++ cmpTo(EOR, rl) ++ cmpTo(ORA, rh))
|
2018-03-16 12:19:54 +00:00
|
|
|
} else if (rType.size == 1 && !rType.isSigned) {
|
2018-06-18 22:00:48 +00:00
|
|
|
Some(cmpTo(LDA, rl) ++ cmpTo(EOR, ll) ++ cmpTo(ORA, lh))
|
2018-03-16 12:19:54 +00:00
|
|
|
} else {
|
|
|
|
None
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
effectiveComparisonType match {
|
|
|
|
case ComparisonType.Equal =>
|
2018-03-16 12:19:54 +00:00
|
|
|
compactEqualityComparison match {
|
|
|
|
case Some(code) => code :+ AssemblyLine.relative(BEQ, Label(x))
|
|
|
|
case None =>
|
2018-07-31 16:16:36 +00:00
|
|
|
val innerLabel = ctx.nextLabel("cp")
|
2018-06-18 22:00:48 +00:00
|
|
|
cmpTo(LDA, ll) ++
|
|
|
|
cmpTo(CMP, rl) ++
|
2018-03-16 12:19:54 +00:00
|
|
|
List(AssemblyLine.relative(BNE, innerLabel)) ++
|
2018-06-18 22:00:48 +00:00
|
|
|
cmpTo(LDA, lh) ++
|
|
|
|
cmpTo(CMP, rh) ++
|
2018-03-16 12:19:54 +00:00
|
|
|
List(
|
|
|
|
AssemblyLine.relative(BEQ, Label(x)),
|
|
|
|
AssemblyLine.label(innerLabel))
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
|
|
|
|
case ComparisonType.NotEqual =>
|
2018-03-16 12:19:54 +00:00
|
|
|
compactEqualityComparison match {
|
|
|
|
case Some(code) => code :+ AssemblyLine.relative(BNE, Label(x))
|
|
|
|
case None =>
|
2018-06-18 22:00:48 +00:00
|
|
|
cmpTo(LDA, ll) ++
|
|
|
|
cmpTo(CMP, rl) ++
|
2018-03-16 12:19:54 +00:00
|
|
|
List(AssemblyLine.relative(BNE, Label(x))) ++
|
2018-06-18 22:00:48 +00:00
|
|
|
cmpTo(LDA, lh) ++
|
|
|
|
cmpTo(CMP, rh) ++
|
2018-03-16 12:19:54 +00:00
|
|
|
List(AssemblyLine.relative(BNE, Label(x)))
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
|
|
|
|
case ComparisonType.LessUnsigned =>
|
2018-07-31 16:16:36 +00:00
|
|
|
val innerLabel = ctx.nextLabel("cp")
|
2018-06-18 22:00:48 +00:00
|
|
|
cmpTo(LDA, lh) ++
|
|
|
|
cmpTo(CMP, rh) ++
|
2017-12-23 23:08:49 +00:00
|
|
|
List(
|
|
|
|
AssemblyLine.relative(BCC, Label(x)),
|
|
|
|
AssemblyLine.relative(BNE, innerLabel)) ++
|
2018-06-18 22:00:48 +00:00
|
|
|
cmpTo(LDA, ll) ++
|
|
|
|
cmpTo(CMP, rl) ++
|
2017-12-23 23:08:49 +00:00
|
|
|
List(
|
|
|
|
AssemblyLine.relative(BCC, Label(x)),
|
|
|
|
AssemblyLine.label(innerLabel))
|
2017-12-06 23:23:30 +00:00
|
|
|
|
|
|
|
case ComparisonType.LessOrEqualUnsigned =>
|
2018-07-31 16:16:36 +00:00
|
|
|
val innerLabel = ctx.nextLabel("cp")
|
2018-06-18 22:00:48 +00:00
|
|
|
cmpTo(LDA, rh) ++
|
|
|
|
cmpTo(CMP, lh) ++
|
2017-12-23 23:08:49 +00:00
|
|
|
List(AssemblyLine.relative(BCC, innerLabel),
|
|
|
|
AssemblyLine.relative(BNE, x)) ++
|
2018-06-18 22:00:48 +00:00
|
|
|
cmpTo(LDA, rl) ++
|
|
|
|
cmpTo(CMP, ll) ++
|
2017-12-23 23:08:49 +00:00
|
|
|
List(AssemblyLine.relative(BCS, x),
|
|
|
|
AssemblyLine.label(innerLabel))
|
2017-12-06 23:23:30 +00:00
|
|
|
|
|
|
|
case ComparisonType.GreaterUnsigned =>
|
2018-07-31 16:16:36 +00:00
|
|
|
val innerLabel = ctx.nextLabel("cp")
|
2018-06-18 22:00:48 +00:00
|
|
|
cmpTo(LDA, rh) ++
|
|
|
|
cmpTo(CMP, lh) ++
|
2017-12-23 23:08:49 +00:00
|
|
|
List(AssemblyLine.relative(BCC, Label(x)),
|
|
|
|
AssemblyLine.relative(BNE, innerLabel)) ++
|
2018-06-18 22:00:48 +00:00
|
|
|
cmpTo(LDA, rl) ++
|
|
|
|
cmpTo(CMP, ll) ++
|
2017-12-23 23:08:49 +00:00
|
|
|
List(AssemblyLine.relative(BCC, Label(x)),
|
|
|
|
AssemblyLine.label(innerLabel))
|
2017-12-06 23:23:30 +00:00
|
|
|
|
|
|
|
case ComparisonType.GreaterOrEqualUnsigned =>
|
2018-07-31 16:16:36 +00:00
|
|
|
val innerLabel = ctx.nextLabel("cp")
|
2018-06-18 22:00:48 +00:00
|
|
|
cmpTo(LDA, lh) ++
|
|
|
|
cmpTo(CMP, rh) ++
|
2017-12-23 23:08:49 +00:00
|
|
|
List(AssemblyLine.relative(BCC, innerLabel),
|
|
|
|
AssemblyLine.relative(BNE, x)) ++
|
2018-06-18 22:00:48 +00:00
|
|
|
cmpTo(LDA, ll) ++
|
|
|
|
cmpTo(CMP, rl) ++
|
2017-12-23 23:08:49 +00:00
|
|
|
List(AssemblyLine.relative(BCS, x),
|
|
|
|
AssemblyLine.label(innerLabel))
|
2017-12-06 23:23:30 +00:00
|
|
|
|
2018-08-03 14:21:02 +00:00
|
|
|
case ComparisonType.LessSigned =>
|
|
|
|
val fixup = ctx.nextLabel("co")
|
|
|
|
cmpTo(LDA, ll) ++
|
|
|
|
List(AssemblyLine.implied(SEC)) ++
|
|
|
|
cmpTo(SBC, rl) ++
|
|
|
|
cmpTo(LDA, lh) ++
|
|
|
|
cmpTo(SBC, rh) ++
|
|
|
|
List(
|
|
|
|
AssemblyLine.relative(BVC, fixup),
|
|
|
|
AssemblyLine.immediate(EOR, 0x80),
|
|
|
|
AssemblyLine.label(fixup))
|
|
|
|
List(AssemblyLine.relative(BCC, x))
|
|
|
|
|
|
|
|
case ComparisonType.GreaterOrEqualSigned =>
|
|
|
|
val fixup = ctx.nextLabel("co")
|
|
|
|
cmpTo(LDA, ll) ++
|
|
|
|
List(AssemblyLine.implied(SEC)) ++
|
|
|
|
cmpTo(SBC, rl) ++
|
|
|
|
cmpTo(LDA, lh) ++
|
|
|
|
cmpTo(SBC, rh) ++
|
|
|
|
List(
|
|
|
|
AssemblyLine.relative(BVC, fixup),
|
|
|
|
AssemblyLine.immediate(EOR, 0x80),
|
|
|
|
AssemblyLine.label(fixup))
|
|
|
|
List(AssemblyLine.relative(BCS, x))
|
2017-12-06 23:23:30 +00:00
|
|
|
case _ => ???
|
2018-08-03 14:21:02 +00:00
|
|
|
// TODO: signed word comparisons: <=, >
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-16 12:19:54 +00:00
|
|
|
def compileLongComparison(ctx: CompilationContext, compType: ComparisonType.Value, lhs: Expression, rhs: Expression, size:Int, branches: BranchSpec, alreadyFlipped: Boolean = false): List[AssemblyLine] = {
|
2018-06-12 20:46:20 +00:00
|
|
|
val rType = MosExpressionCompiler.getExpressionType(ctx, rhs)
|
2018-03-16 12:19:54 +00:00
|
|
|
if (rType.size < size && rType.isSigned) {
|
|
|
|
if (alreadyFlipped) ???
|
|
|
|
else return compileLongComparison(ctx, ComparisonType.flip(compType), rhs, lhs, size, branches, alreadyFlipped = true)
|
|
|
|
}
|
|
|
|
|
|
|
|
val (effectiveComparisonType, label) = branches match {
|
|
|
|
case NoBranching => return Nil
|
|
|
|
case BranchIfTrue(x) => compType -> x
|
|
|
|
case BranchIfFalse(x) => ComparisonType.negate(compType) -> x
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: check for carry flag clobbering
|
|
|
|
val l = getLoadForEachByte(ctx, lhs, size)
|
|
|
|
val r = getLoadForEachByte(ctx, rhs, size)
|
|
|
|
|
|
|
|
val mask = (1L << (size * 8)) - 1
|
|
|
|
(ctx.env.eval(lhs), ctx.env.eval(rhs)) match {
|
|
|
|
case (Some(NumericConstant(lc, _)), Some(NumericConstant(rc, _))) =>
|
|
|
|
return if (effectiveComparisonType match {
|
|
|
|
// TODO: those masks are probably wrong
|
|
|
|
case ComparisonType.Equal =>
|
|
|
|
(lc & mask) == (rc & mask) // ??
|
|
|
|
case ComparisonType.NotEqual =>
|
|
|
|
(lc & mask) != (rc & mask) // ??
|
|
|
|
|
|
|
|
case ComparisonType.LessOrEqualUnsigned =>
|
|
|
|
(lc & mask) <= (rc & mask)
|
|
|
|
case ComparisonType.GreaterOrEqualUnsigned =>
|
|
|
|
(lc & mask) >= (rc & mask)
|
|
|
|
case ComparisonType.GreaterUnsigned =>
|
|
|
|
(lc & mask) > (rc & mask)
|
|
|
|
case ComparisonType.LessUnsigned =>
|
|
|
|
(lc & mask) < (rc & mask)
|
|
|
|
|
|
|
|
case ComparisonType.LessOrEqualSigned =>
|
|
|
|
signExtend(lc, mask) <= signExtend(lc, mask)
|
|
|
|
case ComparisonType.GreaterOrEqualSigned =>
|
|
|
|
signExtend(lc, mask) >= signExtend(lc, mask)
|
|
|
|
case ComparisonType.GreaterSigned =>
|
|
|
|
signExtend(lc, mask) > signExtend(lc, mask)
|
|
|
|
case ComparisonType.LessSigned =>
|
|
|
|
signExtend(lc, mask) < signExtend(lc, mask)
|
|
|
|
}) List(AssemblyLine.absolute(JMP, Label(label))) else Nil
|
|
|
|
case _ =>
|
|
|
|
effectiveComparisonType match {
|
|
|
|
case ComparisonType.Equal =>
|
2018-07-31 16:16:36 +00:00
|
|
|
val innerLabel = ctx.nextLabel("cp")
|
2018-03-16 12:19:54 +00:00
|
|
|
val bytewise = l.zip(r).map{
|
2018-06-18 22:00:48 +00:00
|
|
|
case (cmpL, cmpR) => cmpTo(LDA, cmpL) ++ cmpTo(CMP, cmpR)
|
2018-03-16 12:19:54 +00:00
|
|
|
}
|
|
|
|
bytewise.init.flatMap(b => b :+ AssemblyLine.relative(BNE, innerLabel)) ++ bytewise.last ++List(
|
|
|
|
AssemblyLine.relative(BEQ, Label(label)),
|
|
|
|
AssemblyLine.label(innerLabel))
|
|
|
|
case ComparisonType.NotEqual =>
|
|
|
|
l.zip(r).flatMap {
|
2018-06-18 22:00:48 +00:00
|
|
|
case (cmpL, cmpR) => cmpTo(LDA, cmpL) ++ cmpTo(CMP, cmpR) :+ AssemblyLine.relative(BNE, label)
|
2018-03-16 12:19:54 +00:00
|
|
|
}
|
|
|
|
case ComparisonType.LessUnsigned =>
|
|
|
|
val calculateCarry = AssemblyLine.implied(SEC) :: l.zip(r).flatMap{
|
2018-06-18 22:00:48 +00:00
|
|
|
case (cmpL, cmpR) => cmpTo(LDA, cmpL) ++ cmpTo(SBC, cmpR)
|
2018-03-16 12:19:54 +00:00
|
|
|
}
|
|
|
|
calculateCarry ++ List(AssemblyLine.relative(BCC, Label(label)))
|
|
|
|
case ComparisonType.GreaterOrEqualUnsigned =>
|
|
|
|
val calculateCarry = AssemblyLine.implied(SEC) :: l.zip(r).flatMap{
|
2018-06-18 22:00:48 +00:00
|
|
|
case (cmpL, cmpR) => cmpTo(LDA, cmpL) ++ cmpTo(SBC, cmpR)
|
2018-03-16 12:19:54 +00:00
|
|
|
}
|
|
|
|
calculateCarry ++ List(AssemblyLine.relative(BCS, Label(label)))
|
|
|
|
case ComparisonType.GreaterUnsigned | ComparisonType.LessOrEqualUnsigned =>
|
|
|
|
compileLongComparison(ctx, ComparisonType.flip(compType), rhs, lhs, size, branches, alreadyFlipped = true)
|
|
|
|
case _ =>
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.error("Long signed comparisons are not yet supported", lhs.position)
|
2018-03-16 12:19:54 +00:00
|
|
|
Nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private def signExtend(value: Long, mask: Long): Long = {
|
|
|
|
val masked = value & mask
|
|
|
|
if (masked > mask/2) masked | ~mask
|
|
|
|
else masked
|
|
|
|
}
|
|
|
|
|
2017-12-06 23:23:30 +00:00
|
|
|
def compileInPlaceByteMultiplication(ctx: CompilationContext, v: LhsExpression, addend: Expression): List[AssemblyLine] = {
|
|
|
|
val b = ctx.env.get[Type]("byte")
|
|
|
|
ctx.env.eval(addend) match {
|
|
|
|
case Some(NumericConstant(0, _)) =>
|
2018-06-12 20:46:20 +00:00
|
|
|
MosExpressionCompiler.compile(ctx, v, None, NoBranching) ++ (AssemblyLine.immediate(LDA, 0) :: MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v))
|
2017-12-06 23:23:30 +00:00
|
|
|
case Some(NumericConstant(1, _)) =>
|
2018-06-12 20:46:20 +00:00
|
|
|
MosExpressionCompiler.compile(ctx, v, None, NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
case Some(NumericConstant(x, _)) =>
|
2018-06-12 20:46:20 +00:00
|
|
|
compileByteMultiplication(ctx, v, x.toInt) ++ MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
|
2017-12-06 23:23:30 +00:00
|
|
|
case _ =>
|
2018-06-12 20:46:20 +00:00
|
|
|
PseudoregisterBuiltIns.compileByteMultiplication(ctx, Some(v), addend, storeInRegLo = false) ++ MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
def compileByteMultiplication(ctx: CompilationContext, v: Expression, c: Int): List[AssemblyLine] = {
|
|
|
|
val result = ListBuffer[AssemblyLine]()
|
|
|
|
// TODO: optimise
|
2017-12-16 16:55:08 +00:00
|
|
|
val addingCode = simpleOperation(ADC, ctx, v, IndexChoice.PreferY, preserveA = false, commutative = false, decimal = false)
|
2017-12-06 23:23:30 +00:00
|
|
|
val adc = addingCode.last
|
|
|
|
val indexing = addingCode.init
|
|
|
|
result ++= indexing
|
|
|
|
result += AssemblyLine.immediate(LDA, 0)
|
|
|
|
val mult = c & 0xff
|
|
|
|
var mask = 128
|
|
|
|
var empty = true
|
|
|
|
while (mask > 0) {
|
|
|
|
if (!empty) {
|
|
|
|
result += AssemblyLine.implied(ASL)
|
|
|
|
}
|
|
|
|
if ((mult & mask) != 0) {
|
|
|
|
result ++= List(AssemblyLine.implied(CLC), adc)
|
|
|
|
empty = false
|
|
|
|
}
|
|
|
|
|
|
|
|
mask >>>= 1
|
|
|
|
}
|
|
|
|
result.toList
|
|
|
|
}
|
|
|
|
|
2018-03-05 11:05:37 +00:00
|
|
|
//noinspection ZeroIndexToHead
|
2017-12-06 23:23:30 +00:00
|
|
|
def compileByteMultiplication(ctx: CompilationContext, params: List[Expression]): List[AssemblyLine] = {
|
|
|
|
val (constants, variables) = params.map(p => p -> ctx.env.eval(p)).partition(_._2.exists(_.isInstanceOf[NumericConstant]))
|
|
|
|
val constant = constants.map(_._2.get.asInstanceOf[NumericConstant].value).foldLeft(1L)(_ * _).toInt
|
|
|
|
variables.length match {
|
|
|
|
case 0 => List(AssemblyLine.immediate(LDA, constant & 0xff))
|
2018-03-05 11:05:37 +00:00
|
|
|
case 1 => compileByteMultiplication(ctx, variables.head._1, constant)
|
2017-12-06 23:23:30 +00:00
|
|
|
case 2 =>
|
2018-03-05 11:05:37 +00:00
|
|
|
if (constant == 1)
|
|
|
|
PseudoregisterBuiltIns.compileByteMultiplication(ctx, Some(variables(0)._1), variables(1)._1, storeInRegLo = false)
|
|
|
|
else
|
|
|
|
PseudoregisterBuiltIns.compileByteMultiplication(ctx, Some(variables(0)._1), variables(1)._1, storeInRegLo = true) ++
|
2018-08-03 11:06:23 +00:00
|
|
|
compileByteMultiplication(ctx, VariableExpression("__reg.b0"), constant)
|
2018-03-05 11:05:37 +00:00
|
|
|
case _ => ??? // TODO
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
def compileInPlaceByteAddition(ctx: CompilationContext, v: LhsExpression, addend: Expression, subtract: Boolean, decimal: Boolean): List[AssemblyLine] = {
|
2018-08-03 11:06:23 +00:00
|
|
|
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode) && ctx.options.zpRegisterSize < 4) {
|
|
|
|
ctx.log.error("Unsupported decimal operation. Consider increasing the size of the zeropage register.", v.position)
|
|
|
|
return compileInPlaceByteAddition(ctx, v, addend, subtract, decimal = false)
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
val env = ctx.env
|
|
|
|
val b = env.get[Type]("byte")
|
2018-02-24 23:45:25 +00:00
|
|
|
val lhsIsDirectlyIncrementable = v match {
|
2018-03-07 11:36:21 +00:00
|
|
|
case _: VariableExpression => true
|
|
|
|
case IndexedExpression(pointy, indexExpr) =>
|
|
|
|
val indexerSize = getIndexerSize(ctx, indexExpr)
|
|
|
|
indexerSize <= 1 && (env.getPointy(pointy) match {
|
|
|
|
case _: ConstantPointy => true
|
|
|
|
case _: VariablePointy => false
|
|
|
|
})
|
2018-02-24 23:45:25 +00:00
|
|
|
case _ => false
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
env.eval(addend) match {
|
2018-06-12 20:46:20 +00:00
|
|
|
case Some(NumericConstant(0, _)) => MosExpressionCompiler.compile(ctx, v, None, NoBranching)
|
2018-02-24 23:45:25 +00:00
|
|
|
case Some(NumericConstant(1, _)) if lhsIsDirectlyIncrementable && !decimal => if (subtract) {
|
2017-12-06 23:23:30 +00:00
|
|
|
simpleOperation(DEC, ctx, v, IndexChoice.RequireX, preserveA = false, commutative = true)
|
|
|
|
} else {
|
|
|
|
simpleOperation(INC, ctx, v, IndexChoice.RequireX, preserveA = false, commutative = true)
|
|
|
|
}
|
|
|
|
// TODO: compile +=2 to two INCs
|
2018-02-24 23:45:25 +00:00
|
|
|
case Some(NumericConstant(-1, _)) if lhsIsDirectlyIncrementable && !decimal => if (subtract) {
|
2017-12-06 23:23:30 +00:00
|
|
|
simpleOperation(INC, ctx, v, IndexChoice.RequireX, preserveA = false, commutative = true)
|
|
|
|
} else {
|
|
|
|
simpleOperation(DEC, ctx, v, IndexChoice.RequireX, preserveA = false, commutative = true)
|
|
|
|
}
|
|
|
|
case _ =>
|
2018-08-03 11:06:23 +00:00
|
|
|
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode)) {
|
|
|
|
val reg = ctx.env.get[MemoryVariable]("__reg")
|
|
|
|
val loadRhs = MosExpressionCompiler.compile(ctx, addend, Some(b -> ctx.env.genRelativeVariable(reg.toAddress + 3, b, zeropage = true)), NoBranching)
|
|
|
|
val loadLhs = MosExpressionCompiler.compileToA(ctx, v)
|
|
|
|
val storeLhs = MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
|
|
|
|
val subroutine = if (subtract) "__sub_decimal" else "__adc_decimal"
|
|
|
|
loadRhs ++ MosExpressionCompiler.preserveZpregIfNeededDestroyingAAndX(ctx, 3, loadLhs) ++ List(
|
|
|
|
AssemblyLine.zeropage(STA, reg, 2)) ++ (if (subtract) List(
|
|
|
|
AssemblyLine.absolute(JSR, ctx.env.get[ThingInMemory]("__sub_decimal"))
|
|
|
|
) else List(
|
|
|
|
AssemblyLine.implied(CLC),
|
|
|
|
AssemblyLine.absolute(JSR, ctx.env.get[ThingInMemory]("__adc_decimal"))
|
|
|
|
)) ++ storeLhs
|
|
|
|
} else if (!subtract && simplicity(env, v) > simplicity(env, addend)) {
|
2018-06-12 20:46:20 +00:00
|
|
|
val loadRhs = MosExpressionCompiler.compile(ctx, addend, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
|
2018-01-07 22:30:43 +00:00
|
|
|
val modifyAcc = insertBeforeLast(AssemblyLine.implied(CLC), simpleOperation(ADC, ctx, v, IndexChoice.PreferY, preserveA = true, commutative = true, decimal = decimal))
|
2018-06-12 20:46:20 +00:00
|
|
|
val storeLhs = MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
|
2018-01-07 22:30:43 +00:00
|
|
|
loadRhs ++ modifyAcc ++ storeLhs
|
2017-12-06 23:23:30 +00:00
|
|
|
} else {
|
2018-06-12 20:46:20 +00:00
|
|
|
val loadLhs = MosExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
|
2018-01-07 22:30:43 +00:00
|
|
|
val modifyLhs = if (subtract) {
|
|
|
|
insertBeforeLast(AssemblyLine.implied(SEC), simpleOperation(SBC, ctx, addend, IndexChoice.PreferY, preserveA = true, commutative = false, decimal = decimal))
|
|
|
|
} else {
|
|
|
|
insertBeforeLast(AssemblyLine.implied(CLC), simpleOperation(ADC, ctx, addend, IndexChoice.PreferY, preserveA = true, commutative = true, decimal = decimal))
|
|
|
|
}
|
2018-06-12 20:46:20 +00:00
|
|
|
val storeLhs = MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
|
2018-01-07 22:30:43 +00:00
|
|
|
loadLhs ++ modifyLhs ++ storeLhs
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-20 20:46:53 +00:00
|
|
|
private def getIndexerSize(ctx: CompilationContext, indexExpr: Expression): Int = {
|
|
|
|
ctx.env.evalVariableAndConstantSubParts(indexExpr)._1.map(v => MosExpressionCompiler.getExpressionType(ctx, v).size).sum
|
2018-03-07 11:36:21 +00:00
|
|
|
}
|
|
|
|
|
2017-12-06 23:23:30 +00:00
|
|
|
def compileInPlaceWordOrLongAddition(ctx: CompilationContext, lhs: LhsExpression, addend: Expression, subtract: Boolean, decimal: Boolean): List[AssemblyLine] = {
|
2018-08-03 11:06:23 +00:00
|
|
|
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode) && ctx.options.zpRegisterSize < 4) {
|
|
|
|
ctx.log.error("Unsupported decimal operation. Consider increasing the size of the zeropage register.", lhs.position)
|
|
|
|
return compileInPlaceWordOrLongAddition(ctx, lhs, addend, subtract, decimal = false)
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
val env = ctx.env
|
|
|
|
val b = env.get[Type]("byte")
|
|
|
|
val w = env.get[Type]("word")
|
2018-01-07 22:30:43 +00:00
|
|
|
val targetBytes: List[List[AssemblyLine]] = getStorageForEachByte(ctx, lhs)
|
2017-12-06 23:23:30 +00:00
|
|
|
val lhsIsStack = targetBytes.head.head.opcode == TSX
|
|
|
|
val targetSize = targetBytes.size
|
2018-06-12 20:46:20 +00:00
|
|
|
val addendType = MosExpressionCompiler.getExpressionType(ctx, addend)
|
2017-12-06 23:23:30 +00:00
|
|
|
var addendSize = addendType.size
|
|
|
|
|
|
|
|
def isRhsComplex(xs: List[AssemblyLine]): Boolean = xs match {
|
|
|
|
case AssemblyLine(LDA, _, _, _) :: Nil => false
|
|
|
|
case AssemblyLine(LDA, _, _, _) :: AssemblyLine(LDX, _, _, _) :: Nil => false
|
|
|
|
case _ => true
|
|
|
|
}
|
|
|
|
|
|
|
|
def isRhsStack(xs: List[AssemblyLine]): Boolean = xs.exists(_.opcode == TSX)
|
|
|
|
|
2017-12-18 16:51:48 +00:00
|
|
|
val canUseIncDec = !decimal && targetBytes.forall(_.forall(l => l.opcode != STA || (l.addrMode match {
|
|
|
|
case AddrMode.Absolute => true
|
|
|
|
case AddrMode.AbsoluteX => true
|
|
|
|
case AddrMode.ZeroPage => true
|
|
|
|
case AddrMode.ZeroPageX => true
|
|
|
|
case _ => false
|
|
|
|
})))
|
|
|
|
|
|
|
|
def doDec(lines: List[List[AssemblyLine]]):List[AssemblyLine] = lines match {
|
|
|
|
case Nil => Nil
|
|
|
|
case x :: Nil => staTo(DEC, x)
|
|
|
|
case x :: xs =>
|
2018-07-31 16:16:36 +00:00
|
|
|
val label = ctx.nextLabel("de")
|
2017-12-18 16:51:48 +00:00
|
|
|
staTo(LDA, x) ++
|
|
|
|
List(AssemblyLine.relative(BNE, label)) ++
|
|
|
|
doDec(xs) ++
|
|
|
|
List(AssemblyLine.label(label)) ++
|
|
|
|
staTo(DEC, x)
|
|
|
|
}
|
|
|
|
|
2017-12-06 23:23:30 +00:00
|
|
|
val (calculateRhs, addendByteRead0): (List[AssemblyLine], List[List[AssemblyLine]]) = env.eval(addend) match {
|
2017-12-18 16:51:48 +00:00
|
|
|
case Some(NumericConstant(0, _)) =>
|
2018-06-12 20:46:20 +00:00
|
|
|
return MosExpressionCompiler.compile(ctx, lhs, None, NoBranching)
|
2017-12-18 16:51:48 +00:00
|
|
|
case Some(NumericConstant(1, _)) if canUseIncDec && !subtract =>
|
2018-03-03 00:21:57 +00:00
|
|
|
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
|
|
|
|
targetBytes match {
|
|
|
|
case List(List(AssemblyLine(STA, ZeroPage, l, _)), List(AssemblyLine(STA, ZeroPage, h, _))) =>
|
|
|
|
if (l.+(1).quickSimplify == h) {
|
|
|
|
return List(AssemblyLine.zeropage(INC_W, l))
|
|
|
|
}
|
2018-07-24 21:14:09 +00:00
|
|
|
case _ =>
|
2018-03-03 00:21:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
|
|
|
targetBytes match {
|
|
|
|
case List(List(AssemblyLine(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l, _)), List(AssemblyLine(STA, a2, h, _))) =>
|
|
|
|
if (a1 == a2 && l.+(1).quickSimplify == h) {
|
|
|
|
return List(AssemblyLine.accu16, AssemblyLine(INC_W, a1, l), AssemblyLine.accu8)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-31 16:16:36 +00:00
|
|
|
val label = ctx.nextLabel("in")
|
2017-12-18 16:51:48 +00:00
|
|
|
return staTo(INC, targetBytes.head) ++ targetBytes.tail.flatMap(l => AssemblyLine.relative(BNE, label)::staTo(INC, l)) :+ AssemblyLine.label(label)
|
|
|
|
case Some(NumericConstant(-1, _)) if canUseIncDec && subtract =>
|
2018-03-03 00:21:57 +00:00
|
|
|
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
|
|
|
|
targetBytes match {
|
|
|
|
case List(List(AssemblyLine(STA, ZeroPage, l, _)), List(AssemblyLine(STA, ZeroPage, h, _))) =>
|
|
|
|
if (l.+(1).quickSimplify == h) {
|
|
|
|
return List(AssemblyLine.zeropage(INC_W, l))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
|
|
|
targetBytes match {
|
|
|
|
case List(List(AssemblyLine(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l, _)), List(AssemblyLine(STA, a2, h, _))) =>
|
|
|
|
if (a1 == a2 && l.+(1).quickSimplify == h) {
|
|
|
|
return List(AssemblyLine.accu16, AssemblyLine(INC_W, a1, l), AssemblyLine.accu8)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-31 16:16:36 +00:00
|
|
|
val label = ctx.nextLabel("in")
|
2017-12-18 16:51:48 +00:00
|
|
|
return staTo(INC, targetBytes.head) ++ targetBytes.tail.flatMap(l => AssemblyLine.relative(BNE, label)::staTo(INC, l)) :+ AssemblyLine.label(label)
|
|
|
|
case Some(NumericConstant(1, _)) if canUseIncDec && subtract =>
|
2018-03-03 00:21:57 +00:00
|
|
|
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
|
|
|
|
targetBytes match {
|
|
|
|
case List(List(AssemblyLine(STA, ZeroPage, l, _)), List(AssemblyLine(STA, ZeroPage, h, _))) =>
|
|
|
|
if (l.+(1).quickSimplify == h) {
|
|
|
|
return List(AssemblyLine.zeropage(DEC_W, l))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
|
|
|
targetBytes match {
|
|
|
|
case List(List(AssemblyLine(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l, _)), List(AssemblyLine(STA, a2, h, _))) =>
|
|
|
|
if (a1 == a2 && l.+(1).quickSimplify == h) {
|
|
|
|
return List(AssemblyLine.accu16, AssemblyLine(DEC_W, a1, l), AssemblyLine.accu8)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-31 16:16:36 +00:00
|
|
|
val label = ctx.nextLabel("de")
|
2017-12-18 16:51:48 +00:00
|
|
|
return doDec(targetBytes)
|
|
|
|
case Some(NumericConstant(-1, _)) if canUseIncDec && !subtract =>
|
2018-03-03 00:21:57 +00:00
|
|
|
if (ctx.options.flags(CompilationFlag.Emit65CE02Opcodes)) {
|
|
|
|
targetBytes match {
|
|
|
|
case List(List(AssemblyLine(STA, ZeroPage, l, _)), List(AssemblyLine(STA, ZeroPage, h, _))) =>
|
|
|
|
if (l.+(1).quickSimplify == h) {
|
|
|
|
return List(AssemblyLine.zeropage(DEC_W, l))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
|
|
|
targetBytes match {
|
|
|
|
case List(List(AssemblyLine(STA, a1@(ZeroPage | Absolute | ZeroPageX | AbsoluteX), l, _)), List(AssemblyLine(STA, a2, h, _))) =>
|
|
|
|
if (a1 == a2 && l.+(1).quickSimplify == h) {
|
|
|
|
return List(AssemblyLine.accu16, AssemblyLine(DEC_W, a1, l), AssemblyLine.accu8)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-31 16:16:36 +00:00
|
|
|
val label = ctx.nextLabel("de")
|
2017-12-18 16:51:48 +00:00
|
|
|
return doDec(targetBytes)
|
2017-12-06 23:23:30 +00:00
|
|
|
case Some(constant) =>
|
|
|
|
addendSize = targetSize
|
|
|
|
Nil -> List.tabulate(targetSize)(i => List(AssemblyLine.immediate(LDA, constant.subbyte(i))))
|
|
|
|
case None =>
|
|
|
|
addendSize match {
|
|
|
|
case 1 =>
|
2018-06-12 20:46:20 +00:00
|
|
|
val base = MosExpressionCompiler.compile(ctx, addend, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
if (subtract) {
|
|
|
|
if (isRhsComplex(base)) {
|
|
|
|
if (isRhsStack(base)) {
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.warn("Subtracting a stack-based value", addend.position)
|
2017-12-06 23:23:30 +00:00
|
|
|
???
|
|
|
|
}
|
|
|
|
(base ++ List(AssemblyLine.implied(PHA))) -> List(List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDA, 0x101)))
|
|
|
|
} else {
|
2018-08-03 11:06:23 +00:00
|
|
|
Nil -> base.map(l => l.copy(opcode = LDA) :: Nil)
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
base -> List(Nil)
|
|
|
|
}
|
|
|
|
case 2 =>
|
2018-06-12 20:46:20 +00:00
|
|
|
val base = MosExpressionCompiler.compile(ctx, addend, Some(MosExpressionCompiler.getExpressionType(ctx, addend) -> RegisterVariable(MosRegister.AX, w)), NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
if (isRhsStack(base)) {
|
2018-06-12 20:46:20 +00:00
|
|
|
val fixedBase = MosExpressionCompiler.compile(ctx, addend, Some(MosExpressionCompiler.getExpressionType(ctx, addend) -> RegisterVariable(MosRegister.AY, w)), NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
if (subtract) {
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.warn("Subtracting a stack-based value", addend.position)
|
2017-12-06 23:23:30 +00:00
|
|
|
if (isRhsComplex(base)) {
|
|
|
|
???
|
|
|
|
} else {
|
2018-08-03 11:06:23 +00:00
|
|
|
Nil -> fixedBase.map(l => l.copy(opcode = LDA) :: Nil)
|
2017-12-06 23:23:30 +00:00
|
|
|
???
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fixedBase -> List(Nil, List(AssemblyLine.implied(TYA)))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (subtract) {
|
|
|
|
if (isRhsComplex(base)) {
|
|
|
|
(base ++ List(
|
|
|
|
AssemblyLine.implied(PHA),
|
|
|
|
AssemblyLine.implied(TXA),
|
|
|
|
AssemblyLine.implied(PHA))
|
|
|
|
) -> List(
|
|
|
|
List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDA, 0x102)),
|
|
|
|
List(AssemblyLine.implied(TSX), AssemblyLine.absoluteX(LDA, 0x101)))
|
|
|
|
} else {
|
2018-08-03 11:06:23 +00:00
|
|
|
Nil -> base.map(l => l.copy(opcode = LDA) :: Nil)
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (lhsIsStack) {
|
2018-06-12 20:46:20 +00:00
|
|
|
val fixedBase = MosExpressionCompiler.compile(ctx, addend, Some(MosExpressionCompiler.getExpressionType(ctx, addend) -> RegisterVariable(MosRegister.AY, w)), NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
fixedBase -> List(Nil, List(AssemblyLine.implied(TYA)))
|
|
|
|
} else {
|
|
|
|
base -> List(Nil, List(AssemblyLine.implied(TXA)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-08-01 13:28:47 +00:00
|
|
|
case _ => addend match {
|
2017-12-06 23:23:30 +00:00
|
|
|
case vv: VariableExpression =>
|
|
|
|
val source = env.get[Variable](vv.name)
|
2018-08-01 13:28:47 +00:00
|
|
|
Nil -> List.tabulate(addendSize)(i => AssemblyLine.variable(ctx, LDA, source, i))
|
|
|
|
case f: FunctionCallExpression =>
|
|
|
|
val jsr = MosExpressionCompiler.compile(ctx, addend, None, BranchSpec.None)
|
|
|
|
val result = ctx.env.get[VariableInMemory](f.functionName + ".return")
|
|
|
|
jsr -> List.tabulate(addendSize)(i => AssemblyLine.variable(ctx, LDA, result, i))
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
val addendByteRead = addendByteRead0 ++ List.fill((targetSize - addendByteRead0.size) max 0)(List(AssemblyLine.immediate(LDA, 0)))
|
2018-03-03 00:21:57 +00:00
|
|
|
|
|
|
|
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
2018-03-05 23:21:43 +00:00
|
|
|
(removeTsx(targetBytes), calculateRhs, removeTsx(addendByteRead)) match {
|
|
|
|
case (
|
|
|
|
List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))),
|
|
|
|
Nil,
|
|
|
|
List(List(AssemblyLine(LDA, Immediate, al, _)), List(AssemblyLine(LDA, Immediate, ah, _)))) =>
|
2018-03-03 00:21:57 +00:00
|
|
|
if (ta1 == ta2 && tl.+(1).quickSimplify == th) {
|
|
|
|
return wrapInSedCldIfNeeded(decimal, List(
|
|
|
|
AssemblyLine.implied(if(subtract) SEC else CLC),
|
|
|
|
AssemblyLine.accu16,
|
|
|
|
AssemblyLine(LDA_W, ta1, tl),
|
|
|
|
AssemblyLine(if(subtract) SBC_W else ADC_W, WordImmediate, ah.asl(8).+(al).quickSimplify),
|
|
|
|
AssemblyLine(STA_W, ta1, tl),
|
|
|
|
AssemblyLine.accu8))
|
|
|
|
}
|
2018-03-05 23:21:43 +00:00
|
|
|
case (
|
|
|
|
List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))),
|
|
|
|
Nil,
|
|
|
|
List(List(AssemblyLine(LDA, aa1, al, _)), List(AssemblyLine(LDA, aa2, ah, _)))) =>
|
2018-03-03 00:21:57 +00:00
|
|
|
if (ta1 == ta2 && aa1 == aa2 && tl.+(1).quickSimplify == th && al.+(1).quickSimplify == ah) {
|
|
|
|
return wrapInSedCldIfNeeded(decimal, List(
|
|
|
|
AssemblyLine.accu16,
|
|
|
|
AssemblyLine.implied(if(subtract) SEC else CLC),
|
|
|
|
AssemblyLine(LDA_W, ta1, tl),
|
|
|
|
AssemblyLine(if(subtract) SBC_W else ADC_W, aa1, al),
|
|
|
|
AssemblyLine(STA_W, ta1, tl),
|
|
|
|
AssemblyLine.accu8))
|
|
|
|
}
|
2018-03-05 23:21:43 +00:00
|
|
|
case (
|
|
|
|
List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))),
|
|
|
|
List(AssemblyLine(TSX, _, _, _), AssemblyLine(LDA, AbsoluteX, NumericConstant(al, _), _), AssemblyLine(LDY, AbsoluteX, NumericConstant(ah, _), _)),
|
|
|
|
List(Nil, List(AssemblyLine(TYA, _, _, _)))) =>
|
|
|
|
if (ta1 == ta2 && tl.+(1).quickSimplify == th && al + 1 == ah) {
|
|
|
|
return wrapInSedCldIfNeeded(decimal, List(
|
|
|
|
AssemblyLine.accu16,
|
|
|
|
AssemblyLine.implied(if(subtract) SEC else CLC),
|
|
|
|
AssemblyLine(LDA_W, ta1, tl),
|
|
|
|
AssemblyLine(if(subtract) SBC_W else ADC_W, Stack, NumericConstant(al & 0xff, 1)),
|
|
|
|
AssemblyLine(STA_W, ta1, tl),
|
|
|
|
AssemblyLine.accu8))
|
|
|
|
}
|
2018-03-03 00:21:57 +00:00
|
|
|
case _ =>
|
|
|
|
}
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
val buffer = mutable.ListBuffer[AssemblyLine]()
|
|
|
|
buffer ++= calculateRhs
|
|
|
|
buffer += AssemblyLine.implied(if (subtract) SEC else CLC)
|
|
|
|
val extendMultipleBytes = targetSize > addendSize + 1
|
|
|
|
val extendAtLeastOneByte = targetSize > addendSize
|
|
|
|
for (i <- 0 until targetSize) {
|
2018-08-03 11:06:23 +00:00
|
|
|
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode)) {
|
|
|
|
val reg = ctx.env.get[VariableInMemory]("__reg")
|
|
|
|
buffer ++= staTo(LDA, targetBytes(i))
|
|
|
|
if (targetBytes(i).isEmpty) {
|
|
|
|
buffer += AssemblyLine.immediate(LDA, 0)
|
|
|
|
}
|
|
|
|
buffer += AssemblyLine.zeropage(STA, reg, 2)
|
|
|
|
// TODO: AX?
|
|
|
|
buffer ++= MosExpressionCompiler.preserveZpregIfNeededDestroyingAAndX(ctx, 2, addendByteRead(i))
|
|
|
|
buffer += AssemblyLine.zeropage(STA, reg, 3)
|
|
|
|
if (subtract) {
|
|
|
|
if (i == 0) {
|
|
|
|
buffer += AssemblyLine.absolute(JSR, env.get[ThingInMemory]("__sub_decimal"))
|
|
|
|
} else {
|
|
|
|
buffer += AssemblyLine.absolute(JSR, env.get[ThingInMemory]("__sbc_decimal"))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (i == 0) {
|
|
|
|
buffer += AssemblyLine.implied(CLC)
|
|
|
|
}
|
|
|
|
buffer += AssemblyLine.absolute(JSR, env.get[ThingInMemory]("__adc_decimal"))
|
|
|
|
}
|
|
|
|
buffer ++= targetBytes(i)
|
|
|
|
} else if (subtract) {
|
2017-12-06 23:23:30 +00:00
|
|
|
if (addendSize < targetSize && addendType.isSigned) {
|
|
|
|
// TODO: sign extension
|
|
|
|
???
|
|
|
|
}
|
|
|
|
buffer ++= staTo(LDA, targetBytes(i))
|
2018-07-24 21:14:09 +00:00
|
|
|
buffer ++= wrapInSedCldIfNeeded(decimal, ldTo(SBC, addendByteRead(i)))
|
2017-12-06 23:23:30 +00:00
|
|
|
buffer ++= targetBytes(i)
|
|
|
|
} else {
|
|
|
|
if (i >= addendSize) {
|
|
|
|
if (addendType.isSigned) {
|
2018-07-31 16:16:36 +00:00
|
|
|
val label = ctx.nextLabel("sx")
|
2017-12-06 23:23:30 +00:00
|
|
|
buffer += AssemblyLine.implied(TXA)
|
|
|
|
if (i == addendSize) {
|
|
|
|
buffer += AssemblyLine.immediate(ORA, 0x7f)
|
|
|
|
buffer += AssemblyLine.relative(BMI, label)
|
|
|
|
buffer += AssemblyLine.immediate(LDA, 0)
|
|
|
|
buffer += AssemblyLine.label(label)
|
|
|
|
if (extendMultipleBytes) buffer += AssemblyLine.implied(TAX)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
buffer += AssemblyLine.immediate(LDA, 0)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
buffer ++= addendByteRead(i)
|
|
|
|
if (addendType.isSigned && i == addendSize - 1 && extendAtLeastOneByte) {
|
|
|
|
buffer += AssemblyLine.implied(TAX)
|
|
|
|
}
|
|
|
|
}
|
2017-12-16 16:55:08 +00:00
|
|
|
buffer ++= wrapInSedCldIfNeeded(decimal, staTo(ADC, targetBytes(i)))
|
2017-12-06 23:23:30 +00:00
|
|
|
buffer ++= targetBytes(i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i <- 0 until calculateRhs.count(a => a.opcode == PHA) - calculateRhs.count(a => a.opcode == PLA)) {
|
|
|
|
buffer += AssemblyLine.implied(PLA)
|
|
|
|
}
|
2017-12-16 16:55:08 +00:00
|
|
|
buffer.toList
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
def compileInPlaceByteBitOp(ctx: CompilationContext, v: LhsExpression, param: Expression, operation: Opcode.Value): List[AssemblyLine] = {
|
|
|
|
val env = ctx.env
|
|
|
|
val b = env.get[Type]("byte")
|
|
|
|
(operation, env.eval(param)) match {
|
|
|
|
case (EOR, Some(NumericConstant(0, _)))
|
|
|
|
| (ORA, Some(NumericConstant(0, _)))
|
|
|
|
| (AND, Some(NumericConstant(0xff, _)))
|
|
|
|
| (AND, Some(NumericConstant(-1, _))) =>
|
2018-08-06 17:17:57 +00:00
|
|
|
MosExpressionCompiler.compile(ctx, v, None, NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
case _ =>
|
2018-01-07 22:30:43 +00:00
|
|
|
if (simplicity(env, v) > simplicity(env, param)) {
|
2018-06-12 20:46:20 +00:00
|
|
|
val loadRhs = MosExpressionCompiler.compile(ctx, param, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
|
2018-01-07 22:30:43 +00:00
|
|
|
val modifyAcc = simpleOperation(operation, ctx, v, IndexChoice.PreferY, preserveA = true, commutative = true)
|
2018-06-12 20:46:20 +00:00
|
|
|
val storeLhs = MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
|
2018-01-07 22:30:43 +00:00
|
|
|
loadRhs ++ modifyAcc ++ storeLhs
|
|
|
|
} else {
|
2018-06-12 20:46:20 +00:00
|
|
|
val loadLhs = MosExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
|
2018-01-07 22:30:43 +00:00
|
|
|
val modifyLhs = simpleOperation(operation, ctx, param, IndexChoice.PreferY, preserveA = true, commutative = true)
|
2018-06-12 20:46:20 +00:00
|
|
|
val storeLhs = MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, v)
|
2018-01-07 22:30:43 +00:00
|
|
|
loadLhs ++ modifyLhs ++ storeLhs
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def compileInPlaceWordOrLongBitOp(ctx: CompilationContext, lhs: LhsExpression, param: Expression, operation: Opcode.Value): List[AssemblyLine] = {
|
|
|
|
val env = ctx.env
|
|
|
|
val b = env.get[Type]("byte")
|
|
|
|
val w = env.get[Type]("word")
|
2018-01-07 22:30:43 +00:00
|
|
|
val targetBytes: List[List[AssemblyLine]] = getStorageForEachByte(ctx, lhs)
|
2017-12-06 23:23:30 +00:00
|
|
|
val lo = targetBytes.head
|
|
|
|
val targetSize = targetBytes.size
|
2018-06-12 20:46:20 +00:00
|
|
|
val paramType = MosExpressionCompiler.getExpressionType(ctx, param)
|
2017-12-06 23:23:30 +00:00
|
|
|
var paramSize = paramType.size
|
|
|
|
val extendMultipleBytes = targetSize > paramSize + 1
|
|
|
|
val extendAtLeastOneByte = targetSize > paramSize
|
|
|
|
val (calculateRhs, addendByteRead) = env.eval(param) match {
|
|
|
|
case Some(constant) =>
|
|
|
|
paramSize = targetSize
|
|
|
|
Nil -> List.tabulate(targetSize)(i => List(AssemblyLine.immediate(LDA, constant.subbyte(i))))
|
|
|
|
case None =>
|
|
|
|
paramSize match {
|
|
|
|
case 1 =>
|
2018-06-12 20:46:20 +00:00
|
|
|
val base = MosExpressionCompiler.compile(ctx, param, Some(b -> RegisterVariable(MosRegister.A, b)), NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
base -> List(Nil)
|
|
|
|
case 2 =>
|
2018-06-12 20:46:20 +00:00
|
|
|
val base = MosExpressionCompiler.compile(ctx, param, Some(MosExpressionCompiler.getExpressionType(ctx, param) -> RegisterVariable(MosRegister.AX, w)), NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
base -> List(Nil, List(AssemblyLine.implied(TXA)))
|
2018-08-01 13:28:47 +00:00
|
|
|
case _ => param match {
|
2017-12-06 23:23:30 +00:00
|
|
|
case vv: VariableExpression =>
|
|
|
|
val source = env.get[Variable](vv.name)
|
2018-08-01 13:28:47 +00:00
|
|
|
Nil -> List.tabulate(paramSize)(i => AssemblyLine.variable(ctx, LDA, source, i))
|
|
|
|
case f: FunctionCallExpression =>
|
|
|
|
val jsr = MosExpressionCompiler.compile(ctx, param, None, BranchSpec.None)
|
|
|
|
val result = ctx.env.get[VariableInMemory](f.functionName + ".return")
|
|
|
|
jsr -> List.tabulate(paramSize)(i => AssemblyLine.variable(ctx, LDA, result, i))
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|
|
|
|
}
|
2018-03-03 00:21:57 +00:00
|
|
|
if (ctx.options.flags(CompilationFlag.EmitNative65816Opcodes)) {
|
|
|
|
(removeTsx(targetBytes), removeTsx(addendByteRead)) match {
|
|
|
|
case (List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))), List(List(AssemblyLine(LDA, Immediate, al, _)), List(AssemblyLine(LDA, Immediate, ah, _)))) =>
|
|
|
|
if (ta1 == ta2 && tl.+(1).quickSimplify == th) {
|
|
|
|
return List(
|
|
|
|
AssemblyLine.accu16,
|
|
|
|
AssemblyLine(LDA_W, ta1, tl),
|
|
|
|
AssemblyLine(Opcode.widen(operation).get, WordImmediate, ah.asl(8).+(al).quickSimplify),
|
|
|
|
AssemblyLine(STA_W, ta1, tl),
|
|
|
|
AssemblyLine.accu8)
|
|
|
|
}
|
|
|
|
case (List(List(AssemblyLine(STA, ta1, tl, _)), List(AssemblyLine(STA, ta2, th, _))), List(List(AssemblyLine(LDA, aa1, al, _)), List(AssemblyLine(LDA, aa2, ah, _)))) =>
|
|
|
|
if (ta1 == ta2 && aa1 == aa2 && tl.+(1).quickSimplify == th && al.+(1).quickSimplify == ah) {
|
|
|
|
return List(
|
|
|
|
AssemblyLine.accu16,
|
|
|
|
AssemblyLine(LDA_W, ta1, tl),
|
|
|
|
AssemblyLine(Opcode.widen(operation).get, aa1, al),
|
|
|
|
AssemblyLine(STA_W, ta1, tl),
|
|
|
|
AssemblyLine.accu8)
|
|
|
|
}
|
|
|
|
case _ =>
|
|
|
|
}
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
val AllOnes = (1L << (8 * targetSize)) - 1
|
|
|
|
(operation, env.eval(param)) match {
|
|
|
|
case (EOR, Some(NumericConstant(0, _)))
|
|
|
|
| (ORA, Some(NumericConstant(0, _)))
|
|
|
|
| (AND, Some(NumericConstant(AllOnes, _))) =>
|
2018-06-12 20:46:20 +00:00
|
|
|
MosExpressionCompiler.compile(ctx, lhs, None, NoBranching)
|
2017-12-06 23:23:30 +00:00
|
|
|
case _ =>
|
|
|
|
val buffer = mutable.ListBuffer[AssemblyLine]()
|
|
|
|
buffer ++= calculateRhs
|
|
|
|
for (i <- 0 until targetSize) {
|
|
|
|
if (i < paramSize) {
|
|
|
|
buffer ++= addendByteRead(i)
|
|
|
|
if (paramType.isSigned && i == paramSize - 1 && extendAtLeastOneByte) {
|
|
|
|
buffer += AssemblyLine.implied(TAX)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (paramType.isSigned) {
|
2018-07-31 16:16:36 +00:00
|
|
|
val label = ctx.nextLabel("sx")
|
2017-12-06 23:23:30 +00:00
|
|
|
buffer += AssemblyLine.implied(TXA)
|
|
|
|
if (i == paramSize) {
|
|
|
|
buffer += AssemblyLine.immediate(ORA, 0x7f)
|
|
|
|
buffer += AssemblyLine.relative(BMI, label)
|
|
|
|
buffer += AssemblyLine.immediate(LDA, 0)
|
|
|
|
buffer += AssemblyLine.label(label)
|
|
|
|
if (extendMultipleBytes) buffer += AssemblyLine.implied(TAX)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
buffer += AssemblyLine.immediate(LDA, 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buffer ++= staTo(operation, targetBytes(i))
|
|
|
|
buffer ++= targetBytes(i)
|
|
|
|
}
|
|
|
|
for (i <- 0 until calculateRhs.count(a => a.opcode == PHA) - calculateRhs.count(a => a.opcode == PLA)) {
|
|
|
|
buffer += AssemblyLine.implied(PLA)
|
|
|
|
}
|
|
|
|
buffer.toList
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-03 11:06:23 +00:00
|
|
|
def getStorageForEachByte(ctx: CompilationContext, lhs: LhsExpression): List[List[AssemblyLine]] = {
|
2018-01-07 22:30:43 +00:00
|
|
|
val env = ctx.env
|
|
|
|
lhs match {
|
|
|
|
case v: VariableExpression =>
|
|
|
|
val variable = env.get[Variable](v.name)
|
|
|
|
List.tabulate(variable.typ.size) { i => AssemblyLine.variable(ctx, STA, variable, i) }
|
|
|
|
case IndexedExpression(variable, index) =>
|
2018-06-12 20:46:20 +00:00
|
|
|
List(MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, lhs))
|
2018-01-07 22:30:43 +00:00
|
|
|
case SeparateBytesExpression(h: LhsExpression, l: LhsExpression) =>
|
2018-01-08 00:03:08 +00:00
|
|
|
if (simplicity(ctx.env, h) < 'J' || simplicity(ctx.env, l) < 'J') {
|
|
|
|
// a[b]:c[d] is the most complex expression that doesn't cause the following warning
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.warn("Too complex expression given to the `:` operator, generated code might be wrong", lhs.position)
|
2018-01-08 00:03:08 +00:00
|
|
|
}
|
2018-01-07 22:30:43 +00:00
|
|
|
List(
|
|
|
|
getStorageForEachByte(ctx, l).head,
|
2018-06-12 20:46:20 +00:00
|
|
|
MosExpressionCompiler.preserveRegisterIfNeeded(ctx, MosRegister.A, getStorageForEachByte(ctx, h).head))
|
2018-01-07 22:30:43 +00:00
|
|
|
case _ =>
|
|
|
|
???
|
|
|
|
}
|
|
|
|
}
|
2018-03-16 12:19:54 +00:00
|
|
|
private def getLoadForEachByte(ctx: CompilationContext, expr: Expression, size: Int): List[List[AssemblyLine]] = {
|
|
|
|
val env = ctx.env
|
|
|
|
env.eval(expr) match {
|
|
|
|
case Some(c) =>
|
2018-06-18 22:00:48 +00:00
|
|
|
List.tabulate(size) { i => List(AssemblyLine.immediate(CMP, c.subbyte(i))) }
|
2018-03-16 12:19:54 +00:00
|
|
|
case None =>
|
|
|
|
expr match {
|
|
|
|
case v: VariableExpression =>
|
|
|
|
val variable = env.get[Variable](v.name)
|
|
|
|
List.tabulate(size) { i =>
|
|
|
|
if (i < variable.typ.size) {
|
2018-06-18 22:00:48 +00:00
|
|
|
AssemblyLine.variable(ctx, CMP, variable, i)
|
2018-03-16 12:19:54 +00:00
|
|
|
} else if (variable.typ.isSigned) {
|
2018-07-31 16:16:36 +00:00
|
|
|
val label = ctx.nextLabel("sx")
|
2018-06-18 22:00:48 +00:00
|
|
|
AssemblyLine.variable(ctx, LDA, variable, variable.typ.size - 1) ++ List(
|
2018-03-16 12:19:54 +00:00
|
|
|
AssemblyLine.immediate(ORA, 0x7F),
|
|
|
|
AssemblyLine.relative(BMI, label),
|
2018-06-18 22:00:48 +00:00
|
|
|
AssemblyLine.immediate(CMP, 0),
|
2018-03-16 12:19:54 +00:00
|
|
|
AssemblyLine.label(label))
|
2018-06-18 22:00:48 +00:00
|
|
|
} else List(AssemblyLine.immediate(CMP, 0))
|
2018-03-16 12:19:54 +00:00
|
|
|
}
|
|
|
|
case expr@IndexedExpression(variable, index) =>
|
|
|
|
List.tabulate(size) { i =>
|
2018-06-12 20:46:20 +00:00
|
|
|
if (i == 0) MosExpressionCompiler.compileByteStorage(ctx, MosRegister.A, expr)
|
2018-06-18 22:00:48 +00:00
|
|
|
else List(AssemblyLine.immediate(CMP, 0))
|
2018-03-16 12:19:54 +00:00
|
|
|
}
|
|
|
|
case SeparateBytesExpression(h: LhsExpression, l: LhsExpression) =>
|
|
|
|
if (simplicity(ctx.env, h) < 'J' || simplicity(ctx.env, l) < 'J') {
|
|
|
|
// a[b]:c[d] is the most complex expression that doesn't cause the following warning
|
2018-07-30 16:15:44 +00:00
|
|
|
ctx.log.warn("Too complex expression given to the `:` operator, generated code might be wrong", expr.position)
|
2018-03-16 12:19:54 +00:00
|
|
|
}
|
|
|
|
List.tabulate(size) { i =>
|
|
|
|
if (i == 0) getStorageForEachByte(ctx, l).head
|
2018-06-12 20:46:20 +00:00
|
|
|
else if (i == 1) MosExpressionCompiler.preserveRegisterIfNeeded(ctx, MosRegister.A, getStorageForEachByte(ctx, h).head)
|
2018-06-18 22:00:48 +00:00
|
|
|
else List(AssemblyLine.immediate(CMP, 0))
|
2018-03-16 12:19:54 +00:00
|
|
|
}
|
|
|
|
case _ =>
|
|
|
|
???
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-03 00:21:57 +00:00
|
|
|
|
|
|
|
private def removeTsx(codes: List[List[AssemblyLine]]): List[List[AssemblyLine]] = codes.map {
|
|
|
|
case List(AssemblyLine(TSX, _, _, _), AssemblyLine(op, AbsoluteX, NumericConstant(nn, _), _)) if nn >= 0x100 && nn <= 0x1ff =>
|
|
|
|
List(AssemblyLine(op, Stack, NumericConstant(nn & 0xff, 1)))
|
|
|
|
case x => x
|
|
|
|
}
|
2017-12-06 23:23:30 +00:00
|
|
|
}
|