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

Indexing fixes; 16-bit indexing

This commit is contained in:
Karol Stasiak 2018-03-07 12:36:21 +01:00
parent 11337f4975
commit 1d865302ca
8 changed files with 280 additions and 51 deletions

View File

@ -172,6 +172,7 @@ object OptimizationPresets {
AlwaysGoodOptimizations.PointlessStackStashing,
AlwaysGoodOptimizations.PointlessStashingToIndexOverShortSafeBranch,
AlwaysGoodOptimizations.PoinlessStoreBeforeStore,
AlwaysGoodOptimizations.PointlessStoreToTheSameVariable,
AlwaysGoodOptimizations.RearrangableLoadFromTheSameLocation,
AlwaysGoodOptimizations.RearrangeMath,
AlwaysGoodOptimizations.RemoveNops,

View File

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

View File

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

View File

@ -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)
@ -577,11 +565,13 @@ object BuiltIns {
val env = ctx.env
val b = env.get[Type]("byte")
val lhsIsDirectlyIncrementable = v match {
case _:VariableExpression => true
case IndexedExpression(pointy, _) => env.getPointy(pointy) match {
case _:ConstantPointy => true
case _:VariablePointy => false
}
case _: VariableExpression => true
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)

View File

@ -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, _)) =>

View File

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

View File

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

View File

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