mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-03 19:31:02 +00:00
Better support for big-endian systems
This commit is contained in:
parent
dd4cb17a80
commit
b68e4b67c8
@ -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)
|
||||
|
@ -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)))
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 = ???
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
17
src/main/scala/millfork/env/Constant.scala
vendored
17
src/main/scala/millfork/env/Constant.scala
vendored
@ -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
|
||||
}
|
||||
|
81
src/main/scala/millfork/env/Environment.scala
vendored
81
src/main/scala/millfork/env/Environment.scala
vendored
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 =>
|
||||
|
@ -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)
|
||||
|
@ -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)("<? unknown constant ?>")
|
||||
}).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)("<? unknown constant ?>")
|
||||
}).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 =>
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -140,7 +140,7 @@ class BasicSymonTest extends FunSuite with Matchers {
|
||||
| }
|
||||
| }
|
||||
""".stripMargin) { m =>
|
||||
m.readWord(0xc000) should equal(4)
|
||||
m.readByte(0xc000) should equal(4)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user