1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-09-29 09:55:38 +00:00

Arrays with elements larger than one byte

This commit is contained in:
Karol Stasiak 2019-07-10 16:51:12 +02:00
parent 65338555ad
commit 6d499f3623
25 changed files with 603 additions and 158 deletions

View File

@ -2,6 +2,8 @@
## Current version ## Current version
* Added arrays of elements of size greater than byte.
* Improved passing of register parameters to assembly functions. * Improved passing of register parameters to assembly functions.
* Enabled declaring multiple variables in one line. * Enabled declaring multiple variables in one line.

View File

@ -238,6 +238,15 @@ an access to the element of the array `a` at the location assigned to the key `i
* otherwise: a compile error * otherwise: a compile error
Note that you cannot access a whole array element if it's bigger than 2 bytes, but you can access its fields or take its pointer:
array(int32) a[6]
a[2] // not ok
a[2].b0 // ok
a[2].loword // ok
a[2].pointer // ok
## Built-in functions ## Built-in functions
* `not`: negation of a boolean expression * `not`: negation of a boolean expression

View File

@ -129,7 +129,6 @@ Since 0.3.4, only const arrays can be allocated to ROM, non-const arrays are all
and their contents are uninitialized before a call to `init_rw_memory`. See [the ROM vs RAM guide](../api/rom-vs-ram.md). and their contents are uninitialized before a call to `init_rw_memory`. See [the ROM vs RAM guide](../api/rom-vs-ram.md).
* `<element type>`: type of the elements of the array. * `<element type>`: type of the elements of the array.
It must be of size 1 byte.
If omitted, the default is `byte`. If omitted, the default is `byte`.
* `<size>`: either a constant number, which then defines the size of the array, * `<size>`: either a constant number, which then defines the size of the array,

View File

@ -185,12 +185,12 @@ class AbstractExpressionCompiler[T <: AbstractCode] {
def validateTypeCastAndGetSourceExpressionType(ctx: CompilationContext, typ: Type, params: List[Expression]): Type = { def validateTypeCastAndGetSourceExpressionType(ctx: CompilationContext, typ: Type, params: List[Expression]): Type = {
var failed = false var failed = false
if (typ.name == "pointer") { if (typ.name == "pointer" && typ.name !="pointer" && !typ.isInstanceOf[PointerType]) {
ctx.log.error("Cannot cast into pointer") ctx.log.error("Cannot cast into pointer", params.headOption.flatMap(_.position))
failed = true failed = true
} }
if (params.length != 1) { if (params.length != 1) {
ctx.log.error("Type casting should have exactly one argument") ctx.log.error("Type casting should have exactly one argument", params.headOption.flatMap(_.position))
failed = true failed = true
} }
val sourceType = getExpressionType(ctx, params.head) val sourceType = getExpressionType(ctx, params.head)
@ -286,8 +286,19 @@ object AbstractExpressionCompiler {
ok = false ok = false
} }
} }
for ((fieldName, indices) <- fieldPath) { for ((dot, fieldName, indices) <- fieldPath) {
if (ok) { if (dot && ok) {
fieldName match {
case "addr" => env.get[Type]("pointer")
case "pointer" => env.get[Type]("pointer." + currentType.name)
case "addr.hi" => b
case "addr.lo" => b
case "pointer.hi" => b
case "pointer.lo" => b
log.error(s"Unexpected subfield `$fieldName`", expr.position)
ok = false
}
} else if (ok) {
currentType match { currentType match {
case PointerType(_, _, Some(targetType)) => case PointerType(_, _, Some(targetType)) =>
val tuples = env.getSubvariables(targetType).filter(x => x._1 == "." + fieldName) val tuples = env.getSubvariables(targetType).filter(x => x._1 == "." + fieldName)

View File

@ -128,12 +128,12 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
cv = search(target, cv) cv = search(target, cv)
Assignment(optimizeExpr(target, cv).asInstanceOf[LhsExpression], optimizeExpr(arg, cv)).pos(pos) -> cv Assignment(optimizeExpr(target, cv).asInstanceOf[LhsExpression], optimizeExpr(arg, cv)).pos(pos) -> cv
case Assignment(target:IndexedExpression, arg) if isWordPointy(target.name) => case Assignment(target:IndexedExpression, arg) if isWordPointy(target.name) =>
if (isNonzero(target.index)) {
ctx.log.error("Pointers to word variables can be only indexed by 0")
}
cv = search(arg, cv) cv = search(arg, cv)
cv = search(target, cv) cv = search(target, cv)
Assignment(DerefExpression(VariableExpression(target.name).pos(pos), 0, env.getPointy(target.name).elementType).pos(pos), optimizeExpr(arg, cv)).pos(pos) -> cv Assignment(DerefExpression(SumExpression(List(
false -> FunctionCallExpression("pointer", List(VariableExpression(target.name).pos(pos))).pos(pos),
false -> FunctionCallExpression("<<", List(optimizeExpr(target.index, cv), LiteralExpression(1, 1))).pos(pos)
), decimal = false), 0, env.getPointy(target.name).elementType).pos(pos), optimizeExpr(arg, cv)).pos(pos) -> cv
case Assignment(target:IndexedExpression, arg) => case Assignment(target:IndexedExpression, arg) =>
cv = search(arg, cv) cv = search(arg, cv)
cv = search(target, cv) cv = search(target, cv)
@ -272,6 +272,9 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
case _ => case _ =>
} }
} }
implicit class StringToFunctionNameOps(val functionName: String) {
def <|(exprs: Expression*): Expression = FunctionCallExpression(functionName, exprs.toList).pos(exprs.head.position)
}
// generic warnings: // generic warnings:
expr match { expr match {
case FunctionCallExpression("*" | "*=", params) => case FunctionCallExpression("*" | "*=", params) =>
@ -299,15 +302,55 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
env.eval(index) match { env.eval(index) match {
case Some(NumericConstant(0, _)) => //ok case Some(NumericConstant(0, _)) => //ok
case _ => case _ =>
// TODO: should we keep this?
env.log.error(s"Type `$pt` can be only indexed with 0") env.log.error(s"Type `$pt` can be only indexed with 0")
} }
DerefExpression(result, 0, target) DerefExpression(result, 0, target)
case x if x.isPointy => case x if x.isPointy =>
val (targetType, arraySizeInBytes) = result match {
case VariableExpression(maybePointy) =>
val pointy = env.getPointy(maybePointy)
pointy.elementType -> (pointy match {
case p:ConstantPointy => p.sizeInBytes
case _ => None
})
case _ => env.get[Type](x.pointerTargetName) -> None
}
ctx.log.trace(s"$result is $x and targets $targetType")
env.eval(index) match { env.eval(index) match {
case Some(NumericConstant(n, _)) if n >= 0 && n <= 127 => case Some(NumericConstant(n, _)) if n >= 0 && (targetType.size * n) <= 127 =>
DerefExpression(result, n.toInt, b) x match {
case _: PointerType =>
DerefExpression(result, n.toInt, targetType)
case _ => case _ =>
DerefExpression(SumExpression(List(false -> result, false -> index), decimal = false), 0, b) DerefExpression(
("pointer." + targetType.name) <| result,
n.toInt, targetType)
}
case _ =>
val scaledIndex = arraySizeInBytes match {
case Some(n) if n <= 256 => targetType.size match {
case 1 => "byte" <| index
case 2 => "<<" <| ("byte" <| index, LiteralExpression(1, 1))
case 4 => "<<" <| ("byte" <| index, LiteralExpression(2, 1))
case 8 => "<<" <| ("byte" <| index, LiteralExpression(3, 1))
case _ => "*" <| ("byte" <| index, LiteralExpression(targetType.size, 1))
}
case Some(n) if n <= 512 && targetType.size == 2 =>
"nonet" <| ("<<" <| ("byte" <| index, LiteralExpression(1, 1)))
case _ => targetType.size match {
case 1 => "word" <| index
case 2 => "<<" <| ("word" <| index, LiteralExpression(1, 1))
case 4 => "<<" <| ("word" <| index, LiteralExpression(2, 1))
case 8 => "<<" <| ("word" <| index, LiteralExpression(3, 1))
case _ => "*" <| ("word" <| index, LiteralExpression(targetType.size, 1))
}
}
// TODO: re-cast pointer type
DerefExpression(("pointer." + targetType.name) <| SumExpression(List(
false -> result,
false -> optimizeExpr(scaledIndex, Map())
), decimal = false), 0, targetType)
} }
case _ => case _ =>
ctx.log.error("Not a pointer type on the left-hand side of `[`", pos) ctx.log.error("Not a pointer type on the left-hand side of `[`", pos)
@ -319,9 +362,37 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
for (index <- firstIndices) { for (index <- firstIndices) {
result = applyIndex(result, index) result = applyIndex(result, index)
} }
for ((fieldName, indices) <- fieldPath) { for ((dot, fieldName, indices) <- fieldPath) {
if (ok) { if (dot && ok) {
result = AbstractExpressionCompiler.getExpressionType(env, env.log, result) match { val pointer = result match {
case DerefExpression(inner, 0, _) =>
inner
case DerefExpression(inner, offset, targetType) =>
("pointer." + targetType.name) <| SumExpression(List(
false -> ("pointer" <| inner),
false -> LiteralExpression(offset, 2)
), decimal = false)
case IndexedExpression(name, index) =>
ctx.log.fatal("Oops!")
case _ =>
ok = false
ctx.log.error(s"Not a left-hand-side expression", result.position)
result
}
fieldName match {
case "pointer" => result = pointer
case "pointer.hi" => result = "hi" <| pointer
case "pointer.lo" => result = "lo" <| pointer
case "addr" => result = "pointer" <| pointer
case "addr.hi" => result = "hi" <| pointer
case "addr.lo" => result = "lo" <| pointer
case _ =>
ctx.log.error(s"Unexpected subfield `$fieldName`", result.position)
ok = false
}
} else if (ok) {
val currentResultType = AbstractExpressionCompiler.getExpressionType(env, env.log, result)
result = currentResultType match {
case PointerType(_, _, Some(target)) => case PointerType(_, _, Some(target)) =>
val subvariables = env.getSubvariables(target).filter(x => x._1 == "." + fieldName) val subvariables = env.getSubvariables(target).filter(x => x._1 == "." + fieldName)
if (subvariables.isEmpty) { if (subvariables.isEmpty) {
@ -333,6 +404,7 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
} }
case _ => case _ =>
ctx.log.error("Invalid pointer type on the left-hand side of `->`", result.position) ctx.log.error("Invalid pointer type on the left-hand side of `->`", result.position)
ctx.log.debug(currentResultType.toString)
LiteralExpression(0, 1) LiteralExpression(0, 1)
} }
} }
@ -379,6 +451,42 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
// don't collapse additions, let the later stages deal with it // don't collapse additions, let the later stages deal with it
// expecially important when inside a nonet operation // expecially important when inside a nonet operation
SumExpression(expressions.map{case (minus, arg) => minus -> optimizeExpr(arg, currentVarValues)}, decimal) SumExpression(expressions.map{case (minus, arg) => minus -> optimizeExpr(arg, currentVarValues)}, decimal)
case IndexedExpression(name, index) =>
val pointy = env.getPointy(name)
val targetType = pointy.elementType
targetType.size match {
case 1 => IndexedExpression(name, optimizeExpr(index, Map())).pos(pos)
case _ =>
if (targetType.size != 2) {
ctx.log.error("Cannot access a large array element directly", expr.position)
}
val arraySizeInBytes = pointy match {
case p:ConstantPointy => p.sizeInBytes
case _ => None
}
val scaledIndex = arraySizeInBytes match {
case Some(n) if n <= 256 => targetType.size match {
case 1 => "byte" <| index
case 2 => "<<" <| ("byte" <| index, LiteralExpression(1, 1))
case 4 => "<<" <| ("byte" <| index, LiteralExpression(2, 1))
case 8 => "<<" <| ("byte" <| index, LiteralExpression(3, 1))
case _ => "*" <| ("byte" <| index, LiteralExpression(targetType.size, 1))
}
case Some(n) if n <= 512 && targetType.size == 2 =>
"nonet" <| ("<<" <| ("byte" <| index, LiteralExpression(1, 1)))
case _ => targetType.size match {
case 1 => "word" <| index
case 2 => "<<" <| ("word" <| index, LiteralExpression(1, 1))
case 4 => "<<" <| ("word" <| index, LiteralExpression(2, 1))
case 8 => "<<" <| ("word" <| index, LiteralExpression(3, 1))
case _ => "*" <| ("word" <| index, LiteralExpression(targetType.size, 1))
}
}
DerefExpression(SumExpression(List(
false -> ("pointer" <| VariableExpression(name).pos(pos)),
false -> optimizeExpr(scaledIndex, Map())
), decimal = false), 0, pointy.elementType).pos(pos)
}
case _ => expr // TODO case _ => expr // TODO
} }
} }

View File

@ -333,7 +333,7 @@ object BuiltIns {
def compileInPlaceWordOrLongShiftOps(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, aslRatherThanLsr: Boolean): List[AssemblyLine] = { def compileInPlaceWordOrLongShiftOps(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, aslRatherThanLsr: Boolean): List[AssemblyLine] = {
if (lhs.isInstanceOf[DerefExpression]) { if (lhs.isInstanceOf[DerefExpression]) {
ctx.log.error("Too complex left-hand-side expression") ctx.log.error("Too complex left-hand-side expression", lhs.position)
return MosExpressionCompiler.compileToAX(ctx, lhs) ++ MosExpressionCompiler.compileToAX(ctx, rhs) return MosExpressionCompiler.compileToAX(ctx, lhs) ++ MosExpressionCompiler.compileToAX(ctx, rhs)
} }
val env = ctx.env val env = ctx.env
@ -945,7 +945,7 @@ object BuiltIns {
def compileInPlaceWordMultiplication(ctx: CompilationContext, v: LhsExpression, addend: Expression): List[AssemblyLine] = { def compileInPlaceWordMultiplication(ctx: CompilationContext, v: LhsExpression, addend: Expression): List[AssemblyLine] = {
if (v.isInstanceOf[DerefExpression]) { if (v.isInstanceOf[DerefExpression]) {
ctx.log.error("Too complex left-hand-side expression") ctx.log.error("Too complex left-hand-side expression", v.position)
return MosExpressionCompiler.compileToAX(ctx, v) ++ MosExpressionCompiler.compileToAX(ctx, addend) return MosExpressionCompiler.compileToAX(ctx, v) ++ MosExpressionCompiler.compileToAX(ctx, addend)
} }
val b = ctx.env.get[Type]("byte") val b = ctx.env.get[Type]("byte")
@ -1205,7 +1205,7 @@ object BuiltIns {
return compileInPlaceWordOrLongAddition(ctx, lhs, addend, subtract, decimal = false) return compileInPlaceWordOrLongAddition(ctx, lhs, addend, subtract, decimal = false)
} }
if (lhs.isInstanceOf[DerefExpression]) { if (lhs.isInstanceOf[DerefExpression]) {
ctx.log.error("Too complex left-hand-side expression") ctx.log.error("Too complex left-hand-side expression", lhs.position)
return MosExpressionCompiler.compileToAX(ctx, lhs) ++ MosExpressionCompiler.compileToAX(ctx, addend) return MosExpressionCompiler.compileToAX(ctx, lhs) ++ MosExpressionCompiler.compileToAX(ctx, addend)
} }
val env = ctx.env val env = ctx.env
@ -1534,7 +1534,7 @@ object BuiltIns {
def compileInPlaceWordOrLongBitOp(ctx: CompilationContext, lhs: LhsExpression, param: Expression, operation: Opcode.Value): List[AssemblyLine] = { def compileInPlaceWordOrLongBitOp(ctx: CompilationContext, lhs: LhsExpression, param: Expression, operation: Opcode.Value): List[AssemblyLine] = {
if (lhs.isInstanceOf[DerefExpression]) { if (lhs.isInstanceOf[DerefExpression]) {
ctx.log.error("Too complex left-hand-side expression") ctx.log.error("Too complex left-hand-side expression", lhs.position)
return MosExpressionCompiler.compileToAX(ctx, lhs) ++ MosExpressionCompiler.compileToAX(ctx, param) return MosExpressionCompiler.compileToAX(ctx, lhs) ++ MosExpressionCompiler.compileToAX(ctx, param)
} }
val env = ctx.env val env = ctx.env

View File

@ -337,7 +337,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
ctx.log.error("Writing to a constant array", target.position) ctx.log.error("Writing to a constant array", target.position)
} }
val w = env.get[VariableType]("word") val w = env.get[VariableType]("word")
wrapWordIndexingStorage(prepareWordIndexing(ctx, ConstantPointy(p.value + constIndex, None, if (constIndex.isProvablyZero) p.size else None, w, p.elementType, NoAlignment, p.readOnly), v)) wrapWordIndexingStorage(prepareWordIndexing(ctx, ConstantPointy(p.value + constIndex, None, if (constIndex.isProvablyZero) p.sizeInBytes else None, if (constIndex.isProvablyZero) p.elementCount else None, w, p.elementType, NoAlignment, p.readOnly), v))
case (p: ConstantPointy, Some(v), 1, _) => case (p: ConstantPointy, Some(v), 1, _) =>
if (p.readOnly) { if (p.readOnly) {
ctx.log.error("Writing to a constant array", target.position) ctx.log.error("Writing to a constant array", target.position)
@ -406,13 +406,13 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
Nil Nil
} }
case DerefExpression(inner, offset, targetType) => case DerefExpression(inner, offset, targetType) =>
val (prepare, reg) = getPhysicalPointerForDeref(ctx, inner) val (prepare, addr, am) = getPhysicalPointerForDeref(ctx, inner)
val lo = preserveRegisterIfNeeded(ctx, MosRegister.A, prepare) ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine.indexedY(STA, reg)) val lo = preserveRegisterIfNeeded(ctx, MosRegister.A, prepare) ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine(STA, am, addr))
if (targetType.size == 1) { if (targetType.size == 1) {
lo lo
} else { } else {
lo ++ List(AssemblyLine.immediate(LDA, 0)) ++ lo ++ List(AssemblyLine.immediate(LDA, 0)) ++
List.tabulate(targetType.size - 1)(i => List(AssemblyLine.implied(INY), AssemblyLine.indexedY(STA, reg))).flatten List.tabulate(targetType.size - 1)(i => List(AssemblyLine.implied(INY), AssemblyLine(STA, am, addr))).flatten
} }
} }
} }
@ -437,14 +437,18 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
compile(ctx, expr, Some(p -> env.get[Variable]("__reg.loword")), BranchSpec.None) compile(ctx, expr, Some(p -> env.get[Variable]("__reg.loword")), BranchSpec.None)
} }
def getPhysicalPointerForDeref(ctx: CompilationContext, pointerExpression: Expression): (List[AssemblyLine], ThingInMemory) = { def getPhysicalPointerForDeref(ctx: CompilationContext, pointerExpression: Expression): (List[AssemblyLine], Constant, AddrMode.Value) = {
pointerExpression match { pointerExpression match {
case VariableExpression(name) => case VariableExpression(name) =>
val p = ctx.env.get[ThingInMemory](name) val p = ctx.env.get[ThingInMemory](name)
if (p.zeropage) return Nil -> p if (p.isInstanceOf[MfArray]) return (Nil, p.toAddress, AddrMode.AbsoluteY)
if (p.zeropage) return (Nil, p.toAddress, AddrMode.IndexedY)
case _ => case _ =>
} }
compileToZReg(ctx, pointerExpression) -> ctx.env.get[ThingInMemory]("__reg.loword") ctx.env.eval(pointerExpression) match {
case Some(addr) => (Nil, addr, AddrMode.AbsoluteY)
case _ => (compileToZReg(ctx, pointerExpression), ctx.env.get[ThingInMemory]("__reg.loword").toAddress, AddrMode.IndexedY)
}
} }
def compileStackOffset(ctx: CompilationContext, target: Variable, offset: Int, subbyte: Option[Int]): List[AssemblyLine] = { def compileStackOffset(ctx: CompilationContext, target: Variable, offset: Int, subbyte: Option[Int]): List[AssemblyLine] = {
@ -807,6 +811,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
case IndexedExpression(arrayName, indexExpr) => case IndexedExpression(arrayName, indexExpr) =>
val pointy = env.getPointy(arrayName) val pointy = env.getPointy(arrayName)
AbstractExpressionCompiler.checkIndexType(ctx, pointy, indexExpr) AbstractExpressionCompiler.checkIndexType(ctx, pointy, indexExpr)
if (pointy.elementType.size != 1) ctx.log.fatal("Whee!") // the statement preprocessor should have removed all of those
// TODO: check // TODO: check
val (variableIndex, constantIndex) = env.evalVariableAndConstantSubParts(indexExpr) val (variableIndex, constantIndex) = env.evalVariableAndConstantSubParts(indexExpr)
val variableIndexSize = variableIndex.map(v => getExpressionType(ctx, v).size).getOrElse(0) val variableIndexSize = variableIndex.map(v => getExpressionType(ctx, v).size).getOrElse(0)
@ -870,7 +875,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
prepareWordIndexing(ctx, ConstantPointy( prepareWordIndexing(ctx, ConstantPointy(
a.value + constantIndex, a.value + constantIndex,
None, None,
if (constantIndex.isProvablyZero) a.size else None, if (constantIndex.isProvablyZero) a.sizeInBytes else None,
if (constantIndex.isProvablyZero) a.elementCount else None,
env.get[VariableType]("word"), env.get[VariableType]("word"),
a.elementType, NoAlignment, a.readOnly), v) ++ loadFromReg() a.elementType, NoAlignment, a.readOnly), v) ++ loadFromReg()
case (a: VariablePointy, _, 2, _) => case (a: VariablePointy, _, 2, _) =>
@ -918,18 +924,26 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
} }
} }
case DerefExpression(inner, offset, targetType) => case DerefExpression(inner, offset, targetType) =>
val (prepare, reg) = getPhysicalPointerForDeref(ctx, inner) val (prepare, addr, am) = getPhysicalPointerForDeref(ctx, inner)
targetType.size match { (targetType.size, am) match {
case 1 => case (1, AbsoluteY) =>
prepare ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine.indexedY(LDA, reg)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position) prepare ++ List(AssemblyLine.absolute(LDA, addr + offset)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position)
case 2 => case (1, _) =>
prepare ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine(LDA, am, addr)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position)
case (2, AbsoluteY) =>
prepare ++
List(
AssemblyLine.absolute(LDA, addr + offset),
AssemblyLine.absolute(LDX, addr + offset + 1)) ++
expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
case (2, _) =>
prepare ++ prepare ++
List( List(
AssemblyLine.immediate(LDY, offset+1), AssemblyLine.immediate(LDY, offset+1),
AssemblyLine.indexedY(LDA, reg), AssemblyLine(LDA, am, addr),
AssemblyLine.implied(TAX), AssemblyLine.implied(TAX),
AssemblyLine.implied(DEY), AssemblyLine.implied(DEY),
AssemblyLine.indexedY(LDA, reg)) ++ AssemblyLine(LDA, am, addr)) ++
expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position) expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
case _ => case _ =>
ctx.log.error("Cannot read a large object indirectly") ctx.log.error("Cannot read a large object indirectly")
@ -1287,6 +1301,9 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
l match { l match {
case v: VariableExpression => case v: VariableExpression =>
BuiltIns.compileInPlaceWordOrLongAddition(ctx, v, r, subtract = false, decimal = false) BuiltIns.compileInPlaceWordOrLongAddition(ctx, v, r, subtract = false, decimal = false)
case _ =>
ctx.log.error("Cannot modify large object accessed via such complex expression", l.position)
compile(ctx, r, None, BranchSpec.None)
} }
} }
case "-=" => case "-=" =>
@ -1303,6 +1320,9 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
l match { l match {
case v: VariableExpression => case v: VariableExpression =>
BuiltIns.compileInPlaceWordOrLongAddition(ctx, v, r, subtract = true, decimal = false) BuiltIns.compileInPlaceWordOrLongAddition(ctx, v, r, subtract = true, decimal = false)
case _ =>
ctx.log.error("Cannot modify large object accessed via such complex expression", l.position)
compile(ctx, r, None, BranchSpec.None)
} }
} }
case "+'=" => case "+'=" =>
@ -1319,6 +1339,9 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
l match { l match {
case v: VariableExpression => case v: VariableExpression =>
BuiltIns.compileInPlaceWordOrLongAddition(ctx, v, r, subtract = false, decimal = true) BuiltIns.compileInPlaceWordOrLongAddition(ctx, v, r, subtract = false, decimal = true)
case _ =>
ctx.log.error("Cannot modify large object accessed via such complex expression", l.position)
compile(ctx, r, None, BranchSpec.None)
} }
} }
case "-'=" => case "-'=" =>
@ -1335,6 +1358,9 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
l match { l match {
case v: VariableExpression => case v: VariableExpression =>
BuiltIns.compileInPlaceWordOrLongAddition(ctx, v, r, subtract = true, decimal = true) BuiltIns.compileInPlaceWordOrLongAddition(ctx, v, r, subtract = true, decimal = true)
case _ =>
ctx.log.error("Cannot modify large object accessed via such complex expression", l.position)
compile(ctx, r, None, BranchSpec.None)
} }
} }
case "<<=" => case "<<=" =>
@ -1389,6 +1415,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
BuiltIns.compileInPlaceByteMultiplication(ctx, l, r) BuiltIns.compileInPlaceByteMultiplication(ctx, l, r)
case 2 => case 2 =>
BuiltIns.compileInPlaceWordMultiplication(ctx, l, r) BuiltIns.compileInPlaceWordMultiplication(ctx, l, r)
case _ => ctx.log.fatal("Oops")
} }
case "/=" | "%%=" => case "/=" | "%%=" =>
assertSizesForDivision(ctx, params, inPlace = true) assertSizesForDivision(ctx, params, inPlace = true)
@ -1402,6 +1429,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
} else { } else {
compileAssignment(ctx, FunctionCallExpression("/", List(l, r)).pos(f.position), l) compileAssignment(ctx, FunctionCallExpression("/", List(l, r)).pos(f.position), l)
} }
case _ => ctx.log.fatal("Oops")
} }
case "/" | "%%" => case "/" | "%%" =>
assertSizesForDivision(ctx, params, inPlace = false) assertSizesForDivision(ctx, params, inPlace = false)
@ -1838,69 +1866,122 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
ctx.log.error("Invalid left-hand-side use of `:`") ctx.log.error("Invalid left-hand-side use of `:`")
Nil Nil
case DerefExpression(inner, offset, targetType) => case DerefExpression(inner, offset, targetType) =>
val (prepare, reg) = getPhysicalPointerForDeref(ctx, inner) val (prepare, addr, am) = getPhysicalPointerForDeref(ctx, inner)
env.eval(source) match { env.eval(source) match {
case Some(constant) => case Some(constant) =>
targetType.size match { (targetType.size, am) match {
case 1 => case (1, AbsoluteY) =>
prepare ++ List(
AssemblyLine.immediate(LDA, constant),
AssemblyLine.absolute(STA, addr + offset))
case (1, _) =>
prepare ++ List( prepare ++ List(
AssemblyLine.immediate(LDY, offset), AssemblyLine.immediate(LDY, offset),
AssemblyLine.immediate(LDA, constant), AssemblyLine.immediate(LDA, constant),
AssemblyLine.indexedY(STA, reg)) AssemblyLine(STA, am, addr))
case 2 => case (2, AbsoluteY) =>
prepare ++ List(
AssemblyLine.immediate(LDA, constant.loByte),
AssemblyLine.absolute(STA, addr + offset),
AssemblyLine.immediate(LDA, constant.hiByte),
AssemblyLine.absolute(STA, addr + offset + 1))
case (2, _) =>
prepare ++ List( prepare ++ List(
AssemblyLine.immediate(LDY, offset), AssemblyLine.immediate(LDY, offset),
AssemblyLine.immediate(LDA, constant.loByte), AssemblyLine.immediate(LDA, constant.loByte),
AssemblyLine.indexedY(STA, reg), AssemblyLine(STA, am, addr),
AssemblyLine.implied(INY), AssemblyLine.implied(INY),
AssemblyLine.immediate(LDA, constant.hiByte), AssemblyLine.immediate(LDA, constant.hiByte),
AssemblyLine.indexedY(STA, reg)) AssemblyLine(STA, am, addr))
case _ =>
ctx.log.error("Cannot assign to a large object indirectly", target.position)
Nil
} }
case None => case None =>
source match { source match {
case VariableExpression(vname) => case VariableExpression(vname) =>
val variable = env.get[Variable](vname) val variable = env.get[Variable](vname)
targetType.size match { (targetType.size, am) match {
case 1 => case (1, AbsoluteY) =>
prepare ++
AssemblyLine.variable(ctx, LDA, variable) :+
AssemblyLine.absolute(STA, addr + offset)
case (1, _) =>
prepare ++ prepare ++
AssemblyLine.variable(ctx, LDA, variable) ++ List( AssemblyLine.variable(ctx, LDA, variable) ++ List(
AssemblyLine.immediate(LDY, offset), AssemblyLine.immediate(LDY, offset),
AssemblyLine.indexedY(STA, reg)) AssemblyLine(STA, am, addr))
case 2 => case (2, AbsoluteY) =>
prepare ++
AssemblyLine.variable(ctx, LDA, variable) ++ List(
AssemblyLine.absolute(STA, addr + offset)) ++
AssemblyLine.variable(ctx, LDA, variable, 1) ++ List(
AssemblyLine.absolute(STA, addr + offset + 1))
case (2, _) =>
prepare ++ prepare ++
AssemblyLine.variable(ctx, LDA, variable) ++ List( AssemblyLine.variable(ctx, LDA, variable) ++ List(
AssemblyLine.immediate(LDY, offset), AssemblyLine.immediate(LDY, offset),
AssemblyLine.indexedY(STA, reg)) ++ AssemblyLine(STA, am, addr)) ++
AssemblyLine.variable(ctx, LDA, variable, 1) ++ List( AssemblyLine.variable(ctx, LDA, variable, 1) ++ List(
AssemblyLine.implied(INY), AssemblyLine.implied(INY),
AssemblyLine.indexedY(STA, reg)) AssemblyLine(STA, am, addr))
case _ => case _ =>
ctx.log.error("Cannot assign to a large object indirectly") ctx.log.error("Cannot assign to a large object indirectly", target.position)
Nil Nil
} }
case _ => case _ =>
targetType.size match { (targetType.size, am) match {
case 1 => case (1, _) =>
compile(ctx, source, Some(targetType, RegisterVariable(MosRegister.A, targetType)), BranchSpec.None) ++ compileByteStorage(ctx, MosRegister.A, target) compile(ctx, source, Some(targetType, RegisterVariable(MosRegister.A, targetType)), BranchSpec.None) ++ compileByteStorage(ctx, MosRegister.A, target)
case 2 => case (2, AbsoluteY) =>
val someTuple = Some(targetType, RegisterVariable(MosRegister.AX, targetType)) val someTuple = Some(targetType, RegisterVariable(MosRegister.AX, targetType))
// TODO: optimiza if prepare is empty // TODO: optimize if prepare is empty
if (prepare.isEmpty) {
compile(ctx, source, someTuple, BranchSpec.None) ++ List(
AssemblyLine.absolute(STA, addr + offset),
AssemblyLine.absolute(STX, addr + offset + 1))
} else {
compile(ctx, source, someTuple, BranchSpec.None) ++ List(
AssemblyLine.implied(PHA),
AssemblyLine.implied(TXA),
AssemblyLine.implied(PHA)) ++ prepare ++ List(
AssemblyLine.implied(PLA),
AssemblyLine.absolute(STA, addr + offset + 1),
AssemblyLine.implied(PLA),
AssemblyLine.absolute(STA, addr + offset))
}
case (2, _) =>
val someTuple = Some(targetType, RegisterVariable(MosRegister.AX, targetType))
if (prepare.isEmpty) {
compile(ctx, source, someTuple, BranchSpec.None) ++ List(
AssemblyLine.immediate(LDY, offset),
AssemblyLine.indexedY(STA, addr),
AssemblyLine.implied(TXA),
AssemblyLine.implied(INY),
AssemblyLine.indexedY(STA, addr))
} else {
compile(ctx, source, someTuple, BranchSpec.None) ++ List( compile(ctx, source, someTuple, BranchSpec.None) ++ List(
AssemblyLine.implied(PHA), AssemblyLine.implied(PHA),
AssemblyLine.implied(TXA), AssemblyLine.implied(TXA),
AssemblyLine.implied(PHA)) ++ prepare ++ List( AssemblyLine.implied(PHA)) ++ prepare ++ List(
AssemblyLine.immediate(LDY, offset+1), AssemblyLine.immediate(LDY, offset+1),
AssemblyLine.implied(PLA), AssemblyLine.implied(PLA),
AssemblyLine.indexedY(STA, reg), AssemblyLine.indexedY(STA, addr),
AssemblyLine.implied(PLA), AssemblyLine.implied(PLA),
AssemblyLine.implied(DEY), AssemblyLine.implied(DEY),
AssemblyLine.indexedY(STA, reg)) AssemblyLine.indexedY(STA, addr))
}
case _ => case _ =>
ctx.log.error("Cannot assign to a large object indirectly") ctx.log.error("Cannot assign to a large object indirectly", target.position)
Nil Nil
} }
} }
} }
case i: IndexedExpression =>
if (AbstractExpressionCompiler.getExpressionType(ctx, target).size != 1) {
ctx.log.error("Cannot store a large object this way", target.position)
}
compile(ctx, source, Some(b, RegisterVariable(MosRegister.A, b)), NoBranching) ++ compileByteStorage(ctx, MosRegister.A, target)
case _ => case _ =>
compile(ctx, source, Some(b, RegisterVariable(MosRegister.A, b)), NoBranching) ++ compileByteStorage(ctx, MosRegister.A, target) compile(ctx, source, Some(b, RegisterVariable(MosRegister.A, b)), NoBranching) ++ compileByteStorage(ctx, MosRegister.A, target)
} }
@ -1910,7 +1991,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
if (!ctx.options.flags(CompilationFlag.CheckIndexOutOfBounds)) return Nil if (!ctx.options.flags(CompilationFlag.CheckIndexOutOfBounds)) return Nil
val arrayLength:Int = pointy match { val arrayLength:Int = pointy match {
case _: VariablePointy => return Nil case _: VariablePointy => return Nil
case p: ConstantPointy => p.size match { case p: ConstantPointy => p.sizeInBytes match {
case None => return Nil case None => return Nil
case Some(s) => s case Some(s) => s
} }

View File

@ -1198,23 +1198,32 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
val env = ctx.env val env = ctx.env
val pointy = env.getPointy(i.name) val pointy = env.getPointy(i.name)
AbstractExpressionCompiler.checkIndexType(ctx, pointy, i.index) AbstractExpressionCompiler.checkIndexType(ctx, pointy, i.index)
val elementSize = pointy.elementType.size
val logElemSize = elementSize match {
case 1 => 0
case 2 => 1
case _ =>
ctx.log.error("Cannot access a large object this way", i.position)
0
}
pointy match { pointy match {
case ConstantPointy(baseAddr, _, size, _, _, alignment, readOnly) => case ConstantPointy(baseAddr, _, sizeInBytes, _, _, _, alignment, readOnly) =>
if (forWriting && readOnly) { if (forWriting && readOnly) {
ctx.log.error("Writing to a constant array", i.position) ctx.log.error("Writing to a constant array", i.position)
} }
env.evalVariableAndConstantSubParts(i.index) match { env.evalVariableAndConstantSubParts(i.index) match {
case (None, offset) => List(ZLine.ldImm16(ZRegister.HL, (baseAddr + offset).quickSimplify)) case (None, offset) => List(ZLine.ldImm16(ZRegister.HL, (baseAddr + offset * elementSize).quickSimplify))
case (Some(index), offset) => case (Some(index), offset) =>
val constantPart = (baseAddr + offset).quickSimplify val constantPart = (baseAddr + offset * elementSize).quickSimplify
if (getExpressionType(ctx, i.index).size == 1 && size.exists(_ < 256) && alignment == WithinPageAlignment) { if (getExpressionType(ctx, i.index).size == 1 && sizeInBytes.exists(_ < 256) && alignment == WithinPageAlignment) {
compileToA(ctx, i.index) ++ List( compileToA(ctx, i.index) ++ List.fill(logElemSize)(ZLine.register(ADD, ZRegister.A)) ++ List(
ZLine.imm8(ADD, constantPart.loByte), ZLine.imm8(ADD, constantPart.loByte),
ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ld8(ZRegister.L, ZRegister.A),
ZLine.ldImm8(ZRegister.H, constantPart.hiByte)) ZLine.ldImm8(ZRegister.H, constantPart.hiByte))
} else { } else {
List(ZLine.ldImm16(ZRegister.BC, constantPart)) ++ List(ZLine.ldImm16(ZRegister.BC, constantPart)) ++
stashBCIfChanged(ctx, compileToHL(ctx, index)) ++ stashBCIfChanged(ctx, compileToHL(ctx, index)) ++
List.fill(logElemSize)(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.HL)) ++
List(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC)) List(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
} }
} }
@ -1234,9 +1243,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
case _ => case _ =>
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) { if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
compileToBC(ctx, i.index) ++ compileToBC(ctx, i.index) ++
List( List(ZLine.ldAbs16(ZRegister.HL, varAddr)) ++
ZLine.ldAbs16(ZRegister.HL, varAddr), List.fill(elementSize)(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
} else { } else {
// TODO: is this reasonable? // TODO: is this reasonable?
compileToBC(ctx, i.index) ++ compileToBC(ctx, i.index) ++
@ -1244,14 +1252,14 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
ZLine.ldAbs8(ZRegister.A, varAddr), ZLine.ldAbs8(ZRegister.A, varAddr),
ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ld8(ZRegister.L, ZRegister.A),
ZLine.ldAbs8(ZRegister.A, varAddr + 1), ZLine.ldAbs8(ZRegister.A, varAddr + 1),
ZLine.ld8(ZRegister.H, ZRegister.A), ZLine.ld8(ZRegister.H, ZRegister.A)) ++
ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC)) List.fill(elementSize)(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
} }
} }
case _: StackVariablePointy => case _: StackVariablePointy =>
compileToHL(ctx, VariableExpression(i.name).pos(i.position)) ++ compileToHL(ctx, VariableExpression(i.name).pos(i.position)) ++
stashHLIfChanged(ctx, compileToBC(ctx, i.index)) ++ stashHLIfChanged(ctx, compileToBC(ctx, i.index)) ++
List(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC)) List.fill(elementSize)(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
} }
} }
@ -1489,6 +1497,17 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
} }
} }
def storeConstantWord(ctx: CompilationContext, target: LhsExpression, source: Constant, signedSource: Boolean): List[ZLine] = {
target match {
case e: DerefExpression =>
compileDerefPointer(ctx, e) ++ List(
ZLine.ldImm8(ZRegister.MEM_HL, source.loByte),
ZLine.register(INC_16, ZRegister.HL),
ZLine.ldImm8(ZRegister.MEM_HL, source.hiByte))
case _ => ZLine.ldImm16(ZRegister.HL, source) :: storeHL(ctx, target, signedSource)
}
}
def storeHL(ctx: CompilationContext, target: LhsExpression, signedSource: Boolean): List[ZLine] = { def storeHL(ctx: CompilationContext, target: LhsExpression, signedSource: Boolean): List[ZLine] = {
val env = ctx.env val env = ctx.env
target match { target match {
@ -1535,6 +1554,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
env.evalVariableAndConstantSubParts(indexExpr) match { env.evalVariableAndConstantSubParts(indexExpr) match {
case (None, offset) => ZLine.ld8(ZRegister.A, ZRegister.L) :: storeA(ctx, (p.value + offset).quickSimplify, 1, signedSource) case (None, offset) => ZLine.ld8(ZRegister.A, ZRegister.L) :: storeA(ctx, (p.value + offset).quickSimplify, 1, signedSource)
} }
case _ => ctx.log.fatal("Whee!") // the statement preprocessor should have removed all of those
} }
case SeparateBytesExpression(hi: LhsExpression, lo: LhsExpression) => case SeparateBytesExpression(hi: LhsExpression, lo: LhsExpression) =>
Z80ExpressionCompiler.stashHLIfChanged(ctx, ZLine.ld8(ZRegister.A, ZRegister.L) :: storeA(ctx, lo, signedSource)) ++ Z80ExpressionCompiler.stashHLIfChanged(ctx, ZLine.ld8(ZRegister.A, ZRegister.L) :: storeA(ctx, lo, signedSource)) ++
@ -1846,6 +1866,9 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
Nil Nil
} }
} }
case _ =>
ctx.log.error("Cannot modify large object accessed via such complex expression", lhs.position)
List.fill(size)(Nil)
} }
} }

View File

@ -16,6 +16,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
def compile(ctx: CompilationContext, statement: ExecutableStatement): (List[ZLine], List[ZLine])= { def compile(ctx: CompilationContext, statement: ExecutableStatement): (List[ZLine], List[ZLine])= {
ctx.log.trace(statement.toString)
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)
@ -84,13 +85,23 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
}) -> Nil }) -> Nil
case Assignment(destination, source) => case Assignment(destination, source) =>
val sourceType = AbstractExpressionCompiler.getExpressionType(ctx, source) val sourceType = AbstractExpressionCompiler.getExpressionType(ctx, source)
val targetType = AbstractExpressionCompiler.getExpressionType(ctx, destination)
AbstractExpressionCompiler.checkAssignmentType(ctx, source, targetType)
(sourceType.size match { (sourceType.size match {
case 0 => case 0 =>
ctx.log.error("Cannot assign a void expression", statement.position) ctx.log.error("Cannot assign a void expression", statement.position)
Z80ExpressionCompiler.compile(ctx, source, ZExpressionTarget.NOTHING, BranchSpec.None) ++ Z80ExpressionCompiler.compile(ctx, source, ZExpressionTarget.NOTHING, BranchSpec.None) ++
Z80ExpressionCompiler.compile(ctx, destination, ZExpressionTarget.NOTHING, BranchSpec.None) Z80ExpressionCompiler.compile(ctx, destination, ZExpressionTarget.NOTHING, BranchSpec.None)
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 =>
ctx.env.eval(source) match {
case Some(constantWord) =>
Z80ExpressionCompiler.storeConstantWord(ctx, destination, constantWord, sourceType.isSigned)
case _ =>
val load = Z80ExpressionCompiler.compileToHL(ctx, source)
val store = Z80ExpressionCompiler.storeHL(ctx, destination, sourceType.isSigned)
load ++ store
}
case s => Z80ExpressionCompiler.storeLarge(ctx, destination, source) case s => Z80ExpressionCompiler.storeLarge(ctx, destination, source)
}) -> Nil }) -> Nil
case s: IfStatement => case s: IfStatement =>

View File

@ -29,7 +29,7 @@ class Z80StatementPreprocessor(ctx: CompilationContext, statements: List[Executa
case f: DerefDebuggingExpression => Nil case f: DerefDebuggingExpression => Nil
case IndexedExpression(a, VariableExpression(v)) => if (v == variable) { case IndexedExpression(a, VariableExpression(v)) => if (v == variable) {
ctx.env.maybeGet[Thing](a + ".array") match { ctx.env.maybeGet[Thing](a + ".array") match {
case Some(_: MfArray) => Seq(a) case Some(array: MfArray) if array.elementType.size == 1 => Seq(a)
case _ => Nil case _ => Nil
} }
} else Nil } else Nil
@ -130,7 +130,7 @@ class Z80StatementPreprocessor(ctx: CompilationContext, statements: List[Executa
val array = ctx.env.get[MfArray](name + ".array") val array = ctx.env.get[MfArray](name + ".array")
Assignment( Assignment(
VariableExpression(newVariables(name, f.variable)), VariableExpression(newVariables(name, f.variable)),
FunctionCallExpression("pointer." + array.elementType.name, List( FunctionCallExpression("pointer", List(
SumExpression(List(false -> VariableExpression(name + ".addr"), false -> optStart), decimal = false) SumExpression(List(false -> VariableExpression(name + ".addr"), false -> optStart), decimal = false)
))) )))
}).toList :+ ForStatement(f.variable, optStart, optimizeExpr(f.end, Map()), newDirection, optimizeStmts(newBody, Map())._1), }).toList :+ ForStatement(f.variable, optStart, optimizeExpr(f.end, Map()), newDirection, optimizeStmts(newBody, Map())._1),

View File

@ -468,7 +468,7 @@ object ZBuiltIns {
def performLongInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, opcodeFirst: ZOpcode.Value, opcodeLater: ZOpcode.Value, size: Int, decimal: Boolean = false): List[ZLine] = { def performLongInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, opcodeFirst: ZOpcode.Value, opcodeLater: ZOpcode.Value, size: Int, decimal: Boolean = false): List[ZLine] = {
if (lhs.isInstanceOf[DerefExpression]) { if (lhs.isInstanceOf[DerefExpression]) {
ctx.log.error("Too complex left-hand-side expression") ctx.log.error("Too complex left-hand-side expression", lhs.position)
return Z80ExpressionCompiler.compileToHL(ctx, lhs) ++ Z80ExpressionCompiler.compileToHL(ctx, rhs) return Z80ExpressionCompiler.compileToHL(ctx, lhs) ++ Z80ExpressionCompiler.compileToHL(ctx, rhs)
} }
if (size == 2 && !decimal) { if (size == 2 && !decimal) {

View File

@ -42,6 +42,8 @@ sealed trait Constant {
def +(that: Constant): Constant = CompoundConstant(MathOperator.Plus, this, that) def +(that: Constant): Constant = CompoundConstant(MathOperator.Plus, this, that)
def *(scale: Int): Constant = CompoundConstant(MathOperator.Times, this, NumericConstant(scale, Constant.minimumSize(scale) min 2)).quickSimplify
def -(that: Constant): Constant = CompoundConstant(MathOperator.Minus, this, that) def -(that: Constant): Constant = CompoundConstant(MathOperator.Minus, this, that)
def +(that: Long): Constant = if (that == 0) this else this + NumericConstant(that, minimumSize(that)) def +(that: Long): Constant = if (that == 0) this else this + NumericConstant(that, minimumSize(that))

View File

@ -354,13 +354,13 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
InitializedMemoryVariable InitializedMemoryVariable
UninitializedMemoryVariable UninitializedMemoryVariable
getArrayOrPointer(name) match { getArrayOrPointer(name) match {
case th@InitializedArray(_, _, cs, _, i, e, ro, _) => ConstantPointy(th.toAddress, Some(name), Some(cs.length), i, e, th.alignment, readOnly = ro) case th@InitializedArray(_, _, cs, _, i, e, ro, _) => ConstantPointy(th.toAddress, Some(name), Some(e.size * cs.length), Some(cs.length), i, e, th.alignment, readOnly = ro)
case th@UninitializedArray(_, size, _, i, e, ro, _) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e, th.alignment, readOnly = ro) case th@UninitializedArray(_, elementCount, _, i, e, ro, _) => ConstantPointy(th.toAddress, Some(name), Some(elementCount * e.size), Some(elementCount / e.size), i, e, th.alignment, readOnly = ro)
case th@RelativeArray(_, _, size, _, i, e, ro) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e, NoAlignment, readOnly = ro) case th@RelativeArray(_, _, elementCount, _, i, e, ro) => ConstantPointy(th.toAddress, Some(name), Some(elementCount * e.size), Some(elementCount / e.size), i, e, NoAlignment, readOnly = ro)
case ConstantThing(_, value, typ) if typ.size <= 2 && typ.isPointy => case ConstantThing(_, value, typ) if typ.size <= 2 && typ.isPointy =>
val e = get[VariableType](typ.pointerTargetName) val e = get[VariableType](typ.pointerTargetName)
val w = get[VariableType]("word") val w = get[VariableType]("word")
ConstantPointy(value, None, None, w, e, NoAlignment, readOnly = false) ConstantPointy(value, None, None, None, w, e, NoAlignment, readOnly = false)
case th:VariableInMemory if th.typ.isPointy=> case th:VariableInMemory if th.typ.isPointy=>
val e = get[VariableType](th.typ.pointerTargetName) val e = get[VariableType](th.typ.pointerTargetName)
val w = get[VariableType]("word") val w = get[VariableType]("word")
@ -373,7 +373,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
log.error(s"$name is not a valid pointer or array") log.error(s"$name is not a valid pointer or array")
val b = get[VariableType]("byte") val b = get[VariableType]("byte")
val w = get[VariableType]("word") val w = get[VariableType]("word")
ConstantPointy(Constant.Zero, None, None, w, b, NoAlignment, readOnly = false) ConstantPointy(Constant.Zero, None, None, None, w, b, NoAlignment, readOnly = false)
} }
} }
@ -599,7 +599,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
} }
case IndexedExpression(arrName, index) => case IndexedExpression(arrName, index) =>
getPointy(arrName) match { getPointy(arrName) match {
case ConstantPointy(MemoryAddressConstant(arr:InitializedArray), _, _, _, _, _, _) if arr.readOnly && arr.elementType.size == 1 => case ConstantPointy(MemoryAddressConstant(arr:InitializedArray), _, _, _, _, _, _, _) if arr.readOnly && arr.elementType.size == 1 =>
eval(index).flatMap { eval(index).flatMap {
case NumericConstant(constIndex, _) => case NumericConstant(constIndex, _) =>
if (constIndex >= 0 && constIndex < arr.sizeInBytes) { if (constIndex >= 0 && constIndex < arr.sizeInBytes) {
@ -1303,8 +1303,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
val w = get[VariableType]("word") val w = get[VariableType]("word")
val p = get[Type]("pointer") val p = get[Type]("pointer")
val e = get[VariableType](stmt.elementType) val e = get[VariableType](stmt.elementType)
if (e.size != 1) { if (e.size < 1 && e.size > 127) {
log.error(s"Array elements should be of size 1, `${e.name}` is of size ${e.size}", stmt.position) log.error(s"Array elements should be of size between 1 and 127, `${e.name}` is of size ${e.size}", stmt.position)
} }
stmt.elements match { stmt.elements match {
case None => case None =>
@ -1841,7 +1841,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
case IndirectFieldExpression(inner, firstIndices, fields) => case IndirectFieldExpression(inner, firstIndices, fields) =>
nameCheck(inner) nameCheck(inner)
firstIndices.foreach(nameCheck) firstIndices.foreach(nameCheck)
fields.foreach(f => f._2.foreach(nameCheck)) fields.foreach(f => f._3.foreach(nameCheck))
case SeparateBytesExpression(h, l) => case SeparateBytesExpression(h, l) =>
nameCheck(h) nameCheck(h)
nameCheck(l) nameCheck(l)

View File

@ -21,7 +21,8 @@ case class VariablePointy(addr: Constant, indexType: VariableType, elementType:
case class ConstantPointy(value: Constant, case class ConstantPointy(value: Constant,
name: Option[String], name: Option[String],
size: Option[Int], sizeInBytes: Option[Int],
elementCount: Option[Int],
indexType: VariableType, indexType: VariableType,
elementType: VariableType, elementType: VariableType,
alignment: MemoryAlignment, alignment: MemoryAlignment,

View File

@ -266,12 +266,12 @@ 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 def sizeInBytes: Int
def elementCount: Int
def readOnly: Boolean def readOnly: Boolean
} }
case class UninitializedArray(name: String, /* TODO: what if larger elements? */ sizeInBytes: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val readOnly: Boolean, override val alignment: MemoryAlignment) extends MfArray with UninitializedMemory { case class UninitializedArray(name: String, elementCount: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val readOnly: Boolean, override val alignment: MemoryAlignment) extends MfArray with UninitializedMemory {
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this) override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
override def alloc: VariableAllocationMethod.Value = VariableAllocationMethod.Static override def alloc: VariableAllocationMethod.Value = VariableAllocationMethod.Static
@ -281,9 +281,11 @@ case class UninitializedArray(name: String, /* TODO: what if larger elements? */
override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse("default") override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse("default")
override def zeropage: Boolean = false override def zeropage: Boolean = false
override def sizeInBytes: Int = elementCount * elementType.size
} }
case class RelativeArray(name: String, address: Constant, sizeInBytes: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val readOnly: Boolean) extends MfArray { case class RelativeArray(name: String, address: Constant, elementCount: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val readOnly: Boolean) extends MfArray {
override def toAddress: Constant = address override def toAddress: Constant = address
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false) override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
@ -291,6 +293,8 @@ case class RelativeArray(name: String, address: Constant, sizeInBytes: Int, decl
override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse("default") override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse("default")
override def zeropage: Boolean = false override def zeropage: Boolean = false
override def sizeInBytes: Int = elementCount * elementType.size
} }
case class InitializedArray(name: String, address: Option[Constant], contents: Seq[Expression], declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val readOnly: Boolean, override val alignment: MemoryAlignment) extends MfArray with PreallocableThing { case class InitializedArray(name: String, address: Option[Constant], contents: Seq[Expression], declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val readOnly: Boolean, override val alignment: MemoryAlignment) extends MfArray with PreallocableThing {
@ -303,7 +307,9 @@ case class InitializedArray(name: String, address: Option[Constant], contents: S
override def zeropage: Boolean = false override def zeropage: Boolean = false
override def sizeInBytes: Int = contents.size override def elementCount: Int = contents.size
override def sizeInBytes: Int = contents.size * elementType.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 {

View File

@ -70,7 +70,7 @@ abstract class CallGraph(program: Program, log: Logger) {
case IndirectFieldExpression(root, firstIndices, fields) => case IndirectFieldExpression(root, firstIndices, fields) =>
add(currentFunction, callingFunctions, root) add(currentFunction, callingFunctions, root)
firstIndices.foreach(i => add(currentFunction, callingFunctions, i)) firstIndices.foreach(i => add(currentFunction, callingFunctions, i))
fields.foreach(f => f._2.foreach(i => add(currentFunction, callingFunctions, i))) fields.foreach(f => f._3.foreach(i => add(currentFunction, callingFunctions, i)))
case _ => () case _ => ()
} }
} }

View File

@ -237,32 +237,32 @@ case class IndexedExpression(name: String, index: Expression) extends LhsExpress
override def getAllIdentifiers: Set[String] = index.getAllIdentifiers + name override def getAllIdentifiers: Set[String] = index.getAllIdentifiers + name
} }
case class IndirectFieldExpression(root: Expression, firstIndices: Seq[Expression], fields: Seq[(String, Seq[Expression])]) extends LhsExpression { case class IndirectFieldExpression(root: Expression, firstIndices: Seq[Expression], fields: Seq[(Boolean, String, Seq[Expression])]) extends LhsExpression {
override def replaceVariable(variable: String, actualParam: Expression): Expression = override def replaceVariable(variable: String, actualParam: Expression): Expression =
IndirectFieldExpression( IndirectFieldExpression(
root.replaceVariable(variable, actualParam), root.replaceVariable(variable, actualParam),
firstIndices.map(_.replaceVariable(variable, actualParam)), firstIndices.map(_.replaceVariable(variable, actualParam)),
fields.map{case (f, i) => f -> i.map(_.replaceVariable(variable, actualParam))}) fields.map{case (dot, f, i) => (dot, f, i.map(_.replaceVariable(variable, actualParam)))})
override def replaceIndexedExpression(predicate: IndexedExpression => Boolean, replacement: IndexedExpression => Expression): Expression = override def replaceIndexedExpression(predicate: IndexedExpression => Boolean, replacement: IndexedExpression => Expression): Expression =
IndirectFieldExpression( IndirectFieldExpression(
root.replaceIndexedExpression(predicate, replacement), root.replaceIndexedExpression(predicate, replacement),
firstIndices.map(_.replaceIndexedExpression(predicate, replacement)), firstIndices.map(_.replaceIndexedExpression(predicate, replacement)),
fields.map{case (f, i) => f -> i.map(_.replaceIndexedExpression(predicate, replacement))}) fields.map{case (dot, f, i) => (dot, f, i.map(_.replaceIndexedExpression(predicate, replacement)))})
override def containsVariable(variable: String): Boolean = override def containsVariable(variable: String): Boolean =
root.containsVariable(variable) || root.containsVariable(variable) ||
firstIndices.exists(_.containsVariable(variable)) || firstIndices.exists(_.containsVariable(variable)) ||
fields.exists(_._2.exists(_.containsVariable(variable))) fields.exists(_._3.exists(_.containsVariable(variable)))
override def getPointies: Seq[String] = (root match { override def getPointies: Seq[String] = (root match {
case VariableExpression(v) => List(v) case VariableExpression(v) => List(v)
case _ => root.getPointies case _ => root.getPointies
}) ++ firstIndices.flatMap(_.getPointies) ++ fields.flatMap(_._2.flatMap(_.getPointies)) }) ++ firstIndices.flatMap(_.getPointies) ++ fields.flatMap(_._3.flatMap(_.getPointies))
override def isPure: Boolean = root.isPure && firstIndices.forall(_.isPure) && fields.forall(_._2.forall(_.isPure)) override def isPure: Boolean = root.isPure && firstIndices.forall(_.isPure) && fields.forall(_._3.forall(_.isPure))
override def getAllIdentifiers: Set[String] = root.getAllIdentifiers ++ firstIndices.flatMap(_.getAllIdentifiers) ++ fields.flatMap(_._2.flatMap(_.getAllIdentifiers)) override def getAllIdentifiers: Set[String] = root.getAllIdentifiers ++ firstIndices.flatMap(_.getAllIdentifiers) ++ fields.flatMap(_._3.flatMap(_.getAllIdentifiers))
} }
case class DerefDebuggingExpression(inner: Expression, preferredSize: Int) extends LhsExpression { case class DerefDebuggingExpression(inner: Expression, preferredSize: Int) extends LhsExpression {

View File

@ -120,7 +120,7 @@ object UnusedFunctions extends NodeOptimization {
case IndexedExpression(arr, index) => arr :: getAllCalledFunctions(List(index)) case IndexedExpression(arr, index) => arr :: getAllCalledFunctions(List(index))
case SeparateBytesExpression(h, l) => getAllCalledFunctions(List(h, l)) case SeparateBytesExpression(h, l) => getAllCalledFunctions(List(h, l))
case DerefDebuggingExpression(inner, _) => getAllCalledFunctions(List(inner)) case DerefDebuggingExpression(inner, _) => getAllCalledFunctions(List(inner))
case IndirectFieldExpression(root, firstIndices, fieldPath) => getAllCalledFunctions(root :: firstIndices ++: fieldPath.flatMap(_._2).toList) case IndirectFieldExpression(root, firstIndices, fieldPath) => getAllCalledFunctions(root :: firstIndices ++: fieldPath.flatMap(_._3).toList)
case _ => Nil case _ => Nil
} }

View File

@ -64,7 +64,7 @@ object UnusedGlobalVariables extends NodeOptimization {
case FunctionCallExpression(name, xs) => name :: getAllReadVariables(xs) case FunctionCallExpression(name, xs) => name :: getAllReadVariables(xs)
case IndexedExpression(arr, index) => arr :: getAllReadVariables(List(index)) case IndexedExpression(arr, index) => arr :: getAllReadVariables(List(index))
case SeparateBytesExpression(h, l) => getAllReadVariables(List(h, l)) case SeparateBytesExpression(h, l) => getAllReadVariables(List(h, l))
case IndirectFieldExpression(root, firstIndices, fields) => getAllReadVariables(List(root) ++ firstIndices ++ fields.flatMap(_._2)) case IndirectFieldExpression(root, firstIndices, fields) => getAllReadVariables(List(root) ++ firstIndices ++ fields.flatMap(_._3))
case _ => Nil case _ => Nil
} }

View File

@ -44,7 +44,7 @@ object UnusedLocalVariables extends NodeOptimization {
case IndexedExpression(arr, index) => arr :: getAllReadVariables(List(index)) case IndexedExpression(arr, index) => arr :: getAllReadVariables(List(index))
case DerefExpression(inner, _, _) => getAllReadVariables(List(inner)) case DerefExpression(inner, _, _) => getAllReadVariables(List(inner))
case DerefDebuggingExpression(inner, _) => getAllReadVariables(List(inner)) case DerefDebuggingExpression(inner, _) => getAllReadVariables(List(inner))
case IndirectFieldExpression(inner, firstIndices, fields) => getAllReadVariables(List(inner) ++ firstIndices ++ fields.flatMap(_._2)) case IndirectFieldExpression(inner, firstIndices, fields) => getAllReadVariables(List(inner) ++ firstIndices ++ fields.flatMap(_._3))
case SeparateBytesExpression(h, l) => getAllReadVariables(List(h, l)) case SeparateBytesExpression(h, l) => getAllReadVariables(List(h, l))
case _ => Nil case _ => Nil
} }

View File

@ -267,7 +267,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
}) })
env.allPreallocatables.filterNot(o => unusedRuntimeObjects(o.name)).foreach { env.allPreallocatables.filterNot(o => unusedRuntimeObjects(o.name)).foreach {
case thing@InitializedArray(name, Some(NumericConstant(address, _)), items, _, _, _, readOnly, _) => case thing@InitializedArray(name, Some(NumericConstant(address, _)), items, _, _, elementType, readOnly, _) =>
val bank = thing.bank(options) val bank = thing.bank(options)
if (!readOnly && options.platform.ramInitialValuesBank.isDefined) { if (!readOnly && options.platform.ramInitialValuesBank.isDefined) {
log.error(s"Preinitialized writable array $name cannot be put at a fixed address") log.error(s"Preinitialized writable array $name cannot be put at a fixed address")
@ -278,22 +278,25 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
assembly.append(name + ":") assembly.append(name + ":")
for (item <- items) { for (item <- items) {
env.eval(item) match { env.eval(item) match {
case Some(c) => writeByte(bank, index, c) case Some(c) =>
case None => log.error(s"Non-constant contents of array `$name`", item.position) for(i <- 0 until elementType.size) {
} writeByte(bank, index, c.subbyte(i))
bank0.occupied(index) = true bank0.occupied(index) = true
bank0.initialized(index) = true bank0.initialized(index) = true
bank0.writeable(index) = true bank0.writeable(index) = true
bank0.readable(index) = true bank0.readable(index) = true
index += 1 index += 1
} }
items.grouped(16).foreach { group => case None => log.error(s"Non-constant contents of array `$name`", item.position)
assembly.append(" " + bytePseudoopcode + " " + group.map(expr => env.eval(expr) match {
case Some(c) => c.quickSimplify.toString
case None => "<? unknown constant ?>"
}).mkString(", "))
} }
initializedVariablesSize += items.length }
items.flatMap(expr => env.eval(expr) match {
case Some(c) => List.tabulate(elementType.size)(i => c.subbyte(i).quickSimplify.toString)
case None => List.fill(elementType.size)("<? unknown constant ?>")
}).grouped(16).foreach { group =>
assembly.append(" " + bytePseudoopcode + " " + group.mkString(", "))
}
initializedVariablesSize += thing.sizeInBytes
case thing@InitializedArray(name, Some(_), items, _, _, _, _, _) => ??? case thing@InitializedArray(name, Some(_), items, _, _, _, _, _) => ???
case f: NormalFunction if f.address.isDefined => case f: NormalFunction if f.address.isDefined =>
val bank = f.bank(options) val bank = f.bank(options)
@ -390,32 +393,35 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
} }
} }
env.allPreallocatables.filterNot(o => unusedRuntimeObjects(o.name)).foreach { env.allPreallocatables.filterNot(o => unusedRuntimeObjects(o.name)).foreach {
case thing@InitializedArray(name, None, items, _, _, _, readOnly, alignment) if readOnly == readOnlyPass => case thing@InitializedArray(name, None, items, _, _, elementType, readOnly, alignment) if readOnly == readOnlyPass =>
val bank = thing.bank(options) val bank = thing.bank(options)
if (options.platform.ramInitialValuesBank.isDefined && !readOnly && bank != "default") { if (options.platform.ramInitialValuesBank.isDefined && !readOnly && bank != "default") {
log.error(s"Preinitialized writable array `$name` should be defined in the `default` bank") log.error(s"Preinitialized writable array `$name` should be defined in the `default` bank")
} }
val bank0 = mem.banks(bank) val bank0 = mem.banks(bank)
var index = codeAllocators(bank).allocateBytes(bank0, options, items.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment) var index = codeAllocators(bank).allocateBytes(bank0, options, thing.sizeInBytes, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
labelMap(name) = bank0.index -> index labelMap(name) = bank0.index -> index
if (!readOnlyPass) { if (!readOnlyPass) {
rwDataStart = rwDataStart.min(index) rwDataStart = rwDataStart.min(index)
rwDataEnd = rwDataEnd.min(index + items.size) rwDataEnd = rwDataEnd.min(index + thing.sizeInBytes)
} }
assembly.append("* = $" + index.toHexString) assembly.append("* = $" + index.toHexString)
assembly.append(name + ":") assembly.append(name + ":")
for (item <- items) { for (item <- items) {
env.eval(item) match { env.eval(item) match {
case Some(c) => writeByte(bank, index, c) case Some(c) =>
case None => log.error(s"Non-constant contents of array `$name`", item.position) for (i <- 0 until elementType.size) {
} writeByte(bank, index, c.subbyte(i))
index += 1 index += 1
} }
items.grouped(16).foreach { group => case None => log.error(s"Non-constant contents of array `$name`", item.position)
assembly.append(" " + bytePseudoopcode + " " + group.map(expr => env.eval(expr) match { }
case Some(c) => c.quickSimplify.toString }
case None => "<? unknown constant ?>" items.flatMap(expr => env.eval(expr) match {
}).mkString(", ")) case Some(c) => List.tabulate(elementType.size)(i => c.subbyte(i).quickSimplify.toString)
case None => List.fill(elementType.size)("<? unknown constant ?>")
}).grouped(16).foreach { group =>
assembly.append(" " + bytePseudoopcode + " " + group.mkString(", "))
} }
initializedVariablesSize += items.length initializedVariablesSize += items.length
justAfterCode += bank -> index justAfterCode += bank -> index

View File

@ -326,11 +326,20 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
def mfExpressionWrapper[E <: Expression](inner: P[E]): P[E] = for { def mfExpressionWrapper[E <: Expression](inner: P[E]): P[E] = for {
expr <- inner expr <- inner
firstIndices <- index.rep firstIndices <- index.rep
fieldPath <- (HWS ~ "->" ~/ AWS ~/ identifier ~/ index.rep).rep fieldPath <- (HWS ~ (("->".! ~/ AWS) | ".".!) ~/ AWS ~/ identifier ~/ index.rep).rep
} yield (expr, firstIndices, fieldPath) match { } yield (expr, firstIndices, fieldPath) match {
case (_, Seq(), Seq()) => expr case (_, Seq(), Seq()) => expr
case (VariableExpression(vname), Seq(i), Seq()) => IndexedExpression(vname, i).pos(expr.position).asInstanceOf[E] case (VariableExpression(vname), Seq(i), Seq()) => IndexedExpression(vname, i).pos(expr.position).asInstanceOf[E]
case _ => IndirectFieldExpression(expr, firstIndices, fieldPath).pos(expr.position).asInstanceOf[E] case _ =>
val fixedFieldPath = fieldPath.flatMap { e =>
e match {
case (".", "pointer", _) => Seq(e)
case (".", f, _) if f.startsWith("pointer.") => Seq(e)
case (".", f, i) => Seq((".", "pointer", Nil), ("->", f, i))
case _ => Seq(e)
}
}
IndirectFieldExpression(expr, firstIndices, fixedFieldPath.map {case (a,b,c) => (a == ".", b, c)}).pos(expr.position).asInstanceOf[E]
} }
// def mfLhsExpression: P[LhsExpression] = for { // def mfLhsExpression: P[LhsExpression] = for {

View File

@ -60,6 +60,7 @@ class ArraySuite extends FunSuite with Matchers {
} }
test("Array assignment with offset 1") { test("Array assignment with offset 1") {
try {
val m = new EmuRun(Cpu.StrictMos, Nil, DangerousOptimizations.All ++ OptimizationPresets.Good)( val m = new EmuRun(Cpu.StrictMos, Nil, DangerousOptimizations.All ++ OptimizationPresets.Good)(
""" """
| array output [8] @$c000 | array output [8] @$c000
@ -75,6 +76,11 @@ class ArraySuite extends FunSuite with Matchers {
""".stripMargin) """.stripMargin)
m.readByte(0xc002) should equal(1) m.readByte(0xc002) should equal(1)
m.readByte(0xc007) should equal(6) m.readByte(0xc007) should equal(6)
} catch {
case th: Throwable =>
th.printStackTrace(System.err)
throw th
}
} }
test("Array assignment through a pointer") { test("Array assignment through a pointer") {
@ -375,4 +381,81 @@ class ArraySuite extends FunSuite with Matchers {
| } | }
""".stripMargin).readByte(0xc000) should equal(1) """.stripMargin).readByte(0xc000) should equal(1)
} }
test("Arrays of words") {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80)(
"""
| array(word) words[10] @$c000
| void main () {
| words[2] = $702
| words[3] = $201
| memory_barrier()
| words[1] = words[3]
| words[4] = words[3] + words[2]
| words[5] = $101
| words[5] = words[5] << 1
| words[5] = words[5] + $1001
| }
""".stripMargin){ m =>
m.readWord(0xc004) should equal(0x702)
m.readWord(0xc006) should equal(0x201)
m.readWord(0xc002) should equal(0x201)
m.readWord(0xc008) should equal(0x903)
m.readWord(0xc00a) should equal(0x1203)
}
}
test("Initialized arrays of words") {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80)(
"""
| struct coord { byte x, byte y }
|
| array(word) a = [1,2,3]
| array(coord) c = [coord(1,2),coord(3,4)]
|
| word output @$c000
| coord output2 @$c002
|
| void main () {
| output = a[2]
| output2 = c[1]
| }
|
""".stripMargin){ m =>
m.readWord(0xc000) should equal(3)
m.readByte(0xc002) should equal(3)
m.readByte(0xc003) should equal(4)
}
}
test("Invalid array things that will become valid in the future") {
ShouldNotCompile(
"""
| array(int32) a[7] @$c000
| void main () {
| a[0] = 2
| }
""".stripMargin)
ShouldNotCompile(
"""
| array(int32) a[7] @$c000
| void main () {
| a[0] += 2
| }
""".stripMargin)
ShouldNotCompile(
"""
| array(word) a[7] @$c000
| void main () {
| a[0] += 2
| }
""".stripMargin)
ShouldNotCompile(
"""
| array(int32) a[7] @$c000
| int32 main () {
| return a[4]
| }
""".stripMargin)
}
} }

View File

@ -1,7 +1,7 @@
package millfork.test package millfork.test
import millfork.Cpu import millfork.Cpu
import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCrossPlatformRun} import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCrossPlatformRun, ShouldNotCompile}
import org.scalatest.{AppendedClues, FunSuite, Matchers} import org.scalatest.{AppendedClues, FunSuite, Matchers}
/** /**
@ -231,4 +231,75 @@ class PointerSuite extends FunSuite with Matchers with AppendedClues {
""".stripMargin) { m => """.stripMargin) { m =>
} }
} }
test("Pointers and arrays to large elements") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
"""
| struct P {
| word i
| byte c
| byte d
| }
|
| array(P) a [8]
|
| noinline byte f(byte i) {
| return a[i].c
| }
|
| void main() {
| f(6)
| }
""".stripMargin) { m =>
}
}
test("Page crossing with arrays of large elements") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80)(
"""
| import zp_reg
| struct P {
| word i
| byte c
| byte d
| }
|
| array(P) a [80] @$c080
| array(P) b [500] @$c280
|
| noinline void fill(word i) {
| if i < 80 { a[lo(i)].i = i }
| b[i].i = i
| }
|
| void main() {
| word i
| for i,0,until,500 { fill(i) }
| }
""".stripMargin) { m =>
for (i <- 0 until 80) {
m.readWord(0xc080 + 4*i) should equal(i) withClue s"a[$i]"
}
for (i <- 0 until 500) {
m.readWord(0xc280 + 4*i) should equal(i) withClue s"b[$i]"
}
}
}
test("Word pointers") {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080) (
"""
|pointer.word p
|word output @$c000
|void main () {
| word tmp
| p = tmp.pointer
| tmp = $203
| output = p[0]
|}
""".stripMargin
){ m =>
m.readWord(0xc000) should equal(0x203)
}
}
} }

View File

@ -10,6 +10,9 @@ import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, N
import millfork.node.StandardCallGraph import millfork.node.StandardCallGraph
import millfork.parser.{MosParser, PreprocessingResult, Preprocessor} import millfork.parser.{MosParser, PreprocessingResult, Preprocessor}
import millfork._ import millfork._
import millfork.compiler.m6809.M6809Compiler
import millfork.compiler.z80.Z80Compiler
import millfork.output.{M6809Assembler, MosAssembler, Z80Assembler}
import org.scalatest.Matchers import org.scalatest.Matchers
import scala.collection.JavaConverters._ import scala.collection.JavaConverters._
@ -52,7 +55,12 @@ object ShouldNotCompile extends Matchers {
// print unoptimized asm // print unoptimized asm
env.allPreallocatables.foreach { env.allPreallocatables.foreach {
case f: NormalFunction => case f: NormalFunction =>
val unoptimized = MosCompiler.compile(CompilationContext(f.environment, f, 0, options, Set())) val unoptimized = cpuFamily match {
case CpuFamily.M6502 => MosCompiler.compile(CompilationContext(f.environment, f, 0, options, Set()))
case CpuFamily.I80 => Z80Compiler.compile(CompilationContext(f.environment, f, 0, options, Set()))
case CpuFamily.M6809 => M6809Compiler.compile(CompilationContext(f.environment, f, 0, options, Set()))
case _ => Nil
}
unoptimizedSize += unoptimized.map(_.sizeInBytes).sum unoptimizedSize += unoptimized.map(_.sizeInBytes).sum
case d: InitializedArray => case d: InitializedArray =>
unoptimizedSize += d.contents.length unoptimizedSize += d.contents.length
@ -61,12 +69,27 @@ object ShouldNotCompile extends Matchers {
} }
if (!log.hasErrors) { if (!log.hasErrors) {
val familyName = cpuFamily match { val env2 = new Environment(None, "", cpuFamily, options)
case CpuFamily.M6502 => "6502" env2.collectDeclarations(program, options)
case CpuFamily.I80 => "Z80" cpuFamily match {
case _ => "unknown CPU" case CpuFamily.M6502 =>
val assembler = new MosAssembler(program, env2, platform)
val output = assembler.assemble(callGraph, Nil, options)
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
fail("Failed: Compilation succeeded for 6502")
case CpuFamily.I80 =>
val assembler = new Z80Assembler(program, env2, platform)
val output = assembler.assemble(callGraph, Nil, options)
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
fail("Failed: Compilation succeeded for Z80")
case CpuFamily.M6809 =>
val assembler = new M6809Assembler(program, env2, platform)
val output = assembler.assemble(callGraph, Nil, options)
output.asm.takeWhile(s => !(s.startsWith(".") && s.contains("= $"))).filterNot(_.contains("; DISCARD_")).foreach(println)
fail("Failed: Compilation succeeded for 6809")
case _ =>
fail("Failed: Compilation succeeded for unknown CPU")
} }
fail("Failed: Compilation succeeded for " + familyName)
} }
log.clearErrors() log.clearErrors()