millfork/src/main/scala/millfork/output/VariableAllocator.scala

97 lines
2.8 KiB
Scala

package millfork.output
import millfork.error.ErrorReporting
import millfork.node.{CallGraph, VariableVertex}
import millfork.{CompilationFlag, CompilationOptions}
import scala.collection.mutable
/**
* @author Karol Stasiak
*/
sealed trait ByteAllocator {
def notifyAboutEndOfCode(org: Int): Unit
def allocateBytes(count: Int, options: CompilationOptions): Int
}
class UpwardByteAllocator(startAt: Int, endBefore: Int) extends ByteAllocator {
private var nextByte = startAt
def allocateBytes(count: Int, options: CompilationOptions): Int = {
if (count == 2 && (nextByte & 0xff) == 0xff && options.flag(CompilationFlag.PreventJmpIndirectBug)) nextByte += 1
val t = nextByte
nextByte += count
if (nextByte > endBefore) {
ErrorReporting.fatal("Out of high memory")
}
t
}
def notifyAboutEndOfCode(org: Int): Unit = ()
}
class AfterCodeByteAllocator(endBefore: Int) extends ByteAllocator {
var nextByte = 0x200
def allocateBytes(count: Int, options: CompilationOptions): Int = {
if (count == 2 && (nextByte & 0xff) == 0xff && options.flag(CompilationFlag.PreventJmpIndirectBug)) nextByte += 1
val t = nextByte
nextByte += count
if (nextByte > endBefore) {
ErrorReporting.fatal("Out of high memory")
}
t
}
def notifyAboutEndOfCode(org: Int): Unit = nextByte = org
}
class VariableAllocator(private var pointers: List[Int], private val bytes: ByteAllocator) {
private var pointerMap = mutable.Map[Int, Set[VariableVertex]]()
private var variableMap = mutable.Map[Int, mutable.Map[Int, Set[VariableVertex]]]()
var onEachByte: (Int => Unit) = _
def allocatePointer(callGraph: CallGraph, p: VariableVertex): Int = {
pointerMap.foreach { case (addr, alreadyThere) =>
if (alreadyThere.forall(q => callGraph.canOverlap(p, q))) {
pointerMap(addr) += p
return addr
}
}
pointers match {
case Nil =>
ErrorReporting.fatal("Out of zero-page memory")
case next :: rest =>
pointers = rest
onEachByte(next)
onEachByte(next + 1)
pointerMap(next) = Set(p)
next
}
}
def allocateByte(callGraph: CallGraph, p: VariableVertex, options: CompilationOptions): Int = allocateBytes(callGraph, p, options, 1)
def allocateBytes(callGraph: CallGraph, p: VariableVertex, options: CompilationOptions, count: Int): Int = {
if (!variableMap.contains(count)) {
variableMap(count) = mutable.Map()
}
variableMap(count).foreach { case (a, alreadyThere) =>
if (alreadyThere.forall(q => callGraph.canOverlap(p, q))) {
variableMap(count)(a) += p
return a
}
}
val addr = bytes.allocateBytes(count, options)
(addr to (addr + count)).foreach(onEachByte)
variableMap(count)(addr) = Set(p)
addr
}
def notifyAboutEndOfCode(org: Int): Unit = bytes.notifyAboutEndOfCode(org)
}