mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-09 13:31:32 +00:00
Indexing fixes; 16-bit indexing
This commit is contained in:
parent
11337f4975
commit
1d865302ca
@ -172,6 +172,7 @@ object OptimizationPresets {
|
||||
AlwaysGoodOptimizations.PointlessStackStashing,
|
||||
AlwaysGoodOptimizations.PointlessStashingToIndexOverShortSafeBranch,
|
||||
AlwaysGoodOptimizations.PoinlessStoreBeforeStore,
|
||||
AlwaysGoodOptimizations.PointlessStoreToTheSameVariable,
|
||||
AlwaysGoodOptimizations.RearrangableLoadFromTheSameLocation,
|
||||
AlwaysGoodOptimizations.RearrangeMath,
|
||||
AlwaysGoodOptimizations.RemoveNops,
|
||||
|
@ -156,6 +156,57 @@ object AlwaysGoodOptimizations {
|
||||
(MatchParameter(1) & MatchAddrMode(2) & Set(STA, SAX, STX, STZ)) ~~> (_.tail),
|
||||
)
|
||||
|
||||
private def pointlessNonimmediateStoreToTheSameVariable(ld1: Set[Opcode.Value], st1: Opcode.Value, ld2: Opcode.Value, st2: Opcode.Value) = {
|
||||
(HasOpcodeIn(ld1) & Not(HasAddrMode(Immediate)) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(HasOpcode(st1) & MatchAddrMode(2) & MatchParameter(3)) ~
|
||||
(Linear & DoesntChangeIndexingInAddrMode(0) & DoesntChangeIndexingInAddrMode(2) &
|
||||
DoesNotConcernMemoryAt(0, 1) & DoesntChangeMemoryAt(2, 3)).* ~
|
||||
(Elidable & HasOpcode(ld2) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
(Elidable & HasOpcode(st2) & MatchAddrMode(2) & MatchParameter(3)) ~~> (code => code.init),
|
||||
}
|
||||
|
||||
private def pointlessImmediateStoreToTheSameVariable(match1: AssemblyLinePattern, st1: Opcode.Value, match2: AssemblyLinePattern, st2: Opcode.Value) = {
|
||||
(HasOpcode(st1) & MatchAddrMode(2) & MatchParameter(3) & match1) ~
|
||||
(Linear & DoesntChangeIndexingInAddrMode(2) & (
|
||||
DoesntChangeMemoryAt(2, 3) |
|
||||
HasAddrModeIn(Set(IndexedX, IndexedY, IndexedZ, LongIndexedY, LongIndexedZ, Indirect)) &
|
||||
MatchParameter(3) & HasParameterWhere({
|
||||
case MemoryAddressConstant(th) => th.name.startsWith("__")
|
||||
case _ => false
|
||||
})
|
||||
)).* ~
|
||||
(Elidable & match2 & HasOpcode(st2) & MatchAddrMode(2) & MatchParameter(3)) ~~> (code => code.init),
|
||||
}
|
||||
|
||||
val PointlessStoreToTheSameVariable = new RuleBasedAssemblyOptimization("Pointless store to the same variable",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
|
||||
pointlessNonimmediateStoreToTheSameVariable(Set(LDA, LAX), STA, LDA, STA),
|
||||
pointlessNonimmediateStoreToTheSameVariable(Set(LDA, LAX), STX, LDA, STA),
|
||||
pointlessNonimmediateStoreToTheSameVariable(Set(LDY), STY, LDA, STA),
|
||||
|
||||
pointlessNonimmediateStoreToTheSameVariable(Set(LDA, LAX), STA, LDX, STX),
|
||||
pointlessNonimmediateStoreToTheSameVariable(Set(LDA, LAX), STX, LDX, STX),
|
||||
pointlessNonimmediateStoreToTheSameVariable(Set(LDY), STY, LDX, STX),
|
||||
|
||||
pointlessNonimmediateStoreToTheSameVariable(Set(LDA, LAX), STA, LDY, STY),
|
||||
pointlessNonimmediateStoreToTheSameVariable(Set(LDA, LAX), STX, LDY, STY),
|
||||
pointlessNonimmediateStoreToTheSameVariable(Set(LDY), STY, LDY, STY),
|
||||
|
||||
pointlessImmediateStoreToTheSameVariable(MatchA(0), STA, MatchA(0), STA),
|
||||
pointlessImmediateStoreToTheSameVariable(MatchX(0), STX, MatchA(0), STA),
|
||||
pointlessImmediateStoreToTheSameVariable(MatchY(0), STY, MatchA(0), STA),
|
||||
|
||||
pointlessImmediateStoreToTheSameVariable(MatchA(0), STA, MatchX(0), STX),
|
||||
pointlessImmediateStoreToTheSameVariable(MatchX(0), STX, MatchX(0), STX),
|
||||
pointlessImmediateStoreToTheSameVariable(MatchY(0), STY, MatchX(0), STX),
|
||||
|
||||
pointlessImmediateStoreToTheSameVariable(MatchA(0), STA, MatchY(0), STY),
|
||||
pointlessImmediateStoreToTheSameVariable(MatchX(0), STX, MatchY(0), STY),
|
||||
pointlessImmediateStoreToTheSameVariable(MatchY(0), STY, MatchY(0), STY),
|
||||
|
||||
)
|
||||
|
||||
val PointlessLoadBeforeReturn = new RuleBasedAssemblyOptimization("Pointless load before return",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
(Set(LDA, TXA, TYA, EOR, AND, ORA, ANC) & Elidable) ~ (LinearOrLabel & Not(ConcernsA) & Not(ReadsNOrZ) & Not(HasOpcode(DISCARD_AF))).* ~ HasOpcode(DISCARD_AF) ~~> (_.tail),
|
||||
|
@ -437,6 +437,12 @@ trait TrivialAssemblyLinePattern extends AssemblyLinePattern with (AssemblyLine
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = this (line)
|
||||
}
|
||||
|
||||
case class Match(predicate: (AssemblyMatchingContext => Boolean)) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean = predicate(ctx)
|
||||
|
||||
override def toString: String = "Match(...)"
|
||||
}
|
||||
|
||||
case class WhereNoMemoryAccessOverlapBetweenTwoLineLists(ix1: Int, ix2: Int) extends AssemblyPattern {
|
||||
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, AssemblyLine)]): Option[List[(FlowInfo, AssemblyLine)]] = {
|
||||
val s1s = ctx.get[List[AssemblyLine]](ix1)
|
||||
@ -871,6 +877,10 @@ case class HasImmediateWhere(predicate: Int => Boolean) extends TrivialAssemblyL
|
||||
})
|
||||
}
|
||||
|
||||
case class HasParameterWhere(predicate: Constant => Boolean) extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean = predicate(line.parameter)
|
||||
}
|
||||
|
||||
case class MatchObject(i: Int, f: Function[AssemblyLine, Any]) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
ctx.addObject(i, f(line))
|
||||
|
@ -46,26 +46,26 @@ object BuiltIns {
|
||||
}
|
||||
Nil -> AssemblyLine.variable(ctx, opcode, v)
|
||||
case IndexedExpression(arrayName, index) =>
|
||||
indexChoice match {
|
||||
case IndexChoice.RequireX | IndexChoice.PreferX =>
|
||||
val array = env.getArrayOrPointer(arrayName)
|
||||
val calculateIndex = ExpressionCompiler.compile(ctx, index, Some(b -> RegisterVariable(Register.X, b)), NoBranching)
|
||||
val baseAddress = array match {
|
||||
case c: ConstantThing => c.value
|
||||
case a: MfArray => a.toAddress
|
||||
}
|
||||
calculateIndex -> List(AssemblyLine.absoluteX(opcode, baseAddress))
|
||||
case IndexChoice.PreferY =>
|
||||
val array = env.getArrayOrPointer(arrayName)
|
||||
val calculateIndex = ExpressionCompiler.compile(ctx, index, Some(b -> RegisterVariable(Register.Y, b)), NoBranching)
|
||||
array match {
|
||||
case c: ConstantThing =>
|
||||
calculateIndex -> List(AssemblyLine.absoluteY(opcode, c.value))
|
||||
case a: MfArray =>
|
||||
calculateIndex -> List(AssemblyLine.absoluteY(opcode, a.toAddress))
|
||||
case v: VariableInMemory if v.typ.name == "pointer" =>
|
||||
calculateIndex -> List(AssemblyLine.indexedY(opcode, v.toAddress))
|
||||
}
|
||||
val pointy = env.getPointy(arrayName)
|
||||
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)) =>
|
||||
ExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(Register.X, b)), NoBranching) -> List(AssemblyLine.absoluteX(opcode, p.value + constantPart))
|
||||
case (p: ConstantPointy, _, 1, IndexChoice.PreferY, Some(v)) =>
|
||||
ExpressionCompiler.compile(ctx, v, Some(b -> RegisterVariable(Register.Y, b)), NoBranching) -> List(AssemblyLine.absoluteY(opcode, p.value + constantPart))
|
||||
case (p: VariablePointy, 0 | 1, _, IndexChoice.PreferX | IndexChoice.PreferY, _) =>
|
||||
ExpressionCompiler.compile(ctx, index, Some(b -> RegisterVariable(Register.Y, b)), NoBranching) -> List(AssemblyLine.indexedY(opcode, p.addr))
|
||||
case (p: ConstantPointy, _, 2, IndexChoice.PreferX | IndexChoice.PreferY, Some(v)) =>
|
||||
ExpressionCompiler.prepareWordIndexing(ctx, p, index) -> List(AssemblyLine.indexedY(opcode, env.get[VariableInMemory]("__reg")))
|
||||
case (p: VariablePointy, 2, _, IndexChoice.PreferX | IndexChoice.PreferY, _) =>
|
||||
ExpressionCompiler.prepareWordIndexing(ctx, p, index) -> List(AssemblyLine.indexedY(opcode, env.get[VariableInMemory]("__reg")))
|
||||
case _ =>
|
||||
ErrorReporting.error("Invalid index for simple operation argument", index.position)
|
||||
Nil -> Nil
|
||||
}
|
||||
case FunctionCallExpression(name, List(param)) if env.maybeGet[Type](name).isDefined =>
|
||||
return simpleOperation(opcode, ctx, param, indexChoice, preserveA, commutative, decimal)
|
||||
@ -154,7 +154,7 @@ object BuiltIns {
|
||||
case None => expr match {
|
||||
case VariableExpression(_) => 'V'
|
||||
case IndexedExpression(_, LiteralExpression(_, _)) => 'K'
|
||||
case IndexedExpression(_, VariableExpression(_)) => 'J'
|
||||
case IndexedExpression(_, VariableExpression(v)) if env.get[Variable](v).typ.size == 1 => 'J'
|
||||
case IndexedExpression(_, _) => 'I'
|
||||
case _ => 'A'
|
||||
}
|
||||
@ -165,19 +165,7 @@ object BuiltIns {
|
||||
def compileBitOps(opcode: Opcode.Value, ctx: CompilationContext, params: List[Expression]): List[AssemblyLine] = {
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
|
||||
val sortedParams = params.sortBy { expr =>
|
||||
ctx.env.eval(expr) match {
|
||||
case Some(NumericConstant(_, _)) => "Z"
|
||||
case Some(_) => "Y"
|
||||
case None => expr match {
|
||||
case VariableExpression(_) => "V"
|
||||
case IndexedExpression(_, LiteralExpression(_, _)) => "K"
|
||||
case IndexedExpression(_, VariableExpression(_)) => "J"
|
||||
case IndexedExpression(_, _) => "I"
|
||||
case _ => "A"
|
||||
}
|
||||
}
|
||||
}
|
||||
val sortedParams = params.sortBy { expr => simplicity(ctx.env, expr) }
|
||||
|
||||
val h = sortedParams.head
|
||||
val firstParamCompiled = ExpressionCompiler.compile(ctx, h, Some(b -> RegisterVariable(Register.A, b)), NoBranching)
|
||||
@ -578,10 +566,12 @@ object BuiltIns {
|
||||
val b = env.get[Type]("byte")
|
||||
val lhsIsDirectlyIncrementable = v match {
|
||||
case _: VariableExpression => true
|
||||
case IndexedExpression(pointy, _) => env.getPointy(pointy) match {
|
||||
case IndexedExpression(pointy, indexExpr) =>
|
||||
val indexerSize = getIndexerSize(ctx, indexExpr)
|
||||
indexerSize <= 1 && (env.getPointy(pointy) match {
|
||||
case _: ConstantPointy => true
|
||||
case _: VariablePointy => false
|
||||
}
|
||||
})
|
||||
case _ => false
|
||||
}
|
||||
env.eval(addend) match {
|
||||
@ -616,6 +606,10 @@ object BuiltIns {
|
||||
}
|
||||
}
|
||||
|
||||
private def getIndexerSize(ctx: CompilationContext, indexExpr: Expression) = {
|
||||
ctx.env.evalVariableAndConstantSubParts(indexExpr)._1.map(v => ExpressionCompiler.getExpressionType(ctx, v)).size
|
||||
}
|
||||
|
||||
def compileInPlaceWordOrLongAddition(ctx: CompilationContext, lhs: LhsExpression, addend: Expression, subtract: Boolean, decimal: Boolean): List[AssemblyLine] = {
|
||||
if (decimal && !ctx.options.flag(CompilationFlag.DecimalMode)) {
|
||||
ErrorReporting.warn("Unsupported decimal operation", ctx.options, lhs.position)
|
||||
|
@ -209,6 +209,34 @@ object ExpressionCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
def prepareWordIndexing(ctx: CompilationContext, pointy: Pointy, indexExpression: Expression): List[AssemblyLine] = {
|
||||
val w = ctx.env.get[Type]("word")
|
||||
if (!ctx.options.flag(CompilationFlag.ZeropagePseudoregister)) {
|
||||
ErrorReporting.error("16-bit indexing requires a zeropage pseudoregister")
|
||||
compile(ctx, indexExpression, Some(w -> RegisterVariable(Register.YA, w)), BranchSpec.None)
|
||||
return Nil
|
||||
}
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val compileIndex = compile(ctx, indexExpression, Some(w -> RegisterVariable(Register.YA, w)), BranchSpec.None)
|
||||
val prepareRegister = pointy match {
|
||||
case ConstantPointy(addr, _) =>
|
||||
List(
|
||||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.immediate(ADC, addr.hiByte),
|
||||
AssemblyLine.zeropage(STA, reg, 1),
|
||||
AssemblyLine.immediate(LDA, addr.loByte),
|
||||
AssemblyLine.zeropage(STA, reg))
|
||||
case VariablePointy(addr) =>
|
||||
List(
|
||||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.zeropage(ADC, addr + 1),
|
||||
AssemblyLine.zeropage(STA, reg, 1),
|
||||
AssemblyLine.zeropage(LDA, addr),
|
||||
AssemblyLine.zeropage(STA, reg))
|
||||
}
|
||||
compileIndex ++ prepareRegister
|
||||
}
|
||||
|
||||
def compileByteStorage(ctx: CompilationContext, register: Register.Value, target: LhsExpression): List[AssemblyLine] = {
|
||||
val env = ctx.env
|
||||
val b = env.get[Type]("byte")
|
||||
@ -253,6 +281,8 @@ object ExpressionCompiler {
|
||||
case IndexedExpression(arrayName, indexExpr) =>
|
||||
val pointy = env.getPointy(arrayName)
|
||||
val (variableIndex, constIndex) = env.evalVariableAndConstantSubParts(indexExpr)
|
||||
val variableIndexSize = variableIndex.map(v => getExpressionType(ctx, v).size).getOrElse(0)
|
||||
val totalIndexSize = getExpressionType(ctx, indexExpr).size
|
||||
|
||||
def storeToArrayAtUnknownIndex(variableIndex: Expression, arrayAddr: Constant) = {
|
||||
// TODO check typ
|
||||
@ -274,13 +304,40 @@ object ExpressionCompiler {
|
||||
}
|
||||
}
|
||||
}
|
||||
(pointy, variableIndex) match {
|
||||
case (p: ConstantPointy, None) =>
|
||||
def wrapWordIndexingStorage(code: List[AssemblyLine]) = {
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val cmos = ctx.options.flag(CompilationFlag.EmitCmosOpcodes)
|
||||
register match {
|
||||
case Register.A =>
|
||||
List(AssemblyLine.implied(PHA)) ++ code ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
case Register.X =>
|
||||
if (code.exists(l => OpcodeClasses.ChangesX(l.opcode))) {
|
||||
if (cmos)
|
||||
List(AssemblyLine.implied(PHX)) ++ code ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
else
|
||||
List(AssemblyLine.implied(TXA), AssemblyLine.implied(PHA)) ++ code ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
} else {
|
||||
code ++ List(AssemblyLine.implied(TXA), AssemblyLine.indexedY(STA, reg))
|
||||
}
|
||||
case Register.Y =>
|
||||
if (cmos)
|
||||
List(AssemblyLine.implied(PHY)) ++ code ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
else
|
||||
List(AssemblyLine.implied(TYA), AssemblyLine.implied(PHA)) ++ code ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
}
|
||||
}
|
||||
|
||||
(pointy, variableIndex, variableIndexSize, totalIndexSize) match {
|
||||
case (p: ConstantPointy, None, _, _) =>
|
||||
List(AssemblyLine.absolute(store, env.genRelativeVariable(p.value + constIndex, b, zeropage = false)))
|
||||
case (p: ConstantPointy, Some(v)) =>
|
||||
case (p: VariablePointy, _, _, 2) =>
|
||||
wrapWordIndexingStorage(prepareWordIndexing(ctx, p, indexExpr))
|
||||
case (p: ConstantPointy, Some(v), 2, _) =>
|
||||
wrapWordIndexingStorage(prepareWordIndexing(ctx, ConstantPointy(p.value + constIndex, if (constIndex.isProvablyZero) p.size else None), v))
|
||||
case (p: ConstantPointy, Some(v), 1, _) =>
|
||||
storeToArrayAtUnknownIndex(v, p.value)
|
||||
//TODO: should there be a type check or a zeropage check?
|
||||
case (pointerVariable:VariablePointy, None) =>
|
||||
case (pointerVariable:VariablePointy, None, _, 0 | 1) =>
|
||||
register match {
|
||||
case Register.A =>
|
||||
List(AssemblyLine.immediate(LDY, constIndex), AssemblyLine.indexedY(STA, pointerVariable.addr))
|
||||
@ -292,7 +349,7 @@ object ExpressionCompiler {
|
||||
ErrorReporting.error("Cannot store a word in an array", target.position)
|
||||
Nil
|
||||
}
|
||||
case (pointerVariable:VariablePointy, Some(_)) =>
|
||||
case (pointerVariable:VariablePointy, Some(_), _, 0 | 1) =>
|
||||
val calculatingIndex = compile(ctx, indexExpr, Some(b, RegisterVariable(Register.Y, b)), NoBranching)
|
||||
register match {
|
||||
case Register.A =>
|
||||
@ -307,6 +364,9 @@ object ExpressionCompiler {
|
||||
ErrorReporting.error("Cannot store a word in an array", target.position)
|
||||
Nil
|
||||
}
|
||||
case _ =>
|
||||
ErrorReporting.error("Invalid index for writing", indexExpr.position)
|
||||
Nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -570,6 +630,8 @@ object ExpressionCompiler {
|
||||
val pointy = env.getPointy(arrayName)
|
||||
// TODO: check
|
||||
val (variableIndex, constantIndex) = env.evalVariableAndConstantSubParts(indexExpr)
|
||||
val variableIndexSize = variableIndex.map(v => getExpressionType(ctx, v).size).getOrElse(0)
|
||||
val totalIndexSize = getExpressionType(ctx, indexExpr).size
|
||||
exprTypeAndVariable.fold(noop) { case (exprType, target) =>
|
||||
|
||||
val register = target match {
|
||||
@ -609,12 +671,27 @@ object ExpressionCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
val result = (pointy, variableIndex) match {
|
||||
case (a: ConstantPointy, None) =>
|
||||
def loadFromReg() = {
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
register match {
|
||||
case Register.A =>
|
||||
List(AssemblyLine.indexedY(LDA, reg))
|
||||
case Register.X =>
|
||||
List(AssemblyLine.indexedY(LDX, reg))
|
||||
case Register.Y =>
|
||||
List(AssemblyLine.indexedY(LDA, reg), AssemblyLine.implied(TAY))
|
||||
}
|
||||
}
|
||||
val result = (pointy, variableIndex, totalIndexSize, variableIndexSize) match {
|
||||
case (a: ConstantPointy, None, _, _) =>
|
||||
List(AssemblyLine.absolute(load, env.genRelativeVariable(a.value + constantIndex, b, zeropage = false)))
|
||||
case (a: ConstantPointy, Some(v)) =>
|
||||
case (a: ConstantPointy, Some(v), _, 1) =>
|
||||
loadFromArrayAtUnknownIndex(v, a.value)
|
||||
case (p:VariablePointy, None) =>
|
||||
case (a: ConstantPointy, Some(v), _, 2) =>
|
||||
prepareWordIndexing(ctx, ConstantPointy(a.value + constantIndex, if (constantIndex.isProvablyZero) a.size else None), v) ++ loadFromReg()
|
||||
case (a: VariablePointy, _, 2, _) =>
|
||||
prepareWordIndexing(ctx, a, indexExpr) ++ loadFromReg()
|
||||
case (p:VariablePointy, None, 0 | 1, _) =>
|
||||
register match {
|
||||
case Register.A =>
|
||||
List(AssemblyLine.immediate(LDY, constantIndex), AssemblyLine.indexedY(LDA, p.addr))
|
||||
@ -623,7 +700,7 @@ object ExpressionCompiler {
|
||||
case Register.X =>
|
||||
List(AssemblyLine.immediate(LDY, constantIndex), AssemblyLine.indexedY(LDX, p.addr))
|
||||
}
|
||||
case (p:VariablePointy, Some(_)) =>
|
||||
case (p:VariablePointy, Some(_), 0 | 1, _) =>
|
||||
val calculatingIndex = compile(ctx, indexExpr, Some(b, RegisterVariable(Register.Y, b)), NoBranching)
|
||||
register match {
|
||||
case Register.A =>
|
||||
@ -633,6 +710,9 @@ object ExpressionCompiler {
|
||||
case Register.Y =>
|
||||
calculatingIndex ++ List(AssemblyLine.indexedY(LDA, p.addr), AssemblyLine.implied(TAY))
|
||||
}
|
||||
case _ =>
|
||||
ErrorReporting.error("Invalid index for reading", indexExpr.position)
|
||||
Nil
|
||||
}
|
||||
register match {
|
||||
case Register.A | Register.X | Register.Y => result ++ suffix
|
||||
@ -1116,7 +1196,7 @@ object ExpressionCompiler {
|
||||
exprTypeAndVariable.fold(noop) {
|
||||
case (VoidType, _) => ???
|
||||
case (_, RegisterVariable(Register.A, _)) => noop
|
||||
case (_, RegisterVariable(Register.AW, _)) => List(AssemblyLine.implied(XBA), AssemblyLine.implied(TAX), AssemblyLine.implied(XBA))
|
||||
case (_, RegisterVariable(Register.AW, _)) => List(AssemblyLine.implied(XBA), AssemblyLine.implied(TXA), AssemblyLine.implied(XBA))
|
||||
case (_, RegisterVariable(Register.X, _)) => List(AssemblyLine.implied(TAX))
|
||||
case (_, RegisterVariable(Register.Y, _)) => List(AssemblyLine.implied(TAY))
|
||||
case (_, RegisterVariable(Register.AX, _)) =>
|
||||
|
2
src/main/scala/millfork/env/Constant.scala
vendored
2
src/main/scala/millfork/env/Constant.scala
vendored
@ -20,6 +20,7 @@ import millfork.error.ErrorReporting
|
||||
import millfork.node.Position
|
||||
|
||||
sealed trait Constant {
|
||||
def isProvablyZero: Boolean = false
|
||||
|
||||
def asl(i: Constant): Constant = i match {
|
||||
case NumericConstant(sa, _) => asl(sa.toInt)
|
||||
@ -74,6 +75,7 @@ case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
|
||||
throw new IllegalArgumentException("The constant is too big")
|
||||
}
|
||||
}
|
||||
override def isProvablyZero: Boolean = value == 0
|
||||
|
||||
override def isLowestByteAlwaysEqual(i: Int) : Boolean = (value & 0xff) == (i&0xff)
|
||||
|
||||
|
33
src/main/scala/millfork/env/Environment.scala
vendored
33
src/main/scala/millfork/env/Environment.scala
vendored
@ -239,6 +239,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
}
|
||||
|
||||
def evalVariableAndConstantSubParts(e: Expression): (Option[Expression], Constant) =
|
||||
// TODO: prevent accidental negative indexing more robustly
|
||||
e match {
|
||||
case SumExpression(params, false) =>
|
||||
val (constants, variables) = params.map { case (sign, expr) => (sign, expr, eval(expr)) }.partition(_._3.isDefined)
|
||||
@ -248,7 +249,37 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
case List((false, x, _)) => Some(x)
|
||||
case _ => Some(SumExpression(variables.map(x => (x._1, x._2)), decimal = false))
|
||||
}
|
||||
variable -> constant
|
||||
variable match {
|
||||
case None => variable -> constant
|
||||
case Some(x@VariableExpression(v)) =>
|
||||
if (get[Variable](v).typ.isSigned) Some(FunctionCallExpression("^", List(x, LiteralExpression(0x80, 1)))) -> (constant - 128).quickSimplify
|
||||
else variable -> constant
|
||||
case Some(IndexedExpression(_, _)) => variable -> constant
|
||||
case Some(LiteralExpression(_, _)) => variable -> constant
|
||||
case Some(SumExpression(List(negative@(true, _)), false)) =>
|
||||
Some(SumExpression(List(false -> LiteralExpression(0xff, 1), negative), decimal = false)) -> (constant - 255).quickSimplify
|
||||
case Some(FunctionCallExpression(
|
||||
"<<" | ">>" |
|
||||
"<<'" | ">>'" |
|
||||
"&" | "|" | "^" |
|
||||
">>>>" | "<<<<" |
|
||||
"*" | "*'", _)) => variable -> constant
|
||||
case Some(FunctionCallExpression(fname, _)) =>
|
||||
maybeGet[Thing](fname) match {
|
||||
case Some(ff: MangledFunction) =>
|
||||
if (ff.returnType.isSigned) Some(e) -> Constant.Zero
|
||||
else variable -> constant
|
||||
case Some(t: Type) =>
|
||||
if (t.isSigned) Some(e) -> Constant.Zero
|
||||
else variable -> constant
|
||||
case _ =>
|
||||
// dunno what to do
|
||||
Some(e) -> Constant.Zero
|
||||
}
|
||||
case _ =>
|
||||
// dunno what to do
|
||||
Some(e) -> Constant.Zero
|
||||
}
|
||||
case _ => eval(e) match {
|
||||
case Some(c) => None -> c
|
||||
case None => Some(e) -> Constant.Zero
|
||||
|
@ -184,4 +184,64 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
""".stripMargin)
|
||||
|
||||
}
|
||||
|
||||
test("Negative subindex") {
|
||||
val m = EmuUnoptimizedRun(
|
||||
"""
|
||||
|
|
||||
| array output [$fff] @$c000
|
||||
| void main () {
|
||||
| byte i
|
||||
| output[$100] = 55
|
||||
| i = one()
|
||||
| output[1 - i] = 5
|
||||
| }
|
||||
| noinline byte one() {return 1}
|
||||
""".stripMargin)
|
||||
m.readByte(0xc100) should equal(55)
|
||||
m.readByte(0xc000) should equal(5)
|
||||
|
||||
}
|
||||
|
||||
test("Word subindex 1") {
|
||||
EmuBenchmarkRun(
|
||||
"""
|
||||
|
|
||||
| array output [$fff] @$c000
|
||||
| void main () {
|
||||
| word i
|
||||
| i = big()
|
||||
| output[$64] = 55
|
||||
| output[i] = 6
|
||||
| output[i] = 5
|
||||
| }
|
||||
| noinline word big() {return $564}
|
||||
""".stripMargin) {m =>
|
||||
m.readByte(0xc064) should equal(55)
|
||||
m.readByte(0xc564) should equal(5)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
test("Word subindex 2") {
|
||||
EmuBenchmarkRun(
|
||||
"""
|
||||
|
|
||||
| array output [$fff] @$c000
|
||||
| void main () {
|
||||
| word i
|
||||
| pointer o
|
||||
| o = p()
|
||||
| i = big()
|
||||
| o[$164] = 55
|
||||
| o[i] = 5
|
||||
| }
|
||||
| noinline word big() {return $564}
|
||||
| noinline word p() {return output.addr}
|
||||
""".stripMargin) {m =>
|
||||
m.readByte(0xc164) should equal(55)
|
||||
m.readByte(0xc564) should equal(5)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user