1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-12 06:29:34 +00:00

Array filters (@word, @word_be)

This commit is contained in:
Karol Stasiak 2018-06-18 02:52:14 +02:00
parent 0f453e2d2c
commit 24a3943501
7 changed files with 78 additions and 1 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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