diff --git a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala
index 4e4be85a..a0598ebe 100644
--- a/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala
+++ b/src/main/scala/millfork/compiler/AbstractStatementPreprocessor.scala
@@ -185,6 +185,62 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
val (b, _) = optimizeStmts(body, Map())
ForEachStatement(v, a, b).pos(pos) -> Map()
case f@ForStatement(v, st, en, dir, body) =>
+
+ // detect a memset
+ f.body match {
+ case List(Assignment(target@IndexedExpression(pointy, index), source)) =>
+ val sourceType = AbstractExpressionCompiler.getExpressionType(ctx, source)
+ val targetType = AbstractExpressionCompiler.getExpressionType(ctx, target)
+ if (
+ !env.isVolatile(VariableExpression(pointy)) &&
+ !env.isVolatile(index) &&
+ !env.isVolatile(source) &&
+ !env.isVolatile(f.end) &&
+ index.getAllIdentifiers.forall(iv => !ctx.env.overlapsVariable(iv, source)) &&
+ !env.overlapsVariable(pointy, source) &&
+ !env.overlapsVariable(pointy, index) &&
+ !env.overlapsVariable(f.variable, source) &&
+ !env.overlapsVariable(f.variable, f.start) &&
+ !env.overlapsVariable(f.variable, f.end) &&
+ source.isPure &&
+ sourceType.size == 1 &&
+ targetType.size == 1 &&
+ sourceType.isAssignableTo(targetType)
+ ) {
+ val sizeExpr = f.direction match {
+ case ForDirection.DownTo =>
+ f.start #-# f.end #+# 1
+ case ForDirection.To | ForDirection.ParallelTo =>
+ f.end #-# f.start #+# 1
+ case ForDirection.Until | ForDirection.ParallelUntil =>
+ f.end #-# f.start
+ }
+ val w = env.get[Type]("word")
+ env.eval(sizeExpr) match {
+ case Some(size) =>
+ val startOpt = optimizeExpr(f.start, Map())
+ val sourceOpt = optimizeExpr(source, Map())
+ (env.getPointy(pointy), env.evalVariableAndConstantSubParts(index)) match {
+ case (array: ConstantPointy, (Some(VariableExpression(i)), offset)) if i == f.variable =>
+ // for i,start,until,end { array[i+offset] = source }
+ // println(s"Detected memset via array $array and index $i")
+ return MemsetStatement(startOpt #+# GeneratedConstantExpression(array.value + offset, w), size, sourceOpt, f.direction, Some(f)).pos(pos) -> Map()
+ case (pointer, (Some(VariableExpression(i)), offset)) if i == f.variable =>
+ // for i,start,until,end { array[i+offset] = source }
+ // println(s"Detected memset via pointer $pointer and index $i")
+ return MemsetStatement(startOpt #+# VariableExpression(pointy) #+# GeneratedConstantExpression(offset, w), size, sourceOpt, f.direction, Some(f)).pos(pos) -> Map()
+ case (_, (None, offset)) if pointy == f.variable =>
+ // for pointy,start,until,end { pointy[offset] = source }
+ // println(s"Detected memset via pointer $pointy alone")
+ return MemsetStatement(startOpt #+# GeneratedConstantExpression(offset, w), size, sourceOpt, f.direction, Some(f)).pos(pos) -> Map()
+ case _ =>
+ }
+ case _ =>
+ }
+ }
+ case _ =>
+ }
+
maybeOptimizeForStatement(f) match {
case Some(x) => x
case None =>
@@ -586,4 +642,31 @@ object AbstractStatementPreprocessor {
"==", "!=", "<", ">", ">=", "<=",
"not", "hi", "lo", "nonet", "sizeof"
)
+
+ def mightBeMemset(ctx: CompilationContext, f: ForStatement): Boolean = {
+ val env = ctx.env
+ f.body match {
+ case List(Assignment(target@IndexedExpression(pointy, index), source)) =>
+ val sourceType = AbstractExpressionCompiler.getExpressionType(ctx, source)
+ val targetType = AbstractExpressionCompiler.getExpressionType(ctx, target)
+ if (
+ source.isPure &&
+ index.isPure &&
+ sourceType.size == 1 &&
+ targetType.size == 1 &&
+ sourceType.isAssignableTo(targetType)&&
+ !env.isVolatile(VariableExpression(pointy)) &&
+ !env.isVolatile(index) &&
+ !env.isVolatile(source) &&
+ !env.isVolatile(f.end) &&
+ index.getAllIdentifiers.forall(iv => !ctx.env.overlapsVariable(iv, source)) &&
+ !env.overlapsVariable(pointy, source) &&
+ !env.overlapsVariable(pointy, index) &&
+ !env.overlapsVariable(f.variable, source) &&
+ !env.overlapsVariable(f.variable, f.start) &&
+ !env.overlapsVariable(f.variable, f.end)
+ ) env.eval(f.end #-# f.start).isDefined else false
+ case _ => false
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/scala/millfork/compiler/MacroExpander.scala b/src/main/scala/millfork/compiler/MacroExpander.scala
index 108599a3..981ccd4d 100644
--- a/src/main/scala/millfork/compiler/MacroExpander.scala
+++ b/src/main/scala/millfork/compiler/MacroExpander.scala
@@ -38,6 +38,7 @@ abstract class MacroExpander[T <: AbstractCode] {
case WhileStatement(c, b, i, n) => WhileStatement(f(c), b.map(gx), i.map(gx), n)
case DoWhileStatement(b, i, c, n) => DoWhileStatement(b.map(gx), i.map(gx), f(c), n)
case ForStatement(v, start, end, dir, body) => ForStatement(h(v), f(start), f(end), dir, body.map(gx))
+ case MemsetStatement(start, size, value, dir, original) => MemsetStatement(f(start), size, f(value), dir, original.map(gx).asInstanceOf[Option[ForStatement]])
case IfStatement(c, t, e) => IfStatement(f(c), t.map(gx), e.map(gx))
case s: Z80AssemblyStatement => s.copy(expression = f(s.expression), offsetExpression = s.offsetExpression.map(f))
case s: MosAssemblyStatement => s.copy(expression = f(s.expression))
@@ -76,6 +77,7 @@ abstract class MacroExpander[T <: AbstractCode] {
case WhileStatement(c, b, i, n) => WhileStatement(f(c), b.map(gx), i.map(gx), n)
case DoWhileStatement(b, i, c, n) => DoWhileStatement(b.map(gx), i.map(gx), f(c), n)
case ForStatement(v, start, end, dir, body) => ForStatement(h(v), f(start), f(end), dir, body.map(gx))
+ case MemsetStatement(start, size, value, dir, original) => MemsetStatement(f(start), size, f(value), dir, original.map(gx).asInstanceOf[Option[ForStatement]])
case IfStatement(c, t, e) => IfStatement(f(c), t.map(gx), e.map(gx))
case s: Z80AssemblyStatement => s.copy(expression = f(s.expression), offsetExpression = s.offsetExpression.map(f))
case s: MosAssemblyStatement => s.copy(expression = f(s.expression))
diff --git a/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala b/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala
index bd4feff0..8c62482d 100644
--- a/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala
+++ b/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala
@@ -3,7 +3,7 @@ package millfork.compiler.m6809
import millfork.assembly.BranchingOpcodeMapping
import millfork.assembly.m6809.{MLine, NonExistent}
import millfork.compiler.{AbstractCompiler, AbstractExpressionCompiler, AbstractStatementCompiler, BranchSpec, CompilationContext}
-import millfork.node.{Assignment, BlackHoleExpression, BreakStatement, ContinueStatement, DoWhileStatement, ExecutableStatement, Expression, ExpressionStatement, ForEachStatement, ForStatement, FunctionCallExpression, IfStatement, M6809AssemblyStatement, ReturnDispatchStatement, ReturnStatement, VariableExpression, WhileStatement}
+import millfork.node.{Assignment, BlackHoleExpression, BreakStatement, ContinueStatement, DoWhileStatement, ExecutableStatement, Expression, ExpressionStatement, ForEachStatement, ForStatement, FunctionCallExpression, IfStatement, M6809AssemblyStatement, MemsetStatement, ReturnDispatchStatement, ReturnStatement, VariableExpression, WhileStatement}
import millfork.assembly.m6809.MOpcode._
import millfork.env.{BooleanType, ConstantBooleanType, FatBooleanType, Label, ThingInMemory}
@@ -75,6 +75,8 @@ object M6809StatementCompiler extends AbstractStatementCompiler[MLine] {
compileDoWhileStatement(ctx, s)
case s:ForStatement =>
compileForStatement(ctx, s)
+ case s:MemsetStatement =>
+ compile(ctx, s.original.get)
case s:ForEachStatement =>
compileForEachStatement(ctx, s)
case s:BreakStatement =>
diff --git a/src/main/scala/millfork/compiler/mos/MosBulkMemoryOperations.scala b/src/main/scala/millfork/compiler/mos/MosBulkMemoryOperations.scala
index 9cefa4c0..d092457c 100644
--- a/src/main/scala/millfork/compiler/mos/MosBulkMemoryOperations.scala
+++ b/src/main/scala/millfork/compiler/mos/MosBulkMemoryOperations.scala
@@ -2,7 +2,7 @@ package millfork.compiler.mos
import millfork.CompilationFlag
import millfork.assembly.mos.{AddrMode, AssemblyLine, AssemblyLine0, Opcode}
-import millfork.compiler.{AbstractExpressionCompiler, BranchSpec, CompilationContext}
+import millfork.compiler.{AbstractExpressionCompiler, AbstractStatementPreprocessor, BranchSpec, CompilationContext}
import millfork.env.{ConstantPointy, Label, MemoryAddressConstant, MemoryVariable, NumericConstant, RelativeVariable, StackVariablePointy, Type, Variable, VariableAllocationMethod, VariableInMemory, VariablePointy}
import millfork.node._
import millfork.assembly.mos.Opcode._
@@ -14,11 +14,29 @@ object MosBulkMemoryOperations {
def compileMemset(ctx: CompilationContext, target: IndexedExpression, source: Expression, f: ForStatement): List[AssemblyLine] = {
if (ctx.options.zpRegisterSize < 2 ||
- target.name != f.variable ||
- target.index.containsVariable(f.variable) ||
- !target.index.isPure ||
- f.direction == ForDirection.DownTo) return MosStatementCompiler.compileForStatement(ctx, f)._1
- ctx.env.getPointy(target.name)
+ !AbstractStatementPreprocessor.mightBeMemset(ctx, f) ||
+ f.direction == ForDirection.DownTo) {
+ return MosStatementCompiler.compileForStatement(ctx, f)._1
+ }
+ val pointy = ctx.env.getPointy(target.name)
+ if (pointy.elementType.size != 1) {
+ return MosStatementCompiler.compileForStatement(ctx, f)._1
+ }
+ val w = ctx.env.get[Type]("word")
+ val startExpr = () match {
+ case _ if target.name == f.variable && !ctx.env.overlapsVariable(target.name, target.index) =>
+ f.start #+# target.index
+ case _ if target.name != f.variable && !ctx.env.overlapsVariable(target.name, source) =>
+ (pointy, ctx.env.evalVariableAndConstantSubParts(target.index)) match {
+ case (pty: ConstantPointy, (Some(VariableExpression(n)), offset)) if n == f.variable =>
+ f.start #+# GeneratedConstantExpression(pty.value, w) #+# GeneratedConstantExpression(offset, w)
+ case bad =>
+ val badd = bad
+ return MosStatementCompiler.compileForStatement(ctx, f)._1
+ }
+ case _ =>
+ return MosStatementCompiler.compileForStatement(ctx, f)._1
+ }
val sizeExpr = f.direction match {
case ForDirection.DownTo =>
f.start #-# f.end #+# 1
@@ -27,17 +45,24 @@ object MosBulkMemoryOperations {
case ForDirection.Until | ForDirection.ParallelUntil =>
f.end #-# f.start
}
- val reg = ctx.env.get[VariableInMemory]("__reg.loword")
- val w = ctx.env.get[Type]("word")
val size = ctx.env.eval(sizeExpr) match {
case Some(c) => c.quickSimplify
case _ => return MosStatementCompiler.compileForStatement(ctx, f)._1
}
+ compileMemset(ctx, MemsetStatement(startExpr, size, source, f.direction, Some(f)))
+ }
+
+ def compileMemset(ctx: CompilationContext, m: MemsetStatement): List[AssemblyLine] = {
+ if (m.direction == ForDirection.DownTo) {
+ return MosStatementCompiler.compileForStatement(ctx, m.original.get)._1
+ }
+ val w = ctx.env.get[Type]("word")
+ val reg = ctx.env.get[VariableInMemory]("__reg.loword")
val useTwoRegs = ctx.options.flag(CompilationFlag.OptimizeForSpeed) && ctx.options.zpRegisterSize >= 4
val loadReg =
if (useTwoRegs) {
import millfork.assembly.mos.AddrMode._
- val first = MosExpressionCompiler.compile(ctx, f.start #+# target.index, Some(w -> reg), BranchSpec.None)
+ val first = MosExpressionCompiler.compile(ctx, m.start, Some(w -> reg), BranchSpec.None)
first ++ (first match {
case List(AssemblyLine0(LDA, Immediate, l), AssemblyLine0(LDA, ZeroPage, r0), AssemblyLine0(LDA, Immediate, h), AssemblyLine0(LDA, ZeroPage, r1))
if (r1-r0).quickSimplify.isProvably(1) =>
@@ -57,15 +82,15 @@ object MosBulkMemoryOperations {
AssemblyLine.immediate(ADC, 0),
AssemblyLine.zeropage(STA, reg, 3))
})
- } else MosExpressionCompiler.compile(ctx, f.start #+# target.index, Some(w -> reg), BranchSpec.None)
+ } else MosExpressionCompiler.compile(ctx, m.start, Some(w -> reg), BranchSpec.None)
- val loadSource = MosExpressionCompiler.compileToA(ctx, source)
+ val loadSource = MosExpressionCompiler.compileToA(ctx, m.value)
val loadAll = if (MosExpressionCompiler.changesZpreg(loadSource, 0) || MosExpressionCompiler.changesZpreg(loadSource, 1)) {
loadSource ++ MosExpressionCompiler.preserveRegisterIfNeeded(ctx, MosRegister.A, loadReg)
} else {
loadReg ++ loadSource
}
- val wholePageCount = size.hiByte.quickSimplify
+ val wholePageCount = m.size.quickSimplify.hiByte.quickSimplify
def fillOnePage: List[AssemblyLine] = {
val label = ctx.nextLabel("ms")
@@ -141,7 +166,7 @@ object MosBulkMemoryOperations {
AssemblyLine.relative(BNE, labelX),
AssemblyLine.label(labelXSkip))
}
- val restSize = size.loByte.quickSimplify
+ val restSize = m.size.quickSimplify.loByte.quickSimplify
val setRest = restSize match {
case NumericConstant(0, _) => Nil
case NumericConstant(1, _) =>
@@ -153,7 +178,7 @@ object MosBulkMemoryOperations {
case _ =>
val label = ctx.nextLabel("ms")
val labelSkip = ctx.nextLabel("ms")
- if (f.direction == ForDirection.ParallelUntil) {
+ if (m.direction == ForDirection.ParallelUntil) {
List(
AssemblyLine.immediate(LDY, restSize),
AssemblyLine.relative(BEQ, labelSkip),
diff --git a/src/main/scala/millfork/compiler/mos/MosStatementCompiler.scala b/src/main/scala/millfork/compiler/mos/MosStatementCompiler.scala
index 7fd15d32..0295a446 100644
--- a/src/main/scala/millfork/compiler/mos/MosStatementCompiler.scala
+++ b/src/main/scala/millfork/compiler/mos/MosStatementCompiler.scala
@@ -300,18 +300,25 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
compileWhileStatement(ctx, s)
case s: DoWhileStatement =>
compileDoWhileStatement(ctx, s)
- case f@ForStatement(variable, _, _, _, List(Assignment(target: IndexedExpression, source: Expression))) if !source.containsVariable(variable) =>
+ case f:MemsetStatement =>
+ MosBulkMemoryOperations.compileMemset(ctx, f) -> Nil
+ case f@ForStatement(variable, _, _, _, List(Assignment(target: IndexedExpression, source: Expression))) if !ctx.env.overlapsVariable(variable, source) =>
MosBulkMemoryOperations.compileMemset(ctx, target, source, f) -> Nil
case f@ForStatement(variable, start, end, _, List(ExpressionStatement(
- FunctionCallExpression(operator@("+=" | "-=" | "+'=" | "-'=" | "|=" | "^=" | "&="), List(target: VariableExpression, source))
- ))) if !target.containsVariable(variable) && !source.containsVariable(variable) && !start.containsVariable(target.name) && !end.containsVariable(target.name) =>
+ FunctionCallExpression(operator@("+=" | "-=" | "+'=" | "-'=" | "|=" | "^=" | "&="), List(target: VariableExpression, source))
+ ))) if !ctx.env.overlapsVariable(variable, source) &&
+ !ctx.env.overlapsVariable(variable, target) &&
+ !ctx.env.overlapsVariable(target.name, start) &&
+ !ctx.env.overlapsVariable(target.name, end) =>
MosBulkMemoryOperations.compileFold(ctx, target, operator, source, f) match {
case Some(x) => x -> Nil
case None => compileForStatement(ctx, f)
}
case f@ForStatement(variable, start, end, _, List(ExpressionStatement(
- FunctionCallExpression(operator@("+=" | "-=" | "<<=" | ">>="), List(target: IndexedExpression, source))
- ))) if !source.containsVariable(variable) && !start.containsVariable(target.name) && !end.containsVariable(target.name) && target.name != variable =>
+ FunctionCallExpression(operator@("+=" | "-=" | "<<=" | ">>="), List(target: IndexedExpression, source))
+ ))) if !ctx.env.overlapsVariable(variable, source) &&
+ !ctx.env.overlapsVariable(target.name, start) &&
+ !ctx.env.overlapsVariable(target.name, end) && target.name != variable =>
MosBulkMemoryOperations.compileMemmodify(ctx, target, operator, source, f) match {
case Some(x) => x -> Nil
case None => compileForStatement(ctx, f)
diff --git a/src/main/scala/millfork/compiler/z80/Z80BulkMemoryOperations.scala b/src/main/scala/millfork/compiler/z80/Z80BulkMemoryOperations.scala
index a5a5e56c..72bbaad2 100644
--- a/src/main/scala/millfork/compiler/z80/Z80BulkMemoryOperations.scala
+++ b/src/main/scala/millfork/compiler/z80/Z80BulkMemoryOperations.scala
@@ -3,7 +3,7 @@ package millfork.compiler.z80
import millfork.CompilationFlag
import millfork.assembly.Elidability
import millfork.assembly.z80._
-import millfork.compiler.CompilationContext
+import millfork.compiler.{AbstractStatementPreprocessor, CompilationContext}
import millfork.env._
import millfork.node._
import millfork.assembly.z80.ZOpcode._
@@ -20,7 +20,7 @@ object Z80BulkMemoryOperations {
* Compiles loops like for i,a,until,b { p[i] = q[i] }
*/
def compileMemcpy(ctx: CompilationContext, target: IndexedExpression, source: IndexedExpression, f: ForStatement): List[ZLine] = {
- val sourceOffset = removeVariableOnce(f.variable, source.index).getOrElse(return compileForStatement(ctx, f)._1)
+ val sourceOffset = removeVariableOnce(ctx, f.variable, source.index).getOrElse(return compileForStatement(ctx, f)._1)
if (!sourceOffset.isPure) return compileForStatement(ctx, f)._1
val sourceIndexExpression = sourceOffset #+# f.start
val calculateSource = Z80ExpressionCompiler.calculateAddressToHL(ctx, IndexedExpression(source.name, sourceIndexExpression).pos(source.position), forWriting = false)
@@ -43,6 +43,9 @@ object Z80BulkMemoryOperations {
* where a is an arbitrary expression independent of i
*/
def compileMemset(ctx: CompilationContext, target: IndexedExpression, source: Expression, f: ForStatement): List[ZLine] = {
+ if (f.direction == ForDirection.DownTo ||
+ !AbstractStatementPreprocessor.mightBeMemset(ctx, f)) return Z80StatementCompiler.compileForStatement(ctx, f)._1
+
val loadA = Z80ExpressionCompiler.stashHLIfChanged(ctx, Z80ExpressionCompiler.compileToA(ctx, source)) :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
def compileForZ80(targetOffset: Expression): List[ZLine] = {
@@ -81,12 +84,12 @@ object Z80BulkMemoryOperations {
}
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
- removeVariableOnce(f.variable, target.index) match {
+ removeVariableOnce(ctx, f.variable, target.index) match {
case Some(targetOffset) if targetOffset.isPure =>
return compileForZ80(targetOffset)
case _ =>
}
- if (target.isPure && target.name == f.variable && !target.index.containsVariable(f.variable)) {
+ if (target.isPure && target.name == f.variable && !ctx.env.overlapsVariable(f.variable, target.index)) {
return compileForZ80(target.index)
}
}
@@ -100,12 +103,46 @@ object Z80BulkMemoryOperations {
)
}
+ def compileMemset(ctx: CompilationContext, f: MemsetStatement): List[ZLine] = {
+ if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
+ val w = ctx.env.get[Type]("word")
+ val loadA = Z80ExpressionCompiler.stashHLIfChanged(ctx, Z80ExpressionCompiler.compileToA(ctx, f.value)) :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
+ val startingAdress = f.direction match {
+ case ForDirection.DownTo => f.start #+# GeneratedConstantExpression(f.size, w) #-# 1
+ case _ => f.start
+ }
+ val calculateAddress = Z80ExpressionCompiler.compileToHL(ctx, startingAdress)
+ val calculateSize = List(ZLine.ldImm16(ZRegister.BC, f.size - 1))
+ val (incOp, ldOp) = f.direction match {
+ case ForDirection.DownTo => DEC_16 -> LDDR
+ case _ => INC_16 -> LDIR
+ }
+ val loadFirstValue = ctx.env.eval(f.value) match {
+ case Some(c) => List(ZLine.ldImm8(ZRegister.MEM_HL, c))
+ case _ => Z80ExpressionCompiler.stashBCIfChanged(ctx, loadA)
+ }
+ val loadDE = calculateAddress match {
+ case List(ZLine0(ZOpcode.LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), c)) =>
+ if (incOp == DEC_16) List(ZLine.ldImm16(ZRegister.DE, (c - 1).quickSimplify))
+ else List(ZLine.ldImm16(ZRegister.DE, (c + 1).quickSimplify))
+ case _ => List(
+ ZLine.ld8(ZRegister.D, ZRegister.H),
+ ZLine.ld8(ZRegister.E, ZRegister.L),
+ ZLine.register(incOp, ZRegister.DE))
+ }
+ calculateAddress ++ calculateSize ++ loadFirstValue ++ loadDE :+ ZLine.implied(ldOp)
+ } else {
+ // go to the generic handler:
+ Z80StatementCompiler.compile(ctx, f.original.get)._1
+ }
+ }
+
/**
* Compiles loops like for i,a,until,b { target[i] = z },
* where z is an expression depending on source[i]
*/
def compileMemtransform(ctx: CompilationContext, target: IndexedExpression, operator: String, source: Expression, f: ForStatement): List[ZLine] = {
- val c = determineExtraLoopRegister(ctx, f, source.containsVariable(f.variable))
+ val c = determineExtraLoopRegister(ctx, f, ctx.env.overlapsVariable(f.variable, source))
val load = buildMemtransformLoader(ctx, ZRegister.MEM_HL, f.variable, operator, source, c.loopRegister).getOrElse(return compileForStatement(ctx, f)._1)
import scala.util.control.Breaks._
breakable{
@@ -130,9 +167,9 @@ object Z80BulkMemoryOperations {
target2: IndexedExpression, operator2: String, source2: Expression,
f: ForStatement): List[ZLine] = {
import scala.util.control.Breaks._
- val c = determineExtraLoopRegister(ctx, f, source1.containsVariable(f.variable) || source2.containsVariable(f.variable))
- val target1Offset = removeVariableOnce(f.variable, target2.index).getOrElse(return compileForStatement(ctx, f)._1)
- val target2Offset = removeVariableOnce(f.variable, target2.index).getOrElse(return compileForStatement(ctx, f)._1)
+ val c = determineExtraLoopRegister(ctx, f, ctx.env.overlapsVariable(f.variable, source1) || ctx.env.overlapsVariable(f.variable, source2))
+ val target1Offset = removeVariableOnce(ctx, f.variable, target2.index).getOrElse(return compileForStatement(ctx, f)._1)
+ val target2Offset = removeVariableOnce(ctx, f.variable, target2.index).getOrElse(return compileForStatement(ctx, f)._1)
val target1IndexExpression = if (c.countDownDespiteSyntax) {
target1Offset #+# f.end #-# 1
} else {
@@ -397,7 +434,9 @@ object Z80BulkMemoryOperations {
extraAddressCalculations: Boolean => (List[ZLine], List[ZLine]),
loadA: ZOpcode.Value => List[ZLine],
z80Bulk: Boolean => Option[ZOpcode.Value]): List[ZLine] = {
- val targetOffset = removeVariableOnce(f.variable, target.index).getOrElse(return compileForStatement(ctx, f)._1)
+ val pointy = ctx.env.getPointy(target.name)
+ if (pointy.elementType.size > 1) return Z80StatementCompiler.compileForStatement(ctx, f)._1
+ val targetOffset = removeVariableOnce(ctx, f.variable, target.index).getOrElse(return compileForStatement(ctx, f)._1)
if (!targetOffset.isPure) return compileForStatement(ctx, f)._1
val indexVariableSize = ctx.env.get[Variable](f.variable).typ.size
val wrapper = createForLoopPreconditioningIfStatement(ctx, f)
@@ -469,14 +508,14 @@ object Z80BulkMemoryOperations {
Nil))._1
}
- private def removeVariableOnce(variable: String, expr: Expression): Option[Expression] = {
+ private def removeVariableOnce(ctx: CompilationContext, variable: String, expr: Expression): Option[Expression] = {
expr match {
case VariableExpression(i) => if (i == variable) Some(LiteralExpression(0, 1)) else None
case SumExpression(exprs, false) =>
- if (exprs.count(_._2.containsVariable(variable)) == 1) {
+ if (exprs.count(e => ctx.env.overlapsVariable(variable, e._2)) == 1) {
Some(SumExpression(exprs.map {
- case (false, e) => false -> (if (e.containsVariable(variable)) removeVariableOnce(variable, e).getOrElse(return None) else e)
- case (true, e) => if (e.containsVariable(variable)) return None else true -> e
+ case (false, e) => if (ctx.env.overlapsVariable(variable, e)) false -> removeVariableOnce(ctx, variable, e).getOrElse(return None) else false -> e
+ case (true, e) => if (ctx.env.overlapsVariable(variable, e)) return None else true -> e
}, decimal = false))
} else None
case _ => None
diff --git a/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala b/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala
index c3dc2a09..564337cf 100644
--- a/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala
+++ b/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala
@@ -140,10 +140,13 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
case s: ReturnDispatchStatement =>
Z80ReturnDispatch.compile(ctx, s) -> Nil
+ case f:MemsetStatement =>
+ Z80BulkMemoryOperations.compileMemset(ctx, f) -> Nil
+
case f@ForStatement(_, _, _, _, List(Assignment(target: IndexedExpression, source: IndexedExpression))) =>
Z80BulkMemoryOperations.compileMemcpy(ctx, target, source, f) -> Nil
- case f@ForStatement(variable, _, _, _, List(Assignment(target: IndexedExpression, source: Expression))) if !source.containsVariable(variable) =>
+ case f@ForStatement(variable, _, _, _, List(Assignment(target: IndexedExpression, source: Expression))) if ctx.env.overlapsVariable(variable, source) =>
Z80BulkMemoryOperations.compileMemset(ctx, target, source, f) -> Nil
case f@ForStatement(variable, _, _, _, List(ExpressionStatement(FunctionCallExpression(
diff --git a/src/main/scala/millfork/compiler/z80/Z80StatementPreprocessor.scala b/src/main/scala/millfork/compiler/z80/Z80StatementPreprocessor.scala
index edb59305..8b6926ee 100644
--- a/src/main/scala/millfork/compiler/z80/Z80StatementPreprocessor.scala
+++ b/src/main/scala/millfork/compiler/z80/Z80StatementPreprocessor.scala
@@ -47,8 +47,8 @@ class Z80StatementPreprocessor(ctx: CompilationContext, statements: List[Executa
if (!optimize) return None
if (ctx.env.eval(f.start).isEmpty) return None
if (f.variable.contains(".")) return None
- if (f.start.containsVariable(f.variable)) return None
- if (f.end.containsVariable(f.variable)) return None
+ if (ctx.env.overlapsVariable(f.variable, f.start)) return None
+ if (ctx.env.overlapsVariable(f.variable, f.end)) return None
val indexVariable = env.get[Variable](f.variable)
if (indexVariable.typ.size != 1) return None
if (indexVariable.isVolatile) return None
diff --git a/src/main/scala/millfork/env/Constant.scala b/src/main/scala/millfork/env/Constant.scala
index 22b4006c..01b07fec 100644
--- a/src/main/scala/millfork/env/Constant.scala
+++ b/src/main/scala/millfork/env/Constant.scala
@@ -20,6 +20,7 @@ import millfork.node.Position
sealed trait Constant {
+
def toIntelString: String
def isQuiteNegative: Boolean = false
@@ -127,6 +128,8 @@ sealed trait Constant {
}
final def succ: Constant = (this + 1).quickSimplify
+
+ def rootThingName: String
}
case class AssertByte(c: Constant) extends Constant {
@@ -149,6 +152,7 @@ case class AssertByte(c: Constant) extends Constant {
override def fitsInto(typ: Type): Boolean = true
override def toIntelString: String = c.toIntelString
+ override def rootThingName: String = c.rootThingName
}
case class StructureConstant(typ: StructType, fields: List[Constant]) extends Constant {
@@ -192,6 +196,7 @@ case class StructureConstant(typ: StructType, fields: List[Constant]) extends Co
}
Constant.Zero
}
+ override def rootThingName: String = "?"
}
case class UnexpandedConstant(name: String, requiredSize: Int) extends Constant {
@@ -202,6 +207,8 @@ case class UnexpandedConstant(name: String, requiredSize: Int) extends Constant
override def toIntelString: String = name
override def refersTo(name: String): Boolean = name == this.name
+
+ override def rootThingName: String = "?"
}
case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
@@ -284,6 +291,8 @@ case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
NumericConstant(actualBits, typ.size)
}
}
+
+ override def rootThingName: String = ""
}
case class MemoryAddressConstant(var thing: ThingInMemory) extends Constant {
@@ -322,6 +331,8 @@ case class MemoryAddressConstant(var thing: ThingInMemory) extends Constant {
override def isRelatedTo(v: Thing): Boolean = thing.name == v.name
override def refersTo(name: String): Boolean = name == thing.name
+
+ override def rootThingName: String = thing.rootName
}
case class SubbyteConstant(base: Constant, index: Int) extends Constant {
@@ -360,6 +371,8 @@ case class SubbyteConstant(base: Constant, index: Int) extends Constant {
override def isRelatedTo(v: Thing): Boolean = base.isRelatedTo(v)
override def refersTo(name: String): Boolean = base.refersTo(name)
+
+ override def rootThingName: String = base.rootThingName
}
object MathOperator extends Enumeration {
@@ -681,4 +694,12 @@ case class CompoundConstant(operator: MathOperator.Value, lhs: Constant, rhs: Co
}
result
}
+
+ override def rootThingName: String = (lhs.rootThingName, rhs.rootThingName) match {
+ case ("?", _) => "?"
+ case (_, "?") => "?"
+ case ("", x) => x
+ case (x, "") => x
+ case _ => "?"
+ }
}
diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala
index 89616604..820c5207 100644
--- a/src/main/scala/millfork/env/Environment.scala
+++ b/src/main/scala/millfork/env/Environment.scala
@@ -2228,6 +2228,48 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
}.toMap ++ parent.map(_.getAliases).getOrElse(Map.empty)
}
+ def isVolatile(target: Expression): Boolean = {
+ if (eval(target).isDefined) return false
+ target match {
+ case _: LiteralExpression => false
+ case _: GeneratedConstantExpression => false
+ case e: VariableExpression => maybeGet[Thing](e.name) match {
+ case Some(v: Variable) => v.isVolatile
+ case Some(v: MfArray) => true // TODO: all arrays assumed volatile for now
+ case Some(_: Constant) => false
+ case Some(_: Type) => false
+ case _ => true // TODO: ?
+ }
+ case e: FunctionCallExpression => e.expressions.exists(isVolatile)
+ case e: IndexedExpression => isVolatile(VariableExpression(e.name)) || isVolatile(e.index)
+ case _ => true
+ }
+ }
+
+ def overlapsVariable(variable: String, expr: Expression): Boolean = {
+ if (eval(expr).isDefined) return false
+ if (expr.containsVariable(variable)) return true
+ val varRootName = get[Thing](variable).rootName
+ if (varRootName == "?") return true
+ if (varRootName == "") return false
+ overlapsVariableImpl(varRootName, expr)
+ }
+
+ private def overlapsVariableImpl(varRootName: String, expr: Expression): Boolean = {
+ expr match {
+ case _: LiteralExpression => false
+ case _: GeneratedConstantExpression => false
+ case e: VariableExpression => maybeGet[Thing](e.name) match {
+ case Some(t) =>
+ val rootName = t.rootName
+ rootName == "?" || rootName == varRootName
+ case _ => true // TODO: ?
+ }
+ case e: FunctionCallExpression => e.expressions.exists(x => overlapsVariableImpl(varRootName, x))
+ case e: IndexedExpression => overlapsVariableImpl(varRootName, VariableExpression(e.name)) || overlapsVariableImpl(varRootName, e.index)
+ case _ => true
+ }
+ }
}
object Environment {
diff --git a/src/main/scala/millfork/env/Thing.scala b/src/main/scala/millfork/env/Thing.scala
index 7b09ac0b..01936242 100644
--- a/src/main/scala/millfork/env/Thing.scala
+++ b/src/main/scala/millfork/env/Thing.scala
@@ -7,6 +7,7 @@ import millfork.output.{MemoryAlignment, NoAlignment}
sealed trait Thing {
def name: String
+ def rootName: String = name
}
case class Alias(name: String, target: String, deprecated: Boolean = false) extends Thing
@@ -338,6 +339,8 @@ case class RelativeArray(name: String, address: Constant, elementCount: Int, dec
override def zeropage: Boolean = false
override def sizeInBytes: Int = elementCount * elementType.size
+
+ override def rootName: String = address.rootThingName
}
case class InitializedArray(name: String, address: Option[Constant], contents: Seq[Expression], declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val readOnly: Boolean, override val alignment: MemoryAlignment) extends MfArray with PreallocableThing {
@@ -357,6 +360,8 @@ case class InitializedArray(name: String, address: Option[Constant], contents: S
case class RelativeVariable(name: String, address: Constant, typ: Type, zeropage: Boolean, declaredBank: Option[String], override val isVolatile: Boolean) extends VariableInMemory {
override def toAddress: Constant = address
+
+ override def rootName: String = address.rootThingName
}
sealed trait MangledFunction extends CallableThing {
diff --git a/src/main/scala/millfork/node/Node.scala b/src/main/scala/millfork/node/Node.scala
index 70af40a7..920bfc2b 100644
--- a/src/main/scala/millfork/node/Node.scala
+++ b/src/main/scala/millfork/node/Node.scala
@@ -41,6 +41,7 @@ sealed trait Expression extends Node {
def getPointies: Seq[String]
def isPure: Boolean
def getAllIdentifiers: Set[String]
+ def prettyPrint: String
def #+#(smallInt: Int): Expression = if (smallInt == 0) this else (this #+# LiteralExpression(smallInt, 1).pos(this.position)).pos(this.position)
def #+#(that: Expression): Expression = that match {
@@ -63,6 +64,7 @@ case class ConstantArrayElementExpression(constant: Constant) extends Expression
override def getPointies: Seq[String] = Seq.empty
override def isPure: Boolean = true
override def getAllIdentifiers: Set[String] = Set.empty
+ override def prettyPrint: String = constant.toString
}
case class LiteralExpression(value: Long, requiredSize: Int) extends Expression {
@@ -73,6 +75,7 @@ case class LiteralExpression(value: Long, requiredSize: Int) extends Expression
override def getPointies: Seq[String] = Seq.empty
override def isPure: Boolean = true
override def getAllIdentifiers: Set[String] = Set.empty
+ override def prettyPrint: String = "$" + value.toHexString
}
case class TextLiteralExpression(characters: List[Expression]) extends Expression {
@@ -83,6 +86,7 @@ case class TextLiteralExpression(characters: List[Expression]) extends Expressio
override def getPointies: Seq[String] = Seq.empty
override def isPure: Boolean = true
override def getAllIdentifiers: Set[String] = Set.empty
+ override def prettyPrint: String = characters.map(_.prettyPrint).mkString("[", ",", "]")
}
case class GeneratedConstantExpression(value: Constant, typ: Type) extends Expression {
@@ -93,6 +97,7 @@ case class GeneratedConstantExpression(value: Constant, typ: Type) extends Expre
override def getPointies: Seq[String] = Seq.empty
override def isPure: Boolean = true
override def getAllIdentifiers: Set[String] = Set.empty
+ override def prettyPrint: String = value.toString
}
case class BooleanLiteralExpression(value: Boolean) extends Expression {
@@ -103,6 +108,7 @@ case class BooleanLiteralExpression(value: Boolean) extends Expression {
override def getPointies: Seq[String] = Seq.empty
override def isPure: Boolean = true
override def getAllIdentifiers: Set[String] = Set.empty
+ override def prettyPrint: String = value.toString
}
sealed trait LhsExpression extends Expression
@@ -115,6 +121,7 @@ case object BlackHoleExpression extends LhsExpression {
override def getPointies: Seq[String] = Seq.empty
override def isPure: Boolean = true
override def getAllIdentifiers: Set[String] = Set.empty
+ override def prettyPrint: String = "(_|_)"
}
case class SeparateBytesExpression(hi: Expression, lo: Expression) extends LhsExpression {
@@ -134,6 +141,7 @@ case class SeparateBytesExpression(hi: Expression, lo: Expression) extends LhsEx
override def getPointies: Seq[String] = hi.getPointies ++ lo.getPointies
override def isPure: Boolean = hi.isPure && lo.isPure
override def getAllIdentifiers: Set[String] = hi.getAllIdentifiers ++ lo.getAllIdentifiers
+ override def prettyPrint: String = s"($hi:$lo)"
}
case class SumExpression(expressions: List[(Boolean, Expression)], decimal: Boolean) extends Expression {
@@ -157,6 +165,7 @@ case class SumExpression(expressions: List[(Boolean, Expression)], decimal: Bool
override def #-#(that: Expression): Expression =
if (decimal) super.#-#(that)
else SumExpression(expressions :+ (true -> that), decimal = false)
+ override def prettyPrint: String = '(' + expressions.map{case(neg, e) => (if (neg) "- " else "+ ") + e.prettyPrint}.mkString("", " ", ")").stripPrefix("+ ")
}
case class FunctionCallExpression(functionName: String, expressions: List[Expression]) extends Expression {
@@ -176,6 +185,10 @@ case class FunctionCallExpression(functionName: String, expressions: List[Expres
override def getPointies: Seq[String] = expressions.flatMap(_.getPointies)
override def isPure: Boolean = false // TODO
override def getAllIdentifiers: Set[String] = expressions.map(_.getAllIdentifiers).fold(Set[String]())(_ ++ _) + functionName
+ override def prettyPrint: String =
+ if (expressions.size != 2 || functionName.exists(Character.isAlphabetic(_)))
+ functionName + expressions.mkString("(", ", ", ")")
+ else s"(${expressions.head.prettyPrint} $functionName ${expressions(1).prettyPrint}"
}
case class HalfWordExpression(expression: Expression, hiByte: Boolean) extends Expression {
@@ -189,6 +202,7 @@ case class HalfWordExpression(expression: Expression, hiByte: Boolean) extends E
override def getPointies: Seq[String] = expression.getPointies
override def isPure: Boolean = expression.isPure
override def getAllIdentifiers: Set[String] = expression.getAllIdentifiers
+ override def prettyPrint: String = '(' + expression.prettyPrint + (if (hiByte) ").hi" else ").lo")
}
sealed class NiceFunctionProperty(override val toString: String)
@@ -310,6 +324,7 @@ case class VariableExpression(name: String) extends LhsExpression {
override def getPointies: Seq[String] = if (name.endsWith(".addr.lo")) Seq(name.stripSuffix(".addr.lo")) else Seq.empty
override def isPure: Boolean = true
override def getAllIdentifiers: Set[String] = Set(name)
+ override def prettyPrint: String = name
}
case class IndexedExpression(name: String, index: Expression) extends LhsExpression {
@@ -338,6 +353,7 @@ case class IndexedExpression(name: String, index: Expression) extends LhsExpress
override def getPointies: Seq[String] = Seq(name)
override def isPure: Boolean = index.isPure
override def getAllIdentifiers: Set[String] = index.getAllIdentifiers + name
+ override def prettyPrint: String = s"$name[${index.prettyPrint}]"
}
case class IndirectFieldExpression(root: Expression, firstIndices: Seq[Expression], fields: Seq[(Boolean, String, Seq[Expression])]) extends LhsExpression {
@@ -371,6 +387,10 @@ case class IndirectFieldExpression(root: Expression, firstIndices: Seq[Expressio
override def isPure: Boolean = root.isPure && firstIndices.forall(_.isPure) && fields.forall(_._3.forall(_.isPure))
override def getAllIdentifiers: Set[String] = root.getAllIdentifiers ++ firstIndices.flatMap(_.getAllIdentifiers) ++ fields.flatMap(_._3.flatMap(_.getAllIdentifiers))
+
+ override def prettyPrint: String = root.prettyPrint +
+ firstIndices.map(i => '[' + i.prettyPrint + ']').mkString("") +
+ fields.map{case (dot, f, ixs) => (if (dot) "." else "->") + f + ixs.map(i => '[' + i.prettyPrint + ']').mkString("")}
}
case class DerefDebuggingExpression(inner: Expression, preferredSize: Int) extends LhsExpression {
@@ -391,6 +411,7 @@ case class DerefDebuggingExpression(inner: Expression, preferredSize: Int) exten
override def isPure: Boolean = inner.isPure
override def getAllIdentifiers: Set[String] = inner.getAllIdentifiers
+ override def prettyPrint: String = s"¥deref(${inner.prettyPrint})"
}
case class DerefExpression(inner: Expression, offset: Int, targetType: Type) extends LhsExpression {
@@ -410,6 +431,7 @@ case class DerefExpression(inner: Expression, offset: Int, targetType: Type) ext
override def isPure: Boolean = inner.isPure
override def getAllIdentifiers: Set[String] = inner.getAllIdentifiers
+ override def prettyPrint: String = s"¥deref(${inner.prettyPrint})"
}
sealed trait Statement extends Node {
@@ -718,6 +740,22 @@ case class ForStatement(variable: String, start: Expression, end: Expression, di
override def loopVariable: String = variable
}
+case class MemsetStatement(start: Expression, size: Constant, value: Expression, direction: ForDirection.Value, original: Option[ForStatement]) extends CompoundStatement {
+ if (original.isEmpty && direction != ForDirection.ParallelUntil) {
+ throw new IllegalArgumentException
+ }
+ override def getAllExpressions: List[Expression] = List(start, value) ++ original.toList.flatMap(_.getAllExpressions)
+
+ override def getChildStatements: Seq[Statement] = original.toList.flatMap(_.getChildStatements)
+
+ override def flatMap(f: ExecutableStatement => Option[ExecutableStatement]): Option[ExecutableStatement] =
+ // shouldn't ever yield None, as this is possible only in case of control-flow changing statements:
+ Some(copy(original = original.flatMap(_.flatMap(f).asInstanceOf[Option[ForStatement]])))
+
+
+ override def loopVariable: String = original.fold("_none")(_.loopVariable)
+}
+
case class ForEachStatement(variable: String, values: Either[Expression, List[Expression]], body: List[ExecutableStatement]) extends CompoundStatement {
override def getAllExpressions: List[Expression] = VariableExpression(variable) :: (values.fold[List[Expression]](_ => Nil, identity) ++ body.flatMap(_.getAllExpressions))
diff --git a/src/test/scala/millfork/test/MemBulkSuite.scala b/src/test/scala/millfork/test/MemBulkSuite.scala
new file mode 100644
index 00000000..9e063058
--- /dev/null
+++ b/src/test/scala/millfork/test/MemBulkSuite.scala
@@ -0,0 +1,48 @@
+package millfork.test
+
+import millfork.Cpu
+import millfork.test.emu.EmuCrossPlatformBenchmarkRun
+import org.scalatest.{AppendedClues, FunSuite, Matchers}
+
+/**
+ * @author Karol Stasiak
+ */
+class MemBulkSuite extends FunSuite with Matchers with AppendedClues {
+
+ test("Memcpy should work fine") {
+ EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
+ """
+ |
+ |array input @$c000 = [5,89,6,1,8,6,87,52,6,45,8,52,8,6,14,89]
+ |array output [input.length] @$c100
+ |byte size @$cfff
+ |void main() {
+ | word i
+ | for i,0,paralleluntil,input.length { output[i] = input[i] }
+ | size = input.length
+ |}
+ |""".stripMargin) { m =>
+ val size = m.readByte(0xcfff)
+ size should be >(0)
+ for (i <- 0 until size) {
+ m.readByte(0xc000 + i) should equal(m.readByte(0xc100 + i)) withClue s"[$i]"
+ }
+ }
+ }
+
+ test("Correctly increment array elements") {
+ EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
+ """
+ |array output [$100] @$c000
+ |void main() {
+ | word i
+ | for i,0,paralleluntil,$100 { output[i] = 0 }
+ | for i,0,paralleluntil,$100 { output[i] += i.lo }
+ |}
+ |""".stripMargin) { m =>
+ for (i <- 0 until 0x100) {
+ m.readByte(0xc000 + i) should equal(i) withClue s"[$i]"
+ }
+ }
+ }
+}
diff --git a/src/test/scala/millfork/test/MemsetSuite.scala b/src/test/scala/millfork/test/MemsetSuite.scala
index e35f0aca..3103af57 100644
--- a/src/test/scala/millfork/test/MemsetSuite.scala
+++ b/src/test/scala/millfork/test/MemsetSuite.scala
@@ -9,26 +9,45 @@ import org.scalatest.{AppendedClues, FunSuite, Matchers}
*/
class MemsetSuite extends FunSuite with Matchers with AppendedClues {
- test("memset $1000") {
- memsetCase(0x1000)
+ test("memset pointer $1000") {
+ memsetPointerCase(0x1000)
}
- test("memset $40") {
- memsetCase(0x40)
+ test("memset pointer $40") {
+ memsetPointerCase(0x40)
}
- test("memset $80") {
- memsetCase(0x80)
+ test("memset pointer $80") {
+ memsetPointerCase(0x80)
}
- test("memset $100") {
- memsetCase(0x100)
+ test("memset pointer $100") {
+ memsetPointerCase(0x100)
}
- test("memset $200") {
- memsetCase(0x200)
+ test("memset pointer $200") {
+ memsetPointerCase(0x200)
}
- test("memset $fff") {
- memsetCase(0xfff)
+ test("memset pointer $fff") {
+ memsetPointerCase(0xfff)
}
- def memsetCase(size: Int): Unit = {
+ test("memset array $1000") {
+ memsetArrayCase(0x1000)
+ }
+ test("memset array $40") {
+ memsetArrayCase(0x40)
+ }
+ test("memset array $80") {
+ memsetArrayCase(0x80)
+ }
+ test("memset array $100") {
+ memsetArrayCase(0x100)
+ }
+ test("memset array $200") {
+ memsetArrayCase(0x200)
+ }
+ test("memset array $fff") {
+ memsetArrayCase(0xfff)
+ }
+
+ def memsetPointerCase(size: Int): Unit = {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80, Cpu.Intel8086)(
"const word SIZE = " + size + """
| array output [SIZE] @$c000
@@ -48,4 +67,47 @@ class MemsetSuite extends FunSuite with Matchers with AppendedClues {
}
}
}
+
+ def memsetArrayCase(size: Int): Unit = {
+ val t = if (size < 256) "byte" else "word"
+ EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80, Cpu.Intel8086)(s"""
+ | const $t SIZE = $size
+ | array output [SIZE] @$$c000
+ | void main () {
+ | $t i
+ | for i,0,paralleluntil,SIZE {
+ | output[i] = 42
+ | }
+ | }
+ """.stripMargin) { m =>
+ for (addr <- 0 until 0x1000) {
+ if (addr < size) {
+ m.readByte(addr + 0xc000) should equal(42) withClue f"$$$addr%04x"
+ } else {
+ m.readByte(addr + 0xc000) should equal(0) withClue f"$$$addr%04x"
+ }
+ }
+ }
+ }
+
+ test ("Tricky memset") {
+ EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80, Cpu.Intel8086)("""
+ | const word SIZE = $800
+ | array output [SIZE] @$c000
+ | void main () {
+ | pointer p
+ | for p,output.addr,paralleluntil,output.addr+SIZE {
+ | p[0] = p.lo
+ | }
+ | }
+ """.stripMargin) { m =>
+ for (addr <- 0 until 0x1000) {
+ if (addr < 0x800) {
+ m.readByte(addr + 0xc000) should equal(addr & 0xff) withClue f"$$$addr%04x"
+ } else {
+ m.readByte(addr + 0xc000) should equal(0) withClue f"$$$addr%04x"
+ }
+ }
+ }
+ }
}
\ No newline at end of file