mirror of
https://github.com/KarolS/millfork.git
synced 2025-02-02 05:30:07 +00:00
Add custom segment layout support
This commit is contained in:
parent
2a21dd8e2e
commit
5837c6fd7d
@ -152,6 +152,13 @@ For better debugging on NES, RAM segments should use bank number `$ff`.
|
||||
|
||||
* `segment_NAME_fill` – the byte value used to fill gaps and other unused space in the bank. Default: `0`.
|
||||
|
||||
* `segment_NAME_layout` – a comma-separated list of object names that defines in what order the objects are laid out in the segment.
|
||||
One item has to be `*`, it means "all the other objects".
|
||||
For example, `a,b,*,c,d` means that the output will contain `a` first, then `b`, then everything else except for `c` and `d`,
|
||||
then `c` and finally `d`.
|
||||
If an object from that list does not exist, it is ignored.
|
||||
Default: `main,*`
|
||||
|
||||
#### `[output]` section
|
||||
|
||||
* `style` – how multi-segment programs should be output:
|
||||
|
@ -34,6 +34,7 @@ class Platform(
|
||||
val generateBbcMicroInfFile: Boolean,
|
||||
val generateGameBoyChecksums: Boolean,
|
||||
val bankNumbers: Map[String, Int],
|
||||
val bankLayouts: Map[String, Seq[String]],
|
||||
val bankFill: Map[String, Int],
|
||||
val defaultCodeBank: String,
|
||||
val ramInitialValuesBank: Option[String],
|
||||
@ -174,6 +175,24 @@ object Platform {
|
||||
case "" => 0
|
||||
case x => parseNumber(x)
|
||||
})).toMap
|
||||
// needed for ZX81
|
||||
val bankLayouts = banks.map(b => b -> {
|
||||
val layout = as.get(classOf[String], s"segment_${b}_layout", "main,*").split(',').map(_.trim).toSeq
|
||||
if (layout.isEmpty) {
|
||||
log.error(s"Layout for segment $b shouldn't be empty")
|
||||
} else {
|
||||
if (!layout.contains("*")) {
|
||||
log.error(s"Layout for segment $b should contain *")
|
||||
}
|
||||
if (layout.toSet.size != layout.size) {
|
||||
log.error(s"Layout for segment $b should contains duplicates")
|
||||
}
|
||||
if (ramInitialValuesBank.contains(b) && layout.last != "*") {
|
||||
log.warn(s"Layout for the ram_init_segment $b does not end with *")
|
||||
}
|
||||
}
|
||||
layout
|
||||
}).toMap
|
||||
|
||||
// TODO: validate stuff
|
||||
banks.foreach(b => {
|
||||
@ -281,6 +300,7 @@ object Platform {
|
||||
generateBbcMicroInfFile,
|
||||
generateGameBoyChecksums,
|
||||
bankNumbers,
|
||||
bankLayouts,
|
||||
bankFills,
|
||||
defaultCodeBank,
|
||||
ramInitialValuesBank,
|
||||
|
@ -4,7 +4,7 @@ import millfork.assembly._
|
||||
import millfork.compiler.{AbstractCompiler, CompilationContext}
|
||||
import millfork.env._
|
||||
import millfork.error.Logger
|
||||
import millfork.node.{CallGraph, Expression, LiteralExpression, NiceFunctionProperty, Program}
|
||||
import millfork.node.{CallGraph, Expression, FunctionCallExpression, LiteralExpression, NiceFunctionProperty, Program, SumExpression}
|
||||
import millfork._
|
||||
import millfork.assembly.z80.ZLine
|
||||
|
||||
@ -211,6 +211,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
|
||||
val inliningResult = inliningCalculator.calculate(
|
||||
program,
|
||||
platform.bankLayouts,
|
||||
options.flags(CompilationFlag.InlineFunctions) || options.flags(CompilationFlag.OptimizeForSonicSpeed),
|
||||
if (options.flags(CompilationFlag.OptimizeForSonicSpeed)) 4.0
|
||||
else if (options.flags(CompilationFlag.OptimizeForSpeed)) 1.3
|
||||
@ -310,7 +311,9 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
bank0.readable(index) = true
|
||||
index += 1
|
||||
}
|
||||
case None => log.error(s"Non-constant contents of array `$name`", item.position)
|
||||
case None =>
|
||||
env.debugConstness(item)
|
||||
log.error(s"Non-constant contents of array `$name`: " + item, item.position)
|
||||
}
|
||||
}
|
||||
printArrayToAssemblyOutput(assembly, name, elementType, items)
|
||||
@ -338,148 +341,179 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
||||
val codeAllocators = platform.codeAllocators.mapValues(new VariableAllocator(Nil, _)).view.force
|
||||
var justAfterCode = platform.codeAllocators.mapValues(a => a.startAt).view.force
|
||||
|
||||
val sortedCompilerFunctions = compiledFunctions.toList.sortBy { case (name, cf) => if (name == "main") 0 -> "" else cf.orderKey }
|
||||
sortedCompilerFunctions.filterNot(o => unusedRuntimeObjects(o._1)).foreach {
|
||||
case (_, NormalCompiledFunction(_, _, true, _)) =>
|
||||
// already done before
|
||||
case (name, NormalCompiledFunction(bank, functionCode, false, alignment)) =>
|
||||
val size = functionCode.map(_.sizeInBytes).sum
|
||||
val bank0 = mem.banks(bank)
|
||||
val index = codeAllocators(bank).allocateBytes(bank0, options, size, initialized = true, writeable = false, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = bank0.index -> index
|
||||
justAfterCode += bank -> outputFunction(bank, functionCode, index, assembly, options)
|
||||
case _ =>
|
||||
}
|
||||
sortedCompilerFunctions.foreach {
|
||||
case (name, RedirectedFunction(_, target, offset)) =>
|
||||
val tuple = labelMap(target)
|
||||
labelMap(name) = tuple._1 -> (tuple._2 + offset)
|
||||
case _ =>
|
||||
}
|
||||
|
||||
// force early allocation of text literals:
|
||||
env.allPreallocatables.filterNot(o => unusedRuntimeObjects(o.name)).foreach{
|
||||
case thing@InitializedArray(_, _, items, _, _, _, _, _) =>
|
||||
items.foreach(env.eval(_))
|
||||
case InitializedMemoryVariable(_, _, _, value, _, _, _) =>
|
||||
env.eval(value)
|
||||
case _ =>
|
||||
}
|
||||
|
||||
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
env.allThings.things.foreach {
|
||||
case (_, m@UninitializedMemoryVariable(name, typ, _, _, _, _)) if name.endsWith(".addr") || env.maybeGet[Thing](name + ".array").isDefined =>
|
||||
val isUsed = compiledFunctions.values.exists{
|
||||
case NormalCompiledFunction(_, functionCode, _, _) => functionCode.exists(_.parameter.isRelatedTo(m))
|
||||
case _ => false
|
||||
}
|
||||
// println(m.name -> isUsed)
|
||||
if (isUsed) {
|
||||
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, alignment = m.alignment)
|
||||
labelMap(name) = bank0.index -> (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")
|
||||
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
assembly.append(" " + bytePseudoopcode + " $2c")
|
||||
assembly.append(name + ":")
|
||||
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, subbyte(c, i, typ.size))
|
||||
assembly.append(" " + bytePseudoopcode + " " + subbyte(c, i, typ.size).quickSimplify)
|
||||
index += 1
|
||||
}
|
||||
initializedVariablesSize += typ.size
|
||||
justAfterCode += bank -> index
|
||||
}
|
||||
case _ => ()
|
||||
def getLayoutStage(name: String, segment: String): Int = {
|
||||
var result = platform.bankLayouts(segment).indexOf(name)
|
||||
if (result < 0) {
|
||||
result = platform.bankLayouts(segment).indexOf("*")
|
||||
}
|
||||
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(" " + bytePseudoopcode + " 2 ;; end of LUnix relocatable segment")
|
||||
justAfterCode += "default" -> (index + 1)
|
||||
// log.trace(s"Emit stage for ${name} is $result")
|
||||
result
|
||||
}
|
||||
env.getAllFixedAddressObjects.foreach {
|
||||
case (bank, addr, size) =>
|
||||
val bank0 = mem.banks(bank)
|
||||
for(i <- 0 until size) bank0.occupied(addr + i) = true
|
||||
variableAllocators(bank).notifyAboutHole(bank0, addr, size)
|
||||
|
||||
@inline
|
||||
def getLayoutStageThing(th: ThingInMemory): Int = getLayoutStage(th.name, th.bank(options))
|
||||
|
||||
@inline
|
||||
def getLayoutStageNcf(name: String, th: NormalCompiledFunction[_]): Int = getLayoutStage(name, th.segment)
|
||||
|
||||
val layoutStageCount = platform.bankLayouts.values.map(_.length).max
|
||||
val defaultStage = platform.bankLayouts("default").indexOf("*")
|
||||
if (defaultStage < 0) {
|
||||
log.fatal("The layout for the default segment lacks *")
|
||||
}
|
||||
var rwDataStart = Int.MaxValue
|
||||
var rwDataEnd = 0
|
||||
for(readOnlyPass <- Seq(true, false)) {
|
||||
if (!readOnlyPass) {
|
||||
if (options.platform.ramInitialValuesBank.isDefined) {
|
||||
codeAllocators("default").notifyAboutEndOfCode(codeAllocators("default").heapStart)
|
||||
|
||||
val sortedCompilerFunctions = compiledFunctions.toList.sortBy { case (_, cf) => cf.orderKey }
|
||||
for (layoutStage <- 0 until layoutStageCount) {
|
||||
sortedCompilerFunctions.filterNot(o => unusedRuntimeObjects(o._1)).foreach {
|
||||
case (_, NormalCompiledFunction(_, _, true, _)) =>
|
||||
// already done before
|
||||
case (name, th@NormalCompiledFunction(bank, functionCode, false, alignment)) if layoutStage == getLayoutStageNcf(name, th) =>
|
||||
val size = functionCode.map(_.sizeInBytes).sum
|
||||
val bank0 = mem.banks(bank)
|
||||
val index = codeAllocators(bank).allocateBytes(bank0, options, size, initialized = true, writeable = false, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = bank0.index -> index
|
||||
justAfterCode += bank -> outputFunction(bank, functionCode, index, assembly, options)
|
||||
case _ =>
|
||||
}
|
||||
sortedCompilerFunctions.foreach {
|
||||
case (name, RedirectedFunction(segment, target, offset)) if (layoutStage == getLayoutStage(target, segment)) =>
|
||||
val tuple = labelMap(target)
|
||||
labelMap(name) = tuple._1 -> (tuple._2 + offset)
|
||||
case _ =>
|
||||
}
|
||||
if (layoutStage == 0) {
|
||||
// force early allocation of text literals:
|
||||
env.allPreallocatables.filterNot(o => unusedRuntimeObjects(o.name)).foreach {
|
||||
case thing@InitializedArray(_, _, items, _, _, _, _, _) =>
|
||||
items.foreach(env.eval(_))
|
||||
case InitializedMemoryVariable(_, _, _, value, _, _, _) =>
|
||||
env.eval(value)
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
env.allPreallocatables.filterNot(o => unusedRuntimeObjects(o.name)).foreach {
|
||||
case thing@InitializedArray(name, None, items, _, _, elementType, readOnly, alignment) if readOnly == readOnlyPass =>
|
||||
val bank = thing.bank(options)
|
||||
if (options.platform.ramInitialValuesBank.isDefined && !readOnly && bank != "default") {
|
||||
log.error(s"Preinitialized writable array `$name` should be defined in the `default` bank")
|
||||
}
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, thing.sizeInBytes, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = bank0.index -> index
|
||||
if (!readOnlyPass) {
|
||||
rwDataStart = rwDataStart.min(index)
|
||||
rwDataEnd = rwDataEnd.max(index + thing.sizeInBytes)
|
||||
}
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
assembly.append(name + ":")
|
||||
for (item <- items) {
|
||||
env.eval(item) match {
|
||||
case Some(c) =>
|
||||
for (i <- 0 until elementType.size) {
|
||||
writeByte(bank, index, subbyte(c, i, elementType.size))
|
||||
index += 1
|
||||
}
|
||||
case None => log.error(s"Non-constant contents of array `$name`", item.position)
|
||||
|
||||
if (layoutStage == defaultStage && options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
env.allThings.things.foreach {
|
||||
case (_, m@UninitializedMemoryVariable(name, typ, _, _, _, _)) if name.endsWith(".addr") || env.maybeGet[Thing](name + ".array").isDefined =>
|
||||
val isUsed = compiledFunctions.values.exists {
|
||||
case NormalCompiledFunction(_, functionCode, _, _) => functionCode.exists(_.parameter.isRelatedTo(m))
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
printArrayToAssemblyOutput(assembly, name, elementType, items)
|
||||
initializedVariablesSize += items.length
|
||||
justAfterCode += bank -> index
|
||||
case m@InitializedMemoryVariable(name, None, typ, value, _, alignment, _) if !readOnlyPass=>
|
||||
val bank = m.bank(options)
|
||||
if (options.platform.ramInitialValuesBank.isDefined && bank != "default") {
|
||||
log.error(s"Preinitialized variable `$name` should be defined in the `default` bank")
|
||||
}
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = bank0.index -> index
|
||||
if (!readOnlyPass) {
|
||||
rwDataStart = rwDataStart.min(index)
|
||||
rwDataEnd = rwDataEnd.max(index + typ.size)
|
||||
}
|
||||
val altName = m.name.stripPrefix(env.prefix) + "`"
|
||||
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
assembly.append(name + ":")
|
||||
env.eval(value) match {
|
||||
case Some(c) =>
|
||||
// println(m.name -> isUsed)
|
||||
if (isUsed) {
|
||||
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, alignment = m.alignment)
|
||||
labelMap(name) = bank0.index -> (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")
|
||||
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
assembly.append(" " + bytePseudoopcode + " $2c")
|
||||
assembly.append(name + ":")
|
||||
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, subbyte(c, i, typ.size))
|
||||
assembly.append(" " + bytePseudoopcode + " " + subbyte(c, i, typ.size).quickSimplify)
|
||||
index += 1
|
||||
}
|
||||
case None =>
|
||||
log.error(s"Non-constant initial value for variable `$name`")
|
||||
index += typ.size
|
||||
initializedVariablesSize += typ.size
|
||||
justAfterCode += bank -> index
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
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(" " + bytePseudoopcode + " 2 ;; end of LUnix relocatable segment")
|
||||
justAfterCode += "default" -> (index + 1)
|
||||
}
|
||||
if (layoutStage == 0) {
|
||||
env.getAllFixedAddressObjects.foreach {
|
||||
case (bank, addr, size) =>
|
||||
val bank0 = mem.banks(bank)
|
||||
for (i <- 0 until size) bank0.occupied(addr + i) = true
|
||||
variableAllocators(bank).notifyAboutHole(bank0, addr, size)
|
||||
}
|
||||
}
|
||||
for (readOnlyPass <- Seq(true, false)) {
|
||||
if (layoutStage == defaultStage && !readOnlyPass) {
|
||||
if (options.platform.ramInitialValuesBank.isDefined) {
|
||||
codeAllocators("default").notifyAboutEndOfCode(codeAllocators("default").heapStart)
|
||||
}
|
||||
initializedVariablesSize += typ.size
|
||||
justAfterCode += bank -> index
|
||||
case _ =>
|
||||
}
|
||||
env.allPreallocatables.filterNot(o => unusedRuntimeObjects(o.name)).foreach {
|
||||
case thing@InitializedArray(name, None, items, _, _, elementType, readOnly, alignment) if readOnly == readOnlyPass && layoutStage == getLayoutStageThing(thing) =>
|
||||
val bank = thing.bank(options)
|
||||
if (options.platform.ramInitialValuesBank.isDefined && !readOnly && bank != "default") {
|
||||
log.error(s"Preinitialized writable array `$name` should be defined in the `default` bank")
|
||||
}
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, thing.sizeInBytes, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = bank0.index -> index
|
||||
if (!readOnlyPass) {
|
||||
rwDataStart = rwDataStart.min(index)
|
||||
rwDataEnd = rwDataEnd.max(index + thing.sizeInBytes)
|
||||
}
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
assembly.append(name + ":")
|
||||
for (item <- items) {
|
||||
val w = env
|
||||
env.eval(item) match {
|
||||
case Some(c) =>
|
||||
for (i <- 0 until elementType.size) {
|
||||
writeByte(bank, index, subbyte(c, i, elementType.size))
|
||||
index += 1
|
||||
}
|
||||
case None =>
|
||||
env.debugConstness(item)
|
||||
log.error(s"Non-constant contents of array `$name`:" + item, item.position)
|
||||
}
|
||||
}
|
||||
printArrayToAssemblyOutput(assembly, name, elementType, items)
|
||||
initializedVariablesSize += items.length
|
||||
justAfterCode += bank -> index
|
||||
case m@InitializedMemoryVariable(name, None, typ, value, _, alignment, _) if !readOnlyPass && layoutStage == getLayoutStageThing(m) =>
|
||||
val bank = m.bank(options)
|
||||
if (options.platform.ramInitialValuesBank.isDefined && bank != "default") {
|
||||
log.error(s"Preinitialized variable `$name` should be defined in the `default` bank")
|
||||
}
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = bank0.index -> index
|
||||
if (!readOnlyPass) {
|
||||
rwDataStart = rwDataStart.min(index)
|
||||
rwDataEnd = rwDataEnd.max(index + typ.size)
|
||||
}
|
||||
val altName = m.name.stripPrefix(env.prefix) + "`"
|
||||
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
assembly.append(name + ":")
|
||||
env.eval(value) match {
|
||||
case Some(c) =>
|
||||
for (i <- 0 until typ.size) {
|
||||
writeByte(bank, index, subbyte(c, i, typ.size))
|
||||
assembly.append(" " + bytePseudoopcode + " " + subbyte(c, i, typ.size).quickSimplify)
|
||||
index += 1
|
||||
}
|
||||
case None =>
|
||||
env.debugConstness(value)
|
||||
log.error(s"Non-constant initial value for variable `$name`")
|
||||
index += typ.size
|
||||
}
|
||||
initializedVariablesSize += typ.size
|
||||
justAfterCode += bank -> index
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rwDataEnd == 0 && rwDataStart == Int.MaxValue) {
|
||||
|
@ -25,6 +25,7 @@ abstract class AbstractInliningCalculator[T <: AbstractCode] {
|
||||
private val sizes = Seq(64, 64, 8, 6, 5, 5, 4)
|
||||
|
||||
def calculate(program: Program,
|
||||
bankLayouts: Map[String, Seq[String]],
|
||||
inlineByDefault: Boolean,
|
||||
aggressivenessForNormal: Double,
|
||||
aggressivenessForRecommended: Double): InliningResult = {
|
||||
@ -48,6 +49,7 @@ abstract class AbstractInliningCalculator[T <: AbstractCode] {
|
||||
|| f.interrupt
|
||||
|| f.reentrant
|
||||
|| f.name == "main"
|
||||
|| bankLayouts.values.exists(_.contains(f.name))
|
||||
|| containsReturnDispatch(f.statements.getOrElse(Nil))) badFunctions += f.name
|
||||
case _ =>
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
val representative = if (set("main")) "main" else set.head
|
||||
options.log.debug(s"Functions [${set.mkString(",")}] are identical")
|
||||
for (function <- set) {
|
||||
if (function != representative) {
|
||||
if (function != representative && !options.platform.bankLayouts(segmentName).contains(function)) {
|
||||
result += function -> RedirectedFunction(segmentName, representative, 0)
|
||||
} else {
|
||||
segContents(function) match {
|
||||
@ -216,6 +216,7 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
else getJump(code.last)
|
||||
.filter(segContents.contains)
|
||||
.filter(_ != name)
|
||||
.filter(n => !options.platform.bankLayouts(segmentName).contains(n))
|
||||
.filter(_ != "main")
|
||||
.flatMap(to => follow(segContents, to))
|
||||
.map(name -> _)
|
||||
@ -250,6 +251,7 @@ abstract class Deduplicate[T <: AbstractCode](env: Environment, options: Compila
|
||||
else getJump(code.last)
|
||||
.filter(segContents.contains)
|
||||
.filter(_ != name)
|
||||
.filter(n => !options.platform.bankLayouts(segmentName).contains(n))
|
||||
.filter(_ != "main")
|
||||
.map(name -> _)
|
||||
case _ => None
|
||||
|
@ -36,6 +36,7 @@ object EmuPlatform {
|
||||
false,
|
||||
false,
|
||||
Map("default" -> 0, "second" -> 2, "third" -> 3),
|
||||
Map("default" -> Seq("main", "*"), "second" -> Seq("main", "*"), "third" -> Seq("main", "*")),
|
||||
Map("default" -> 0, "second" -> 0, "third" -> 0),
|
||||
"default",
|
||||
None,
|
||||
|
Loading…
x
Reference in New Issue
Block a user