mirror of
https://github.com/KarolS/millfork.git
synced 2024-09-10 11:54:30 +00:00
Preliminary support for object alignment
This commit is contained in:
parent
60d2cc1959
commit
c846a19eef
@ -29,7 +29,10 @@ class AbstractExpressionCompiler[T <: AbstractCode] {
|
||||
|
||||
def callingContext(ctx: CompilationContext, v: MemoryVariable): CompilationContext = {
|
||||
val result = new Environment(Some(ctx.env), "", ctx.options.platform.cpuFamily, ctx.jobContext)
|
||||
result.registerVariable(VariableDeclarationStatement(v.name, v.typ.name, stack = false, global = false, constant = false, volatile = false, register = false, initialValue = None, address = None, bank = v.declaredBank), ctx.options)
|
||||
result.registerVariable(VariableDeclarationStatement(
|
||||
v.name, v.typ.name,
|
||||
stack = false, global = false, constant = false, volatile = false, register = false,
|
||||
initialValue = None, address = None, bank = v.declaredBank, alignment = None), ctx.options)
|
||||
ctx.copy(env = result)
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import millfork.assembly.AbstractCode
|
||||
import millfork.env._
|
||||
import millfork.error.ConsoleLogger
|
||||
import millfork.node._
|
||||
import millfork.output.NoAlignment
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
@ -132,7 +133,7 @@ abstract class AbstractReturnDispatch[T <: AbstractCode] {
|
||||
val a = InitializedArray(label + "$" + ix + ".array", None, (paramMins(ix) to paramMaxes(ix)).map { key =>
|
||||
map(key)._2.lift(ix).getOrElse(LiteralExpression(0, 1))
|
||||
}.toList,
|
||||
ctx.function.declaredBank, b, b)
|
||||
ctx.function.declaredBank, b, b, NoAlignment)
|
||||
env.registerUnnamedArray(a)
|
||||
a
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ abstract class AbstractStatementPreprocessor(ctx: CompilationContext, statements
|
||||
}
|
||||
protected val reentrantVars: Set[String] = trackableVars.filter(v => env.get[Variable](v) match {
|
||||
case _: StackVariable => true
|
||||
case UninitializedMemoryVariable(_, _, VariableAllocationMethod.Auto, _) => ctx.options.flag(CompilationFlag.DangerousOptimizations)
|
||||
case UninitializedMemoryVariable(_, _, VariableAllocationMethod.Auto, _, _) => ctx.options.flag(CompilationFlag.DangerousOptimizations)
|
||||
case _ => false
|
||||
})
|
||||
protected val nonreentrantVars: Set[String] = trackableVars -- reentrantVars
|
||||
@ -172,7 +172,7 @@ abstract class AbstractStatementPreprocessor(ctx: CompilationContext, statements
|
||||
case TextLiteralExpression(characters) =>
|
||||
val name = genName(characters)
|
||||
if (ctx.env.maybeGet[Thing](name).isEmpty) {
|
||||
ctx.env.root.registerArray(ArrayDeclarationStatement(name, None, None, None, Some(LiteralContents(characters))).pos(pos), ctx.options)
|
||||
ctx.env.root.registerArray(ArrayDeclarationStatement(name, None, None, None, Some(LiteralContents(characters)), None).pos(pos), ctx.options)
|
||||
}
|
||||
VariableExpression(name).pos(pos)
|
||||
case VariableExpression(v) if currentVarValues.contains(v) =>
|
||||
|
@ -7,6 +7,7 @@ import millfork.compiler.{AbstractReturnDispatch, BranchSpec, CompilationContext
|
||||
import millfork.env._
|
||||
import millfork.error.ConsoleLogger
|
||||
import millfork.node._
|
||||
import millfork.output.NoAlignment
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
@ -40,7 +41,7 @@ object MosReturnDispatch extends AbstractReturnDispatch[AssemblyLine] {
|
||||
}
|
||||
|
||||
if (useJmpaix) {
|
||||
val jumpTable = InitializedArray(label + "$jt.array", None, (actualMin to actualMax).flatMap(i => List(lobyte0(map(i)._1), hibyte0(map(i)._1))).toList, ctx.function.declaredBank, b, b)
|
||||
val jumpTable = InitializedArray(label + "$jt.array", None, (actualMin to actualMax).flatMap(i => List(lobyte0(map(i)._1), hibyte0(map(i)._1))).toList, ctx.function.declaredBank, b, b, NoAlignment)
|
||||
env.registerUnnamedArray(jumpTable)
|
||||
if (copyParams.isEmpty) {
|
||||
val loadIndex = MosExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None)
|
||||
@ -55,8 +56,8 @@ object MosReturnDispatch extends AbstractReturnDispatch[AssemblyLine] {
|
||||
}
|
||||
} else {
|
||||
val loadIndex = MosExpressionCompiler.compile(ctx, stmt.indexer, Some(b -> RegisterVariable(MosRegister.X, b)), BranchSpec.None)
|
||||
val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => lobyte1(map(i)._1)).toList, ctx.function.declaredBank, b, b)
|
||||
val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => hibyte1(map(i)._1)).toList, ctx.function.declaredBank, b, b)
|
||||
val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => lobyte1(map(i)._1)).toList, ctx.function.declaredBank, b, b, NoAlignment)
|
||||
val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => hibyte1(map(i)._1)).toList, ctx.function.declaredBank, b, b, NoAlignment)
|
||||
env.registerUnnamedArray(jumpTableLo)
|
||||
env.registerUnnamedArray(jumpTableHi)
|
||||
val actualJump = if (ctx.options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
|
@ -5,6 +5,7 @@ import millfork.compiler.{AbstractReturnDispatch, CompilationContext}
|
||||
import millfork.env.{Constant, InitializedArray, ThingInMemory, VariableType}
|
||||
import millfork.error.ConsoleLogger
|
||||
import millfork.node.{Expression, ReturnDispatchStatement, ZRegister}
|
||||
import millfork.output.NoAlignment
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
@ -50,8 +51,8 @@ object Z80ReturnDispatch extends AbstractReturnDispatch[ZLine] {
|
||||
}
|
||||
val copyParams = pair._2.reverse.flatten
|
||||
val offsetAfterParams = pair._1
|
||||
val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => lobyte0(map(i)._1)).toList, ctx.function.declaredBank, b, b)
|
||||
val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => hibyte0(map(i)._1)).toList, ctx.function.declaredBank, b, b)
|
||||
val jumpTableLo = InitializedArray(label + "$jl.array", None, (actualMin to actualMax).map(i => lobyte0(map(i)._1)).toList, ctx.function.declaredBank, b, b, NoAlignment)
|
||||
val jumpTableHi = InitializedArray(label + "$jh.array", None, (actualMin to actualMax).map(i => hibyte0(map(i)._1)).toList, ctx.function.declaredBank, b, b, NoAlignment)
|
||||
env.registerUnnamedArray(jumpTableLo)
|
||||
env.registerUnnamedArray(jumpTableHi)
|
||||
|
||||
|
57
src/main/scala/millfork/env/Environment.scala
vendored
57
src/main/scala/millfork/env/Environment.scala
vendored
@ -7,7 +7,7 @@ import millfork.assembly.z80.{IfFlagClear, IfFlagSet, ZFlag}
|
||||
import millfork.compiler.LabelGenerator
|
||||
import millfork.error.Logger
|
||||
import millfork.node._
|
||||
import millfork.output.{AllocationLocation, CompiledMemory, VariableAllocator}
|
||||
import millfork.output._
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
@ -156,7 +156,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
case VariableAllocationMethod.Zeropage =>
|
||||
if (forZpOnly || !options.platform.hasZeroPage) {
|
||||
val addr =
|
||||
allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Zeropage)
|
||||
allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Zeropage, alignment = m.alignment)
|
||||
onEachVariable(m.name, addr)
|
||||
List(
|
||||
ConstantThing(m.name.stripPrefix(prefix) + "`", NumericConstant(addr, 2), p)
|
||||
@ -170,7 +170,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
val graveName = m.name.stripPrefix(prefix) + "`"
|
||||
if (forZpOnly) {
|
||||
if (bank == "default") {
|
||||
allocators(bank).tryAllocateZeropageBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes) match {
|
||||
allocators(bank).tryAllocateZeropageBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, alignment = m.alignment) match {
|
||||
case None => Nil
|
||||
case Some(addr) =>
|
||||
onEachVariable(m.name, addr)
|
||||
@ -182,7 +182,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
} else if (things.contains(graveName)) {
|
||||
Nil
|
||||
} else {
|
||||
val addr = allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Either)
|
||||
val addr = allocators(bank).allocateBytes(mem.banks(bank), callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Either, alignment = m.alignment)
|
||||
onEachVariable(m.name, addr)
|
||||
List(
|
||||
ConstantThing(graveName, NumericConstant(addr, 2), p)
|
||||
@ -294,8 +294,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
InitializedMemoryVariable
|
||||
UninitializedMemoryVariable
|
||||
getArrayOrPointer(name) match {
|
||||
case th@InitializedArray(_, _, cs, _, i, e) => ConstantPointy(th.toAddress, Some(name), Some(cs.length), i, e)
|
||||
case th@UninitializedArray(_, size, _, i, e) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e)
|
||||
case th@InitializedArray(_, _, cs, _, i, e, _) => ConstantPointy(th.toAddress, Some(name), Some(cs.length), i, e)
|
||||
case th@UninitializedArray(_, size, _, i, e, _) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e)
|
||||
case th@RelativeArray(_, _, size, _, i, e) => ConstantPointy(th.toAddress, Some(name), Some(size), i, e)
|
||||
case ConstantThing(_, value, typ) if typ.size <= 2 =>
|
||||
val b = get[VariableType]("byte")
|
||||
@ -713,7 +713,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
})
|
||||
}
|
||||
if (resultType.size > Cpu.getMaxSizeReturnableViaRegisters(options.platform.cpu, options)) {
|
||||
registerVariable(VariableDeclarationStatement(stmt.name + ".return", stmt.resultType, None, global = true, stack = false, constant = false, volatile = false, register = false, None, None), options)
|
||||
registerVariable(VariableDeclarationStatement(stmt.name + ".return", stmt.resultType, None, global = true, stack = false, constant = false, volatile = false, register = false, None, None, None), options)
|
||||
}
|
||||
stmt.statements match {
|
||||
case None =>
|
||||
@ -786,7 +786,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if (!thing.zeropage && options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
val b = get[Type]("byte")
|
||||
val w = get[Type]("word")
|
||||
val relocatable = UninitializedMemoryVariable(thing.name + ".addr", w, VariableAllocationMethod.Static, None)
|
||||
val relocatable = UninitializedMemoryVariable(thing.name + ".addr", w, VariableAllocationMethod.Static, None, defaultVariableAlignment(options, 2))
|
||||
val addr = relocatable.toAddress
|
||||
addThing(relocatable, position)
|
||||
addThing(RelativeVariable(thing.name + ".addr.hi", addr + 1, b, zeropage = false, None), position)
|
||||
@ -814,7 +814,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
stmt.assemblyParamPassingConvention match {
|
||||
case ByVariable(name) =>
|
||||
val zp = typ.name == "pointer" // TODO
|
||||
val v = UninitializedMemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto, None)
|
||||
val v = UninitializedMemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto, None, defaultVariableAlignment(options, 2))
|
||||
addThing(v, stmt.position)
|
||||
registerAddressConstant(v, stmt.position, options)
|
||||
val addr = v.toAddress
|
||||
@ -870,6 +870,18 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
}
|
||||
}
|
||||
|
||||
private def defaultArrayAlignment(options: CompilationOptions, size: Long): MemoryAlignment = {
|
||||
if (options.platform.cpuFamily == CpuFamily.M6502 &&
|
||||
options.flag(CompilationFlag.OptimizeForSpeed) &&
|
||||
size <= 256) WithinPageAlignment
|
||||
else NoAlignment
|
||||
}
|
||||
|
||||
private def defaultVariableAlignment(options: CompilationOptions, size: Long): MemoryAlignment = {
|
||||
if (options.flag(CompilationFlag.PreventJmpIndirectBug) && size == 2) WithinPageAlignment
|
||||
else NoAlignment
|
||||
}
|
||||
|
||||
def registerArray(stmt: ArrayDeclarationStatement, options: CompilationOptions): Unit = {
|
||||
val b = get[VariableType]("byte")
|
||||
val w = get[VariableType]("word")
|
||||
@ -897,14 +909,15 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
lengthConst match {
|
||||
case NumericConstant(length, _) =>
|
||||
if (length > 0xffff || length < 0) log.error(s"Array `${stmt.name}` has invalid length", stmt.position)
|
||||
val alignment = stmt.alignment.getOrElse(defaultArrayAlignment(options, length))
|
||||
val array = address match {
|
||||
case None => UninitializedArray(stmt.name + ".array", length.toInt,
|
||||
declaredBank = stmt.bank, indexType, b)
|
||||
declaredBank = stmt.bank, indexType, b, alignment)
|
||||
case Some(aa) => RelativeArray(stmt.name + ".array", aa, length.toInt,
|
||||
declaredBank = stmt.bank, indexType, b)
|
||||
}
|
||||
addThing(array, stmt.position)
|
||||
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, stmt.bank), stmt.position, options)
|
||||
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, stmt.bank, NoAlignment), stmt.position, options)
|
||||
val a = address match {
|
||||
case None => array.toAddress
|
||||
case Some(aa) => aa
|
||||
@ -914,7 +927,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
val b = get[Type]("byte")
|
||||
val w = get[Type]("word")
|
||||
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None)
|
||||
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None, NoAlignment)
|
||||
val addr = relocatable.toAddress
|
||||
addThing(relocatable, stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".addr.hi", addr + 1, b, zeropage = false, None), stmt.position)
|
||||
@ -968,11 +981,12 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
}
|
||||
val length = contents.length
|
||||
if (length > 0xffff || length < 0) log.error(s"Array `${stmt.name}` has invalid length", stmt.position)
|
||||
val alignment = stmt.alignment.getOrElse(defaultArrayAlignment(options, length))
|
||||
val address = stmt.address.map(a => eval(a).getOrElse(errorConstant(s"Array `${stmt.name}` has non-constant address", stmt.position)))
|
||||
val array = InitializedArray(stmt.name + ".array", address, contents, declaredBank = stmt.bank, indexType, b)
|
||||
val array = InitializedArray(stmt.name + ".array", address, contents, declaredBank = stmt.bank, indexType, b, alignment)
|
||||
addThing(array, stmt.position)
|
||||
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None,
|
||||
declaredBank = stmt.bank), stmt.position, options)
|
||||
declaredBank = stmt.bank, alignment), stmt.position, options)
|
||||
val a = address match {
|
||||
case None => array.toAddress
|
||||
case Some(aa) => aa
|
||||
@ -982,7 +996,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
val b = get[Type]("byte")
|
||||
val w = get[Type]("word")
|
||||
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None)
|
||||
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None, NoAlignment)
|
||||
val addr = relocatable.toAddress
|
||||
addThing(relocatable, stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".array.hi", addr + 1, b, zeropage = false, None), stmt.position)
|
||||
@ -1023,6 +1037,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
val alignment = stmt.alignment.getOrElse(defaultVariableAlignment(options, typ.size))
|
||||
if (stmt.constant) {
|
||||
if (stmt.stack) log.error(s"`$name` is a constant and cannot be on stack", position)
|
||||
if (stmt.register) log.error(s"`$name` is a constant and cannot be in a register", position)
|
||||
@ -1060,11 +1075,11 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
log.error(s"`$name` cannot be preinitialized`", position)
|
||||
}
|
||||
val v = stmt.initialValue.fold[MemoryVariable](UninitializedMemoryVariable(prefix + name, typ, alloc,
|
||||
declaredBank = stmt.bank)){ive =>
|
||||
declaredBank = stmt.bank, alignment)){ive =>
|
||||
if (options.flags(CompilationFlag.ReadOnlyArrays)) {
|
||||
log.warn("Initialized variable in read-only segment", position)
|
||||
}
|
||||
InitializedMemoryVariable(name, None, typ, ive, declaredBank = stmt.bank)
|
||||
InitializedMemoryVariable(name, None, typ, ive, declaredBank = stmt.bank, alignment)
|
||||
}
|
||||
registerAddressConstant(v, stmt.position, options)
|
||||
(v, v.toAddress)
|
||||
@ -1096,6 +1111,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
if (typ.name == "__reg$type") {
|
||||
return (".lo", 0, b) ::
|
||||
(".hi", 1, b) ::
|
||||
(".loword", 0, w) ::
|
||||
(".b2b3", 2, w) ::
|
||||
List.tabulate(typ.size) { i => (".b" + i, i, b) }
|
||||
}
|
||||
@ -1161,7 +1177,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
def collectDeclarations(program: Program, options: CompilationOptions): Unit = {
|
||||
val b = get[VariableType]("byte")
|
||||
if (options.flag(CompilationFlag.OptimizeForSonicSpeed)) {
|
||||
addThing(InitializedArray("identity$", None, List.tabulate(256)(n => LiteralExpression(n, 1)), declaredBank = None, b, b), None)
|
||||
addThing(InitializedArray("identity$", None, List.tabulate(256)(n => LiteralExpression(n, 1)), declaredBank = None, b, b, defaultArrayAlignment(options, 256)), None)
|
||||
}
|
||||
program.declarations.foreach {
|
||||
case a: AliasDefinitionStatement => registerAlias(a)
|
||||
@ -1189,11 +1205,12 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
volatile = false,
|
||||
register = false,
|
||||
initialValue = None,
|
||||
address = None), options)
|
||||
address = None,
|
||||
alignment = None), options)
|
||||
}
|
||||
if (CpuFamily.forType(options.platform.cpu) == CpuFamily.M6502) {
|
||||
if (!things.contains("__constant8")) {
|
||||
things("__constant8") = InitializedArray("__constant8", None, List(LiteralExpression(8, 1)), declaredBank = None, b, b)
|
||||
things("__constant8") = InitializedArray("__constant8", None, List(LiteralExpression(8, 1)), declaredBank = None, b, b, NoAlignment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
22
src/main/scala/millfork/env/Thing.scala
vendored
22
src/main/scala/millfork/env/Thing.scala
vendored
@ -1,9 +1,9 @@
|
||||
package millfork.env
|
||||
|
||||
import millfork.assembly.BranchingOpcodeMapping
|
||||
import millfork.{CompilationFlag, CompilationOptions, CpuFamily}
|
||||
import millfork.assembly.mos.Opcode
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.node._
|
||||
import millfork.output.{MemoryAlignment, NoAlignment}
|
||||
|
||||
sealed trait Thing {
|
||||
def name: String
|
||||
@ -27,7 +27,7 @@ sealed trait Type extends CallableThing {
|
||||
|
||||
def isCompatible(other: Type): Boolean = this == other
|
||||
|
||||
override def toString(): String = name
|
||||
override def toString: String = name
|
||||
|
||||
def isAssignableTo(targetType: Type): Boolean = isCompatible(targetType)
|
||||
|
||||
@ -110,6 +110,8 @@ sealed trait PreallocableThing extends ThingInMemory {
|
||||
|
||||
def address: Option[Constant]
|
||||
|
||||
def alignment: MemoryAlignment
|
||||
|
||||
def toAddress: Constant = address.getOrElse(MemoryAddressConstant(this))
|
||||
}
|
||||
|
||||
@ -152,6 +154,8 @@ sealed trait UninitializedMemory extends ThingInMemory {
|
||||
def sizeInBytes: Int
|
||||
|
||||
def alloc: VariableAllocationMethod.Value
|
||||
|
||||
def alignment: MemoryAlignment
|
||||
}
|
||||
|
||||
object VariableAllocationMethod extends Enumeration {
|
||||
@ -170,7 +174,7 @@ abstract class MemoryVariable extends VariableInMemory {
|
||||
def alloc: VariableAllocationMethod.Value
|
||||
}
|
||||
|
||||
case class UninitializedMemoryVariable(name: String, typ: Type, alloc: VariableAllocationMethod.Value, declaredBank: Option[String]) extends MemoryVariable with UninitializedMemory {
|
||||
case class UninitializedMemoryVariable(name: String, typ: Type, alloc: VariableAllocationMethod.Value, declaredBank: Option[String], override val alignment: MemoryAlignment) extends MemoryVariable with UninitializedMemory {
|
||||
override def sizeInBytes: Int = typ.size
|
||||
|
||||
override def zeropage: Boolean = alloc == VariableAllocationMethod.Zeropage
|
||||
@ -178,7 +182,7 @@ case class UninitializedMemoryVariable(name: String, typ: Type, alloc: VariableA
|
||||
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
|
||||
}
|
||||
|
||||
case class InitializedMemoryVariable(name: String, address: Option[Constant], typ: Type, initialValue: Expression, declaredBank: Option[String]) extends MemoryVariable with PreallocableThing {
|
||||
case class InitializedMemoryVariable(name: String, address: Option[Constant], typ: Type, initialValue: Expression, declaredBank: Option[String], override val alignment: MemoryAlignment) extends MemoryVariable with PreallocableThing {
|
||||
override def zeropage: Boolean = false
|
||||
|
||||
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
|
||||
@ -193,10 +197,10 @@ trait MfArray extends ThingInMemory with IndexableThing {
|
||||
def elementType: VariableType
|
||||
}
|
||||
|
||||
case class UninitializedArray(name: String, sizeInBytes: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType) extends MfArray with UninitializedMemory {
|
||||
case class UninitializedArray(name: String, sizeInBytes: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val alignment: MemoryAlignment) extends MfArray with UninitializedMemory {
|
||||
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
|
||||
|
||||
override def alloc = VariableAllocationMethod.Static
|
||||
override def alloc: VariableAllocationMethod.Value = VariableAllocationMethod.Static
|
||||
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
|
||||
|
||||
@ -215,7 +219,7 @@ case class RelativeArray(name: String, address: Constant, sizeInBytes: Int, decl
|
||||
override def zeropage: Boolean = false
|
||||
}
|
||||
|
||||
case class InitializedArray(name: String, address: Option[Constant], contents: List[Expression], declaredBank: Option[String], indexType: VariableType, elementType: VariableType) extends MfArray with PreallocableThing {
|
||||
case class InitializedArray(name: String, address: Option[Constant], contents: List[Expression], declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val alignment: MemoryAlignment) extends MfArray with PreallocableThing {
|
||||
override def shouldGenerate = true
|
||||
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
|
||||
@ -293,6 +297,8 @@ case class NormalFunction(name: String,
|
||||
override def shouldGenerate = true
|
||||
|
||||
override def zeropage: Boolean = false
|
||||
|
||||
override def alignment: MemoryAlignment = NoAlignment
|
||||
}
|
||||
|
||||
case class ConstantThing(name: String, value: Constant, typ: Type) extends TypedThing with VariableLikeThing with IndexableThing {
|
||||
|
@ -3,6 +3,7 @@ package millfork.node
|
||||
import millfork.assembly.mos.{AddrMode, Opcode}
|
||||
import millfork.assembly.z80.{ZOpcode, ZRegisters}
|
||||
import millfork.env.{Constant, ParamPassingConvention, Type}
|
||||
import millfork.output.MemoryAlignment
|
||||
|
||||
case class Position(moduleName: String, line: Int, column: Int, cursor: Int)
|
||||
|
||||
@ -203,7 +204,8 @@ case class VariableDeclarationStatement(name: String,
|
||||
volatile: Boolean,
|
||||
register: Boolean,
|
||||
initialValue: Option[Expression],
|
||||
address: Option[Expression]) extends DeclarationStatement {
|
||||
address: Option[Expression],
|
||||
alignment: Option[MemoryAlignment]) extends DeclarationStatement {
|
||||
override def getAllExpressions: List[Expression] = List(initialValue, address).flatten
|
||||
}
|
||||
|
||||
@ -268,7 +270,8 @@ case class ArrayDeclarationStatement(name: String,
|
||||
bank: Option[String],
|
||||
length: Option[Expression],
|
||||
address: Option[Expression],
|
||||
elements: Option[ArrayContents]) extends DeclarationStatement {
|
||||
elements: Option[ArrayContents],
|
||||
alignment: Option[MemoryAlignment]) extends DeclarationStatement {
|
||||
override def getAllExpressions: List[Expression] = List(length, address).flatten ++ elements.fold(List[Expression]())(_.getAllExpressions)
|
||||
}
|
||||
|
||||
|
@ -227,7 +227,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
}
|
||||
|
||||
env.allPreallocatables.foreach {
|
||||
case thing@InitializedArray(name, Some(NumericConstant(address, _)), items, _, _, _) =>
|
||||
case thing@InitializedArray(name, Some(NumericConstant(address, _)), items, _, _, _, NoAlignment) =>
|
||||
val bank = thing.bank(options)
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = address.toInt
|
||||
@ -251,7 +251,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
}).mkString(", "))
|
||||
}
|
||||
initializedVariablesSize += items.length
|
||||
case thing@InitializedArray(name, Some(_), items, _, _, _) => ???
|
||||
case thing@InitializedArray(name, Some(_), items, _, _, _, NoAlignment) => ???
|
||||
case f: NormalFunction if f.address.isDefined =>
|
||||
val bank = f.bank(options)
|
||||
val bank0 = mem.banks(bank)
|
||||
@ -279,7 +279,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
// already done before
|
||||
case (name, NormalCompiledFunction(bank, code, false)) =>
|
||||
val size = code.map(_.sizeInBytes).sum
|
||||
val index = codeAllocators(bank).allocateBytes(mem.banks(bank), options, size, initialized = true, writeable = false, location = AllocationLocation.High)
|
||||
val index = codeAllocators(bank).allocateBytes(mem.banks(bank), options, size, initialized = true, writeable = false, location = AllocationLocation.High, alignment = NoAlignment)
|
||||
labelMap(name) = index
|
||||
justAfterCode += bank -> outputFunction(bank, code, index, assembly, options)
|
||||
case (_, NonexistentFunction()) =>
|
||||
@ -289,7 +289,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
|
||||
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
env.allThings.things.foreach {
|
||||
case (_, m@UninitializedMemoryVariable(name, typ, _, _)) if name.endsWith(".addr") || env.maybeGet[Thing](name + ".array").isDefined =>
|
||||
case (_, m@UninitializedMemoryVariable(name, typ, _, _, NoAlignment)) if name.endsWith(".addr") || env.maybeGet[Thing](name + ".array").isDefined =>
|
||||
val isUsed = compiledFunctions.values.exists{
|
||||
case NormalCompiledFunction(_, code, _) => code.exists(_.parameter.isRelatedTo(m))
|
||||
case _ => false
|
||||
@ -299,7 +299,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
val bank = m.bank(options)
|
||||
if (bank != "default") ???
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size + 1, initialized = true, writeable = false, location = AllocationLocation.High)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size + 1, initialized = true, writeable = false, location = AllocationLocation.High, alignment = NoAlignment)
|
||||
labelMap(name) = index + 1
|
||||
val altName = m.name.stripPrefix(env.prefix) + "`"
|
||||
val thing = if (name.endsWith(".addr")) env.get[ThingInMemory](name.stripSuffix(".addr")) else env.get[ThingInMemory](name + ".array")
|
||||
@ -320,17 +320,17 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
val index = codeAllocators("default").allocateBytes(mem.banks("default"), options, 1, initialized = true, writeable = false, location = AllocationLocation.High)
|
||||
val index = codeAllocators("default").allocateBytes(mem.banks("default"), options, 1, initialized = true, writeable = false, location = AllocationLocation.High, alignment = NoAlignment)
|
||||
writeByte("default", index, 2.toByte) // BIT abs
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
assembly.append(" !byte 2 ;; end of LUnix relocatable segment")
|
||||
justAfterCode += "default" -> (index + 1)
|
||||
}
|
||||
env.allPreallocatables.foreach {
|
||||
case thing@InitializedArray(name, None, items, _, _, _) =>
|
||||
case thing@InitializedArray(name, None, items, _, _, _, alignment) =>
|
||||
val bank = thing.bank(options)
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, items.size, initialized = true, writeable = true, location = AllocationLocation.High)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, items.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = index
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
assembly.append(name)
|
||||
@ -349,10 +349,10 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
}
|
||||
initializedVariablesSize += items.length
|
||||
justAfterCode += bank -> index
|
||||
case m@InitializedMemoryVariable(name, None, typ, value, _) =>
|
||||
case m@InitializedMemoryVariable(name, None, typ, value, _, alignment) =>
|
||||
val bank = m.bank(options)
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size, initialized = true, writeable = true, location = AllocationLocation.High)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = index
|
||||
val altName = m.name.stripPrefix(env.prefix) + "`"
|
||||
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
|
||||
|
10
src/main/scala/millfork/output/MemoryAlignment.scala
Normal file
10
src/main/scala/millfork/output/MemoryAlignment.scala
Normal file
@ -0,0 +1,10 @@
|
||||
package millfork.output
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
sealed trait MemoryAlignment
|
||||
|
||||
case object NoAlignment extends MemoryAlignment
|
||||
case object WithinPageAlignment extends MemoryAlignment
|
||||
case class DivisibleAlignment(divisor: Int) extends MemoryAlignment
|
@ -28,13 +28,19 @@ sealed trait ByteAllocator {
|
||||
|
||||
def notifyAboutEndOfCode(org: Int): Unit
|
||||
|
||||
def findFreeBytes(mem: MemoryBank, count: Int, options: CompilationOptions): Int = {
|
||||
def findFreeBytes(mem: MemoryBank, count: Int, options: CompilationOptions, alignment: MemoryAlignment): Int = {
|
||||
var lastFree = startAt
|
||||
var counter = 0
|
||||
val occupied = mem.occupied
|
||||
var previous = -800
|
||||
for(i <- preferredOrder.getOrElse(startAt until endBefore)) {
|
||||
if (occupied(i) || counter == 0 && count == 2 && i.&(0xff) == 0xff && options.flags(CompilationFlag.PreventJmpIndirectBug)) {
|
||||
if (occupied(i)) {
|
||||
counter = 0
|
||||
} else if (counter == 0 && (alignment match {
|
||||
case WithinPageAlignment => i.&(0xff00) != i.+(count - 1).&(0xff00)
|
||||
case DivisibleAlignment(divisor) => i % divisor != 0
|
||||
case NoAlignment => false
|
||||
})) {
|
||||
counter = 0
|
||||
} else {
|
||||
if (previous != i - 1) {
|
||||
@ -87,7 +93,7 @@ class VariableAllocator(pointers: List[Int], private val bytes: ByteAllocator) {
|
||||
|
||||
var heapStart: Int = bytes.startAt
|
||||
|
||||
def allocateBytes(mem: MemoryBank, callGraph: CallGraph, p: VariableVertex, options: CompilationOptions, count: Int, initialized: Boolean, writeable: Boolean, location: AllocationLocation.Value): Int = {
|
||||
def allocateBytes(mem: MemoryBank, callGraph: CallGraph, p: VariableVertex, options: CompilationOptions, count: Int, initialized: Boolean, writeable: Boolean, location: AllocationLocation.Value, alignment: MemoryAlignment): Int = {
|
||||
if (!variableMap.contains(count)) {
|
||||
variableMap(count) = mutable.Map()
|
||||
}
|
||||
@ -97,12 +103,12 @@ class VariableAllocator(pointers: List[Int], private val bytes: ByteAllocator) {
|
||||
return a
|
||||
}
|
||||
}
|
||||
val addr = allocateBytes(mem, options, count, initialized, writeable, location)
|
||||
val addr = allocateBytes(mem, options, count, initialized, writeable, location, alignment)
|
||||
variableMap(count)(addr) = Set(p)
|
||||
addr
|
||||
}
|
||||
|
||||
def tryAllocateZeropageBytes(mem: MemoryBank, callGraph: CallGraph, p: VariableVertex, options: CompilationOptions, count: Int): Option[Int]={
|
||||
def tryAllocateZeropageBytes(mem: MemoryBank, callGraph: CallGraph, p: VariableVertex, options: CompilationOptions, count: Int, alignment: MemoryAlignment): Option[Int]={
|
||||
if (!variableMap.contains(count)) {
|
||||
variableMap(count) = mutable.Map()
|
||||
}
|
||||
@ -112,32 +118,32 @@ class VariableAllocator(pointers: List[Int], private val bytes: ByteAllocator) {
|
||||
return Some(a)
|
||||
}
|
||||
}
|
||||
val addr = zeropage.findFreeBytes(mem, count, options)
|
||||
val addr = zeropage.findFreeBytes(mem, count, options, alignment)
|
||||
if (addr < 0) None else {
|
||||
markBytes(options.log, mem, addr, count, initialized = false, writeable = true)
|
||||
Some(addr)
|
||||
}
|
||||
}
|
||||
|
||||
def allocateBytes(mem: MemoryBank, options: CompilationOptions, count: Int, initialized: Boolean, writeable: Boolean, location: AllocationLocation.Value): Int = {
|
||||
def allocateBytes(mem: MemoryBank, options: CompilationOptions, count: Int, initialized: Boolean, writeable: Boolean, location: AllocationLocation.Value, alignment: MemoryAlignment): Int = {
|
||||
val addr = if (options.platform.hasZeroPage) {
|
||||
location match {
|
||||
case AllocationLocation.Zeropage =>
|
||||
val a = zeropage.findFreeBytes(mem, count, options)
|
||||
val a = zeropage.findFreeBytes(mem, count, options, alignment)
|
||||
if (a < 0) {
|
||||
options.log.fatal("Out of zeropage memory")
|
||||
}
|
||||
a
|
||||
case AllocationLocation.High =>
|
||||
val a = bytes.findFreeBytes(mem, count, options)
|
||||
val a = bytes.findFreeBytes(mem, count, options, alignment)
|
||||
if (a < 0) {
|
||||
options.log.fatal("Out of high memory")
|
||||
}
|
||||
a
|
||||
case AllocationLocation.Either =>
|
||||
var a = zeropage.findFreeBytes(mem, count, options)
|
||||
var a = zeropage.findFreeBytes(mem, count, options, alignment)
|
||||
if (a < 0) {
|
||||
a = bytes.findFreeBytes(mem, count, options)
|
||||
a = bytes.findFreeBytes(mem, count, options, alignment)
|
||||
if (a < 0) {
|
||||
options.log.fatal("Out of high memory")
|
||||
}
|
||||
@ -145,7 +151,7 @@ class VariableAllocator(pointers: List[Int], private val bytes: ByteAllocator) {
|
||||
a
|
||||
}
|
||||
} else {
|
||||
val a = bytes.findFreeBytes(mem, count, options)
|
||||
val a = bytes.findFreeBytes(mem, count, options, alignment)
|
||||
if (a < 0) {
|
||||
options.log.fatal("Out of high memory")
|
||||
}
|
||||
|
@ -107,6 +107,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
name <- identifier ~/ HWS ~/ Pass
|
||||
addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).?.opaque("<address>") ~ HWS
|
||||
initialValue <- ("=" ~/ HWS ~/ mfExpression(1, false)).? ~ HWS
|
||||
alignment = None // TODO
|
||||
_ <- &(EOL) ~/ ""
|
||||
} yield {
|
||||
Seq(VariableDeclarationStatement(name, typ,
|
||||
@ -116,7 +117,7 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
constant = flags("const"),
|
||||
volatile = flags("volatile"),
|
||||
register = flags("register"),
|
||||
initialValue, addr).pos(p))
|
||||
initialValue, addr, alignment).pos(p))
|
||||
}
|
||||
|
||||
val paramDefinition: P[ParameterDeclaration] = for {
|
||||
@ -216,8 +217,9 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri
|
||||
name <- "array" ~ !letterOrDigit ~/ SWS ~ identifier ~ HWS
|
||||
length <- ("[" ~/ AWS ~/ mfExpression(nonStatementLevel, false) ~ AWS ~ "]").? ~ HWS
|
||||
addr <- ("@" ~/ HWS ~/ mfExpression(1, false)).? ~/ HWS
|
||||
alignment = None // TODO
|
||||
contents <- ("=" ~/ HWS ~/ arrayContents).? ~/ HWS
|
||||
} yield Seq(ArrayDeclarationStatement(name, bank, length, addr, contents).pos(p))
|
||||
} yield Seq(ArrayDeclarationStatement(name, bank, length, addr, contents, alignment).pos(p))
|
||||
|
||||
def tightMfExpression(allowIntelHex: Boolean): P[Expression] = {
|
||||
val a = if (allowIntelHex) atomWithIntel else atom
|
||||
|
Loading…
Reference in New Issue
Block a user