mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-13 21:37:08 +00:00
Arrays with elements larger than one byte
This commit is contained in:
parent
65338555ad
commit
6d499f3623
@ -2,6 +2,8 @@
|
||||
|
||||
## Current version
|
||||
|
||||
* Added arrays of elements of size greater than byte.
|
||||
|
||||
* Improved passing of register parameters to assembly functions.
|
||||
|
||||
* Enabled declaring multiple variables in one line.
|
||||
|
@ -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
|
||||
|
||||
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
|
||||
|
||||
* `not`: negation of a boolean expression
|
||||
|
@ -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).
|
||||
|
||||
* `<element type>`: type of the elements of the array.
|
||||
It must be of size 1 byte.
|
||||
If omitted, the default is `byte`.
|
||||
|
||||
* `<size>`: either a constant number, which then defines the size of the array,
|
||||
|
@ -185,12 +185,12 @@ class AbstractExpressionCompiler[T <: AbstractCode] {
|
||||
|
||||
def validateTypeCastAndGetSourceExpressionType(ctx: CompilationContext, typ: Type, params: List[Expression]): Type = {
|
||||
var failed = false
|
||||
if (typ.name == "pointer") {
|
||||
ctx.log.error("Cannot cast into pointer")
|
||||
if (typ.name == "pointer" && typ.name !="pointer" && !typ.isInstanceOf[PointerType]) {
|
||||
ctx.log.error("Cannot cast into pointer", params.headOption.flatMap(_.position))
|
||||
failed = true
|
||||
}
|
||||
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
|
||||
}
|
||||
val sourceType = getExpressionType(ctx, params.head)
|
||||
@ -286,8 +286,19 @@ object AbstractExpressionCompiler {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
for ((fieldName, indices) <- fieldPath) {
|
||||
if (ok) {
|
||||
for ((dot, fieldName, indices) <- fieldPath) {
|
||||
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 {
|
||||
case PointerType(_, _, Some(targetType)) =>
|
||||
val tuples = env.getSubvariables(targetType).filter(x => x._1 == "." + fieldName)
|
||||
|
@ -128,12 +128,12 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
||||
cv = search(target, cv)
|
||||
Assignment(optimizeExpr(target, cv).asInstanceOf[LhsExpression], optimizeExpr(arg, cv)).pos(pos) -> cv
|
||||
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(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) =>
|
||||
cv = search(arg, cv)
|
||||
cv = search(target, cv)
|
||||
@ -272,6 +272,9 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
implicit class StringToFunctionNameOps(val functionName: String) {
|
||||
def <|(exprs: Expression*): Expression = FunctionCallExpression(functionName, exprs.toList).pos(exprs.head.position)
|
||||
}
|
||||
// generic warnings:
|
||||
expr match {
|
||||
case FunctionCallExpression("*" | "*=", params) =>
|
||||
@ -299,15 +302,55 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
||||
env.eval(index) match {
|
||||
case Some(NumericConstant(0, _)) => //ok
|
||||
case _ =>
|
||||
// TODO: should we keep this?
|
||||
env.log.error(s"Type `$pt` can be only indexed with 0")
|
||||
}
|
||||
DerefExpression(result, 0, target)
|
||||
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 {
|
||||
case Some(NumericConstant(n, _)) if n >= 0 && n <= 127 =>
|
||||
DerefExpression(result, n.toInt, b)
|
||||
case Some(NumericConstant(n, _)) if n >= 0 && (targetType.size * n) <= 127 =>
|
||||
x match {
|
||||
case _: PointerType =>
|
||||
DerefExpression(result, n.toInt, targetType)
|
||||
case _ =>
|
||||
DerefExpression(
|
||||
("pointer." + targetType.name) <| result,
|
||||
n.toInt, targetType)
|
||||
}
|
||||
case _ =>
|
||||
DerefExpression(SumExpression(List(false -> result, false -> index), decimal = false), 0, b)
|
||||
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 _ =>
|
||||
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) {
|
||||
result = applyIndex(result, index)
|
||||
}
|
||||
for ((fieldName, indices) <- fieldPath) {
|
||||
if (ok) {
|
||||
result = AbstractExpressionCompiler.getExpressionType(env, env.log, result) match {
|
||||
for ((dot, fieldName, indices) <- fieldPath) {
|
||||
if (dot && ok) {
|
||||
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)) =>
|
||||
val subvariables = env.getSubvariables(target).filter(x => x._1 == "." + fieldName)
|
||||
if (subvariables.isEmpty) {
|
||||
@ -333,6 +404,7 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
||||
}
|
||||
case _ =>
|
||||
ctx.log.error("Invalid pointer type on the left-hand side of `->`", result.position)
|
||||
ctx.log.debug(currentResultType.toString)
|
||||
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
|
||||
// expecially important when inside a nonet operation
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -333,7 +333,7 @@ object BuiltIns {
|
||||
|
||||
def compileInPlaceWordOrLongShiftOps(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, aslRatherThanLsr: Boolean): List[AssemblyLine] = {
|
||||
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)
|
||||
}
|
||||
val env = ctx.env
|
||||
@ -945,7 +945,7 @@ object BuiltIns {
|
||||
|
||||
def compileInPlaceWordMultiplication(ctx: CompilationContext, v: LhsExpression, addend: Expression): List[AssemblyLine] = {
|
||||
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)
|
||||
}
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
@ -1205,7 +1205,7 @@ object BuiltIns {
|
||||
return compileInPlaceWordOrLongAddition(ctx, lhs, addend, subtract, decimal = false)
|
||||
}
|
||||
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)
|
||||
}
|
||||
val env = ctx.env
|
||||
@ -1534,7 +1534,7 @@ object BuiltIns {
|
||||
|
||||
def compileInPlaceWordOrLongBitOp(ctx: CompilationContext, lhs: LhsExpression, param: Expression, operation: Opcode.Value): List[AssemblyLine] = {
|
||||
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)
|
||||
}
|
||||
val env = ctx.env
|
||||
|
@ -337,7 +337,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
ctx.log.error("Writing to a constant array", target.position)
|
||||
}
|
||||
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, _) =>
|
||||
if (p.readOnly) {
|
||||
ctx.log.error("Writing to a constant array", target.position)
|
||||
@ -406,13 +406,13 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
Nil
|
||||
}
|
||||
case DerefExpression(inner, offset, targetType) =>
|
||||
val (prepare, reg) = getPhysicalPointerForDeref(ctx, inner)
|
||||
val lo = preserveRegisterIfNeeded(ctx, MosRegister.A, prepare) ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine.indexedY(STA, reg))
|
||||
val (prepare, addr, am) = getPhysicalPointerForDeref(ctx, inner)
|
||||
val lo = preserveRegisterIfNeeded(ctx, MosRegister.A, prepare) ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine(STA, am, addr))
|
||||
if (targetType.size == 1) {
|
||||
lo
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
|
||||
def getPhysicalPointerForDeref(ctx: CompilationContext, pointerExpression: Expression): (List[AssemblyLine], ThingInMemory) = {
|
||||
def getPhysicalPointerForDeref(ctx: CompilationContext, pointerExpression: Expression): (List[AssemblyLine], Constant, AddrMode.Value) = {
|
||||
pointerExpression match {
|
||||
case VariableExpression(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 _ =>
|
||||
}
|
||||
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] = {
|
||||
@ -807,6 +811,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case IndexedExpression(arrayName, indexExpr) =>
|
||||
val pointy = env.getPointy(arrayName)
|
||||
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
|
||||
val (variableIndex, constantIndex) = env.evalVariableAndConstantSubParts(indexExpr)
|
||||
val variableIndexSize = variableIndex.map(v => getExpressionType(ctx, v).size).getOrElse(0)
|
||||
@ -870,7 +875,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
prepareWordIndexing(ctx, ConstantPointy(
|
||||
a.value + constantIndex,
|
||||
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"),
|
||||
a.elementType, NoAlignment, a.readOnly), v) ++ loadFromReg()
|
||||
case (a: VariablePointy, _, 2, _) =>
|
||||
@ -918,18 +924,26 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
}
|
||||
}
|
||||
case DerefExpression(inner, offset, targetType) =>
|
||||
val (prepare, reg) = getPhysicalPointerForDeref(ctx, inner)
|
||||
targetType.size match {
|
||||
case 1 =>
|
||||
prepare ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine.indexedY(LDA, reg)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position)
|
||||
case 2 =>
|
||||
val (prepare, addr, am) = getPhysicalPointerForDeref(ctx, inner)
|
||||
(targetType.size, am) match {
|
||||
case (1, AbsoluteY) =>
|
||||
prepare ++ List(AssemblyLine.absolute(LDA, addr + offset)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position)
|
||||
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 ++
|
||||
List(
|
||||
AssemblyLine.immediate(LDY, offset+1),
|
||||
AssemblyLine.indexedY(LDA, reg),
|
||||
AssemblyLine(LDA, am, addr),
|
||||
AssemblyLine.implied(TAX),
|
||||
AssemblyLine.implied(DEY),
|
||||
AssemblyLine.indexedY(LDA, reg)) ++
|
||||
AssemblyLine(LDA, am, addr)) ++
|
||||
expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
|
||||
case _ =>
|
||||
ctx.log.error("Cannot read a large object indirectly")
|
||||
@ -1287,6 +1301,9 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
l match {
|
||||
case v: VariableExpression =>
|
||||
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 "-=" =>
|
||||
@ -1303,6 +1320,9 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
l match {
|
||||
case v: VariableExpression =>
|
||||
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 "+'=" =>
|
||||
@ -1319,6 +1339,9 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
l match {
|
||||
case v: VariableExpression =>
|
||||
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 "-'=" =>
|
||||
@ -1335,6 +1358,9 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
l match {
|
||||
case v: VariableExpression =>
|
||||
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 "<<=" =>
|
||||
@ -1389,6 +1415,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
BuiltIns.compileInPlaceByteMultiplication(ctx, l, r)
|
||||
case 2 =>
|
||||
BuiltIns.compileInPlaceWordMultiplication(ctx, l, r)
|
||||
case _ => ctx.log.fatal("Oops")
|
||||
}
|
||||
case "/=" | "%%=" =>
|
||||
assertSizesForDivision(ctx, params, inPlace = true)
|
||||
@ -1402,6 +1429,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
} else {
|
||||
compileAssignment(ctx, FunctionCallExpression("/", List(l, r)).pos(f.position), l)
|
||||
}
|
||||
case _ => ctx.log.fatal("Oops")
|
||||
}
|
||||
case "/" | "%%" =>
|
||||
assertSizesForDivision(ctx, params, inPlace = false)
|
||||
@ -1838,69 +1866,122 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
ctx.log.error("Invalid left-hand-side use of `:`")
|
||||
Nil
|
||||
case DerefExpression(inner, offset, targetType) =>
|
||||
val (prepare, reg) = getPhysicalPointerForDeref(ctx, inner)
|
||||
val (prepare, addr, am) = getPhysicalPointerForDeref(ctx, inner)
|
||||
env.eval(source) match {
|
||||
case Some(constant) =>
|
||||
targetType.size match {
|
||||
case 1 =>
|
||||
(targetType.size, am) match {
|
||||
case (1, AbsoluteY) =>
|
||||
prepare ++ List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.immediate(LDA, constant),
|
||||
AssemblyLine.indexedY(STA, reg))
|
||||
case 2 =>
|
||||
AssemblyLine.immediate(LDA, constant),
|
||||
AssemblyLine.absolute(STA, addr + offset))
|
||||
case (1, _) =>
|
||||
prepare ++ List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.immediate(LDA, constant.loByte),
|
||||
AssemblyLine.indexedY(STA, reg),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.immediate(LDA, constant.hiByte),
|
||||
AssemblyLine.indexedY(STA, reg))
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.immediate(LDA, constant),
|
||||
AssemblyLine(STA, am, addr))
|
||||
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(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.immediate(LDA, constant.loByte),
|
||||
AssemblyLine(STA, am, addr),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.immediate(LDA, constant.hiByte),
|
||||
AssemblyLine(STA, am, addr))
|
||||
case _ =>
|
||||
ctx.log.error("Cannot assign to a large object indirectly", target.position)
|
||||
Nil
|
||||
}
|
||||
case None =>
|
||||
source match {
|
||||
case VariableExpression(vname) =>
|
||||
val variable = env.get[Variable](vname)
|
||||
targetType.size match {
|
||||
case 1 =>
|
||||
(targetType.size, am) match {
|
||||
case (1, AbsoluteY) =>
|
||||
prepare ++
|
||||
AssemblyLine.variable(ctx, LDA, variable) ++ List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.indexedY(STA, reg))
|
||||
case 2 =>
|
||||
AssemblyLine.variable(ctx, LDA, variable) :+
|
||||
AssemblyLine.absolute(STA, addr + offset)
|
||||
case (1, _) =>
|
||||
prepare ++
|
||||
AssemblyLine.variable(ctx, LDA, variable) ++ List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.indexedY(STA, reg)) ++
|
||||
AssemblyLine(STA, am, addr))
|
||||
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 ++
|
||||
AssemblyLine.variable(ctx, LDA, variable) ++ List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine(STA, am, addr)) ++
|
||||
AssemblyLine.variable(ctx, LDA, variable, 1) ++ List(
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.indexedY(STA, reg))
|
||||
AssemblyLine(STA, am, addr))
|
||||
case _ =>
|
||||
ctx.log.error("Cannot assign to a large object indirectly")
|
||||
ctx.log.error("Cannot assign to a large object indirectly", target.position)
|
||||
Nil
|
||||
}
|
||||
case _ =>
|
||||
targetType.size match {
|
||||
case 1 =>
|
||||
(targetType.size, am) match {
|
||||
case (1, _) =>
|
||||
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))
|
||||
// TODO: optimiza if prepare is empty
|
||||
compile(ctx, source, someTuple, BranchSpec.None) ++ List(
|
||||
AssemblyLine.implied(PHA),
|
||||
AssemblyLine.implied(TXA),
|
||||
AssemblyLine.implied(PHA)) ++ prepare ++ List(
|
||||
AssemblyLine.immediate(LDY, offset+1),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.indexedY(STA, reg),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.implied(DEY),
|
||||
AssemblyLine.indexedY(STA, reg))
|
||||
// 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(
|
||||
AssemblyLine.implied(PHA),
|
||||
AssemblyLine.implied(TXA),
|
||||
AssemblyLine.implied(PHA)) ++ prepare ++ List(
|
||||
AssemblyLine.immediate(LDY, offset+1),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.indexedY(STA, addr),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.implied(DEY),
|
||||
AssemblyLine.indexedY(STA, addr))
|
||||
}
|
||||
case _ =>
|
||||
ctx.log.error("Cannot assign to a large object indirectly")
|
||||
ctx.log.error("Cannot assign to a large object indirectly", target.position)
|
||||
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 _ =>
|
||||
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
|
||||
val arrayLength:Int = pointy match {
|
||||
case _: VariablePointy => return Nil
|
||||
case p: ConstantPointy => p.size match {
|
||||
case p: ConstantPointy => p.sizeInBytes match {
|
||||
case None => return Nil
|
||||
case Some(s) => s
|
||||
}
|
||||
|
@ -1198,23 +1198,32 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
val env = ctx.env
|
||||
val pointy = env.getPointy(i.name)
|
||||
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 {
|
||||
case ConstantPointy(baseAddr, _, size, _, _, alignment, readOnly) =>
|
||||
case ConstantPointy(baseAddr, _, sizeInBytes, _, _, _, alignment, readOnly) =>
|
||||
if (forWriting && readOnly) {
|
||||
ctx.log.error("Writing to a constant array", i.position)
|
||||
}
|
||||
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) =>
|
||||
val constantPart = (baseAddr + offset).quickSimplify
|
||||
if (getExpressionType(ctx, i.index).size == 1 && size.exists(_ < 256) && alignment == WithinPageAlignment) {
|
||||
compileToA(ctx, i.index) ++ List(
|
||||
val constantPart = (baseAddr + offset * elementSize).quickSimplify
|
||||
if (getExpressionType(ctx, i.index).size == 1 && sizeInBytes.exists(_ < 256) && alignment == WithinPageAlignment) {
|
||||
compileToA(ctx, i.index) ++ List.fill(logElemSize)(ZLine.register(ADD, ZRegister.A)) ++ List(
|
||||
ZLine.imm8(ADD, constantPart.loByte),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.H, constantPart.hiByte))
|
||||
} else {
|
||||
List(ZLine.ldImm16(ZRegister.BC, constantPart)) ++
|
||||
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))
|
||||
}
|
||||
}
|
||||
@ -1234,9 +1243,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
case _ =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
compileToBC(ctx, i.index) ++
|
||||
List(
|
||||
ZLine.ldAbs16(ZRegister.HL, varAddr),
|
||||
ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
List(ZLine.ldAbs16(ZRegister.HL, varAddr)) ++
|
||||
List.fill(elementSize)(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
} else {
|
||||
// TODO: is this reasonable?
|
||||
compileToBC(ctx, i.index) ++
|
||||
@ -1244,14 +1252,14 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
ZLine.ldAbs8(ZRegister.A, varAddr),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldAbs8(ZRegister.A, varAddr + 1),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.A),
|
||||
ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
ZLine.ld8(ZRegister.H, ZRegister.A)) ++
|
||||
List.fill(elementSize)(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
}
|
||||
}
|
||||
case _: StackVariablePointy =>
|
||||
compileToHL(ctx, VariableExpression(i.name).pos(i.position)) ++
|
||||
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] = {
|
||||
val env = ctx.env
|
||||
target match {
|
||||
@ -1535,6 +1554,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
env.evalVariableAndConstantSubParts(indexExpr) match {
|
||||
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) =>
|
||||
Z80ExpressionCompiler.stashHLIfChanged(ctx, ZLine.ld8(ZRegister.A, ZRegister.L) :: storeA(ctx, lo, signedSource)) ++
|
||||
@ -1846,6 +1866,9 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
Nil
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
ctx.log.error("Cannot modify large object accessed via such complex expression", lhs.position)
|
||||
List.fill(size)(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
||||
|
||||
|
||||
def compile(ctx: CompilationContext, statement: ExecutableStatement): (List[ZLine], List[ZLine])= {
|
||||
ctx.log.trace(statement.toString)
|
||||
val options = ctx.options
|
||||
val env = ctx.env
|
||||
val ret = Z80Compiler.restoreRegistersAndReturn(ctx)
|
||||
@ -84,13 +85,23 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
||||
}) -> Nil
|
||||
case Assignment(destination, source) =>
|
||||
val sourceType = AbstractExpressionCompiler.getExpressionType(ctx, source)
|
||||
val targetType = AbstractExpressionCompiler.getExpressionType(ctx, destination)
|
||||
AbstractExpressionCompiler.checkAssignmentType(ctx, source, targetType)
|
||||
(sourceType.size match {
|
||||
case 0 =>
|
||||
ctx.log.error("Cannot assign a void expression", statement.position)
|
||||
Z80ExpressionCompiler.compile(ctx, source, 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 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)
|
||||
}) -> Nil
|
||||
case s: IfStatement =>
|
||||
|
@ -29,7 +29,7 @@ class Z80StatementPreprocessor(ctx: CompilationContext, statements: List[Executa
|
||||
case f: DerefDebuggingExpression => Nil
|
||||
case IndexedExpression(a, VariableExpression(v)) => if (v == variable) {
|
||||
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
|
||||
}
|
||||
} else Nil
|
||||
@ -130,7 +130,7 @@ class Z80StatementPreprocessor(ctx: CompilationContext, statements: List[Executa
|
||||
val array = ctx.env.get[MfArray](name + ".array")
|
||||
Assignment(
|
||||
VariableExpression(newVariables(name, f.variable)),
|
||||
FunctionCallExpression("pointer." + array.elementType.name, List(
|
||||
FunctionCallExpression("pointer", List(
|
||||
SumExpression(List(false -> VariableExpression(name + ".addr"), false -> optStart), decimal = false)
|
||||
)))
|
||||
}).toList :+ ForStatement(f.variable, optStart, optimizeExpr(f.end, Map()), newDirection, optimizeStmts(newBody, Map())._1),
|
||||
|
@ -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] = {
|
||||
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)
|
||||
}
|
||||
if (size == 2 && !decimal) {
|
||||
|
2
src/main/scala/millfork/env/Constant.scala
vendored
2
src/main/scala/millfork/env/Constant.scala
vendored
@ -42,6 +42,8 @@ sealed trait Constant {
|
||||
|
||||
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: Long): Constant = if (that == 0) this else this + NumericConstant(that, minimumSize(that))
|
||||
|
18
src/main/scala/millfork/env/Environment.scala
vendored
18
src/main/scala/millfork/env/Environment.scala
vendored
@ -354,13 +354,13 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
InitializedMemoryVariable
|
||||
UninitializedMemoryVariable
|
||||
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@UninitializedArray(_, size, _, i, e, ro, _) => ConstantPointy(th.toAddress, Some(name), Some(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@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(_, 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(_, _, 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 =>
|
||||
val e = get[VariableType](typ.pointerTargetName)
|
||||
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=>
|
||||
val e = get[VariableType](th.typ.pointerTargetName)
|
||||
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")
|
||||
val b = get[VariableType]("byte")
|
||||
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) =>
|
||||
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 {
|
||||
case NumericConstant(constIndex, _) =>
|
||||
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 p = get[Type]("pointer")
|
||||
val e = get[VariableType](stmt.elementType)
|
||||
if (e.size != 1) {
|
||||
log.error(s"Array elements should be of size 1, `${e.name}` is of size ${e.size}", stmt.position)
|
||||
if (e.size < 1 && e.size > 127) {
|
||||
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 {
|
||||
case None =>
|
||||
@ -1841,7 +1841,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
case IndirectFieldExpression(inner, firstIndices, fields) =>
|
||||
nameCheck(inner)
|
||||
firstIndices.foreach(nameCheck)
|
||||
fields.foreach(f => f._2.foreach(nameCheck))
|
||||
fields.foreach(f => f._3.foreach(nameCheck))
|
||||
case SeparateBytesExpression(h, l) =>
|
||||
nameCheck(h)
|
||||
nameCheck(l)
|
||||
|
3
src/main/scala/millfork/env/Pointy.scala
vendored
3
src/main/scala/millfork/env/Pointy.scala
vendored
@ -21,7 +21,8 @@ case class VariablePointy(addr: Constant, indexType: VariableType, elementType:
|
||||
|
||||
case class ConstantPointy(value: Constant,
|
||||
name: Option[String],
|
||||
size: Option[Int],
|
||||
sizeInBytes: Option[Int],
|
||||
elementCount: Option[Int],
|
||||
indexType: VariableType,
|
||||
elementType: VariableType,
|
||||
alignment: MemoryAlignment,
|
||||
|
14
src/main/scala/millfork/env/Thing.scala
vendored
14
src/main/scala/millfork/env/Thing.scala
vendored
@ -266,12 +266,12 @@ trait MfArray extends ThingInMemory with IndexableThing {
|
||||
def indexType: VariableType
|
||||
def elementType: VariableType
|
||||
override def isVolatile: Boolean = false
|
||||
/* TODO: what if larger elements? */
|
||||
def sizeInBytes: Int
|
||||
def elementCount: Int
|
||||
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 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 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 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 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 {
|
||||
@ -303,7 +307,9 @@ case class InitializedArray(name: String, address: Option[Constant], contents: S
|
||||
|
||||
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 {
|
||||
|
@ -70,7 +70,7 @@ abstract class CallGraph(program: Program, log: Logger) {
|
||||
case IndirectFieldExpression(root, firstIndices, fields) =>
|
||||
add(currentFunction, callingFunctions, root)
|
||||
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 _ => ()
|
||||
}
|
||||
}
|
||||
|
@ -237,32 +237,32 @@ case class IndexedExpression(name: String, index: Expression) extends LhsExpress
|
||||
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 =
|
||||
IndirectFieldExpression(
|
||||
root.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 =
|
||||
IndirectFieldExpression(
|
||||
root.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 =
|
||||
root.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 {
|
||||
case VariableExpression(v) => List(v)
|
||||
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 {
|
||||
|
@ -120,7 +120,7 @@ object UnusedFunctions extends NodeOptimization {
|
||||
case IndexedExpression(arr, index) => arr :: getAllCalledFunctions(List(index))
|
||||
case SeparateBytesExpression(h, l) => getAllCalledFunctions(List(h, l))
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ object UnusedGlobalVariables extends NodeOptimization {
|
||||
case FunctionCallExpression(name, xs) => name :: getAllReadVariables(xs)
|
||||
case IndexedExpression(arr, index) => arr :: getAllReadVariables(List(index))
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ object UnusedLocalVariables extends NodeOptimization {
|
||||
case IndexedExpression(arr, index) => arr :: getAllReadVariables(List(index))
|
||||
case DerefExpression(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 _ => Nil
|
||||
}
|
||||
|
@ -267,7 +267,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
})
|
||||
|
||||
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)
|
||||
if (!readOnly && options.platform.ramInitialValuesBank.isDefined) {
|
||||
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 + ":")
|
||||
for (item <- items) {
|
||||
env.eval(item) match {
|
||||
case Some(c) => writeByte(bank, index, c)
|
||||
case Some(c) =>
|
||||
for(i <- 0 until elementType.size) {
|
||||
writeByte(bank, index, c.subbyte(i))
|
||||
bank0.occupied(index) = true
|
||||
bank0.initialized(index) = true
|
||||
bank0.writeable(index) = true
|
||||
bank0.readable(index) = true
|
||||
index += 1
|
||||
}
|
||||
case None => log.error(s"Non-constant contents of array `$name`", item.position)
|
||||
}
|
||||
bank0.occupied(index) = true
|
||||
bank0.initialized(index) = true
|
||||
bank0.writeable(index) = true
|
||||
bank0.readable(index) = true
|
||||
index += 1
|
||||
}
|
||||
items.grouped(16).foreach { group =>
|
||||
assembly.append(" " + bytePseudoopcode + " " + group.map(expr => env.eval(expr) match {
|
||||
case Some(c) => c.quickSimplify.toString
|
||||
case None => "<? unknown constant ?>"
|
||||
}).mkString(", "))
|
||||
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 += items.length
|
||||
initializedVariablesSize += thing.sizeInBytes
|
||||
case thing@InitializedArray(name, Some(_), items, _, _, _, _, _) => ???
|
||||
case f: NormalFunction if f.address.isDefined =>
|
||||
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 {
|
||||
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)
|
||||
if (options.platform.ramInitialValuesBank.isDefined && !readOnly && bank != "default") {
|
||||
log.error(s"Preinitialized writable array `$name` should be defined in the `default` 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
|
||||
if (!readOnlyPass) {
|
||||
rwDataStart = rwDataStart.min(index)
|
||||
rwDataEnd = rwDataEnd.min(index + items.size)
|
||||
rwDataEnd = rwDataEnd.min(index + thing.sizeInBytes)
|
||||
}
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
assembly.append(name + ":")
|
||||
for (item <- items) {
|
||||
env.eval(item) match {
|
||||
case Some(c) => writeByte(bank, index, c)
|
||||
case Some(c) =>
|
||||
for (i <- 0 until elementType.size) {
|
||||
writeByte(bank, index, c.subbyte(i))
|
||||
index += 1
|
||||
}
|
||||
case None => log.error(s"Non-constant contents of array `$name`", item.position)
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
items.grouped(16).foreach { group =>
|
||||
assembly.append(" " + bytePseudoopcode + " " + group.map(expr => env.eval(expr) match {
|
||||
case Some(c) => c.quickSimplify.toString
|
||||
case None => "<? unknown constant ?>"
|
||||
}).mkString(", "))
|
||||
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 += items.length
|
||||
justAfterCode += bank -> index
|
||||
|
@ -326,11 +326,20 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
def mfExpressionWrapper[E <: Expression](inner: P[E]): P[E] = for {
|
||||
expr <- inner
|
||||
firstIndices <- index.rep
|
||||
fieldPath <- (HWS ~ "->" ~/ AWS ~/ identifier ~/ index.rep).rep
|
||||
fieldPath <- (HWS ~ (("->".! ~/ AWS) | ".".!) ~/ AWS ~/ identifier ~/ index.rep).rep
|
||||
} yield (expr, firstIndices, fieldPath) match {
|
||||
case (_, Seq(), Seq()) => expr
|
||||
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 {
|
||||
|
@ -60,21 +60,27 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Array assignment with offset 1") {
|
||||
val m = new EmuRun(Cpu.StrictMos, Nil, DangerousOptimizations.All ++ OptimizationPresets.Good)(
|
||||
"""
|
||||
| array output [8] @$c000
|
||||
| void main () {
|
||||
| byte i
|
||||
| i = 0
|
||||
| while i != 6 {
|
||||
| output[i + 2] = i + 1
|
||||
| output[i] = output[i]
|
||||
| i += 1
|
||||
| }
|
||||
| }
|
||||
""".stripMargin)
|
||||
try {
|
||||
val m = new EmuRun(Cpu.StrictMos, Nil, DangerousOptimizations.All ++ OptimizationPresets.Good)(
|
||||
"""
|
||||
| array output [8] @$c000
|
||||
| void main () {
|
||||
| byte i
|
||||
| i = 0
|
||||
| while i != 6 {
|
||||
| output[i + 2] = i + 1
|
||||
| output[i] = output[i]
|
||||
| i += 1
|
||||
| }
|
||||
| }
|
||||
""".stripMargin)
|
||||
m.readByte(0xc002) should equal(1)
|
||||
m.readByte(0xc007) should equal(6)
|
||||
} catch {
|
||||
case th: Throwable =>
|
||||
th.printStackTrace(System.err)
|
||||
throw th
|
||||
}
|
||||
}
|
||||
|
||||
test("Array assignment through a pointer") {
|
||||
@ -375,4 +381,81 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
| }
|
||||
""".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)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.Cpu
|
||||
import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCrossPlatformRun}
|
||||
import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCrossPlatformRun, ShouldNotCompile}
|
||||
import org.scalatest.{AppendedClues, FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
@ -231,4 +231,75 @@ class PointerSuite extends FunSuite with Matchers with AppendedClues {
|
||||
""".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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,9 @@ import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, N
|
||||
import millfork.node.StandardCallGraph
|
||||
import millfork.parser.{MosParser, PreprocessingResult, Preprocessor}
|
||||
import millfork._
|
||||
import millfork.compiler.m6809.M6809Compiler
|
||||
import millfork.compiler.z80.Z80Compiler
|
||||
import millfork.output.{M6809Assembler, MosAssembler, Z80Assembler}
|
||||
import org.scalatest.Matchers
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
@ -52,7 +55,12 @@ object ShouldNotCompile extends Matchers {
|
||||
// print unoptimized asm
|
||||
env.allPreallocatables.foreach {
|
||||
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
|
||||
case d: InitializedArray =>
|
||||
unoptimizedSize += d.contents.length
|
||||
@ -61,12 +69,27 @@ object ShouldNotCompile extends Matchers {
|
||||
}
|
||||
|
||||
if (!log.hasErrors) {
|
||||
val familyName = cpuFamily match {
|
||||
case CpuFamily.M6502 => "6502"
|
||||
case CpuFamily.I80 => "Z80"
|
||||
case _ => "unknown CPU"
|
||||
val env2 = new Environment(None, "", cpuFamily, options)
|
||||
env2.collectDeclarations(program, options)
|
||||
cpuFamily match {
|
||||
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()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user