mirror of
https://github.com/KarolS/millfork.git
synced 2024-09-10 11:54:30 +00:00
Foreach statement; emitting subprocedures
This commit is contained in:
parent
fc10746522
commit
960cee5124
@ -34,6 +34,8 @@ where `11111` is a sequential number and `xx` is the type:
|
|||||||
|
|
||||||
* `fi` – end of an `if` statement
|
* `fi` – end of an `if` statement
|
||||||
|
|
||||||
|
* `fe` – body of an `for` statement over a list
|
||||||
|
|
||||||
* `he` – beginning of the body of a `while` statement
|
* `he` – beginning of the body of a `while` statement
|
||||||
|
|
||||||
* `in` – increment for larger types
|
* `in` – increment for larger types
|
||||||
|
@ -255,7 +255,11 @@ do {
|
|||||||
Syntax:
|
Syntax:
|
||||||
|
|
||||||
```
|
```
|
||||||
for <variable>,<start>,<direction>,<end> {
|
for <variable> , <start> , <direction> , <end> {
|
||||||
|
}
|
||||||
|
for <variable> : <array,enum type or expression> {
|
||||||
|
}
|
||||||
|
for <variable> : [ <comma separated expressions> ] {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -278,6 +282,14 @@ for <variable>,<start>,<direction>,<end> {
|
|||||||
|
|
||||||
There is no `paralleldownto`, because it would do the same as `parallelto`.
|
There is no `paralleldownto`, because it would do the same as `parallelto`.
|
||||||
|
|
||||||
|
* `<array>` – traverse indices of an array, from 0 to length–1
|
||||||
|
|
||||||
|
* `<enum type>` – traverse enum constants of given type, in arbitrary order
|
||||||
|
|
||||||
|
* `<expression>` – traverse from 0 to `expression` – 1
|
||||||
|
|
||||||
|
* `<comma separated expressions>` – traverse every value in the list
|
||||||
|
|
||||||
### `break` and `continue` statements
|
### `break` and `continue` statements
|
||||||
|
|
||||||
Syntax:
|
Syntax:
|
||||||
|
0
docs/stdlib/string.md
Normal file
0
docs/stdlib/string.md
Normal file
@ -7,4 +7,5 @@ import millfork.assembly.AbstractCode
|
|||||||
*/
|
*/
|
||||||
abstract class AbstractCompiler[T <: AbstractCode] {
|
abstract class AbstractCompiler[T <: AbstractCode] {
|
||||||
def compile(ctx: CompilationContext): List[T]
|
def compile(ctx: CompilationContext): List[T]
|
||||||
|
def packHalves(tuple: (List[T], List[T])): List[T] = tuple._1 ++ tuple._2
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package millfork.compiler
|
package millfork.compiler
|
||||||
|
|
||||||
import millfork.CpuFamily
|
import millfork.CompilationFlag
|
||||||
import millfork.assembly.{AbstractCode, BranchingOpcodeMapping}
|
import millfork.assembly.{AbstractCode, BranchingOpcodeMapping}
|
||||||
import millfork.env._
|
import millfork.env._
|
||||||
import millfork.error.ConsoleLogger
|
|
||||||
import millfork.node._
|
import millfork.node._
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -11,11 +10,12 @@ import millfork.node._
|
|||||||
*/
|
*/
|
||||||
abstract class AbstractStatementCompiler[T <: AbstractCode] {
|
abstract class AbstractStatementCompiler[T <: AbstractCode] {
|
||||||
|
|
||||||
def compile(ctx: CompilationContext, statements: List[ExecutableStatement]): List[T] = {
|
def compile(ctx: CompilationContext, statements: List[ExecutableStatement]): (List[T], List[T]) = {
|
||||||
statements.flatMap(s => compile(ctx, s))
|
val chunks = statements.map(s => compile(ctx, s))
|
||||||
|
chunks.flatMap(_._1) -> chunks.flatMap(_._2)
|
||||||
}
|
}
|
||||||
|
|
||||||
def compile(ctx: CompilationContext, statement: ExecutableStatement): List[T]
|
def compile(ctx: CompilationContext, statement: ExecutableStatement): (List[T], List[T])
|
||||||
|
|
||||||
def labelChunk(labelName: String): List[T]
|
def labelChunk(labelName: String): List[T]
|
||||||
|
|
||||||
@ -27,18 +27,24 @@ abstract class AbstractStatementCompiler[T <: AbstractCode] {
|
|||||||
|
|
||||||
def compileExpressionForBranching(ctx: CompilationContext, expr: Expression, branching: BranchSpec): List[T]
|
def compileExpressionForBranching(ctx: CompilationContext, expr: Expression, branching: BranchSpec): List[T]
|
||||||
|
|
||||||
|
def replaceLabel(ctx: CompilationContext, line: T, from: String, to: String): T
|
||||||
|
|
||||||
|
def returnAssemblyStatement: ExecutableStatement
|
||||||
|
|
||||||
|
def callChunk(label: ThingInMemory): List[T]
|
||||||
|
|
||||||
def areBlocksLarge(blocks: List[T]*): Boolean
|
def areBlocksLarge(blocks: List[T]*): Boolean
|
||||||
|
|
||||||
def compileWhileStatement(ctx: CompilationContext, s: WhileStatement): List[T] = {
|
def compileWhileStatement(ctx: CompilationContext, s: WhileStatement): (List[T], List[T]) = {
|
||||||
val start = ctx.nextLabel("wh")
|
val start = ctx.nextLabel("wh")
|
||||||
val middle = ctx.nextLabel("he")
|
val middle = ctx.nextLabel("he")
|
||||||
val inc = ctx.nextLabel("fp")
|
val inc = ctx.nextLabel("fp")
|
||||||
val end = ctx.nextLabel("ew")
|
val end = ctx.nextLabel("ew")
|
||||||
val condType = AbstractExpressionCompiler.getExpressionType(ctx, s.condition)
|
val condType = AbstractExpressionCompiler.getExpressionType(ctx, s.condition)
|
||||||
val bodyBlock = compile(ctx.addLabels(s.labels, Label(end), Label(inc)), s.body)
|
val (bodyBlock, extraBlock) = compile(ctx.addLabels(s.labels, Label(end), Label(inc)), s.body)
|
||||||
val incrementBlock = compile(ctx.addLabels(s.labels, Label(end), Label(inc)), s.increment)
|
val (incrementBlock, extraBlock2) = compile(ctx.addLabels(s.labels, Label(end), Label(inc)), s.increment)
|
||||||
val largeBodyBlock = areBlocksLarge(bodyBlock, incrementBlock)
|
val largeBodyBlock = areBlocksLarge(bodyBlock, incrementBlock)
|
||||||
condType match {
|
(condType match {
|
||||||
case ConstantBooleanType(_, true) =>
|
case ConstantBooleanType(_, true) =>
|
||||||
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||||
case ConstantBooleanType(_, false) => Nil
|
case ConstantBooleanType(_, false) => Nil
|
||||||
@ -63,18 +69,18 @@ abstract class AbstractStatementCompiler[T <: AbstractCode] {
|
|||||||
case _ =>
|
case _ =>
|
||||||
ctx.log.error(s"Illegal type for a condition: `$condType`", s.condition.position)
|
ctx.log.error(s"Illegal type for a condition: `$condType`", s.condition.position)
|
||||||
Nil
|
Nil
|
||||||
}
|
}) -> (extraBlock ++ extraBlock2)
|
||||||
}
|
}
|
||||||
|
|
||||||
def compileDoWhileStatement(ctx: CompilationContext, s: DoWhileStatement): List[T] = {
|
def compileDoWhileStatement(ctx: CompilationContext, s: DoWhileStatement): (List[T], List[T]) = {
|
||||||
val start = ctx.nextLabel("do")
|
val start = ctx.nextLabel("do")
|
||||||
val inc = ctx.nextLabel("fp")
|
val inc = ctx.nextLabel("fp")
|
||||||
val end = ctx.nextLabel("od")
|
val end = ctx.nextLabel("od")
|
||||||
val condType = AbstractExpressionCompiler.getExpressionType(ctx, s.condition)
|
val condType = AbstractExpressionCompiler.getExpressionType(ctx, s.condition)
|
||||||
val bodyBlock = compile(ctx.addLabels(s.labels, Label(end), Label(inc)), s.body)
|
val (bodyBlock, extraBlock) = compile(ctx.addLabels(s.labels, Label(end), Label(inc)), s.body)
|
||||||
val incrementBlock = compile(ctx.addLabels(s.labels, Label(end), Label(inc)), s.increment)
|
val (incrementBlock, extraBlock2) = compile(ctx.addLabels(s.labels, Label(end), Label(inc)), s.increment)
|
||||||
val largeBodyBlock = areBlocksLarge(bodyBlock, incrementBlock)
|
val largeBodyBlock = areBlocksLarge(bodyBlock, incrementBlock)
|
||||||
condType match {
|
(condType match {
|
||||||
case ConstantBooleanType(_, true) =>
|
case ConstantBooleanType(_, true) =>
|
||||||
val conditionBlock = compileExpressionForBranching(ctx, s.condition, NoBranching)
|
val conditionBlock = compileExpressionForBranching(ctx, s.condition, NoBranching)
|
||||||
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
List(labelChunk(start), bodyBlock, labelChunk(inc), incrementBlock, jmpChunk(start), labelChunk(end)).flatten
|
||||||
@ -98,10 +104,10 @@ abstract class AbstractStatementCompiler[T <: AbstractCode] {
|
|||||||
case _ =>
|
case _ =>
|
||||||
ctx.log.error(s"Illegal type for a condition: `$condType`", s.condition.position)
|
ctx.log.error(s"Illegal type for a condition: `$condType`", s.condition.position)
|
||||||
Nil
|
Nil
|
||||||
}
|
}) -> (extraBlock ++ extraBlock2)
|
||||||
}
|
}
|
||||||
|
|
||||||
def compileForStatement(ctx: CompilationContext, f: ForStatement): List[T] = {
|
def compileForStatement(ctx: CompilationContext, f: ForStatement): (List[T], List[T]) = {
|
||||||
// TODO: check sizes
|
// TODO: check sizes
|
||||||
// TODO: special faster cases
|
// TODO: special faster cases
|
||||||
val p = f.position
|
val p = f.position
|
||||||
@ -133,15 +139,17 @@ abstract class AbstractStatementCompiler[T <: AbstractCode] {
|
|||||||
|
|
||||||
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e - 1 =>
|
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e - 1 =>
|
||||||
val end = ctx.nextLabel("of")
|
val end = ctx.nextLabel("of")
|
||||||
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, f.start).pos(p) :: f.body) ++ labelChunk(end)
|
val (main, extra) = compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, f.start).pos(p) :: f.body)
|
||||||
|
main ++ labelChunk(end) -> extra
|
||||||
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s >= e =>
|
case (ForDirection.Until | ForDirection.ParallelUntil, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s >= e =>
|
||||||
Nil
|
Nil -> Nil
|
||||||
|
|
||||||
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e =>
|
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s == e =>
|
||||||
val end = ctx.nextLabel("of")
|
val end = ctx.nextLabel("of")
|
||||||
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, f.start).pos(p) :: f.body) ++ labelChunk(end)
|
val (main, extra) = compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, f.start).pos(p) :: f.body)
|
||||||
|
main ++ labelChunk(end) -> extra
|
||||||
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s > e =>
|
case (ForDirection.To | ForDirection.ParallelTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, _))) if s > e =>
|
||||||
Nil
|
Nil -> Nil
|
||||||
|
|
||||||
case (ForDirection.Until | ForDirection.ParallelUntil, Some(c), Some(NumericConstant(256, _)))
|
case (ForDirection.Until | ForDirection.ParallelUntil, Some(c), Some(NumericConstant(256, _)))
|
||||||
if variable.map(_.typ.size).contains(1) && c.requiredSize == 1 && c.isProvablyNonnegative =>
|
if variable.map(_.typ.size).contains(1) && c.requiredSize == 1 && c.isProvablyNonnegative =>
|
||||||
@ -163,9 +171,10 @@ abstract class AbstractStatementCompiler[T <: AbstractCode] {
|
|||||||
|
|
||||||
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s == e =>
|
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s == e =>
|
||||||
val end = ctx.nextLabel("of")
|
val end = ctx.nextLabel("of")
|
||||||
compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, LiteralExpression(s, ssize)).pos(p) :: f.body) ++ labelChunk(end)
|
val (main, extra) = compile(ctx.addLabels(names, Label(end), Label(end)), Assignment(vex, LiteralExpression(s, ssize)).pos(p) :: f.body)
|
||||||
|
main ++ labelChunk(end) -> extra
|
||||||
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s < e =>
|
case (ForDirection.DownTo, Some(NumericConstant(s, ssize)), Some(NumericConstant(e, esize))) if s < e =>
|
||||||
Nil
|
Nil -> Nil
|
||||||
case (ForDirection.DownTo, Some(NumericConstant(s, 1)), Some(NumericConstant(0, _))) if s > 0 =>
|
case (ForDirection.DownTo, Some(NumericConstant(s, 1)), Some(NumericConstant(0, _))) if s > 0 =>
|
||||||
compile(ctx, List(
|
compile(ctx, List(
|
||||||
Assignment(
|
Assignment(
|
||||||
@ -246,6 +255,131 @@ abstract class AbstractStatementCompiler[T <: AbstractCode] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def tryExtractForEachBodyToNewFunction(variable: String, stmts: List[ExecutableStatement]): (Boolean, List[ExecutableStatement]) = {
|
||||||
|
def inner2(stmt: ExecutableStatement): Option[ExecutableStatement] = stmt match {
|
||||||
|
case s: CompoundStatement => s.flatMap(inner2)
|
||||||
|
case _: BreakStatement => None
|
||||||
|
case _: ReturnStatement => None
|
||||||
|
case _: ContinueStatement => None
|
||||||
|
case s => Some(s)
|
||||||
|
}
|
||||||
|
def inner(stmt: ExecutableStatement): Option[ExecutableStatement] = stmt match {
|
||||||
|
case s: CompoundStatement => if (s.loopVariable == variable) s.flatMap(inner2) else s.flatMap(inner)
|
||||||
|
case _: BreakStatement => None
|
||||||
|
case _: ReturnStatement => None
|
||||||
|
case s@ContinueStatement(l) if l == variable => Some(returnAssemblyStatement.pos(s.position))
|
||||||
|
case _: ContinueStatement => None
|
||||||
|
case s => Some(s)
|
||||||
|
}
|
||||||
|
def toplevel(stmt: ExecutableStatement): Option[ExecutableStatement] = stmt match {
|
||||||
|
case s: IfStatement => s.flatMap(toplevel)
|
||||||
|
case s: CompoundStatement => s.flatMap(inner)
|
||||||
|
case _: BreakStatement => None
|
||||||
|
case _: ReturnStatement => None
|
||||||
|
case s@ContinueStatement(l) if l == variable => Some(returnAssemblyStatement.pos(s.position))
|
||||||
|
case s@ContinueStatement("") => Some(returnAssemblyStatement.pos(s.position))
|
||||||
|
case s => Some(s)
|
||||||
|
}
|
||||||
|
val list = stmts.map(toplevel)
|
||||||
|
if (list.forall(_.isDefined)) true -> list.map(_.get)
|
||||||
|
else false -> stmts
|
||||||
|
}
|
||||||
|
|
||||||
|
def compileForEachStatement(ctx: CompilationContext, f: ForEachStatement): (List[T], List[T]) = {
|
||||||
|
val values = f.values match {
|
||||||
|
case Left(expr) =>
|
||||||
|
expr match {
|
||||||
|
case VariableExpression(id) =>
|
||||||
|
ctx.env.maybeGet[Thing](id + ".array") match {
|
||||||
|
case Some(arr:MfArray) =>
|
||||||
|
return compile(ctx, ForStatement(
|
||||||
|
f.variable,
|
||||||
|
LiteralExpression(0, 1),
|
||||||
|
LiteralExpression(arr.sizeInBytes, Constant.minimumSize(arr.sizeInBytes - 1)),
|
||||||
|
ForDirection.Until,
|
||||||
|
f.body
|
||||||
|
))
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
ctx.env.get[Thing](id) match {
|
||||||
|
case EnumType(_, Some(count)) =>
|
||||||
|
return compile(ctx, ForStatement(
|
||||||
|
f.variable,
|
||||||
|
FunctionCallExpression(id, List(LiteralExpression(0, 1))),
|
||||||
|
FunctionCallExpression(id, List(LiteralExpression(count, 1))),
|
||||||
|
ForDirection.ParallelUntil,
|
||||||
|
f.body
|
||||||
|
))
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
|
||||||
|
return compile(ctx, ForStatement(
|
||||||
|
f.variable,
|
||||||
|
LiteralExpression(0, 1),
|
||||||
|
expr,
|
||||||
|
ForDirection.Until,
|
||||||
|
f.body
|
||||||
|
))
|
||||||
|
case Right(vs) => vs
|
||||||
|
}
|
||||||
|
val endLabel = ctx.nextLabel("fe")
|
||||||
|
val continueLabelPlaceholder = ctx.nextLabel("fe")
|
||||||
|
val (inlinedBody, extra) = compile(ctx.addLabels(Set("", f.variable), Label(endLabel), Label(continueLabelPlaceholder)), f.body)
|
||||||
|
values.size match {
|
||||||
|
case 0 => Nil -> Nil
|
||||||
|
case 1 =>
|
||||||
|
val tuple = compile(ctx,
|
||||||
|
Assignment(
|
||||||
|
VariableExpression(f.variable).pos(f.position),
|
||||||
|
values.head
|
||||||
|
).pos(f.position)
|
||||||
|
)
|
||||||
|
tuple._1 ++ inlinedBody -> tuple._2
|
||||||
|
case valueCount =>
|
||||||
|
val (extractable, extracted) = tryExtractForEachBodyToNewFunction(f.variable, f.body)
|
||||||
|
val (extractedBody, extra2) = compile(ctx.addStack(2), extracted :+ returnAssemblyStatement)
|
||||||
|
val inlinedBodySize = inlinedBody.map(_.sizeInBytes).sum
|
||||||
|
val extractedBodySize = extractedBody.map(_.sizeInBytes).sum
|
||||||
|
val sizeIfInlined = inlinedBodySize * valueCount
|
||||||
|
val sizeIfExtracted = extractedBodySize + 3 * valueCount
|
||||||
|
val expectedOptimizationPotentialFromInlining = valueCount * 2
|
||||||
|
val shouldExtract = true
|
||||||
|
if (ctx.options.flag(CompilationFlag.OptimizeForSonicSpeed)) false
|
||||||
|
else sizeIfInlined - expectedOptimizationPotentialFromInlining > sizeIfExtracted
|
||||||
|
if (shouldExtract) {
|
||||||
|
if (extractable) {
|
||||||
|
val callLabel = ctx.nextLabel("fe")
|
||||||
|
val calls = values.flatMap(expr => compile(ctx,
|
||||||
|
Assignment(
|
||||||
|
VariableExpression(f.variable).pos(f.position),
|
||||||
|
expr
|
||||||
|
)
|
||||||
|
)._1 ++ callChunk(Label(callLabel)))
|
||||||
|
return calls -> (labelChunk(callLabel) ++ extractedBody ++ extra ++ extra2)
|
||||||
|
} else {
|
||||||
|
ctx.log.warn("For loop too complex to extract, inlining", f.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val inlinedEverything = values.flatMap { expr =>
|
||||||
|
val tuple = compile(ctx,
|
||||||
|
Assignment(
|
||||||
|
VariableExpression(f.variable).pos(f.position),
|
||||||
|
expr
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (tuple._2.nonEmpty) ???
|
||||||
|
val compiled = tuple._1 ++ inlinedBody
|
||||||
|
val continueLabel = ctx.nextLabel("fe")
|
||||||
|
compiled.map(replaceLabel(ctx, _, continueLabelPlaceholder, continueLabel)) ++ labelChunk(continueLabel)
|
||||||
|
} ++ labelChunk(endLabel)
|
||||||
|
|
||||||
|
inlinedEverything -> extra
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def compileBreakStatement(ctx: CompilationContext, s: BreakStatement) :List[T] = {
|
def compileBreakStatement(ctx: CompilationContext, s: BreakStatement) :List[T] = {
|
||||||
ctx.breakLabels.get(s.label) match {
|
ctx.breakLabels.get(s.label) match {
|
||||||
case None =>
|
case None =>
|
||||||
@ -268,13 +402,13 @@ abstract class AbstractStatementCompiler[T <: AbstractCode] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def compileIfStatement(ctx: CompilationContext, s: IfStatement): List[T] = {
|
def compileIfStatement(ctx: CompilationContext, s: IfStatement): (List[T], List[T]) = {
|
||||||
val condType = AbstractExpressionCompiler.getExpressionType(ctx, s.condition)
|
val condType = AbstractExpressionCompiler.getExpressionType(ctx, s.condition)
|
||||||
val thenBlock = compile(ctx, s.thenBranch)
|
val (thenBlock, extra1) = compile(ctx, s.thenBranch)
|
||||||
val elseBlock = compile(ctx, s.elseBranch)
|
val (elseBlock, extra2) = compile(ctx, s.elseBranch)
|
||||||
val largeThenBlock = areBlocksLarge(thenBlock)
|
val largeThenBlock = areBlocksLarge(thenBlock)
|
||||||
val largeElseBlock = areBlocksLarge(elseBlock)
|
val largeElseBlock = areBlocksLarge(elseBlock)
|
||||||
condType match {
|
val mainCode: List[T] = condType match {
|
||||||
case ConstantBooleanType(_, true) =>
|
case ConstantBooleanType(_, true) =>
|
||||||
compileExpressionForBranching(ctx, s.condition, NoBranching) ++ thenBlock
|
compileExpressionForBranching(ctx, s.condition, NoBranching) ++ thenBlock
|
||||||
case ConstantBooleanType(_, false) =>
|
case ConstantBooleanType(_, false) =>
|
||||||
@ -373,6 +507,6 @@ abstract class AbstractStatementCompiler[T <: AbstractCode] {
|
|||||||
ctx.log.error(s"Illegal type for a condition: `$condType`", s.condition.position)
|
ctx.log.error(s"Illegal type for a condition: `$condType`", s.condition.position)
|
||||||
Nil
|
Nil
|
||||||
}
|
}
|
||||||
|
mainCode -> (extra1 ++ extra2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,11 @@ abstract class AbstractStatementPreprocessor(ctx: CompilationContext, statements
|
|||||||
val (b, _) = optimizeStmts(body, Map())
|
val (b, _) = optimizeStmts(body, Map())
|
||||||
val (i, _) = optimizeStmts(inc, Map())
|
val (i, _) = optimizeStmts(inc, Map())
|
||||||
DoWhileStatement(b, i, c, labels).pos(pos) -> Map()
|
DoWhileStatement(b, i, c, labels).pos(pos) -> Map()
|
||||||
|
case f@ForEachStatement(v, arr, body) =>
|
||||||
|
for (a <- arr.right.getOrElse(Nil)) cv = search(a, cv)
|
||||||
|
val a = arr.map(_.map(optimizeExpr(_, Map())))
|
||||||
|
val (b, _) = optimizeStmts(body, Map())
|
||||||
|
ForEachStatement(v, a, b).pos(pos) -> Map()
|
||||||
case f@ForStatement(v, st, en, dir, body) =>
|
case f@ForStatement(v, st, en, dir, body) =>
|
||||||
maybeOptimizeForStatement(f) match {
|
maybeOptimizeForStatement(f) match {
|
||||||
case Some(x) => x
|
case Some(x) => x
|
||||||
|
@ -17,7 +17,7 @@ object MosBulkMemoryOperations {
|
|||||||
target.name != f.variable ||
|
target.name != f.variable ||
|
||||||
target.index.containsVariable(f.variable) ||
|
target.index.containsVariable(f.variable) ||
|
||||||
!target.index.isPure ||
|
!target.index.isPure ||
|
||||||
f.direction == ForDirection.DownTo) return MosStatementCompiler.compileForStatement(ctx, f)
|
f.direction == ForDirection.DownTo) return MosStatementCompiler.compileForStatement(ctx, f)._1
|
||||||
ctx.env.getPointy(target.name)
|
ctx.env.getPointy(target.name)
|
||||||
val sizeExpr = f.direction match {
|
val sizeExpr = f.direction match {
|
||||||
case ForDirection.DownTo =>
|
case ForDirection.DownTo =>
|
||||||
@ -31,7 +31,7 @@ object MosBulkMemoryOperations {
|
|||||||
val w = ctx.env.get[Type]("word")
|
val w = ctx.env.get[Type]("word")
|
||||||
val size = ctx.env.eval(sizeExpr) match {
|
val size = ctx.env.eval(sizeExpr) match {
|
||||||
case Some(c) => c.quickSimplify
|
case Some(c) => c.quickSimplify
|
||||||
case _ => return MosStatementCompiler.compileForStatement(ctx, f)
|
case _ => return MosStatementCompiler.compileForStatement(ctx, f)._1
|
||||||
}
|
}
|
||||||
val useTwoRegs = ctx.options.flag(CompilationFlag.OptimizeForSpeed) && ctx.options.zpRegisterSize >= 4
|
val useTwoRegs = ctx.options.flag(CompilationFlag.OptimizeForSpeed) && ctx.options.zpRegisterSize >= 4
|
||||||
val loadReg = MosExpressionCompiler.compile(ctx, SumExpression(List(false -> f.start, false -> target.index), decimal = false), Some(w -> reg), BranchSpec.None) ++ (
|
val loadReg = MosExpressionCompiler.compile(ctx, SumExpression(List(false -> f.start, false -> target.index), decimal = false), Some(w -> reg), BranchSpec.None) ++ (
|
||||||
|
@ -16,7 +16,7 @@ object MosCompiler extends AbstractCompiler[AssemblyLine] {
|
|||||||
|
|
||||||
override def compile(ctx: CompilationContext): List[AssemblyLine] = {
|
override def compile(ctx: CompilationContext): List[AssemblyLine] = {
|
||||||
ctx.env.nameCheck(ctx.function.code)
|
ctx.env.nameCheck(ctx.function.code)
|
||||||
val chunk = MosStatementCompiler.compile(ctx, new MosStatementPreprocessor(ctx, ctx.function.code)())
|
val chunk = packHalves(MosStatementCompiler.compile(ctx, new MosStatementPreprocessor(ctx, ctx.function.code)()))
|
||||||
val zpRegisterSize = ctx.options.zpRegisterSize
|
val zpRegisterSize = ctx.options.zpRegisterSize
|
||||||
|
|
||||||
val storeParamsFromRegisters = (ctx.function.params match {
|
val storeParamsFromRegisters = (ctx.function.params match {
|
||||||
|
@ -28,7 +28,16 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||||||
MosExpressionCompiler.compile(ctx, expr, Some(b, RegisterVariable(MosRegister.A, b)), branching)
|
MosExpressionCompiler.compile(ctx, expr, Some(b, RegisterVariable(MosRegister.A, b)), branching)
|
||||||
}
|
}
|
||||||
|
|
||||||
def compile(ctx: CompilationContext, statement: ExecutableStatement): List[AssemblyLine] = {
|
override def replaceLabel(ctx: CompilationContext, line: AssemblyLine, from: String, to: String): AssemblyLine = line.parameter match {
|
||||||
|
case MemoryAddressConstant(Label(l)) if l == from => line.copy(parameter = MemoryAddressConstant(Label(to)))
|
||||||
|
case _ => line
|
||||||
|
}
|
||||||
|
|
||||||
|
override def returnAssemblyStatement: ExecutableStatement = MosAssemblyStatement(RTS, AddrMode.Implied, LiteralExpression(0,1), Elidability.Elidable)
|
||||||
|
|
||||||
|
override def callChunk(label: ThingInMemory): List[AssemblyLine] = List(AssemblyLine.absolute(JSR, label.toAddress))
|
||||||
|
|
||||||
|
def compile(ctx: CompilationContext, statement: ExecutableStatement): (List[AssemblyLine], List[AssemblyLine]) = {
|
||||||
val env = ctx.env
|
val env = ctx.env
|
||||||
val m = ctx.function
|
val m = ctx.function
|
||||||
val b = env.get[Type]("byte")
|
val b = env.get[Type]("byte")
|
||||||
@ -142,10 +151,10 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||||||
List(AssemblyLine.implied(RTS))
|
List(AssemblyLine.implied(RTS))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
(statement match {
|
val code: (List[AssemblyLine], List[AssemblyLine]) = statement match {
|
||||||
case EmptyStatement(stmts) =>
|
case EmptyStatement(stmts) =>
|
||||||
stmts.foreach(s => compile(ctx, s))
|
stmts.foreach(s => compile(ctx, s))
|
||||||
Nil
|
Nil -> Nil
|
||||||
case MosAssemblyStatement(o, a, x, e) =>
|
case MosAssemblyStatement(o, a, x, e) =>
|
||||||
val c: Constant = x match {
|
val c: Constant = x match {
|
||||||
// TODO: hmmm
|
// TODO: hmmm
|
||||||
@ -165,7 +174,7 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||||||
case Indirect if o != JMP => IndexedZ
|
case Indirect if o != JMP => IndexedZ
|
||||||
case _ => a
|
case _ => a
|
||||||
}
|
}
|
||||||
List(AssemblyLine(o, actualAddrMode, c, e))
|
List(AssemblyLine(o, actualAddrMode, c, e)) -> Nil
|
||||||
case RawBytesStatement(contents) =>
|
case RawBytesStatement(contents) =>
|
||||||
env.extractArrayContents(contents).map { expr =>
|
env.extractArrayContents(contents).map { expr =>
|
||||||
env.eval(expr) match {
|
env.eval(expr) match {
|
||||||
@ -174,16 +183,16 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||||||
ctx.log.error("Non-constant raw byte", position = statement.position)
|
ctx.log.error("Non-constant raw byte", position = statement.position)
|
||||||
AssemblyLine(BYTE, RawByte, Constant.Zero, elidability = Elidability.Fixed)
|
AssemblyLine(BYTE, RawByte, Constant.Zero, elidability = Elidability.Fixed)
|
||||||
}
|
}
|
||||||
}
|
} -> Nil
|
||||||
case Assignment(dest, source) =>
|
case Assignment(dest, source) =>
|
||||||
MosExpressionCompiler.compileAssignment(ctx, source, dest)
|
MosExpressionCompiler.compileAssignment(ctx, source, dest) -> Nil
|
||||||
case ExpressionStatement(e@FunctionCallExpression(name, params)) =>
|
case ExpressionStatement(e@FunctionCallExpression(name, params)) =>
|
||||||
env.lookupFunction(name, params.map(p => MosExpressionCompiler.getExpressionType(ctx, p) -> p)) match {
|
env.lookupFunction(name, params.map(p => MosExpressionCompiler.getExpressionType(ctx, p) -> p)) match {
|
||||||
case Some(i: MacroFunction) =>
|
case Some(i: MacroFunction) =>
|
||||||
val (paramPreparation, inlinedStatements) = MosMacroExpander.inlineFunction(ctx, i, params, e.position)
|
val (paramPreparation, inlinedStatements) = MosMacroExpander.inlineFunction(ctx, i, params, e.position)
|
||||||
paramPreparation ++ compile(ctx.withInlinedEnv(i.environment, ctx.nextLabel("en")), inlinedStatements)
|
paramPreparation ++ compile(ctx.withInlinedEnv(i.environment, ctx.nextLabel("en")), inlinedStatements)._1 -> Nil
|
||||||
case _ =>
|
case _ =>
|
||||||
MosExpressionCompiler.compile(ctx, e, None, NoBranching)
|
MosExpressionCompiler.compile(ctx, e, None, NoBranching) -> Nil
|
||||||
}
|
}
|
||||||
case ExpressionStatement(e) =>
|
case ExpressionStatement(e) =>
|
||||||
e match {
|
e match {
|
||||||
@ -191,11 +200,11 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||||||
ctx.log.warn("Pointless expression statement", statement.position)
|
ctx.log.warn("Pointless expression statement", statement.position)
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
MosExpressionCompiler.compile(ctx, e, None, NoBranching)
|
MosExpressionCompiler.compile(ctx, e, None, NoBranching) -> Nil
|
||||||
case ReturnStatement(None) =>
|
case ReturnStatement(None) =>
|
||||||
// TODO: return type check
|
// TODO: return type check
|
||||||
// TODO: better stackpointer fix
|
// TODO: better stackpointer fix
|
||||||
ctx.function.returnType match {
|
(ctx.function.returnType match {
|
||||||
case _: BooleanType =>
|
case _: BooleanType =>
|
||||||
stackPointerFixBeforeReturn(ctx) ++ returnInstructions
|
stackPointerFixBeforeReturn(ctx) ++ returnInstructions
|
||||||
case t => t.size match {
|
case t => t.size match {
|
||||||
@ -221,11 +230,11 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||||||
stackPointerFixBeforeReturn(ctx) ++
|
stackPointerFixBeforeReturn(ctx) ++
|
||||||
List(AssemblyLine.discardAF(), AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
|
List(AssemblyLine.discardAF(), AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
|
||||||
}
|
}
|
||||||
}
|
}) -> Nil
|
||||||
case s : ReturnDispatchStatement =>
|
case s : ReturnDispatchStatement =>
|
||||||
MosReturnDispatch.compile(ctx, s)
|
MosReturnDispatch.compile(ctx, s) -> Nil
|
||||||
case ReturnStatement(Some(e)) =>
|
case ReturnStatement(Some(e)) =>
|
||||||
m.returnType match {
|
(m.returnType match {
|
||||||
case _: BooleanType =>
|
case _: BooleanType =>
|
||||||
m.returnType.size match {
|
m.returnType.size match {
|
||||||
case 0 =>
|
case 0 =>
|
||||||
@ -262,7 +271,7 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||||||
MosExpressionCompiler.compileAssignment(ctx, e, VariableExpression(ctx.function.name + ".return")) ++
|
MosExpressionCompiler.compileAssignment(ctx, e, VariableExpression(ctx.function.name + ".return")) ++
|
||||||
stackPointerFixBeforeReturn(ctx) ++ List(AssemblyLine.discardAF(), AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
|
stackPointerFixBeforeReturn(ctx) ++ List(AssemblyLine.discardAF(), AssemblyLine.discardXF(), AssemblyLine.discardYF()) ++ returnInstructions
|
||||||
}
|
}
|
||||||
}
|
}) -> Nil
|
||||||
case s: IfStatement =>
|
case s: IfStatement =>
|
||||||
compileIfStatement(ctx, s)
|
compileIfStatement(ctx, s)
|
||||||
case s: WhileStatement =>
|
case s: WhileStatement =>
|
||||||
@ -270,14 +279,17 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||||||
case s: DoWhileStatement =>
|
case s: DoWhileStatement =>
|
||||||
compileDoWhileStatement(ctx, s)
|
compileDoWhileStatement(ctx, s)
|
||||||
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 !source.containsVariable(variable) =>
|
||||||
MosBulkMemoryOperations.compileMemset(ctx, target, source, f)
|
MosBulkMemoryOperations.compileMemset(ctx, target, source, f) -> Nil
|
||||||
case f:ForStatement =>
|
case f:ForStatement =>
|
||||||
compileForStatement(ctx,f)
|
compileForStatement(ctx,f)
|
||||||
|
case f:ForEachStatement =>
|
||||||
|
compileForEachStatement(ctx, f)
|
||||||
case s:BreakStatement =>
|
case s:BreakStatement =>
|
||||||
compileBreakStatement(ctx, s)
|
compileBreakStatement(ctx, s) -> Nil
|
||||||
case s:ContinueStatement =>
|
case s:ContinueStatement =>
|
||||||
compileContinueStatement(ctx, s)
|
compileContinueStatement(ctx, s) -> Nil
|
||||||
}).map(_.positionIfEmpty(statement.position))
|
}
|
||||||
|
code._1.map(_.positionIfEmpty(statement.position)) -> code._2.map(_.positionIfEmpty(statement.position))
|
||||||
}
|
}
|
||||||
|
|
||||||
private def stackPointerFixBeforeReturn(ctx: CompilationContext, preserveA: Boolean = false, preserveX: Boolean = false, preserveY: Boolean = false): List[AssemblyLine] = {
|
private def stackPointerFixBeforeReturn(ctx: CompilationContext, preserveA: Boolean = false, preserveX: Boolean = false, preserveY: Boolean = false): List[AssemblyLine] = {
|
||||||
|
@ -20,8 +20,8 @@ object Z80BulkMemoryOperations {
|
|||||||
* Compiles loops like <code>for i,a,until,b { p[i] = q[i] }</code>
|
* Compiles loops like <code>for i,a,until,b { p[i] = q[i] }</code>
|
||||||
*/
|
*/
|
||||||
def compileMemcpy(ctx: CompilationContext, target: IndexedExpression, source: IndexedExpression, f: ForStatement): List[ZLine] = {
|
def compileMemcpy(ctx: CompilationContext, target: IndexedExpression, source: IndexedExpression, f: ForStatement): List[ZLine] = {
|
||||||
val sourceOffset = removeVariableOnce(f.variable, source.index).getOrElse(return compileForStatement(ctx, f))
|
val sourceOffset = removeVariableOnce(f.variable, source.index).getOrElse(return compileForStatement(ctx, f)._1)
|
||||||
if (!sourceOffset.isPure) return compileForStatement(ctx, f)
|
if (!sourceOffset.isPure) return compileForStatement(ctx, f)._1
|
||||||
val sourceIndexExpression = SumExpression(List(false -> sourceOffset, false -> f.start), decimal = false)
|
val sourceIndexExpression = SumExpression(List(false -> sourceOffset, false -> f.start), decimal = false)
|
||||||
val calculateSource = Z80ExpressionCompiler.calculateAddressToHL(ctx, IndexedExpression(source.name, sourceIndexExpression))
|
val calculateSource = Z80ExpressionCompiler.calculateAddressToHL(ctx, IndexedExpression(source.name, sourceIndexExpression))
|
||||||
compileMemoryBulk(ctx, target, f,
|
compileMemoryBulk(ctx, target, f,
|
||||||
@ -106,7 +106,7 @@ object Z80BulkMemoryOperations {
|
|||||||
*/
|
*/
|
||||||
def compileMemtransform(ctx: CompilationContext, target: IndexedExpression, operator: String, source: Expression, f: ForStatement): List[ZLine] = {
|
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, source.containsVariable(f.variable))
|
||||||
val load = buildMemtransformLoader(ctx, ZRegister.MEM_HL, f.variable, operator, source, c.loopRegister).getOrElse(return compileForStatement(ctx, f))
|
val load = buildMemtransformLoader(ctx, ZRegister.MEM_HL, f.variable, operator, source, c.loopRegister).getOrElse(return compileForStatement(ctx, f)._1)
|
||||||
import scala.util.control.Breaks._
|
import scala.util.control.Breaks._
|
||||||
breakable{
|
breakable{
|
||||||
return compileMemoryBulk(ctx, target, f,
|
return compileMemoryBulk(ctx, target, f,
|
||||||
@ -117,7 +117,7 @@ object Z80BulkMemoryOperations {
|
|||||||
_ => None
|
_ => None
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
compileForStatement(ctx, f)
|
compileForStatement(ctx, f)._1
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,8 +131,8 @@ object Z80BulkMemoryOperations {
|
|||||||
f: ForStatement): List[ZLine] = {
|
f: ForStatement): List[ZLine] = {
|
||||||
import scala.util.control.Breaks._
|
import scala.util.control.Breaks._
|
||||||
val c = determineExtraLoopRegister(ctx, f, source1.containsVariable(f.variable) || source2.containsVariable(f.variable))
|
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))
|
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))
|
val target2Offset = removeVariableOnce(f.variable, target2.index).getOrElse(return compileForStatement(ctx, f)._1)
|
||||||
val target1IndexExpression = if (c.countDownDespiteSyntax) {
|
val target1IndexExpression = if (c.countDownDespiteSyntax) {
|
||||||
SumExpression(List(false -> target1Offset, false -> f.end, true -> LiteralExpression(1, 1)), decimal = false)
|
SumExpression(List(false -> target1Offset, false -> f.end, true -> LiteralExpression(1, 1)), decimal = false)
|
||||||
} else {
|
} else {
|
||||||
@ -148,8 +148,8 @@ object Z80BulkMemoryOperations {
|
|||||||
case _ => false
|
case _ => false
|
||||||
})
|
})
|
||||||
if (fused) {
|
if (fused) {
|
||||||
val load1 = buildMemtransformLoader(ctx, ZRegister.MEM_HL, f.variable, operator1, source1, c.loopRegister).getOrElse(return compileForStatement(ctx, f))
|
val load1 = buildMemtransformLoader(ctx, ZRegister.MEM_HL, f.variable, operator1, source1, c.loopRegister).getOrElse(return compileForStatement(ctx, f)._1)
|
||||||
val load2 = buildMemtransformLoader(ctx, ZRegister.MEM_HL, f.variable, operator2, source2, c.loopRegister).getOrElse(return compileForStatement(ctx, f))
|
val load2 = buildMemtransformLoader(ctx, ZRegister.MEM_HL, f.variable, operator2, source2, c.loopRegister).getOrElse(return compileForStatement(ctx, f)._1)
|
||||||
val loads = load1 ++ load2
|
val loads = load1 ++ load2
|
||||||
breakable{
|
breakable{
|
||||||
return compileMemoryBulk(ctx, target1, f,
|
return compileMemoryBulk(ctx, target1, f,
|
||||||
@ -164,12 +164,12 @@ object Z80BulkMemoryOperations {
|
|||||||
val goodness1 = goodnessForHL(ctx, operator1, source1)
|
val goodness1 = goodnessForHL(ctx, operator1, source1)
|
||||||
val goodness2 = goodnessForHL(ctx, operator2, source2)
|
val goodness2 = goodnessForHL(ctx, operator2, source2)
|
||||||
val loads = if (goodness1 <= goodness2) {
|
val loads = if (goodness1 <= goodness2) {
|
||||||
val load1 = buildMemtransformLoader(ctx, ZRegister.MEM_DE, f.variable, operator1, source1, c.loopRegister).getOrElse(return compileForStatement(ctx, f))
|
val load1 = buildMemtransformLoader(ctx, ZRegister.MEM_DE, f.variable, operator1, source1, c.loopRegister).getOrElse(return compileForStatement(ctx, f)._1)
|
||||||
val load2 = buildMemtransformLoader(ctx, ZRegister.MEM_HL, f.variable, operator2, source2, c.loopRegister).getOrElse(return compileForStatement(ctx, f))
|
val load2 = buildMemtransformLoader(ctx, ZRegister.MEM_HL, f.variable, operator2, source2, c.loopRegister).getOrElse(return compileForStatement(ctx, f)._1)
|
||||||
load1 ++ load2
|
load1 ++ load2
|
||||||
} else {
|
} else {
|
||||||
val load1 = buildMemtransformLoader(ctx, ZRegister.MEM_HL, f.variable, operator1, source1, c.loopRegister).getOrElse(return compileForStatement(ctx, f))
|
val load1 = buildMemtransformLoader(ctx, ZRegister.MEM_HL, f.variable, operator1, source1, c.loopRegister).getOrElse(return compileForStatement(ctx, f)._1)
|
||||||
val load2 = buildMemtransformLoader(ctx, ZRegister.MEM_DE, f.variable, operator2, source2, c.loopRegister).getOrElse(return compileForStatement(ctx, f))
|
val load2 = buildMemtransformLoader(ctx, ZRegister.MEM_DE, f.variable, operator2, source2, c.loopRegister).getOrElse(return compileForStatement(ctx, f)._1)
|
||||||
load1 ++ load2
|
load1 ++ load2
|
||||||
}
|
}
|
||||||
val targetForDE = if (goodness1 <= goodness2) target1 else target2
|
val targetForDE = if (goodness1 <= goodness2) target1 else target2
|
||||||
@ -187,7 +187,7 @@ object Z80BulkMemoryOperations {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileForStatement(ctx, f)
|
compileForStatement(ctx, f)._1
|
||||||
}
|
}
|
||||||
|
|
||||||
private case class ExtraLoopRegister(loopRegister: ZRegister.Value, initC: List[ZLine], nextC: List[ZLine], countDownDespiteSyntax: Boolean)
|
private case class ExtraLoopRegister(loopRegister: ZRegister.Value, initC: List[ZLine], nextC: List[ZLine], countDownDespiteSyntax: Boolean)
|
||||||
@ -398,8 +398,8 @@ object Z80BulkMemoryOperations {
|
|||||||
loadA: ZOpcode.Value => List[ZLine],
|
loadA: ZOpcode.Value => List[ZLine],
|
||||||
z80Bulk: Boolean => Option[ZOpcode.Value]): List[ZLine] = {
|
z80Bulk: Boolean => Option[ZOpcode.Value]): List[ZLine] = {
|
||||||
val one = LiteralExpression(1, 1)
|
val one = LiteralExpression(1, 1)
|
||||||
val targetOffset = removeVariableOnce(f.variable, target.index).getOrElse(return compileForStatement(ctx, f))
|
val targetOffset = removeVariableOnce(f.variable, target.index).getOrElse(return compileForStatement(ctx, f)._1)
|
||||||
if (!targetOffset.isPure) return compileForStatement(ctx, f)
|
if (!targetOffset.isPure) return compileForStatement(ctx, f)._1
|
||||||
val indexVariableSize = ctx.env.get[Variable](f.variable).typ.size
|
val indexVariableSize = ctx.env.get[Variable](f.variable).typ.size
|
||||||
val wrapper = createForLoopPreconditioningIfStatement(ctx, f)
|
val wrapper = createForLoopPreconditioningIfStatement(ctx, f)
|
||||||
val decreasingDespiteSyntax = preferDecreasing && (f.direction == ForDirection.ParallelTo || f.direction == ForDirection.ParallelUntil)
|
val decreasingDespiteSyntax = preferDecreasing && (f.direction == ForDirection.ParallelTo || f.direction == ForDirection.ParallelUntil)
|
||||||
@ -467,7 +467,7 @@ object Z80BulkMemoryOperations {
|
|||||||
Z80StatementCompiler.compile(ctx, IfStatement(
|
Z80StatementCompiler.compile(ctx, IfStatement(
|
||||||
FunctionCallExpression(operator, List(f.start, f.end)),
|
FunctionCallExpression(operator, List(f.start, f.end)),
|
||||||
List(Z80AssemblyStatement(ZOpcode.NOP, NoRegisters, None, LiteralExpression(0, 1), elidability = Elidability.Fixed)),
|
List(Z80AssemblyStatement(ZOpcode.NOP, NoRegisters, None, LiteralExpression(0, 1), elidability = Elidability.Fixed)),
|
||||||
Nil))
|
Nil))._1
|
||||||
}
|
}
|
||||||
|
|
||||||
private def removeVariableOnce(variable: String, expr: Expression): Option[Expression] = {
|
private def removeVariableOnce(variable: String, expr: Expression): Option[Expression] = {
|
||||||
|
@ -14,7 +14,7 @@ object Z80Compiler extends AbstractCompiler[ZLine] {
|
|||||||
|
|
||||||
override def compile(ctx: CompilationContext): List[ZLine] = {
|
override def compile(ctx: CompilationContext): List[ZLine] = {
|
||||||
ctx.env.nameCheck(ctx.function.code)
|
ctx.env.nameCheck(ctx.function.code)
|
||||||
val chunk = Z80StatementCompiler.compile(ctx, new Z80StatementPreprocessor(ctx, ctx.function.code)())
|
val chunk = packHalves(Z80StatementCompiler.compile(ctx, new Z80StatementPreprocessor(ctx, ctx.function.code)()))
|
||||||
val label = ZLine.label(Label(ctx.function.name)).copy(elidability = Elidability.Fixed)
|
val label = ZLine.label(Label(ctx.function.name)).copy(elidability = Elidability.Fixed)
|
||||||
val storeParamsFromRegisters = ctx.function.params match {
|
val storeParamsFromRegisters = ctx.function.params match {
|
||||||
case NormalParamSignature(List(param)) if param.typ.size == 1 =>
|
case NormalParamSignature(List(param)) if param.typ.size == 1 =>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package millfork.compiler.z80
|
package millfork.compiler.z80
|
||||||
|
|
||||||
import millfork.CompilationFlag
|
import millfork.CompilationFlag
|
||||||
import millfork.assembly.BranchingOpcodeMapping
|
import millfork.assembly.{BranchingOpcodeMapping, Elidability}
|
||||||
import millfork.assembly.z80._
|
import millfork.assembly.z80._
|
||||||
import millfork.compiler._
|
import millfork.compiler._
|
||||||
import millfork.env._
|
import millfork.env._
|
||||||
@ -15,14 +15,14 @@ import millfork.error.ConsoleLogger
|
|||||||
object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
||||||
|
|
||||||
|
|
||||||
def compile(ctx: CompilationContext, statement: ExecutableStatement): List[ZLine] = {
|
def compile(ctx: CompilationContext, statement: ExecutableStatement): (List[ZLine], List[ZLine])= {
|
||||||
val options = ctx.options
|
val options = ctx.options
|
||||||
val env = ctx.env
|
val env = ctx.env
|
||||||
val ret = Z80Compiler.restoreRegistersAndReturn(ctx)
|
val ret = Z80Compiler.restoreRegistersAndReturn(ctx)
|
||||||
(statement match {
|
val code: (List[ZLine], List[ZLine]) = statement match {
|
||||||
case EmptyStatement(stmts) =>
|
case EmptyStatement(stmts) =>
|
||||||
stmts.foreach(s => compile(ctx, s))
|
stmts.foreach(s => compile(ctx, s))
|
||||||
Nil
|
Nil -> Nil
|
||||||
case ReturnStatement(None) =>
|
case ReturnStatement(None) =>
|
||||||
fixStackOnReturn(ctx) ++ (ctx.function.returnType match {
|
fixStackOnReturn(ctx) ++ (ctx.function.returnType match {
|
||||||
case _: BooleanType =>
|
case _: BooleanType =>
|
||||||
@ -34,9 +34,9 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
|||||||
ctx.log.warn("Returning without a value", statement.position)
|
ctx.log.warn("Returning without a value", statement.position)
|
||||||
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BC), ZLine.implied(DISCARD_DE)) ++ ret
|
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BC), ZLine.implied(DISCARD_DE)) ++ ret
|
||||||
}
|
}
|
||||||
})
|
}) -> Nil
|
||||||
case ReturnStatement(Some(e)) =>
|
case ReturnStatement(Some(e)) =>
|
||||||
ctx.function.returnType match {
|
(ctx.function.returnType match {
|
||||||
case t: BooleanType => t.size match {
|
case t: BooleanType => t.size match {
|
||||||
case 0 =>
|
case 0 =>
|
||||||
ctx.log.error("Cannot return anything from a void function", statement.position)
|
ctx.log.error("Cannot return anything from a void function", statement.position)
|
||||||
@ -76,15 +76,15 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
|||||||
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BC), ZLine.implied(DISCARD_DE), ZLine.implied(RET))
|
List(ZLine.implied(DISCARD_F), ZLine.implied(DISCARD_A), ZLine.implied(DISCARD_HL), ZLine.implied(DISCARD_BC), ZLine.implied(DISCARD_DE), ZLine.implied(RET))
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}) -> Nil
|
||||||
case Assignment(destination, source) =>
|
case Assignment(destination, source) =>
|
||||||
val sourceType = AbstractExpressionCompiler.getExpressionType(ctx, source)
|
val sourceType = AbstractExpressionCompiler.getExpressionType(ctx, source)
|
||||||
sourceType.size match {
|
(sourceType.size match {
|
||||||
case 0 => ???
|
case 0 => ???
|
||||||
case 1 => Z80ExpressionCompiler.compileToA(ctx, source) ++ Z80ExpressionCompiler.storeA(ctx, destination, sourceType.isSigned)
|
case 1 => Z80ExpressionCompiler.compileToA(ctx, source) ++ Z80ExpressionCompiler.storeA(ctx, destination, sourceType.isSigned)
|
||||||
case 2 => Z80ExpressionCompiler.compileToHL(ctx, source) ++ Z80ExpressionCompiler.storeHL(ctx, destination, sourceType.isSigned)
|
case 2 => Z80ExpressionCompiler.compileToHL(ctx, source) ++ Z80ExpressionCompiler.storeHL(ctx, destination, sourceType.isSigned)
|
||||||
case s => Z80ExpressionCompiler.storeLarge(ctx, destination, source)
|
case s => Z80ExpressionCompiler.storeLarge(ctx, destination, source)
|
||||||
}
|
}) -> Nil
|
||||||
case s: IfStatement =>
|
case s: IfStatement =>
|
||||||
compileIfStatement(ctx, s)
|
compileIfStatement(ctx, s)
|
||||||
case s: WhileStatement =>
|
case s: WhileStatement =>
|
||||||
@ -92,19 +92,19 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
|||||||
case s: DoWhileStatement =>
|
case s: DoWhileStatement =>
|
||||||
compileDoWhileStatement(ctx, s)
|
compileDoWhileStatement(ctx, s)
|
||||||
case s: ReturnDispatchStatement =>
|
case s: ReturnDispatchStatement =>
|
||||||
Z80ReturnDispatch.compile(ctx, s)
|
Z80ReturnDispatch.compile(ctx, s) -> Nil
|
||||||
|
|
||||||
case f@ForStatement(_, _, _, _, List(Assignment(target: IndexedExpression, source: IndexedExpression))) =>
|
case f@ForStatement(_, _, _, _, List(Assignment(target: IndexedExpression, source: IndexedExpression))) =>
|
||||||
Z80BulkMemoryOperations.compileMemcpy(ctx, target, source, f)
|
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 !source.containsVariable(variable) =>
|
||||||
Z80BulkMemoryOperations.compileMemset(ctx, target, source, f)
|
Z80BulkMemoryOperations.compileMemset(ctx, target, source, f) -> Nil
|
||||||
|
|
||||||
case f@ForStatement(variable, _, _, _, List(ExpressionStatement(FunctionCallExpression(
|
case f@ForStatement(variable, _, _, _, List(ExpressionStatement(FunctionCallExpression(
|
||||||
operator@("+=" | "-=" | "|=" | "&=" | "^=" | "+'=" | "-'=" | "<<=" | ">>="),
|
operator@("+=" | "-=" | "|=" | "&=" | "^=" | "+'=" | "-'=" | "<<=" | ">>="),
|
||||||
List(target: IndexedExpression, source: Expression)
|
List(target: IndexedExpression, source: Expression)
|
||||||
)))) =>
|
)))) =>
|
||||||
Z80BulkMemoryOperations.compileMemtransform(ctx, target, operator, source, f)
|
Z80BulkMemoryOperations.compileMemtransform(ctx, target, operator, source, f) -> Nil
|
||||||
|
|
||||||
case f@ForStatement(variable, _, _, _, List(
|
case f@ForStatement(variable, _, _, _, List(
|
||||||
ExpressionStatement(FunctionCallExpression(
|
ExpressionStatement(FunctionCallExpression(
|
||||||
@ -116,7 +116,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
|||||||
List(target2: IndexedExpression, source2: Expression)
|
List(target2: IndexedExpression, source2: Expression)
|
||||||
))
|
))
|
||||||
)) =>
|
)) =>
|
||||||
Z80BulkMemoryOperations.compileMemtransform2(ctx, target1, operator1, source1, target2, operator2, source2, f)
|
Z80BulkMemoryOperations.compileMemtransform2(ctx, target1, operator1, source1, target2, operator2, source2, f) -> Nil
|
||||||
|
|
||||||
case f@ForStatement(variable, _, _, _, List(
|
case f@ForStatement(variable, _, _, _, List(
|
||||||
Assignment(target1: IndexedExpression, source1: Expression),
|
Assignment(target1: IndexedExpression, source1: Expression),
|
||||||
@ -125,7 +125,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
|||||||
List(target2: IndexedExpression, source2: Expression)
|
List(target2: IndexedExpression, source2: Expression)
|
||||||
))
|
))
|
||||||
)) =>
|
)) =>
|
||||||
Z80BulkMemoryOperations.compileMemtransform2(ctx, target1, "=", source1, target2, operator2, source2, f)
|
Z80BulkMemoryOperations.compileMemtransform2(ctx, target1, "=", source1, target2, operator2, source2, f) -> Nil
|
||||||
|
|
||||||
case f@ForStatement(variable, _, _, _, List(
|
case f@ForStatement(variable, _, _, _, List(
|
||||||
ExpressionStatement(FunctionCallExpression(
|
ExpressionStatement(FunctionCallExpression(
|
||||||
@ -134,30 +134,33 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
|||||||
)),
|
)),
|
||||||
Assignment(target2: IndexedExpression, source2: Expression)
|
Assignment(target2: IndexedExpression, source2: Expression)
|
||||||
)) =>
|
)) =>
|
||||||
Z80BulkMemoryOperations.compileMemtransform2(ctx, target1, operator1, source1, target2, "=", source2, f)
|
Z80BulkMemoryOperations.compileMemtransform2(ctx, target1, operator1, source1, target2, "=", source2, f) -> Nil
|
||||||
|
|
||||||
case f@ForStatement(variable, _, _, _, List(
|
case f@ForStatement(variable, _, _, _, List(
|
||||||
Assignment(target1: IndexedExpression, source1: Expression),
|
Assignment(target1: IndexedExpression, source1: Expression),
|
||||||
Assignment(target2: IndexedExpression, source2: Expression)
|
Assignment(target2: IndexedExpression, source2: Expression)
|
||||||
)) =>
|
)) =>
|
||||||
Z80BulkMemoryOperations.compileMemtransform2(ctx, target1, "=", source1, target2, "=", source2, f)
|
Z80BulkMemoryOperations.compileMemtransform2(ctx, target1, "=", source1, target2, "=", source2, f) -> Nil
|
||||||
|
|
||||||
case f: ForStatement =>
|
case f: ForStatement =>
|
||||||
compileForStatement(ctx, f)
|
compileForStatement(ctx, f)
|
||||||
|
case f:ForEachStatement =>
|
||||||
|
compileForEachStatement(ctx, f)
|
||||||
case s: BreakStatement =>
|
case s: BreakStatement =>
|
||||||
compileBreakStatement(ctx, s)
|
compileBreakStatement(ctx, s) -> Nil
|
||||||
case s: ContinueStatement =>
|
case s: ContinueStatement =>
|
||||||
compileContinueStatement(ctx, s)
|
compileContinueStatement(ctx, s) -> Nil
|
||||||
case ExpressionStatement(e@FunctionCallExpression(name, params)) =>
|
case ExpressionStatement(e@FunctionCallExpression(name, params)) =>
|
||||||
env.lookupFunction(name, params.map(p => Z80ExpressionCompiler.getExpressionType(ctx, p) -> p)) match {
|
env.lookupFunction(name, params.map(p => Z80ExpressionCompiler.getExpressionType(ctx, p) -> p)) match {
|
||||||
case Some(i: MacroFunction) =>
|
case Some(i: MacroFunction) =>
|
||||||
val (paramPreparation, inlinedStatements) = Z80MacroExpander.inlineFunction(ctx, i, params, e.position)
|
val (paramPreparation, inlinedStatements) = Z80MacroExpander.inlineFunction(ctx, i, params, e.position)
|
||||||
paramPreparation ++ compile(ctx.withInlinedEnv(i.environment, ctx.nextLabel("en")), inlinedStatements)
|
val (main, extra) = compile(ctx.withInlinedEnv(i.environment, ctx.nextLabel("en")), inlinedStatements)
|
||||||
|
paramPreparation ++ main -> extra
|
||||||
case _ =>
|
case _ =>
|
||||||
Z80ExpressionCompiler.compile(ctx, e, ZExpressionTarget.NOTHING)
|
Z80ExpressionCompiler.compile(ctx, e, ZExpressionTarget.NOTHING) -> Nil
|
||||||
}
|
}
|
||||||
case ExpressionStatement(e) =>
|
case ExpressionStatement(e) =>
|
||||||
Z80ExpressionCompiler.compile(ctx, e, ZExpressionTarget.NOTHING)
|
Z80ExpressionCompiler.compile(ctx, e, ZExpressionTarget.NOTHING) -> Nil
|
||||||
case Z80AssemblyStatement(op, reg, offset, expression, elidability) =>
|
case Z80AssemblyStatement(op, reg, offset, expression, elidability) =>
|
||||||
val param: Constant = expression match {
|
val param: Constant = expression match {
|
||||||
// TODO: hmmm
|
// TODO: hmmm
|
||||||
@ -191,8 +194,9 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
|||||||
}
|
}
|
||||||
case _ => reg
|
case _ => reg
|
||||||
}
|
}
|
||||||
List(ZLine(op, registers, param, elidability))
|
List(ZLine(op, registers, param, elidability)) -> Nil
|
||||||
}).map(_.positionIfEmpty(statement.position))
|
}
|
||||||
|
code._1.map(_.positionIfEmpty(statement.position)) -> code._2.map(_.positionIfEmpty(statement.position))
|
||||||
}
|
}
|
||||||
|
|
||||||
private def fixStackOnReturn(ctx: CompilationContext): List[ZLine] = {
|
private def fixStackOnReturn(ctx: CompilationContext): List[ZLine] = {
|
||||||
@ -265,4 +269,13 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
|||||||
|
|
||||||
override def compileExpressionForBranching(ctx: CompilationContext, expr: Expression, branching: BranchSpec): List[ZLine] =
|
override def compileExpressionForBranching(ctx: CompilationContext, expr: Expression, branching: BranchSpec): List[ZLine] =
|
||||||
Z80ExpressionCompiler.compile(ctx, expr, ZExpressionTarget.NOTHING, branching)
|
Z80ExpressionCompiler.compile(ctx, expr, ZExpressionTarget.NOTHING, branching)
|
||||||
|
|
||||||
|
override def replaceLabel(ctx: CompilationContext, line: ZLine, from: String, to: String): ZLine = line.parameter match {
|
||||||
|
case MemoryAddressConstant(Label(l)) if l == from => line.copy(parameter = MemoryAddressConstant(Label(to)))
|
||||||
|
case _ => line
|
||||||
|
}
|
||||||
|
|
||||||
|
override def returnAssemblyStatement: ExecutableStatement = Z80AssemblyStatement(RET, NoRegisters, None, LiteralExpression(0,1), Elidability.Elidable)
|
||||||
|
|
||||||
|
override def callChunk(label: ThingInMemory): List[ZLine] = List(ZLine(CALL, NoRegisters, label.toAddress))
|
||||||
}
|
}
|
||||||
|
4
src/main/scala/millfork/env/Thing.scala
vendored
4
src/main/scala/millfork/env/Thing.scala
vendored
@ -224,6 +224,8 @@ trait MfArray extends ThingInMemory with IndexableThing {
|
|||||||
def indexType: VariableType
|
def indexType: VariableType
|
||||||
def elementType: VariableType
|
def elementType: VariableType
|
||||||
override def isVolatile: Boolean = false
|
override def isVolatile: Boolean = false
|
||||||
|
/* TODO: what if larger elements? */
|
||||||
|
def sizeInBytes: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
case class UninitializedArray(name: String, /* TODO: what if larger elements? */ sizeInBytes: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val alignment: MemoryAlignment) extends MfArray with UninitializedMemory {
|
case class UninitializedArray(name: String, /* TODO: what if larger elements? */ sizeInBytes: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val alignment: MemoryAlignment) extends MfArray with UninitializedMemory {
|
||||||
@ -256,6 +258,8 @@ case class InitializedArray(name: String, address: Option[Constant], contents: L
|
|||||||
override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse(compilationOptions.platform.defaultCodeBank)
|
override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse(compilationOptions.platform.defaultCodeBank)
|
||||||
|
|
||||||
override def zeropage: Boolean = false
|
override def zeropage: Boolean = false
|
||||||
|
|
||||||
|
override def sizeInBytes: Int = contents.size
|
||||||
}
|
}
|
||||||
|
|
||||||
case class RelativeVariable(name: String, address: Constant, typ: Type, zeropage: Boolean, declaredBank: Option[String], override val isVolatile: Boolean) extends VariableInMemory {
|
case class RelativeVariable(name: String, address: Constant, typ: Type, zeropage: Boolean, declaredBank: Option[String], override val isVolatile: Boolean) extends VariableInMemory {
|
||||||
|
@ -313,6 +313,10 @@ case class RawBytesStatement(contents: ArrayContents) extends ExecutableStatemen
|
|||||||
|
|
||||||
sealed trait CompoundStatement extends ExecutableStatement {
|
sealed trait CompoundStatement extends ExecutableStatement {
|
||||||
def getChildStatements: Seq[Statement]
|
def getChildStatements: Seq[Statement]
|
||||||
|
|
||||||
|
def flatMap(f: ExecutableStatement => Option[ExecutableStatement]): Option[ExecutableStatement]
|
||||||
|
|
||||||
|
def loopVariable: String
|
||||||
}
|
}
|
||||||
|
|
||||||
case class ExpressionStatement(expression: Expression) extends ExecutableStatement {
|
case class ExpressionStatement(expression: Expression) extends ExecutableStatement {
|
||||||
@ -363,12 +367,30 @@ case class IfStatement(condition: Expression, thenBranch: List[ExecutableStateme
|
|||||||
override def getAllExpressions: List[Expression] = condition :: (thenBranch ++ elseBranch).flatMap(_.getAllExpressions)
|
override def getAllExpressions: List[Expression] = condition :: (thenBranch ++ elseBranch).flatMap(_.getAllExpressions)
|
||||||
|
|
||||||
override def getChildStatements: Seq[Statement] = thenBranch ++ elseBranch
|
override def getChildStatements: Seq[Statement] = thenBranch ++ elseBranch
|
||||||
|
|
||||||
|
override def flatMap(f: ExecutableStatement => Option[ExecutableStatement]): Option[ExecutableStatement] = {
|
||||||
|
val t = thenBranch.map(f)
|
||||||
|
val e = elseBranch.map(f)
|
||||||
|
if (t.forall(_.isDefined) && e.forall(_.isDefined)) Some(IfStatement(condition, t.map(_.get), e.map(_.get)).pos(this.position))
|
||||||
|
else None
|
||||||
|
}
|
||||||
|
|
||||||
|
override def loopVariable: String = "-none-"
|
||||||
}
|
}
|
||||||
|
|
||||||
case class WhileStatement(condition: Expression, body: List[ExecutableStatement], increment: List[ExecutableStatement], labels: Set[String] = Set("", "while")) extends CompoundStatement {
|
case class WhileStatement(condition: Expression, body: List[ExecutableStatement], increment: List[ExecutableStatement], labels: Set[String] = Set("", "while")) extends CompoundStatement {
|
||||||
override def getAllExpressions: List[Expression] = condition :: body.flatMap(_.getAllExpressions)
|
override def getAllExpressions: List[Expression] = condition :: body.flatMap(_.getAllExpressions)
|
||||||
|
|
||||||
override def getChildStatements: Seq[Statement] = body ++ increment
|
override def getChildStatements: Seq[Statement] = body ++ increment
|
||||||
|
|
||||||
|
override def flatMap(f: ExecutableStatement => Option[ExecutableStatement]): Option[ExecutableStatement] = {
|
||||||
|
val b = body.map(f)
|
||||||
|
val i = increment.map(f)
|
||||||
|
if (b.forall(_.isDefined) && i.forall(_.isDefined)) Some(WhileStatement(condition, b.map(_.get), i.map(_.get), labels).pos(this.position))
|
||||||
|
else None
|
||||||
|
}
|
||||||
|
|
||||||
|
override def loopVariable: String = "-none-"
|
||||||
}
|
}
|
||||||
|
|
||||||
object ForDirection extends Enumeration {
|
object ForDirection extends Enumeration {
|
||||||
@ -379,12 +401,42 @@ case class ForStatement(variable: String, start: Expression, end: Expression, di
|
|||||||
override def getAllExpressions: List[Expression] = VariableExpression(variable) :: start :: end :: body.flatMap(_.getAllExpressions)
|
override def getAllExpressions: List[Expression] = VariableExpression(variable) :: start :: end :: body.flatMap(_.getAllExpressions)
|
||||||
|
|
||||||
override def getChildStatements: Seq[Statement] = body
|
override def getChildStatements: Seq[Statement] = body
|
||||||
|
|
||||||
|
override def flatMap(f: ExecutableStatement => Option[ExecutableStatement]): Option[ExecutableStatement] = {
|
||||||
|
val b = body.map(f)
|
||||||
|
if (b.forall(_.isDefined)) Some(ForStatement(variable, start, end, direction, b.map(_.get)).pos(this.position))
|
||||||
|
else None
|
||||||
|
}
|
||||||
|
|
||||||
|
override def loopVariable: String = variable
|
||||||
|
}
|
||||||
|
|
||||||
|
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(_), identity) ++ body.flatMap(_.getAllExpressions))
|
||||||
|
|
||||||
|
override def getChildStatements: Seq[Statement] = body
|
||||||
|
override def flatMap(f: ExecutableStatement => Option[ExecutableStatement]): Option[ExecutableStatement] = {
|
||||||
|
val b = body.map(f)
|
||||||
|
if (b.forall(_.isDefined)) Some(ForEachStatement(variable,values, b.map(_.get)).pos(this.position))
|
||||||
|
else None
|
||||||
|
}
|
||||||
|
|
||||||
|
override def loopVariable: String = variable
|
||||||
}
|
}
|
||||||
|
|
||||||
case class DoWhileStatement(body: List[ExecutableStatement], increment: List[ExecutableStatement], condition: Expression, labels: Set[String] = Set("", "do")) extends CompoundStatement {
|
case class DoWhileStatement(body: List[ExecutableStatement], increment: List[ExecutableStatement], condition: Expression, labels: Set[String] = Set("", "do")) extends CompoundStatement {
|
||||||
override def getAllExpressions: List[Expression] = condition :: body.flatMap(_.getAllExpressions)
|
override def getAllExpressions: List[Expression] = condition :: body.flatMap(_.getAllExpressions)
|
||||||
|
|
||||||
override def getChildStatements: Seq[Statement] = body ++ increment
|
override def getChildStatements: Seq[Statement] = body ++ increment
|
||||||
|
|
||||||
|
override def flatMap(f: ExecutableStatement => Option[ExecutableStatement]): Option[ExecutableStatement] = {
|
||||||
|
val b = body.map(f)
|
||||||
|
val i = increment.map(f)
|
||||||
|
if (b.forall(_.isDefined) && i.forall(_.isDefined)) Some(DoWhileStatement(b.map(_.get), i.map(_.get), condition, labels).pos(this.position))
|
||||||
|
else None
|
||||||
|
}
|
||||||
|
|
||||||
|
override def loopVariable: String = "-none-"
|
||||||
}
|
}
|
||||||
|
|
||||||
case class BreakStatement(label: String) extends ExecutableStatement {
|
case class BreakStatement(label: String) extends ExecutableStatement {
|
||||||
|
@ -331,6 +331,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
ifStatement |
|
ifStatement |
|
||||||
whileStatement |
|
whileStatement |
|
||||||
forStatement |
|
forStatement |
|
||||||
|
forEachStatement |
|
||||||
doWhileStatement |
|
doWhileStatement |
|
||||||
breakStatement |
|
breakStatement |
|
||||||
continueStatement |
|
continueStatement |
|
||||||
@ -390,13 +391,19 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
|||||||
} yield Seq(WhileStatement(condition, body.toList, Nil))
|
} yield Seq(WhileStatement(condition, body.toList, Nil))
|
||||||
|
|
||||||
def forStatement: P[Seq[ExecutableStatement]] = for {
|
def forStatement: P[Seq[ExecutableStatement]] = for {
|
||||||
identifier <- "for" ~ SWS ~/ identifier ~/ HWS ~ "," ~/ HWS ~ Pass
|
identifier <- "for" ~ SWS ~ identifier ~ HWS ~ "," ~/ HWS ~ Pass
|
||||||
start <- mfExpression(nonStatementLevel, false) ~ HWS ~ "," ~/ HWS ~/ Pass
|
start <- mfExpression(nonStatementLevel, false) ~ HWS ~ "," ~/ HWS ~/ Pass
|
||||||
direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass
|
direction <- forDirection ~/ HWS ~/ "," ~/ HWS ~/ Pass
|
||||||
end <- mfExpression(nonStatementLevel, false)
|
end <- mfExpression(nonStatementLevel, false)
|
||||||
body <- AWS ~ executableStatements
|
body <- AWS ~ executableStatements
|
||||||
} yield Seq(ForStatement(identifier, start, end, direction, body.toList))
|
} yield Seq(ForStatement(identifier, start, end, direction, body.toList))
|
||||||
|
|
||||||
|
def forEachStatement: P[Seq[ExecutableStatement]] = for {
|
||||||
|
id <- "for" ~ SWS ~/ identifier ~/ HWS ~ ":" ~/ HWS ~ Pass
|
||||||
|
values <- ("[" ~/ AWS ~/ mfExpression(0, false).rep(min = 0, sep = AWS ~ "," ~/ AWS) ~ AWS ~/ "]" ~/ "").map(seq => Right(seq.toList)) | mfExpression(0, false).map(Left(_))
|
||||||
|
body <- AWS ~ executableStatements
|
||||||
|
} yield Seq(ForEachStatement(id, values, body.toList))
|
||||||
|
|
||||||
def inlineAssembly: P[Seq[ExecutableStatement]] = "asm" ~ !letterOrDigit ~/ AWS ~ asmStatements
|
def inlineAssembly: P[Seq[ExecutableStatement]] = "asm" ~ !letterOrDigit ~/ AWS ~ asmStatements
|
||||||
|
|
||||||
//noinspection MutatorLikeMethodIsParameterless
|
//noinspection MutatorLikeMethodIsParameterless
|
||||||
|
@ -114,6 +114,21 @@ class ForLoopSuite extends FunSuite with Matchers {
|
|||||||
| }
|
| }
|
||||||
""".stripMargin)(_.readByte(0xc000) should equal(15))
|
""".stripMargin)(_.readByte(0xc000) should equal(15))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("For-until 2") {
|
||||||
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||||
|
"""
|
||||||
|
| word output @$c000
|
||||||
|
| void main () {
|
||||||
|
| byte i
|
||||||
|
| output = 0
|
||||||
|
| for i : 6 {
|
||||||
|
| output += i
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
""".stripMargin)(_.readByte(0xc000) should equal(15))
|
||||||
|
}
|
||||||
|
|
||||||
test("For-parallelto") {
|
test("For-parallelto") {
|
||||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||||
"""
|
"""
|
||||||
@ -127,6 +142,7 @@ class ForLoopSuite extends FunSuite with Matchers {
|
|||||||
| }
|
| }
|
||||||
""".stripMargin)(_.readByte(0xc000) should equal(15))
|
""".stripMargin)(_.readByte(0xc000) should equal(15))
|
||||||
}
|
}
|
||||||
|
|
||||||
test("For-paralleluntil") {
|
test("For-paralleluntil") {
|
||||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||||
"""
|
"""
|
||||||
@ -198,6 +214,24 @@ class ForLoopSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("Memcpy 2") {
|
||||||
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||||
|
"""
|
||||||
|
| array output[5]@$c001
|
||||||
|
| array input = [0,1,4,9,16,25,36,49]
|
||||||
|
| void main () {
|
||||||
|
| byte i
|
||||||
|
| for i : output {
|
||||||
|
| output[i] = input[i+1]
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
| void _panic(){while(true){}}
|
||||||
|
""".stripMargin){ m=>
|
||||||
|
m.readByte(0xc001) should equal (1)
|
||||||
|
m.readByte(0xc005) should equal (25)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test("Memset with index") {
|
test("Memset with index") {
|
||||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||||
"""
|
"""
|
||||||
@ -215,6 +249,23 @@ class ForLoopSuite extends FunSuite with Matchers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("Memset with index 2") {
|
||||||
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||||
|
"""
|
||||||
|
| array output[5]@$c001
|
||||||
|
| void main () {
|
||||||
|
| byte i
|
||||||
|
| for i : output {
|
||||||
|
| output[i] = 22
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
| void _panic(){while(true){}}
|
||||||
|
""".stripMargin){ m=>
|
||||||
|
m.readByte(0xc001) should equal (22)
|
||||||
|
m.readByte(0xc005) should equal (22)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test("Memset with pointer") {
|
test("Memset with pointer") {
|
||||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||||
"""
|
"""
|
||||||
@ -339,4 +390,30 @@ class ForLoopSuite extends FunSuite with Matchers {
|
|||||||
""".stripMargin)
|
""".stripMargin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
test("For each") {
|
||||||
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
|
||||||
|
"""
|
||||||
|
| array output[$400]@$c000
|
||||||
|
| void main () {
|
||||||
|
| pointer p
|
||||||
|
| for p:[$c000, $c003, $c005, $c007]{
|
||||||
|
| p[0] = 34
|
||||||
|
| }
|
||||||
|
| for p:[$c001, $c004, $c006, $c008]{
|
||||||
|
| p[0] = 42
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
| void _panic(){while(true){}}
|
||||||
|
""".stripMargin) { m =>
|
||||||
|
m.readByte(0xc000) should equal(34)
|
||||||
|
m.readByte(0xc003) should equal(34)
|
||||||
|
m.readByte(0xc005) should equal(34)
|
||||||
|
m.readByte(0xc007) should equal(34)
|
||||||
|
m.readByte(0xc001) should equal(42)
|
||||||
|
m.readByte(0xc004) should equal(42)
|
||||||
|
m.readByte(0xc006) should equal(42)
|
||||||
|
m.readByte(0xc008) should equal(42)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user