diff --git a/docs/lang/literals.md b/docs/lang/literals.md index c61507e2..88ad2a4e 100644 --- a/docs/lang/literals.md +++ b/docs/lang/literals.md @@ -109,14 +109,16 @@ An array is initialized with either: * (only byte arrays) a format, followed by an array initializer: - * `@word` (=`@word_le`): for every term of the array initializer, emit two bytes, first being the low byte of the value, second being the high byte: - `@word [$1122]` is equivalent to `[$22, $11]` + * `@word_le`: for every term of the array initializer, emit two bytes, first being the low byte of the value, second being the high byte: + `@word_le [$1122]` is equivalent to `[$22, $11]` * `@word_be` – like the above, but opposite: `@word_be [$1122]` is equivalent to `[$11, $22]` - * `@long` (=`@long_le`), `@long_be`: similar, but with four bytes - `@long [$11223344]` is equivalent to `[$44, $33, $22, $11]` + * `@word`: equivalent to `@word_le` on little-endian architectures and `@word_be` on big-endian architectures + + * `@long`, `@long_le`, `@long_be`: similar, but with four bytes + `@long_le [$11223344]` is equivalent to `[$44, $33, $22, $11]` `@long_be [$11223344]` is equivalent to `[$11, $22, $33, $44]` * `@struct`: every term of the initializer is interpreted as a struct constructor (see below) diff --git a/src/main/scala/millfork/CompilationOptions.scala b/src/main/scala/millfork/CompilationOptions.scala index 56d7cc75..feb09c8e 100644 --- a/src/main/scala/millfork/CompilationOptions.scala +++ b/src/main/scala/millfork/CompilationOptions.scala @@ -21,6 +21,8 @@ case class CompilationOptions(platform: Platform, def log: Logger = jobContext.log @inline def nextLabel: LabelGenerator = jobContext.nextLabel + @inline + def isBigEndian: Boolean = platform.isBigEndian val flags: Map[CompilationFlag.Value, Boolean] = CompilationFlag.values.map { f => f -> commandLineFlags.getOrElse(f, platform.flagOverrides.getOrElse(f, Cpu.defaultFlags(platform.cpu)(f))) diff --git a/src/main/scala/millfork/assembly/m6809/MLine.scala b/src/main/scala/millfork/assembly/m6809/MLine.scala index 0647ea28..1b524e8c 100644 --- a/src/main/scala/millfork/assembly/m6809/MLine.scala +++ b/src/main/scala/millfork/assembly/m6809/MLine.scala @@ -47,10 +47,12 @@ object MLine { def immediate(opcode: MOpcode.Value, param: Constant): MLine = MLine(opcode, Immediate, param) - def immediate(opcode: MOpcode.Value, param: Int): MLine = MLine(opcode, Immediate, NumericConstant(param, Constant.minimumSize(param))) + def immediate(opcode: MOpcode.Value, param: Int): MLine = MLine(opcode, Immediate, Constant(param)) def absolute(opcode: MOpcode.Value, param: Constant): MLine = MLine(opcode, Absolute(false), param) + def userstack(opcode: MOpcode.Value, offset: Int): MLine = MLine(opcode, Indexed(M6809Register.U, indirect = false), Constant(offset)) + def variable(opcode: MOpcode.Value, variable: Variable, offset: Int = 0): MLine = { variable match { case v: VariableInMemory => MLine.absolute(opcode, v.toAddress) diff --git a/src/main/scala/millfork/compiler/MacroExpander.scala b/src/main/scala/millfork/compiler/MacroExpander.scala index bfd505ce..f15dfcf5 100644 --- a/src/main/scala/millfork/compiler/MacroExpander.scala +++ b/src/main/scala/millfork/compiler/MacroExpander.scala @@ -25,7 +25,7 @@ abstract class MacroExpander[T <: AbstractCode] { def h(s: String) = if (s == paramName) target.asInstanceOf[VariableExpression].name else s (stmt match { - case RawBytesStatement(contents) => RawBytesStatement(contents.replaceVariable(paramName, target)) + case RawBytesStatement(contents, be) => RawBytesStatement(contents.replaceVariable(paramName, target), be) case ExpressionStatement(e) => ExpressionStatement(e.replaceVariable(paramName, target)) case ReturnStatement(e) => ReturnStatement(e.map(f)) case ReturnDispatchStatement(i, ps, bs) => ReturnDispatchStatement(i.replaceVariable(paramName, target), ps.map(fx), bs.map { diff --git a/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala b/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala index 3f63e4aa..fc70a250 100644 --- a/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/m6809/M6809ExpressionCompiler.scala @@ -1,10 +1,10 @@ package millfork.compiler.m6809 -import millfork.assembly.m6809.{Indexed, MLine, MOpcode, TwoRegisters} +import millfork.assembly.m6809.{DAccumulatorIndexed, Indexed, MLine, MOpcode, TwoRegisters} import millfork.compiler.{AbstractExpressionCompiler, BranchIfFalse, BranchIfTrue, BranchSpec, ComparisonType, CompilationContext, NoBranching} import millfork.node.{DerefExpression, Expression, FunctionCallExpression, GeneratedConstantExpression, IndexedExpression, LhsExpression, LiteralExpression, M6809Register, SumExpression, VariableExpression} import millfork.assembly.m6809.MOpcode._ -import millfork.env.{ConstantBooleanType, ConstantPointy, FatBooleanType, MathOperator, MemoryVariable, NormalFunction, NormalParamSignature, NumericConstant, Variable} +import millfork.env.{Constant, ConstantBooleanType, ConstantPointy, FatBooleanType, MathOperator, MemoryVariable, NormalFunction, NormalParamSignature, NumericConstant, StackVariablePointy, Variable, VariablePointy} import scala.collection.GenTraversableOnce @@ -272,6 +272,11 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] { } } + def stashBIfNeeded(ctx: CompilationContext, lines: List[MLine]): List[MLine] = { + // TODO: only push if needed + MLine.pp(PSHS, M6809Register.B) :: (lines :+ MLine.pp(PULS, M6809Register.B)) + } + def storeB(ctx: CompilationContext, target: LhsExpression): List[MLine] = { target match { case VariableExpression(name) => @@ -283,6 +288,21 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] { ctx.env.getPointy(name) match { case p: ConstantPointy => compileToX(ctx, index) :+ MLine(STB, Indexed(M6809Register.X, indirect = false), p.value) + case v: VariablePointy => + ctx.env.eval(index) match { + case Some(ix) => List(MLine.absolute(LDX, v.addr), MLine(STB, Indexed(M6809Register.X, indirect = false), ix * v.elementType.size)) + case _ => + v.indexType.size match { + case 1 => + stashBIfNeeded(ctx, + compileToD(ctx, index) :+ MLine(LEAX, DAccumulatorIndexed(M6809Register.X, indirect = false), Constant.Zero)) :+ + MLine(STB, Indexed(M6809Register.X, indirect = false), Constant.Zero) + } + } + case v: StackVariablePointy => + ctx.env.eval(index) match { + case Some(ix) => List(MLine.userstack(LDX, v.offset), MLine(STB, Indexed(M6809Register.X, indirect = false), ix * v.elementType.size)) + } } } } diff --git a/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala b/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala index f51ad9b4..5f948725 100644 --- a/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala +++ b/src/main/scala/millfork/compiler/m6809/M6809StatementCompiler.scala @@ -17,6 +17,18 @@ object M6809StatementCompiler extends AbstractStatementCompiler[MLine] { // TODO: clean stack // TODO: RTI List(MLine.inherent(RTS)) -> Nil + case ReturnStatement(Some(e)) => + // TODO: clean stack + // TODO: RTI + val rts = List(MLine.inherent(RTS)) + val eval = ctx.function.returnType.size match { + case 0 => + ctx.log.error("Cannot return anything from a void function", statement.position) + M6809ExpressionCompiler.compile(ctx, e, MExpressionTarget.NOTHING) + case 1 => M6809ExpressionCompiler.compileToB(ctx, e) + case 2 => M6809ExpressionCompiler.compileToD(ctx, e) + } + (eval ++ rts) -> Nil case M6809AssemblyStatement(opcode, addrMode, expression, elidability) => ctx.env.evalForAsm(expression) match { case Some(e) => List(MLine(opcode, addrMode, e, elidability)) -> Nil @@ -62,7 +74,8 @@ object M6809StatementCompiler extends AbstractStatementCompiler[MLine] { override def branchChunk(opcode: BranchingOpcodeMapping, labelName: String): List[MLine] = ??? - override def compileExpressionForBranching(ctx: CompilationContext, expr: Expression, branching: BranchSpec): List[MLine] = ??? + override def compileExpressionForBranching(ctx: CompilationContext, expr: Expression, branching: BranchSpec): List[MLine] = + M6809ExpressionCompiler.compile(ctx, expr, MExpressionTarget.NOTHING, branching) override def replaceLabel(ctx: CompilationContext, line: MLine, from: String, to: String): MLine = ??? diff --git a/src/main/scala/millfork/compiler/mos/MosStatementCompiler.scala b/src/main/scala/millfork/compiler/mos/MosStatementCompiler.scala index 37cca242..a33f6652 100644 --- a/src/main/scala/millfork/compiler/mos/MosStatementCompiler.scala +++ b/src/main/scala/millfork/compiler/mos/MosStatementCompiler.scala @@ -179,7 +179,7 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] { case _ => a } List(AssemblyLine(o, actualAddrMode, c, e)) -> Nil - case RawBytesStatement(contents) => + case RawBytesStatement(contents, _) => env.extractArrayContents(contents).map { expr => env.eval(expr) match { case Some(c) => AssemblyLine(BYTE, RawByte, c, elidability = Elidability.Fixed) diff --git a/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala b/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala index 6569edad..668016e9 100644 --- a/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala +++ b/src/main/scala/millfork/compiler/z80/Z80StatementCompiler.scala @@ -206,7 +206,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] { } case ExpressionStatement(e) => Z80ExpressionCompiler.compile(ctx, e, ZExpressionTarget.NOTHING) -> Nil - case RawBytesStatement(contents) => + case RawBytesStatement(contents, _) => env.extractArrayContents(contents).map { expr => env.eval(expr) match { case Some(c) => ZLine(BYTE, NoRegisters, c, elidability = Elidability.Fixed) diff --git a/src/main/scala/millfork/env/Constant.scala b/src/main/scala/millfork/env/Constant.scala index 12673a8b..77b1bee8 100644 --- a/src/main/scala/millfork/env/Constant.scala +++ b/src/main/scala/millfork/env/Constant.scala @@ -69,6 +69,8 @@ sealed trait Constant { } } + def subbyteBe(index: Int, totalSize: Int): Constant = subbyte(totalSize - 1 - index) + def subword(index: Int): Constant = { if (requiredSize <= index) Constant.Zero else { @@ -146,7 +148,20 @@ case class StructureConstant(typ: StructType, fields: List[Constant]) extends Co for ((fv, (ft, _)) <- fields.zip(typ.mutableFieldsWithTypes)) { val fs = ft.size if (index < offset + fs) { - return fv.subbyte(index - offset) + val indexInField = index - offset + return fv.subbyte(indexInField) + } + offset += fs + } + Constant.Zero + } + override def subbyteBe(index: Int, totalSize: Int): Constant = { + var offset = 0 + for ((fv, (ft, _)) <- fields.zip(typ.mutableFieldsWithTypes)) { + val fs = ft.size + if (index < offset + fs) { + val indexInField = index - offset + return fv.subbyteBe(indexInField, fs) } offset += fs } diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index 53674752..f3206ac3 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -1169,7 +1169,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa } if (maybeGet[Thing](name).isEmpty) { println("registering text literal") - root.registerArray(ArrayDeclarationStatement(name, None, None, "byte", None, const = true, Some(LiteralContents(literal.characters)), None).pos(literal.position), options) + root.registerArray(ArrayDeclarationStatement(name, None, None, "byte", None, const = true, Some(LiteralContents(literal.characters)), None, options.isBigEndian).pos(literal.position), options) } name } @@ -1341,11 +1341,11 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa case CombinedContents(xs) => xs.flatMap(extractArrayContents) case pc@ProcessedContents("struct", xs: CombinedContents) => checkIfArrayContentsAreSimple(xs) - xs.getAllExpressions.flatMap(x => extractStructArrayContents(x, None)) + xs.getAllExpressions(options.isBigEndian).flatMap(x => extractStructArrayContents(x, None)) case pc@ProcessedContents("struct", _) => log.error(s"Invalid struct array contents", pc.position) Nil - case pc@ProcessedContents(f, xs) => pc.getAllExpressions + case pc@ProcessedContents(f, xs) => pc.getAllExpressions(options.isBigEndian) case ForLoopContents(v, start, end, direction, body) => (eval(start), eval(end)) match { case (Some(NumericConstant(s, sz1)), Some(NumericConstant(e, sz2))) => @@ -1412,7 +1412,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa case VariableExpression(name) => maybeGet[Type](name) match { case Some(typ@EnumType(_, Some(count))) => - typ -> NumericConstant(count, 1) + typ -> NumericConstant(count, Constant.minimumSize(count)) case Some(typ) => log.error(s"Type $name cannot be used as an array index", l.position) w -> Constant.Zero @@ -1666,6 +1666,9 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa val b = get[VariableType]("byte") val w = get[VariableType]("word") if (typ.name == "__reg$type") { + if (options.isBigEndian) { + throw new IllegalArgumentException("__reg$type on 6809???") + } return (".lo", 0, b) :: (".hi", 1, b) :: (".loword", 0, w) :: @@ -1678,10 +1681,23 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa } typ match { case _: PlainType => typ.size match { - case 2 => List( + case 2 => if (options.isBigEndian) List( + (".lo", 1, b), + (".hi", 0, b) + ) else List( (".lo", 0, b), (".hi", 1, b)) - case 3 => List( + case 3 => if (options.isBigEndian) List( + (".loword", 1, w), + (".loword.lo", 2, b), + (".loword.hi", 1, b), + (".hiword", 0, w), + (".hiword.lo", 1, b), + (".hiword.hi", 0, b), + (".b0", 2, b), + (".b1", 1, b), + (".b2", 0, b) + ) else List( (".loword", 0, w), (".loword.lo", 0, b), (".loword.hi", 1, b), @@ -1691,7 +1707,18 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa (".b0", 0, b), (".b1", 1, b), (".b2", 2, b)) - case 4 => List( + case 4 => if (options.isBigEndian) List( + (".loword", 2, w), + (".hiword", 0, w), + (".loword.lo", 3, b), + (".loword.hi", 2, b), + (".hiword.lo", 1, b), + (".hiword.hi", 0, b), + (".b0", 3, b), + (".b1", 2, b), + (".b2", 1, b), + (".b3", 0, b) + ) else List( (".loword", 0, w), (".hiword", 2, w), (".loword.lo", 0, b), @@ -1701,22 +1728,36 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa (".b0", 0, b), (".b1", 1, b), (".b2", 2, b), - (".b3", 3, b)) + (".b3", 3, b) + ) case sz if sz > 4 => - (".lo", 0, b) :: - (".loword", 0, w) :: - (".loword.lo", 0, b) :: - (".loword.hi", 1, b) :: - List.tabulate(sz){ i => (".b" + i, i, b) } + if (options.isBigEndian) { + (".lo", sz - 1, b) :: + (".loword", sz - 2, w) :: + (".loword.lo", sz - 1, b) :: + (".loword.hi", sz - 2, b) :: + List.tabulate(sz){ i => (".b" + i, sz - 1 - i, b) } + } else { + (".lo", 0, b) :: + (".loword", 0, w) :: + (".loword.lo", 0, b) :: + (".loword.hi", 1, b) :: + List.tabulate(sz){ i => (".b" + i, i, b) } + } case _ => Nil } - case p: PointerType => - List( - (".raw", 0, p), - (".raw.lo", 0, b), - (".raw.hi", 1, b), - (".lo", 0, b), - (".hi", 1, b)) + case p: PointerType => if (options.isBigEndian) List( + (".raw", 0, p), + (".raw.lo", 1, b), + (".raw.hi", 0, b), + (".lo", 1, b), + (".hi", 0, b) + ) else List( + (".raw", 0, p), + (".raw.lo", 0, b), + (".raw.hi", 1, b), + (".lo", 0, b), + (".hi", 1, b)) case s: StructType => val builder = new ListBuffer[(String, Int, VariableType)] var offset = 0 diff --git a/src/main/scala/millfork/node/Node.scala b/src/main/scala/millfork/node/Node.scala index 0e9b47f4..f0bd6086 100644 --- a/src/main/scala/millfork/node/Node.scala +++ b/src/main/scala/millfork/node/Node.scala @@ -352,19 +352,19 @@ case class VariableDeclarationStatement(name: String, } trait ArrayContents extends Node { - def getAllExpressions: List[Expression] + def getAllExpressions(bigEndian: Boolean): List[Expression] def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents } case class LiteralContents(contents: List[Expression]) extends ArrayContents { - override def getAllExpressions: List[Expression] = contents + override def getAllExpressions(bigEndian: Boolean): List[Expression] = contents override def replaceVariable(variable: String, expression: Expression): ArrayContents = LiteralContents(contents.map(_.replaceVariable(variable, expression))) } case class ForLoopContents(variable: String, start: Expression, end: Expression, direction: ForDirection.Value, body: ArrayContents) extends ArrayContents { - override def getAllExpressions: List[Expression] = start :: end :: body.getAllExpressions.map(_.replaceVariable(variable, LiteralExpression(0, 1))) + override def getAllExpressions(bigEndian: Boolean): List[Expression] = start :: end :: body.getAllExpressions(bigEndian).map(_.replaceVariable(variable, LiteralExpression(0, 1))) override def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents = if (variableToReplace == variable) this else ForLoopContents( @@ -376,39 +376,44 @@ case class ForLoopContents(variable: String, start: Expression, end: Expression, } case class CombinedContents(contents: List[ArrayContents]) extends ArrayContents { - override def getAllExpressions: List[Expression] = contents.flatMap(_.getAllExpressions) + override def getAllExpressions(bigEndian: Boolean): List[Expression] = contents.flatMap(_.getAllExpressions(bigEndian)) override def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents = CombinedContents(contents.map(_.replaceVariable(variableToReplace, expression))) } case class ProcessedContents(processor: String, values: ArrayContents) extends ArrayContents { - override def getAllExpressions: List[Expression] = processor match { - case "word" | "word_le" => - values.getAllExpressions.flatMap(expr => List( + private def normalizeProcessor(bigEndian: Boolean): String = processor match { + case "word" => if (bigEndian) "word_be" else "word_le" + case "long" => if (bigEndian) "long_be" else "long_le" + case x => x + } + override def getAllExpressions(bigEndian: Boolean): List[Expression] = normalizeProcessor(bigEndian) match { + case "word_le" => + values.getAllExpressions(bigEndian).flatMap(expr => List( FunctionCallExpression("lo", List(expr)).pos(expr.position), FunctionCallExpression("hi", List(expr)).pos(expr.position) )) case "word_be" => - values.getAllExpressions.flatMap(expr => List( + values.getAllExpressions(bigEndian).flatMap(expr => List( FunctionCallExpression("hi", List(expr)).pos(expr.position), FunctionCallExpression("lo", List(expr)).pos(expr.position) )) - case "long" | "long_le" => - values.getAllExpressions.flatMap(expr => List( + case "long_le" => + values.getAllExpressions(bigEndian).flatMap(expr => List( FunctionCallExpression("lo", List(expr)).pos(expr.position), FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(8, 1))).pos(expr.position))).pos(expr.position), FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(16, 1))).pos(expr.position))).pos(expr.position), FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(24, 1))).pos(expr.position))).pos(expr.position) )) case "long_be" => - values.getAllExpressions.flatMap(expr => List( + values.getAllExpressions(bigEndian).flatMap(expr => List( FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(24, 1))).pos(expr.position))).pos(expr.position), FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(16, 1))).pos(expr.position))).pos(expr.position), FunctionCallExpression("lo", List(FunctionCallExpression(">>", List(expr, LiteralExpression(8, 1))).pos(expr.position))).pos(expr.position), FunctionCallExpression("lo", List(expr)).pos(expr.position) )) - case "struct" => values.getAllExpressions // not used for emitting actual arrays + case "struct" => values.getAllExpressions(bigEndian) // not used for emitting actual arrays } override def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents = @@ -438,8 +443,9 @@ case class ArrayDeclarationStatement(name: String, address: Option[Expression], const: Boolean, elements: Option[ArrayContents], - alignment: Option[MemoryAlignment]) extends BankedDeclarationStatement { - override def getAllExpressions: List[Expression] = List(length, address).flatten ++ elements.fold(List[Expression]())(_.getAllExpressions) + alignment: Option[MemoryAlignment], + bigEndian: Boolean) extends BankedDeclarationStatement { + override def getAllExpressions: List[Expression] = List(length, address).flatten ++ elements.fold(List[Expression]())(_.getAllExpressions(bigEndian)) override def withChangedBank(bank: String): BankedDeclarationStatement = copy(bank = Some(bank)) } @@ -473,8 +479,8 @@ case class FunctionDeclarationStatement(name: String, sealed trait ExecutableStatement extends Statement -case class RawBytesStatement(contents: ArrayContents) extends ExecutableStatement { - override def getAllExpressions: List[Expression] = contents.getAllExpressions +case class RawBytesStatement(contents: ArrayContents, bigEndian: Boolean) extends ExecutableStatement { + override def getAllExpressions: List[Expression] = contents.getAllExpressions(bigEndian) } sealed trait CompoundStatement extends ExecutableStatement { diff --git a/src/main/scala/millfork/node/opt/UnusedFunctions.scala b/src/main/scala/millfork/node/opt/UnusedFunctions.scala index 1c61f52c..375c2758 100644 --- a/src/main/scala/millfork/node/opt/UnusedFunctions.scala +++ b/src/main/scala/millfork/node/opt/UnusedFunctions.scala @@ -100,7 +100,7 @@ object UnusedFunctions extends NodeOptimization { def getAllCalledFunctions(expressions: List[Node]): List[String] = expressions.flatMap { case s: VariableDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.initialValue.toList) case s: ArrayDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.elements.toList) - case s: ArrayContents => getAllCalledFunctions(s.getAllExpressions) + case s: ArrayContents => getAllCalledFunctions(s.getAllExpressions(false)) // endianness doesn't matter here at all case s: FunctionDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.statements.getOrElse(Nil)) case Assignment(target, expr) => getAllCalledFunctions(target :: expr :: Nil) case s: ReturnDispatchStatement => diff --git a/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala b/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala index 200bed9e..28052fc6 100644 --- a/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala +++ b/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala @@ -52,7 +52,7 @@ object UnusedGlobalVariables extends NodeOptimization { def getAllReadVariables(expressions: List[Node]): List[String] = expressions.flatMap { case s: VariableDeclarationStatement => getAllReadVariables(s.address.toList) ++ getAllReadVariables(s.initialValue.toList) ++ (if (s.stack) List("__sp", "__stack") else Nil) case s: ArrayDeclarationStatement => getAllReadVariables(s.address.toList) ++ getAllReadVariables(s.elements.toList) - case s: ArrayContents => getAllReadVariables(s.getAllExpressions) + case s: ArrayContents => getAllReadVariables(s.getAllExpressions(false)) // endianness doesn't matter here at all case s: FunctionDeclarationStatement => getAllReadVariables(s.address.toList) ++ getAllReadVariables(s.statements.getOrElse(Nil)) case Assignment(VariableExpression(_), expr) => getAllReadVariables(expr :: Nil) case ExpressionStatement(FunctionCallExpression(op, VariableExpression(_) :: params)) if op.endsWith("=") => getAllReadVariables(params) diff --git a/src/main/scala/millfork/output/AbstractAssembler.scala b/src/main/scala/millfork/output/AbstractAssembler.scala index 585fc505..e16957b5 100644 --- a/src/main/scala/millfork/output/AbstractAssembler.scala +++ b/src/main/scala/millfork/output/AbstractAssembler.scala @@ -30,7 +30,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program var initializedVariablesSize: Int = 0 protected val log: Logger = rootEnv.log - val mem = new CompiledMemory(platform.bankNumbers.toList) + val mem = new CompiledMemory(platform.bankNumbers.toList, platform.isBigEndian) val labelMap: mutable.Map[String, (Int, Int)] = mutable.Map() private val bytesToWriteLater = mutable.ListBuffer[(String, Int, Constant)]() private val wordsToWriteLater = mutable.ListBuffer[(String, Int, Constant)]() @@ -139,7 +139,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program s.typ.size match { case 0 => 0 case 1 => deepConstResolve(s.subbyte(0)) - case 2 => deepConstResolve(s.subword(0)) + case 2 => deepConstResolve(s.subword(0)) // TODO: endianness? case _ => ??? } case CompoundConstant(operator, lc, rc) => @@ -189,6 +189,8 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program def deduplicate(options: CompilationOptions, compiledFunctions: mutable.Map[String, CompiledFunction[T]]): Unit + protected def subbyte(c: Constant, index: Int, totalSize: Int): Constant = if (platform.isBigEndian) c.subbyteBe(index, totalSize) else c.subbyte(index) + def assemble(callGraph: CallGraph, unfilteredOptimizations: Seq[AssemblyOptimization[T]], options: CompilationOptions): AssemblerOutput = { mem.programName = options.outputFileName.getOrElse("MILLFORK") val platform = options.platform @@ -291,7 +293,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program env.eval(item) match { case Some(c) => for(i <- 0 until elementType.size) { - writeByte(bank, index, c.subbyte(i)) + writeByte(bank, index, subbyte(c, i, elementType.size)) bank0.occupied(index) = true bank0.initialized(index) = true bank0.writeable(index) = true @@ -302,7 +304,8 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program } } items.flatMap(expr => env.eval(expr) match { - case Some(c) => List.tabulate(elementType.size)(i => c.subbyte(i).quickSimplify.toString) + case Some(c) => + List.tabulate(elementType.size)(i => subbyte(c, i, elementType.size).quickSimplify.toString) case None => List.fill(elementType.size)("") }).grouped(16).foreach { group => assembly.append(" " + bytePseudoopcode + " " + group.mkString(", ")) @@ -382,9 +385,12 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program val c = thing.toAddress writeByte(bank, index, 0x2c.toByte) // BIT abs index += 1 + if (platform.isBigEndian) { + throw new IllegalStateException("LUnix cannot run on big-endian architectures") + } for (i <- 0 until typ.size) { - writeByte(bank, index, c.subbyte(i)) - assembly.append(" " + bytePseudoopcode + " " + c.subbyte(i).quickSimplify) + writeByte(bank, index, subbyte(c, i, typ.size)) + assembly.append(" " + bytePseudoopcode + " " + subbyte(c, i, typ.size).quickSimplify) index += 1 } initializedVariablesSize += typ.size @@ -431,14 +437,14 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program env.eval(item) match { case Some(c) => for (i <- 0 until elementType.size) { - writeByte(bank, index, c.subbyte(i)) + writeByte(bank, index, subbyte(c, i, elementType.size)) index += 1 } case None => log.error(s"Non-constant contents of array `$name`", item.position) } } items.flatMap(expr => env.eval(expr) match { - case Some(c) => List.tabulate(elementType.size)(i => c.subbyte(i).quickSimplify.toString) + case Some(c) => List.tabulate(elementType.size)(i => subbyte(c, i, elementType.size).quickSimplify.toString) case None => List.fill(elementType.size)("") }).grouped(16).foreach { group => assembly.append(" " + bytePseudoopcode + " " + group.mkString(", ")) @@ -464,8 +470,8 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program env.eval(value) match { case Some(c) => for (i <- 0 until typ.size) { - writeByte(bank, index, c.subbyte(i)) - assembly.append(" " + bytePseudoopcode + " " + c.subbyte(i).quickSimplify) + writeByte(bank, index, subbyte(c, i, typ.size)) + assembly.append(" " + bytePseudoopcode + " " + subbyte(c, i, typ.size).quickSimplify) index += 1 } case None => diff --git a/src/main/scala/millfork/output/AbstractInliningCalculator.scala b/src/main/scala/millfork/output/AbstractInliningCalculator.scala index 92948c7f..8455d688 100644 --- a/src/main/scala/millfork/output/AbstractInliningCalculator.scala +++ b/src/main/scala/millfork/output/AbstractInliningCalculator.scala @@ -69,7 +69,7 @@ abstract class AbstractInliningCalculator[T <: AbstractCode] { case ReturnDispatchStatement(index, params, branches) => getAllCalledFunctions(List(index)) ++ getAllCalledFunctions(params) ++ getAllCalledFunctions(branches.map(b => b.function)) case s: ArrayDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.elements.toList) - case s: ArrayContents => getAllCalledFunctions(s.getAllExpressions) + case s: ArrayContents => getAllCalledFunctions(s.getAllExpressions(false)) // endianness doesn't matter here at all case s: FunctionDeclarationStatement => getAllCalledFunctions(s.address.toList) ++ getAllCalledFunctions(s.statements.getOrElse(Nil)) case Assignment(VariableExpression(_), expr) => getAllCalledFunctions(expr :: Nil) case MosAssemblyStatement(Opcode.JSR, _, VariableExpression(name), Elidability.Elidable) => (name -> false) :: Nil diff --git a/src/main/scala/millfork/output/CompiledMemory.scala b/src/main/scala/millfork/output/CompiledMemory.scala index cb105f4e..731a7d5c 100644 --- a/src/main/scala/millfork/output/CompiledMemory.scala +++ b/src/main/scala/millfork/output/CompiledMemory.scala @@ -5,19 +5,25 @@ import scala.collection.mutable /** * @author Karol Stasiak */ -class CompiledMemory(bankNames: List[(String, Int)]) { +class CompiledMemory(bankNames: List[(String, Int)], bigEndian: Boolean) { var programName = "MILLFORK" - val banks: mutable.Map[String, MemoryBank] = mutable.Map(bankNames.map(p => p._1 -> new MemoryBank(p._2)): _*) + val banks: mutable.Map[String, MemoryBank] = mutable.Map(bankNames.map(p => p._1 -> new MemoryBank(p._2, bigEndian)): _*) } -class MemoryBank(val index: Int) { +class MemoryBank(val index: Int, val isBigEndian: Boolean) { def readByte(addr: Int): Int = output(addr) & 0xff - def readWord(addr: Int): Int = readByte(addr) + (readByte(addr + 1) << 8) + def readWord(addr: Int): Int = + if (isBigEndian) readByte(addr + 1) + (readByte(addr) << 8) + else readByte(addr) + (readByte(addr + 1) << 8) - def readMedium(addr: Int): Int = readByte(addr) + (readByte(addr + 1) << 8) + (readByte(addr + 2) << 16) + def readMedium(addr: Int): Int = + if (isBigEndian) readByte(addr + 2) + (readByte(addr + 1) << 8) + (readByte(addr) << 16) + else readByte(addr) + (readByte(addr + 1) << 8) + (readByte(addr + 2) << 16) - def readLong(addr: Int): Int = readByte(addr) + (readByte(addr + 1) << 8) + (readByte(addr + 2) << 16) + (readByte(addr + 3) << 24) + def readLong(addr: Int): Int = + if (isBigEndian) readByte(addr + 3) + (readByte(addr + 2) << 8) + (readByte(addr + 1) << 16) + (readByte(addr) << 24) + else readByte(addr) + (readByte(addr + 1) << 8) + (readByte(addr + 2) << 16) + (readByte(addr + 3) << 24) def readWord(addrHi: Int, addrLo: Int): Int = readByte(addrLo) + (readByte(addrHi) << 8) diff --git a/src/main/scala/millfork/parser/MfParser.scala b/src/main/scala/millfork/parser/MfParser.scala index c6325d2f..4b19a795 100644 --- a/src/main/scala/millfork/parser/MfParser.scala +++ b/src/main/scala/millfork/parser/MfParser.scala @@ -215,7 +215,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri def arrayContents: P[ArrayContents] = arrayProcessedContents | arrayListContents | arrayLoopContents | arrayFileContents | arrayStringContents - def arrayContentsForAsm: P[RawBytesStatement] = (arrayListContents | arrayStringContents).map(RawBytesStatement) + def arrayContentsForAsm: P[RawBytesStatement] = (arrayListContents | arrayStringContents).map(c => RawBytesStatement(c, options.isBigEndian)) val aliasDefinition: P[Seq[AliasDefinitionStatement]] = for { p <- position() @@ -252,7 +252,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri alignment <- alignmentDeclaration(fastAlignmentForFunctions).? ~/ HWS addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).? ~/ HWS contents <- ("=" ~/ HWS ~/ arrayContents).? ~/ HWS - } yield Seq(ArrayDeclarationStatement(name, bank, length, elementType.getOrElse("byte"), addr, const.isDefined, contents, alignment).pos(p)) + } yield Seq(ArrayDeclarationStatement(name, bank, length, elementType.getOrElse("byte"), addr, const.isDefined, contents, alignment, options.isBigEndian).pos(p)) def tightMfExpression(allowIntelHex: Boolean, allowTopLevelIndexing: Boolean): P[Expression] = { val a = if (allowIntelHex) atomWithIntel else atom diff --git a/src/test/scala/millfork/test/BasicSymonTest.scala b/src/test/scala/millfork/test/BasicSymonTest.scala index 7e52b418..babcbb8e 100644 --- a/src/test/scala/millfork/test/BasicSymonTest.scala +++ b/src/test/scala/millfork/test/BasicSymonTest.scala @@ -140,7 +140,7 @@ class BasicSymonTest extends FunSuite with Matchers { | } | } """.stripMargin) { m => - m.readWord(0xc000) should equal(4) + m.readByte(0xc000) should equal(4) } } diff --git a/src/test/scala/millfork/test/StructSuite.scala b/src/test/scala/millfork/test/StructSuite.scala index cb3b4b88..cf591e55 100644 --- a/src/test/scala/millfork/test/StructSuite.scala +++ b/src/test/scala/millfork/test/StructSuite.scala @@ -40,7 +40,7 @@ class StructSuite extends FunSuite with Matchers { } test("Nested structs") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Intel8086)(""" + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Intel8086, Cpu.Motorola6809)(""" | struct inner { word x, word y } | struct s { | word w @@ -53,7 +53,11 @@ class StructSuite extends FunSuite with Matchers { | output.b = 5 | output.w.hi = output.b | output.p = output.w.addr + | #if BIG_ENDIAN + | output.p[1] = 6 + | #else | output.p[0] = 6 + | #endif | output.i.x.lo = 55 | output.i.x.hi = s.p.offset | output.i.y = 777 @@ -62,14 +66,13 @@ class StructSuite extends FunSuite with Matchers { m.readWord(0xc000) should equal(0x506) m.readByte(0xc002) should equal(5) m.readWord(0xc003) should equal(0xc000) - m.readByte(0xc005) should equal(55) - m.readByte(0xc006) should equal(3) + m.readWord(0xc005) should equal(3 * 256 + 55) m.readWord(0xc007) should equal(777) } } test("Basic union support") { - EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Intel8086)(""" + EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Intel8086, Cpu.Motorola6809)(""" | struct point { byte x, byte y } | union point_or_word { point p, word w } | word output @$c000 @@ -80,7 +83,11 @@ class StructSuite extends FunSuite with Matchers { | output = u.w | } """.stripMargin) { m => - m.readWord(0xc000) should equal(0x201) + if (m.isBigEndian) { + m.readWord(0xc000) should equal(0x102) + } else { + m.readWord(0xc000) should equal(0x201) + } } } diff --git a/src/test/scala/millfork/test/emu/EmuI86Run.scala b/src/test/scala/millfork/test/emu/EmuI86Run.scala index e14a59f9..09641e7c 100644 --- a/src/test/scala/millfork/test/emu/EmuI86Run.scala +++ b/src/test/scala/millfork/test/emu/EmuI86Run.scala @@ -74,7 +74,7 @@ class EmuI86Run(nodeOptimizations: List[NodeOptimization], assemblyOptimizations } def apply2(source: String): (Timings, MemoryBank) = { - if (!Settings.enableIntel8086Tests) return Timings(-1, -1) -> new MemoryBank(0) + if (!Settings.enableIntel8086Tests) return Timings(-1, -1) -> new MemoryBank(0, false) Console.out.flush() Console.err.flush() val log = TestErrorReporting.log