From 24a3943501a088d2224634b4d9b8dffc0de32289 Mon Sep 17 00:00:00 2001 From: Karol Stasiak Date: Mon, 18 Jun 2018 02:52:14 +0200 Subject: [PATCH] Array filters (@word, @word_be) --- CHANGELOG.md | 2 ++ docs/lang/literals.md | 9 +++++++ src/main/scala/millfork/env/Constant.scala | 2 ++ src/main/scala/millfork/env/Environment.scala | 26 +++++++++++++++++++ src/main/scala/millfork/node/Node.scala | 18 +++++++++++++ src/main/scala/millfork/parser/MfParser.scala | 9 ++++++- src/test/scala/millfork/test/ArraySuite.scala | 13 ++++++++++ 7 files changed, 78 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30baf141..f9c67897 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ * Added 24-bit `farword` type. +* Special array layouts, e.g. `@word`. + * Fixed invalid offsets for branching instructions. * Fixed incorrectly overlapping local variables. diff --git a/docs/lang/literals.md b/docs/lang/literals.md index 63f4bd55..85ea8e87 100644 --- a/docs/lang/literals.md +++ b/docs/lang/literals.md @@ -66,6 +66,15 @@ An array is initialized with either: * a `for`-style expression +* a format, followed by an array initializer: + + * `@word` (=`@word_le`): for every term of the array initializer, emit two bytes, first being the low byte of the value, second being the high byte: + `@word [$1122]` is equivalent to `[$22, $11]` + + * `@word_be` – like the above, but opposite: + `@word_be [$1122]` is equivalent to `[$11, $22]` + + * a list of byte literals and/or other array initializers, surrounded by brackets: diff --git a/src/main/scala/millfork/env/Constant.scala b/src/main/scala/millfork/env/Constant.scala index c7b52573..7335bbb7 100644 --- a/src/main/scala/millfork/env/Constant.scala +++ b/src/main/scala/millfork/env/Constant.scala @@ -7,6 +7,8 @@ object Constant { val Zero: Constant = NumericConstant(0, 1) val One: Constant = NumericConstant(1, 1) + def apply(i: Long): Constant = NumericConstant(i, minimumSize(i)) + def error(msg: String, position: Option[Position] = None): Constant = { ErrorReporting.error(msg, position) Zero diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index c456d869..1c782599 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -365,6 +365,7 @@ class Environment(val parent: Option[Environment], val prefix: String) { } } + //noinspection ScalaUnnecessaryParentheses,ZeroIndexToHead def eval(e: Expression): Option[Constant] = { e match { case LiteralExpression(value, size) => Some(NumericConstant(value, size)) @@ -410,6 +411,30 @@ class Environment(val parent: Option[Environment], val prefix: String) { ErrorReporting.error("Invalid number of parameters for `lo`", e.position) None } + case "sin" => + if (params.size == 2) { + (eval(params(0)) -> eval(params(1))) match { + case (Some(NumericConstant(angle, _)), Some(NumericConstant(scale, _))) => + val value = (scale * math.sin(angle * math.Pi / 128)).toInt + Some(Constant(value)) + case _ => None + } + } else { + ErrorReporting.error("Invalid number of parameters for `sin`", e.position) + None + } + case "cos" => + if (params.size == 2) { + (eval(params(0)) -> eval(params(1))) match { + case (Some(NumericConstant(angle, _)), Some(NumericConstant(scale, _))) => + val value = (scale * math.cos(angle * math.Pi / 128)).toInt + Some(Constant(value)) + case _ => None + } + } else { + ErrorReporting.error("Invalid number of parameters for `cos`", e.position) + None + } case "nonet" => params match { case List(FunctionCallExpression("<<", ps@List(_, _))) => @@ -711,6 +736,7 @@ class Environment(val parent: Option[Environment], val prefix: String) { def extractArrayContents(contents1: ArrayContents): List[Expression] = contents1 match { case LiteralContents(xs) => xs case CombinedContents(xs) => xs.flatMap(extractArrayContents) + case pc@ProcessedContents(f, xs) => pc.getAllExpressions case ForLoopContents(v, start, end, direction, body) => (eval(start), eval(end)) match { case (Some(NumericConstant(s, sz1)), Some(NumericConstant(e, sz2))) => diff --git a/src/main/scala/millfork/node/Node.scala b/src/main/scala/millfork/node/Node.scala index dd5e2d31..d8f979d8 100644 --- a/src/main/scala/millfork/node/Node.scala +++ b/src/main/scala/millfork/node/Node.scala @@ -159,6 +159,24 @@ case class CombinedContents(contents: List[ArrayContents]) extends ArrayContents CombinedContents(contents.map(_.replaceVariable(variableToReplace, expression))) } +case class ProcessedContents(processor: String, values: ArrayContents) extends ArrayContents { + override def getAllExpressions: List[Expression] = processor match { + case "word" | "word_le" => + values.getAllExpressions.flatMap(expr => List( + FunctionCallExpression("lo", List(expr)), + FunctionCallExpression("hi", List(expr)) + )) + case "word_be" => + values.getAllExpressions.flatMap(expr => List( + FunctionCallExpression("hi", List(expr)), + FunctionCallExpression("lo", List(expr)) + )) + } + + override def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents = + ProcessedContents(processor, values.replaceVariable(variableToReplace, expression)) +} + case class ArrayDeclarationStatement(name: String, bank: Option[String], length: Option[Expression], diff --git a/src/main/scala/millfork/parser/MfParser.scala b/src/main/scala/millfork/parser/MfParser.scala index 267944fb..00be4152 100644 --- a/src/main/scala/millfork/parser/MfParser.scala +++ b/src/main/scala/millfork/parser/MfParser.scala @@ -195,6 +195,13 @@ abstract class MfParser[T](filename: String, input: String, currentDirectory: St def arrayListElement: P[ArrayContents] = arrayStringContents | arrayLoopContents | mfExpression(nonStatementLevel).map(e => LiteralContents(List(e))) + def arrayProcessedContents: P[ArrayContents] = for { + _ <- "@" ~/ HWS + filter <- identifier + _ <- AWS + contents <- arrayContents + } yield ProcessedContents(filter, contents) + def arrayListContents: P[ArrayContents] = ("[" ~/ AWS ~/ arrayListElement.rep(sep = AWS ~ "," ~/ AWS) ~ AWS ~ "]" ~/ Pass).map(c => CombinedContents(c.toList)) val doubleQuotedString: P[List[Char]] = P("\"" ~/ CharsWhile(c => c != '\"' && c != '\n' && c != '\r').! ~ "\"").map(_.toList) @@ -259,7 +266,7 @@ abstract class MfParser[T](filename: String, input: String, currentDirectory: St ForLoopContents(identifier, start, end, fixedDirection, body) } - def arrayContents: P[ArrayContents] = arrayListContents | arrayLoopContents | arrayFileContents | arrayStringContents + def arrayContents: P[ArrayContents] = arrayProcessedContents | arrayListContents | arrayLoopContents | arrayFileContents | arrayStringContents def arrayContentsForAsm: P[RawBytesStatement] = (arrayListContents | arrayStringContents).map(RawBytesStatement) diff --git a/src/test/scala/millfork/test/ArraySuite.scala b/src/test/scala/millfork/test/ArraySuite.scala index 3cda94c3..bd9e29ff 100644 --- a/src/test/scala/millfork/test/ArraySuite.scala +++ b/src/test/scala/millfork/test/ArraySuite.scala @@ -244,4 +244,17 @@ class ArraySuite extends FunSuite with Matchers { } } + + test("Array filters") { + EmuCrossPlatformBenchmarkRun(CpuFamily.M6502, CpuFamily.I80)( + """ + | array x = @word [$1144] + | byte output @$c000 + | void main () { + | output = x[0] + | } + """.stripMargin) { m => + m.readByte(0xc000) should equal(0x44) + } + } }