mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-06 09:33:22 +00:00
The @struct array format; fix the @long format
This commit is contained in:
parent
d9f88cdfad
commit
1862fed70f
@ -20,7 +20,7 @@
|
||||
|
||||
* Added `nullptr`.
|
||||
|
||||
* Arrays can now have elements of types other than `byte` (still limited in size to 1 byte though).
|
||||
* Arrays can now have elements of types other than `byte` (still limited in size to 1 byte though) and be built out of struct literals.
|
||||
|
||||
* Arrays can now be constant.
|
||||
|
||||
@ -42,6 +42,8 @@
|
||||
|
||||
* Fixed `#pragma` not respecting `#if`.
|
||||
|
||||
* Fixed `@long` and `@long_be` array filters.
|
||||
|
||||
* 8080 and LR35902: fixed large stack variables.
|
||||
|
||||
* Optimization improvements.
|
||||
|
@ -110,6 +110,11 @@ An array is initialized with either:
|
||||
* `@long` (=`@long_le`), `@long_be`: similar, but with four bytes
|
||||
`@long [$11223344]` is equivalent to `[$44, $33, $22, $11]`
|
||||
`@long_be [$11223344]` is equivalent to `[$11, $22, $33, $44]`
|
||||
|
||||
* `@struct`: every term of the initializer is interpreted as a struct constructor (see below)
|
||||
and treated as a list of bytes with no padding
|
||||
`@struct [s(1, 2)]` is equivalent to `[1, 2]` when `struct s {byte x, byte y}` is defined
|
||||
`@struct [s(1, 2), s(3, 4)]` is equivalent to `[1, 0, 2, 0, 3, 0, 4, 0]` when `struct s {word x, word y}` is defined
|
||||
|
||||
|
||||
* a list of byte literals and/or other array initializers, surrounded by brackets:
|
||||
@ -131,6 +136,12 @@ The `for`-style expression has a variable, a starting index, a direction, a fina
|
||||
and a parameterizable array initializer.
|
||||
The initializer is repeated for every value of the variable in the given range.
|
||||
|
||||
Struct constructors look like a function call, where the callee name is the name of the struct type
|
||||
and the parameters are the values of fields in the order of declaration.
|
||||
Fields of arithmetic, pointer and enum types are declared using normal expressions.
|
||||
Fields of struct types are declared using struct constructors.
|
||||
Fields of union types cannot be declared.
|
||||
|
||||
What might be useful is the fact that the compiler allows for built-in trigonometric functions
|
||||
in constant expressions only:
|
||||
|
||||
|
73
src/main/scala/millfork/env/Environment.scala
vendored
73
src/main/scala/millfork/env/Environment.scala
vendored
@ -1163,9 +1163,82 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
addThing(array, None)
|
||||
}
|
||||
|
||||
def extractStructArrayContents(expr: Expression, targetType: Option[Type]): List[Expression] = {
|
||||
(targetType, expr) match {
|
||||
|
||||
case (Some(tt: StructType), FunctionCallExpression(fname, fieldValues)) =>
|
||||
maybeGet[Thing](fname) match {
|
||||
case Some(tt2:StructType) if tt2.name == tt.name =>
|
||||
if (tt.fields.length != fieldValues.length) {
|
||||
log.error(s"Invalid number of struct fields for struct const `${tt.name}`", fieldValues.headOption.flatMap(_.position))
|
||||
List.fill(tt.size)(LiteralExpression(0, 1))
|
||||
} else {
|
||||
tt.fields.zip(fieldValues).flatMap {
|
||||
case ((fieldTypeName, _), expr) => extractStructArrayContents(expr, Some(get[Type](fieldTypeName)))
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
log.error(s"Invalid struct type: `$fname`", expr.position)
|
||||
List.fill(tt.size)(LiteralExpression(0, 1))
|
||||
}
|
||||
|
||||
case (Some(tt: StructType), _) =>
|
||||
log.error(s"Invalid struct initializer for type `${tt.name}`", expr.position)
|
||||
List.fill(tt.size)(LiteralExpression(0, 1))
|
||||
|
||||
case (Some(tt: PlainType), _) =>
|
||||
tt.size match {
|
||||
case 1 => List(expr)
|
||||
case 2 => List(FunctionCallExpression("lo", List(expr)), FunctionCallExpression("hi", List(expr)))
|
||||
case n => List.tabulate(n)(i => FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(8 * i, 1))))))
|
||||
}
|
||||
|
||||
case (Some(tt: PointerType), _) => List(FunctionCallExpression("lo", List(expr)), FunctionCallExpression("hi", List(expr)))
|
||||
case (Some(tt: EnumType), _) => List(FunctionCallExpression("byte", List(expr)))
|
||||
|
||||
case (Some(tt), _) =>
|
||||
log.error("Invalid field type for use in array initializers", expr.position)
|
||||
List.fill(tt.size)(LiteralExpression(0, 1))
|
||||
|
||||
case (None, FunctionCallExpression(fname, fieldValues)) =>
|
||||
maybeGet[Thing](fname) match {
|
||||
case Some(tt:StructType) =>
|
||||
if (tt.fields.length != fieldValues.length) {
|
||||
log.error(s"Invalid number of struct fields for struct const `${tt.name}`", fieldValues.headOption.flatMap(_.position))
|
||||
List.fill(tt.size)(LiteralExpression(0, 1))
|
||||
} else {
|
||||
tt.fields.zip(fieldValues).flatMap {
|
||||
case ((fieldTypeName, _), expr) => extractStructArrayContents(expr, Some(get[Type](fieldTypeName)))
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
log.error(s"Invalid struct type: `$fname`", expr.position)
|
||||
Nil
|
||||
}
|
||||
|
||||
case _ =>
|
||||
log.error(s"Invalid struct initializer for unknown type", expr.position)
|
||||
Nil
|
||||
}
|
||||
}
|
||||
|
||||
def checkIfArrayContentsAreSimple(xs: CombinedContents): Unit = {
|
||||
xs.contents.foreach{
|
||||
case x:CombinedContents => checkIfArrayContentsAreSimple(x)
|
||||
case x:LiteralContents => ()
|
||||
case x => log.error(s"Invalid struct array contents", x.position)
|
||||
}
|
||||
}
|
||||
|
||||
def extractArrayContents(contents1: ArrayContents): List[Expression] = contents1 match {
|
||||
case LiteralContents(xs) => xs
|
||||
case CombinedContents(xs) => xs.flatMap(extractArrayContents)
|
||||
case pc@ProcessedContents("struct", xs: CombinedContents) =>
|
||||
checkIfArrayContentsAreSimple(xs)
|
||||
xs.getAllExpressions.flatMap(x => extractStructArrayContents(x, None))
|
||||
case pc@ProcessedContents("struct", _) =>
|
||||
log.error(s"Invalid struct array contents", pc.position)
|
||||
Nil
|
||||
case pc@ProcessedContents(f, xs) => pc.getAllExpressions
|
||||
case ForLoopContents(v, start, end, direction, body) =>
|
||||
(eval(start), eval(end)) match {
|
||||
|
@ -327,18 +327,19 @@ case class ProcessedContents(processor: String, values: ArrayContents) extends A
|
||||
))
|
||||
case "long" | "long_le" =>
|
||||
values.getAllExpressions.flatMap(expr => List(
|
||||
FunctionCallExpression("byte", List(expr)).pos(expr.position),
|
||||
FunctionCallExpression("byte", List(FunctionCallExpression(">>", List(expr, LiteralExpression(8, 1))).pos(expr.position))).pos(expr.position),
|
||||
FunctionCallExpression("byte", List(FunctionCallExpression(">>", List(expr, LiteralExpression(16, 1))).pos(expr.position))).pos(expr.position),
|
||||
FunctionCallExpression("byte", List(FunctionCallExpression(">>", List(expr, LiteralExpression(24, 1))).pos(expr.position))).pos(expr.position)
|
||||
FunctionCallExpression("lo", List(expr)).pos(expr.position),
|
||||
FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(8, 1))).pos(expr.position))).pos(expr.position),
|
||||
FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(16, 1))).pos(expr.position))).pos(expr.position),
|
||||
FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(24, 1))).pos(expr.position))).pos(expr.position)
|
||||
))
|
||||
case "long_be" =>
|
||||
values.getAllExpressions.flatMap(expr => List(
|
||||
FunctionCallExpression("byte", List(FunctionCallExpression(">>", List(expr, LiteralExpression(24, 1))).pos(expr.position))).pos(expr.position),
|
||||
FunctionCallExpression("byte", List(FunctionCallExpression(">>", List(expr, LiteralExpression(16, 1))).pos(expr.position))).pos(expr.position),
|
||||
FunctionCallExpression("byte", List(FunctionCallExpression(">>", List(expr, LiteralExpression(8, 1))).pos(expr.position))).pos(expr.position),
|
||||
FunctionCallExpression("byte", List(expr)).pos(expr.position)
|
||||
FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(24, 1))).pos(expr.position))).pos(expr.position),
|
||||
FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(16, 1))).pos(expr.position))).pos(expr.position),
|
||||
FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(8, 1))).pos(expr.position))).pos(expr.position),
|
||||
FunctionCallExpression("lo", List(expr)).pos(expr.position)
|
||||
))
|
||||
case "struct" => values.getAllExpressions // not used for emitting actual arrays
|
||||
}
|
||||
|
||||
override def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents =
|
||||
|
@ -271,6 +271,19 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
}
|
||||
}
|
||||
|
||||
test("Array filters 2") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||
"""
|
||||
| array x = @long [$1144]
|
||||
| byte output @$c000
|
||||
| void main () {
|
||||
| output = x[0]
|
||||
| }
|
||||
""".stripMargin) { m =>
|
||||
m.readByte(0xc000) should equal(0x44)
|
||||
}
|
||||
}
|
||||
|
||||
test("Const arrays") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||
"""
|
||||
@ -297,4 +310,34 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
| }
|
||||
""".stripMargin)
|
||||
}
|
||||
|
||||
test("Struct array initializers") {
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80)(
|
||||
"""
|
||||
| struct p { byte x, byte y }
|
||||
| struct line { p from, p to }
|
||||
| struct ratio { int32 num, int32 den }
|
||||
| const array data1 @$c000 = @struct [p(2,3), p(4,5)]
|
||||
| const array data2 @$c010 = @struct [line(p(6,7), p(8,9))]
|
||||
| const array data3 @$c020 = @struct [ratio($666, $777)]
|
||||
| void main () { }
|
||||
""".stripMargin) { m =>
|
||||
m.readByte(0xc000) should equal(2)
|
||||
m.readByte(0xc001) should equal(3)
|
||||
m.readByte(0xc002) should equal(4)
|
||||
m.readByte(0xc003) should equal(5)
|
||||
m.readByte(0xc010) should equal(6)
|
||||
m.readByte(0xc011) should equal(7)
|
||||
m.readByte(0xc012) should equal(8)
|
||||
m.readByte(0xc013) should equal(9)
|
||||
m.readByte(0xc020) should equal(0x66)
|
||||
m.readByte(0xc021) should equal(6)
|
||||
m.readByte(0xc022) should equal(0)
|
||||
m.readByte(0xc023) should equal(0)
|
||||
m.readByte(0xc024) should equal(0x77)
|
||||
m.readByte(0xc025) should equal(7)
|
||||
m.readByte(0xc026) should equal(0)
|
||||
m.readByte(0xc027) should equal(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user